// implement RelOptRule public void onMatch(RelOptRuleCall call) { CalcRel calcRel = (CalcRel) call.rels[0]; RexProgram program = calcRel.getProgram(); // check the projection List<Integer> projOrdinals = new ArrayList<Integer>(); RelDataType outputRowType = isProjectSimple(calcRel, projOrdinals); if (outputRowType == null) { return; } RexLocalRef condition = program.getCondition(); CompOperatorEnum compOp = CompOperatorEnum.COMP_NOOP; Integer[] filterOrdinals = {}; List<RexLiteral> filterLiterals = new ArrayList<RexLiteral>(); // check the condition if (condition != null) { RexNode filterExprs = program.expandLocalRef(condition); List<Integer> filterList = new ArrayList<Integer>(); List<CompOperatorEnum> op = new ArrayList<CompOperatorEnum>(); if (!isConditionSimple(calcRel, filterExprs, filterList, filterLiterals, op)) { return; } compOp = op.get(0); filterOrdinals = filterList.toArray(new Integer[filterList.size()]); } RelNode fennelInput = mergeTraitsAndConvert( calcRel.getTraits(), FennelRel.FENNEL_EXEC_CONVENTION, calcRel.getChild()); if (fennelInput == null) { return; } Integer[] projection = projOrdinals.toArray(new Integer[projOrdinals.size()]); FennelReshapeRel reshapeRel = new FennelReshapeRel( calcRel.getCluster(), fennelInput, projection, outputRowType, compOp, filterOrdinals, filterLiterals, new FennelRelParamId[] {}, new Integer[] {}, null); call.transformTo(reshapeRel); }
/** * Creates a relational expression which permutes the output fields of a relational expression * according to a permutation. * * <p>Optimizations: * * <ul> * <li>If the relational expression is a {@link CalcRel} or {@link ProjectRel} which is already * acting as a permutation, combines the new permutation with the old; * <li>If the permutation is the identity, returns the original relational expression. * </ul> * * <p>If a permutation is combined with its inverse, these optimizations would combine to remove * them both. * * @param rel Relational expression * @param permutation Permutation to apply to fields * @param fieldNames Field names; if null, or if a particular entry is null, the name of the * permuted field is used * @return relational expression which permutes its input fields */ public static RelNode permute(RelNode rel, Permutation permutation, List<String> fieldNames) { if (permutation.isIdentity()) { return rel; } if (rel instanceof CalcRel) { CalcRel calcRel = (CalcRel) rel; Permutation permutation1 = calcRel.getProgram().getPermutation(); if (permutation1 != null) { Permutation permutation2 = permutation.product(permutation1); return permute(rel, permutation2, null); } } if (rel instanceof ProjectRel) { Permutation permutation1 = ((ProjectRel) rel).getPermutation(); if (permutation1 != null) { Permutation permutation2 = permutation.product(permutation1); return permute(rel, permutation2, null); } } final List<RelDataType> outputTypeList = new ArrayList<RelDataType>(); final List<String> outputNameList = new ArrayList<String>(); final List<RexNode> exprList = new ArrayList<RexNode>(); final List<RexLocalRef> projectRefList = new ArrayList<RexLocalRef>(); final List<RelDataTypeField> fields = rel.getRowType().getFieldList(); for (int i = 0; i < permutation.getTargetCount(); i++) { int target = permutation.getTarget(i); final RelDataTypeField targetField = fields.get(target); outputTypeList.add(targetField.getType()); outputNameList.add( ((fieldNames == null) || (fieldNames.size() <= i) || (fieldNames.get(i) == null)) ? targetField.getName() : fieldNames.get(i)); exprList.add(rel.getCluster().getRexBuilder().makeInputRef(fields.get(i).getType(), i)); final int source = permutation.getSource(i); projectRefList.add(new RexLocalRef(source, fields.get(source).getType())); } final RexProgram program = new RexProgram( rel.getRowType(), exprList, projectRefList, null, rel.getCluster().getTypeFactory().createStructType(outputTypeList, outputNameList)); return new CalcRel( rel.getCluster(), rel.getTraitSet(), rel, program.getOutputRowType(), program, Collections.<RelCollation>emptyList()); }
/** * Determines if a filter condition is a simple one and returns the parameters corresponding to * the simple filters. * * @param calcRel original CalcRel * @param filterExprs filter expression being analyzed * @param filterList returns the list of filter ordinals in the simple expression * @param literals returns the list of literals to be used in the simple comparisons * @param op returns the operator to be used in the simple comparison * @return true if the filter condition is simple */ private boolean isConditionSimple( CalcRel calcRel, RexNode filterExprs, List<Integer> filterList, List<RexLiteral> literals, List<CompOperatorEnum> op) { SargFactory sargFactory = new SargFactory(calcRel.getCluster().getRexBuilder()); SargRexAnalyzer rexAnalyzer = sargFactory.newRexAnalyzer(true); List<SargBinding> sargBindingList = rexAnalyzer.analyzeAll(filterExprs); // Currently, it's all or nothing. So, if there are filters rejected // by the analyzer, we can't process a subset using the reshape // exec stream if (rexAnalyzer.getNonSargFilterRexNode() != null) { return false; } List<RexInputRef> filterCols = new ArrayList<RexInputRef>(); List<RexNode> filterOperands = new ArrayList<RexNode>(); if (FennelRelUtil.extractSimplePredicates(sargBindingList, filterCols, filterOperands, op)) { for (RexInputRef filterCol : filterCols) { filterList.add(filterCol.getIndex()); } for (RexNode operand : filterOperands) { literals.add((RexLiteral) operand); } return true; } else { return false; } }
/** * Determines if a projection is simple. * * @param calcRel CalcRel containing the projection * @param projOrdinals if the projection is simple, returns the ordinals of the projection inputs * @return rowtype corresponding to the projection, provided it is simple; otherwise null is * returned */ private RelDataType isProjectSimple(CalcRel calcRel, List<Integer> projOrdinals) { // Loop through projection expressions. If we find a non-simple // projection expression, simply return. RexProgram program = calcRel.getProgram(); List<RexLocalRef> projList = program.getProjectList(); int nProjExprs = projList.size(); RelDataType[] types = new RelDataType[nProjExprs]; String[] fieldNames = new String[nProjExprs]; RelDataTypeField[] projFields = calcRel.getRowType().getFields(); for (int i = 0; i < nProjExprs; i++) { RexNode projExpr = program.expandLocalRef(projList.get(i)); if (projExpr instanceof RexInputRef) { projOrdinals.add(((RexInputRef) projExpr).getIndex()); types[i] = projExpr.getType(); fieldNames[i] = projFields[i].getName(); continue; } else if (!(projExpr instanceof RexCall)) { return null; } RexCall rexCall = (RexCall) projExpr; if (rexCall.getOperator() != SqlStdOperatorTable.castFunc) { return null; } RexNode castOperand = rexCall.getOperands()[0]; if (!(castOperand instanceof RexInputRef)) { return null; } RelDataType castType = projExpr.getType(); RelDataType origType = castOperand.getType(); if (isCastSimple(origType, castType)) { projOrdinals.add(((RexInputRef) castOperand).getIndex()); types[i] = castType; fieldNames[i] = projFields[i].getName(); } else { return null; } } // return the rowtype corresponding to the output of the projection return calcRel.getCluster().getTypeFactory().createStructType(types, fieldNames); }
/** * Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link SetOpRel} (including UNION and * UNION ALL). */ public TrimResult trimFields( SetOpRel setOp, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final RelDataType rowType = setOp.getRowType(); final int fieldCount = rowType.getFieldCount(); int changeCount = 0; // Fennel abhors an empty row type, so pretend that the parent rel // wants the last field. (The last field is the least likely to be a // system field.) if (fieldsUsed.isEmpty()) { fieldsUsed.set(rowType.getFieldCount() - 1); } // Compute the desired field mapping. Give the consumer the fields they // want, in the order that they appear in the bitset. final Mapping mapping = createMapping(fieldsUsed, fieldCount); // Create input with trimmed columns. final List<RelNode> newInputs = new ArrayList<RelNode>(); for (RelNode input : setOp.getInputs()) { TrimResult trimResult = trimChild(setOp, input, fieldsUsed, extraFields); RelNode newInput = trimResult.left; final Mapping inputMapping = trimResult.right; // We want "mapping", the input gave us "inputMapping", compute // "remaining" mapping. // | | | // |---------------- mapping ---------->| // |-- inputMapping -->| | // | |-- remaining -->| // // For instance, suppose we have columns [a, b, c, d], // the consumer asked for mapping = [b, d], // and the transformed input has columns inputMapping = [d, a, b]. // remaining will permute [b, d] to [d, a, b]. Mapping remaining = Mappings.divide(mapping, inputMapping); // Create a projection; does nothing if remaining is identity. newInput = CalcRel.projectMapping(newInput, remaining, null); if (input != newInput) { ++changeCount; } newInputs.add(newInput); } // If the input is unchanged, and we need to project all columns, // there's to do. if (changeCount == 0 && mapping.isIdentity()) { return new TrimResult(setOp, mapping); } RelNode newSetOp = setOp.copy(setOp.getTraitSet(), newInputs); return new TrimResult(newSetOp, mapping); }
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); }
public static RelNode createProject(final RelNode child, final List<Integer> posList) { return CalcRel.createProject( child, new AbstractList<RexNode>() { public int size() { return posList.size(); } public RexNode get(int index) { final int pos = posList.get(index); return child .getCluster() .getRexBuilder() .makeInputRef(child.getRowType().getFieldList().get(pos).getType(), pos); } }, null); }
public void onMatch(RelOptRuleCall call) { FilterRel filter = (FilterRel) call.rels[0]; List<RexNode> expList = new ArrayList<RexNode>(Arrays.asList(filter.getChildExps())); RexNode newConditionExp; boolean reduced; if (reduceExpressions(filter, expList)) { assert (expList.size() == 1); newConditionExp = expList.get(0); reduced = true; } else { // No reduction, but let's still test the original // predicate to see if it was already a constant, // in which case we don't need any runtime decision // about filtering. newConditionExp = filter.getChildExps()[0]; reduced = false; } if (newConditionExp.isAlwaysTrue()) { call.transformTo(filter.getChild()); } else if ((newConditionExp instanceof RexLiteral) || RexUtil.isNullLiteral(newConditionExp, true)) { call.transformTo(new EmptyRel(filter.getCluster(), filter.getRowType())); } else if (reduced) { call.transformTo(CalcRel.createFilter(filter.getChild(), expList.get(0))); } else { if (newConditionExp instanceof RexCall) { RexCall rexCall = (RexCall) newConditionExp; boolean reverse = (rexCall.getOperator() == SqlStdOperatorTable.notOperator); if (reverse) { rexCall = (RexCall) rexCall.getOperands()[0]; } reduceNotNullableFilter(call, filter, rexCall, reverse); } return; } // New plan is absolutely better than old plan. call.getPlanner().setImportance(filter, 0.0); }
/** * 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())); } }
protected void executeImpl() throws Exception { RelNode oneRowRel = new OneRowRel(getPreparingStmt().getRelOptCluster()); RelNode projectRel = CalcRel.createProject(oneRowRel, exprs, null); executePlan(projectRel, exprs, false, true); }
public void onMatch(RelOptRuleCall call) { CalcRel calc = (CalcRel) call.getRels()[0]; RexProgram program = calc.getProgram(); final List<RexNode> exprList = program.getExprList(); // Form a list of expressions with sub-expressions fully // expanded. final List<RexNode> expandedExprList = new ArrayList<RexNode>(exprList.size()); final RexShuttle shuttle = new RexShuttle() { public RexNode visitLocalRef(RexLocalRef localRef) { return expandedExprList.get(localRef.getIndex()); } }; for (RexNode expr : exprList) { expandedExprList.add(expr.accept(shuttle)); } if (reduceExpressions(calc, expandedExprList)) { final RexProgramBuilder builder = new RexProgramBuilder( calc.getChild().getRowType(), calc.getCluster().getRexBuilder()); List<RexLocalRef> list = new ArrayList<RexLocalRef>(); for (RexNode expr : expandedExprList) { list.add(builder.registerInput(expr)); } if (program.getCondition() != null) { final int conditionIndex = program.getCondition().getIndex(); final RexNode newConditionExp = expandedExprList.get(conditionIndex); if (newConditionExp.isAlwaysTrue()) { // condition is always TRUE - drop it } else if ((newConditionExp instanceof RexLiteral) || RexUtil.isNullLiteral(newConditionExp, true)) { // condition is always NULL or FALSE - replace calc // with empty call.transformTo(new EmptyRel(calc.getCluster(), calc.getRowType())); return; } else { builder.addCondition(list.get(conditionIndex)); } } int k = 0; for (RexLocalRef projectExpr : program.getProjectList()) { final int index = projectExpr.getIndex(); builder.addProject( list.get(index).getIndex(), program.getOutputRowType().getFieldList().get(k++).getName()); } call.transformTo( new CalcRel( calc.getCluster(), calc.getTraits(), calc.getChild(), calc.getRowType(), builder.getProgram(), calc.getCollationList())); // New plan is absolutely better than old plan. call.getPlanner().setImportance(calc, 0.0); } }