RexBuilder.java

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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.apache.calcite.rex;

import org.apache.calcite.avatica.util.ByteString;
import org.apache.calcite.avatica.util.DateTimeUtils;
import org.apache.calcite.avatica.util.Spaces;
import org.apache.calcite.avatica.util.TimeUnit;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.AggregateCall;
import org.apache.calcite.rel.core.CorrelationId;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rel.type.RelDataTypeSystemImpl;
import org.apache.calcite.runtime.FlatLists;
import org.apache.calcite.runtime.SqlFunctions;
import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlCollation;
import org.apache.calcite.sql.SqlIntervalQualifier;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlSpecialOperator;
import org.apache.calcite.sql.SqlUtil;
import org.apache.calcite.sql.fun.SqlCountAggFunction;
import org.apache.calcite.sql.fun.SqlLibraryOperators;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.ArraySqlType;
import org.apache.calcite.sql.type.IntervalSqlType;
import org.apache.calcite.sql.type.MapSqlType;
import org.apache.calcite.sql.type.MultisetSqlType;
import org.apache.calcite.sql.type.SqlTypeFactoryImpl;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.calcite.util.DateString;
import org.apache.calcite.util.NlsString;
import org.apache.calcite.util.Sarg;
import org.apache.calcite.util.TimeString;
import org.apache.calcite.util.TimeWithTimeZoneString;
import org.apache.calcite.util.TimestampString;
import org.apache.calcite.util.TimestampWithTimeZoneString;
import org.apache.calcite.util.Util;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableRangeSet;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.PolyNull;
import org.locationtech.jts.geom.Geometry;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.function.IntPredicate;
import java.util.stream.Collectors;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Verify.verifyNotNull;
import static com.google.common.collect.ImmutableList.toImmutableList;

import static org.apache.calcite.linq4j.Nullness.castNonNull;

import static java.util.Objects.requireNonNull;

/**
 * Factory for row expressions.
 *
 * <p>Some common literal values (NULL, TRUE, FALSE, 0, 1, '') are cached.
 */
public class RexBuilder {

  /** Default RexBuilder. */
  public static final RexBuilder DEFAULT =
      new RexBuilder(new SqlTypeFactoryImpl(RelDataTypeSystemImpl.DEFAULT));

  /**
   * 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);

  /** The smallest valid {@code int} value, as a {@link BigDecimal}. */
  private static final BigDecimal INT_MIN =
      BigDecimal.valueOf(Integer.MIN_VALUE);

  /** The largest valid {@code int} value, as a {@link BigDecimal}. */
  private static final BigDecimal INT_MAX =
      BigDecimal.valueOf(Integer.MAX_VALUE);

  //~ 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
   */
  @SuppressWarnings("method.invocation.invalid")
  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);
  }

  /** Creates a list of {@link org.apache.calcite.rex.RexInputRef} expressions,
   * projecting the fields of a given record type. */
  public List<RexNode> identityProjects(final RelDataType rowType) {
    return Util.transform(rowType.getFieldList(),
        input -> new RexInputRef(input.getIndex(), input.getType()));
  }

  //~ 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.
   *
   * @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();
    if (type.getSqlTypeName() == SqlTypeName.VARIANT) {
      // VARIANT.field is rewritten as an VARIANT[field]
      return this.makeCall(SqlStdOperatorTable.ITEM, expr, this.makeLiteral(fieldName));
    }
    final RelDataTypeField field =
        type.getField(fieldName, caseSensitive, false);
    if (field == null) {
      throw new AssertionError("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 new AssertionError("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) {
    RelDataType fieldType = field.getType();
    if (expr instanceof RexRangeRef) {
      RexRangeRef range = (RexRangeRef) expr;
      if (field.getIndex() < 0) {
        return makeCall(
            fieldType,
            GET_OPERATOR,
            ImmutableList.of(
                expr,
                makeLiteral(field.getName())));
      }
      return new RexInputRef(
          range.getOffset() + field.getIndex(),
          fieldType);
    }

    if (expr.getType().isNullable()) {
      fieldType = typeFactory.enforceTypeWithNullability(fieldType, true);
    }
    return new RexFieldAccess(expr, field, fieldType);
  }

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

  /**
   * Creates a call with a list of arguments and a predetermined type.
   *
   * @param pos should be different from ZERO if the call can
   *            fail at runtime.
   */
  public RexNode makeCall(
      SqlParserPos pos,
      RelDataType returnType,
      SqlOperator op,
      List<RexNode> exprs) {
    return new RexCall(pos, returnType, op, exprs);
  }

  /**
   * Creates a call with an array of arguments.
   *
   * <p>If you already know the return type of the call, then
   * {@link #makeCall(SqlParserPos, RelDataType, SqlOperator, List)}
   * is preferred.
   */
  public RexNode makeCall(
      SqlOperator op,
      List<? extends RexNode> exprs) {
    return makeCall(SqlParserPos.ZERO, op, exprs);
  }

  /**
   * Creates a call with an array of arguments.
   *
   * @param pos should be different from ZERO if the call can
   *            fail at runtime.
   *
   * <p>If you already know the return type of the call, then
   * {@link #makeCall(SqlParserPos, RelDataType, SqlOperator, List)}
   * is preferred.
   */
  public RexNode makeCall(
      SqlParserPos pos, SqlOperator op,
      List<? extends RexNode> exprs) {
    final RelDataType type = deriveReturnType(op, exprs);
    return new RexCall(pos, type, op, exprs);
  }

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

  /**
   * Creates a call with a list of arguments.
   *
   * @param pos should be different from ZERO if the call can
   *            fail at runtime.
   *
   * <p>Equivalent to
   * <code>makeCall(pos, op, exprList.toArray(new RexNode[exprList.size()]))</code>.
   */
  public final RexNode makeCall(
      SqlParserPos pos,
      SqlOperator op,
      RexNode... exprs) {
    return makeCall(pos, op, ImmutableList.copyOf(exprs));
  }

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

  /**
   * 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(*).
   *
   * @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 isNullable Whether input field i is nullable
   *
   * @return Rex expression for the given aggregate call
   */
  public RexNode addAggCall(AggregateCall aggCall, int groupCount,
      List<AggregateCall> aggCalls,
      Map<AggregateCall, RexNode> aggCallMapping,
      IntPredicate isNullable) {
    if (aggCall.getAggregation() instanceof SqlCountAggFunction
        && !aggCall.isDistinct()) {
      final List<Integer> args = aggCall.getArgList();
      final List<Integer> nullableArgs = nullableArgs(args, isNullable);
      aggCall = aggCall.withArgList(nullableArgs);
    }
    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;
  }

  @Deprecated // to be removed before 2.0
  public RexNode addAggCall(final AggregateCall aggCall, int groupCount,
      List<AggregateCall> aggCalls,
      Map<AggregateCall, RexNode> aggCallMapping,
      final @Nullable List<RelDataType> aggArgTypes) {
    return addAggCall(aggCall, groupCount, aggCalls, aggCallMapping, i ->
        requireNonNull(aggArgTypes, "aggArgTypes")
            .get(aggCall.getArgList().indexOf(i)).isNullable());
  }

  /**
   * Creates a reference to an aggregate call, checking for repeated calls.
   */
  @Deprecated // to be removed before 2.0
  public RexNode addAggCall(AggregateCall aggCall, int groupCount,
      boolean indicator, List<AggregateCall> aggCalls,
      Map<AggregateCall, RexNode> aggCallMapping,
      final @Nullable List<RelDataType> aggArgTypes) {
    checkArgument(!indicator,
        "indicator is deprecated, use GROUPING function instead");
    return addAggCall(aggCall, groupCount, aggCalls,
        aggCallMapping, aggArgTypes);

  }

  private static List<Integer> nullableArgs(List<Integer> list0,
      IntPredicate isNullable) {
    return list0.stream()
        .filter(isNullable::test)
        .collect(toImmutableList());
  }

  @Deprecated // to be removed before 2.0
  public RexNode makeOver(RelDataType type, SqlAggFunction operator,
      List<RexNode> exprs, List<RexNode> partitionKeys,
      ImmutableList<RexFieldCollation> orderKeys,
      RexWindowBound lowerBound, RexWindowBound upperBound,
      boolean rows, boolean allowPartial, boolean nullWhenCountZero,
      boolean distinct) {
    return makeOver(type, operator, exprs, partitionKeys, orderKeys, lowerBound,
        upperBound, rows, allowPartial, nullWhenCountZero, distinct, false);
  }

  /**
   * 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 rows,
      boolean allowPartial,
      boolean nullWhenCountZero,
      boolean distinct,
      boolean ignoreNulls) {
    return makeOver(type, operator, exprs, partitionKeys, orderKeys, lowerBound, upperBound,
        RexWindowExclusion.EXCLUDE_NO_OTHER, rows, allowPartial, nullWhenCountZero, distinct,
        ignoreNulls);
  }

  /**
   * 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,
      RexWindowExclusion exclude,
      boolean rows,
      boolean allowPartial,
      boolean nullWhenCountZero,
      boolean distinct,
      boolean ignoreNulls) {
    final RexWindow window =
        makeWindow(
            partitionKeys,
            orderKeys,
            lowerBound,
            upperBound,
            rows,
            exclude);
    RexNode result =
        new RexOver(type, operator, exprs, window, distinct, ignoreNulls);

    // 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 =
          typeFactory.createSqlType(SqlTypeName.BIGINT);
      result =
          makeCall(SqlStdOperatorTable.CASE,
              makeCall(SqlStdOperatorTable.GREATER_THAN,
                  new RexOver(bigintType, SqlStdOperatorTable.COUNT, exprs,
                      window, distinct, ignoreNulls),
                  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, distinct, ignoreNulls),
                  false),
              makeNullLiteral(type));
    }
    if (!allowPartial) {
      checkArgument(rows, "DISALLOW PARTIAL over RANGE");
      final RelDataType bigintType =
          typeFactory.createSqlType(SqlTypeName.BIGINT);
      // todo: read bound
      result =
          makeCall(
              SqlStdOperatorTable.CASE,
              makeCall(
                  SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
                  new RexOver(
                      bigintType,
                      SqlStdOperatorTable.COUNT,
                      ImmutableList.of(),
                      window,
                      distinct,
                      ignoreNulls),
                  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 rows          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 rows) {
    return makeWindow(partitionKeys, orderKeys, lowerBound,
        upperBound, rows, RexWindowExclusion.EXCLUDE_NO_OTHER);
  }

  /**
   * Creates a window specification.
   *
   * @param partitionKeys Partition keys
   * @param orderKeys     Order keys
   * @param lowerBound    Lower bound
   * @param upperBound    Upper bound
   * @param rows          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 rows,
      RexWindowExclusion exclude) {
    if (orderKeys.isEmpty() && !rows) {
      lowerBound = RexWindowBounds.UNBOUNDED_PRECEDING;
      upperBound = RexWindowBounds.UNBOUNDED_FOLLOWING;
    }
    if (lowerBound.isUnboundedPreceding()
        && upperBound.isUnboundedFollowing()) {
      // RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
      //   is equivalent to
      // ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
      //   but we prefer "RANGE"
      rows = false;
    }
    return new RexWindow(
        partitionKeys,
        orderKeys,
        lowerBound,
        upperBound,
        rows,
        exclude);
  }

  /**
   * Creates a constant for the SQL <code>NULL</code> value.
   *
   * @deprecated Use {@link #makeNullLiteral(RelDataType)}, which produces a
   * NULL of the correct type
   */
  @Deprecated // to be removed before 2.0
  public RexLiteral constantNull() {
    return constantNull;
  }

  /**
   * Creates an expression referencing a correlation variable.
   *
   * @param id Name of variable
   * @param type Type of variable
   * @return Correlation variable
   */
  public RexNode makeCorrel(
      RelDataType type,
      CorrelationId id) {
    return new RexCorrelVariable(id, 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.
   *
   * @param type Type to cast to
   * @param exp  Expression being cast
   * @return Call to CAST operator
   */
  public RexNode makeCast(
      RelDataType type,
      RexNode exp) {
    return makeCast(type, exp, false, false, constantNull);
  }

  /**
   * Creates a call to the CAST operator.
   *
   * @param pos  Parser position of the cast.
   * @param type Type to cast to
   * @param exp  Expression being cast
   * @return Call to CAST operator
   */
  public RexNode makeCast(
      SqlParserPos pos,
      RelDataType type,
      RexNode exp) {
    return makeCast(pos, type, exp, false, false, constantNull);
  }

  @Deprecated // to be removed before 2.0
  public RexNode makeCast(
      RelDataType type,
      RexNode exp,
      boolean matchNullability) {
    return makeCast(type, exp, matchNullability, false, constantNull);
  }

  /**
   * Creates a call to the CAST operator, expanding if possible, and optionally
   * also preserving nullability, and optionally in safe mode.
   *
   * <p>Tries to expand the cast, and therefore the result may be something
   * other than a {@link RexCall} to the CAST operator, such as a
   * {@link RexLiteral}.
   *
   * @param pos  Parser position
   * @param type Type to cast to
   * @param exp  Expression being cast
   * @param matchNullability Whether to ensure the result has the same
   * nullability as {@code type}
   * @param safe Whether to return NULL if cast fails
   * @return Call to CAST operator
   */
  public RexNode makeCast(
      SqlParserPos pos,
      RelDataType type,
      RexNode exp,
      boolean matchNullability,
      boolean safe) {
    return makeCast(pos, type, exp, matchNullability, safe, constantNull);
  }

  /**
   * Creates a call to the CAST operator, expanding if possible, and optionally
   * also preserving nullability, and optionally in safe mode.
   *
   * <p>Tries to expand the cast, and therefore the result may be something
   * other than a {@link RexCall} to the CAST operator, such as a
   * {@link RexLiteral}.
   *
   * @param type Type to cast to
   * @param exp  Expression being cast
   * @param matchNullability Whether to ensure the result has the same
   * nullability as {@code type}
   * @param safe Whether to return NULL if cast fails
   * @return Call to CAST operator
   */
  public RexNode makeCast(
      RelDataType type,
      RexNode exp,
      boolean matchNullability,
      boolean safe) {
    return makeCast(type, exp, matchNullability, safe, constantNull);
  }


  /**
   * Creates a call to the CAST operator, expanding if possible, and optionally
   * also preserving nullability, and optionally in safe mode.
   *
   * <p>Tries to expand the cast, and therefore the result may be something
   * other than a {@link RexCall} to the CAST operator, such as a
   * {@link RexLiteral}.
   *
   * @param type Type to cast to
   * @param exp  Expression being cast
   * @param matchNullability Whether to ensure the result has the same
   * nullability as {@code type}
   * @param safe Whether to return NULL if cast fails
   * @param format Type Format to cast into
   * @return Call to CAST operator
   */
  public RexNode makeCast(
      RelDataType type,
      RexNode exp,
      boolean matchNullability,
      boolean safe,
      RexLiteral format) {
    return makeCast(SqlParserPos.ZERO, type, exp, matchNullability, safe, format);
  }

  /**
   * Creates a call to the CAST operator, expanding if possible, and optionally
   * also preserving nullability, and optionally in safe mode.
   *
   * <p>Tries to expand the cast, and therefore the result may be something
   * other than a {@link RexCall} to the CAST operator, such as a
   * {@link RexLiteral}.
   *
   * @param pos  Parser position
   * @param type Type to cast to
   * @param exp  Expression being cast
   * @param matchNullability Whether to ensure the result has the same
   * nullability as {@code type}
   * @param safe Whether to return NULL if cast fails
   * @param format Type Format to cast into
   * @return Call to CAST operator
   */
  public RexNode makeCast(
      SqlParserPos pos,
      RelDataType type,
      RexNode exp,
      boolean matchNullability,
      boolean safe,
      RexLiteral format) {
    final SqlTypeName sqlType = type.getSqlTypeName();
    if (exp instanceof RexLiteral) {
      RexLiteral literal = (RexLiteral) exp;
      Comparable value = literal.getValueAs(Comparable.class);
      SqlTypeName typeName = literal.getTypeName();

      // Allow casting boolean literals to integer types.
      if (exp.getType().getSqlTypeName() == SqlTypeName.BOOLEAN
          && SqlTypeUtil.isExactNumeric(type)) {
        return makeCastBooleanToExact(type, exp);
      }
      if (canRemoveCastFromLiteral(type, value, typeName)) {
        switch (typeName) {
        case INTERVAL_YEAR:
        case INTERVAL_YEAR_MONTH:
        case INTERVAL_MONTH:
        case INTERVAL_DAY:
        case INTERVAL_DAY_HOUR:
        case INTERVAL_DAY_MINUTE:
        case INTERVAL_DAY_SECOND:
        case INTERVAL_HOUR:
        case INTERVAL_HOUR_MINUTE:
        case INTERVAL_HOUR_SECOND:
        case INTERVAL_MINUTE:
        case INTERVAL_MINUTE_SECOND:
        case INTERVAL_SECOND:
          assert value instanceof BigDecimal;
          typeName = type.getSqlTypeName();
          switch (typeName) {
          case BIGINT:
          case INTEGER:
          case SMALLINT:
          case TINYINT:
          case DOUBLE:
          case FLOAT:
          case REAL:
          case DECIMAL:
            BigDecimal value2 = (BigDecimal) value;
            final BigDecimal multiplier =
                baseUnit(literal.getTypeName()).multiplier;
            final BigDecimal divider =
                literal.getTypeName().getEndUnit().multiplier;
            value = value2.multiply(multiplier)
                .divide(divider, 0, RoundingMode.HALF_DOWN);
            break;
          default:
            break;
          }

          // Not all types are allowed for literals
          switch (typeName) {
          case INTEGER:
            typeName = SqlTypeName.BIGINT;
            break;
          default:
            break;
          }
          break;
        default:
          break;
        }
        final RexLiteral literal2 =
            makeLiteral(value, type, typeName);
        if (type.isNullable()
            && !literal2.getType().isNullable()
            && matchNullability) {
          return makeAbstractCast(pos, type, literal2, safe, format);
        }
        return literal2;
      }
    } else if (SqlTypeUtil.isExactNumeric(type)
        && SqlTypeUtil.isInterval(exp.getType())) {
      return makeCastIntervalToExact(pos, 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(pos, type, exp, safe, format);
  }

  /** Returns the lowest granularity unit for the given unit.
   * YEAR and MONTH intervals are stored as months;
   * HOUR, MINUTE, SECOND intervals are stored as milliseconds. */
  protected static TimeUnit baseUnit(SqlTypeName unit) {
    if (unit.isYearMonth()) {
      return TimeUnit.MONTH;
    } else {
      return TimeUnit.MILLISECOND;
    }
  }

  boolean canRemoveCastFromLiteral(RelDataType toType,
      @SuppressWarnings("rawtypes") @Nullable Comparable value,
      SqlTypeName fromTypeName) {
    if (value == null) {
      return true;
    }
    final SqlTypeName sqlType = toType.getSqlTypeName();
    if (sqlType == SqlTypeName.MEASURE
        || sqlType == SqlTypeName.VARIANT
        || sqlType == SqlTypeName.UUID
        || SqlTypeName.UNSIGNED_TYPES.contains(sqlType)) {
      return false;
    }
    if (!RexLiteral.valueMatchesType(value, sqlType, false)) {
      return false;
    }
    if (toType.getSqlTypeName() != fromTypeName
        && (SqlTypeFamily.DATETIME.getTypeNames().contains(fromTypeName)
        || SqlTypeFamily.INTERVAL_DAY_TIME.getTypeNames().contains(fromTypeName)
        || SqlTypeFamily.INTERVAL_YEAR_MONTH.getTypeNames().contains(fromTypeName))) {
      return false;
    }
    if (value instanceof NlsString) {
      final int length = ((NlsString) value).getValue().length();
      switch (toType.getSqlTypeName()) {
      case CHAR:
        return SqlTypeUtil.comparePrecision(toType.getPrecision(), length) == 0;
      case VARCHAR:
        return SqlTypeUtil.comparePrecision(toType.getPrecision(), length) >= 0;
      default:
        throw new AssertionError(toType);
      }
    }
    if (value instanceof ByteString) {
      final int length = ((ByteString) value).length();
      switch (toType.getSqlTypeName()) {
      case BINARY:
        return SqlTypeUtil.comparePrecision(toType.getPrecision(), length) == 0;
      case VARBINARY:
        return SqlTypeUtil.comparePrecision(toType.getPrecision(), length) >= 0;
      default:
        throw new AssertionError(toType);
      }
    }

    if (toType.getSqlTypeName() == SqlTypeName.DECIMAL
        && fromTypeName.getFamily() == SqlTypeFamily.NUMERIC) {
      final BigDecimal decimalValue = (BigDecimal) value;
      return SqlTypeUtil.canBeRepresentedExactly(decimalValue, toType);
    }

    if (SqlTypeName.INT_TYPES.contains(sqlType)) {
      final BigDecimal decimalValue = (BigDecimal) value;
      try {
        // Will throw ArithmeticException if the value cannot be represented using a 'long'
        long l = decimalValue.longValueExact();
        switch (sqlType) {
        case TINYINT:
          return l >= Byte.MIN_VALUE && l <= Byte.MAX_VALUE;
        case SMALLINT:
          return l >= Short.MIN_VALUE && l <= Short.MAX_VALUE;
        case INTEGER:
          return l >= Integer.MIN_VALUE && l <= Integer.MAX_VALUE;
        case BIGINT:
        default:
          return true;
        }
      } catch (ArithmeticException ex) {
        return false;
      }
    }
    return true;
  }

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

  /** Some data types do not have literals; this creates an expression that
   * evaluates to a zero of the specified type.
   *
   * @param type A numeric type.
   * @return     An expression that evaluates to 0 of the specified type.
   */
  public RexNode makeZeroValue(RelDataType type) {
    if (SqlTypeUtil.hasLiterals(type)) {
      return makeZeroLiteral(type);
    } else {
      // This is e.g., an unsigned type
      RelDataType i = typeFactory.createSqlType(SqlTypeName.INTEGER);
      return makeAbstractCast(type, makeZeroValue(i), false);
    }
  }

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

  private RexNode makeCastIntervalToExact(SqlParserPos pos, RelDataType toType, RexNode exp) {
    final TimeUnit endUnit = exp.getType().getSqlTypeName().getEndUnit();
    final TimeUnit baseUnit = baseUnit(exp.getType().getSqlTypeName());
    final BigDecimal multiplier = baseUnit.multiplier;
    final BigDecimal divider = endUnit.multiplier;
    RexNode value =
        multiplyDivide(pos, decodeIntervalOrDecimal(pos, exp), multiplier, divider);
    return ensureType(pos, toType, value, false);
  }

  public RexNode multiplyDivide(RexNode e, BigDecimal multiplier,
      BigDecimal divider) {
    return multiplyDivide(SqlParserPos.ZERO, e, multiplier, divider);
  }

  public RexNode multiplyDivide(SqlParserPos pos, RexNode e, BigDecimal multiplier,
      BigDecimal divider) {
    assert multiplier.signum() > 0;
    assert divider.signum() > 0;
    switch (multiplier.compareTo(divider)) {
    case 0:
      return e;
    case 1:
      // E.g. multiplyDivide(e, 1000, 10) ==> e * 100
      return makeCall(pos, SqlStdOperatorTable.MULTIPLY, e,
          makeExactLiteral(
              multiplier.divide(divider, RoundingMode.UNNECESSARY)));
    case -1:
      // E.g. multiplyDivide(e, 10, 1000) ==> e / 100
      return makeCall(pos, SqlStdOperatorTable.DIVIDE_INTEGER, e,
          makeExactLiteral(
              divider.divide(multiplier, RoundingMode.UNNECESSARY)));
    default:
      throw new AssertionError(multiplier + "/" + divider);
    }
  }

  /**
   * 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) {
    return encodeIntervalOrDecimal(SqlParserPos.ZERO, value, type, checkOverflow);
  }

  public RexNode encodeIntervalOrDecimal(
      SqlParserPos pos,
      RexNode value,
      RelDataType type,
      boolean checkOverflow) {
    RelDataType bigintType =
        typeFactory.createSqlType(SqlTypeName.BIGINT);
    RexNode cast = ensureType(pos, bigintType, value, true);
    return makeReinterpretCast(pos, 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) {
    return decodeIntervalOrDecimal(SqlParserPos.ZERO, node);
  }

  public RexNode decodeIntervalOrDecimal(SqlParserPos pos, RexNode node) {
    assert SqlTypeUtil.isDecimal(node.getType())
        || SqlTypeUtil.isInterval(node.getType());
    RelDataType bigintType = typeFactory.createSqlType(SqlTypeName.BIGINT);
    return makeReinterpretCast(pos,
        matchNullability(bigintType, node), node, makeLiteral(false));
  }

  @Deprecated // to be removed before 2.0
  public RexNode makeAbstractCast(RelDataType type, RexNode exp) {
    return makeAbstractCast(type, exp, false);
  }

  /**
   * Creates a call to CAST or SAFE_CAST operator.
   *
   * @param type Type to cast to
   * @param exp  Expression being cast
   * @param safe Whether to return NULL if cast fails
   * @return Call to CAST operator
   */
  public RexNode makeAbstractCast(RelDataType type, RexNode exp, boolean safe) {
    return makeAbstractCast(SqlParserPos.ZERO, type, exp, safe);
  }

  /**
   * Creates a call to CAST or SAFE_CAST operator.
   *
   * @param pos  Parser position.
   * @param type Type to cast to
   * @param exp  Expression being cast
   * @param safe Whether to return NULL if cast fails
   * @return Call to CAST operator
   * Casts can fail at runtime, so we expect position information.
   */
  public RexNode makeAbstractCast(SqlParserPos pos, RelDataType type, RexNode exp, boolean safe) {
    final SqlOperator operator =
        safe ? SqlLibraryOperators.SAFE_CAST
            : SqlStdOperatorTable.CAST;
    return new RexCall(pos, type, operator, ImmutableList.of(exp));
  }

  /**
   * Creates a call to CAST or SAFE_CAST operator with a FORMAT clause.
   *
   * @param type Type to cast to
   * @param exp  Expression being cast
   * @param safe Whether to return NULL if cast fails
   * @param format Conversion format for target type
   * @return Call to CAST operator
   */
  public RexNode makeAbstractCast(RelDataType type, RexNode exp, boolean safe, RexLiteral format) {
    return makeAbstractCast(SqlParserPos.ZERO, type, exp, safe, format);
  }

  /**
     * Creates a call to CAST or SAFE_CAST operator with a FORMAT clause.
     *
     * @param pos  Parser position
     * @param type Type to cast to
     * @param exp  Expression being cast
     * @param safe Whether to return NULL if cast fails
     * @param format Conversion format for target type
     * @return Call to CAST operator
     */
  public RexNode makeAbstractCast(
      SqlParserPos pos, RelDataType type, RexNode exp, boolean safe, RexLiteral format) {
    final SqlOperator operator =
        safe ? SqlLibraryOperators.SAFE_CAST
            : SqlStdOperatorTable.CAST;
    if (format.isNull()) {
      return new RexCall(pos, type, operator, ImmutableList.of(exp));
    }
    return new RexCall(pos, type, operator, ImmutableList.of(exp, format));
  }

  /**
   * 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) {
    return makeReinterpretCast(SqlParserPos.ZERO, type, exp, checkOverflow);
  }

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

  /**
   * Makes a cast of an expression to nullable;
   * returns the expression unchanged if its type is already nullable.
   */
  public RexNode makeNullable(RexNode exp) {
    return makeNullable(exp, true);
  }

  /**
   * Makes a cast of an expression to NOT NULL;
   * returns the expression unchanged if its type already has NOT NULL.
   */
  public RexNode makeNotNull(RexNode exp) {
    return makeNullable(exp, false);
  }

  /**
   * Makes a cast of an expression to the required nullability; returns
   * the expression unchanged if its type already has the desired nullability.
   */
  public RexNode makeNullable(RexNode exp, boolean nullability) {
    final RelDataType type = exp.getType();
    if (type.isNullable() == nullability) {
      return exp;
    }
    final RelDataType type2 =
        typeFactory.createTypeWithNullability(type, nullability);
    return makeAbstractCast(SqlParserPos.ZERO, type2, exp, false);
  }

  /**
   * 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 RexRangeRef 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
   *
   * @see #identityProjects(RelDataType)
   */
  public RexInputRef makeInputRef(RelNode input, int i) {
    return makeInputRef(input.getRowType().getFieldList().get(i).getType(), i);
  }

  /**
   * Creates a reference to a given field of the pattern.
   *
   * @param alpha the pattern name
   * @param type Type of field
   * @param i    Ordinal of field
   * @return Reference to field of pattern
   */
  public RexPatternFieldRef makePatternFieldRef(String alpha, RelDataType type, int i) {
    type = SqlTypeUtil.addCharsetAndCollation(type, typeFactory);
    return new RexPatternFieldRef(alpha, i, type);
  }

  /**
   * Create a reference to local variable.
   *
   * @param type Type of variable
   * @param i    Ordinal of variable
   * @return  Reference to local variable
   */
  public RexLocalRef makeLocalRef(RelDataType type, int i) {
    type = SqlTypeUtil.addCharsetAndCollation(type, typeFactory);
    return new RexLocalRef(i, type);
  }

  /**
   * Creates a literal representing a flag.
   *
   * @param flag Flag value
   */
  public RexLiteral makeFlag(Enum flag) {
    return makeLiteral(requireNonNull(flag, "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(DateString)}, {@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(
      @Nullable Comparable o,
      RelDataType type,
      SqlTypeName typeName) {
    // All literals except NULL have NOT NULL types.
    type = typeFactory.createTypeWithNullability(type, o == null);
    int p;
    switch (typeName) {
    case 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
          || !Objects.equals(nlsString.getCharset(), type.getCharset())
          || !Objects.equals(nlsString.getCollation(), type.getCollation())) {
        assert type.getSqlTypeName() == SqlTypeName.CHAR
            || type.getSqlTypeName() == SqlTypeName.VARCHAR;
        Charset charset =
            requireNonNull(type.getCharset(), "type.getCharset()");
        final SqlCollation collation =
            requireNonNull(type.getCollation(), "type.getCollation()");
        o = new NlsString(nlsString.getValue(), charset.name(), collation);
      }
      break;
    case TIME:
    case TIME_WITH_LOCAL_TIME_ZONE:
      assert o instanceof TimeString;
      p = type.getPrecision();
      if (p == RelDataType.PRECISION_NOT_SPECIFIED) {
        p = 0;
      }
      o = ((TimeString) o).round(p);
      break;
    case TIME_TZ:
      assert o instanceof TimeWithTimeZoneString;
      p = type.getPrecision();
      if (p == RelDataType.PRECISION_NOT_SPECIFIED) {
        p = 0;
      }
      o = ((TimeWithTimeZoneString) o).round(p);
      break;
    case TIMESTAMP:
    case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
      assert o instanceof TimestampString;
      p = type.getPrecision();
      if (p == RelDataType.PRECISION_NOT_SPECIFIED) {
        p = 0;
      }
      o = ((TimestampString) o).round(p);
      break;
    case TIMESTAMP_TZ:
      assert o instanceof TimestampWithTimeZoneString;
      p = type.getPrecision();
      if (p == RelDataType.PRECISION_NOT_SPECIFIED) {
        p = 0;
      }
      o = ((TimestampWithTimeZoneString) o).round(p);
      break;
    case DECIMAL:
      if (o == null) {
        break;
      }
      assert o instanceof BigDecimal;
      if (type instanceof IntervalSqlType) {
        SqlIntervalQualifier qualifier = ((IntervalSqlType) type).getIntervalQualifier();
        o = ((BigDecimal) o).multiply(qualifier.getUnit().multiplier);
        typeName = type.getSqlTypeName();
      } else if (type.getScale() != RelDataType.SCALE_NOT_SPECIFIED) {
        o = ((BigDecimal) o).setScale(type.getScale(), typeFactory.getTypeSystem().roundingMode());
        if (type.getScale() < 0) {
          o = new BigDecimal(((BigDecimal) o).toPlainString());
        }
      }
      break;
    default:
      break;
    }
    if (typeName == SqlTypeName.DECIMAL
        && !SqlTypeUtil.isValidDecimalValue((BigDecimal) o, type)) {
      throw new IllegalArgumentException(
          "Cannot convert " + o + " to " + type  + " due to overflow");
    }
    return new RexLiteral(o, type, typeName);
  }

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

  public RexLiteral makeUuidLiteral(@Nullable UUID uuid) {
    return new RexLiteral(uuid, typeFactory.createSqlType(SqlTypeName.UUID), SqlTypeName.UUID);
  }

  /**
   * Creates a numeric literal.
   */
  @SuppressWarnings("deprecation") // [CALCITE-6598]
  public RexLiteral makeExactLiteral(BigDecimal bd) {
    RelDataType relType;
    int scale = bd.scale();
    assert scale >= 0;
    assert scale <= typeFactory.getTypeSystem().getMaxNumericScale() : scale;
    if (scale == 0) {
      if (bd.compareTo(INT_MIN) >= 0 && bd.compareTo(INT_MAX) <= 0) {
        relType = typeFactory.createSqlType(SqlTypeName.INTEGER);
      } else {
        relType = typeFactory.createSqlType(SqlTypeName.BIGINT);
      }
    } else {
      int precision = bd.unscaledValue().abs().toString().length();
      if (precision > scale) {
        // bd is greater than or equal to 1
        relType =
            typeFactory.createSqlType(SqlTypeName.DECIMAL, precision, scale);
      } else {
        // bd is less than 1
        relType =
            typeFactory.createSqlType(SqlTypeName.DECIMAL, scale + 1, scale);
      }
    }
    return makeExactLiteral(bd, relType);
  }

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

  /**
   * Creates a numeric literal.
   */
  public RexLiteral makeExactLiteral(@Nullable 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(@Nullable BigDecimal bd, RelDataType type) {
    assert SqlTypeFamily.APPROXIMATE_NUMERIC.getTypeNames().contains(
        type.getSqlTypeName());
    return makeLiteral(bd != null ? bd.doubleValue() : null, type, SqlTypeName.DOUBLE);
  }

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

  /**
   * Creates a search argument literal.
   */
  public RexLiteral makeSearchArgumentLiteral(Sarg s, RelDataType type) {
    return makeLiteral(requireNonNull(s, "s"), type, SqlTypeName.SARG);
  }

  /**
   * Creates a character string literal.
   */
  public RexLiteral makeLiteral(String s) {
    return makePreciseStringLiteral(requireNonNull(s, "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) {
    if (s.isEmpty()) {
      return charEmpty;
    }
    return makeCharLiteral(new NlsString(s, null, null));
  }

  /**
   * Creates a character string literal with type CHAR.
   *
   * @param value       String value in bytes
   * @param charsetName SQL-level charset name
   * @param collation   Sql collation
   * @return String     literal
   */
  protected RexLiteral makePreciseStringLiteral(ByteString value,
      String charsetName, SqlCollation collation) {
    return makeCharLiteral(new NlsString(value, charsetName, collation));
  }

  /**
   * 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) {
    return ensureType(SqlParserPos.ZERO, type, node, matchNullability);
  }

    /**
     * Ensures expression is interpreted as a specified type. The returned
     * expression may be wrapped with a cast.
     *
     * @param pos              parser position
     * @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(
      SqlParserPos pos,
      RelDataType type,
      RexNode node,
      boolean matchNullability) {
    RelDataType targetType = type;
    if (matchNullability) {
      targetType = matchNullability(type, node);
    }

    if (targetType.getSqlTypeName() == SqlTypeName.ANY
        && (!matchNullability
            || targetType.isNullable() == node.getType().isNullable())) {
      return node;
    }

    if (!node.getType().equals(targetType)) {
      return makeCast(pos, 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 typeFactory.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) {
    RelDataType type = SqlUtil.createNlsStringType(typeFactory, str);
    return makeLiteral(str, type, SqlTypeName.CHAR);
  }

  // CHECKSTYLE: IGNORE 1
  /** @deprecated Use {@link #makeDateLiteral(DateString)}. */
  @Deprecated // to be removed before 2.0
  public RexLiteral makeDateLiteral(Calendar calendar) {
    return makeDateLiteral(DateString.fromCalendarFields(calendar));
  }

  /**
   * Creates a Date literal.
   */
  public RexLiteral makeDateLiteral(DateString date) {
    return makeLiteral(
        requireNonNull(date, "date"),
        typeFactory.createSqlType(SqlTypeName.DATE), SqlTypeName.DATE);
  }

  // CHECKSTYLE: IGNORE 1
  /** @deprecated Use {@link #makeTimeLiteral(TimeString, int)}. */
  @Deprecated // to be removed before 2.0
  public RexLiteral makeTimeLiteral(Calendar calendar, int precision) {
    return makeTimeLiteral(TimeString.fromCalendarFields(calendar), precision);
  }

  /**
   * Creates a Time literal.
   */
  public RexLiteral makeTimeLiteral(TimeString time, int precision) {
    return makeLiteral(
        requireNonNull(time, "time"),
        typeFactory.createSqlType(SqlTypeName.TIME, precision),
        SqlTypeName.TIME);
  }

  /**
   * Creates a Time with local time-zone literal.
   */
  public RexLiteral makeTimeWithLocalTimeZoneLiteral(
      TimeString time,
      int precision) {
    return makeLiteral(
        requireNonNull(time, "time"),
        typeFactory.createSqlType(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE, precision),
        SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
  }

  /** Creates a Time with time-zone literal. */
  public RexLiteral makeTimeTzLiteral(
      TimeWithTimeZoneString time,
      int precision) {
    return makeLiteral(
        requireNonNull(time, "time"),
        typeFactory.createSqlType(SqlTypeName.TIME_TZ, precision),
        SqlTypeName.TIME_TZ);
  }

  // CHECKSTYLE: IGNORE 1
  /** @deprecated Use {@link #makeTimestampLiteral(TimestampString, int)}. */
  @Deprecated // to be removed before 2.0
  public RexLiteral makeTimestampLiteral(Calendar calendar, int precision) {
    return makeTimestampLiteral(TimestampString.fromCalendarFields(calendar),
        precision);
  }

  /**
   * Creates a Timestamp literal.
   */
  public RexLiteral makeTimestampLiteral(TimestampString timestamp,
      int precision) {
    return makeLiteral(
        requireNonNull(timestamp, "timestamp"),
        typeFactory.createSqlType(SqlTypeName.TIMESTAMP, precision),
        SqlTypeName.TIMESTAMP);
  }

  /**
   * Creates a Timestamp with local time-zone literal.
   */
  public RexLiteral makeTimestampWithLocalTimeZoneLiteral(
      TimestampString timestamp,
      int precision) {
    return makeLiteral(
        requireNonNull(timestamp, "timestamp"),
        typeFactory.createSqlType(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE, precision),
        SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
  }

  public RexLiteral makeTimestampTzLiteral(
      TimestampWithTimeZoneString timestamp,
      int precision) {
    return makeLiteral(
        requireNonNull(timestamp, "timestamp"),
        typeFactory.createSqlType(SqlTypeName.TIMESTAMP_TZ, precision),
        SqlTypeName.TIMESTAMP_TZ);
  }

  /**
   * Creates a literal representing an interval type, for example
   * {@code YEAR TO MONTH} or {@code DOW}.
   */
  public RexLiteral makeIntervalLiteral(
      SqlIntervalQualifier intervalQualifier) {
    verifyNotNull(intervalQualifier);
    if (intervalQualifier.timeFrameName != null) {
      return makePreciseStringLiteral(intervalQualifier.timeFrameName);
    }
    return makeFlag(intervalQualifier.timeUnitRange);
  }

  /**
   * Creates a literal representing an interval value, for example
   * {@code INTERVAL '3-7' YEAR TO MONTH}.
   */
  public RexLiteral makeIntervalLiteral(
      @Nullable BigDecimal v,
      SqlIntervalQualifier intervalQualifier) {
    return makeLiteral(
        v,
        typeFactory.createSqlIntervalType(intervalQualifier),
        intervalQualifier.typeName());
  }

  /**
   * 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 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 type Type to cast NULL to
   * @return NULL literal of given type
   */
  public RexLiteral makeNullLiteral(RelDataType type) {
    if (!type.isNullable()) {
      type = typeFactory.createTypeWithNullability(type, true);
    }
    return (RexLiteral) makeCast(type, constantNull);
  }

  // CHECKSTYLE: IGNORE 1
  /** @deprecated Use {@link #makeNullLiteral(RelDataType)} */
  @Deprecated // to be removed before 2.0
  public RexNode makeNullLiteral(SqlTypeName typeName, int precision) {
    return makeNullLiteral(typeFactory.createSqlType(typeName, precision));
  }

  // CHECKSTYLE: IGNORE 1
  /** @deprecated Use {@link #makeNullLiteral(RelDataType)} */
  @Deprecated // to be removed before 2.0
  public RexNode makeNullLiteral(SqlTypeName typeName) {
    return makeNullLiteral(typeFactory.createSqlType(typeName));
  }

  /** Creates a {@link RexNode} representation a SQL "arg IN (point, ...)"
   * expression.
   *
   * <p>If all of the expressions are literals, creates a call {@link Sarg}
   * literal, "SEARCH(arg, SARG([point0..point0], [point1..point1], ...)";
   * otherwise creates a disjunction, "arg = point0 OR arg = point1 OR ...".
   *
   * <p>If there is only a single expression, creates a plain equality
   * {@code arg = point0}. */
  public RexNode makeIn(RexNode arg, List<? extends RexNode> ranges) {
    if (areAssignable(arg, ranges)) {
      final Sarg sarg = toSarg(Comparable.class, ranges, RexUnknownAs.UNKNOWN);
      if (sarg != null) {
        if (sarg.isPoints() && sarg.pointCount == 1) {
          return makeCall(SqlStdOperatorTable.EQUALS, arg, ranges.get(0));
        }
        final List<RelDataType> types = ranges.stream()
            .map(RexNode::getType)
            .collect(Collectors.toList());
        RelDataType sargType =
            requireNonNull(typeFactory.leastRestrictive(types),
                () -> "Can't find leastRestrictive type for SARG among " + types);
        return makeCall(SqlStdOperatorTable.SEARCH, arg,
            makeSearchArgumentLiteral(sarg, sargType));
      }
    }
    return RexUtil.composeDisjunction(this, ranges.stream()
        .map(r -> makeCall(SqlStdOperatorTable.EQUALS, arg, r))
        .collect(toImmutableList()));
  }

  /** Returns whether and argument and bounds are have types that are
   * sufficiently compatible to be converted to a {@link Sarg}. */
  private static boolean areAssignable(RexNode arg, List<? extends RexNode> bounds) {
    for (RexNode bound : bounds) {
      if (!SqlTypeUtil.inSameFamily(arg.getType(), bound.getType())
          && !(arg.getType().isStruct() && bound.getType().isStruct())) {
        return false;
      }
    }
    return true;
  }

  /** Creates a {@link RexNode} representation a SQL
   * "arg BETWEEN lower AND upper" expression.
   *
   * <p>If the expressions are all literals of compatible type, creates a call
   * to {@link Sarg} literal, {@code SEARCH(arg, SARG([lower..upper])};
   * otherwise creates a disjunction, {@code arg >= lower AND arg <= upper}. */
  public RexNode makeBetween(RexNode arg, RexNode lower, RexNode upper) {
    final Comparable lowerValue = toComparable(Comparable.class, lower);
    final Comparable upperValue = toComparable(Comparable.class, upper);
    if (lowerValue != null
        && upperValue != null
        && areAssignable(arg, Arrays.asList(lower, upper))) {
      final Sarg sarg =
          Sarg.of(RexUnknownAs.UNKNOWN,
              ImmutableRangeSet.<Comparable>of(
                  Range.closed(lowerValue, upperValue)));
      List<RelDataType> types = ImmutableList.of(lower.getType(), upper.getType());
      RelDataType sargType =
          requireNonNull(typeFactory.leastRestrictive(types),
              () -> "Can't find leastRestrictive type for SARG among " + types);
      return makeCall(SqlStdOperatorTable.SEARCH, arg,
          makeSearchArgumentLiteral(sarg, sargType));
    }
    return makeCall(SqlStdOperatorTable.AND,
        makeCall(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, arg, lower),
        makeCall(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, arg, upper));
  }

  /** Converts a list of expressions to a search argument, or returns null if
   * not possible. */
  private static <C extends Comparable<C>> @Nullable Sarg<C> toSarg(Class<C> clazz,
      List<? extends RexNode> ranges, RexUnknownAs unknownAs) {
    if (ranges.isEmpty()) {
      // Cannot convert an empty list to a Sarg (by this interface, at least)
      // because we use the type of the first element.
      return null;
    }
    final RangeSet<C> rangeSet = TreeRangeSet.create();
    for (RexNode range : ranges) {
      final C value = toComparable(clazz, range);
      if (value == null) {
        return null;
      }
      rangeSet.add(Range.singleton(value));
    }
    return Sarg.of(unknownAs, rangeSet);
  }

  private static <C extends Comparable<C>> @Nullable C toComparable(Class<C> clazz,
      RexNode point) {
    switch (point.getKind()) {
    case LITERAL:
      final RexLiteral literal = (RexLiteral) point;
      return literal.getValueAs(clazz);

    case ROW:
    case ARRAY_VALUE_CONSTRUCTOR:
      final RexCall call = (RexCall) point;
      final ImmutableList.Builder<Comparable> b = ImmutableList.builder();
      for (RexNode operand : call.operands) {
        //noinspection unchecked
        final Comparable value = toComparable(Comparable.class, operand);
        if (value == null) {
          return null; // not a constant value
        }
        b.add(value);
      }
      return clazz.cast(FlatLists.ofComparable(b.build()));

    default:
      return null; // not a constant value
    }
  }

  /**
   * 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:
   *
   * <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
   */
  public RexLiteral makeZeroLiteral(RelDataType type) {
    return makeLiteral(zeroValue(type), type);
  }

  public RexNode makeZeroRexNode(RelDataType type) {
    switch (type.getSqlTypeName()) {
    case ARRAY:
      return makeCast(type,
          makeCall(type, SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR, ImmutableList.of()));
    case MULTISET:
      return makeCast(type,
          makeCall(type, SqlStdOperatorTable.MULTISET_VALUE, ImmutableList.of()));
    case MAP:
      return makeCast(type,
          makeCall(type, SqlStdOperatorTable.MAP_VALUE_CONSTRUCTOR, ImmutableList.of()));
    case ROW:
      List<RexNode> zeroFields = type.getFieldList().stream()
              .map(field -> makeZeroRexNode(field.getType()))
              .collect(Collectors.toList());
      return makeCall(type, SqlStdOperatorTable.ROW, zeroFields);
    default:
      return makeZeroValue(type);
    }
  }

  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 UTINYINT:
    case USMALLINT:
    case UINTEGER:
    case UBIGINT:
    case DECIMAL:
    case FLOAT:
    case REAL:
    case DOUBLE:
      return BigDecimal.ZERO;
    case BOOLEAN:
      return false;
    case TIME:
    case DATE:
    case TIMESTAMP:
      return DateTimeUtils.ZERO_CALENDAR;
    case TIME_WITH_LOCAL_TIME_ZONE:
      return new TimeString(0, 0, 0);
    case TIME_TZ:
      return new TimeWithTimeZoneString(0, 0, 0, "GMT+00");
    case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
      return new TimestampString(1, 1, 1, 0, 0, 0);
    case TIMESTAMP_TZ:
      return new TimestampWithTimeZoneString(1, 1, 1, 0, 0, 0, "GMT+00");
    default:
      throw Util.unexpected(type.getSqlTypeName());
    }
  }

  /**
   * Creates a literal of a given type, padding values of constant-width
   * types to match their type, not allowing casts.
   *
   * @param value     Value
   * @param type      Type
   * @return Simple literal
   */
  public RexLiteral makeLiteral(@Nullable Object value, RelDataType type) {
    return (RexLiteral) makeLiteral(value, type, false, false);
  }

  /**
   * Creates a literal of a given type, padding values of constant-width
   * types to match their 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(@Nullable Object value, RelDataType type,
      boolean allowCast) {
    return makeLiteral(value, type, allowCast, false);
  }

  /**
   * Creates a literal of a given type. The value is assumed to be
   * compatible with the type.
   *
   * <p>The {@code trim} parameter controls whether to trim values of
   * constant-width types such as {@code CHAR}. Consider a call to
   * {@code makeLiteral("foo ", CHAR(5)}, and note that the value is too short
   * for its type. If {@code trim} is true, the value is converted to "foo"
   * and the type to {@code CHAR(3)}; if {@code trim} is false, the value is
   * right-padded with spaces to {@code "foo  "}, to match the type
   * {@code CHAR(5)}.
   *
   * @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
   * @param trim      Whether to trim values and type to the shortest equivalent
   *                  value; for example whether to convert CHAR(4) 'foo '
   *                  to CHAR(3) 'foo'
   * @return Simple literal, or cast simple literal
   */
  public RexNode makeLiteral(@Nullable Object value, RelDataType type,
      boolean allowCast, boolean trim) {
    if (value == null) {
      return makeCast(type, constantNull);
    }
    if (type.isNullable()) {
      final RelDataType typeNotNull =
          typeFactory.createTypeWithNullability(type, false);
      if (allowCast) {
        RexNode literalNotNull = makeLiteral(value, typeNotNull, allowCast);
        return makeAbstractCast(type, literalNotNull, false);
      }
      type = typeNotNull;
    }
    value = clean(value, type);
    RexLiteral literal;
    final List<RexNode> operands;
    final SqlTypeName sqlTypeName = type.getSqlTypeName();
    switch (sqlTypeName) {
    case CHAR:
      final NlsString nlsString = (NlsString) value;
      if (trim) {
        return makeCharLiteral(nlsString.rtrim(type.getPrecision()));
      } else {
        return makeCharLiteral(padRight(nlsString, 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:
      if (value instanceof RexLiteral
          && ((RexLiteral) value).getTypeName() == SqlTypeName.SARG) {
        return (RexNode) value;
      }
      return makeExactLiteral((BigDecimal) value, type);
    case FLOAT:
    case REAL:
    case DOUBLE:
      if (value instanceof Double) {
        return makeApproxLiteral((Double) value, type);
      }
      return makeApproxLiteral(((BigDecimal) value).doubleValue(), type);
    case BOOLEAN:
      return (Boolean) value ? booleanTrue : booleanFalse;
    case TIME:
      return makeTimeLiteral((TimeString) value, type.getPrecision());
    case TIME_WITH_LOCAL_TIME_ZONE:
      return makeTimeWithLocalTimeZoneLiteral((TimeString) value, type.getPrecision());
    case TIME_TZ:
      return makeTimeTzLiteral((TimeWithTimeZoneString) value, type.getPrecision());
    case DATE:
      return makeDateLiteral((DateString) value);
    case TIMESTAMP:
      return makeTimestampLiteral((TimestampString) value, type.getPrecision());
    case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
      return makeTimestampWithLocalTimeZoneLiteral((TimestampString) value, type.getPrecision());
    case TIMESTAMP_TZ:
      return makeTimestampTzLiteral(
          (TimestampWithTimeZoneString) value, type.getPrecision());
    case INTERVAL_YEAR:
    case INTERVAL_YEAR_MONTH:
    case INTERVAL_MONTH:
    case INTERVAL_DAY:
    case INTERVAL_DAY_HOUR:
    case INTERVAL_DAY_MINUTE:
    case INTERVAL_DAY_SECOND:
    case INTERVAL_HOUR:
    case INTERVAL_HOUR_MINUTE:
    case INTERVAL_HOUR_SECOND:
    case INTERVAL_MINUTE:
    case INTERVAL_MINUTE_SECOND:
    case INTERVAL_SECOND:
      return makeIntervalLiteral((BigDecimal) value,
          castNonNull(type.getIntervalQualifier()));
    case SYMBOL:
      return makeFlag((Enum) value);
    case MAP:
      final MapSqlType mapType = (MapSqlType) type;
      @SuppressWarnings("unchecked")
      final Map<Object, Object> map = (Map) value;
      operands = new ArrayList<>();
      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<>();
      for (Object entry : listValue) {
        operands.add(
            makeLiteral(entry, arrayType.getComponentType(), allowCast));
      }
      return makeCall(SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR, operands);
    case MULTISET:
      final MultisetSqlType multisetType = (MultisetSqlType) type;
      operands = new ArrayList<>();
      for (Object entry : (List) value) {
        final RexNode e = entry instanceof RexLiteral
            ? (RexNode) entry
            : makeLiteral(entry, multisetType.getComponentType(), allowCast);
        operands.add(e);
      }
      if (allowCast) {
        return makeCall(SqlStdOperatorTable.MULTISET_VALUE, operands);
      } else {
        return new RexLiteral((Comparable) FlatLists.of(operands), type,
            sqlTypeName);
      }
    case ROW:
      operands = new ArrayList<>();
      for (int i = 0; i < type.getFieldList().size(); i++) {
        final RelDataTypeField relDataTypeField = type.getFieldList().get(i);
        final Object fieldValue = SqlFunctions.structAccess(value, i, relDataTypeField.getName());
        final RexNode e = fieldValue instanceof RexLiteral
            ? (RexNode) fieldValue
            : makeLiteral(fieldValue, relDataTypeField.getType(), allowCast);
        operands.add(e);
      }
      if (allowCast) {
        return makeCall(type, SqlStdOperatorTable.ROW, operands);
      } else {
        return new RexLiteral((Comparable) FlatLists.of(operands), type, sqlTypeName);
      }
    case GEOMETRY:
      return new RexLiteral((Comparable) value, guessType(value),
          SqlTypeName.GEOMETRY);
    case ANY:
      return makeLiteral(value, guessType(value), allowCast);
    case UUID:
      return makeUuidLiteral((UUID) value);
    default:
      throw new IllegalArgumentException(
          "Cannot create literal for type '" + sqlTypeName + "'");
    }
  }

  /**
   * Creates a lambda expression.
   *
   * @param expr expression of the lambda
   * @param parameters parameters of the lambda
   * @return RexNode representing the lambda
   */
  public RexNode makeLambdaCall(RexNode expr, List<RexLambdaRef> parameters) {
    return new RexLambda(parameters, expr);
  }

  /** Converts the type of a value to comply with
   * {@link org.apache.calcite.rex.RexLiteral#valueMatchesType}.
   *
   * <p>Returns null if and only if {@code o} is null. */
  private @PolyNull Object clean(@PolyNull Object o, RelDataType type) {
    if (o == null) {
      return o;
    }
    if (o instanceof Sarg) {
      return makeSearchArgumentLiteral((Sarg) o, type);
    }
    switch (type.getSqlTypeName()) {
    case TINYINT:
    case SMALLINT:
    case INTEGER:
    case BIGINT:
    case DECIMAL:
    case INTERVAL_YEAR:
    case INTERVAL_YEAR_MONTH:
    case INTERVAL_MONTH:
    case INTERVAL_DAY:
    case INTERVAL_DAY_HOUR:
    case INTERVAL_DAY_MINUTE:
    case INTERVAL_DAY_SECOND:
    case INTERVAL_HOUR:
    case INTERVAL_HOUR_MINUTE:
    case INTERVAL_HOUR_SECOND:
    case INTERVAL_MINUTE:
    case INTERVAL_MINUTE_SECOND:
    case INTERVAL_SECOND:
      if (o instanceof BigDecimal) {
        return o;
      }
      assert !(o instanceof Float || o instanceof Double)
          : String.format(Locale.ROOT,
              "%s is not compatible with %s, try to use makeExactLiteral",
              o.getClass().getCanonicalName(),
              type.getSqlTypeName());
      return new BigDecimal(((Number) o).longValue());
    case REAL:
      if (o instanceof BigDecimal) {
        return o;
      }
      // Float values are stored as Doubles
      if (o instanceof Float) {
        return ((Float) o).doubleValue();
      }
      if (o instanceof Double) {
        return o;
      }
      return new BigDecimal(((Number) o).doubleValue(), MathContext.DECIMAL32)
          .stripTrailingZeros();
    case FLOAT:
    case DOUBLE:
      if (o instanceof Double) {
        return o;
      }
      return ((Number) o).doubleValue();
    case CHAR:
    case VARCHAR:
      if (o instanceof NlsString) {
        return o;
      }
      final Charset charset = type.getCharset();
      if (charset == null) {
        throw new AssertionError(type + ".getCharset() must not be null");
      }
      if (o instanceof Character) {
        return new NlsString(o.toString(), charset.name(), type.getCollation());
      }
      return new NlsString((String) o, charset.name(), type.getCollation());
    case TIME:
      if (o instanceof TimeString) {
        return o;
      } else if (o instanceof Calendar) {
        if (!((Calendar) o).getTimeZone().equals(DateTimeUtils.UTC_ZONE)) {
          throw new AssertionError();
        }
        return TimeString.fromCalendarFields((Calendar) o);
      } else {
        return TimeString.fromMillisOfDay((Integer) o);
      }
    case TIME_TZ:
      if (o instanceof TimeWithTimeZoneString) {
        return o;
      } else if (o instanceof Calendar) {
        return TimeWithTimeZoneString.fromCalendarFields((Calendar) o);
      } else {
        throw new AssertionError("Value does not contain time zone");
      }
    case TIME_WITH_LOCAL_TIME_ZONE:
      if (o instanceof TimeString) {
        return o;
      } else {
        return TimeString.fromMillisOfDay((Integer) o);
      }
    case DATE:
      if (o instanceof DateString) {
        return o;
      } else if (o instanceof Calendar) {
        if (!((Calendar) o).getTimeZone().equals(DateTimeUtils.UTC_ZONE)) {
          throw new AssertionError();
        }
        return DateString.fromCalendarFields((Calendar) o);
      } else {
        return DateString.fromDaysSinceEpoch((Integer) o);
      }
    case TIMESTAMP:
      if (o instanceof TimestampString) {
        return o;
      } else if (o instanceof Calendar) {
        if (!((Calendar) o).getTimeZone().equals(DateTimeUtils.UTC_ZONE)) {
          throw new AssertionError();
        }
        return TimestampString.fromCalendarFields((Calendar) o);
      } else {
        return TimestampString.fromMillisSinceEpoch((Long) o);
      }
    case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
      if (o instanceof TimestampString) {
        return o;
      } else {
        return TimestampString.fromMillisSinceEpoch((Long) o);
      }
    case TIMESTAMP_TZ:
      if (o instanceof TimestampWithTimeZoneString) {
        return o;
      } else if (o instanceof Calendar) {
        return TimestampWithTimeZoneString.fromCalendarFields((Calendar) o);
      } else {
        throw new AssertionError("Value does not contain time zone");
      }
    default:
      return o;
    }
  }

  private RelDataType guessType(@Nullable 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());
    }
    if (value instanceof Geometry) {
      return typeFactory.createSqlType(SqlTypeName.GEOMETRY);
    }
    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));
  }
}