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); } }
private void reduceNotNullableFilter( RelOptRuleCall call, FilterRel filter, RexCall rexCall, boolean reverse) { // If the expression is a IS [NOT] NULL on a non-nullable // column, then we can either remove the filter or replace // it with an EmptyRel. SqlOperator op = rexCall.getOperator(); boolean alwaysTrue; if (op == SqlStdOperatorTable.isNullOperator || op == SqlStdOperatorTable.isUnknownOperator) { alwaysTrue = false; } else if (op == SqlStdOperatorTable.isNotNullOperator) { alwaysTrue = true; } else { return; } if (reverse) { alwaysTrue = !alwaysTrue; } RexNode operand = rexCall.getOperands()[0]; if (operand instanceof RexInputRef) { RexInputRef inputRef = (RexInputRef) operand; if (!inputRef.getType().isNullable()) { if (alwaysTrue) { call.transformTo(filter.getChild()); } else { call.transformTo(new EmptyRel(filter.getCluster(), filter.getRowType())); } } } }
private Expression translate0(RexNode expr) { if (expr instanceof RexInputRef) { // TODO: multiple inputs, e.g. joins final Expression input = getInput(0); final int index = ((RexInputRef) expr).getIndex(); final List<RelDataTypeField> fields = program.getInputRowType().getFieldList(); final RelDataTypeField field = fields.get(index); if (fields.size() == 1) { return input; } else if (input.getType() == Object[].class) { return Expressions.convert_( Expressions.arrayIndex(input, Expressions.constant(field.getIndex())), Types.box(JavaRules.EnumUtil.javaClass(typeFactory, field.getType()))); } else { return Expressions.field(input, field.getName()); } } if (expr instanceof RexLocalRef) { return translate(program.getExprList().get(((RexLocalRef) expr).getIndex())); } if (expr instanceof RexLiteral) { return Expressions.constant( ((RexLiteral) expr).getValue(), typeFactory.getJavaClass(expr.getType())); } if (expr instanceof RexCall) { final RexCall call = (RexCall) expr; final SqlOperator operator = call.getOperator(); final ExpressionType expressionType = SQL_TO_LINQ_OPERATOR_MAP.get(operator); if (expressionType != null) { switch (operator.getSyntax()) { case Binary: return Expressions.makeBinary( expressionType, translate(call.getOperands()[0]), translate(call.getOperands()[1])); case Postfix: case Prefix: return Expressions.makeUnary(expressionType, translate(call.getOperands()[0])); default: throw new RuntimeException("unknown syntax " + operator.getSyntax()); } } Method method = SQL_OP_TO_JAVA_METHOD_MAP.get(operator); if (method != null) { List<Expression> exprs = translateList(Arrays.asList(call.operands)); return !Modifier.isStatic(method.getModifiers()) ? Expressions.call(exprs.get(0), method, exprs.subList(1, exprs.size())) : Expressions.call(method, exprs); } switch (expr.getKind()) { default: throw new RuntimeException("cannot translate expression " + expr); } } throw new RuntimeException("cannot translate expression " + expr); }
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); }
// implement CallConvertlet public void convert(RexCall call) { // REVIEW jvs 18-Mar-2006: The test for variableSeen // here and elsewhere precludes predicates like where // (boolean_col AND (int_col > 10)). Should probably // support bare boolean columns as predicates with // coordinate TRUE. if (variableSeen || (coordinate != null)) { failed = true; } if (failed) { return; } int nOperands = call.getOperands().size(); assert (exprStack.size() >= nOperands); SargSetExpr expr = factory.newSetExpr(boundInputRef.getType(), setOp); // Pop the correct number of operands off the stack // and transfer them to the new set expression. ListIterator<SargExpr> iter = exprStack.listIterator(exprStack.size() - nOperands); while (iter.hasNext()) { expr.addChild(iter.next()); iter.remove(); } exprStack.add(expr); }
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); }
/** * 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); }
// 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; }
public Void visitCall(RexCall call) { CallConvertlet convertlet = convertletMap.get(call.getOperator()); if (convertlet == null) { failed = true; return null; } // visit operands first super.visitCall(call); convertlet.convert(call); return 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); }
private void reduceCasts(RexCall outerCast) { RexNode[] operands = outerCast.getOperands(); if (operands.length != 1) { return; } RelDataType outerCastType = outerCast.getType(); RelDataType operandType = operands[0].getType(); if (operandType.equals(outerCastType)) { removableCasts.add(outerCast); return; } // See if the reduction // CAST((CAST x AS type) AS type NOT NULL) // -> CAST(x AS type NOT NULL) // applies. TODO jvs 15-Dec-2008: consider // similar cases for precision changes. if (!(operands[0] instanceof RexCall)) { return; } RexCall innerCast = (RexCall) operands[0]; if (innerCast.getOperator() != SqlStdOperatorTable.castFunc) { return; } if (innerCast.getOperands().length != 1) { return; } RelDataTypeFactory typeFactory = preparingStmt.getFarragoTypeFactory(); RelDataType outerTypeNullable = typeFactory.createTypeWithNullability(outerCastType, true); RelDataType innerTypeNullable = typeFactory.createTypeWithNullability(operandType, true); if (outerTypeNullable != innerTypeNullable) { return; } if (operandType.isNullable()) { removableCasts.add(innerCast); } }
// implement FarragoOJRexImplementor public Expression implementFarrago( FarragoRexToOJTranslator translator, RexCall call, Expression[] operands) { RelDataType lhsType = call.getType(); RelDataType rhsType = call.operands[0].getType(); Expression rhsExp = operands[0]; SqlTypeName lhsTypeName = lhsType.getSqlTypeName(); if ((lhsTypeName == SqlTypeName.CURSOR) || (lhsTypeName == SqlTypeName.COLUMN_LIST)) { // Conversion should already have been taken care of outside. return rhsExp; } // NOTE jvs 19-Nov-2008: In some cases (e.g. FRG-273) a cast // may be illegal at the SQL level, but allowable as part of // implementation, so don't try to enforce // SqlTypeUtil.canCastFrom here. Anything which was supposed // to have been prevented should already have been caught // by the validator. CastHelper helper = new CastHelper(translator, null, call.toString(), lhsType, rhsType, null, rhsExp); return helper.implement(); }
// implement CallConvertlet public void convert(RexCall call) { if (!variableSeen) { failed = true; } SqlOperator op = call.getOperator(); if ((op == SqlStdOperatorTable.isNullOperator) || (op == SqlStdOperatorTable.isUnknownOperator)) { coordinate = factory.getRexBuilder().constantNull(); } else if (op == SqlStdOperatorTable.isTrueOperator) { coordinate = factory.getRexBuilder().makeLiteral(true); } else if (op == SqlStdOperatorTable.isFalseOperator) { coordinate = factory.getRexBuilder().makeLiteral(false); } else if (coordinate == null) { failed = true; } if (failed) { return; } SargIntervalExpr expr = factory.newIntervalExpr(boundInputRef.getType()); if (boundType == null) { expr.setPoint(coordinate); } else { SargBoundType actualBound = boundType; if (reverse) { if (actualBound == SargBoundType.LOWER) { actualBound = SargBoundType.UPPER; } else { actualBound = SargBoundType.LOWER; } } if (actualBound == SargBoundType.LOWER) { expr.setLower(coordinate, strictness); } else { expr.setUpper(coordinate, strictness); } } exprStack.add(expr); clearLeaf(); }
/** Converts an expression from {@link RexNode} to {@link SqlNode} format. */ SqlNode toSql(RexProgram program, RexNode rex) { if (rex instanceof RexLocalRef) { final int index = ((RexLocalRef) rex).getIndex(); return toSql(program, program.getExprList().get(index)); } else if (rex instanceof RexInputRef) { return field(((RexInputRef) rex).getIndex()); } else if (rex instanceof RexLiteral) { final RexLiteral literal = (RexLiteral) rex; switch (literal.getTypeName().getFamily()) { case CHARACTER: return SqlLiteral.createCharString((String) literal.getValue2(), POS); case NUMERIC: case EXACT_NUMERIC: return SqlLiteral.createExactNumeric(literal.getValue().toString(), POS); case APPROXIMATE_NUMERIC: return SqlLiteral.createApproxNumeric(literal.getValue().toString(), POS); case BOOLEAN: return SqlLiteral.createBoolean((Boolean) literal.getValue(), POS); case DATE: return SqlLiteral.createDate((Calendar) literal.getValue(), POS); case TIME: return SqlLiteral.createTime( (Calendar) literal.getValue(), literal.getType().getPrecision(), POS); case TIMESTAMP: return SqlLiteral.createTimestamp( (Calendar) literal.getValue(), literal.getType().getPrecision(), POS); case ANY: switch (literal.getTypeName()) { case NULL: return SqlLiteral.createNull(POS); // fall through } default: throw new AssertionError(literal + ": " + literal.getTypeName()); } } else if (rex instanceof RexCall) { final RexCall call = (RexCall) rex; final SqlOperator op = call.getOperator(); final List<SqlNode> nodeList = toSql(program, call.getOperands()); if (op == SqlStdOperatorTable.CAST) { RelDataType type = call.getType(); if (type.getSqlTypeName() == SqlTypeName.VARCHAR && dialect.getDatabaseProduct() == SqlDialect.DatabaseProduct.MYSQL) { // MySQL doesn't have a VARCHAR type, only CHAR. nodeList.add( new SqlDataTypeSpec( new SqlIdentifier("CHAR", POS), type.getPrecision(), -1, null, null, POS)); } else { nodeList.add(toSql(type)); } } if (op == SqlStdOperatorTable.CASE) { final SqlNode valueNode; final List<SqlNode> whenList = Expressions.list(); final List<SqlNode> thenList = Expressions.list(); final SqlNode elseNode; if (nodeList.size() % 2 == 0) { // switched: // "case x when v1 then t1 when v2 then t2 ... else e end" valueNode = nodeList.get(0); for (int i = 1; i < nodeList.size() - 1; i += 2) { whenList.add(nodeList.get(i)); thenList.add(nodeList.get(i + 1)); } } else { // other: "case when w1 then t1 when w2 then t2 ... else e end" valueNode = null; for (int i = 0; i < nodeList.size() - 1; i += 2) { whenList.add(nodeList.get(i)); thenList.add(nodeList.get(i + 1)); } } elseNode = nodeList.get(nodeList.size() - 1); return op.createCall( POS, valueNode, new SqlNodeList(whenList, POS), new SqlNodeList(thenList, POS), elseNode); } if (op instanceof SqlBinaryOperator && nodeList.size() > 2) { // In RexNode trees, OR and AND have any number of children; // SqlCall requires exactly 2. So, convert to a left-deep binary tree. return createLeftCall(op, nodeList); } return op.createCall(new SqlNodeList(nodeList, POS)); } else { throw new AssertionError(rex); } }
@Override public TupleFilter visitCall(RexCall call) { TupleFilter filter = null; SqlOperator op = call.getOperator(); switch (op.getKind()) { case AND: filter = new LogicalTupleFilter(FilterOperatorEnum.AND); break; case OR: filter = new LogicalTupleFilter(FilterOperatorEnum.OR); break; case NOT: filter = new LogicalTupleFilter(FilterOperatorEnum.NOT); break; case EQUALS: filter = new CompareTupleFilter(FilterOperatorEnum.EQ); break; case GREATER_THAN: filter = new CompareTupleFilter(FilterOperatorEnum.GT); break; case LESS_THAN: filter = new CompareTupleFilter(FilterOperatorEnum.LT); break; case GREATER_THAN_OR_EQUAL: filter = new CompareTupleFilter(FilterOperatorEnum.GTE); break; case LESS_THAN_OR_EQUAL: filter = new CompareTupleFilter(FilterOperatorEnum.LTE); break; case NOT_EQUALS: filter = new CompareTupleFilter(FilterOperatorEnum.NEQ); break; case IS_NULL: filter = new CompareTupleFilter(FilterOperatorEnum.ISNULL); break; case IS_NOT_NULL: filter = new CompareTupleFilter(FilterOperatorEnum.ISNOTNULL); break; case CAST: case REINTERPRET: // NOTE: use child directly break; case CASE: filter = new CaseTupleFilter(); break; case OTHER: if (op.getName().equalsIgnoreCase("extract_date")) { filter = new ExtractTupleFilter(FilterOperatorEnum.EXTRACT); } else { throw new UnsupportedOperationException(op.getName()); } break; default: throw new UnsupportedOperationException(op.getName()); } for (RexNode operand : call.operands) { TupleFilter childFilter = operand.accept(this); if (filter == null) { filter = childFilter; } else { filter.addChild(childFilter); } } if (op.getKind() == SqlKind.OR) { CompareTupleFilter inFilter = mergeToInClause(filter); if (inFilter != null) { filter = inFilter; } } return filter; }
/** * Reduces a list of expressions. * * @param rel Relational expression * @param expList List of expressions, modified in place * @return whether reduction found something to change, and succeeded */ static boolean reduceExpressions(RelNode rel, List<RexNode> expList) { RexBuilder rexBuilder = rel.getCluster().getRexBuilder(); // Find reducible expressions. FarragoSessionPlanner planner = (FarragoSessionPlanner) rel.getCluster().getPlanner(); FarragoSessionPreparingStmt preparingStmt = planner.getPreparingStmt(); List<RexNode> constExps = new ArrayList<RexNode>(); List<Boolean> addCasts = new ArrayList<Boolean>(); List<RexNode> removableCasts = new ArrayList<RexNode>(); findReducibleExps(preparingStmt, expList, constExps, addCasts, removableCasts); if (constExps.isEmpty() && removableCasts.isEmpty()) { return false; } // Remove redundant casts before reducing constant expressions. // If the argument to the redundant cast is a reducible constant, // reducing that argument to a constant first will result in not being // able to locate the original cast expression. if (!removableCasts.isEmpty()) { List<RexNode> reducedExprs = new ArrayList<RexNode>(); List<Boolean> noCasts = new ArrayList<Boolean>(); for (RexNode exp : removableCasts) { RexCall call = (RexCall) exp; reducedExprs.add(call.getOperands()[0]); noCasts.add(false); } RexReplacer replacer = new RexReplacer(rexBuilder, removableCasts, reducedExprs, noCasts); replacer.apply(expList); } if (constExps.isEmpty()) { return true; } // Compute the values they reduce to. List<RexNode> reducedValues = new ArrayList<RexNode>(); ReentrantValuesStmt reentrantStmt = new ReentrantValuesStmt( preparingStmt.getRootStmtContext(), rexBuilder, constExps, reducedValues); FarragoSession session = getSession(rel); reentrantStmt.execute(session, true); if (reentrantStmt.failed) { return false; } // For ProjectRel, we have to be sure to preserve the result // types, so always cast regardless of the expression type. // For other RelNodes like FilterRel, in general, this isn't necessary, // and the presence of casts could hinder other rules such as sarg // analysis, which require bare literals. But there are special cases, // like when the expression is a UDR argument, that need to be // handled as special cases. if (rel instanceof ProjectRel) { for (int i = 0; i < reducedValues.size(); i++) { addCasts.set(i, true); } } RexReplacer replacer = new RexReplacer(rexBuilder, constExps, reducedValues, addCasts); replacer.apply(expList); return true; }