From 4fba7e64a62bc06e9ae9642bb3f05419d1133acb Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Mon, 8 Apr 2024 14:41:55 -0700 Subject: [PATCH 1/4] Adds a CompilationErrorHandling flag to evaluator --- .../kotlin/org/partiql/eval/PartiQLEngine.kt | 31 ++++++++++++++++++- .../org/partiql/eval/internal/Compiler.kt | 19 +++++++++--- .../eval/internal/operator/rex/ExprError.kt | 14 +++++++++ 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprError.kt diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/PartiQLEngine.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/PartiQLEngine.kt index 20f5e81390..f93840d0c8 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/PartiQLEngine.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/PartiQLEngine.kt @@ -36,11 +36,40 @@ public interface PartiQLEngine { public class Session( val catalogs: Map = mapOf(), - val mode: Mode = Mode.PERMISSIVE + val mode: Mode = Mode.PERMISSIVE, + val errorHandling: CompilationErrorHandling = CompilationErrorHandling.SIGNALING ) + /** + * This determines the behavior when the evaluator encounters scenarios in which a type check exception occurs. + */ public enum class Mode { + /** + * Returns MISSING when a type check exception occurs. + */ PERMISSIVE, + + /** + * Propagates the type check exception. + */ STRICT // AKA, Type Checking Mode in the PartiQL Specification } + + /** + * When the PartiQL Plan has determined that a function call or variable reference will always error, the + * [CompilationErrorHandling] will determine how the internal compiler will treat the error. Note that this is subtly + * different than [Mode]. [CompilationErrorHandling] is specifically used when nodes are known to ALWAYS return + * MISSING. The difference can be understood as compile-time ([CompilationErrorHandling]) vs run-time ([Mode]). + */ + public enum class CompilationErrorHandling { + /** + * Returns a literal MISSING. + */ + QUIET, + + /** + * Errors out. + */ + SIGNALING + } } 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 d096a765c7..56189eacd5 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 @@ -25,6 +25,7 @@ import org.partiql.eval.internal.operator.rex.ExprCase import org.partiql.eval.internal.operator.rex.ExprCast import org.partiql.eval.internal.operator.rex.ExprCoalesce import org.partiql.eval.internal.operator.rex.ExprCollection +import org.partiql.eval.internal.operator.rex.ExprError import org.partiql.eval.internal.operator.rex.ExprLiteral import org.partiql.eval.internal.operator.rex.ExprNullIf import org.partiql.eval.internal.operator.rex.ExprPathIndex @@ -69,12 +70,22 @@ internal class Compiler( TODO("Not yet implemented") } + /** + * [Rex.Op.Err] comes from the inability for the planner to resolve a variable/function/etc. Depending on the + * configuration, this will either return MISSING or throw an error. + */ + @OptIn(PartiQLValueExperimental::class) override fun visitRexOpErr(node: Rex.Op.Err, ctx: StaticType?): Operator { - val message = buildString { - this.appendLine(node.message) - PlanPrinter.append(this, plan) + return when (session.errorHandling) { + PartiQLEngine.CompilationErrorHandling.QUIET -> ExprError() + PartiQLEngine.CompilationErrorHandling.SIGNALING -> { + val message = buildString { + this.appendLine(node.message) + PlanPrinter.append(this, plan) + } + throw IllegalStateException(message) + } } - throw IllegalStateException(message) } override fun visitRelOpErr(node: Rel.Op.Err, ctx: StaticType?): Operator { diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprError.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprError.kt new file mode 100644 index 0000000000..e96ed35bc2 --- /dev/null +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprError.kt @@ -0,0 +1,14 @@ +package org.partiql.eval.internal.operator.rex + +import org.partiql.errors.TypeCheckException +import org.partiql.eval.internal.Environment +import org.partiql.eval.internal.operator.Operator +import org.partiql.value.PartiQLValue +import org.partiql.value.PartiQLValueExperimental + +internal class ExprError : Operator.Expr { + @OptIn(PartiQLValueExperimental::class) + override fun eval(env: Environment): PartiQLValue { + throw TypeCheckException() + } +} From 2cd476c2e264a27525b857f010716de29937ec5b Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Thu, 11 Apr 2024 14:55:10 -0700 Subject: [PATCH 2/4] Adds support for evaluation of RexOpMissing --- .../src/main/kotlin/org/partiql/eval/internal/Compiler.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 56189eacd5..69cc199c4c 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 @@ -54,6 +54,7 @@ import org.partiql.spi.fn.FnExperimental import org.partiql.types.StaticType import org.partiql.value.PartiQLValueExperimental import org.partiql.value.PartiQLValueType +import org.partiql.value.missingValue import java.lang.IllegalStateException internal class Compiler( @@ -74,7 +75,6 @@ internal class Compiler( * [Rex.Op.Err] comes from the inability for the planner to resolve a variable/function/etc. Depending on the * configuration, this will either return MISSING or throw an error. */ - @OptIn(PartiQLValueExperimental::class) override fun visitRexOpErr(node: Rex.Op.Err, ctx: StaticType?): Operator { return when (session.errorHandling) { PartiQLEngine.CompilationErrorHandling.QUIET -> ExprError() @@ -119,6 +119,11 @@ internal class Compiler( return ExprStruct(fields) } + @OptIn(PartiQLValueExperimental::class) + override fun visitRexOpMissing(node: Rex.Op.Missing, ctx: StaticType?): Operator { + return ExprLiteral(missingValue()) + } + override fun visitRexOpSelect(node: Rex.Op.Select, ctx: StaticType?): Operator { val rel = visitRel(node.rel, ctx) val ordered = node.rel.type.props.contains(Rel.Prop.ORDERED) From 3c80d0e38fc948cad837ee674f309b0ae9bc2e53 Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Tue, 16 Apr 2024 14:22:14 -0700 Subject: [PATCH 3/4] Removes unnecessary evaluation session properties --- .../kotlin/org/partiql/eval/PartiQLEngine.kt | 31 +------------------ .../org/partiql/eval/internal/Compiler.kt | 27 +++++----------- .../eval/internal/operator/rex/ExprError.kt | 7 +++-- 3 files changed, 14 insertions(+), 51 deletions(-) diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/PartiQLEngine.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/PartiQLEngine.kt index f93840d0c8..20f5e81390 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/PartiQLEngine.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/PartiQLEngine.kt @@ -36,40 +36,11 @@ public interface PartiQLEngine { public class Session( val catalogs: Map = mapOf(), - val mode: Mode = Mode.PERMISSIVE, - val errorHandling: CompilationErrorHandling = CompilationErrorHandling.SIGNALING + val mode: Mode = Mode.PERMISSIVE ) - /** - * This determines the behavior when the evaluator encounters scenarios in which a type check exception occurs. - */ public enum class Mode { - /** - * Returns MISSING when a type check exception occurs. - */ PERMISSIVE, - - /** - * Propagates the type check exception. - */ STRICT // AKA, Type Checking Mode in the PartiQL Specification } - - /** - * When the PartiQL Plan has determined that a function call or variable reference will always error, the - * [CompilationErrorHandling] will determine how the internal compiler will treat the error. Note that this is subtly - * different than [Mode]. [CompilationErrorHandling] is specifically used when nodes are known to ALWAYS return - * MISSING. The difference can be understood as compile-time ([CompilationErrorHandling]) vs run-time ([Mode]). - */ - public enum class CompilationErrorHandling { - /** - * Returns a literal MISSING. - */ - QUIET, - - /** - * Errors out. - */ - SIGNALING - } } 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 69cc199c4c..cccc6f3ffc 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 @@ -47,14 +47,12 @@ import org.partiql.plan.Ref import org.partiql.plan.Rel import org.partiql.plan.Rex import org.partiql.plan.Statement -import org.partiql.plan.debug.PlanPrinter import org.partiql.plan.visitor.PlanBaseVisitor import org.partiql.spi.fn.Agg import org.partiql.spi.fn.FnExperimental import org.partiql.types.StaticType import org.partiql.value.PartiQLValueExperimental import org.partiql.value.PartiQLValueType -import org.partiql.value.missingValue import java.lang.IllegalStateException internal class Compiler( @@ -72,20 +70,16 @@ internal class Compiler( } /** - * [Rex.Op.Err] comes from the inability for the planner to resolve a variable/function/etc. Depending on the - * configuration, this will either return MISSING or throw an error. + * Realistically, this should never execute -- since the problems of severity ERROR are sent to the problem handler + * at planning time. Implementors SHOULD fail compilation, however, if they decide not to, we will allow its execution + * and will treat this identically to [visitRexOpMissing]. */ override fun visitRexOpErr(node: Rex.Op.Err, ctx: StaticType?): Operator { - return when (session.errorHandling) { - PartiQLEngine.CompilationErrorHandling.QUIET -> ExprError() - PartiQLEngine.CompilationErrorHandling.SIGNALING -> { - val message = buildString { - this.appendLine(node.message) - PlanPrinter.append(this, plan) - } - throw IllegalStateException(message) - } - } + return ExprError(node.message) + } + + override fun visitRexOpMissing(node: Rex.Op.Missing, ctx: StaticType?): Operator { + return ExprError(node.message) } override fun visitRelOpErr(node: Rel.Op.Err, ctx: StaticType?): Operator { @@ -119,11 +113,6 @@ internal class Compiler( return ExprStruct(fields) } - @OptIn(PartiQLValueExperimental::class) - override fun visitRexOpMissing(node: Rex.Op.Missing, ctx: StaticType?): Operator { - return ExprLiteral(missingValue()) - } - override fun visitRexOpSelect(node: Rex.Op.Select, ctx: StaticType?): Operator { val rel = visitRel(node.rel, ctx) val ordered = node.rel.type.props.contains(Rel.Prop.ORDERED) diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprError.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprError.kt index e96ed35bc2..b439241e71 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprError.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprError.kt @@ -6,9 +6,12 @@ import org.partiql.eval.internal.operator.Operator import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -internal class ExprError : Operator.Expr { +internal class ExprError( + private val message: String, +) : Operator.Expr { + @OptIn(PartiQLValueExperimental::class) override fun eval(env: Environment): PartiQLValue { - throw TypeCheckException() + throw TypeCheckException(message) } } From 5b2ab76095b6e60890e5fdd9cfd7ed1217fcde98 Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Thu, 18 Apr 2024 11:28:41 -0700 Subject: [PATCH 4/4] Reverts changes for RexOpErr --- .../org/partiql/eval/internal/Compiler.kt | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 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 cccc6f3ffc..d096a765c7 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 @@ -25,7 +25,6 @@ import org.partiql.eval.internal.operator.rex.ExprCase import org.partiql.eval.internal.operator.rex.ExprCast import org.partiql.eval.internal.operator.rex.ExprCoalesce import org.partiql.eval.internal.operator.rex.ExprCollection -import org.partiql.eval.internal.operator.rex.ExprError import org.partiql.eval.internal.operator.rex.ExprLiteral import org.partiql.eval.internal.operator.rex.ExprNullIf import org.partiql.eval.internal.operator.rex.ExprPathIndex @@ -47,6 +46,7 @@ import org.partiql.plan.Ref import org.partiql.plan.Rel import org.partiql.plan.Rex import org.partiql.plan.Statement +import org.partiql.plan.debug.PlanPrinter import org.partiql.plan.visitor.PlanBaseVisitor import org.partiql.spi.fn.Agg import org.partiql.spi.fn.FnExperimental @@ -69,17 +69,12 @@ internal class Compiler( TODO("Not yet implemented") } - /** - * Realistically, this should never execute -- since the problems of severity ERROR are sent to the problem handler - * at planning time. Implementors SHOULD fail compilation, however, if they decide not to, we will allow its execution - * and will treat this identically to [visitRexOpMissing]. - */ override fun visitRexOpErr(node: Rex.Op.Err, ctx: StaticType?): Operator { - return ExprError(node.message) - } - - override fun visitRexOpMissing(node: Rex.Op.Missing, ctx: StaticType?): Operator { - return ExprError(node.message) + val message = buildString { + this.appendLine(node.message) + PlanPrinter.append(this, plan) + } + throw IllegalStateException(message) } override fun visitRelOpErr(node: Rel.Op.Err, ctx: StaticType?): Operator {