1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 13:37:44 +00:00

LibSQL: Support 64-bit integer values and handle overflow errors

Currently, integers are stored in LibSQL as 32-bit signed integers, even
if the provided type is unsigned. This resulted in a series of unchecked
unsigned-to-signed conversions, and prevented storing 64-bit values.
Further, mathematical operations were performed without similar checks,
and without checking for overflow.

This changes SQL::Value to behave like SQLite for INTEGER types. In
SQLite, the INTEGER type does not imply a size or signedness of the
underlying type. Instead, SQLite determines on-the-fly what type is
needed as values are created and updated.

To do so, the SQL::Value variant can now hold an i64 or u64 integer. If
a specific type is requested, invalid conversions are now explictly an
error (e.g. converting a stored -1 to a u64 will fail). When binary
mathematical operations are performed, we now try to coerce the RHS
value to a type that works with the LHS value, failing the operation if
that isn't possible. Any overflow or invalid operation (e.g. bitshifting
a 64-bit value by more than 64 bytes) is an error.
This commit is contained in:
Timothy Flynn 2022-12-11 11:44:11 -05:00 committed by Tim Flynn
parent a1007c37a4
commit 72e41a7dbd
13 changed files with 1241 additions and 283 deletions

View file

@ -211,7 +211,7 @@ void insert_into_and_scan_btree(int num_keys)
if (prev.size()) {
EXPECT(prev < key);
}
auto key_value = key[0].to_int();
auto key_value = key[0].to_int<i32>();
for (auto ix = 0; ix < num_keys; ix++) {
if (keys[ix] == key_value) {
EXPECT_EQ(key.pointer(), pointers[ix]);

View file

@ -68,10 +68,10 @@ void verify_table_contents(SQL::Database& db, int expected_count)
EXPECT(!rows_or_error.is_error());
for (auto& row : rows_or_error.value()) {
StringBuilder builder;
builder.appendff("Test{}", row["IntColumn"].to_int().value());
builder.appendff("Test{}", row["IntColumn"].to_int<i32>().value());
EXPECT_EQ(row["TextColumn"].to_deprecated_string(), builder.build());
count++;
sum += row["IntColumn"].to_int().value();
sum += row["IntColumn"].to_int<i32>().value();
}
EXPECT_EQ(count, expected_count);
EXPECT_EQ(sum, (expected_count * (expected_count - 1)) / 2);

View file

@ -268,7 +268,7 @@ void insert_into_and_scan_hash_index(int num_keys)
int count = 0;
for (auto iter = hash_index->begin(); !iter.is_end(); iter++, count++) {
auto key = (*iter);
auto key_value = key[0].to_int();
auto key_value = key[0].to_int<i32>();
VERIFY(key_value.has_value());
for (auto ix = 0; ix < num_keys; ix++) {

View file

@ -105,7 +105,7 @@ TEST_CASE(insert_into_table)
EXPECT(!rows_or_error.is_error());
for (auto& row : rows_or_error.value()) {
EXPECT_EQ(row["TEXTCOLUMN"].to_deprecated_string(), "Test");
EXPECT_EQ(row["INTCOLUMN"].to_int().value(), 42);
EXPECT_EQ(row["INTCOLUMN"].to_int<i32>(), 42);
count++;
}
EXPECT_EQ(count, 1);
@ -318,7 +318,7 @@ TEST_CASE(select_with_where)
result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable WHERE IntColumn > 44;");
EXPECT_EQ(result.size(), 2u);
for (auto& row : result) {
EXPECT(row.row[1].to_int().value() > 44);
EXPECT(row.row[1].to_int<i32>().value() > 44);
}
}
@ -348,10 +348,10 @@ TEST_CASE(select_cross_join)
EXPECT_EQ(result.size(), 25u);
for (auto& row : result) {
EXPECT(row.row.size() == 4);
EXPECT(row.row[1].to_int().value() >= 42);
EXPECT(row.row[1].to_int().value() <= 46);
EXPECT(row.row[3].to_int().value() >= 40);
EXPECT(row.row[3].to_int().value() <= 48);
EXPECT(row.row[1].to_int<i32>().value() >= 42);
EXPECT(row.row[1].to_int<i32>().value() <= 46);
EXPECT(row.row[3].to_int<i32>().value() >= 40);
EXPECT(row.row[3].to_int<i32>().value() <= 48);
}
}
@ -383,7 +383,7 @@ TEST_CASE(select_inner_join)
"WHERE TestTable1.IntColumn = TestTable2.IntColumn;");
EXPECT_EQ(result.size(), 1u);
EXPECT_EQ(result[0].row.size(), 3u);
EXPECT_EQ(result[0].row[0].to_int().value(), 42);
EXPECT_EQ(result[0].row[0].to_int<i32>(), 42);
EXPECT_EQ(result[0].row[1].to_deprecated_string(), "Test_1");
EXPECT_EQ(result[0].row[2].to_deprecated_string(), "Test_12");
}
@ -463,11 +463,11 @@ TEST_CASE(select_with_order)
result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;");
EXPECT_EQ(result.size(), 5u);
EXPECT_EQ(result[0].row[1].to_int().value(), 40);
EXPECT_EQ(result[1].row[1].to_int().value(), 41);
EXPECT_EQ(result[2].row[1].to_int().value(), 42);
EXPECT_EQ(result[3].row[1].to_int().value(), 44);
EXPECT_EQ(result[4].row[1].to_int().value(), 47);
EXPECT_EQ(result[0].row[1].to_int<i32>(), 40);
EXPECT_EQ(result[1].row[1].to_int<i32>(), 41);
EXPECT_EQ(result[2].row[1].to_int<i32>(), 42);
EXPECT_EQ(result[3].row[1].to_int<i32>(), 44);
EXPECT_EQ(result[4].row[1].to_int<i32>(), 47);
result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY TextColumn;");
EXPECT_EQ(result.size(), 5u);
@ -547,15 +547,15 @@ TEST_CASE(select_with_order_two_columns)
result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY TextColumn, IntColumn;");
EXPECT_EQ(result.size(), 5u);
EXPECT_EQ(result[0].row[0].to_deprecated_string(), "Test_1");
EXPECT_EQ(result[0].row[1].to_int().value(), 47);
EXPECT_EQ(result[0].row[1].to_int<i32>(), 47);
EXPECT_EQ(result[1].row[0].to_deprecated_string(), "Test_2");
EXPECT_EQ(result[1].row[1].to_int().value(), 40);
EXPECT_EQ(result[1].row[1].to_int<i32>(), 40);
EXPECT_EQ(result[2].row[0].to_deprecated_string(), "Test_2");
EXPECT_EQ(result[2].row[1].to_int().value(), 42);
EXPECT_EQ(result[2].row[1].to_int<i32>(), 42);
EXPECT_EQ(result[3].row[0].to_deprecated_string(), "Test_4");
EXPECT_EQ(result[3].row[1].to_int().value(), 41);
EXPECT_EQ(result[3].row[1].to_int<i32>(), 41);
EXPECT_EQ(result[4].row[0].to_deprecated_string(), "Test_5");
EXPECT_EQ(result[4].row[1].to_int().value(), 44);
EXPECT_EQ(result[4].row[1].to_int<i32>(), 44);
}
TEST_CASE(select_with_order_by_column_not_in_result)
@ -626,16 +626,16 @@ TEST_CASE(select_with_order_limit_and_offset)
}
auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY IntColumn LIMIT 10 OFFSET 10;");
EXPECT_EQ(result.size(), 10u);
EXPECT_EQ(result[0].row[1].to_int().value(), 10);
EXPECT_EQ(result[1].row[1].to_int().value(), 11);
EXPECT_EQ(result[2].row[1].to_int().value(), 12);
EXPECT_EQ(result[3].row[1].to_int().value(), 13);
EXPECT_EQ(result[4].row[1].to_int().value(), 14);
EXPECT_EQ(result[5].row[1].to_int().value(), 15);
EXPECT_EQ(result[6].row[1].to_int().value(), 16);
EXPECT_EQ(result[7].row[1].to_int().value(), 17);
EXPECT_EQ(result[8].row[1].to_int().value(), 18);
EXPECT_EQ(result[9].row[1].to_int().value(), 19);
EXPECT_EQ(result[0].row[1].to_int<i32>(), 10);
EXPECT_EQ(result[1].row[1].to_int<i32>(), 11);
EXPECT_EQ(result[2].row[1].to_int<i32>(), 12);
EXPECT_EQ(result[3].row[1].to_int<i32>(), 13);
EXPECT_EQ(result[4].row[1].to_int<i32>(), 14);
EXPECT_EQ(result[5].row[1].to_int<i32>(), 15);
EXPECT_EQ(result[6].row[1].to_int<i32>(), 16);
EXPECT_EQ(result[7].row[1].to_int<i32>(), 17);
EXPECT_EQ(result[8].row[1].to_int<i32>(), 18);
EXPECT_EQ(result[9].row[1].to_int<i32>(), 19);
}
TEST_CASE(select_with_limit_out_of_bounds)
@ -707,7 +707,7 @@ TEST_CASE(binary_operator_execution)
auto const& result_row = result.at(i).row;
EXPECT_EQ(result_row.size(), 1u);
auto result_column = result_row[0].to_int();
auto result_column = result_row[0].to_int<i32>();
result_values.append(result_column.value());
}

View file

@ -19,8 +19,8 @@ TEST_CASE(null_value)
EXPECT_EQ(v.type(), SQL::SQLType::Null);
EXPECT_EQ(v.to_deprecated_string(), "(null)"sv);
EXPECT(!v.to_bool().has_value());
EXPECT(!v.to_int().has_value());
EXPECT(!v.to_u32().has_value());
EXPECT(!v.to_int<i32>().has_value());
EXPECT(!v.to_int<u32>().has_value());
EXPECT(!v.to_double().has_value());
}
@ -72,8 +72,8 @@ TEST_CASE(text_value_to_other_types)
SQL::Value v("42");
EXPECT_EQ(v.type(), SQL::SQLType::Text);
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().value(), 42);
EXPECT(v.to_int<i32>().has_value());
EXPECT_EQ(v.to_int<i32>().value(), 42);
EXPECT(v.to_double().has_value());
EXPECT((v.to_double().value() - 42.0) < NumericLimits<double>().epsilon());
@ -97,8 +97,8 @@ TEST_CASE(text_value_to_other_types)
EXPECT_EQ(v.type(), SQL::SQLType::Text);
EXPECT(!v.to_bool().has_value());
EXPECT(!v.to_int().has_value());
EXPECT(!v.to_u32().has_value());
EXPECT(!v.to_int<i32>().has_value());
EXPECT(!v.to_int<u32>().has_value());
EXPECT(!v.to_double().has_value());
}
{
@ -147,8 +147,8 @@ TEST_CASE(integer_value)
v = 42;
EXPECT_EQ(v.type(), SQL::SQLType::Integer);
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().value(), 42);
EXPECT(v.to_int<i32>().has_value());
EXPECT_EQ(v.to_int<i32>().value(), 42);
EXPECT_EQ(v.to_deprecated_string(), "42"sv);
EXPECT(v.to_double().has_value());
@ -161,8 +161,8 @@ TEST_CASE(integer_value)
SQL::Value v(0);
EXPECT_EQ(v.type(), SQL::SQLType::Integer);
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().value(), 0);
EXPECT(v.to_int<i32>().has_value());
EXPECT_EQ(v.to_int<i32>().value(), 0);
EXPECT(v.to_bool().has_value());
EXPECT(!v.to_bool().value());
@ -171,16 +171,16 @@ TEST_CASE(integer_value)
SQL::Value v(42);
EXPECT_EQ(v.type(), SQL::SQLType::Integer);
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().value(), 42);
EXPECT(v.to_int<i32>().has_value());
EXPECT_EQ(v.to_int<i32>().value(), 42);
}
{
SQL::Value text("42");
SQL::Value integer(SQL::SQLType::Integer);
integer = text;
EXPECT(integer.to_int().has_value());
EXPECT_EQ(integer.to_int().value(), 42);
EXPECT(integer.to_int<i32>().has_value());
EXPECT_EQ(integer.to_int<i32>().value(), 42);
}
}
@ -200,6 +200,52 @@ TEST_CASE(serialize_int_value)
EXPECT_EQ(v2, v);
}
TEST_CASE(serialize_downsized_int_value)
{
auto run_test_for_value = [](auto value) {
using T = decltype(value);
SQL::Value v(value);
SQL::Serializer serializer;
serializer.serialize(v);
serializer.rewind();
auto type_flags = serializer.deserialize<u8>();
auto type_data = type_flags & 0xf0;
auto type = static_cast<SQL::SQLType>(type_flags & 0x0f);
EXPECT_NE(type_data, 0);
EXPECT_EQ(type, SQL::SQLType::Integer);
auto deserialized = serializer.deserialize<T>();
EXPECT_EQ(deserialized, value);
};
run_test_for_value(NumericLimits<i8>::min());
run_test_for_value(NumericLimits<i8>::max());
run_test_for_value(NumericLimits<i16>::min());
run_test_for_value(NumericLimits<i16>::max());
run_test_for_value(NumericLimits<i32>::min());
run_test_for_value(NumericLimits<i32>::max());
run_test_for_value(NumericLimits<i64>::min());
run_test_for_value(NumericLimits<i64>::max());
run_test_for_value(NumericLimits<u8>::min());
run_test_for_value(NumericLimits<u8>::max());
run_test_for_value(NumericLimits<u16>::min());
run_test_for_value(NumericLimits<u16>::max());
run_test_for_value(NumericLimits<u32>::min());
run_test_for_value(NumericLimits<u32>::max());
run_test_for_value(NumericLimits<u64>::min());
run_test_for_value(NumericLimits<u64>::max());
}
TEST_CASE(float_value)
{
{
@ -213,8 +259,8 @@ TEST_CASE(float_value)
EXPECT(v.to_double().has_value());
EXPECT((v.to_double().value() - 3.14) < NumericLimits<double>().epsilon());
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().value(), 3);
EXPECT(v.to_int<i32>().has_value());
EXPECT_EQ(v.to_int<i32>().value(), 3);
EXPECT_EQ(v.to_deprecated_string(), "3.14");
EXPECT(v.to_bool().has_value());
@ -226,8 +272,8 @@ TEST_CASE(float_value)
EXPECT(v.to_double().has_value());
EXPECT(v.to_double().value() < NumericLimits<double>().epsilon());
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().value(), 0);
EXPECT(v.to_int<i32>().has_value());
EXPECT_EQ(v.to_int<i32>().value(), 0);
EXPECT_EQ(v.to_deprecated_string(), "0"sv);
EXPECT(v.to_bool().has_value());
@ -242,22 +288,22 @@ TEST_CASE(float_value)
SQL::Value v(3.51);
EXPECT_EQ(v.type(), SQL::SQLType::Float);
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().value(), 4);
EXPECT(v.to_int<i32>().has_value());
EXPECT_EQ(v.to_int<i32>().value(), 4);
}
{
SQL::Value v(-3.14);
EXPECT_EQ(v.type(), SQL::SQLType::Float);
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().value(), -3);
EXPECT(v.to_int<i32>().has_value());
EXPECT_EQ(v.to_int<i32>().value(), -3);
}
{
SQL::Value v(-3.51);
EXPECT_EQ(v.type(), SQL::SQLType::Float);
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().value(), -4);
EXPECT(v.to_int<i32>().has_value());
EXPECT_EQ(v.to_int<i32>().value(), -4);
}
}
@ -284,12 +330,79 @@ TEST_CASE(copy_value)
EXPECT_EQ(copy, "42"sv);
}
TEST_CASE(compare_text_to_int)
TEST_CASE(to_int)
{
SQL::Value text("42");
SQL::Value integer(42);
EXPECT_EQ(text, integer);
EXPECT_EQ(integer, text);
SQL::Value int_64 { static_cast<i64>(123) };
EXPECT_EQ(int_64.to_int<i8>(), 123);
EXPECT_EQ(int_64.to_int<i16>(), 123);
EXPECT_EQ(int_64.to_int<i32>(), 123);
EXPECT_EQ(int_64.to_int<u8>(), 123u);
EXPECT_EQ(int_64.to_int<u16>(), 123u);
EXPECT_EQ(int_64.to_int<u32>(), 123u);
EXPECT_EQ(int_64.to_int<u64>(), 123u);
SQL::Value uint_64 { static_cast<i64>(123) };
EXPECT_EQ(uint_64.to_int<i8>(), 123);
EXPECT_EQ(uint_64.to_int<i16>(), 123);
EXPECT_EQ(uint_64.to_int<i32>(), 123);
EXPECT_EQ(uint_64.to_int<i64>(), 123);
EXPECT_EQ(uint_64.to_int<u8>(), 123u);
EXPECT_EQ(uint_64.to_int<u16>(), 123u);
EXPECT_EQ(uint_64.to_int<u32>(), 123u);
}
TEST_CASE(to_int_failures)
{
SQL::Value large_int_64 { NumericLimits<i64>::max() };
EXPECT(!large_int_64.to_int<i8>().has_value());
EXPECT(!large_int_64.to_int<i16>().has_value());
EXPECT(!large_int_64.to_int<i32>().has_value());
EXPECT(!large_int_64.to_int<u8>().has_value());
EXPECT(!large_int_64.to_int<u16>().has_value());
EXPECT(!large_int_64.to_int<u32>().has_value());
SQL::Value large_int_32 { NumericLimits<i32>::max() };
EXPECT(!large_int_32.to_int<i8>().has_value());
EXPECT(!large_int_32.to_int<i16>().has_value());
EXPECT(!large_int_32.to_int<u8>().has_value());
EXPECT(!large_int_32.to_int<u16>().has_value());
SQL::Value small_int_64 { NumericLimits<i64>::min() };
EXPECT(!small_int_64.to_int<i8>().has_value());
EXPECT(!small_int_64.to_int<i16>().has_value());
EXPECT(!small_int_64.to_int<i32>().has_value());
EXPECT(!small_int_64.to_int<u8>().has_value());
EXPECT(!small_int_64.to_int<u16>().has_value());
EXPECT(!small_int_64.to_int<u32>().has_value());
EXPECT(!small_int_64.to_int<u64>().has_value());
SQL::Value small_int_32 { NumericLimits<i32>::min() };
EXPECT(!small_int_32.to_int<i8>().has_value());
EXPECT(!small_int_32.to_int<i16>().has_value());
EXPECT(!small_int_32.to_int<u8>().has_value());
EXPECT(!small_int_32.to_int<u16>().has_value());
EXPECT(!small_int_32.to_int<u32>().has_value());
EXPECT(!small_int_32.to_int<u64>().has_value());
SQL::Value large_uint_64 { NumericLimits<u64>::max() };
EXPECT(!large_uint_64.to_int<i8>().has_value());
EXPECT(!large_uint_64.to_int<i16>().has_value());
EXPECT(!large_uint_64.to_int<i32>().has_value());
EXPECT(!large_uint_64.to_int<i64>().has_value());
EXPECT(!large_uint_64.to_int<u8>().has_value());
EXPECT(!large_uint_64.to_int<u16>().has_value());
EXPECT(!large_uint_64.to_int<u32>().has_value());
SQL::Value large_uint_32 { NumericLimits<u32>::max() };
EXPECT(!large_uint_32.to_int<i8>().has_value());
EXPECT(!large_uint_32.to_int<i16>().has_value());
EXPECT(!large_uint_32.to_int<u8>().has_value());
EXPECT(!large_uint_32.to_int<u16>().has_value());
}
TEST_CASE(bool_value)
@ -305,8 +418,8 @@ TEST_CASE(bool_value)
EXPECT(v.to_bool().has_value());
EXPECT(v.to_bool().value());
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().value(), 1);
EXPECT(v.to_int<i32>().has_value());
EXPECT_EQ(v.to_int<i32>().value(), 1);
EXPECT_EQ(v.to_deprecated_string(), "true"sv);
EXPECT(v.to_double().has_value());
@ -319,8 +432,8 @@ TEST_CASE(bool_value)
EXPECT(v.to_bool().has_value());
EXPECT(!v.to_bool().value());
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().value(), 0);
EXPECT(v.to_int<i32>().has_value());
EXPECT_EQ(v.to_int<i32>().value(), 0);
EXPECT_EQ(v.to_deprecated_string(), "false"sv);
EXPECT(v.to_double().has_value());
@ -333,8 +446,8 @@ TEST_CASE(bool_value)
EXPECT(v.to_bool().has_value());
EXPECT(v.to_bool().value());
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().value(), 1);
EXPECT(v.to_int<i32>().has_value());
EXPECT_EQ(v.to_int<i32>().value(), 1);
EXPECT_EQ(v.to_deprecated_string(), "true"sv);
EXPECT(v.to_double().has_value());
@ -567,3 +680,626 @@ TEST_CASE(compare_tuples)
EXPECT(tuple3 >= tuple1);
EXPECT(tuple3 > tuple1);
}
TEST_CASE(add)
{
{
SQL::Value value1 { 21 };
SQL::Value value2 { 42 };
auto result = value1.add(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 63);
}
{
SQL::Value value1 { 21 };
SQL::Value value2 { static_cast<u8>(42) };
auto result = value1.add(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 63);
}
{
SQL::Value value1 { static_cast<u8>(21) };
SQL::Value value2 { 42 };
auto result = value1.add(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 63);
}
{
SQL::Value value1 { static_cast<double>(21) };
SQL::Value value2 { 42 };
auto result = value1.add(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 63);
}
{
SQL::Value value1 { static_cast<double>(21.5) };
SQL::Value value2 { 42 };
auto result = value1.add(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Float);
EXPECT((result.value().to_double().value() - 63.5) < NumericLimits<double>().epsilon());
}
}
TEST_CASE(add_error)
{
{
// Fails to coerce value2 to the signedness of value1.
SQL::Value value1 { 1 };
SQL::Value value2 { NumericLimits<u64>::max() };
auto result = value1.add(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// Fails to coerce value2 to the signedness of value1.
SQL::Value value1 { static_cast<u64>(1) };
SQL::Value value2 { -1 };
auto result = value1.add(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// The operation itself would overflow.
SQL::Value value1 { static_cast<u64>(1) };
SQL::Value value2 { NumericLimits<u64>::max() };
auto result = value1.add(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// Cannot convert value to a number.
SQL::Value value1 { 1 };
SQL::Value value2 { "foo"sv };
auto result = value1.add(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch);
}
}
TEST_CASE(subtract)
{
{
SQL::Value value1 { 21 };
SQL::Value value2 { 42 };
auto result = value1.subtract(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), -21);
}
{
SQL::Value value1 { 21 };
SQL::Value value2 { static_cast<u8>(42) };
auto result = value1.subtract(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), -21);
}
{
SQL::Value value1 { static_cast<u8>(42) };
SQL::Value value2 { 21 };
auto result = value1.subtract(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 21);
}
{
SQL::Value value1 { static_cast<double>(21) };
SQL::Value value2 { 42 };
auto result = value1.subtract(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), -21);
}
{
SQL::Value value1 { static_cast<double>(21.5) };
SQL::Value value2 { 42 };
auto result = value1.subtract(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Float);
EXPECT((result.value().to_double().value() - 20.5) < NumericLimits<double>().epsilon());
}
}
TEST_CASE(subtract_error)
{
{
// Fails to coerce value2 to the signedness of value1.
SQL::Value value1 { 1 };
SQL::Value value2 { NumericLimits<u64>::max() };
auto result = value1.subtract(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// Fails to coerce value2 to the signedness of value1.
SQL::Value value1 { static_cast<u64>(1) };
SQL::Value value2 { -1 };
auto result = value1.subtract(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// The operation itself would overflow.
SQL::Value value1 { static_cast<u64>(0) };
SQL::Value value2 { static_cast<u64>(1) };
auto result = value1.subtract(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// Cannot convert value to a number.
SQL::Value value1 { 1 };
SQL::Value value2 { "foo"sv };
auto result = value1.subtract(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch);
}
}
TEST_CASE(multiply)
{
{
SQL::Value value1 { 2 };
SQL::Value value2 { 21 };
auto result = value1.multiply(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 42);
}
{
SQL::Value value1 { 2 };
SQL::Value value2 { static_cast<u8>(21) };
auto result = value1.multiply(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 42);
}
{
SQL::Value value1 { static_cast<u8>(2) };
SQL::Value value2 { 21 };
auto result = value1.multiply(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 42);
}
{
SQL::Value value1 { static_cast<double>(2) };
SQL::Value value2 { 21 };
auto result = value1.multiply(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 42);
}
{
SQL::Value value1 { static_cast<double>(2.5) };
SQL::Value value2 { 21 };
auto result = value1.multiply(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Float);
EXPECT((result.value().to_double().value() - 52.5) < NumericLimits<double>().epsilon());
}
}
TEST_CASE(multiply_error)
{
{
// Fails to coerce value2 to the signedness of value1.
SQL::Value value1 { 1 };
SQL::Value value2 { NumericLimits<u64>::max() };
auto result = value1.multiply(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// Fails to coerce value2 to the signedness of value1.
SQL::Value value1 { static_cast<u64>(1) };
SQL::Value value2 { -1 };
auto result = value1.multiply(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// The operation itself would overflow.
SQL::Value value1 { NumericLimits<i64>::max() };
SQL::Value value2 { 2 };
auto result = value1.multiply(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// Cannot convert value to a number.
SQL::Value value1 { 1 };
SQL::Value value2 { "foo"sv };
auto result = value1.multiply(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch);
}
}
TEST_CASE(divide)
{
{
SQL::Value value1 { 42 };
SQL::Value value2 { -2 };
auto result = value1.divide(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), -21);
}
{
SQL::Value value1 { 42 };
SQL::Value value2 { static_cast<u8>(2) };
auto result = value1.divide(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 21);
}
{
SQL::Value value1 { static_cast<u8>(42) };
SQL::Value value2 { 2 };
auto result = value1.divide(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 21);
}
{
SQL::Value value1 { static_cast<double>(42) };
SQL::Value value2 { 2 };
auto result = value1.divide(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 21);
}
{
SQL::Value value1 { static_cast<double>(43) };
SQL::Value value2 { 2 };
auto result = value1.divide(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Float);
EXPECT((result.value().to_double().value() - 21.5) < NumericLimits<double>().epsilon());
}
}
TEST_CASE(divide_error)
{
{
// The operation itself would overflow.
SQL::Value value1 { 1 };
SQL::Value value2 { 0 };
auto result = value1.divide(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// Cannot convert value to a number.
SQL::Value value1 { 1 };
SQL::Value value2 { "foo"sv };
auto result = value1.divide(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch);
}
}
TEST_CASE(modulo)
{
{
SQL::Value value1 { 21 };
SQL::Value value2 { 2 };
auto result = value1.modulo(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 1);
}
{
SQL::Value value1 { 21 };
SQL::Value value2 { static_cast<u8>(2) };
auto result = value1.modulo(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 1);
}
{
SQL::Value value1 { static_cast<u8>(21) };
SQL::Value value2 { 2 };
auto result = value1.modulo(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 1);
}
{
SQL::Value value1 { static_cast<double>(21) };
SQL::Value value2 { 2 };
auto result = value1.modulo(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 1);
}
}
TEST_CASE(modulo_error)
{
{
// Fails to coerce value2 to the signedness of value1.
SQL::Value value1 { 1 };
SQL::Value value2 { NumericLimits<u64>::max() };
auto result = value1.modulo(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// Fails to coerce value2 to the signedness of value1.
SQL::Value value1 { static_cast<u64>(1) };
SQL::Value value2 { -1 };
auto result = value1.modulo(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// The operation itself would overflow.
SQL::Value value1 { 21 };
SQL::Value value2 { 0 };
auto result = value1.modulo(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// Cannot convert value to an integer.
SQL::Value value1 { 1 };
SQL::Value value2 { "foo"sv };
auto result = value1.modulo(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch);
}
{
// Cannot convert value to an integer.
SQL::Value value1 { static_cast<double>(21.5) };
SQL::Value value2 { 2 };
auto result = value1.modulo(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch);
}
}
TEST_CASE(shift_left)
{
{
SQL::Value value1 { 0b0011'0000 };
SQL::Value value2 { 2 };
auto result = value1.shift_left(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 0b1100'0000);
}
{
SQL::Value value1 { 0b0011'0000 };
SQL::Value value2 { static_cast<u8>(2) };
auto result = value1.shift_left(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 0b1100'0000);
}
{
SQL::Value value1 { static_cast<u8>(0b0011'0000) };
SQL::Value value2 { 2 };
auto result = value1.shift_left(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 0b1100'0000);
}
{
SQL::Value value1 { static_cast<double>(0b0011'0000) };
SQL::Value value2 { 2 };
auto result = value1.shift_left(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 0b1100'0000);
}
}
TEST_CASE(shift_left_error)
{
{
// Fails to coerce value2 to the signedness of value1.
SQL::Value value1 { 1 };
SQL::Value value2 { NumericLimits<u64>::max() };
auto result = value1.shift_left(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// Fails to coerce value2 to the signedness of value1.
SQL::Value value1 { static_cast<u64>(1) };
SQL::Value value2 { -1 };
auto result = value1.shift_left(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// The operation itself would overflow.
SQL::Value value1 { 21 };
SQL::Value value2 { -1 };
auto result = value1.shift_left(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// The operation itself would overflow.
SQL::Value value1 { 21 };
SQL::Value value2 { 64 };
auto result = value1.shift_left(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// Cannot convert value to an integer.
SQL::Value value1 { 1 };
SQL::Value value2 { "foo"sv };
auto result = value1.shift_left(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch);
}
{
// Cannot convert value to an integer.
SQL::Value value1 { static_cast<double>(21.5) };
SQL::Value value2 { 2 };
auto result = value1.shift_left(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch);
}
}
TEST_CASE(shift_right)
{
{
SQL::Value value1 { 0b0011'0000 };
SQL::Value value2 { 2 };
auto result = value1.shift_right(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 0b0000'1100);
}
{
SQL::Value value1 { 0b0011'0000 };
SQL::Value value2 { static_cast<u8>(2) };
auto result = value1.shift_right(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 0b0000'1100);
}
{
SQL::Value value1 { static_cast<u8>(0b0011'0000) };
SQL::Value value2 { 2 };
auto result = value1.shift_right(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 0b0000'1100);
}
{
SQL::Value value1 { static_cast<double>(0b0011'0000) };
SQL::Value value2 { 2 };
auto result = value1.shift_right(value2);
EXPECT(!result.is_error());
EXPECT_EQ(result.value().type(), SQL::SQLType::Integer);
EXPECT_EQ(result.value(), 0b0000'1100);
}
}
TEST_CASE(shift_right_error)
{
{
// Fails to coerce value2 to the signedness of value1.
SQL::Value value1 { 1 };
SQL::Value value2 { NumericLimits<u64>::max() };
auto result = value1.shift_right(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// Fails to coerce value2 to the signedness of value1.
SQL::Value value1 { static_cast<u64>(1) };
SQL::Value value2 { -1 };
auto result = value1.shift_right(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// The operation itself would overflow.
SQL::Value value1 { 21 };
SQL::Value value2 { -1 };
auto result = value1.shift_right(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// The operation itself would overflow.
SQL::Value value1 { 21 };
SQL::Value value2 { 64 };
auto result = value1.shift_right(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow);
}
{
// Cannot convert value to an integer.
SQL::Value value1 { 1 };
SQL::Value value2 { "foo"sv };
auto result = value1.shift_right(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch);
}
{
// Cannot convert value to an integer.
SQL::Value value1 { static_cast<double>(21.5) };
SQL::Value value2 { 2 };
auto result = value1.shift_right(value2);
EXPECT(result.is_error());
EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch);
}
}