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);
136 }
else if (joinCond.getKind() == SqlKind.AND || joinCond.getKind() == SqlKind.OR) {
137 for (RexNode n : joinCond.getOperands()) {
138 if (n instanceof RexCall) {
139 RexCall op = (RexCall) n;
140 if (op.getOperands().size() > 2
141 && op.getOperands().get(1) instanceof RexLiteral) {
144 capturedFilterPredFromJoin.add(op);
151 leftJoinColToColNameMap,
152 rightJoinColToColNameMap,
153 originalLeftJoinCols,
154 originalRightJoinCols,
155 originalLeftJoinColToColNameMap,
156 originalRightJoinColToColNameMap);
161 if (leftJoinCols.isEmpty() || rightJoinCols.isEmpty()) {
166 RelNode
root = call.getPlanner().getRoot();
167 List<LogicalFilter> collectedFilterNodes =
new ArrayList<>();
168 RelNode curNode =
root;
169 final RelBuilder relBuilder = call.builder();
172 if (collectedFilterNodes.isEmpty()) {
181 Set<Integer> nullRejectedLeftJoinCols =
new HashSet<>();
182 Set<Integer> nullRejectedRightJoinCols =
new HashSet<>();
183 for (LogicalFilter filter : collectedFilterNodes) {
184 RexNode node = filter.getCondition();
185 if (node instanceof RexCall) {
186 RexCall curExpr = (RexCall) node;
187 if (curExpr.getKind() == SqlKind.AND || curExpr.getKind() == SqlKind.OR) {
188 for (RexNode n : curExpr.getOperands()) {
189 if (n instanceof RexCall) {
190 RexCall c = (RexCall) n;
192 RexInputRef col = (RexInputRef) c.getOperands().get(0);
193 int colId = col.getIndex();
194 boolean leftFilter = leftJoinCols.contains(colId);
195 boolean rightFilter = rightJoinCols.contains(colId);
196 if (leftFilter && rightFilter) {
203 nullRejectedLeftJoinCols,
204 nullRejectedRightJoinCols,
205 leftJoinColToColNameMap,
206 rightJoinColToColNameMap);
211 if (curExpr instanceof RexCall) {
213 RexInputRef col = (RexInputRef) curExpr.getOperands().get(0);
214 int colId = col.getIndex();
215 boolean leftFilter = leftJoinCols.contains(colId);
216 boolean rightFilter = rightJoinCols.contains(colId);
217 if (leftFilter && rightFilter) {
224 nullRejectedLeftJoinCols,
225 nullRejectedRightJoinCols,
226 leftJoinColToColNameMap,
227 rightJoinColToColNameMap);
234 if (!capturedFilterPredFromJoin.isEmpty()) {
235 for (RexCall c : capturedFilterPredFromJoin) {
236 RexInputRef col = (RexInputRef) c.getOperands().get(0);
237 int colId = col.getIndex();
238 String colName = join.getRowType().getFieldNames().get(colId);
241 if (originalLeftJoinColToColNameMap.containsKey(colId)
242 && originalLeftJoinColToColNameMap.get(colId).equals(colName)) {
245 if (originalRightJoinColToColNameMap.containsKey(colId)
246 && originalRightJoinColToColNameMap.get(colId).equals(colName)) {
250 nullRejectedLeftJoinCols.add(colId);
251 }
else if (r && !l) {
252 nullRejectedRightJoinCols.add(colId);
259 Boolean leftNullRejected =
false;
260 Boolean rightNullRejected =
false;
261 if (!nullRejectedLeftJoinCols.isEmpty()
262 && leftJoinCols.equals(nullRejectedLeftJoinCols)) {
263 leftNullRejected =
true;
265 if (!nullRejectedRightJoinCols.isEmpty()
266 && rightJoinCols.equals(nullRejectedRightJoinCols)) {
267 rightNullRejected =
true;
271 RelNode newJoinNode = null;
272 Boolean needTransform =
false;
273 if (join.getJoinType() == JoinRelType.FULL) {
275 if (leftNullRejected && !rightNullRejected) {
276 newJoinNode = join.copy(join.getTraitSet(),
281 join.isSemiJoinDone());
282 needTransform =
true;
286 if (leftNullRejected && rightNullRejected) {
287 newJoinNode = join.copy(join.getTraitSet(),
292 join.isSemiJoinDone());
293 needTransform =
true;
295 }
else if (join.getJoinType() == JoinRelType.LEFT) {
297 if (rightNullRejected) {
298 newJoinNode = join.copy(join.getTraitSet(),
303 join.isSemiJoinDone());
304 needTransform =
true;
308 relBuilder.push(newJoinNode);
309 parentNode.replaceInput(0, newJoinNode);
310 call.transformTo(parentNode);
317 Set<Integer> leftJoinCols,
318 Set<Integer> rightJoinCols,
319 Map<Integer, String> leftJoinColToColNameMap,
320 Map<Integer, String> rightJoinColToColNameMap,
321 Set<Integer> originalLeftJoinCols,
322 Set<Integer> originalRightJoinCols,
323 Map<Integer, String> originalLeftJoinColToColNameMap,
324 Map<Integer, String> originalRightJoinColToColNameMap) {
325 if (joinCond.getOperands().size() != 2
326 || !(joinCond.getOperands().get(0) instanceof RexInputRef)
327 || !(joinCond.getOperands().
get(1) instanceof RexInputRef)) {
330 RexInputRef leftJoinCol = (RexInputRef) joinCond.getOperands().get(0);
331 RexInputRef rightJoinCol = (RexInputRef) joinCond.getOperands().get(1);
332 originalLeftJoinCols.add(leftJoinCol.getIndex());
333 originalRightJoinCols.add(rightJoinCol.getIndex());
334 originalLeftJoinColToColNameMap.put(leftJoinCol.getIndex(),
335 joinOp.getRowType().getFieldNames().
get(leftJoinCol.getIndex()));
336 originalRightJoinColToColNameMap.put(rightJoinCol.getIndex(),
337 joinOp.getRowType().getFieldNames().
get(rightJoinCol.getIndex()));
338 if (leftJoinCol.getIndex() > rightJoinCol.getIndex()) {
339 leftJoinCol = (RexInputRef) joinCond.getOperands().get(1);
340 rightJoinCol = (RexInputRef) joinCond.getOperands().get(0);
342 int originalLeftColOffset =
traceColOffset(joinOp.getLeft(), leftJoinCol, 0);
345 joinOp.getLeft().getRowType().getFieldCount());
346 if (originalLeftColOffset != -1) {
350 originalLeftColOffset == -1 ? leftJoinCol.getIndex() : originalLeftColOffset;
351 int rightColOffset = originalRightColOffset == -1 ? rightJoinCol.getIndex()
352 : originalRightColOffset;
353 String leftJoinColName = joinOp.getRowType().getFieldNames().get(leftColOffset);
354 String rightJoinColName =
355 joinOp.getRowType().getFieldNames().get(rightJoinCol.getIndex());
356 leftJoinCols.add(leftColOffset);
357 rightJoinCols.add(rightColOffset);
358 leftJoinColToColNameMap.put(leftColOffset, leftJoinColName);
359 rightJoinColToColNameMap.put(rightColOffset, rightJoinColName);
364 LogicalFilter targetFilter,
365 Set<Integer> nullRejectedLeftJoinCols,
366 Set<Integer> nullRejectedRightJoinCols,
367 Map<Integer, String> leftJoinColToColNameMap,
368 Map<Integer, String> rightJoinColToColNameMap) {
370 RexInputRef col = (RexInputRef) call.getOperands().get(0);
371 int colId = col.getIndex();
372 String colName = targetFilter.getRowType().getFieldNames().get(colId);
375 if (leftJoinColToColNameMap.containsKey(colId)
376 && leftJoinColToColNameMap.get(colId).equals(colName)) {
379 if (rightJoinColToColNameMap.containsKey(colId)
380 && rightJoinColToColNameMap.get(colId).equals(colName)) {
384 nullRejectedLeftJoinCols.add(colId);
388 nullRejectedRightJoinCols.add(colId);
395 if (curNode instanceof HepRelVertex) {
396 curNode = ((HepRelVertex) curNode).getCurrentRel();
398 if (curNode instanceof LogicalFilter) {
399 collectedFilterNodes.add((LogicalFilter) curNode);
401 if (curNode.getInputs().size() == 0) {
405 for (
int i = 0;
i < curNode.getInputs().size();
i++) {
411 if (curNode instanceof HepRelVertex) {
412 curNode = ((HepRelVertex) curNode).getCurrentRel();
414 if (curNode instanceof LogicalProject) {
415 collectedProject.add((LogicalProject) curNode);
417 if (curNode.getInputs().size() == 0) {
421 for (
int i = 0;
i < curNode.getInputs().size();
i++) {
428 ArrayList<LogicalProject> collectedProjectNodes =
new ArrayList<>();
431 if (!collectedProjectNodes.isEmpty()) {
433 LogicalProject projectNode = collectedProjectNodes.get(0);
434 Mappings.TargetMapping targetMapping = projectNode.getMapping();
435 if (null != colRef && null != targetMapping) {
437 int base_offset = colRef.getIndex() - startOffset;
439 if (base_offset >= 0 && base_offset < targetMapping.getSourceCount()) {
440 colOffset = targetMapping.getSourceOpt(base_offset);
448 SqlKind opKind = c.getKind();
449 return (SqlKind.BINARY_COMPARISON.contains(opKind)
450 || SqlKind.BINARY_EQUALITY.contains(opKind));
454 return (c.op.kind == SqlKind.IS_NOT_NULL && c.operands.size() == 1);
460 && c.operands.get(0) instanceof RexInputRef
461 && c.operands.get(1) instanceof RexLiteral));
void collectFilterCondition(RelNode curNode, List< LogicalFilter > collectedFilterNodes)
std::string toString(const ExtArgumentType &sig_type)
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)
static final Logger MAPDLOGGER
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)