From 17f59e22d8a133a0746020491bd884c6aba111cb Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Wed, 10 Jul 2024 11:43:27 -0700 Subject: [PATCH] Fixes null comparisons and coercions of null collections --- .../eval/internal/operator/rex/ExprCast.kt | 13 +- .../internal/typer/PlanTyperTestsPorted.kt | 7 +- .../partiql/spi/connector/sql/SqlBuiltins.kt | 27 - .../spi/connector/sql/builtins/FnEq.kt | 774 +----------------- .../spi/connector/sql/builtins/FnIsNull.kt | 6 +- 5 files changed, 36 insertions(+), 791 deletions(-) diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCast.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCast.kt index eb67517299..c59ca45214 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCast.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCast.kt @@ -322,12 +322,17 @@ internal class ExprCast(val arg: Operator.Expr, val cast: Ref.Cast) : Operator.E } } - // TODO: Fix NULL Collection @OptIn(PartiQLValueExperimental::class) private fun castFromCollection(value: CollectionValue<*>, t: PType): PartiQLValue { - val elements = mutableListOf() - value.iterator().forEachRemaining { - elements.add(it) + val elements = when (value.isNull) { + true -> null + false -> { + val elements = mutableListOf() + value.iterator().forEachRemaining { + elements.add(it) + } + elements + } } return when (t.kind) { PType.Kind.BAG -> bagValue(elements) diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt index 481a819cad..4680c49d2f 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt @@ -595,14 +595,13 @@ internal class PlanTyperTestsPorted { catalog = "pql", expected = StaticType.BOOL, ), - ErrorTestCase( + // TODO: For some reason, the conformance tests say that this results in TRUE. Regardless, we know it returns + // a boolean. We should re-look at what the conformance tests should return. + SuccessTestCase( name = "MISSING IS NULL", key = key("is-type-04"), catalog = "pql", expected = StaticType.BOOL, - problemHandler = assertProblemExists( - ProblemGenerator.expressionAlwaysReturnsMissing("Static function always receives MISSING arguments.") - ) ), SuccessTestCase( name = "NULL IS NULL", diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/SqlBuiltins.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/SqlBuiltins.kt index 6678cb0840..d81ff9334e 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/SqlBuiltins.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/SqlBuiltins.kt @@ -121,33 +121,6 @@ internal object SqlBuiltins { Fn_DIVIDE__FLOAT32_FLOAT32__FLOAT32, Fn_DIVIDE__FLOAT64_FLOAT64__FLOAT64, Fn_DIVIDE__DECIMAL_ARBITRARY_DECIMAL_ARBITRARY__DECIMAL_ARBITRARY, - Fn_EQ__NULL_NULL__BOOL, - Fn_EQ__MISSING_MISSING__BOOL, - Fn_EQ__BOOL_BOOL__BOOL, - Fn_EQ__INT8_INT8__BOOL, - Fn_EQ__INT16_INT16__BOOL, - Fn_EQ__INT32_INT32__BOOL, - Fn_EQ__INT64_INT64__BOOL, - Fn_EQ__INT_INT__BOOL, - Fn_EQ__DECIMAL_DECIMAL__BOOL, - Fn_EQ__FLOAT32_FLOAT32__BOOL, - Fn_EQ__FLOAT64_FLOAT64__BOOL, - Fn_EQ__DECIMAL_ARBITRARY_DECIMAL_ARBITRARY__BOOL, - Fn_EQ__CHAR_CHAR__BOOL, - Fn_EQ__STRING_STRING__BOOL, - Fn_EQ__CLOB_CLOB__BOOL, - Fn_EQ__SYMBOL_SYMBOL__BOOL, - Fn_EQ__BINARY_BINARY__BOOL, - Fn_EQ__BYTE_BYTE__BOOL, - Fn_EQ__BLOB_BLOB__BOOL, - Fn_EQ__DATE_DATE__BOOL, - Fn_EQ__TIME_TIME__BOOL, - Fn_EQ__TIMESTAMP_TIMESTAMP__BOOL, - Fn_EQ__INTERVAL_INTERVAL__BOOL, - Fn_EQ__LIST_LIST__BOOL, - Fn_EQ__SEXP_SEXP__BOOL, - Fn_EQ__BAG_BAG__BOOL, - Fn_EQ__STRUCT_STRUCT__BOOL, Fn_EQ__ANY_ANY__BOOL, Fn_EXTRACT_DAY__DATE__INT32, Fn_EXTRACT_DAY__TIMESTAMP__INT32, diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnEq.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnEq.kt index dbdee96799..3febb655a6 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnEq.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnEq.kt @@ -7,69 +7,32 @@ import org.partiql.spi.fn.Fn import org.partiql.spi.fn.FnExperimental import org.partiql.spi.fn.FnParameter import org.partiql.spi.fn.FnSignature -import org.partiql.value.BagValue -import org.partiql.value.BinaryValue -import org.partiql.value.BlobValue -import org.partiql.value.BoolValue -import org.partiql.value.ByteValue -import org.partiql.value.CharValue -import org.partiql.value.ClobValue -import org.partiql.value.DateValue -import org.partiql.value.DecimalValue -import org.partiql.value.Float32Value -import org.partiql.value.Float64Value -import org.partiql.value.Int16Value -import org.partiql.value.Int32Value -import org.partiql.value.Int64Value -import org.partiql.value.Int8Value -import org.partiql.value.IntValue -import org.partiql.value.IntervalValue -import org.partiql.value.ListValue import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental import org.partiql.value.PartiQLValueType.ANY -import org.partiql.value.PartiQLValueType.BAG -import org.partiql.value.PartiQLValueType.BINARY -import org.partiql.value.PartiQLValueType.BLOB import org.partiql.value.PartiQLValueType.BOOL -import org.partiql.value.PartiQLValueType.BYTE -import org.partiql.value.PartiQLValueType.CHAR -import org.partiql.value.PartiQLValueType.CLOB -import org.partiql.value.PartiQLValueType.DATE -import org.partiql.value.PartiQLValueType.DECIMAL -import org.partiql.value.PartiQLValueType.DECIMAL_ARBITRARY -import org.partiql.value.PartiQLValueType.FLOAT32 -import org.partiql.value.PartiQLValueType.FLOAT64 -import org.partiql.value.PartiQLValueType.INT -import org.partiql.value.PartiQLValueType.INT16 -import org.partiql.value.PartiQLValueType.INT32 -import org.partiql.value.PartiQLValueType.INT64 -import org.partiql.value.PartiQLValueType.INT8 -import org.partiql.value.PartiQLValueType.INTERVAL -import org.partiql.value.PartiQLValueType.LIST import org.partiql.value.PartiQLValueType.MISSING -import org.partiql.value.PartiQLValueType.NULL -import org.partiql.value.PartiQLValueType.SEXP -import org.partiql.value.PartiQLValueType.STRING -import org.partiql.value.PartiQLValueType.STRUCT -import org.partiql.value.PartiQLValueType.SYMBOL -import org.partiql.value.PartiQLValueType.TIME -import org.partiql.value.PartiQLValueType.TIMESTAMP -import org.partiql.value.SexpValue -import org.partiql.value.StringValue -import org.partiql.value.StructValue -import org.partiql.value.SymbolValue -import org.partiql.value.TimeValue -import org.partiql.value.TimestampValue import org.partiql.value.boolValue -import org.partiql.value.check +/** + * According to SQL:1999: + * > If either XV or YV is the null value, then `X Y` is unknown + * + * According to the PartiQL Specification: + * > Equality never fails in the type-checking mode and never returns MISSING in the permissive mode. Instead, it can + * compare values of any two types, according to the rules of the PartiQL type system. For example, 5 = 'a' is false. + * + * For the existing conformance tests, whenever an operand is NULL or MISSING, the output is NULL. This implementation + * follows this. + * + * TODO: The PartiQL Specification needs to clearly define the semantics of MISSING. That being said, this implementation + * follows the existing conformance tests and SQL:1999. + */ @OptIn(PartiQLValueExperimental::class, FnExperimental::class) internal object Fn_EQ__ANY_ANY__BOOL : Fn { private val comparator = PartiQLValue.comparator() - // Since the ANY_ANY function is the catch-all, we must be able to take in nulls. Therefore, isNullCall must be false. override val signature = FnSignature( name = "eq", returns = BOOL, @@ -77,717 +40,18 @@ internal object Fn_EQ__ANY_ANY__BOOL : Fn { FnParameter("lhs", ANY), FnParameter("rhs", ANY), ), - isNullable = false, - isNullCall = false, + isNullable = true, + isNullCall = true, isMissable = false, isMissingCall = false, ) - // TODO ANY, ANY equals not clearly defined at the moment. override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } val lhs = args[0] val rhs = args[1] - return boolValue(comparator.compare(lhs, rhs) == 0) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__BOOL_BOOL__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", BOOL), - FnParameter("rhs", BOOL), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__INT8_INT8__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", INT8), - FnParameter("rhs", INT8), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__INT16_INT16__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", INT16), - FnParameter("rhs", INT16), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__INT32_INT32__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", INT32), - FnParameter("rhs", INT32), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__INT64_INT64__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", INT64), - FnParameter("rhs", INT64), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__INT_INT__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", INT), - FnParameter("rhs", INT), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__DECIMAL_DECIMAL__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", DECIMAL), - FnParameter("rhs", DECIMAL), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__DECIMAL_ARBITRARY_DECIMAL_ARBITRARY__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", DECIMAL_ARBITRARY), - FnParameter("rhs", DECIMAL_ARBITRARY), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__FLOAT32_FLOAT32__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", FLOAT32), - FnParameter("rhs", FLOAT32), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__FLOAT64_FLOAT64__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", FLOAT64), - FnParameter("rhs", FLOAT64), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__CHAR_CHAR__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", CHAR), - FnParameter("rhs", CHAR), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__STRING_STRING__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", STRING), - FnParameter("rhs", STRING), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__SYMBOL_SYMBOL__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", SYMBOL), - FnParameter("rhs", SYMBOL), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__BINARY_BINARY__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", BINARY), - FnParameter("rhs", BINARY), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__BYTE_BYTE__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", BYTE), - FnParameter("rhs", BYTE), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__BLOB_BLOB__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", BLOB), - FnParameter("rhs", BLOB), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__CLOB_CLOB__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", CLOB), - FnParameter("rhs", CLOB), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__DATE_DATE__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", DATE), - FnParameter("rhs", DATE), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__TIME_TIME__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", TIME), - FnParameter("rhs", TIME), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__TIMESTAMP_TIMESTAMP__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", TIMESTAMP), - FnParameter("rhs", TIMESTAMP), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__INTERVAL_INTERVAL__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", INTERVAL), - FnParameter("rhs", INTERVAL), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check() - val rhs = args[1].check() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__BAG_BAG__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", BAG), - FnParameter("rhs", BAG), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) + if (lhs.type == MISSING || rhs.type == MISSING) { + return boolValue(null) } - val lhs = args[0].check>() - val rhs = args[1].check>() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__LIST_LIST__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", LIST), - FnParameter("rhs", LIST), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check>() - val rhs = args[1].check>() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__SEXP_SEXP__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", SEXP), - FnParameter("rhs", SEXP), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check>() - val rhs = args[1].check>() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__STRUCT_STRUCT__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", STRUCT), - FnParameter("rhs", STRUCT), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - val lhs = args[0].check>() - val rhs = args[1].check>() - return boolValue(lhs == rhs) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__NULL_NULL__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", NULL), - FnParameter("rhs", NULL), - ), - isNullable = false, - isNullCall = true, - isMissable = false, - isMissingCall = false, - ) - - // TODO how does null comparison work? ie null.null == null.null or int8.null == null.null ?? - override fun invoke(args: Array): PartiQLValue { - if (args[0].type == MISSING || args[1].type == MISSING) { - return boolValue(args[0] == args[1]) - } - // According to the conformance tests, NULL = NULL -> NULL - return boolValue(null) - } -} - -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) -internal object Fn_EQ__MISSING_MISSING__BOOL : Fn { - - override val signature = FnSignature( - name = "eq", - returns = BOOL, - parameters = listOf( - FnParameter("lhs", MISSING), - FnParameter("rhs", MISSING), - ), - isNullable = false, - isNullCall = false, - isMissable = false, - isMissingCall = false, - ) - - // TODO how does `=` work with MISSING? As of now, always false. - override fun invoke(args: Array): PartiQLValue { - return boolValue(false) + return boolValue(comparator.compare(lhs, rhs) == 0) } } diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnIsNull.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnIsNull.kt index 6c1a8ff7cf..671364bb3a 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnIsNull.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnIsNull.kt @@ -11,6 +11,7 @@ import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental import org.partiql.value.PartiQLValueType.ANY import org.partiql.value.PartiQLValueType.BOOL +import org.partiql.value.PartiQLValueType.MISSING import org.partiql.value.boolValue @OptIn(PartiQLValueExperimental::class, FnExperimental::class) @@ -23,10 +24,13 @@ internal object Fn_IS_NULL__ANY__BOOL : Fn { isNullable = false, isNullCall = false, isMissable = false, - isMissingCall = true, + isMissingCall = false, ) override fun invoke(args: Array): PartiQLValue { + if (args[0].type == MISSING) { + return boolValue(true) + } return boolValue(args[0].isNull) } }