use of org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.rex.RexNode 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.RexNode in project calcite by apache.
the class LoptOptimizeJoinRule method findRemovableSelfJoins.
/**
* Locates pairs of joins that are self-joins where the join can be removed
* because the join condition between the two factors is an equality join on
* unique keys.
*
* @param multiJoin join factors being optimized
*/
private void findRemovableSelfJoins(RelMetadataQuery mq, LoptMultiJoin multiJoin) {
// Candidates for self-joins must be simple factors
Map<Integer, RelOptTable> simpleFactors = getSimpleFactors(mq, multiJoin);
// See if a simple factor is repeated and therefore potentially is
// part of a self-join. Restrict each factor to at most one
// self-join.
final List<RelOptTable> repeatedTables = new ArrayList<>();
final TreeSet<Integer> sortedFactors = new TreeSet<>();
sortedFactors.addAll(simpleFactors.keySet());
final Map<Integer, Integer> selfJoinPairs = new HashMap<>();
Integer[] factors = sortedFactors.toArray(new Integer[sortedFactors.size()]);
for (int i = 0; i < factors.length; i++) {
if (repeatedTables.contains(simpleFactors.get(factors[i]))) {
continue;
}
for (int j = i + 1; j < factors.length; j++) {
int leftFactor = factors[i];
int rightFactor = factors[j];
if (simpleFactors.get(leftFactor).getQualifiedName().equals(simpleFactors.get(rightFactor).getQualifiedName())) {
selfJoinPairs.put(leftFactor, rightFactor);
repeatedTables.add(simpleFactors.get(leftFactor));
break;
}
}
}
// allow the join to be removed.
for (Integer factor1 : selfJoinPairs.keySet()) {
final int factor2 = selfJoinPairs.get(factor1);
final List<RexNode> selfJoinFilters = new ArrayList<>();
for (RexNode filter : multiJoin.getJoinFilters()) {
ImmutableBitSet joinFactors = multiJoin.getFactorsRefByJoinFilter(filter);
if ((joinFactors.cardinality() == 2) && joinFactors.get(factor1) && joinFactors.get(factor2)) {
selfJoinFilters.add(filter);
}
}
if ((selfJoinFilters.size() > 0) && isSelfJoinFilterUnique(mq, multiJoin, factor1, factor2, selfJoinFilters)) {
multiJoin.addRemovableSelfJoinPair(factor1, factor2);
}
}
}
use of org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.rex.RexNode in project calcite by apache.
the class CalcRelSplitter method traceLevelExpressions.
/**
* Traces the given array of level expression lists at the finer level.
*
* @param exprs Array expressions
* @param exprLevels For each expression, the ordinal of its level
* @param levelTypeOrdinals For each level, the ordinal of its type in
* the {@link #relTypes} array
* @param levelCount The number of levels
*/
private void traceLevelExpressions(RexNode[] exprs, int[] exprLevels, int[] levelTypeOrdinals, int levelCount) {
StringWriter traceMsg = new StringWriter();
PrintWriter traceWriter = new PrintWriter(traceMsg);
traceWriter.println("FarragoAutoCalcRule result expressions for: ");
traceWriter.println(program.toString());
for (int level = 0; level < levelCount; level++) {
traceWriter.println("Rel Level " + level + ", type " + relTypes[levelTypeOrdinals[level]]);
for (int i = 0; i < exprs.length; i++) {
RexNode expr = exprs[i];
assert (exprLevels[i] >= -1) && (exprLevels[i] < levelCount) : "expression's level is out of range";
if (exprLevels[i] == level) {
traceWriter.println("\t" + i + ": " + expr);
}
}
traceWriter.println();
}
String msg = traceMsg.toString();
RULE_LOGGER.trace(msg);
}
use of org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.rex.RexNode in project calcite by apache.
the class CalcRelSplitter method computeTopologicalOrdering.
/**
* Computes the order in which to visit expressions, so that we decide the
* level of an expression only after the levels of lower expressions have
* been decided.
*
* <p>First, we need to ensure that an expression is visited after all of
* its inputs.
*
* <p>Further, if the expression is a member of a cohort, we need to visit
* it after the inputs of all other expressions in that cohort. With this
* condition, expressions in the same cohort will very likely end up in the
* same level.
*
* <p>Note that if there are no cohorts, the expressions from the
* {@link RexProgram} are already in a suitable order. We perform the
* topological sort just to ensure that the code path is well-trodden.
*
* @param exprs Expressions
* @param cohorts List of cohorts, each of which is a set of expr ordinals
* @return Expression ordinals in topological order
*/
private List<Integer> computeTopologicalOrdering(RexNode[] exprs, List<Set<Integer>> cohorts) {
final DirectedGraph<Integer, DefaultEdge> graph = DefaultDirectedGraph.create();
for (int i = 0; i < exprs.length; i++) {
graph.addVertex(i);
}
for (int i = 0; i < exprs.length; i++) {
final RexNode expr = exprs[i];
final Set<Integer> cohort = findCohort(cohorts, i);
final Set<Integer> targets;
if (cohort == null) {
targets = Collections.singleton(i);
} else {
targets = cohort;
}
expr.accept(new RexVisitorImpl<Void>(true) {
public Void visitLocalRef(RexLocalRef localRef) {
for (Integer target : targets) {
graph.addEdge(localRef.getIndex(), target);
}
return null;
}
});
}
TopologicalOrderIterator<Integer, DefaultEdge> iter = new TopologicalOrderIterator<>(graph);
final List<Integer> permutation = new ArrayList<>();
while (iter.hasNext()) {
permutation.add(iter.next());
}
return permutation;
}
use of org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.rex.RexNode in project calcite by apache.
the class CalcRelSplitter method chooseLevels.
/**
* Figures out which expressions to calculate at which level.
*
* @param exprs Array of expressions
* @param conditionOrdinal Ordinal of the condition expression, or -1 if no
* condition
* @param exprLevels Level ordinal for each expression (output)
* @param levelTypeOrdinals The type of each level (output)
* @return Number of levels required
*/
private int chooseLevels(final RexNode[] exprs, int conditionOrdinal, int[] exprLevels, int[] levelTypeOrdinals) {
final int inputFieldCount = program.getInputRowType().getFieldCount();
int levelCount = 0;
final MaxInputFinder maxInputFinder = new MaxInputFinder(exprLevels);
boolean[] relTypesPossibleForTopLevel = new boolean[relTypes.length];
Arrays.fill(relTypesPossibleForTopLevel, true);
// Compute the order in which to visit expressions.
final List<Set<Integer>> cohorts = getCohorts();
final List<Integer> permutation = computeTopologicalOrdering(exprs, cohorts);
for (int i : permutation) {
RexNode expr = exprs[i];
final boolean condition = i == conditionOrdinal;
if (i < inputFieldCount) {
assert expr instanceof RexInputRef;
exprLevels[i] = -1;
continue;
}
// Deduce the minimum level of the expression. An expression must
// be at a level greater than or equal to all of its inputs.
int level = maxInputFinder.maxInputFor(expr);
// If the expression is in a cohort, it can occur no lower than the
// levels of other expressions in the same cohort.
Set<Integer> cohort = findCohort(cohorts, i);
if (cohort != null) {
for (Integer exprOrdinal : cohort) {
if (exprOrdinal == i) {
// of effort to repeat.
continue;
}
final RexNode cohortExpr = exprs[exprOrdinal];
int cohortLevel = maxInputFinder.maxInputFor(cohortExpr);
if (cohortLevel > level) {
level = cohortLevel;
}
}
}
// If that is not possible, try to implement it at higher levels.
levelLoop: for (; ; ++level) {
if (level >= levelCount) {
// This is a new level. We can use any type we like.
for (int relTypeOrdinal = 0; relTypeOrdinal < relTypes.length; relTypeOrdinal++) {
if (!relTypesPossibleForTopLevel[relTypeOrdinal]) {
continue;
}
if (relTypes[relTypeOrdinal].canImplement(expr, condition)) {
// Success. We have found a type where we can
// implement this expression.
exprLevels[i] = level;
levelTypeOrdinals[level] = relTypeOrdinal;
assert (level == 0) || (levelTypeOrdinals[level - 1] != levelTypeOrdinals[level]) : "successive levels of same type";
// Previous reltypes are not possible.
for (int j = 0; j < relTypeOrdinal; ++j) {
relTypesPossibleForTopLevel[j] = false;
}
// Successive reltypes may be possible.
for (int j = relTypeOrdinal + 1; j < relTypes.length; ++j) {
if (relTypesPossibleForTopLevel[j]) {
relTypesPossibleForTopLevel[j] = relTypes[j].canImplement(expr, condition);
}
}
// Move to next level.
levelTypeOrdinals[levelCount] = firstSet(relTypesPossibleForTopLevel);
++levelCount;
Arrays.fill(relTypesPossibleForTopLevel, true);
break levelLoop;
}
}
// level, with all options open?
if (count(relTypesPossibleForTopLevel) >= relTypes.length) {
// Cannot implement for any type.
throw new AssertionError("cannot implement " + expr);
}
levelTypeOrdinals[levelCount] = firstSet(relTypesPossibleForTopLevel);
++levelCount;
Arrays.fill(relTypesPossibleForTopLevel, true);
} else {
final int levelTypeOrdinal = levelTypeOrdinals[level];
if (!relTypes[levelTypeOrdinal].canImplement(expr, condition)) {
// continue to next level.
continue;
}
exprLevels[i] = level;
break;
}
}
}
if (levelCount > 0) {
// implemented.
assert "CalcRelType".equals(relTypes[0].name) : "The first RelType should be CalcRelType for proper RexLiteral" + " implementation at the last level, got " + relTypes[0].name;
if (levelTypeOrdinals[levelCount - 1] != 0) {
levelCount++;
}
}
return levelCount;
}
Aggregations