diff --git a/velox/expression/CastExpr.cpp b/velox/expression/CastExpr.cpp index c5c8ecb0bb79..1431a015e594 100644 --- a/velox/expression/CastExpr.cpp +++ b/velox/expression/CastExpr.cpp @@ -89,7 +89,10 @@ Status detail::parseDecimalComponents( // Optional exponent. if (s[pos] == 'e' || s[pos] == 'E') { ++pos; - bool withSign = pos < size && (s[pos] == '+' || s[pos] == '-'); + if (pos == size) { + return Status::UserError("The exponent part is empty."); + } + bool withSign = s[pos] == '+' || s[pos] == '-'; if (withSign && pos == size - 1) { return Status::UserError("The exponent part only contains sign."); } @@ -98,17 +101,13 @@ Status detail::parseDecimalComponents( for (auto i = static_cast(withSign); i < size - pos; ++i) { if (!std::isdigit(s[pos + i])) { return Status::UserError( - "Non-digit character '{}' is not allowed in the exponent part.", - s[pos + i]); + "Non-digit character is not allowed in the exponent part."); } } out.exponent = folly::to(folly::StringPiece(s + pos, size - pos)); return Status::OK(); } - return pos == size - ? Status::OK() - : Status::UserError( - "Chars '{}' are invalid.", std::string(s + pos, size - pos)); + return pos == size ? Status::OK() : Status::UserError("Chars are invalid."); } Status detail::parseHugeInt( diff --git a/velox/expression/fuzzer/SparkSpecialFormSignatureGenerator.cpp b/velox/expression/fuzzer/SparkSpecialFormSignatureGenerator.cpp index 0c1cef30722a..36a801990c7c 100644 --- a/velox/expression/fuzzer/SparkSpecialFormSignatureGenerator.cpp +++ b/velox/expression/fuzzer/SparkSpecialFormSignatureGenerator.cpp @@ -27,8 +27,10 @@ SparkSpecialFormSignatureGenerator::getSignaturesForCast() const { signatures.push_back(makeCastSignature(fromType, "varbinary")); } - // Cast tinyint/smallint/integer/bigint as timestamp is supported in Spark. - for (auto fromType : {"tinyint", "smallint", "integer", "bigint"}) { + // Cast tinyint/smallint/integer/bigint/real/double as timestamp is supported + // in Spark. + for (auto fromType : + {"tinyint", "smallint", "integer", "bigint", "real", "double"}) { signatures.push_back(makeCastSignature(fromType, "timestamp")); } return signatures; diff --git a/velox/expression/fuzzer/SpecialFormSignatureGenerator.cpp b/velox/expression/fuzzer/SpecialFormSignatureGenerator.cpp index 0428e613422f..950dd98fc07f 100644 --- a/velox/expression/fuzzer/SpecialFormSignatureGenerator.cpp +++ b/velox/expression/fuzzer/SpecialFormSignatureGenerator.cpp @@ -51,6 +51,14 @@ void SpecialFormSignatureGenerator::addCastFromFloatingPointSignatures( } } +void SpecialFormSignatureGenerator::addCastFromDecimalSignatures( + const std::string& toType, + std::vector& signatures) const { + for (const auto& fromType : kDecimalTypes_) { + signatures.push_back(makeCastSignature(fromType, toType)); + } +} + void SpecialFormSignatureGenerator::addCastFromVarcharSignature( const std::string& toType, std::vector& signatures) const { @@ -163,6 +171,7 @@ SpecialFormSignatureGenerator::getSignaturesForCast() const { for (const auto& toType : kIntegralTypes_) { addCastFromIntegralSignatures(toType, signatures); addCastFromFloatingPointSignatures(toType, signatures); + addCastFromDecimalSignatures(toType, signatures); addCastFromVarcharSignature(toType, signatures); } @@ -170,12 +179,22 @@ SpecialFormSignatureGenerator::getSignaturesForCast() const { for (const auto& toType : kFloatingPointTypes_) { addCastFromIntegralSignatures(toType, signatures); addCastFromFloatingPointSignatures(toType, signatures); + addCastFromDecimalSignatures(toType, signatures); + addCastFromVarcharSignature(toType, signatures); + } + + // To decimal type. + for (const auto& toType : kDecimalTypes_) { + addCastFromIntegralSignatures(toType, signatures); + addCastFromFloatingPointSignatures(toType, signatures); + addCastFromDecimalSignatures(toType, signatures); addCastFromVarcharSignature(toType, signatures); } // To varchar type. addCastFromIntegralSignatures("varchar", signatures); addCastFromFloatingPointSignatures("varchar", signatures); + addCastFromDecimalSignatures("varchar", signatures); addCastFromVarcharSignature("varchar", signatures); addCastFromDateSignature("varchar", signatures); addCastFromTimestampSignature("varchar", signatures); @@ -195,6 +214,11 @@ SpecialFormSignatureGenerator::getSignaturesForCast() const { auto from = signatures[i]->argumentTypes()[0].baseName(); auto to = signatures[i]->returnType().baseName(); + // Signature parsing of nested decimal type is not supported. + if (from == "DECIMAL" || to == "DECIMAL") { + continue; + } + signatures.push_back(makeCastSignature( fmt::format("array({})", from), fmt::format("array({})", to))); diff --git a/velox/expression/fuzzer/SpecialFormSignatureGenerator.h b/velox/expression/fuzzer/SpecialFormSignatureGenerator.h index 2b986872c77e..6882c4597f7d 100644 --- a/velox/expression/fuzzer/SpecialFormSignatureGenerator.h +++ b/velox/expression/fuzzer/SpecialFormSignatureGenerator.h @@ -45,8 +45,14 @@ class SpecialFormSignatureGenerator { const std::string& toType, std::vector& signatures) const; - /// Generates signatures for cast from varchar to the given type and adds them - /// to signatures. + /// Generates signatures for cast from decimal types to the given type + /// and adds them to signatures. + void addCastFromDecimalSignatures( + const std::string& toType, + std::vector& signatures) const; + + /// Generates signatures for cast from varchar to the given type and adds + /// them to signatures. void addCastFromVarcharSignature( const std::string& toType, std::vector& signatures) const; @@ -101,6 +107,12 @@ class SpecialFormSignatureGenerator { "boolean"}; const std::vector kFloatingPointTypes_{"real", "double"}; + + const std::vector kDecimalTypes_{ + "DECIMAL(6, 0)", + "DECIMAL(18, 6)", + "DECIMAL(38, 18)", + "DECIMAL(38, 38)"}; }; } // namespace facebook::velox::fuzzer diff --git a/velox/expression/tests/CastExprTest.cpp b/velox/expression/tests/CastExprTest.cpp index 198c8b34934d..89093b143770 100644 --- a/velox/expression/tests/CastExprTest.cpp +++ b/velox/expression/tests/CastExprTest.cpp @@ -2048,7 +2048,7 @@ TEST_F(CastExprTest, varcharToDecimal) { VARCHAR(), DECIMAL(38, 0), {"0.0444a"}, - "Cannot cast VARCHAR '0.0444a' to DECIMAL(38, 0). Value is not a number. Chars 'a' are invalid."); + "Cannot cast VARCHAR '0.0444a' to DECIMAL(38, 0). Value is not a number."); testThrow( VARCHAR(), @@ -2081,29 +2081,29 @@ TEST_F(CastExprTest, varcharToDecimal) { VARCHAR(), DECIMAL(38, 0), {"23e-5d"}, - "Cannot cast VARCHAR '23e-5d' to DECIMAL(38, 0). Value is not a number. Non-digit character 'd' is not allowed in the exponent part."); + "Cannot cast VARCHAR '23e-5d' to DECIMAL(38, 0). Value is not a number. Non-digit character is not allowed in the exponent part."); // Whitespaces. testThrow( VARCHAR(), DECIMAL(38, 0), {"1. 23"}, - "Cannot cast VARCHAR '1. 23' to DECIMAL(38, 0). Value is not a number. Chars ' 23' are invalid."); + "Cannot cast VARCHAR '1. 23' to DECIMAL(38, 0). Value is not a number."); testThrow( VARCHAR(), DECIMAL(12, 2), {"-3E+ 2"}, - "Cannot cast VARCHAR '-3E+ 2' to DECIMAL(12, 2). Value is not a number. Non-digit character ' ' is not allowed in the exponent part."); + "Cannot cast VARCHAR '-3E+ 2' to DECIMAL(12, 2). Value is not a number. Non-digit character is not allowed in the exponent part."); testThrow( VARCHAR(), DECIMAL(38, 0), {"1.23 "}, - "Cannot cast VARCHAR '1.23 ' to DECIMAL(38, 0). Value is not a number. Chars ' ' are invalid."); + "Cannot cast VARCHAR '1.23 ' to DECIMAL(38, 0). Value is not a number."); testThrow( VARCHAR(), DECIMAL(12, 2), {"-3E+2 "}, - "Cannot cast VARCHAR '-3E+2 ' to DECIMAL(12, 2). Value is not a number. Non-digit character ' ' is not allowed in the exponent part."); + "Cannot cast VARCHAR '-3E+2 ' to DECIMAL(12, 2). Value is not a number. Non-digit character is not allowed in the exponent part."); testThrow( VARCHAR(), DECIMAL(38, 0), @@ -2119,7 +2119,7 @@ TEST_F(CastExprTest, varcharToDecimal) { VARCHAR(), DECIMAL(12, 2), {"-3E+2.1"}, - "Cannot cast VARCHAR '-3E+2.1' to DECIMAL(12, 2). Value is not a number. Non-digit character '.' is not allowed in the exponent part."); + "Cannot cast VARCHAR '-3E+2.1' to DECIMAL(12, 2). Value is not a number. Non-digit character is not allowed in the exponent part."); testThrow( VARCHAR(), @@ -2132,6 +2132,18 @@ TEST_F(CastExprTest, varcharToDecimal) { DECIMAL(12, 2), {"-3E-"}, "Cannot cast VARCHAR '-3E-' to DECIMAL(12, 2). Value is not a number. The exponent part only contains sign."); + + testThrow( + VARCHAR(), + DECIMAL(12, 2), + {"9e"}, + "Cannot cast VARCHAR '9e' to DECIMAL(12, 2). Value is not a number. The exponent part is empty."); + + testThrow( + VARCHAR(), + DECIMAL(12, 2), + {"09{xi+yD"}, + "Cannot cast VARCHAR '09{xi+yD' to DECIMAL(12, 2). Value is not a number. Chars are invalid."); } TEST_F(CastExprTest, castInTry) {