From 97602cec77bdedc6dd4d20e5acef4f2b62211279 Mon Sep 17 00:00:00 2001 From: "R. C. Howell" Date: Wed, 20 Dec 2023 11:13:05 -0800 Subject: [PATCH 1/2] Use ANY as parameter for IS TYPE operators --- .../org/partiql/planner/PartiQLHeader.kt | 83 +++++++++---------- 1 file changed, 38 insertions(+), 45 deletions(-) diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/PartiQLHeader.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/PartiQLHeader.kt index 2bbae8b14..e7bd29109 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/PartiQLHeader.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/PartiQLHeader.kt @@ -403,68 +403,61 @@ internal object PartiQLHeader : Header() { // To model type assertion, generating a list of assertion function based on the type, // and the parameter will be the value entered. // i.e., 1 is INT2 => is_int16(1) - private fun isType(): List = types.all.filterNot { it == NULL || it == MISSING }.flatMap { element -> - types.all.filterNot { it == MISSING || it == ANY }.map { operand -> + private fun isType(): List = + types.all.filterNot { it == NULL || it == MISSING }.map { element -> FunctionSignature.Scalar( name = "is_${element.name.lowercase()}", returns = BOOL, parameters = listOf( - FunctionParameter("value", operand) + FunctionParameter("value", ANY) ), isNullCall = false, // TODO: Should this be true? isNullable = false ) } - } // In type assertion, it is possible for types to have args // i.e., 'a' is CHAR(2) // we put type parameter before value. - private fun isTypeSingleArg(): List = listOf(CHAR, STRING).flatMap { element -> - types.all.filterNot { it == MISSING }.map { operand -> - FunctionSignature.Scalar( - name = "is_${element.name.lowercase()}", - returns = BOOL, - parameters = listOf( - FunctionParameter("type_parameter_1", INT32), - FunctionParameter("value", operand) - ), - isNullable = false, // TODO: Should this be true? - isNullCall = false - ) - } + private fun isTypeSingleArg(): List = listOf(CHAR, STRING).map { element -> + FunctionSignature.Scalar( + name = "is_${element.name.lowercase()}", + returns = BOOL, + parameters = listOf( + FunctionParameter("type_parameter_1", INT32), + FunctionParameter("value", ANY) + ), + isNullable = false, // TODO: Should this be true? + isNullCall = false + ) } - private fun isTypeDoubleArgsInt(): List = listOf(DECIMAL).flatMap { element -> - types.all.filterNot { it == MISSING }.map { operand -> - FunctionSignature.Scalar( - name = "is_${element.name.lowercase()}", - returns = BOOL, - parameters = listOf( - FunctionParameter("type_parameter_1", INT32), - FunctionParameter("type_parameter_2", INT32), - FunctionParameter("value", operand) - ), - isNullable = false, - isNullCall = false - ) - } + private fun isTypeDoubleArgsInt(): List = listOf(DECIMAL).map { element -> + FunctionSignature.Scalar( + name = "is_${element.name.lowercase()}", + returns = BOOL, + parameters = listOf( + FunctionParameter("type_parameter_1", INT32), + FunctionParameter("type_parameter_2", INT32), + FunctionParameter("value", ANY) + ), + isNullable = false, + isNullCall = false + ) } - private fun isTypeTime(): List = listOf(TIME, TIMESTAMP).flatMap { element -> - types.all.filterNot { it == MISSING }.map { operand -> - FunctionSignature.Scalar( - name = "is_${element.name.lowercase()}", - returns = BOOL, - parameters = listOf( - FunctionParameter("type_parameter_1", BOOL), - FunctionParameter("type_parameter_2", INT32), - FunctionParameter("value", operand) // TODO: Decide if we need to further segment this - ), - isNullCall = false, - isNullable = false - ) - } + private fun isTypeTime(): List = listOf(TIME, TIMESTAMP).map { element -> + FunctionSignature.Scalar( + name = "is_${element.name.lowercase()}", + returns = BOOL, + parameters = listOf( + FunctionParameter("type_parameter_1", BOOL), + FunctionParameter("type_parameter_2", INT32), + FunctionParameter("value", ANY) // TODO: Decide if we need to further segment this + ), + isNullCall = false, + isNullable = false + ) } // SUBSTRING (expression, start[, length]?) From caca0496c57d72986d5a101a5d5576b39ce3098d Mon Sep 17 00:00:00 2001 From: "R. C. Howell" Date: Fri, 22 Dec 2023 12:49:29 -0800 Subject: [PATCH 2/2] Adds IS tests --- .../partiql/planner/internal/PartiQLHeader.kt | 10 +-- .../internal/typer/PlanTyperTestsPorted.kt | 72 ++++++++++++++++++- .../inputs/schema_inferencer/is_type.sql | 27 +++++++ 3 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 partiql-planner/src/testFixtures/resources/inputs/schema_inferencer/is_type.sql diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/PartiQLHeader.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/PartiQLHeader.kt index 38d851d94..24e5e22bf 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/PartiQLHeader.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/PartiQLHeader.kt @@ -411,8 +411,8 @@ internal object PartiQLHeader : Header() { parameters = listOf( FunctionParameter("value", ANY) ), - isNullCall = false, // TODO: Should this be true? - isNullable = false + isNullable = false, + isNullCall = false, ) } @@ -427,7 +427,7 @@ internal object PartiQLHeader : Header() { FunctionParameter("type_parameter_1", INT32), FunctionParameter("value", ANY) ), - isNullable = false, // TODO: Should this be true? + isNullable = false, isNullCall = false ) } @@ -455,8 +455,8 @@ internal object PartiQLHeader : Header() { FunctionParameter("type_parameter_2", INT32), FunctionParameter("value", ANY) // TODO: Decide if we need to further segment this ), - isNullCall = false, - isNullable = false + isNullable = false, + isNullCall = false ) } 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 f88852c3c..0f7b548c9 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 @@ -388,6 +388,66 @@ class PlanTyperTestsPorted { ), ) + @JvmStatic + fun isTypeCases() = listOf( + SuccessTestCase( + name = "IS BOOL", + key = key("is-type-00"), + catalog = "pql", + catalogPath = listOf("main"), + expected = StaticType.BOOL + ), + SuccessTestCase( + name = "IS INT", + key = key("is-type-01"), + catalog = "pql", + catalogPath = listOf("main"), + expected = StaticType.BOOL + ), + SuccessTestCase( + name = "IS STRING", + key = key("is-type-02"), + catalog = "pql", + catalogPath = listOf("main"), + expected = StaticType.BOOL + ), + SuccessTestCase( + name = "IS NULL", + key = key("is-type-03"), + catalog = "pql", + expected = StaticType.BOOL, + ), + SuccessTestCase( + name = "MISSING IS NULL", + key = key("is-type-04"), + catalog = "pql", + expected = StaticType.BOOL, + ), + SuccessTestCase( + name = "NULL IS NULL", + key = key("is-type-05"), + catalog = "pql", + expected = StaticType.BOOL, + ), + SuccessTestCase( + name = "MISSING IS MISSING", + key = key("is-type-06"), + catalog = "pql", + expected = StaticType.BOOL, + ), + SuccessTestCase( + name = "NULL IS MISSING", + key = key("is-type-07"), + catalog = "pql", + expected = StaticType.BOOL, + ), + ErrorTestCase( + name = "ERROR always MISSING", + key = key("is-type-08"), + catalog = "pql", + ), + ) + @JvmStatic fun sessionVariables() = listOf( SuccessTestCase( @@ -536,7 +596,10 @@ class PlanTyperTestsPorted { problemHandler = assertProblemExists { Problem( UNKNOWN_PROBLEM_LOCATION, - PlanningProblemDetails.UnknownFunction("bitwise_and", listOf(StaticType.INT4, StaticType.STRING)) + PlanningProblemDetails.UnknownFunction( + "bitwise_and", + listOf(StaticType.INT4, StaticType.STRING) + ) ) } ), @@ -2972,6 +3035,11 @@ class PlanTyperTestsPorted { @Execution(ExecutionMode.CONCURRENT) fun testPivot(tc: TestCase) = runTest(tc) + @ParameterizedTest + @MethodSource("isTypeCases") + @Execution(ExecutionMode.CONCURRENT) + fun testIsType(tc: TestCase) = runTest(tc) + // --------- Finish Parameterized Tests ------ // @@ -2980,7 +3048,7 @@ class PlanTyperTestsPorted { private fun infer( query: String, session: PartiQLPlanner.Session, - problemCollector: ProblemCollector + problemCollector: ProblemCollector, ): PartiQLPlan { val ast = parser.parse(query).root return planner.plan(ast, session, problemCollector).plan diff --git a/partiql-planner/src/testFixtures/resources/inputs/schema_inferencer/is_type.sql b/partiql-planner/src/testFixtures/resources/inputs/schema_inferencer/is_type.sql new file mode 100644 index 000000000..7a8da457f --- /dev/null +++ b/partiql-planner/src/testFixtures/resources/inputs/schema_inferencer/is_type.sql @@ -0,0 +1,27 @@ +--#[is-type-00] +false IS BOOL; + +--#[is-type-01] +item.i_class_id IS INT; + +--#[is-type-02] +item.i_brand IS STRING; + +--#[is-type-03] +1 IS NULL; + +--#[is-type-04] +MISSING IS NULL; + +--#[is-type-05] +NULL IS NULL; + +--#[is-type-06] +MISSING IS MISSING; + +--#[is-type-07] +NULL IS MISSING; + +--#[is-type-08] +-- ERROR! always MISSING +MISSING IS BOOL;