content: /*
   * 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.test;
  
  import org.apache.calcite.adapter.java.JavaTypeFactory;
  import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
  import org.apache.calcite.rel.type.RelDataType;
  import org.apache.calcite.rel.type.RelDataTypeSystem;
  import org.apache.calcite.rex.RexBuilder;
  import org.apache.calcite.rex.RexCallBinding;
  import org.apache.calcite.rex.RexNode;
  import org.apache.calcite.rex.RexUtil;
  import org.apache.calcite.sql.SqlCallBinding;
  import org.apache.calcite.sql.SqlCharStringLiteral;
  import org.apache.calcite.sql.SqlDataTypeSpec;
  import org.apache.calcite.sql.SqlLiteral;
  import org.apache.calcite.sql.SqlNode;
  import org.apache.calcite.sql.SqlOperatorBinding;
  import org.apache.calcite.sql.SqlUtil;
  import org.apache.calcite.sql.fun.SqlStdOperatorTable;
  import org.apache.calcite.sql.parser.SqlParserPos;
  import org.apache.calcite.sql.type.SqlTypeName;
  import org.apache.calcite.sql.type.SqlTypeUtil;
  
  import com.google.common.collect.Lists;
  
  import org.junit.jupiter.api.BeforeEach;
  import org.junit.jupiter.api.Test;
  
  import static org.hamcrest.CoreMatchers.is;
  import static org.hamcrest.MatcherAssert.assertThat;
  
  /**
   * Unit tests for {@link SqlOperatorBinding} and its sub-classes
   * {@link SqlCallBinding} and {@link RexCallBinding}.
   */
  class SqlOperatorBindingTest {
    private RexBuilder rexBuilder;
    private RelDataType integerDataType;
    private SqlDataTypeSpec integerType;
  
    @BeforeEach
    void setUp() {
      JavaTypeFactory typeFactory = new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
      integerDataType = typeFactory.createSqlType(SqlTypeName.INTEGER);
      integerType = SqlTypeUtil.convertTypeToSpec(integerDataType);
      rexBuilder = new RexBuilder(typeFactory);
    }
  
    /** Tests {@link org.apache.calcite.sql.SqlUtil#isLiteral(SqlNode, boolean)},
     * which was added to enhance Calcite's public API
     * <a href="https://issues.apache.org/jira/browse/CALCITE-1219">[CALCITE-1219]
     * Add a method to SqlOperatorBinding to determine whether operand is a
     * literal</a>.
     */
    @Test void testSqlNodeLiteral() {
      final SqlParserPos pos = SqlParserPos.ZERO;
      final SqlNode zeroLiteral = SqlLiteral.createExactNumeric("0", pos);
      final SqlNode oneLiteral = SqlLiteral.createExactNumeric("1", pos);
      final SqlNode nullLiteral = SqlLiteral.createNull(pos);
      final SqlCharStringLiteral aLiteral = SqlLiteral.createCharString("a", pos);
  
      final SqlNode castLiteral =
          SqlStdOperatorTable.CAST.createCall(pos, zeroLiteral, integerType);
      final SqlNode castCastLiteral =
          SqlStdOperatorTable.CAST.createCall(pos, castLiteral, integerType);
      final SqlNode mapLiteral =
          SqlStdOperatorTable.MAP_VALUE_CONSTRUCTOR.createCall(pos,
              aLiteral, oneLiteral);
      final SqlNode map2Literal =
          SqlStdOperatorTable.MAP_VALUE_CONSTRUCTOR.createCall(pos,
              aLiteral, castLiteral);
      final SqlNode arrayLiteral =
          SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR.createCall(pos,
              zeroLiteral, oneLiteral);
      final SqlNode defaultCall = SqlStdOperatorTable.DEFAULT.createCall(pos);
  
      // SqlLiteral is considered a literal
      assertThat(SqlUtil.isLiteral(zeroLiteral, false), is(true));
      assertThat(SqlUtil.isLiteral(zeroLiteral, true), is(true));
      // NULL literal is considered a literal
      assertThat(SqlUtil.isLiteral(nullLiteral, false), is(true));
      assertThat(SqlUtil.isLiteral(nullLiteral, true), is(true));
      // CAST(SqlLiteral as type) is considered a literal, iff allowCast
      assertThat(SqlUtil.isLiteral(castLiteral, false), is(false));
      assertThat(SqlUtil.isLiteral(castLiteral, true), is(true));
      // CAST(CAST(SqlLiteral as type) as type) is considered a literal,
      // iff allowCast
      assertThat(SqlUtil.isLiteral(castCastLiteral, false), is(false));
      assertThat(SqlUtil.isLiteral(castCastLiteral, true), is(true));
      // MAP['a', 1] and MAP['a', CAST(0 AS INTEGER)] are considered literals,
      // iff allowCast
      assertThat(SqlUtil.isLiteral(mapLiteral, false), is(false));
      assertThat(SqlUtil.isLiteral(mapLiteral, true), is(true));
      assertThat(SqlUtil.isLiteral(map2Literal, false), is(false));
      assertThat(SqlUtil.isLiteral(map2Literal, true), is(true));
      // ARRAY[0, 1] is considered a literal, iff allowCast
      assertThat(SqlUtil.isLiteral(arrayLiteral, false), is(false));
      assertThat(SqlUtil.isLiteral(arrayLiteral, true), is(true));
      // DEFAULT is considered a literal, iff allowCast
      assertThat(SqlUtil.isLiteral(defaultCall, false), is(false));
      assertThat(SqlUtil.isLiteral(defaultCall, true), is(true));
    }
  
    /** Tests {@link org.apache.calcite.rex.RexUtil#isLiteral(RexNode, boolean)},
     * which was added to enhance Calcite's public API
     * <a href="https://issues.apache.org/jira/browse/CALCITE-1219">[CALCITE-1219]
     * Add a method to SqlOperatorBinding to determine whether operand is a
     * literal</a>.
     */
    @Test void testRexNodeLiteral() {
      final RexNode literal =
          rexBuilder.makeZeroLiteral(integerDataType);
  
      final RexNode castLiteral =
          rexBuilder.makeCall(integerDataType,
              SqlStdOperatorTable.CAST,
              Lists.newArrayList(literal));
  
      final RexNode castCastLiteral =
          rexBuilder.makeCall(integerDataType,
              SqlStdOperatorTable.CAST,
              Lists.newArrayList(castLiteral));
  
      // RexLiteral is considered a literal
      assertThat(RexUtil.isLiteral(literal, true), is(true));
      // CAST(RexLiteral as type) is considered a literal
      assertThat(RexUtil.isLiteral(castLiteral, true), is(true));
      // CAST(CAST(RexLiteral as type) as type) is NOT considered a literal
      assertThat(RexUtil.isLiteral(castCastLiteral, true), is(false));
    }
  }
  