Java Code Examples for org.apache.calcite.tools.RelBuilder#push()
The following examples show how to use
org.apache.calcite.tools.RelBuilder#push() .
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: FlinkAggregateRemoveRule.java From flink with Apache License 2.0 | 6 votes |
public void onMatch(RelOptRuleCall call) { final Aggregate aggregate = call.rel(0); final RelNode input = call.rel(1); // Distinct is "GROUP BY c1, c2" (where c1, c2 are a set of columns on // which the input is unique, i.e. contain a key) and has no aggregate // functions or the functions we enumerated. It can be removed. final RelNode newInput = convert(input, aggregate.getTraitSet().simplify()); // If aggregate was projecting a subset of columns, add a project for the // same effect. final RelBuilder relBuilder = call.builder(); relBuilder.push(newInput); List<Integer> projectIndices = new ArrayList<>(aggregate.getGroupSet().asList()); for (AggregateCall aggCall : aggregate.getAggCallList()) { projectIndices.addAll(aggCall.getArgList()); } relBuilder.project(relBuilder.fields(projectIndices)); // Create a project if some of the columns have become // NOT NULL due to aggregate functions are removed relBuilder.convert(aggregate.getRowType(), true); call.transformTo(relBuilder.build()); }
Example 2
Source File: PushProjectIntoScanRule.java From dremio-oss with Apache License 2.0 | 5 votes |
@Override public void onMatch(RelOptRuleCall call) { final Project proj = call.rel(0); final ScanCrel scan = call.rel(1); ProjectPushInfo columnInfo = PrelUtil.getColumns(scan.getRowType(), proj.getProjects()); // get TableBase, either wrapped in RelOptTable, or TranslatableTable. TableBase table = scan.getTable().unwrap(TableBase.class); if (columnInfo == null || columnInfo.isStarQuery()) { return; } ScanCrel newScan = scan.cloneWithProject(columnInfo.columns); List<RexNode> newProjects = Lists.newArrayList(); for (RexNode n : proj.getChildExps()) { newProjects.add(n.accept(columnInfo.getInputRewriter())); } final RelBuilder relBuilder = relBuilderFactory.create(proj.getCluster(), null); relBuilder.push(newScan); relBuilder.project(newProjects, proj.getRowType().getFieldNames()); final RelNode newProj = relBuilder.build(); if (newProj instanceof Project && ProjectRemoveRule.isTrivial((Project) newProj) && newScan.getRowType().getFullTypeString().equals(newProj.getRowType().getFullTypeString())) { call.transformTo(newScan); } else { if(newScan.getProjectedColumns().equals(scan.getProjectedColumns())) { // no point in doing a pushdown that doesn't change anything. return; } call.transformTo(newProj); } }
Example 3
Source File: SemiJoinProjectTransposeRule.java From calcite with Apache License 2.0 | 5 votes |
public void onMatch(RelOptRuleCall call) { LogicalJoin semiJoin = call.rel(0); LogicalProject project = call.rel(1); // Convert the LHS semi-join keys to reference the child projection // expression; all projection expressions must be RexInputRefs, // otherwise, we wouldn't have created this semi-join. // convert the semijoin condition to reflect the LHS with the project // pulled up RexNode newCondition = adjustCondition(project, semiJoin); LogicalJoin newSemiJoin = LogicalJoin.create(project.getInput(), semiJoin.getRight(), // No need to copy the hints, the framework would try to do that. ImmutableList.of(), newCondition, ImmutableSet.of(), JoinRelType.SEMI); // Create the new projection. Note that the projection expressions // are the same as the original because they only reference the LHS // of the semijoin and the semijoin only projects out the LHS final RelBuilder relBuilder = call.builder(); relBuilder.push(newSemiJoin); relBuilder.project(project.getProjects(), project.getRowType().getFieldNames()); call.transformTo(relBuilder.build()); }
Example 4
Source File: CalcSplitRule.java From Bats with Apache License 2.0 | 5 votes |
@Override public void onMatch(RelOptRuleCall call) { final Calc calc = call.rel(0); final Pair<ImmutableList<RexNode>, ImmutableList<RexNode>> projectFilter = calc.getProgram().split(); final RelBuilder relBuilder = call.builder(); relBuilder.push(calc.getInput()); relBuilder.filter(projectFilter.right); relBuilder.project(projectFilter.left, calc.getRowType().getFieldNames()); call.transformTo(relBuilder.build()); }
Example 5
Source File: AggregateExpandDistinctAggregatesRule.java From calcite with Apache License 2.0 | 5 votes |
/** * Converts an aggregate relational expression that contains just one * distinct aggregate function (or perhaps several over the same arguments) * and no non-distinct aggregate functions. */ private RelBuilder convertMonopole(RelBuilder relBuilder, Aggregate aggregate, List<Integer> argList, int filterArg) { // For example, // SELECT deptno, COUNT(DISTINCT sal), SUM(DISTINCT sal) // FROM emp // GROUP BY deptno // // becomes // // SELECT deptno, COUNT(distinct_sal), SUM(distinct_sal) // FROM ( // SELECT DISTINCT deptno, sal AS distinct_sal // FROM EMP GROUP BY deptno) // GROUP BY deptno // Project the columns of the GROUP BY plus the arguments // to the agg function. final Map<Integer, Integer> sourceOf = new HashMap<>(); createSelectDistinct(relBuilder, aggregate, argList, filterArg, sourceOf); // Create an aggregate on top, with the new aggregate list. final List<AggregateCall> newAggCalls = Lists.newArrayList(aggregate.getAggCallList()); rewriteAggCalls(newAggCalls, argList, sourceOf); final int cardinality = aggregate.getGroupSet().cardinality(); relBuilder.push( aggregate.copy(aggregate.getTraitSet(), relBuilder.build(), ImmutableBitSet.range(cardinality), null, newAggCalls)); return relBuilder; }
Example 6
Source File: SemiJoinProjectTransposeRule.java From Bats with Apache License 2.0 | 5 votes |
public void onMatch(RelOptRuleCall call) { SemiJoin semiJoin = call.rel(0); LogicalProject project = call.rel(1); // Convert the LHS semi-join keys to reference the child projection // expression; all projection expressions must be RexInputRefs, // otherwise, we wouldn't have created this semi-join. final List<Integer> newLeftKeys = new ArrayList<>(); final List<Integer> leftKeys = semiJoin.getLeftKeys(); final List<RexNode> projExprs = project.getProjects(); for (int leftKey : leftKeys) { RexInputRef inputRef = (RexInputRef) projExprs.get(leftKey); newLeftKeys.add(inputRef.getIndex()); } // convert the semijoin condition to reflect the LHS with the project // pulled up RexNode newCondition = adjustCondition(project, semiJoin); SemiJoin newSemiJoin = SemiJoin.create(project.getInput(), semiJoin.getRight(), newCondition, ImmutableIntList.copyOf(newLeftKeys), semiJoin.getRightKeys()); // Create the new projection. Note that the projection expressions // are the same as the original because they only reference the LHS // of the semijoin and the semijoin only projects out the LHS final RelBuilder relBuilder = call.builder(); relBuilder.push(newSemiJoin); relBuilder.project(projExprs, project.getRowType().getFieldNames()); call.transformTo(relBuilder.build()); }
Example 7
Source File: PushFilterPastProjectRule.java From dremio-oss with Apache License 2.0 | 4 votes |
@Override public void onMatch(RelOptRuleCall call) { Filter filterRel = call.rel(0); Project projRel = call.rel(1); // get a conjunctions of the filter condition. For each conjunction, if it refers to ITEM or FLATTEN expression // then we could not pushed down. Otherwise, it's qualified to be pushed down. final List<RexNode> predList = RelOptUtil.conjunctions(filterRel.getCondition()); final List<RexNode> qualifiedPredList = Lists.newArrayList(); final List<RexNode> unqualifiedPredList = Lists.newArrayList(); for (final RexNode pred : predList) { if (qualifies(pred, projRel.getProjects())) { qualifiedPredList.add(pred); } else { unqualifiedPredList.add(pred); } } final RexNode qualifedPred = RexUtil.composeConjunction(filterRel.getCluster().getRexBuilder(), qualifiedPredList, true); if (qualifedPred == null) { return; } // convert the filter to one that references the child of the project RexNode newCondition = RelOptUtil.pushPastProject(qualifedPred, projRel); RelBuilder relBuilder = relBuilderFactory.create(filterRel.getCluster(), null); relBuilder.push(projRel.getInput()); relBuilder.filter(newCondition); relBuilder.project(Pair.left(projRel.getNamedProjects()), Pair.right(projRel.getNamedProjects())); final RexNode unqualifiedPred = RexUtil.composeConjunction(filterRel.getCluster().getRexBuilder(), unqualifiedPredList, true); if (unqualifiedPred == null) { call.transformTo(relBuilder.build()); } else { // if there are filters not qualified to be pushed down, then we have to put those filters on top of // the new Project operator. // Filter -- unqualified filters // \ // Project // \ // Filter -- qualified filters relBuilder.filter(unqualifiedPred); call.transformTo(relBuilder.build()); } }
Example 8
Source File: RelWriterTest.java From calcite with Apache License 2.0 | 4 votes |
@Test void testTableModifyMerge() { final FrameworkConfig config = RelBuilderTest.config().build(); final RelBuilder builder = RelBuilder.create(config); RelNode deptScan = builder.scan("DEPT").build(); RelNode empScan = builder.scan("EMP").build(); builder.push(deptScan); builder.push(empScan); RelNode project = builder .join(JoinRelType.LEFT, builder.call( SqlStdOperatorTable.EQUALS, builder.field(2, 0, "DEPTNO"), builder.field(2, 1, "DEPTNO"))) .project( builder.literal(0), builder.literal("x"), builder.literal("x"), builder.literal(0), builder.literal("20200501 10:00:00"), builder.literal(0), builder.literal(0), builder.literal(0), builder.literal("false"), builder.field(1, 0, 2), builder.field(1, 0, 3), builder.field(1, 0, 4), builder.field(1, 0, 5), builder.field(1, 0, 6), builder.field(1, 0, 7), builder.field(1, 0, 8), builder.field(1, 0, 9), builder.field(1, 0, 10), builder.literal("a")) .build(); // for sql: // merge into emp using dept on emp.deptno = dept.deptno // when matched then update set job = 'a' // when not matched then insert values(0, 'x', 'x', 0, '20200501 10:00:00', 0, 0, 0, 0) LogicalTableModify modify = LogicalTableModify.create( empScan.getTable(), (Prepare.CatalogReader) empScan.getTable().getRelOptSchema(), project, TableModify.Operation.MERGE, ImmutableList.of("ENAME"), null, false); String relJson = RelOptUtil.dumpPlan("", modify, SqlExplainFormat.JSON, SqlExplainLevel.EXPPLAN_ATTRIBUTES); String s = deserializeAndDumpToTextFormat(getSchema(modify), relJson); final String expected = "" + "LogicalTableModify(table=[[scott, EMP]], operation=[MERGE], " + "updateColumnList=[[ENAME]], flattened=[false])\n" + " LogicalProject($f0=[0], $f1=['x'], $f2=['x'], $f3=[0], $f4=['20200501 10:00:00'], " + "$f5=[0], $f6=[0], $f7=[0], $f8=['false'], LOC=[$2], EMPNO=[$3], ENAME=[$4], JOB=[$5], " + "MGR=[$6], HIREDATE=[$7], SAL=[$8], COMM=[$9], DEPTNO=[$10], $f18=['a'])\n" + " LogicalJoin(condition=[=($0, $10)], joinType=[left])\n" + " LogicalTableScan(table=[[scott, DEPT]])\n" + " LogicalTableScan(table=[[scott, EMP]])\n"; assertThat(s, isLinux(expected)); }
Example 9
Source File: FlinkAggregateExpandDistinctAggregatesRule.java From flink with Apache License 2.0 | 4 votes |
/** * Given an {@link org.apache.calcite.rel.core.Aggregate} * and the ordinals of the arguments to a * particular call to an aggregate function, creates a 'select distinct' * relational expression which projects the group columns and those * arguments but nothing else. * * <p>For example, given * * <blockquote> * <pre>select f0, count(distinct f1), count(distinct f2) * from t group by f0</pre> * </blockquote> * * <p>and the argument list * * <blockquote>{2}</blockquote> * * <p>returns * * <blockquote> * <pre>select distinct f0, f2 from t</pre> * </blockquote> * * <p>The <code>sourceOf</code> map is populated with the source of each * column; in this case sourceOf.get(0) = 0, and sourceOf.get(1) = 2. * * @param relBuilder Relational expression builder * @param aggregate Aggregate relational expression * @param argList Ordinals of columns to make distinct * @param filterArg Ordinal of column to filter on, or -1 * @param sourceOf Out parameter, is populated with a map of where each * output field came from * @return Aggregate relational expression which projects the required * columns */ private RelBuilder createSelectDistinct(RelBuilder relBuilder, Aggregate aggregate, List<Integer> argList, int filterArg, Map<Integer, Integer> sourceOf) { relBuilder.push(aggregate.getInput()); final List<Pair<RexNode, String>> projects = new ArrayList<>(); final List<RelDataTypeField> childFields = relBuilder.peek().getRowType().getFieldList(); for (int i : aggregate.getGroupSet()) { sourceOf.put(i, projects.size()); projects.add(RexInputRef.of2(i, childFields)); } if (filterArg >= 0) { sourceOf.put(filterArg, projects.size()); projects.add(RexInputRef.of2(filterArg, childFields)); } for (Integer arg : argList) { if (filterArg >= 0) { // Implement // agg(DISTINCT arg) FILTER $f // by generating // SELECT DISTINCT ... CASE WHEN $f THEN arg ELSE NULL END AS arg // and then applying // agg(arg) // as usual. // // It works except for (rare) agg functions that need to see null // values. final RexBuilder rexBuilder = aggregate.getCluster().getRexBuilder(); final RexInputRef filterRef = RexInputRef.of(filterArg, childFields); final Pair<RexNode, String> argRef = RexInputRef.of2(arg, childFields); RexNode condition = rexBuilder.makeCall(SqlStdOperatorTable.CASE, filterRef, argRef.left, rexBuilder.ensureType(argRef.left.getType(), rexBuilder.makeCast(argRef.left.getType(), rexBuilder.constantNull()), true)); sourceOf.put(arg, projects.size()); projects.add(Pair.of(condition, "i$" + argRef.right)); continue; } if (sourceOf.get(arg) != null) { continue; } sourceOf.put(arg, projects.size()); projects.add(RexInputRef.of2(arg, childFields)); } relBuilder.project(Pair.left(projects), Pair.right(projects)); // Get the distinct values of the GROUP BY fields and the arguments // to the agg functions. relBuilder.push( aggregate.copy(aggregate.getTraitSet(), relBuilder.build(), false, ImmutableBitSet.range(projects.size()), null, com.google.common.collect.ImmutableList.<AggregateCall>of())); return relBuilder; }
Example 10
Source File: UnionMergeRule.java From calcite with Apache License 2.0 | 4 votes |
public void onMatch(RelOptRuleCall call) { final SetOp topOp = call.rel(0); @SuppressWarnings("unchecked") final Class<? extends SetOp> setOpClass = (Class) operands.get(0).getMatchedClass(); // For Union and Intersect, we want to combine the set-op that's in the // second input first. // // For example, we reduce // Union(Union(a, b), Union(c, d)) // to // Union(Union(a, b), c, d) // in preference to // Union(a, b, Union(c, d)) // // But for Minus, we can only reduce the left input. It is not valid to // reduce // Minus(a, Minus(b, c)) // to // Minus(a, b, c) // // Hence, that's why the rule pattern matches on generic RelNodes rather // than explicit sub-classes of SetOp. By doing so, and firing this rule // in a bottom-up order, it allows us to only specify a single // pattern for this rule. final SetOp bottomOp; if (setOpClass.isInstance(call.rel(2)) && !Minus.class.isAssignableFrom(setOpClass)) { bottomOp = call.rel(2); } else if (setOpClass.isInstance(call.rel(1))) { bottomOp = call.rel(1); } else { return; } // Can only combine (1) if all operators are ALL, // or (2) top operator is DISTINCT (i.e. not ALL). // In case (2), all operators become DISTINCT. if (topOp.all && !bottomOp.all) { return; } // Combine the inputs from the bottom set-op with the other inputs from // the top set-op. final RelBuilder relBuilder = call.builder(); if (setOpClass.isInstance(call.rel(2)) && !Minus.class.isAssignableFrom(setOpClass)) { relBuilder.push(topOp.getInput(0)); relBuilder.pushAll(bottomOp.getInputs()); // topOp.getInputs().size() may be more than 2 for (int index = 2; index < topOp.getInputs().size(); index++) { relBuilder.push(topOp.getInput(index)); } } else { relBuilder.pushAll(bottomOp.getInputs()); relBuilder.pushAll(Util.skip(topOp.getInputs())); } int n = bottomOp.getInputs().size() + topOp.getInputs().size() - 1; if (topOp instanceof Union) { relBuilder.union(topOp.all, n); } else if (topOp instanceof Intersect) { relBuilder.intersect(topOp.all, n); } else if (topOp instanceof Minus) { relBuilder.minus(topOp.all, n); } call.transformTo(relBuilder.build()); }
Example 11
Source File: ProjectMergeRule.java From Bats with Apache License 2.0 | 4 votes |
public void onMatch(RelOptRuleCall call) { final Project topProject = call.rel(0); final Project bottomProject = call.rel(1); final RelBuilder relBuilder = call.builder(); // If one or both projects are permutations, short-circuit the complex logic // of building a RexProgram. final Permutation topPermutation = topProject.getPermutation(); if (topPermutation != null) { if (topPermutation.isIdentity()) { // Let ProjectRemoveRule handle this. return; } final Permutation bottomPermutation = bottomProject.getPermutation(); if (bottomPermutation != null) { if (bottomPermutation.isIdentity()) { // Let ProjectRemoveRule handle this. return; } final Permutation product = topPermutation.product(bottomPermutation); relBuilder.push(bottomProject.getInput()); relBuilder.project(relBuilder.fields(product), topProject.getRowType().getFieldNames()); call.transformTo(relBuilder.build()); return; } } // If we're not in force mode and the two projects reference identical // inputs, then return and let ProjectRemoveRule replace the projects. if (!force) { if (RexUtil.isIdentity(topProject.getProjects(), topProject.getInput().getRowType())) { return; } } final List<RexNode> newProjects = RelOptUtil.pushPastProject(topProject.getProjects(), bottomProject); final RelNode input = bottomProject.getInput(); if (RexUtil.isIdentity(newProjects, input.getRowType())) { if (force || input.getRowType().getFieldNames() .equals(topProject.getRowType().getFieldNames())) { call.transformTo(input); return; } } // replace the two projects with a combined projection relBuilder.push(bottomProject.getInput()); relBuilder.project(newProjects, topProject.getRowType().getFieldNames()); call.transformTo(relBuilder.build()); }
Example 12
Source File: SemiJoinRule.java From Bats with Apache License 2.0 | 4 votes |
protected void perform(RelOptRuleCall call, Project project, Join join, RelNode left, Aggregate aggregate) { final RelOptCluster cluster = join.getCluster(); final RexBuilder rexBuilder = cluster.getRexBuilder(); if (project != null) { final ImmutableBitSet bits = RelOptUtil.InputFinder.bits(project.getProjects(), null); final ImmutableBitSet rightBits = ImmutableBitSet.range(left.getRowType().getFieldCount(), join.getRowType().getFieldCount()); if (bits.intersects(rightBits)) { return; } } final JoinInfo joinInfo = join.analyzeCondition(); if (!joinInfo.rightSet().equals( ImmutableBitSet.range(aggregate.getGroupCount()))) { // Rule requires that aggregate key to be the same as the join key. // By the way, neither a super-set nor a sub-set would work. return; } if (!joinInfo.isEqui()) { return; } final RelBuilder relBuilder = call.builder(); relBuilder.push(left); switch (join.getJoinType()) { case INNER: final List<Integer> newRightKeyBuilder = new ArrayList<>(); final List<Integer> aggregateKeys = aggregate.getGroupSet().asList(); for (int key : joinInfo.rightKeys) { newRightKeyBuilder.add(aggregateKeys.get(key)); } final ImmutableIntList newRightKeys = ImmutableIntList.copyOf(newRightKeyBuilder); relBuilder.push(aggregate.getInput()); final RexNode newCondition = RelOptUtil.createEquiJoinCondition(relBuilder.peek(2, 0), joinInfo.leftKeys, relBuilder.peek(2, 1), newRightKeys, rexBuilder); relBuilder.semiJoin(newCondition); break; case LEFT: // The right-hand side produces no more than 1 row (because of the // Aggregate) and no fewer than 1 row (because of LEFT), and therefore // we can eliminate the semi-join. break; default: throw new AssertionError(join.getJoinType()); } if (project != null) { relBuilder.project(project.getProjects(), project.getRowType().getFieldNames()); } call.transformTo(relBuilder.build()); }
Example 13
Source File: IncrementalUpdateUtils.java From dremio-oss with Apache License 2.0 | 4 votes |
public RelNode updateScan(IncrementallyUpdateable scan) { RelNode newScan = scan; RelDataTypeField refreshField = scan.getRowType().getField(refreshColumn, false, false); if (refreshField == null) { // Check if the field exist as part of the schema newScan = scan.projectInvisibleColumn(refreshColumn); if (newScan == null) { throw UserException.dataReadError() .message("Table does not include column identified for incremental update of name '%s'.", refreshColumn) .build(logger); } refreshField = newScan.getRowType().getField(refreshColumn, false, false); } switch(refreshField.getType().getSqlTypeName()) { case INTEGER: case BIGINT: case FLOAT: case DOUBLE: case VARCHAR: case TIMESTAMP: case DATE: case DECIMAL: break; default: throw UserException.dataReadError() .message("Dremio only supports incremental column update on INTEGER, BIGINT, FLOAT, DOUBLE, VARCHAR, TIMESTAMP, DATE, and DECIMAL types. The identified column was of type %s.", refreshField.getType().getSqlTypeName()) .build(logger); } // No need to add a project if field name is correct if (UPDATE_COLUMN.equals(refreshColumn)) { return newScan; } final RelBuilder relBuilder = newCalciteRelBuilderWithoutContext(newScan.getCluster()); relBuilder.push(newScan); List<String> newFieldNames = ImmutableList.<String>builder().addAll(newScan.getRowType().getFieldNames()).add(UPDATE_COLUMN).build(); Iterable<RexNode> projects = Stream.concat( scan.getRowType().getFieldNames().stream().map(relBuilder::field), Stream.of(refreshRex(relBuilder))) .collect(Collectors.toList()); relBuilder.project(projects, newFieldNames); return relBuilder.build(); }
Example 14
Source File: FilterCorrelateRule.java From Bats with Apache License 2.0 | 4 votes |
public void onMatch(RelOptRuleCall call) { final Filter filter = call.rel(0); final Correlate corr = call.rel(1); final List<RexNode> aboveFilters = RelOptUtil.conjunctions(filter.getCondition()); final List<RexNode> leftFilters = new ArrayList<>(); final List<RexNode> rightFilters = new ArrayList<>(); // Try to push down above filters. These are typically where clause // filters. They can be pushed down if they are not on the NULL // generating side. RelOptUtil.classifyFilters( corr, aboveFilters, JoinRelType.INNER, false, !corr.getJoinType().toJoinType().generatesNullsOnLeft(), !corr.getJoinType().toJoinType().generatesNullsOnRight(), aboveFilters, leftFilters, rightFilters); if (leftFilters.isEmpty() && rightFilters.isEmpty()) { // no filters got pushed return; } // Create Filters on top of the children if any filters were // pushed to them. final RexBuilder rexBuilder = corr.getCluster().getRexBuilder(); final RelBuilder relBuilder = call.builder(); final RelNode leftRel = relBuilder.push(corr.getLeft()).filter(leftFilters).build(); final RelNode rightRel = relBuilder.push(corr.getRight()).filter(rightFilters).build(); // Create the new Correlate RelNode newCorrRel = corr.copy(corr.getTraitSet(), ImmutableList.of(leftRel, rightRel)); call.getPlanner().onCopy(corr, newCorrRel); if (!leftFilters.isEmpty()) { call.getPlanner().onCopy(filter, leftRel); } if (!rightFilters.isEmpty()) { call.getPlanner().onCopy(filter, rightRel); } // Create a Filter on top of the join if needed relBuilder.push(newCorrRel); relBuilder.filter( RexUtil.fixUp(rexBuilder, aboveFilters, RelOptUtil.getFieldTypeList(relBuilder.peek().getRowType()))); call.transformTo(relBuilder.build()); }
Example 15
Source File: AggregateProjectMergeRule.java From calcite with Apache License 2.0 | 4 votes |
public static RelNode apply(RelOptRuleCall call, Aggregate aggregate, Project project) { // Find all fields which we need to be straightforward field projections. final Set<Integer> interestingFields = RelOptUtil.getAllFields(aggregate); // Build the map from old to new; abort if any entry is not a // straightforward field projection. final Map<Integer, Integer> map = new HashMap<>(); for (int source : interestingFields) { final RexNode rex = project.getProjects().get(source); if (!(rex instanceof RexInputRef)) { return null; } map.put(source, ((RexInputRef) rex).getIndex()); } final ImmutableBitSet newGroupSet = aggregate.getGroupSet().permute(map); ImmutableList<ImmutableBitSet> newGroupingSets = null; if (aggregate.getGroupType() != Group.SIMPLE) { newGroupingSets = ImmutableBitSet.ORDERING.immutableSortedCopy( ImmutableBitSet.permute(aggregate.getGroupSets(), map)); } final ImmutableList.Builder<AggregateCall> aggCalls = ImmutableList.builder(); final int sourceCount = aggregate.getInput().getRowType().getFieldCount(); final int targetCount = project.getInput().getRowType().getFieldCount(); final Mappings.TargetMapping targetMapping = Mappings.target(map, sourceCount, targetCount); for (AggregateCall aggregateCall : aggregate.getAggCallList()) { aggCalls.add(aggregateCall.transform(targetMapping)); } final Aggregate newAggregate = aggregate.copy(aggregate.getTraitSet(), project.getInput(), newGroupSet, newGroupingSets, aggCalls.build()); // Add a project if the group set is not in the same order or // contains duplicates. final RelBuilder relBuilder = call.builder(); relBuilder.push(newAggregate); final List<Integer> newKeys = Lists.transform(aggregate.getGroupSet().asList(), map::get); if (!newKeys.equals(newGroupSet.asList())) { final List<Integer> posList = new ArrayList<>(); for (int newKey : newKeys) { posList.add(newGroupSet.indexOf(newKey)); } for (int i = newAggregate.getGroupCount(); i < newAggregate.getRowType().getFieldCount(); i++) { posList.add(i); } relBuilder.project(relBuilder.fields(posList)); } return relBuilder.build(); }
Example 16
Source File: UnionMergeRule.java From Bats with Apache License 2.0 | 4 votes |
public void onMatch(RelOptRuleCall call) { final SetOp topOp = call.rel(0); @SuppressWarnings("unchecked") final Class<? extends SetOp> setOpClass = (Class) operands.get(0).getMatchedClass(); // For Union and Intersect, we want to combine the set-op that's in the // second input first. // // For example, we reduce // Union(Union(a, b), Union(c, d)) // to // Union(Union(a, b), c, d) // in preference to // Union(a, b, Union(c, d)) // // But for Minus, we can only reduce the left input. It is not valid to // reduce // Minus(a, Minus(b, c)) // to // Minus(a, b, c) // // Hence, that's why the rule pattern matches on generic RelNodes rather // than explicit sub-classes of SetOp. By doing so, and firing this rule // in a bottom-up order, it allows us to only specify a single // pattern for this rule. final SetOp bottomOp; if (setOpClass.isInstance(call.rel(2)) && !Minus.class.isAssignableFrom(setOpClass)) { bottomOp = call.rel(2); } else if (setOpClass.isInstance(call.rel(1))) { bottomOp = call.rel(1); } else { return; } // Can only combine (1) if all operators are ALL, // or (2) top operator is DISTINCT (i.e. not ALL). // In case (2), all operators become DISTINCT. if (topOp.all && !bottomOp.all) { return; } // Combine the inputs from the bottom set-op with the other inputs from // the top set-op. final RelBuilder relBuilder = call.builder(); if (setOpClass.isInstance(call.rel(2)) && !Minus.class.isAssignableFrom(setOpClass)) { relBuilder.push(topOp.getInput(0)); relBuilder.pushAll(bottomOp.getInputs()); // topOp.getInputs().size() may be more than 2 for (int index = 2; index < topOp.getInputs().size(); index++) { relBuilder.push(topOp.getInput(index)); } } else { relBuilder.pushAll(bottomOp.getInputs()); relBuilder.pushAll(Util.skip(topOp.getInputs())); } int n = bottomOp.getInputs().size() + topOp.getInputs().size() - 1; if (topOp instanceof Union) { relBuilder.union(topOp.all, n); } else if (topOp instanceof Intersect) { relBuilder.intersect(topOp.all, n); } else if (topOp instanceof Minus) { relBuilder.minus(topOp.all, n); } call.transformTo(relBuilder.build()); }
Example 17
Source File: AggregateReduceFunctionsRule.java From calcite with Apache License 2.0 | 4 votes |
/** * Reduces calls to functions AVG, SUM, STDDEV_POP, STDDEV_SAMP, VAR_POP, * VAR_SAMP, COVAR_POP, COVAR_SAMP, REGR_SXX, REGR_SYY if the function is * present in {@link AggregateReduceFunctionsRule#functionsToReduce} * * <p>It handles newly generated common subexpressions since this was done * at the sql2rel stage. */ private void reduceAggs( RelOptRuleCall ruleCall, Aggregate oldAggRel) { RexBuilder rexBuilder = oldAggRel.getCluster().getRexBuilder(); List<AggregateCall> oldCalls = oldAggRel.getAggCallList(); final int groupCount = oldAggRel.getGroupCount(); final List<AggregateCall> newCalls = new ArrayList<>(); final Map<AggregateCall, RexNode> aggCallMapping = new HashMap<>(); final List<RexNode> projList = new ArrayList<>(); // pass through group key for (int i = 0; i < groupCount; ++i) { projList.add( rexBuilder.makeInputRef( getFieldType(oldAggRel, i), i)); } // List of input expressions. If a particular aggregate needs more, it // will add an expression to the end, and we will create an extra // project. final RelBuilder relBuilder = ruleCall.builder(); relBuilder.push(oldAggRel.getInput()); final List<RexNode> inputExprs = new ArrayList<>(relBuilder.fields()); // create new agg function calls and rest of project list together for (AggregateCall oldCall : oldCalls) { projList.add( reduceAgg( oldAggRel, oldCall, newCalls, aggCallMapping, inputExprs)); } final int extraArgCount = inputExprs.size() - relBuilder.peek().getRowType().getFieldCount(); if (extraArgCount > 0) { relBuilder.project(inputExprs, CompositeList.of( relBuilder.peek().getRowType().getFieldNames(), Collections.nCopies(extraArgCount, null))); } newAggregateRel(relBuilder, oldAggRel, newCalls); newCalcRel(relBuilder, oldAggRel.getRowType(), projList); ruleCall.transformTo(relBuilder.build()); }
Example 18
Source File: AggregateUnionTransposeRule.java From Bats with Apache License 2.0 | 4 votes |
public void onMatch(RelOptRuleCall call) { Aggregate aggRel = call.rel(0); Union union = call.rel(1); if (!union.all) { // This transformation is only valid for UNION ALL. // Consider t1(i) with rows (5), (5) and t2(i) with // rows (5), (10), and the query // select sum(i) from (select i from t1) union (select i from t2). // The correct answer is 15. If we apply the transformation, // we get // select sum(i) from // (select sum(i) as i from t1) union (select sum(i) as i from t2) // which yields 25 (incorrect). return; } int groupCount = aggRel.getGroupSet().cardinality(); List<AggregateCall> transformedAggCalls = transformAggCalls( aggRel.copy(aggRel.getTraitSet(), aggRel.getInput(), false, aggRel.getGroupSet(), null, aggRel.getAggCallList()), groupCount, aggRel.getAggCallList()); if (transformedAggCalls == null) { // we've detected the presence of something like AVG, // which we can't handle return; } // create corresponding aggregates on top of each union child final RelBuilder relBuilder = call.builder(); int transformCount = 0; final RelMetadataQuery mq = call.getMetadataQuery(); for (RelNode input : union.getInputs()) { boolean alreadyUnique = RelMdUtil.areColumnsDefinitelyUnique(mq, input, aggRel.getGroupSet()); relBuilder.push(input); if (!alreadyUnique) { ++transformCount; relBuilder.aggregate(relBuilder.groupKey(aggRel.getGroupSet()), aggRel.getAggCallList()); } } if (transformCount == 0) { // none of the children could benefit from the push-down, // so bail out (preventing the infinite loop to which most // planners would succumb) return; } // create a new union whose children are the aggregates created above relBuilder.union(true, union.getInputs().size()); relBuilder.aggregate( relBuilder.groupKey(aggRel.getGroupSet(), aggRel.getGroupSets()), transformedAggCalls); call.transformTo(relBuilder.build()); }
Example 19
Source File: UnionPullUpConstantsRule.java From Bats with Apache License 2.0 | 4 votes |
@Override public void onMatch(RelOptRuleCall call) { final Union union = call.rel(0); final int count = union.getRowType().getFieldCount(); if (count == 1) { // No room for optimization since we cannot create an empty Project // operator. If we created a Project with one column, this rule would // cycle. return; } final RexBuilder rexBuilder = union.getCluster().getRexBuilder(); final RelMetadataQuery mq = call.getMetadataQuery(); final RelOptPredicateList predicates = mq.getPulledUpPredicates(union); if (predicates == null) { return; } final Map<Integer, RexNode> constants = new HashMap<>(); for (Map.Entry<RexNode, RexNode> e : predicates.constantMap.entrySet()) { if (e.getKey() instanceof RexInputRef) { constants.put(((RexInputRef) e.getKey()).getIndex(), e.getValue()); } } // None of the expressions are constant. Nothing to do. if (constants.isEmpty()) { return; } // Create expressions for Project operators before and after the Union List<RelDataTypeField> fields = union.getRowType().getFieldList(); List<RexNode> topChildExprs = new ArrayList<>(); List<String> topChildExprsFields = new ArrayList<>(); List<RexNode> refs = new ArrayList<>(); ImmutableBitSet.Builder refsIndexBuilder = ImmutableBitSet.builder(); for (RelDataTypeField field : fields) { final RexNode constant = constants.get(field.getIndex()); if (constant != null) { topChildExprs.add(constant); topChildExprsFields.add(field.getName()); } else { final RexNode expr = rexBuilder.makeInputRef(union, field.getIndex()); topChildExprs.add(expr); topChildExprsFields.add(field.getName()); refs.add(expr); refsIndexBuilder.set(field.getIndex()); } } ImmutableBitSet refsIndex = refsIndexBuilder.build(); // Update top Project positions final Mappings.TargetMapping mapping = RelOptUtil.permutation(refs, union.getInput(0).getRowType()).inverse(); topChildExprs = ImmutableList.copyOf(RexUtil.apply(mapping, topChildExprs)); // Create new Project-Union-Project sequences final RelBuilder relBuilder = call.builder(); for (RelNode input : union.getInputs()) { List<Pair<RexNode, String>> newChildExprs = new ArrayList<>(); for (int j : refsIndex) { newChildExprs.add( Pair.of(rexBuilder.makeInputRef(input, j), input.getRowType().getFieldList().get(j).getName())); } if (newChildExprs.isEmpty()) { // At least a single item in project is required. newChildExprs.add( Pair.of(topChildExprs.get(0), topChildExprsFields.get(0))); } // Add the input with project on top relBuilder.push(input); relBuilder.project(Pair.left(newChildExprs), Pair.right(newChildExprs)); } relBuilder.union(union.all, union.getInputs().size()); // Create top Project fixing nullability of fields relBuilder.project(topChildExprs, topChildExprsFields); relBuilder.convert(union.getRowType(), false); call.transformTo(relBuilder.build()); }
Example 20
Source File: AbstractMaterializedViewRule.java From Bats with Apache License 2.0 | 4 votes |
@Override protected RelNode createUnion(RelBuilder relBuilder, RexBuilder rexBuilder, RelNode topProject, RelNode unionInputQuery, RelNode unionInputView) { // Union relBuilder.push(unionInputQuery); relBuilder.push(unionInputView); relBuilder.union(true); List<RexNode> exprList = new ArrayList<>(relBuilder.peek().getRowType().getFieldCount()); List<String> nameList = new ArrayList<>(relBuilder.peek().getRowType().getFieldCount()); for (int i = 0; i < relBuilder.peek().getRowType().getFieldCount(); i++) { // We can take unionInputQuery as it is query based. RelDataTypeField field = unionInputQuery.getRowType().getFieldList().get(i); exprList.add( rexBuilder.ensureType(field.getType(), rexBuilder.makeInputRef(relBuilder.peek(), i), true)); nameList.add(field.getName()); } relBuilder.project(exprList, nameList); // Rollup aggregate Aggregate aggregate = (Aggregate) unionInputQuery; final ImmutableBitSet groupSet = ImmutableBitSet.range(aggregate.getGroupCount()); final List<AggCall> aggregateCalls = new ArrayList<>(); for (int i = 0; i < aggregate.getAggCallList().size(); i++) { AggregateCall aggCall = aggregate.getAggCallList().get(i); if (aggCall.isDistinct()) { // Cannot ROLLUP distinct return null; } SqlAggFunction rollupAgg = getRollup(aggCall.getAggregation()); if (rollupAgg == null) { // Cannot rollup this aggregate, bail out return null; } final RexInputRef operand = rexBuilder.makeInputRef(relBuilder.peek(), aggregate.getGroupCount() + i); aggregateCalls.add( // TODO: handle aggregate ordering relBuilder.aggregateCall(rollupAgg, operand).distinct(aggCall.isDistinct()) .approximate(aggCall.isApproximate()).as(aggCall.name)); } RelNode prevNode = relBuilder.peek(); RelNode result = relBuilder.aggregate(relBuilder.groupKey(groupSet), aggregateCalls).build(); if (prevNode == result && groupSet.cardinality() != result.getRowType().getFieldCount()) { // Aggregate was not inserted but we need to prune columns result = relBuilder.push(result).project(relBuilder.fields(groupSet.asList())).build(); } if (topProject != null) { // Top project return topProject.copy(topProject.getTraitSet(), ImmutableList.of(result)); } // Result return result; }