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);
}
}

View file

@ -128,15 +128,7 @@ ResultOr<Value> UnaryOperatorExpression::evaluate(ExecutionContext& context) con
return expression_value;
return Result { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, UnaryOperator_name(type()) };
case UnaryOperator::Minus:
if (expression_value.type() == SQLType::Integer) {
expression_value = -expression_value.to_int().value();
return expression_value;
}
if (expression_value.type() == SQLType::Float) {
expression_value = -expression_value.to_double().value();
return expression_value;
}
return Result { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, UnaryOperator_name(type()) };
return expression_value.negate();
case UnaryOperator::Not:
if (expression_value.type() == SQLType::Boolean) {
expression_value = !expression_value.to_bool().value();
@ -144,11 +136,7 @@ ResultOr<Value> UnaryOperatorExpression::evaluate(ExecutionContext& context) con
}
return Result { SQLCommand::Unknown, SQLErrorCode::BooleanOperatorTypeMismatch, UnaryOperator_name(type()) };
case UnaryOperator::BitwiseNot:
if (expression_value.type() == SQLType::Integer) {
expression_value = ~expression_value.to_u32().value();
return expression_value;
}
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOperatorTypeMismatch, UnaryOperator_name(type()) };
return expression_value.bitwise_not();
default:
VERIFY_NOT_REACHED();
}

View file

@ -119,7 +119,7 @@ ResultOr<ResultSet> Select::execute(ExecutionContext& context) const
auto limit = TRY(m_limit_clause->limit_expression()->evaluate(context));
if (!limit.is_null()) {
auto limit_value_maybe = limit.to_u32();
auto limit_value_maybe = limit.to_int<size_t>();
if (!limit_value_maybe.has_value())
return Result { SQLCommand::Select, SQLErrorCode::SyntaxError, "LIMIT clause must evaluate to an integer value"sv };
@ -129,7 +129,7 @@ ResultOr<ResultSet> Select::execute(ExecutionContext& context) const
if (m_limit_clause->offset_expression() != nullptr) {
auto offset = TRY(m_limit_clause->offset_expression()->evaluate(context));
if (!offset.is_null()) {
auto offset_value_maybe = offset.to_u32();
auto offset_value_maybe = offset.to_int<size_t>();
if (!offset_value_maybe.has_value())
return Result { SQLCommand::Select, SQLErrorCode::SyntaxError, "OFFSET clause must evaluate to an integer value"sv };

View file

@ -165,7 +165,7 @@ ResultOr<NonnullRefPtr<TableDef>> Database::get_table(DeprecatedString const& sc
auto table_hash = table_def->hash();
auto column_key = ColumnDef::make_key(table_def);
for (auto it = m_table_columns->find(column_key); !it.is_end() && ((*it)["table_hash"].to_u32().value() == table_hash); ++it)
for (auto it = m_table_columns->find(column_key); !it.is_end() && ((*it)["table_hash"].to_int<u32>() == table_hash); ++it)
table_def->append_column(*it);
return table_def;

View file

@ -33,7 +33,7 @@ class Heap : public Core::Object {
C_OBJECT(Heap);
public:
static constexpr inline u32 current_version = 2;
static constexpr inline u32 current_version = 3;
virtual ~Heap() override;

View file

@ -59,9 +59,9 @@ Key ColumnDef::key() const
{
auto key = Key(index_def());
key["table_hash"] = parent_relation()->hash();
key["column_number"] = (int)column_number();
key["column_number"] = column_number();
key["column_name"] = name();
key["column_type"] = (int)type();
key["column_type"] = to_underlying(type());
return key;
}
@ -183,7 +183,7 @@ void TableDef::append_column(DeprecatedString name, SQLType sql_type)
void TableDef::append_column(Key const& column)
{
auto column_type = column["column_type"].to_int();
auto column_type = column["column_type"].to_int<UnderlyingType<SQLType>>();
VERIFY(column_type.has_value());
append_column(column["column_name"].to_deprecated_string(), static_cast<SQLType>(*column_type));

View file

@ -48,6 +48,7 @@ constexpr char const* command_tag(SQLCommand command)
S(DatabaseDoesNotExist, "Database '{}' does not exist") \
S(DatabaseUnavailable, "Database Unavailable") \
S(IntegerOperatorTypeMismatch, "Cannot apply '{}' operator to non-numeric operands") \
S(IntegerOverflow, "Operation would cause integer overflow") \
S(InternalError, "{}") \
S(InvalidDatabaseName, "Invalid database name '{}'") \
S(InvalidNumberOfPlaceholderValues, "Number of values does not match number of placeholders") \

View file

@ -12,11 +12,87 @@
#include <LibSQL/Serializer.h>
#include <LibSQL/TupleDescriptor.h>
#include <LibSQL/Value.h>
#include <math.h>
#include <string.h>
namespace SQL {
// We use the upper 4 bits of the encoded type to store extra information about the type. This
// includes if the value is null, and the encoded size of any integer type. Of course, this encoding
// only works if the SQL type itself fits in the lower 4 bits.
enum class SQLTypeWithCount {
#undef __ENUMERATE_SQL_TYPE
#define __ENUMERATE_SQL_TYPE(name, type) type,
ENUMERATE_SQL_TYPES(__ENUMERATE_SQL_TYPE)
#undef __ENUMERATE_SQL_TYPE
Count,
};
static_assert(to_underlying(SQLTypeWithCount::Count) <= 0x0f, "Too many SQL types for current encoding");
// Adding to this list is fine, but changing the order of any value here will result in LibSQL
// becoming unable to read existing .db files. If the order must absolutely be changed, be sure
// to bump Heap::current_version.
enum class TypeData : u8 {
Null = 1 << 4,
Int8 = 2 << 4,
Int16 = 3 << 4,
Int32 = 4 << 4,
Int64 = 5 << 4,
Uint8 = 6 << 4,
Uint16 = 7 << 4,
Uint32 = 8 << 4,
Uint64 = 9 << 4,
};
template<typename Callback>
static decltype(auto) downsize_integer(Integer auto value, Callback&& callback)
{
if constexpr (IsSigned<decltype(value)>) {
if (AK::is_within_range<i8>(value))
return callback(static_cast<i8>(value), TypeData::Int8);
if (AK::is_within_range<i16>(value))
return callback(static_cast<i16>(value), TypeData::Int16);
if (AK::is_within_range<i32>(value))
return callback(static_cast<i32>(value), TypeData::Int32);
return callback(value, TypeData::Int64);
} else {
if (AK::is_within_range<u8>(value))
return callback(static_cast<i8>(value), TypeData::Uint8);
if (AK::is_within_range<u16>(value))
return callback(static_cast<i16>(value), TypeData::Uint16);
if (AK::is_within_range<u32>(value))
return callback(static_cast<i32>(value), TypeData::Uint32);
return callback(value, TypeData::Uint64);
}
}
template<typename Callback>
static decltype(auto) downsize_integer(Value const& value, Callback&& callback)
{
VERIFY(value.is_int());
if (value.value().has<i64>())
return downsize_integer(value.value().get<i64>(), forward<Callback>(callback));
return downsize_integer(value.value().get<u64>(), forward<Callback>(callback));
}
template<typename Callback>
static ResultOr<Value> perform_integer_operation(Value const& lhs, Value const& rhs, Callback&& callback)
{
VERIFY(lhs.is_int());
VERIFY(rhs.is_int());
if (lhs.value().has<i64>()) {
if (auto rhs_value = rhs.to_int<i64>(); rhs_value.has_value())
return callback(lhs.to_int<i64>().value(), rhs_value.value());
} else {
if (auto rhs_value = rhs.to_int<u64>(); rhs_value.has_value())
return callback(lhs.to_int<u64>().value(), rhs_value.value());
}
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
}
Value::Value(SQLType type)
: m_type(type)
{
@ -28,22 +104,23 @@ Value::Value(DeprecatedString value)
{
}
Value::Value(int value)
: m_type(SQLType::Integer)
, m_value(value)
{
}
Value::Value(u32 value)
: m_type(SQLType::Integer)
, m_value(static_cast<int>(value)) // FIXME: Handle signed overflow.
{
}
Value::Value(double value)
: m_type(SQLType::Float)
, m_value(value)
{
if (trunc(value) == value) {
if (AK::is_within_range<i64>(value)) {
m_type = SQLType::Integer;
m_value = static_cast<i64>(value);
return;
}
if (AK::is_within_range<u64>(value)) {
m_type = SQLType::Integer;
m_value = static_cast<u64>(value);
return;
}
}
m_type = SQLType::Float;
m_value = value;
}
Value::Value(NonnullRefPtr<TupleDescriptor> descriptor, Vector<Value> values)
@ -122,6 +199,11 @@ bool Value::is_null() const
return !m_value.has_value();
}
bool Value::is_int() const
{
return m_value.has_value() && (m_value->has<i64>() || m_value->has<u64>());
}
DeprecatedString Value::to_deprecated_string() const
{
if (is_null())
@ -129,7 +211,7 @@ DeprecatedString Value::to_deprecated_string() const
return m_value->visit(
[](DeprecatedString const& value) -> DeprecatedString { return value; },
[](int value) -> DeprecatedString { return DeprecatedString::number(value); },
[](Integer auto value) -> DeprecatedString { return DeprecatedString::number(value); },
[](double value) -> DeprecatedString { return DeprecatedString::number(value); },
[](bool value) -> DeprecatedString { return value ? "true"sv : "false"sv; },
[](TupleValue const& value) -> DeprecatedString {
@ -143,33 +225,6 @@ DeprecatedString Value::to_deprecated_string() const
});
}
Optional<int> Value::to_int() const
{
if (is_null())
return {};
return m_value->visit(
[](DeprecatedString const& value) -> Optional<int> { return value.to_int(); },
[](int value) -> Optional<int> { return value; },
[](double value) -> Optional<int> {
if (value > static_cast<double>(NumericLimits<int>::max()))
return {};
if (value < static_cast<double>(NumericLimits<int>::min()))
return {};
return static_cast<int>(round(value));
},
[](bool value) -> Optional<int> { return static_cast<int>(value); },
[](TupleValue const&) -> Optional<int> { return {}; });
}
Optional<u32> Value::to_u32() const
{
// FIXME: Handle negative values.
if (auto result = to_int(); result.has_value())
return static_cast<u32>(result.value());
return {};
}
Optional<double> Value::to_double() const
{
if (is_null())
@ -184,7 +239,7 @@ Optional<double> Value::to_double() const
return {};
return result;
},
[](int value) -> Optional<double> { return static_cast<double>(value); },
[](Integer auto value) -> Optional<double> { return static_cast<double>(value); },
[](double value) -> Optional<double> { return value; },
[](bool value) -> Optional<double> { return static_cast<double>(value); },
[](TupleValue const&) -> Optional<double> { return {}; });
@ -203,7 +258,7 @@ Optional<bool> Value::to_bool() const
return false;
return {};
},
[](int value) -> Optional<bool> { return static_cast<bool>(value); },
[](Integer auto value) -> Optional<bool> { return static_cast<bool>(value); },
[](double value) -> Optional<bool> { return fabs(value) > NumericLimits<double>::epsilon(); },
[](bool value) -> Optional<bool> { return value; },
[](TupleValue const& value) -> Optional<bool> {
@ -242,20 +297,6 @@ Value& Value::operator=(DeprecatedString value)
return *this;
}
Value& Value::operator=(int value)
{
m_type = SQLType::Integer;
m_value = value;
return *this;
}
Value& Value::operator=(u32 value)
{
m_type = SQLType::Integer;
m_value = static_cast<int>(value); // FIXME: Handle signed overflow.
return *this;
}
Value& Value::operator=(double value)
{
m_type = SQLType::Float;
@ -318,7 +359,11 @@ size_t Value::length() const
// FIXME: This seems to be more of an encoded byte size rather than a length.
return m_value->visit(
[](DeprecatedString const& value) -> size_t { return sizeof(u32) + value.length(); },
[](int value) -> size_t { return sizeof(value); },
[](Integer auto value) -> size_t {
return downsize_integer(value, [](auto integer, auto) {
return sizeof(integer);
});
},
[](double value) -> size_t { return sizeof(value); },
[](bool value) -> size_t { return sizeof(value); },
[](TupleValue const& value) -> size_t {
@ -338,7 +383,14 @@ u32 Value::hash() const
return m_value->visit(
[](DeprecatedString const& value) -> u32 { return value.hash(); },
[](int value) -> u32 { return int_hash(value); },
[](Integer auto value) -> u32 {
return downsize_integer(value, [](auto integer, auto) {
if constexpr (sizeof(decltype(integer)) == 8)
return u64_hash(integer);
else
return int_hash(integer);
});
},
[](double) -> u32 { VERIFY_NOT_REACHED(); },
[](bool value) -> u32 { return int_hash(value); },
[](TupleValue const& value) -> u32 {
@ -364,8 +416,8 @@ int Value::compare(Value const& other) const
return m_value->visit(
[&](DeprecatedString const& value) -> int { return value.view().compare(other.to_deprecated_string()); },
[&](int value) -> int {
auto casted = other.to_int();
[&](Integer auto value) -> int {
auto casted = other.to_int<IntegerType<decltype(value)>>();
if (!casted.has_value())
return 1;
@ -427,16 +479,6 @@ bool Value::operator==(StringView value) const
return to_deprecated_string() == value;
}
bool Value::operator==(int value) const
{
return to_int() == value;
}
bool Value::operator==(u32 value) const
{
return to_u32() == value;
}
bool Value::operator==(double value) const
{
return to_double() == value;
@ -467,133 +509,218 @@ bool Value::operator>=(Value const& value) const
return compare(value) >= 0;
}
static Result invalid_type_for_numeric_operator(AST::BinaryOperator op)
template<typename Operator>
static Result invalid_type_for_numeric_operator(Operator op)
{
return { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, BinaryOperator_name(op) };
if constexpr (IsSame<Operator, AST::BinaryOperator>)
return { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, BinaryOperator_name(op) };
else if constexpr (IsSame<Operator, AST::UnaryOperator>)
return { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, UnaryOperator_name(op) };
else
static_assert(DependentFalse<Operator>);
}
ResultOr<Value> Value::add(Value const& other) const
{
if (auto double_maybe = to_double(); double_maybe.has_value()) {
if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
return Value(double_maybe.value() + other_double_maybe.value());
if (auto int_maybe = other.to_int(); int_maybe.has_value())
return Value(double_maybe.value() + (double)int_maybe.value());
} else if (auto int_maybe = to_int(); int_maybe.has_value()) {
if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
return Value(other_double_maybe.value() + (double)int_maybe.value());
if (auto other_int_maybe = other.to_int(); other_int_maybe.has_value())
return Value(int_maybe.value() + other_int_maybe.value());
if (is_int() && other.is_int()) {
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> {
Checked result { lhs };
result.add(rhs);
if (result.has_overflow())
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
return Value { result.value_unchecked() };
});
}
return invalid_type_for_numeric_operator(AST::BinaryOperator::Plus);
auto lhs = to_double();
auto rhs = other.to_double();
if (!lhs.has_value() || !rhs.has_value())
return invalid_type_for_numeric_operator(AST::BinaryOperator::Plus);
return Value { lhs.value() + rhs.value() };
}
ResultOr<Value> Value::subtract(Value const& other) const
{
if (auto double_maybe = to_double(); double_maybe.has_value()) {
if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
return Value(double_maybe.value() - other_double_maybe.value());
if (auto int_maybe = other.to_int(); int_maybe.has_value())
return Value(double_maybe.value() - (double)int_maybe.value());
} else if (auto int_maybe = to_int(); int_maybe.has_value()) {
if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
return Value((double)int_maybe.value() - other_double_maybe.value());
if (auto other_int_maybe = other.to_int(); other_int_maybe.has_value())
return Value(int_maybe.value() - other_int_maybe.value());
if (is_int() && other.is_int()) {
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> {
Checked result { lhs };
result.sub(rhs);
if (result.has_overflow())
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
return Value { result.value_unchecked() };
});
}
return invalid_type_for_numeric_operator(AST::BinaryOperator::Minus);
auto lhs = to_double();
auto rhs = other.to_double();
if (!lhs.has_value() || !rhs.has_value())
return invalid_type_for_numeric_operator(AST::BinaryOperator::Minus);
return Value { lhs.value() - rhs.value() };
}
ResultOr<Value> Value::multiply(Value const& other) const
{
if (auto double_maybe = to_double(); double_maybe.has_value()) {
if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
return Value(double_maybe.value() * other_double_maybe.value());
if (auto int_maybe = other.to_int(); int_maybe.has_value())
return Value(double_maybe.value() * (double)int_maybe.value());
} else if (auto int_maybe = to_int(); int_maybe.has_value()) {
if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
return Value((double)int_maybe.value() * other_double_maybe.value());
if (auto other_int_maybe = other.to_int(); other_int_maybe.has_value())
return Value(int_maybe.value() * other_int_maybe.value());
if (is_int() && other.is_int()) {
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> {
Checked result { lhs };
result.mul(rhs);
if (result.has_overflow())
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
return Value { result.value_unchecked() };
});
}
return invalid_type_for_numeric_operator(AST::BinaryOperator::Multiplication);
auto lhs = to_double();
auto rhs = other.to_double();
if (!lhs.has_value() || !rhs.has_value())
return invalid_type_for_numeric_operator(AST::BinaryOperator::Multiplication);
return Value { lhs.value() * rhs.value() };
}
ResultOr<Value> Value::divide(Value const& other) const
{
if (auto double_maybe = to_double(); double_maybe.has_value()) {
if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
return Value(double_maybe.value() / other_double_maybe.value());
if (auto int_maybe = other.to_int(); int_maybe.has_value())
return Value(double_maybe.value() / (double)int_maybe.value());
} else if (auto int_maybe = to_int(); int_maybe.has_value()) {
if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
return Value((double)int_maybe.value() / other_double_maybe.value());
if (auto other_int_maybe = other.to_int(); other_int_maybe.has_value())
return Value(int_maybe.value() / other_int_maybe.value());
}
return invalid_type_for_numeric_operator(AST::BinaryOperator::Division);
auto lhs = to_double();
auto rhs = other.to_double();
if (!lhs.has_value() || !rhs.has_value())
return invalid_type_for_numeric_operator(AST::BinaryOperator::Division);
if (rhs == 0.0)
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
return Value { lhs.value() / rhs.value() };
}
ResultOr<Value> Value::modulo(Value const& other) const
{
auto int_maybe_1 = to_int();
auto int_maybe_2 = other.to_int();
if (!int_maybe_1.has_value() || !int_maybe_2.has_value())
if (!is_int() || !other.is_int())
return invalid_type_for_numeric_operator(AST::BinaryOperator::Modulo);
return Value(int_maybe_1.value() % int_maybe_2.value());
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> {
Checked result { lhs };
result.mod(rhs);
if (result.has_overflow())
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
return Value { result.value_unchecked() };
});
}
ResultOr<Value> Value::negate() const
{
if (type() == SQLType::Integer) {
auto value = to_int<i64>();
if (!value.has_value())
return invalid_type_for_numeric_operator(AST::UnaryOperator::Minus);
return Value { value.value() * -1 };
}
if (type() == SQLType::Float)
return Value { -to_double().value() };
return invalid_type_for_numeric_operator(AST::UnaryOperator::Minus);
}
ResultOr<Value> Value::shift_left(Value const& other) const
{
auto u32_maybe = to_u32();
auto num_bytes_maybe = other.to_int();
if (!u32_maybe.has_value() || !num_bytes_maybe.has_value())
if (!is_int() || !other.is_int())
return invalid_type_for_numeric_operator(AST::BinaryOperator::ShiftLeft);
return Value(u32_maybe.value() << num_bytes_maybe.value());
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> {
using LHS = decltype(lhs);
using RHS = decltype(rhs);
static constexpr auto max_shift = static_cast<RHS>(sizeof(LHS) * 8);
if (rhs < 0 || rhs >= max_shift)
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
return Value { lhs << rhs };
});
}
ResultOr<Value> Value::shift_right(Value const& other) const
{
auto u32_maybe = to_u32();
auto num_bytes_maybe = other.to_int();
if (!u32_maybe.has_value() || !num_bytes_maybe.has_value())
if (!is_int() || !other.is_int())
return invalid_type_for_numeric_operator(AST::BinaryOperator::ShiftRight);
return Value(u32_maybe.value() >> num_bytes_maybe.value());
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> {
using LHS = decltype(lhs);
using RHS = decltype(rhs);
static constexpr auto max_shift = static_cast<RHS>(sizeof(LHS) * 8);
if (rhs < 0 || rhs >= max_shift)
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
return Value { lhs >> rhs };
});
}
ResultOr<Value> Value::bitwise_or(Value const& other) const
{
auto u32_maybe_1 = to_u32();
auto u32_maybe_2 = other.to_u32();
if (!u32_maybe_1.has_value() || !u32_maybe_2.has_value())
if (!is_int() || !other.is_int())
return invalid_type_for_numeric_operator(AST::BinaryOperator::BitwiseOr);
return Value(u32_maybe_1.value() | u32_maybe_2.value());
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) {
return Value { lhs | rhs };
});
}
ResultOr<Value> Value::bitwise_and(Value const& other) const
{
auto u32_maybe_1 = to_u32();
auto u32_maybe_2 = other.to_u32();
if (!u32_maybe_1.has_value() || !u32_maybe_2.has_value())
if (!is_int() || !other.is_int())
return invalid_type_for_numeric_operator(AST::BinaryOperator::BitwiseAnd);
return Value(u32_maybe_1.value() & u32_maybe_2.value());
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) {
return Value { lhs & rhs };
});
}
static constexpr auto sql_type_null_as_flag = static_cast<u8>(SQLType::Null);
ResultOr<Value> Value::bitwise_not() const
{
if (!is_int())
return invalid_type_for_numeric_operator(AST::UnaryOperator::BitwiseNot);
return downsize_integer(*this, [](auto value, auto) {
return Value { ~value };
});
}
static u8 encode_type_flags(Value const& value)
{
auto type_flags = to_underlying(value.type());
if (value.is_null()) {
type_flags |= to_underlying(TypeData::Null);
} else if (value.is_int()) {
downsize_integer(value, [&](auto, auto type_data) {
type_flags |= to_underlying(type_data);
});
}
return type_flags;
}
void Value::serialize(Serializer& serializer) const
{
auto type_flags = static_cast<u8>(type());
if (is_null())
type_flags |= sql_type_null_as_flag;
auto type_flags = encode_type_flags(*this);
serializer.serialize<u8>(type_flags);
if (is_null())
return;
if (is_int()) {
downsize_integer(*this, [&](auto integer, auto) {
serializer.serialize(integer);
});
return;
}
m_value->visit(
[&](TupleValue const& value) {
serializer.serialize<TupleDescriptor>(*value.descriptor);
@ -608,16 +735,11 @@ void Value::serialize(Serializer& serializer) const
void Value::deserialize(Serializer& serializer)
{
auto type_flags = serializer.deserialize<u8>();
bool has_value = true;
if ((type_flags & sql_type_null_as_flag) && (type_flags != sql_type_null_as_flag)) {
type_flags &= ~sql_type_null_as_flag;
has_value = false;
}
auto type_data = static_cast<TypeData>(type_flags & 0xf0);
m_type = static_cast<SQLType>(type_flags & 0x0f);
m_type = static_cast<SQLType>(type_flags);
if (!has_value)
if (type_data == TypeData::Null)
return;
switch (m_type) {
@ -628,7 +750,35 @@ void Value::deserialize(Serializer& serializer)
m_value = serializer.deserialize<DeprecatedString>();
break;
case SQLType::Integer:
m_value = serializer.deserialize<int>(0);
switch (type_data) {
case TypeData::Int8:
m_value = static_cast<i64>(serializer.deserialize<i8>(0));
break;
case TypeData::Int16:
m_value = static_cast<i64>(serializer.deserialize<i16>(0));
break;
case TypeData::Int32:
m_value = static_cast<i64>(serializer.deserialize<i32>(0));
break;
case TypeData::Int64:
m_value = static_cast<i64>(serializer.deserialize<i64>(0));
break;
case TypeData::Uint8:
m_value = static_cast<u64>(serializer.deserialize<u8>(0));
break;
case TypeData::Uint16:
m_value = static_cast<u64>(serializer.deserialize<u16>(0));
break;
case TypeData::Uint32:
m_value = static_cast<u64>(serializer.deserialize<u32>(0));
break;
case TypeData::Uint64:
m_value = static_cast<u64>(serializer.deserialize<u64>(0));
break;
default:
VERIFY_NOT_REACHED();
break;
}
break;
case SQLType::Float:
m_value = serializer.deserialize<double>(0.0);
@ -673,11 +823,9 @@ ResultOr<NonnullRefPtr<TupleDescriptor>> Value::infer_tuple_descriptor(Vector<Va
template<>
bool IPC::encode(Encoder& encoder, SQL::Value const& value)
{
auto type_flags = to_underlying(value.type());
if (value.is_null())
type_flags |= SQL::sql_type_null_as_flag;
auto type_flags = encode_type_flags(value);
encoder << type_flags;
if (value.is_null())
return true;
@ -688,7 +836,9 @@ bool IPC::encode(Encoder& encoder, SQL::Value const& value)
encoder << value.to_deprecated_string();
break;
case SQL::SQLType::Integer:
encoder << value.to_int().value();
SQL::downsize_integer(value, [&](auto integer, auto) {
encoder << integer;
});
break;
case SQL::SQLType::Float:
encoder << value.to_double().value();
@ -704,46 +854,72 @@ bool IPC::encode(Encoder& encoder, SQL::Value const& value)
return true;
}
template<typename T>
static ErrorOr<void> decode_scalar(IPC::Decoder& decoder, SQL::Value& value)
{
T decoded {};
TRY(decoder.decode(decoded));
value = move(decoded);
return {};
}
template<>
ErrorOr<void> IPC::decode(Decoder& decoder, SQL::Value& value)
{
UnderlyingType<SQL::SQLType> type_flags;
u8 type_flags { 0 };
TRY(decoder.decode(type_flags));
if ((type_flags & SQL::sql_type_null_as_flag) && (type_flags != SQL::sql_type_null_as_flag)) {
type_flags &= ~SQL::sql_type_null_as_flag;
auto type_data = static_cast<SQL::TypeData>(type_flags & 0xf0);
auto type = static_cast<SQL::SQLType>(type_flags & 0x0f);
value = SQL::Value(static_cast<SQL::SQLType>(type_flags));
if (type_data == SQL::TypeData::Null) {
value = SQL::Value(type);
return {};
}
switch (static_cast<SQL::SQLType>(type_flags)) {
switch (type) {
case SQL::SQLType::Null:
break;
case SQL::SQLType::Text: {
DeprecatedString text;
TRY(decoder.decode(text));
value = move(text);
case SQL::SQLType::Text:
TRY(decode_scalar<DeprecatedString>(decoder, value));
break;
}
case SQL::SQLType::Integer: {
int number { 0 };
TRY(decoder.decode(number));
value = number;
case SQL::SQLType::Integer:
switch (type_data) {
case SQL::TypeData::Int8:
TRY(decode_scalar<i8>(decoder, value));
break;
case SQL::TypeData::Int16:
TRY(decode_scalar<i16>(decoder, value));
break;
case SQL::TypeData::Int32:
TRY(decode_scalar<i32>(decoder, value));
break;
case SQL::TypeData::Int64:
TRY(decode_scalar<i64>(decoder, value));
break;
case SQL::TypeData::Uint8:
TRY(decode_scalar<u8>(decoder, value));
break;
case SQL::TypeData::Uint16:
TRY(decode_scalar<u16>(decoder, value));
break;
case SQL::TypeData::Uint32:
TRY(decode_scalar<u32>(decoder, value));
break;
case SQL::TypeData::Uint64:
TRY(decode_scalar<u64>(decoder, value));
break;
default:
VERIFY_NOT_REACHED();
break;
}
break;
}
case SQL::SQLType::Float: {
double number { 0.0 };
TRY(decoder.decode(number));
value = number;
case SQL::SQLType::Float:
TRY(decode_scalar<double>(decoder, value));
break;
}
case SQL::SQLType::Boolean: {
bool boolean { false };
TRY(decoder.decode(boolean));
value = boolean;
case SQL::SQLType::Boolean:
TRY(decode_scalar<bool>(decoder, value));
break;
}
case SQL::SQLType::Tuple: {
Vector<SQL::Value> tuple;
TRY(decoder.decode(tuple));

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/Checked.h>
#include <AK/DeprecatedString.h>
#include <AK/Format.h>
#include <AK/Optional.h>
@ -17,58 +18,107 @@
#include <LibSQL/Forward.h>
#include <LibSQL/Result.h>
#include <LibSQL/Type.h>
#include <math.h>
namespace SQL {
template<typename T>
concept Boolean = SameAs<RemoveCVReference<T>, bool>;
template<typename T>
concept Integer = (Integral<T> && !Boolean<T>);
/**
* A `Value` is an atomic piece of SQL data`. A `Value` has a basic type
* (Text/String, Integer, Float, etc). Richer types are implemented in higher
* level layers, but the resulting data is stored in these `Value` objects.
*/
class Value {
template<Integer T>
using IntegerType = Conditional<IsSigned<T>, i64, u64>;
public:
explicit Value(SQLType sql_type = SQLType::Null);
explicit Value(DeprecatedString);
explicit Value(int);
explicit Value(u32);
explicit Value(double);
Value(Value const&);
Value(Value&&);
~Value();
static ResultOr<Value> create_tuple(NonnullRefPtr<TupleDescriptor>);
static ResultOr<Value> create_tuple(Vector<Value>);
explicit Value(Integer auto value)
: m_type(SQLType::Integer)
, m_value(static_cast<IntegerType<decltype(value)>>(value))
{
}
template<typename T>
requires(SameAs<RemoveCVReference<T>, bool>) explicit Value(T value)
explicit Value(Boolean auto value)
: m_type(SQLType::Boolean)
, m_value(value)
{
}
static ResultOr<Value> create_tuple(NonnullRefPtr<TupleDescriptor>);
static ResultOr<Value> create_tuple(Vector<Value>);
[[nodiscard]] SQLType type() const;
[[nodiscard]] StringView type_name() const;
[[nodiscard]] bool is_type_compatible_with(SQLType) const;
[[nodiscard]] bool is_null() const;
[[nodiscard]] bool is_int() const;
[[nodiscard]] auto const& value() const
{
VERIFY(m_value.has_value());
return *m_value;
}
[[nodiscard]] DeprecatedString to_deprecated_string() const;
[[nodiscard]] Optional<int> to_int() const;
[[nodiscard]] Optional<u32> to_u32() const;
[[nodiscard]] Optional<double> to_double() const;
[[nodiscard]] Optional<bool> to_bool() const;
[[nodiscard]] Optional<Vector<Value>> to_vector() const;
template<Integer T>
[[nodiscard]] Optional<T> to_int() const
{
if (is_null())
return {};
return m_value->visit(
[](DeprecatedString const& value) -> Optional<T> {
if constexpr (IsSigned<T>)
return value.to_int<T>();
else
return value.to_uint<T>();
},
[](Integer auto value) -> Optional<T> {
if (!AK::is_within_range<T>(value))
return {};
return static_cast<T>(value);
},
[](double value) -> Optional<T> {
if (!AK::is_within_range<T>(value))
return {};
return static_cast<T>(round(value));
},
[](bool value) -> Optional<T> { return static_cast<T>(value); },
[](TupleValue const&) -> Optional<T> { return {}; });
}
Value& operator=(Value);
Value& operator=(DeprecatedString);
Value& operator=(int);
Value& operator=(u32);
Value& operator=(double);
Value& operator=(Integer auto value)
{
m_type = SQLType::Integer;
m_value = static_cast<IntegerType<decltype(value)>>(value);
return *this;
}
ResultOr<void> assign_tuple(NonnullRefPtr<TupleDescriptor>);
ResultOr<void> assign_tuple(Vector<Value>);
template<typename T>
requires(SameAs<RemoveCVReference<T>, bool>) Value& operator=(T value)
Value& operator=(Boolean auto value)
{
m_type = SQLType::Boolean;
m_value = value;
@ -83,9 +133,14 @@ public:
[[nodiscard]] int compare(Value const&) const;
bool operator==(Value const&) const;
bool operator==(StringView) const;
bool operator==(int) const;
bool operator==(u32) const;
bool operator==(double) const;
template<Integer T>
bool operator==(T value)
{
return to_int<T>() == value;
}
bool operator!=(Value const&) const;
bool operator<(Value const&) const;
bool operator<=(Value const&) const;
@ -97,10 +152,12 @@ public:
ResultOr<Value> multiply(Value const&) const;
ResultOr<Value> divide(Value const&) const;
ResultOr<Value> modulo(Value const&) const;
ResultOr<Value> negate() const;
ResultOr<Value> shift_left(Value const&) const;
ResultOr<Value> shift_right(Value const&) const;
ResultOr<Value> bitwise_or(Value const&) const;
ResultOr<Value> bitwise_and(Value const&) const;
ResultOr<Value> bitwise_not() const;
[[nodiscard]] TupleElementDescriptor descriptor() const;
@ -112,7 +169,7 @@ private:
Vector<Value> values;
};
using ValueType = Variant<DeprecatedString, int, double, bool, TupleValue>;
using ValueType = Variant<DeprecatedString, i64, u64, double, bool, TupleValue>;
static ResultOr<NonnullRefPtr<TupleDescriptor>> infer_tuple_descriptor(Vector<Value> const& values);
Value(NonnullRefPtr<TupleDescriptor> descriptor, Vector<Value> values);