This is an automated email from the ASF dual-hosted git repository. liyafan pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/calcite.git
commit 9d02d458e3113915858d928b5fbccc7636c0644d Author: Jing Zhang <[email protected]> AuthorDate: Wed Dec 29 16:11:10 2021 +0800 [CALCITE-4967] Support SQL hints for temporal table join close apache/calcite#2664 --- .../adapter/enumerable/EnumerableCorrelate.java | 2 +- .../org/apache/calcite/adapter/jdbc/JdbcRules.java | 2 +- .../org/apache/calcite/rel/core/Correlate.java | 24 ++++++++- .../org/apache/calcite/rel/core/RelFactories.java | 12 +++-- .../apache/calcite/rel/hint/HintPredicates.java | 5 ++ .../calcite/rel/hint/NodeTypeHintPredicate.java | 8 ++- .../calcite/rel/logical/LogicalCorrelate.java | 41 ++++++++++++-- .../apache/calcite/rel/mutable/MutableRels.java | 5 +- .../calcite/rel/rules/JoinToCorrelateRule.java | 1 + .../sql2rel/RelStructuredTypeFlattener.java | 1 + .../apache/calcite/sql2rel/SqlToRelConverter.java | 2 +- .../java/org/apache/calcite/tools/RelBuilder.java | 4 +- .../org/apache/calcite/test/RelBuilderTest.java | 30 +++++++++++ .../org/apache/calcite/test/RelOptRulesTest.java | 3 +- .../apache/calcite/test/SqlHintsConverterTest.java | 62 +++++++++++++++++++++- .../org/apache/calcite/test/SqlToRelTestBase.java | 11 +++- .../apache/calcite/test/SqlHintsConverterTest.xml | 26 +++++++++ 17 files changed, 218 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCorrelate.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCorrelate.java index 6f88372..efb0267 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCorrelate.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCorrelate.java @@ -52,7 +52,7 @@ public class EnumerableCorrelate extends Correlate RelNode left, RelNode right, CorrelationId correlationId, ImmutableBitSet requiredColumns, JoinRelType joinType) { - super(cluster, traits, left, right, correlationId, requiredColumns, + super(cluster, traits, ImmutableList.of(), left, right, correlationId, requiredColumns, joinType); } diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java index 5b9f8b7..37fb747 100644 --- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java +++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java @@ -127,7 +127,7 @@ public class JdbcRules { }; static final RelFactories.CorrelateFactory CORRELATE_FACTORY = - (left, right, correlationId, requiredColumns, joinType) -> { + (left, right, hints, correlationId, requiredColumns, joinType) -> { throw new UnsupportedOperationException("JdbcCorrelate"); }; diff --git a/core/src/main/java/org/apache/calcite/rel/core/Correlate.java b/core/src/main/java/org/apache/calcite/rel/core/Correlate.java index d7d3602..f5194a1 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Correlate.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Correlate.java @@ -25,6 +25,8 @@ import org.apache.calcite.rel.BiRel; 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.metadata.RelMetadataQuery; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.validate.SqlValidatorUtil; @@ -68,12 +70,13 @@ import static java.util.Objects.requireNonNull; * * @see CorrelationId */ -public abstract class Correlate extends BiRel { +public abstract class Correlate extends BiRel implements Hintable { //~ Instance fields -------------------------------------------------------- protected final CorrelationId correlationId; protected final ImmutableBitSet requiredColumns; protected final JoinRelType joinType; + protected final ImmutableList<RelHint> hints; //~ Constructors ----------------------------------------------------------- @@ -91,6 +94,7 @@ public abstract class Correlate extends BiRel { protected Correlate( RelOptCluster cluster, RelTraitSet traitSet, + List<RelHint> hints, RelNode left, RelNode right, CorrelationId correlationId, @@ -101,9 +105,23 @@ public abstract class Correlate extends BiRel { this.joinType = requireNonNull(joinType, "joinType"); this.correlationId = requireNonNull(correlationId, "correlationId"); this.requiredColumns = requireNonNull(requiredColumns, "requiredColumns"); + this.hints = ImmutableList.copyOf(hints); assert isValid(Litmus.THROW, null); } + @Deprecated // to be removed before 2.0 + protected Correlate( + RelOptCluster cluster, + RelTraitSet traitSet, + RelNode left, + RelNode right, + CorrelationId correlationId, + ImmutableBitSet requiredColumns, + JoinRelType joinType) { + this(cluster, traitSet, ImmutableList.of(), left, right, + correlationId, requiredColumns, joinType); + } + /** * Creates a Correlate by parsing serialized output. * @@ -235,4 +253,8 @@ public abstract class Correlate extends BiRel { rowCount /* generate results */ + leftRowCount /* scan left results */, 0, 0).plus(rescanCost); } + + @Override public ImmutableList<RelHint> getHints() { + return hints; + } } diff --git a/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java b/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java index d6ab518..0998c99 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java +++ b/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java @@ -392,16 +392,18 @@ public class RelFactories { * <p>The result is typically a {@link Correlate}. */ public interface CorrelateFactory { + /** * Creates a correlate. * * @param left Left input * @param right Right input + * @param hints Hints * @param correlationId Variable name for the row of left input * @param requiredColumns Required columns * @param joinType Join type */ - RelNode createCorrelate(RelNode left, RelNode right, + RelNode createCorrelate(RelNode left, RelNode right, List<RelHint> hints, CorrelationId correlationId, ImmutableBitSet requiredColumns, JoinRelType joinType); } @@ -411,10 +413,10 @@ public class RelFactories { * {@link org.apache.calcite.rel.logical.LogicalCorrelate}. */ private static class CorrelateFactoryImpl implements CorrelateFactory { - @Override public RelNode createCorrelate(RelNode left, RelNode right, - CorrelationId correlationId, ImmutableBitSet requiredColumns, - JoinRelType joinType) { - return LogicalCorrelate.create(left, right, correlationId, + + @Override public RelNode createCorrelate(RelNode left, RelNode right, List<RelHint> hints, + CorrelationId correlationId, ImmutableBitSet requiredColumns, JoinRelType joinType) { + return LogicalCorrelate.create(left, right, hints, correlationId, requiredColumns, joinType); } } 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 266ccf4..97e6bf3 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 @@ -50,6 +50,11 @@ public abstract class HintPredicates { public static final HintPredicate CALC = new NodeTypeHintPredicate(NodeTypeHintPredicate.NodeType.CALC); + /** A hint predicate that indicates a hint can only be used to + * {@link org.apache.calcite.rel.core.Correlate} nodes. */ + public static final HintPredicate CORRELATE = + new NodeTypeHintPredicate(NodeTypeHintPredicate.NodeType.CORRELATE); + /** * 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 02cccc5..cdcd7ba 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 @@ -19,6 +19,7 @@ package org.apache.calcite.rel.hint; 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.Join; import org.apache.calcite.rel.core.Project; import org.apache.calcite.rel.core.TableScan; @@ -63,7 +64,12 @@ public class NodeTypeHintPredicate implements HintPredicate { /** * The hint would be propagated to the Calc nodes. */ - CALC(Calc.class); + CALC(Calc.class), + + /** + * The hint would be propagated to the Correlate nodes. + */ + CORRELATE(Correlate.class); /** Relational expression clazz that the hint can apply to. */ @SuppressWarnings("ImmutableEnumChecker") diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java index d73ce12..2929f03 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java @@ -25,8 +25,13 @@ import org.apache.calcite.rel.RelShuttle; import org.apache.calcite.rel.core.Correlate; import org.apache.calcite.rel.core.CorrelationId; import org.apache.calcite.rel.core.JoinRelType; +import org.apache.calcite.rel.hint.RelHint; import org.apache.calcite.util.ImmutableBitSet; +import com.google.common.collect.ImmutableList; + +import java.util.List; + import static java.util.Objects.requireNonNull; /** @@ -58,6 +63,7 @@ public final class LogicalCorrelate extends Correlate { public LogicalCorrelate( RelOptCluster cluster, RelTraitSet traitSet, + List<RelHint> hints, RelNode left, RelNode right, CorrelationId correlationId, @@ -66,6 +72,7 @@ public final class LogicalCorrelate extends Correlate { super( cluster, traitSet, + hints, left, right, correlationId, @@ -73,11 +80,25 @@ public final class LogicalCorrelate extends Correlate { joinType); } + @Deprecated // to be removed before 2.0 + public LogicalCorrelate( + RelOptCluster cluster, + RelTraitSet traitSet, + RelNode left, + RelNode right, + CorrelationId correlationId, + ImmutableBitSet requiredColumns, + JoinRelType joinType) { + this(cluster, traitSet, ImmutableList.of(), left, right, + correlationId, requiredColumns, joinType); + } + /** * Creates a LogicalCorrelate by parsing serialized output. */ public LogicalCorrelate(RelInput input) { - this(input.getCluster(), input.getTraitSet(), input.getInputs().get(0), + this(input.getCluster(), input.getTraitSet(), ImmutableList.of(), + input.getInputs().get(0), input.getInputs().get(1), new CorrelationId( (Integer) requireNonNull(input.get("correlation"), "correlation")), @@ -86,26 +107,38 @@ public final class LogicalCorrelate extends Correlate { } /** Creates a LogicalCorrelate. */ - public static LogicalCorrelate create(RelNode left, RelNode right, + public static LogicalCorrelate create(RelNode left, RelNode right, List<RelHint> hints, CorrelationId correlationId, ImmutableBitSet requiredColumns, JoinRelType joinType) { final RelOptCluster cluster = left.getCluster(); final RelTraitSet traitSet = cluster.traitSetOf(Convention.NONE); - return new LogicalCorrelate(cluster, traitSet, left, right, correlationId, + return new LogicalCorrelate(cluster, traitSet, hints, left, right, correlationId, requiredColumns, joinType); } + @Deprecated // to be removed before 2.0 + public static LogicalCorrelate create(RelNode left, RelNode right, + CorrelationId correlationId, ImmutableBitSet requiredColumns, + JoinRelType joinType) { + return create(left, right, ImmutableList.of(), correlationId, requiredColumns, joinType); + } + //~ Methods ---------------------------------------------------------------- @Override public LogicalCorrelate copy(RelTraitSet traitSet, RelNode left, RelNode right, CorrelationId correlationId, ImmutableBitSet requiredColumns, JoinRelType joinType) { assert traitSet.containsIfApplicable(Convention.NONE); - return new LogicalCorrelate(getCluster(), traitSet, left, right, + return new LogicalCorrelate(getCluster(), traitSet, hints, left, right, correlationId, requiredColumns, joinType); } @Override public RelNode accept(RelShuttle shuttle) { return shuttle.visit(this); } + + @Override public RelNode withHints(List<RelHint> hintList) { + return new LogicalCorrelate(getCluster(), traitSet, hintList, left, right, + correlationId, requiredColumns, joinType); + } } diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java index ab8902e..708e881 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java @@ -60,6 +60,8 @@ import org.apache.calcite.util.mapping.Mapping; import org.apache.calcite.util.mapping.MappingType; import org.apache.calcite.util.mapping.Mappings; +import com.google.common.collect.ImmutableList; + import org.checkerframework.checker.nullness.qual.Nullable; import java.util.AbstractList; @@ -297,7 +299,8 @@ public abstract class MutableRels { case CORRELATE: final MutableCorrelate correlate = (MutableCorrelate) node; return LogicalCorrelate.create(fromMutable(correlate.getLeft(), relBuilder), - fromMutable(correlate.getRight(), relBuilder), correlate.correlationId, + fromMutable(correlate.getRight(), relBuilder), + ImmutableList.of(), correlate.correlationId, correlate.requiredColumns, correlate.joinType); case UNION: final MutableUnion union = (MutableUnion) node; diff --git a/core/src/main/java/org/apache/calcite/rel/rules/JoinToCorrelateRule.java b/core/src/main/java/org/apache/calcite/rel/rules/JoinToCorrelateRule.java index 154a200..296433e 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/JoinToCorrelateRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/JoinToCorrelateRule.java @@ -124,6 +124,7 @@ public class JoinToCorrelateRule RelNode newRel = LogicalCorrelate.create(left, relBuilder.build(), + join.getHints(), correlationId, requiredColumns.build(), join.getJoinType()); diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java b/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java index 2ad5955..f4bf28a 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java @@ -482,6 +482,7 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor { LogicalCorrelate newRel = LogicalCorrelate.create(getNewForOldRel(rel.getLeft()), getNewForOldRel(rel.getRight()), + rel.getHints(), rel.getCorrelationId(), newPos.build(), rel.getJoinType()); diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java index 355614a..e6e79c7 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java @@ -2756,7 +2756,7 @@ public class SqlToRelConverter { .union(p.requiredColumns); } - return LogicalCorrelate.create(leftRel, innerRel, + return LogicalCorrelate.create(leftRel, innerRel, ImmutableList.of(), p.id, requiredCols, joinType); } diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java index 7d4b408..9c178c0 100644 --- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java +++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java @@ -2791,7 +2791,7 @@ public class RelBuilder { } final ImmutableBitSet requiredColumns = RelOptUtil.correlationColumns(id, right.rel); join = - struct.correlateFactory.createCorrelate(left.rel, right.rel, id, + struct.correlateFactory.createCorrelate(left.rel, right.rel, ImmutableList.of(), id, requiredColumns, joinType); } else { RelNode join0 = @@ -2836,7 +2836,7 @@ public class RelBuilder { Frame left = stack.pop(); final RelNode correlate = - struct.correlateFactory.createCorrelate(left.rel, right.rel, + struct.correlateFactory.createCorrelate(left.rel, right.rel, ImmutableList.of(), correlationId, ImmutableBitSet.of(requiredOrdinals), joinType); final ImmutableList.Builder<Field> fields = ImmutableList.builder(); 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 9f08de9..6887198 100644 --- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java @@ -4403,6 +4403,10 @@ public class RelBuilderTest { final RelHint noHashJoinHint = RelHint.builder("NO_HASH_JOIN") .inheritPath(0) .build(); + final RelHint hashJoinHint = RelHint.builder("USE_HASH_JOIN") + .hintOption("orders") + .hintOption("products_temporal") + .build(); final RelBuilder builder = RelBuilder.create(config().build()); // Equivalent SQL: // SELECT * @@ -4440,6 +4444,32 @@ public class RelBuilderTest { .hints(noHashJoinHint) .build(); assertThat(root2, hasHints("[[NO_HASH_JOIN inheritPath:[0]]]")); + + // Equivalent SQL: + // SELECT * + // FROM orders + // JOIN products_temporal FOR SYSTEM_TIME AS OF orders.rowtime + // ON orders.product = products_temporal.id + RelNode left = builder.scan("orders").build(); + RelNode right = builder.scan("products_temporal").build(); + RexNode period = builder.getRexBuilder().makeFieldAccess( + builder.getRexBuilder().makeCorrel(left.getRowType(), new CorrelationId(0)), + 0); + RelNode root3 = + builder + .push(left) + .push(right) + .snapshot(period) + .correlate( + JoinRelType.INNER, + new CorrelationId(0), + builder.field(2, 0, "ROWTIME"), + builder.field(2, 0, "ID"), + builder.field(2, 0, "PRODUCT")) + .hints(hashJoinHint) + .build(); + assertThat(root3, + hasHints("[[USE_HASH_JOIN inheritPath:[] options:[orders, products_temporal]]]")); } @Test void testHintsOnEmptyStack() { diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java index 864cb95..46e1ab2 100644 --- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java @@ -1743,7 +1743,7 @@ class RelOptRulesTest extends RelOptTestBase { .project(b.field(0), b.getRexBuilder().makeFieldAccess(rexCorrel, 0)).build(); LogicalCorrelate correlate = new LogicalCorrelate(left.getCluster(), - left.getTraitSet(), left, right, correlationId, + left.getTraitSet(), ImmutableList.of(), left, right, correlationId, ImmutableBitSet.of(0), type); b.push(correlate); @@ -4061,6 +4061,7 @@ class RelOptRulesTest extends RelOptTestBase { CustomCorrelate customCorrelate = new CustomCorrelate( logicalCorrelate.getCluster(), logicalCorrelate.getTraitSet(), + logicalCorrelate.getHints(), logicalCorrelate.getLeft(), logicalCorrelate.getRight(), logicalCorrelate.getCorrelationId(), 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 9084efa..c2c7783 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlHintsConverterTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlHintsConverterTest.java @@ -39,8 +39,10 @@ import org.apache.calcite.rel.RelVisitor; import org.apache.calcite.rel.convert.ConverterRule; import org.apache.calcite.rel.core.Aggregate; import org.apache.calcite.rel.core.Calc; +import org.apache.calcite.rel.core.Filter; 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.hint.HintPredicate; import org.apache.calcite.rel.hint.HintPredicates; @@ -49,6 +51,7 @@ import org.apache.calcite.rel.hint.HintStrategyTable; 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.LogicalJoin; import org.apache.calcite.rel.logical.LogicalProject; import org.apache.calcite.rel.rules.CoreRules; @@ -75,8 +78,10 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.function.Predicate; import java.util.function.UnaryOperator; import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsIn.in; @@ -145,6 +150,19 @@ class SqlHintsConverterTest extends SqlToRelTestBase { sql(sql).ok(); } + @Test void testCorrelateHints() { + final String sql = "select /*+ use_hash_join (orders, products_temporal) */ stream *\n" + + "from orders join products_temporal for system_time as of orders.rowtime\n" + + "on orders.productid = products_temporal.productid and orders.orderId is not null"; + sql(sql).ok(); + } + + @Test void testCrossCorrelateHints() { + final String sql = "select /*+ use_hash_join (orders, products_temporal) */ stream *\n" + + "from orders, products_temporal for system_time as of orders.rowtime"; + 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" @@ -693,6 +711,13 @@ class SqlHintsConverterTest extends SqlToRelTestBase { } return super.visit(aggregate); } + + @Override public RelNode visit(LogicalCorrelate correlate) { + if (correlate.getHints().size() > 0) { + this.hintsCollect.add("Correlate:" + correlate.getHints().toString()); + } + return super.visit(correlate); + } } } @@ -771,7 +796,9 @@ class SqlHintsConverterTest extends SqlToRelTestBase { + "allowed options: [ONE_PHASE, TWO_PHASE]", hint.hintName)).build()) .hintStrategy("use_hash_join", - HintPredicates.and(HintPredicates.JOIN, joinWithFixedTableName())) + HintPredicates.or( + HintPredicates.and(HintPredicates.CORRELATE, temporalJoinWithFixedTableName()), + HintPredicates.and(HintPredicates.JOIN, joinWithFixedTableName()))) .hintStrategy("use_merge_join", HintStrategy.builder( HintPredicates.and(HintPredicates.JOIN, joinWithFixedTableName())) @@ -779,6 +806,39 @@ class SqlHintsConverterTest extends SqlToRelTestBase { .build(); } + /** Returns a {@link HintPredicate} for temporal join with specified table references. */ + private static HintPredicate temporalJoinWithFixedTableName() { + return (hint, rel) -> { + if (!(rel instanceof LogicalCorrelate)) { + return false; + } + LogicalCorrelate correlate = (LogicalCorrelate) rel; + Predicate<RelNode> isScan = r -> r instanceof TableScan; + if (!(isScan.test(correlate.getLeft()))) { + return false; + } + RelNode rightInput = correlate.getRight(); + Predicate<RelNode> isSnapshotOnScan = r -> r instanceof Snapshot + && isScan.test(((Snapshot) r).getInput()); + RelNode rightScan; + if (isSnapshotOnScan.test(rightInput)) { + rightScan = ((Snapshot) rightInput).getInput(); + } else if (rightInput instanceof Filter + && isSnapshotOnScan.test(((Filter) rightInput).getInput())) { + rightScan = ((Snapshot) ((Filter) rightInput).getInput()).getInput(); + } else { + // right child of correlate must be a snapshot on table scan directly or a Filter which + // input is snapshot on table scan + return false; + } + final List<String> tableNames = hint.listOptions; + final List<String> inputTables = Stream.of(correlate.getLeft(), rightScan) + .map(scan -> Util.last(scan.getTable().getQualifiedName())) + .collect(Collectors.toList()); + return equalsStringList(inputTables, tableNames); + }; + } + /** Returns a {@link HintPredicate} for join with specified table references. */ private static HintPredicate joinWithFixedTableName() { return (hint, rel) -> { diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java b/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java index 5f9a0ad..4feeb39 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java +++ b/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java @@ -41,6 +41,7 @@ import org.apache.calcite.rel.core.Correlate; import org.apache.calcite.rel.core.CorrelationId; import org.apache.calcite.rel.core.JoinRelType; import org.apache.calcite.rel.core.RelFactories; +import org.apache.calcite.rel.hint.RelHint; import org.apache.calcite.rel.logical.LogicalTableScan; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; @@ -1078,23 +1079,29 @@ public abstract class SqlToRelTestBase { public CustomCorrelate( RelOptCluster cluster, RelTraitSet traits, + List<RelHint> hints, RelNode left, RelNode right, CorrelationId correlationId, ImmutableBitSet requiredColumns, JoinRelType joinType) { - super(cluster, traits, left, right, correlationId, requiredColumns, joinType); + super(cluster, traits, hints, left, right, correlationId, requiredColumns, joinType); } @Override public Correlate copy(RelTraitSet traitSet, RelNode left, RelNode right, CorrelationId correlationId, ImmutableBitSet requiredColumns, JoinRelType joinType) { - return new CustomCorrelate(getCluster(), traitSet, left, right, + return new CustomCorrelate(getCluster(), traitSet, hints, left, right, correlationId, requiredColumns, joinType); } @Override public RelNode accept(RelShuttle shuttle) { return shuttle.visit(this); } + + @Override public RelNode withHints(List<RelHint> hintList) { + return new CustomCorrelate(getCluster(), traitSet, hintList, left, right, + correlationId, requiredColumns, joinType); + } } } 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 4bb56c2..dbed18a 100644 --- a/core/src/test/resources/org/apache/calcite/test/SqlHintsConverterTest.xml +++ b/core/src/test/resources/org/apache/calcite/test/SqlHintsConverterTest.xml @@ -33,6 +33,32 @@ Project:[[RESOURCE inheritPath:[0, 0, 0, 0] options:{MEM=1024}]] ]]> </Resource> </TestCase> + <TestCase name="testCorrelateHints"> + <Resource name="sql"> + <![CDATA[select /*+ use_hash_join (orders, products_temporal) */ stream * +from orders join products_temporal for system_time as of orders.rowtime +on orders.productid = products_temporal.productid and orders.orderId is not null]]> + </Resource> + <Resource name="hints"> + <![CDATA[ +Project:[[USE_HASH_JOIN inheritPath:[] options:[ORDERS, PRODUCTS_TEMPORAL]]] +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 * +from orders, products_temporal for system_time as of orders.rowtime]]> + </Resource> + <Resource name="hints"> + <![CDATA[ +Project:[[USE_HASH_JOIN inheritPath:[] options:[ORDERS, PRODUCTS_TEMPORAL]]] +Correlate:[[USE_HASH_JOIN inheritPath:[0] options:[ORDERS, PRODUCTS_TEMPORAL]]] +]]> + </Resource> + </TestCase> <TestCase name="testFourLevelNestedQueryHint"> <Resource name="sql"> <