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())); } } } }
/** * Replaces the operands of a call. The new operands' types must match the old operands' types. */ public static RexCall replaceOperands(RexCall call, RexNode[] operands) { if (call.operands == operands) { return call; } for (int i = 0; i < operands.length; i++) { RelDataType oldType = call.operands[i].getType(); RelDataType newType = operands[i].getType(); if (!oldType.isNullable() && newType.isNullable()) { throw Util.newInternal("invalid nullability"); } assert (oldType.toString().equals(newType.toString())); } return new RexCall(call.getType(), call.getOperator(), operands); }
/** * Returns whether a list of expressions contains complex expressions, that is, a call whose * arguments are not {@link RexVariable} (or a subtype such as {@link RexInputRef}) or {@link * RexLiteral}. */ public static boolean containComplexExprs(List<RexNode> exprs) { for (RexNode expr : exprs) { if (expr instanceof RexCall) { RexCall rexCall = (RexCall) expr; final RexNode[] operands = rexCall.getOperands(); for (int j = 0; j < operands.length; j++) { RexNode operand = operands[j]; if (!isAtomic(operand)) { return true; } } } } return false; }
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); }
public Void visitCall(RexCall call) { final RexNode[] operands = call.getOperands(); for (int i = 0; i < operands.length; i++) { RexNode operand = operands[i]; operand.accept(this); } return null; }
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); }
/** * Returns whether an array of exp contains aggregate function calls whose arguments are not * {@link RexInputRef}.s * * @param exprs Expressions * @param fail Whether to assert if there is such a function call */ static boolean containNonTrivialAggs(RexNode[] exprs, boolean fail) { for (int i = 0; i < exprs.length; i++) { RexNode expr = exprs[i]; if (expr instanceof RexCall) { RexCall rexCall = (RexCall) expr; if (rexCall.getOperator() instanceof SqlAggFunction) { final RexNode[] operands = rexCall.getOperands(); for (int j = 0; j < operands.length; j++) { RexNode operand = operands[j]; if (!(operand instanceof RexLocalRef)) { assert !fail : "contains non trivial agg"; return true; } } } } } return false; }
/** * Determines whether a {@link RexCall} requires decimal expansion. It usually requires expansion * if it has decimal operands. * * <p>Exceptions to this rule are: * * <ul> * <li>isNull doesn't require expansion * <li>It's okay to cast decimals to and from char types * <li>It's okay to cast nulls as decimals * <li>Casts require expansion if their return type is decimal * <li>Reinterpret casts can handle a decimal operand * </ul> * * @param expr expression possibly in need of expansion * @param recurse whether to check nested calls * @return whether the expression requires expansion */ public static boolean requiresDecimalExpansion(RexNode expr, boolean recurse) { if (!(expr instanceof RexCall)) { return false; } RexCall call = (RexCall) expr; boolean localCheck = true; switch (call.getKind()) { case Reinterpret: case IsNull: localCheck = false; break; case Cast: RelDataType lhsType = call.getType(); RelDataType rhsType = call.operands[0].getType(); if (rhsType.getSqlTypeName() == SqlTypeName.NULL) { return false; } if (SqlTypeUtil.inCharFamily(lhsType) || SqlTypeUtil.inCharFamily(rhsType)) { localCheck = false; } else if (SqlTypeUtil.isDecimal(lhsType) && (lhsType != rhsType)) { return true; } break; default: localCheck = call.getOperator().requiresDecimalExpansion(); } if (localCheck) { if (SqlTypeUtil.isDecimal(call.getType())) { // NOTE jvs 27-Mar-2007: Depending on the type factory, the // result of a division may be decimal, even though both inputs // are integer. return true; } for (int i = 0; i < call.operands.length; i++) { if (SqlTypeUtil.isDecimal(call.operands[i].getType())) { return true; } } } return (recurse && requiresDecimalExpansion(call.operands, recurse)); }
// 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 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); } }
public static boolean canReinterpretOverflow(RexCall call) { assert (call.isA(RexKind.Reinterpret)) : "call is not a reinterpret"; return call.operands.length > 1; }
/** * 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; }