Selaa lähdekoodia

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.
Timothy Flynn 2 vuotta sitten
vanhempi
commit
72e41a7dbd

+ 1 - 1
Tests/LibSQL/TestSqlBtreeIndex.cpp

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

+ 2 - 2
Tests/LibSQL/TestSqlDatabase.cpp

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

+ 1 - 1
Tests/LibSQL/TestSqlHashIndex.cpp

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

+ 28 - 28
Tests/LibSQL/TestSqlStatementExecution.cpp

@@ -105,7 +105,7 @@ TEST_CASE(insert_into_table)
     EXPECT(!rows_or_error.is_error());
     EXPECT(!rows_or_error.is_error());
     for (auto& row : rows_or_error.value()) {
     for (auto& row : rows_or_error.value()) {
         EXPECT_EQ(row["TEXTCOLUMN"].to_deprecated_string(), "Test");
         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++;
         count++;
     }
     }
     EXPECT_EQ(count, 1);
     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;");
     result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable WHERE IntColumn > 44;");
     EXPECT_EQ(result.size(), 2u);
     EXPECT_EQ(result.size(), 2u);
     for (auto& row : result) {
     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);
     EXPECT_EQ(result.size(), 25u);
     for (auto& row : result) {
     for (auto& row : result) {
         EXPECT(row.row.size() == 4);
         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;");
         "WHERE TestTable1.IntColumn = TestTable2.IntColumn;");
     EXPECT_EQ(result.size(), 1u);
     EXPECT_EQ(result.size(), 1u);
     EXPECT_EQ(result[0].row.size(), 3u);
     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[1].to_deprecated_string(), "Test_1");
     EXPECT_EQ(result[0].row[2].to_deprecated_string(), "Test_12");
     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;");
     result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;");
     EXPECT_EQ(result.size(), 5u);
     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;");
     result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY TextColumn;");
     EXPECT_EQ(result.size(), 5u);
     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;");
     result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY TextColumn, IntColumn;");
     EXPECT_EQ(result.size(), 5u);
     EXPECT_EQ(result.size(), 5u);
     EXPECT_EQ(result[0].row[0].to_deprecated_string(), "Test_1");
     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[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[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[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[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)
 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;");
     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.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)
 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;
             auto const& result_row = result.at(i).row;
             EXPECT_EQ(result_row.size(), 1u);
             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());
             result_values.append(result_column.value());
         }
         }
 
 

+ 767 - 31
Tests/LibSQL/TestSqlValueAndTuple.cpp

@@ -19,8 +19,8 @@ TEST_CASE(null_value)
     EXPECT_EQ(v.type(), SQL::SQLType::Null);
     EXPECT_EQ(v.type(), SQL::SQLType::Null);
     EXPECT_EQ(v.to_deprecated_string(), "(null)"sv);
     EXPECT_EQ(v.to_deprecated_string(), "(null)"sv);
     EXPECT(!v.to_bool().has_value());
     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());
     EXPECT(!v.to_double().has_value());
 }
 }
 
 
@@ -72,8 +72,8 @@ TEST_CASE(text_value_to_other_types)
         SQL::Value v("42");
         SQL::Value v("42");
         EXPECT_EQ(v.type(), SQL::SQLType::Text);
         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().has_value());
         EXPECT((v.to_double().value() - 42.0) < NumericLimits<double>().epsilon());
         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_EQ(v.type(), SQL::SQLType::Text);
 
 
         EXPECT(!v.to_bool().has_value());
         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());
         EXPECT(!v.to_double().has_value());
     }
     }
     {
     {
@@ -147,8 +147,8 @@ TEST_CASE(integer_value)
         v = 42;
         v = 42;
         EXPECT_EQ(v.type(), SQL::SQLType::Integer);
         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_EQ(v.to_deprecated_string(), "42"sv);
 
 
         EXPECT(v.to_double().has_value());
         EXPECT(v.to_double().has_value());
@@ -161,8 +161,8 @@ TEST_CASE(integer_value)
         SQL::Value v(0);
         SQL::Value v(0);
         EXPECT_EQ(v.type(), SQL::SQLType::Integer);
         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().has_value());
         EXPECT(!v.to_bool().value());
         EXPECT(!v.to_bool().value());
@@ -171,16 +171,16 @@ TEST_CASE(integer_value)
         SQL::Value v(42);
         SQL::Value v(42);
         EXPECT_EQ(v.type(), SQL::SQLType::Integer);
         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 text("42");
         SQL::Value integer(SQL::SQLType::Integer);
         SQL::Value integer(SQL::SQLType::Integer);
         integer = text;
         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);
     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)
 TEST_CASE(float_value)
 {
 {
     {
     {
@@ -213,8 +259,8 @@ TEST_CASE(float_value)
         EXPECT(v.to_double().has_value());
         EXPECT(v.to_double().has_value());
         EXPECT((v.to_double().value() - 3.14) < NumericLimits<double>().epsilon());
         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_EQ(v.to_deprecated_string(), "3.14");
 
 
         EXPECT(v.to_bool().has_value());
         EXPECT(v.to_bool().has_value());
@@ -226,8 +272,8 @@ TEST_CASE(float_value)
         EXPECT(v.to_double().has_value());
         EXPECT(v.to_double().has_value());
         EXPECT(v.to_double().value() < NumericLimits<double>().epsilon());
         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_EQ(v.to_deprecated_string(), "0"sv);
 
 
         EXPECT(v.to_bool().has_value());
         EXPECT(v.to_bool().has_value());
@@ -242,22 +288,22 @@ TEST_CASE(float_value)
         SQL::Value v(3.51);
         SQL::Value v(3.51);
         EXPECT_EQ(v.type(), SQL::SQLType::Float);
         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);
         SQL::Value v(-3.14);
         EXPECT_EQ(v.type(), SQL::SQLType::Float);
         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);
         SQL::Value v(-3.51);
         EXPECT_EQ(v.type(), SQL::SQLType::Float);
         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);
     EXPECT_EQ(copy, "42"sv);
 }
 }
 
 
-TEST_CASE(compare_text_to_int)
+TEST_CASE(to_int)
 {
 {
     SQL::Value text("42");
     SQL::Value text("42");
     SQL::Value integer(42);
     SQL::Value integer(42);
     EXPECT_EQ(text, integer);
     EXPECT_EQ(text, integer);
     EXPECT_EQ(integer, text);
     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)
 TEST_CASE(bool_value)
@@ -305,8 +418,8 @@ TEST_CASE(bool_value)
         EXPECT(v.to_bool().has_value());
         EXPECT(v.to_bool().has_value());
         EXPECT(v.to_bool().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_EQ(v.to_deprecated_string(), "true"sv);
 
 
         EXPECT(v.to_double().has_value());
         EXPECT(v.to_double().has_value());
@@ -319,8 +432,8 @@ TEST_CASE(bool_value)
         EXPECT(v.to_bool().has_value());
         EXPECT(v.to_bool().has_value());
         EXPECT(!v.to_bool().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_EQ(v.to_deprecated_string(), "false"sv);
 
 
         EXPECT(v.to_double().has_value());
         EXPECT(v.to_double().has_value());
@@ -333,8 +446,8 @@ TEST_CASE(bool_value)
         EXPECT(v.to_bool().has_value());
         EXPECT(v.to_bool().has_value());
         EXPECT(v.to_bool().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_EQ(v.to_deprecated_string(), "true"sv);
 
 
         EXPECT(v.to_double().has_value());
         EXPECT(v.to_double().has_value());
@@ -567,3 +680,626 @@ TEST_CASE(compare_tuples)
     EXPECT(tuple3 >= tuple1);
     EXPECT(tuple3 >= tuple1);
     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);
+    }
+}

+ 2 - 14
Userland/Libraries/LibSQL/AST/Expression.cpp

@@ -128,15 +128,7 @@ ResultOr<Value> UnaryOperatorExpression::evaluate(ExecutionContext& context) con
             return expression_value;
             return expression_value;
         return Result { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, UnaryOperator_name(type()) };
         return Result { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, UnaryOperator_name(type()) };
     case UnaryOperator::Minus:
     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:
     case UnaryOperator::Not:
         if (expression_value.type() == SQLType::Boolean) {
         if (expression_value.type() == SQLType::Boolean) {
             expression_value = !expression_value.to_bool().value();
             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()) };
         return Result { SQLCommand::Unknown, SQLErrorCode::BooleanOperatorTypeMismatch, UnaryOperator_name(type()) };
     case UnaryOperator::BitwiseNot:
     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:
     default:
         VERIFY_NOT_REACHED();
         VERIFY_NOT_REACHED();
     }
     }

+ 2 - 2
Userland/Libraries/LibSQL/AST/Select.cpp

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

+ 1 - 1
Userland/Libraries/LibSQL/Database.cpp

@@ -165,7 +165,7 @@ ResultOr<NonnullRefPtr<TableDef>> Database::get_table(DeprecatedString const& sc
 
 
     auto table_hash = table_def->hash();
     auto table_hash = table_def->hash();
     auto column_key = ColumnDef::make_key(table_def);
     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);
         table_def->append_column(*it);
 
 
     return table_def;
     return table_def;

+ 1 - 1
Userland/Libraries/LibSQL/Heap.h

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

+ 3 - 3
Userland/Libraries/LibSQL/Meta.cpp

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

+ 1 - 0
Userland/Libraries/LibSQL/Result.h

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

+ 359 - 183
Userland/Libraries/LibSQL/Value.cpp

@@ -12,38 +12,115 @@
 #include <LibSQL/Serializer.h>
 #include <LibSQL/Serializer.h>
 #include <LibSQL/TupleDescriptor.h>
 #include <LibSQL/TupleDescriptor.h>
 #include <LibSQL/Value.h>
 #include <LibSQL/Value.h>
-#include <math.h>
 #include <string.h>
 #include <string.h>
 
 
 namespace SQL {
 namespace SQL {
 
 
-Value::Value(SQLType type)
-    : m_type(type)
+// 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));
 }
 }
 
 
-Value::Value(DeprecatedString value)
-    : m_type(SQLType::Text)
-    , m_value(move(value))
+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(int value)
-    : m_type(SQLType::Integer)
-    , m_value(value)
+Value::Value(SQLType type)
+    : m_type(type)
 {
 {
 }
 }
 
 
-Value::Value(u32 value)
-    : m_type(SQLType::Integer)
-    , m_value(static_cast<int>(value)) // FIXME: Handle signed overflow.
+Value::Value(DeprecatedString value)
+    : m_type(SQLType::Text)
+    , m_value(move(value))
 {
 {
 }
 }
 
 
 Value::Value(double value)
 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)
 Value::Value(NonnullRefPtr<TupleDescriptor> descriptor, Vector<Value> values)
@@ -122,6 +199,11 @@ bool Value::is_null() const
     return !m_value.has_value();
     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
 DeprecatedString Value::to_deprecated_string() const
 {
 {
     if (is_null())
     if (is_null())
@@ -129,7 +211,7 @@ DeprecatedString Value::to_deprecated_string() const
 
 
     return m_value->visit(
     return m_value->visit(
         [](DeprecatedString const& value) -> DeprecatedString { return value; },
         [](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); },
         [](double value) -> DeprecatedString { return DeprecatedString::number(value); },
         [](bool value) -> DeprecatedString { return value ? "true"sv : "false"sv; },
         [](bool value) -> DeprecatedString { return value ? "true"sv : "false"sv; },
         [](TupleValue const& value) -> DeprecatedString {
         [](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
 Optional<double> Value::to_double() const
 {
 {
     if (is_null())
     if (is_null())
@@ -184,7 +239,7 @@ Optional<double> Value::to_double() const
                 return {};
                 return {};
             return result;
             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; },
         [](double value) -> Optional<double> { return value; },
         [](bool value) -> Optional<double> { return static_cast<double>(value); },
         [](bool value) -> Optional<double> { return static_cast<double>(value); },
         [](TupleValue const&) -> Optional<double> { return {}; });
         [](TupleValue const&) -> Optional<double> { return {}; });
@@ -203,7 +258,7 @@ Optional<bool> Value::to_bool() const
                 return false;
                 return false;
             return {};
             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(); },
         [](double value) -> Optional<bool> { return fabs(value) > NumericLimits<double>::epsilon(); },
         [](bool value) -> Optional<bool> { return value; },
         [](bool value) -> Optional<bool> { return value; },
         [](TupleValue const& value) -> Optional<bool> {
         [](TupleValue const& value) -> Optional<bool> {
@@ -242,20 +297,6 @@ Value& Value::operator=(DeprecatedString value)
     return *this;
     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)
 Value& Value::operator=(double value)
 {
 {
     m_type = SQLType::Float;
     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.
     // FIXME: This seems to be more of an encoded byte size rather than a length.
     return m_value->visit(
     return m_value->visit(
         [](DeprecatedString const& value) -> size_t { return sizeof(u32) + value.length(); },
         [](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); },
         [](double value) -> size_t { return sizeof(value); },
         [](bool value) -> size_t { return sizeof(value); },
         [](bool value) -> size_t { return sizeof(value); },
         [](TupleValue const& value) -> size_t {
         [](TupleValue const& value) -> size_t {
@@ -338,7 +383,14 @@ u32 Value::hash() const
 
 
     return m_value->visit(
     return m_value->visit(
         [](DeprecatedString const& value) -> u32 { return value.hash(); },
         [](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(); },
         [](double) -> u32 { VERIFY_NOT_REACHED(); },
         [](bool value) -> u32 { return int_hash(value); },
         [](bool value) -> u32 { return int_hash(value); },
         [](TupleValue const& value) -> u32 {
         [](TupleValue const& value) -> u32 {
@@ -364,8 +416,8 @@ int Value::compare(Value const& other) const
 
 
     return m_value->visit(
     return m_value->visit(
         [&](DeprecatedString const& value) -> int { return value.view().compare(other.to_deprecated_string()); },
         [&](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())
             if (!casted.has_value())
                 return 1;
                 return 1;
 
 
@@ -427,16 +479,6 @@ bool Value::operator==(StringView value) const
     return to_deprecated_string() == value;
     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
 bool Value::operator==(double value) const
 {
 {
     return to_double() == value;
     return to_double() == value;
@@ -467,133 +509,218 @@ bool Value::operator>=(Value const& value) const
     return compare(value) >= 0;
     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
 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
 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
 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
 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
 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 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
 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 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
 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 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
 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 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
 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 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 };
+    });
+}
 
 
-void Value::serialize(Serializer& serializer) const
+static u8 encode_type_flags(Value const& value)
 {
 {
-    auto type_flags = static_cast<u8>(type());
-    if (is_null())
-        type_flags |= sql_type_null_as_flag;
+    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 = encode_type_flags(*this);
     serializer.serialize<u8>(type_flags);
     serializer.serialize<u8>(type_flags);
 
 
     if (is_null())
     if (is_null())
         return;
         return;
 
 
+    if (is_int()) {
+        downsize_integer(*this, [&](auto integer, auto) {
+            serializer.serialize(integer);
+        });
+        return;
+    }
+
     m_value->visit(
     m_value->visit(
         [&](TupleValue const& value) {
         [&](TupleValue const& value) {
             serializer.serialize<TupleDescriptor>(*value.descriptor);
             serializer.serialize<TupleDescriptor>(*value.descriptor);
@@ -608,16 +735,11 @@ void Value::serialize(Serializer& serializer) const
 void Value::deserialize(Serializer& serializer)
 void Value::deserialize(Serializer& serializer)
 {
 {
     auto type_flags = serializer.deserialize<u8>();
     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;
-    }
-
-    m_type = static_cast<SQLType>(type_flags);
+    auto type_data = static_cast<TypeData>(type_flags & 0xf0);
+    m_type = static_cast<SQLType>(type_flags & 0x0f);
 
 
-    if (!has_value)
+    if (type_data == TypeData::Null)
         return;
         return;
 
 
     switch (m_type) {
     switch (m_type) {
@@ -628,7 +750,35 @@ void Value::deserialize(Serializer& serializer)
         m_value = serializer.deserialize<DeprecatedString>();
         m_value = serializer.deserialize<DeprecatedString>();
         break;
         break;
     case SQLType::Integer:
     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;
         break;
     case SQLType::Float:
     case SQLType::Float:
         m_value = serializer.deserialize<double>(0.0);
         m_value = serializer.deserialize<double>(0.0);
@@ -673,11 +823,9 @@ ResultOr<NonnullRefPtr<TupleDescriptor>> Value::infer_tuple_descriptor(Vector<Va
 template<>
 template<>
 bool IPC::encode(Encoder& encoder, SQL::Value const& value)
 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;
     encoder << type_flags;
+
     if (value.is_null())
     if (value.is_null())
         return true;
         return true;
 
 
@@ -688,7 +836,9 @@ bool IPC::encode(Encoder& encoder, SQL::Value const& value)
         encoder << value.to_deprecated_string();
         encoder << value.to_deprecated_string();
         break;
         break;
     case SQL::SQLType::Integer:
     case SQL::SQLType::Integer:
-        encoder << value.to_int().value();
+        SQL::downsize_integer(value, [&](auto integer, auto) {
+            encoder << integer;
+        });
         break;
         break;
     case SQL::SQLType::Float:
     case SQL::SQLType::Float:
         encoder << value.to_double().value();
         encoder << value.to_double().value();
@@ -704,46 +854,72 @@ bool IPC::encode(Encoder& encoder, SQL::Value const& value)
     return true;
     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<>
 template<>
 ErrorOr<void> IPC::decode(Decoder& decoder, SQL::Value& value)
 ErrorOr<void> IPC::decode(Decoder& decoder, SQL::Value& value)
 {
 {
-    UnderlyingType<SQL::SQLType> type_flags;
+    u8 type_flags { 0 };
     TRY(decoder.decode(type_flags));
     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 {};
         return {};
     }
     }
 
 
-    switch (static_cast<SQL::SQLType>(type_flags)) {
+    switch (type) {
     case SQL::SQLType::Null:
     case SQL::SQLType::Null:
         break;
         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;
         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;
         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;
         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;
         break;
-    }
     case SQL::SQLType::Tuple: {
     case SQL::SQLType::Tuple: {
         Vector<SQL::Value> tuple;
         Vector<SQL::Value> tuple;
         TRY(decoder.decode(tuple));
         TRY(decoder.decode(tuple));

+ 72 - 15
Userland/Libraries/LibSQL/Value.h

@@ -7,6 +7,7 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <AK/Checked.h>
 #include <AK/DeprecatedString.h>
 #include <AK/DeprecatedString.h>
 #include <AK/Format.h>
 #include <AK/Format.h>
 #include <AK/Optional.h>
 #include <AK/Optional.h>
@@ -17,58 +18,107 @@
 #include <LibSQL/Forward.h>
 #include <LibSQL/Forward.h>
 #include <LibSQL/Result.h>
 #include <LibSQL/Result.h>
 #include <LibSQL/Type.h>
 #include <LibSQL/Type.h>
+#include <math.h>
 
 
 namespace SQL {
 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
  * 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
  * (Text/String, Integer, Float, etc). Richer types are implemented in higher
  * level layers, but the resulting data is stored in these `Value` objects.
  * level layers, but the resulting data is stored in these `Value` objects.
  */
  */
 class Value {
 class Value {
+    template<Integer T>
+    using IntegerType = Conditional<IsSigned<T>, i64, u64>;
+
 public:
 public:
     explicit Value(SQLType sql_type = SQLType::Null);
     explicit Value(SQLType sql_type = SQLType::Null);
     explicit Value(DeprecatedString);
     explicit Value(DeprecatedString);
-    explicit Value(int);
-    explicit Value(u32);
     explicit Value(double);
     explicit Value(double);
     Value(Value const&);
     Value(Value const&);
     Value(Value&&);
     Value(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_type(SQLType::Boolean)
         , m_value(value)
         , m_value(value)
     {
     {
     }
     }
 
 
+    static ResultOr<Value> create_tuple(NonnullRefPtr<TupleDescriptor>);
+    static ResultOr<Value> create_tuple(Vector<Value>);
+
     [[nodiscard]] SQLType type() const;
     [[nodiscard]] SQLType type() const;
     [[nodiscard]] StringView type_name() const;
     [[nodiscard]] StringView type_name() const;
     [[nodiscard]] bool is_type_compatible_with(SQLType) const;
     [[nodiscard]] bool is_type_compatible_with(SQLType) const;
     [[nodiscard]] bool is_null() 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]] 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<double> to_double() const;
     [[nodiscard]] Optional<bool> to_bool() const;
     [[nodiscard]] Optional<bool> to_bool() const;
     [[nodiscard]] Optional<Vector<Value>> to_vector() 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=(Value);
     Value& operator=(DeprecatedString);
     Value& operator=(DeprecatedString);
-    Value& operator=(int);
-    Value& operator=(u32);
     Value& operator=(double);
     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(NonnullRefPtr<TupleDescriptor>);
     ResultOr<void> assign_tuple(Vector<Value>);
     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_type = SQLType::Boolean;
         m_value = value;
         m_value = value;
@@ -83,9 +133,14 @@ public:
     [[nodiscard]] int compare(Value const&) const;
     [[nodiscard]] int compare(Value const&) const;
     bool operator==(Value const&) const;
     bool operator==(Value const&) const;
     bool operator==(StringView) const;
     bool operator==(StringView) const;
-    bool operator==(int) const;
-    bool operator==(u32) const;
     bool operator==(double) 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;
     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> multiply(Value const&) const;
     ResultOr<Value> divide(Value const&) const;
     ResultOr<Value> divide(Value const&) const;
     ResultOr<Value> modulo(Value const&) const;
     ResultOr<Value> modulo(Value const&) const;
+    ResultOr<Value> negate() const;
     ResultOr<Value> shift_left(Value const&) const;
     ResultOr<Value> shift_left(Value const&) const;
     ResultOr<Value> shift_right(Value const&) const;
     ResultOr<Value> shift_right(Value const&) const;
     ResultOr<Value> bitwise_or(Value const&) const;
     ResultOr<Value> bitwise_or(Value const&) const;
     ResultOr<Value> bitwise_and(Value const&) const;
     ResultOr<Value> bitwise_and(Value const&) const;
+    ResultOr<Value> bitwise_not() const;
 
 
     [[nodiscard]] TupleElementDescriptor descriptor() const;
     [[nodiscard]] TupleElementDescriptor descriptor() const;
 
 
@@ -112,7 +169,7 @@ private:
         Vector<Value> values;
         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);
     static ResultOr<NonnullRefPtr<TupleDescriptor>> infer_tuple_descriptor(Vector<Value> const& values);
     Value(NonnullRefPtr<TupleDescriptor> descriptor, Vector<Value> values);
     Value(NonnullRefPtr<TupleDescriptor> descriptor, Vector<Value> values);