Skip to content

Fix Decimal Match Logic #974

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- CLI no longer terminates on user errors in submitted PartiQL (when printing out the AST with !!)
and no longer prints out stack traces upon user errors.

- Constrained Decimal matching logic.

### Removed
- The deprecated `IonValue` property in `ExprValue` interface is now removed.
- Removed partiql-extensions to partiql-cli `org.partiql.cli.functions`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ class PartiqlAstSanityValidator : PartiqlAst.Visitor() {

private fun validateDecimalOrNumericType(scale: LongPrimitive?, precision: LongPrimitive?, metas: MetaContainer) {
if (scale != null && precision != null && compileOptions.typedOpBehavior == TypedOpBehavior.HONOR_PARAMETERS) {
if (precision.value <= 0L) {
err(
"Precision ${precision.value} should be a positive integer",
errorCode = ErrorCode.SEMANTIC_INVALID_DECIMAL_ARGUMENTS,
errorContext = errorContextFrom(metas),
internal = false
)
}
if (scale.value !in 0..precision.value) {
err(
"Scale ${scale.value} should be between 0 and precision ${precision.value}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,10 @@ abstract class CastTestBase : EvaluatorTestBase() {
listOf(
case("1", ErrorCode.SEMANTIC_INVALID_DECIMAL_ARGUMENTS)
).types(ExprValueType.DECIMAL.typeAliases().map { "$it(2,4)" }),
// DECIMAL(0, 0) is a compilation failure in this mode because we should not allow precision to be zero
listOf(
case("1", ErrorCode.SEMANTIC_INVALID_DECIMAL_ARGUMENTS)
).types(ExprValueType.DECIMAL.typeAliases().map { "$it(0,0)" }),
// VARCHAR(4) should truncate to size <= 4
listOf(
// from string types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,20 @@ class EvaluatingCompilerIsTests : EvaluatorTestBase() {
expectedIsDecimalHonorParamsResult = "TRUE"
),

// Equal Precision and scale
isDecimalTypeTestCase(
sql = "0.001 is DECIMAL(3,3)",
expectedLegacyResult = "TRUE",
expectedIsDecimalHonorParamsResult = "TRUE"
),

// Equal Precision and scale
isDecimalTypeTestCase(
sql = "1.000 is DECIMAL(4,3)",
expectedLegacyResult = "TRUE",
expectedIsDecimalHonorParamsResult = "TRUE"
),

// less precision and scale
isDecimalTypeTestCase(
sql = "123.456 IS DECIMAL(2, 2)",
Expand Down
17 changes: 13 additions & 4 deletions partiql-types/src/main/kotlin/org/partiql/types/StaticType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package org.partiql.types

import java.math.BigDecimal
import java.math.RoundingMode

/**
* Represents static types available in the language and ways to extends them to create new types.
Expand Down Expand Up @@ -300,11 +301,19 @@ data class DecimalType(

data class Constrained(val precision: Int, val scale: Int = 0) : PrecisionScaleConstraint() {
override fun matches(d: BigDecimal): Boolean {
val dv = d.stripTrailingZeros()

val integerDigits = dv.precision() - dv.scale()
// check scale
val decimalPoint = if (d.scale() >= 0) d.scale() else 0
if (decimalPoint > scale) {
return false
}
// check integer part
val integerPart = d.setScale(0, RoundingMode.DOWN)
val integerLength = if (integerPart.signum() != 0) integerPart.precision() - integerPart.scale() else 0
// PartiQL precision semantics -> the maximum number of total digit (left of decimal place + right of decimal place)
// PartiQL scale semantics -> the total number of digit after the decimal point.
val expectedIntegerDigits = precision - scale
return integerDigits <= expectedIntegerDigits && dv.scale() <= scale

return expectedIntegerDigits >= integerLength
}
}
}
Expand Down