/** * Creates a relational expression which projects an array of expressions, and optionally * optimizes. * * <p>The result may not be a {@link ProjectRel}. If the projection is trivial, <code>child</code> * is returned directly; and future versions may return other formulations of expressions, such as * {@link CalcRel}. * * @param child input relational expression * @param exprs list of expressions for the input columns * @param fieldNames aliases of the expressions, or null to generate * @param optimize Whether to return <code>child</code> unchanged if the projections are trivial. */ public static RelNode createProject( RelNode child, List<RexNode> exprs, List<String> fieldNames, boolean optimize) { final RelOptCluster cluster = child.getCluster(); final RexProgram program = RexProgram.create(child.getRowType(), exprs, null, fieldNames, cluster.getRexBuilder()); final List<RelCollation> collationList = program.getCollations(child.getCollationList()); if (DEPRECATE_PROJECT_AND_FILTER) { return new CalcRel( cluster, child.getTraitSet(), child, program.getOutputRowType(), program, collationList); } else { final RelDataType rowType = RexUtil.createStructType( cluster.getTypeFactory(), exprs, fieldNames == null ? null : SqlValidatorUtil.uniquify(fieldNames, SqlValidatorUtil.F_SUGGESTER)); if (optimize && RemoveTrivialProjectRule.isIdentity(exprs, rowType, child.getRowType())) { return child; } return new ProjectRel( cluster, cluster.traitSetOf( collationList.isEmpty() ? RelCollationImpl.EMPTY : collationList.get(0)), child, exprs, rowType, ProjectRelBase.Flags.BOXED); } }
/** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link TableFunctionRel}. */ public TrimResult trimFields( TableFunctionRel tabFun, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final RelDataType rowType = tabFun.getRowType(); final int fieldCount = rowType.getFieldCount(); List<RelNode> newInputs = new ArrayList<RelNode>(); for (RelNode input : tabFun.getInputs()) { final int inputFieldCount = input.getRowType().getFieldCount(); BitSet inputFieldsUsed = Util.bitSetBetween(0, inputFieldCount); // Create input with trimmed columns. final Set<RelDataTypeField> inputExtraFields = Collections.emptySet(); TrimResult trimResult = trimChildRestore(tabFun, input, inputFieldsUsed, inputExtraFields); assert trimResult.right.isIdentity(); newInputs.add(trimResult.left); } TableFunctionRel newTabFun = tabFun; if (!tabFun.getInputs().equals(newInputs)) { newTabFun = tabFun.copy(tabFun.getTraitSet(), newInputs); } assert newTabFun.getClass() == tabFun.getClass(); // Always project all fields. Mapping mapping = Mappings.createIdentity(fieldCount); return new TrimResult(newTabFun, mapping); }
public static void foo(RelNode rel) { // Convert rel tree to JSON. final RelJsonWriter writer = new RelJsonWriter(); rel.explain(writer); final String json = writer.asString(); // Find the schema. If there are no tables in the plan, we won't need one. final RelOptSchema[] schemas = {null}; rel.accept( new RelShuttleImpl() { @Override public RelNode visit(TableAccessRelBase scan) { schemas[0] = scan.getTable().getRelOptSchema(); return super.visit(scan); } }); // Convert JSON back to rel tree. Frameworks.withPlanner( new Frameworks.PlannerAction<Object>() { public Object apply( RelOptCluster cluster, RelOptSchema relOptSchema, SchemaPlus rootSchema) { final RelJsonReader reader = new RelJsonReader(cluster, schemas[0], rootSchema); try { RelNode x = reader.read(json); } catch (IOException e) { throw new RuntimeException(e); } return null; } }); }
/** * Creates a relational expression which projects an array of expressions, and optionally * optimizes. * * <p>The result may not be a {@link ProjectRel}. If the projection is trivial, <code>child</code> * is returned directly; and future versions may return other formulations of expressions, such as * {@link CalcRel}. * * @param child input relational expression * @param exprs list of expressions for the input columns * @param fieldNames aliases of the expressions, or null to generate * @param optimize Whether to return <code>child</code> unchanged if the projections are trivial. */ public static RelNode createProject( RelNode child, List<RexNode> exprs, List<String> fieldNames, boolean optimize) { final RelOptCluster cluster = child.getCluster(); final RexProgram program = RexProgram.create(child.getRowType(), exprs, null, fieldNames, cluster.getRexBuilder()); final List<RelCollation> collationList = program.getCollations(child.getCollationList()); if (DeprecateProjectAndFilter) { return new CalcRel( cluster, child.getTraitSet(), child, program.getOutputRowType(), program, collationList); } else { final RelDataType rowType = RexUtil.createStructType(cluster.getTypeFactory(), exprs, fieldNames); if (optimize && RemoveTrivialProjectRule.isIdentity(exprs, rowType, child.getRowType())) { return child; } return new ProjectRel( cluster, cluster.traitSetOf( collationList.isEmpty() ? RelCollationImpl.EMPTY : collationList.get(0)), child, exprs, rowType, ProjectRelBase.Flags.Boxed); } }
public void visit(RelNode rel, int ordinal, RelNode parent) { // REVIEW: SWZ: 1/31/06: We assume that any special RelNodes, such // as the VolcanoPlanner's RelSubset always have a full complement // of traits and that they either appear as registered or do nothing // when childrenAccept is called on them. if (planner.isRegistered(rel)) { return; } RelTraitSet relTraits = rel.getTraitSet(); for (int i = 0; i < baseTraits.size(); i++) { if (i >= relTraits.size()) { // Copy traits that the new rel doesn't know about. Util.discard(RelOptUtil.addTrait(rel, baseTraits.getTrait(i))); // FIXME: Return the new rel. We can no longer traits in-place, // because rels and traits are immutable. throw new AssertionError(); } else { // Verify that the traits are from the same RelTraitDef assert relTraits.getTrait(i).getTraitDef() == baseTraits.getTrait(i).getTraitDef(); } } rel.childrenAccept(this); }
/** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link TableModificationRel}. */ public TrimResult trimFields( TableModificationRel modifier, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { // Ignore what consumer wants. We always project all columns. Util.discard(fieldsUsed); final RelDataType rowType = modifier.getRowType(); final int fieldCount = rowType.getFieldCount(); RelNode input = modifier.getChild(); // We want all fields from the child. final int inputFieldCount = input.getRowType().getFieldCount(); BitSet inputFieldsUsed = Util.bitSetBetween(0, inputFieldCount); // Create input with trimmed columns. final Set<RelDataTypeField> inputExtraFields = Collections.emptySet(); TrimResult trimResult = trimChild(modifier, input, inputFieldsUsed, inputExtraFields); RelNode newInput = trimResult.left; final Mapping inputMapping = trimResult.right; if (!inputMapping.isIdentity()) { // We asked for all fields. Can't believe that the child decided // to permute them! throw Util.newInternal("Expected identity mapping, got " + inputMapping); } TableModificationRel newModifier = modifier; if (newInput != input) { newModifier = modifier.copy(modifier.getTraitSet(), Collections.singletonList(newInput)); } assert newModifier.getClass() == modifier.getClass(); // Always project all fields. Mapping mapping = Mappings.createIdentity(fieldCount); return new TrimResult(newModifier, mapping); }
/** * Combines the outer join conditions and join types from the left and right join inputs. If the * join itself is either a left or right outer join, then the join condition corresponding to the * join is also set in the position corresponding to the null-generating input into the join. The * join type is also set. * * @param joinRel join rel * @param combinedInputs the combined inputs to the join * @param left left child of the joinrel * @param right right child of the joinrel * @param combinedConds the array containing the combined join conditions * @param joinTypes the array containing the combined join types * @return combined join filters AND'd together */ private RexNode[] combineOuterJoins( JoinRel joinRel, RelNode[] combinedInputs, RelNode left, RelNode right, RexNode[] combinedConds, JoinRelType[] joinTypes) { JoinRelType joinType = joinRel.getJoinType(); int nCombinedInputs = combinedInputs.length; boolean leftCombined = canCombine(left, joinType.generatesNullsOnLeft()); boolean rightCombined = canCombine(right, joinType.generatesNullsOnRight()); if (joinType == JoinRelType.LEFT) { if (leftCombined) { copyOuterJoinInfo((MultiJoinRel) left, combinedConds, joinTypes, 0, 0, null, null); } else { joinTypes[0] = JoinRelType.INNER; } combinedConds[nCombinedInputs - 1] = joinRel.getCondition(); joinTypes[nCombinedInputs - 1] = joinType; } else if (joinType == JoinRelType.RIGHT) { if (rightCombined) { copyOuterJoinInfo( (MultiJoinRel) right, combinedConds, joinTypes, 1, left.getRowType().getFieldCount(), right.getRowType().getFields(), joinRel.getRowType().getFields()); } else { joinTypes[nCombinedInputs - 1] = JoinRelType.INNER; } combinedConds[0] = joinRel.getCondition(); joinTypes[0] = joinType; } else { int nInputsLeft; if (leftCombined) { nInputsLeft = left.getInputs().length; copyOuterJoinInfo((MultiJoinRel) left, combinedConds, joinTypes, 0, 0, null, null); } else { nInputsLeft = 1; joinTypes[0] = JoinRelType.INNER; } if (rightCombined) { copyOuterJoinInfo( (MultiJoinRel) right, combinedConds, joinTypes, nInputsLeft, left.getRowType().getFieldCount(), right.getRowType().getFields(), joinRel.getRowType().getFields()); } else { joinTypes[nInputsLeft] = JoinRelType.INNER; } } return combinedConds; }
/** * Combines the inputs into a JoinRel into an array of inputs. * * @param join original join * @param left left input into join * @param right right input into join * @param projFieldsList returns a list of the new combined projection fields * @param joinFieldRefCountsList returns a list of the new combined join field reference counts * @return combined left and right inputs in an array */ private RelNode[] combineInputs( JoinRel join, RelNode left, RelNode right, List<BitSet> projFieldsList, List<int[]> joinFieldRefCountsList) { // leave the null generating sides of an outer join intact; don't // pull up those children inputs into the array we're constructing int nInputs; int nInputsOnLeft; MultiJoinRel leftMultiJoin = null; JoinRelType joinType = join.getJoinType(); boolean combineLeft = canCombine(left, joinType.generatesNullsOnLeft()); if (combineLeft) { leftMultiJoin = (MultiJoinRel) left; nInputs = left.getInputs().length; nInputsOnLeft = nInputs; } else { nInputs = 1; nInputsOnLeft = 1; } MultiJoinRel rightMultiJoin = null; boolean combineRight = canCombine(right, joinType.generatesNullsOnRight()); if (combineRight) { rightMultiJoin = (MultiJoinRel) right; nInputs += right.getInputs().length; } else { nInputs += 1; } RelNode[] newInputs = new RelNode[nInputs]; int i = 0; if (combineLeft) { for (; i < left.getInputs().length; i++) { newInputs[i] = leftMultiJoin.getInput(i); projFieldsList.add(((MultiJoinRel) left).getProjFields()[i]); joinFieldRefCountsList.add(((MultiJoinRel) left).getJoinFieldRefCountsMap().get(i)); } } else { newInputs[0] = left; i = 1; projFieldsList.add(null); joinFieldRefCountsList.add(new int[left.getRowType().getFieldCount()]); } if (combineRight) { for (; i < nInputs; i++) { newInputs[i] = rightMultiJoin.getInput(i - nInputsOnLeft); projFieldsList.add(((MultiJoinRel) right).getProjFields()[i - nInputsOnLeft]); joinFieldRefCountsList.add( ((MultiJoinRel) right).getJoinFieldRefCountsMap().get(i - nInputsOnLeft)); } } else { newInputs[i] = right; projFieldsList.add(null); joinFieldRefCountsList.add(new int[right.getRowType().getFieldCount()]); } return newInputs; }
/** * Trims the fields of an input relational expression. * * @param rel Relational expression * @param input Input relational expression, whose fields to trim * @param fieldsUsed Bitmap of fields needed by the consumer * @return New relational expression and its field mapping */ protected TrimResult trimChild( RelNode rel, RelNode input, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { Util.discard(rel); if (input.getClass().getName().endsWith("MedMdrClassExtentRel")) { // MedMdrJoinRule cannot handle Join of Project of // MedMdrClassExtentRel, only naked MedMdrClassExtentRel. // So, disable trimming. fieldsUsed = Util.bitSetBetween(0, input.getRowType().getFieldCount()); } return dispatchTrimFields(input, fieldsUsed, extraFields); }
private List<Object> explainInputs(List<RelNode> inputs) { final List<Object> list = jsonBuilder.list(); for (RelNode input : inputs) { String id = relIdMap.get(input); if (id == null) { input.explain(this); id = previousId; } list.add(id); } return list; }
/** * Returns the index of the first field in <code>rel</code> which comes from its <code>ordinal * </code>th input. * * <p>For example, if rel joins T0(A,B,C) to T1(D,E), then countFields(0,rel) yields 0, and * countFields(1,rel) yields 3. */ private int computeFieldOffset(RelNode rel, int ordinal) { if (ordinal == 0) { // short-circuit for the common case return 0; } int fieldOffset = 0; final List<RelNode> inputs = rel.getInputs(); for (int i = 0; i < ordinal; i++) { RelNode input = inputs.get(i); fieldOffset += input.getRowType().getFieldList().size(); } return fieldOffset; }
// implement RelOptRule public void onMatch(RelOptRuleCall call) { ProjectRel origProj = call.rel(0); JoinRel joinRel = call.rel(1); // locate all fields referenced in the projection and join condition; // determine which inputs are referenced in the projection and // join condition; if all fields are being referenced and there are no // special expressions, no point in proceeding any further PushProjector pushProject = new PushProjector(origProj, joinRel.getCondition(), joinRel, preserveExprCondition); if (pushProject.locateAllRefs()) { return; } // create left and right projections, projecting only those // fields referenced on each side RelNode leftProjRel = pushProject.createProjectRefsAndExprs(joinRel.getLeft(), true, false); RelNode rightProjRel = pushProject.createProjectRefsAndExprs(joinRel.getRight(), true, true); // convert the join condition to reference the projected columns RexNode newJoinFilter = null; int[] adjustments = pushProject.getAdjustments(); if (joinRel.getCondition() != null) { List<RelDataTypeField> projJoinFieldList = new ArrayList<RelDataTypeField>(); projJoinFieldList.addAll(joinRel.getSystemFieldList()); projJoinFieldList.addAll(leftProjRel.getRowType().getFieldList()); projJoinFieldList.addAll(rightProjRel.getRowType().getFieldList()); newJoinFilter = pushProject.convertRefsAndExprs(joinRel.getCondition(), projJoinFieldList, adjustments); } // create a new joinrel with the projected children JoinRel newJoinRel = new JoinRel( joinRel.getCluster(), leftProjRel, rightProjRel, newJoinFilter, joinRel.getJoinType(), Collections.<String>emptySet(), joinRel.isSemiJoinDone(), joinRel.getSystemFieldList()); // put the original project on top of the join, converting it to // reference the modified projection list ProjectRel topProject = pushProject.createNewProject(newJoinRel, adjustments); call.transformTo(topProject); }
/** * Generates generic XML (sometimes called 'element-oriented XML'). Like this: * * <blockquote> * * <code> * <RelNode id="1" type="Join"><br/> * <Property name="condition">EMP.DEPTNO = * DEPT.DEPTNO</Property><br/> * <Inputs><br/> * <RelNode id="2" type="Project"><br/> * <Property name="expr1">x + * y</Property><br/> * <Property * name="expr2">45</Property><br/> * </RelNode><br/> * <RelNode id="3" type="TableAccess"><br/> * <Property * name="table">SALES.EMP</Property><br/> * </RelNode><br/> * </Inputs><br/> * </RelNode><br/> * </code> * * </blockquote> * * @param rel Relational expression * @param terms Names of the attributes of the plan * @param values Values of the attributes of the plan */ private void explainGeneric(RelNode rel, String[] terms, Object[] values) { RelNode[] inputs = rel.getInputs(); RexNode[] children = rel.getChildExps(); assert terms.length == (inputs.length + children.length + values.length) : "terms.length=" + terms.length + " inputs.length=" + inputs.length + " children.length=" + children.length + " values.length=" + values.length; String relType = rel.getRelTypeName(); xmlOutput.beginBeginTag("RelNode"); xmlOutput.attribute("type", relType); // xmlOutput.attribute("id", rel.getId() + ""); xmlOutput.endBeginTag("RelNode"); int j = 0; for (int i = 0; i < children.length; i++) { RexNode child = children[i]; xmlOutput.beginBeginTag("Property"); xmlOutput.attribute("name", terms[inputs.length + j++]); xmlOutput.endBeginTag("Property"); xmlOutput.cdata(child.toString()); xmlOutput.endTag("Property"); } for (int i = 0; i < values.length; i++) { Object value = values[i]; if (value != null) { xmlOutput.beginBeginTag("Property"); xmlOutput.attribute("name", terms[inputs.length + j++]); xmlOutput.endBeginTag("Property"); xmlOutput.cdata(value.toString()); xmlOutput.endTag("Property"); } } xmlOutput.beginTag("Inputs", null); level++; for (int i = 0; i < inputs.length; i++) { RelNode child = inputs[i]; child.explain(this); } level--; xmlOutput.endTag("Inputs"); xmlOutput.endTag("RelNode"); }
public void onMatch(RelOptRuleCall call) { assert matches(call); final JoinRel join = (JoinRel) call.rels[0]; final List<Integer> leftKeys = new ArrayList<Integer>(); final List<Integer> rightKeys = new ArrayList<Integer>(); RelNode right = join.getRight(); final RelNode left = join.getLeft(); RexNode remainingCondition = RelOptUtil.splitJoinCondition(left, right, join.getCondition(), leftKeys, rightKeys); assert leftKeys.size() == rightKeys.size(); final List<CorrelatorRel.Correlation> correlationList = new ArrayList<CorrelatorRel.Correlation>(); if (leftKeys.size() > 0) { final RelOptCluster cluster = join.getCluster(); final RexBuilder rexBuilder = cluster.getRexBuilder(); int k = 0; RexNode condition = null; for (Integer leftKey : leftKeys) { Integer rightKey = rightKeys.get(k++); final String dyn_inIdStr = cluster.getQuery().createCorrel(); final int dyn_inId = RelOptQuery.getCorrelOrdinal(dyn_inIdStr); // Create correlation to say 'each row, set variable #id // to the value of column #leftKey'. correlationList.add(new CorrelatorRel.Correlation(dyn_inId, leftKey)); condition = RelOptUtil.andJoinFilters( rexBuilder, condition, rexBuilder.makeCall( SqlStdOperatorTable.equalsOperator, rexBuilder.makeInputRef( right.getRowType().getFieldList().get(rightKey).getType(), rightKey), rexBuilder.makeCorrel( left.getRowType().getFieldList().get(leftKey).getType(), dyn_inIdStr))); } right = CalcRel.createFilter(right, condition); } RelNode newRel = new CorrelatorRel( join.getCluster(), left, right, remainingCondition, correlationList, join.getJoinType()); call.transformTo(newRel); }
@Override public ParseTree visitChildInternal(RelNode child, int ordinal) { final Convention convention = child.getConvention(); if (!(child instanceof JavaRel)) { throw Util.newInternal( "Relational expression '" + child + "' has '" + convention + "' calling convention, so must implement interface " + JavaRel.class); } JavaRel javaRel = (JavaRel) child; final ParseTree p = javaRel.implement(this); if ((convention == CallingConvention.JAVA) && (p != null)) { throw Util.newInternal( "Relational expression '" + child + "' returned '" + p + " on implement, but should have " + "returned null, because it has JAVA calling-convention. " + "(Note that similar calling-conventions, such as " + "Iterator, must return a value.)"); } return p; }
private void bindDeferred(JavaFrame frame, final RelNode rel) { final StatementList statementList = getStatementList(); if (frame.bind == null) { // this relational expression has not bound itself, so we presume // that we can call its implementSelf() method if (!(rel instanceof JavaSelfRel)) { throw Util.newInternal( "In order to bind-deferred, a " + "relational expression must implement JavaSelfRel: " + rel); } final JavaSelfRel selfRel = (JavaSelfRel) rel; LazyBind lazyBind = new LazyBind( newVariable(), statementList, getTypeFactory(), rel.getRowType(), new VariableInitializerThunk() { public VariableInitializer getInitializer() { return selfRel.implementSelf(JavaRelImplementor.this); } }); bind(rel, lazyBind); } else if ((frame.bind instanceof LazyBind) && (((LazyBind) frame.bind).statementList != statementList)) { // Frame is already bound, but to a variable declared in a different // scope. Re-bind it. final LazyBind lazyBind = (LazyBind) frame.bind; lazyBind.statementList = statementList; lazyBind.bound = false; } }
private JavaFrame findFrame() { RelNode previous = rel; while (true) { JavaFrame frame = (JavaFrame) mapRel2Frame.get(previous); if (frame.bind != null) { tracer.log( Level.FINE, "Bind " + rel.toString() + " to " + previous.toString() + "(" + frame.bind + ")"); return frame; } // go deeper List<RelNode> inputs = previous.getInputs(); assert (inputs.size() == 1) : "input is not bound"; previous = inputs.get(0); } }
// Ditto for getNonCumulativeCost public RelOptCost getCumulativeCost(RelNode rel) { RelOptCost cost = RelMetadataQuery.getNonCumulativeCost(rel); List<RelNode> inputs = rel.getInputs(); for (RelNode input : inputs) { cost = cost.plus(RelMetadataQuery.getCumulativeCost(input)); } return cost; }
/** Creates a result based on a single relational expression. */ public Result result(SqlNode node, Collection<Clause> clauses, RelNode rel) { final String alias2 = SqlValidatorUtil.getAlias(node, -1); final String alias3 = alias2 != null ? alias2 : "t"; final String alias4 = SqlValidatorUtil.uniquify(alias3, aliasSet, SqlValidatorUtil.EXPR_SUGGESTER); final String alias5 = alias2 == null || !alias2.equals(alias4) ? alias4 : null; return new Result( node, clauses, alias5, Collections.singletonList(Pair.of(alias4, rel.getRowType()))); }
/** * Trims unused fields from a relational expression. * * <p>We presume that all fields of the relational expression are wanted by its consumer, so only * trim fields that are not used within the tree. * * @param root Root node of relational expression * @return Trimmed relational expression */ public RelNode trim(RelNode root) { final int fieldCount = root.getRowType().getFieldCount(); final BitSet fieldsUsed = Util.bitSetBetween(0, fieldCount); final Set<RelDataTypeField> extraFields = Collections.emptySet(); final TrimResult trimResult = dispatchTrimFields(root, fieldsUsed, extraFields); if (!trimResult.right.isIdentity()) { throw new IllegalArgumentException(); } return trimResult.left; }
/** * Creates a relational expression which filters according to a given condition, returning the * same fields as its input. * * @param child Child relational expression * @param condition Condition * @return Relational expression */ public static RelNode createFilter(RelNode child, RexNode condition) { if (DeprecateProjectAndFilter) { final RelOptCluster cluster = child.getCluster(); RexProgramBuilder builder = new RexProgramBuilder(child.getRowType(), cluster.getRexBuilder()); builder.addIdentity(); builder.addCondition(condition); final RexProgram program = builder.getProgram(); return new CalcRel( cluster, child.getTraitSet(), child, program.getOutputRowType(), program, Collections.<RelCollation>emptyList()); } else { return new FilterRel(child.getCluster(), child, condition); } }
/** * Records the fact that instances of <code>rel</code> are available via <code>bind</code> (which * may be eager or lazy). */ private void bind(RelNode rel, Bind bind) { tracer.log(Level.FINE, "Bind " + rel.toString() + " to " + bind); JavaFrame frame = (JavaFrame) mapRel2Frame.get(rel); frame.bind = bind; boolean stupid = SaffronProperties.instance().stupid.get(); if (stupid) { // trigger the declaration of the variable, even though it // may not be used Util.discard(bind.getVariable()); } }
public RelNode convert( RelOptPlanner planner, RelNode rel, RelDistribution toDistribution, boolean allowInfiniteCostConverters) { if (toDistribution == RelDistributions.ANY) { return rel; } // Create a logical sort, then ask the planner to convert its remaining // traits (e.g. convert it to an EnumerableSortRel if rel is enumerable // convention) final Exchange exchange = LogicalExchange.create(rel, toDistribution); RelNode newRel = planner.register(exchange, rel); final RelTraitSet newTraitSet = rel.getTraitSet().replace(toDistribution); if (!newRel.getTraitSet().equals(newTraitSet)) { newRel = planner.changeTraits(newRel, newTraitSet); } return newRel; }
/** Creates a RelOptMaterialization. */ public RelOptMaterialization(RelNode tableRel, RelNode queryRel, RelOptTable starRelOptTable) { this.tableRel = tableRel; this.starRelOptTable = starRelOptTable; if (starRelOptTable == null) { this.starTable = null; } else { this.starTable = starRelOptTable.unwrap(StarTable.class); assert starTable != null; } this.table = tableRel.getTable(); this.queryRel = queryRel; }
/** * Generates specific XML (sometimes called 'attribute-oriented XML'). Like this: * * <pre> * <Join condition="EMP.DEPTNO = DEPT.DEPTNO"> * <Project expr1="x + y" expr2="42"> * <TableAccess table="SALES.EMPS"> * </Join> * </pre> * * @param rel Relational expression * @param terms Names of the attributes of the plan * @param values Values of the attributes of the plan */ private void explainSpecific(RelNode rel, String[] terms, Object[] values) { RelNode[] inputs = rel.getInputs(); RexNode[] children = rel.getChildExps(); assert terms.length == (inputs.length + children.length + values.length) : "terms.length=" + terms.length + " inputs.length=" + inputs.length + " children.length=" + children.length + " values.length=" + values.length; String tagName = rel.getRelTypeName(); xmlOutput.beginBeginTag(tagName); xmlOutput.attribute("id", rel.getId() + ""); int j = 0; for (int i = 0; i < children.length; i++) { RexNode child = children[i]; xmlOutput.attribute(terms[inputs.length + j++], child.toString()); } for (int i = 0; i < values.length; i++) { Object value = values[i]; if (value != null) { xmlOutput.attribute(terms[inputs.length + j++], value.toString()); } } xmlOutput.endBeginTag(tagName); level++; for (int i = 0; i < inputs.length; i++) { RelNode child = inputs[i]; child.explain(this); } level--; }
/** * Invokes {@link #trimFields}, or the appropriate method for the type of the rel parameter, using * multi-method dispatch. * * @param rel Relational expression * @param fieldsUsed Bitmap of fields needed by the consumer * @return New relational expression and its field mapping */ protected final TrimResult dispatchTrimFields( RelNode rel, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final TrimResult trimResult = trimFieldsDispatcher.invoke(rel, fieldsUsed, extraFields); final RelNode newRel = trimResult.left; final Mapping mapping = trimResult.right; final int fieldCount = rel.getRowType().getFieldCount(); assert mapping.getSourceCount() == fieldCount : "source: " + mapping.getSourceCount() + " != " + fieldCount; final int newFieldCount = newRel.getRowType().getFieldCount(); assert mapping.getTargetCount() + extraFields.size() == newFieldCount : "target: " + mapping.getTargetCount() + " + " + extraFields.size() + " != " + newFieldCount; if (Bug.TodoFixed) assert newFieldCount > 0 : "rel has no fields after trim: " + rel; if (newRel.equals(rel)) { return new TrimResult(rel, mapping); } return trimResult; }
/** * Returns the variable which, in the generated program, will hold the current row of a given * relational expression. This method is only applicable if the relational expression is the * current one or an input; if it is an ancestor, there is no current value, and this method * returns null. */ public Variable findInputVariable(RelNode rel) { while (true) { JavaFrame frame = (JavaFrame) mapRel2Frame.get(rel); if ((frame != null) && frame.hasVariable()) { return frame.getVariable(); } List<RelNode> inputs = rel.getInputs(); if (inputs.size() == 1) { rel = inputs.get(0); } else { return null; } } }
/** * Trims a child relational expression, then adds back a dummy project to restore the fields that * were removed. * * <p>Sounds pointless? It causes unused fields to be removed further down the tree (towards the * leaves), but it ensure that the consuming relational expression continues to see the same * fields. * * @param rel Relational expression * @param input Input relational expression, whose fields to trim * @param fieldsUsed Bitmap of fields needed by the consumer * @return New relational expression and its field mapping */ protected TrimResult trimChildRestore( RelNode rel, RelNode input, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { TrimResult trimResult = trimChild(rel, input, fieldsUsed, extraFields); if (trimResult.right.isIdentity()) { return trimResult; } final RelDataType rowType = input.getRowType(); List<RelDataTypeField> fieldList = rowType.getFieldList(); final List<RexNode> exprList = new ArrayList<RexNode>(); final List<String> nameList = rowType.getFieldNames(); RexBuilder rexBuilder = rel.getCluster().getRexBuilder(); assert trimResult.right.getSourceCount() == fieldList.size(); for (int i = 0; i < fieldList.size(); i++) { int source = trimResult.right.getTargetOpt(i); RelDataTypeField field = fieldList.get(i); exprList.add( source < 0 ? rexBuilder.makeZeroLiteral(field.getType()) : rexBuilder.makeInputRef(field.getType(), source)); } RelNode project = CalcRel.createProject(trimResult.left, exprList, nameList); return new TrimResult(project, Mappings.createIdentity(fieldList.size())); }
/** * Creates new RelNodes replacing/removing the original project/row scan * * @param projectedScan new scan that is now projected * @param origProject original projection * @param needRename true if fields from the row scan need to be renamed * @param newProject projection that contains the new projection expressions, in the case where * the original projection cannot be removed because it projects expressions * @return new RelNode */ public RelNode createNewRelNode( RelNode projectedScan, ProjectRel origProject, boolean needRename, ProjectRel newProject) { RelNode scanRel; if (needRename) { // Replace calling convention with FENNEL_EXEC_CONVENTION RelTraitSet traits = RelOptUtil.clone(origProject.getTraits()); traits.setTrait(CallingConventionTraitDef.instance, FennelRel.FENNEL_EXEC_CONVENTION); if (!traits.equals(projectedScan.getTraits())) { RelNode mergedProjectedScan = convert(projectedScan, traits); RelOptPlanner planner = projectedScan.getCluster().getPlanner(); // register projectedScan == mergedProjectedScan // so mergedProjectedScan will have a set later on projectedScan = planner.ensureRegistered(mergedProjectedScan, projectedScan); } scanRel = new FennelRenameRel( origProject.getCluster(), projectedScan, RelOptUtil.getFieldNames(origProject.getRowType()), traits); } else { scanRel = projectedScan; } if (newProject == null) { return scanRel; } else { // in the case where the projection had expressions, put the // new, modified projection on top of the projected row scan return (ProjectRel) CalcRel.createProject( scanRel, newProject.getProjectExps(), RelOptUtil.getFieldNames(newProject.getRowType())); } }
/** * Returns a relational expression which has the same fields as the underlying expression, but the * fields have different names. * * @param rel Relational expression * @param fieldNames Field names * @return Renamed relational expression */ public static RelNode createRename(RelNode rel, List<String> fieldNames) { final List<RelDataTypeField> fields = rel.getRowType().getFieldList(); assert fieldNames.size() == fields.size(); final List<Pair<RexNode, String>> refs = new AbstractList<Pair<RexNode, String>>() { public int size() { return fields.size(); } public Pair<RexNode, String> get(int index) { return RexInputRef.of2(index, fields); } }; return createProject(rel, refs, true); }