mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 11:32: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
				
			
		|  | @ -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); | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy Flynn
						Timothy Flynn