|
| 1 | +/* |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more |
| 3 | + * contributor license agreements. See the NOTICE file distributed with |
| 4 | + * this work for additional information regarding copyright ownership. |
| 5 | + * The ASF licenses this file to you under the Apache License, Version 2.0 |
| 6 | + * (the "License"); you may not use this file except in compliance with |
| 7 | + * the License. You may obtain a copy of the License at |
| 8 | + * |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | + * |
| 11 | + * Unless required by applicable law or agreed to in writing, software |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | + * See the License for the specific language governing permissions and |
| 15 | + * limitations under the License. |
| 16 | + */ |
| 17 | +package org.apache.calcite.rel.rules; |
| 18 | + |
| 19 | +import org.apache.calcite.plan.RelOptRuleCall; |
| 20 | +import org.apache.calcite.plan.RelOptUtil.Exists; |
| 21 | +import org.apache.calcite.plan.RelRule; |
| 22 | +import org.apache.calcite.plan.hep.HepRelVertex; |
| 23 | +import org.apache.calcite.rel.RelNode; |
| 24 | +import org.apache.calcite.rel.core.CorrelationId; |
| 25 | +import org.apache.calcite.rel.core.Intersect; |
| 26 | +import org.apache.calcite.rel.logical.LogicalIntersect; |
| 27 | +import org.apache.calcite.rel.type.RelDataTypeField; |
| 28 | +import org.apache.calcite.rex.RexBuilder; |
| 29 | +import org.apache.calcite.rex.RexNode; |
| 30 | +import org.apache.calcite.rex.RexSubQuery; |
| 31 | +import org.apache.calcite.rex.RexUtil; |
| 32 | +import org.apache.calcite.sql.fun.SqlStdOperatorTable; |
| 33 | +import org.apache.calcite.tools.RelBuilder; |
| 34 | +import org.apache.calcite.tools.RelBuilderFactory; |
| 35 | + |
| 36 | +import com.google.common.collect.ImmutableSet; |
| 37 | + |
| 38 | +import org.immutables.value.Value; |
| 39 | + |
| 40 | +import java.util.ArrayList; |
| 41 | +import java.util.List; |
| 42 | +import java.util.stream.Collectors; |
| 43 | + |
| 44 | +/** |
| 45 | + * Planner rule that translates a {@link Intersect} |
| 46 | + * (<code>all</code> = <code>false</code>) |
| 47 | + * into a {@link Exists}. |
| 48 | + * |
| 49 | + * @see CoreRules#INTERSECT_TO_EXISTS |
| 50 | + */ |
| 51 | +@Value.Enclosing |
| 52 | +public class IntersectToExistsRule |
| 53 | + extends RelRule<IntersectToExistsRule.Config> |
| 54 | + implements TransformationRule { |
| 55 | + |
| 56 | + /** Creates an IntersectToExistRule. */ |
| 57 | + protected IntersectToExistsRule(Config config) { |
| 58 | + super(config); |
| 59 | + } |
| 60 | + |
| 61 | + @Deprecated // to be removed before 2.0 |
| 62 | + public IntersectToExistsRule(Class<? extends Intersect> intersectClass, |
| 63 | + RelBuilderFactory relBuilderFactory) { |
| 64 | + this(Config.DEFAULT.withRelBuilderFactory(relBuilderFactory) |
| 65 | + .as(Config.class) |
| 66 | + .withOperandFor(intersectClass)); |
| 67 | + } |
| 68 | + |
| 69 | + //~ Methods ---------------------------------------------------------------- |
| 70 | + |
| 71 | + @Override public void onMatch(RelOptRuleCall call) { |
| 72 | + final Intersect intersect = call.rel(0); |
| 73 | + final RelBuilder builder = call.builder(); |
| 74 | + final RexBuilder rexBuilder = builder.getRexBuilder(); |
| 75 | + |
| 76 | + // Recursive processing of all inputs as logical operators |
| 77 | + List<RelNode> inputs = new ArrayList<>(); |
| 78 | + for (RelNode rel : intersect.getInputs()) { |
| 79 | + inputs.add(toLogicalRel(rel)); |
| 80 | + } |
| 81 | + |
| 82 | + // Get all column indices of intersect |
| 83 | + List<Integer> fieldIndices = intersect.getRowType().getFieldList() |
| 84 | + .stream().map(RelDataTypeField::getIndex) |
| 85 | + .collect(Collectors.toList()); |
| 86 | + |
| 87 | + RelNode current = inputs.get(0); |
| 88 | + |
| 89 | + // Iterate over the inputs and apply EXISTS subquery |
| 90 | + for (int i = 1; i < inputs.size(); i++) { |
| 91 | + RelNode nextInput = inputs.get(i); |
| 92 | + |
| 93 | + // Create correlation variable |
| 94 | + CorrelationId correlationId = intersect.getCluster().createCorrel(); |
| 95 | + RexNode correlVariable = |
| 96 | + rexBuilder.makeCorrel(nextInput.getRowType(), correlationId); |
| 97 | + |
| 98 | + // Create EXISTS subquery condition |
| 99 | + List<RexNode> conditions = new ArrayList<>(); |
| 100 | + for (int fieldIndex : fieldIndices) { |
| 101 | + RexNode outerField = rexBuilder.makeInputRef(current, fieldIndex); |
| 102 | + RexNode innerField = |
| 103 | + rexBuilder.makeFieldAccess(correlVariable, fieldIndex); |
| 104 | + conditions.add( |
| 105 | + rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, |
| 106 | + outerField, innerField)); |
| 107 | + } |
| 108 | + RexNode condition = RexUtil.composeConjunction(rexBuilder, conditions); |
| 109 | + |
| 110 | + // Build EXISTS subquery |
| 111 | + RelNode existsSubQuery = builder.push(nextInput) |
| 112 | + .filter(condition) |
| 113 | + .project(builder.fields(fieldIndices)) |
| 114 | + .build(); |
| 115 | + |
| 116 | + // Apply EXISTS subquery to the current relation |
| 117 | + current = builder.push(current) |
| 118 | + .filter(ImmutableSet.of(correlationId), |
| 119 | + RexSubQuery.exists(existsSubQuery)) |
| 120 | + .build(); |
| 121 | + } |
| 122 | + |
| 123 | + RelNode result = builder.push(current) |
| 124 | + .project(builder.fields(fieldIndices)) |
| 125 | + .distinct() |
| 126 | + .build(); |
| 127 | + |
| 128 | + call.transformTo(result); |
| 129 | + } |
| 130 | + |
| 131 | + /** |
| 132 | + * Recursively convert RelNodes into logical operators. |
| 133 | + * |
| 134 | + * @param node Relnode that may contain HepRelVertex |
| 135 | + * @return Relnode only contains Logical RelNode */ |
| 136 | + private RelNode toLogicalRel(RelNode node) { |
| 137 | + if (node instanceof HepRelVertex) { |
| 138 | + node = ((HepRelVertex) node).getCurrentRel(); |
| 139 | + List<RelNode> inputs = new ArrayList<>(); |
| 140 | + for (RelNode input : node.getInputs()) { |
| 141 | + inputs.add(toLogicalRel(input)); |
| 142 | + } |
| 143 | + return node.copy(node.getTraitSet(), inputs); |
| 144 | + } |
| 145 | + throw new AssertionError("Not support type " + node.getClass()); |
| 146 | + } |
| 147 | + |
| 148 | + /** Rule configuration. */ |
| 149 | + @Value.Immutable |
| 150 | + public interface Config extends RelRule.Config { |
| 151 | + Config DEFAULT = ImmutableIntersectToExistsRule.Config.of() |
| 152 | + .withOperandFor(LogicalIntersect.class); |
| 153 | + |
| 154 | + @Override default IntersectToExistsRule toRule() { |
| 155 | + return new IntersectToExistsRule(this); |
| 156 | + } |
| 157 | + |
| 158 | + /** Defines an operand tree for the given classes. */ |
| 159 | + default Config withOperandFor(Class<? extends Intersect> intersectClass) { |
| 160 | + return withOperandSupplier(b -> b.operand(intersectClass).anyInputs()) |
| 161 | + .as(Config.class); |
| 162 | + } |
| 163 | + } |
| 164 | +} |
0 commit comments