This is an automated email from the ASF dual-hosted git repository.
danny0405 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/master by this push:
new c98171da57 [CALCITE-5107] Support SQL hint for Filter, SetOp, Sort,
Window, Values
c98171da57 is described below
commit c98171da5790d3d5ee7d2eb48a21d0080d0d68f3
Author: godfreyhe <[email protected]>
AuthorDate: Thu Apr 21 18:58:08 2022 +0800
[CALCITE-5107] Support SQL hint for Filter, SetOp, Sort, Window, Values
close apache/calcite#2775
---
.../java/org/apache/calcite/rel/core/Filter.java | 35 +++++-
.../org/apache/calcite/rel/core/Intersect.java | 16 ++-
.../java/org/apache/calcite/rel/core/Minus.java | 10 +-
.../java/org/apache/calcite/rel/core/SetOp.java | 25 ++++-
.../java/org/apache/calcite/rel/core/Sort.java | 60 +++++++---
.../java/org/apache/calcite/rel/core/Union.java | 13 ++-
.../java/org/apache/calcite/rel/core/Values.java | 36 +++++-
.../java/org/apache/calcite/rel/core/Window.java | 31 +++++-
.../apache/calcite/rel/hint/HintPredicates.java | 25 +++++
.../calcite/rel/hint/NodeTypeHintPredicate.java | 32 +++++-
.../apache/calcite/rel/logical/LogicalFilter.java | 33 +++++-
.../calcite/rel/logical/LogicalIntersect.java | 24 +++-
.../apache/calcite/rel/logical/LogicalMinus.java | 20 +++-
.../apache/calcite/rel/logical/LogicalSort.java | 20 +++-
.../apache/calcite/rel/logical/LogicalUnion.java | 23 +++-
.../apache/calcite/rel/logical/LogicalValues.java | 29 ++++-
.../apache/calcite/rel/logical/LogicalWindow.java | 31 +++++-
.../org/apache/calcite/test/RelBuilderTest.java | 71 ++++++++++--
.../apache/calcite/test/SqlHintsConverterTest.java | 124 ++++++++++++++++++++-
.../apache/calcite/test/SqlHintsConverterTest.xml | 94 +++++++++++++++-
20 files changed, 696 insertions(+), 56 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Filter.java
b/core/src/main/java/org/apache/calcite/rel/core/Filter.java
index ce4e0c0ec6..dbe44e4df0 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Filter.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Filter.java
@@ -24,6 +24,8 @@ import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelWriter;
import org.apache.calcite.rel.SingleRel;
+import org.apache.calcite.rel.hint.Hintable;
+import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rel.metadata.RelMdUtil;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rex.RexChecker;
@@ -34,6 +36,8 @@ import org.apache.calcite.rex.RexShuttle;
import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.util.Litmus;
+import com.google.common.collect.ImmutableList;
+
import org.apiguardian.api.API;
import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -53,11 +57,13 @@ import static java.util.Objects.requireNonNull;
*
* @see org.apache.calcite.rel.logical.LogicalFilter
*/
-public abstract class Filter extends SingleRel {
+public abstract class Filter extends SingleRel implements Hintable {
//~ Instance fields --------------------------------------------------------
protected final RexNode condition;
+ protected final ImmutableList<RelHint> hints;
+
//~ Constructors -----------------------------------------------------------
/**
@@ -65,6 +71,7 @@ public abstract class Filter extends SingleRel {
*
* @param cluster Cluster that this relational expression belongs to
* @param traits the traits of this rel
+ * @param hints Hints for this node
* @param child input relational expression
* @param condition boolean expression which determines whether a row is
* allowed to pass
@@ -73,12 +80,31 @@ public abstract class Filter extends SingleRel {
protected Filter(
RelOptCluster cluster,
RelTraitSet traits,
+ List<RelHint> hints,
RelNode child,
RexNode condition) {
super(cluster, traits, child);
this.condition = requireNonNull(condition, "condition");
assert RexUtil.isFlat(condition) : "RexUtil.isFlat should be true for
condition " + condition;
assert isValid(Litmus.THROW, null);
+ this.hints = ImmutableList.copyOf(hints);
+ }
+
+ /**
+ * Creates a filter.
+ *
+ * @param cluster Cluster that this relational expression belongs to
+ * @param traits the traits of this rel
+ * @param child input relational expression
+ * @param condition boolean expression which determines whether a row is
+ * allowed to pass
+ */
+ protected Filter(
+ RelOptCluster cluster,
+ RelTraitSet traits,
+ RelNode child,
+ RexNode condition) {
+ this(cluster, traits, ImmutableList.of(), child, condition);
}
/**
@@ -169,6 +195,7 @@ public abstract class Filter extends SingleRel {
}
Filter o = (Filter) obj;
return traitSet.equals(o.traitSet)
+ && hints.equals(o.hints)
&& input.deepEquals(o.input)
&& condition.equals(o.condition)
&& getRowType().equalsSansFieldNames(o.getRowType());
@@ -176,6 +203,10 @@ public abstract class Filter extends SingleRel {
@API(since = "1.24", status = API.Status.INTERNAL)
protected int deepHashCode0() {
- return Objects.hash(traitSet, input.deepHashCode(), condition);
+ return Objects.hash(traitSet, hints, input.deepHashCode(), condition);
+ }
+
+ @Override public ImmutableList<RelHint> getHints() {
+ return hints;
}
}
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Intersect.java
b/core/src/main/java/org/apache/calcite/rel/core/Intersect.java
index c159353dad..e6ea2f6c24 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Intersect.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Intersect.java
@@ -20,9 +20,11 @@ import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.sql.SqlKind;
+import java.util.Collections;
import java.util.List;
/**
@@ -33,6 +35,18 @@ import java.util.List;
* performs set set intersection (implying no duplicates in the results).
*/
public abstract class Intersect extends SetOp {
+ /**
+ * Creates an Intersect.
+ */
+ public Intersect(
+ RelOptCluster cluster,
+ RelTraitSet traits,
+ List<RelHint> hints,
+ List<RelNode> inputs,
+ boolean all) {
+ super(cluster, traits, hints, inputs, SqlKind.INTERSECT, all);
+ }
+
/**
* Creates an Intersect.
*/
@@ -41,7 +55,7 @@ public abstract class Intersect extends SetOp {
RelTraitSet traits,
List<RelNode> inputs,
boolean all) {
- super(cluster, traits, inputs, SqlKind.INTERSECT, all);
+ this(cluster, traits, Collections.emptyList(), inputs, all);
}
/**
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Minus.java
b/core/src/main/java/org/apache/calcite/rel/core/Minus.java
index 099443b458..3a68945acc 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Minus.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Minus.java
@@ -20,10 +20,12 @@ import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rel.metadata.RelMdUtil;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.sql.SqlKind;
+import java.util.Collections;
import java.util.List;
/**
@@ -37,9 +39,15 @@ import java.util.List;
* the results).
*/
public abstract class Minus extends SetOp {
+
+ public Minus(RelOptCluster cluster, RelTraitSet traits, List<RelHint> hints,
+ List<RelNode> inputs, boolean all) {
+ super(cluster, traits, hints, inputs, SqlKind.EXCEPT, all);
+ }
+
protected Minus(RelOptCluster cluster, RelTraitSet traits, List<RelNode>
inputs,
boolean all) {
- super(cluster, traits, inputs, SqlKind.EXCEPT, all);
+ this(cluster, traits, Collections.emptyList(), inputs, all);
}
/**
diff --git a/core/src/main/java/org/apache/calcite/rel/core/SetOp.java
b/core/src/main/java/org/apache/calcite/rel/core/SetOp.java
index 381277c9aa..1b5d57158d 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/SetOp.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/SetOp.java
@@ -24,6 +24,8 @@ import org.apache.calcite.rel.AbstractRelNode;
import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelWriter;
+import org.apache.calcite.rel.hint.Hintable;
+import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.util.Util;
@@ -32,25 +34,27 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
* <code>SetOp</code> is an abstract base for relational set operators such
* as UNION, MINUS (aka EXCEPT), and INTERSECT.
*/
-public abstract class SetOp extends AbstractRelNode {
+public abstract class SetOp extends AbstractRelNode implements Hintable {
//~ Instance fields --------------------------------------------------------
protected ImmutableList<RelNode> inputs;
public final SqlKind kind;
public final boolean all;
+ protected final ImmutableList<RelHint> hints;
//~ Constructors -----------------------------------------------------------
/**
* Creates a SetOp.
*/
- protected SetOp(RelOptCluster cluster, RelTraitSet traits,
+ protected SetOp(RelOptCluster cluster, RelTraitSet traits, List<RelHint>
hints,
List<RelNode> inputs, SqlKind kind, boolean all) {
super(cluster, traits);
Preconditions.checkArgument(kind == SqlKind.UNION
@@ -59,14 +63,23 @@ public abstract class SetOp extends AbstractRelNode {
this.kind = kind;
this.inputs = ImmutableList.copyOf(inputs);
this.all = all;
+ this.hints = ImmutableList.copyOf(hints);
+ }
+
+ /**
+ * Creates a SetOp.
+ */
+ protected SetOp(RelOptCluster cluster, RelTraitSet traits,
+ List<RelNode> inputs, SqlKind kind, boolean all) {
+ this(cluster, traits, Collections.emptyList(), inputs, kind, all);
}
/**
* Creates a SetOp by parsing serialized output.
*/
protected SetOp(RelInput input) {
- this(input.getCluster(), input.getTraitSet(), input.getInputs(),
- SqlKind.UNION, input.getBoolean("all", false));
+ this(input.getCluster(), input.getTraitSet(), Collections.emptyList(),
+ input.getInputs(), SqlKind.UNION, input.getBoolean("all", false));
}
//~ Methods ----------------------------------------------------------------
@@ -112,6 +125,10 @@ public abstract class SetOp extends AbstractRelNode {
return rowType;
}
+ @Override public ImmutableList<RelHint> getHints() {
+ return hints;
+ }
+
/**
* Returns whether all the inputs of this set operator have the same row
* type as its output row.
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Sort.java
b/core/src/main/java/org/apache/calcite/rel/core/Sort.java
index 1faad39d15..6297def223 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Sort.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Sort.java
@@ -28,14 +28,19 @@ import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelWriter;
import org.apache.calcite.rel.SingleRel;
+import org.apache.calcite.rel.hint.Hintable;
+import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexShuttle;
import org.apache.calcite.util.Util;
+import com.google.common.collect.ImmutableList;
+
import org.checkerframework.checker.nullness.qual.Nullable;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -43,15 +48,50 @@ import java.util.Objects;
* Relational expression that imposes a particular sort order on its input
* without otherwise changing its content.
*/
-public abstract class Sort extends SingleRel {
+public abstract class Sort extends SingleRel implements Hintable {
//~ Instance fields --------------------------------------------------------
public final RelCollation collation;
public final @Nullable RexNode offset;
public final @Nullable RexNode fetch;
+ protected final ImmutableList<RelHint> hints;
//~ Constructors -----------------------------------------------------------
+ /**
+ * Creates a Sort.
+ *
+ * @param cluster Cluster this relational expression belongs to
+ * @param traits Traits
+ * @param hints Hints for this node
+ * @param child input relational expression
+ * @param collation array of sort specifications
+ * @param offset Expression for number of rows to discard before returning
+ * first row
+ * @param fetch Expression for number of rows to fetch
+ */
+ protected Sort(
+ RelOptCluster cluster,
+ RelTraitSet traits,
+ List<RelHint> hints,
+ RelNode child,
+ RelCollation collation,
+ @Nullable RexNode offset,
+ @Nullable RexNode fetch) {
+ super(cluster, traits, child);
+ this.collation = collation;
+ this.offset = offset;
+ this.fetch = fetch;
+ this.hints = ImmutableList.copyOf(hints);
+
+ assert traits.containsIfApplicable(collation)
+ : "traits=" + traits + ", collation=" + collation;
+ assert !(fetch == null
+ && offset == null
+ && collation.getFieldCollations().isEmpty())
+ : "trivial sort";
+ }
+
/**
* Creates a Sort.
*
@@ -65,7 +105,7 @@ public abstract class Sort extends SingleRel {
RelTraitSet traits,
RelNode child,
RelCollation collation) {
- this(cluster, traits, child, collation, null, null);
+ this(cluster, traits, Collections.emptyList(), child, collation, null,
null);
}
/**
@@ -86,17 +126,7 @@ public abstract class Sort extends SingleRel {
RelCollation collation,
@Nullable RexNode offset,
@Nullable RexNode fetch) {
- super(cluster, traits, child);
- this.collation = collation;
- this.offset = offset;
- this.fetch = fetch;
-
- assert traits.containsIfApplicable(collation)
- : "traits=" + traits + ", collation=" + collation;
- assert !(fetch == null
- && offset == null
- && collation.getFieldCollations().isEmpty())
- : "trivial sort";
+ this(cluster, traits, Collections.emptyList(), child, collation, offset,
fetch);
}
/**
@@ -252,4 +282,8 @@ public abstract class Sort extends SingleRel {
? ((RexLiteral) r).getValueAs(Double.class)
: null;
}
+
+ @Override public ImmutableList<RelHint> getHints() {
+ return hints;
+ }
}
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Union.java
b/core/src/main/java/org/apache/calcite/rel/core/Union.java
index adf5d3f566..3aebe1e85b 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Union.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Union.java
@@ -20,10 +20,12 @@ import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rel.metadata.RelMdUtil;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.sql.SqlKind;
+import java.util.Collections;
import java.util.List;
/**
@@ -38,9 +40,18 @@ public abstract class Union extends SetOp {
protected Union(
RelOptCluster cluster,
RelTraitSet traits,
+ List<RelHint> hints,
List<RelNode> inputs,
boolean all) {
- super(cluster, traits, inputs, SqlKind.UNION, all);
+ super(cluster, traits, hints, inputs, SqlKind.UNION, all);
+ }
+
+ protected Union(
+ RelOptCluster cluster,
+ RelTraitSet traits,
+ List<RelNode> inputs,
+ boolean all) {
+ super(cluster, traits, Collections.emptyList(), inputs, SqlKind.UNION,
all);
}
/**
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Values.java
b/core/src/main/java/org/apache/calcite/rel/core/Values.java
index c671dba13a..c16954bd4d 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Values.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Values.java
@@ -24,6 +24,8 @@ import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.AbstractRelNode;
import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelWriter;
+import org.apache.calcite.rel.hint.Hintable;
+import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
@@ -37,6 +39,7 @@ import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.Nullable;
+import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -45,10 +48,12 @@ import java.util.stream.Collectors;
* Relational expression whose value is a sequence of zero or more literal row
* values.
*/
-public abstract class Values extends AbstractRelNode {
+public abstract class Values extends AbstractRelNode implements Hintable {
public static final Predicate<? super Values> IS_EMPTY_J = Values::isEmpty;
+ protected final ImmutableList<RelHint> hints;
+
@SuppressWarnings("Guava")
@Deprecated // to be removed before 2.0
public static final com.google.common.base.Predicate<? super Values>
@@ -73,6 +78,7 @@ public abstract class Values extends AbstractRelNode {
* call, otherwise bad things will happen.
*
* @param cluster Cluster that this relational expression belongs to
+ * @param hints Hints for this node
* @param rowType Row type for tuples produced by this rel
* @param tuples 2-dimensional array of tuple values to be produced; outer
* list contains tuples; each inner list is one tuple; all
@@ -81,15 +87,39 @@ public abstract class Values extends AbstractRelNode {
@SuppressWarnings("method.invocation.invalid")
protected Values(
RelOptCluster cluster,
+ List<RelHint> hints,
RelDataType rowType,
ImmutableList<ImmutableList<RexLiteral>> tuples,
RelTraitSet traits) {
super(cluster, traits);
this.rowType = rowType;
this.tuples = tuples;
+ this.hints = ImmutableList.copyOf(hints);
assert assertRowType();
}
+ /**
+ * Creates a new Values.
+ *
+ * <p>Note that tuples passed in become owned by
+ * this rel (without a deep copy), so caller must not modify them after this
+ * call, otherwise bad things will happen.
+ *
+ * @param cluster Cluster that this relational expression belongs to
+ * @param rowType Row type for tuples produced by this rel
+ * @param tuples 2-dimensional array of tuple values to be produced; outer
+ * list contains tuples; each inner list is one tuple; all
+ * tuples must be of same length, conforming to rowType
+ */
+ @SuppressWarnings("method.invocation.invalid")
+ protected Values(
+ RelOptCluster cluster,
+ RelDataType rowType,
+ ImmutableList<ImmutableList<RexLiteral>> tuples,
+ RelTraitSet traits) {
+ this(cluster, Collections.emptyList(), rowType, tuples, traits);
+ }
+
/**
* Creates a Values by parsing serialized output.
*/
@@ -199,4 +229,8 @@ public abstract class Values extends AbstractRelNode {
}
return relWriter;
}
+
+ @Override public ImmutableList<RelHint> getHints() {
+ return hints;
+ }
}
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Window.java
b/core/src/main/java/org/apache/calcite/rel/core/Window.java
index f13bd91785..e744f940ff 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Window.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Window.java
@@ -27,6 +27,8 @@ import org.apache.calcite.rel.RelFieldCollation;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelWriter;
import org.apache.calcite.rel.SingleRel;
+import org.apache.calcite.rel.hint.Hintable;
+import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexCall;
@@ -50,6 +52,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
import java.util.AbstractList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -67,27 +70,45 @@ import java.util.Objects;
*
* <p>Created by {@link org.apache.calcite.rel.rules.ProjectToWindowRule}.
*/
-public abstract class Window extends SingleRel {
+public abstract class Window extends SingleRel implements Hintable {
public final ImmutableList<Group> groups;
public final ImmutableList<RexLiteral> constants;
+ protected final ImmutableList<RelHint> hints;
/**
* Creates a window relational expression.
*
* @param cluster Cluster
* @param traitSet Trait set
+ * @param hints Hints for this node
* @param input Input relational expression
* @param constants List of constants that are additional inputs
* @param rowType Output row type
* @param groups Windows
*/
- protected Window(RelOptCluster cluster, RelTraitSet traitSet, RelNode input,
- List<RexLiteral> constants, RelDataType rowType, List<Group> groups) {
+ protected Window(RelOptCluster cluster, RelTraitSet traitSet, List<RelHint>
hints,
+ RelNode input, List<RexLiteral> constants, RelDataType rowType,
List<Group> groups) {
super(cluster, traitSet, input);
this.constants = ImmutableList.copyOf(constants);
assert rowType != null;
this.rowType = rowType;
this.groups = ImmutableList.copyOf(groups);
+ this.hints = ImmutableList.copyOf(hints);
+ }
+
+ /**
+ * Creates a window relational expression.
+ *
+ * @param cluster Cluster
+ * @param traitSet Trait set
+ * @param input Input relational expression
+ * @param constants List of constants that are additional inputs
+ * @param rowType Output row type
+ * @param groups Windows
+ */
+ public Window(RelOptCluster cluster, RelTraitSet traitSet, RelNode input,
+ List<RexLiteral> constants, RelDataType rowType, List<Group> groups) {
+ this(cluster, traitSet, Collections.emptyList(), input, constants,
rowType, groups);
}
@Override public boolean isValid(Litmus litmus, @Nullable Context context) {
@@ -428,4 +449,8 @@ public abstract class Window extends SingleRel {
return super.clone(type, operands);
}
}
+
+ @Override public ImmutableList<RelHint> getHints() {
+ return hints;
+ }
}
diff --git a/core/src/main/java/org/apache/calcite/rel/hint/HintPredicates.java
b/core/src/main/java/org/apache/calcite/rel/hint/HintPredicates.java
index 97e6bf33bb..f847982968 100644
--- a/core/src/main/java/org/apache/calcite/rel/hint/HintPredicates.java
+++ b/core/src/main/java/org/apache/calcite/rel/hint/HintPredicates.java
@@ -55,6 +55,31 @@ public abstract class HintPredicates {
public static final HintPredicate CORRELATE =
new NodeTypeHintPredicate(NodeTypeHintPredicate.NodeType.CORRELATE);
+ /** A hint predicate that indicates a hint can only be used to
+ * {@link org.apache.calcite.rel.core.Filter} nodes. */
+ public static final HintPredicate FILTER =
+ new NodeTypeHintPredicate(NodeTypeHintPredicate.NodeType.FILTER);
+
+ /** A hint predicate that indicates a hint can only be used to
+ * {@link org.apache.calcite.rel.core.SetOp} nodes. */
+ public static final HintPredicate SETOP =
+ new NodeTypeHintPredicate(NodeTypeHintPredicate.NodeType.SETOP);
+
+ /** A hint predicate that indicates a hint can only be used to
+ * {@link org.apache.calcite.rel.core.Sort} nodes. */
+ public static final HintPredicate SORT =
+ new NodeTypeHintPredicate(NodeTypeHintPredicate.NodeType.SORT);
+
+ /** A hint predicate that indicates a hint can only be used to
+ * {@link org.apache.calcite.rel.core.Values} nodes. */
+ public static final HintPredicate VALUES =
+ new NodeTypeHintPredicate(NodeTypeHintPredicate.NodeType.VALUES);
+
+ /** A hint predicate that indicates a hint can only be used to
+ * {@link org.apache.calcite.rel.core.Window} nodes. */
+ public static final HintPredicate WINDOW =
+ new NodeTypeHintPredicate(NodeTypeHintPredicate.NodeType.WINDOW);
+
/**
* Returns a composed hint predicate that represents a short-circuiting
logical
* AND of an array of hint predicates {@code hintPredicates}. When
evaluating the composed
diff --git
a/core/src/main/java/org/apache/calcite/rel/hint/NodeTypeHintPredicate.java
b/core/src/main/java/org/apache/calcite/rel/hint/NodeTypeHintPredicate.java
index cdcd7ba52d..225453d543 100644
--- a/core/src/main/java/org/apache/calcite/rel/hint/NodeTypeHintPredicate.java
+++ b/core/src/main/java/org/apache/calcite/rel/hint/NodeTypeHintPredicate.java
@@ -20,9 +20,14 @@ import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Aggregate;
import org.apache.calcite.rel.core.Calc;
import org.apache.calcite.rel.core.Correlate;
+import org.apache.calcite.rel.core.Filter;
import org.apache.calcite.rel.core.Join;
import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.core.SetOp;
+import org.apache.calcite.rel.core.Sort;
import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.core.Values;
+import org.apache.calcite.rel.core.Window;
/**
* A hint predicate that specifies which kind of relational
@@ -69,7 +74,32 @@ public class NodeTypeHintPredicate implements HintPredicate {
/**
* The hint would be propagated to the Correlate nodes.
*/
- CORRELATE(Correlate.class);
+ CORRELATE(Correlate.class),
+
+ /**
+ * The hint would be propagated to the Filter nodes.
+ */
+ FILTER(Filter.class),
+
+ /**
+ * The hint would be propagated to the SetOp(Union, Intersect, Minus)
nodes.
+ */
+ SETOP(SetOp.class),
+
+ /**
+ * The hint would be propagated to the Sort nodes.
+ */
+ SORT(Sort.class),
+
+ /**
+ * The hint would be propagated to the Values nodes.
+ */
+ VALUES(Values.class),
+
+ /**
+ * The hint would be propagated to the Window nodes.
+ */
+ WINDOW(Window.class);
/** Relational expression clazz that the hint can apply to. */
@SuppressWarnings("ImmutableEnumChecker")
diff --git
a/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
b/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
index 9bc9903cec..829117c784 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
@@ -27,15 +27,18 @@ import org.apache.calcite.rel.RelShuttle;
import org.apache.calcite.rel.RelWriter;
import org.apache.calcite.rel.core.CorrelationId;
import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rel.metadata.RelMdCollation;
import org.apache.calcite.rel.metadata.RelMdDistribution;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rex.RexNode;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.checkerframework.checker.nullness.qual.Nullable;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -63,13 +66,35 @@ public final class LogicalFilter extends Filter {
public LogicalFilter(
RelOptCluster cluster,
RelTraitSet traitSet,
+ List<RelHint> hints,
RelNode child,
RexNode condition,
ImmutableSet<CorrelationId> variablesSet) {
- super(cluster, traitSet, child, condition);
+ super(cluster, traitSet, hints, child, condition);
this.variablesSet = Objects.requireNonNull(variablesSet, "variablesSet");
}
+ /**
+ * Creates a LogicalFilter.
+ *
+ * <p>Use {@link #create} unless you know what you're doing.
+ *
+ * @param cluster Cluster that this relational expression belongs to
+ * @param child Input relational expression
+ * @param condition Boolean expression which determines whether a row is
+ * allowed to pass
+ * @param variablesSet Correlation variables set by this relational
expression
+ * to be used by nested expressions
+ */
+ public LogicalFilter(
+ RelOptCluster cluster,
+ RelTraitSet traitSet,
+ RelNode child,
+ RexNode condition,
+ ImmutableSet<CorrelationId> variablesSet) {
+ this(cluster, traitSet, ImmutableList.of(), child, condition,
variablesSet);
+ }
+
@Deprecated // to be removed before 2.0
public LogicalFilter(
RelOptCluster cluster,
@@ -123,7 +148,7 @@ public final class LogicalFilter extends Filter {
@Override public LogicalFilter copy(RelTraitSet traitSet, RelNode input,
RexNode condition) {
assert traitSet.containsIfApplicable(Convention.NONE);
- return new LogicalFilter(getCluster(), traitSet, input, condition,
+ return new LogicalFilter(getCluster(), traitSet, hints, input, condition,
variablesSet);
}
@@ -144,4 +169,8 @@ public final class LogicalFilter extends Filter {
@Override public int deepHashCode() {
return Objects.hash(deepHashCode0(), variablesSet);
}
+
+ @Override public RelNode withHints(List<RelHint> hintList) {
+ return new LogicalFilter(getCluster(), traitSet, hintList, input,
condition, variablesSet);
+ }
}
diff --git
a/core/src/main/java/org/apache/calcite/rel/logical/LogicalIntersect.java
b/core/src/main/java/org/apache/calcite/rel/logical/LogicalIntersect.java
index 99ecf659df..f31f11292f 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalIntersect.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalIntersect.java
@@ -23,7 +23,9 @@ import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelShuttle;
import org.apache.calcite.rel.core.Intersect;
+import org.apache.calcite.rel.hint.RelHint;
+import java.util.Collections;
import java.util.List;
/**
@@ -41,9 +43,23 @@ public final class LogicalIntersect extends Intersect {
public LogicalIntersect(
RelOptCluster cluster,
RelTraitSet traitSet,
+ List<RelHint> hints,
List<RelNode> inputs,
boolean all) {
- super(cluster, traitSet, inputs, all);
+ super(cluster, traitSet, hints, inputs, all);
+ }
+
+ /**
+ * Creates a LogicalIntersect.
+ *
+ * <p>Use {@link #create} unless you know what you're doing.
+ */
+ public LogicalIntersect(
+ RelOptCluster cluster,
+ RelTraitSet traitSet,
+ List<RelNode> inputs,
+ boolean all) {
+ this(cluster, traitSet, Collections.emptyList(), inputs, all);
}
@Deprecated // to be removed before 2.0
@@ -69,10 +85,14 @@ public final class LogicalIntersect extends Intersect {
@Override public LogicalIntersect copy(RelTraitSet traitSet,
List<RelNode> inputs, boolean all) {
- return new LogicalIntersect(getCluster(), traitSet, inputs, all);
+ return new LogicalIntersect(getCluster(), traitSet, hints, inputs, all);
}
@Override public RelNode accept(RelShuttle shuttle) {
return shuttle.visit(this);
}
+
+ @Override public RelNode withHints(List<RelHint> hintList) {
+ return new LogicalIntersect(getCluster(), traitSet, hintList, inputs, all);
+ }
}
diff --git
a/core/src/main/java/org/apache/calcite/rel/logical/LogicalMinus.java
b/core/src/main/java/org/apache/calcite/rel/logical/LogicalMinus.java
index 357c403510..745d2174df 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalMinus.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalMinus.java
@@ -23,7 +23,9 @@ import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelShuttle;
import org.apache.calcite.rel.core.Minus;
+import org.apache.calcite.rel.hint.RelHint;
+import java.util.Collections;
import java.util.List;
/**
@@ -33,6 +35,16 @@ import java.util.List;
public final class LogicalMinus extends Minus {
//~ Constructors -----------------------------------------------------------
+ /**
+ * Creates a LogicalMinus.
+ *
+ * <p>Use {@link #create} unless you know what you're doing.
+ */
+ public LogicalMinus(RelOptCluster cluster, RelTraitSet traitSet,
+ List<RelHint> hints, List<RelNode> inputs, boolean all) {
+ super(cluster, traitSet, hints, inputs, all);
+ }
+
/**
* Creates a LogicalMinus.
*
@@ -40,7 +52,7 @@ public final class LogicalMinus extends Minus {
*/
public LogicalMinus(RelOptCluster cluster, RelTraitSet traitSet,
List<RelNode> inputs, boolean all) {
- super(cluster, traitSet, inputs, all);
+ this(cluster, traitSet, Collections.emptyList(), inputs, all);
}
@Deprecated // to be removed before 2.0
@@ -69,10 +81,14 @@ public final class LogicalMinus extends Minus {
@Override public LogicalMinus copy(RelTraitSet traitSet, List<RelNode>
inputs,
boolean all) {
assert traitSet.containsIfApplicable(Convention.NONE);
- return new LogicalMinus(getCluster(), traitSet, inputs, all);
+ return new LogicalMinus(getCluster(), traitSet, hints, inputs, all);
}
@Override public RelNode accept(RelShuttle shuttle) {
return shuttle.visit(this);
}
+
+ @Override public RelNode withHints(List<RelHint> hintList) {
+ return new LogicalMinus(getCluster(), traitSet, hintList, inputs, all);
+ }
}
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalSort.java
b/core/src/main/java/org/apache/calcite/rel/logical/LogicalSort.java
index c52acf80b4..31b8b4b006 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalSort.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalSort.java
@@ -25,10 +25,14 @@ import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelShuttle;
import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rex.RexNode;
import org.checkerframework.checker.nullness.qual.Nullable;
+import java.util.Collections;
+import java.util.List;
+
/**
* Sub-class of {@link org.apache.calcite.rel.core.Sort} not
* targeted at any particular engine or calling convention.
@@ -36,7 +40,12 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public final class LogicalSort extends Sort {
private LogicalSort(RelOptCluster cluster, RelTraitSet traitSet,
RelNode input, RelCollation collation, @Nullable RexNode offset,
@Nullable RexNode fetch) {
- super(cluster, traitSet, input, collation, offset, fetch);
+ this(cluster, traitSet, Collections.emptyList(), input, collation, offset,
fetch);
+ }
+
+ private LogicalSort(RelOptCluster cluster, RelTraitSet traitSet,
List<RelHint> hints,
+ RelNode input, RelCollation collation, @Nullable RexNode offset,
@Nullable RexNode fetch) {
+ super(cluster, traitSet, hints, input, collation, offset, fetch);
assert traitSet.containsIfApplicable(Convention.NONE);
}
@@ -69,11 +78,16 @@ public final class LogicalSort extends Sort {
@Override public Sort copy(RelTraitSet traitSet, RelNode newInput,
RelCollation newCollation, @Nullable RexNode offset, @Nullable RexNode
fetch) {
- return new LogicalSort(getCluster(), traitSet, newInput, newCollation,
- offset, fetch);
+ return new LogicalSort(getCluster(), traitSet, hints, newInput,
+ newCollation, offset, fetch);
}
@Override public RelNode accept(RelShuttle shuttle) {
return shuttle.visit(this);
}
+
+ @Override public RelNode withHints(List<RelHint> hintList) {
+ return new LogicalSort(getCluster(), traitSet, hintList,
+ input, collation, offset, fetch);
+ }
}
diff --git
a/core/src/main/java/org/apache/calcite/rel/logical/LogicalUnion.java
b/core/src/main/java/org/apache/calcite/rel/logical/LogicalUnion.java
index a56ba11567..e8855c6568 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalUnion.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalUnion.java
@@ -23,7 +23,9 @@ import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelShuttle;
import org.apache.calcite.rel.core.Union;
+import org.apache.calcite.rel.hint.RelHint;
+import java.util.Collections;
import java.util.List;
/**
@@ -40,9 +42,22 @@ public final class LogicalUnion extends Union {
*/
public LogicalUnion(RelOptCluster cluster,
RelTraitSet traitSet,
+ List<RelHint> hints,
List<RelNode> inputs,
boolean all) {
- super(cluster, traitSet, inputs, all);
+ super(cluster, traitSet, hints, inputs, all);
+ }
+
+ /**
+ * Creates a LogicalUnion.
+ *
+ * <p>Use {@link #create} unless you know what you're doing.
+ */
+ public LogicalUnion(RelOptCluster cluster,
+ RelTraitSet traitSet,
+ List<RelNode> inputs,
+ boolean all) {
+ this(cluster, traitSet, Collections.emptyList(), inputs, all);
}
@Deprecated // to be removed before 2.0
@@ -70,10 +85,14 @@ public final class LogicalUnion extends Union {
@Override public LogicalUnion copy(
RelTraitSet traitSet, List<RelNode> inputs, boolean all) {
assert traitSet.containsIfApplicable(Convention.NONE);
- return new LogicalUnion(getCluster(), traitSet, inputs, all);
+ return new LogicalUnion(getCluster(), traitSet, hints, inputs, all);
}
@Override public RelNode accept(RelShuttle shuttle) {
return shuttle.visit(this);
}
+
+ @Override public RelNode withHints(List<RelHint> hintList) {
+ return new LogicalUnion(getCluster(), traitSet, hintList, inputs, all);
+ }
}
diff --git
a/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
b/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
index 11ea70f1b2..024f0112ed 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
@@ -25,6 +25,7 @@ import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelShuttle;
import org.apache.calcite.rel.core.Values;
+import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rel.metadata.RelMdCollation;
import org.apache.calcite.rel.metadata.RelMdDistribution;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
@@ -35,6 +36,7 @@ import org.apache.calcite.sql.type.SqlTypeName;
import com.google.common.collect.ImmutableList;
import java.math.BigDecimal;
+import java.util.Collections;
import java.util.List;
/**
@@ -50,6 +52,7 @@ public class LogicalValues extends Values {
* <p>Use {@link #create} unless you know what you're doing.
*
* @param cluster Cluster that this relational expression belongs to
+ * @param hints Hints for this node
* @param rowType Row type for tuples produced by this rel
* @param tuples 2-dimensional array of tuple values to be produced; outer
* list contains tuples; each inner list is one tuple; all
@@ -58,9 +61,29 @@ public class LogicalValues extends Values {
public LogicalValues(
RelOptCluster cluster,
RelTraitSet traitSet,
+ List<RelHint> hints,
RelDataType rowType,
ImmutableList<ImmutableList<RexLiteral>> tuples) {
- super(cluster, rowType, tuples, traitSet);
+ super(cluster, hints, rowType, tuples, traitSet);
+ }
+
+ /**
+ * Creates a LogicalValues.
+ *
+ * <p>Use {@link #create} unless you know what you're doing.
+ *
+ * @param cluster Cluster that this relational expression belongs to
+ * @param rowType Row type for tuples produced by this rel
+ * @param tuples 2-dimensional array of tuple values to be produced; outer
+ * list contains tuples; each inner list is one tuple; all
+ * tuples must be of same length, conforming to rowType
+ */
+ public LogicalValues(
+ RelOptCluster cluster,
+ RelTraitSet traitSet,
+ RelDataType rowType,
+ ImmutableList<ImmutableList<RexLiteral>> tuples) {
+ this(cluster, traitSet, Collections.emptyList(), rowType, tuples);
}
@Deprecated // to be removed before 2.0
@@ -121,4 +144,8 @@ public class LogicalValues extends Values {
@Override public RelNode accept(RelShuttle shuttle) {
return shuttle.visit(this);
}
+
+ @Override public RelNode withHints(List<RelHint> hintList) {
+ return new LogicalValues(getCluster(), traitSet, hintList, getRowType(),
tuples);
+ }
}
diff --git
a/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
b/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
index c53e03953a..2ebc48b858 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
@@ -23,6 +23,7 @@ import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Window;
+import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexInputRef;
import org.apache.calcite.rex.RexLiteral;
@@ -48,6 +49,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
@@ -66,15 +68,33 @@ public final class LogicalWindow extends Window {
*
* @param cluster Cluster
* @param traitSet Trait set
+ * @param hints Hints for this node
* @param input Input relational expression
* @param constants List of constants that are additional inputs
* @param rowType Output row type
* @param groups Window groups
*/
public LogicalWindow(RelOptCluster cluster, RelTraitSet traitSet,
- RelNode input, List<RexLiteral> constants, RelDataType rowType,
- List<Group> groups) {
- super(cluster, traitSet, input, constants, rowType, groups);
+ List<RelHint> hints, RelNode input, List<RexLiteral> constants,
+ RelDataType rowType, List<Group> groups) {
+ super(cluster, traitSet, hints, input, constants, rowType, groups);
+ }
+
+ /**
+ * Creates a LogicalWindow.
+ *
+ * <p>Use {@link #create} unless you know what you're doing.
+ *
+ * @param cluster Cluster
+ * @param traitSet Trait set
+ * @param input Input relational expression
+ * @param constants List of constants that are additional inputs
+ * @param rowType Output row type
+ * @param groups Window groups
+ */
+ public LogicalWindow(RelOptCluster cluster, RelTraitSet traitSet, RelNode
input,
+ List<RexLiteral> constants, RelDataType rowType, List<Group> groups) {
+ this(cluster, traitSet, Collections.emptyList(), input, constants,
rowType, groups);
}
@Override public LogicalWindow copy(RelTraitSet traitSet,
@@ -366,4 +386,9 @@ public final class LogicalWindow extends Window {
aggWindow.getLowerBound(), aggWindow.getUpperBound());
windowMap.put(windowKey, over);
}
+
+ @Override public RelNode withHints(List<RelHint> hintList) {
+ return new LogicalWindow(getCluster(), traitSet, hintList,
+ input, constants, getRowType(), groups);
+ }
}
diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
index e174fae8e6..6f13e8736c 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -4554,17 +4554,74 @@ public class RelBuilderTest {
final AssertionError error1 = assertThrows(
AssertionError.class,
() -> {
- final RelBuilder builder = RelBuilder.create(config().build());
// Equivalent SQL:
// SELECT *
// FROM emp
- // WHERE EMPNO = 124
+ // MATCH_RECOGNIZE (
+ // PARTITION BY deptno
+ // ORDER BY empno asc
+ // MEASURES
+ // STRT.mgr as start_nw,
+ // LAST(DOWN.mgr) as bottom_nw,
+ // PATTERN (STRT DOWN+ UP+) WITHIN INTERVAL '5' SECOND
+ // DEFINE
+ // DOWN as DOWN.mgr < PREV(DOWN.mgr),
+ // UP as UP.mgr > PREV(UP.mgr)
+ // )
+ final RelBuilder builder =
RelBuilder.create(config().build()).scan("EMP");
+ final RelDataTypeFactory typeFactory = builder.getTypeFactory();
+ final RelDataType intType =
typeFactory.createSqlType(SqlTypeName.INTEGER);
+
+ RexNode pattern = builder.patternConcat(
+ builder.literal("STRT"),
+ builder.patternQuantify(builder.literal("DOWN"),
builder.literal(1),
+ builder.literal(-1), builder.literal(false)),
+ builder.patternQuantify(builder.literal("UP"),
builder.literal(1),
+ builder.literal(-1), builder.literal(false)));
+
+ ImmutableMap.Builder<String, RexNode> pdBuilder = new
ImmutableMap.Builder<>();
+ RexNode downDefinition = builder.lessThan(
+ builder.call(SqlStdOperatorTable.PREV,
+ builder.patternField("DOWN", intType, 3),
+ builder.literal(0)),
+ builder.call(SqlStdOperatorTable.PREV,
+ builder.patternField("DOWN", intType, 3),
+ builder.literal(1)));
+ pdBuilder.put("DOWN", downDefinition);
+ RexNode upDefinition = builder.greaterThan(
+ builder.call(SqlStdOperatorTable.PREV,
+ builder.patternField("UP", intType, 3),
+ builder.literal(0)),
+ builder.call(SqlStdOperatorTable.PREV,
+ builder.patternField("UP", intType, 3),
+ builder.literal(1)));
+ pdBuilder.put("UP", upDefinition);
+
+ ImmutableList.Builder<RexNode> measuresBuilder = new
ImmutableList.Builder<>();
+ measuresBuilder.add(
+ builder.alias(builder.patternField("STRT", intType, 3),
"start_nw"));
+ measuresBuilder.add(
+ builder.alias(
+ builder.call(SqlStdOperatorTable.LAST,
+ builder.patternField("DOWN", intType, 3),
+ builder.literal(0)),
+ "bottom_nw"));
+
+ RexNode after = builder.getRexBuilder().makeFlag(
+ SqlMatchRecognize.AfterOption.SKIP_TO_NEXT_ROW);
+
+ ImmutableList.Builder<RexNode> partitionKeysBuilder = new
ImmutableList.Builder<>();
+ partitionKeysBuilder.add(builder.field("DEPTNO"));
+
+ ImmutableList.Builder<RexNode> orderKeysBuilder = new
ImmutableList.Builder<>();
+ orderKeysBuilder.add(builder.field("EMPNO"));
+
+ RexNode interval = builder.literal("INTERVAL '5' SECOND");
+
builder
- .scan("EMP")
- .filter(
- builder.equals(
- builder.field("EMPNO"),
- builder.literal(124)))
+ .match(pattern, false, false, pdBuilder.build(),
+ measuresBuilder.build(), after, ImmutableMap.of(), false,
+ partitionKeysBuilder.build(), orderKeysBuilder.build(),
interval)
.hints(indexHint);
},
"hints() should fail on non Hintable relational expression");
diff --git
a/core/src/test/java/org/apache/calcite/test/SqlHintsConverterTest.java
b/core/src/test/java/org/apache/calcite/test/SqlHintsConverterTest.java
index 07b3356922..0f3154a0ef 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlHintsConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlHintsConverterTest.java
@@ -40,6 +40,7 @@ import org.apache.calcite.rel.core.Join;
import org.apache.calcite.rel.core.JoinInfo;
import org.apache.calcite.rel.core.Snapshot;
import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.core.Window;
import org.apache.calcite.rel.hint.HintPredicate;
import org.apache.calcite.rel.hint.HintPredicates;
import org.apache.calcite.rel.hint.HintStrategy;
@@ -48,8 +49,14 @@ import org.apache.calcite.rel.hint.Hintable;
import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rel.logical.LogicalAggregate;
import org.apache.calcite.rel.logical.LogicalCorrelate;
+import org.apache.calcite.rel.logical.LogicalFilter;
+import org.apache.calcite.rel.logical.LogicalIntersect;
import org.apache.calcite.rel.logical.LogicalJoin;
+import org.apache.calcite.rel.logical.LogicalMinus;
import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalSort;
+import org.apache.calcite.rel.logical.LogicalUnion;
+import org.apache.calcite.rel.logical.LogicalValues;
import org.apache.calcite.rel.rules.CoreRules;
import org.apache.calcite.sql.SqlDelete;
import org.apache.calcite.sql.SqlInsert;
@@ -187,6 +194,54 @@ class SqlHintsConverterTest {
sql(sql).ok();
}
+ @Test void testFilterHints() {
+ final String sql = "select /*+ resource(parallelism='3') */ avg(sal) as
avg_sal, deptno\n"
+ + "from emp group by deptno having avg(sal) > 5000";
+ sql(sql).ok();
+ }
+
+ @Test void testUnionHints() {
+ final String sql = "select /*+ breakable */ deptno from\n"
+ + "(select ename, deptno from emp\n"
+ + "union all\n"
+ + "select name, deptno from dept)";
+ sql(sql).ok();
+ }
+
+ @Test void testMinusHints() {
+ final String sql = "select /*+ breakable */ deptno from\n"
+ + "(select ename, deptno from emp\n"
+ + "except all\n"
+ + "select name, deptno from dept)";
+ sql(sql).ok();
+ }
+
+ @Test void testIntersectHints() {
+ final String sql = "select /*+ breakable */ deptno from\n"
+ + "(select ename, deptno from emp\n"
+ + "intersect all\n"
+ + "select name, deptno from dept)";
+ sql(sql).ok();
+ }
+
+ @Test void testSortHints() {
+ final String sql = "select /*+ async_merge */ empno from emp order by
empno, empno desc";
+ sql(sql).ok();
+ }
+
+ @Test void testValuesHints() {
+ final String sql = "select /*+ resource(parallelism='3') */ a, max(b),
max(b + 1)\n"
+ + "from (values (1, 2)) as t(a, b)\n"
+ + "group by a";
+ sql(sql).ok();
+ }
+
+ @Test void testWindowHints() {
+ final String sql = "select /*+ mini_batch */ last_value(deptno)\n"
+ + "over (order by empno rows 2 following) from emp";
+ sql(sql).ok();
+ }
+
@Test void testHintsInSubQueryWithDecorrelation() {
final String sql = "select /*+ resource(parallelism='3'),
AGG_STRATEGY(TWO_PHASE) */\n"
+ "sum(e1.empno) from emp e1, dept d1\n"
@@ -711,38 +766,90 @@ class SqlHintsConverterTest {
@Override public RelNode visit(TableScan scan) {
if (scan.getHints().size() > 0) {
- this.hintsCollect.add("TableScan:" + scan.getHints().toString());
+ this.hintsCollect.add("TableScan:" + scan.getHints());
}
return super.visit(scan);
}
@Override public RelNode visit(LogicalJoin join) {
if (join.getHints().size() > 0) {
- this.hintsCollect.add("LogicalJoin:" + join.getHints().toString());
+ this.hintsCollect.add("LogicalJoin:" + join.getHints());
}
return super.visit(join);
}
@Override public RelNode visit(LogicalProject project) {
if (project.getHints().size() > 0) {
- this.hintsCollect.add("Project:" + project.getHints().toString());
+ this.hintsCollect.add("Project:" + project.getHints());
}
return super.visit(project);
}
@Override public RelNode visit(LogicalAggregate aggregate) {
if (aggregate.getHints().size() > 0) {
- this.hintsCollect.add("Aggregate:" +
aggregate.getHints().toString());
+ this.hintsCollect.add("Aggregate:" + aggregate.getHints());
}
return super.visit(aggregate);
}
@Override public RelNode visit(LogicalCorrelate correlate) {
if (correlate.getHints().size() > 0) {
- this.hintsCollect.add("Correlate:" +
correlate.getHints().toString());
+ this.hintsCollect.add("Correlate:" + correlate.getHints());
}
return super.visit(correlate);
}
+
+ @Override public RelNode visit(LogicalFilter filter) {
+ if (filter.getHints().size() > 0) {
+ this.hintsCollect.add("Filter:" + filter.getHints());
+ }
+ return super.visit(filter);
+ }
+
+ @Override public RelNode visit(LogicalUnion union) {
+ if (union.getHints().size() > 0) {
+ this.hintsCollect.add("Union:" + union.getHints());
+ }
+ return super.visit(union);
+ }
+
+ @Override public RelNode visit(LogicalIntersect intersect) {
+ if (intersect.getHints().size() > 0) {
+ this.hintsCollect.add("Intersect:" + intersect.getHints());
+ }
+ return super.visit(intersect);
+ }
+
+ @Override public RelNode visit(LogicalMinus minus) {
+ if (minus.getHints().size() > 0) {
+ this.hintsCollect.add("Minus:" + minus.getHints());
+ }
+ return super.visit(minus);
+ }
+
+ @Override public RelNode visit(LogicalSort sort) {
+ if (sort.getHints().size() > 0) {
+ this.hintsCollect.add("Sort:" + sort.getHints());
+ }
+ return super.visit(sort);
+ }
+
+ @Override public RelNode visit(LogicalValues values) {
+ if (values.getHints().size() > 0) {
+ this.hintsCollect.add("Values:" + values.getHints());
+ }
+ return super.visit(values);
+ }
+
+ @Override public RelNode visit(RelNode other) {
+ if (other instanceof Window) {
+ Window window = (Window) other;
+ if (window.getHints().size() > 0) {
+ this.hintsCollect.add("Window:" + window.getHints());
+ }
+ }
+ return super.visit(other);
+ }
}
}
@@ -809,7 +916,8 @@ class SqlHintsConverterTest {
.hintStrategy("properties", HintPredicates.TABLE_SCAN)
.hintStrategy(
"resource", HintPredicates.or(
- HintPredicates.PROJECT, HintPredicates.AGGREGATE,
HintPredicates.CALC))
+ HintPredicates.PROJECT, HintPredicates.AGGREGATE,
+ HintPredicates.CALC, HintPredicates.VALUES,
HintPredicates.FILTER))
.hintStrategy("AGG_STRATEGY",
HintStrategy.builder(HintPredicates.AGGREGATE)
.optionChecker(
@@ -824,6 +932,10 @@ class SqlHintsConverterTest {
HintPredicates.or(
HintPredicates.and(HintPredicates.CORRELATE,
temporalJoinWithFixedTableName()),
HintPredicates.and(HintPredicates.JOIN,
joinWithFixedTableName())))
+ .hintStrategy("breakable", HintPredicates.SETOP)
+ .hintStrategy("async_merge", HintPredicates.SORT)
+ .hintStrategy("mini_batch",
+ HintPredicates.and(HintPredicates.WINDOW,
HintPredicates.PROJECT))
.hintStrategy("use_merge_join",
HintStrategy.builder(
HintPredicates.and(HintPredicates.JOIN,
joinWithFixedTableName()))
diff --git
a/core/src/test/resources/org/apache/calcite/test/SqlHintsConverterTest.xml
b/core/src/test/resources/org/apache/calcite/test/SqlHintsConverterTest.xml
index 3b4422a65b..052a6b72a0 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlHintsConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlHintsConverterTest.xml
@@ -46,7 +46,6 @@ Correlate:[[USE_HASH_JOIN inheritPath:[0] options:[ORDERS,
PRODUCTS_TEMPORAL]]]
]]>
</Resource>
</TestCase>
-
<TestCase name="testCrossCorrelateHints">
<Resource name="sql">
<![CDATA[select /*+ use_hash_join (orders, products_temporal) */ stream *
@@ -56,6 +55,20 @@ from orders, products_temporal for system_time as of
orders.rowtime]]>
<![CDATA[
Project:[[USE_HASH_JOIN inheritPath:[] options:[ORDERS, PRODUCTS_TEMPORAL]]]
Correlate:[[USE_HASH_JOIN inheritPath:[0] options:[ORDERS, PRODUCTS_TEMPORAL]]]
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testFilterHints">
+ <Resource name="sql">
+ <![CDATA[select /*+ resource(parallelism='3') */ avg(sal) as avg_sal,
deptno
+from emp group by deptno having avg(sal) > 5000]]>
+ </Resource>
+ <Resource name="hints">
+ <![CDATA[
+Project:[[RESOURCE inheritPath:[] options:{PARALLELISM=3}]]
+Filter:[[RESOURCE inheritPath:[0] options:{PARALLELISM=3}]]
+Aggregate:[[RESOURCE inheritPath:[0, 0] options:{PARALLELISM=3}]]
+Project:[[RESOURCE inheritPath:[0, 0, 0] options:{PARALLELISM=3}]]
]]>
</Resource>
</TestCase>
@@ -155,8 +168,10 @@ select /*+ resource(cpu='2') */ avg(e2.sal) from emp e2
where e2.deptno = d1.dep
<![CDATA[
Aggregate:[[RESOURCE inheritPath:[] options:{PARALLELISM=3}]]
Project:[[RESOURCE inheritPath:[0] options:{PARALLELISM=3}]]
+Filter:[[RESOURCE inheritPath:[0, 0] options:{PARALLELISM=3}]]
Aggregate:[[RESOURCE inheritPath:[] options:{CPU=2}], [RESOURCE
inheritPath:[0, 0, 0, 1] options:{PARALLELISM=3}]]
Project:[[RESOURCE inheritPath:[0] options:{CPU=2}], [RESOURCE inheritPath:[0,
0, 0, 1, 0] options:{PARALLELISM=3}]]
+Filter:[[RESOURCE inheritPath:[0, 0] options:{CPU=2}], [RESOURCE
inheritPath:[0, 0, 0, 1, 0, 0] options:{PARALLELISM=3}]]
]]>
</Resource>
</TestCase>
@@ -180,6 +195,20 @@ EnumerableProject(ENAME=[$3], JOB=[$4], SAL=[$7],
NAME=[$1])
EnumerableHashJoin(condition=[=($0, $9)], joinType=[inner])
EnumerableTableScan(table=[[CATALOG, SALES, DEPT]])
EnumerableTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testIntersectHints">
+ <Resource name="sql">
+ <![CDATA[select /*+ breakable */ deptno from
+(select ename, deptno from emp
+intersect all
+select name, deptno from dept)]]>
+ </Resource>
+ <Resource name="hints">
+ <![CDATA[
+Project:[[BREAKABLE inheritPath:[]]]
+Intersect:[[BREAKABLE inheritPath:[0]]]
]]>
</Resource>
</TestCase>
@@ -193,6 +222,20 @@ from emp join dept on emp.deptno = dept.deptno]]>
<![CDATA[
Project:[[USE_HASH_JOIN inheritPath:[] options:[R, S]], [USE_HASH_JOIN
inheritPath:[] options:[EMP, DEPT]]]
LogicalJoin:[[USE_HASH_JOIN inheritPath:[0] options:[EMP, DEPT]]]
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testMinusHints">
+ <Resource name="sql">
+ <![CDATA[select /*+ breakable */ deptno from
+(select ename, deptno from emp
+except all
+select name, deptno from dept)]]>
+ </Resource>
+ <Resource name="hints">
+ <![CDATA[
+Project:[[BREAKABLE inheritPath:[]]]
+Minus:[[BREAKABLE inheritPath:[0]]]
]]>
</Resource>
</TestCase>
@@ -249,6 +292,16 @@ on emp.deptno = dept.deptno]]>
Project:[[PROPERTIES inheritPath:[] options:{K1=v1, K2=v2}]]
TableScan:[[INDEX inheritPath:[] options:[IDX1, IDX2]], [PROPERTIES
inheritPath:[0, 0] options:{K1=v1, K2=v2}]]
TableScan:[[PROPERTIES inheritPath:[] options:{K1=v1, K2=v2}], [PROPERTIES
inheritPath:[0, 1] options:{K1=v1, K2=v2}]]
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testSortHints">
+ <Resource name="sql">
+ <![CDATA[select /*+ async_merge */ empno from emp order by empno, empno
desc]]>
+ </Resource>
+ <Resource name="hints">
+ <![CDATA[
+Sort:[[ASYNC_MERGE inheritPath:[]]]
]]>
</Resource>
</TestCase>
@@ -304,6 +357,20 @@ from emp left join dept on emp.deptno = dept.deptno)]]>
<![CDATA[
Project:[[RESOURCE inheritPath:[] options:{MEM=20Mb}], [RESOURCE
inheritPath:[] options:{PARALLELISM=3}], [NO_HASH_JOIN inheritPath:[]]]
LogicalJoin:[[NO_HASH_JOIN inheritPath:[0]]]
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testUnionHints">
+ <Resource name="sql">
+ <![CDATA[select /*+ breakable */ deptno from
+(select ename, deptno from emp
+union all
+select name, deptno from dept)]]>
+ </Resource>
+ <Resource name="hints">
+ <![CDATA[
+Project:[[BREAKABLE inheritPath:[]]]
+Union:[[BREAKABLE inheritPath:[0]]]
]]>
</Resource>
</TestCase>
@@ -329,6 +396,31 @@ LogicalProject(ENAME=[$1], JOB=[$2], SAL=[$5], NAME=[$10])
LogicalJoin(condition=[=($7, $9)], joinType=[inner])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testValuesHints">
+ <Resource name="sql">
+ <![CDATA[select /*+ resource(parallelism='3') */ a, max(b), max(b + 1)
+from (values (1, 2)) as t(a, b)
+group by a]]>
+ </Resource>
+ <Resource name="hints">
+ <![CDATA[
+Aggregate:[[RESOURCE inheritPath:[] options:{PARALLELISM=3}]]
+Project:[[RESOURCE inheritPath:[0] options:{PARALLELISM=3}]]
+Values:[[RESOURCE inheritPath:[0, 0] options:{PARALLELISM=3}]]
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testWindowHints">
+ <Resource name="sql">
+ <![CDATA[select /*+ mini_batch */ last_value(deptno)
+over (order by empno rows 2 following) from emp]]>
+ </Resource>
+ <Resource name="hints">
+ <![CDATA[
+Project:[[MINI_BATCH inheritPath:[]]]
]]>
</Resource>
</TestCase>