/*
 * 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.DataContext;
import org.apache.calcite.adapter.java.JavaTypeFactory;
import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
import org.apache.calcite.linq4j.QueryProvider;
import org.apache.calcite.plan.RelOptPredicateList;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeSystem;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.type.SqlTypeName;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;

import org.junit.jupiter.api.BeforeEach;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;

/**
 * This class provides helper methods to build rex expressions.
 */
public abstract class RexProgramBuilderBase {
  /**
   * Input variables for tests should come from a struct type, so
   * a struct is created where the first {@code MAX_FIELDS} are nullable,
   * and the next {@code MAX_FIELDS} are not nullable.
   */
  protected static final int MAX_FIELDS = 10;

  protected JavaTypeFactory typeFactory;
  protected RexBuilder rexBuilder;
  protected RexExecutor executor;
  protected RexSimplify simplify;

  protected RexLiteral trueLiteral;
  protected RexLiteral falseLiteral;
  protected RexLiteral nullBool;
  protected RexLiteral nullInt;
  protected RexLiteral nullSmallInt;
  protected RexLiteral nullVarchar;
  protected RexLiteral nullDecimal;

  private RelDataType nullableBool;
  private RelDataType nonNullableBool;

  private RelDataType nullableSmallInt;
  private RelDataType nonNullableSmallInt;

  private RelDataType nullableInt;
  private RelDataType nonNullableInt;

  private RelDataType nullableVarchar;
  private RelDataType nonNullableVarchar;

  private RelDataType nullableDecimal;
  private RelDataType nonNullableDecimal;

  // Note: JUnit 4 creates new instance for each test method,
  // so we initialize these structures on demand
  // It maps non-nullable type to struct of (10 nullable, 10 non-nullable) fields
  private Map<RelDataType, RexDynamicParam> dynamicParams;

  /**
   * Dummy data context for test.
   */
  private static class DummyTestDataContext implements DataContext {
    private final ImmutableMap<String, Object> map;

    DummyTestDataContext() {
      this.map =
          ImmutableMap.of(
              Variable.TIME_ZONE.camelName, TimeZone.getTimeZone("America/Los_Angeles"),
              Variable.CURRENT_TIMESTAMP.camelName, 1311120000000L);
    }

    public SchemaPlus getRootSchema() {
      return null;
    }

    public JavaTypeFactory getTypeFactory() {
      return null;
    }

    public QueryProvider getQueryProvider() {
      return null;
    }

    public Object get(String name) {
      return map.get(name);
    }
  }

  @BeforeEach public void setUp() {
    typeFactory = new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
    rexBuilder = new RexBuilder(typeFactory);
    executor =
        new RexExecutorImpl(new DummyTestDataContext());
    simplify =
        new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, executor)
            .withParanoid(true);
    trueLiteral = rexBuilder.makeLiteral(true);
    falseLiteral = rexBuilder.makeLiteral(false);

    nonNullableInt = typeFactory.createSqlType(SqlTypeName.INTEGER);
    nullableInt = typeFactory.createTypeWithNullability(nonNullableInt, true);
    nullInt = rexBuilder.makeNullLiteral(nullableInt);

    nonNullableSmallInt = typeFactory.createSqlType(SqlTypeName.SMALLINT);
    nullableSmallInt = typeFactory.createTypeWithNullability(nonNullableSmallInt, true);
    nullSmallInt = rexBuilder.makeNullLiteral(nullableSmallInt);

    nonNullableBool = typeFactory.createSqlType(SqlTypeName.BOOLEAN);
    nullableBool = typeFactory.createTypeWithNullability(nonNullableBool, true);
    nullBool = rexBuilder.makeNullLiteral(nullableBool);

    nonNullableVarchar = typeFactory.createSqlType(SqlTypeName.VARCHAR);
    nullableVarchar = typeFactory.createTypeWithNullability(nonNullableVarchar, true);
    nullVarchar = rexBuilder.makeNullLiteral(nullableVarchar);

    nonNullableDecimal = typeFactory.createSqlType(SqlTypeName.DECIMAL);
    nullableDecimal = typeFactory.createTypeWithNullability(nonNullableDecimal, true);
    nullDecimal = rexBuilder.makeNullLiteral(nullableDecimal);
  }

  private RexDynamicParam getDynamicParam(RelDataType type, String fieldNamePrefix) {
    if (dynamicParams == null) {
      dynamicParams = new HashMap<>();
    }
    return dynamicParams.computeIfAbsent(type, k -> {
      RelDataType nullableType = typeFactory.createTypeWithNullability(k, true);
      RelDataTypeFactory.Builder builder = typeFactory.builder();
      for (int i = 0; i < MAX_FIELDS; i++) {
        builder.add(fieldNamePrefix + i, nullableType);
      }
      String notNullPrefix = "notNull"
          + Character.toUpperCase(fieldNamePrefix.charAt(0))
          + fieldNamePrefix.substring(1);

      for (int i = 0; i < MAX_FIELDS; i++) {
        builder.add(notNullPrefix + i, k);
      }
      return rexBuilder.makeDynamicParam(builder.build(), 0);
    });
  }

  protected RexNode isNull(RexNode node) {
    return rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, node);
  }

  protected RexNode isUnknown(RexNode node) {
    return rexBuilder.makeCall(SqlStdOperatorTable.IS_UNKNOWN, node);
  }

  protected RexNode isNotNull(RexNode node) {
    return rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, node);
  }

  protected RexNode isFalse(RexNode node) {
    return rexBuilder.makeCall(SqlStdOperatorTable.IS_FALSE, node);
  }

  protected RexNode isNotFalse(RexNode node) {
    return rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_FALSE, node);
  }

  protected RexNode isTrue(RexNode node) {
    return rexBuilder.makeCall(SqlStdOperatorTable.IS_TRUE, node);
  }

  protected RexNode isNotTrue(RexNode node) {
    return rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_TRUE, node);
  }

  protected RexNode isDistinctFrom(RexNode a, RexNode b) {
    return rexBuilder.makeCall(SqlStdOperatorTable.IS_DISTINCT_FROM, a, b);
  }

  protected RexNode isNotDistinctFrom(RexNode a, RexNode b) {
    return rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_DISTINCT_FROM, a, b);
  }

  protected RexNode nullIf(RexNode node1, RexNode node2) {
    return rexBuilder.makeCall(SqlStdOperatorTable.NULLIF, node1, node2);
  }

  protected RexNode not(RexNode node) {
    return rexBuilder.makeCall(SqlStdOperatorTable.NOT, node);
  }

  protected RexNode unaryMinus(RexNode node) {
    return rexBuilder.makeCall(SqlStdOperatorTable.UNARY_MINUS, node);
  }

  protected RexNode unaryPlus(RexNode node) {
    return rexBuilder.makeCall(SqlStdOperatorTable.UNARY_PLUS, node);
  }

  protected RexNode and(RexNode... nodes) {
    return and(ImmutableList.copyOf(nodes));
  }

  protected RexNode and(Iterable<? extends RexNode> nodes) {
    // Does not flatten nested ANDs. We want test input to contain nested ANDs.
    return rexBuilder.makeCall(SqlStdOperatorTable.AND,
        ImmutableList.copyOf(nodes));
  }

  protected RexNode or(RexNode... nodes) {
    return or(ImmutableList.copyOf(nodes));
  }

  protected RexNode or(Iterable<? extends RexNode> nodes) {
    // Does not flatten nested ORs. We want test input to contain nested ORs.
    return rexBuilder.makeCall(SqlStdOperatorTable.OR,
        ImmutableList.copyOf(nodes));
  }

  protected RexNode case_(RexNode... nodes) {
    return case_(ImmutableList.copyOf(nodes));
  }

  protected RexNode case_(Iterable<? extends RexNode> nodes) {
    return rexBuilder.makeCall(SqlStdOperatorTable.CASE, ImmutableList.copyOf(nodes));
  }

  /**
   * Creates a call to the CAST operator.
   *
   * <p>This method enables to create {@code CAST(42 nullable int)} expressions.</p>
   *
   * @param e input node
   * @param type type to cast to
   * @return call to CAST operator
   */
  protected RexNode abstractCast(RexNode e, RelDataType type) {
    return rexBuilder.makeAbstractCast(type, e);
  }

  /**
   * Creates a call to the CAST operator, expanding if possible, and not
   * preserving nullability.
   *
   * <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}.</p>

   * @param e input node
   * @param type type to cast to
   * @return input node converted to given type
   */
  protected RexNode cast(RexNode e, RelDataType type) {
    return rexBuilder.makeCast(type, e);
  }

  protected RexNode eq(RexNode n1, RexNode n2) {
    return rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, n1, n2);
  }

  protected RexNode ne(RexNode n1, RexNode n2) {
    return rexBuilder.makeCall(SqlStdOperatorTable.NOT_EQUALS, n1, n2);
  }

  protected RexNode le(RexNode n1, RexNode n2) {
    return rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, n1, n2);
  }

  protected RexNode lt(RexNode n1, RexNode n2) {
    return rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, n1, n2);
  }

  protected RexNode ge(RexNode n1, RexNode n2) {
    return rexBuilder.makeCall(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, n1, n2);
  }

  protected RexNode gt(RexNode n1, RexNode n2) {
    return rexBuilder.makeCall(SqlStdOperatorTable.GREATER_THAN, n1, n2);
  }

  protected RexNode plus(RexNode n1, RexNode n2) {
    return rexBuilder.makeCall(SqlStdOperatorTable.PLUS, n1, n2);
  }

  protected RexNode mul(RexNode n1, RexNode n2) {
    return rexBuilder.makeCall(SqlStdOperatorTable.MULTIPLY, n1, n2);
  }

  protected RexNode coalesce(RexNode... nodes) {
    return rexBuilder.makeCall(SqlStdOperatorTable.COALESCE, nodes);
  }

  protected RexNode divInt(RexNode n1, RexNode n2) {
    return rexBuilder.makeCall(SqlStdOperatorTable.DIVIDE_INTEGER, n1, n2);
  }

  protected RexNode div(RexNode n1, RexNode n2) {
    return rexBuilder.makeCall(SqlStdOperatorTable.DIVIDE, n1, n2);
  }

  protected RexNode sub(RexNode n1, RexNode n2) {
    return rexBuilder.makeCall(SqlStdOperatorTable.MINUS, n1, n2);
  }

  protected RexNode add(RexNode n1, RexNode n2) {
    return rexBuilder.makeCall(SqlStdOperatorTable.PLUS, n1, n2);
  }

  protected RexNode item(RexNode inputRef, RexNode literal) {
    RexNode rexNode = rexBuilder.makeCall(
        SqlStdOperatorTable.ITEM,
        inputRef,
        literal);
    return rexNode;
  }

  /**
   * Generates {@code x IN (y, z)} expression when called as {@code in(x, y, z)}.
   * @param node left side of the IN expression
   * @param nodes nodes in the right side of IN expression
   * @return IN expression
   */
  protected RexNode in(RexNode node, RexNode... nodes) {
    return rexBuilder.makeCall(SqlStdOperatorTable.IN,
        ImmutableList.<RexNode>builder().add(node).add(nodes).build());
  }

  // Types
  protected RelDataType nullable(RelDataType type) {
    if (type.isNullable()) {
      return type;
    }
    return typeFactory.createTypeWithNullability(type, true);
  }

  protected RelDataType tVarchar() {
    return nonNullableVarchar;
  }

  protected RelDataType tVarchar(boolean nullable) {
    return nullable ? nullableVarchar : nonNullableVarchar;
  }

  protected RelDataType tVarchar(int precision) {
    return tVarchar(false, precision);
  }

  protected RelDataType tVarchar(boolean nullable, int precision) {
    RelDataType sqlType = typeFactory.createSqlType(SqlTypeName.VARCHAR, precision);
    if (nullable) {
      sqlType = typeFactory.createTypeWithNullability(sqlType, true);
    }
    return sqlType;
  }


  protected RelDataType tChar(int precision) {
    return tChar(false, precision);
  }

  protected RelDataType tChar(boolean nullable, int precision) {
    RelDataType sqlType = typeFactory.createSqlType(SqlTypeName.CHAR, precision);
    if (nullable) {
      sqlType = typeFactory.createTypeWithNullability(sqlType, true);
    }
    return sqlType;
  }

  protected RelDataType tBool() {
    return nonNullableBool;
  }

  protected RelDataType tBool(boolean nullable) {
    return nullable ? nullableBool : nonNullableBool;
  }

  protected RelDataType tInt() {
    return nonNullableInt;
  }

  protected RelDataType tInt(boolean nullable) {
    return nullable ? nullableInt : nonNullableInt;
  }

  protected RelDataType tSmallInt() {
    return nonNullableSmallInt;
  }

  protected RelDataType tSmallInt(boolean nullable) {
    return nullable ? nullableSmallInt : nonNullableSmallInt;
  }

  protected RelDataType tDecimal() {
    return nonNullableDecimal;
  }

  protected RelDataType tDecimal(boolean nullable) {
    return nullable ? nullableDecimal : nonNullableDecimal;
  }

  protected RelDataType tBigInt() {
    return tBigInt(false);
  }

  protected RelDataType tBigInt(boolean nullable) {
    RelDataType type = typeFactory.createSqlType(SqlTypeName.BIGINT);
    if (nullable) {
      type = nullable(type);
    }
    return type;
  }

  protected RelDataType tArray(RelDataType elemType) {
    return typeFactory.createArrayType(elemType, -1);
  }
  // Literals

  /**
   * Creates null literal with given type.
   * For instance: {@code null_(tInt())}
   *
   * @param type type of required null
   * @return null literal of a given type
   */
  protected RexLiteral null_(RelDataType type) {
    return rexBuilder.makeNullLiteral(nullable(type));
  }

  protected RexNode literal(boolean value) {
    return rexBuilder.makeLiteral(value, nonNullableBool, false);
  }

  protected RexNode literal(Boolean value) {
    if (value == null) {
      return rexBuilder.makeNullLiteral(nullableBool);
    }
    return literal(value.booleanValue());
  }

  protected RexNode literal(int value) {
    return rexBuilder.makeLiteral(value, nonNullableInt, false);
  }

  protected RexNode literal(BigDecimal value) {
    return rexBuilder.makeExactLiteral(value);
  }

  protected RexNode literal(BigDecimal value, RelDataType type) {
    return rexBuilder.makeExactLiteral(value, type);
  }

  protected RexNode literal(Integer value) {
    if (value == null) {
      return rexBuilder.makeNullLiteral(nullableInt);
    }
    return literal(value.intValue());
  }

  protected RexNode literal(String value) {
    if (value == null) {
      return rexBuilder.makeNullLiteral(nullableVarchar);
    }
    return rexBuilder.makeLiteral(value, nonNullableVarchar, false);
  }

  // Variables

  /**
   * Generates input ref with given type and index.
   *
   * <p>Prefer {@link #vBool()}, {@link #vInt()} and so on.
   *
   * <p>The problem with "input refs" is {@code input(tInt(), 0).toString()}
   * yields {@code $0}, so the type of the expression is not printed, and it
   * makes it hard to analyze the expressions.
   *
   * @param type desired type of the node
   * @param arg argument index (0-based)
   * @return input ref with given type and index
   */
  protected RexNode input(RelDataType type, int arg) {
    return rexBuilder.makeInputRef(type, arg);
  }

  private void assertArgValue(int arg) {
    assert arg >= 0 && arg < MAX_FIELDS
        : "arg should be in 0.." + (MAX_FIELDS - 1) + " range. Actual value was " + arg;
  }

  /**
   * Creates {@code nullable boolean variable} with index of 0.
   * If you need several distinct variables, use {@link #vBool(int)}
   * @return nullable boolean variable with index of 0
   */
  protected RexNode vBool() {
    return vBool(0);
  }

  /**
   * Creates {@code nullable boolean variable} with index of {@code arg} (0-based).
   * The resulting node would look like {@code ?0.bool3} if {@code arg} is {@code 3}.
   *
   * @param arg argument index (0-based)
   * @return nullable boolean variable with given index (0-based)
   */
  protected RexNode vBool(int arg) {
    return vParam("bool", arg, nonNullableBool);
  }

  /**
   * Creates {@code non-nullable boolean variable} with index of 0.
   * If you need several distinct variables, use {@link #vBoolNotNull(int)}.
   * The resulting node would look like {@code ?0.notNullBool0}
   *
   * @return non-nullable boolean variable with index of 0
   */
  protected RexNode vBoolNotNull() {
    return vBoolNotNull(0);
  }

  /**
   * Creates {@code non-nullable boolean variable} with index of {@code arg} (0-based).
   * The resulting node would look like {@code ?0.notNullBool3} if {@code arg} is {@code 3}.
   *
   * @param arg argument index (0-based)
   * @return non-nullable boolean variable with given index (0-based)
   */
  protected RexNode vBoolNotNull(int arg) {
    return vParamNotNull("bool", arg, nonNullableBool);
  }

  /**
   * Creates {@code nullable int variable} with index of 0.
   * If you need several distinct variables, use {@link #vInt(int)}.
   * The resulting node would look like {@code ?0.notNullInt0}
   *
   * @return nullable int variable with index of 0
   */
  protected RexNode vInt() {
    return vInt(0);
  }

  /**
   * Creates {@code nullable int variable} with index of {@code arg} (0-based).
   * The resulting node would look like {@code ?0.int3} if {@code arg} is {@code 3}.
   *
   * @param arg argument index (0-based)
   * @return nullable int variable with given index (0-based)
   */
  protected RexNode vInt(int arg) {
    return vParam("int", arg, nonNullableInt);
  }

  /**
   * Creates {@code non-nullable int variable} with index of 0.
   * If you need several distinct variables, use {@link #vIntNotNull(int)}.
   * The resulting node would look like {@code ?0.notNullInt0}
   *
   * @return non-nullable int variable with index of 0
   */
  protected RexNode vIntNotNull() {
    return vIntNotNull(0);
  }

  /**
   * Creates {@code non-nullable int variable} with index of {@code arg} (0-based).
   * The resulting node would look like {@code ?0.notNullInt3} if {@code arg} is {@code 3}.
   *
   * @param arg argument index (0-based)
   * @return non-nullable int variable with given index (0-based)
   */
  protected RexNode vIntNotNull(int arg) {
    return vParamNotNull("int", arg, nonNullableInt);
  }

  /**
   * Creates {@code nullable int variable} with index of 0.
   * If you need several distinct variables, use {@link #vSmallInt(int)}.
   * The resulting node would look like {@code ?0.notNullSmallInt0}
   *
   * @return nullable int variable with index of 0
   */
  protected RexNode vSmallInt() {
    return vSmallInt(0);
  }

  /**
   * Creates {@code nullable int variable} with index of {@code arg} (0-based).
   * The resulting node would look like {@code ?0.int3} if {@code arg} is {@code 3}.
   *
   * @param arg argument index (0-based)
   * @return nullable int variable with given index (0-based)
   */
  protected RexNode vSmallInt(int arg) {
    return vParam("smallint", arg, nonNullableSmallInt);
  }

  /**
   * Creates {@code non-nullable int variable} with index of 0.
   * If you need several distinct variables, use {@link #vSmallIntNotNull(int)}.
   * The resulting node would look like {@code ?0.notNullSmallInt0}
   *
   * @return non-nullable int variable with index of 0
   */
  protected RexNode vSmallIntNotNull() {
    return vSmallIntNotNull(0);
  }

  /**
   * Creates {@code non-nullable int variable} with index of {@code arg} (0-based).
   * The resulting node would look like {@code ?0.notNullSmallInt3} if {@code arg} is {@code 3}.
   *
   * @param arg argument index (0-based)
   * @return non-nullable int variable with given index (0-based)
   */
  protected RexNode vSmallIntNotNull(int arg) {
    return vParamNotNull("smallint", arg, nonNullableSmallInt);
  }

  /**
   * Creates {@code nullable varchar variable} with index of 0.
   * If you need several distinct variables, use {@link #vVarchar(int)}.
   * The resulting node would look like {@code ?0.notNullVarchar0}
   *
   * @return nullable varchar variable with index of 0
   */
  protected RexNode vVarchar() {
    return vVarchar(0);
  }

  /**
   * Creates {@code nullable varchar variable} with index of {@code arg} (0-based).
   * The resulting node would look like {@code ?0.varchar3} if {@code arg} is {@code 3}.
   *
   * @param arg argument index (0-based)
   * @return nullable varchar variable with given index (0-based)
   */
  protected RexNode vVarchar(int arg) {
    return vParam("varchar", arg, nonNullableVarchar);
  }

  /**
   * Creates {@code non-nullable varchar variable} with index of 0.
   * If you need several distinct variables, use {@link #vVarcharNotNull(int)}.
   * The resulting node would look like {@code ?0.notNullVarchar0}
   *
   * @return non-nullable varchar variable with index of 0
   */
  protected RexNode vVarcharNotNull() {
    return vVarcharNotNull(0);
  }

  /**
   * Creates {@code non-nullable varchar variable} with index of {@code arg} (0-based).
   * The resulting node would look like {@code ?0.notNullVarchar3} if {@code arg} is {@code 3}.
   *
   * @param arg argument index (0-based)
   * @return non-nullable varchar variable with given index (0-based)
   */
  protected RexNode vVarcharNotNull(int arg) {
    return vParamNotNull("varchar", arg, nonNullableVarchar);
  }

  /**
   * Creates {@code nullable decimal variable} with index of 0.
   * If you need several distinct variables, use {@link #vDecimal(int)}.
   * The resulting node would look like {@code ?0.notNullDecimal0}
   *
   * @return nullable decimal with index of 0
   */
  protected RexNode vDecimal() {
    return vDecimal(0);
  }

  /**
   * Creates {@code nullable decimal variable} with index of {@code arg} (0-based).
   * The resulting node would look like {@code ?0.decimal3} if {@code arg} is {@code 3}.
   *
   * @param arg argument index (0-based)
   * @return nullable decimal variable with given index (0-based)
   */
  protected RexNode vDecimal(int arg) {
    return vParam("decimal", arg, nonNullableDecimal);
  }

  /**
   * Creates {@code non-nullable decimal variable} with index of 0.
   * If you need several distinct variables, use {@link #vDecimalNotNull(int)}.
   * The resulting node would look like {@code ?0.notNullDecimal0}
   *
   * @return non-nullable decimal variable with index of 0
   */
  protected RexNode vDecimalNotNull() {
    return vDecimalNotNull(0);
  }

  /**
   * Creates {@code non-nullable decimal variable} with index of {@code arg} (0-based).
   * The resulting node would look like {@code ?0.notNullDecimal3} if {@code arg} is {@code 3}.
   *
   * @param arg argument index (0-based)
   * @return non-nullable decimal variable with given index (0-based)
   */
  protected RexNode vDecimalNotNull(int arg) {
    return vParamNotNull("decimal", arg, nonNullableDecimal);
  }

  /**
   * Creates {@code nullable variable} with given type and name of {@code arg} (0-based).
   * This enables cases when type is built dynamically.
   * For instance {@code vParam("char(2)_", tChar(2))} would generate a nullable
   * char(2) variable that would look like {@code ?0.char(2)_0}.
   * If you need multiple variables of that kind, use {@link #vParam(String, int, RelDataType)}.
   *
   * @param name variable name prefix
   * @return nullable variable of a given type
   */
  protected RexNode vParam(String name, RelDataType type) {
    return vParam(name, 0, type);
  }

  /**
   * Creates {@code nullable variable} with given type and name with index of {@code arg} (0-based).
   * This enables cases when type is built dynamically.
   * For instance {@code vParam("char(2)_", 3, tChar(2))} would generate a nullable
   * char(2) variable that would look like {@code ?0.char(2)_3}.
   *
   * @param name variable name prefix
   * @param arg argument index (0-based)
   * @return nullable varchar variable with given index (0-based)
   */
  protected RexNode vParam(String name, int arg, RelDataType type) {
    assertArgValue(arg);
    RelDataType nonNullableType = typeFactory.createTypeWithNullability(type, false);
    return rexBuilder.makeFieldAccess(getDynamicParam(nonNullableType, name), arg);
  }

  /**
   * Creates {@code non-nullable variable} with given type and name.
   * This enables cases when type is built dynamically.
   * For instance {@code vParam("char(2)_", tChar(2))} would generate a non-nullable
   * char(2) variable that would look like {@code ?0.char(2)_0}.
   * If you need multiple variables of that kind, use
   * {@link #vParamNotNull(String, int, RelDataType)}
   *
   * @param name variable name prefix
   * @return nullable variable of a given type
   */
  protected RexNode vParamNotNull(String name, RelDataType type) {
    return vParamNotNull(name, 0, type);
  }

  /**
   * Creates {@code non-nullable variable} with given type and name with index of
   * {@code arg} (0-based).
   * This enables cases when type is built dynamically.
   * For instance {@code vParam("char(2)_", 3, tChar(2))} would generate a non-nullable
   * char(2) variable that would look like {@code ?0.char(2)_3}.
   *
   * @param name variable name prefix
   * @param arg argument index (0-based)
   * @return nullable varchar variable with given index (0-based)
   */
  protected RexNode vParamNotNull(String name, int arg, RelDataType type) {
    assertArgValue(arg);
    RelDataType nonNullableType = typeFactory.createTypeWithNullability(type, false);
    return rexBuilder.makeFieldAccess(getDynamicParam(nonNullableType, name), arg + MAX_FIELDS);
  }
}