IMPALA-3437: DECIMAL_V2: avoid implicit decimal->double conversion This changes the behaviour of applying an arithmetic operator to constant DECIMAL and non-DECIMAL arguments. In DECIMAL_V1, this caused an implicit conversion to floating point, which caused users a lot of confusion in some cases. In DECIMAL_V2 the typing rules are simplified: constant decimals are treated the same as any other decimals.
Testing: Added some expression tests for different arithmetic operators and binary predicates (the two Expr subclasses that call convertNumericLiteralsFromDecimal()). Extended analyzer tests to test DECIMAL_V2 behaviour. Added many additional test for various combinations of literals and non-literals to get better coverage of existing and new behaviour. Ran core tests. Change-Id: Ie419a75784eec2294947103e6e1465dfadfc29da Reviewed-on: http://gerrit.cloudera.org:8080/7916 Reviewed-by: Tim Armstrong <[email protected]> Tested-by: Impala Public Jenkins Project: http://git-wip-us.apache.org/repos/asf/incubator-impala/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-impala/commit/0a54cb5e Tree: http://git-wip-us.apache.org/repos/asf/incubator-impala/tree/0a54cb5e Diff: http://git-wip-us.apache.org/repos/asf/incubator-impala/diff/0a54cb5e Branch: refs/heads/master Commit: 0a54cb5ec0f5d1646e5fa3d211dd2b765242816e Parents: 032dee2 Author: Tim Armstrong <[email protected]> Authored: Wed Aug 30 16:41:56 2017 -0700 Committer: Impala Public Jenkins <[email protected]> Committed: Fri Sep 15 02:30:29 2017 +0000 ---------------------------------------------------------------------- be/src/exprs/expr-test.cc | 41 +++ .../java/org/apache/impala/analysis/Expr.java | 17 +- .../impala/analysis/AnalyzeExprsTest.java | 254 +++++++++++++++---- 3 files changed, 256 insertions(+), 56 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/0a54cb5e/be/src/exprs/expr-test.cc ---------------------------------------------------------------------- diff --git a/be/src/exprs/expr-test.cc b/be/src/exprs/expr-test.cc index 3899a7a..33d77e0 100644 --- a/be/src/exprs/expr-test.cc +++ b/be/src/exprs/expr-test.cc @@ -2261,6 +2261,47 @@ TEST_F(ExprTest, DecimalArithmeticExprs) { } } +// Tests for expressions that mix decimal and non-decimal arguments with DECIMAL_V2=false. +TEST_F(ExprTest, DecimalV1MixedArithmeticExprs) { + executor_->PushExecOption("DECIMAL_V2=false"); + // IMPALA-3437: decimal constants are implicitly converted to double. + TestValue("10.0 + 3", TYPE_DOUBLE, 13.0); + TestValue("10 + 3.0", TYPE_DOUBLE, 13.0); + TestValue("10.0 - 3", TYPE_DOUBLE, 7.0); + TestValue("10.0 * 3", TYPE_DOUBLE, 30.0); + TestValue("10.0 / 3", TYPE_DOUBLE, 10.0 / 3); + // Conversion to DOUBLE loses some precision. + TestValue("0.999999999999999999999999999999 = 1", TYPE_BOOLEAN, true); + TestValue("0.999999999999999999999999999999 != 1", TYPE_BOOLEAN, false); + TestValue("0.999999999999999999999999999999 < 1", TYPE_BOOLEAN, false); + TestValue("0.999999999999999999999999999999 >= 1", TYPE_BOOLEAN, true); + TestValue("0.999999999999999999999999999999 > 1", TYPE_BOOLEAN, false); + executor_->PopExecOption(); +} + +// Tests the same expressions as above with DECIMAL_V2=true. +TEST_F(ExprTest, DecimalV2MixedArithmeticExprs) { + executor_->PushExecOption("DECIMAL_V2=true"); + // IMPALA-3437: decimal constants remain decimal. + TestDecimalValue( + "10.0 + 3", Decimal4Value(130), ColumnType::CreateDecimalType(5, 1)); + TestDecimalValue( + "10 + 3.0", Decimal4Value(130), ColumnType::CreateDecimalType(5, 1)); + TestDecimalValue( + "10.0 - 3", Decimal4Value(70), ColumnType::CreateDecimalType(5, 1)); + TestDecimalValue( + "10.0 * 3", Decimal4Value(300), ColumnType::CreateDecimalType(6, 1)); + TestDecimalValue( + "10.0 / 3", Decimal4Value(3333333), ColumnType::CreateDecimalType(8, 6)); + // Comparisons between DECIMAL values are precise. + TestValue("0.999999999999999999999999999999 = 1", TYPE_BOOLEAN, false); + TestValue("0.999999999999999999999999999999 != 1", TYPE_BOOLEAN, true); + TestValue("0.999999999999999999999999999999 < 1", TYPE_BOOLEAN, true); + TestValue("0.999999999999999999999999999999 >= 1", TYPE_BOOLEAN, false); + TestValue("0.999999999999999999999999999999 > 1", TYPE_BOOLEAN, false); + executor_->PopExecOption(); +} + // There are two tests of ranges, the second of which requires a cast // of the second operand to a higher-resolution type. TEST_F(ExprTest, BinaryPredicates) { http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/0a54cb5e/fe/src/main/java/org/apache/impala/analysis/Expr.java ---------------------------------------------------------------------- diff --git a/fe/src/main/java/org/apache/impala/analysis/Expr.java b/fe/src/main/java/org/apache/impala/analysis/Expr.java index 2b31b61..1eba24f 100644 --- a/fe/src/main/java/org/apache/impala/analysis/Expr.java +++ b/fe/src/main/java/org/apache/impala/analysis/Expr.java @@ -510,8 +510,12 @@ abstract public class Expr extends TreeNode<Expr> implements ParseNode, Cloneabl } /** - * Converts numeric literal in the expr tree rooted at this expr to return floating - * point types instead of decimals, if possible. + * DECIMAL_V1: + * ---------- + * This function applies a heuristic that casts literal child exprs of this expr from + * decimal to floating point in certain circumstances to reduce processing cost. In + * earlier versions of Impala's decimal support, it was much slower than floating point + * arithmetic. The original rationale for the automatic casting follows. * * Decimal has a higher processing cost than floating point and we should not pay * the cost if the user does not require the accuracy. For example: @@ -524,14 +528,19 @@ abstract public class Expr extends TreeNode<Expr> implements ParseNode, Cloneabl * * Another way to think about it is that DecimalLiterals are analyzed as returning * decimals (of the narrowest precision/scale) and we later convert them to a floating - * point type when it is consistent with the user's intent. + * point type according to a heuristic that attempts to guess what the user intended. * - * TODO: another option is to do constant folding in the FE and then apply this rule. + * DECIMAL_V2: + * ---------- + * This function does nothing. All decimal numeric literals are interpreted as decimals + * and the normal expression typing rules apply. */ protected void convertNumericLiteralsFromDecimal(Analyzer analyzer) throws AnalysisException { Preconditions.checkState(this instanceof ArithmeticExpr || this instanceof BinaryPredicate); + // This heuristic conversion is not part of DECIMAL_V2. + if (analyzer.getQueryOptions().isDecimal_v2()) return; if (children_.size() == 1) return; // Do not attempt to convert for unary ops Preconditions.checkState(children_.size() == 2); Type t0 = getChild(0).getType(); http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/0a54cb5e/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java ---------------------------------------------------------------------- diff --git a/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java b/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java index 5d843bf..18c7263 100644 --- a/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java +++ b/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java @@ -104,18 +104,64 @@ public class AnalyzeExprsTest extends AnalyzerTest { AnalysisError(String.format("select %s1", Double.toString(Double.MIN_VALUE)), "Numeric literal '4.9E-3241' underflows minimum resolution of doubles."); + // Test edge cases near the upper limits of decimal precision - 38 digits. + // Integer literal. + checkDecimalReturnType("select 12345678901234567890123456789012345678", + ScalarType.createDecimalType(38, 0)); + // Decimal point at front. testNumericLiteral("0.99999999999999999999999999999999999999", ScalarType.createDecimalType(38,38)); + // Decimal point at back. testNumericLiteral("99999999999999999999999999999999999999.", ScalarType.createDecimalType(38,0)); + // Negative values. testNumericLiteral("-0.99999999999999999999999999999999999999", ScalarType.createDecimalType(38,38)); testNumericLiteral("-99999999999999999999999999999999999999.", ScalarType.createDecimalType(38,0)); + // Decimal point in middle. testNumericLiteral("999999999999999999999.99999999999999999", ScalarType.createDecimalType(38,17)); testNumericLiteral("-999999999999999999.99999999999999999999", ScalarType.createDecimalType(38,20)); + + // Test literals that do not fit into a decimal and are treated as DOUBLE. + // Edge cases that have 39 digits. + testNumericLiteral("123456789012345678901234567890123456789", Type.DOUBLE); + testNumericLiteral("123456789012345678901234567890123456789.", Type.DOUBLE); + testNumericLiteral("1234567890123.45678901234567890123456789", Type.DOUBLE); + testNumericLiteral(".123456789012345678901234567890123456789", Type.DOUBLE); + + // Really huge literals are always interpreted as DOUBLE. + testNumericLiteral("12345678901234567890123456789012345678123455555555555555555555" + + "55559999999999999999999999999999999999999999999999999999999", Type.DOUBLE); + testNumericLiteral("1234567890123456789012345678901234567812.345555555555555555555" + + "555559999999999999999999999999999999999999999999999999999999", Type.DOUBLE); + testNumericLiteral(".1234567890123456789012345678901234567812345555555555555555555" + + "555559999999999999999999999999999999999999999999999999999999", Type.DOUBLE); + } + + /** + * Test scientific notation typing. Values in scientific notation are expanded to + * decimal or integer literals and the same typing rules then apply. + */ + @Test + public void TestScientificNumericLiterals() { + checkDecimalReturnType("select 1e9", Type.INT); + checkDecimalReturnType("select 1.123456789e9", Type.INT); + checkDecimalReturnType("select 1.1234567891e9", + ScalarType.createDecimalType(11, 1)); + checkDecimalReturnType("select 1.1234567891e6", + ScalarType.createDecimalType(11, 4)); + checkDecimalReturnType("select 9e9", Type.BIGINT); + checkDecimalReturnType("select 1e-2", ScalarType.createDecimalType(2, 2)); + checkDecimalReturnType("select 1.123e-2", ScalarType.createDecimalType(5, 5)); + + // Scientific notation - edge cases between decimal and double. + checkDecimalReturnType("select 1e37", ScalarType.createDecimalType(38, 0)); + checkDecimalReturnType("select 1.23456e37", ScalarType.createDecimalType(38, 0)); + checkDecimalReturnType("select 1e38", Type.DOUBLE); + checkDecimalReturnType("select 1.23456e38", Type.DOUBLE); } /** @@ -303,7 +349,8 @@ public class AnalyzeExprsTest extends AnalyzerTest { "Decimal precision must be > 0: 0"); // IMPALA-2264: decimal is implicitly cast to lower-precision integer in edge cases. - checkReturnType("select CAST(999 AS DECIMAL(3,0))", ScalarType.createDecimalType(3,0)); + checkDecimalReturnType( + "select CAST(999 AS DECIMAL(3,0))", ScalarType.createDecimalType(3,0)); AnalysisError("insert into functional.alltypesinsert (tinyint_col, year, month) " + "values(CAST(999 AS DECIMAL(3,0)), 1, 1)", "Possible loss of precision for target table 'functional.alltypesinsert'.\n" + @@ -1279,80 +1326,183 @@ public class AnalyzeExprsTest extends AnalyzerTest { } } - private void checkReturnType(String stmt, Type resultType) { - SelectStmt select = (SelectStmt) AnalyzesOk(stmt); + /** + * Get the result type of a select statement with a single select list element. + */ + Type getReturnType(String stmt, Analyzer analyzer) { + SelectStmt select = (SelectStmt) AnalyzesOk(stmt, analyzer, null); List<Expr> selectListExprs = select.getResultExprs(); assertNotNull(selectListExprs); assertEquals(selectListExprs.size(), 1); // check the first expr in select list Expr expr = selectListExprs.get(0); - assertEquals("Expected: " + resultType + " != " + expr.getType(), - resultType, expr.getType()); + return expr.getType(); + } + + private void checkReturnType(String stmt, Type resultType, Analyzer analyzer) { + Type exprType = getReturnType(stmt, analyzer); + assertEquals("Expected: " + resultType + " != " + exprType, resultType, exprType); + } + + private void checkReturnType(String stmt, Type resultType) { + checkReturnType(stmt, resultType, createAnalyzer(Catalog.DEFAULT_DB)); + } + + /** + * Test expressions involving decimal types that return different numeric types + * depending on the DECIMAL_V2 setting. + */ + private void checkDecimalReturnType(String stmt, Type decimalV1ResultType, + Type decimalV2ResultType) { + Analyzer analyzer = createAnalyzer(Catalog.DEFAULT_DB); + analyzer.getQueryOptions().setDecimal_v2(false); + checkReturnType(stmt, decimalV1ResultType, analyzer); + analyzer = createAnalyzer(Catalog.DEFAULT_DB); + analyzer.getQueryOptions().setDecimal_v2(true); + checkReturnType(stmt, decimalV2ResultType, analyzer); + } + + /** + * Test expressions that return the same type with either DECIMAL_V2 setting. + */ + private void checkDecimalReturnType(String stmt, Type resultType) { + checkDecimalReturnType(stmt, resultType, resultType); } @Test public void TestNumericLiteralTypeResolution() throws AnalysisException { checkReturnType("select 1", Type.TINYINT); - checkReturnType("select 1.1", ScalarType.createDecimalType(2,1)); - checkReturnType("select 01.1", ScalarType.createDecimalType(2,1)); - checkReturnType("select 1 + 1.1", Type.DOUBLE); - checkReturnType("select 0.23 + 1.1", ScalarType.createDecimalType(4,2)); + checkDecimalReturnType("select 1.1", ScalarType.createDecimalType(2,1)); + checkDecimalReturnType("select 01.1", ScalarType.createDecimalType(2,1)); + checkDecimalReturnType("select 1 + 1.1", + Type.DOUBLE, ScalarType.createDecimalType(5, 1)); + checkDecimalReturnType("select 0.23 + 1.1", ScalarType.createDecimalType(4,2)); checkReturnType("select float_col + float_col from functional.alltypestiny", Type.DOUBLE); checkReturnType("select int_col + int_col from functional.alltypestiny", Type.BIGINT); - // floating point + numeric literal = floating point - checkReturnType("select float_col + 1.1 from functional.alltypestiny", + // All arithmetic operators except multiplication involving floating point follow the + // same rules for deciding whether the output is a decimal or floating point. + // + // DECIMAL_V1: floating point + any numeric literal = floating point + // DECIMAL_V2: floating point + any expr of type decimal = decimal + checkDecimalReturnType("select float_col + 1.1 from functional.alltypestiny", + Type.DOUBLE, ScalarType.createDecimalType(38, 9)); + checkDecimalReturnType("select float_col - 1.1 from functional.alltypestiny", + Type.DOUBLE, ScalarType.createDecimalType(38, 9)); + checkDecimalReturnType("select float_col / 1.1 from functional.alltypestiny", + Type.DOUBLE, ScalarType.createDecimalType(38, 8)); + checkDecimalReturnType("select float_col % 1.1 from functional.alltypestiny", + Type.FLOAT, ScalarType.createDecimalType(10, 9)); + // BOTH: decimal + decimal literal = decimal + checkDecimalReturnType("select d1 + 1.1 from functional.decimal_tbl", + ScalarType.createDecimalType(11, 1)); + checkDecimalReturnType("select d1 - 1.1 from functional.decimal_tbl", + ScalarType.createDecimalType(11, 1)); + checkDecimalReturnType("select d1 * 1.1 from functional.decimal_tbl", + ScalarType.createDecimalType(11, 1)); + checkDecimalReturnType("select d1 / 1.1 from functional.decimal_tbl", + ScalarType.createDecimalType(14, 4), ScalarType.createDecimalType(16, 6)); + checkDecimalReturnType("select d1 % 1.1 from functional.decimal_tbl", + ScalarType.createDecimalType(2, 1)); + // BOTH: decimal + int literal = decimal + checkDecimalReturnType("select d1 + 2 from functional.decimal_tbl", + ScalarType.createDecimalType(10, 0)); + checkDecimalReturnType("select d1 - 2 from functional.decimal_tbl", + ScalarType.createDecimalType(10, 0)); + checkDecimalReturnType("select d1 * 2 from functional.decimal_tbl", + ScalarType.createDecimalType(12, 0)); + checkDecimalReturnType("select d1 / 2 from functional.decimal_tbl", + ScalarType.createDecimalType(13, 4), ScalarType.createDecimalType(15, 6)); + checkDecimalReturnType("select d1 % 2 from functional.decimal_tbl", + ScalarType.createDecimalType(3, 0)); + // DECIMAL_V1: int + numeric literal = floating point + // DECIMAL_V2: int + decimal expr = decimal + checkDecimalReturnType("select int_col + 1.1 from functional.alltypestiny", + Type.DOUBLE, ScalarType.createDecimalType(12, 1)); + checkDecimalReturnType("select int_col - 1.1 from functional.alltypestiny", + Type.DOUBLE, ScalarType.createDecimalType(12, 1)); + checkDecimalReturnType("select int_col * 1.1 from functional.alltypestiny", + Type.DOUBLE, ScalarType.createDecimalType(12, 1)); + checkDecimalReturnType("select int_col / 1.1 from functional.alltypestiny", + Type.DOUBLE, ScalarType.createDecimalType(17, 6)); + checkDecimalReturnType("select int_col % 1.1 from functional.alltypestiny", + Type.DOUBLE, ScalarType.createDecimalType(2, 1)); + + // Multiplying a floating point type with any other type always results in a double. + checkDecimalReturnType("select float_col * d1 from functional.alltypestiny " + + "join functional.decimal_tbl", Type.DOUBLE); + checkDecimalReturnType("select d1 * float_col from functional.alltypestiny " + + "join functional.decimal_tbl", Type.DOUBLE); + checkDecimalReturnType("select double_col * d1 from functional.alltypestiny " + + "join functional.decimal_tbl", Type.DOUBLE); + checkDecimalReturnType("select float_col * 10 from functional.alltypestiny", Type.DOUBLE); - // decimal + numeric literal = decimal - checkReturnType("select d1 + 1.1 from functional.decimal_tbl", - ScalarType.createDecimalType(11,1)); - // int + numeric literal = floating point - checkReturnType("select int_col + 1.1 from functional.alltypestiny", + checkDecimalReturnType("select 10 * float_col from functional.alltypestiny", Type.DOUBLE); - - // IMPALA-3439: Non-trivial constant expression with decimal + double = double. - // Tests that only the decimal literals in the constant decimal expr are cast - // to double. The second argument of round() must be an integer. - checkReturnType("select round(1.2345, 2) * pow(10, 10)", Type.DOUBLE); - - // Explicitly casting the literal to a decimal will override the behavior - checkReturnType("select int_col + cast(1.1 as decimal(2,1)) from " - + " functional.alltypestiny", ScalarType.createDecimalType(12,1)); - checkReturnType("select float_col + cast(1.1 as decimal(2,1)) from " - + " functional.alltypestiny", ScalarType.createDecimalType(38,9)); - checkReturnType("select float_col + cast(1.1*1.2+1.3 as decimal(2,1)) from " - + " functional.alltypestiny", ScalarType.createDecimalType(38,9)); - - // The location and complexity of the expr should not matter. - checkReturnType("select 1.0 + float_col + 1.1 from functional.alltypestiny", + checkDecimalReturnType("select double_col * 10 from functional.alltypestiny", Type.DOUBLE); - checkReturnType("select 1.0 + 2.0 + float_col from functional.alltypestiny", + checkDecimalReturnType("select float_col * 10.0 from functional.alltypestiny", Type.DOUBLE); - checkReturnType("select 1.0 + 2.0 + pi() * float_col from functional.alltypestiny", + checkDecimalReturnType("select 10.0 * float_col from functional.alltypestiny", Type.DOUBLE); - checkReturnType("select 1.0 + d1 + 1.1 from functional.decimal_tbl", - ScalarType.createDecimalType(12,1)); - checkReturnType("select 1.0 + 2.0 + d1 from functional.decimal_tbl", - ScalarType.createDecimalType(11,1)); - checkReturnType("select 1.0 + 2.0 + pi()*d1 from functional.decimal_tbl", + checkDecimalReturnType("select double_col * 10.0 from functional.alltypestiny", Type.DOUBLE); - // Test with multiple cols - checkReturnType("select double_col + 1.23 + float_col + 1.0 " + - " from functional.alltypestiny", Type.DOUBLE); - checkReturnType("select double_col + 1.23 + float_col + 1.0 + int_col " + - " + bigint_col from functional.alltypestiny", Type.DOUBLE); - checkReturnType("select d1 + 1.23 + d2 + 1.0 " + - " from functional.decimal_tbl", ScalarType.createDecimalType(14,2)); - - // Test with slot of both decimal and non-decimal - checkReturnType("select t1.int_col + t2.c1 from functional.alltypestiny t1 " + - " cross join functional.decimal_tiny t2", ScalarType.createDecimalType(15,4)); - checkReturnType("select 1.1 + t1.int_col + t2.c1 from functional.alltypestiny t1 " + - " cross join functional.decimal_tiny t2", ScalarType.createDecimalType(38,17)); + // IMPALA-3439: constant expression involving a function with mixed decimal and + // integer inputs. Regression tests to make sure that only the decimal literals + // in the constant decimal expr are cast to double. The second argument of round() + // must be an integer. + checkDecimalReturnType("select round(1.2345, 2) * pow(10, 10)", Type.DOUBLE); + checkDecimalReturnType("select round(1.2345, 2) + pow(10, 10)", + Type.DOUBLE, ScalarType.createDecimalType(38, 17)); + + // Explicitly casting the literal to a decimal or float changes the type of the + // literal. This is independent of the DECIMAL_V2 setting. + checkDecimalReturnType("select int_col + cast(1.1 as decimal(2,1)) from " + + " functional.alltypestiny", ScalarType.createDecimalType(12, 1)); + checkDecimalReturnType("select float_col + cast(1.1 as decimal(2,1)) from " + + " functional.alltypestiny", ScalarType.createDecimalType(38, 9)); + checkDecimalReturnType("select float_col + cast(1.1*1.2+1.3 as decimal(2,1)) from " + + " functional.alltypestiny", ScalarType.createDecimalType(38, 9)); + checkDecimalReturnType("select float_col + cast(1.1 as float) from " + + " functional.alltypestiny", Type.DOUBLE); + checkDecimalReturnType("select float_col + cast(1.1 as float) from " + + " functional.alltypestiny", Type.DOUBLE); + checkDecimalReturnType("select float_col + cast(1.1 as double) from " + + " functional.alltypestiny", Type.DOUBLE); + + // Test behavior of compound expressions with a single slot ref and many literals. + checkDecimalReturnType("select 1.0 + float_col + 1.1 from functional.alltypestiny", + Type.DOUBLE, ScalarType.createDecimalType(38, 9)); + checkDecimalReturnType("select 1.0 + 2.0 + float_col from functional.alltypestiny", + Type.DOUBLE, ScalarType.createDecimalType(38, 9)); + checkDecimalReturnType("select 1.0 + 2.0 + pi() * float_col from functional.alltypestiny", + Type.DOUBLE, ScalarType.createDecimalType(38, 17)); + checkDecimalReturnType("select 1.0 + d1 + 1.1 from functional.decimal_tbl", + ScalarType.createDecimalType(12, 1)); + checkDecimalReturnType("select 1.0 + 2.0 + d1 from functional.decimal_tbl", + ScalarType.createDecimalType(11, 1)); + checkDecimalReturnType("select 1.0 + 2.0 + pi() * d1 from functional.decimal_tbl", + Type.DOUBLE, ScalarType.createDecimalType(38, 17)); + + // Test behavior of compound expressions with multiple slot refs and literals. + checkDecimalReturnType("select double_col + 1.23 + float_col + 1.0 " + + " from functional.alltypestiny", Type.DOUBLE, ScalarType.createDecimalType(38, 17)); + checkDecimalReturnType("select double_col + 1.23 + float_col + 1.0 + int_col " + + " + bigint_col from functional.alltypestiny", Type.DOUBLE, + ScalarType.createDecimalType(38, 17)); + checkDecimalReturnType("select d1 + 1.23 + d2 + 1.0 " + + " from functional.decimal_tbl", ScalarType.createDecimalType(14, 2)); + + // Test with slot refs of both decimal and non-decimal + checkDecimalReturnType("select t1.int_col + t2.c1 from functional.alltypestiny t1 " + + " cross join functional.decimal_tiny t2", ScalarType.createDecimalType(15, 4)); + checkDecimalReturnType("select 1.1 + t1.int_col + t2.c1 from functional.alltypestiny t1 " + + " cross join functional.decimal_tiny t2", ScalarType.createDecimalType(38, 17), + ScalarType.createDecimalType(16, 4)); } /**
