17 package org.apache.calcite.rel.rules;
19 import org.apache.calcite.plan.RelOptRuleCall;
20 import org.apache.calcite.plan.hep.HepRelVertex;
21 import org.apache.calcite.rel.RelNode;
22 import org.apache.calcite.rel.core.Join;
23 import org.apache.calcite.rel.core.JoinRelType;
24 import org.apache.calcite.rel.logical.LogicalFilter;
25 import org.apache.calcite.rel.logical.LogicalJoin;
26 import org.apache.calcite.rel.logical.LogicalProject;
27 import org.apache.calcite.rel.logical.LogicalTableScan;
28 import org.apache.calcite.rex.RexCall;
29 import org.apache.calcite.rex.RexInputRef;
30 import org.apache.calcite.rex.RexLiteral;
31 import org.apache.calcite.rex.RexNode;
32 import org.apache.calcite.sql.SqlKind;
33 import org.apache.calcite.tools.RelBuilder;
34 import org.apache.calcite.tools.RelBuilderFactory;
35 import org.apache.calcite.util.mapping.Mappings;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
39 import java.util.ArrayList;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.List;
75 LoggerFactory.getLogger(OuterJoinOptViaNullRejectionRule.class);
78 super(operand(RelNode.class, operand(Join.class, null, any())),
80 "OuterJoinOptViaNullRejectionRule");
85 visitedJoinMemo.clear();
89 public void onMatch(RelOptRuleCall call) {
90 RelNode parentNode = call.rel(0);
91 LogicalJoin
join = (LogicalJoin) call.rel(1);
92 String condString = join.getCondition().
toString();
96 visitedJoinMemo.add(condString);
98 if (!(join.getCondition() instanceof RexCall)) {
101 if (join.getJoinType() == JoinRelType.INNER || join.getJoinType() == JoinRelType.SEMI
102 || join.getJoinType() == JoinRelType.ANTI) {
105 RelNode joinLeftChild = ((HepRelVertex) join.getLeft()).getCurrentRel();
106 RelNode joinRightChild = ((HepRelVertex) join.getRight()).getCurrentRel();
107 if (joinLeftChild instanceof LogicalProject) {
110 if (!(joinRightChild instanceof LogicalTableScan)) {
115 RexCall joinCond = (RexCall) join.getCondition();
116 Set<Integer> leftJoinCols =
new HashSet<>();
117 Set<Integer> rightJoinCols =
new HashSet<>();
118 Map<Integer, String> leftJoinColToColNameMap =
new HashMap<>();
119 Map<Integer, String> rightJoinColToColNameMap =
new HashMap<>();
120 Set<Integer> originalLeftJoinCols =
new HashSet<>();
121 Set<Integer> originalRightJoinCols =
new HashSet<>();
122 Map<Integer, String> originalLeftJoinColToColNameMap =
new HashMap<>();
123 Map<Integer, String> originalRightJoinColToColNameMap =
new HashMap<>();
124 List<RexCall> capturedFilterPredFromJoin =
new ArrayList<>();
125 if (joinCond.getKind() == SqlKind.EQUALS) {
130 leftJoinColToColNameMap,
131 rightJoinColToColNameMap,
132 originalLeftJoinCols,
133 originalRightJoinCols,
134 originalLeftJoinColToColNameMap,
135 originalRightJoinColToColNameMap);
137 }
else if (joinCond.getKind() == SqlKind.AND) {
138 for (RexNode
n : joinCond.getOperands()) {
139 if (
n instanceof RexCall) {
140 RexCall op = (RexCall)
n;
141 if (op.getOperands().size() > 2
142 && op.getOperands().get(1) instanceof RexLiteral) {
145 capturedFilterPredFromJoin.add(op);
152 leftJoinColToColNameMap,
153 rightJoinColToColNameMap,
154 originalLeftJoinCols,
155 originalRightJoinCols,
156 originalLeftJoinColToColNameMap,
157 originalRightJoinColToColNameMap);
162 if (leftJoinCols.isEmpty() || rightJoinCols.isEmpty()) {
167 RelNode
root = call.getPlanner().getRoot();
168 List<LogicalFilter> collectedFilterNodes =
new ArrayList<>();
169 RelNode curNode =
root;
170 final RelBuilder relBuilder = call.builder();
173 if (collectedFilterNodes.isEmpty()) {
182 Set<Integer> nullRejectedLeftJoinCols =
new HashSet<>();
183 Set<Integer> nullRejectedRightJoinCols =
new HashSet<>();
184 boolean hasExprsConnectedViaOR =
false;
185 for (LogicalFilter filter : collectedFilterNodes) {
186 RexNode node = filter.getCondition();
187 if (node instanceof RexCall) {
188 RexCall curExpr = (RexCall) node;
190 if (curExpr.getKind() == SqlKind.OR) {
191 hasExprsConnectedViaOR =
true;
194 if (curExpr.getKind() == SqlKind.AND) {
195 for (RexNode
n : curExpr.getOperands()) {
196 if (
n instanceof RexCall) {
197 RexCall c = (RexCall)
n;
199 RexInputRef col = (RexInputRef) c.getOperands().get(0);
200 int colId = col.getIndex();
201 boolean leftFilter = leftJoinCols.contains(colId);
202 boolean rightFilter = rightJoinCols.contains(colId);
203 if (leftFilter && rightFilter) {
210 nullRejectedLeftJoinCols,
211 nullRejectedRightJoinCols,
212 leftJoinColToColNameMap,
213 rightJoinColToColNameMap);
218 if (curExpr instanceof RexCall) {
220 RexInputRef col = (RexInputRef) curExpr.getOperands().get(0);
221 int colId = col.getIndex();
222 boolean leftFilter = leftJoinCols.contains(colId);
223 boolean rightFilter = rightJoinCols.contains(colId);
224 if (leftFilter && rightFilter) {
231 nullRejectedLeftJoinCols,
232 nullRejectedRightJoinCols,
233 leftJoinColToColNameMap,
234 rightJoinColToColNameMap);
243 if (hasExprsConnectedViaOR) {
247 if (!capturedFilterPredFromJoin.isEmpty()) {
248 for (RexCall c : capturedFilterPredFromJoin) {
249 RexInputRef col = (RexInputRef) c.getOperands().get(0);
250 int colId = col.getIndex();
251 String colName = join.getRowType().getFieldNames().get(colId);
254 if (originalLeftJoinColToColNameMap.containsKey(colId)
255 && originalLeftJoinColToColNameMap.get(colId).equals(colName)) {
258 if (originalRightJoinColToColNameMap.containsKey(colId)
259 && originalRightJoinColToColNameMap.get(colId).equals(colName)) {
263 nullRejectedLeftJoinCols.add(colId);
264 }
else if (r && !l) {
265 nullRejectedRightJoinCols.add(colId);
272 Boolean leftNullRejected =
false;
273 Boolean rightNullRejected =
false;
274 if (!nullRejectedLeftJoinCols.isEmpty()
275 && leftJoinCols.equals(nullRejectedLeftJoinCols)) {
276 leftNullRejected =
true;
278 if (!nullRejectedRightJoinCols.isEmpty()
279 && rightJoinCols.equals(nullRejectedRightJoinCols)) {
280 rightNullRejected =
true;
284 RelNode newJoinNode = null;
285 Boolean needTransform =
false;
286 if (join.getJoinType() == JoinRelType.FULL) {
288 if (leftNullRejected && !rightNullRejected) {
289 newJoinNode = join.copy(join.getTraitSet(),
294 join.isSemiJoinDone());
295 needTransform =
true;
299 if (leftNullRejected && rightNullRejected) {
300 newJoinNode = join.copy(join.getTraitSet(),
305 join.isSemiJoinDone());
306 needTransform =
true;
308 }
else if (join.getJoinType() == JoinRelType.LEFT) {
310 if (rightNullRejected) {
311 newJoinNode = join.copy(join.getTraitSet(),
316 join.isSemiJoinDone());
317 needTransform =
true;
321 relBuilder.push(newJoinNode);
322 parentNode.replaceInput(0, newJoinNode);
323 call.transformTo(parentNode);
330 Set<Integer> leftJoinCols,
331 Set<Integer> rightJoinCols,
332 Map<Integer, String> leftJoinColToColNameMap,
333 Map<Integer, String> rightJoinColToColNameMap,
334 Set<Integer> originalLeftJoinCols,
335 Set<Integer> originalRightJoinCols,
336 Map<Integer, String> originalLeftJoinColToColNameMap,
337 Map<Integer, String> originalRightJoinColToColNameMap) {
338 if (joinCond.getOperands().size() != 2
339 || !(joinCond.getOperands().get(0) instanceof RexInputRef)
340 || !(joinCond.getOperands().
get(1) instanceof RexInputRef)) {
343 RexInputRef leftJoinCol = (RexInputRef) joinCond.getOperands().get(0);
344 RexInputRef rightJoinCol = (RexInputRef) joinCond.getOperands().get(1);
345 originalLeftJoinCols.add(leftJoinCol.getIndex());
346 originalRightJoinCols.add(rightJoinCol.getIndex());
347 originalLeftJoinColToColNameMap.put(leftJoinCol.getIndex(),
348 joinOp.getRowType().getFieldNames().
get(leftJoinCol.getIndex()));
349 originalRightJoinColToColNameMap.put(rightJoinCol.getIndex(),
350 joinOp.getRowType().getFieldNames().
get(rightJoinCol.getIndex()));
351 if (leftJoinCol.getIndex() > rightJoinCol.getIndex()) {
352 leftJoinCol = (RexInputRef) joinCond.getOperands().get(1);
353 rightJoinCol = (RexInputRef) joinCond.getOperands().get(0);
355 int originalLeftColOffset =
traceColOffset(joinOp.getLeft(), leftJoinCol, 0);
358 joinOp.getLeft().getRowType().getFieldCount());
359 if (originalLeftColOffset != -1) {
363 originalLeftColOffset == -1 ? leftJoinCol.getIndex() : originalLeftColOffset;
364 int rightColOffset = originalRightColOffset == -1 ? rightJoinCol.getIndex()
365 : originalRightColOffset;
366 String leftJoinColName = joinOp.getRowType().getFieldNames().get(leftColOffset);
367 String rightJoinColName =
368 joinOp.getRowType().getFieldNames().get(rightJoinCol.getIndex());
369 leftJoinCols.add(leftColOffset);
370 rightJoinCols.add(rightColOffset);
371 leftJoinColToColNameMap.put(leftColOffset, leftJoinColName);
372 rightJoinColToColNameMap.put(rightColOffset, rightJoinColName);
377 LogicalFilter targetFilter,
378 Set<Integer> nullRejectedLeftJoinCols,
379 Set<Integer> nullRejectedRightJoinCols,
380 Map<Integer, String> leftJoinColToColNameMap,
381 Map<Integer, String> rightJoinColToColNameMap) {
383 RexInputRef col = (RexInputRef) call.getOperands().get(0);
384 int colId = col.getIndex();
385 String colName = targetFilter.getRowType().getFieldNames().get(colId);
388 if (leftJoinColToColNameMap.containsKey(colId)
389 && leftJoinColToColNameMap.get(colId).equals(colName)) {
392 if (rightJoinColToColNameMap.containsKey(colId)
393 && rightJoinColToColNameMap.get(colId).equals(colName)) {
397 nullRejectedLeftJoinCols.add(colId);
401 nullRejectedRightJoinCols.add(colId);
408 if (curNode instanceof HepRelVertex) {
409 curNode = ((HepRelVertex) curNode).getCurrentRel();
411 if (curNode instanceof LogicalFilter) {
412 collectedFilterNodes.add((LogicalFilter) curNode);
414 if (curNode.getInputs().size() == 0) {
418 for (
int i = 0; i < curNode.getInputs().size(); i++) {
424 if (curNode instanceof HepRelVertex) {
425 curNode = ((HepRelVertex) curNode).getCurrentRel();
427 if (curNode instanceof LogicalProject) {
428 collectedProject.add((LogicalProject) curNode);
430 if (curNode.getInputs().size() == 0) {
434 for (
int i = 0; i < curNode.getInputs().size(); i++) {
441 ArrayList<LogicalProject> collectedProjectNodes =
new ArrayList<>();
444 if (!collectedProjectNodes.isEmpty()) {
446 LogicalProject projectNode = collectedProjectNodes.get(0);
447 Mappings.TargetMapping targetMapping = projectNode.getMapping();
448 if (null != colRef && null != targetMapping) {
450 int base_offset = colRef.getIndex() - startOffset;
452 if (base_offset >= 0 && base_offset < targetMapping.getSourceCount()) {
453 colOffset = targetMapping.getSourceOpt(base_offset);
461 SqlKind opKind = c.getKind();
462 return (SqlKind.BINARY_COMPARISON.contains(opKind)
463 || SqlKind.BINARY_EQUALITY.contains(opKind));
467 return (c.op.kind == SqlKind.IS_NOT_NULL && c.operands.size() == 1);
473 && c.operands.get(0) instanceof RexInputRef
474 && c.operands.get(1) instanceof RexLiteral));
void collectFilterCondition(RelNode curNode, List< LogicalFilter > collectedFilterNodes)
void addNullRejectedJoinCols(RexCall call, LogicalFilter targetFilter, Set< Integer > nullRejectedLeftJoinCols, Set< Integer > nullRejectedRightJoinCols, Map< Integer, String > leftJoinColToColNameMap, Map< Integer, String > rightJoinColToColNameMap)
boolean isCandidateFilterPred(RexCall c)
OuterJoinOptViaNullRejectionRule(RelBuilderFactory relBuilderFactory)
boolean isNotNullFilter(RexCall c)
void onMatch(RelOptRuleCall call)
void addJoinCols(RexCall joinCond, LogicalJoin joinOp, Set< Integer > leftJoinCols, Set< Integer > rightJoinCols, Map< Integer, String > leftJoinColToColNameMap, Map< Integer, String > rightJoinColToColNameMap, Set< Integer > originalLeftJoinCols, Set< Integer > originalRightJoinCols, Map< Integer, String > originalLeftJoinColToColNameMap, Map< Integer, String > originalRightJoinColToColNameMap)
std::string toString(const ExecutorDeviceType &device_type)
int traceColOffset(RelNode curNode, RexInputRef colRef, int startOffset)
void collectProjectNode(RelNode curNode, List< LogicalProject > collectedProject)
static Set< String > visitedJoinMemo
boolean isComparisonOp(RexCall c)
static final Logger HEAVYDBLOGGER