/** * 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); }
/** * 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); } }
/** * Adds a child to this expression. * * @param child child to add */ public void addChild(SargExpr child) { assert (child.getDataType() == dataType); if (setOp == SargSetOperator.COMPLEMENT) { assert (children.isEmpty()); } children.add(child); }
private void analyzeCall(RexCall call, Constancy callConstancy) { parentCallTypeStack.add(call.getOperator()); // visit operands, pushing their states onto stack super.visitCall(call); // look for NON_CONSTANT operands int nOperands = call.getOperands().length; List<Constancy> operandStack = stack.subList(stack.size() - nOperands, stack.size()); for (Constancy operandConstancy : operandStack) { if (operandConstancy == Constancy.NON_CONSTANT) { callConstancy = Constancy.NON_CONSTANT; } } // Even if all operands are constant, the call itself may // be non-deterministic. if (!call.getOperator().isDeterministic()) { callConstancy = Constancy.NON_CONSTANT; } else if (call.getOperator().isDynamicFunction()) { // We can reduce the call to a constant, but we can't // cache the plan if the function is dynamic preparingStmt.disableStatementCaching(); } // Row operator itself can't be reduced to a literal, but if // the operands are constants, we still want to reduce those if ((callConstancy == Constancy.REDUCIBLE_CONSTANT) && (call.getOperator() instanceof SqlRowOperator)) { callConstancy = Constancy.NON_CONSTANT; } if (callConstancy == Constancy.NON_CONSTANT) { // any REDUCIBLE_CONSTANT children are now known to be maximal // reducible subtrees, so they can be added to the result // list for (int iOperand = 0; iOperand < nOperands; ++iOperand) { Constancy constancy = operandStack.get(iOperand); if (constancy == Constancy.REDUCIBLE_CONSTANT) { addResult(call.getOperands()[iOperand]); } } // if this cast expression can't be reduced to a literal, // then see if we can remove the cast if (call.getOperator() == SqlStdOperatorTable.castFunc) { reduceCasts(call); } } // pop operands off of the stack operandStack.clear(); // pop this parent call operator off the stack parentCallTypeStack.remove(parentCallTypeStack.size() - 1); // push constancy result for this call onto stack stack.add(callConstancy); }
/** * Applies a visitor to a list of expressions and, if specified, a single expression. * * @param visitor Visitor * @param exprs List of expressions * @param expr Single expression, may be null */ public static void apply(RexVisitor<Void> visitor, List<? extends RexNode> exprs, RexNode expr) { for (int i = 0; i < exprs.size(); i++) { exprs.get(i).accept(visitor); } if (expr != null) { expr.accept(visitor); } }
/** * Applies a mapping to a list of field collations. * * @param mapping Mapping * @param fieldCollations Field collations * @return collations with mapping applied */ public static List<RelFieldCollation> apply( Mapping mapping, List<RelFieldCollation> fieldCollations) { final List<RelFieldCollation> newFieldCollations = new ArrayList<RelFieldCollation>(fieldCollations.size()); for (RelFieldCollation fieldCollation : fieldCollations) { newFieldCollations.add(apply(mapping, fieldCollation)); } return newFieldCollations; }
public static Mapping target(List<Integer> sources, int sourceCount) { final int targetCount = sources.size(); final PartialFunctionImpl mapping = new PartialFunctionImpl(sourceCount, targetCount, MappingType.FUNCTION); for (int target = 0; target < targetCount; target++) { int source = sources.get(target); mapping.set(source, target); } return mapping; }
public static Mapping source(List<Integer> targets, int targetCount) { final int sourceCount = targets.size(); final PartialFunctionImpl mapping = new PartialFunctionImpl(sourceCount, targetCount, MappingType.FUNCTION); for (int source = 0; source < sourceCount; source++) { int target = targets.get(source); mapping.set(source, target); } return mapping; }
private List<SargIntervalSequence> evaluateChildren(SargSetExpr setExpr) { List<SargIntervalSequence> list = new ArrayList<SargIntervalSequence>(); for (SargExpr child : setExpr.children) { SargIntervalSequence newSeq = child.evaluate(); list.add(newSeq); } return list; }
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); } }
/** * 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); }
/** * Creates an OR expression from a list of RexNodes * * @param rexList list of RexNodes * @return OR'd expression */ public static RexNode orRexNodeList(RexBuilder rexBuilder, List<RexNode> rexList) { if (rexList.isEmpty()) { return null; } RexNode orExpr = rexList.get(rexList.size() - 1); for (int i = rexList.size() - 2; i >= 0; i--) { orExpr = rexBuilder.makeCall(SqlStdOperatorTable.orOperator, rexList.get(i), orExpr); } return orExpr; }
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 whether a list of integers is the identity mapping [0, ..., n - 1]. */ public static boolean isIdentity(List<Integer> list, int count) { if (list.size() != count) { return false; } for (int i = 0; i < count; i++) { final Integer o = list.get(i); if (o == null || o != i) { return false; } } return true; }
/** * Creates an AND expression from a list of RexNodes * * @param rexList list of RexNodes * @return AND'd expression */ public static RexNode andRexNodeList(RexBuilder rexBuilder, List<RexNode> rexList) { if (rexList.isEmpty()) { return null; } // create a right-deep tree to allow short-circuiting during // expression evaluation RexNode andExpr = rexList.get(rexList.size() - 1); for (int i = rexList.size() - 2; i >= 0; i--) { andExpr = rexBuilder.makeCall(SqlStdOperatorTable.andOperator, rexList.get(i), andExpr); } return andExpr; }
/** * Derives the list of column names suitable for NATURAL JOIN. These are the columns that occur * exactly once on each side of the join. * * @param leftRowType Row type of left input to the join * @param rightRowType Row type of right input to the join * @return List of columns that occur once on each side */ public static List<String> deriveNaturalJoinColumnList( RelDataType leftRowType, RelDataType rightRowType) { List<String> naturalColumnNames = new ArrayList<String>(); final List<String> leftNames = leftRowType.getFieldNames(); final List<String> rightNames = rightRowType.getFieldNames(); for (String name : leftNames) { if ((Collections.frequency(leftNames, name) == 1) && (Collections.frequency(rightNames, name) == 1)) { naturalColumnNames.add(name); } } return naturalColumnNames; }
/** * Locates expressions that can be reduced to literals or converted to expressions with redundant * casts removed. * * @param preparingStmt the statement containing the expressions * @param exps list of candidate expressions to be examined for reduction * @param constExps returns the list of expressions that can be constant reduced * @param addCasts indicator for each expression that can be constant reduced, whether a cast of * the resulting reduced expression is potentially necessary * @param removableCasts returns the list of cast expressions where the cast can be removed */ private static void findReducibleExps( FarragoSessionPreparingStmt preparingStmt, List<RexNode> exps, List<RexNode> constExps, List<Boolean> addCasts, List<RexNode> removableCasts) { ReducibleExprLocator gardener = new ReducibleExprLocator(preparingStmt, constExps, addCasts, removableCasts); for (RexNode exp : exps) { gardener.analyze(exp); } assert (constExps.size() == addCasts.size()); }
/** * Resolves a multi-part identifier such as "SCHEMA.EMP.EMPNO" to a namespace. The returned * namespace may represent a schema, table, column, etc. * * @pre names.size() > 0 * @post return != null */ public static SqlValidatorNamespace lookup(SqlValidatorScope scope, List<String> names) { Util.pre(names.size() > 0, "names.size() > 0"); SqlValidatorNamespace namespace = null; for (int i = 0; i < names.size(); i++) { String name = names.get(i); if (i == 0) { namespace = scope.resolve(name, null, null); } else { namespace = namespace.lookupChild(name); } } Util.permAssert(namespace != null, "post: namespace != null"); return namespace; }
public static RelDataType createTypeFromProjection( RelDataType type, List<String> columnNameList, RelDataTypeFactory typeFactory, boolean caseSensitive) { // If the names in columnNameList and type have case-sensitive differences, // the resulting type will use those from type. These are presumably more // canonical. final List<RelDataTypeField> fields = new ArrayList<RelDataTypeField>(columnNameList.size()); for (String name : columnNameList) { RelDataTypeField field = type.getField(name, caseSensitive); fields.add(type.getFieldList().get(field.getIndex())); } return typeFactory.createStructType(fields); }
public static void getSchemaObjectMonikers( SqlValidatorCatalogReader catalogReader, List<String> names, List<SqlMoniker> hints) { // Assume that the last name is 'dummy' or similar. List<String> subNames = Util.skipLast(names); hints.addAll(catalogReader.getAllSchemaObjectNames(subNames)); // If the name has length 0, try prepending the name of the default // schema. So, the empty name would yield a list of tables in the // default schema, as well as a list of schemas from the above code. if (subNames.size() == 0) { hints.addAll( catalogReader.getAllSchemaObjectNames( Collections.singletonList(catalogReader.getSchemaName()))); } }
public void analyze(RexNode exp) { assert (stack.isEmpty()); exp.accept(this); // Deal with top of stack assert (stack.size() == 1); assert (parentCallTypeStack.isEmpty()); Constancy rootConstancy = stack.get(0); if (rootConstancy == Constancy.REDUCIBLE_CONSTANT) { // The entire subtree was constant, so add it to the result. addResult(exp); } stack.clear(); }
/** * 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); }
private SargIntervalSequence evaluateIntersection(List<SargIntervalSequence> list) { SargIntervalSequence seq = null; if (list.isEmpty()) { // Counterintuitive but true: intersection of no sets is the // universal set (kinda like 2^0=1). One way to prove this to // yourself is to apply DeMorgan's law. The union of no sets is // certainly the empty set. So the complement of that union is the // universal set. That's equivalent to the intersection of the // complements of no sets, which is the intersection of no sets. // QED. seq = new SargIntervalSequence(); seq.addInterval(new SargInterval(factory, getDataType())); return seq; } // The way we evaluate the intersection is to start with the first // entry as a baseline, and then keep deleting stuff from it by // intersecting the other entrie in turn. Whatever makes it through // this filtering remains as the final result. for (SargIntervalSequence newSeq : list) { if (seq == null) { // first child seq = newSeq; continue; } intersectSequences(seq, newSeq); } return seq; }
public RelOptTable getTableForMember(List<String> names) { final SqlValidatorTable table = catalogReader.getTable(names); final RelDataType rowType = table.getRowType(); final List<RelCollation> collationList = deduceMonotonicity(table); if (names.size() < 3) { String[] newNames2 = {"CATALOG", "SALES", ""}; List<String> newNames = new ArrayList<String>(); int i = 0; while (newNames.size() < newNames2.length) { newNames.add(i, newNames2[i]); ++i; } names = newNames; } return createColumnSet(table, names, rowType, collationList); }
public RexNode visitCall(RexCall call) { List<RexNode> normalizedOperands = new ArrayList<RexNode>(); int diffCount = 0; for (RexNode operand : call.getOperands()) { operand.accept(this); final RexNode normalizedOperand = lookup(operand); normalizedOperands.add(normalizedOperand); if (normalizedOperand != operand) { ++diffCount; } } if (diffCount > 0) { call = call.clone(call.getType(), normalizedOperands); } return register(call); }
/** * Applies a mapping to a list. * * @param mapping Mapping * @param list List * @param <T> Element type * @return List with elements permuted according to mapping */ public static <T> List<T> apply(final Mapping mapping, final List<T> list) { if (mapping.getSourceCount() != list.size()) { // REVIEW: too strict? throw new IllegalArgumentException( "mapping source count " + mapping.getSourceCount() + " does not match list size " + list.size()); } final int targetCount = mapping.getTargetCount(); final List<T> list2 = new ArrayList<T>(targetCount); for (int target = 0; target < targetCount; ++target) { final int source = mapping.getSource(target); list2.add(list.get(source)); } return list2; }
public void onMatch(RelOptRuleCall call) { ProjectRel project = (ProjectRel) call.rels[0]; List<RexNode> expList = new ArrayList<RexNode>(Arrays.asList(project.getChildExps())); if (reduceExpressions(project, expList)) { call.transformTo( new ProjectRel( project.getCluster(), project.getChild(), expList.toArray(new RexNode[expList.size()]), project.getRowType(), ProjectRel.Flags.Boxed, Collections.<RelCollation>emptyList())); // New plan is absolutely better than old plan. call.getPlanner().setImportance(project, 0.0); } }
public void onMatch(RelOptRuleCall call) { JoinRel join = (JoinRel) call.rels[0]; List<RexNode> expList = new ArrayList<RexNode>(Arrays.asList(join.getChildExps())); if (reduceExpressions(join, expList)) { call.transformTo( new JoinRel( join.getCluster(), join.getLeft(), join.getRight(), expList.get(0), join.getJoinType(), join.getVariablesStopped())); // New plan is absolutely better than old plan. call.getPlanner().setImportance(join, 0.0); } }
/** * Creates a partial mapping from a list. For example, <code> * PartialMapping({1, 2, 4}, 6)</code> creates the mapping * * <table border="1"> * <caption>Example</caption> * <tr> * <th>source</th> * <td>0</td> * <td>1</td> * <td>2</td> * <td>3</td> * <td>4</td> * <td>5</td> * </tr> * <tr> * <th>target</th> * <td>-1</td> * <td>0</td> * <td>1</td> * <td>-1</td> * <td>2</td> * <td>-1</td> * </tr> * </table> * * @param sourceList List whose i'th element is the source of target #i * @param sourceCount Number of elements in the source domain * @param mappingType Mapping type, must be {@link * org.eigenbase.util.mapping.MappingType#PARTIAL_SURJECTION} or stronger. */ public PartialMapping(List<Integer> sourceList, int sourceCount, MappingType mappingType) { this.mappingType = mappingType; assert mappingType.isSingleSource(); assert mappingType.isSingleTarget(); int targetCount = sourceList.size(); this.targets = new int[sourceCount]; this.sources = new int[targetCount]; Arrays.fill(sources, -1); for (int i = 0; i < sourceList.size(); ++i) { final int source = sourceList.get(i); sources[i] = source; if (source >= 0) { targets[source] = i; } else { assert !this.mappingType.isMandatorySource(); } } }