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 && c.getOperands().
get(0) instanceof RexInputRef) {
200 RexInputRef col = (RexInputRef) c.getOperands().get(0);
201 int colId = col.getIndex();
202 boolean leftFilter = leftJoinCols.contains(colId);
203 boolean rightFilter = rightJoinCols.contains(colId);
204 if (leftFilter && rightFilter) {
211 nullRejectedLeftJoinCols,
212 nullRejectedRightJoinCols,
213 leftJoinColToColNameMap,
214 rightJoinColToColNameMap);
219 if (curExpr instanceof RexCall) {
221 && curExpr.getOperands().get(0) instanceof RexInputRef) {
222 RexInputRef col = (RexInputRef) curExpr.getOperands().get(0);
223 int colId = col.getIndex();
224 boolean leftFilter = leftJoinCols.contains(colId);
225 boolean rightFilter = rightJoinCols.contains(colId);
226 if (leftFilter && rightFilter) {
233 nullRejectedLeftJoinCols,
234 nullRejectedRightJoinCols,
235 leftJoinColToColNameMap,
236 rightJoinColToColNameMap);
245 if (hasExprsConnectedViaOR) {
249 if (!capturedFilterPredFromJoin.isEmpty()) {
250 for (RexCall c : capturedFilterPredFromJoin) {
251 if (c.getOperands().get(0) instanceof RexInputRef) {
252 RexInputRef col = (RexInputRef) c.getOperands().get(0);
253 int colId = col.getIndex();
254 String colName = join.getRowType().getFieldNames().get(colId);
257 if (originalLeftJoinColToColNameMap.containsKey(colId)
258 && originalLeftJoinColToColNameMap.get(colId).equals(colName)) {
261 if (originalRightJoinColToColNameMap.containsKey(colId)
262 && originalRightJoinColToColNameMap.get(colId).equals(colName)) {
266 nullRejectedLeftJoinCols.add(colId);
267 }
else if (r && !l) {
268 nullRejectedRightJoinCols.add(colId);
276 Boolean leftNullRejected =
false;
277 Boolean rightNullRejected =
false;
278 if (!nullRejectedLeftJoinCols.isEmpty()
279 && leftJoinCols.equals(nullRejectedLeftJoinCols)) {
280 leftNullRejected =
true;
282 if (!nullRejectedRightJoinCols.isEmpty()
283 && rightJoinCols.equals(nullRejectedRightJoinCols)) {
284 rightNullRejected =
true;
288 RelNode newJoinNode = null;
289 Boolean needTransform =
false;
290 if (join.getJoinType() == JoinRelType.FULL) {
292 if (leftNullRejected && !rightNullRejected) {
293 newJoinNode = join.copy(join.getTraitSet(),
298 join.isSemiJoinDone());
299 needTransform =
true;
303 if (leftNullRejected && rightNullRejected) {
304 newJoinNode = join.copy(join.getTraitSet(),
309 join.isSemiJoinDone());
310 needTransform =
true;
312 }
else if (join.getJoinType() == JoinRelType.LEFT) {
314 if (rightNullRejected) {
315 newJoinNode = join.copy(join.getTraitSet(),
320 join.isSemiJoinDone());
321 needTransform =
true;
325 relBuilder.push(newJoinNode);
326 parentNode.replaceInput(0, newJoinNode);
327 call.transformTo(parentNode);
334 Set<Integer> leftJoinCols,
335 Set<Integer> rightJoinCols,
336 Map<Integer, String> leftJoinColToColNameMap,
337 Map<Integer, String> rightJoinColToColNameMap,
338 Set<Integer> originalLeftJoinCols,
339 Set<Integer> originalRightJoinCols,
340 Map<Integer, String> originalLeftJoinColToColNameMap,
341 Map<Integer, String> originalRightJoinColToColNameMap) {
342 if (joinCond.getOperands().size() != 2
343 || !(joinCond.getOperands().get(0) instanceof RexInputRef)
344 || !(joinCond.getOperands().
get(1) instanceof RexInputRef)) {
347 RexInputRef leftJoinCol = (RexInputRef) joinCond.getOperands().get(0);
348 RexInputRef rightJoinCol = (RexInputRef) joinCond.getOperands().get(1);
349 originalLeftJoinCols.add(leftJoinCol.getIndex());
350 originalRightJoinCols.add(rightJoinCol.getIndex());
351 originalLeftJoinColToColNameMap.put(leftJoinCol.getIndex(),
352 joinOp.getRowType().getFieldNames().
get(leftJoinCol.getIndex()));
353 originalRightJoinColToColNameMap.put(rightJoinCol.getIndex(),
354 joinOp.getRowType().getFieldNames().
get(rightJoinCol.getIndex()));
355 if (leftJoinCol.getIndex() > rightJoinCol.getIndex()) {
356 leftJoinCol = (RexInputRef) joinCond.getOperands().get(1);
357 rightJoinCol = (RexInputRef) joinCond.getOperands().get(0);
359 int originalLeftColOffset =
traceColOffset(joinOp.getLeft(), leftJoinCol, 0);
362 joinOp.getLeft().getRowType().getFieldCount());
363 if (originalLeftColOffset != -1) {
367 originalLeftColOffset == -1 ? leftJoinCol.getIndex() : originalLeftColOffset;
368 int rightColOffset = originalRightColOffset == -1 ? rightJoinCol.getIndex()
369 : originalRightColOffset;
370 String leftJoinColName = joinOp.getRowType().getFieldNames().get(leftColOffset);
371 String rightJoinColName =
372 joinOp.getRowType().getFieldNames().get(rightJoinCol.getIndex());
373 leftJoinCols.add(leftColOffset);
374 rightJoinCols.add(rightColOffset);
375 leftJoinColToColNameMap.put(leftColOffset, leftJoinColName);
376 rightJoinColToColNameMap.put(rightColOffset, rightJoinColName);
381 LogicalFilter targetFilter,
382 Set<Integer> nullRejectedLeftJoinCols,
383 Set<Integer> nullRejectedRightJoinCols,
384 Map<Integer, String> leftJoinColToColNameMap,
385 Map<Integer, String> rightJoinColToColNameMap) {
387 RexInputRef col = (RexInputRef) call.getOperands().get(0);
388 int colId = col.getIndex();
389 String colName = targetFilter.getRowType().getFieldNames().get(colId);
392 if (leftJoinColToColNameMap.containsKey(colId)
393 && leftJoinColToColNameMap.get(colId).equals(colName)) {
396 if (rightJoinColToColNameMap.containsKey(colId)
397 && rightJoinColToColNameMap.get(colId).equals(colName)) {
401 nullRejectedLeftJoinCols.add(colId);
405 nullRejectedRightJoinCols.add(colId);
412 if (curNode instanceof HepRelVertex) {
413 curNode = ((HepRelVertex) curNode).getCurrentRel();
415 if (curNode instanceof LogicalFilter) {
416 collectedFilterNodes.add((LogicalFilter) curNode);
418 if (curNode.getInputs().size() == 0) {
422 for (
int i = 0; i < curNode.getInputs().size(); i++) {
428 if (curNode instanceof HepRelVertex) {
429 curNode = ((HepRelVertex) curNode).getCurrentRel();
431 if (curNode instanceof LogicalProject) {
432 collectedProject.add((LogicalProject) curNode);
434 if (curNode.getInputs().size() == 0) {
438 for (
int i = 0; i < curNode.getInputs().size(); i++) {
445 ArrayList<LogicalProject> collectedProjectNodes =
new ArrayList<>();
448 if (!collectedProjectNodes.isEmpty()) {
450 LogicalProject projectNode = collectedProjectNodes.get(0);
451 Mappings.TargetMapping targetMapping = projectNode.getMapping();
452 if (null != colRef && null != targetMapping) {
454 int base_offset = colRef.getIndex() - startOffset;
456 if (base_offset >= 0 && base_offset < targetMapping.getSourceCount()) {
457 colOffset = targetMapping.getSourceOpt(base_offset);
465 SqlKind opKind = c.getKind();
466 return (SqlKind.BINARY_COMPARISON.contains(opKind)
467 || SqlKind.BINARY_EQUALITY.contains(opKind));
471 return (c.op.kind == SqlKind.IS_NOT_NULL && c.operands.size() == 1);
477 && c.operands.get(0) instanceof RexInputRef
478 && 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 Executor::ExtModuleKinds &kind)
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