Example #1
0
  /**
   * 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;
 }
Example #3
0
 /**
  * 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);
 }
Example #4
0
 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);
 }
    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);
      }
    }
Example #6
0
 /** 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);
   }
 }