Skip to content

Simplifies logical model of the path expression #1306

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 14 additions & 20 deletions partiql-plan/src/main/resources/partiql_plan.ion
Original file line number Diff line number Diff line change
Expand Up @@ -73,30 +73,24 @@ rex::{
ref: int,
},

path::{
root: rex,
steps: list::[step],
_: [
step::[
// The key MUST be an integer expression. Ex: a[0], a[1 + 1]
index::{ key: rex },
path::[
// The key MUST be an integer expression. Ex: a[0], a[1 + 1]
index::{ root: rex, key: rex },

// Case-sensitive lookup. The key MUST be a string expression. Ex: a["b"], a."b", a[CAST(b AS STRING)]
key::{ key: rex },
// Case-sensitive lookup. The key MUST be a string expression. Ex: a["b"], a."b", a[CAST(b AS STRING)]
key::{ root: rex, key: rex },

// Case-insensitive lookup. The key MUST be a literal string. Ex: a.b
symbol::{ key: string },
// Case-insensitive lookup. The key MUST be a literal string. Ex: a.b
symbol::{ root: rex, key: string },

// For arrays. Ex: a[*]
// TODO: Do we need this? According to specification: [1,2,3][*] ⇔ SELECT VALUE v FROM [1, 2, 3] AS v
wildcard::{},
// For arrays. Ex: a[*]
// TODO: Do we need this? According to specification: [1,2,3][*] ⇔ SELECT VALUE v FROM [1, 2, 3] AS v
wildcard::{ root: rex },

// For tuples. Ex: a.*
// TODO: Do we need this? According to specification: {'a':1, 'b':2}.* ⇔ SELECT VALUE v FROM UNPIVOT {'a':1, 'b':2} AS v
unpivot::{},
],
],
},
// For tuples. Ex: a.*
// TODO: Do we need this? According to specification: {'a':1, 'b':2}.* ⇔ SELECT VALUE v FROM UNPIVOT {'a':1, 'b':2} AS v
unpivot::{ root: rex },
],

call::[
static::{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,13 @@ internal class TypeEnv(

/**
* Metadata regarding a resolved variable.
* @property depth The depth/level of the path match.
*/
internal sealed interface ResolvedVar {

public val type: StaticType
public val ordinal: Int
public val depth: Int

/**
* Metadata for a resolved local variable.
Expand All @@ -88,7 +90,7 @@ internal sealed interface ResolvedVar {
override val ordinal: Int,
val rootType: StaticType,
val replacementSteps: List<BindingName>,
val depth: Int
override val depth: Int
) : ResolvedVar

/**
Expand All @@ -101,7 +103,7 @@ internal sealed interface ResolvedVar {
class Global(
override val type: StaticType,
override val ordinal: Int,
val depth: Int,
override val depth: Int,
) : ResolvedVar
}

Expand Down
211 changes: 93 additions & 118 deletions partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/Nodes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

package org.partiql.planner.internal.ir

import org.partiql.plan.builder.RexOpPathIndexBuilder
import org.partiql.plan.builder.RexOpPathKeyBuilder
import org.partiql.plan.builder.RexOpPathSymbolBuilder
import org.partiql.plan.builder.RexOpPathUnpivotBuilder
import org.partiql.plan.builder.RexOpPathWildcardBuilder
import org.partiql.planner.internal.ir.builder.AggResolvedBuilder
import org.partiql.planner.internal.ir.builder.AggUnresolvedBuilder
import org.partiql.planner.internal.ir.builder.FnResolvedBuilder
Expand Down Expand Up @@ -46,11 +51,6 @@ import org.partiql.planner.internal.ir.builder.RexOpCollectionBuilder
import org.partiql.planner.internal.ir.builder.RexOpErrBuilder
import org.partiql.planner.internal.ir.builder.RexOpGlobalBuilder
import org.partiql.planner.internal.ir.builder.RexOpLitBuilder
import org.partiql.planner.internal.ir.builder.RexOpPathBuilder
import org.partiql.planner.internal.ir.builder.RexOpPathStepIndexBuilder
import org.partiql.planner.internal.ir.builder.RexOpPathStepSymbolBuilder
import org.partiql.planner.internal.ir.builder.RexOpPathStepUnpivotBuilder
import org.partiql.planner.internal.ir.builder.RexOpPathStepWildcardBuilder
import org.partiql.planner.internal.ir.builder.RexOpPivotBuilder
import org.partiql.planner.internal.ir.builder.RexOpSelectBuilder
import org.partiql.planner.internal.ir.builder.RexOpStructBuilder
Expand Down Expand Up @@ -393,142 +393,117 @@ internal data class Rex(
}
}

internal data class Path(
@JvmField
internal val root: Rex,
@JvmField
internal val steps: List<Step>,
) : Op() {
internal override val children: List<PlanNode> by lazy {
val kids = mutableListOf<PlanNode?>()
kids.add(root)
kids.addAll(steps)
kids.filterNotNull()
public sealed class Path : Op() {
public override fun <R, C> accept(visitor: PlanVisitor<R, C>, ctx: C): R = when (this) {
is Index -> visitor.visitRexOpPathIndex(this, ctx)
is Key -> visitor.visitRexOpPathKey(this, ctx)
is Symbol -> visitor.visitRexOpPathSymbol(this, ctx)
is Wildcard -> visitor.visitRexOpPathWildcard(this, ctx)
is Unpivot -> visitor.visitRexOpPathUnpivot(this, ctx)
}

internal override fun <R, C> accept(visitor: PlanVisitor<R, C>, ctx: C): R =
visitor.visitRexOpPath(this, ctx)

internal sealed class Step : PlanNode() {
internal override fun <R, C> accept(visitor: PlanVisitor<R, C>, ctx: C): R = when (this) {
is Index -> visitor.visitRexOpPathStepIndex(this, ctx)
is Symbol -> visitor.visitRexOpPathStepSymbol(this, ctx)
is Wildcard -> visitor.visitRexOpPathStepWildcard(this, ctx)
is Unpivot -> visitor.visitRexOpPathStepUnpivot(this, ctx)
is Key -> visitor.visitRexOpPathStepKey(this, ctx)
public data class Index(
@JvmField
public val root: Rex,
@JvmField
public val key: Rex,
) : Path() {
public override val children: List<PlanNode> by lazy {
val kids = mutableListOf<PlanNode?>()
kids.add(root)
kids.add(key)
kids.filterNotNull()
}

internal data class Index(
@JvmField
internal val key: Rex,
) : Step() {
internal override val children: List<PlanNode> by lazy {
val kids = mutableListOf<PlanNode?>()
kids.add(key)
kids.filterNotNull()
}

internal override fun <R, C> accept(visitor: PlanVisitor<R, C>, ctx: C): R =
visitor.visitRexOpPathStepIndex(this, ctx)
public override fun <R, C> accept(visitor: PlanVisitor<R, C>, ctx: C): R =
visitor.visitRexOpPathIndex(this, ctx)

internal companion object {
@JvmStatic
internal fun builder(): RexOpPathStepIndexBuilder = RexOpPathStepIndexBuilder()
}
public companion object {
@JvmStatic
public fun builder(): RexOpPathIndexBuilder = RexOpPathIndexBuilder()
}
}

/**
* This represents a case-sensitive lookup on a tuple. Ex: a['b'] or a[CAST('a' || 'b' AS STRING)].
* This would normally contain the dot notation for case-sensitive lookup, however, due to
* limitations -- we cannot consolidate these. See [Symbol] for more information.
*
* The main difference is that this does NOT include `a."b"`
*/
internal data class Key(
@JvmField
internal val key: Rex,
) : Step() {
internal override val children: List<PlanNode> by lazy {
val kids = mutableListOf<PlanNode?>()
kids.add(key)
kids.filterNotNull()
}
public data class Key(
@JvmField
public val root: Rex,
@JvmField
public val key: Rex,
) : Path() {
public override val children: List<PlanNode> by lazy {
val kids = mutableListOf<PlanNode?>()
kids.add(root)
kids.add(key)
kids.filterNotNull()
}

internal override fun <R, C> accept(visitor: PlanVisitor<R, C>, ctx: C): R =
visitor.visitRexOpPathStepKey(this, ctx)
public override fun <R, C> accept(visitor: PlanVisitor<R, C>, ctx: C): R =
visitor.visitRexOpPathKey(this, ctx)

internal companion object {
@JvmStatic
internal fun builder(): RexOpPathStepIndexBuilder = RexOpPathStepIndexBuilder()
}
public companion object {
@JvmStatic
public fun builder(): RexOpPathKeyBuilder = RexOpPathKeyBuilder()
}
}

/**
* This represents a lookup on a tuple. We differentiate a [Key] and a [Symbol] at this point in the
* pipeline because we NEED to retain some syntactic knowledge for the following reason: we cannot
* use the syntactic index operation on a schema -- as it is not synonymous with a tuple. In other words,
* `<schema-name>."<value-name>"` is not interchangeable with `<schema-name>['<value-name>']`.
*
* So, in order to temporarily differentiate the `a."b"` from `a['b']` (see [Key]), we need to maintain
* the syntactic difference here. Note that this would potentially be mitigated by typing during the AST to Plan
* transformation.
*
* That being said, this represents a lookup on a tuple such as `a.b` or `a."b"`.
*/
internal data class Symbol(
@JvmField
internal val identifier: Identifier.Symbol,
) : Step() {
internal override val children: List<PlanNode> by lazy {
val kids = mutableListOf<PlanNode?>()
kids.add(identifier)
kids.filterNotNull()
}
public data class Symbol(
@JvmField
public val root: Rex,
@JvmField
public val key: String,
) : Path() {
public override val children: List<PlanNode> by lazy {
val kids = mutableListOf<PlanNode?>()
kids.add(root)
kids.filterNotNull()
}

internal override fun <R, C> accept(visitor: PlanVisitor<R, C>, ctx: C): R =
visitor.visitRexOpPathStepSymbol(this, ctx)
public override fun <R, C> accept(visitor: PlanVisitor<R, C>, ctx: C): R =
visitor.visitRexOpPathSymbol(this, ctx)

internal companion object {
@JvmStatic
internal fun builder(): RexOpPathStepSymbolBuilder = RexOpPathStepSymbolBuilder()
}
public companion object {
@JvmStatic
public fun builder(): RexOpPathSymbolBuilder = RexOpPathSymbolBuilder()
}
}

internal data class Wildcard(
@JvmField
internal val ` `: Char = ' ',
) : Step() {
internal override val children: List<PlanNode> = emptyList()
public data class Wildcard(
@JvmField
public val root: Rex,
) : Path() {
public override val children: List<PlanNode> by lazy {
val kids = mutableListOf<PlanNode?>()
kids.add(root)
kids.filterNotNull()
}

internal override fun <R, C> accept(visitor: PlanVisitor<R, C>, ctx: C): R =
visitor.visitRexOpPathStepWildcard(this, ctx)
public override fun <R, C> accept(visitor: PlanVisitor<R, C>, ctx: C): R =
visitor.visitRexOpPathWildcard(this, ctx)

internal companion object {
@JvmStatic
internal fun builder(): RexOpPathStepWildcardBuilder = RexOpPathStepWildcardBuilder()
}
public companion object {
@JvmStatic
public fun builder(): RexOpPathWildcardBuilder = RexOpPathWildcardBuilder()
}
}

internal data class Unpivot(
@JvmField
internal val ` `: Char = ' ',
) : Step() {
internal override val children: List<PlanNode> = emptyList()
public data class Unpivot(
@JvmField
public val root: Rex,
) : Path() {
public override val children: List<PlanNode> by lazy {
val kids = mutableListOf<PlanNode?>()
kids.add(root)
kids.filterNotNull()
}

internal override fun <R, C> accept(visitor: PlanVisitor<R, C>, ctx: C): R =
visitor.visitRexOpPathStepUnpivot(this, ctx)
public override fun <R, C> accept(visitor: PlanVisitor<R, C>, ctx: C): R =
visitor.visitRexOpPathUnpivot(this, ctx)

internal companion object {
@JvmStatic
internal fun builder(): RexOpPathStepUnpivotBuilder = RexOpPathStepUnpivotBuilder()
}
public companion object {
@JvmStatic
public fun builder(): RexOpPathUnpivotBuilder = RexOpPathUnpivotBuilder()
}
}

internal companion object {
@JvmStatic
internal fun builder(): RexOpPathBuilder = RexOpPathBuilder()
}
}

internal sealed class Call : Op() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,18 @@ internal fun rexOpVarUnresolved(identifier: Identifier, scope: Rex.Op.Var.Scope)

internal fun rexOpGlobal(ref: Int): Rex.Op.Global = Rex.Op.Global(ref)

internal fun rexOpPath(root: Rex, steps: List<Rex.Op.Path.Step>): Rex.Op.Path = Rex.Op.Path(
root,
steps
)

internal fun rexOpPathStepIndex(key: Rex): Rex.Op.Path.Step.Index = Rex.Op.Path.Step.Index(key)
internal fun rexOpPathIndex(root: Rex, key: Rex): Rex.Op.Path.Index = Rex.Op.Path.Index(root, key)

internal fun rexOpPathStepKey(key: Rex): Rex.Op.Path.Step.Key = Rex.Op.Path.Step.Key(key)
internal fun rexOpPathKey(root: Rex, key: Rex): Rex.Op.Path.Key = Rex.Op.Path.Key(root, key)

internal fun rexOpPathStepSymbol(identifier: Identifier.Symbol): Rex.Op.Path.Step.Symbol =
Rex.Op.Path.Step.Symbol(identifier)
internal fun rexOpPathSymbol(root: Rex, key: String): Rex.Op.Path.Symbol = Rex.Op.Path.Symbol(
root,
key
)

internal fun rexOpPathStepWildcard(): Rex.Op.Path.Step.Wildcard = Rex.Op.Path.Step.Wildcard()
internal fun rexOpPathWildcard(root: Rex): Rex.Op.Path.Wildcard = Rex.Op.Path.Wildcard(root)

internal fun rexOpPathStepUnpivot(): Rex.Op.Path.Step.Unpivot = Rex.Op.Path.Step.Unpivot()
internal fun rexOpPathUnpivot(root: Rex): Rex.Op.Path.Unpivot = Rex.Op.Path.Unpivot(root)

internal fun rexOpCallStatic(fn: Fn, args: List<Rex>): Rex.Op.Call.Static = Rex.Op.Call.Static(
fn,
Expand Down
Loading