mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 19:12:43 +00:00 
			
		
		
		
	LibSQL: Support 64-bit integer values and handle overflow errors
Currently, integers are stored in LibSQL as 32-bit signed integers, even if the provided type is unsigned. This resulted in a series of unchecked unsigned-to-signed conversions, and prevented storing 64-bit values. Further, mathematical operations were performed without similar checks, and without checking for overflow. This changes SQL::Value to behave like SQLite for INTEGER types. In SQLite, the INTEGER type does not imply a size or signedness of the underlying type. Instead, SQLite determines on-the-fly what type is needed as values are created and updated. To do so, the SQL::Value variant can now hold an i64 or u64 integer. If a specific type is requested, invalid conversions are now explictly an error (e.g. converting a stored -1 to a u64 will fail). When binary mathematical operations are performed, we now try to coerce the RHS value to a type that works with the LHS value, failing the operation if that isn't possible. Any overflow or invalid operation (e.g. bitshifting a 64-bit value by more than 64 bytes) is an error.
This commit is contained in:
		
							parent
							
								
									a1007c37a4
								
							
						
					
					
						commit
						72e41a7dbd
					
				
					 13 changed files with 1241 additions and 283 deletions
				
			
		|  | @ -211,7 +211,7 @@ void insert_into_and_scan_btree(int num_keys) | |||
|             if (prev.size()) { | ||||
|                 EXPECT(prev < key); | ||||
|             } | ||||
|             auto key_value = key[0].to_int(); | ||||
|             auto key_value = key[0].to_int<i32>(); | ||||
|             for (auto ix = 0; ix < num_keys; ix++) { | ||||
|                 if (keys[ix] == key_value) { | ||||
|                     EXPECT_EQ(key.pointer(), pointers[ix]); | ||||
|  |  | |||
|  | @ -68,10 +68,10 @@ void verify_table_contents(SQL::Database& db, int expected_count) | |||
|     EXPECT(!rows_or_error.is_error()); | ||||
|     for (auto& row : rows_or_error.value()) { | ||||
|         StringBuilder builder; | ||||
|         builder.appendff("Test{}", row["IntColumn"].to_int().value()); | ||||
|         builder.appendff("Test{}", row["IntColumn"].to_int<i32>().value()); | ||||
|         EXPECT_EQ(row["TextColumn"].to_deprecated_string(), builder.build()); | ||||
|         count++; | ||||
|         sum += row["IntColumn"].to_int().value(); | ||||
|         sum += row["IntColumn"].to_int<i32>().value(); | ||||
|     } | ||||
|     EXPECT_EQ(count, expected_count); | ||||
|     EXPECT_EQ(sum, (expected_count * (expected_count - 1)) / 2); | ||||
|  |  | |||
|  | @ -268,7 +268,7 @@ void insert_into_and_scan_hash_index(int num_keys) | |||
|         int count = 0; | ||||
|         for (auto iter = hash_index->begin(); !iter.is_end(); iter++, count++) { | ||||
|             auto key = (*iter); | ||||
|             auto key_value = key[0].to_int(); | ||||
|             auto key_value = key[0].to_int<i32>(); | ||||
|             VERIFY(key_value.has_value()); | ||||
| 
 | ||||
|             for (auto ix = 0; ix < num_keys; ix++) { | ||||
|  |  | |||
|  | @ -105,7 +105,7 @@ TEST_CASE(insert_into_table) | |||
|     EXPECT(!rows_or_error.is_error()); | ||||
|     for (auto& row : rows_or_error.value()) { | ||||
|         EXPECT_EQ(row["TEXTCOLUMN"].to_deprecated_string(), "Test"); | ||||
|         EXPECT_EQ(row["INTCOLUMN"].to_int().value(), 42); | ||||
|         EXPECT_EQ(row["INTCOLUMN"].to_int<i32>(), 42); | ||||
|         count++; | ||||
|     } | ||||
|     EXPECT_EQ(count, 1); | ||||
|  | @ -318,7 +318,7 @@ TEST_CASE(select_with_where) | |||
|     result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable WHERE IntColumn > 44;"); | ||||
|     EXPECT_EQ(result.size(), 2u); | ||||
|     for (auto& row : result) { | ||||
|         EXPECT(row.row[1].to_int().value() > 44); | ||||
|         EXPECT(row.row[1].to_int<i32>().value() > 44); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -348,10 +348,10 @@ TEST_CASE(select_cross_join) | |||
|     EXPECT_EQ(result.size(), 25u); | ||||
|     for (auto& row : result) { | ||||
|         EXPECT(row.row.size() == 4); | ||||
|         EXPECT(row.row[1].to_int().value() >= 42); | ||||
|         EXPECT(row.row[1].to_int().value() <= 46); | ||||
|         EXPECT(row.row[3].to_int().value() >= 40); | ||||
|         EXPECT(row.row[3].to_int().value() <= 48); | ||||
|         EXPECT(row.row[1].to_int<i32>().value() >= 42); | ||||
|         EXPECT(row.row[1].to_int<i32>().value() <= 46); | ||||
|         EXPECT(row.row[3].to_int<i32>().value() >= 40); | ||||
|         EXPECT(row.row[3].to_int<i32>().value() <= 48); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -383,7 +383,7 @@ TEST_CASE(select_inner_join) | |||
|         "WHERE TestTable1.IntColumn = TestTable2.IntColumn;"); | ||||
|     EXPECT_EQ(result.size(), 1u); | ||||
|     EXPECT_EQ(result[0].row.size(), 3u); | ||||
|     EXPECT_EQ(result[0].row[0].to_int().value(), 42); | ||||
|     EXPECT_EQ(result[0].row[0].to_int<i32>(), 42); | ||||
|     EXPECT_EQ(result[0].row[1].to_deprecated_string(), "Test_1"); | ||||
|     EXPECT_EQ(result[0].row[2].to_deprecated_string(), "Test_12"); | ||||
| } | ||||
|  | @ -463,11 +463,11 @@ TEST_CASE(select_with_order) | |||
| 
 | ||||
|     result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); | ||||
|     EXPECT_EQ(result.size(), 5u); | ||||
|     EXPECT_EQ(result[0].row[1].to_int().value(), 40); | ||||
|     EXPECT_EQ(result[1].row[1].to_int().value(), 41); | ||||
|     EXPECT_EQ(result[2].row[1].to_int().value(), 42); | ||||
|     EXPECT_EQ(result[3].row[1].to_int().value(), 44); | ||||
|     EXPECT_EQ(result[4].row[1].to_int().value(), 47); | ||||
|     EXPECT_EQ(result[0].row[1].to_int<i32>(), 40); | ||||
|     EXPECT_EQ(result[1].row[1].to_int<i32>(), 41); | ||||
|     EXPECT_EQ(result[2].row[1].to_int<i32>(), 42); | ||||
|     EXPECT_EQ(result[3].row[1].to_int<i32>(), 44); | ||||
|     EXPECT_EQ(result[4].row[1].to_int<i32>(), 47); | ||||
| 
 | ||||
|     result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY TextColumn;"); | ||||
|     EXPECT_EQ(result.size(), 5u); | ||||
|  | @ -547,15 +547,15 @@ TEST_CASE(select_with_order_two_columns) | |||
|     result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY TextColumn, IntColumn;"); | ||||
|     EXPECT_EQ(result.size(), 5u); | ||||
|     EXPECT_EQ(result[0].row[0].to_deprecated_string(), "Test_1"); | ||||
|     EXPECT_EQ(result[0].row[1].to_int().value(), 47); | ||||
|     EXPECT_EQ(result[0].row[1].to_int<i32>(), 47); | ||||
|     EXPECT_EQ(result[1].row[0].to_deprecated_string(), "Test_2"); | ||||
|     EXPECT_EQ(result[1].row[1].to_int().value(), 40); | ||||
|     EXPECT_EQ(result[1].row[1].to_int<i32>(), 40); | ||||
|     EXPECT_EQ(result[2].row[0].to_deprecated_string(), "Test_2"); | ||||
|     EXPECT_EQ(result[2].row[1].to_int().value(), 42); | ||||
|     EXPECT_EQ(result[2].row[1].to_int<i32>(), 42); | ||||
|     EXPECT_EQ(result[3].row[0].to_deprecated_string(), "Test_4"); | ||||
|     EXPECT_EQ(result[3].row[1].to_int().value(), 41); | ||||
|     EXPECT_EQ(result[3].row[1].to_int<i32>(), 41); | ||||
|     EXPECT_EQ(result[4].row[0].to_deprecated_string(), "Test_5"); | ||||
|     EXPECT_EQ(result[4].row[1].to_int().value(), 44); | ||||
|     EXPECT_EQ(result[4].row[1].to_int<i32>(), 44); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(select_with_order_by_column_not_in_result) | ||||
|  | @ -626,16 +626,16 @@ TEST_CASE(select_with_order_limit_and_offset) | |||
|     } | ||||
|     auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY IntColumn LIMIT 10 OFFSET 10;"); | ||||
|     EXPECT_EQ(result.size(), 10u); | ||||
|     EXPECT_EQ(result[0].row[1].to_int().value(), 10); | ||||
|     EXPECT_EQ(result[1].row[1].to_int().value(), 11); | ||||
|     EXPECT_EQ(result[2].row[1].to_int().value(), 12); | ||||
|     EXPECT_EQ(result[3].row[1].to_int().value(), 13); | ||||
|     EXPECT_EQ(result[4].row[1].to_int().value(), 14); | ||||
|     EXPECT_EQ(result[5].row[1].to_int().value(), 15); | ||||
|     EXPECT_EQ(result[6].row[1].to_int().value(), 16); | ||||
|     EXPECT_EQ(result[7].row[1].to_int().value(), 17); | ||||
|     EXPECT_EQ(result[8].row[1].to_int().value(), 18); | ||||
|     EXPECT_EQ(result[9].row[1].to_int().value(), 19); | ||||
|     EXPECT_EQ(result[0].row[1].to_int<i32>(), 10); | ||||
|     EXPECT_EQ(result[1].row[1].to_int<i32>(), 11); | ||||
|     EXPECT_EQ(result[2].row[1].to_int<i32>(), 12); | ||||
|     EXPECT_EQ(result[3].row[1].to_int<i32>(), 13); | ||||
|     EXPECT_EQ(result[4].row[1].to_int<i32>(), 14); | ||||
|     EXPECT_EQ(result[5].row[1].to_int<i32>(), 15); | ||||
|     EXPECT_EQ(result[6].row[1].to_int<i32>(), 16); | ||||
|     EXPECT_EQ(result[7].row[1].to_int<i32>(), 17); | ||||
|     EXPECT_EQ(result[8].row[1].to_int<i32>(), 18); | ||||
|     EXPECT_EQ(result[9].row[1].to_int<i32>(), 19); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(select_with_limit_out_of_bounds) | ||||
|  | @ -707,7 +707,7 @@ TEST_CASE(binary_operator_execution) | |||
|             auto const& result_row = result.at(i).row; | ||||
|             EXPECT_EQ(result_row.size(), 1u); | ||||
| 
 | ||||
|             auto result_column = result_row[0].to_int(); | ||||
|             auto result_column = result_row[0].to_int<i32>(); | ||||
|             result_values.append(result_column.value()); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,8 +19,8 @@ TEST_CASE(null_value) | |||
|     EXPECT_EQ(v.type(), SQL::SQLType::Null); | ||||
|     EXPECT_EQ(v.to_deprecated_string(), "(null)"sv); | ||||
|     EXPECT(!v.to_bool().has_value()); | ||||
|     EXPECT(!v.to_int().has_value()); | ||||
|     EXPECT(!v.to_u32().has_value()); | ||||
|     EXPECT(!v.to_int<i32>().has_value()); | ||||
|     EXPECT(!v.to_int<u32>().has_value()); | ||||
|     EXPECT(!v.to_double().has_value()); | ||||
| } | ||||
| 
 | ||||
|  | @ -72,8 +72,8 @@ TEST_CASE(text_value_to_other_types) | |||
|         SQL::Value v("42"); | ||||
|         EXPECT_EQ(v.type(), SQL::SQLType::Text); | ||||
| 
 | ||||
|         EXPECT(v.to_int().has_value()); | ||||
|         EXPECT_EQ(v.to_int().value(), 42); | ||||
|         EXPECT(v.to_int<i32>().has_value()); | ||||
|         EXPECT_EQ(v.to_int<i32>().value(), 42); | ||||
| 
 | ||||
|         EXPECT(v.to_double().has_value()); | ||||
|         EXPECT((v.to_double().value() - 42.0) < NumericLimits<double>().epsilon()); | ||||
|  | @ -97,8 +97,8 @@ TEST_CASE(text_value_to_other_types) | |||
|         EXPECT_EQ(v.type(), SQL::SQLType::Text); | ||||
| 
 | ||||
|         EXPECT(!v.to_bool().has_value()); | ||||
|         EXPECT(!v.to_int().has_value()); | ||||
|         EXPECT(!v.to_u32().has_value()); | ||||
|         EXPECT(!v.to_int<i32>().has_value()); | ||||
|         EXPECT(!v.to_int<u32>().has_value()); | ||||
|         EXPECT(!v.to_double().has_value()); | ||||
|     } | ||||
|     { | ||||
|  | @ -147,8 +147,8 @@ TEST_CASE(integer_value) | |||
|         v = 42; | ||||
|         EXPECT_EQ(v.type(), SQL::SQLType::Integer); | ||||
| 
 | ||||
|         EXPECT(v.to_int().has_value()); | ||||
|         EXPECT_EQ(v.to_int().value(), 42); | ||||
|         EXPECT(v.to_int<i32>().has_value()); | ||||
|         EXPECT_EQ(v.to_int<i32>().value(), 42); | ||||
|         EXPECT_EQ(v.to_deprecated_string(), "42"sv); | ||||
| 
 | ||||
|         EXPECT(v.to_double().has_value()); | ||||
|  | @ -161,8 +161,8 @@ TEST_CASE(integer_value) | |||
|         SQL::Value v(0); | ||||
|         EXPECT_EQ(v.type(), SQL::SQLType::Integer); | ||||
| 
 | ||||
|         EXPECT(v.to_int().has_value()); | ||||
|         EXPECT_EQ(v.to_int().value(), 0); | ||||
|         EXPECT(v.to_int<i32>().has_value()); | ||||
|         EXPECT_EQ(v.to_int<i32>().value(), 0); | ||||
| 
 | ||||
|         EXPECT(v.to_bool().has_value()); | ||||
|         EXPECT(!v.to_bool().value()); | ||||
|  | @ -171,16 +171,16 @@ TEST_CASE(integer_value) | |||
|         SQL::Value v(42); | ||||
|         EXPECT_EQ(v.type(), SQL::SQLType::Integer); | ||||
| 
 | ||||
|         EXPECT(v.to_int().has_value()); | ||||
|         EXPECT_EQ(v.to_int().value(), 42); | ||||
|         EXPECT(v.to_int<i32>().has_value()); | ||||
|         EXPECT_EQ(v.to_int<i32>().value(), 42); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value text("42"); | ||||
|         SQL::Value integer(SQL::SQLType::Integer); | ||||
|         integer = text; | ||||
| 
 | ||||
|         EXPECT(integer.to_int().has_value()); | ||||
|         EXPECT_EQ(integer.to_int().value(), 42); | ||||
|         EXPECT(integer.to_int<i32>().has_value()); | ||||
|         EXPECT_EQ(integer.to_int<i32>().value(), 42); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -200,6 +200,52 @@ TEST_CASE(serialize_int_value) | |||
|     EXPECT_EQ(v2, v); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(serialize_downsized_int_value) | ||||
| { | ||||
|     auto run_test_for_value = [](auto value) { | ||||
|         using T = decltype(value); | ||||
|         SQL::Value v(value); | ||||
| 
 | ||||
|         SQL::Serializer serializer; | ||||
|         serializer.serialize(v); | ||||
|         serializer.rewind(); | ||||
| 
 | ||||
|         auto type_flags = serializer.deserialize<u8>(); | ||||
|         auto type_data = type_flags & 0xf0; | ||||
|         auto type = static_cast<SQL::SQLType>(type_flags & 0x0f); | ||||
| 
 | ||||
|         EXPECT_NE(type_data, 0); | ||||
|         EXPECT_EQ(type, SQL::SQLType::Integer); | ||||
| 
 | ||||
|         auto deserialized = serializer.deserialize<T>(); | ||||
|         EXPECT_EQ(deserialized, value); | ||||
|     }; | ||||
| 
 | ||||
|     run_test_for_value(NumericLimits<i8>::min()); | ||||
|     run_test_for_value(NumericLimits<i8>::max()); | ||||
| 
 | ||||
|     run_test_for_value(NumericLimits<i16>::min()); | ||||
|     run_test_for_value(NumericLimits<i16>::max()); | ||||
| 
 | ||||
|     run_test_for_value(NumericLimits<i32>::min()); | ||||
|     run_test_for_value(NumericLimits<i32>::max()); | ||||
| 
 | ||||
|     run_test_for_value(NumericLimits<i64>::min()); | ||||
|     run_test_for_value(NumericLimits<i64>::max()); | ||||
| 
 | ||||
|     run_test_for_value(NumericLimits<u8>::min()); | ||||
|     run_test_for_value(NumericLimits<u8>::max()); | ||||
| 
 | ||||
|     run_test_for_value(NumericLimits<u16>::min()); | ||||
|     run_test_for_value(NumericLimits<u16>::max()); | ||||
| 
 | ||||
|     run_test_for_value(NumericLimits<u32>::min()); | ||||
|     run_test_for_value(NumericLimits<u32>::max()); | ||||
| 
 | ||||
|     run_test_for_value(NumericLimits<u64>::min()); | ||||
|     run_test_for_value(NumericLimits<u64>::max()); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(float_value) | ||||
| { | ||||
|     { | ||||
|  | @ -213,8 +259,8 @@ TEST_CASE(float_value) | |||
|         EXPECT(v.to_double().has_value()); | ||||
|         EXPECT((v.to_double().value() - 3.14) < NumericLimits<double>().epsilon()); | ||||
| 
 | ||||
|         EXPECT(v.to_int().has_value()); | ||||
|         EXPECT_EQ(v.to_int().value(), 3); | ||||
|         EXPECT(v.to_int<i32>().has_value()); | ||||
|         EXPECT_EQ(v.to_int<i32>().value(), 3); | ||||
|         EXPECT_EQ(v.to_deprecated_string(), "3.14"); | ||||
| 
 | ||||
|         EXPECT(v.to_bool().has_value()); | ||||
|  | @ -226,8 +272,8 @@ TEST_CASE(float_value) | |||
|         EXPECT(v.to_double().has_value()); | ||||
|         EXPECT(v.to_double().value() < NumericLimits<double>().epsilon()); | ||||
| 
 | ||||
|         EXPECT(v.to_int().has_value()); | ||||
|         EXPECT_EQ(v.to_int().value(), 0); | ||||
|         EXPECT(v.to_int<i32>().has_value()); | ||||
|         EXPECT_EQ(v.to_int<i32>().value(), 0); | ||||
|         EXPECT_EQ(v.to_deprecated_string(), "0"sv); | ||||
| 
 | ||||
|         EXPECT(v.to_bool().has_value()); | ||||
|  | @ -242,22 +288,22 @@ TEST_CASE(float_value) | |||
|         SQL::Value v(3.51); | ||||
|         EXPECT_EQ(v.type(), SQL::SQLType::Float); | ||||
| 
 | ||||
|         EXPECT(v.to_int().has_value()); | ||||
|         EXPECT_EQ(v.to_int().value(), 4); | ||||
|         EXPECT(v.to_int<i32>().has_value()); | ||||
|         EXPECT_EQ(v.to_int<i32>().value(), 4); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value v(-3.14); | ||||
|         EXPECT_EQ(v.type(), SQL::SQLType::Float); | ||||
| 
 | ||||
|         EXPECT(v.to_int().has_value()); | ||||
|         EXPECT_EQ(v.to_int().value(), -3); | ||||
|         EXPECT(v.to_int<i32>().has_value()); | ||||
|         EXPECT_EQ(v.to_int<i32>().value(), -3); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value v(-3.51); | ||||
|         EXPECT_EQ(v.type(), SQL::SQLType::Float); | ||||
| 
 | ||||
|         EXPECT(v.to_int().has_value()); | ||||
|         EXPECT_EQ(v.to_int().value(), -4); | ||||
|         EXPECT(v.to_int<i32>().has_value()); | ||||
|         EXPECT_EQ(v.to_int<i32>().value(), -4); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -284,12 +330,79 @@ TEST_CASE(copy_value) | |||
|     EXPECT_EQ(copy, "42"sv); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(compare_text_to_int) | ||||
| TEST_CASE(to_int) | ||||
| { | ||||
|     SQL::Value text("42"); | ||||
|     SQL::Value integer(42); | ||||
|     EXPECT_EQ(text, integer); | ||||
|     EXPECT_EQ(integer, text); | ||||
| 
 | ||||
|     SQL::Value int_64 { static_cast<i64>(123) }; | ||||
|     EXPECT_EQ(int_64.to_int<i8>(), 123); | ||||
|     EXPECT_EQ(int_64.to_int<i16>(), 123); | ||||
|     EXPECT_EQ(int_64.to_int<i32>(), 123); | ||||
|     EXPECT_EQ(int_64.to_int<u8>(), 123u); | ||||
|     EXPECT_EQ(int_64.to_int<u16>(), 123u); | ||||
|     EXPECT_EQ(int_64.to_int<u32>(), 123u); | ||||
|     EXPECT_EQ(int_64.to_int<u64>(), 123u); | ||||
| 
 | ||||
|     SQL::Value uint_64 { static_cast<i64>(123) }; | ||||
|     EXPECT_EQ(uint_64.to_int<i8>(), 123); | ||||
|     EXPECT_EQ(uint_64.to_int<i16>(), 123); | ||||
|     EXPECT_EQ(uint_64.to_int<i32>(), 123); | ||||
|     EXPECT_EQ(uint_64.to_int<i64>(), 123); | ||||
|     EXPECT_EQ(uint_64.to_int<u8>(), 123u); | ||||
|     EXPECT_EQ(uint_64.to_int<u16>(), 123u); | ||||
|     EXPECT_EQ(uint_64.to_int<u32>(), 123u); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(to_int_failures) | ||||
| { | ||||
|     SQL::Value large_int_64 { NumericLimits<i64>::max() }; | ||||
|     EXPECT(!large_int_64.to_int<i8>().has_value()); | ||||
|     EXPECT(!large_int_64.to_int<i16>().has_value()); | ||||
|     EXPECT(!large_int_64.to_int<i32>().has_value()); | ||||
|     EXPECT(!large_int_64.to_int<u8>().has_value()); | ||||
|     EXPECT(!large_int_64.to_int<u16>().has_value()); | ||||
|     EXPECT(!large_int_64.to_int<u32>().has_value()); | ||||
| 
 | ||||
|     SQL::Value large_int_32 { NumericLimits<i32>::max() }; | ||||
|     EXPECT(!large_int_32.to_int<i8>().has_value()); | ||||
|     EXPECT(!large_int_32.to_int<i16>().has_value()); | ||||
|     EXPECT(!large_int_32.to_int<u8>().has_value()); | ||||
|     EXPECT(!large_int_32.to_int<u16>().has_value()); | ||||
| 
 | ||||
|     SQL::Value small_int_64 { NumericLimits<i64>::min() }; | ||||
|     EXPECT(!small_int_64.to_int<i8>().has_value()); | ||||
|     EXPECT(!small_int_64.to_int<i16>().has_value()); | ||||
|     EXPECT(!small_int_64.to_int<i32>().has_value()); | ||||
|     EXPECT(!small_int_64.to_int<u8>().has_value()); | ||||
|     EXPECT(!small_int_64.to_int<u16>().has_value()); | ||||
|     EXPECT(!small_int_64.to_int<u32>().has_value()); | ||||
|     EXPECT(!small_int_64.to_int<u64>().has_value()); | ||||
| 
 | ||||
|     SQL::Value small_int_32 { NumericLimits<i32>::min() }; | ||||
|     EXPECT(!small_int_32.to_int<i8>().has_value()); | ||||
|     EXPECT(!small_int_32.to_int<i16>().has_value()); | ||||
|     EXPECT(!small_int_32.to_int<u8>().has_value()); | ||||
|     EXPECT(!small_int_32.to_int<u16>().has_value()); | ||||
|     EXPECT(!small_int_32.to_int<u32>().has_value()); | ||||
|     EXPECT(!small_int_32.to_int<u64>().has_value()); | ||||
| 
 | ||||
|     SQL::Value large_uint_64 { NumericLimits<u64>::max() }; | ||||
|     EXPECT(!large_uint_64.to_int<i8>().has_value()); | ||||
|     EXPECT(!large_uint_64.to_int<i16>().has_value()); | ||||
|     EXPECT(!large_uint_64.to_int<i32>().has_value()); | ||||
|     EXPECT(!large_uint_64.to_int<i64>().has_value()); | ||||
|     EXPECT(!large_uint_64.to_int<u8>().has_value()); | ||||
|     EXPECT(!large_uint_64.to_int<u16>().has_value()); | ||||
|     EXPECT(!large_uint_64.to_int<u32>().has_value()); | ||||
| 
 | ||||
|     SQL::Value large_uint_32 { NumericLimits<u32>::max() }; | ||||
|     EXPECT(!large_uint_32.to_int<i8>().has_value()); | ||||
|     EXPECT(!large_uint_32.to_int<i16>().has_value()); | ||||
|     EXPECT(!large_uint_32.to_int<u8>().has_value()); | ||||
|     EXPECT(!large_uint_32.to_int<u16>().has_value()); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(bool_value) | ||||
|  | @ -305,8 +418,8 @@ TEST_CASE(bool_value) | |||
|         EXPECT(v.to_bool().has_value()); | ||||
|         EXPECT(v.to_bool().value()); | ||||
| 
 | ||||
|         EXPECT(v.to_int().has_value()); | ||||
|         EXPECT_EQ(v.to_int().value(), 1); | ||||
|         EXPECT(v.to_int<i32>().has_value()); | ||||
|         EXPECT_EQ(v.to_int<i32>().value(), 1); | ||||
|         EXPECT_EQ(v.to_deprecated_string(), "true"sv); | ||||
| 
 | ||||
|         EXPECT(v.to_double().has_value()); | ||||
|  | @ -319,8 +432,8 @@ TEST_CASE(bool_value) | |||
|         EXPECT(v.to_bool().has_value()); | ||||
|         EXPECT(!v.to_bool().value()); | ||||
| 
 | ||||
|         EXPECT(v.to_int().has_value()); | ||||
|         EXPECT_EQ(v.to_int().value(), 0); | ||||
|         EXPECT(v.to_int<i32>().has_value()); | ||||
|         EXPECT_EQ(v.to_int<i32>().value(), 0); | ||||
|         EXPECT_EQ(v.to_deprecated_string(), "false"sv); | ||||
| 
 | ||||
|         EXPECT(v.to_double().has_value()); | ||||
|  | @ -333,8 +446,8 @@ TEST_CASE(bool_value) | |||
|         EXPECT(v.to_bool().has_value()); | ||||
|         EXPECT(v.to_bool().value()); | ||||
| 
 | ||||
|         EXPECT(v.to_int().has_value()); | ||||
|         EXPECT_EQ(v.to_int().value(), 1); | ||||
|         EXPECT(v.to_int<i32>().has_value()); | ||||
|         EXPECT_EQ(v.to_int<i32>().value(), 1); | ||||
|         EXPECT_EQ(v.to_deprecated_string(), "true"sv); | ||||
| 
 | ||||
|         EXPECT(v.to_double().has_value()); | ||||
|  | @ -567,3 +680,626 @@ TEST_CASE(compare_tuples) | |||
|     EXPECT(tuple3 >= tuple1); | ||||
|     EXPECT(tuple3 > tuple1); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(add) | ||||
| { | ||||
|     { | ||||
|         SQL::Value value1 { 21 }; | ||||
|         SQL::Value value2 { 42 }; | ||||
| 
 | ||||
|         auto result = value1.add(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 63); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { 21 }; | ||||
|         SQL::Value value2 { static_cast<u8>(42) }; | ||||
| 
 | ||||
|         auto result = value1.add(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 63); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<u8>(21) }; | ||||
|         SQL::Value value2 { 42 }; | ||||
| 
 | ||||
|         auto result = value1.add(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 63); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<double>(21) }; | ||||
|         SQL::Value value2 { 42 }; | ||||
| 
 | ||||
|         auto result = value1.add(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 63); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<double>(21.5) }; | ||||
|         SQL::Value value2 { 42 }; | ||||
| 
 | ||||
|         auto result = value1.add(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Float); | ||||
|         EXPECT((result.value().to_double().value() - 63.5) < NumericLimits<double>().epsilon()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(add_error) | ||||
| { | ||||
|     { | ||||
|         // Fails to coerce value2 to the signedness of value1.
 | ||||
|         SQL::Value value1 { 1 }; | ||||
|         SQL::Value value2 { NumericLimits<u64>::max() }; | ||||
| 
 | ||||
|         auto result = value1.add(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // Fails to coerce value2 to the signedness of value1.
 | ||||
|         SQL::Value value1 { static_cast<u64>(1) }; | ||||
|         SQL::Value value2 { -1 }; | ||||
| 
 | ||||
|         auto result = value1.add(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // The operation itself would overflow.
 | ||||
|         SQL::Value value1 { static_cast<u64>(1) }; | ||||
|         SQL::Value value2 { NumericLimits<u64>::max() }; | ||||
| 
 | ||||
|         auto result = value1.add(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // Cannot convert value to a number.
 | ||||
|         SQL::Value value1 { 1 }; | ||||
|         SQL::Value value2 { "foo"sv }; | ||||
| 
 | ||||
|         auto result = value1.add(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(subtract) | ||||
| { | ||||
|     { | ||||
|         SQL::Value value1 { 21 }; | ||||
|         SQL::Value value2 { 42 }; | ||||
| 
 | ||||
|         auto result = value1.subtract(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), -21); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { 21 }; | ||||
|         SQL::Value value2 { static_cast<u8>(42) }; | ||||
| 
 | ||||
|         auto result = value1.subtract(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), -21); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<u8>(42) }; | ||||
|         SQL::Value value2 { 21 }; | ||||
| 
 | ||||
|         auto result = value1.subtract(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 21); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<double>(21) }; | ||||
|         SQL::Value value2 { 42 }; | ||||
| 
 | ||||
|         auto result = value1.subtract(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), -21); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<double>(21.5) }; | ||||
|         SQL::Value value2 { 42 }; | ||||
| 
 | ||||
|         auto result = value1.subtract(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Float); | ||||
|         EXPECT((result.value().to_double().value() - 20.5) < NumericLimits<double>().epsilon()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(subtract_error) | ||||
| { | ||||
|     { | ||||
|         // Fails to coerce value2 to the signedness of value1.
 | ||||
|         SQL::Value value1 { 1 }; | ||||
|         SQL::Value value2 { NumericLimits<u64>::max() }; | ||||
| 
 | ||||
|         auto result = value1.subtract(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // Fails to coerce value2 to the signedness of value1.
 | ||||
|         SQL::Value value1 { static_cast<u64>(1) }; | ||||
|         SQL::Value value2 { -1 }; | ||||
| 
 | ||||
|         auto result = value1.subtract(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // The operation itself would overflow.
 | ||||
|         SQL::Value value1 { static_cast<u64>(0) }; | ||||
|         SQL::Value value2 { static_cast<u64>(1) }; | ||||
| 
 | ||||
|         auto result = value1.subtract(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // Cannot convert value to a number.
 | ||||
|         SQL::Value value1 { 1 }; | ||||
|         SQL::Value value2 { "foo"sv }; | ||||
| 
 | ||||
|         auto result = value1.subtract(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(multiply) | ||||
| { | ||||
|     { | ||||
|         SQL::Value value1 { 2 }; | ||||
|         SQL::Value value2 { 21 }; | ||||
| 
 | ||||
|         auto result = value1.multiply(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 42); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { 2 }; | ||||
|         SQL::Value value2 { static_cast<u8>(21) }; | ||||
| 
 | ||||
|         auto result = value1.multiply(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 42); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<u8>(2) }; | ||||
|         SQL::Value value2 { 21 }; | ||||
| 
 | ||||
|         auto result = value1.multiply(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 42); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<double>(2) }; | ||||
|         SQL::Value value2 { 21 }; | ||||
| 
 | ||||
|         auto result = value1.multiply(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 42); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<double>(2.5) }; | ||||
|         SQL::Value value2 { 21 }; | ||||
| 
 | ||||
|         auto result = value1.multiply(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Float); | ||||
|         EXPECT((result.value().to_double().value() - 52.5) < NumericLimits<double>().epsilon()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(multiply_error) | ||||
| { | ||||
|     { | ||||
|         // Fails to coerce value2 to the signedness of value1.
 | ||||
|         SQL::Value value1 { 1 }; | ||||
|         SQL::Value value2 { NumericLimits<u64>::max() }; | ||||
| 
 | ||||
|         auto result = value1.multiply(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // Fails to coerce value2 to the signedness of value1.
 | ||||
|         SQL::Value value1 { static_cast<u64>(1) }; | ||||
|         SQL::Value value2 { -1 }; | ||||
| 
 | ||||
|         auto result = value1.multiply(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // The operation itself would overflow.
 | ||||
|         SQL::Value value1 { NumericLimits<i64>::max() }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.multiply(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // Cannot convert value to a number.
 | ||||
|         SQL::Value value1 { 1 }; | ||||
|         SQL::Value value2 { "foo"sv }; | ||||
| 
 | ||||
|         auto result = value1.multiply(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(divide) | ||||
| { | ||||
|     { | ||||
|         SQL::Value value1 { 42 }; | ||||
|         SQL::Value value2 { -2 }; | ||||
| 
 | ||||
|         auto result = value1.divide(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), -21); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { 42 }; | ||||
|         SQL::Value value2 { static_cast<u8>(2) }; | ||||
| 
 | ||||
|         auto result = value1.divide(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 21); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<u8>(42) }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.divide(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 21); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<double>(42) }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.divide(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 21); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<double>(43) }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.divide(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Float); | ||||
|         EXPECT((result.value().to_double().value() - 21.5) < NumericLimits<double>().epsilon()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(divide_error) | ||||
| { | ||||
|     { | ||||
|         // The operation itself would overflow.
 | ||||
|         SQL::Value value1 { 1 }; | ||||
|         SQL::Value value2 { 0 }; | ||||
| 
 | ||||
|         auto result = value1.divide(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // Cannot convert value to a number.
 | ||||
|         SQL::Value value1 { 1 }; | ||||
|         SQL::Value value2 { "foo"sv }; | ||||
| 
 | ||||
|         auto result = value1.divide(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(modulo) | ||||
| { | ||||
|     { | ||||
|         SQL::Value value1 { 21 }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.modulo(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 1); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { 21 }; | ||||
|         SQL::Value value2 { static_cast<u8>(2) }; | ||||
| 
 | ||||
|         auto result = value1.modulo(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 1); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<u8>(21) }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.modulo(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 1); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<double>(21) }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.modulo(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(modulo_error) | ||||
| { | ||||
|     { | ||||
|         // Fails to coerce value2 to the signedness of value1.
 | ||||
|         SQL::Value value1 { 1 }; | ||||
|         SQL::Value value2 { NumericLimits<u64>::max() }; | ||||
| 
 | ||||
|         auto result = value1.modulo(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // Fails to coerce value2 to the signedness of value1.
 | ||||
|         SQL::Value value1 { static_cast<u64>(1) }; | ||||
|         SQL::Value value2 { -1 }; | ||||
| 
 | ||||
|         auto result = value1.modulo(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // The operation itself would overflow.
 | ||||
|         SQL::Value value1 { 21 }; | ||||
|         SQL::Value value2 { 0 }; | ||||
| 
 | ||||
|         auto result = value1.modulo(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // Cannot convert value to an integer.
 | ||||
|         SQL::Value value1 { 1 }; | ||||
|         SQL::Value value2 { "foo"sv }; | ||||
| 
 | ||||
|         auto result = value1.modulo(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); | ||||
|     } | ||||
|     { | ||||
|         // Cannot convert value to an integer.
 | ||||
|         SQL::Value value1 { static_cast<double>(21.5) }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.modulo(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(shift_left) | ||||
| { | ||||
|     { | ||||
|         SQL::Value value1 { 0b0011'0000 }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.shift_left(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 0b1100'0000); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { 0b0011'0000 }; | ||||
|         SQL::Value value2 { static_cast<u8>(2) }; | ||||
| 
 | ||||
|         auto result = value1.shift_left(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 0b1100'0000); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<u8>(0b0011'0000) }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.shift_left(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 0b1100'0000); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<double>(0b0011'0000) }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.shift_left(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 0b1100'0000); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(shift_left_error) | ||||
| { | ||||
|     { | ||||
|         // Fails to coerce value2 to the signedness of value1.
 | ||||
|         SQL::Value value1 { 1 }; | ||||
|         SQL::Value value2 { NumericLimits<u64>::max() }; | ||||
| 
 | ||||
|         auto result = value1.shift_left(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // Fails to coerce value2 to the signedness of value1.
 | ||||
|         SQL::Value value1 { static_cast<u64>(1) }; | ||||
|         SQL::Value value2 { -1 }; | ||||
| 
 | ||||
|         auto result = value1.shift_left(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // The operation itself would overflow.
 | ||||
|         SQL::Value value1 { 21 }; | ||||
|         SQL::Value value2 { -1 }; | ||||
| 
 | ||||
|         auto result = value1.shift_left(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // The operation itself would overflow.
 | ||||
|         SQL::Value value1 { 21 }; | ||||
|         SQL::Value value2 { 64 }; | ||||
| 
 | ||||
|         auto result = value1.shift_left(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // Cannot convert value to an integer.
 | ||||
|         SQL::Value value1 { 1 }; | ||||
|         SQL::Value value2 { "foo"sv }; | ||||
| 
 | ||||
|         auto result = value1.shift_left(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); | ||||
|     } | ||||
|     { | ||||
|         // Cannot convert value to an integer.
 | ||||
|         SQL::Value value1 { static_cast<double>(21.5) }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.shift_left(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(shift_right) | ||||
| { | ||||
|     { | ||||
|         SQL::Value value1 { 0b0011'0000 }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.shift_right(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 0b0000'1100); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { 0b0011'0000 }; | ||||
|         SQL::Value value2 { static_cast<u8>(2) }; | ||||
| 
 | ||||
|         auto result = value1.shift_right(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 0b0000'1100); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<u8>(0b0011'0000) }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.shift_right(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 0b0000'1100); | ||||
|     } | ||||
|     { | ||||
|         SQL::Value value1 { static_cast<double>(0b0011'0000) }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.shift_right(value2); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); | ||||
|         EXPECT_EQ(result.value(), 0b0000'1100); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(shift_right_error) | ||||
| { | ||||
|     { | ||||
|         // Fails to coerce value2 to the signedness of value1.
 | ||||
|         SQL::Value value1 { 1 }; | ||||
|         SQL::Value value2 { NumericLimits<u64>::max() }; | ||||
| 
 | ||||
|         auto result = value1.shift_right(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // Fails to coerce value2 to the signedness of value1.
 | ||||
|         SQL::Value value1 { static_cast<u64>(1) }; | ||||
|         SQL::Value value2 { -1 }; | ||||
| 
 | ||||
|         auto result = value1.shift_right(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // The operation itself would overflow.
 | ||||
|         SQL::Value value1 { 21 }; | ||||
|         SQL::Value value2 { -1 }; | ||||
| 
 | ||||
|         auto result = value1.shift_right(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // The operation itself would overflow.
 | ||||
|         SQL::Value value1 { 21 }; | ||||
|         SQL::Value value2 { 64 }; | ||||
| 
 | ||||
|         auto result = value1.shift_right(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); | ||||
|     } | ||||
|     { | ||||
|         // Cannot convert value to an integer.
 | ||||
|         SQL::Value value1 { 1 }; | ||||
|         SQL::Value value2 { "foo"sv }; | ||||
| 
 | ||||
|         auto result = value1.shift_right(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); | ||||
|     } | ||||
|     { | ||||
|         // Cannot convert value to an integer.
 | ||||
|         SQL::Value value1 { static_cast<double>(21.5) }; | ||||
|         SQL::Value value2 { 2 }; | ||||
| 
 | ||||
|         auto result = value1.shift_right(value2); | ||||
|         EXPECT(result.is_error()); | ||||
|         EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -128,15 +128,7 @@ ResultOr<Value> UnaryOperatorExpression::evaluate(ExecutionContext& context) con | |||
|             return expression_value; | ||||
|         return Result { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, UnaryOperator_name(type()) }; | ||||
|     case UnaryOperator::Minus: | ||||
|         if (expression_value.type() == SQLType::Integer) { | ||||
|             expression_value = -expression_value.to_int().value(); | ||||
|             return expression_value; | ||||
|         } | ||||
|         if (expression_value.type() == SQLType::Float) { | ||||
|             expression_value = -expression_value.to_double().value(); | ||||
|             return expression_value; | ||||
|         } | ||||
|         return Result { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, UnaryOperator_name(type()) }; | ||||
|         return expression_value.negate(); | ||||
|     case UnaryOperator::Not: | ||||
|         if (expression_value.type() == SQLType::Boolean) { | ||||
|             expression_value = !expression_value.to_bool().value(); | ||||
|  | @ -144,11 +136,7 @@ ResultOr<Value> UnaryOperatorExpression::evaluate(ExecutionContext& context) con | |||
|         } | ||||
|         return Result { SQLCommand::Unknown, SQLErrorCode::BooleanOperatorTypeMismatch, UnaryOperator_name(type()) }; | ||||
|     case UnaryOperator::BitwiseNot: | ||||
|         if (expression_value.type() == SQLType::Integer) { | ||||
|             expression_value = ~expression_value.to_u32().value(); | ||||
|             return expression_value; | ||||
|         } | ||||
|         return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOperatorTypeMismatch, UnaryOperator_name(type()) }; | ||||
|         return expression_value.bitwise_not(); | ||||
|     default: | ||||
|         VERIFY_NOT_REACHED(); | ||||
|     } | ||||
|  |  | |||
|  | @ -119,7 +119,7 @@ ResultOr<ResultSet> Select::execute(ExecutionContext& context) const | |||
| 
 | ||||
|         auto limit = TRY(m_limit_clause->limit_expression()->evaluate(context)); | ||||
|         if (!limit.is_null()) { | ||||
|             auto limit_value_maybe = limit.to_u32(); | ||||
|             auto limit_value_maybe = limit.to_int<size_t>(); | ||||
|             if (!limit_value_maybe.has_value()) | ||||
|                 return Result { SQLCommand::Select, SQLErrorCode::SyntaxError, "LIMIT clause must evaluate to an integer value"sv }; | ||||
| 
 | ||||
|  | @ -129,7 +129,7 @@ ResultOr<ResultSet> Select::execute(ExecutionContext& context) const | |||
|         if (m_limit_clause->offset_expression() != nullptr) { | ||||
|             auto offset = TRY(m_limit_clause->offset_expression()->evaluate(context)); | ||||
|             if (!offset.is_null()) { | ||||
|                 auto offset_value_maybe = offset.to_u32(); | ||||
|                 auto offset_value_maybe = offset.to_int<size_t>(); | ||||
|                 if (!offset_value_maybe.has_value()) | ||||
|                     return Result { SQLCommand::Select, SQLErrorCode::SyntaxError, "OFFSET clause must evaluate to an integer value"sv }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -165,7 +165,7 @@ ResultOr<NonnullRefPtr<TableDef>> Database::get_table(DeprecatedString const& sc | |||
| 
 | ||||
|     auto table_hash = table_def->hash(); | ||||
|     auto column_key = ColumnDef::make_key(table_def); | ||||
|     for (auto it = m_table_columns->find(column_key); !it.is_end() && ((*it)["table_hash"].to_u32().value() == table_hash); ++it) | ||||
|     for (auto it = m_table_columns->find(column_key); !it.is_end() && ((*it)["table_hash"].to_int<u32>() == table_hash); ++it) | ||||
|         table_def->append_column(*it); | ||||
| 
 | ||||
|     return table_def; | ||||
|  |  | |||
|  | @ -33,7 +33,7 @@ class Heap : public Core::Object { | |||
|     C_OBJECT(Heap); | ||||
| 
 | ||||
| public: | ||||
|     static constexpr inline u32 current_version = 2; | ||||
|     static constexpr inline u32 current_version = 3; | ||||
| 
 | ||||
|     virtual ~Heap() override; | ||||
| 
 | ||||
|  |  | |||
|  | @ -59,9 +59,9 @@ Key ColumnDef::key() const | |||
| { | ||||
|     auto key = Key(index_def()); | ||||
|     key["table_hash"] = parent_relation()->hash(); | ||||
|     key["column_number"] = (int)column_number(); | ||||
|     key["column_number"] = column_number(); | ||||
|     key["column_name"] = name(); | ||||
|     key["column_type"] = (int)type(); | ||||
|     key["column_type"] = to_underlying(type()); | ||||
|     return key; | ||||
| } | ||||
| 
 | ||||
|  | @ -183,7 +183,7 @@ void TableDef::append_column(DeprecatedString name, SQLType sql_type) | |||
| 
 | ||||
| void TableDef::append_column(Key const& column) | ||||
| { | ||||
|     auto column_type = column["column_type"].to_int(); | ||||
|     auto column_type = column["column_type"].to_int<UnderlyingType<SQLType>>(); | ||||
|     VERIFY(column_type.has_value()); | ||||
| 
 | ||||
|     append_column(column["column_name"].to_deprecated_string(), static_cast<SQLType>(*column_type)); | ||||
|  |  | |||
|  | @ -48,6 +48,7 @@ constexpr char const* command_tag(SQLCommand command) | |||
|     S(DatabaseDoesNotExist, "Database '{}' does not exist")                                       \ | ||||
|     S(DatabaseUnavailable, "Database Unavailable")                                                \ | ||||
|     S(IntegerOperatorTypeMismatch, "Cannot apply '{}' operator to non-numeric operands")          \ | ||||
|     S(IntegerOverflow, "Operation would cause integer overflow")                                  \ | ||||
|     S(InternalError, "{}")                                                                        \ | ||||
|     S(InvalidDatabaseName, "Invalid database name '{}'")                                          \ | ||||
|     S(InvalidNumberOfPlaceholderValues, "Number of values does not match number of placeholders") \ | ||||
|  |  | |||
|  | @ -12,11 +12,87 @@ | |||
| #include <LibSQL/Serializer.h> | ||||
| #include <LibSQL/TupleDescriptor.h> | ||||
| #include <LibSQL/Value.h> | ||||
| #include <math.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| namespace SQL { | ||||
| 
 | ||||
| // We use the upper 4 bits of the encoded type to store extra information about the type. This
 | ||||
| // includes if the value is null, and the encoded size of any integer type. Of course, this encoding
 | ||||
| // only works if the SQL type itself fits in the lower 4 bits.
 | ||||
| enum class SQLTypeWithCount { | ||||
| #undef __ENUMERATE_SQL_TYPE | ||||
| #define __ENUMERATE_SQL_TYPE(name, type) type, | ||||
|     ENUMERATE_SQL_TYPES(__ENUMERATE_SQL_TYPE) | ||||
| #undef __ENUMERATE_SQL_TYPE | ||||
|         Count, | ||||
| }; | ||||
| 
 | ||||
| static_assert(to_underlying(SQLTypeWithCount::Count) <= 0x0f, "Too many SQL types for current encoding"); | ||||
| 
 | ||||
| // Adding to this list is fine, but changing the order of any value here will result in LibSQL
 | ||||
| // becoming unable to read existing .db files. If the order must absolutely be changed, be sure
 | ||||
| // to bump Heap::current_version.
 | ||||
| enum class TypeData : u8 { | ||||
|     Null = 1 << 4, | ||||
|     Int8 = 2 << 4, | ||||
|     Int16 = 3 << 4, | ||||
|     Int32 = 4 << 4, | ||||
|     Int64 = 5 << 4, | ||||
|     Uint8 = 6 << 4, | ||||
|     Uint16 = 7 << 4, | ||||
|     Uint32 = 8 << 4, | ||||
|     Uint64 = 9 << 4, | ||||
| }; | ||||
| 
 | ||||
| template<typename Callback> | ||||
| static decltype(auto) downsize_integer(Integer auto value, Callback&& callback) | ||||
| { | ||||
|     if constexpr (IsSigned<decltype(value)>) { | ||||
|         if (AK::is_within_range<i8>(value)) | ||||
|             return callback(static_cast<i8>(value), TypeData::Int8); | ||||
|         if (AK::is_within_range<i16>(value)) | ||||
|             return callback(static_cast<i16>(value), TypeData::Int16); | ||||
|         if (AK::is_within_range<i32>(value)) | ||||
|             return callback(static_cast<i32>(value), TypeData::Int32); | ||||
|         return callback(value, TypeData::Int64); | ||||
|     } else { | ||||
|         if (AK::is_within_range<u8>(value)) | ||||
|             return callback(static_cast<i8>(value), TypeData::Uint8); | ||||
|         if (AK::is_within_range<u16>(value)) | ||||
|             return callback(static_cast<i16>(value), TypeData::Uint16); | ||||
|         if (AK::is_within_range<u32>(value)) | ||||
|             return callback(static_cast<i32>(value), TypeData::Uint32); | ||||
|         return callback(value, TypeData::Uint64); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template<typename Callback> | ||||
| static decltype(auto) downsize_integer(Value const& value, Callback&& callback) | ||||
| { | ||||
|     VERIFY(value.is_int()); | ||||
| 
 | ||||
|     if (value.value().has<i64>()) | ||||
|         return downsize_integer(value.value().get<i64>(), forward<Callback>(callback)); | ||||
|     return downsize_integer(value.value().get<u64>(), forward<Callback>(callback)); | ||||
| } | ||||
| 
 | ||||
| template<typename Callback> | ||||
| static ResultOr<Value> perform_integer_operation(Value const& lhs, Value const& rhs, Callback&& callback) | ||||
| { | ||||
|     VERIFY(lhs.is_int()); | ||||
|     VERIFY(rhs.is_int()); | ||||
| 
 | ||||
|     if (lhs.value().has<i64>()) { | ||||
|         if (auto rhs_value = rhs.to_int<i64>(); rhs_value.has_value()) | ||||
|             return callback(lhs.to_int<i64>().value(), rhs_value.value()); | ||||
|     } else { | ||||
|         if (auto rhs_value = rhs.to_int<u64>(); rhs_value.has_value()) | ||||
|             return callback(lhs.to_int<u64>().value(), rhs_value.value()); | ||||
|     } | ||||
| 
 | ||||
|     return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow }; | ||||
| } | ||||
| 
 | ||||
| Value::Value(SQLType type) | ||||
|     : m_type(type) | ||||
| { | ||||
|  | @ -28,22 +104,23 @@ Value::Value(DeprecatedString value) | |||
| { | ||||
| } | ||||
| 
 | ||||
| Value::Value(int value) | ||||
|     : m_type(SQLType::Integer) | ||||
|     , m_value(value) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| Value::Value(u32 value) | ||||
|     : m_type(SQLType::Integer) | ||||
|     , m_value(static_cast<int>(value)) // FIXME: Handle signed overflow.
 | ||||
| { | ||||
| } | ||||
| 
 | ||||
| Value::Value(double value) | ||||
|     : m_type(SQLType::Float) | ||||
|     , m_value(value) | ||||
| { | ||||
|     if (trunc(value) == value) { | ||||
|         if (AK::is_within_range<i64>(value)) { | ||||
|             m_type = SQLType::Integer; | ||||
|             m_value = static_cast<i64>(value); | ||||
|             return; | ||||
|         } | ||||
|         if (AK::is_within_range<u64>(value)) { | ||||
|             m_type = SQLType::Integer; | ||||
|             m_value = static_cast<u64>(value); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     m_type = SQLType::Float; | ||||
|     m_value = value; | ||||
| } | ||||
| 
 | ||||
| Value::Value(NonnullRefPtr<TupleDescriptor> descriptor, Vector<Value> values) | ||||
|  | @ -122,6 +199,11 @@ bool Value::is_null() const | |||
|     return !m_value.has_value(); | ||||
| } | ||||
| 
 | ||||
| bool Value::is_int() const | ||||
| { | ||||
|     return m_value.has_value() && (m_value->has<i64>() || m_value->has<u64>()); | ||||
| } | ||||
| 
 | ||||
| DeprecatedString Value::to_deprecated_string() const | ||||
| { | ||||
|     if (is_null()) | ||||
|  | @ -129,7 +211,7 @@ DeprecatedString Value::to_deprecated_string() const | |||
| 
 | ||||
|     return m_value->visit( | ||||
|         [](DeprecatedString const& value) -> DeprecatedString { return value; }, | ||||
|         [](int value) -> DeprecatedString { return DeprecatedString::number(value); }, | ||||
|         [](Integer auto value) -> DeprecatedString { return DeprecatedString::number(value); }, | ||||
|         [](double value) -> DeprecatedString { return DeprecatedString::number(value); }, | ||||
|         [](bool value) -> DeprecatedString { return value ? "true"sv : "false"sv; }, | ||||
|         [](TupleValue const& value) -> DeprecatedString { | ||||
|  | @ -143,33 +225,6 @@ DeprecatedString Value::to_deprecated_string() const | |||
|         }); | ||||
| } | ||||
| 
 | ||||
| Optional<int> Value::to_int() const | ||||
| { | ||||
|     if (is_null()) | ||||
|         return {}; | ||||
| 
 | ||||
|     return m_value->visit( | ||||
|         [](DeprecatedString const& value) -> Optional<int> { return value.to_int(); }, | ||||
|         [](int value) -> Optional<int> { return value; }, | ||||
|         [](double value) -> Optional<int> { | ||||
|             if (value > static_cast<double>(NumericLimits<int>::max())) | ||||
|                 return {}; | ||||
|             if (value < static_cast<double>(NumericLimits<int>::min())) | ||||
|                 return {}; | ||||
|             return static_cast<int>(round(value)); | ||||
|         }, | ||||
|         [](bool value) -> Optional<int> { return static_cast<int>(value); }, | ||||
|         [](TupleValue const&) -> Optional<int> { return {}; }); | ||||
| } | ||||
| 
 | ||||
| Optional<u32> Value::to_u32() const | ||||
| { | ||||
|     // FIXME: Handle negative values.
 | ||||
|     if (auto result = to_int(); result.has_value()) | ||||
|         return static_cast<u32>(result.value()); | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| Optional<double> Value::to_double() const | ||||
| { | ||||
|     if (is_null()) | ||||
|  | @ -184,7 +239,7 @@ Optional<double> Value::to_double() const | |||
|                 return {}; | ||||
|             return result; | ||||
|         }, | ||||
|         [](int value) -> Optional<double> { return static_cast<double>(value); }, | ||||
|         [](Integer auto value) -> Optional<double> { return static_cast<double>(value); }, | ||||
|         [](double value) -> Optional<double> { return value; }, | ||||
|         [](bool value) -> Optional<double> { return static_cast<double>(value); }, | ||||
|         [](TupleValue const&) -> Optional<double> { return {}; }); | ||||
|  | @ -203,7 +258,7 @@ Optional<bool> Value::to_bool() const | |||
|                 return false; | ||||
|             return {}; | ||||
|         }, | ||||
|         [](int value) -> Optional<bool> { return static_cast<bool>(value); }, | ||||
|         [](Integer auto value) -> Optional<bool> { return static_cast<bool>(value); }, | ||||
|         [](double value) -> Optional<bool> { return fabs(value) > NumericLimits<double>::epsilon(); }, | ||||
|         [](bool value) -> Optional<bool> { return value; }, | ||||
|         [](TupleValue const& value) -> Optional<bool> { | ||||
|  | @ -242,20 +297,6 @@ Value& Value::operator=(DeprecatedString value) | |||
|     return *this; | ||||
| } | ||||
| 
 | ||||
| Value& Value::operator=(int value) | ||||
| { | ||||
|     m_type = SQLType::Integer; | ||||
|     m_value = value; | ||||
|     return *this; | ||||
| } | ||||
| 
 | ||||
| Value& Value::operator=(u32 value) | ||||
| { | ||||
|     m_type = SQLType::Integer; | ||||
|     m_value = static_cast<int>(value); // FIXME: Handle signed overflow.
 | ||||
|     return *this; | ||||
| } | ||||
| 
 | ||||
| Value& Value::operator=(double value) | ||||
| { | ||||
|     m_type = SQLType::Float; | ||||
|  | @ -318,7 +359,11 @@ size_t Value::length() const | |||
|     // FIXME: This seems to be more of an encoded byte size rather than a length.
 | ||||
|     return m_value->visit( | ||||
|         [](DeprecatedString const& value) -> size_t { return sizeof(u32) + value.length(); }, | ||||
|         [](int value) -> size_t { return sizeof(value); }, | ||||
|         [](Integer auto value) -> size_t { | ||||
|             return downsize_integer(value, [](auto integer, auto) { | ||||
|                 return sizeof(integer); | ||||
|             }); | ||||
|         }, | ||||
|         [](double value) -> size_t { return sizeof(value); }, | ||||
|         [](bool value) -> size_t { return sizeof(value); }, | ||||
|         [](TupleValue const& value) -> size_t { | ||||
|  | @ -338,7 +383,14 @@ u32 Value::hash() const | |||
| 
 | ||||
|     return m_value->visit( | ||||
|         [](DeprecatedString const& value) -> u32 { return value.hash(); }, | ||||
|         [](int value) -> u32 { return int_hash(value); }, | ||||
|         [](Integer auto value) -> u32 { | ||||
|             return downsize_integer(value, [](auto integer, auto) { | ||||
|                 if constexpr (sizeof(decltype(integer)) == 8) | ||||
|                     return u64_hash(integer); | ||||
|                 else | ||||
|                     return int_hash(integer); | ||||
|             }); | ||||
|         }, | ||||
|         [](double) -> u32 { VERIFY_NOT_REACHED(); }, | ||||
|         [](bool value) -> u32 { return int_hash(value); }, | ||||
|         [](TupleValue const& value) -> u32 { | ||||
|  | @ -364,8 +416,8 @@ int Value::compare(Value const& other) const | |||
| 
 | ||||
|     return m_value->visit( | ||||
|         [&](DeprecatedString const& value) -> int { return value.view().compare(other.to_deprecated_string()); }, | ||||
|         [&](int value) -> int { | ||||
|             auto casted = other.to_int(); | ||||
|         [&](Integer auto value) -> int { | ||||
|             auto casted = other.to_int<IntegerType<decltype(value)>>(); | ||||
|             if (!casted.has_value()) | ||||
|                 return 1; | ||||
| 
 | ||||
|  | @ -427,16 +479,6 @@ bool Value::operator==(StringView value) const | |||
|     return to_deprecated_string() == value; | ||||
| } | ||||
| 
 | ||||
| bool Value::operator==(int value) const | ||||
| { | ||||
|     return to_int() == value; | ||||
| } | ||||
| 
 | ||||
| bool Value::operator==(u32 value) const | ||||
| { | ||||
|     return to_u32() == value; | ||||
| } | ||||
| 
 | ||||
| bool Value::operator==(double value) const | ||||
| { | ||||
|     return to_double() == value; | ||||
|  | @ -467,133 +509,218 @@ bool Value::operator>=(Value const& value) const | |||
|     return compare(value) >= 0; | ||||
| } | ||||
| 
 | ||||
| static Result invalid_type_for_numeric_operator(AST::BinaryOperator op) | ||||
| template<typename Operator> | ||||
| static Result invalid_type_for_numeric_operator(Operator op) | ||||
| { | ||||
|     return { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, BinaryOperator_name(op) }; | ||||
|     if constexpr (IsSame<Operator, AST::BinaryOperator>) | ||||
|         return { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, BinaryOperator_name(op) }; | ||||
|     else if constexpr (IsSame<Operator, AST::UnaryOperator>) | ||||
|         return { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, UnaryOperator_name(op) }; | ||||
|     else | ||||
|         static_assert(DependentFalse<Operator>); | ||||
| } | ||||
| 
 | ||||
| ResultOr<Value> Value::add(Value const& other) const | ||||
| { | ||||
|     if (auto double_maybe = to_double(); double_maybe.has_value()) { | ||||
|         if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value()) | ||||
|             return Value(double_maybe.value() + other_double_maybe.value()); | ||||
|         if (auto int_maybe = other.to_int(); int_maybe.has_value()) | ||||
|             return Value(double_maybe.value() + (double)int_maybe.value()); | ||||
|     } else if (auto int_maybe = to_int(); int_maybe.has_value()) { | ||||
|         if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value()) | ||||
|             return Value(other_double_maybe.value() + (double)int_maybe.value()); | ||||
|         if (auto other_int_maybe = other.to_int(); other_int_maybe.has_value()) | ||||
|             return Value(int_maybe.value() + other_int_maybe.value()); | ||||
|     if (is_int() && other.is_int()) { | ||||
|         return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> { | ||||
|             Checked result { lhs }; | ||||
|             result.add(rhs); | ||||
| 
 | ||||
|             if (result.has_overflow()) | ||||
|                 return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow }; | ||||
|             return Value { result.value_unchecked() }; | ||||
|         }); | ||||
|     } | ||||
|     return invalid_type_for_numeric_operator(AST::BinaryOperator::Plus); | ||||
| 
 | ||||
|     auto lhs = to_double(); | ||||
|     auto rhs = other.to_double(); | ||||
| 
 | ||||
|     if (!lhs.has_value() || !rhs.has_value()) | ||||
|         return invalid_type_for_numeric_operator(AST::BinaryOperator::Plus); | ||||
|     return Value { lhs.value() + rhs.value() }; | ||||
| } | ||||
| 
 | ||||
| ResultOr<Value> Value::subtract(Value const& other) const | ||||
| { | ||||
|     if (auto double_maybe = to_double(); double_maybe.has_value()) { | ||||
|         if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value()) | ||||
|             return Value(double_maybe.value() - other_double_maybe.value()); | ||||
|         if (auto int_maybe = other.to_int(); int_maybe.has_value()) | ||||
|             return Value(double_maybe.value() - (double)int_maybe.value()); | ||||
|     } else if (auto int_maybe = to_int(); int_maybe.has_value()) { | ||||
|         if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value()) | ||||
|             return Value((double)int_maybe.value() - other_double_maybe.value()); | ||||
|         if (auto other_int_maybe = other.to_int(); other_int_maybe.has_value()) | ||||
|             return Value(int_maybe.value() - other_int_maybe.value()); | ||||
|     if (is_int() && other.is_int()) { | ||||
|         return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> { | ||||
|             Checked result { lhs }; | ||||
|             result.sub(rhs); | ||||
| 
 | ||||
|             if (result.has_overflow()) | ||||
|                 return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow }; | ||||
|             return Value { result.value_unchecked() }; | ||||
|         }); | ||||
|     } | ||||
|     return invalid_type_for_numeric_operator(AST::BinaryOperator::Minus); | ||||
| 
 | ||||
|     auto lhs = to_double(); | ||||
|     auto rhs = other.to_double(); | ||||
| 
 | ||||
|     if (!lhs.has_value() || !rhs.has_value()) | ||||
|         return invalid_type_for_numeric_operator(AST::BinaryOperator::Minus); | ||||
|     return Value { lhs.value() - rhs.value() }; | ||||
| } | ||||
| 
 | ||||
| ResultOr<Value> Value::multiply(Value const& other) const | ||||
| { | ||||
|     if (auto double_maybe = to_double(); double_maybe.has_value()) { | ||||
|         if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value()) | ||||
|             return Value(double_maybe.value() * other_double_maybe.value()); | ||||
|         if (auto int_maybe = other.to_int(); int_maybe.has_value()) | ||||
|             return Value(double_maybe.value() * (double)int_maybe.value()); | ||||
|     } else if (auto int_maybe = to_int(); int_maybe.has_value()) { | ||||
|         if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value()) | ||||
|             return Value((double)int_maybe.value() * other_double_maybe.value()); | ||||
|         if (auto other_int_maybe = other.to_int(); other_int_maybe.has_value()) | ||||
|             return Value(int_maybe.value() * other_int_maybe.value()); | ||||
|     if (is_int() && other.is_int()) { | ||||
|         return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> { | ||||
|             Checked result { lhs }; | ||||
|             result.mul(rhs); | ||||
| 
 | ||||
|             if (result.has_overflow()) | ||||
|                 return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow }; | ||||
|             return Value { result.value_unchecked() }; | ||||
|         }); | ||||
|     } | ||||
|     return invalid_type_for_numeric_operator(AST::BinaryOperator::Multiplication); | ||||
| 
 | ||||
|     auto lhs = to_double(); | ||||
|     auto rhs = other.to_double(); | ||||
| 
 | ||||
|     if (!lhs.has_value() || !rhs.has_value()) | ||||
|         return invalid_type_for_numeric_operator(AST::BinaryOperator::Multiplication); | ||||
|     return Value { lhs.value() * rhs.value() }; | ||||
| } | ||||
| 
 | ||||
| ResultOr<Value> Value::divide(Value const& other) const | ||||
| { | ||||
|     if (auto double_maybe = to_double(); double_maybe.has_value()) { | ||||
|         if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value()) | ||||
|             return Value(double_maybe.value() / other_double_maybe.value()); | ||||
|         if (auto int_maybe = other.to_int(); int_maybe.has_value()) | ||||
|             return Value(double_maybe.value() / (double)int_maybe.value()); | ||||
|     } else if (auto int_maybe = to_int(); int_maybe.has_value()) { | ||||
|         if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value()) | ||||
|             return Value((double)int_maybe.value() / other_double_maybe.value()); | ||||
|         if (auto other_int_maybe = other.to_int(); other_int_maybe.has_value()) | ||||
|             return Value(int_maybe.value() / other_int_maybe.value()); | ||||
|     } | ||||
|     return invalid_type_for_numeric_operator(AST::BinaryOperator::Division); | ||||
|     auto lhs = to_double(); | ||||
|     auto rhs = other.to_double(); | ||||
| 
 | ||||
|     if (!lhs.has_value() || !rhs.has_value()) | ||||
|         return invalid_type_for_numeric_operator(AST::BinaryOperator::Division); | ||||
|     if (rhs == 0.0) | ||||
|         return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow }; | ||||
| 
 | ||||
|     return Value { lhs.value() / rhs.value() }; | ||||
| } | ||||
| 
 | ||||
| ResultOr<Value> Value::modulo(Value const& other) const | ||||
| { | ||||
|     auto int_maybe_1 = to_int(); | ||||
|     auto int_maybe_2 = other.to_int(); | ||||
|     if (!int_maybe_1.has_value() || !int_maybe_2.has_value()) | ||||
|     if (!is_int() || !other.is_int()) | ||||
|         return invalid_type_for_numeric_operator(AST::BinaryOperator::Modulo); | ||||
|     return Value(int_maybe_1.value() % int_maybe_2.value()); | ||||
| 
 | ||||
|     return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> { | ||||
|         Checked result { lhs }; | ||||
|         result.mod(rhs); | ||||
| 
 | ||||
|         if (result.has_overflow()) | ||||
|             return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow }; | ||||
|         return Value { result.value_unchecked() }; | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| ResultOr<Value> Value::negate() const | ||||
| { | ||||
|     if (type() == SQLType::Integer) { | ||||
|         auto value = to_int<i64>(); | ||||
|         if (!value.has_value()) | ||||
|             return invalid_type_for_numeric_operator(AST::UnaryOperator::Minus); | ||||
| 
 | ||||
|         return Value { value.value() * -1 }; | ||||
|     } | ||||
| 
 | ||||
|     if (type() == SQLType::Float) | ||||
|         return Value { -to_double().value() }; | ||||
| 
 | ||||
|     return invalid_type_for_numeric_operator(AST::UnaryOperator::Minus); | ||||
| } | ||||
| 
 | ||||
| ResultOr<Value> Value::shift_left(Value const& other) const | ||||
| { | ||||
|     auto u32_maybe = to_u32(); | ||||
|     auto num_bytes_maybe = other.to_int(); | ||||
|     if (!u32_maybe.has_value() || !num_bytes_maybe.has_value()) | ||||
|     if (!is_int() || !other.is_int()) | ||||
|         return invalid_type_for_numeric_operator(AST::BinaryOperator::ShiftLeft); | ||||
|     return Value(u32_maybe.value() << num_bytes_maybe.value()); | ||||
| 
 | ||||
|     return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> { | ||||
|         using LHS = decltype(lhs); | ||||
|         using RHS = decltype(rhs); | ||||
| 
 | ||||
|         static constexpr auto max_shift = static_cast<RHS>(sizeof(LHS) * 8); | ||||
|         if (rhs < 0 || rhs >= max_shift) | ||||
|             return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow }; | ||||
| 
 | ||||
|         return Value { lhs << rhs }; | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| ResultOr<Value> Value::shift_right(Value const& other) const | ||||
| { | ||||
|     auto u32_maybe = to_u32(); | ||||
|     auto num_bytes_maybe = other.to_int(); | ||||
|     if (!u32_maybe.has_value() || !num_bytes_maybe.has_value()) | ||||
|     if (!is_int() || !other.is_int()) | ||||
|         return invalid_type_for_numeric_operator(AST::BinaryOperator::ShiftRight); | ||||
|     return Value(u32_maybe.value() >> num_bytes_maybe.value()); | ||||
| 
 | ||||
|     return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> { | ||||
|         using LHS = decltype(lhs); | ||||
|         using RHS = decltype(rhs); | ||||
| 
 | ||||
|         static constexpr auto max_shift = static_cast<RHS>(sizeof(LHS) * 8); | ||||
|         if (rhs < 0 || rhs >= max_shift) | ||||
|             return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow }; | ||||
| 
 | ||||
|         return Value { lhs >> rhs }; | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| ResultOr<Value> Value::bitwise_or(Value const& other) const | ||||
| { | ||||
|     auto u32_maybe_1 = to_u32(); | ||||
|     auto u32_maybe_2 = other.to_u32(); | ||||
|     if (!u32_maybe_1.has_value() || !u32_maybe_2.has_value()) | ||||
|     if (!is_int() || !other.is_int()) | ||||
|         return invalid_type_for_numeric_operator(AST::BinaryOperator::BitwiseOr); | ||||
|     return Value(u32_maybe_1.value() | u32_maybe_2.value()); | ||||
| 
 | ||||
|     return perform_integer_operation(*this, other, [](auto lhs, auto rhs) { | ||||
|         return Value { lhs | rhs }; | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| ResultOr<Value> Value::bitwise_and(Value const& other) const | ||||
| { | ||||
|     auto u32_maybe_1 = to_u32(); | ||||
|     auto u32_maybe_2 = other.to_u32(); | ||||
|     if (!u32_maybe_1.has_value() || !u32_maybe_2.has_value()) | ||||
|     if (!is_int() || !other.is_int()) | ||||
|         return invalid_type_for_numeric_operator(AST::BinaryOperator::BitwiseAnd); | ||||
|     return Value(u32_maybe_1.value() & u32_maybe_2.value()); | ||||
| 
 | ||||
|     return perform_integer_operation(*this, other, [](auto lhs, auto rhs) { | ||||
|         return Value { lhs & rhs }; | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| static constexpr auto sql_type_null_as_flag = static_cast<u8>(SQLType::Null); | ||||
| ResultOr<Value> Value::bitwise_not() const | ||||
| { | ||||
|     if (!is_int()) | ||||
|         return invalid_type_for_numeric_operator(AST::UnaryOperator::BitwiseNot); | ||||
| 
 | ||||
|     return downsize_integer(*this, [](auto value, auto) { | ||||
|         return Value { ~value }; | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| static u8 encode_type_flags(Value const& value) | ||||
| { | ||||
|     auto type_flags = to_underlying(value.type()); | ||||
| 
 | ||||
|     if (value.is_null()) { | ||||
|         type_flags |= to_underlying(TypeData::Null); | ||||
|     } else if (value.is_int()) { | ||||
|         downsize_integer(value, [&](auto, auto type_data) { | ||||
|             type_flags |= to_underlying(type_data); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     return type_flags; | ||||
| } | ||||
| 
 | ||||
| void Value::serialize(Serializer& serializer) const | ||||
| { | ||||
|     auto type_flags = static_cast<u8>(type()); | ||||
|     if (is_null()) | ||||
|         type_flags |= sql_type_null_as_flag; | ||||
| 
 | ||||
|     auto type_flags = encode_type_flags(*this); | ||||
|     serializer.serialize<u8>(type_flags); | ||||
| 
 | ||||
|     if (is_null()) | ||||
|         return; | ||||
| 
 | ||||
|     if (is_int()) { | ||||
|         downsize_integer(*this, [&](auto integer, auto) { | ||||
|             serializer.serialize(integer); | ||||
|         }); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     m_value->visit( | ||||
|         [&](TupleValue const& value) { | ||||
|             serializer.serialize<TupleDescriptor>(*value.descriptor); | ||||
|  | @ -608,16 +735,11 @@ void Value::serialize(Serializer& serializer) const | |||
| void Value::deserialize(Serializer& serializer) | ||||
| { | ||||
|     auto type_flags = serializer.deserialize<u8>(); | ||||
|     bool has_value = true; | ||||
| 
 | ||||
|     if ((type_flags & sql_type_null_as_flag) && (type_flags != sql_type_null_as_flag)) { | ||||
|         type_flags &= ~sql_type_null_as_flag; | ||||
|         has_value = false; | ||||
|     } | ||||
|     auto type_data = static_cast<TypeData>(type_flags & 0xf0); | ||||
|     m_type = static_cast<SQLType>(type_flags & 0x0f); | ||||
| 
 | ||||
|     m_type = static_cast<SQLType>(type_flags); | ||||
| 
 | ||||
|     if (!has_value) | ||||
|     if (type_data == TypeData::Null) | ||||
|         return; | ||||
| 
 | ||||
|     switch (m_type) { | ||||
|  | @ -628,7 +750,35 @@ void Value::deserialize(Serializer& serializer) | |||
|         m_value = serializer.deserialize<DeprecatedString>(); | ||||
|         break; | ||||
|     case SQLType::Integer: | ||||
|         m_value = serializer.deserialize<int>(0); | ||||
|         switch (type_data) { | ||||
|         case TypeData::Int8: | ||||
|             m_value = static_cast<i64>(serializer.deserialize<i8>(0)); | ||||
|             break; | ||||
|         case TypeData::Int16: | ||||
|             m_value = static_cast<i64>(serializer.deserialize<i16>(0)); | ||||
|             break; | ||||
|         case TypeData::Int32: | ||||
|             m_value = static_cast<i64>(serializer.deserialize<i32>(0)); | ||||
|             break; | ||||
|         case TypeData::Int64: | ||||
|             m_value = static_cast<i64>(serializer.deserialize<i64>(0)); | ||||
|             break; | ||||
|         case TypeData::Uint8: | ||||
|             m_value = static_cast<u64>(serializer.deserialize<u8>(0)); | ||||
|             break; | ||||
|         case TypeData::Uint16: | ||||
|             m_value = static_cast<u64>(serializer.deserialize<u16>(0)); | ||||
|             break; | ||||
|         case TypeData::Uint32: | ||||
|             m_value = static_cast<u64>(serializer.deserialize<u32>(0)); | ||||
|             break; | ||||
|         case TypeData::Uint64: | ||||
|             m_value = static_cast<u64>(serializer.deserialize<u64>(0)); | ||||
|             break; | ||||
|         default: | ||||
|             VERIFY_NOT_REACHED(); | ||||
|             break; | ||||
|         } | ||||
|         break; | ||||
|     case SQLType::Float: | ||||
|         m_value = serializer.deserialize<double>(0.0); | ||||
|  | @ -673,11 +823,9 @@ ResultOr<NonnullRefPtr<TupleDescriptor>> Value::infer_tuple_descriptor(Vector<Va | |||
| template<> | ||||
| bool IPC::encode(Encoder& encoder, SQL::Value const& value) | ||||
| { | ||||
|     auto type_flags = to_underlying(value.type()); | ||||
|     if (value.is_null()) | ||||
|         type_flags |= SQL::sql_type_null_as_flag; | ||||
| 
 | ||||
|     auto type_flags = encode_type_flags(value); | ||||
|     encoder << type_flags; | ||||
| 
 | ||||
|     if (value.is_null()) | ||||
|         return true; | ||||
| 
 | ||||
|  | @ -688,7 +836,9 @@ bool IPC::encode(Encoder& encoder, SQL::Value const& value) | |||
|         encoder << value.to_deprecated_string(); | ||||
|         break; | ||||
|     case SQL::SQLType::Integer: | ||||
|         encoder << value.to_int().value(); | ||||
|         SQL::downsize_integer(value, [&](auto integer, auto) { | ||||
|             encoder << integer; | ||||
|         }); | ||||
|         break; | ||||
|     case SQL::SQLType::Float: | ||||
|         encoder << value.to_double().value(); | ||||
|  | @ -704,46 +854,72 @@ bool IPC::encode(Encoder& encoder, SQL::Value const& value) | |||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template<typename T> | ||||
| static ErrorOr<void> decode_scalar(IPC::Decoder& decoder, SQL::Value& value) | ||||
| { | ||||
|     T decoded {}; | ||||
|     TRY(decoder.decode(decoded)); | ||||
|     value = move(decoded); | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| ErrorOr<void> IPC::decode(Decoder& decoder, SQL::Value& value) | ||||
| { | ||||
|     UnderlyingType<SQL::SQLType> type_flags; | ||||
|     u8 type_flags { 0 }; | ||||
|     TRY(decoder.decode(type_flags)); | ||||
| 
 | ||||
|     if ((type_flags & SQL::sql_type_null_as_flag) && (type_flags != SQL::sql_type_null_as_flag)) { | ||||
|         type_flags &= ~SQL::sql_type_null_as_flag; | ||||
|     auto type_data = static_cast<SQL::TypeData>(type_flags & 0xf0); | ||||
|     auto type = static_cast<SQL::SQLType>(type_flags & 0x0f); | ||||
| 
 | ||||
|         value = SQL::Value(static_cast<SQL::SQLType>(type_flags)); | ||||
|     if (type_data == SQL::TypeData::Null) { | ||||
|         value = SQL::Value(type); | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     switch (static_cast<SQL::SQLType>(type_flags)) { | ||||
|     switch (type) { | ||||
|     case SQL::SQLType::Null: | ||||
|         break; | ||||
|     case SQL::SQLType::Text: { | ||||
|         DeprecatedString text; | ||||
|         TRY(decoder.decode(text)); | ||||
|         value = move(text); | ||||
|     case SQL::SQLType::Text: | ||||
|         TRY(decode_scalar<DeprecatedString>(decoder, value)); | ||||
|         break; | ||||
|     } | ||||
|     case SQL::SQLType::Integer: { | ||||
|         int number { 0 }; | ||||
|         TRY(decoder.decode(number)); | ||||
|         value = number; | ||||
|     case SQL::SQLType::Integer: | ||||
|         switch (type_data) { | ||||
|         case SQL::TypeData::Int8: | ||||
|             TRY(decode_scalar<i8>(decoder, value)); | ||||
|             break; | ||||
|         case SQL::TypeData::Int16: | ||||
|             TRY(decode_scalar<i16>(decoder, value)); | ||||
|             break; | ||||
|         case SQL::TypeData::Int32: | ||||
|             TRY(decode_scalar<i32>(decoder, value)); | ||||
|             break; | ||||
|         case SQL::TypeData::Int64: | ||||
|             TRY(decode_scalar<i64>(decoder, value)); | ||||
|             break; | ||||
|         case SQL::TypeData::Uint8: | ||||
|             TRY(decode_scalar<u8>(decoder, value)); | ||||
|             break; | ||||
|         case SQL::TypeData::Uint16: | ||||
|             TRY(decode_scalar<u16>(decoder, value)); | ||||
|             break; | ||||
|         case SQL::TypeData::Uint32: | ||||
|             TRY(decode_scalar<u32>(decoder, value)); | ||||
|             break; | ||||
|         case SQL::TypeData::Uint64: | ||||
|             TRY(decode_scalar<u64>(decoder, value)); | ||||
|             break; | ||||
|         default: | ||||
|             VERIFY_NOT_REACHED(); | ||||
|             break; | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|     case SQL::SQLType::Float: { | ||||
|         double number { 0.0 }; | ||||
|         TRY(decoder.decode(number)); | ||||
|         value = number; | ||||
|     case SQL::SQLType::Float: | ||||
|         TRY(decode_scalar<double>(decoder, value)); | ||||
|         break; | ||||
|     } | ||||
|     case SQL::SQLType::Boolean: { | ||||
|         bool boolean { false }; | ||||
|         TRY(decoder.decode(boolean)); | ||||
|         value = boolean; | ||||
|     case SQL::SQLType::Boolean: | ||||
|         TRY(decode_scalar<bool>(decoder, value)); | ||||
|         break; | ||||
|     } | ||||
|     case SQL::SQLType::Tuple: { | ||||
|         Vector<SQL::Value> tuple; | ||||
|         TRY(decoder.decode(tuple)); | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <AK/Checked.h> | ||||
| #include <AK/DeprecatedString.h> | ||||
| #include <AK/Format.h> | ||||
| #include <AK/Optional.h> | ||||
|  | @ -17,58 +18,107 @@ | |||
| #include <LibSQL/Forward.h> | ||||
| #include <LibSQL/Result.h> | ||||
| #include <LibSQL/Type.h> | ||||
| #include <math.h> | ||||
| 
 | ||||
| namespace SQL { | ||||
| 
 | ||||
| template<typename T> | ||||
| concept Boolean = SameAs<RemoveCVReference<T>, bool>; | ||||
| 
 | ||||
| template<typename T> | ||||
| concept Integer = (Integral<T> && !Boolean<T>); | ||||
| 
 | ||||
| /**
 | ||||
|  * A `Value` is an atomic piece of SQL data`. A `Value` has a basic type | ||||
|  * (Text/String, Integer, Float, etc). Richer types are implemented in higher | ||||
|  * level layers, but the resulting data is stored in these `Value` objects. | ||||
|  */ | ||||
| class Value { | ||||
|     template<Integer T> | ||||
|     using IntegerType = Conditional<IsSigned<T>, i64, u64>; | ||||
| 
 | ||||
| public: | ||||
|     explicit Value(SQLType sql_type = SQLType::Null); | ||||
|     explicit Value(DeprecatedString); | ||||
|     explicit Value(int); | ||||
|     explicit Value(u32); | ||||
|     explicit Value(double); | ||||
|     Value(Value const&); | ||||
|     Value(Value&&); | ||||
|     ~Value(); | ||||
| 
 | ||||
|     static ResultOr<Value> create_tuple(NonnullRefPtr<TupleDescriptor>); | ||||
|     static ResultOr<Value> create_tuple(Vector<Value>); | ||||
|     explicit Value(Integer auto value) | ||||
|         : m_type(SQLType::Integer) | ||||
|         , m_value(static_cast<IntegerType<decltype(value)>>(value)) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     template<typename T> | ||||
|     requires(SameAs<RemoveCVReference<T>, bool>) explicit Value(T value) | ||||
|     explicit Value(Boolean auto value) | ||||
|         : m_type(SQLType::Boolean) | ||||
|         , m_value(value) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     static ResultOr<Value> create_tuple(NonnullRefPtr<TupleDescriptor>); | ||||
|     static ResultOr<Value> create_tuple(Vector<Value>); | ||||
| 
 | ||||
|     [[nodiscard]] SQLType type() const; | ||||
|     [[nodiscard]] StringView type_name() const; | ||||
|     [[nodiscard]] bool is_type_compatible_with(SQLType) const; | ||||
|     [[nodiscard]] bool is_null() const; | ||||
|     [[nodiscard]] bool is_int() const; | ||||
| 
 | ||||
|     [[nodiscard]] auto const& value() const | ||||
|     { | ||||
|         VERIFY(m_value.has_value()); | ||||
|         return *m_value; | ||||
|     } | ||||
| 
 | ||||
|     [[nodiscard]] DeprecatedString to_deprecated_string() const; | ||||
|     [[nodiscard]] Optional<int> to_int() const; | ||||
|     [[nodiscard]] Optional<u32> to_u32() const; | ||||
|     [[nodiscard]] Optional<double> to_double() const; | ||||
|     [[nodiscard]] Optional<bool> to_bool() const; | ||||
|     [[nodiscard]] Optional<Vector<Value>> to_vector() const; | ||||
| 
 | ||||
|     template<Integer T> | ||||
|     [[nodiscard]] Optional<T> to_int() const | ||||
|     { | ||||
|         if (is_null()) | ||||
|             return {}; | ||||
| 
 | ||||
|         return m_value->visit( | ||||
|             [](DeprecatedString const& value) -> Optional<T> { | ||||
|                 if constexpr (IsSigned<T>) | ||||
|                     return value.to_int<T>(); | ||||
|                 else | ||||
|                     return value.to_uint<T>(); | ||||
|             }, | ||||
|             [](Integer auto value) -> Optional<T> { | ||||
|                 if (!AK::is_within_range<T>(value)) | ||||
|                     return {}; | ||||
|                 return static_cast<T>(value); | ||||
|             }, | ||||
|             [](double value) -> Optional<T> { | ||||
|                 if (!AK::is_within_range<T>(value)) | ||||
|                     return {}; | ||||
|                 return static_cast<T>(round(value)); | ||||
|             }, | ||||
|             [](bool value) -> Optional<T> { return static_cast<T>(value); }, | ||||
|             [](TupleValue const&) -> Optional<T> { return {}; }); | ||||
|     } | ||||
| 
 | ||||
|     Value& operator=(Value); | ||||
|     Value& operator=(DeprecatedString); | ||||
|     Value& operator=(int); | ||||
|     Value& operator=(u32); | ||||
|     Value& operator=(double); | ||||
| 
 | ||||
|     Value& operator=(Integer auto value) | ||||
|     { | ||||
|         m_type = SQLType::Integer; | ||||
|         m_value = static_cast<IntegerType<decltype(value)>>(value); | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     ResultOr<void> assign_tuple(NonnullRefPtr<TupleDescriptor>); | ||||
|     ResultOr<void> assign_tuple(Vector<Value>); | ||||
| 
 | ||||
|     template<typename T> | ||||
|     requires(SameAs<RemoveCVReference<T>, bool>) Value& operator=(T value) | ||||
|     Value& operator=(Boolean auto value) | ||||
|     { | ||||
|         m_type = SQLType::Boolean; | ||||
|         m_value = value; | ||||
|  | @ -83,9 +133,14 @@ public: | |||
|     [[nodiscard]] int compare(Value const&) const; | ||||
|     bool operator==(Value const&) const; | ||||
|     bool operator==(StringView) const; | ||||
|     bool operator==(int) const; | ||||
|     bool operator==(u32) const; | ||||
|     bool operator==(double) const; | ||||
| 
 | ||||
|     template<Integer T> | ||||
|     bool operator==(T value) | ||||
|     { | ||||
|         return to_int<T>() == value; | ||||
|     } | ||||
| 
 | ||||
|     bool operator!=(Value const&) const; | ||||
|     bool operator<(Value const&) const; | ||||
|     bool operator<=(Value const&) const; | ||||
|  | @ -97,10 +152,12 @@ public: | |||
|     ResultOr<Value> multiply(Value const&) const; | ||||
|     ResultOr<Value> divide(Value const&) const; | ||||
|     ResultOr<Value> modulo(Value const&) const; | ||||
|     ResultOr<Value> negate() const; | ||||
|     ResultOr<Value> shift_left(Value const&) const; | ||||
|     ResultOr<Value> shift_right(Value const&) const; | ||||
|     ResultOr<Value> bitwise_or(Value const&) const; | ||||
|     ResultOr<Value> bitwise_and(Value const&) const; | ||||
|     ResultOr<Value> bitwise_not() const; | ||||
| 
 | ||||
|     [[nodiscard]] TupleElementDescriptor descriptor() const; | ||||
| 
 | ||||
|  | @ -112,7 +169,7 @@ private: | |||
|         Vector<Value> values; | ||||
|     }; | ||||
| 
 | ||||
|     using ValueType = Variant<DeprecatedString, int, double, bool, TupleValue>; | ||||
|     using ValueType = Variant<DeprecatedString, i64, u64, double, bool, TupleValue>; | ||||
| 
 | ||||
|     static ResultOr<NonnullRefPtr<TupleDescriptor>> infer_tuple_descriptor(Vector<Value> const& values); | ||||
|     Value(NonnullRefPtr<TupleDescriptor> descriptor, Vector<Value> values); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy Flynn
						Timothy Flynn