334 lines
16 KiB
C++
334 lines
16 KiB
C++
#include "catch.hpp"
|
|
#include "duckdb/common/operator/cast_operators.hpp"
|
|
#include "duckdb/common/string_util.hpp"
|
|
#include "duckdb/common/limits.hpp"
|
|
#include "duckdb/common/types.hpp"
|
|
#include "duckdb/common/types/vector.hpp"
|
|
#include "duckdb/common/vector.hpp"
|
|
|
|
using namespace duckdb; // NOLINT
|
|
using namespace std; // NOLINT
|
|
|
|
template <class SRC, class DST>
|
|
struct ExpectedNumericCast {
|
|
static inline DST Operation(SRC value) {
|
|
return (DST)value;
|
|
}
|
|
};
|
|
|
|
template <class DST>
|
|
struct ExpectedNumericCast<double, DST> {
|
|
static inline DST Operation(double value) {
|
|
return (DST)nearbyint(value);
|
|
}
|
|
};
|
|
|
|
template <class DST>
|
|
struct ExpectedNumericCast<float, DST> {
|
|
static inline DST Operation(float value) {
|
|
return (DST)nearbyintf(value);
|
|
}
|
|
};
|
|
|
|
template <class SRC, class DST>
|
|
static void TestNumericCast(duckdb::vector<SRC> &working_values, duckdb::vector<SRC> &broken_values) {
|
|
DST result;
|
|
for (auto value : working_values) {
|
|
REQUIRE_NOTHROW(Cast::Operation<SRC, DST>(value) == (DST)value);
|
|
REQUIRE(TryCast::Operation<SRC, DST>(value, result));
|
|
REQUIRE(result == ExpectedNumericCast<SRC, DST>::Operation(value));
|
|
}
|
|
for (auto value : broken_values) {
|
|
REQUIRE_THROWS(Cast::Operation<SRC, DST>(value));
|
|
REQUIRE(!TryCast::Operation<SRC, DST>(value, result));
|
|
}
|
|
}
|
|
|
|
template <class DST>
|
|
static void TestStringCast(duckdb::vector<string> &working_values, duckdb::vector<DST> &expected_values,
|
|
duckdb::vector<string> &broken_values) {
|
|
DST result;
|
|
for (idx_t i = 0; i < working_values.size(); i++) {
|
|
auto &value = working_values[i];
|
|
auto expected_value = expected_values[i];
|
|
REQUIRE_NOTHROW(Cast::Operation<string_t, DST>(string_t(value)) == expected_value);
|
|
REQUIRE(TryCast::Operation<string_t, DST>(string_t(value), result));
|
|
REQUIRE(result == expected_value);
|
|
|
|
StringUtil::Trim(value);
|
|
duckdb::vector<string> splits;
|
|
splits = StringUtil::Split(value, 'e');
|
|
if (splits.size() > 1 || value[0] == '+') {
|
|
continue;
|
|
}
|
|
splits = StringUtil::Split(value, '.');
|
|
REQUIRE(ConvertToString::Operation<DST>(result) == splits[0]);
|
|
}
|
|
for (auto &value : broken_values) {
|
|
REQUIRE_THROWS(Cast::Operation<string_t, DST>(string_t(value)));
|
|
REQUIRE(!TryCast::Operation<string_t, DST>(string_t(value), result));
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
static void TestExponent() {
|
|
T parse_result;
|
|
string str;
|
|
double value = 1;
|
|
T expected_value = 1;
|
|
for (idx_t exponent = 0; exponent < 100; exponent++) {
|
|
if (value < (double)NumericLimits<T>::Maximum()) {
|
|
// expect success
|
|
str = "1e" + to_string(exponent);
|
|
REQUIRE(TryCast::Operation<string_t, T>(string_t(str), parse_result));
|
|
REQUIRE(parse_result == expected_value);
|
|
str = "-1e" + to_string(exponent);
|
|
REQUIRE(TryCast::Operation<string_t, T>(string_t(str), parse_result));
|
|
REQUIRE(parse_result == -expected_value);
|
|
value *= 10;
|
|
// check again because otherwise this overflows
|
|
if (value < (double)NumericLimits<T>::Maximum()) {
|
|
expected_value *= 10;
|
|
}
|
|
} else {
|
|
// expect failure
|
|
str = "1e" + to_string(exponent);
|
|
REQUIRE(!TryCast::Operation<string_t, T>(string_t(str), parse_result));
|
|
str = "-1e" + to_string(exponent);
|
|
REQUIRE(!TryCast::Operation<string_t, T>(string_t(str), parse_result));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Test casting to boolean", "[cast]") {
|
|
duckdb::vector<string> working_values = {"true", "false", "TRUE", "FALSE", "T", "F", "1", "0", "False", "True"};
|
|
duckdb::vector<bool> expected_values = {true, false, true, false, true, false, true, false, false, true};
|
|
duckdb::vector<string> broken_values = {"304", "1002", "blabla", "", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"};
|
|
|
|
bool result;
|
|
for (idx_t i = 0; i < working_values.size(); i++) {
|
|
auto &value = working_values[i];
|
|
auto expected_value = expected_values[i];
|
|
REQUIRE_NOTHROW(Cast::Operation<string_t, bool>(value) == expected_value);
|
|
REQUIRE(TryCast::Operation<string_t, bool>(value, result));
|
|
REQUIRE(result == expected_value);
|
|
}
|
|
for (auto &value : broken_values) {
|
|
REQUIRE_THROWS(Cast::Operation<string_t, bool>(value));
|
|
REQUIRE(!TryCast::Operation<string_t, bool>(value, result));
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Test casting to int8_t", "[cast]") {
|
|
// int16_t -> int8_t
|
|
duckdb::vector<int16_t> working_values_int16 = {10, -10, 127, -128};
|
|
duckdb::vector<int16_t> broken_values_int16 = {128, -129, 1000, -1000};
|
|
TestNumericCast<int16_t, int8_t>(working_values_int16, broken_values_int16);
|
|
// int32_t -> int8_t
|
|
duckdb::vector<int32_t> working_values_int32 = {10, -10, 127, -128};
|
|
duckdb::vector<int32_t> broken_values_int32 = {128, -129, 1000000, -1000000};
|
|
TestNumericCast<int32_t, int8_t>(working_values_int32, broken_values_int32);
|
|
// int64_t -> int8_t
|
|
duckdb::vector<int64_t> working_values_int64 = {10, -10, 127, -128};
|
|
duckdb::vector<int64_t> broken_values_int64 = {128, -129, 10000000000LL, -10000000000LL};
|
|
TestNumericCast<int64_t, int8_t>(working_values_int64, broken_values_int64);
|
|
// float -> int8_t
|
|
duckdb::vector<float> working_values_float = {10, -10, 127, -128, 1.3f, -2.7f};
|
|
duckdb::vector<float> broken_values_float = {128, -129, 10000000000.0f, -10000000000.0f, 1e30f, -1e30f};
|
|
TestNumericCast<float, int8_t>(working_values_float, broken_values_float);
|
|
// double -> int8_t
|
|
duckdb::vector<double> working_values_double = {10, -10, 127, -128, 1.3, -2.7};
|
|
duckdb::vector<double> broken_values_double = {128, -129, 10000000000.0, -10000000000.0, 1e100, -1e100};
|
|
TestNumericCast<double, int8_t>(working_values_double, broken_values_double);
|
|
// string -> int8_t
|
|
duckdb::vector<string> working_values_str = {"10", "+10", "-10", "127", "-128", "1.3", "1e2",
|
|
"2e1", "2e0", "20e-1", "1.", " 3", " 3 ", "\t3 \t \n"};
|
|
duckdb::vector<int8_t> expected_values_str = {10, 10, -10, 127, -128, 1, 100, 20, 2, 2, 1, 3, 3, 3};
|
|
duckdb::vector<string> broken_values_str = {"128",
|
|
"-129",
|
|
"10000000000000000000000000000000000000000000000000000000000000",
|
|
"aaaa",
|
|
"19A",
|
|
"",
|
|
"1e3",
|
|
"1e",
|
|
"1e-",
|
|
"1e100",
|
|
"1e100000000",
|
|
"10000e-1",
|
|
" 3 2",
|
|
"+"};
|
|
TestStringCast<int8_t>(working_values_str, expected_values_str, broken_values_str);
|
|
TestExponent<int8_t>();
|
|
}
|
|
|
|
TEST_CASE("Test casting to int16_t", "[cast]") {
|
|
// int32_t -> int16_t
|
|
duckdb::vector<int32_t> working_values_int32 = {10, -10, 127, -127, 32767, -32768};
|
|
duckdb::vector<int32_t> broken_values_int32 = {32768, -32769, 1000000, -1000000};
|
|
TestNumericCast<int32_t, int16_t>(working_values_int32, broken_values_int32);
|
|
// int64_t -> int16_t
|
|
duckdb::vector<int64_t> working_values_int64 = {10, -10, 127, -127, 32767, -32768};
|
|
duckdb::vector<int64_t> broken_values_int64 = {32768, -32769, 10000000000LL, -10000000000LL};
|
|
TestNumericCast<int64_t, int16_t>(working_values_int64, broken_values_int64);
|
|
// float -> int16_t
|
|
duckdb::vector<float> working_values_float = {10.0f, -10.0f, 32767.0f, -32768.0f, 1.3f, -2.7f};
|
|
duckdb::vector<float> broken_values_float = {32768.0f, -32769.0f, 10000000000.0f, -10000000000.0f, 1e30f, -1e30f};
|
|
TestNumericCast<float, int16_t>(working_values_float, broken_values_float);
|
|
// double -> int16_t
|
|
duckdb::vector<double> working_values_double = {10, -10, 32767, -32768, 1.3, -2.7};
|
|
duckdb::vector<double> broken_values_double = {32768, -32769, 10000000000.0, -10000000000.0, 1e100, -1e100};
|
|
TestNumericCast<double, int16_t>(working_values_double, broken_values_double);
|
|
// string -> int16_t
|
|
duckdb::vector<string> working_values_str = {"10", "-10", "32767", "-32768", "1.3",
|
|
"3e4", "250e2", "3e+4", "3e0", "30e-1"};
|
|
duckdb::vector<int16_t> expected_values_str = {10, -10, 32767, -32768, 1, 30000, 25000, 30000, 3, 3};
|
|
duckdb::vector<string> broken_values_str = {
|
|
"32768", "-32769", "10000000000000000000000000000000000000000000000000000000000000",
|
|
"aaaa", "19A", "",
|
|
"1.A", "1e", "1e-",
|
|
"1e100", "1e100000000", "+"};
|
|
TestStringCast<int16_t>(working_values_str, expected_values_str, broken_values_str);
|
|
TestExponent<int16_t>();
|
|
}
|
|
|
|
TEST_CASE("Test casting to int32_t", "[cast]") {
|
|
// int64_t -> int32_t
|
|
duckdb::vector<int64_t> working_values_int64 = {10, -10, 127, -127, 32767, -32768, 2147483647LL, -2147483648LL};
|
|
duckdb::vector<int64_t> broken_values_int64 = {2147483648LL, -2147483649LL, 10000000000LL, -10000000000LL};
|
|
TestNumericCast<int64_t, int32_t>(working_values_int64, broken_values_int64);
|
|
// float -> int32_t
|
|
duckdb::vector<float> working_values_float = {10.0f, -10.0f, 2000000000.0f, -2000000000.0f, 1.3f, -2.7f};
|
|
duckdb::vector<float> broken_values_float = {3000000000.0f, -3000000000.0f, 10000000000.0f,
|
|
-10000000000.0f, 1e30f, -1e30f};
|
|
TestNumericCast<float, int32_t>(working_values_float, broken_values_float);
|
|
// double -> int32_t
|
|
duckdb::vector<double> working_values_double = {10, -10, 32767.0, -32768.0, 1.3, -2.7, 2147483647.0, -2147483648.0};
|
|
duckdb::vector<double> broken_values_double = {2147483648.0, -2147483649.0, 10000000000.0,
|
|
-10000000000.0, 1e100, -1e100};
|
|
TestNumericCast<double, int32_t>(working_values_double, broken_values_double);
|
|
// string -> int32_t
|
|
duckdb::vector<string> working_values_str = {"10", "-10", "2147483647", "-2147483647", "1.3", "-1.3", "1e6"};
|
|
duckdb::vector<int32_t> expected_values_str = {10, -10, 2147483647, -2147483647, 1, -1, 1000000};
|
|
duckdb::vector<string> broken_values_str = {
|
|
"2147483648", "-2147483649", "10000000000000000000000000000000000000000000000000000000000000",
|
|
"aaaa", "19A", "",
|
|
"1.A", "1e1e1e1"};
|
|
TestStringCast<int32_t>(working_values_str, expected_values_str, broken_values_str);
|
|
TestExponent<int32_t>();
|
|
}
|
|
|
|
TEST_CASE("Test casting to int64_t", "[cast]") {
|
|
// float -> int64_t
|
|
duckdb::vector<float> working_values_float = {10.0f,
|
|
-10.0f,
|
|
32767.0f,
|
|
-32768.0f,
|
|
1.3f,
|
|
-2.7f,
|
|
2000000000.0f,
|
|
-2000000000.0f,
|
|
4000000000000000000.0f,
|
|
-4000000000000000000.0f};
|
|
duckdb::vector<float> broken_values_float = {20000000000000000000.0f, -20000000000000000000.0f, 1e30f, -1e30f};
|
|
TestNumericCast<float, int64_t>(working_values_float, broken_values_float);
|
|
// double -> int64_t
|
|
duckdb::vector<double> working_values_double = {
|
|
10, -10, 32767, -32768, 1.3, -2.7, 2147483647, -2147483648.0, 4611686018427387904.0, -4611686018427387904.0};
|
|
duckdb::vector<double> broken_values_double = {18446744073709551616.0, -18446744073709551617.0, 1e100, -1e100};
|
|
TestNumericCast<double, int64_t>(working_values_double, broken_values_double);
|
|
// string -> int64_t
|
|
duckdb::vector<string> working_values_str = {
|
|
"10", "-10", "9223372036854775807", "-9223372036854775807", "1.3", "-9223372036854775807.1293813", "1e18",
|
|
"1e+18", "1."};
|
|
duckdb::vector<int64_t> expected_values_str = {10,
|
|
-10,
|
|
9223372036854775807LL,
|
|
-9223372036854775807LL,
|
|
1,
|
|
-9223372036854775807LL,
|
|
1000000000000000000LL,
|
|
1000000000000000000LL,
|
|
1};
|
|
duckdb::vector<string> broken_values_str = {"9223372036854775808",
|
|
"-9223372036854775809",
|
|
"10000000000000000000000000000000000000000000000000000000000000",
|
|
"aaaa",
|
|
"19A",
|
|
"",
|
|
"1.A",
|
|
"1.2382398723A",
|
|
"1e++1",
|
|
"1e+1+1",
|
|
"1e+1-1",
|
|
"+"};
|
|
TestStringCast<int64_t>(working_values_str, expected_values_str, broken_values_str);
|
|
TestExponent<int64_t>();
|
|
}
|
|
|
|
template <class DST>
|
|
static void TestStringCastDouble(duckdb::vector<string> &working_values, duckdb::vector<DST> &expected_values,
|
|
duckdb::vector<string> &broken_values) {
|
|
DST result;
|
|
for (idx_t i = 0; i < working_values.size(); i++) {
|
|
auto &value = working_values[i];
|
|
auto expected_value = expected_values[i];
|
|
REQUIRE_NOTHROW(Cast::Operation<string_t, DST>(string_t(value)) == expected_value);
|
|
REQUIRE(TryCast::Operation<string_t, DST>(string_t(value), result));
|
|
REQUIRE(ApproxEqual(result, expected_value));
|
|
|
|
auto to_str_and_back =
|
|
Cast::Operation<string_t, DST>(string_t(ConvertToString::Operation<DST>(expected_value)));
|
|
REQUIRE(ApproxEqual(to_str_and_back, expected_value));
|
|
}
|
|
for (auto &value : broken_values) {
|
|
REQUIRE_THROWS(Cast::Operation<string_t, DST>(string_t(value)));
|
|
REQUIRE(!TryCast::Operation<string_t, DST>(string_t(value), result));
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Test casting to float", "[cast]") {
|
|
// string -> float
|
|
duckdb::vector<string> working_values = {
|
|
"1.3", "1.34514", "1e10", "1e-2", "-1e-1", "1.1781237378938173987123987123981723981723981723987123",
|
|
"1.123456789", "1."};
|
|
duckdb::vector<float> expected_values = {
|
|
1.3f, 1.34514f, 1e10f, 1e-2f, -1e-1f, 1.1781237378938173987123987123981723981723981723987123f,
|
|
1.123456789f, 1.0f};
|
|
duckdb::vector<string> broken_values = {
|
|
"-", "", "aaa",
|
|
"12aaa", "1e10e10", "1e",
|
|
"1e-", "1e10a", "1.1781237378938173987123987123981723981723981723934834583490587123w",
|
|
"1.2.3"};
|
|
TestStringCastDouble<float>(working_values, expected_values, broken_values);
|
|
}
|
|
|
|
TEST_CASE("Test casting to double", "[cast]") {
|
|
// string -> double
|
|
duckdb::vector<string> working_values = {"1.3",
|
|
"+1.3",
|
|
"1.34514",
|
|
"1e10",
|
|
"1e-2",
|
|
"-1e-1",
|
|
"1.1781237378938173987123987123981723981723981723987123",
|
|
"1.123456789",
|
|
"1.",
|
|
"-1.2",
|
|
"-1.2e1",
|
|
" 1.2 ",
|
|
" 1.2e2 ",
|
|
" \t 1.2e2 \t"};
|
|
duckdb::vector<double> expected_values = {
|
|
1.3, 1.3, 1.34514, 1e10, 1e-2, -1e-1, 1.1781237378938173987123987123981723981723981723987123,
|
|
1.123456789, 1.0, -1.2, -12, 1.2, 120, 120};
|
|
duckdb::vector<string> broken_values = {
|
|
"-", "", "aaa",
|
|
"12aaa", "1e10e10", "1e",
|
|
"1e-", "1e10a", "1.1781237378938173987123987123981723981723981723934834583490587123w",
|
|
"1.2.3", "1.222.", "1..",
|
|
"1 . 2", "1. 2", "1.2 e20",
|
|
"+"};
|
|
TestStringCastDouble<double>(working_values, expected_values, broken_values);
|
|
}
|