You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@impala.apache.org by bh...@apache.org on 2017/09/15 04:33:41 UTC
[4/4] incubator-impala git commit: IMPALA-3437: DECIMAL_V2: avoid
implicit decimal->double conversion
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 <ta...@cloudera.com>
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 <ta...@cloudera.com>
Authored: Wed Aug 30 16:41:56 2017 -0700
Committer: Impala Public Jenkins <im...@gerrit.cloudera.org>
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));
}
/**