1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 07:08:10 +00:00

LibSQL: Rewrite the SQL::Value type to be contained within one class

Currently, the Value class is essentially a "pImpl" wrapper around the
ValueImpl hierarchy of classes. This is a bit difficult to follow and
reason about, as methods jump between the Value class and its impl
classes.

This changes the Variant held by Value to instead store the specified
types (String, int, etc.) directly. In doing so, the ValueImpl classes
are removed, and all methods are now just concise Variant visitors.

As part of this rewrite, support for the "array" type is dropped (or
rather, just not re-implemented) as it was unused. If it's needed in the
future, support can be re-added.

This does retain the ability for non-NULL types to store NULL values
(i.e. an empty Optional). I tried dropping this support as well, but it
is depended upon by the on-disk storage classes in non-trivial ways.
This commit is contained in:
Timothy Flynn 2022-09-21 13:48:02 -04:00 committed by Ali Mohammad Pur
parent 7d41b46a7d
commit 1524288127
7 changed files with 640 additions and 1409 deletions

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, Jan de Visser <jan@de-visser.net>
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -15,121 +16,170 @@
TEST_CASE(null_value)
{
SQL::Value v(SQL::SQLType::Null);
EXPECT(v.type() == SQL::SQLType::Null);
EXPECT_EQ(v.type(), SQL::SQLType::Null);
EXPECT_EQ(v.to_string(), "(null)"sv);
EXPECT(!v.to_bool().has_value());
EXPECT(!v.to_int().has_value());
EXPECT(!v.to_u32().has_value());
EXPECT(!v.to_double().has_value());
}
TEST_CASE(assign_null)
{
SQL::Value v("Test");
EXPECT_EQ(v.type(), SQL::SQLType::Text);
EXPECT(!v.is_null());
v = SQL::Value();
EXPECT_EQ(v.type(), SQL::SQLType::Null);
EXPECT(v.is_null());
v = "Test";
EXPECT(v.is_null());
EXPECT(v.to_string() == "(null)");
}
TEST_CASE(text_value)
{
{
SQL::Value v(SQL::SQLType::Text);
EXPECT_EQ(v.type(), SQL::SQLType::Text);
EXPECT(v.is_null());
v = "Test";
EXPECT(!v.is_null());
EXPECT(v.to_string() == "Test");
}
{
SQL::Value v(SQL::SQLType::Text, String("String Test"));
EXPECT(!v.is_null());
EXPECT(v.to_string() == "String Test");
}
{
SQL::Value v(SQL::SQLType::Text, "const char * Test");
EXPECT(!v.is_null());
EXPECT_EQ(v.to_string(), "const char * Test");
}
{
SQL::Value v(String("String Test"));
EXPECT(v.type() == SQL::SQLType::Text);
EXPECT(!v.is_null());
EXPECT(v.to_string() == "String Test");
}
{
SQL::Value v(SQL::SQLType::Text, SQL::Value(42));
EXPECT(v.type() == SQL::SQLType::Text);
EXPECT(!v.is_null());
EXPECT(v.to_string() == "42");
}
}
TEST_CASE(assign_null)
{
SQL::Value v("Test");
EXPECT(!v.is_null());
v = SQL::Value::null();
EXPECT(v.is_null());
v = "Test"sv;
EXPECT_EQ(v.type(), SQL::SQLType::Text);
EXPECT_EQ(v.to_string(), "Test"sv);
}
{
SQL::Value v(String("String Test"sv));
EXPECT_EQ(v.type(), SQL::SQLType::Text);
EXPECT_EQ(v.to_string(), "String Test"sv);
v = String("String Test 2"sv);
EXPECT_EQ(v.type(), SQL::SQLType::Text);
EXPECT_EQ(v.to_string(), "String Test 2"sv);
}
{
SQL::Value v("const char * Test");
EXPECT_EQ(v.type(), SQL::SQLType::Text);
EXPECT_EQ(v.to_string(), "const char * Test"sv);
v = "const char * Test 2";
EXPECT_EQ(v.type(), SQL::SQLType::Text);
EXPECT_EQ(v.to_string(), "const char * Test 2"sv);
}
}
TEST_CASE(text_value_to_other_types)
{
{
SQL::Value v(SQL::SQLType::Text, "42");
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_double().has_value());
EXPECT(v.to_double().value() - 42.0 < NumericLimits<double>().epsilon());
EXPECT((v.to_double().value() - 42.0) < NumericLimits<double>().epsilon());
}
{
SQL::Value v("true");
EXPECT_EQ(v.type(), SQL::SQLType::Text);
EXPECT(v.to_bool().has_value());
EXPECT(v.to_bool().value());
}
{
SQL::Value v("false");
EXPECT_EQ(v.type(), SQL::SQLType::Text);
EXPECT(v.to_bool().has_value());
EXPECT(!v.to_bool().value());
}
{
SQL::Value v("foo");
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_double().has_value());
}
{
SQL::Value v("3.14");
EXPECT_EQ(v.type(), SQL::SQLType::Text);
EXPECT(v.to_double().has_value());
EXPECT((v.to_double().value() - 3.14) < NumericLimits<double>().epsilon());
}
}
TEST_CASE(assign_int_to_text_value)
{
SQL::Value v(SQL::SQLType::Text);
EXPECT_EQ(v.type(), SQL::SQLType::Text);
EXPECT(v.is_null());
v = 42;
EXPECT_EQ(v.type(), SQL::SQLType::Integer);
EXPECT_EQ(v, 42);
}
TEST_CASE(serialize_text_value)
{
SQL::Value v("Test");
EXPECT(v.to_string() == "Test");
EXPECT_EQ(v.type(), SQL::SQLType::Text);
EXPECT_EQ(v, "Test"sv);
SQL::Serializer serializer;
serializer.serialize<SQL::Value>(v);
serializer.rewind();
auto v2 = serializer.deserialize<SQL::Value>();
EXPECT(v2.to_string() == "Test");
EXPECT_EQ(v2.type(), SQL::SQLType::Text);
EXPECT_EQ(v2, "Test"sv);
EXPECT_EQ(v2, v);
}
TEST_CASE(integer_value)
{
{
SQL::Value v(SQL::SQLType::Integer);
EXPECT_EQ(v.type(), SQL::SQLType::Integer);
EXPECT(v.is_null());
v = 42;
EXPECT(!v.is_null());
EXPECT(v.to_int().value() == 42);
EXPECT(v.to_string() == "42");
EXPECT_EQ(v.type(), SQL::SQLType::Integer);
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().value(), 42);
EXPECT_EQ(v.to_string(), "42"sv);
EXPECT(v.to_double().has_value());
EXPECT(v.to_double().value() - 42.0 < NumericLimits<double>().epsilon());
EXPECT((v.to_double().value() - 42.0) < NumericLimits<double>().epsilon());
EXPECT(v.to_bool().has_value());
EXPECT(v.to_bool().value());
}
{
SQL::Value v(0);
EXPECT(!v.is_null());
EXPECT(v.to_int().value() == 0);
EXPECT_EQ(v.type(), SQL::SQLType::Integer);
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().value(), 0);
EXPECT(v.to_bool().has_value());
EXPECT(!v.to_bool().value());
}
{
SQL::Value v(SQL::SQLType::Integer, "42");
EXPECT_EQ(v.to_int().value(), 42);
}
{
SQL::Value v(SQL::SQLType::Integer, SQL::Value("42"));
SQL::Value v(42);
EXPECT_EQ(v.type(), SQL::SQLType::Integer);
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().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);
}
}
@ -138,66 +188,77 @@ TEST_CASE(serialize_int_value)
{
SQL::Value v(42);
EXPECT_EQ(v.type(), SQL::SQLType::Integer);
EXPECT_EQ(v.to_int().value(), 42);
EXPECT_EQ(v, 42);
SQL::Serializer serializer;
serializer.serialize<SQL::Value>(v);
serializer.rewind();
auto v2 = serializer.deserialize<SQL::Value>();
EXPECT(!v2.is_null());
EXPECT_EQ(v2.type(), SQL::SQLType::Integer);
EXPECT_EQ(v2.to_int().value(), 42);
EXPECT(v2 == v);
EXPECT_EQ(v2, 42);
EXPECT_EQ(v2, v);
}
TEST_CASE(float_value)
{
{
SQL::Value v(SQL::SQLType::Float);
EXPECT_EQ(v.type(), SQL::SQLType::Float);
EXPECT(v.is_null());
v = 3.14;
EXPECT(!v.is_null());
EXPECT_EQ(v.type(), SQL::SQLType::Float);
EXPECT(v.to_double().has_value());
EXPECT(v.to_double().value() - 3.14 < NumericLimits<double>().epsilon());
EXPECT((v.to_double().value() - 3.14) < NumericLimits<double>().epsilon());
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().value(), 3);
EXPECT_EQ(v.to_string(), "3.14");
EXPECT(v.to_bool().has_value());
EXPECT(v.to_bool().value());
v = 0.0;
EXPECT(!v.is_null());
EXPECT_EQ(v.type(), SQL::SQLType::Float);
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_EQ(v.to_string(), "0");
EXPECT_EQ(v.to_string(), "0"sv);
EXPECT(v.to_bool().has_value());
EXPECT(!v.to_bool().value());
}
{
SQL::Value v(3.14);
EXPECT(!v.is_null());
EXPECT(v.to_double().value() - 3.14 < NumericLimits<double>().epsilon());
EXPECT_EQ(v.type(), SQL::SQLType::Float);
EXPECT((v.to_double().value() - 3.14) < NumericLimits<double>().epsilon());
}
{
SQL::Value v(3.51);
EXPECT(!v.is_null());
EXPECT_EQ(v.type(), SQL::SQLType::Float);
EXPECT(v.to_int().has_value());
EXPECT_EQ(v.to_int().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);
}
{
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);
}
{
SQL::Value v(SQL::SQLType::Float, "3.14");
EXPECT(v.to_double().value() - 3.14 < NumericLimits<double>().epsilon());
}
}
TEST_CASE(serialize_float_value)
@ -211,63 +272,73 @@ TEST_CASE(serialize_float_value)
serializer.rewind();
auto v2 = serializer.deserialize<SQL::Value>();
EXPECT(!v2.is_null());
EXPECT_EQ(v2.type(), SQL::SQLType::Float);
EXPECT(v.to_double().value() - 3.14 < NumericLimits<double>().epsilon());
}
TEST_CASE(assign_int_to_text_value)
{
SQL::Value text(SQL::SQLType::Text);
text = 42;
EXPECT_EQ(text.to_string(), "42");
EXPECT((v.to_double().value() - 3.14) < NumericLimits<double>().epsilon());
EXPECT_EQ(v2, v);
}
TEST_CASE(copy_value)
{
SQL::Value text(SQL::SQLType::Text, 42);
SQL::Value text("42");
SQL::Value copy(text);
EXPECT_EQ(copy.to_string(), "42");
EXPECT_EQ(copy, "42"sv);
}
TEST_CASE(compare_text_to_int)
{
SQL::Value text(SQL::SQLType::Text);
text = 42;
SQL::Value integer(SQL::SQLType::Integer);
integer = 42;
EXPECT(text == integer);
EXPECT(integer == text);
SQL::Value text("42");
SQL::Value integer(42);
EXPECT_EQ(text, integer);
EXPECT_EQ(integer, text);
}
TEST_CASE(bool_value)
{
{
SQL::Value v(SQL::SQLType::Boolean);
EXPECT_EQ(v.type(), SQL::SQLType::Boolean);
EXPECT(v.is_null());
v = true;
EXPECT(!v.is_null());
EXPECT_EQ(v.type(), SQL::SQLType::Boolean);
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_EQ(v.to_string(), "true");
EXPECT(!v.to_double().has_value());
EXPECT_EQ(v.to_string(), "true"sv);
EXPECT(v.to_double().has_value());
EXPECT((v.to_double().value() - 1.0) < NumericLimits<double>().epsilon());
}
{
SQL::Value v(SQL::SQLType::Boolean, false);
EXPECT(!v.is_null());
SQL::Value v(false);
EXPECT_EQ(v.type(), SQL::SQLType::Boolean);
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_EQ(v.to_string(), "false");
EXPECT(!v.to_double().has_value());
EXPECT_EQ(v.to_string(), "false"sv);
EXPECT(v.to_double().has_value());
EXPECT(v.to_double().value() < NumericLimits<double>().epsilon());
}
{
SQL::Value v(true);
EXPECT_EQ(v.type(), SQL::SQLType::Boolean);
EXPECT(!v.is_null());
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_EQ(v.to_string(), "true"sv);
EXPECT(v.to_double().has_value());
EXPECT((v.to_double().value() - 1.0) < NumericLimits<double>().epsilon());
}
}
@ -282,7 +353,6 @@ TEST_CASE(serialize_boolean_value)
serializer.rewind();
auto v2 = serializer.deserialize<SQL::Value>();
EXPECT(!v2.is_null());
EXPECT_EQ(v2.type(), SQL::SQLType::Boolean);
EXPECT_EQ(v2.to_bool(), true);
EXPECT_EQ(v, v2);
@ -293,12 +363,12 @@ TEST_CASE(tuple_value)
NonnullRefPtr<SQL::TupleDescriptor> descriptor = adopt_ref(*new SQL::TupleDescriptor);
descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending });
descriptor->append({ "schema", "table", "col2", SQL::SQLType::Integer, SQL::Order::Descending });
auto v = MUST(SQL::Value::create_tuple(move(descriptor)));
auto v = SQL::Value::create_tuple(descriptor);
Vector<SQL::Value> values;
values.append(SQL::Value("Test"));
values.append(SQL::Value(42));
v = values;
values.empend("Test");
values.empend(42);
MUST(v.assign_tuple(values));
auto values2 = v.to_vector();
EXPECT(values2.has_value());
@ -310,16 +380,16 @@ TEST_CASE(copy_tuple_value)
NonnullRefPtr<SQL::TupleDescriptor> descriptor = adopt_ref(*new SQL::TupleDescriptor);
descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending });
descriptor->append({ "schema", "table", "col2", SQL::SQLType::Integer, SQL::Order::Descending });
auto v = MUST(SQL::Value::create_tuple(move(descriptor)));
auto v = SQL::Value::create_tuple(descriptor);
Vector<SQL::Value> values;
values.append(SQL::Value("Test"));
values.append(SQL::Value(42));
v = values;
values.empend("Test");
values.empend(42);
MUST(v.assign_tuple(values));
auto values2 = v;
EXPECT(values2.type() == v.type());
EXPECT(!values2.is_null());
EXPECT_EQ(values2.type(), v.type());
EXPECT_EQ(v.type(), SQL::SQLType::Tuple);
EXPECT_EQ(values, values2.to_vector().value());
}
@ -327,25 +397,27 @@ TEST_CASE(tuple_value_wrong_type)
{
NonnullRefPtr<SQL::TupleDescriptor> descriptor = adopt_ref(*new SQL::TupleDescriptor);
descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending });
auto v = MUST(SQL::Value::create_tuple(move(descriptor)));
auto v = SQL::Value::create_tuple(descriptor);
Vector<SQL::Value> values;
values.append(SQL::Value(42));
v = values;
EXPECT(v.is_null());
values.empend(42);
auto result = v.assign_tuple(move(values));
EXPECT(result.is_error());
}
TEST_CASE(tuple_value_too_many_values)
{
NonnullRefPtr<SQL::TupleDescriptor> descriptor = adopt_ref(*new SQL::TupleDescriptor);
descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending });
auto v = MUST(SQL::Value::create_tuple(move(descriptor)));
auto v = SQL::Value::create_tuple(descriptor);
Vector<SQL::Value> values;
values.append(SQL::Value("Test"));
values.append(SQL::Value(42));
v = values;
EXPECT(v.is_null());
values.empend("Test");
values.empend(42);
auto result = v.assign_tuple(move(values));
EXPECT(result.is_error());
}
TEST_CASE(tuple_value_not_enough_values)
@ -353,18 +425,20 @@ TEST_CASE(tuple_value_not_enough_values)
NonnullRefPtr<SQL::TupleDescriptor> descriptor = adopt_ref(*new SQL::TupleDescriptor);
descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending });
descriptor->append({ "schema", "table", "col2", SQL::SQLType::Integer, SQL::Order::Ascending });
auto v = MUST(SQL::Value::create_tuple(move(descriptor)));
auto v = SQL::Value::create_tuple(descriptor);
Vector<SQL::Value> values;
values.append(SQL::Value("Test"));
v = values;
EXPECT(!v.is_null());
values.empend("Test");
MUST(v.assign_tuple(values));
EXPECT_EQ(v.type(), SQL::SQLType::Tuple);
auto values_opt = v.to_vector();
EXPECT(values_opt.has_value());
EXPECT_EQ(values_opt.value().size(), 2u);
auto col2 = values_opt.value()[1];
EXPECT_EQ(col2.type(), SQL::SQLType::Integer);
EXPECT(col2.is_null());
}
TEST_CASE(serialize_tuple_value)
@ -372,89 +446,22 @@ TEST_CASE(serialize_tuple_value)
NonnullRefPtr<SQL::TupleDescriptor> descriptor = adopt_ref(*new SQL::TupleDescriptor);
descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending });
descriptor->append({ "schema", "table", "col2", SQL::SQLType::Integer, SQL::Order::Descending });
auto v = MUST(SQL::Value::create_tuple(move(descriptor)));
auto v = SQL::Value::create_tuple(descriptor);
Vector<SQL::Value> values;
values.append(SQL::Value("Test"));
values.append(SQL::Value(42));
v = values;
values.empend("Test");
values.empend(42);
MUST(v.assign_tuple(values));
SQL::Serializer serializer;
serializer.serialize<SQL::Value>(v);
serializer.rewind();
auto v2 = serializer.deserialize<SQL::Value>();
EXPECT(!v2.is_null());
EXPECT_EQ(v2.type(), SQL::SQLType::Tuple);
EXPECT_EQ(v, v2);
}
TEST_CASE(array_value)
{
auto v = SQL::Value::create_array(SQL::SQLType::Text, 3);
Vector<SQL::Value> values;
values.append(SQL::Value("Test 1"));
values.append(SQL::Value("Test 2"));
v = values;
auto values2 = v.to_vector();
EXPECT(values2.has_value());
EXPECT_EQ(values, values2.value());
}
TEST_CASE(array_value_wrong_type)
{
auto v = SQL::Value::create_array(SQL::SQLType::Text, 2);
Vector<SQL::Value> values;
values.append(SQL::Value("Test 1"));
values.append(SQL::Value(42));
v = values;
EXPECT(v.is_null());
}
TEST_CASE(array_value_too_many_values)
{
auto v = SQL::Value::create_array(SQL::SQLType::Text, 2);
Vector<SQL::Value> values;
values.append(SQL::Value("Test 1"));
values.append(SQL::Value("Test 2"));
values.append(SQL::Value("Test 3"));
v = values;
EXPECT(v.is_null());
}
TEST_CASE(copy_array_value)
{
auto v = SQL::Value::create_array(SQL::SQLType::Text, 3);
Vector<SQL::Value> values;
values.append(SQL::Value("Test 1"));
values.append(SQL::Value("Test 2"));
v = values;
auto values2 = v;
EXPECT(values2.type() == v.type());
EXPECT(!values2.is_null());
EXPECT_EQ(values, values2.to_vector().value());
}
TEST_CASE(serialize_array_value)
{
auto v = SQL::Value::create_array(SQL::SQLType::Text, 3);
Vector<SQL::Value> values;
values.append(SQL::Value("Test 1"));
values.append(SQL::Value("Test 2"));
v = values;
SQL::Serializer serializer;
serializer.serialize<SQL::Value>(v);
serializer.rewind();
auto v2 = serializer.deserialize<SQL::Value>();
EXPECT(!v2.is_null());
EXPECT_EQ(v2.type(), SQL::SQLType::Array);
EXPECT_EQ(v, v2);
}
TEST_CASE(order_text_values)
{
SQL::Value v1(SQL::SQLType::Text);
@ -488,8 +495,8 @@ TEST_CASE(tuple)
tuple["col1"] = "Test";
tuple["col2"] = 42;
EXPECT(tuple[0] == "Test");
EXPECT(tuple[1] == 42);
EXPECT_EQ(tuple[0], "Test"sv);
EXPECT_EQ(tuple[1], 42);
}
TEST_CASE(serialize_tuple)
@ -502,7 +509,7 @@ TEST_CASE(serialize_tuple)
tuple["col1"] = "Test";
tuple["col2"] = 42;
EXPECT_EQ(tuple[0], "Test");
EXPECT_EQ(tuple[0], "Test"sv);
EXPECT_EQ(tuple[1], 42);
SQL::Serializer serializer;
@ -510,8 +517,8 @@ TEST_CASE(serialize_tuple)
serializer.rewind();
auto tuple2 = serializer.deserialize<SQL::Tuple>();
EXPECT(tuple2[0] == "Test");
EXPECT(tuple2[1] == 42);
EXPECT_EQ(tuple2[0], "Test"sv);
EXPECT_EQ(tuple2[1], 42);
}
TEST_CASE(copy_tuple)
@ -526,10 +533,10 @@ TEST_CASE(copy_tuple)
SQL::Tuple copy;
copy = tuple;
EXPECT(tuple == copy);
EXPECT_EQ(tuple, copy);
SQL::Tuple copy_2(copy);
EXPECT(tuple == copy_2);
EXPECT_EQ(tuple, copy_2);
}
TEST_CASE(compare_tuples)