Skip to content

Commit

Permalink
Adds operand concept for handling vararg inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
RCHowell committed Dec 5, 2024
1 parent 63c88d4 commit 1a0f6a9
Show file tree
Hide file tree
Showing 54 changed files with 356 additions and 227 deletions.
4 changes: 2 additions & 2 deletions docs/wiki/v1/compiler.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ I have labelled these children in the illustration so that you can see where the
RelLimit
/ \
x:<Value> RelOffset
/ \
y:<Value> z:<Rel>
/ \
y:<Value> z:<Rel>
```

The compiler will look for this pattern in the operator tree, and produce a match like so,
Expand Down
11 changes: 7 additions & 4 deletions partiql-eval/api/partiql-eval.api
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,8 @@ public abstract interface class org/partiql/eval/Statement {
}

public class org/partiql/eval/compiler/Match {
public fun <init> (Lorg/partiql/plan/Operator;Ljava/util/List;)V
public fun children (I)Ljava/util/List;
public fun operator (I)Lorg/partiql/plan/Operator;
public fun <init> (Lorg/partiql/plan/Operand;)V
public fun getOperand ()Lorg/partiql/plan/Operand;
}

public abstract interface class org/partiql/eval/compiler/PartiQLCompiler {
Expand All @@ -70,7 +69,11 @@ public class org/partiql/eval/compiler/Pattern {

public abstract class org/partiql/eval/compiler/Strategy {
public fun <init> (Lorg/partiql/eval/compiler/Pattern;)V
public abstract fun apply (Lorg/partiql/eval/compiler/Match;)Lorg/partiql/eval/Expr;
public abstract fun apply (Lorg/partiql/eval/compiler/Match;Lorg/partiql/eval/compiler/Strategy$Callback;)Lorg/partiql/eval/Expr;
public fun getPattern ()Lorg/partiql/eval/compiler/Pattern;
}

public abstract interface class org/partiql/eval/compiler/Strategy$Callback {
public abstract fun apply (Lorg/partiql/plan/Operator;)Lorg/partiql/eval/Expr;
}

39 changes: 10 additions & 29 deletions partiql-eval/src/main/java/org/partiql/eval/compiler/Match.java
Original file line number Diff line number Diff line change
@@ -1,50 +1,31 @@
package org.partiql.eval.compiler;

import org.jetbrains.annotations.NotNull;
import org.partiql.eval.Expr;
import org.partiql.plan.Operator;

import java.util.Collections;
import java.util.List;
import org.partiql.plan.Operand;

/**
* Match represents a subtree match to be sent to the
*/
public class Match {

private final Operator[] operators;
private final List<List<Expr>> children;
private final Operand[] operands;

/**
* Single operator match with zero-or-more inputs.
* Single operand match with zero-or-more inputs.
*
* @param operator matched logical operator.
* @param children compiled child operators.
* @param operand matched logical operand.
*/
public Match(@NotNull Operator operator, @NotNull List<Expr> children) {
this.operators = new Operator[]{operator};
this.children = Collections.singletonList(children);
}

/**
* Get the i-th operator (pre-order) matched by the pattern.
*
* @param i 0-indexed
* @return Operator
*/
@NotNull
public Operator operator(int i) {
return operators[i];
public Match(@NotNull Operand operand) {
this.operands = new Operand[]{operand};
}

/**
* Get the i-th input to this pattern.
* Get the first (or only) operand
*
* @param i 0-indexed
* @return Expr
* @return Operand
*/
@NotNull
public List<Expr> children(int i) {
return children.get(i);
public Operand getOperand() {
return operands[0];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,20 @@ public Pattern getPattern() {
* Applies the strategy to a logical plan operator and returns the physical operation (expr).
*
* @param match holds the matched operators
* @param callback for compiling arguments of matched operators
* @return the physical operation
*/
@NotNull
public abstract Expr apply(@NotNull Match match);
public abstract Expr apply(@NotNull Match match, @NotNull Callback callback);

/**
* A compilation callback for strategies to compile arguments of matched operators.
*/
public interface Callback {

/**
* @return a physical operator (expr) for the logical operator.
*/
Expr apply(Operator operator);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ 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.Operand
import org.partiql.plan.Operator
import org.partiql.plan.OperatorVisitor
import org.partiql.plan.Plan
Expand Down Expand Up @@ -170,25 +171,25 @@ internal class StandardCompiler(strategies: List<Strategy>) : PartiQLCompiler {
* @param operator
* @return
*/
private fun compileWithStrategies(operator: Operator, ctx: Unit): Expr {
private fun compileWithStrategies(operator: Operator): Expr {
// if strategy matches root, compile children to form a match.
for (strategy in strategies) {
// first match
if (strategy.pattern.matches(operator)) {
// compile children
val children = operator.getOperands().map { compileWithStrategies(it, ctx) }
val match = Match(operator, children)
return strategy.apply(match)
// assume single match
val operand = Operand.single(operator)
val match = Match(operand)
return strategy.apply(match, ::compileWithStrategies)
}
}
return operator.accept(this, Unit)
}

// TODO REMOVE ME
private fun compile(rel: Rel, ctx: Unit): ExprRelation = compileWithStrategies(rel, ctx) as ExprRelation
private fun compile(rel: Rel, ctx: Unit): ExprRelation = compileWithStrategies(rel) as ExprRelation

// TODO REMOVE ME
private fun compile(rex: Rex, ctx: Unit): ExprValue = compileWithStrategies(rex, ctx) as ExprValue
private fun compile(rex: Rex, ctx: Unit): ExprValue = compileWithStrategies(rex) as ExprValue

override fun defaultReturn(operator: Operator, ctx: Unit): Expr {
error("No compiler strategy matches the operator: ${operator::class.java.simpleName}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class StrategyTest {
var trigged = false
val pattern = Pattern(RelLimit::class.java)
val strategy = object : Strategy(pattern) {
override fun apply(match: Match): Expr {
override fun apply(match: Match, callback: Callback): Expr {
trigged = true
return MyLimit()
}
Expand Down
65 changes: 53 additions & 12 deletions partiql-plan/README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,73 @@
# partiql-plan

## Classes
This package holds logical plans which are composed of actions and operator trees.

* Operator
* Operator (tree node interface)
* Rel & RelBase
* Rex & RexBase

## Visitors

* OperatorVisitor
* OperatorRewriter

## Design

For the rule and strategy patterns to work, we need to model classes whose operands have a stable ordering;
so we have defined an abstract base class for all operators which holds operands and controls the access to them.
We use base implementations for state management and enforcing operands ordering; however we use interfaces for the
top-level of each domain. The abstract bases can be extended, and the operator/rel/rex interfaces can be implemented
directly.
## Operands

What are operands?

* Operands are an important concept
* An operand is an input to some operator.
* An operator may have more than one operand e.g. join (left and right).
* Rel operands are typically called "inputs"
* Operands unify inputs since PartiQL bridges rel/rex domains.
* Not all operators are operands e.g. the limit of RelLimit is a rex, but not an operand - it is an "arg"

| Operator | Operands | Arguments |
|-----------------|------------------|------------------------|
| RelAggregate | input | measures, groups |
| RelCorrelate | left, right | joinType |
| RelDistinct | input | |
| RelExcept | left, right | all |
| RelExclude | input | exclusions |
| RelFilter | input | predicate |
| RelIntersect | left, right | all |
| RelIterate | rex | |
| RelJoin | left, right | joinType, condition |
| RelLimit | input | limit |
| RelOffset | input | offset |
| RelProject | input | projections |
| RelScan | rex | |
| RelSort | input | collations |
| RelUnion | left, right | all |
| RelUnpivot | rex | |
| RexArray | values (vararg) | |
| RexBag | values (vararg) | |
| RexCall | args | |
| RexCase | match (optional) | branches, default |
| RexCast | operand | type |
| RexCoalesce | args (vararg) | |
| RexDispatch | args (vararg) | |
| RexLit | | value |
| RexNullIf | v1, v2 | |
| RexPathIndex | operand | index |
| RexPathKey | operand | key |
| RexPathSymbol | operand | symbol |
| RexPivot | input | key, value |
| RexSelect | input | constructor |
| RexSpread | args (vararg) | |
| RexStruct | | fields |
| RexSubquery | input | |
| RexSubqueryComp | input | comparison, quantifier |
| RexSubqueryIn | input | args |
| RexSubqueryTest | input | test |
| RexTable | | table |
| RexVar | | scope, offset |

## Design

For the rule and strategy patterns to work, we need to model classes whose operands have a stable ordering;
so we have defined an abstract base class for all operators which holds operands and controls the access to them.
We use base implementations for state management and enforcing operands ordering; however we use interfaces for the
top-level of each domain. The abstract bases can be extended, and the operator/rel/rex interfaces can be implemented
directly.

Why interfaces for top-level domain entities and not abstract base classes?

* We don’t want to force materialization of operands (consider wrapping a serde class)
Expand Down
25 changes: 20 additions & 5 deletions partiql-plan/api/partiql-plan.api
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,24 @@ public class org/partiql/plan/JoinType : org/partiql/spi/Enum {
public static fun RIGHT ()Lorg/partiql/plan/JoinType;
}

public abstract interface class org/partiql/plan/Operand : java/lang/Iterable {
public static fun single (Lorg/partiql/plan/Operator;)Lorg/partiql/plan/Operand;
public static fun vararg (Ljava/util/List;)Lorg/partiql/plan/Operand;
}

public class org/partiql/plan/Operand$Single : org/partiql/plan/Operand {
public final field operator Lorg/partiql/plan/Operator;
public fun iterator ()Ljava/util/Iterator;
}

public class org/partiql/plan/Operand$Variadic : org/partiql/plan/Operand {
public final field operators Ljava/util/List;
public fun iterator ()Ljava/util/Iterator;
}

public abstract interface class org/partiql/plan/Operator {
public abstract fun accept (Lorg/partiql/plan/OperatorVisitor;Ljava/lang/Object;)Ljava/lang/Object;
public abstract fun getOperand (I)Lorg/partiql/plan/Operator;
public abstract fun getOperand (I)Lorg/partiql/plan/Operand;
public abstract fun getOperands ()Ljava/util/List;
public abstract fun getTag ()I
public abstract fun setTag (I)V
Expand Down Expand Up @@ -408,7 +423,7 @@ public class org/partiql/plan/rel/RelAggregate$Measure {

public abstract class org/partiql/plan/rel/RelBase : org/partiql/plan/rel/Rel {
public fun <init> ()V
public final fun getOperand (I)Lorg/partiql/plan/Operator;
public final fun getOperand (I)Lorg/partiql/plan/Operand;
public final fun getOperands ()Ljava/util/List;
public fun getTag ()I
public final fun getType ()Lorg/partiql/plan/rel/RelType;
Expand Down Expand Up @@ -573,7 +588,7 @@ public abstract class org/partiql/plan/rel/RelSort : org/partiql/plan/rel/RelBas
protected final fun type ()Lorg/partiql/plan/rel/RelType;
}

public class org/partiql/plan/rel/RelType {
public final class org/partiql/plan/rel/RelType {
public static final field ORDERED I
public fun getDegree ()I
public fun getField (I)Lorg/partiql/types/Field;
Expand Down Expand Up @@ -632,7 +647,7 @@ public abstract class org/partiql/plan/rex/RexBag : org/partiql/plan/rex/RexBase

public abstract class org/partiql/plan/rex/RexBase : org/partiql/plan/rex/Rex {
public fun <init> ()V
public final fun getOperand (I)Lorg/partiql/plan/Operator;
public final fun getOperand (I)Lorg/partiql/plan/Operand;
public final fun getOperands ()Ljava/util/List;
public fun getTag ()I
public final fun getType ()Lorg/partiql/plan/rex/RexType;
Expand Down Expand Up @@ -887,7 +902,7 @@ public abstract class org/partiql/plan/rex/RexTable : org/partiql/plan/rex/RexBa
protected final fun type ()Lorg/partiql/plan/rex/RexType;
}

public class org/partiql/plan/rex/RexType {
public final class org/partiql/plan/rex/RexType {
public fun equals (Ljava/lang/Object;)Z
public fun getPType ()Lorg/partiql/types/PType;
public fun hashCode ()I
Expand Down
68 changes: 68 additions & 0 deletions partiql-plan/src/main/java/org/partiql/plan/Operand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.partiql.plan;

import org.jetbrains.annotations.NotNull;

import java.util.Iterator;
import java.util.List;

/**
* Operands in an operator tree used for strategy and rule pattern matching.
*/
public interface Operand extends Iterable<Operator> {

/**
* @return a single operand
*/
public static Operand single(Operator operator) {
return new Single(operator);
}

/**
* @return a variadic operand
*
* See ImmutableCollections.java ListCopy.
*/
@SuppressWarnings("unchecked")
public static Operand vararg(List<? extends Operator> operators) {
return new Variadic((List<Operator>) operators);
}

/**
* A single operator.
*/
public class Single implements Operand {

@NotNull
public final Operator operator;

private Single(@NotNull Operator operator) {
this.operator = operator;
}


@NotNull
@Override
public Iterator<Operator> iterator() {
return List.of(operator).iterator();
}
}

/**
* A variadic operator.
*/
public class Variadic implements Operand {

@NotNull
public final List<Operator> operators;

private Variadic(@NotNull List<Operator> operators) {
this.operators = operators;
}

@NotNull
@Override
public Iterator<Operator> iterator() {
return operators.iterator();
}
}
}
Loading

0 comments on commit 1a0f6a9

Please sign in to comment.