From bf6c3c13a637bea39a15ac0295b33423184846c6 Mon Sep 17 00:00:00 2001 From: rui-mo Date: Thu, 7 Dec 2023 23:42:17 -0800 Subject: [PATCH] Separate and move testCast function (#7912) Summary: Moves `testCast` function from CastExprTest.cpp to CastBaseTest.h for other test files to use. Separates `testCast` function into `testCast` (for valid output test), `testTryCast` (for try_cast test), and `testInvalidCast` (for exception test). https://github.com/facebookincubator/velox/pull/7377#discussion_r1415330093 https://github.com/facebookincubator/velox/pull/7377#discussion_r1415332611 Pull Request resolved: https://github.com/facebookincubator/velox/pull/7912 Reviewed By: xiaoxmeng Differential Revision: D51968059 Pulled By: mbasmanova fbshipit-source-id: 26fe9896766ce5b1ed175d3b85c930e7a0b2a1c8 --- velox/expression/tests/CastExprTest.cpp | 592 ++++++++++-------- .../functions/prestosql/tests/CastBaseTest.h | 60 ++ 2 files changed, 396 insertions(+), 256 deletions(-) diff --git a/velox/expression/tests/CastExprTest.cpp b/velox/expression/tests/CastExprTest.cpp index 7c35dfe8a163..41cb79d714b8 100644 --- a/velox/expression/tests/CastExprTest.cpp +++ b/velox/expression/tests/CastExprTest.cpp @@ -162,54 +162,6 @@ class CastExprTest : public functions::test::CastBaseTest { } } - /** - * @tparam From Source type for cast - * @tparam To Destination type for cast - * @param typeString Cast type in string - * @param input Input vector of type From - * @param expectedResult Expected output vector of type To - * @param inputNulls Input null indexes - * @param expectedNulls Expected output null indexes - */ - template - void testCast( - const std::string& typeString, - std::vector> input, - std::vector> expectedResult, - bool expectFailure = false, - bool tryCast = false, - const TypePtr& fromType = CppToType::create(), - const TypePtr& toType = CppToType::create()) { - std::vector rawInput(input.size()); - for (auto index = 0; index < input.size(); index++) { - if (input[index].has_value()) { - rawInput[index] = input[index].value(); - } - } - // Create input vector using values and nulls - auto inputVector = makeFlatVector(rawInput, fromType); - - for (auto index = 0; index < input.size(); index++) { - if (!input[index].has_value()) { - inputVector->setNull(index, true); - } - } - auto rowVector = makeRowVector({inputVector}); - std::string castFunction = tryCast ? "try_cast" : "cast"; - if (expectFailure) { - EXPECT_THROW( - evaluate( - fmt::format("{}(c0 as {})", castFunction, typeString), rowVector), - VeloxUserError); - return; - } - // run try cast and get the result vector - auto result = - evaluate(castFunction + "(c0 as " + typeString + ")", rowVector); - auto expected = makeNullableFlatVector(expectedResult, toType); - assertEqualVectors(expected, result); - } - template void testDecimalToFloatCasts() { // short to short, scale up. @@ -819,8 +771,6 @@ TEST_F(CastExprTest, dateToTimestamp) { Timestamp(1257724800, 0), std::nullopt, }, - false, - false, DATE(), TIMESTAMP()); } @@ -843,8 +793,6 @@ TEST_F(CastExprTest, timestampToDate) { 14557, std::nullopt, }, - false, - false, TIMESTAMP(), DATE()); @@ -858,23 +806,29 @@ TEST_F(CastExprTest, timestampToDate) { 14556, std::nullopt, }, - false, - false, TIMESTAMP(), DATE()); } TEST_F(CastExprTest, timestampInvalid) { - testCast("timestamp", {12}, {Timestamp(0, 0)}, true); - testCast("timestamp", {1234}, {Timestamp(0, 0)}, true); - testCast("timestamp", {1234}, {Timestamp(0, 0)}, true); - testCast("timestamp", {1234}, {Timestamp(0, 0)}, true); - - testCast("timestamp", {12.99}, {Timestamp(0, 0)}, true); - testCast("timestamp", {12.99}, {Timestamp(0, 0)}, true); - - testCast( - "timestamp", {"2012-Oct-01"}, {Timestamp(0, 0)}, true); + testInvalidCast( + "timestamp", {12}, "Conversion to Timestamp is not supported"); + testInvalidCast( + "timestamp", {1234}, "Conversion to Timestamp is not supported"); + testInvalidCast( + "timestamp", {1234}, "Conversion to Timestamp is not supported"); + testInvalidCast( + "timestamp", {1234}, "Conversion to Timestamp is not supported"); + + testInvalidCast( + "timestamp", {12.99}, "Conversion to Timestamp is not supported"); + testInvalidCast( + "timestamp", {12.99}, "Conversion to Timestamp is not supported"); + + testInvalidCast( + "timestamp", + {"2012-Oct-01"}, + "Unable to parse timestamp value: \"2012-Oct-01\""); } TEST_F(CastExprTest, timestampAdjustToTimezone) { @@ -951,8 +905,6 @@ TEST_F(CastExprTest, date) { -719893, 0, std::nullopt}, - false, - false, VARCHAR(), DATE()); } @@ -968,8 +920,6 @@ TEST_F(CastExprTest, date) { "2015-03-18 123142", "2015-03-18 (BC)"}, {3789391, 16436, 16495, 16512, 16512, 16512, 16512}, - false, - false, VARCHAR(), DATE()); } @@ -978,51 +928,97 @@ TEST_F(CastExprTest, invalidDate) { for (bool isIso8601 : {true, false}) { setCastStringToDateIsIso8601(isIso8601); - testCast( - "date", {12}, {0}, true, false, TINYINT(), DATE()); - testCast( - "date", {1234}, {0}, true, false, SMALLINT(), DATE()); - testCast( - "date", {1234}, {0}, true, false, INTEGER(), DATE()); - testCast( - "date", {1234}, {0}, true, false, BIGINT(), DATE()); + testInvalidCast( + "date", {12}, "Cast from TINYINT to DATE is not supported", TINYINT()); + testInvalidCast( + "date", + {1234}, + "Cast from SMALLINT to DATE is not supported", + SMALLINT()); + testInvalidCast( + "date", + {1234}, + "Cast from INTEGER to DATE is not supported", + INTEGER()); + testInvalidCast( + "date", {1234}, "Cast from BIGINT to DATE is not supported", BIGINT()); - testCast("date", {12.99}, {0}, true, false, REAL(), DATE()); - testCast( - "date", {12.99}, {0}, true, false, DOUBLE(), DATE()); + testInvalidCast( + "date", {12.99}, "Cast from REAL to DATE is not supported", REAL()); + testInvalidCast( + "date", {12.99}, "Cast from DOUBLE to DATE is not supported", DOUBLE()); // Parsing ill-formated dates. - testCast( - "date", {"2012-Oct-23"}, {0}, true, false, VARCHAR(), DATE()); - testCast( - "date", {"2015-03-18X"}, {0}, true, false, VARCHAR(), DATE()); - testCast( - "date", {"2015/03/18"}, {0}, true, false, VARCHAR(), DATE()); - testCast( - "date", {"2015.03.18"}, {0}, true, false, VARCHAR(), DATE()); - testCast( - "date", {"20150318"}, {0}, true, false, VARCHAR(), DATE()); - testCast( - "date", {"2015-031-8"}, {0}, true, false, VARCHAR(), DATE()); + testInvalidCast( + "date", + {"2012-Oct-23"}, + "Unable to parse date value: \"2012-Oct-23\"", + VARCHAR()); + testInvalidCast( + "date", + {"2015-03-18X"}, + "Unable to parse date value: \"2015-03-18X\"", + VARCHAR()); + testInvalidCast( + "date", + {"2015/03/18"}, + "Unable to parse date value: \"2015/03/18\"", + VARCHAR()); + testInvalidCast( + "date", + {"2015.03.18"}, + "Unable to parse date value: \"2015.03.18\"", + VARCHAR()); + testInvalidCast( + "date", + {"20150318"}, + "Unable to parse date value: \"20150318\"", + VARCHAR()); + testInvalidCast( + "date", + {"2015-031-8"}, + "Unable to parse date value: \"2015-031-8\"", + VARCHAR()); } setCastStringToDateIsIso8601(true); - testCast( - "date", {"12345"}, {0}, true, false, VARCHAR(), DATE()); - testCast( - "date", {"2015-03"}, {0}, true, false, VARCHAR(), DATE()); - testCast( - "date", {"2015-03-18 123412"}, {0}, true, false, VARCHAR(), DATE()); - testCast( - "date", {"2015-03-18T"}, {0}, true, false, VARCHAR(), DATE()); - testCast( - "date", {"2015-03-18T123412"}, {0}, true, false, VARCHAR(), DATE()); - testCast( - "date", {"2015-03-18 (BC)"}, {0}, true, false, VARCHAR(), DATE()); - testCast( - "date", {"1970-01-01 "}, {0}, true, false, VARCHAR(), DATE()); - testCast( - "date", {" 1970-01-01 "}, {0}, true, false, VARCHAR(), DATE()); + testInvalidCast( + "date", {"12345"}, "Unable to parse date value: \"12345\"", VARCHAR()); + testInvalidCast( + "date", + {"2015-03"}, + "Unable to parse date value: \"2015-03\"", + VARCHAR()); + testInvalidCast( + "date", + {"2015-03-18 123412"}, + "Unable to parse date value: \"2015-03-18 123412\"", + VARCHAR()); + testInvalidCast( + "date", + {"2015-03-18T"}, + "Unable to parse date value: \"2015-03-18T\"", + VARCHAR()); + testInvalidCast( + "date", + {"2015-03-18T123412"}, + "Unable to parse date value: \"2015-03-18T123412\"", + VARCHAR()); + testInvalidCast( + "date", + {"2015-03-18 (BC)"}, + "Unable to parse date value: \"2015-03-18 (BC)\"", + VARCHAR()); + testInvalidCast( + "date", + {"1970-01-01 "}, + "Unable to parse date value: \"1970-01-01 \"", + VARCHAR()); + testInvalidCast( + "date", + {" 1970-01-01 "}, + "Unable to parse date value: \" 1970-01-01 \"", + VARCHAR()); } TEST_F(CastExprTest, primitiveInvalidCornerCases) { @@ -1030,84 +1026,170 @@ TEST_F(CastExprTest, primitiveInvalidCornerCases) { // To integer. { // Overflow. - testCast("tinyint", {1234567}, {0}, true); - testCast("tinyint", {-1234567}, {0}, true); - testCast("tinyint", {12345.67}, {0}, true); - testCast("tinyint", {-12345.67}, {0}, true); - testCast("tinyint", {127.8}, {128}, true); - testCast("integer", {kInf}, {0}, true); - testCast("bigint", {kInf}, {0}, true); + testInvalidCast( + "tinyint", {1234567}, "Overflow during arithmetic conversion"); + testInvalidCast( + "tinyint", + {-1234567}, + "Negative overflow during arithmetic conversion"); + testInvalidCast( + "tinyint", + {12345.67}, + "Loss of precision during arithmetic conversion"); + testInvalidCast( + "tinyint", + {-12345.67}, + "Loss of precision during arithmetic conversion"); + testInvalidCast( + "tinyint", {127.8}, "Loss of precision during arithmetic conversion"); + testInvalidCast( + "integer", {kInf}, "Loss of precision during arithmetic conversion"); + testInvalidCast( + "bigint", {kInf}, "Loss of precision during arithmetic conversion"); // Presto throws on cast(nan() as bigint), but we let it return 0 to be // consistent with other cases. - testCast("bigint", {kNan}, {0}, true); - testCast("integer", {kNan}, {0}, true); - testCast("smallint", {kNan}, {0}, true); - testCast("tinyint", {kNan}, {0}, true); + testInvalidCast( + "bigint", {kNan}, "Cannot cast NaN to an integral value"); + testInvalidCast( + "integer", {kNan}, "Cannot cast NaN to an integral value"); + testInvalidCast( + "smallint", {kNan}, "Cannot cast NaN to an integral value"); + testInvalidCast( + "tinyint", {kNan}, "Cannot cast NaN to an integral value"); // Invalid strings. - testCast("tinyint", {"1234567"}, {0}, true); - testCast("tinyint", {"1.2"}, {0}, true); - testCast("tinyint", {"1.23444"}, {0}, true); - testCast("tinyint", {".2355"}, {0}, true); - testCast("tinyint", {"1a"}, {0}, true); - testCast("tinyint", {""}, {0}, true); - testCast("integer", {"1'234'567"}, {0}, true); - testCast("integer", {"1,234,567"}, {0}, true); - testCast("bigint", {"infinity"}, {0}, true); - testCast("bigint", {"nan"}, {0}, true); + testInvalidCast( + "tinyint", {"1234567"}, "Overflow during conversion"); + testInvalidCast( + "tinyint", + {"1.2"}, + "Non-whitespace character found after end of conversion"); + testInvalidCast( + "tinyint", + {"1.23444"}, + "Non-whitespace character found after end of conversion"); + testInvalidCast( + "tinyint", {".2355"}, "Invalid leading character"); + testInvalidCast( + "tinyint", + {"1a"}, + "Non-whitespace character found after end of conversion"); + testInvalidCast("tinyint", {""}, "Empty string"); + testInvalidCast( + "integer", + {"1'234'567"}, + "Non-whitespace character found after end of conversion"); + testInvalidCast( + "integer", + {"1,234,567"}, + "Non-whitespace character found after end of conversion"); + testInvalidCast( + "bigint", {"infinity"}, "Invalid leading character"); + testInvalidCast( + "bigint", {"nan"}, "Invalid leading character"); } // To floating-point. { // TODO: Presto returns Infinity in this case. - testCast("real", {1.7E308}, {0}, true); + testInvalidCast( + "real", {1.7E308}, "Overflow during arithmetic conversion"); // Invalid strings. - testCast("real", {"1.2a"}, {0}, true); - testCast("real", {"1.2.3"}, {0}, true); + testInvalidCast( + "real", + {"1.2a"}, + "Non-whitespace character found after end of conversion"); + testInvalidCast( + "real", + {"1.2.3"}, + "Non-whitespace character found after end of conversion"); } // To boolean. { - testCast("boolean", {"1.7E308"}, {0}, true); - testCast("boolean", {"nan"}, {0}, true); - testCast("boolean", {"infinity"}, {0}, true); - testCast("boolean", {"12"}, {0}, true); - testCast("boolean", {"-1"}, {0}, true); - testCast("boolean", {"tr"}, {0}, true); - testCast("boolean", {"tru"}, {0}, true); + testInvalidCast( + "boolean", + {"1.7E308"}, + "Non-whitespace character found after end of conversion"); + testInvalidCast( + "boolean", + {"nan"}, + "Non-whitespace character found after end of conversion"); + testInvalidCast( + "boolean", {"infinity"}, "Invalid value for bool"); + testInvalidCast( + "boolean", + {"12"}, + "Integer overflow when parsing bool (must be 0 or 1)"); + testInvalidCast("boolean", {"-1"}, "Invalid value for bool"); + testInvalidCast( + "boolean", + {"tr"}, + "Non-whitespace character found after end of conversion"); + testInvalidCast( + "boolean", + {"tru"}, + "Non-whitespace character found after end of conversion"); } setCastIntByTruncate(true); // To integer. { // Invalid strings. - testCast("tinyint", {"1234567"}, {0}, true); - testCast("tinyint", {"1a"}, {0}, true); - testCast("tinyint", {""}, {0}, true); - testCast("integer", {"1'234'567"}, {0}, true); - testCast("integer", {"1,234,567"}, {0}, true); - testCast("bigint", {"infinity"}, {0}, true); - testCast("bigint", {"nan"}, {0}, true); - testCast("tinyint", {"+1"}, {0}, true); + testInvalidCast( + "tinyint", {"1234567"}, "Value is too large for type"); + testInvalidCast( + "tinyint", {"1a"}, "Encountered a non-digit character"); + testInvalidCast("tinyint", {""}, "Empty string"); + testInvalidCast( + "integer", {"1'234'567"}, "Encountered a non-digit character"); + testInvalidCast( + "integer", {"1,234,567"}, "Encountered a non-digit character"); + testInvalidCast( + "bigint", {"infinity"}, "Encountered a non-digit character"); + testInvalidCast( + "bigint", {"nan"}, "Encountered a non-digit character"); + testInvalidCast( + "tinyint", {"+1"}, "Encountered a non-digit character"); } // To floating-point. { // Invalid strings. - testCast("real", {"1.2a"}, {0}, true); - testCast("real", {"1.2.3"}, {0}, true); + testInvalidCast( + "real", + {"1.2a"}, + "Non-whitespace character found after end of conversion"); + testInvalidCast( + "real", + {"1.2.3"}, + "Non-whitespace character found after end of conversion"); } // To boolean. { - testCast("boolean", {"1.7E308"}, {0}, true); - testCast("boolean", {"nan"}, {0}, true); - testCast("boolean", {"infinity"}, {0}, true); - testCast("boolean", {"12"}, {0}, true); - testCast("boolean", {"-1"}, {0}, true); - testCast("boolean", {"tr"}, {0}, true); - testCast("boolean", {"tru"}, {0}, true); + testInvalidCast( + "boolean", + {"1.7E308"}, + "Non-whitespace character found after end of conversion"); + testInvalidCast( + "boolean", + {"nan"}, + "Non-whitespace character found after end of conversion"); + testInvalidCast( + "boolean", {"infinity"}, "Invalid value for bool"); + testInvalidCast( + "boolean", {"12"}, "Integer overflow when parsing bool"); + testInvalidCast("boolean", {"-1"}, "Invalid value for bool"); + testInvalidCast( + "boolean", + {"tr"}, + "Non-whitespace character found after end of conversion"); + testInvalidCast( + "boolean", + {"tru"}, + "Non-whitespace character found after end of conversion"); } } @@ -1115,123 +1197,123 @@ TEST_F(CastExprTest, primitiveValidCornerCases) { setCastIntByTruncate(false); // To integer. { - testCast("tinyint", {127.1}, {127}, false); - testCast("bigint", {12345.12}, {12345}, false); - testCast("bigint", {12345.67}, {12346}, false); - testCast("tinyint", {"+1"}, {1}, false); + testCast("tinyint", {127.1}, {127}); + testCast("bigint", {12345.12}, {12345}); + testCast("bigint", {12345.67}, {12346}); + testCast("tinyint", {"+1"}, {1}); } // To floating-point. { - testCast("real", {"1.7E308"}, {kInf}, false); - testCast("real", {"1."}, {1.0}, false); - testCast("real", {"1"}, {1}, false); + testCast("real", {"1.7E308"}, {kInf}); + testCast("real", {"1."}, {1.0}); + testCast("real", {"1"}, {1}); // When casting from "Infinity" and "NaN", Presto is case sensitive. But we // let them be case insensitive to be consistent with other conversions. - testCast("real", {"infinity"}, {kInf}, false); - testCast("real", {"-infinity"}, {-kInf}, false); - testCast("real", {"InfiNiTy"}, {kInf}, false); - testCast("real", {"-InfiNiTy"}, {-kInf}, false); - testCast("real", {"nan"}, {kNan}, false); - testCast("real", {"nAn"}, {kNan}, false); + testCast("real", {"infinity"}, {kInf}); + testCast("real", {"-infinity"}, {-kInf}); + testCast("real", {"InfiNiTy"}, {kInf}); + testCast("real", {"-InfiNiTy"}, {-kInf}); + testCast("real", {"nan"}, {kNan}); + testCast("real", {"nAn"}, {kNan}); } // To boolean. { - testCast("boolean", {1}, {true}, false); - testCast("boolean", {0}, {false}, false); - testCast("boolean", {12}, {true}, false); - testCast("boolean", {-1}, {true}, false); - testCast("boolean", {1.0}, {true}, false); - testCast("boolean", {1.1}, {true}, false); - testCast("boolean", {0.1}, {true}, false); - testCast("boolean", {-0.1}, {true}, false); - testCast("boolean", {-1.0}, {true}, false); - testCast("boolean", {kNan}, {true}, false); - testCast("boolean", {kInf}, {true}, false); - testCast("boolean", {0.0000000000001}, {true}, false); - - testCast("boolean", {"1"}, {true}, false); - testCast("boolean", {"0"}, {false}, false); - testCast("boolean", {"t"}, {true}, false); - testCast("boolean", {"true"}, {true}, false); + testCast("boolean", {1}, {true}); + testCast("boolean", {0}, {false}); + testCast("boolean", {12}, {true}); + testCast("boolean", {-1}, {true}); + testCast("boolean", {1.0}, {true}); + testCast("boolean", {1.1}, {true}); + testCast("boolean", {0.1}, {true}); + testCast("boolean", {-0.1}, {true}); + testCast("boolean", {-1.0}, {true}); + testCast("boolean", {kNan}, {true}); + testCast("boolean", {kInf}, {true}); + testCast("boolean", {0.0000000000001}, {true}); + + testCast("boolean", {"1"}, {true}); + testCast("boolean", {"0"}, {false}); + testCast("boolean", {"t"}, {true}); + testCast("boolean", {"true"}, {true}); } // To string. { - testCast("varchar", {kInf}, {"Infinity"}, false); - testCast("varchar", {kNan}, {"NaN"}, false); + testCast("varchar", {kInf}, {"Infinity"}); + testCast("varchar", {kNan}, {"NaN"}); } setCastIntByTruncate(true); // To integer. { // Valid strings. - testCast("tinyint", {"1.2"}, {1}, false); - testCast("tinyint", {"1.23444"}, {1}, false); - testCast("tinyint", {".2355"}, {0}, false); - testCast("tinyint", {"-1.8"}, {-1}, false); - testCast("tinyint", {"1."}, {1}, false); - testCast("tinyint", {"-1."}, {-1}, false); - testCast("tinyint", {"0."}, {0}, false); - testCast("tinyint", {"."}, {0}, false); - testCast("tinyint", {"-."}, {0}, false); - - testCast("tinyint", {1234567}, {-121}, false); - testCast("tinyint", {-1234567}, {121}, false); - testCast("tinyint", {12345.67}, {57}, false); - testCast("tinyint", {-12345.67}, {-57}, false); - testCast("tinyint", {127.1}, {127}, false); - testCast("bigint", {kInf}, {9223372036854775807}, false); - testCast("bigint", {kNan}, {0}, false); - testCast("integer", {kNan}, {0}, false); - testCast("smallint", {kNan}, {0}, false); - testCast("tinyint", {kNan}, {0}, false); - - testCast("bigint", {12345.12}, {12345}, false); - testCast("bigint", {12345.67}, {12345}, false); + testCast("tinyint", {"1.2"}, {1}); + testCast("tinyint", {"1.23444"}, {1}); + testCast("tinyint", {".2355"}, {0}); + testCast("tinyint", {"-1.8"}, {-1}); + testCast("tinyint", {"1."}, {1}); + testCast("tinyint", {"-1."}, {-1}); + testCast("tinyint", {"0."}, {0}); + testCast("tinyint", {"."}, {0}); + testCast("tinyint", {"-."}, {0}); + + testCast("tinyint", {1234567}, {-121}); + testCast("tinyint", {-1234567}, {121}); + testCast("tinyint", {12345.67}, {57}); + testCast("tinyint", {-12345.67}, {-57}); + testCast("tinyint", {127.1}, {127}); + testCast("bigint", {kInf}, {9223372036854775807}); + testCast("bigint", {kNan}, {0}); + testCast("integer", {kNan}, {0}); + testCast("smallint", {kNan}, {0}); + testCast("tinyint", {kNan}, {0}); + + testCast("bigint", {12345.12}, {12345}); + testCast("bigint", {12345.67}, {12345}); } // To floating-point. { - testCast("real", {1.7E308}, {kInf}, false); - - testCast("real", {"1.7E308"}, {kInf}, false); - testCast("real", {"1."}, {1.0}, false); - testCast("real", {"1"}, {1}, false); - testCast("real", {"infinity"}, {kInf}, false); - testCast("real", {"-infinity"}, {-kInf}, false); - testCast("real", {"nan"}, {kNan}, false); - testCast("real", {"InfiNiTy"}, {kInf}, false); - testCast("real", {"-InfiNiTy"}, {-kInf}, false); - testCast("real", {"nAn"}, {kNan}, false); + testCast("real", {1.7E308}, {kInf}); + + testCast("real", {"1.7E308"}, {kInf}); + testCast("real", {"1."}, {1.0}); + testCast("real", {"1"}, {1}); + testCast("real", {"infinity"}, {kInf}); + testCast("real", {"-infinity"}, {-kInf}); + testCast("real", {"nan"}, {kNan}); + testCast("real", {"InfiNiTy"}, {kInf}); + testCast("real", {"-InfiNiTy"}, {-kInf}); + testCast("real", {"nAn"}, {kNan}); } // To boolean. { - testCast("boolean", {1}, {true}, false); - testCast("boolean", {0}, {false}, false); - testCast("boolean", {12}, {true}, false); - testCast("boolean", {-1}, {true}, false); - testCast("boolean", {1.0}, {true}, false); - testCast("boolean", {1.1}, {true}, false); - testCast("boolean", {0.1}, {true}, false); - testCast("boolean", {-0.1}, {true}, false); - testCast("boolean", {-1.0}, {true}, false); - testCast("boolean", {kNan}, {false}, false); - testCast("boolean", {kInf}, {true}, false); - testCast("boolean", {0.0000000000001}, {true}, false); - - testCast("boolean", {"1"}, {true}, false); - testCast("boolean", {"0"}, {false}, false); - testCast("boolean", {"t"}, {true}, false); - testCast("boolean", {"true"}, {true}, false); + testCast("boolean", {1}, {true}); + testCast("boolean", {0}, {false}); + testCast("boolean", {12}, {true}); + testCast("boolean", {-1}, {true}); + testCast("boolean", {1.0}, {true}); + testCast("boolean", {1.1}, {true}); + testCast("boolean", {0.1}, {true}); + testCast("boolean", {-0.1}, {true}); + testCast("boolean", {-1.0}, {true}); + testCast("boolean", {kNan}, {false}); + testCast("boolean", {kInf}, {true}); + testCast("boolean", {0.0000000000001}, {true}); + + testCast("boolean", {"1"}, {true}); + testCast("boolean", {"0"}, {false}); + testCast("boolean", {"t"}, {true}); + testCast("boolean", {"true"}, {true}); } // To string. { - testCast("varchar", {kInf}, {"Infinity"}, false); - testCast("varchar", {kNan}, {"NaN"}, false); + testCast("varchar", {kInf}, {"Infinity"}); + testCast("varchar", {kNan}, {"NaN"}); } } @@ -1288,15 +1370,13 @@ TEST_F(CastExprTest, nullInputs) { TEST_F(CastExprTest, errorHandling) { // Making sure error cases lead to null outputs - testCast( + testTryCast( "tinyint", {"1abc", "2", "3", "100", std::nullopt}, - {std::nullopt, 2, 3, 100, std::nullopt}, - false, - true); + {std::nullopt, 2, 3, 100, std::nullopt}); setCastIntByTruncate(true); - testCast( + testTryCast( "tinyint", {"-", "-0", @@ -1331,26 +1411,26 @@ TEST_F(CastExprTest, errorHandling) { std::nullopt, 125, 127, - -128}, - false, - true); + -128}); - testCast( + testTryCast( "integer", {1e12, 2.5, 3.6, 100.44, -100.101}, - {std::numeric_limits::max(), 2, 3, 100, -100}, - false, - true); + {std::numeric_limits::max(), 2, 3, 100, -100}); setCastIntByTruncate(false); testCast( "int", {1.888, 2.5, 3.6, 100.44, -100.101}, {2, 3, 4, 100, -100}); - testCast( - "tinyint", {"1abc", "2", "3", "100", "-100"}, {1, 2, 3, 100, -100}, true); + testInvalidCast( + "tinyint", + {"1abc", "2", "3", "100", "-100"}, + "Non-whitespace character found after end of conversion"); - testCast( - "tinyint", {"1", "2", "3", "100", "-100.5"}, {1, 2, 3, 100, -100}, true); + testInvalidCast( + "tinyint", + {"1", "2", "3", "100", "-100.5"}, + "Non-whitespace character found after end of conversion"); } constexpr vector_size_t kVectorSize = 1'000; diff --git a/velox/functions/prestosql/tests/CastBaseTest.h b/velox/functions/prestosql/tests/CastBaseTest.h index 52a0fbb98c2f..bff27878d9b2 100644 --- a/velox/functions/prestosql/tests/CastBaseTest.h +++ b/velox/functions/prestosql/tests/CastBaseTest.h @@ -127,6 +127,66 @@ class CastBaseTest : public FunctionBaseTest { assertEqualVectors(wrapInDictionary(indices, expected), result); } + /** + * @tparam From Source type for cast. + * @tparam To Destination type for cast. + * @param typeString Cast type in string. + * @param input Input vector of type From. + * @param expectedResult Expected output vector of type To. + */ + template + void testCast( + const std::string& typeString, + const std::vector>& input, + const std::vector>& expectedResult, + const TypePtr& fromType = CppToType::create(), + const TypePtr& toType = CppToType::create()) { + auto result = evaluate( + fmt::format("cast(c0 as {})", typeString), + makeRowVector({makeNullableFlatVector(input, fromType)})); + auto expected = makeNullableFlatVector(expectedResult, toType); + assertEqualVectors(expected, result); + } + + /** + * @tparam From Source type for cast. + * @tparam To Destination type for cast. + * @param typeString Cast type in string. + * @param input Input vector of type From. + * @param expectedResult Expected output vector of type To. + */ + template + void testTryCast( + const std::string& typeString, + const std::vector>& input, + const std::vector>& expectedResult, + const TypePtr& fromType = CppToType::create(), + const TypePtr& toType = CppToType::create()) { + auto result = evaluate( + fmt::format("try_cast(c0 as {})", typeString), + makeRowVector({makeNullableFlatVector(input, fromType)})); + auto expected = makeNullableFlatVector(expectedResult, toType); + assertEqualVectors(expected, result); + } + + /** + * @tparam From Source type for cast. + * @param typeString Cast type in string. + * @param input Input vector of type From. + */ + template + void testInvalidCast( + const std::string& typeString, + const std::vector>& input, + const std::string& expectedErrorMessage, + const TypePtr& fromType = CppToType::create()) { + VELOX_ASSERT_THROW( + evaluate( + fmt::format("cast(c0 as {})", typeString), + makeRowVector({makeNullableFlatVector(input, fromType)})), + expectedErrorMessage); + } + void testCast( const TypePtr& fromType, const TypePtr& toType,