use of org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.rex.RexBuilder in project calcite by apache.
the class LoptOptimizeJoinRule method pushDownFactor.
/**
* Creates a join tree where the new factor is pushed down one of the
* operands of the current join tree
*
* @param multiJoin join factors being optimized
* @param semiJoinOpt optimal semijoins for each factor
* @param joinTree current join tree
* @param factorToAdd new factor to be added
* @param factorsNeeded factors that must precede the factor to be added
* @param filtersToAdd filters remaining to be added; filters that are added
* to the join tree are removed from the list
* @param selfJoin true if the factor being added is part of a removable
* self-join
*
* @return optimal join tree with the new factor pushed down the current
* join tree if it is possible to do the pushdown; otherwise, null is
* returned
*/
private LoptJoinTree pushDownFactor(RelMetadataQuery mq, RelBuilder relBuilder, LoptMultiJoin multiJoin, LoptSemiJoinOptimizer semiJoinOpt, LoptJoinTree joinTree, int factorToAdd, BitSet factorsNeeded, List<RexNode> filtersToAdd, boolean selfJoin) {
// pushdown option only works if we already have a join tree
if (!isJoinTree(joinTree.getJoinTree())) {
return null;
}
int childNo = -1;
LoptJoinTree left = joinTree.getLeft();
LoptJoinTree right = joinTree.getRight();
Join joinRel = (Join) joinTree.getJoinTree();
JoinRelType joinType = joinRel.getJoinType();
// them, we need to keep the factors together
if (joinTree.isRemovableSelfJoin()) {
return null;
}
// half of the self-join.
if (selfJoin) {
BitSet selfJoinFactor = new BitSet(multiJoin.getNumJoinFactors());
selfJoinFactor.set(multiJoin.getOtherSelfJoinFactor(factorToAdd));
if (multiJoin.hasAllFactors(left, selfJoinFactor)) {
childNo = 0;
} else {
assert multiJoin.hasAllFactors(right, selfJoinFactor);
childNo = 1;
}
} else if ((factorsNeeded.cardinality() == 0) && !joinType.generatesNullsOnLeft()) {
childNo = 0;
} else {
// same check for RHS
if (multiJoin.hasAllFactors(left, factorsNeeded) && !joinType.generatesNullsOnLeft()) {
childNo = 0;
} else if (multiJoin.hasAllFactors(right, factorsNeeded) && !joinType.generatesNullsOnRight()) {
childNo = 1;
}
// if it couldn't be pushed down to either side, then it can
// only be put on top
}
if (childNo == -1) {
return null;
}
// remember the original join order before the pushdown so we can
// appropriately adjust any filters already attached to the join
// node
final List<Integer> origJoinOrder = joinTree.getTreeOrder();
// recursively pushdown the factor
LoptJoinTree subTree = (childNo == 0) ? left : right;
subTree = addFactorToTree(mq, relBuilder, multiJoin, semiJoinOpt, subTree, factorToAdd, factorsNeeded, filtersToAdd, selfJoin);
if (childNo == 0) {
left = subTree;
} else {
right = subTree;
}
// adjust the join condition from the original join tree to reflect
// pushdown of the new factor as well as any swapping that may have
// been done during the pushdown
RexNode newCondition = ((Join) joinTree.getJoinTree()).getCondition();
newCondition = adjustFilter(multiJoin, left, right, newCondition, factorToAdd, origJoinOrder, joinTree.getJoinTree().getRowType().getFieldList());
// join in createJoinSubtree
if ((joinType != JoinRelType.LEFT) && (joinType != JoinRelType.RIGHT)) {
RexNode condition = addFilters(multiJoin, left, -1, right, filtersToAdd, true);
RexBuilder rexBuilder = multiJoin.getMultiJoinRel().getCluster().getRexBuilder();
newCondition = RelOptUtil.andJoinFilters(rexBuilder, newCondition, condition);
}
// create the new join tree with the factor pushed down
return createJoinSubtree(mq, relBuilder, multiJoin, left, right, newCondition, joinType, filtersToAdd, false, false);
}
use of org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.rex.RexBuilder in project calcite by apache.
the class LoptOptimizeJoinRule method createReplacementJoin.
/**
* Creates a replacement join, projecting either dummy columns or
* replacement keys from the factor that doesn't actually need to be joined.
*
* @param multiJoin join factors being optimized
* @param semiJoinOpt optimal semijoins for each factor
* @param currJoinTree current join tree being added to
* @param leftIdx if ≥ 0, when creating the replacement join, only consider
* filters that reference leftIdx in currJoinTree; otherwise, consider all
* filters that reference any factor in currJoinTree
* @param factorToAdd new factor whose join can be removed
* @param newKeys join keys that need to be replaced
* @param replacementKeys the keys that replace the join keys; null if we're
* removing the null generating factor in an outer join
* @param filtersToAdd filters remaining to be added; filters added to the
* new join tree are removed from the list
*
* @return created join tree with an appropriate projection for the factor
* that can be removed
*/
private LoptJoinTree createReplacementJoin(RelBuilder relBuilder, LoptMultiJoin multiJoin, LoptSemiJoinOptimizer semiJoinOpt, LoptJoinTree currJoinTree, int leftIdx, int factorToAdd, ImmutableIntList newKeys, Integer[] replacementKeys, List<RexNode> filtersToAdd) {
// create a projection, projecting the fields from the join tree
// containing the current joinRel and the new factor; for fields
// corresponding to join keys, replace them with the corresponding key
// from the replacementKeys passed in; for other fields, just create a
// null expression as a placeholder for the column; this is done so we
// don't have to adjust the offsets of other expressions that reference
// the new factor; the placeholder expression values should never be
// referenced, so that's why it's ok to create these possibly invalid
// expressions
RelNode currJoinRel = currJoinTree.getJoinTree();
List<RelDataTypeField> currFields = currJoinRel.getRowType().getFieldList();
final int nCurrFields = currFields.size();
List<RelDataTypeField> newFields = multiJoin.getJoinFactor(factorToAdd).getRowType().getFieldList();
final int nNewFields = newFields.size();
List<Pair<RexNode, String>> projects = Lists.newArrayList();
RexBuilder rexBuilder = currJoinRel.getCluster().getRexBuilder();
RelDataTypeFactory typeFactory = rexBuilder.getTypeFactory();
for (int i = 0; i < nCurrFields; i++) {
projects.add(Pair.of((RexNode) rexBuilder.makeInputRef(currFields.get(i).getType(), i), currFields.get(i).getName()));
}
for (int i = 0; i < nNewFields; i++) {
RexNode projExpr;
RelDataType newType = newFields.get(i).getType();
if (!newKeys.contains(i)) {
if (replacementKeys == null) {
// null generating factor in an outer join; so make the
// type nullable
newType = typeFactory.createTypeWithNullability(newType, true);
}
projExpr = rexBuilder.makeCast(newType, rexBuilder.constantNull());
} else {
RelDataTypeField mappedField = currFields.get(replacementKeys[i]);
RexNode mappedInput = rexBuilder.makeInputRef(mappedField.getType(), replacementKeys[i]);
// if the types aren't the same, create a cast
if (mappedField.getType() == newType) {
projExpr = mappedInput;
} else {
projExpr = rexBuilder.makeCast(newFields.get(i).getType(), mappedInput);
}
}
projects.add(Pair.of(projExpr, newFields.get(i).getName()));
}
relBuilder.push(currJoinRel);
relBuilder.project(Pair.left(projects), Pair.right(projects));
// remove the join conditions corresponding to the join we're removing;
// we don't actually need to use them, but we need to remove them
// from the list since they're no longer needed
LoptJoinTree newTree = new LoptJoinTree(semiJoinOpt.getChosenSemiJoin(factorToAdd), factorToAdd);
addFilters(multiJoin, currJoinTree, leftIdx, newTree, filtersToAdd, false);
// LogicalFilter placed on top off the projection created above.
if (leftIdx >= 0) {
addAdditionalFilters(relBuilder, multiJoin, currJoinTree, newTree, filtersToAdd);
}
// from the new factor as we go up in the join tree
return new LoptJoinTree(relBuilder.build(), currJoinTree.getFactorTree(), newTree.getFactorTree());
}
use of org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.rex.RexBuilder in project calcite by apache.
the class LoptOptimizeJoinRule method createJoinSubtree.
/**
* Creates a LogicalJoin given left and right operands and a join condition.
* Swaps the operands if beneficial.
*
* @param multiJoin join factors being optimized
* @param left left operand
* @param right right operand
* @param condition join condition
* @param joinType the join type
* @param fullAdjust true if the join condition reflects the original join
* ordering and therefore has not gone through any type of adjustment yet;
* otherwise, the condition has already been partially adjusted and only
* needs to be further adjusted if swapping is done
* @param filtersToAdd additional filters that may be added on top of the
* resulting LogicalJoin, if the join is a left or right outer join
* @param selfJoin true if the join being created is a self-join that's
* removable
*
* @return created LogicalJoin
*/
private LoptJoinTree createJoinSubtree(RelMetadataQuery mq, RelBuilder relBuilder, LoptMultiJoin multiJoin, LoptJoinTree left, LoptJoinTree right, RexNode condition, JoinRelType joinType, List<RexNode> filtersToAdd, boolean fullAdjust, boolean selfJoin) {
RexBuilder rexBuilder = multiJoin.getMultiJoinRel().getCluster().getRexBuilder();
// swap the inputs if beneficial
if (swapInputs(mq, multiJoin, left, right, selfJoin)) {
LoptJoinTree tmp = right;
right = left;
left = tmp;
if (!fullAdjust) {
condition = swapFilter(rexBuilder, multiJoin, right, left, condition);
}
if ((joinType != JoinRelType.INNER) && (joinType != JoinRelType.FULL)) {
joinType = (joinType == JoinRelType.LEFT) ? JoinRelType.RIGHT : JoinRelType.LEFT;
}
}
if (fullAdjust) {
int[] adjustments = new int[multiJoin.getNumTotalFields()];
if (needsAdjustment(multiJoin, adjustments, left, right, selfJoin)) {
condition = condition.accept(new RelOptUtil.RexInputConverter(rexBuilder, multiJoin.getMultiJoinFields(), left.getJoinTree().getRowType().getFieldList(), right.getJoinTree().getRowType().getFieldList(), adjustments));
}
}
relBuilder.push(left.getJoinTree()).push(right.getJoinTree()).join(joinType, condition);
// as a filter on top of the outer join result
if ((joinType == JoinRelType.LEFT) || (joinType == JoinRelType.RIGHT)) {
assert !selfJoin;
addAdditionalFilters(relBuilder, multiJoin, left, right, filtersToAdd);
}
return new LoptJoinTree(relBuilder.build(), left.getFactorTree(), right.getFactorTree(), selfJoin);
}
use of org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.rex.RexBuilder in project calcite by apache.
the class IntersectToDistinctRule method onMatch.
// ~ Methods ----------------------------------------------------------------
public void onMatch(RelOptRuleCall call) {
final Intersect intersect = call.rel(0);
if (intersect.all) {
// nothing we can do
return;
}
final RelOptCluster cluster = intersect.getCluster();
final RexBuilder rexBuilder = cluster.getRexBuilder();
final RelBuilder relBuilder = call.builder();
// 1st level GB: create a GB (col0, col1, count() as c) for each branch
for (RelNode input : intersect.getInputs()) {
relBuilder.push(input);
relBuilder.aggregate(relBuilder.groupKey(relBuilder.fields()), relBuilder.countStar(null));
}
// create a union above all the branches
final int branchCount = intersect.getInputs().size();
relBuilder.union(true, branchCount);
final RelNode union = relBuilder.peek();
// 2nd level GB: create a GB (col0, col1, count(c)) for each branch
// the index of c is union.getRowType().getFieldList().size() - 1
final int fieldCount = union.getRowType().getFieldCount();
final ImmutableBitSet groupSet = ImmutableBitSet.range(fieldCount - 1);
relBuilder.aggregate(relBuilder.groupKey(groupSet, null), relBuilder.countStar(null));
// add a filter count(c) = #branches
relBuilder.filter(relBuilder.equals(relBuilder.field(fieldCount - 1), rexBuilder.makeBigintLiteral(new BigDecimal(branchCount))));
// Project all but the last field
relBuilder.project(Util.skipLast(relBuilder.fields()));
// the schema for intersect distinct is like this
// R3 on all attributes + count(c) as cnt
// finally add a project to project out the last column
call.transformTo(relBuilder.build());
}
use of org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.rex.RexBuilder in project calcite by apache.
the class JoinAssociateRule method onMatch.
// ~ Methods ----------------------------------------------------------------
public void onMatch(final RelOptRuleCall call) {
final Join topJoin = call.rel(0);
final Join bottomJoin = call.rel(1);
final RelNode relA = bottomJoin.getLeft();
final RelNode relB = bottomJoin.getRight();
final RelSubset relC = call.rel(2);
final RelOptCluster cluster = topJoin.getCluster();
final RexBuilder rexBuilder = cluster.getRexBuilder();
if (relC.getConvention() != relA.getConvention()) {
// EnumerableConvention, we're only interested in enumerable subsets.
return;
}
// 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(0, aCount);
final ImmutableBitSet bBitSet = ImmutableBitSet.range(aCount, aCount + bCount);
if (!topJoin.getSystemFieldList().isEmpty()) {
// FIXME Enable this rule for joins with system fields
return;
}
// (Is this too strict?)
if (topJoin.getJoinType() != JoinRelType.INNER || bottomJoin.getJoinType() != JoinRelType.INNER) {
return;
}
// Goal is to transform to
//
// newTopJoin
// / \
// A newBottomJoin
// / \
// B C
// Split the condition of topJoin and bottomJoin into a conjunctions. A
// condition can be pushed down if it does not use columns from A.
final List<RexNode> top = Lists.newArrayList();
final List<RexNode> bottom = Lists.newArrayList();
JoinPushThroughJoinRule.split(topJoin.getCondition(), aBitSet, top, bottom);
JoinPushThroughJoinRule.split(bottomJoin.getCondition(), aBitSet, top, bottom);
// Mapping for moving conditions from topJoin or bottomJoin to
// newBottomJoin.
// target: | B | C |
// source: | A | B | C |
final Mappings.TargetMapping bottomMapping = Mappings.createShiftMapping(aCount + bCount + cCount, 0, aCount, bCount, bCount, aCount + bCount, cCount);
final List<RexNode> newBottomList = Lists.newArrayList();
new RexPermuteInputsShuttle(bottomMapping, relB, relC).visitList(bottom, newBottomList);
RexNode newBottomCondition = RexUtil.composeConjunction(rexBuilder, newBottomList, false);
final Join newBottomJoin = bottomJoin.copy(bottomJoin.getTraitSet(), newBottomCondition, relB, relC, JoinRelType.INNER, false);
// Condition for newTopJoin consists of pieces from bottomJoin and topJoin.
// Field ordinals do not need to be changed.
RexNode newTopCondition = RexUtil.composeConjunction(rexBuilder, top, false);
final Join newTopJoin = topJoin.copy(topJoin.getTraitSet(), newTopCondition, relA, newBottomJoin, JoinRelType.INNER, false);
call.transformTo(newTopJoin);
}
Aggregations