17 package org.apache.calcite.rel.rules;
37 import java.util.ArrayList;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.List;
74 super(operand(RelNode.class, operand(Join.class, null, any())),
76 "OuterJoinOptViaNullRejectionRule");
81 visitedJoinMemo.clear();
85 public void onMatch(RelOptRuleCall call) {
86 RelNode parentNode = call.rel(0);
87 LogicalJoin
join = (LogicalJoin) call.rel(1);
88 String condString = join.getCondition().toString();
89 if (visitedJoinMemo.contains(condString)) {
92 visitedJoinMemo.add(condString);
94 if (!(join.getCondition() instanceof RexCall)) {
97 if (join.getJoinType() == JoinRelType.INNER || join.getJoinType() == JoinRelType.SEMI
98 || join.getJoinType() == JoinRelType.ANTI) {
101 RelNode joinLeftChild = ((HepRelVertex) join.getLeft()).getCurrentRel();
102 RelNode joinRightChild = ((HepRelVertex) join.getRight()).getCurrentRel();
103 if (joinLeftChild instanceof LogicalProject) {
106 if (!(joinRightChild instanceof LogicalTableScan)) {
111 RexCall joinCond = (RexCall) join.getCondition();
112 Set<Integer> leftJoinCols =
new HashSet<>();
113 Set<Integer> rightJoinCols =
new HashSet<>();
114 Map<Integer, String> leftJoinColToColNameMap =
new HashMap<>();
115 Map<Integer, String> rightJoinColToColNameMap =
new HashMap<>();
116 Set<Integer> originalLeftJoinCols =
new HashSet<>();
117 Set<Integer> originalRightJoinCols =
new HashSet<>();
118 Map<Integer, String> originalLeftJoinColToColNameMap =
new HashMap<>();
119 Map<Integer, String> originalRightJoinColToColNameMap =
new HashMap<>();
120 List<RexCall> capturedFilterPredFromJoin =
new ArrayList<>();
121 if (joinCond.getKind() == SqlKind.EQUALS) {
126 leftJoinColToColNameMap,
127 rightJoinColToColNameMap,
128 originalLeftJoinCols,
129 originalRightJoinCols,
130 originalLeftJoinColToColNameMap,
131 originalRightJoinColToColNameMap);
132 }
else if (joinCond.getKind() == SqlKind.AND || joinCond.getKind() == SqlKind.OR) {
133 for (RexNode n : joinCond.getOperands()) {
134 if (n instanceof RexCall) {
135 RexCall op = (RexCall) n;
136 if (op.getOperands().size() > 2
137 && op.getOperands().get(1) instanceof RexLiteral) {
140 capturedFilterPredFromJoin.add(op);
147 leftJoinColToColNameMap,
148 rightJoinColToColNameMap,
149 originalLeftJoinCols,
150 originalRightJoinCols,
151 originalLeftJoinColToColNameMap,
152 originalRightJoinColToColNameMap);
157 if (leftJoinCols.isEmpty() || rightJoinCols.isEmpty()) {
162 RelNode root = call.getPlanner().getRoot();
163 List<LogicalFilter> collectedFilterNodes =
new ArrayList<>();
164 RelNode curNode = root;
165 final RelBuilder relBuilder = call.builder();
168 if (collectedFilterNodes.isEmpty()) {
177 Set<Integer> nullRejectedLeftJoinCols =
new HashSet<>();
178 Set<Integer> nullRejectedRightJoinCols =
new HashSet<>();
179 for (LogicalFilter filter : collectedFilterNodes) {
180 List<RexNode> filterExprs = filter.getChildExps();
181 for (RexNode node : filterExprs) {
182 if (node instanceof RexCall) {
183 RexCall curExpr = (RexCall) node;
184 if (curExpr.getKind() == SqlKind.AND || curExpr.getKind() == SqlKind.OR) {
185 for (RexNode n : curExpr.getOperands()) {
186 if (n instanceof RexCall) {
187 RexCall c = (RexCall) n;
189 RexInputRef col = (RexInputRef) c.getOperands().get(0);
190 int colId = col.getIndex();
191 boolean leftFilter = leftJoinCols.contains(colId);
192 boolean rightFilter = rightJoinCols.contains(colId);
193 if (leftFilter && rightFilter) {
200 nullRejectedLeftJoinCols,
201 nullRejectedRightJoinCols,
202 leftJoinColToColNameMap,
203 rightJoinColToColNameMap);
208 if (curExpr instanceof RexCall) {
210 RexInputRef col = (RexInputRef) curExpr.getOperands().get(0);
211 int colId = col.getIndex();
212 boolean leftFilter = leftJoinCols.contains(colId);
213 boolean rightFilter = rightJoinCols.contains(colId);
214 if (leftFilter && rightFilter) {
221 nullRejectedLeftJoinCols,
222 nullRejectedRightJoinCols,
223 leftJoinColToColNameMap,
224 rightJoinColToColNameMap);
232 if (!capturedFilterPredFromJoin.isEmpty()) {
233 for (RexCall c : capturedFilterPredFromJoin) {
234 RexInputRef col = (RexInputRef) c.getOperands().get(0);
235 int colId = col.getIndex();
236 String colName = join.getRowType().getFieldNames().get(colId);
239 if (originalLeftJoinColToColNameMap.containsKey(colId)
240 && originalLeftJoinColToColNameMap.get(colId).equals(colName)) {
243 if (originalRightJoinColToColNameMap.containsKey(colId)
244 && originalRightJoinColToColNameMap.get(colId).equals(colName)) {
248 nullRejectedLeftJoinCols.add(colId);
249 }
else if (r && !l) {
250 nullRejectedRightJoinCols.add(colId);
257 Boolean leftNullRejected =
false;
258 Boolean rightNullRejected =
false;
259 if (!nullRejectedLeftJoinCols.isEmpty()
260 && leftJoinCols.equals(nullRejectedLeftJoinCols)) {
261 leftNullRejected =
true;
263 if (!nullRejectedRightJoinCols.isEmpty()
264 && rightJoinCols.equals(nullRejectedRightJoinCols)) {
265 rightNullRejected =
true;
269 RelNode newJoinNode = null;
270 Boolean needTransform =
false;
271 if (join.getJoinType() == JoinRelType.FULL) {
273 if (leftNullRejected && !rightNullRejected) {
274 newJoinNode = join.copy(join.getTraitSet(),
279 join.isSemiJoinDone());
280 needTransform =
true;
284 if (leftNullRejected && rightNullRejected) {
285 newJoinNode = join.copy(join.getTraitSet(),
290 join.isSemiJoinDone());
291 needTransform =
true;
293 }
else if (join.getJoinType() == JoinRelType.LEFT) {
295 if (leftNullRejected) {
296 newJoinNode = join.copy(join.getTraitSet(),
301 join.isSemiJoinDone());
302 needTransform =
true;
306 relBuilder.push(newJoinNode);
307 parentNode.replaceInput(0, newJoinNode);
308 call.transformTo(parentNode);
315 Set<Integer> leftJoinCols,
316 Set<Integer> rightJoinCols,
317 Map<Integer, String> leftJoinColToColNameMap,
318 Map<Integer, String> rightJoinColToColNameMap,
319 Set<Integer> originalLeftJoinCols,
320 Set<Integer> originalRightJoinCols,
321 Map<Integer, String> originalLeftJoinColToColNameMap,
322 Map<Integer, String> originalRightJoinColToColNameMap) {
323 if (joinCond.getOperands().size() != 2
324 || !(joinCond.getOperands().get(0) instanceof RexInputRef)
325 || !(joinCond.getOperands().get(1) instanceof RexInputRef)) {
328 RexInputRef leftJoinCol = (RexInputRef) joinCond.getOperands().get(0);
329 RexInputRef rightJoinCol = (RexInputRef) joinCond.getOperands().get(1);
330 originalLeftJoinCols.add(leftJoinCol.getIndex());
331 originalRightJoinCols.add(rightJoinCol.getIndex());
332 originalLeftJoinColToColNameMap.put(leftJoinCol.getIndex(),
333 joinOp.getRowType().getFieldNames().get(leftJoinCol.getIndex()));
334 originalRightJoinColToColNameMap.put(rightJoinCol.getIndex(),
335 joinOp.getRowType().getFieldNames().get(rightJoinCol.getIndex()));
336 if (leftJoinCol.getIndex() > rightJoinCol.getIndex()) {
337 leftJoinCol = (RexInputRef) joinCond.getOperands().get(1);
338 rightJoinCol = (RexInputRef) joinCond.getOperands().get(0);
340 int originalLeftColOffset =
traceColOffset(joinOp.getLeft(), leftJoinCol, 0);
343 joinOp.getLeft().getRowType().getFieldCount());
344 if (originalLeftColOffset != -1) {
348 originalLeftColOffset == -1 ? leftJoinCol.getIndex() : originalLeftColOffset;
349 int rightColOffset = originalRightColOffset == -1 ? rightJoinCol.getIndex()
350 : originalRightColOffset;
351 String leftJoinColName = joinOp.getRowType().getFieldNames().get(leftColOffset);
352 String rightJoinColName =
353 joinOp.getRowType().getFieldNames().get(rightJoinCol.getIndex());
354 leftJoinCols.add(leftColOffset);
355 rightJoinCols.add(rightColOffset);
356 leftJoinColToColNameMap.put(leftColOffset, leftJoinColName);
357 rightJoinColToColNameMap.put(rightColOffset, rightJoinColName);
362 LogicalFilter targetFilter,
363 Set<Integer> nullRejectedLeftJoinCols,
364 Set<Integer> nullRejectedRightJoinCols,
365 Map<Integer, String> leftJoinColToColNameMap,
366 Map<Integer, String> rightJoinColToColNameMap) {
368 RexInputRef col = (RexInputRef) call.getOperands().get(0);
369 int colId = col.getIndex();
370 String colName = targetFilter.getRowType().getFieldNames().get(colId);
373 if (leftJoinColToColNameMap.containsKey(colId)
374 && leftJoinColToColNameMap.get(colId).equals(colName)) {
377 if (rightJoinColToColNameMap.containsKey(colId)
378 && rightJoinColToColNameMap.get(colId).equals(colName)) {
382 nullRejectedLeftJoinCols.add(colId);
386 nullRejectedRightJoinCols.add(colId);
393 if (curNode instanceof HepRelVertex) {
394 curNode = ((HepRelVertex) curNode).getCurrentRel();
396 if (curNode instanceof LogicalFilter) {
397 collectedFilterNodes.add((LogicalFilter) curNode);
399 if (curNode.getInputs().size() == 0) {
403 for (
int i = 0; i < curNode.getInputs().size(); i++) {
409 if (curNode instanceof HepRelVertex) {
410 curNode = ((HepRelVertex) curNode).getCurrentRel();
412 if (curNode instanceof LogicalProject) {
413 collectedProject.add((LogicalProject) curNode);
415 if (curNode.getInputs().size() == 0) {
419 for (
int i = 0; i < curNode.getInputs().size(); i++) {
426 ArrayList<LogicalProject> collectedProjectNodes =
new ArrayList<>();
429 if (!collectedProjectNodes.isEmpty()) {
431 LogicalProject projectNode = collectedProjectNodes.get(0);
432 Mappings.TargetMapping targetMapping = projectNode.getMapping();
433 if (null != colRef && null != targetMapping) {
435 int base_offset = colRef.getIndex() - startOffset;
437 if (base_offset >= 0 && base_offset < targetMapping.getSourceCount()) {
438 colOffset = targetMapping.getSourceOpt(base_offset);
446 SqlKind opKind = c.getKind();
447 return (SqlKind.BINARY_COMPARISON.contains(opKind)
448 || SqlKind.BINARY_EQUALITY.contains(opKind));
452 return ((c.op.kind == SqlKind.IS_NOT_NULL && c.operands.size() == 1)
454 && c.operands.get(0) instanceof RexInputRef
455 && 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)
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)
int traceColOffset(RelNode curNode, RexInputRef colRef, int startOffset)
void collectProjectNode(RelNode curNode, List< LogicalProject > collectedProject)
static Set< String > visitedJoinMemo
boolean isComparisonOp(RexCall c)