From dafdac57df9f004af8c5df7acc1c479b00dbbb8a Mon Sep 17 00:00:00 2001 From: Alan Cai Date: Fri, 15 Dec 2023 13:26:22 -0800 Subject: [PATCH] Refactor modeling of EXCLUDE in partiql-ast and partiql-plan (#1311) --- CHANGELOG.md | 2 + .../org/partiql/ast/helpers/ToLegacyAst.kt | 22 +++---- .../kotlin/org/partiql/ast/sql/SqlDialect.kt | 14 ++-- .../src/main/resources/partiql_ast.ion | 14 ++-- .../org/partiql/ast/sql/SqlDialectTest.kt | 66 +++++++++---------- .../parser/impl/PartiQLParserDefault.kt | 25 +++---- .../src/main/resources/partiql_plan.ion | 12 ++-- .../org/partiql/planner/internal/Env.kt | 2 +- .../org/partiql/planner/internal/ir/Nodes.kt | 36 +++++----- .../org/partiql/planner/internal/ir/Plan.kt | 15 +++-- .../internal/ir/builder/PlanBuilder.kt | 30 +++++---- .../internal/ir/builder/PlanBuilders.kt | 24 +++---- .../planner/internal/ir/util/PlanRewriter.kt | 20 +++--- .../internal/ir/visitor/PlanBaseVisitor.kt | 23 +++---- .../internal/ir/visitor/PlanVisitor.kt | 11 +--- .../internal/transforms/PlanTransform.kt | 25 ++++--- .../internal/transforms/RelConverter.kt | 20 +++--- .../planner/internal/typer/PlanTyper.kt | 61 +++++++++++++---- .../planner/internal/typer/TypeUtils.kt | 6 +- 19 files changed, 238 insertions(+), 190 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7a6959e4..e60cfef79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ Thank you to all who have contributed! - Changes the return type of `filter_distinct` to a list if input collection is list - Changes the `PartiQLValue` collections to implement Iterable rather than Sequence, allowing for multiple consumption. - **BREAKING** Moves PartiQLParserBuilder.standard().build() to be PartiQLParser.default(). +- **BREAKING** Changed modeling of `EXCLUDE` in `partiql-ast` ### Deprecated @@ -72,6 +73,7 @@ Thank you to all who have contributed! Thank you to all who have contributed! - @rchowell - @johnedquinn +- @alancai98 ## [0.13.2-alpha] - 2023-09-29 diff --git a/partiql-ast/src/main/kotlin/org/partiql/ast/helpers/ToLegacyAst.kt b/partiql-ast/src/main/kotlin/org/partiql/ast/helpers/ToLegacyAst.kt index e48445a8d..0dd1a879f 100644 --- a/partiql-ast/src/main/kotlin/org/partiql/ast/helpers/ToLegacyAst.kt +++ b/partiql-ast/src/main/kotlin/org/partiql/ast/helpers/ToLegacyAst.kt @@ -756,42 +756,42 @@ private class AstTranslator(val metas: Map) : AstBaseVisi } override fun visitExclude(node: Exclude, ctx: Ctx): PartiqlAst.ExcludeOp = translate(node) { metas -> - val excludeExprs = node.exprs.translate(ctx) + val excludeExprs = node.items.translate(ctx) excludeOp(excludeExprs, metas) } - override fun visitExcludeExcludeExpr(node: Exclude.ExcludeExpr, ctx: Ctx) = translate(node) { metas -> - val root = visitIdentifierSymbol(node.root, ctx) + override fun visitExcludeItem(node: Exclude.Item, ctx: Ctx) = translate(node) { metas -> + val root = visitExprVar(node.root, ctx) val steps = node.steps.translate(ctx) - excludeExpr(root = root, steps = steps, metas) + excludeExpr(root = identifier_(root.name, root.case), steps = steps, metas) } override fun visitExcludeStep(node: Exclude.Step, ctx: Ctx) = super.visitExcludeStep(node, ctx) as PartiqlAst.ExcludeStep - override fun visitExcludeStepExcludeTupleAttr(node: Exclude.Step.ExcludeTupleAttr, ctx: Ctx) = translate(node) { metas -> + override fun visitExcludeStepStructField(node: Exclude.Step.StructField, ctx: Ctx) = translate(node) { metas -> val attr = node.symbol.symbol val case = node.symbol.caseSensitivity.toLegacyCaseSensitivity() excludeTupleAttr(identifier(attr, case), metas) } - override fun visitExcludeStepExcludeCollectionIndex( - node: Exclude.Step.ExcludeCollectionIndex, + override fun visitExcludeStepCollIndex( + node: Exclude.Step.CollIndex, ctx: Ctx ) = translate(node) { metas -> val index = node.index.toLong() excludeCollectionIndex(index, metas) } - override fun visitExcludeStepExcludeTupleWildcard( - node: Exclude.Step.ExcludeTupleWildcard, + override fun visitExcludeStepStructWildcard( + node: Exclude.Step.StructWildcard, ctx: Ctx ) = translate(node) { metas -> excludeTupleWildcard(metas) } - override fun visitExcludeStepExcludeCollectionWildcard( - node: Exclude.Step.ExcludeCollectionWildcard, + override fun visitExcludeStepCollWildcard( + node: Exclude.Step.CollWildcard, ctx: Ctx ) = translate(node) { metas -> excludeCollectionWildcard(metas) diff --git a/partiql-ast/src/main/kotlin/org/partiql/ast/sql/SqlDialect.kt b/partiql-ast/src/main/kotlin/org/partiql/ast/sql/SqlDialect.kt index 676f687dc..05dc0a961 100644 --- a/partiql-ast/src/main/kotlin/org/partiql/ast/sql/SqlDialect.kt +++ b/partiql-ast/src/main/kotlin/org/partiql/ast/sql/SqlDialect.kt @@ -86,32 +86,32 @@ public abstract class SqlDialect : AstBaseVisitor() { override fun visitExclude(node: Exclude, head: SqlBlock): SqlBlock { var h = head h = h concat " EXCLUDE " - h = h concat list(start = null, end = null) { node.exprs } + h = h concat list(start = null, end = null) { node.items } return h } - override fun visitExcludeExcludeExpr(node: Exclude.ExcludeExpr, head: SqlBlock): SqlBlock { + override fun visitExcludeItem(node: Exclude.Item, head: SqlBlock): SqlBlock { var h = head - h = h concat visitIdentifierSymbol(node.root, SqlBlock.Nil) + h = h concat visitExprVar(node.root, SqlBlock.Nil) h = h concat list(delimiter = null, start = null, end = null) { node.steps } return h } - override fun visitExcludeStepExcludeCollectionIndex(node: Exclude.Step.ExcludeCollectionIndex, head: SqlBlock): SqlBlock { + override fun visitExcludeStepCollIndex(node: Exclude.Step.CollIndex, head: SqlBlock): SqlBlock { return head concat r("[${node.index}]") } - override fun visitExcludeStepExcludeTupleWildcard(node: Exclude.Step.ExcludeTupleWildcard, head: SqlBlock): SqlBlock { + override fun visitExcludeStepStructWildcard(node: Exclude.Step.StructWildcard, head: SqlBlock): SqlBlock { return head concat r(".*") } - override fun visitExcludeStepExcludeTupleAttr(node: Exclude.Step.ExcludeTupleAttr, head: SqlBlock): SqlBlock { + override fun visitExcludeStepStructField(node: Exclude.Step.StructField, head: SqlBlock): SqlBlock { var h = head concat r(".") h = h concat visitIdentifierSymbol(node.symbol, SqlBlock.Nil) return h } - override fun visitExcludeStepExcludeCollectionWildcard(node: Exclude.Step.ExcludeCollectionWildcard, head: SqlBlock): SqlBlock { + override fun visitExcludeStepCollWildcard(node: Exclude.Step.CollWildcard, head: SqlBlock): SqlBlock { return head concat r("[*]") } diff --git a/partiql-ast/src/main/resources/partiql_ast.ion b/partiql-ast/src/main/resources/partiql_ast.ion index 1c598fcc9..67c48663c 100644 --- a/partiql-ast/src/main/resources/partiql_ast.ion +++ b/partiql-ast/src/main/resources/partiql_ast.ion @@ -563,17 +563,17 @@ select::[ ] exclude::{ - exprs: list::[exclude_expr], + items: list::[item], _: [ - exclude_expr::{ - root: '.identifier.symbol', + item::{ + root: '.expr.var', steps: list::[step], }, step::[ - exclude_tuple_attr::{ symbol: '.identifier.symbol' }, - exclude_collection_index::{ index: int }, - exclude_tuple_wildcard::{}, - exclude_collection_wildcard::{}, + struct_field::{ symbol: '.identifier.symbol' }, + coll_index::{ index: int }, + struct_wildcard::{}, + coll_wildcard::{}, ] ] } diff --git a/partiql-ast/src/test/kotlin/org/partiql/ast/sql/SqlDialectTest.kt b/partiql-ast/src/test/kotlin/org/partiql/ast/sql/SqlDialectTest.kt index 422d88f5c..28b161bef 100644 --- a/partiql-ast/src/test/kotlin/org/partiql/ast/sql/SqlDialectTest.kt +++ b/partiql-ast/src/test/kotlin/org/partiql/ast/sql/SqlDialectTest.kt @@ -1073,11 +1073,9 @@ class SqlDialectTest { type = From.Value.Type.SCAN } exclude = exclude { - exprs += excludeExcludeExpr { - root = id("t", Identifier.CaseSensitivity.INSENSITIVE) - steps += excludeStepExcludeTupleAttr { - symbol = id("a", Identifier.CaseSensitivity.INSENSITIVE) - } + items += excludeItem { + root = v("t") + steps += insensitiveExcludeStructField("a") } } } @@ -1090,21 +1088,21 @@ class SqlDialectTest { type = From.Value.Type.SCAN } exclude = exclude { - exprs += excludeExcludeExpr { - root = id("a", Identifier.CaseSensitivity.INSENSITIVE) - steps += insensitiveExcludeTupleAttr("b") + items += excludeItem { + root = v("a") + steps += insensitiveExcludeStructField("b") } - exprs += excludeExcludeExpr { - root = id("c", Identifier.CaseSensitivity.INSENSITIVE) - steps += insensitiveExcludeTupleAttr("d") + items += excludeItem { + root = v("c") + steps += insensitiveExcludeStructField("d") } - exprs += excludeExcludeExpr { - root = id("e", Identifier.CaseSensitivity.INSENSITIVE) - steps += insensitiveExcludeTupleAttr("f") + items += excludeItem { + root = v("e") + steps += insensitiveExcludeStructField("f") } - exprs += excludeExcludeExpr { - root = id("g", Identifier.CaseSensitivity.INSENSITIVE) - steps += insensitiveExcludeTupleAttr("h") + items += excludeItem { + root = v("g") + steps += insensitiveExcludeStructField("h") } } } @@ -1117,25 +1115,25 @@ class SqlDialectTest { type = From.Value.Type.SCAN } exclude = exclude { - exprs += excludeExcludeExpr { - root = id("t", Identifier.CaseSensitivity.INSENSITIVE) + items += excludeItem { + root = v("t") steps += mutableListOf( - insensitiveExcludeTupleAttr("a"), - sensitiveExcludeTupleAttr("b"), - excludeStepExcludeTupleWildcard(), - excludeStepExcludeCollectionWildcard(), - insensitiveExcludeTupleAttr("c"), + insensitiveExcludeStructField("a"), + sensitiveExcludeStructField("b"), + excludeStepStructWildcard(), + excludeStepCollWildcard(), + insensitiveExcludeStructField("c"), ) } - exprs += excludeExcludeExpr { - root = id("s", Identifier.CaseSensitivity.SENSITIVE) + items += excludeItem { + root = exprVar(id("s", Identifier.CaseSensitivity.SENSITIVE), Expr.Var.Scope.DEFAULT) steps += mutableListOf( - excludeStepExcludeCollectionIndex(0), - insensitiveExcludeTupleAttr("d"), - sensitiveExcludeTupleAttr("e"), - excludeStepExcludeCollectionWildcard(), - insensitiveExcludeTupleAttr("f"), - excludeStepExcludeTupleWildcard(), + excludeStepCollIndex(0), + insensitiveExcludeStructField("d"), + sensitiveExcludeStructField("e"), + excludeStepCollWildcard(), + insensitiveExcludeStructField("f"), + excludeStepStructWildcard(), ) } } @@ -1143,11 +1141,11 @@ class SqlDialectTest { }, ) - private fun AstBuilder.insensitiveExcludeTupleAttr(str: String) = excludeStepExcludeTupleAttr { + private fun AstBuilder.insensitiveExcludeStructField(str: String) = excludeStepStructField { symbol = id(str, Identifier.CaseSensitivity.INSENSITIVE) } - private fun AstBuilder.sensitiveExcludeTupleAttr(str: String) = excludeStepExcludeTupleAttr { + private fun AstBuilder.sensitiveExcludeStructField(str: String) = excludeStepStructField { symbol = id(str, Identifier.CaseSensitivity.SENSITIVE) } diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/impl/PartiQLParserDefault.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/impl/PartiQLParserDefault.kt index 1a08125ae..870f42742 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/impl/PartiQLParserDefault.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/impl/PartiQLParserDefault.kt @@ -52,11 +52,11 @@ import org.partiql.ast.Statement import org.partiql.ast.TableDefinition import org.partiql.ast.Type import org.partiql.ast.exclude -import org.partiql.ast.excludeExcludeExpr -import org.partiql.ast.excludeStepExcludeCollectionIndex -import org.partiql.ast.excludeStepExcludeCollectionWildcard -import org.partiql.ast.excludeStepExcludeTupleAttr -import org.partiql.ast.excludeStepExcludeTupleWildcard +import org.partiql.ast.excludeItem +import org.partiql.ast.excludeStepCollIndex +import org.partiql.ast.excludeStepCollWildcard +import org.partiql.ast.excludeStepStructField +import org.partiql.ast.excludeStepStructWildcard import org.partiql.ast.exprAgg import org.partiql.ast.exprBagOp import org.partiql.ast.exprBetween @@ -1058,20 +1058,21 @@ internal class PartiQLParserDefault : PartiQLParser { } override fun visitExcludeExpr(ctx: GeneratedParser.ExcludeExprContext) = translate(ctx) { - val root = visitSymbolPrimitive(ctx.symbolPrimitive()) + val rootId = visitSymbolPrimitive(ctx.symbolPrimitive()) + val root = exprVar(rootId, Expr.Var.Scope.DEFAULT) val steps = visitOrEmpty(ctx.excludeExprSteps()) - excludeExcludeExpr(root, steps) + excludeItem(root, steps) } override fun visitExcludeExprTupleAttr(ctx: GeneratedParser.ExcludeExprTupleAttrContext) = translate(ctx) { val identifier = visitSymbolPrimitive(ctx.symbolPrimitive()) - excludeStepExcludeTupleAttr(identifier) + excludeStepStructField(identifier) } override fun visitExcludeExprCollectionIndex(ctx: GeneratedParser.ExcludeExprCollectionIndexContext) = translate(ctx) { val index = ctx.index.text.toInt() - excludeStepExcludeCollectionIndex(index) + excludeStepCollIndex(index) } override fun visitExcludeExprCollectionAttr(ctx: GeneratedParser.ExcludeExprCollectionAttrContext) = @@ -1081,17 +1082,17 @@ internal class PartiQLParserDefault : PartiQLParser { attr, Identifier.CaseSensitivity.SENSITIVE, ) - excludeStepExcludeTupleAttr(identifier) + excludeStepStructField(identifier) } override fun visitExcludeExprCollectionWildcard(ctx: org.partiql.parser.antlr.PartiQLParser.ExcludeExprCollectionWildcardContext) = translate(ctx) { - excludeStepExcludeCollectionWildcard() + excludeStepCollWildcard() } override fun visitExcludeExprTupleWildcard(ctx: org.partiql.parser.antlr.PartiQLParser.ExcludeExprTupleWildcardContext) = translate(ctx) { - excludeStepExcludeTupleWildcard() + excludeStepStructWildcard() } /** diff --git a/partiql-plan/src/main/resources/partiql_plan.ion b/partiql-plan/src/main/resources/partiql_plan.ion index 170fa2901..2600b2c42 100644 --- a/partiql-plan/src/main/resources/partiql_plan.ion +++ b/partiql-plan/src/main/resources/partiql_plan.ion @@ -288,18 +288,14 @@ rel::{ items: list::[item], _: [ item::{ - root: '.identifier.symbol', + root: '.rex.op.var', steps: list::[step], }, step::[ - attr::{ - symbol: '.identifier.symbol', - }, - pos::{ - index: int, - }, + struct_field::{ symbol: '.identifier.symbol' }, + coll_index::{ index: int }, struct_wildcard::{}, - collection_wildcard::{}, + coll_wildcard::{}, ], ], }, 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 74ea78ba4..25be6532b 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 @@ -281,7 +281,7 @@ internal class Env( /** * Check locals, else search structs. */ - private fun resolveLocalBind(path: BindingPath, locals: List): ResolvedVar? { + internal fun resolveLocalBind(path: BindingPath, locals: List): ResolvedVar? { if (path.steps.isEmpty()) { return null } diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/Nodes.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/Nodes.kt index d357cfbb2..907a8c7c5 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/Nodes.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/Nodes.kt @@ -19,9 +19,9 @@ import org.partiql.planner.internal.ir.builder.RelOpErrBuilder import org.partiql.planner.internal.ir.builder.RelOpExceptBuilder import org.partiql.planner.internal.ir.builder.RelOpExcludeBuilder import org.partiql.planner.internal.ir.builder.RelOpExcludeItemBuilder -import org.partiql.planner.internal.ir.builder.RelOpExcludeStepAttrBuilder -import org.partiql.planner.internal.ir.builder.RelOpExcludeStepCollectionWildcardBuilder -import org.partiql.planner.internal.ir.builder.RelOpExcludeStepPosBuilder +import org.partiql.planner.internal.ir.builder.RelOpExcludeStepCollIndexBuilder +import org.partiql.planner.internal.ir.builder.RelOpExcludeStepCollWildcardBuilder +import org.partiql.planner.internal.ir.builder.RelOpExcludeStepStructFieldBuilder import org.partiql.planner.internal.ir.builder.RelOpExcludeStepStructWildcardBuilder import org.partiql.planner.internal.ir.builder.RelOpFilterBuilder import org.partiql.planner.internal.ir.builder.RelOpIntersectBuilder @@ -1272,7 +1272,7 @@ internal data class Rel( internal data class Item( @JvmField - internal val root: Identifier.Symbol, + internal val root: Rex.Op.Var, @JvmField internal val steps: List, ) : PlanNode() { @@ -1294,13 +1294,13 @@ internal data class Rel( internal sealed class Step : PlanNode() { internal override fun accept(visitor: PlanVisitor, ctx: C): R = when (this) { - is Attr -> visitor.visitRelOpExcludeStepAttr(this, ctx) - is Pos -> visitor.visitRelOpExcludeStepPos(this, ctx) + is StructField -> visitor.visitRelOpExcludeStepStructField(this, ctx) + is CollIndex -> visitor.visitRelOpExcludeStepCollIndex(this, ctx) is StructWildcard -> visitor.visitRelOpExcludeStepStructWildcard(this, ctx) - is CollectionWildcard -> visitor.visitRelOpExcludeStepCollectionWildcard(this, ctx) + is CollWildcard -> visitor.visitRelOpExcludeStepCollWildcard(this, ctx) } - internal data class Attr( + internal data class StructField( @JvmField internal val symbol: Identifier.Symbol, ) : Step() { @@ -1311,26 +1311,28 @@ internal data class Rel( } internal override fun accept(visitor: PlanVisitor, ctx: C): R = - visitor.visitRelOpExcludeStepAttr(this, ctx) + visitor.visitRelOpExcludeStepStructField(this, ctx) internal companion object { @JvmStatic - internal fun builder(): RelOpExcludeStepAttrBuilder = RelOpExcludeStepAttrBuilder() + internal fun builder(): RelOpExcludeStepStructFieldBuilder = + RelOpExcludeStepStructFieldBuilder() } } - internal data class Pos( + internal data class CollIndex( @JvmField internal val index: Int, ) : Step() { internal override val children: List = emptyList() internal override fun accept(visitor: PlanVisitor, ctx: C): R = - visitor.visitRelOpExcludeStepPos(this, ctx) + visitor.visitRelOpExcludeStepCollIndex(this, ctx) internal companion object { @JvmStatic - internal fun builder(): RelOpExcludeStepPosBuilder = RelOpExcludeStepPosBuilder() + internal fun builder(): RelOpExcludeStepCollIndexBuilder = + RelOpExcludeStepCollIndexBuilder() } } @@ -1350,19 +1352,19 @@ internal data class Rel( } } - internal data class CollectionWildcard( + internal data class CollWildcard( @JvmField internal val ` `: Char = ' ', ) : Step() { internal override val children: List = emptyList() internal override fun accept(visitor: PlanVisitor, ctx: C): R = - visitor.visitRelOpExcludeStepCollectionWildcard(this, ctx) + visitor.visitRelOpExcludeStepCollWildcard(this, ctx) internal companion object { @JvmStatic - internal fun builder(): RelOpExcludeStepCollectionWildcardBuilder = - RelOpExcludeStepCollectionWildcardBuilder() + internal fun builder(): RelOpExcludeStepCollWildcardBuilder = + RelOpExcludeStepCollWildcardBuilder() } } } diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/Plan.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/Plan.kt index ddd3b9547..fa002edad 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/Plan.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/Plan.kt @@ -163,19 +163,20 @@ internal fun relOpAggregateCall(agg: Agg, args: List): Rel.Op.Aggregate.Cal internal fun relOpExclude(input: Rel, items: List): Rel.Op.Exclude = Rel.Op.Exclude(input, items) -internal fun relOpExcludeItem(root: Identifier.Symbol, steps: List): - Rel.Op.Exclude.Item = Rel.Op.Exclude.Item(root, steps) +internal fun relOpExcludeItem(root: Rex.Op.Var, steps: List): Rel.Op.Exclude.Item = + Rel.Op.Exclude.Item(root, steps) -internal fun relOpExcludeStepAttr(symbol: Identifier.Symbol): Rel.Op.Exclude.Step.Attr = - Rel.Op.Exclude.Step.Attr(symbol) +internal fun relOpExcludeStepStructField(symbol: Identifier.Symbol): Rel.Op.Exclude.Step.StructField = + Rel.Op.Exclude.Step.StructField(symbol) -internal fun relOpExcludeStepPos(index: Int): Rel.Op.Exclude.Step.Pos = Rel.Op.Exclude.Step.Pos(index) +internal fun relOpExcludeStepCollIndex(index: Int): Rel.Op.Exclude.Step.CollIndex = + Rel.Op.Exclude.Step.CollIndex(index) internal fun relOpExcludeStepStructWildcard(): Rel.Op.Exclude.Step.StructWildcard = Rel.Op.Exclude.Step.StructWildcard() -internal fun relOpExcludeStepCollectionWildcard(): Rel.Op.Exclude.Step.CollectionWildcard = - Rel.Op.Exclude.Step.CollectionWildcard() +internal fun relOpExcludeStepCollWildcard(): Rel.Op.Exclude.Step.CollWildcard = + Rel.Op.Exclude.Step.CollWildcard() internal fun relOpErr(message: String): Rel.Op.Err = Rel.Op.Err(message) diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/builder/PlanBuilder.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/builder/PlanBuilder.kt index 8ea53d5c5..4cefed1c5 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/builder/PlanBuilder.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/builder/PlanBuilder.kt @@ -501,7 +501,7 @@ internal class PlanBuilder { } internal fun relOpExcludeItem( - root: Identifier.Symbol? = null, + root: Rex.Op.Var? = null, steps: MutableList = mutableListOf(), block: RelOpExcludeItemBuilder.() -> Unit = {}, ): Rel.Op.Exclude.Item { @@ -510,36 +510,38 @@ internal class PlanBuilder { return builder.build() } - internal fun relOpExcludeStepAttr( + internal fun relOpExcludeStepStructField( symbol: Identifier.Symbol? = null, - block: RelOpExcludeStepAttrBuilder.() -> Unit = {}, - ): Rel.Op.Exclude.Step.Attr { - val builder = RelOpExcludeStepAttrBuilder(symbol) + block: RelOpExcludeStepStructFieldBuilder.() -> Unit = {} + ): Rel.Op.Exclude.Step.StructField { + val builder = RelOpExcludeStepStructFieldBuilder(symbol) builder.block() return builder.build() } - internal fun relOpExcludeStepPos( + internal fun relOpExcludeStepCollIndex( index: Int? = null, - block: RelOpExcludeStepPosBuilder.() -> Unit = {}, - ): Rel.Op.Exclude.Step.Pos { - val builder = RelOpExcludeStepPosBuilder(index) + block: RelOpExcludeStepCollIndexBuilder.() -> Unit = {} + ): Rel.Op.Exclude.Step.CollIndex { + val builder = RelOpExcludeStepCollIndexBuilder(index) builder.block() return builder.build() } internal fun relOpExcludeStepStructWildcard( - block: RelOpExcludeStepStructWildcardBuilder.() -> Unit = {}, + block: RelOpExcludeStepStructWildcardBuilder.() -> Unit = + {} ): Rel.Op.Exclude.Step.StructWildcard { val builder = RelOpExcludeStepStructWildcardBuilder() builder.block() return builder.build() } - internal fun relOpExcludeStepCollectionWildcard( - block: RelOpExcludeStepCollectionWildcardBuilder.() -> Unit = {}, - ): Rel.Op.Exclude.Step.CollectionWildcard { - val builder = RelOpExcludeStepCollectionWildcardBuilder() + internal fun relOpExcludeStepCollWildcard( + block: RelOpExcludeStepCollWildcardBuilder.() -> Unit = + {} + ): Rel.Op.Exclude.Step.CollWildcard { + val builder = RelOpExcludeStepCollWildcardBuilder() builder.block() return builder.build() } diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/builder/PlanBuilders.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/builder/PlanBuilders.kt index 8f4cf3197..51f6f9122 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/builder/PlanBuilders.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/builder/PlanBuilders.kt @@ -749,10 +749,10 @@ internal class RelOpExcludeBuilder( } internal class RelOpExcludeItemBuilder( - internal var root: Identifier.Symbol? = null, + internal var root: Rex.Op.Var? = null, internal var steps: MutableList = mutableListOf(), ) { - internal fun root(root: Identifier.Symbol?): RelOpExcludeItemBuilder = this.apply { + internal fun root(root: Rex.Op.Var?): RelOpExcludeItemBuilder = this.apply { this.root = root } @@ -763,33 +763,35 @@ internal class RelOpExcludeItemBuilder( internal fun build(): Rel.Op.Exclude.Item = Rel.Op.Exclude.Item(root = root!!, steps = steps) } -internal class RelOpExcludeStepAttrBuilder( +internal class RelOpExcludeStepStructFieldBuilder( internal var symbol: Identifier.Symbol? = null, ) { - internal fun symbol(symbol: Identifier.Symbol?): RelOpExcludeStepAttrBuilder = this.apply { + internal fun symbol(symbol: Identifier.Symbol?): RelOpExcludeStepStructFieldBuilder = this.apply { this.symbol = symbol } - internal fun build(): Rel.Op.Exclude.Step.Attr = Rel.Op.Exclude.Step.Attr(symbol = symbol!!) + internal fun build(): Rel.Op.Exclude.Step.StructField = Rel.Op.Exclude.Step.StructField( + symbol = + symbol!! + ) } -internal class RelOpExcludeStepPosBuilder( +internal class RelOpExcludeStepCollIndexBuilder( internal var index: Int? = null, ) { - internal fun index(index: Int?): RelOpExcludeStepPosBuilder = this.apply { + internal fun index(index: Int?): RelOpExcludeStepCollIndexBuilder = this.apply { this.index = index } - internal fun build(): Rel.Op.Exclude.Step.Pos = Rel.Op.Exclude.Step.Pos(index = index!!) + internal fun build(): Rel.Op.Exclude.Step.CollIndex = Rel.Op.Exclude.Step.CollIndex(index = index!!) } internal class RelOpExcludeStepStructWildcardBuilder() { internal fun build(): Rel.Op.Exclude.Step.StructWildcard = Rel.Op.Exclude.Step.StructWildcard() } -internal class RelOpExcludeStepCollectionWildcardBuilder() { - internal fun build(): Rel.Op.Exclude.Step.CollectionWildcard = - Rel.Op.Exclude.Step.CollectionWildcard() +internal class RelOpExcludeStepCollWildcardBuilder() { + internal fun build(): Rel.Op.Exclude.Step.CollWildcard = Rel.Op.Exclude.Step.CollWildcard() } internal class RelOpErrBuilder( diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/util/PlanRewriter.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/util/PlanRewriter.kt index 0aae2cd0d..c42f7ac3b 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/util/PlanRewriter.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/util/PlanRewriter.kt @@ -542,7 +542,7 @@ internal abstract class PlanRewriter : PlanBaseVisitor() { } override fun visitRelOpExcludeItem(node: Rel.Op.Exclude.Item, ctx: C): PlanNode { - val root = visitIdentifierSymbol(node.root, ctx) as Identifier.Symbol + val root = visitRexOpVar(node.root, ctx) as Rex.Op.Var val steps = _visitList(node.steps, ctx, ::visitRelOpExcludeStep) return if (root !== node.root || steps !== node.steps) { Rel.Op.Exclude.Item(root, steps) @@ -551,28 +551,32 @@ internal abstract class PlanRewriter : PlanBaseVisitor() { } } - override fun visitRelOpExcludeStepAttr(node: Rel.Op.Exclude.Step.Attr, ctx: C): PlanNode { + override fun visitRelOpExcludeStepStructField( + node: Rel.Op.Exclude.Step.StructField, + ctx: C + ): PlanNode { val symbol = visitIdentifierSymbol(node.symbol, ctx) as Identifier.Symbol return if (symbol !== node.symbol) { - Rel.Op.Exclude.Step.Attr(symbol) + Rel.Op.Exclude.Step.StructField(symbol) } else { node } } - override fun visitRelOpExcludeStepPos(node: Rel.Op.Exclude.Step.Pos, ctx: C): PlanNode { + override fun visitRelOpExcludeStepCollIndex(node: Rel.Op.Exclude.Step.CollIndex, ctx: C): + PlanNode { val index = node.index return node } override fun visitRelOpExcludeStepStructWildcard( node: Rel.Op.Exclude.Step.StructWildcard, - ctx: C, + ctx: C ): PlanNode = node - override fun visitRelOpExcludeStepCollectionWildcard( - node: Rel.Op.Exclude.Step.CollectionWildcard, - ctx: C, + override fun visitRelOpExcludeStepCollWildcard( + node: Rel.Op.Exclude.Step.CollWildcard, + ctx: C ): PlanNode = node override fun visitRelOpErr(node: Rel.Op.Err, ctx: C): PlanNode { diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/visitor/PlanBaseVisitor.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/visitor/PlanBaseVisitor.kt index afe8dac28..3539c90b9 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/visitor/PlanBaseVisitor.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/visitor/PlanBaseVisitor.kt @@ -236,27 +236,28 @@ internal abstract class PlanBaseVisitor : PlanVisitor { defaultVisit(node, ctx) override fun visitRelOpExcludeStep(node: Rel.Op.Exclude.Step, ctx: C): R = when (node) { - is Rel.Op.Exclude.Step.Attr -> visitRelOpExcludeStepAttr(node, ctx) - is Rel.Op.Exclude.Step.Pos -> visitRelOpExcludeStepPos(node, ctx) + is Rel.Op.Exclude.Step.StructField -> visitRelOpExcludeStepStructField(node, ctx) + is Rel.Op.Exclude.Step.CollIndex -> visitRelOpExcludeStepCollIndex(node, ctx) is Rel.Op.Exclude.Step.StructWildcard -> visitRelOpExcludeStepStructWildcard(node, ctx) - is Rel.Op.Exclude.Step.CollectionWildcard -> visitRelOpExcludeStepCollectionWildcard(node, ctx) + is Rel.Op.Exclude.Step.CollWildcard -> visitRelOpExcludeStepCollWildcard(node, ctx) } - override fun visitRelOpExcludeStepAttr(node: Rel.Op.Exclude.Step.Attr, ctx: C): R = - defaultVisit(node, ctx) + override fun visitRelOpExcludeStepStructField( + node: Rel.Op.Exclude.Step.StructField, + ctx: C + ): R = defaultVisit(node, ctx) - override fun visitRelOpExcludeStepPos(node: Rel.Op.Exclude.Step.Pos, ctx: C): R = + override fun visitRelOpExcludeStepCollIndex(node: Rel.Op.Exclude.Step.CollIndex, ctx: C): R = defaultVisit(node, ctx) override fun visitRelOpExcludeStepStructWildcard( node: Rel.Op.Exclude.Step.StructWildcard, - ctx: C, + ctx: C ): R = defaultVisit(node, ctx) - override - fun visitRelOpExcludeStepCollectionWildcard( - node: Rel.Op.Exclude.Step.CollectionWildcard, - ctx: C, + override fun visitRelOpExcludeStepCollWildcard( + node: Rel.Op.Exclude.Step.CollWildcard, + ctx: C ): R = defaultVisit(node, ctx) override fun visitRelOpErr(node: Rel.Op.Err, ctx: C): R = defaultVisit(node, ctx) diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/visitor/PlanVisitor.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/visitor/PlanVisitor.kt index f3114e780..0181fd2a2 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/visitor/PlanVisitor.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/visitor/PlanVisitor.kt @@ -142,19 +142,14 @@ internal interface PlanVisitor { fun visitRelOpExcludeStep(node: Rel.Op.Exclude.Step, ctx: C): R - fun visitRelOpExcludeStepAttr(node: Rel.Op.Exclude.Step.Attr, ctx: C): R + fun visitRelOpExcludeStepStructField(node: Rel.Op.Exclude.Step.StructField, ctx: C): R - fun visitRelOpExcludeStepPos(node: Rel.Op.Exclude.Step.Pos, ctx: C): R + fun visitRelOpExcludeStepCollIndex(node: Rel.Op.Exclude.Step.CollIndex, ctx: C): R fun visitRelOpExcludeStepStructWildcard(node: Rel.Op.Exclude.Step.StructWildcard, ctx: C): R - fun visitRelOpExcludeStepCollectionWildcard( - node: Rel.Op.Exclude.Step.CollectionWildcard, - ctx: C, - ): R - - fun visitRelOpErr(node: Rel.Op.Err, ctx: C): R + fun visitRelOpExcludeStepCollWildcard(node: Rel.Op.Exclude.Step.CollWildcard, ctx: C): R fun visitRelOpErr(node: Rel.Op.Err, ctx: C): R fun visitRelBinding(node: Rel.Binding, ctx: C): R } 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 b52698db6..7f2ae50e4 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 @@ -352,22 +352,27 @@ internal object PlanTransform : PlanBaseVisitor() { items = node.items.map { visitRelOpExcludeItem(it, ctx) }, ) - override fun visitRelOpExcludeItem(node: Rel.Op.Exclude.Item, ctx: ProblemCallback) = - org.partiql.plan.Rel.Op.Exclude.Item( - root = visitIdentifierSymbol(node.root, ctx), + override fun visitRelOpExcludeItem(node: Rel.Op.Exclude.Item, ctx: ProblemCallback): org.partiql.plan.Rel.Op.Exclude.Item { + val root = when (node.root) { + is Rex.Op.Var.Resolved -> visitRexOpVar(node.root, ctx) as org.partiql.plan.Rex.Op.Var + is Rex.Op.Var.Unresolved -> org.partiql.plan.Rex.Op.Var(-1) // unresolved in `PlanTyper` results in error + } + return org.partiql.plan.Rel.Op.Exclude.Item( + root = root, steps = node.steps.map { visitRelOpExcludeStep(it, ctx) }, ) + } override fun visitRelOpExcludeStep(node: Rel.Op.Exclude.Step, ctx: ProblemCallback) = super.visit(node, ctx) as org.partiql.plan.Rel.Op.Exclude.Step - override fun visitRelOpExcludeStepAttr(node: Rel.Op.Exclude.Step.Attr, ctx: ProblemCallback) = - org.partiql.plan.Rel.Op.Exclude.Step.Attr( + override fun visitRelOpExcludeStepStructField(node: Rel.Op.Exclude.Step.StructField, ctx: ProblemCallback) = + org.partiql.plan.Rel.Op.Exclude.Step.StructField( symbol = visitIdentifierSymbol(node.symbol, ctx), ) - override fun visitRelOpExcludeStepPos(node: Rel.Op.Exclude.Step.Pos, ctx: ProblemCallback) = - org.partiql.plan.Rel.Op.Exclude.Step.Pos( + override fun visitRelOpExcludeStepCollIndex(node: Rel.Op.Exclude.Step.CollIndex, ctx: ProblemCallback) = + org.partiql.plan.Rel.Op.Exclude.Step.CollIndex( index = node.index, ) @@ -376,10 +381,10 @@ internal object PlanTransform : PlanBaseVisitor() { ctx: ProblemCallback, ) = org.partiql.plan.Rel.Op.Exclude.Step.StructWildcard() - override fun visitRelOpExcludeStepCollectionWildcard( - node: Rel.Op.Exclude.Step.CollectionWildcard, + override fun visitRelOpExcludeStepCollWildcard( + node: Rel.Op.Exclude.Step.CollWildcard, ctx: ProblemCallback, - ) = org.partiql.plan.Rel.Op.Exclude.Step.CollectionWildcard() + ) = org.partiql.plan.Rel.Op.Exclude.Step.CollWildcard() override fun visitRelOpErr(node: Rel.Op.Err, ctx: ProblemCallback) = org.partiql.plan.Rel.Op.Err(node.message) 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 09772acbf..73cba006f 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 @@ -43,9 +43,9 @@ import org.partiql.planner.internal.ir.relOpErr import org.partiql.planner.internal.ir.relOpExcept import org.partiql.planner.internal.ir.relOpExclude import org.partiql.planner.internal.ir.relOpExcludeItem -import org.partiql.planner.internal.ir.relOpExcludeStepAttr -import org.partiql.planner.internal.ir.relOpExcludeStepCollectionWildcard -import org.partiql.planner.internal.ir.relOpExcludeStepPos +import org.partiql.planner.internal.ir.relOpExcludeStepCollIndex +import org.partiql.planner.internal.ir.relOpExcludeStepCollWildcard +import org.partiql.planner.internal.ir.relOpExcludeStepStructField import org.partiql.planner.internal.ir.relOpExcludeStepStructWildcard import org.partiql.planner.internal.ir.relOpFilter import org.partiql.planner.internal.ir.relOpIntersect @@ -483,22 +483,22 @@ internal object RelConverter { return input } val type = input.type // PlanTyper handles typing the exclusion - val items = exclude.exprs.map { convertExcludeItem(it) } + val items = exclude.items.map { convertExcludeItem(it) } val op = relOpExclude(input, items) return rel(type, op) } - private fun convertExcludeItem(expr: Exclude.ExcludeExpr): Rel.Op.Exclude.Item { - val root = AstToPlan.convert(expr.root) + private fun convertExcludeItem(expr: Exclude.Item): Rel.Op.Exclude.Item { + val root = (expr.root.toRex(env)).op as Rex.Op.Var val steps = expr.steps.map { convertExcludeStep(it) } return relOpExcludeItem(root, steps) } private fun convertExcludeStep(step: Exclude.Step): Rel.Op.Exclude.Step = when (step) { - is Exclude.Step.ExcludeTupleAttr -> relOpExcludeStepAttr(AstToPlan.convert(step.symbol)) - is Exclude.Step.ExcludeCollectionIndex -> relOpExcludeStepPos(step.index) - is Exclude.Step.ExcludeCollectionWildcard -> relOpExcludeStepCollectionWildcard() - is Exclude.Step.ExcludeTupleWildcard -> relOpExcludeStepStructWildcard() + is Exclude.Step.StructField -> relOpExcludeStepStructField(AstToPlan.convert(step.symbol)) + is Exclude.Step.CollIndex -> relOpExcludeStepCollIndex(step.index) + is Exclude.Step.StructWildcard -> relOpExcludeStepStructWildcard() + is Exclude.Step.CollWildcard -> relOpExcludeStepCollWildcard() } // /** 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 10b56080b..12c9ab8a9 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 @@ -40,6 +40,8 @@ import org.partiql.planner.internal.ir.relOpAggregate import org.partiql.planner.internal.ir.relOpAggregateCall import org.partiql.planner.internal.ir.relOpDistinct import org.partiql.planner.internal.ir.relOpErr +import org.partiql.planner.internal.ir.relOpExclude +import org.partiql.planner.internal.ir.relOpExcludeItem import org.partiql.planner.internal.ir.relOpFilter import org.partiql.planner.internal.ir.relOpJoin import org.partiql.planner.internal.ir.relOpLimit @@ -353,7 +355,29 @@ internal class PlanTyper( // rewrite val type = ctx!!.copy(schema) - return rel(type, node) + + // resolve exclude path roots + val newItems = node.items.map { item -> + val resolvedRoot = when (val root = item.root) { + is Rex.Op.Var.Unresolved -> { + // resolve `root` to local binding + val bindingPath = root.identifier.toBindingPath() + when (val resolved = env.resolveLocalBind(bindingPath, init)) { + null -> { + handleUnresolvedExcludeRoot(root.identifier) + root + } + else -> rexOpVarResolved(resolved.ordinal) + } + } + is Rex.Op.Var.Resolved -> root + } + val steps = item.steps + relOpExcludeItem(resolvedRoot, steps) + } + + val op = relOpExclude(input, newItems) + return rel(type, op) } override fun visitRelOpAggregate(node: Rel.Op.Aggregate, ctx: Rel.Type?): Rel { @@ -1409,11 +1433,16 @@ internal class PlanTyper( ) } - private fun handleUnresolvedExcludeRoot(root: String) { + private fun handleUnresolvedExcludeRoot(root: Identifier) { onProblem( Problem( sourceLocation = UNKNOWN_PROBLEM_LOCATION, - details = PlanningProblemDetails.UnresolvedExcludeExprRoot(root) + details = PlanningProblemDetails.UnresolvedExcludeExprRoot( + when (root) { + is Identifier.Symbol -> root.symbol + is Identifier.Qualified -> root.toString() + } + ) ) ) } @@ -1473,16 +1502,26 @@ internal class PlanTyper( private fun excludeBindings(input: List, item: Rel.Op.Exclude.Item): List { var matchedRoot = false val output = input.map { - if (item.root.isEquivalentTo(it.name)) { - matchedRoot = true - // recompute the StaticType of this binding after apply the exclusions - val type = it.type.exclude(item.steps, false) - it.copy(type = type) - } else { - it + when (val root = item.root) { + is Rex.Op.Var.Unresolved -> { + when (val id = root.identifier) { + is Identifier.Symbol -> { + if (id.isEquivalentTo(it.name)) { + matchedRoot = true + // recompute the StaticType of this binding after apply the exclusions + val type = it.type.exclude(item.steps, false) + it.copy(type = type) + } else { + it + } + } + is Identifier.Qualified -> it + } + } + is Rex.Op.Var.Resolved -> it } } - if (!matchedRoot) handleUnresolvedExcludeRoot(item.root.symbol) + if (!matchedRoot && item.root is Rex.Op.Var.Unresolved) handleUnresolvedExcludeRoot(item.root.identifier) return output } 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 index 436311682..081f2a0cb 100644 --- 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 @@ -178,7 +178,7 @@ internal fun StructType.exclude(steps: List, lastStepOption StructType.Field(k, v) } when (step) { - is Rel.Op.Exclude.Step.Attr -> { + is Rel.Op.Exclude.Step.StructField -> { if (step.symbol.isEquivalentTo(field.key)) { newField } else { @@ -202,12 +202,12 @@ internal fun StructType.exclude(steps: List, lastStepOption internal fun CollectionType.exclude(steps: List, lastStepOptional: Boolean = true): StaticType { var e = this.elementType when (steps.first()) { - is Rel.Op.Exclude.Step.Pos -> { + is Rel.Op.Exclude.Step.CollIndex -> { if (steps.size > 1) { e = e.exclude(steps.drop(1), true) } } - is Rel.Op.Exclude.Step.CollectionWildcard -> { + is Rel.Op.Exclude.Step.CollWildcard -> { if (steps.size > 1) { e = e.exclude(steps.drop(1), lastStepOptional) }