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); } }
/** * 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; }
/** * 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; }
/** * 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()); }
public Prepare.PreparingTable getTable(final List<String> names) { switch (names.size()) { case 1: // assume table in SALES schema (the original default) // if it's not supplied, because SqlValidatorTest is effectively // using SALES as its default schema. return tables.get(ImmutableList.of(defaultCatalog, defaultSchema, names.get(0))); case 2: return tables.get(ImmutableList.of(defaultCatalog, names.get(0), names.get(1))); case 3: return tables.get(names); default: return null; } }
public List<SqlMoniker> getAllSchemaObjectNames(List<String> names) { List<SqlMoniker> result; switch (names.size()) { case 0: // looking for schema names result = new ArrayList<SqlMoniker>(); for (MockSchema schema : schemas.values()) { result.add(new SqlMonikerImpl(schema.name, SqlMonikerType.Schema)); } return result; case 1: // looking for table names in the given schema MockSchema schema = schemas.get(names.get(0)); if (schema == null) { return Collections.emptyList(); } result = new ArrayList<SqlMoniker>(); for (String tableName : schema.tableNames) { result.add(new SqlMonikerImpl(tableName, SqlMonikerType.Table)); } return result; default: return Collections.emptyList(); } }
private void addResult(RexNode exp) { // Cast of literal can't be reduced, so skip those (otherwise we'd // go into an infinite loop as we add them back). if (exp.getKind() == RexKind.Cast) { RexCall cast = (RexCall) exp; RexNode operand = cast.getOperands()[0]; if (operand instanceof RexLiteral) { return; } } constExprs.add(exp); // In the case where the expression corresponds to a UDR argument, // we need to preserve casts. Note that this only applies to // the topmost argument, not expressions nested within the UDR // call. // // REVIEW zfong 6/13/08 - Are there other expressions where we // also need to preserve casts? if (parentCallTypeStack.isEmpty()) { addCasts.add(false); } else { addCasts.add( parentCallTypeStack.get(parentCallTypeStack.size() - 1) instanceof FarragoUserDefinedRoutine); } }
/** * 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); } }
/** * 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); } }
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); } }
/** * Creates projection list for scan. If the projection contains expressions, then the input * references from those expressions are extracted and that list of references becomes the * projection list. * * @param origScan row scan underneath the project * @param projRel ProjectRel that we will be creating the projection for * @param projectedColumns returns a list of the projected column ordinals, if it is possible to * project * @param preserveExprCondition condition that identifies special expressions that should be * preserved in the projection * @param defaultExpr expression to be used in the projection if no fields or special columns are * selected * @param newProjList returns a new projection RelNode corresponding to a projection that now * references a rowscan that is projecting the input references that were extracted from the * original projection expressions; if the original expression didn't contain expressions, * then this list is returned empty * @return true if columns in projection list from the scan need to be renamed */ public boolean createProjectionList( FennelRel origScan, ProjectRel projRel, List<Integer> projectedColumns, PushProjector.ExprCondition preserveExprCondition, RexNode defaultExpr, List<ProjectRel> newProjList) { // REVIEW: what about AnonFields? int n = projRel.getChildExps().length; RelDataType rowType = origScan.getRowType(); RelDataType projType = projRel.getRowType(); RelDataTypeField[] projFields = projType.getFields(); List<Integer> tempProjList = new ArrayList<Integer>(); boolean needRename = false; for (int i = 0; i < n; ++i) { RexNode exp = projRel.getChildExps()[i]; List<String> origFieldName = new ArrayList<String>(); Integer projIndex = mapProjCol(exp, origFieldName, rowType); if (projIndex == null) { // there are expressions in the projection; we need to extract // all input references and any special expressions from the // projection PushProjector pushProject = new PushProjector(projRel, null, origScan, preserveExprCondition); ProjectRel newProject = pushProject.convertProject(defaultExpr); if (newProject == null) { // can't do any further projection return false; } newProjList.add(newProject); // using the input references we just extracted, it should now // be possible to create a projection for the row scan needRename = createProjectionList( origScan, (ProjectRel) newProject.getChild(), projectedColumns, preserveExprCondition, defaultExpr, newProjList); assert (projectedColumns.size() > 0); return needRename; } String projFieldName = projFields[i].getName(); if (!projFieldName.equals(origFieldName.get(0))) { needRename = true; } tempProjList.add(projIndex); } // now that we've determined it is possible to project, add the // ordinals to the return list projectedColumns.addAll(tempProjList); return needRename; }
// override RexShuttle public RexNode visitCall(final RexCall call) { int i = reducibleExps.indexOf(call); if (i == -1) { return super.visitCall(call); } RexNode replacement = reducedValues.get(i); if (addCasts.get(i) && (replacement.getType() != call.getType())) { // Handle change from nullable to NOT NULL by claiming // that the result is still nullable, even though // we know it isn't. // // Also, we cannot reduce CAST('abc' AS VARCHAR(4)) to 'abc'. // If we make 'abc' of type VARCHAR(4), we may later encounter // the same expression in a ProjectRel's digest where it has // type VARCHAR(3), and that's wrong. replacement = rexBuilder.makeCast(call.getType(), replacement); } return replacement; }
// 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 projects the output fields of a relational expression * according to a partial mapping. * * <p>A partial mapping is weaker than a permutation: every target has one source, but a source * may have 0, 1 or more than one targets. Usually the result will have fewer fields than the * source, unless some source fields are projected multiple times. * * <p>This method could optimize the result as {@link #permute} does, but does not at present. * * @param rel Relational expression * @param mapping Mapping from source fields to target fields. The mapping type must obey the * constaints {@link MappingType#isMandatorySource()} and {@link * MappingType#isSingleSource()}, as does {@link MappingType#InverseFunction}. * @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 projects a subset of the input fields */ public static RelNode projectMapping(RelNode rel, Mapping mapping, List<String> fieldNames) { assert mapping.getMappingType().isSingleSource(); assert mapping.getMappingType().isMandatorySource(); if (mapping.isIdentity()) { return rel; } 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 < fields.size(); i++) { final RelDataTypeField field = fields.get(i); exprList.add(rel.getCluster().getRexBuilder().makeInputRef(field.getType(), i)); } for (int i = 0; i < mapping.getTargetCount(); i++) { int source = mapping.getSource(i); final RelDataTypeField sourceField = fields.get(source); outputTypeList.add(sourceField.getType()); outputNameList.add( ((fieldNames == null) || (fieldNames.size() <= i) || (fieldNames.get(i) == null)) ? sourceField.getName() : fieldNames.get(i)); projectRefList.add(new RexLocalRef(source, sourceField.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()); }
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); }
/** * 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 Boolean areColumnsUnique(ProjectRelBase rel, BitSet columns, boolean ignoreNulls) { // ProjectRel maps a set of rows to a different set; // Without knowledge of the mapping function(whether it // preserves uniqueness), it is only safe to derive uniqueness // info from the child of a project when the mapping is f(a) => a. // // Also need to map the input column set to the corresponding child // references List<RexNode> projExprs = rel.getProjects(); BitSet childColumns = new BitSet(); for (int bit : BitSets.toIter(columns)) { RexNode projExpr = projExprs.get(bit); if (projExpr instanceof RexInputRef) { childColumns.set(((RexInputRef) projExpr).getIndex()); } else if (projExpr instanceof RexCall && ignoreNulls) { // If the expression is a cast such that the types are the same // except for the nullability, then if we're ignoring nulls, // it doesn't matter whether the underlying column reference // is nullable. Check that the types are the same by making a // nullable copy of both types and then comparing them. RexCall call = (RexCall) projExpr; if (call.getOperator() != SqlStdOperatorTable.CAST) { continue; } RexNode castOperand = call.getOperands().get(0); if (!(castOperand instanceof RexInputRef)) { continue; } RelDataTypeFactory typeFactory = rel.getCluster().getTypeFactory(); RelDataType castType = typeFactory.createTypeWithNullability(projExpr.getType(), true); RelDataType origType = typeFactory.createTypeWithNullability(castOperand.getType(), true); if (castType.equals(origType)) { childColumns.set(((RexInputRef) castOperand).getIndex()); } } else { // If the expression will not influence uniqueness of the // projection, then skip it. continue; } } // If no columns can affect uniqueness, then return unknown if (childColumns.cardinality() == 0) { return null; } return RelMetadataQuery.areColumnsUnique(rel.getChild(), childColumns, ignoreNulls); }
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(); }
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); } }
// implement SargExpr public SargIntervalSequence evaluate() { if (setOp == SargSetOperator.COMPLEMENT) { assert (children.size() == 1); SargExpr child = children.get(0); return child.evaluateComplemented(); } List<SargIntervalSequence> list = evaluateChildren(this); switch (setOp) { case UNION: return evaluateUnion(list); case INTERSECTION: return evaluateIntersection(list); default: throw Util.newInternal(setOp.toString()); } }
/** * 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); }
// implement SargExpr public SargIntervalSequence evaluateComplemented() { if (setOp == SargSetOperator.COMPLEMENT) { // Double negation is a nop return children.get(0).evaluate(); } // Use DeMorgan's Law: complement of union is intersection of // complements, and vice versa List<SargIntervalSequence> list = new ArrayList<SargIntervalSequence>(); for (SargExpr child : children) { SargIntervalSequence newSeq = child.evaluateComplemented(); list.add(newSeq); } switch (setOp) { case INTERSECTION: return evaluateUnion(list); case UNION: return evaluateIntersection(list); default: throw Util.newInternal(setOp.toString()); } }
/** * 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())); }
/** * Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link org.eigenbase.rel.ValuesRel}. */ public TrimResult trimFields( ValuesRel values, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final RelDataType rowType = values.getRowType(); final int fieldCount = rowType.getFieldCount(); // If they are asking for no fields, we can't give them what they want, // because zero-column records are illegal. Give them the last field, // which is unlikely to be a system field. if (fieldsUsed.isEmpty()) { fieldsUsed = Util.bitSetBetween(fieldCount - 1, fieldCount); } // If all fields are used, return unchanged. if (fieldsUsed.equals(Util.bitSetBetween(0, fieldCount))) { Mapping mapping = Mappings.createIdentity(fieldCount); return new TrimResult(values, mapping); } List<List<RexLiteral>> newTuples = new ArrayList<List<RexLiteral>>(); for (List<RexLiteral> tuple : values.getTuples()) { List<RexLiteral> newTuple = new ArrayList<RexLiteral>(); for (int field : Util.toIter(fieldsUsed)) { newTuple.add(tuple.get(field)); } newTuples.add(newTuple); } final Mapping mapping = createMapping(fieldsUsed, fieldCount); RelDataType newRowType = values .getCluster() .getTypeFactory() .createStructType(Mappings.apply3(mapping, rowType.getFieldList())); final ValuesRel newValues = new ValuesRel(values.getCluster(), newRowType, newTuples); return new TrimResult(newValues, mapping); }
/** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link JoinRel}. */ public TrimResult trimFields(JoinRel join, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final RelDataType rowType = join.getRowType(); final int fieldCount = rowType.getFieldCount(); final RexNode conditionExpr = join.getCondition(); final int systemFieldCount = join.getSystemFieldList().size(); // Add in fields used in the condition. BitSet fieldsUsedPlus = (BitSet) fieldsUsed.clone(); final Set<RelDataTypeField> combinedInputExtraFields = new LinkedHashSet<RelDataTypeField>(extraFields); RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(fieldsUsedPlus, combinedInputExtraFields); conditionExpr.accept(inputFinder); // If no system fields are used, we can remove them. int systemFieldUsedCount = 0; for (int i = 0; i < systemFieldCount; ++i) { if (fieldsUsed.get(i)) { ++systemFieldUsedCount; } } final int newSystemFieldCount; if (systemFieldUsedCount == 0) { newSystemFieldCount = 0; } else { newSystemFieldCount = systemFieldCount; } int offset = systemFieldCount; int changeCount = 0; int newFieldCount = newSystemFieldCount; List<RelNode> newInputs = new ArrayList<RelNode>(2); List<Mapping> inputMappings = new ArrayList<Mapping>(); List<Integer> inputExtraFieldCounts = new ArrayList<Integer>(); for (RelNode input : join.getInputs()) { final RelDataType inputRowType = input.getRowType(); final int inputFieldCount = inputRowType.getFieldCount(); // Compute required mapping. BitSet inputFieldsUsed = new BitSet(inputFieldCount); for (int bit : Util.toIter(fieldsUsedPlus)) { if (bit >= offset && bit < offset + inputFieldCount) { inputFieldsUsed.set(bit - offset); } } // If there are system fields, we automatically use the // corresponding field in each input. if (newSystemFieldCount > 0) { // calling with newSystemFieldCount == 0 should be safe but hits // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6222207 inputFieldsUsed.set(0, newSystemFieldCount); } // FIXME: We ought to collect extra fields for each input // individually. For now, we assume that just one input has // on-demand fields. Set<RelDataTypeField> inputExtraFields = input.getRowType().getField("_extra") == null ? Collections.<RelDataTypeField>emptySet() : combinedInputExtraFields; inputExtraFieldCounts.add(inputExtraFields.size()); TrimResult trimResult = trimChild(join, input, inputFieldsUsed, inputExtraFields); newInputs.add(trimResult.left); if (trimResult.left != input) { ++changeCount; } final Mapping inputMapping = trimResult.right; inputMappings.add(inputMapping); // Move offset to point to start of next input. offset += inputFieldCount; newFieldCount += inputMapping.getTargetCount() + inputExtraFields.size(); } Mapping mapping = Mappings.create(MappingType.InverseSurjection, fieldCount, newFieldCount); for (int i = 0; i < newSystemFieldCount; ++i) { mapping.set(i, i); } offset = systemFieldCount; int newOffset = newSystemFieldCount; for (int i = 0; i < inputMappings.size(); i++) { Mapping inputMapping = inputMappings.get(i); for (IntPair pair : inputMapping) { mapping.set(pair.source + offset, pair.target + newOffset); } offset += inputMapping.getSourceCount(); newOffset += inputMapping.getTargetCount() + inputExtraFieldCounts.get(i); } if (changeCount == 0 && mapping.isIdentity()) { return new TrimResult(join, Mappings.createIdentity(fieldCount)); } // Build new join. final RexVisitor<RexNode> shuttle = new RexPermuteInputsShuttle(mapping, newInputs.get(0), newInputs.get(1)); RexNode newConditionExpr = conditionExpr.accept(shuttle); final JoinRel newJoin = join.copy(join.getTraitSet(), newConditionExpr, newInputs.get(0), newInputs.get(1)); return new TrimResult(newJoin, mapping); }
public int reduceExpr(int opOrdinal, List<Object> list) { final SqlParserUtil.ToTreeListItem betweenNode = (SqlParserUtil.ToTreeListItem) list.get(opOrdinal); SqlOperator op = betweenNode.getOperator(); assert op == this; // Break the expression up into expressions. For example, a simple // expression breaks down as follows: // // opOrdinal endExp1 // | | // a + b BETWEEN c + d AND e + f // |_____| |_____| |_____| // exp0 exp1 exp2 // Create the expression between 'BETWEEN' and 'AND'. final SqlParserPos pos = ((SqlNode) list.get(opOrdinal + 1)).getParserPosition(); SqlNode exp1 = SqlParserUtil.toTreeEx(list, opOrdinal + 1, 0, SqlKind.AND); if ((opOrdinal + 2) >= list.size()) { SqlParserPos lastPos = ((SqlNode) list.get(list.size() - 1)).getParserPosition(); final int line = lastPos.getEndLineNum(); final int col = lastPos.getEndColumnNum() + 1; SqlParserPos errPos = new SqlParserPos(line, col, line, col); throw SqlUtil.newContextException( errPos, EigenbaseResource.instance().BetweenWithoutAnd.ex()); } final Object o = list.get(opOrdinal + 2); if (!(o instanceof SqlParserUtil.ToTreeListItem)) { SqlParserPos errPos = ((SqlNode) o).getParserPosition(); throw SqlUtil.newContextException( errPos, EigenbaseResource.instance().BetweenWithoutAnd.ex()); } if (((SqlParserUtil.ToTreeListItem) o).getOperator().getKind() != SqlKind.AND) { SqlParserPos errPos = ((SqlParserUtil.ToTreeListItem) o).getPos(); throw SqlUtil.newContextException( errPos, EigenbaseResource.instance().BetweenWithoutAnd.ex()); } // Create the expression after 'AND', but stopping if we encounter an // operator of lower precedence. // // For example, // a BETWEEN b AND c + d OR e // becomes // (a BETWEEN b AND c + d) OR e // because OR has lower precedence than BETWEEN. SqlNode exp2 = SqlParserUtil.toTreeEx(list, opOrdinal + 3, getRightPrec(), SqlKind.OTHER); // Create the call. SqlNode exp0 = (SqlNode) list.get(opOrdinal - 1); SqlCall newExp = createCall( betweenNode.getPos(), exp0, exp1, exp2, SqlLiteral.createSymbol(flag, SqlParserPos.ZERO)); // Replace all of the matched nodes with the single reduced node. SqlParserUtil.replaceSublist(list, opOrdinal - 1, opOrdinal + 4, newExp); // Return the ordinal of the new current node. return opOrdinal - 1; }
/** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link ProjectRel}. */ public TrimResult trimFields( ProjectRel project, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final RelDataType rowType = project.getRowType(); final int fieldCount = rowType.getFieldCount(); final RelNode input = project.getChild(); final RelDataType inputRowType = input.getRowType(); // Which fields are required from the input? BitSet inputFieldsUsed = new BitSet(inputRowType.getFieldCount()); final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<RelDataTypeField>(extraFields); RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(inputFieldsUsed, inputExtraFields); for (Ord<RexNode> ord : Ord.zip(project.getProjects())) { if (fieldsUsed.get(ord.i)) { ord.e.accept(inputFinder); } } // Create input with trimmed columns. TrimResult trimResult = trimChild(project, input, inputFieldsUsed, inputExtraFields); RelNode newInput = trimResult.left; final Mapping inputMapping = trimResult.right; // If the input is unchanged, and we need to project all columns, // there's nothing we can do. if (newInput == input && fieldsUsed.cardinality() == fieldCount) { return new TrimResult(project, Mappings.createIdentity(fieldCount)); } // Some parts of the system can't handle rows with zero fields, so // pretend that one field is used. if (fieldsUsed.cardinality() == 0) { final Mapping mapping = Mappings.create(MappingType.InverseSurjection, fieldCount, 1); final RexLiteral expr = project.getCluster().getRexBuilder().makeExactLiteral(BigDecimal.ZERO); RelDataType newRowType = project .getCluster() .getTypeFactory() .createStructType( Collections.singletonList(expr.getType()), Collections.singletonList("DUMMY")); ProjectRel newProject = new ProjectRel( project.getCluster(), project.getCluster().traitSetOf(RelCollationImpl.EMPTY), newInput, Collections.<RexNode>singletonList(expr), newRowType, project.getFlags()); return new TrimResult(newProject, mapping); } // Build new project expressions, and populate the mapping. List<RexNode> newProjectExprList = new ArrayList<RexNode>(); final RexVisitor<RexNode> shuttle = new RexPermuteInputsShuttle(inputMapping, newInput); final Mapping mapping = Mappings.create(MappingType.InverseSurjection, fieldCount, fieldsUsed.cardinality()); for (Ord<RexNode> ord : Ord.zip(project.getProjects())) { if (fieldsUsed.get(ord.i)) { mapping.set(ord.i, newProjectExprList.size()); RexNode newProjectExpr = ord.e.accept(shuttle); newProjectExprList.add(newProjectExpr); } } final RelDataType newRowType = project .getCluster() .getTypeFactory() .createStructType(Mappings.apply3(mapping, rowType.getFieldList())); final List<RelCollation> newCollations = RexUtil.apply(inputMapping, project.getCollationList()); final RelNode newProject; if (RemoveTrivialProjectRule.isIdentity( newProjectExprList, newRowType, newInput.getRowType())) { // The new project would be the identity. It is equivalent to return // its child. newProject = newInput; } else { newProject = new ProjectRel( project.getCluster(), project .getCluster() .traitSetOf( newCollations.isEmpty() ? RelCollationImpl.EMPTY : newCollations.get(0)), newInput, newProjectExprList, newRowType, project.getFlags()); assert newProject.getClass() == project.getClass(); } return new TrimResult(newProject, mapping); }