From c16584f41487de2c01bbe696531ea3a4e6b6e059 Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Fri, 26 Jan 2024 10:13:51 -0800 Subject: [PATCH] Handles permissive/strict for function calls --- .../org/partiql/eval/internal/Compiler.kt | 12 +++++--- .../internal/operator/rex/ExprCallDynamic.kt | 3 ++ .../internal/operator/rex/ExprCallStatic.kt | 29 ------------------- .../eval/internal/PartiQLEngineDefaultTest.kt | 8 +++-- 4 files changed, 17 insertions(+), 35 deletions(-) diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Compiler.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Compiler.kt index 025b5425f4..b3f1baa0df 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Compiler.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Compiler.kt @@ -41,6 +41,7 @@ import org.partiql.spi.function.PartiQLFunction import org.partiql.spi.function.PartiQLFunctionExperimental import org.partiql.types.function.FunctionSignature import org.partiql.value.PartiQLValueExperimental +import org.partiql.value.PartiQLValueType import java.lang.IllegalStateException internal class Compiler @OptIn(PartiQLFunctionExperimental::class) constructor( @@ -141,19 +142,22 @@ internal class Compiler @OptIn(PartiQLFunctionExperimental::class) constructor( return ExprPathIndex(root, index) } - @OptIn(PartiQLFunctionExperimental::class) + @OptIn(PartiQLFunctionExperimental::class, PartiQLValueExperimental::class) override fun visitRexOpCallStatic(node: Rex.Op.Call.Static, ctx: Unit): Operator { val function = getFunction(node.fn.signature) val args = node.args.map { visitRex(it, ctx) }.toTypedArray() - return when (function.signature.name.equals("is_missing", ignoreCase = true)) { - false -> ExprCallStatic(function, args) + val fnTakesInMissing = function.signature.parameters.any { + it.type == PartiQLValueType.MISSING || it.type == PartiQLValueType.ANY + } + return when (fnTakesInMissing) { true -> ExprCallStatic(function, args.map { it.modeHandled() }.toTypedArray()) + false -> ExprCallStatic(function, args) } } @OptIn(PartiQLFunctionExperimental::class, PartiQLValueExperimental::class) override fun visitRexOpCallDynamic(node: Rex.Op.Call.Dynamic, ctx: Unit): Operator { - val args = node.args.map { visitRex(it, ctx) }.toTypedArray() + val args = node.args.map { visitRex(it, ctx).modeHandled() }.toTypedArray() val candidates = node.candidates.map { candidate -> val fn = getFunction(candidate.fn.signature) val coercions = candidate.coercions.map { it?.signature?.let { sig -> getFunction(sig) } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt index 3eb91f0205..04f17204f3 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt @@ -58,6 +58,9 @@ internal class ExprCallDynamic( @OptIn(PartiQLValueExperimental::class) internal fun matches(args: Array): Boolean { for (i in args.indices) { + if (types[i] == PartiQLValueType.ANY) { + return true + } if (args[i].type != types[i]) { return false } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallStatic.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallStatic.kt index 6a51e7ae06..2c3731d34a 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallStatic.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallStatic.kt @@ -1,6 +1,5 @@ package org.partiql.eval.internal.operator.rex -import org.partiql.errors.TypeCheckException import org.partiql.eval.internal.Record import org.partiql.eval.internal.helpers.toNull import org.partiql.eval.internal.operator.Operator @@ -8,7 +7,6 @@ import org.partiql.spi.function.PartiQLFunction import org.partiql.spi.function.PartiQLFunctionExperimental import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.missingValue @OptIn(PartiQLValueExperimental::class, PartiQLFunctionExperimental::class) internal class ExprCallStatic( @@ -31,31 +29,4 @@ internal class ExprCallStatic( }.toTypedArray() return fn.invoke(args) } - - @OptIn(PartiQLValueExperimental::class, PartiQLFunctionExperimental::class) - internal class Permissive( - private val fn: PartiQLFunction.Scalar, - private val inputs: Array, - ) : Operator.Expr { - - /** - * Memoize creation of - */ - @OptIn(PartiQLValueExperimental::class) - private val nil = fn.signature.returns.toNull() - - override fun eval(record: Record): PartiQLValue { - // Evaluate arguments - val args = inputs.map { input -> - try { - val r = input.eval(record) - if (r.isNull && fn.signature.isNullCall) return nil() - r - } catch (e: TypeCheckException) { - missingValue() - } - }.toTypedArray() - return fn.invoke(args) - } - } } diff --git a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEngineDefaultTest.kt b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEngineDefaultTest.kt index 5095b6c48e..781268e056 100644 --- a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEngineDefaultTest.kt +++ b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEngineDefaultTest.kt @@ -270,6 +270,10 @@ class PartiQLEngineDefaultTest { expected = boolValue(true), // TODO: Is this right? mode = PartiQLEngine.Mode.STRICT ), + SuccessTestCase( + input = "SELECT VALUE t.a IS MISSING FROM << { 'b': 1 }, { 'a': 2 } >> AS t;", + expected = bagValue(boolValue(true), boolValue(false)) + ), // PartiQL Specification Section 7.1.1 -- Equality SuccessTestCase( input = "5 = 'a';", @@ -278,7 +282,7 @@ class PartiQLEngineDefaultTest { // PartiQL Specification Section 7.1.1 -- Equality SuccessTestCase( input = "5 = 'a';", - expected = boolValue(false), // TODO: Is this correct? See: The eqg, unlike the =, returns true when a NULL is compared to a NULL or a MISSING to a MISSING + expected = boolValue(false), // TODO: Is this correct? mode = PartiQLEngine.Mode.STRICT ), // PartiQL Specification Section 8 @@ -462,7 +466,7 @@ class PartiQLEngineDefaultTest { val functions = mapOf( "partiql" to PartiQLPlugin.functions ) - val prepared = engine.prepare(plan.plan, PartiQLEngine.Session(functions = functions)) + val prepared = engine.prepare(plan.plan, PartiQLEngine.Session(functions = functions, mode = mode)) val result = engine.execute(prepared) as PartiQLResult.Value val output = result.value assertEquals(expected, output, comparisonString(expected, output))