/** * 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; }
/** * 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--; }
/** * 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; }
// 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; }
/** * 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; }
/** * 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; } } }
// Catch-all rule when none of the others apply. public Double getPercentageOriginalRows(RelNode rel) { if (rel.getInputs().size() > 1) { // No generic formula available for multiple inputs. return null; } if (rel.getInputs().size() == 0) { // Assume no filtering happening at leaf. return 1.0; } RelNode child = rel.getInputs().get(0); Double childPercentage = RelMetadataQuery.getPercentageOriginalRows(child); if (childPercentage == null) { return null; } // Compute product of percentage filtering from this rel (assuming any // filtering is the effect of single-table filters) with the percentage // filtering performed by the child. Double relPercentage = quotientForPercentage( RelMetadataQuery.getRowCount(rel), RelMetadataQuery.getRowCount(child)); if (relPercentage == null) { return null; } double percent = relPercentage * childPercentage; // this check is needed in cases where this method is called on a // physical rel if ((percent < 0.0) || (percent > 1.0)) { return null; } return relPercentage * childPercentage; }
/** * 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"); }
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); } }
protected void explain_(RelNode rel, List<Pair<String, Object>> values) { final Map<String, Object> map = jsonBuilder.map(); map.put("id", null); // ensure that id is the first attribute map.put("relOp", relJson.classToTypeName(rel.getClass())); for (Pair<String, Object> value : values) { if (value.right instanceof RelNode) { continue; } put(map, value.left, value.right); } // omit 'inputs: ["3"]' if "3" is the preceding rel final List<Object> list = explainInputs(rel.getInputs()); if (list.size() != 1 || !list.get(0).equals(previousId)) { map.put("inputs", list); } final String id = Integer.toString(relIdMap.size()); relIdMap.put(rel, id); map.put("id", id); relList.add(map); previousId = id; }