use of org.apache.calcite.plan.RelOptCluster in project calcite by apache.
the class ProjectCalcMergeRule method onMatch.
// ~ Methods ----------------------------------------------------------------
public void onMatch(RelOptRuleCall call) {
final LogicalProject project = call.rel(0);
final LogicalCalc calc = call.rel(1);
// Don't merge a project which contains windowed aggregates onto a
// calc. That would effectively be pushing a windowed aggregate down
// through a filter. Transform the project into an identical calc,
// which we'll have chance to merge later, after the over is
// expanded.
final RelOptCluster cluster = project.getCluster();
RexProgram program = RexProgram.create(calc.getRowType(), project.getProjects(), null, project.getRowType(), cluster.getRexBuilder());
if (RexOver.containsOver(program)) {
LogicalCalc projectAsCalc = LogicalCalc.create(calc, program);
call.transformTo(projectAsCalc);
return;
}
// Create a program containing the project node's expressions.
final RexBuilder rexBuilder = cluster.getRexBuilder();
final RexProgramBuilder progBuilder = new RexProgramBuilder(calc.getRowType(), rexBuilder);
for (Pair<RexNode, String> field : project.getNamedProjects()) {
progBuilder.addProject(field.left, field.right);
}
RexProgram topProgram = progBuilder.getProgram();
RexProgram bottomProgram = calc.getProgram();
// Merge the programs together.
RexProgram mergedProgram = RexProgramBuilder.mergePrograms(topProgram, bottomProgram, rexBuilder);
final LogicalCalc newCalc = LogicalCalc.create(calc.getInput(), mergedProgram);
call.transformTo(newCalc);
}
use of org.apache.calcite.plan.RelOptCluster in project calcite by apache.
the class JoinPushThroughJoinRule method onMatchRight.
private void onMatchRight(RelOptRuleCall call) {
final Join topJoin = call.rel(0);
final Join bottomJoin = call.rel(1);
final RelNode relC = call.rel(2);
final RelNode relA = bottomJoin.getLeft();
final RelNode relB = bottomJoin.getRight();
final RelOptCluster cluster = topJoin.getCluster();
// topJoin
// / \
// bottomJoin C
// / \
// A B
final int aCount = relA.getRowType().getFieldCount();
final int bCount = relB.getRowType().getFieldCount();
final int cCount = relC.getRowType().getFieldCount();
final ImmutableBitSet bBitSet = ImmutableBitSet.range(aCount, aCount + bCount);
// (Is this too strict?)
if (topJoin.getJoinType() != JoinRelType.INNER || bottomJoin.getJoinType() != JoinRelType.INNER) {
return;
}
// Split the condition of topJoin into a conjunction. Each of the
// parts that does not use columns from B can be pushed down.
final List<RexNode> intersecting = new ArrayList<>();
final List<RexNode> nonIntersecting = new ArrayList<>();
split(topJoin.getCondition(), bBitSet, intersecting, nonIntersecting);
// If there's nothing to push down, it's not worth proceeding.
if (nonIntersecting.isEmpty()) {
return;
}
// Split the condition of bottomJoin into a conjunction. Each of the
// parts that use columns from B will need to be pulled up.
final List<RexNode> bottomIntersecting = new ArrayList<>();
final List<RexNode> bottomNonIntersecting = new ArrayList<>();
split(bottomJoin.getCondition(), bBitSet, bottomIntersecting, bottomNonIntersecting);
// target: | A | C |
// source: | A | B | C |
final Mappings.TargetMapping bottomMapping = Mappings.createShiftMapping(aCount + bCount + cCount, 0, 0, aCount, aCount, aCount + bCount, cCount);
final List<RexNode> newBottomList = new ArrayList<>();
new RexPermuteInputsShuttle(bottomMapping, relA, relC).visitList(nonIntersecting, newBottomList);
new RexPermuteInputsShuttle(bottomMapping, relA, relC).visitList(bottomNonIntersecting, newBottomList);
final RexBuilder rexBuilder = cluster.getRexBuilder();
RexNode newBottomCondition = RexUtil.composeConjunction(rexBuilder, newBottomList, false);
final Join newBottomJoin = bottomJoin.copy(bottomJoin.getTraitSet(), newBottomCondition, relA, relC, bottomJoin.getJoinType(), bottomJoin.isSemiJoinDone());
// target: | A | C | B |
// source: | A | B | C |
final Mappings.TargetMapping topMapping = Mappings.createShiftMapping(aCount + bCount + cCount, 0, 0, aCount, aCount + cCount, aCount, bCount, aCount, aCount + bCount, cCount);
final List<RexNode> newTopList = new ArrayList<>();
new RexPermuteInputsShuttle(topMapping, newBottomJoin, relB).visitList(intersecting, newTopList);
new RexPermuteInputsShuttle(topMapping, newBottomJoin, relB).visitList(bottomIntersecting, newTopList);
RexNode newTopCondition = RexUtil.composeConjunction(rexBuilder, newTopList, false);
@SuppressWarnings("SuspiciousNameCombination") final Join newTopJoin = topJoin.copy(topJoin.getTraitSet(), newTopCondition, newBottomJoin, relB, topJoin.getJoinType(), topJoin.isSemiJoinDone());
assert !Mappings.isIdentity(topMapping);
final RelBuilder relBuilder = call.builder();
relBuilder.push(newTopJoin);
relBuilder.project(relBuilder.fields(topMapping));
call.transformTo(relBuilder.build());
}
use of org.apache.calcite.plan.RelOptCluster in project calcite by apache.
the class JoinPushThroughJoinRule method onMatchLeft.
/**
* Similar to {@link #onMatch}, but swaps the upper sibling with the left
* of the two lower siblings, rather than the right.
*/
private void onMatchLeft(RelOptRuleCall call) {
final Join topJoin = call.rel(0);
final Join bottomJoin = call.rel(1);
final RelNode relC = call.rel(2);
final RelNode relA = bottomJoin.getLeft();
final RelNode relB = bottomJoin.getRight();
final RelOptCluster cluster = topJoin.getCluster();
// topJoin
// / \
// bottomJoin C
// / \
// A B
final int aCount = relA.getRowType().getFieldCount();
final int bCount = relB.getRowType().getFieldCount();
final int cCount = relC.getRowType().getFieldCount();
final ImmutableBitSet aBitSet = ImmutableBitSet.range(aCount);
// (Is this too strict?)
if (topJoin.getJoinType() != JoinRelType.INNER || bottomJoin.getJoinType() != JoinRelType.INNER) {
return;
}
// Split the condition of topJoin into a conjunction. Each of the
// parts that does not use columns from A can be pushed down.
final List<RexNode> intersecting = new ArrayList<>();
final List<RexNode> nonIntersecting = new ArrayList<>();
split(topJoin.getCondition(), aBitSet, intersecting, nonIntersecting);
// If there's nothing to push down, it's not worth proceeding.
if (nonIntersecting.isEmpty()) {
return;
}
// Split the condition of bottomJoin into a conjunction. Each of the
// parts that use columns from A will need to be pulled up.
final List<RexNode> bottomIntersecting = new ArrayList<>();
final List<RexNode> bottomNonIntersecting = new ArrayList<>();
split(bottomJoin.getCondition(), aBitSet, bottomIntersecting, bottomNonIntersecting);
// target: | C | B |
// source: | A | B | C |
final Mappings.TargetMapping bottomMapping = Mappings.createShiftMapping(aCount + bCount + cCount, cCount, aCount, bCount, 0, aCount + bCount, cCount);
final List<RexNode> newBottomList = new ArrayList<>();
new RexPermuteInputsShuttle(bottomMapping, relC, relB).visitList(nonIntersecting, newBottomList);
new RexPermuteInputsShuttle(bottomMapping, relC, relB).visitList(bottomNonIntersecting, newBottomList);
final RexBuilder rexBuilder = cluster.getRexBuilder();
RexNode newBottomCondition = RexUtil.composeConjunction(rexBuilder, newBottomList, false);
final Join newBottomJoin = bottomJoin.copy(bottomJoin.getTraitSet(), newBottomCondition, relC, relB, bottomJoin.getJoinType(), bottomJoin.isSemiJoinDone());
// target: | C | B | A |
// source: | A | B | C |
final Mappings.TargetMapping topMapping = Mappings.createShiftMapping(aCount + bCount + cCount, cCount + bCount, 0, aCount, cCount, aCount, bCount, 0, aCount + bCount, cCount);
final List<RexNode> newTopList = new ArrayList<>();
new RexPermuteInputsShuttle(topMapping, newBottomJoin, relA).visitList(intersecting, newTopList);
new RexPermuteInputsShuttle(topMapping, newBottomJoin, relA).visitList(bottomIntersecting, newTopList);
RexNode newTopCondition = RexUtil.composeConjunction(rexBuilder, newTopList, false);
@SuppressWarnings("SuspiciousNameCombination") final Join newTopJoin = topJoin.copy(topJoin.getTraitSet(), newTopCondition, newBottomJoin, relA, topJoin.getJoinType(), topJoin.isSemiJoinDone());
final RelBuilder relBuilder = call.builder();
relBuilder.push(newTopJoin);
relBuilder.project(relBuilder.fields(topMapping));
call.transformTo(relBuilder.build());
}
use of org.apache.calcite.plan.RelOptCluster in project calcite by apache.
the class ReduceExpressionsRule method reduceExpressions.
/**
* Reduces a list of expressions.
*
* <p>The {@code matchNullability} flag comes into play when reducing a
* expression whose type is nullable. Suppose we are reducing an expression
* {@code CASE WHEN 'a' = 'a' THEN 1 ELSE NULL END}. Before reduction the
* type is {@code INTEGER} (nullable), but after reduction the literal 1 has
* type {@code INTEGER NOT NULL}.
*
* <p>In some situations it is more important to preserve types; in this
* case you should use {@code matchNullability = true} (which used to be
* the default behavior of this method), and it will cast the literal to
* {@code INTEGER} (nullable).
*
* <p>In other situations, you would rather propagate the new stronger type,
* because it may allow further optimizations later; pass
* {@code matchNullability = false} and no cast will be added, but you may
* need to adjust types elsewhere in the expression tree.
*
* @param rel Relational expression
* @param expList List of expressions, modified in place
* @param predicates Constraints known to hold on input expressions
* @param unknownAsFalse Whether UNKNOWN will be treated as FALSE
* @param matchNullability Whether Calcite should add a CAST to a literal
* resulting from simplification and expression if the
* expression had nullable type and the literal is
* NOT NULL
*
* @return whether reduction found something to change, and succeeded
*/
protected static boolean reduceExpressions(RelNode rel, List<RexNode> expList, RelOptPredicateList predicates, boolean unknownAsFalse, boolean matchNullability) {
final RelOptCluster cluster = rel.getCluster();
final RexBuilder rexBuilder = cluster.getRexBuilder();
final RexExecutor executor = Util.first(cluster.getPlanner().getExecutor(), RexUtil.EXECUTOR);
final RexSimplify simplify = new RexSimplify(rexBuilder, predicates, unknownAsFalse, executor);
// Simplify predicates in place
boolean reduced = reduceExpressionsInternal(rel, simplify, expList, predicates);
final ExprSimplifier simplifier = new ExprSimplifier(simplify, matchNullability);
boolean simplified = false;
for (int i = 0; i < expList.size(); i++) {
RexNode expr2 = simplifier.apply(expList.get(i));
if (!expr2.toString().equals(expList.get(i).toString())) {
expList.remove(i);
expList.add(i, expr2);
simplified = true;
}
}
return reduced || simplified;
}
use of org.apache.calcite.plan.RelOptCluster in project calcite by apache.
the class FilterTableFunctionTransposeRule method onMatch.
// ~ Methods ----------------------------------------------------------------
// implement RelOptRule
public void onMatch(RelOptRuleCall call) {
LogicalFilter filter = call.rel(0);
LogicalTableFunctionScan funcRel = call.rel(1);
Set<RelColumnMapping> columnMappings = funcRel.getColumnMappings();
if (columnMappings == null || columnMappings.isEmpty()) {
// possible.
return;
}
List<RelNode> funcInputs = funcRel.getInputs();
if (funcInputs.size() != 1) {
// offsetting field indices, similar to join
return;
}
// TODO: support mappings other than 1-to-1
if (funcRel.getRowType().getFieldCount() != funcInputs.get(0).getRowType().getFieldCount()) {
return;
}
for (RelColumnMapping mapping : columnMappings) {
if (mapping.iInputColumn != mapping.iOutputColumn) {
return;
}
if (mapping.derived) {
return;
}
}
final List<RelNode> newFuncInputs = new ArrayList<RelNode>();
final RelOptCluster cluster = funcRel.getCluster();
final RexNode condition = filter.getCondition();
// create filters on top of each func input, modifying the filter
// condition to reference the child instead
RexBuilder rexBuilder = filter.getCluster().getRexBuilder();
List<RelDataTypeField> origFields = funcRel.getRowType().getFieldList();
// TODO: these need to be non-zero once we
// support arbitrary mappings
int[] adjustments = new int[origFields.size()];
for (RelNode funcInput : funcInputs) {
RexNode newCondition = condition.accept(new RelOptUtil.RexInputConverter(rexBuilder, origFields, funcInput.getRowType().getFieldList(), adjustments));
newFuncInputs.add(LogicalFilter.create(funcInput, newCondition));
}
// create a new UDX whose children are the filters created above
LogicalTableFunctionScan newFuncRel = LogicalTableFunctionScan.create(cluster, newFuncInputs, funcRel.getCall(), funcRel.getElementType(), funcRel.getRowType(), columnMappings);
call.transformTo(newFuncRel);
}
Aggregations