Skip to content
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

[V1] Address 1.0 partiql-plan feedback and cleanup #1665

Merged
merged 19 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -821,7 +821,7 @@ breaking changes if migrating from v0.9.2. The breaking changes accidentally int
`PlannerPipe.Builder.plannerEventCallback` for details.
- Adds the following optimization passes, none of which are enabled by default:
- `FilterScanToKeyLookupPass` which performs a simple optimization common to most databases: it converts a filter
predicate covering a table's complete primary key into a single get-by-key operation, thereby avoiding a full table
predicate covering a table's complete primary key into a single get-by-key action, thereby avoiding a full table
scan. This may pass leave behind some useless `and` expressions if more `and` operands exist in the filter predicate
other than primary key field equality expressions.
- `RemoveUselessAndsPass`, which removes any useless `and` expressions introduced by the previous pass or by the
Expand Down
4 changes: 2 additions & 2 deletions docs/wiki/design/Architecture Design.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ We can illustrate this technique with a simple integer evaluator. Consider the

```java
interface Operation {
/** Evaluates the operation against the given variables. */
/** Evaluates the action against the given variables. */
int eval(Map<String, Integer> env);
}
```
Expand Down Expand Up @@ -73,7 +73,7 @@ interface IntValue {
}

interface Operation {
/** Evaluates the operation against the given variables. */
/** Evaluates the action against the given variables. */
IntValue eval(Map<String, IntValue> env);
}
```
Expand Down
2 changes: 1 addition & 1 deletion docs/wiki/documentation/Exceptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ This is ​**not** a complete list. Also look at `org.partiql.lang.eval.Evaluati
| Expected 1 argument for `functionName` instead of `n of args` | -- | 1 argument function is called with wrong arity |
| Internal error, For input string: `string` | NumberFormatException | `CAST` from String is unsuccessful |
| / by zero | ArithmeticException | Division by zero |
| `Not enough or Too many` arguments | -- | Wrong operation arity, e.g.: `1+1+` |
| `Not enough or Too many` arguments | -- | Wrong action arity, e.g.: `1+1+` |

6 changes: 3 additions & 3 deletions docs/wiki/documentation/Functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ Header

Purpose
: Given a string, `s`, alter every upper case character in `s` to lower case. Any non-upper cased characters
remain unchanged. This operation does rely on the locale specified by the runtime configuration.
remain unchanged. This action does rely on the locale specified by the runtime configuration.
The implementation, currently, relies on Java's
[String.toLowerCase()](https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#toLowerCase()) documentation.

Expand Down Expand Up @@ -889,7 +889,7 @@ TO_STRING(`1969-07-20T20:18+08:00`, 'y-MM-dd''T''H:m:ssXXXXX') -- "1969-07-20T2

### TO_TIMESTAMP -- since v0.1.0

Given a string convert it to a timestamp. This is the inverse operation of [`TO_STRING`](#to_string)
Given a string convert it to a timestamp. This is the inverse action of [`TO_STRING`](#to_string)


Signature
Expand Down Expand Up @@ -995,7 +995,7 @@ Header

Purpose
: Given a string, `str`, alter every upper case character is `str` to lower case. Any non-lower cases characters remain
unchanged. This operation does rely on the locale specified by the runtime configuration.
unchanged. This action does rely on the locale specified by the runtime configuration.
The implementation, currently, relies on Java's
[String.toLowerCase()](https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#toLowerCase()) documentation.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ PartiQL also support `Positive Infinity, Negative Infinity, and NaN` as `FLOAT`

Unless otherwise specified, a mathematical operator:
1) takes a field name or expression of a numeric data type as operand
2) For Unary operation, the result type will be the same as the operand
3) For Binary operation, and the two operands are of the same type, the result will be the same as the type of operands.
4) For Binary operation, and the two operands are not of the same type, PartiQL will attempt to automatically coerce the operand.
2) For Unary action, the result type will be the same as the operand
rchowell marked this conversation as resolved.
Show resolved Hide resolved
3) For Binary action, and the two operands are of the same type, the result will be the same as the type of operands.
4) For Binary action, and the two operands are not of the same type, PartiQL will attempt to automatically coerce the operand.
5) If one or more operands are MISSING, then the result will be missing. else if one or more operands are null, then the result will be null.

### Overflow
With Type Inferencer and runtime type check enabled, Integer constraint will be honored and we check if the result of the mathematical operation exceeds the range that can be represented with the result type.
With Type Inferencer and runtime type check enabled, Integer constraint will be honored and we check if the result of the mathematical action exceeds the range that can be represented with the result type.

Without type inferencer and runtime type check, the default runtime integer representation is `INT8`, and overflow can still happen if the result exceed the range that can be represented with the `INT8` type.

Expand All @@ -32,16 +32,16 @@ When Permissive mode is enabled, overflowed values will be shown as `MISSING` in
### Conversion Map
Operators involving multiple argument data types, such as Integer + Float, the conversion map determines the datatype PartiQL uses. Decimal has the highest numeric precedence, followed by float, and finally by INT.

If either operand has type of Decimal, then PartiQL will attempt to convert the operands implicitly to Decimal before performing the operation.
If none of the operand has Decimal type but any of the operands is Float, then PartiQL will attempt to convert the operands implicitly to Float before performing the operation.
If either operand has type of Decimal, then PartiQL will attempt to convert the operands implicitly to Decimal before performing the action.
If none of the operand has Decimal type but any of the operands is Float, then PartiQL will attempt to convert the operands implicitly to Float before performing the action.

### HonorParameter
If precision and scale matter, i.e. doing operation on monetary value, make sure to turn on the honorTypedOpParameters() option in Compile Option.
If precision and scale matter, i.e. doing action on monetary value, make sure to turn on the honorTypedOpParameters() option in Compile Option.

The honorTypedOpParameters() determines how CAST and other typed operations behave. The default CompileOptions uses LEGACY which ignores the additional type arguments. Using the HONOR_PARAMETERS mode will take into account type parameters.

### Unary Plus:
Returns the operand without operation.
Returns the operand without action.

Syntax
: ` + expression`
Expand Down Expand Up @@ -209,7 +209,7 @@ Example
```

### Bitwise And
Performs a bitwise logical AND operation between two integer values.
Performs a bitwise logical AND action between two integer values.

Syntax
: `expression & expression`
Expand Down
2 changes: 1 addition & 1 deletion docs/wiki/documentation/Window Functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ LEAD(expr [, offset [, default]])
Purpose:
Returns the value from a binding tuple at a given offset *after* the current binding tuple position in the window partition.

Note that `Lag` and `Lead` perform similar operation and have similar semantics, except for the fact that `Lag` looks for `x` rows prior to the current row and `Lead` looks for `x` rows after.
Note that `Lag` and `Lead` perform similar action and have similar semantics, except for the fact that `Lag` looks for `x` rows prior to the current row and `Lead` looks for `x` rows after.

Arguments:
* expr:
Expand Down
2 changes: 1 addition & 1 deletion docs/wiki/upgrades/Rewriter to Visitor Transform Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ It's also worth noting that `innerRewriteDataManipulation` and the visitor trans
fun transformDataManipulationEvaluationOrder(node: PartiqlAst.Statement.Dml): PartiqlAst.Statement {
val from = node.from?.let { transformFromSource(it) }
val where = node.where?.let { transformStatementDml_where(node) }
val dmlOperation = transformDmlOp(node.operation)
val dmlOperation = transformDmlOp(node.action)
val metas = transformMetas(node.metas)

return PartiqlAst.build {
Expand Down
2 changes: 1 addition & 1 deletion partiql-ast/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ The Visitor pattern is the most widely misunderstood pattern in all of Design Pa

The trouble starts with terminology. The pattern isn’t about “visiting”, and the “accept” method in it doesn’t conjure up any helpful imagery either. Many think the pattern has to do with traversing trees, which isn’t the case at all. We are going to use it on a set of classes that are tree-like, but that’s a coincidence. As you’ll see, the pattern works as well on a single object.

The Visitor pattern is really about approximating the functional style within an OOP language. It lets us add new columns to that table easily. We can define all of the behavior for a new operation on a set of types in one place, without having to touch the types themselves. It does this the same way we solve almost every problem in computer science: by adding a layer of indirection.
The Visitor pattern is really about approximating the functional style within an OOP language. It lets us add new columns to that table easily. We can define all of the behavior for a new action on a set of types in one place, without having to touch the types themselves. It does this the same way we solve almost every problem in computer science: by adding a layer of indirection.

-- Robert Nystrom, Crafting Interpreters
____
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ import org.partiql.eval.internal.operator.rex.ExprSubquery
import org.partiql.eval.internal.operator.rex.ExprSubqueryRow
import org.partiql.eval.internal.operator.rex.ExprTable
import org.partiql.eval.internal.operator.rex.ExprVar
import org.partiql.plan.Action
import org.partiql.plan.Collation
import org.partiql.plan.JoinType
import org.partiql.plan.Operation
import org.partiql.plan.Operator
import org.partiql.plan.OperatorVisitor
import org.partiql.plan.Plan
import org.partiql.plan.Visitor
import org.partiql.plan.rel.Rel
import org.partiql.plan.rel.RelAggregate
import org.partiql.plan.rel.RelDistinct
Expand All @@ -85,10 +85,10 @@ import org.partiql.plan.rex.Rex
import org.partiql.plan.rex.RexArray
import org.partiql.plan.rex.RexBag
import org.partiql.plan.rex.RexCall
import org.partiql.plan.rex.RexCallDynamic
import org.partiql.plan.rex.RexCase
import org.partiql.plan.rex.RexCast
import org.partiql.plan.rex.RexCoalesce
import org.partiql.plan.rex.RexDispatch
import org.partiql.plan.rex.RexError
import org.partiql.plan.rex.RexLit
import org.partiql.plan.rex.RexNullIf
Expand Down Expand Up @@ -123,10 +123,13 @@ internal class StandardCompiler(strategies: List<Strategy>) : PartiQLCompiler {

override fun prepare(plan: Plan, mode: Mode, ctx: Context): Statement {
try {
val visitor = _Visitor(mode)
val operation = plan.getOperation()
val visitor = _Operator_Visitor(mode)
if (plan.actions.size != 1) {
throw IllegalArgumentException("Only single actions are supported")
}
val operation = plan.actions[0]
val statement: Statement = when {
operation is Operation.Query -> visitor.compile(operation)
operation is Action.Query -> visitor.compile(operation)
else -> throw IllegalArgumentException("Only query statements are supported")
}
return statement
Expand All @@ -143,17 +146,17 @@ internal class StandardCompiler(strategies: List<Strategy>) : PartiQLCompiler {
* Transforms plan relation operators into the internal physical operators.
*/
@Suppress("ClassName")
private inner class _Visitor(mode: Mode) : Visitor<Expr, Unit> {
private inner class _Operator_Visitor(mode: Mode) : OperatorVisitor<Expr, Unit> {

private val mode = mode.code()

/**
* Compile a query operation to a query statement.
*/
fun compile(operation: Operation.Query) = object : Statement {
fun compile(action: Action.Query) = object : Statement {

// compile the query root
private val root = compile(operation.getRex(), Unit).catch()
private val root = compile(action.getRex(), Unit).catch()

// execute with no parameters
override fun execute(): Datum = root.eval(Environment())
Expand All @@ -171,9 +174,9 @@ internal class StandardCompiler(strategies: List<Strategy>) : PartiQLCompiler {
// if strategy matches root, compile children to form a match.
for (strategy in strategies) {
// first match
if (strategy.getPattern().matches(operator)) {
if (strategy.pattern.matches(operator)) {
// compile children
val children = operator.getChildren().map { compileWithStrategies(it, ctx) }
val children = operator.getOperands().map { compileWithStrategies(it, ctx) }
val match = Match(operator, children)
return strategy.apply(match)
}
Expand All @@ -195,7 +198,7 @@ internal class StandardCompiler(strategies: List<Strategy>) : PartiQLCompiler {

override fun visitAggregate(rel: RelAggregate, ctx: Unit): ExprRelation {
val input = compile(rel.getInput(), ctx)
val aggs = rel.getCalls().map { call ->
val aggs = rel.getMeasures().map { call ->
val agg = call.getAgg()
val args = call.getArgs().map { compile(it, ctx).catch() }
val distinct = call.isDistinct()
Expand Down Expand Up @@ -241,7 +244,7 @@ internal class StandardCompiler(strategies: List<Strategy>) : PartiQLCompiler {
}

override fun visitIterate(rel: RelIterate, ctx: Unit): ExprRelation {
val input = compile(rel.getInput(), ctx)
val input = compile(rel.getRex(), ctx)
return when (mode) {
Mode.PERMISSIVE -> RelOpIteratePermissive(input)
Mode.STRICT -> RelOpIterate(input)
Expand All @@ -250,19 +253,20 @@ internal class StandardCompiler(strategies: List<Strategy>) : PartiQLCompiler {
}

override fun visitJoin(rel: RelJoin, ctx: Unit): ExprRelation {
val lhs = compile(rel.getLeft(), ctx)
val rhs = compile(rel.getRight(), ctx)
val condition = rel.getCondition()?.let { compile(it, ctx) } ?: ExprLit(Datum.bool(true))

// TODO JOIN SCHEMAS
val lhsType = rel.getLeftSchema()
val rhsType = rel.getRightSchema()

return when (rel.getJoinType()) {
val lrel = rel.left
val rrel = rel.right
val lhs = compile(lrel, ctx)
val rhs = compile(rrel, ctx)
val condition = compile(rel.getCondition(), ctx)
// use schema for null padding
val lhsType = lrel.type
val rhsType = rrel.type
return when (rel.joinType.code()) {
JoinType.INNER -> RelOpJoinInner(lhs, rhs, condition)
JoinType.LEFT -> RelOpJoinOuterLeft(lhs, rhs, condition, rhsType!!)
JoinType.RIGHT -> RelOpJoinOuterRight(lhs, rhs, condition, lhsType!!)
JoinType.FULL -> RelOpJoinOuterFull(lhs, rhs, condition, lhsType!!, rhsType!!)
JoinType.LEFT -> RelOpJoinOuterLeft(lhs, rhs, condition, rhsType)
JoinType.RIGHT -> RelOpJoinOuterRight(lhs, rhs, condition, lhsType)
JoinType.FULL -> RelOpJoinOuterFull(lhs, rhs, condition, lhsType, rhsType)
else -> error("Unsupported join type: ${rel.joinType}")
}
}

Expand All @@ -285,7 +289,7 @@ internal class StandardCompiler(strategies: List<Strategy>) : PartiQLCompiler {
}

override fun visitScan(rel: RelScan, ctx: Unit): ExprRelation {
val input = compile(rel.getInput(), ctx)
val input = compile(rel.rex, ctx)
return when (mode) {
Mode.PERMISSIVE -> RelOpScanPermissive(input)
Mode.STRICT -> RelOpScan(input)
Expand All @@ -296,9 +300,9 @@ internal class StandardCompiler(strategies: List<Strategy>) : PartiQLCompiler {
override fun visitSort(rel: RelSort, ctx: Unit): ExprRelation {
val input = compile(rel.getInput(), ctx)
val collations = rel.getCollations().map {
val expr = compile(it.getRex(), ctx)
val desc = it.getOrder() == Collation.Order.DESC
val last = it.getNulls() == Collation.Nulls.LAST
val expr = compile(it.column, ctx)
val desc = it.order.code() == Collation.Order.DESC
val last = it.nulls.code() == Collation.Nulls.LAST
RelOpSort.Collation(expr, desc, last)
}
return RelOpSort(input, collations)
Expand All @@ -314,7 +318,7 @@ internal class StandardCompiler(strategies: List<Strategy>) : PartiQLCompiler {
}

override fun visitUnpivot(rel: RelUnpivot, ctx: Unit): ExprRelation {
val input = compile(rel.getInput(), ctx)
val input = compile(rel.rex, ctx)
return when (mode) {
Mode.PERMISSIVE -> RelOpUnpivot.Permissive(input)
Mode.STRICT -> RelOpUnpivot.Strict(input)
Expand All @@ -338,7 +342,7 @@ internal class StandardCompiler(strategies: List<Strategy>) : PartiQLCompiler {
return ExprBag(values)
}

override fun visitCallDynamic(rex: RexCallDynamic, ctx: Unit): ExprValue {
override fun visitDispatch(rex: RexDispatch, ctx: Unit): ExprValue {
// Check candidate arity for uniformity
var arity: Int = -1
val name = rex.getName()
Expand Down Expand Up @@ -401,7 +405,7 @@ internal class StandardCompiler(strategies: List<Strategy>) : PartiQLCompiler {
}

override fun visitLit(rex: RexLit, ctx: Unit): ExprValue {
return ExprLit(rex.getValue())
return ExprLit(rex.getDatum())
}

override fun visitNullIf(rex: RexNullIf, ctx: Unit): ExprValue {
Expand Down Expand Up @@ -442,14 +446,14 @@ internal class StandardCompiler(strategies: List<Strategy>) : PartiQLCompiler {
override fun visitSelect(rex: RexSelect, ctx: Unit): ExprValue {
val input = compile(rex.getInput(), ctx)
val constructor = compile(rex.getConstructor(), ctx).catch()
val ordered = rex.getInput().isOrdered()
val ordered = rex.getInput().type.isOrdered
return ExprSelect(input, constructor, ordered)
}

override fun visitStruct(rex: RexStruct, ctx: Unit): ExprValue {
val fields = rex.getFields().map {
val k = compile(it.getKey(), ctx)
val v = compile(it.getValue(), ctx).catch()
val k = compile(it.key, ctx)
val v = compile(it.value, ctx).catch()
ExprStructField(k, v)
}
return when (mode) {
Expand All @@ -460,9 +464,9 @@ internal class StandardCompiler(strategies: List<Strategy>) : PartiQLCompiler {
}

override fun visitSubquery(rex: RexSubquery, ctx: Unit): ExprValue {
val rel = compile(rex.getRel(), ctx)
val rel = compile(rex.getInput(), ctx)
val constructor = compile(rex.getConstructor(), ctx)
return when (rex.asScalar()) {
return when (rex.isScalar()) {
true -> ExprSubquery(rel, constructor)
else -> ExprSubqueryRow(rel, constructor)
}
Expand Down Expand Up @@ -490,9 +494,9 @@ internal class StandardCompiler(strategies: List<Strategy>) : PartiQLCompiler {
}

override fun visitVar(rex: RexVar, ctx: Unit): ExprValue {
val depth = rex.getDepth()
val scope = rex.scope
val offset = rex.getOffset()
return ExprVar(depth, offset)
return ExprVar(scope, offset)
}

/**
Expand Down
Loading
Loading