diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/PartiQLPlanner.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/PartiQLPlanner.kt index b393eb0b8c..dba1dfe66f 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/PartiQLPlanner.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/PartiQLPlanner.kt @@ -47,9 +47,19 @@ public interface PartiQLPlanner { public companion object { + /** + * Create a new [PartiQLPlannerBuilder] instance. + * + * @return + */ @JvmStatic public fun builder(): PartiQLPlannerBuilder = PartiQLPlannerBuilder() + /** + * Create a new [PartiQLPlanner] instance with the default configuration. + * + * @return + */ @JvmStatic public fun standard(): PartiQLPlanner = PartiQLPlannerBuilder().build() } diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/PartiQLPlannerPass.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/PartiQLPlannerPass.kt index a402261c3e..5b415e0b2e 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/PartiQLPlannerPass.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/PartiQLPlannerPass.kt @@ -3,7 +3,16 @@ package org.partiql.planner import org.partiql.plan.Plan import org.partiql.spi.Context +/** + * Interface specifies a pass that can be applied to a [Plan] by the [PartiQLPlanner]. + */ public interface PartiQLPlannerPass { - + /** + * Applies this pass to the given [Plan] and returns the resulting [Plan]. + * + * @param plan The [Plan] to apply this pass to. + * @param ctx The [Context] to use for this pass. + * @return The resulting [Plan] after applying this pass. + */ public fun apply(plan: Plan, ctx: Context): Plan } diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/builder/PartiQLPlannerBuilder.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/builder/PartiQLPlannerBuilder.kt index d2494f181f..af88c36c5a 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/builder/PartiQLPlannerBuilder.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/builder/PartiQLPlannerBuilder.kt @@ -9,10 +9,12 @@ import org.partiql.planner.internal.SqlPlanner * PartiQLPlannerBuilder is used to programmatically construct a [PartiQLPlanner] implementation. * * Usage: - * PartiQLPlanner.builder() - * .signal() - * .addPass(myPass) - * .build() + * ``` + * PartiQLPlanner.builder() + * .signal() + * .addPass(myPass) + * .build() + * ``` */ public class PartiQLPlannerBuilder { @@ -49,7 +51,7 @@ public class PartiQLPlannerBuilder { } /** - * Java style method for setting the planner to signal mode + * Java style method for setting the planner to signal mode. */ public fun signal(signal: Boolean = true): PartiQLPlannerBuilder { if (signal) { diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/CoercionFamily.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/CoercionFamily.kt index ddeb3cfb32..3da3ea8e28 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/CoercionFamily.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/CoercionFamily.kt @@ -6,7 +6,7 @@ import org.partiql.spi.types.PType * This represents SQL:1999 Section 4.1.2 "Type conversions and mixing of data types" and breaks down the different * coercion groups. * - * TODO: [UNKNOWN] should likely be removed in the future. However, it is needed due to literal nulls and missings. + * TODO: [UNKNOWN] should likely be removed in the future. However, it is needed due to literal null and missing. * TODO: [DYNAMIC] should likely be removed in the future. This is currently only kept to map function signatures. */ internal enum class CoercionFamily { @@ -25,10 +25,10 @@ internal enum class CoercionFamily { companion object { /** - * Gets the coercion family for the given [PType.Kind]. + * Gets the coercion family for the given [PType.code]. * * @see CoercionFamily - * @see PType.Kind + * @see PType.code * @see family */ @JvmStatic diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/Env.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/Env.kt index 5e28380240..1ad5fe1028 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/Env.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/Env.kt @@ -282,10 +282,9 @@ internal class Env(private val session: Session) { for (i in args.indices) { val a = args[i] val p = parameters[i] - val m = p.getMatch(a) - when { - m == null -> return null - m == a -> continue + when (val m = p.getMatch(a)) { + null -> return null + a -> continue else -> mapping[i] = coercion(a, m) } } diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/PlannerFlag.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/PlannerFlag.kt index bd91be0535..12b9c2365c 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/PlannerFlag.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/PlannerFlag.kt @@ -6,7 +6,7 @@ internal enum class PlannerFlag { * * If this flag is included: * - * The problematic operation will be tracked in problem callback as a error. + * The problematic operation will be tracked in problem callback as an error. * * The result plan will turn the problematic operation into an error node. * diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/SqlPlanner.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/SqlPlanner.kt index 63351201fc..c76595553e 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/SqlPlanner.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/SqlPlanner.kt @@ -7,8 +7,9 @@ import org.partiql.plan.Plan import org.partiql.planner.PartiQLPlanner import org.partiql.planner.PartiQLPlanner.Result import org.partiql.planner.PartiQLPlannerPass -import org.partiql.planner.internal.normalize.normalize import org.partiql.planner.internal.transforms.AstToPlan +import org.partiql.planner.internal.transforms.NormalizeFromSource +import org.partiql.planner.internal.transforms.NormalizeGroupBy import org.partiql.planner.internal.transforms.PlanTransform import org.partiql.planner.internal.typer.PlanTyper import org.partiql.spi.Context @@ -60,6 +61,17 @@ internal class SqlPlanner( } } + /** + * AST normalization + */ + private fun Statement.normalize(): Statement { + // could be a fold, but this is nice for setting breakpoints + var ast = this + ast = NormalizeFromSource.apply(ast) + ast = NormalizeGroupBy.apply(ast) + return ast + } + /** * Create a plan with a query action and error node. * diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/helpers/ToBinder.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/helpers/ToBinder.kt deleted file mode 100644 index 93f94339cd..0000000000 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/helpers/ToBinder.kt +++ /dev/null @@ -1,64 +0,0 @@ -package org.partiql.planner.internal.helpers - -import org.partiql.ast.Identifier -import org.partiql.ast.Identifier.Simple -import org.partiql.ast.Literal -import org.partiql.ast.expr.Expr -import org.partiql.ast.expr.ExprCast -import org.partiql.ast.expr.ExprLit -import org.partiql.ast.expr.ExprPath -import org.partiql.ast.expr.ExprSessionAttribute -import org.partiql.ast.expr.ExprVarRef -import org.partiql.ast.expr.PathStep - -private val col = { index: () -> Int -> "_${index()}" } - -/** - * Produces a "binder" (AS alias) for an expression following the given rules: - * - * 1. If item is an id, use the last symbol - * 2. If item is a path with a final symbol step, use the symbol — else 4 - * 3. If item is a cast, use the value name - * 4. Else, use item index with prefix _ - * - * See https://github.com/partiql/partiql-lang-kotlin/issues/1122 - */ -internal fun Expr.toBinder(index: () -> Int): Simple = when (this) { - is ExprVarRef -> this.identifier.toBinder() - is ExprPath -> this.toBinder(index) - is ExprCast -> this.value.toBinder(index) - is ExprSessionAttribute -> this.sessionAttribute.name().uppercase().toBinder() - else -> col(index).toBinder() -} - -/** - * Simple toBinder that uses an int literal rather than a closure. - * - * @param index - * @return - */ -internal fun Expr.toBinder(index: Int): Simple = toBinder { index } - -private fun String.toBinder(): Simple = - // Every binder preserves case - Simple.delimited(this@toBinder) - -private fun Identifier.toBinder(): Simple = this.identifier.toBinder() - -private fun Simple.toBinder(): Simple = text.toBinder() - -private fun ExprPath.toBinder(index: () -> Int): Simple { - if (steps.isEmpty()) return root.toBinder(index) - return when (val last = steps.last()) { - is PathStep.Field -> last.field.toBinder() - is PathStep.Element -> { - val k = last.element - if (k is ExprLit && k.lit.code() == Literal.STRING) { - k.lit.stringValue().toBinder() - } else { - col(index).toBinder() - } - } - else -> col(index).toBinder() - } -} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/normalize/Normalize.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/normalize/Normalize.kt deleted file mode 100644 index 435ae7245a..0000000000 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/normalize/Normalize.kt +++ /dev/null @@ -1,29 +0,0 @@ -@file:JvmName("Normalize") -/* - * Copyright 2022 Amazon.com, Inc. or its affiliates. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at: - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific - * language governing permissions and limitations under the License. - */ - -package org.partiql.planner.internal.normalize - -import org.partiql.ast.Statement - -/** - * AST normalization - */ -internal fun Statement.normalize(): Statement { - // could be a fold, but this is nice for setting breakpoints - var ast = this - ast = NormalizeFromSource.apply(ast) - ast = NormalizeGroupBy.apply(ast) - return ast -} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/passes/.gitkeep b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/passes/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/normalize/AstPass.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/AstPass.kt similarity index 94% rename from partiql-planner/src/main/kotlin/org/partiql/planner/internal/normalize/AstPass.kt rename to partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/AstPass.kt index 4a822d122e..6a74bb7ecd 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/normalize/AstPass.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/AstPass.kt @@ -12,7 +12,7 @@ * language governing permissions and limitations under the License. */ -package org.partiql.planner.internal.normalize +package org.partiql.planner.internal.transforms import org.partiql.ast.Statement diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/AstToPlan.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/AstToPlan.kt index 79217a70d6..636a037248 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/AstToPlan.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/AstToPlan.kt @@ -36,7 +36,6 @@ internal object AstToPlan { @JvmStatic fun apply(statement: AstStatement, env: Env): PlanStatement = statement.accept(ToPlanStatement, env) - @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") private object ToPlanStatement : AstVisitor() { override fun defaultReturn(node: AstNode, env: Env) = throw IllegalArgumentException("Unsupported statement") diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/normalize/NormalizeFromSource.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeFromSource.kt similarity index 96% rename from partiql-planner/src/main/kotlin/org/partiql/planner/internal/normalize/NormalizeFromSource.kt rename to partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeFromSource.kt index 8d91688333..2ef9d554e0 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/normalize/NormalizeFromSource.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeFromSource.kt @@ -12,7 +12,7 @@ * language governing permissions and limitations under the License. */ -package org.partiql.planner.internal.normalize +package org.partiql.planner.internal.transforms import org.partiql.ast.Ast.fromExpr import org.partiql.ast.Ast.fromJoin @@ -26,7 +26,7 @@ import org.partiql.ast.FromType import org.partiql.ast.QueryBody import org.partiql.ast.Statement import org.partiql.ast.expr.Expr -import org.partiql.planner.internal.helpers.toBinder +import org.partiql.planner.internal.util.BinderUtils.toBinder /** * Assign aliases to any FROM source which does not have one. diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/normalize/NormalizeGroupBy.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeGroupBy.kt similarity index 94% rename from partiql-planner/src/main/kotlin/org/partiql/planner/internal/normalize/NormalizeGroupBy.kt rename to partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeGroupBy.kt index bd23b6a4ed..8fde1367e3 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/normalize/NormalizeGroupBy.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeGroupBy.kt @@ -12,7 +12,7 @@ * language governing permissions and limitations under the License. */ -package org.partiql.planner.internal.normalize +package org.partiql.planner.internal.transforms import org.partiql.ast.Ast.groupBy import org.partiql.ast.Ast.groupByKey @@ -21,7 +21,7 @@ import org.partiql.ast.AstRewriter import org.partiql.ast.GroupBy import org.partiql.ast.Statement import org.partiql.ast.expr.Expr -import org.partiql.planner.internal.helpers.toBinder +import org.partiql.planner.internal.util.BinderUtils.toBinder /** * Adds a unique binder to each group key. diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeSelect.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeSelect.kt index fa93c8c9a0..93ab2041ab 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeSelect.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeSelect.kt @@ -47,7 +47,7 @@ import org.partiql.ast.expr.ExprCase import org.partiql.ast.expr.ExprQuerySet import org.partiql.ast.expr.ExprStruct import org.partiql.ast.expr.ExprVarRef -import org.partiql.planner.internal.helpers.toBinder +import org.partiql.planner.internal.util.BinderUtils.toBinder /** * Converts SQL-style SELECT to PartiQL SELECT VALUE. @@ -92,7 +92,7 @@ import org.partiql.planner.internal.helpers.toBinder * } FROM A AS x * ``` * - * NOTE: This does NOT transform subqueries. It operates directly on an [QueryExpr.SFW] -- and that is it. Therefore: + * NOTE: This does NOT transform subqueries. It operates directly on an [ExprQuerySet] -- and that is it. Therefore: * ``` * SELECT * (SELECT 1 FROM T AS "T") @@ -181,7 +181,7 @@ internal object NormalizeSelect { */ private val col = { index: Int -> "_${index + 1}" } - internal fun visitSFW(node: QueryBody.SFW, ctx: () -> Int): QueryBody.SFW { + fun visitSFW(node: QueryBody.SFW, ctx: () -> Int): QueryBody.SFW { val sfw = super.visitQueryBodySFW(node, ctx) as QueryBody.SFW return when (val select = sfw.select) { is SelectStar -> { @@ -242,7 +242,7 @@ internal object NormalizeSelect { // Helpers /** - * We need to call this from [visitExprSFW] and not override [visitSelectStar] because we need access to the + * We need to call this from [visitQueryBodySFW] and not override [visitSelectStar] because we need access to the * [From] aliases. * * Note: We assume that [select] and [from] have already been visited. @@ -271,7 +271,7 @@ internal object NormalizeSelect { } /** - * We need to call this from [visitExprSFW] and not override [visitSelectStar] because we need access to the + * We need to call this from [visitQueryBodySFW] and not override [visitSelectStar] because we need access to the * [GroupBy] aliases. * * Note: We assume that [select] and [group] have already been visited. diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/PlanTransform.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/PlanTransform.kt index aea08c8fe3..6c2a3d3220 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/PlanTransform.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/PlanTransform.kt @@ -79,7 +79,7 @@ internal class PlanTransform(private val flags: Set) { return operators.error(ctx) } - override fun visitRelOpErr(node: org.partiql.planner.internal.ir.Rel.Op.Err, ctx: PType): Any { + override fun visitRelOpErr(node: Rel.Op.Err, ctx: PType): Any { // Listener should have already received the error. This node is a dud. Registered error listeners should // have failed compilation already. return operators.scan(operators.error(ctx)) diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RelConverter.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RelConverter.kt index af93475127..bba4470bbc 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RelConverter.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RelConverter.kt @@ -47,7 +47,6 @@ import org.partiql.ast.expr.Expr import org.partiql.ast.expr.ExprCall import org.partiql.ast.expr.ExprQuerySet import org.partiql.planner.internal.Env -import org.partiql.planner.internal.helpers.toBinder import org.partiql.planner.internal.ir.Rel import org.partiql.planner.internal.ir.Rex import org.partiql.planner.internal.ir.rel @@ -83,6 +82,7 @@ import org.partiql.planner.internal.ir.rexOpStruct import org.partiql.planner.internal.ir.rexOpStructField import org.partiql.planner.internal.ir.rexOpVarLocal import org.partiql.planner.internal.typer.CompilerType +import org.partiql.planner.internal.util.BinderUtils.toBinder import org.partiql.spi.types.PType import org.partiql.spi.value.Datum @@ -156,7 +156,7 @@ internal object RelConverter { */ private fun Expr.toRex(env: Env): Rex = RexConverter.apply(this, env) - @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE", "LocalVariableName") + @Suppress("LocalVariableName") internal class ToRel(private val env: Env) : AstVisitor() { override fun defaultReturn(node: AstNode, input: Rel): Rel = @@ -713,7 +713,7 @@ internal object RelConverter { private fun Identifier.isAggregateCall(): Boolean = identifier.text.lowercase().isAggregateCall() - override fun defaultReturn(node: AstNode, ctx: Context) = node + override fun defaultReturn(node: AstNode, context: Context) = node } private fun syntheticAgg(i: Int) = "\$agg_$i" diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RexConverter.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RexConverter.kt index cb89451252..2a2eb5c322 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RexConverter.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RexConverter.kt @@ -89,7 +89,7 @@ import org.partiql.planner.internal.ir.rexOpVarUnresolved import org.partiql.planner.internal.typer.CompilerType import org.partiql.planner.internal.typer.PlanTyper.Companion.toCType import org.partiql.planner.internal.util.DateTimeUtils -import org.partiql.planner.internal.utils.FunctionUtils +import org.partiql.planner.internal.util.FunctionUtils import org.partiql.spi.catalog.Identifier import org.partiql.spi.errors.TypeCheckException import org.partiql.spi.types.PType @@ -109,7 +109,6 @@ internal object RexConverter { internal fun applyRel(expr: Expr, context: Env): Rex = expr.accept(ToRex, context) - @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") private object ToRex : AstVisitor() { private val COLL_AGG_NAMES = setOf( @@ -246,7 +245,7 @@ internal object RexConverter { * @param ctx * @return */ - internal fun visitExprCoerce( + fun visitExprCoerce( node: Expr, ctx: Env, coercion: Rex.Op.Subquery.Coercion = Rex.Op.Subquery.Coercion.SCALAR, @@ -871,7 +870,7 @@ internal object RexConverter { DataType.BOOLEAN, DataType.BOOL -> call(FunctionUtils.OP_IS_BOOL, arg0) // DataType.DATE -> call(FunctionUtils.OP_IS_DATE, arg0) - // TODO: DO we want to seperate with time zone vs without time zone into two different type in the plan? + // TODO: DO we want to separate with time zone vs without time zone into two different type in the plan? // leave the parameterized type out for now until the above is answered DataType.TIME -> call(FunctionUtils.OP_IS_TIME, arg0) DataType.TIME_WITH_TIME_ZONE -> call(FunctionUtils.OP_IS_TIMEZ, arg0) diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/CompilerType.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/CompilerType.kt index de7ac6a100..e50abd8006 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/CompilerType.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/CompilerType.kt @@ -77,14 +77,14 @@ internal class CompilerType( internal fun isNumeric(): Boolean { return this.code() in setOf( - PType.INTEGER, - PType.NUMERIC, - PType.BIGINT, - PType.TINYINT, - PType.SMALLINT, - PType.REAL, - PType.DOUBLE, - PType.DECIMAL, + INTEGER, + NUMERIC, + BIGINT, + TINYINT, + SMALLINT, + REAL, + DOUBLE, + DECIMAL, ) } @@ -94,8 +94,8 @@ internal class CompilerType( * @return null when the field definitely does not exist; dynamic when the type cannot be determined */ internal fun getSymbol(field: String): Pair? { - if (this.code() == PType.STRUCT) { - return Identifier.Simple.regular(field) to CompilerType(PType.dynamic()) + if (this.code() == STRUCT) { + return Identifier.Simple.regular(field) to CompilerType(dynamic()) } val fields = this.fields.mapNotNull { when (it.name.equals(field, true)) { @@ -116,12 +116,12 @@ internal class CompilerType( @JvmStatic internal fun anyOf(types: Collection): CompilerType { if (types.isEmpty()) { - return CompilerType(PType.dynamic()) + return CompilerType(dynamic()) } val unique = types.toSet() return when (unique.size) { 1 -> unique.first() - else -> CompilerType(PType.dynamic()) + else -> CompilerType(dynamic()) } } } diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/DynamicTyper.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/DynamicTyper.kt index d8020ecc15..86025b1624 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/DynamicTyper.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/DynamicTyper.kt @@ -64,7 +64,7 @@ internal class DynamicTyper { /** * This adds non-absent types (aka not NULL / MISSING literals) to the typing accumulator. - * @param type + * @param rex */ private fun accumulateConcrete(rex: Rex) { types.add(rex.type) @@ -123,8 +123,8 @@ internal class DynamicTyper { if (type.code() != PType.DECIMAL) { return null } - val precision = Math.max(type.precision, acc.first) - val scale = Math.max(type.scale, acc.second) + val precision = type.precision.coerceAtLeast(acc.first) + val scale = type.scale.coerceAtLeast(acc.second) precision to scale } return PType.decimal(precision, scale).toCType() @@ -144,10 +144,10 @@ internal class DynamicTyper { } PType.VARCHAR -> { containsVarChar = true - Math.max(acc, type.length) + acc.coerceAtLeast(type.length) } PType.CHAR -> { - Math.max(acc, type.length) + acc.coerceAtLeast(type.length) } else -> error("Received type: $type") } diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/PlanTyper.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/PlanTyper.kt index 94f4d0be9e..6f65f39fd4 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/PlanTyper.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/PlanTyper.kt @@ -51,6 +51,7 @@ import org.partiql.planner.internal.ir.rexOpStructField import org.partiql.planner.internal.ir.rexOpSubquery import org.partiql.planner.internal.ir.statementQuery import org.partiql.planner.internal.ir.util.PlanRewriter +import org.partiql.planner.internal.util.TypeUtils.exclude import org.partiql.spi.Context import org.partiql.spi.catalog.Identifier import org.partiql.spi.errors.PError @@ -100,7 +101,7 @@ internal class PlanTyper(private val env: Env, config: Context) { fun anyOfLiterals(types: Collection): PType? { // Grab unique var unique: Collection = types.map { it.getDelegate() }.toSet() - if (unique.size == 0) { + if (unique.isEmpty()) { return null } else if (unique.size == 1) { return unique.first() @@ -108,7 +109,7 @@ internal class PlanTyper(private val env: Env, config: Context) { // Filter out UNKNOWN unique = unique.filter { it.code() != PType.UNKNOWN } - if (unique.size == 0) { + if (unique.isEmpty()) { return PType.unknown() } else if (unique.size == 1) { return unique.first() @@ -137,11 +138,11 @@ internal class PlanTyper(private val env: Env, config: Context) { } private fun collapseRows(rows: Iterable): PType { - val firstFields = rows.first().fields!! + val firstFields = rows.first().fields val fieldNames = firstFields.map { it.name } val fieldTypes = firstFields.map { mutableListOf(it.type.toCType()) } rows.map { struct -> - val fields = struct.fields!! + val fields = struct.fields if (fields.map { it.name } != fieldNames) { return PType.struct() } @@ -231,7 +232,7 @@ internal class PlanTyper(private val env: Env, config: Context) { // Check Root val vType = when (rex.type.code()) { - PType.ROW -> anyOf(rex.type.fields!!.map { it.type }) ?: PType.dynamic() + PType.ROW -> anyOf(rex.type.fields.map { it.type }) ?: PType.dynamic() PType.STRUCT -> PType.dynamic() else -> rex.type } @@ -538,9 +539,9 @@ internal class PlanTyper(private val env: Env, config: Context) { // typing of aggregate calls is slightly more complicated because they are not expressions. val calls = node.calls.mapIndexed { i, call -> - when (val agg = call) { + when (call) { is Rel.Op.Aggregate.Call.Resolved -> call to ctx!!.schema[i].type - is Rel.Op.Aggregate.Call.Unresolved -> typer.resolveAgg(agg) + is Rel.Op.Aggregate.Call.Unresolved -> typer.resolveAgg(call) } } val groups = node.groups.map { typer.visitRex(it, null) } @@ -665,7 +666,7 @@ internal class PlanTyper(private val env: Env, config: Context) { // Get Literal Key val keyOp = key.op val keyLiteral = when (keyOp is Rex.Op.Lit && keyOp.value.isTextValue() && !keyOp.value.isNull) { - true -> keyOp.value.string!! + true -> keyOp.value.string false -> return rex(CompilerType(PType.dynamic()), rexOpPathKey(root, key)) } @@ -719,7 +720,7 @@ internal class PlanTyper(private val env: Env, config: Context) { if (this.code() == PType.STRUCT) { return CompilerType(PType.dynamic()) } - val fields = this.fields!!.filter { it.name.equals(field, ignoreCase) }.map { it.type }.toSet() + val fields = this.fields.filter { it.name.equals(field, ignoreCase) }.map { it.type }.toSet() return when (fields.size) { 0 -> return null 1 -> fields.first() @@ -943,7 +944,7 @@ internal class PlanTyper(private val env: Env, config: Context) { * ``` * When we type the above, if we know that `a` can be many different types (one of them being a struct), * then when we see the top-level `a IS STRUCT`, then we can assume that the `a` on the RHS is definitely a - * struct. We handle this by using [foldCaseBranch]. + * struct. */ override fun visitRexOpCaseBranch(node: Rex.Op.Case.Branch, ctx: CompilerType?): Rex.Op.Case.Branch { val visitedCondition = visitRex(node.condition, node.condition.type) @@ -984,7 +985,7 @@ internal class PlanTyper(private val env: Env, config: Context) { structIsClosed = false continue } - structTypeFields.add(CompilerType.Field(keyOp.value.string!!, field.v.type)) + structTypeFields.add(CompilerType.Field(keyOp.value.string, field.v.type)) } val type = when (structIsClosed) { true -> CompilerType(PType.row(structTypeFields as Collection)) @@ -1046,12 +1047,12 @@ internal class PlanTyper(private val env: Env, config: Context) { if (cons.code() != PType.ROW) { error("Subquery with non-SQL SELECT cannot be coerced to a scalar. Found constructor type: $cons") } - val n = cons.fields!!.size + val n = cons.fields.size if (n != 1) { error("SELECT constructor with $n attributes cannot be coerced to a scalar. Found constructor type: $cons") } // If we made it this far, then we can coerce this subquery to a scalar - val type = cons.fields!!.first().type + val type = cons.fields.first().type return Rex(type, subquery) } @@ -1184,7 +1185,7 @@ internal class PlanTyper(private val env: Env, config: Context) { return@forEach } when (arg.code()) { - PType.ROW -> fields.addAll(arg.fields!!) + PType.ROW -> fields.addAll(arg.fields) PType.STRUCT -> structIsOpen = true PType.DYNAMIC, PType.VARIANT -> containsDynamic = true PType.UNKNOWN -> structIsOpen = true diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/RexReplacer.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/RexReplacer.kt deleted file mode 100644 index 6cee3d555a..0000000000 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/RexReplacer.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at: - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific - * language governing permissions and limitations under the License. - */ - -package org.partiql.planner.internal.typer - -import org.partiql.planner.internal.ir.Rex -import org.partiql.planner.internal.ir.rex -import org.partiql.planner.internal.ir.util.PlanRewriter - -/** - * Uses to replace [Rex]'s within an expression tree. - * - * TODO remove me? - */ -internal object RexReplacer { - - /** - * Within the [Rex] tree of [rex], replaces all instances of [replace] with the [with]. - */ - internal fun replace(rex: Rex, replace: Rex, with: Rex): Rex { - val params = ReplaceParams(replace, with) - return RexReplacerImpl.visitRex(rex, params) - } - - private class ReplaceParams(val replace: Rex, val with: Rex) - - private object RexReplacerImpl : PlanRewriter() { - - override fun visitRex(node: Rex, ctx: ReplaceParams): Rex { - if (node == ctx.replace) { return ctx.with } - val op = visitRexOp(node.op, ctx) as Rex.Op - return if (op !== node.op) rex(node.type, op) else node - } - } -} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/Scope.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/Scope.kt index ab18071c74..d3cb647422 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/Scope.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/Scope.kt @@ -18,8 +18,8 @@ import org.partiql.spi.value.Datum * @property outer refers to the outer variable scopes that we have access to. */ internal data class Scope( - public val schema: List, - public val outer: List + val schema: List, + val outer: List ) { internal fun getScope(depth: Int): Scope { diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/TypeEnv.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/TypeEnv.kt index b605f9eaef..59dd88f2e8 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/TypeEnv.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/TypeEnv.kt @@ -8,7 +8,7 @@ import org.partiql.spi.catalog.Identifier * TypeEnv represents the variables type environment (holds references to both locals and globals). */ internal class TypeEnv( - val globals: Env, + private val globals: Env, val locals: Scope ) { diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/TypeUtils.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/TypeUtils.kt deleted file mode 100644 index 7b32c345b9..0000000000 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/TypeUtils.kt +++ /dev/null @@ -1,112 +0,0 @@ -package org.partiql.planner.internal.typer - -import org.partiql.planner.internal.ir.Rel -import org.partiql.planner.internal.typer.PlanTyper.Companion.toCType -import org.partiql.spi.types.PType - -/** - * Applies the given exclusion path to produce the reduced [CompilerType]. [lastStepOptional] indicates if a previous - * step in the exclude path includes a collection index exclude step. Currently, for paths with the last step as - * a struct symbol/key, the type inference will define that struct value as optional if [lastStepOptional] is true. - * Note, this specific behavior could change depending on `EXCLUDE`'s static typing behavior in a future RFC. - * - * e.g. EXCLUDE t.a[1].field_x will define the struct value `field_x` as optional - * - * @param steps - * @param lastStepOptional - * @return - */ -internal fun CompilerType.exclude(steps: List, lastStepOptional: Boolean = false): CompilerType { - val type = this - return steps.fold(type) { acc, step -> - when (acc.code()) { - PType.DYNAMIC -> CompilerType(PType.dynamic()) - PType.ROW -> acc.excludeStruct(step, lastStepOptional) - PType.STRUCT -> acc - PType.ARRAY, PType.BAG -> acc.excludeCollection(step, lastStepOptional) - else -> acc - } - } -} - -/** - * Applies exclusions to struct fields. - * - * @param step - * @param lastStepOptional - * @return - */ -internal fun CompilerType.excludeStruct(step: Rel.Op.Exclude.Step, lastStepOptional: Boolean = false): CompilerType { - val type = step.type - val substeps = step.substeps - val output = fields.mapNotNull { field -> - val newField = if (substeps.isEmpty()) { - if (lastStepOptional) { - CompilerType.Field(field.name, field.type) - } else { - null - } - } else { - val k = field.name - val v = field.type.exclude(substeps, lastStepOptional) - CompilerType.Field(k, v) - } - when (type) { - is Rel.Op.Exclude.Type.StructSymbol -> { - if (type.symbol.equals(field.name, ignoreCase = true)) { - newField - } else { - field - } - } - - is Rel.Op.Exclude.Type.StructKey -> { - if (type.key == field.name) { - newField - } else { - field - } - } - is Rel.Op.Exclude.Type.StructWildcard -> newField - else -> field - } - } - return CompilerType(PType.row(output)) -} - -/** - * Applies exclusions to collection element type. - * - * @param step - * @param lastStepOptional - * @return - */ -internal fun CompilerType.excludeCollection(step: Rel.Op.Exclude.Step, lastStepOptional: Boolean = false): CompilerType { - var e = this.typeParameter - val substeps = step.substeps - when (step.type) { - is Rel.Op.Exclude.Type.CollIndex -> { - if (substeps.isNotEmpty()) { - e = e.exclude(substeps, lastStepOptional = true) - } - } - - is Rel.Op.Exclude.Type.CollWildcard -> { - if (substeps.isNotEmpty()) { - e = e.exclude(substeps, lastStepOptional) - } - // currently no change to elementType if collection wildcard is last element; this behavior could - // change based on RFC definition - } - - else -> { - // currently no change to elementType and no error thrown; could consider an error/warning in - // the future - } - } - return when (this.code()) { - PType.ARRAY -> PType.array(e).toCType() - PType.BAG -> PType.bag(e).toCType() - else -> throw IllegalStateException() - } -} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/util/BinderUtils.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/util/BinderUtils.kt new file mode 100644 index 0000000000..f6997e2d49 --- /dev/null +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/util/BinderUtils.kt @@ -0,0 +1,66 @@ +package org.partiql.planner.internal.util + +import org.partiql.ast.Identifier +import org.partiql.ast.Identifier.Simple +import org.partiql.ast.Literal +import org.partiql.ast.expr.Expr +import org.partiql.ast.expr.ExprCast +import org.partiql.ast.expr.ExprLit +import org.partiql.ast.expr.ExprPath +import org.partiql.ast.expr.ExprSessionAttribute +import org.partiql.ast.expr.ExprVarRef +import org.partiql.ast.expr.PathStep + +internal object BinderUtils { + private val col = { index: () -> Int -> "_${index()}" } + + /** + * Produces a "binder" (AS alias) for an expression following the given rules: + * + * 1. If item is an id, use the last symbol + * 2. If item is a path with a final symbol step, use the symbol — else 4 + * 3. If item is a cast, use the value name + * 4. Else, use item index with prefix _ + * + * See https://github.com/partiql/partiql-lang-kotlin/issues/1122 + */ + internal fun Expr.toBinder(index: () -> Int): Simple = when (this) { + is ExprVarRef -> this.identifier.toBinder() + is ExprPath -> this.toBinder(index) + is ExprCast -> this.value.toBinder(index) + is ExprSessionAttribute -> this.sessionAttribute.name().uppercase().toBinder() + else -> col(index).toBinder() + } + + /** + * Simple toBinder that uses an int literal rather than a closure. + * + * @param index + * @return + */ + internal fun Expr.toBinder(index: Int): Simple = toBinder { index } + + private fun String.toBinder(): Simple = + // Every binder preserves case + Simple.delimited(this@toBinder) + + private fun Identifier.toBinder(): Simple = this.identifier.toBinder() + + private fun Simple.toBinder(): Simple = text.toBinder() + + private fun ExprPath.toBinder(index: () -> Int): Simple { + if (steps.isEmpty()) return root.toBinder(index) + return when (val last = steps.last()) { + is PathStep.Field -> last.field.toBinder() + is PathStep.Element -> { + val k = last.element + if (k is ExprLit && k.lit.code() == Literal.STRING) { + k.lit.stringValue().toBinder() + } else { + col(index).toBinder() + } + } + else -> col(index).toBinder() + } + } +} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/utils/FunctionUtils.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/util/FunctionUtils.kt similarity index 99% rename from partiql-planner/src/main/kotlin/org/partiql/planner/internal/utils/FunctionUtils.kt rename to partiql-planner/src/main/kotlin/org/partiql/planner/internal/util/FunctionUtils.kt index 4cb37bec12..2f92b5772e 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/utils/FunctionUtils.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/util/FunctionUtils.kt @@ -1,4 +1,4 @@ -package org.partiql.planner.internal.utils +package org.partiql.planner.internal.util import org.partiql.ast.DatetimeField diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/util/TypeUtils.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/util/TypeUtils.kt new file mode 100644 index 0000000000..5869abbaa4 --- /dev/null +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/util/TypeUtils.kt @@ -0,0 +1,115 @@ +package org.partiql.planner.internal.util + +import org.partiql.planner.internal.ir.Rel +import org.partiql.planner.internal.typer.CompilerType +import org.partiql.planner.internal.typer.PlanTyper.Companion.toCType +import org.partiql.spi.types.PType + +internal object TypeUtils { + /** + * Applies the given exclusion path to produce the reduced [CompilerType]. [lastStepOptional] indicates if a previous + * step in the exclude path includes a collection index exclude step. Currently, for paths with the last step as + * a struct symbol/key, the type inference will define that struct value as optional if [lastStepOptional] is true. + * Note, this specific behavior could change depending on `EXCLUDE`'s static typing behavior in a future RFC. + * + * e.g. EXCLUDE t.a[1].field_x will define the struct value `field_x` as optional + * + * @param steps + * @param lastStepOptional + * @return + */ + internal fun CompilerType.exclude(steps: List, lastStepOptional: Boolean = false): CompilerType { + val type = this + return steps.fold(type) { acc, step -> + when (acc.code()) { + PType.DYNAMIC -> CompilerType(PType.dynamic()) + PType.ROW -> acc.excludeStruct(step, lastStepOptional) + PType.STRUCT -> acc + PType.ARRAY, PType.BAG -> acc.excludeCollection(step, lastStepOptional) + else -> acc + } + } + } + + /** + * Applies exclusions to struct fields. + * + * @param step + * @param lastStepOptional + * @return + */ + internal fun CompilerType.excludeStruct(step: Rel.Op.Exclude.Step, lastStepOptional: Boolean = false): CompilerType { + val type = step.type + val substeps = step.substeps + val output = fields.mapNotNull { field -> + val newField = if (substeps.isEmpty()) { + if (lastStepOptional) { + CompilerType.Field(field.name, field.type) + } else { + null + } + } else { + val k = field.name + val v = field.type.exclude(substeps, lastStepOptional) + CompilerType.Field(k, v) + } + when (type) { + is Rel.Op.Exclude.Type.StructSymbol -> { + if (type.symbol.equals(field.name, ignoreCase = true)) { + newField + } else { + field + } + } + + is Rel.Op.Exclude.Type.StructKey -> { + if (type.key == field.name) { + newField + } else { + field + } + } + is Rel.Op.Exclude.Type.StructWildcard -> newField + else -> field + } + } + return CompilerType(PType.row(output)) + } + + /** + * Applies exclusions to collection element type. + * + * @param step + * @param lastStepOptional + * @return + */ + internal fun CompilerType.excludeCollection(step: Rel.Op.Exclude.Step, lastStepOptional: Boolean = false): CompilerType { + var e = this.typeParameter + val substeps = step.substeps + when (step.type) { + is Rel.Op.Exclude.Type.CollIndex -> { + if (substeps.isNotEmpty()) { + e = e.exclude(substeps, lastStepOptional = true) + } + } + + is Rel.Op.Exclude.Type.CollWildcard -> { + if (substeps.isNotEmpty()) { + e = e.exclude(substeps, lastStepOptional) + } + // currently no change to elementType if collection wildcard is last element; this behavior could + // change based on RFC definition + } + + else -> { + // currently no change to elementType and no error thrown; could consider an error/warning in + // the future + } + } + return when (this.code()) { + PType.ARRAY -> PType.array(e).toCType() + PType.BAG -> PType.bag(e).toCType() + else -> throw IllegalStateException() + } + } +} diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/PlanEquivalenceOperatorVisitor.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/PlanEquivalenceOperatorVisitor.kt index bbb5535e30..f19281380c 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/PlanEquivalenceOperatorVisitor.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/PlanEquivalenceOperatorVisitor.kt @@ -52,9 +52,9 @@ import org.partiql.plan.rex.RexVar object PlanEquivalenceOperatorVisitor : OperatorVisitor { @JvmStatic - public fun equals(p1: Plan, p2: Plan): Boolean = visitPlan(p1, p2) + fun equals(p1: Plan, p2: Plan): Boolean = visitPlan(p1, p2) - public fun visitPlan(plan: Plan, other: Any): Boolean { + private fun visitPlan(plan: Plan, other: Any): Boolean { if (other !is Plan) { return false } @@ -63,7 +63,7 @@ object PlanEquivalenceOperatorVisitor : OperatorVisitor { return visitOperation(op1, op2) } - public fun visitOperation(action: Action, other: Any): Boolean { + private fun visitOperation(action: Action, other: Any): Boolean { if (other !is Action) { return false } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt index 91f4b80452..5f2bef19b4 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt @@ -116,10 +116,9 @@ class PlanTest { DynamicTest.dynamicTest(displayName) { val input = input[test.key] ?: error("no test cases") val originalQuery = input - val normalizedQuery = test listOf(true, false).forEach { isSignal -> val inputPlan = pipeline.invoke(originalQuery, isSignal).plan - val outputPlan = pipeline.invoke(normalizedQuery, isSignal).plan + val outputPlan = pipeline.invoke(test, isSignal).plan assertPlanEqual(inputPlan, outputPlan) } } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt index ae1d17a42c..1f3e725dcf 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt @@ -89,7 +89,7 @@ internal class PlannerPErrorReportingTests { } companion object { - fun closedStruct(vararg field: StructType.Field): StructType = + private fun closedStruct(vararg field: StructType.Field): StructType = StructType( field.toList(), contentClosed = true, @@ -190,7 +190,7 @@ internal class PlannerPErrorReportingTests { true, assertOnProblemCount(0, 1) ), - // Chained, demostrate missing trace. + // Chained, demonstrate missing trace. // TODO: We currently don't have a good way to retain missing value information. The following test // could have 2 warnings. One for executing a path operation on a literal missing. And one for // executing a path operation on an expression that is known to result in the missing value. diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/TestCatalog.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/TestCatalog.kt index 50b079523f..2b719a2f7b 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/TestCatalog.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/TestCatalog.kt @@ -12,7 +12,7 @@ import org.partiql.spi.types.PType * * TODO COMBINE WITH MemoryCatalog as the standard catalog implementation. */ -public class TestCatalog private constructor( +class TestCatalog private constructor( private val name: String, private val root: Tree, ) : Catalog { diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt index 9695540547..ef6e981161 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt @@ -31,15 +31,13 @@ abstract class PartiQLTyperTestBase { override fun toString(): String = "Success_$expectedType" } - object Failure : TestResult() { - override fun toString(): String = "Failure" - } + data object Failure : TestResult() } companion object { - public val parser = PartiQLParser.standard() - public val planner = PartiQLPlanner.standard() + val parser = PartiQLParser.standard() + val planner = PartiQLPlanner.standard() internal val session: ((String, Catalog) -> Session) = { catalog, metadata -> Session.builder() diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTest.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTest.kt index 126b2b9fce..e61f2c4073 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTest.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTest.kt @@ -132,7 +132,7 @@ class PlanTyperTest { } private class PlanTyperWrapper( - internal val typer: PlanTyper, + val typer: PlanTyper, ) /** diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/ScopeTest.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/ScopeTest.kt index e9c89bbb9a..a6be37c984 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/ScopeTest.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/ScopeTest.kt @@ -62,7 +62,7 @@ internal class ScopeTest { } @JvmStatic - public fun cases() = listOf>( + fun cases() = listOf( // root matching """ A.B """ to null, """ A."B" """ to null, @@ -92,13 +92,11 @@ internal class ScopeTest { val path = case.first.path() val expected = case.second val rex = locals.resolve(path) - if (rex == null) { - if (expected == null) { + ?: if (expected == null) { return // pass } else { fail("could not resolve variable") } - } // For now, just traverse to the root var root = rex.op while (root !is Rex.Op.Var.Local) { diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/logical/OpLogicalTest.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/logical/OpLogicalTest.kt index c72488e92b..8a3161b4c1 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/logical/OpLogicalTest.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/logical/OpLogicalTest.kt @@ -29,9 +29,7 @@ class OpLogicalTest : PartiQLTyperTestBase() { val argsMap = buildMap { val successArgs = supportedType.map { t -> listOf(t) }.toSet() successArgs.forEach { args: List -> - (this[TestResult.Success(StaticType.BOOL)] ?: setOf(args)).let { - put(TestResult.Success(StaticType.BOOL), it + setOf(args)) - } + put(TestResult.Success(StaticType.BOOL), (this[TestResult.Success(StaticType.BOOL)] ?: setOf(args)) + setOf(args)) Unit } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/path/SanityTests.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/path/SanityTests.kt index 852ff3cb5c..4345b7129f 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/path/SanityTests.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/path/SanityTests.kt @@ -28,7 +28,7 @@ class SanityTests : PartiQLTyperTestBase() { // -- All paths return ANY because t1 and t2 are both ANY val argsMap: Map>> = buildMap { put(TestResult.Success(StaticType.ANY), setOf(argTypes)) - put(TestResult.Failure, emptySet>()) + put(TestResult.Failure, emptySet()) } return super.testGen("path", tests, argsMap) } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalCatalog.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalCatalog.kt index c1cd967db4..011996fb70 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalCatalog.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalCatalog.kt @@ -14,7 +14,7 @@ import kotlin.io.path.notExists /** * Implementation of [Catalog] where dirs are namespaces and files are table metadata. */ -public class LocalCatalog internal constructor( +class LocalCatalog internal constructor( private val name: String, private val root: Path, ) : Catalog { @@ -102,24 +102,24 @@ public class LocalCatalog internal constructor( // return Namespace.of(path.relativize(root).map { it.toString() }) // } - public companion object { + companion object { private const val EXT = ".ion" @JvmStatic - public fun builder(): Builder = + fun builder(): Builder = Builder() } - public class Builder internal constructor() { + class Builder internal constructor() { private var name: String? = null private var root: Path? = null - public fun name(name: String): Builder = apply { this.name = name } + fun name(name: String): Builder = apply { this.name = name } - public fun root(root: Path): Builder = apply { this.root = root } + fun root(root: Path): Builder = apply { this.root = root } - public fun build(): LocalCatalog = LocalCatalog(name!!, root!!) + fun build(): LocalCatalog = LocalCatalog(name!!, root!!) } } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalConnector.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalConnector.kt index 818f5cb373..d82dc12734 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalConnector.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalConnector.kt @@ -29,14 +29,14 @@ import java.nio.file.Path * // use catalog * ``` */ -public class LocalConnector : Connector { +class LocalConnector : Connector { /** * Context arguments for instantiating a [LocalConnector] catalog. * * @property root The catalog root directory. */ - public class Context(@JvmField public val root: Path) : Connector.Context + class Context(@JvmField val root: Path) : Connector.Context override fun getCatalog(name: String): Catalog { throw IllegalArgumentException("LocalConnector cannot instantiate a catalog with no context") diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalSchema.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalSchema.kt index 6793d5cc0d..3987d1c3af 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalSchema.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalSchema.kt @@ -50,7 +50,7 @@ import org.partiql.types.TupleConstraint // Use some generated serde eventually -public inline fun StructElement.getAngry(name: String): T { +inline fun StructElement.getAngry(name: String): T { val f = getOptional(name) ?: error("Expected field `$name`") if (f !is T) { error("Expected field `name` to be of type ${T::class.simpleName}") @@ -63,7 +63,7 @@ public inline fun StructElement.getAngry(name: String): * * The format used is effectively Avro JSON, but with PartiQL type names. */ -public fun IonElement.toStaticType(): StaticType { +fun IonElement.toStaticType(): StaticType { return when (this) { is StringElement -> this.toStaticType() is ListElement -> this.toStaticType() @@ -73,7 +73,7 @@ public fun IonElement.toStaticType(): StaticType { } // Atomic type -public fun StringElement.toStaticType(): StaticType = when (textValue) { +fun StringElement.toStaticType(): StaticType = when (textValue) { "any" -> StaticType.ANY "bool" -> StaticType.BOOL "int8" -> error("`int8` is currently not supported") @@ -103,15 +103,14 @@ public fun StringElement.toStaticType(): StaticType = when (textValue) { } // Union type -public fun ListElement.toStaticType(): StaticType { +fun ListElement.toStaticType(): StaticType { val types = values.map { it.toStaticType() }.toSet() return StaticType.unionOf(types) } // Complex type -public fun StructElement.toStaticType(): StaticType { - val type = getAngry("type").textValue - return when (type) { +fun StructElement.toStaticType(): StaticType { + return when (val type = getAngry("type").textValue) { "bag" -> toBagType() "list" -> toListType() "sexp" -> toSexpType() @@ -122,22 +121,22 @@ public fun StructElement.toStaticType(): StaticType { } } -public fun StructElement.toBagType(): StaticType { +fun StructElement.toBagType(): StaticType { val items = getAngry("items").toStaticType() return BagType(items) } -public fun StructElement.toListType(): StaticType { +fun StructElement.toListType(): StaticType { val items = getAngry("items").toStaticType() return ListType(items) } -public fun StructElement.toSexpType(): StaticType { +fun StructElement.toSexpType(): StaticType { val items = getAngry("items").toStaticType() return SexpType(items) } -public fun StructElement.toStructType(): StaticType { +fun StructElement.toStructType(): StaticType { // Constraints var contentClosed = false val constraintsE = getOptional("constraints") ?: ionListOf() @@ -166,14 +165,14 @@ public fun StructElement.toStructType(): StaticType { return StructType(fields, contentClosed, constraints = constraints) } -public fun StructElement.toDecimalType(): StaticType { +fun StructElement.toDecimalType(): StaticType { val precision = get("precision").bigIntegerValue.intValueExact() val scale = get("scale").bigIntegerValue.intValueExact() val precisionScaleConstraint = DecimalType.PrecisionScaleConstraint.Constrained(precision, scale) return DecimalType(precisionScaleConstraint) } -public fun StructElement.toStringType(): StaticType { +fun StructElement.toStringType(): StaticType { return when { getOptional("min_length") != null -> { val length = get("min_length") @@ -187,7 +186,7 @@ public fun StructElement.toStringType(): StaticType { } } -public fun StaticType.toIon(): IonElement = when (this) { +fun StaticType.toIon(): IonElement = when (this) { is AnyOfType -> this.toIon() is AnyType -> ionString("any") is BlobType -> ionString("blob") diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/util/PlanPrinter.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/util/PlanPrinter.kt index febdd6e11a..6ee02c9336 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/util/PlanPrinter.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/util/PlanPrinter.kt @@ -9,7 +9,7 @@ import org.partiql.spi.types.PType * * Useful for debugging while the Jackson Poem doesn't handle map serde. */ -public object PlanPrinter { +object PlanPrinter { fun toString(plan: Plan): String = buildString { append(this, plan) } @@ -29,7 +29,7 @@ public object PlanPrinter { sealed interface TypeInfo { class Rel(val type: RelType) : TypeInfo class Rex(val type: PType) : TypeInfo - object Nil : TypeInfo + data object Nil : TypeInfo } // leading characters of a tree print diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/util/Utils.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/util/Utils.kt index ec3202fd0a..63d320f52a 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/util/Utils.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/util/Utils.kt @@ -280,8 +280,7 @@ val castTablePType: ((PType, PType) -> CastType) = { from, to -> // Ref.Cast.Safety.EXPLICIT -> CastType.EXPLICIT // } // } - val fromKind = from.code() - when (fromKind) { + when (val fromKind = from.code()) { PType.DYNAMIC -> CastType.UNSAFE PType.BLOB -> when (to.code()) { PType.BLOB -> CastType.COERCION @@ -309,7 +308,7 @@ val castTablePType: ((PType, PType) -> CastType) = { from, to -> else -> CastType.UNSAFE } PType.DECIMAL -> { - when (val toKind = to.code()) { + when (to.code()) { PType.DECIMAL -> { val toPrecision = to.precision val toScale = to.scale diff --git a/partiql-planner/src/testFixtures/kotlin/org/partiql/planner/test/PartiQLTest.kt b/partiql-planner/src/testFixtures/kotlin/org/partiql/planner/test/PartiQLTest.kt index 0e8f762a2d..032ed94bff 100644 --- a/partiql-planner/src/testFixtures/kotlin/org/partiql/planner/test/PartiQLTest.kt +++ b/partiql-planner/src/testFixtures/kotlin/org/partiql/planner/test/PartiQLTest.kt @@ -20,9 +20,9 @@ package org.partiql.planner.test * --#[example-test] * SELECT * FROM example; */ -public data class PartiQLTest( - public val key: Key, - public val statement: String, +data class PartiQLTest( + val key: Key, + val statement: String, ) { /** @@ -31,8 +31,8 @@ public data class PartiQLTest( * @property group * @property name */ - public data class Key( - public val group: String, - public val name: String, + data class Key( + val group: String, + val name: String, ) } diff --git a/partiql-planner/src/testFixtures/kotlin/org/partiql/planner/test/PartiQLTestProvider.kt b/partiql-planner/src/testFixtures/kotlin/org/partiql/planner/test/PartiQLTestProvider.kt index c2c56ea144..6e3af7e1d0 100644 --- a/partiql-planner/src/testFixtures/kotlin/org/partiql/planner/test/PartiQLTestProvider.kt +++ b/partiql-planner/src/testFixtures/kotlin/org/partiql/planner/test/PartiQLTestProvider.kt @@ -31,7 +31,7 @@ class PartiQLTestProvider { /** * Load test groups from a directory. */ - public fun load(root: Path? = null) { + fun load(root: Path? = null) { if (root != null) { val dir = root.toFile() dir.listFiles { f -> f.isDirectory }!!.map { @@ -62,7 +62,7 @@ class PartiQLTestProvider { * @param key * @return */ - public operator fun get(key: PartiQLTest.Key): PartiQLTest? = map[key] + operator fun get(key: PartiQLTest.Key): PartiQLTest? = map[key] /** * Lookup a test by key parts @@ -71,7 +71,7 @@ class PartiQLTestProvider { * @param name * @return */ - public fun get(group: String, name: String): PartiQLTest? = get(PartiQLTest.Key(group, name)) + fun get(group: String, name: String): PartiQLTest? = get(PartiQLTest.Key(group, name)) // load all tests in a directory private fun load(dir: File) = dir.listFiles()!!.flatMap { load(dir.name, it) }