This is an automated email from the ASF dual-hosted git repository.

tanner pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 08b94e33ec [CALCITE-5747] Conflicting FLOOR return type between 
Calcite and BigQuery
08b94e33ec is described below

commit 08b94e33ec96e1c100faa466f1a9e701626c7e91
Author: Tanner Clary <[email protected]>
AuthorDate: Sat Jun 3 09:23:28 2023 -0700

    [CALCITE-5747] Conflicting FLOOR return type between Calcite and BigQuery
---
 core/src/main/codegen/templates/Parser.jj          |  5 ++-
 .../calcite/adapter/enumerable/RexImpTable.java    |  5 +++
 .../apache/calcite/sql/fun/SqlFloorFunction.java   | 38 +++++++++++++++++++++-
 .../calcite/sql/fun/SqlLibraryOperators.java       | 14 ++++++++
 .../calcite/sql/fun/SqlMonotonicUnaryFunction.java |  4 +--
 .../calcite/sql/fun/SqlStdOperatorTable.java       | 11 +++++++
 .../org/apache/calcite/sql/type/ReturnTypes.java   | 23 +++++++++++++
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java | 25 ++++++++++++++
 site/_docs/reference.md                            |  2 ++
 .../org/apache/calcite/test/SqlOperatorTest.java   | 32 ++++++++++++++++++
 10 files changed, 153 insertions(+), 6 deletions(-)

diff --git a/core/src/main/codegen/templates/Parser.jj 
b/core/src/main/codegen/templates/Parser.jj
index fcee00802b..56e44c06e8 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -114,6 +114,7 @@ import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.parser.SqlParserUtil;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.validate.SqlConformance;
+import org.apache.calcite.sql.validate.SqlConformanceEnum;
 import org.apache.calcite.util.Glossary;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.SourceStringReader;
@@ -7335,9 +7336,7 @@ SqlNode StandardFloorCeilOptions(Span s, boolean 
floorFlag) :
         }
     )?
     <RPAREN> {
-        SqlOperator op = floorFlag
-            ? SqlStdOperatorTable.FLOOR
-            : SqlStdOperatorTable.CEIL;
+        SqlOperator op = SqlStdOperatorTable.floorCeil(floorFlag, 
(SqlConformanceEnum) this.conformance);
         function =  op.createCall(s.end(this), args);
     }
     (
diff --git 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 3a27c361b1..ec7c628262 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -141,6 +141,7 @@ import static 
org.apache.calcite.sql.fun.SqlLibraryOperators.ASINH;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.ATANH;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.BOOL_AND;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.BOOL_OR;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.CEIL_BIG_QUERY;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.CHAR;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.CHR;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.COMPRESS;
@@ -163,6 +164,7 @@ import static 
org.apache.calcite.sql.fun.SqlLibraryOperators.ENDS_WITH;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.EXISTS_NODE;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.EXTRACT_VALUE;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.EXTRACT_XML;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.FLOOR_BIG_QUERY;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.FORMAT_DATE;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.FORMAT_DATETIME;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.FORMAT_TIME;
@@ -618,6 +620,9 @@ public class RexImpTable {
       map.put(TIMESTAMP_TRUNC, map.get(FLOOR));
       map.put(TIME_TRUNC, map.get(FLOOR));
       map.put(DATETIME_TRUNC, map.get(FLOOR));
+      // BigQuery FLOOR and CEIL should use same implementation as standard
+      map.put(CEIL_BIG_QUERY, map.get(CEIL));
+      map.put(FLOOR_BIG_QUERY, map.get(FLOOR));
 
       map.put(LAST_DAY,
           new LastDayImplementor("lastDay", BuiltInMethod.LAST_DAY));
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlFloorFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlFloorFunction.java
index 9a089890d7..77321bc0fe 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlFloorFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlFloorFunction.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.sql.fun;
 
+import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlFunction;
 import org.apache.calcite.sql.SqlFunctionCategory;
@@ -29,18 +30,29 @@ import org.apache.calcite.sql.SqlWriter;
 import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlOperandTypeChecker;
+import org.apache.calcite.sql.type.SqlOperandTypeInference;
+import org.apache.calcite.sql.type.SqlReturnTypeInference;
 import org.apache.calcite.sql.validate.SqlMonotonicity;
 import org.apache.calcite.sql.validate.SqlValidator;
 import org.apache.calcite.sql.validate.SqlValidatorScope;
 
 import com.google.common.base.Preconditions;
 
+import org.checkerframework.checker.nullness.qual.Nullable;
+
 /**
  * Definition of the "FLOOR" and "CEIL" built-in SQL functions.
  */
 public class SqlFloorFunction extends SqlMonotonicUnaryFunction {
   //~ Constructors -----------------------------------------------------------
-
+  private SqlFloorFunction(String name, SqlKind kind,
+      @Nullable SqlReturnTypeInference returnTypeInference,
+      @Nullable SqlOperandTypeInference operandTypeInference,
+      @Nullable SqlOperandTypeChecker operandTypeChecker,
+      SqlFunctionCategory funcType) {
+    super(name, kind, returnTypeInference, operandTypeInference, 
operandTypeChecker, funcType);
+  }
   public SqlFloorFunction(SqlKind kind) {
     super(kind.name(), kind, ReturnTypes.ARG0_OR_EXACT_NO_SCALE, null,
         OperandTypes.NUMERIC_OR_INTERVAL.or(
@@ -53,6 +65,16 @@ public class SqlFloorFunction extends 
SqlMonotonicUnaryFunction {
     Preconditions.checkArgument(kind == SqlKind.FLOOR || kind == SqlKind.CEIL);
   }
 
+  public SqlFloorFunction withName(String name) {
+    return new SqlFloorFunction(name, getKind(), getReturnTypeInference(),
+        getOperandTypeInference(), getOperandTypeChecker(), getFunctionType());
+  }
+
+  public SqlFloorFunction withReturnTypeInference(SqlReturnTypeInference 
returnTypeInference) {
+    return new SqlFloorFunction(getName(), getKind(), returnTypeInference,
+        getOperandTypeInference(), getOperandTypeChecker(), getFunctionType());
+  }
+
   //~ Methods ----------------------------------------------------------------
 
   @Override public SqlMonotonicity getMonotonicity(SqlOperatorBinding call) {
@@ -74,6 +96,16 @@ public class SqlFloorFunction extends 
SqlMonotonicUnaryFunction {
     writer.endFunCall(frame);
   }
 
+  @Override public RelDataType deriveType(SqlValidator validator,
+      SqlValidatorScope scope, SqlCall call) {
+    // To prevent operator rewriting by SqlFunction#deriveType.
+    for (SqlNode operand : call.getOperandList()) {
+      RelDataType nodeType = validator.deriveType(scope, operand);
+      validator.setValidatedNodeType(operand, nodeType);
+    }
+    return validateOperands(validator, scope, call);
+  }
+
   @Override public void validateCall(SqlCall call, SqlValidator validator,
       SqlValidatorScope scope, SqlValidatorScope operandScope) {
     super.validateCall(call, validator, scope, operandScope);
@@ -93,6 +125,10 @@ public class SqlFloorFunction extends 
SqlMonotonicUnaryFunction {
     }
   }
 
+  @Override public String getName() {
+    return kind.name();
+  }
+
   /**
    * Copies a {@link SqlCall}, replacing the time unit operand with the given
    * literal.
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
index ebcbf20e7f..fa9c4e4f85 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
@@ -405,6 +405,20 @@ public abstract class SqlLibraryOperators {
           ReturnTypes.LEAST_RESTRICTIVE.andThen(SqlTypeTransforms.TO_NULLABLE),
           OperandTypes.SAME_VARIADIC);
 
+  /** The "CEIL(value)" function. Identical to the standard <code>CEIL</code> 
function
+   * except the return type should be a double if the operand is an integer. */
+  @LibraryOperator(libraries = {BIG_QUERY})
+  public static final SqlFunction CEIL_BIG_QUERY = new 
SqlFloorFunction(SqlKind.CEIL)
+      .withName("CEIL_BIG_QUERY")
+      .withReturnTypeInference(ReturnTypes.ARG0_EXCEPT_INTEGER_NULLABLE);
+
+  /** The "FLOOR(value)" function. Identical to the stadnard 
<code>FLOOR</code> function
+   * except the return type should be a double if the operand is an integer. */
+  @LibraryOperator(libraries = {BIG_QUERY})
+  public static final SqlFunction FLOOR_BIG_QUERY = new 
SqlFloorFunction(SqlKind.FLOOR)
+      .withName("FLOOR_BIG_QUERY")
+      .withReturnTypeInference(ReturnTypes.ARG0_EXCEPT_INTEGER_NULLABLE);
+
   /**
    * The <code>TRANSLATE(<i>string_expr</i>, <i>search_chars</i>,
    * <i>replacement_chars</i>)</code> function returns <i>string_expr</i> with
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlMonotonicUnaryFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlMonotonicUnaryFunction.java
index bee1155203..22e9cd5859 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlMonotonicUnaryFunction.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlMonotonicUnaryFunction.java
@@ -37,9 +37,9 @@ public class SqlMonotonicUnaryFunction extends SqlFunction {
   protected SqlMonotonicUnaryFunction(
       String name,
       SqlKind kind,
-      SqlReturnTypeInference returnTypeInference,
+      @Nullable SqlReturnTypeInference returnTypeInference,
       @Nullable SqlOperandTypeInference operandTypeInference,
-      SqlOperandTypeChecker operandTypeChecker,
+      @Nullable SqlOperandTypeChecker operandTypeChecker,
       SqlFunctionCategory funcType) {
     super(
         name,
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index 1eca1e2549..c4c8afd3a8 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -68,6 +68,7 @@ import org.apache.calcite.sql.type.SqlTypeFamily;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.util.ReflectiveSqlOperatorTable;
 import org.apache.calcite.sql.validate.SqlConformance;
+import org.apache.calcite.sql.validate.SqlConformanceEnum;
 import org.apache.calcite.sql.validate.SqlModality;
 import org.apache.calcite.sql2rel.AuxiliaryConverter;
 import org.apache.calcite.util.Litmus;
@@ -2698,4 +2699,14 @@ public class SqlStdOperatorTable extends 
ReflectiveSqlOperatorTable {
     }
   }
 
+  /** Returns the operator for {@code FLOOR} and {@code CEIL} with given floor 
flag
+   * and library. */
+  public static SqlOperator floorCeil(boolean floor, SqlConformanceEnum 
conformance) {
+    switch (conformance) {
+    case BIG_QUERY:
+      return floor ? SqlLibraryOperators.FLOOR_BIG_QUERY : 
SqlLibraryOperators.CEIL_BIG_QUERY;
+    default:
+      return floor ? SqlStdOperatorTable.FLOOR : SqlStdOperatorTable.CEIL;
+    }
+  }
 }
diff --git a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java 
b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
index da77594623..e2dffc5e89 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
@@ -511,6 +511,29 @@ public abstract class ReturnTypes {
       opBinding -> opBinding.getTypeFactory().leastRestrictive(
           opBinding.collectOperandTypes());
 
+  /**
+   * Type-inference strategy that returns the type of the first operand, 
unless it
+   * is an integer type, in which case the return type is DOUBLE.
+   */
+  public static final SqlReturnTypeInference ARG0_EXCEPT_INTEGER = opBinding 
->  {
+    RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
+    SqlTypeName op = opBinding.getOperandType(0).getSqlTypeName();
+    if (SqlTypeName.INT_TYPES.contains(op)) {
+      return typeFactory.createTypeWithNullability(
+          typeFactory.createSqlType(SqlTypeName.DOUBLE), true);
+    } else {
+      return 
typeFactory.createTypeWithNullability(typeFactory.createSqlType(op), true);
+    }
+  };
+
+  /**
+   * Same as {@link #ARG0_EXCEPT_INTEGER} but returns with nullability if any 
of
+   * the operands is nullable by using
+   * {@link org.apache.calcite.sql.type.SqlTypeTransforms#TO_NULLABLE}.
+   */
+  public static final SqlReturnTypeInference ARG0_EXCEPT_INTEGER_NULLABLE =
+      ARG0_EXCEPT_INTEGER.andThen(SqlTypeTransforms.TO_NULLABLE);
+
   /**
    * Returns the same type as the multiset carries. The multiset type returned
    * is the least restrictive of the call's multiset operands
diff --git 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index 3eafff29d6..45997d6cfd 100644
--- 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -54,6 +54,7 @@ import org.apache.calcite.sql.SqlSelect;
 import org.apache.calcite.sql.SqlWriter;
 import org.apache.calcite.sql.SqlWriterConfig;
 import org.apache.calcite.sql.dialect.AnsiSqlDialect;
+import org.apache.calcite.sql.dialect.BigQuerySqlDialect;
 import org.apache.calcite.sql.dialect.CalciteSqlDialect;
 import org.apache.calcite.sql.dialect.HiveSqlDialect;
 import org.apache.calcite.sql.dialect.JethroDataSqlDialect;
@@ -490,6 +491,30 @@ class RelToSqlConverterTest {
         .withPresto().ok(expected);
   }
 
+  /** When ceiling/flooring an integer, BigQuery returns a double while 
Calcite and other dialects
+   * return an integer. Therefore, casts to integer types should be preserved 
for BigQuery. */
+  @Test void testBigQueryCeilPreservesCast() {
+    final String query = "SELECT TIMESTAMP_SECONDS(CAST(CEIL(CAST(3 AS 
BIGINT)) AS BIGINT)) "
+        + "as created_thing\n FROM `foodmart`.`product`";
+    final SqlParser.Config parserConfig =
+        BigQuerySqlDialect.DEFAULT.configureParser(SqlParser.config());
+    final Sql sql = fixture()
+        
.withBigQuery().withLibrary(SqlLibrary.BIG_QUERY).parserConfig(parserConfig);
+    sql.withSql(query).ok("SELECT TIMESTAMP_SECONDS(CAST(CEIL(3) AS INT64)) AS 
"
+        + "created_thing\nFROM foodmart.product");
+  }
+
+  @Test void testBigQueryFloorPreservesCast() {
+    final String query = "SELECT TIMESTAMP_SECONDS(CAST(FLOOR(CAST(3 AS 
BIGINT)) AS BIGINT)) "
+        + "as created_thing\n FROM `foodmart`.`product`";
+    final SqlParser.Config parserConfig =
+        BigQuerySqlDialect.DEFAULT.configureParser(SqlParser.config());
+    final Sql sql = fixture()
+        
.withBigQuery().withLibrary(SqlLibrary.BIG_QUERY).parserConfig(parserConfig);
+    sql.withSql(query).ok("SELECT TIMESTAMP_SECONDS(CAST(FLOOR(3) AS INT64)) 
AS "
+        + "created_thing\nFROM foodmart.product");
+  }
+
   @Test void testSelectLiteralAgg() {
     final Function<RelBuilder, RelNode> relFn = b -> b
         .scan("EMP")
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 351cbb8b23..f6aabaef0b 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2673,6 +2673,7 @@ BigQuery's type system uses confusingly different names 
for types and functions:
 | s | SORT_ARRAY(array [, ascendingOrder])           | Sorts the *array* in 
ascending or descending order according to the natural ordering of the array 
elements. The default order is ascending if *ascendingOrder* is not specified. 
Null elements will be placed at the beginning of the returned array in 
ascending order or at the end of the returned array in descending order
 | * | ASINH(numeric)                                 | Returns the inverse 
hyperbolic sine of *numeric*
 | * | ATANH(numeric)                                 | Returns the inverse 
hyperbolic tangent of *numeric*
+| b | CEIL(value)                                    | Similar to standard 
`CEIL(value)` except if *value* is an integer type, the return type is a double
 | m s | CHAR(integer)                                | Returns the character 
whose ASCII code is *integer* % 256, or null if *integer* &lt; 0
 | b o p | CHR(integer)                               | Returns the character 
whose UTF-8 code is *integer*
 | o | CONCAT(string, string)                         | Concatenates two 
strings, returns null only when both string arguments are null, otherwise 
treats null as empty string
@@ -2715,6 +2716,7 @@ BigQuery's type system uses confusingly different names 
for types and functions:
 | o | EXTRACT(xml, xpath, [, namespaces ])           | Returns the XML 
fragment of the element or elements matched by the XPath expression. The 
optional namespace value that specifies a default mapping or namespace mapping 
for prefixes, which is used when evaluating the XPath expression
 | o | EXISTSNODE(xml, xpath, [, namespaces ])        | Determines whether 
traversal of a XML document using a specified xpath results in any nodes. 
Returns 0 if no nodes remain after applying the XPath traversal on the document 
fragment of the element or elements matched by the XPath expression. Returns 1 
if any nodes remain. The optional namespace value that specifies a default 
mapping or namespace mapping for prefixes, which is used when evaluating the 
XPath expression.
 | m | EXTRACTVALUE(xml, xpathExpr))                  | Returns the text of the 
first text node which is a child of the element or elements matched by the 
XPath expression.
+| b | FLOOR(value)                                   | Similar to standard 
`FLOOR(value)` except if *value* is an integer type, the return type is a double
 | b | FORMAT_DATE(string, date)                      | Formats *date* 
according to the specified format *string*
 | b | FORMAT_DATETIME(string, timestamp)             | Formats *timestamp* 
according to the specified format *string*
 | b | FORMAT_TIME(string, time)                      | Formats *time* 
according to the specified format *string*
diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java 
b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
index 77d9734afe..4553073d89 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -8699,6 +8699,38 @@ public class SqlOperatorTest {
     f.checkNull("floor(cast(null as real))");
   }
 
+  @Test void testBigQueryCeilFunc() {
+    final SqlOperatorFixture f0 = fixture();
+    f0.checkType("ceil(cast(3 as tinyint))", "TINYINT NOT NULL");
+    final SqlOperatorFixture f = f0.setFor(SqlLibraryOperators.FLOOR_BIG_QUERY)
+        
.withLibrary(SqlLibrary.BIG_QUERY).withConformance(SqlConformanceEnum.BIG_QUERY);
+    f.checkScalarExact("ceil(cast(3 as tinyint))", "DOUBLE", "3.0");
+    f.checkScalarExact("ceil(cast(3 as smallint))", "DOUBLE", "3.0");
+    f.checkScalarExact("ceil(cast(3 as integer))", "DOUBLE", "3.0");
+    f.checkScalarExact("ceil(cast(3 as bigint))", "DOUBLE", "3.0");
+    f.checkScalarExact("ceil(cast(3.5 as double))", "DOUBLE", "4.0");
+    f.checkScalarExact("ceil(cast(3.45 as decimal))",
+        "DECIMAL(19, 0)", "4");
+    f.checkScalarExact("ceil(cast(3.45 as float))", "FLOAT", "4.0");
+    f.checkNull("ceil(cast(null as tinyint))");
+  }
+
+  @Test void testBigQueryFloorFunc() {
+    final SqlOperatorFixture f0 = fixture();
+    f0.checkType("floor(cast(3 as tinyint))", "TINYINT NOT NULL");
+    final SqlOperatorFixture f = f0.setFor(SqlLibraryOperators.FLOOR_BIG_QUERY)
+        
.withLibrary(SqlLibrary.BIG_QUERY).withConformance(SqlConformanceEnum.BIG_QUERY);
+    f.checkScalarExact("floor(cast(3 as tinyint))", "DOUBLE", "3.0");
+    f.checkScalarExact("floor(cast(3 as smallint))", "DOUBLE", "3.0");
+    f.checkScalarExact("floor(cast(3 as integer))", "DOUBLE", "3.0");
+    f.checkScalarExact("floor(cast(3 as bigint))", "DOUBLE", "3.0");
+    f.checkScalarExact("floor(cast(3.5 as double))", "DOUBLE", "3.0");
+    f.checkScalarExact("floor(cast(3.45 as decimal))",
+        "DECIMAL(19, 0)", "3");
+    f.checkScalarExact("floor(cast(3.45 as float))", "FLOAT", "3.0");
+    f.checkNull("floor(cast(null as tinyint))");
+  }
+
   @Test void testFloorFuncDateTime() {
     final SqlOperatorFixture f = fixture();
     f.enableTypeCoercion(false)

Reply via email to