Package org.eigenbase.rex

Source Code of org.eigenbase.rex.RexBuilder

/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
package org.eigenbase.rex;

import java.math.*;
import java.util.*;

import org.eigenbase.rel.*;
import org.eigenbase.reltype.*;
import org.eigenbase.sql.*;
import org.eigenbase.sql.SqlIntervalQualifier.TimeUnit;
import org.eigenbase.sql.fun.*;
import org.eigenbase.sql.type.*;
import org.eigenbase.util.*;
import org.eigenbase.util14.DateTimeUtil;

import net.hydromatic.avatica.ByteString;

import net.hydromatic.optiq.runtime.Spaces;
import net.hydromatic.optiq.runtime.SqlFunctions;

import com.google.common.collect.ImmutableList;

/**
* Factory for row expressions.
*
* <p>Some common literal values (NULL, TRUE, FALSE, 0, 1, '') are cached.</p>
*/
public class RexBuilder {
  /**
   * Special operator that accesses an unadvertised field of an input record.
   * This operator cannot be used in SQL queries; it is introduced temporarily
   * during sql-to-rel translation, then replaced during the process that
   * trims unwanted fields.
   */
  public static final SqlSpecialOperator GET_OPERATOR =
      new SqlSpecialOperator("_get", SqlKind.OTHER_FUNCTION);

  //~ Instance fields --------------------------------------------------------

  protected final RelDataTypeFactory typeFactory;
  private final RexLiteral booleanTrue;
  private final RexLiteral booleanFalse;
  private final RexLiteral charEmpty;
  private final RexLiteral constantNull;
  private final SqlStdOperatorTable opTab = SqlStdOperatorTable.instance();

  //~ Constructors -----------------------------------------------------------

  /**
   * Creates a RexBuilder.
   *
   * @param typeFactory Type factory
   */
  public RexBuilder(RelDataTypeFactory typeFactory) {
    this.typeFactory = typeFactory;
    this.booleanTrue =
        makeLiteral(
            Boolean.TRUE,
            typeFactory.createSqlType(SqlTypeName.BOOLEAN),
            SqlTypeName.BOOLEAN);
    this.booleanFalse =
        makeLiteral(
            Boolean.FALSE,
            typeFactory.createSqlType(SqlTypeName.BOOLEAN),
            SqlTypeName.BOOLEAN);
    this.charEmpty =
        makeLiteral(
            new NlsString("", null, null),
            typeFactory.createSqlType(SqlTypeName.CHAR, 0),
            SqlTypeName.CHAR);
    this.constantNull =
        makeLiteral(
            null,
            typeFactory.createSqlType(SqlTypeName.NULL),
            SqlTypeName.NULL);
  }

  //~ Methods ----------------------------------------------------------------

  /**
   * Returns this RexBuilder's type factory
   *
   * @return type factory
   */
  public RelDataTypeFactory getTypeFactory() {
    return typeFactory;
  }

  /**
   * Returns this RexBuilder's operator table
   *
   * @return operator table
   */
  public SqlStdOperatorTable getOpTab() {
    return opTab;
  }

  /**
   * Creates an expression accessing a given named field from a record.
   *
   * <p>NOTE: Be careful choosing the value of {@code caseSensitive}.
   * If the field name was supplied by an end-user (e.g. as a column alias in
   * SQL), use your session's case-sensitivity setting.
   * Only hard-code {@code true} if you are sure that the field name is
   * internally generated.
   * Hard-coding {@code false} is almost certainly wrong.</p>
   *
   * @param expr      Expression yielding a record
   * @param fieldName Name of field in record
   * @param caseSensitive Whether match is case-sensitive
   * @return Expression accessing a given named field
   */
  public RexNode makeFieldAccess(RexNode expr, String fieldName,
      boolean caseSensitive) {
    final RelDataType type = expr.getType();
    final RelDataTypeField field = type.getField(fieldName, caseSensitive);
    if (field == null) {
      throw Util.newInternal(
          "Type '" + type + "' has no field '"
          + fieldName + "'");
    }
    return makeFieldAccessInternal(expr, field);
  }

  /**
   * Creates an expression accessing a field with a given ordinal from a
   * record.
   *
   * @param expr Expression yielding a record
   * @param i    Ordinal of field
   * @return Expression accessing given field
   */
  public RexNode makeFieldAccess(
      RexNode expr,
      int i) {
    final RelDataType type = expr.getType();
    final List<RelDataTypeField> fields = type.getFieldList();
    if ((i < 0) || (i >= fields.size())) {
      throw Util.newInternal(
          "Field ordinal " + i + " is invalid for "
          + " type '" + type + "'");
    }
    return makeFieldAccessInternal(expr, fields.get(i));
  }

  /**
   * Creates an expression accessing a given field from a record.
   *
   * @param expr  Expression yielding a record
   * @param field Field
   * @return Expression accessing given field
   */
  private RexNode makeFieldAccessInternal(
      RexNode expr,
      final RelDataTypeField field) {
    if (expr instanceof RexRangeRef) {
      RexRangeRef range = (RexRangeRef) expr;
      if (field.getIndex() < 0) {
        return makeCall(
            field.getType(),
            GET_OPERATOR,
            ImmutableList.of(
                expr,
                makeLiteral(field.getName())));
      }
      return new RexInputRef(
          range.getOffset() + field.getIndex(),
          field.getType());
    }
    return new RexFieldAccess(expr, field);
  }

  /**
   * Creates a call with an array of arguments and a predetermined type.
   */
  public RexNode makeCall(
      RelDataType returnType,
      SqlOperator op,
      List<RexNode> exprs) {
    return new RexCall(returnType, op, exprs);
  }

  /**
   * Creates a call with an array of arguments.
   *
   * <p>This is the fundamental method called by all of the other <code>
   * makeCall</code> methods. If you derive a class from {@link RexBuilder},
   * this is the only method you need to override.</p>
   */
  public RexNode makeCall(
      SqlOperator op,
      List<? extends RexNode> exprs) {
    // TODO jvs 12-Jun-2010:  Find a better place for this;
    // it surely does not belong here.
    if (op == SqlStdOperatorTable.AND
        && exprs.size() == 2
        && exprs.get(0).equals(exprs.get(1))) {
      // Avoid generating 'AND(x, x)'; this can cause plan explosions if a
      // relnode is its own child and is merged with itself.
      return exprs.get(0);
    }

    final RelDataType type = deriveReturnType(op, typeFactory, exprs);
    return new RexCall(type, op, exprs);
  }

  /**
   * Creates a call with a list of arguments.
   *
   * <p>Equivalent to <code>makeCall(op, exprList.toArray(new
   * RexNode[exprList.size()]))</code>.
   */
  public final RexNode makeCall(
      SqlOperator op,
      RexNode... exprs) {
    return makeCall(op, ImmutableList.copyOf(exprs));
  }

  /**
   * Creates a call with an array of arguments.
   *
   * <p>This is the fundamental method called by all of the other <code>
   * makeCall</code> methods. If you derive a class from {@link RexBuilder},
   * this is the only method you need to override.</p>
   */
  public RexNode makeFlatCall(
      SqlOperator op,
      List<? extends RexNode> exprs) {
    return makeCall(op, RexUtil.flatten(exprs, op));
  }

  /**
   * Derives the return type of a call to an operator.
   *
   * @param op          the operator being called
   * @param typeFactory factory for return type
   * @param exprs       actual operands
   * @return derived type
   */
  public RelDataType deriveReturnType(
      SqlOperator op,
      RelDataTypeFactory typeFactory,
      List<? extends RexNode> exprs) {
    return op.inferReturnType(new RexCallBinding(typeFactory, op, exprs));
  }

  /**
   * Creates a reference to an aggregate call, checking for repeated calls.
   *
   * <p>Argument types help to optimize for repeated aggregates.
   * For instance count(42) is equivalent to count(*).</p>
   *
   * @param aggCall aggregate call to be added
   * @param groupCount number of groups in the aggregate relation
   * @param aggCalls destination list of aggregate calls
   * @param aggCallMapping the dictionary of already added calls
   * @param aggArgTypes Argument types, not null
   * @return Rex expression for the given aggregate call
   */
  public RexNode addAggCall(
      AggregateCall aggCall,
      int groupCount,
      List<AggregateCall> aggCalls,
      Map<AggregateCall, RexNode> aggCallMapping,
      final List<RelDataType> aggArgTypes) {
    if (aggCall.getAggregation() instanceof SqlCountAggFunction
        && !aggCall.isDistinct()) {
      final List<Integer> notNullArgList =
          nullableArgs(aggCall.getArgList(), aggArgTypes);
      if (!notNullArgList.equals(aggCall.getArgList())) {
        aggCall = new AggregateCall(aggCall.getAggregation(),
            aggCall.isDistinct(), notNullArgList,
            aggCall.getType(), aggCall.getName());
      }
    }
    RexNode rex = aggCallMapping.get(aggCall);
    if (rex == null) {
      int index = aggCalls.size() + groupCount;
      aggCalls.add(aggCall);
      rex = makeInputRef(aggCall.getType(), index);
      aggCallMapping.put(aggCall, rex);
    }
    return rex;
  }

  private static List<Integer> nullableArgs(List<Integer> list0,
      List<RelDataType> types) {
    final List<Integer> list = new ArrayList<Integer>();
    for (Pair<Integer, RelDataType> pair : Pair.zip(list0, types)) {
      if (pair.right.isNullable()) {
        list.add(pair.left);
      }
    }
    return list;
  }

  /**
   * Creates a call to a windowed agg.
   */
  public RexNode makeOver(
      RelDataType type,
      SqlAggFunction operator,
      List<RexNode> exprs,
      List<RexNode> partitionKeys,
      ImmutableList<RexFieldCollation> orderKeys,
      RexWindowBound lowerBound,
      RexWindowBound upperBound,
      boolean physical,
      boolean allowPartial,
      boolean nullWhenCountZero) {
    assert operator != null;
    assert exprs != null;
    assert partitionKeys != null;
    assert orderKeys != null;
    final RexWindow window =
        makeWindow(
            partitionKeys,
            orderKeys,
            lowerBound,
            upperBound,
            physical);
    final RexOver over = new RexOver(type, operator, exprs, window);
    RexNode result = over;

    // This should be correct but need time to go over test results.
    // Also want to look at combing with section below.
    if (nullWhenCountZero) {
      final RelDataType bigintType = getTypeFactory().createSqlType(
          SqlTypeName.BIGINT);
      result = makeCall(
          SqlStdOperatorTable.CASE,
          makeCall(
              SqlStdOperatorTable.GREATER_THAN,
              new RexOver(
                  bigintType,
                  SqlStdOperatorTable.COUNT,
                  exprs,
                  window),
              makeLiteral(
                  BigDecimal.ZERO,
                  bigintType,
                  SqlTypeName.DECIMAL)),
          ensureType(type, // SUM0 is non-nullable, thus need a cast
              new RexOver(typeFactory.createTypeWithNullability(type, false),
              operator, exprs, window),
              false),
          makeCast(type, constantNull()));
    }
    if (!allowPartial) {
      Util.permAssert(physical, "DISALLOW PARTIAL over RANGE");
      final RelDataType bigintType = getTypeFactory().createSqlType(
          SqlTypeName.BIGINT);
      // todo: read bound
      result =
          makeCall(
              SqlStdOperatorTable.CASE,
              makeCall(
                  SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
                  new RexOver(
                      bigintType,
                      SqlStdOperatorTable.COUNT,
                      ImmutableList.<RexNode>of(),
                      window),
                  makeLiteral(
                      BigDecimal.valueOf(2),
                      bigintType,
                      SqlTypeName.DECIMAL)),
              result,
              constantNull);
    }
    return result;
  }

  /**
   * Creates a window specification.
   *
   * @param partitionKeys Partition keys
   * @param orderKeys     Order keys
   * @param lowerBound    Lower bound
   * @param upperBound    Upper bound
   * @param isRows        Whether physical. True if row-based, false if
   *                      range-based
   * @return window specification
   */
  public RexWindow makeWindow(
      List<RexNode> partitionKeys,
      ImmutableList<RexFieldCollation> orderKeys,
      RexWindowBound lowerBound,
      RexWindowBound upperBound,
      boolean isRows) {
    return new RexWindow(
        partitionKeys,
        orderKeys,
        lowerBound,
        upperBound,
        isRows);
  }

  /**
   * Creates a constant for the SQL <code>NULL</code> value.
   */
  public RexLiteral constantNull() {
    return constantNull;
  }

  /**
   * Creates an expression referencing a correlation variable.
   *
   * @param type Type of variable
   * @param name Name of variable
   * @return Correlation variable
   */
  public RexNode makeCorrel(
      RelDataType type,
      String name) {
    return new RexCorrelVariable(name, type);
  }

  /**
   * Creates an invocation of the NEW operator.
   *
   * @param type  Type to be instantiated
   * @param exprs Arguments to NEW operator
   * @return Expression invoking NEW operator
   */
  public RexNode makeNewInvocation(
      RelDataType type,
      List<RexNode> exprs) {
    return new RexCall(
        type,
        SqlStdOperatorTable.NEW,
        exprs);
  }

  /**
   * Creates a call to the CAST operator, expanding if possible.
   *
   * @param type Type to cast to
   * @param exp  Expression being cast
   * @return Call to CAST operator
   */
  public RexNode makeCast(
      RelDataType type,
      RexNode exp) {
    final SqlTypeName sqlType = type.getSqlTypeName();
    if (exp instanceof RexLiteral) {
      RexLiteral literal = (RexLiteral) exp;
      Comparable value = literal.getValue();
      if (RexLiteral.valueMatchesType(value, sqlType, false)
          && (!(value instanceof NlsString)
          || (type.getPrecision()
          >= ((NlsString) value).getValue().length()))
          && (!(value instanceof ByteString)
          || (type.getPrecision()
          >= ((ByteString) value).length()))) {
        switch (literal.getTypeName()) {
        case CHAR:
          if (value instanceof NlsString) {
            value = ((NlsString) value).rtrim();
          }
          break;
        case TIMESTAMP:
        case TIME:
          final Calendar calendar = (Calendar) value;
          int scale = type.getScale();
          if (scale == RelDataType.SCALE_NOT_SPECIFIED) {
            scale = 0;
          }
          calendar.setTimeInMillis(
              SqlFunctions.round(
                  calendar.getTimeInMillis(),
                  SqlFunctions.powerX(10, 3 - scale)));
          break;
        case INTERVAL_DAY_TIME:
          BigDecimal value2 = (BigDecimal) value;
          final long multiplier =
              literal.getType().getIntervalQualifier().getStartUnit()
                  .multiplier;
          SqlTypeName typeName = type.getSqlTypeName();
          // Not all types are allowed for literals
          switch (typeName) {
          case INTEGER:
            typeName = SqlTypeName.BIGINT;
          }
          return makeLiteral(
              value2.divide(
                  BigDecimal.valueOf(multiplier), 0,
                  BigDecimal.ROUND_HALF_DOWN),
              type, typeName);
        }
        return makeLiteral(value, type, literal.getTypeName());
      }
    } else if (SqlTypeUtil.isInterval(type)
        && SqlTypeUtil.isExactNumeric(exp.getType())) {
      return makeCastExactToInterval(type, exp);
    } else if (SqlTypeUtil.isExactNumeric(type)
        && SqlTypeUtil.isInterval(exp.getType())) {
      return makeCastIntervalToExact(type, exp);
    } else if (sqlType == SqlTypeName.BOOLEAN
        && SqlTypeUtil.isExactNumeric(exp.getType())) {
      return makeCastExactToBoolean(type, exp);
    } else if (exp.getType().getSqlTypeName() == SqlTypeName.BOOLEAN
        && SqlTypeUtil.isExactNumeric(type)) {
      return makeCastBooleanToExact(type, exp);
    }
    return makeAbstractCast(type, exp);
  }

  private RexNode makeCastExactToBoolean(RelDataType toType, RexNode exp) {
    return makeCall(
        toType,
        SqlStdOperatorTable.NOT_EQUALS,
        ImmutableList.of(exp, makeZeroLiteral(exp.getType())));
  }

  private RexNode makeCastBooleanToExact(RelDataType toType, RexNode exp) {
    final RexNode casted = makeCall(
        SqlStdOperatorTable.CASE,
        exp,
        makeExactLiteral(BigDecimal.ONE, toType),
        makeZeroLiteral(toType));
    if (!exp.getType().isNullable()) {
      return casted;
    }
    return makeCall(
        toType,
        SqlStdOperatorTable.CASE,
        ImmutableList.<RexNode>of(
            makeCall(SqlStdOperatorTable.IS_NOT_NULL, exp),
            casted,
            makeNullLiteral(toType.getSqlTypeName())));
  }

  private RexNode makeCastIntervalToExact(RelDataType toType, RexNode exp) {
    IntervalSqlType intervalType = (IntervalSqlType) exp.getType();
    TimeUnit endUnit = intervalType.getIntervalQualifier().getEndUnit();
    if (endUnit == null) {
      endUnit = intervalType.getIntervalQualifier().getStartUnit();
    }
    int scale = 0;
    if (endUnit == TimeUnit.SECOND) {
      scale = Math.min(
          intervalType.getIntervalQualifier()
              .getFractionalSecondPrecision(), 3);
    }
    BigDecimal multiplier = BigDecimal.valueOf(endUnit.multiplier)
        .divide(BigDecimal.TEN.pow(scale));
    RexNode value = decodeIntervalOrDecimal(exp);
    if (multiplier.longValue() != 1) {
      value = makeCall(
          SqlStdOperatorTable.DIVIDE_INTEGER,
          value, makeBigintLiteral(multiplier));
    }
    if (scale > 0) {
      RelDataType decimalType =
          getTypeFactory().createSqlType(
              SqlTypeName.DECIMAL,
              scale + intervalType.getPrecision(),
              scale);
      value = encodeIntervalOrDecimal(value, decimalType, false);
    }
    return ensureType(toType, value, false);
  }

  private RexNode makeCastExactToInterval(RelDataType toType, RexNode exp) {
    IntervalSqlType intervalType = (IntervalSqlType) toType;
    TimeUnit endUnit = intervalType.getIntervalQualifier().getEndUnit();
    if (endUnit == null) {
      endUnit = intervalType.getIntervalQualifier().getStartUnit();
    }
    int scale = 0;
    if (endUnit == TimeUnit.SECOND) {
      scale = Math.min(
          intervalType.getIntervalQualifier()
              .getFractionalSecondPrecision(), 3);
    }
    BigDecimal multiplier = BigDecimal.valueOf(endUnit.multiplier)
        .divide(BigDecimal.TEN.pow(scale));
    RelDataType decimalType =
        getTypeFactory().createSqlType(
            SqlTypeName.DECIMAL,
            scale + intervalType.getPrecision(),
            scale);
    RexNode value = decodeIntervalOrDecimal(ensureType(decimalType, exp, true));
    if (multiplier.longValue() != 1) {
      value = makeCall(
          SqlStdOperatorTable.MULTIPLY,
          value, makeExactLiteral(multiplier));
    }
    return encodeIntervalOrDecimal(value, toType, false);
  }

  /**
   * Casts a decimal's integer representation to a decimal node. If the
   * expression is not the expected integer type, then it is casted first.
   *
   * <p>An overflow check may be requested to ensure the internal value
   * does not exceed the maximum value of the decimal type.
   *
   * @param value         integer representation of decimal
   * @param type          type integer will be reinterpreted as
   * @param checkOverflow indicates whether an overflow check is required
   *                      when reinterpreting this particular value as the
   *                      decimal type. A check usually not required for
   *                      arithmetic, but is often required for rounding and
   *                      explicit casts.
   * @return the integer reinterpreted as an opaque decimal type
   */
  public RexNode encodeIntervalOrDecimal(
      RexNode value,
      RelDataType type,
      boolean checkOverflow) {
    RelDataType bigintType =
        typeFactory.createSqlType(
            SqlTypeName.BIGINT);
    RexNode cast = ensureType(bigintType, value, true);
    return makeReinterpretCast(type, cast, makeLiteral(checkOverflow));
  }

  /**
   * Retrieves an interval or decimal node's integer representation
   *
   * @param node the interval or decimal value as an opaque type
   * @return an integer representation of the decimal value
   */
  public RexNode decodeIntervalOrDecimal(RexNode node) {
    assert SqlTypeUtil.isDecimal(node.getType())
        || SqlTypeUtil.isInterval(node.getType());
    RelDataType bigintType = typeFactory.createSqlType(SqlTypeName.BIGINT);
    return makeReinterpretCast(
        matchNullability(bigintType, node), node, makeLiteral(false));
  }

  /**
   * Creates a call to the CAST operator.
   *
   * @param type Type to cast to
   * @param exp  Expression being cast
   * @return Call to CAST operator
   */
  public RexNode makeAbstractCast(
      RelDataType type,
      RexNode exp) {
    return new RexCall(
        type,
        SqlStdOperatorTable.CAST,
        ImmutableList.of(exp));
  }

  /**
   * Makes a reinterpret cast.
   *
   * @param type          type returned by the cast
   * @param exp           expression to be casted
   * @param checkOverflow whether an overflow check is required
   * @return a RexCall with two operands and a special return type
   */
  public RexNode makeReinterpretCast(
      RelDataType type,
      RexNode exp,
      RexNode checkOverflow) {
    List<RexNode> args;
    if ((checkOverflow != null) && checkOverflow.isAlwaysTrue()) {
      args = ImmutableList.of(exp, checkOverflow);
    } else {
      args = ImmutableList.of(exp);
    }
    return new RexCall(
        type,
        SqlStdOperatorTable.REINTERPRET,
        args);
  }

  /**
   * Makes an expression which converts a value of type T to a value of type T
   * NOT NULL, or throws if the value is NULL. If the expression is already
   * NOT NULL, does nothing.
   */
  public RexNode makeNotNullCast(RexNode expr) {
    RelDataType type = expr.getType();
    if (!type.isNullable()) {
      return expr;
    }
    RelDataType typeNotNull =
        getTypeFactory().createTypeWithNullability(type, false);
    return new RexCall(
        typeNotNull,
        SqlStdOperatorTable.CAST,
        ImmutableList.of(expr));
  }

  /**
   * Creates a reference to all the fields in the row. That is, the whole row
   * as a single record object.
   *
   * @param input Input relational expression
   */
  public RexNode makeRangeReference(RelNode input) {
    return new RexRangeRef(input.getRowType(), 0);
  }

  /**
   * Creates a reference to all the fields in the row.
   *
   * <p>For example, if the input row has type <code>T{f0,f1,f2,f3,f4}</code>
   * then <code>makeRangeReference(T{f0,f1,f2,f3,f4}, S{f3,f4}, 3)</code> is
   * an expression which yields the last 2 fields.
   *
   * @param type     Type of the resulting range record.
   * @param offset   Index of first field.
   * @param nullable Whether the record is nullable.
   */
  public RexNode makeRangeReference(
      RelDataType type,
      int offset,
      boolean nullable) {
    if (nullable && !type.isNullable()) {
      type =
          typeFactory.createTypeWithNullability(
              type,
              nullable);
    }
    return new RexRangeRef(type, offset);
  }

  /**
   * Creates a reference to a given field of the input record.
   *
   * @param type Type of field
   * @param i    Ordinal of field
   * @return Reference to field
   */
  public RexInputRef makeInputRef(
      RelDataType type,
      int i) {
    type = SqlTypeUtil.addCharsetAndCollation(type, typeFactory);
    return new RexInputRef(i, type);
  }

  /**
   * Creates a reference to a given field of the input relational expression.
   *
   * @param input Input relational expression
   * @param i    Ordinal of field
   * @return Reference to field
   */
  public RexInputRef makeInputRef(RelNode input, int i) {
    return makeInputRef(input.getRowType().getFieldList().get(i).getType(), i);
  }

  /**
   * Creates a literal representing a flag.
   *
   * @param flag Flag value
   */
  public RexLiteral makeFlag(Enum flag) {
    assert flag != null;
    return makeLiteral(flag,
        typeFactory.createSqlType(SqlTypeName.SYMBOL),
        SqlTypeName.SYMBOL);
  }

  /**
   * Internal method to create a call to a literal. Code outside this package
   * should call one of the type-specific methods such as {@link
   * #makeDateLiteral(Calendar)}, {@link #makeLiteral(boolean)}, {@link
   * #makeLiteral(String)}.
   *
   * @param o        Value of literal, must be appropriate for the type
   * @param type     Type of literal
   * @param typeName SQL type of literal
   * @return Literal
   */
  protected RexLiteral makeLiteral(
      Comparable o,
      RelDataType type,
      SqlTypeName typeName) {
    // All literals except NULL have NOT NULL types.
    type = typeFactory.createTypeWithNullability(type, o == null);
    if (typeName == SqlTypeName.CHAR) {
      // Character literals must have a charset and collation. Populate
      // from the type if necessary.
      assert o instanceof NlsString;
      NlsString nlsString = (NlsString) o;
      if ((nlsString.getCollation() == null)
          || (nlsString.getCharset() == null)) {
        assert type.getSqlTypeName() == SqlTypeName.CHAR;
        assert type.getCharset().name() != null;
        assert type.getCollation() != null;
        o = new NlsString(
            nlsString.getValue(),
            type.getCharset().name(),
            type.getCollation());
      }
    }
    return new RexLiteral(o, type, typeName);
  }

  /**
   * Creates a boolean literal.
   */
  public RexLiteral makeLiteral(boolean b) {
    return b ? booleanTrue : booleanFalse;
  }

  /**
   * Creates a numeric literal.
   */
  public RexLiteral makeExactLiteral(BigDecimal bd) {
    RelDataType relType;
    int scale = bd.scale();
    long l = bd.unscaledValue().longValue();
    assert (scale >= 0) && (scale <= SqlTypeName.MAX_NUMERIC_SCALE);
    assert BigDecimal.valueOf(l, scale).equals(bd);
    if (scale == 0) {
      if ((l >= Integer.MIN_VALUE) && (l <= Integer.MAX_VALUE)) {
        relType = typeFactory.createSqlType(SqlTypeName.INTEGER);
      } else {
        relType = typeFactory.createSqlType(SqlTypeName.BIGINT);
      }
    } else {
      int precision = bd.unscaledValue().toString().length();
      relType =
          typeFactory.createSqlType(
              SqlTypeName.DECIMAL, scale, precision);
    }
    return makeExactLiteral(bd, relType);
  }

  /**
   * Creates a BIGINT literal.
   */
  public RexLiteral makeBigintLiteral(BigDecimal bd) {
    RelDataType bigintType =
        typeFactory.createSqlType(
            SqlTypeName.BIGINT);
    return makeLiteral(bd, bigintType, SqlTypeName.DECIMAL);
  }

  /**
   * Creates a numeric literal.
   */
  public RexLiteral makeExactLiteral(BigDecimal bd, RelDataType type) {
    return makeLiteral(bd, type, SqlTypeName.DECIMAL);
  }

  /**
   * Creates a byte array literal.
   */
  public RexLiteral makeBinaryLiteral(ByteString byteString) {
    return makeLiteral(
        byteString,
        typeFactory.createSqlType(SqlTypeName.BINARY, byteString.length()),
        SqlTypeName.BINARY);
  }

  /**
   * Creates a double-precision literal.
   */
  public RexLiteral makeApproxLiteral(BigDecimal bd) {
    // Validator should catch if underflow is allowed
    // If underflow is allowed, let underflow become zero
    if (bd.doubleValue() == 0) {
      bd = BigDecimal.ZERO;
    }
    return makeApproxLiteral(bd, typeFactory.createSqlType(SqlTypeName.DOUBLE));
  }

  /**
   * Creates an approximate numeric literal (double or float).
   *
   * @param bd   literal value
   * @param type approximate numeric type
   * @return new literal
   */
  public RexLiteral makeApproxLiteral(BigDecimal bd, RelDataType type) {
    assert SqlTypeFamily.APPROXIMATE_NUMERIC.getTypeNames().contains(
        type.getSqlTypeName());
    return makeLiteral(bd, type, SqlTypeName.DOUBLE);
  }

  /**
   * Creates a character string literal.
   */
  public RexLiteral makeLiteral(String s) {
    assert s != null;
    return makePreciseStringLiteral(s);
  }

  /**
   * Creates a character string literal with type CHAR and default charset and
   * collation.
   *
   * @param s String value
   * @return Character string literal
   */
  protected RexLiteral makePreciseStringLiteral(String s) {
    assert s != null;
    if (s.equals("")) {
      return charEmpty;
    } else {
      return makeLiteral(
          new NlsString(s, null, null),
          typeFactory.createSqlType(
              SqlTypeName.CHAR,
              s.length()),
          SqlTypeName.CHAR);
    }
  }

  /**
   * Ensures expression is interpreted as a specified type. The returned
   * expression may be wrapped with a cast.
   *
   * @param type             desired type
   * @param node             expression
   * @param matchNullability whether to correct nullability of specified
   *                         type to match the expression; this usually should
   *                         be true, except for explicit casts which can
   *                         override default nullability
   * @return a casted expression or the original expression
   */
  public RexNode ensureType(
      RelDataType type,
      RexNode node,
      boolean matchNullability) {
    RelDataType targetType = type;
    if (matchNullability) {
      targetType = matchNullability(type, node);
    }
    if (!node.getType().equals(targetType)) {
      return makeCast(targetType, node);
    }
    return node;
  }

  /**
   * Ensures that a type's nullability matches a value's nullability.
   */
  public RelDataType matchNullability(
      RelDataType type,
      RexNode value) {
    boolean typeNullability = type.isNullable();
    boolean valueNullability = value.getType().isNullable();
    if (typeNullability != valueNullability) {
      return getTypeFactory().createTypeWithNullability(
          type,
          valueNullability);
    }
    return type;
  }

  /**
   * Creates a character string literal from an {@link NlsString}.
   *
   * <p>If the string's charset and collation are not set, uses the system
   * defaults.
   */
  public RexLiteral makeCharLiteral(NlsString str) {
    assert str != null;
    RelDataType type = SqlUtil.createNlsStringType(typeFactory, str);
    return makeLiteral(str, type, SqlTypeName.CHAR);
  }

  /**
   * Creates a Date literal.
   */
  public RexLiteral makeDateLiteral(Calendar date) {
    assert date != null;
    return makeLiteral(
        date, typeFactory.createSqlType(SqlTypeName.DATE), SqlTypeName.DATE);
  }

  /**
   * Creates a Time literal.
   */
  public RexLiteral makeTimeLiteral(
      Calendar time,
      int precision) {
    assert time != null;
    return makeLiteral(
        time,
        typeFactory.createSqlType(SqlTypeName.TIME, precision),
        SqlTypeName.TIME);
  }

  /**
   * Creates a Timestamp literal.
   */
  public RexLiteral makeTimestampLiteral(
      Calendar timestamp,
      int precision) {
    assert timestamp != null;
    return makeLiteral(
        timestamp,
        typeFactory.createSqlType(SqlTypeName.TIMESTAMP, precision),
        SqlTypeName.TIMESTAMP);
  }

  /**
   * Creates an interval literal.
   */
  public RexLiteral makeIntervalLiteral(
      SqlIntervalQualifier intervalQualifier) {
    assert intervalQualifier != null;
    return makeFlag(intervalQualifier.foo());
  }

  /**
   * Creates an interval literal.
   */
  public RexLiteral makeIntervalLiteral(
      BigDecimal v,
      SqlIntervalQualifier intervalQualifier) {
    return makeLiteral(
        v,
        typeFactory.createSqlIntervalType(intervalQualifier),
        intervalQualifier.isYearMonth() ? SqlTypeName.INTERVAL_YEAR_MONTH
            : SqlTypeName.INTERVAL_DAY_TIME);
  }

  /**
   * Creates a reference to a dynamic parameter
   *
   * @param type  Type of dynamic parameter
   * @param index Index of dynamic parameter
   * @return Expression referencing dynamic parameter
   */
  public RexDynamicParam makeDynamicParam(
      RelDataType type,
      int index) {
    return new RexDynamicParam(type, index);
  }

  /**
   * Creates an expression corresponding to a null literal, cast to a specific
   * type and precision
   *
   * @param typeName  name of the type that the null will be cast to
   * @param precision precision of the type
   * @return created expression
   */
  public RexNode makeNullLiteral(SqlTypeName typeName, int precision) {
    RelDataType type =
        typeFactory.createTypeWithNullability(
            typeFactory.createSqlType(typeName, precision),
            true);
    return makeCast(type, constantNull());
  }

  /**
   * Creates a literal whose value is NULL, with a particular type.
   *
   * <p>The typing is necessary because RexNodes are strictly typed. For
   * example, in the Rex world the <code>NULL</code> parameter to <code>
   * SUBSTRING(NULL FROM 2 FOR 4)</code> must have a valid VARCHAR type so
   * that the result type can be determined.
   *
   * @param typeName Type to cast NULL to
   * @return NULL literal of given type
   */
  public RexNode makeNullLiteral(SqlTypeName typeName) {
    RelDataType type =
        typeFactory.createTypeWithNullability(
            typeFactory.createSqlType(typeName),
            true);
    return makeCast(type, constantNull());
  }

  /**
   * Creates a copy of an expression, which may have been created using a
   * different RexBuilder and/or {@link RelDataTypeFactory}, using this
   * RexBuilder.
   *
   * @param expr Expression
   * @return Copy of expression
   * @see RelDataTypeFactory#copyType(RelDataType)
   */
  public RexNode copy(RexNode expr) {
    return expr.accept(new RexCopier(this));
  }

  /**
   * Creates a literal of the default value for the given type.
   *
   * <p>This value is:</p>
   *
   * <ul>
   * <li>0 for numeric types;
   * <li>FALSE for BOOLEAN;
   * <li>The epoch for TIMESTAMP and DATE;
   * <li>Midnight for TIME;
   * <li>The empty string for string types (CHAR, BINARY, VARCHAR, VARBINARY).
   * </ul>
   *
   * @param type      Type
   * @return Simple literal, or cast simple literal
   */
  public RexNode makeZeroLiteral(RelDataType type) {
    return makeLiteral(zeroValue(type), type, false);
  }

  private static Comparable zeroValue(RelDataType type) {
    switch (type.getSqlTypeName()) {
    case CHAR:
      return new NlsString(Spaces.of(type.getPrecision()), null, null);
    case VARCHAR:
      return new NlsString("", null, null);
    case BINARY:
      return new ByteString(new byte[type.getPrecision()]);
    case VARBINARY:
      return ByteString.EMPTY;
    case TINYINT:
    case SMALLINT:
    case INTEGER:
    case BIGINT:
    case DECIMAL:
    case FLOAT:
    case REAL:
    case DOUBLE:
      return BigDecimal.ZERO;
    case BOOLEAN:
      return false;
    case TIME:
    case DATE:
    case TIMESTAMP:
      return DateTimeUtil.ZERO_CALENDAR;
    default:
      throw Util.unexpected(type.getSqlTypeName());
    }
  }

  /**
   * Creates a literal of a given type. The value is assumed to be
   * compatible with the type.
   *
   * @param value     Value
   * @param type      Type
   * @param allowCast Whether to allow a cast. If false, value is always a
   *                  {@link RexLiteral} but may not be the exact type
   * @return Simple literal, or cast simple literal
   */
  public RexNode makeLiteral(Object value, RelDataType type,
      boolean allowCast) {
    if (value == null) {
      return makeCast(type, constantNull);
    }
    if (type.isNullable()) {
      final RelDataType typeNotNull =
          typeFactory.createTypeWithNullability(type, false);
      RexNode literalNotNull = makeLiteral(value, typeNotNull, allowCast);
      return makeAbstractCast(type, literalNotNull);
    }
    value = clean(value, type);
    RexLiteral literal;
    final List<RexNode> operands;
    switch (type.getSqlTypeName()) {
    case CHAR:
      return makeCharLiteral(padRight((NlsString) value, type.getPrecision()));
    case VARCHAR:
      literal = makeCharLiteral((NlsString) value);
      if (allowCast) {
        return makeCast(type, literal);
      } else {
        return literal;
      }
    case BINARY:
      return makeBinaryLiteral(
          padRight((ByteString) value, type.getPrecision()));
    case VARBINARY:
      literal = makeBinaryLiteral((ByteString) value);
      if (allowCast) {
        return makeCast(type, literal);
      } else {
        return literal;
      }
    case TINYINT:
    case SMALLINT:
    case INTEGER:
    case BIGINT:
    case DECIMAL:
      return makeExactLiteral((BigDecimal) value, type);
    case FLOAT:
    case REAL:
    case DOUBLE:
      return makeApproxLiteral((BigDecimal) value, type);
    case BOOLEAN:
      return (Boolean) value ? booleanTrue : booleanFalse;
    case TIME:
      return makeTimeLiteral((Calendar) value, type.getPrecision());
    case DATE:
      return makeDateLiteral((Calendar) value);
    case TIMESTAMP:
      return makeTimestampLiteral((Calendar) value, type.getPrecision());
    case INTERVAL_YEAR_MONTH:
    case INTERVAL_DAY_TIME:
      return makeIntervalLiteral((BigDecimal) value,
          type.getIntervalQualifier());
    case MAP:
      final MapSqlType mapType = (MapSqlType) type;
      @SuppressWarnings("unchecked")
      final Map<Object, Object> map = (Map) value;
      operands = new ArrayList<RexNode>();
      for (Map.Entry<Object, Object> entry : map.entrySet()) {
        operands.add(
            makeLiteral(entry.getKey(), mapType.getKeyType(), allowCast));
        operands.add(
            makeLiteral(entry.getValue(), mapType.getValueType(), allowCast));
      }
      return makeCall(SqlStdOperatorTable.MAP_VALUE_CONSTRUCTOR, operands);
    case ARRAY:
      final ArraySqlType arrayType = (ArraySqlType) type;
      @SuppressWarnings("unchecked")
      final List<Object> listValue = (List) value;
      operands = new ArrayList<RexNode>();
      for (Object entry : listValue) {
        operands.add(
            makeLiteral(entry, arrayType.getComponentType(), allowCast));
      }
      return makeCall(SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR, operands);
    case ANY:
      return makeLiteral(value, guessType(value), allowCast);
    default:
      throw Util.unexpected(type.getSqlTypeName());
    }
  }

  /** Converts the type of a value to comply with
   * {@link org.eigenbase.rex.RexLiteral#valueMatchesType}. */
  private static Object clean(Object o, RelDataType type) {
    if (o == null) {
      return null;
    }
    final Calendar calendar;
    switch (type.getSqlTypeName()) {
    case TINYINT:
    case SMALLINT:
    case INTEGER:
    case BIGINT:
    case DECIMAL:
    case INTERVAL_YEAR_MONTH:
    case INTERVAL_DAY_TIME:
      if (o instanceof BigDecimal) {
        return o;
      }
      return new BigDecimal(((Number) o).longValue());
    case FLOAT:
    case REAL:
    case DOUBLE:
      if (o instanceof BigDecimal) {
        return o;
      }
      return new BigDecimal(((Number) o).doubleValue());
    case CHAR:
    case VARCHAR:
      if (o instanceof NlsString) {
        return o;
      }
      return new NlsString((String) o, type.getCharset().name(),
          type.getCollation());
    case TIME:
      if (o instanceof Calendar) {
        return o;
      }
      calendar = Calendar.getInstance(DateTimeUtil.GMT_ZONE);
      calendar.setTimeInMillis((Integer) o);
      return calendar;
    case DATE:
      if (o instanceof Calendar) {
        return o;
      }
      calendar = Calendar.getInstance(DateTimeUtil.GMT_ZONE);
      calendar.setTimeInMillis(0);
      calendar.add(Calendar.DAY_OF_YEAR, (Integer) o);
      return calendar;
    case TIMESTAMP:
      if (o instanceof Calendar) {
        return o;
      }
      calendar = Calendar.getInstance(DateTimeUtil.GMT_ZONE);
      calendar.setTimeInMillis((Long) o);
      return calendar;
    default:
      return o;
    }
  }

  private RelDataType guessType(Object value) {
    if (value == null) {
      return typeFactory.createSqlType(SqlTypeName.NULL);
    }
    if (value instanceof Float || value instanceof Double) {
      return typeFactory.createSqlType(SqlTypeName.DOUBLE);
    }
    if (value instanceof Number) {
      return typeFactory.createSqlType(SqlTypeName.BIGINT);
    }
    if (value instanceof Boolean) {
      return typeFactory.createSqlType(SqlTypeName.BOOLEAN);
    }
    if (value instanceof String) {
      return typeFactory.createSqlType(SqlTypeName.CHAR,
          ((String) value).length());
    }
    if (value instanceof ByteString) {
      return typeFactory.createSqlType(SqlTypeName.BINARY,
          ((ByteString) value).length());
    }
    throw new AssertionError("unknown type " + value.getClass());
  }

  /** Returns an {@link NlsString} with spaces to make it at least a given
   * length. */
  private static NlsString padRight(NlsString s, int length) {
    if (s.getValue().length() >= length) {
      return s;
    }
    return s.copy(padRight(s.getValue(), length));
  }

  /** Returns a string padded with spaces to make it at least a given length. */
  private static String padRight(String s, int length) {
    if (s.length() >= length) {
      return s;
    }
    return new StringBuilder()
        .append(s)
        .append(Spaces.MAX, s.length(), length)
        .toString();
  }

  /** Returns a byte-string padded with zero bytes to make it at least a given
   * length, */
  private static ByteString padRight(ByteString s, int length) {
    if (s.length() >= length) {
      return s;
    }
    return new ByteString(Arrays.copyOf(s.getBytes(), length));
  }
}

// End RexBuilder.java
TOP

Related Classes of org.eigenbase.rex.RexBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.