alex-plekhanov commented on code in PR #11972: URL: https://github.com/apache/ignite/pull/11972#discussion_r2030955054
########## modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java: ########## @@ -586,6 +590,117 @@ private RelNode visitLeftAndRightCorrelateHands(LogicalCorrelate correlate, Corr return relShuttle.visit(rel); } + /** + * Due to distributive property of conjunction over disjunction we can extract common part of conjunctions. + * This can help us to simplify and push down filters. + * For example, condition: + * (a = 0 and x = 1) or (a = 0 and y = 2) or (a = 0 and z = 3) + * can be translated to: + * a = 0 and (x = 1 or y = 2 or z = 3) + * after such a transformation condition "a = 0" can be used as index access predicate. + */ + public RelNode extractConjunctionOverDisjunctionCommonPart(RelNode rel) { + return new RelHomogeneousShuttle() { + /** {@inheritDoc} */ + @Override public RelNode visit(LogicalFilter filter) { + RexNode condition = transform(filter.getCluster().getRexBuilder(), filter.getCondition()); + + if (condition != filter.getCondition()) + filter = filter.copy(filter.getTraitSet(), filter.getInput(), condition); + + return super.visit(filter); + } + + /** {@inheritDoc} */ + @Override public RelNode visit(LogicalJoin join) { + RexNode condition = transform(join.getCluster().getRexBuilder(), join.getCondition()); + + if (condition != join.getCondition()) { + join = join.copy(join.getTraitSet(), condition, join.getLeft(), join.getRight(), + join.getJoinType(), join.isSemiJoinDone()); + } + + return super.visit(join); + } + + /** */ + private RexNode transform(RexBuilder rexBuilder, RexNode condition) { + // It makes sence to extract only top level disjunction common part. + if (!condition.isA(SqlKind.OR)) + return condition; + + Set<RexNode> commonPart = new HashSet<>(); + + List<RexNode> orOps = ((RexCall)condition).getOperands(); + + RexNode firstOp = orOps.get(0); + + for (RexNode andOpFirst : conjunctionOperands(firstOp)) { + boolean found = false; + + for (int i = 1; i < orOps.size(); i++) { + found = false; + + for (RexNode andOpOther : conjunctionOperands(orOps.get(i))) { + if (andOpFirst.equals(andOpOther)) { + found = true; + break; + } + } + + if (!found) + break; + } + + if (found) + commonPart.add(andOpFirst); + } + + if (commonPart.isEmpty()) + return condition; + + List<RexNode> newOrOps = new ArrayList<>(orOps.size()); + + for (RexNode orOp : orOps) { + List<RexNode> newAndOps = new ArrayList<>(); + + for (RexNode andOp : conjunctionOperands(orOp)) { + if (!commonPart.contains(andOp)) + newAndOps.add(andOp); + } + + if (!newAndOps.isEmpty()) { + RexNode newAnd = RexUtil.composeConjunction(rexBuilder, newAndOps); Review Comment: Almos all tests in `testExtractCommonDisjunctionPart` have `newOrOps.size() > 1`. "Three operands disjunction" has `newOrOps.size() == 3`, for example. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: notifications-unsubscr...@ignite.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org