diff --git a/Tests/LibSQL/TestSqlDatabase.cpp b/Tests/LibSQL/TestSqlDatabase.cpp index a63187ac66..9c67267c5e 100644 --- a/Tests/LibSQL/TestSqlDatabase.cpp +++ b/Tests/LibSQL/TestSqlDatabase.cpp @@ -66,7 +66,7 @@ void verify_table_contents(SQL::Database& db, int expected_count) for (auto& row : db.select_all(*table)) { StringBuilder builder; builder.appendff("Test{}", row["IntColumn"].to_int().value()); - EXPECT_EQ(row["TextColumn"].to_string().value(), builder.build()); + EXPECT_EQ(row["TextColumn"].to_string(), builder.build()); count++; sum += row["IntColumn"].to_int().value(); } diff --git a/Tests/LibSQL/TestSqlValueAndTuple.cpp b/Tests/LibSQL/TestSqlValueAndTuple.cpp index 2c3d6e25d3..cbedb15836 100644 --- a/Tests/LibSQL/TestSqlValueAndTuple.cpp +++ b/Tests/LibSQL/TestSqlValueAndTuple.cpp @@ -12,76 +12,203 @@ #include #include -TEST_CASE(text_value) +TEST_CASE(null_value) { - SQL::Value v(SQL::SQLType::Text); + SQL::Value v(SQL::SQLType::Null); + EXPECT(v.type() == SQL::SQLType::Null); + EXPECT(v.is_null()); v = "Test"; - VERIFY(v.to_string().value() == "Test"); + EXPECT(v.is_null()); + EXPECT(v.to_string() == "(null)"); } -TEST_CASE(text_value_to_int) +TEST_CASE(text_value) { - SQL::Value v(SQL::SQLType::Text); - v = "42"; - EXPECT_EQ(v.to_int().value(), 42); + { + SQL::Value v(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()); +} + +TEST_CASE(text_value_to_other_types) +{ + { + SQL::Value v(SQL::SQLType::Text, "42"); + 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().epsilon()); + } + { + SQL::Value v("true"); + EXPECT(v.to_bool().has_value()); + EXPECT(v.to_bool().value()); + } + { + SQL::Value v("false"); + EXPECT(v.to_bool().has_value()); + EXPECT(!v.to_bool().value()); + } } TEST_CASE(text_value_to_int_crash) { - SQL::Value v(SQL::SQLType::Text); - v = "Test"; - EXPECT_CRASH("Can't convert 'Test' to integer", [&]() { (void) (int) v; return Test::Crash::Failure::DidNotCrash; }); + SQL::Value v(SQL::SQLType::Text, "Not a valid integer"); + EXPECT_CRASH("Can't convert 'Not a valid integer' to integer", [&]() { (void) (int) v; return Test::Crash::Failure::DidNotCrash; }); } TEST_CASE(serialize_text_value) { - SQL::Value v(SQL::SQLType::Text); - v = "Test"; - VERIFY(v.to_string().value() == "Test"); + SQL::Value v("Test"); + EXPECT(v.to_string() == "Test"); ByteBuffer buffer; - v.serialize(buffer); + v.serialize_to(buffer); size_t offset = 0; - SQL::Value v2(SQL::SQLType::Text, buffer, offset); - VERIFY((String)v2 == "Test"); + auto v2 = SQL::Value::deserialize_from(buffer, offset); + EXPECT((String)v2 == "Test"); } TEST_CASE(integer_value) { - SQL::Value v(SQL::SQLType::Integer); - v = 42; - VERIFY(v.to_int().value() == 42); + { + SQL::Value v(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(v.to_double().has_value()); + EXPECT(v.to_double().value() - 42.0 < NumericLimits().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(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")); + EXPECT_EQ(v.to_int().value(), 42); + } + { + SQL::Value text("42"); + SQL::Value integer(SQL::SQLType::Integer); + integer = text; + EXPECT_EQ(integer.to_int().value(), 42); + } } TEST_CASE(serialize_int_value) { - SQL::Value v(SQL::SQLType::Text); - v = 42; - VERIFY(v.to_int().value() == 42); + SQL::Value v(42); + EXPECT_EQ(v.type(), SQL::SQLType::Integer); + EXPECT_EQ(v.to_int().value(), 42); ByteBuffer buffer; - v.serialize(buffer); + v.serialize_to(buffer); size_t offset = 0; - SQL::Value v2(SQL::SQLType::Text, buffer, offset); - VERIFY(v2 == v); + auto v2 = SQL::Value::deserialize_from(buffer, offset); + EXPECT(!v2.is_null()); + EXPECT_EQ(v2.type(), SQL::SQLType::Integer); + EXPECT_EQ(v2.to_int().value(), 42); + EXPECT(v2 == v); } TEST_CASE(float_value) { - SQL::Value v(SQL::SQLType::Float); - v = 3.14; - VERIFY(v.to_double().value() - 3.14 < 0.001); + { + SQL::Value v(SQL::SQLType::Float); + EXPECT(v.is_null()); + v = 3.14; + EXPECT(!v.is_null()); + EXPECT(v.to_double().has_value()); + EXPECT(v.to_double().value() - 3.14 < NumericLimits().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()); + } + { + SQL::Value v(3.14); + EXPECT(!v.is_null()); + EXPECT(v.to_double().value() - 3.14 < NumericLimits().epsilon()); + } + { + SQL::Value v(3.51); + EXPECT(!v.is_null()); + EXPECT_EQ(v.to_int().value(), 4); + } + { + SQL::Value v(-3.14); + EXPECT_EQ(v.to_int().value(), -3); + } + { + SQL::Value v(-3.51); + EXPECT_EQ(v.to_int().value(), -4); + } + { + SQL::Value v(SQL::SQLType::Float, "3.14"); + EXPECT(v.to_double().value() - 3.14 < NumericLimits().epsilon()); + } } -TEST_CASE(assign_text_value_to_int) +TEST_CASE(serialize_float_value) { - SQL::Value text(SQL::SQLType::Text); - text = "42"; - SQL::Value integer(SQL::SQLType::Integer); - integer = text; - EXPECT_EQ(integer.to_int().value(), 42); + SQL::Value v(3.14); + EXPECT_EQ(v.type(), SQL::SQLType::Float); + EXPECT(v.to_double().value() - 3.14 < NumericLimits().epsilon()); + + ByteBuffer buffer; + v.serialize_to(buffer); + + size_t offset = 0; + auto v2 = SQL::Value::deserialize_from(buffer, offset); + EXPECT(!v2.is_null()); + EXPECT_EQ(v2.type(), SQL::SQLType::Float); + EXPECT(v.to_double().value() - 3.14 < NumericLimits().epsilon()); } TEST_CASE(assign_int_to_text_value) @@ -93,8 +220,7 @@ TEST_CASE(assign_int_to_text_value) TEST_CASE(copy_value) { - SQL::Value text(SQL::SQLType::Text); - text = 42; + SQL::Value text(SQL::SQLType::Text, 42); SQL::Value copy(text); EXPECT_EQ((String)copy, "42"); } @@ -109,6 +235,221 @@ TEST_CASE(compare_text_to_int) EXPECT(integer == text); } +TEST_CASE(bool_value) +{ + { + SQL::Value v(SQL::SQLType::Boolean); + EXPECT(v.is_null()); + v = true; + EXPECT(!v.is_null()); + EXPECT(v.to_bool().has_value()); + EXPECT(v.to_bool().value()); + EXPECT_EQ(v.to_int().value(), 1); + EXPECT_EQ(v.to_string(), "true"); + EXPECT(!v.to_double().has_value()); + } + { + SQL::Value v(SQL::SQLType::Boolean, false); + EXPECT(!v.is_null()); + EXPECT(v.to_bool().has_value()); + EXPECT(!v.to_bool().value()); + EXPECT_EQ(v.to_int().value(), 0); + EXPECT_EQ(v.to_string(), "false"); + EXPECT(!v.to_double().has_value()); + } + { + 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()); + } +} + +TEST_CASE(serialize_boolean_value) +{ + SQL::Value v(true); + EXPECT_EQ(v.type(), SQL::SQLType::Boolean); + EXPECT(bool(v)); + + ByteBuffer buffer; + v.serialize_to(buffer); + + size_t offset = 0; + auto v2 = SQL::Value::deserialize_from(buffer, offset); + EXPECT(!v2.is_null()); + EXPECT_EQ(v2.type(), SQL::SQLType::Boolean); + EXPECT(bool(v2)); + EXPECT_EQ(v, v2); +} + +TEST_CASE(tuple_value) +{ + NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); + descriptor->append({ "col1", SQL::SQLType::Text, SQL::Order::Ascending }); + descriptor->append({ "col2", SQL::SQLType::Integer, SQL::Order::Descending }); + + auto v = SQL::Value::create_tuple(descriptor); + Vector values; + values.append(SQL::Value("Test")); + values.append(SQL::Value(42)); + v = values; + + auto values2 = v.to_vector(); + EXPECT(values2.has_value()); + EXPECT_EQ(values, values2.value()); +} + +TEST_CASE(copy_tuple_value) +{ + NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); + descriptor->append({ "col1", SQL::SQLType::Text, SQL::Order::Ascending }); + descriptor->append({ "col2", SQL::SQLType::Integer, SQL::Order::Descending }); + + auto v = SQL::Value::create_tuple(descriptor); + Vector values; + values.append(SQL::Value("Test")); + values.append(SQL::Value(42)); + v = values; + + auto values2 = v; + EXPECT(values2.type() == v.type()); + EXPECT(!values2.is_null()); + EXPECT_EQ(values, values2.to_vector().value()); +} + +TEST_CASE(tuple_value_wrong_type) +{ + NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); + descriptor->append({ "col1", SQL::SQLType::Text, SQL::Order::Ascending }); + + auto v = SQL::Value::create_tuple(descriptor); + Vector values; + values.append(SQL::Value(42)); + v = values; + EXPECT(v.is_null()); +} + +TEST_CASE(tuple_value_too_many_values) +{ + NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); + descriptor->append({ "col1", SQL::SQLType::Text, SQL::Order::Ascending }); + + auto v = SQL::Value::create_tuple(descriptor); + Vector values; + values.append(SQL::Value("Test")); + values.append(SQL::Value(42)); + v = values; + EXPECT(v.is_null()); +} + +TEST_CASE(tuple_value_not_enough_values) +{ + NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); + descriptor->append({ "col1", SQL::SQLType::Text, SQL::Order::Ascending }); + descriptor->append({ "col2", SQL::SQLType::Integer, SQL::Order::Ascending }); + + auto v = SQL::Value::create_tuple(descriptor); + Vector values; + values.append(SQL::Value("Test")); + v = values; + EXPECT(!v.is_null()); + 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) +{ + NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); + descriptor->append({ "col1", SQL::SQLType::Text, SQL::Order::Ascending }); + descriptor->append({ "col2", SQL::SQLType::Integer, SQL::Order::Descending }); + + auto v = SQL::Value::create_tuple(descriptor); + Vector values; + values.append(SQL::Value("Test")); + values.append(SQL::Value(42)); + v = values; + + ByteBuffer buffer; + v.serialize_to(buffer); + + size_t offset = 0; + auto v2 = SQL::Value::deserialize_from(buffer, offset); + 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 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 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 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 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 values; + values.append(SQL::Value("Test 1")); + values.append(SQL::Value("Test 2")); + v = values; + + ByteBuffer buffer; + v.serialize_to(buffer); + + size_t offset = 0; + auto v2 = SQL::Value::deserialize_from(buffer, offset); + 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); @@ -142,8 +483,8 @@ TEST_CASE(tuple) tuple["col1"] = "Test"; tuple["col2"] = 42; - VERIFY(tuple[0] == "Test"); - VERIFY(tuple[1] == 42); + EXPECT(tuple[0] == "Test"); + EXPECT(tuple[1] == 42); } TEST_CASE(serialize_tuple) @@ -163,8 +504,8 @@ TEST_CASE(serialize_tuple) size_t offset = 0; SQL::Tuple tuple2(descriptor, buffer, offset); - VERIFY(tuple2[0] == "Test"); - VERIFY(tuple2[1] == 42); + EXPECT(tuple2[0] == "Test"); + EXPECT(tuple2[1] == 42); } TEST_CASE(copy_tuple) @@ -179,10 +520,10 @@ TEST_CASE(copy_tuple) SQL::Tuple copy; copy = tuple; - VERIFY(tuple == copy); + EXPECT(tuple == copy); SQL::Tuple copy_2(copy); - VERIFY(tuple == copy_2); + EXPECT(tuple == copy_2); } TEST_CASE(compare_tuples) diff --git a/Userland/Libraries/LibSQL/Meta.cpp b/Userland/Libraries/LibSQL/Meta.cpp index 9307624ec9..66d141f752 100644 --- a/Userland/Libraries/LibSQL/Meta.cpp +++ b/Userland/Libraries/LibSQL/Meta.cpp @@ -16,7 +16,7 @@ SchemaDef::SchemaDef(String name) } SchemaDef::SchemaDef(Key const& key) - : Relation(key["schema_name"].to_string().value()) + : Relation(key["schema_name"].to_string()) { } diff --git a/Userland/Libraries/LibSQL/Serialize.h b/Userland/Libraries/LibSQL/Serialize.h index de9e027e7d..bc03590058 100644 --- a/Userland/Libraries/LibSQL/Serialize.h +++ b/Userland/Libraries/LibSQL/Serialize.h @@ -7,22 +7,75 @@ #pragma once #include +#include +#include +#include #include namespace SQL { +inline void dump(u8 const* ptr, size_t sz, String const& prefix) +{ + StringBuilder builder; + builder.appendff("{0} {1:04x} | ", prefix, sz); + Vector bytes; + for (auto ix = 0u; ix < sz; ++ix) { + bytes.append(String::formatted("{0:02x}", *(ptr + ix))); + } + StringBuilder bytes_builder; + bytes_builder.join(" ", bytes); + builder.append(bytes_builder.to_string()); + dbgln(builder.to_string()); +} + +inline void write(ByteBuffer& buffer, u8 const* ptr, size_t sz) +{ + if constexpr (SQL_DEBUG) + dump(ptr, sz, "->"); + buffer.append(ptr, sz); +} + +inline u8* read(ByteBuffer& buffer, size_t& at_offset, size_t sz) +{ + auto buffer_ptr = buffer.offset_pointer((int)at_offset); + if constexpr (SQL_DEBUG) + dump(buffer_ptr, sz, "<-"); + at_offset += sz; + return buffer_ptr; +} + template void deserialize_from(ByteBuffer& buffer, size_t& at_offset, T& t) { - auto ptr = buffer.offset_pointer((int)at_offset); - memcpy(&t, ptr, sizeof(T)); - at_offset += sizeof(T); + memcpy(&t, read(buffer, at_offset, sizeof(T)), sizeof(T)); } template void serialize_to(ByteBuffer& buffer, T const& t) { - buffer.append(&t, sizeof(T)); + write(buffer, (u8 const*)(&t), sizeof(T)); +} + +template<> +inline void deserialize_from(ByteBuffer& buffer, size_t& at_offset, String& string) +{ + u32 length; + deserialize_from(buffer, at_offset, length); + if (length > 0) { + string = String((const char*)read(buffer, at_offset, length), length); + } else { + string = ""; + } +} + +template<> +inline void serialize_to(ByteBuffer& buffer, String const& t) +{ + u32 number_of_bytes = min(t.length(), 64); + serialize_to(buffer, number_of_bytes); + if (t.length() > 0) { + write(buffer, (u8 const*)(t.characters()), number_of_bytes); + } } } diff --git a/Userland/Libraries/LibSQL/Tuple.cpp b/Userland/Libraries/LibSQL/Tuple.cpp index fde324b9bb..8706a07d50 100644 --- a/Userland/Libraries/LibSQL/Tuple.cpp +++ b/Userland/Libraries/LibSQL/Tuple.cpp @@ -51,7 +51,7 @@ void Tuple::deserialize(ByteBuffer& buffer, size_t& offset) dbgln_if(SQL_DEBUG, "pointer: {}", m_pointer); m_data.clear(); for (auto& part : *m_descriptor) { - m_data.empend(part.type, buffer, offset); + m_data.append(Value::deserialize_from(buffer, offset)); dbgln_if(SQL_DEBUG, "Deserialized element {} = {}", part.name, m_data.last().to_string()); } } @@ -64,11 +64,11 @@ void Tuple::serialize(ByteBuffer& buffer) const for (auto ix = 0u; ix < m_descriptor->size(); ix++) { auto& key_part = m_data[ix]; if constexpr (SQL_DEBUG) { - auto str_opt = key_part.to_string(); + auto key_string = key_part.to_string(); auto& key_part_definition = (*m_descriptor)[ix]; - dbgln("Serialized part {} = {}", key_part_definition.name, (str_opt.has_value()) ? str_opt.value() : "(null)"); + dbgln("Serialized part {} = {}", key_part_definition.name, key_string); } - key_part.serialize(buffer); + key_part.serialize_to(buffer); } } @@ -165,8 +165,7 @@ String Tuple::to_string() const if (!builder.is_empty()) { builder.append('|'); } - auto str_opt = part.to_string(); - builder.append((str_opt.has_value()) ? str_opt.value() : "(null)"); + builder.append(part.to_string()); } if (pointer() != 0) { builder.appendff(":{}", pointer()); @@ -178,7 +177,7 @@ Vector Tuple::to_string_vector() const { Vector ret; for (auto& value : m_data) { - ret.append(value.to_string().value()); + ret.append(value.to_string()); } return ret; } diff --git a/Userland/Libraries/LibSQL/TupleDescriptor.h b/Userland/Libraries/LibSQL/TupleDescriptor.h index 6f6e7b1641..afb93a00f7 100644 --- a/Userland/Libraries/LibSQL/TupleDescriptor.h +++ b/Userland/Libraries/LibSQL/TupleDescriptor.h @@ -34,6 +34,22 @@ public: } return sz; } + + [[nodiscard]] int compare_ignoring_names(TupleDescriptor const& other) const + { + if (size() != other.size()) + return (int)size() - (int)other.size(); + for (auto ix = 0u; ix < size(); ++ix) { + auto elem = (*this)[ix]; + auto other_elem = other[ix]; + if ((elem.type != other_elem.type) || (elem.order != other_elem.order)) { + return 1; + } + } + return 0; + } + + using Vector::operator==; }; } diff --git a/Userland/Libraries/LibSQL/Type.h b/Userland/Libraries/LibSQL/Type.h index 865276a1de..fd4b32f21e 100644 --- a/Userland/Libraries/LibSQL/Type.h +++ b/Userland/Libraries/LibSQL/Type.h @@ -12,17 +12,35 @@ namespace SQL { #define ENUMERATE_SQL_TYPES(S) \ - S("text", 0, Text, String, 64 + sizeof(int)) \ - S("int", 1, Integer, int, sizeof(int)) \ - S("float", 2, Float, double, sizeof(double)) + S("null", 1, Null, int, sizeof(int)) \ + S("text", 2, Text, String, 65 + sizeof(u32)) \ + S("int", 4, Integer, int, sizeof(int)) \ + S("float", 8, Float, double, sizeof(double)) \ + S("bool", 16, Boolean, bool, sizeof(bool)) \ + S("tuple", 32, Tuple, int, sizeof(int)) \ + S("array", 64, Array, int, sizeof(int)) enum class SQLType { #undef __ENUMERATE_SQL_TYPE -#define __ENUMERATE_SQL_TYPE(name, cardinal, type, impl, size) type = (cardinal), +#define __ENUMERATE_SQL_TYPE(name, cardinal, type, impl, size) type = cardinal, ENUMERATE_SQL_TYPES(__ENUMERATE_SQL_TYPE) #undef __ENUMERATE_SQL_TYPE }; +inline static String SQLType_name(SQLType t) +{ + switch (t) { +#undef __ENUMERATE_SQL_TYPE +#define __ENUMERATE_SQL_TYPE(name, cardinal, type, impl, size) \ + case SQLType::type: \ + return name; + ENUMERATE_SQL_TYPES(__ENUMERATE_SQL_TYPE) +#undef __ENUMERATE_SQL_TYPE + default: + VERIFY_NOT_REACHED(); + } +} + inline static size_t size_of(SQLType t) { switch (t) { diff --git a/Userland/Libraries/LibSQL/Value.cpp b/Userland/Libraries/LibSQL/Value.cpp index 122ad7c41b..c415a130f1 100644 --- a/Userland/Libraries/LibSQL/Value.cpp +++ b/Userland/Libraries/LibSQL/Value.cpp @@ -4,112 +4,187 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include -#include +#include +#include namespace SQL { Value::Value(SQLType sql_type) - : m_impl(0) { setup(sql_type); } -Value::Value(SQLType sql_type, ByteBuffer& buffer, size_t& offset) - : m_impl(0) +void Value::setup(SQLType type) { - setup(sql_type); - m_deserialize(buffer, offset); - m_is_null = false; + switch (type) { +#undef __ENUMERATE_SQL_TYPE +#define __ENUMERATE_SQL_TYPE(name, cardinal, type, impl, size) \ + case SQLType::type: \ + m_impl.set(type##Impl()); \ + break; + ENUMERATE_SQL_TYPES(__ENUMERATE_SQL_TYPE) +#undef __ENUMERATE_SQL_TYPE + default: + VERIFY_NOT_REACHED(); + } } -Value::Value(Value const& other) - : m_impl(0) +Value::Value(SQLType sql_type, Value const& value) + : Value(sql_type) { - setup(other.type()); - m_is_null = other.is_null(); - if (!m_is_null) - m_assign_value(other); + assign(value); } -Value::~Value() +Value::Value(SQLType sql_type, String const& string) + : Value(sql_type) { + assign(string); +} + +Value::Value(SQLType sql_type, char const* string) + : Value(sql_type) +{ + assign(String(string)); +} + +Value::Value(SQLType sql_type, int integer) + : Value(sql_type) +{ + assign(integer); +} + +Value::Value(SQLType sql_type, double dbl) + : Value(sql_type) +{ + assign(dbl); +} + +Value::Value(SQLType sql_type, bool boolean) + : Value(sql_type) +{ + assign(boolean); +} + +Value::Value(String const& string) + : Value(SQLType::Text) +{ + assign(string); +} + +Value::Value(char const* string) + : Value(SQLType::Text) +{ + assign(String(string)); +} + +Value::Value(int integer) + : Value(SQLType::Integer) +{ + assign(integer); +} + +Value::Value(double dbl) + : Value(SQLType::Float) +{ + assign(dbl); +} + +Value::Value(bool boolean) + : Value(SQLType::Boolean) +{ + assign(boolean); +} + +Value Value::create_tuple(NonnullRefPtr const& tuple_descriptor) +{ + return Value(Value::SetImplementationSingleton, TupleImpl(tuple_descriptor)); +} + +Value Value::create_array(SQLType element_type, Optional const& max_size) +{ + return Value(Value::SetImplementationSingleton, ArrayImpl(element_type, max_size)); } Value const& Value::null() { - static Value s_null; + static Value s_null(SQLType::Null); return s_null; } -Value& Value::operator=(Value const& other) +bool Value::is_null() const { - if (this != &other) { - m_is_null = other.is_null(); - if (!m_is_null) { - VERIFY(can_cast(other)); - m_assign_value(other); - } - } - return (*this); + return m_impl.visit([&](auto& impl) { return impl.is_null(); }); } -Value& Value::operator=(String const& value) +SQLType Value::type() const { - m_assign_string(value); - m_is_null = false; - return (*this); + return m_impl.visit([&](auto& impl) { return impl.type(); }); } -Value& Value::operator=(int value) +String Value::type_name() const { - m_assign_int(value); - m_is_null = false; - return (*this); + return m_impl.visit([&](auto& impl) { return impl.type_name(); }); } -Value& Value::operator=(u32 value) +BaseTypeImpl Value::downcast_to_basetype() const { - m_assign_int(static_cast(value)); - m_is_null = false; - return (*this); + return m_impl.downcast(); } -Value& Value::operator=(double value) +String Value::to_string() const { - m_assign_double(value); - m_is_null = false; - return (*this); + if (is_null()) + return "(null)"; + return m_impl.visit([&](auto& impl) { return impl.to_string(); }); } -Value& Value::set_null() +Optional Value::to_int() const { - m_is_null = true; - return (*this); + if (is_null()) + return {}; + return m_impl.visit([&](auto& impl) { return impl.to_int(); }); } -Optional Value::to_string() const +Optional Value::to_u32() const { - if (!m_is_null) - return m_to_string(); + if (is_null()) + return {}; + auto ret = to_int(); + if (ret.has_value()) + return static_cast(ret.value()); + return {}; +} + +Optional Value::to_double() const +{ + if (is_null()) + return {}; + return m_impl.visit([&](auto& impl) { return impl.to_double(); }); +} + +Optional Value::to_bool() const +{ + if (is_null()) + return {}; + return m_impl.visit([&](auto& impl) { return impl.to_bool(); }); +} + +Optional> Value::to_vector() const +{ + if (is_null()) + return {}; + Vector vector; + if (m_impl.visit([&](auto& impl) { return impl.to_vector(vector); })) + return vector; else return {}; } Value::operator String() const { - auto str = to_string(); - VERIFY(str.has_value()); - return str.value(); -} - -Optional Value::to_int() const -{ - if (!m_is_null) { - return m_to_int(); - } else { - return {}; - } + return to_string(); } Value::operator int() const @@ -119,19 +194,6 @@ Value::operator int() const return i.value(); } -Optional Value::to_u32() const -{ - if (!m_is_null) { - auto ret = m_to_int(); - if (ret.has_value()) - return static_cast(ret.value()); - else - return {}; - } else { - return {}; - } -} - Value::operator u32() const { auto i = to_u32(); @@ -139,278 +201,792 @@ Value::operator u32() const return i.value(); } -Optional Value::to_double() const -{ - if (!m_is_null) - return m_to_double(); - else - return {}; -} - Value::operator double() const { - auto dbl = to_double(); - VERIFY(dbl.has_value()); - return dbl.value(); + auto d = to_double(); + VERIFY(d.has_value()); + return d.value(); } -bool Value::can_cast(Value const& other) const +Value::operator bool() const { + auto b = to_bool(); + VERIFY(b.has_value()); + return b.value(); +} + +void Value::assign(Value const& other_value) +{ + m_impl.visit([&](auto& impl) { impl.assign(other_value); }); +} + +void Value::assign(String const& string_value) +{ + m_impl.visit([&](auto& impl) { impl.assign_string(string_value); }); +} + +void Value::assign(int const& int_value) +{ + m_impl.visit([&](auto& impl) { impl.assign_int(int_value); }); +} + +void Value::assign(double const& double_value) +{ + m_impl.visit([&](auto& impl) { impl.assign_double(double_value); }); +} + +void Value::assign(bool const& bool_value) +{ + m_impl.visit([&](auto& impl) { impl.assign_bool(bool_value); }); +} + +void Value::assign(Vector const& values) +{ + m_impl.visit([&](auto& impl) { impl.assign_vector(values); }); +} + +Value& Value::operator=(Value const& other) +{ + if (this != &other) { + if (other.is_null()) { + assign(null()); + } else { + VERIFY(can_cast(other)); + assign(other); + } + } + return (*this); +} + +Value& Value::operator=(String const& value) +{ + assign(value); + return (*this); +} + +Value& Value::operator=(char const* value) +{ + assign(String(value)); + return (*this); +} + +Value& Value::operator=(int value) +{ + assign(value); + return (*this); +} + +Value& Value::operator=(u32 value) +{ + assign(static_cast(value)); + return (*this); +} + +Value& Value::operator=(double value) +{ + assign(value); + return (*this); +} + +Value& Value::operator=(bool value) +{ + assign(value); + return (*this); +} + +Value& Value::operator=(Vector const& vector) +{ + assign(vector); + return (*this); +} + +size_t Value::length() const +{ + return m_impl.visit([&](auto& impl) { return impl.length(); }); +} + +u32 Value::hash() const +{ + return (is_null()) ? 0u : m_impl.visit([&](auto& impl) { return impl.hash(); }); +} + +bool Value::can_cast(Value const& other_value) const +{ + if (type() == other_value.type()) + return true; + return m_impl.visit([&](auto& impl) { return impl.can_cast(other_value); }); +} + +int Value::compare(Value const& other) const +{ + if (is_null()) + return -1; if (other.is_null()) + return 1; + return m_impl.visit([&](auto& impl) { return impl.compare(other); }); +} + +bool Value::operator==(Value const& other) const +{ + return compare(other) == 0; +} + +bool Value::operator==(String const& string_value) const +{ + return to_string() == string_value; +} + +bool Value::operator==(int int_value) const +{ + auto i = to_int(); + if (!i.has_value()) + return false; + return i.value() == int_value; +} + +bool Value::operator==(double double_value) const +{ + auto d = to_double(); + if (!d.has_value()) + return false; + return d.value() == double_value; +} + +bool Value::operator!=(Value const& other) const +{ + return compare(other) != 0; +} + +bool Value::operator<(Value const& other) const +{ + return compare(other) < 0; +} + +bool Value::operator<=(Value const& other) const +{ + return compare(other) <= 0; +} + +bool Value::operator>(Value const& other) const +{ + return compare(other) > 0; +} + +bool Value::operator>=(Value const& other) const +{ + return compare(other) >= 0; +} + +void Value::serialize_to(ByteBuffer& buffer) const +{ + u8 type_flags = (u8)type(); + if (is_null()) + type_flags |= (u8)SQLType::Null; + SQL::serialize_to(buffer, type_flags); + if (!is_null()) + m_impl.visit([&](auto& impl) { impl.serialize(buffer); }); +} + +void Value::deserialize(ByteBuffer& buffer, size_t& offset_at) +{ + m_impl.visit([&](auto& impl) { impl.deserialize(buffer, offset_at); }); +} + +Value Value::deserialize_from(ByteBuffer& buffer, size_t& at_offset) +{ + u8 type_flags; + SQL::deserialize_from(buffer, at_offset, type_flags); + bool is_null = false; + if ((type_flags & (u8)SQLType::Null) && (type_flags != (u8)SQLType::Null)) { + type_flags &= ~((u8)SQLType::Null); + is_null = true; + } + auto type = (SQLType)type_flags; + VERIFY(!is_null || (type != SQLType::Tuple && type != SQLType::Array)); + Value ret(type); + if (!is_null) { + ret.deserialize(buffer, at_offset); + } + return ret; +} + +// ----------------------------------------------------------------- + +bool NullImpl::can_cast(Value const& value) +{ + return value.is_null(); +} + +int NullImpl::compare(Value const& other) +{ + return other.type() == SQLType::Null; +} + +// ----------------------------------------------------------------- + +String TextImpl::to_string() const +{ + return value(); +} + +Optional TextImpl::to_int() const +{ + if (!m_value.has_value()) + return {}; + return value().to_int(); +} + +Optional TextImpl::to_double() const +{ + if (!m_value.has_value()) + return {}; + char* end_ptr; + double ret = strtod(value().characters(), &end_ptr); + if (end_ptr == value().characters()) { + return {}; + } + return ret; +} + +Optional TextImpl::to_bool() const +{ + if (!m_value.has_value()) + return {}; + if (value().equals_ignoring_case("true") || value().equals_ignoring_case("t")) return true; - if (type() == other.type()) - return true; - return m_can_cast(other); + if (value().equals_ignoring_case("false") || value().equals_ignoring_case("f")) + return false; + return {}; } -bool Value::operator==(String const& other) const +void TextImpl::assign(Value const& other_value) { - return operator String() == other; -} - -bool Value::operator==(int other) const -{ - return operator int() == other; -} - -bool Value::operator==(double other) const -{ - return operator double() == other; -} - -void Value::setup(SQLType sql_type) -{ - m_type = sql_type; - switch (sql_type) { - case SQLType::Text: - setup_text(); - break; - case SQLType::Integer: - setup_int(); - break; - case SQLType::Float: - setup_float(); - break; - default: - VERIFY_NOT_REACHED(); + if (other_value.type() == SQLType::Null) { + m_value = {}; + } else { + m_value = other_value.to_string(); } } -void Value::setup_text() +void TextImpl::assign_string(String const& string_value) { - m_impl = String(""); - m_type_name = []() { return "Text"; }; - m_size = []() { return 64 + sizeof(int); }; + m_value = string_value; +} - m_deserialize = [&](ByteBuffer& buffer, size_t& at_offset) { - int len; - memcpy(&len, buffer.offset_pointer((int)at_offset), sizeof(int)); - at_offset += sizeof(int); - m_impl = String((const char*)buffer.offset_pointer((int)at_offset)); - at_offset += 64; - }; +void TextImpl::assign_int(int int_value) +{ + m_value = String::number(int_value); +} - m_serialize = [&](ByteBuffer& buffer) { - char zeroes[64]; +void TextImpl::assign_double(double double_value) +{ + m_value = String::number(double_value); +} - int len = min((int)m_impl.get().length(), 63); - buffer.append(&len, sizeof(int)); - buffer.append(m_impl.get().characters(), len); - memset(zeroes, 0, 64); - buffer.append(zeroes, 64 - len); - }; +void TextImpl::assign_bool(bool bool_value) +{ + m_value = (bool_value) ? "true" : "false"; +} - m_assign_value = [&](Value const& other) { - auto str = other.to_string(); - VERIFY(str.has_value()); - m_impl = str.value(); - }; +size_t TextImpl::length() const +{ + return (is_null()) ? 0 : sizeof(u32) + min(value().length(), 64) + 1; +} - m_assign_string = [&](String const& string) { - m_impl = string; - }; +int TextImpl::compare(Value const& other) const +{ + if (is_null()) + return -1; + auto s1 = value(); + auto s2 = other.to_string(); + if (s1 == s2) + return 0; + return (s1 < s2) ? -1 : 1; +} - m_assign_int = [&](int i) { - m_impl = String::number(i); - }; +u32 TextImpl::hash() const +{ + return value().hash(); +} - m_assign_double = [&](double d) { - m_impl = String::number(d); - }; +String IntegerImpl::to_string() const +{ + return String::formatted("{}", value()); +} - m_to_string = [&]() -> Optional { - return m_impl.get(); - }; +Optional IntegerImpl::to_int() const +{ + return value(); +} - m_to_int = [&]() -> Optional { - return m_impl.get().to_int(); - }; +Optional IntegerImpl::to_double() const +{ + return static_cast(value()); +} - m_to_double = [&]() -> Optional { - char* end_ptr; - double ret = strtod(m_impl.get().characters(), &end_ptr); - if (end_ptr == m_impl.get().characters()) { - return {}; +Optional IntegerImpl::to_bool() const +{ + return value() != 0; +} + +void IntegerImpl::assign(Value const& other_value) +{ + auto i = other_value.to_int(); + if (!i.has_value()) + m_value = {}; + else + m_value = i.value(); +} + +void IntegerImpl::assign_string(String const& string_value) +{ + auto i = string_value.to_int(); + if (!i.has_value()) + m_value = {}; + else + m_value = i.value(); +} + +void IntegerImpl::assign_int(int int_value) +{ + m_value = int_value; +} + +void IntegerImpl::assign_double(double double_value) +{ + m_value = static_cast(round(double_value)); +} + +void IntegerImpl::assign_bool(bool bool_value) +{ + m_value = (bool_value) ? 1 : 0; +} + +bool IntegerImpl::can_cast(Value const& other_value) +{ + return other_value.to_int().has_value(); +} + +int IntegerImpl::compare(Value const& other) const +{ + auto casted = other.to_int(); + if (!casted.has_value()) { + return 1; + } + return value() - casted.value(); +} + +u32 IntegerImpl::hash() const +{ + return int_hash(value()); +} + +String FloatImpl::to_string() const +{ + return String::formatted("{}", value()); +} + +Optional FloatImpl::to_int() const +{ + return static_cast(round(value())); +} + +Optional FloatImpl::to_double() const +{ + return value(); +} + +void FloatImpl::assign(Value const& other_value) +{ + auto i = other_value.to_double(); + if (!i.has_value()) + m_value = {}; + else + m_value = i.value(); +} + +void FloatImpl::assign_string(String const& string_value) +{ + char* end_ptr; + auto dbl = strtod(string_value.characters(), &end_ptr); + if (end_ptr == string_value.characters()) + m_value = {}; + else + m_value = dbl; +} + +void FloatImpl::assign_int(int int_value) +{ + m_value = int_value; +} + +void FloatImpl::assign_double(double double_value) +{ + m_value = double_value; +} + +bool FloatImpl::can_cast(Value const& other_value) +{ + return other_value.to_double().has_value(); +} + +int FloatImpl::compare(Value const& other) const +{ + auto casted = other.to_double(); + if (!casted.has_value()) { + return 1; + } + auto diff = value() - casted.value(); + return (diff < NumericLimits::epsilon()) ? 0 : ((diff > 0) ? 1 : -1); +} + +String BooleanImpl::to_string() const +{ + return (value()) ? "true" : "false"; +} + +Optional BooleanImpl::to_int() const +{ + return (value()) ? 1 : 0; +} + +Optional BooleanImpl::to_double() +{ + return {}; +} + +Optional BooleanImpl::to_bool() const +{ + return value(); +} + +void BooleanImpl::assign(Value const& other_value) +{ + auto b = other_value.to_bool(); + if (!b.has_value()) + m_value = {}; + else + m_value = b.value(); +} + +void BooleanImpl::assign_string(String const& string_value) +{ + return assign(Value(string_value)); +} + +void BooleanImpl::assign_int(int int_value) +{ + m_value = (int_value != 0); +} + +void BooleanImpl::assign_double(double) +{ + m_value = {}; +} + +void BooleanImpl::assign_bool(bool bool_value) +{ + m_value = bool_value; +} + +bool BooleanImpl::can_cast(Value const& other_value) +{ + return other_value.to_bool().has_value(); +} + +int BooleanImpl::compare(Value const& other) const +{ + auto casted = other.to_bool(); + if (!casted.has_value()) { + return 1; + } + return value() ^ casted.value(); // xor - zero if both true or both false, 1 otherwise. +} + +u32 BooleanImpl::hash() const +{ + return int_hash(value()); +} + +void ContainerValueImpl::assign_vector(Vector const& vector_values) +{ + if (!validate_before_assignment(vector_values)) { + m_value = {}; + return; + } + m_value = Vector(); + for (auto& value : vector_values) { + if (!append(value)) { + m_value = {}; + return; } - return ret; - }; + } + if (!validate_after_assignment()) + m_value = {}; +} - m_compare = [&](Value const& other) -> int { - auto s1 = to_string(); - auto s2 = other.to_string(); - VERIFY(s1.has_value()); - if (!s2.has_value()) { - return 1; +bool ContainerValueImpl::to_vector(Vector& vector) const +{ + vector.clear(); + for (auto& value : value()) { + vector.empend(Value(value)); + } + return true; +} + +Vector ContainerValueImpl::to_string_vector() const +{ + Vector ret; + for (auto& value : value()) { + ret.append(Value(value).to_string()); + } + return ret; +} + +String ContainerValueImpl::to_string() const +{ + StringBuilder builder; + builder.append("("); + StringBuilder joined; + joined.join(", ", to_string_vector()); + builder.append(joined.string_view()); + builder.append(")"); + return builder.build(); +} + +u32 ContainerValueImpl::hash() const +{ + u32 ret = 0u; + for (auto& value : value()) { + Value v(value); + // This is an extension of the pair_int_hash function from AK/HashFunctions.h: + if (!ret) + ret = v.hash(); + else + ret = int_hash((ret * 209) ^ (v.hash() * 413)); + } + return ret; +} + +bool ContainerValueImpl::append(Value const& value) +{ + if (value.type() == SQLType::Tuple || value.type() == SQLType::Array) + return false; + return append(value.downcast_to_basetype()); +} + +bool ContainerValueImpl::append(BaseTypeImpl const& impl) +{ + if (m_max_size.has_value() && (size() >= m_max_size.value())) + return false; + if (!validate(impl)) + return false; + m_value.value().empend(impl); + return true; +} + +void ContainerValueImpl::serialize_values(ByteBuffer& buffer) const +{ + serialize_to(buffer, (u32)size()); + for (auto& value : value()) { + Value(value).serialize_to(buffer); + } +} + +void ContainerValueImpl::deserialize_values(ByteBuffer& buffer, size_t& at_offset) +{ + u32 sz; + deserialize_from(buffer, at_offset, sz); + m_value = Vector(); + for (auto ix = 0u; ix < sz; ix++) { + append(Value::deserialize_from(buffer, at_offset)); + } +} + +void TupleImpl::assign(Value const& other) +{ + if (other.type() != SQLType::Tuple) { + m_value = {}; + return; + } + auto& other_impl = other.get_impl({}); + auto other_descriptor = other_impl.m_descriptor; + if (m_descriptor && other_descriptor && m_descriptor->compare_ignoring_names(*other_descriptor)) { + m_value = {}; + return; + } + assign_vector(other.to_vector().value()); +} + +size_t TupleImpl::length() const +{ + return (m_descriptor) ? m_descriptor->data_length() : 0; +} + +bool TupleImpl::can_cast(Value const& other_value) const +{ + if (other_value.type() != SQLType::Tuple) + return false; + return (m_descriptor == other_value.get_impl({}).m_descriptor); +} + +int TupleImpl::compare(Value const& other) const +{ + if (other.type() != SQLType::Tuple) + return 1; + + auto& other_impl = other.get_impl({}); + if (m_descriptor && other_impl.m_descriptor && m_descriptor->compare_ignoring_names(*other_impl.m_descriptor)) + return 1; + + auto other_values = other_impl.value(); + if (size() != other_impl.size()) + return (int)value().size() - (int)other_impl.size(); + for (auto ix = 0u; ix < value().size(); ix++) { + auto ret = Value(value()[ix]).compare(Value(other_impl.value()[ix])); + if (ret != 0) { + if (m_descriptor && (ix < m_descriptor->size()) && (*m_descriptor)[ix].order == Order::Descending) + ret = -ret; + return ret; } - if (s1.value() == s2.value()) - return 0; - return (s1.value() < s2.value()) ? -1 : 1; - }; + } + return 0; +} - m_can_cast = [](Value const&) -> bool { +void TupleImpl::serialize(ByteBuffer& buffer) const +{ + if (m_descriptor) { + serialize_to(buffer, (u32)m_descriptor->size()); + for (auto& tuple_element : *m_descriptor) { + u8 elem_type = (u8)tuple_element.type; + serialize_to(buffer, elem_type); + u8 elem_order = (u8)tuple_element.order; + serialize_to(buffer, elem_order); + } + } else { + serialize_to(buffer, (u32)-1); + } + serialize_values(buffer); +} + +void TupleImpl::deserialize(ByteBuffer& buffer, size_t& at_offset) +{ + u32 sz; + deserialize_from(buffer, at_offset, sz); + if (sz != (u32)-1) { + NonnullRefPtr serialized_descriptor = adopt_ref(*new TupleDescriptor); + for (auto ix = 0u; ix < sz; ix++) { + u8 elem_type, elem_order; + deserialize_from(buffer, at_offset, elem_type); + deserialize_from(buffer, at_offset, elem_order); + serialized_descriptor->empend("", (SQLType)elem_type, (Order)elem_order); + } + m_descriptor = serialized_descriptor; + m_max_size = m_descriptor->size(); + } else { + m_descriptor = nullptr; + m_max_size = {}; + } + deserialize_values(buffer, at_offset); +} + +bool TupleImpl::validate(BaseTypeImpl const& value) +{ + if (!m_descriptor) return true; - }; - - m_hash = [&]() { - return m_impl.get().hash(); - }; + auto required_type = (*m_descriptor)[size()].type; + return Value(value).type() == required_type; } -void Value::setup_int() +bool TupleImpl::validate_after_assignment() { - m_impl.set(0); - m_type_name = []() { return "Integer"; }; - m_size = []() { return sizeof(int); }; - - m_deserialize = [&](ByteBuffer& buffer, size_t& at_offset) { - memcpy(m_impl.get_pointer(), buffer.offset_pointer((int)at_offset), sizeof(int)); - at_offset += sizeof(int); - }; - - m_serialize = [&](ByteBuffer& buffer) { - buffer.append(m_impl.get_pointer(), sizeof(int)); - }; - - m_assign_value = [&](Value const& other) { - auto i = other.to_int(); - VERIFY(i.has_value()); - m_impl = i.value(); - }; - - m_assign_string = [&](String const& string) { - auto i = string.to_int(); - VERIFY(i.has_value()); - m_impl = i.value(); - }; - - m_assign_int = [&](int i) { - m_impl.set(move(i)); - }; - - m_assign_double = [&](double d) { - m_impl.set((int)d); - }; - - m_to_string = [&]() -> Optional { - StringBuilder builder; - builder.appendff("{}", m_impl.get()); - return builder.build(); - }; - - m_to_int = [&]() -> Optional { - return m_impl.get(); - }; - - m_to_double = [&]() -> Optional { - return static_cast(m_impl.get()); - }; - - m_compare = [&](Value const& other) -> int { - auto casted = other.to_int(); - if (!casted.has_value()) { - return 1; - } - return m_impl.get() - casted.value(); - }; - - m_can_cast = [](Value const& other) -> bool { - auto i = other.to_int(); - return i.has_value(); - }; - - m_hash = [&]() -> u32 { - return int_hash(m_impl.get()); - }; + if (!m_descriptor) + return true; + for (auto ix = value().size(); ix < m_descriptor->size(); ++ix) { + auto required_type = (*m_descriptor)[ix].type; + append(Value(required_type)); + } + return true; } -void Value::setup_float() +void ArrayImpl::assign(Value const& other) { - m_impl.set(0.0); - m_type_name = []() { return "Float"; }; - m_size = []() { return sizeof(double); }; + if (other.type() != SQLType::Array) { + m_value = {}; + return; + } + auto& other_impl = other.get_impl({}); + if (m_max_size != other_impl.m_max_size || m_element_type != other_impl.m_element_type) { + m_value = {}; + return; + } + assign_vector(other.to_vector().value()); +} - m_deserialize = [&](ByteBuffer& buffer, size_t& at_offset) { - memcpy(m_impl.get_pointer(), buffer.offset_pointer((int)at_offset), sizeof(double)); - at_offset += sizeof(double); - }; +size_t ArrayImpl::length() const +{ + size_t ret = 0; + for (auto& value : value()) { + ret += Value(value).length(); + } + return ret; +} - m_serialize = [&](ByteBuffer& buffer) { - buffer.append(m_impl.get_pointer(), sizeof(double)); - }; +bool ArrayImpl::can_cast(Value const& other_value) const +{ + if (other_value.type() != SQLType::Array) + return false; + auto& other_impl = other_value.get_impl({}); + return (m_max_size != other_impl.m_max_size || m_element_type != other_impl.m_element_type); +} - m_to_string = [&]() -> Optional { - StringBuilder builder; - builder.appendff("{}", m_impl.get()); - return builder.build(); - }; - - m_to_int = [&]() -> Optional { - return (int)m_impl.get(); - }; - - m_to_double = [&]() -> Optional { - return m_impl.get(); - }; - - m_assign_value = [&](Value const& other) { - auto dbl = other.to_double(); - VERIFY(dbl.has_value()); - m_impl.set(move(dbl.value())); - }; - - m_assign_string = [&](String const& string) { - char* end_ptr; - auto dbl = strtod(string.characters(), &end_ptr); - VERIFY(end_ptr != string.characters()); - m_impl.set(move(dbl)); - }; - - m_assign_int = [&](int i) { - m_impl.set(static_cast(i)); - }; - - m_assign_double = [&](double d) { - m_impl.set(move(d)); - }; - - m_compare = [&](Value const& other) -> int { - auto casted = other.to_double(); - if (!casted.has_value()) { - return 1; +int ArrayImpl::compare(Value const& other) const +{ + if (other.type() != SQLType::Array) + return 1; + auto other_impl = other.get_impl({}); + if (other_impl.m_element_type != m_element_type) + return 1; + if (other_impl.m_max_size.has_value() && m_max_size.has_value() && other_impl.m_max_size != m_max_size) + return (int)m_max_size.value() - (int)other_impl.m_max_size.value(); + if (size() != other_impl.size()) + return (int)size() - (int)other_impl.size(); + for (auto ix = 0u; ix < size(); ix++) { + auto ret = Value(value()[ix]).compare(Value(other_impl.value()[ix])); + if (ret != 0) { + return ret; } - auto diff = m_impl.get() - casted.value(); - return (diff < NumericLimits::epsilon()) ? 0 : ((diff > 0) ? 1 : -1); - }; + } + return 0; +} - m_can_cast = [](Value const& other) -> bool { - auto dbl = other.to_double(); - return dbl.has_value(); - }; +void ArrayImpl::serialize(ByteBuffer& buffer) const +{ + serialize_to(buffer, (u8)m_element_type); + if (m_max_size.has_value()) + serialize_to(buffer, (u32)m_max_size.value()); + else + serialize_to(buffer, (u32)0); + serialize_values(buffer); +} - // Using floats in hash functions is a bad idea. Let's disable that for now. - m_hash = []() -> u32 { - VERIFY_NOT_REACHED(); - }; +void ArrayImpl::deserialize(ByteBuffer& buffer, size_t& at_offset) +{ + u8 elem_type; + deserialize_from(buffer, at_offset, elem_type); + m_element_type = (SQLType)elem_type; + u32 max_sz; + deserialize_from(buffer, at_offset, max_sz); + if (max_sz) + m_max_size = max_sz; + else + m_max_size = {}; + deserialize_values(buffer, at_offset); +} + +bool ArrayImpl::validate(BaseTypeImpl const& impl) +{ + return Value(impl).type() == m_element_type; } } diff --git a/Userland/Libraries/LibSQL/Value.h b/Userland/Libraries/LibSQL/Value.h index 655aac93f0..624e32e4ae 100644 --- a/Userland/Libraries/LibSQL/Value.h +++ b/Userland/Libraries/LibSQL/Value.h @@ -6,14 +6,290 @@ #pragma once +#include #include -#include +#include #include #include +#include +#include #include +#include namespace SQL { +class Value; + +class BaseImpl { +public: + explicit BaseImpl(SQLType type = SQLType::Null) + : m_type(type) + { + } + + [[nodiscard]] SQLType type() const { return m_type; } + [[nodiscard]] String type_name() const { return SQLType_name(type()); } + +private: + SQLType m_type { SQLType::Null }; +}; + +class NullImpl : public BaseImpl { +public: + explicit NullImpl() + : BaseImpl(SQLType::Null) + { + } + + [[nodiscard]] static bool is_null() { return true; } + [[nodiscard]] static String to_string() { return "(null)"; } + [[nodiscard]] static Optional to_int() { return {}; } + [[nodiscard]] static Optional to_double() { return {}; } + [[nodiscard]] static Optional to_bool() { return {}; } + [[nodiscard]] static bool to_vector(Vector&) { return false; } + static void assign(Value const&) { } + static void assign_string(String const&) { } + static void assign_int(int) { } + static void assign_double(double) { } + static void assign_bool(bool) { } + static void assign_vector(Vector const&) { } + [[nodiscard]] static size_t length() { return 0; } + [[nodiscard]] static bool can_cast(Value const&); + [[nodiscard]] static int compare(Value const&); + static void serialize(ByteBuffer&) { } + static void deserialize(ByteBuffer&, size_t&) { } + [[nodiscard]] static u32 hash() { return 0; } +}; + +template +class Impl : public BaseImpl { +public: + [[nodiscard]] bool is_null() const + { + return !m_value.has_value(); + } + + [[nodiscard]] T const& value() const + { + VERIFY(m_value.has_value()); + return m_value.value(); + } + + [[nodiscard]] size_t length() const + { + return sizeof(T); + } + + void serialize(ByteBuffer& buffer) const + { + serialize_to(buffer, value()); + } + + void deserialize(ByteBuffer& buffer, size_t& at_offset) + { + T value; + deserialize_from(buffer, at_offset, value); + m_value = value; + } + +protected: + explicit Impl(SQLType sql_type) + : BaseImpl(sql_type) + { + } + + Optional m_value {}; +}; + +class TextImpl : public Impl { +public: + explicit TextImpl() + : Impl(SQLType::Text) + { + } + + [[nodiscard]] String to_string() const; + [[nodiscard]] Optional to_int() const; + [[nodiscard]] Optional to_double() const; + [[nodiscard]] Optional to_bool() const; + [[nodiscard]] static bool to_vector(Vector&) { return false; } + void assign(Value const&); + void assign_string(String const&); + void assign_int(int); + void assign_double(double); + void assign_bool(bool); + void assign_vector(Vector const&) { m_value = {}; } + [[nodiscard]] size_t length() const; + [[nodiscard]] static bool can_cast(Value const&) { return true; } + [[nodiscard]] int compare(Value const& other) const; + [[nodiscard]] u32 hash() const; +}; + +class IntegerImpl : public Impl { +public: + IntegerImpl() + : Impl(SQLType::Integer) + { + } + + [[nodiscard]] String to_string() const; + [[nodiscard]] Optional to_int() const; + [[nodiscard]] Optional to_double() const; + [[nodiscard]] Optional to_bool() const; + [[nodiscard]] static bool to_vector(Vector&) { return false; } + void assign(Value const&); + void assign_string(String const&); + void assign_int(int); + void assign_double(double); + void assign_bool(bool); + void assign_vector(Vector const&) { m_value = {}; } + [[nodiscard]] static bool can_cast(Value const&); + [[nodiscard]] int compare(Value const& other) const; + [[nodiscard]] u32 hash() const; +}; + +class FloatImpl : public Impl { +public: + explicit FloatImpl() + : Impl(SQLType::Float) + { + } + + [[nodiscard]] String to_string() const; + [[nodiscard]] Optional to_int() const; + [[nodiscard]] Optional to_double() const; + [[nodiscard]] static Optional to_bool() { return {}; } + [[nodiscard]] static bool to_vector(Vector&) { return false; } + void assign(Value const&); + void assign_string(String const&); + void assign_int(int); + void assign_double(double); + void assign_bool(bool) { m_value = {}; } + void assign_vector(Vector const&) { m_value = {}; } + [[nodiscard]] static bool can_cast(Value const&); + [[nodiscard]] int compare(Value const& other) const; + + // Using floats in hash functions is a bad idea. Let's disable that for now. + [[nodiscard]] static u32 hash() { VERIFY_NOT_REACHED(); } +}; + +class BooleanImpl : public Impl { +public: + explicit BooleanImpl() + : Impl(SQLType::Boolean) + { + } + + [[nodiscard]] String to_string() const; + [[nodiscard]] Optional to_int() const; + [[nodiscard]] static Optional to_double(); + [[nodiscard]] Optional to_bool() const; + [[nodiscard]] static bool to_vector(Vector&) { return false; } + void assign(Value const&); + void assign_string(String const&); + void assign_int(int); + void assign_double(double); + void assign_bool(bool); + void assign_vector(Vector const&) { m_value = {}; } + [[nodiscard]] static bool can_cast(Value const&); + [[nodiscard]] int compare(Value const& other) const; + [[nodiscard]] u32 hash() const; +}; + +using BaseTypeImpl = Variant; + +class ContainerValueImpl : public Impl> { +public: + virtual ~ContainerValueImpl() = default; + + [[nodiscard]] String to_string() const; + [[nodiscard]] static Optional to_int() { return {}; } + [[nodiscard]] static Optional to_double() { return {}; } + [[nodiscard]] static Optional to_bool() { return {}; } + [[nodiscard]] bool to_vector(Vector&) const; + void assign_string(String const&) { m_value = {}; } + void assign_int(int) { m_value = {}; } + void assign_double(double) { m_value = {}; } + void assign_bool(bool) { m_value = {}; } + void assign_vector(Vector const&); + [[nodiscard]] u32 hash() const; + + virtual bool validate_before_assignment(Vector const&) { return true; } + virtual bool validate(BaseTypeImpl const&) { return true; } + virtual bool validate_after_assignment() { return true; } + [[nodiscard]] Vector to_string_vector() const; + [[nodiscard]] size_t size() const { return is_null() ? 0 : value().size(); } + bool append(Value const&); + bool append(BaseTypeImpl const& value); + void serialize_values(ByteBuffer& buffer) const; + void deserialize_values(ByteBuffer&, size_t& at_offset); + +protected: + explicit ContainerValueImpl(SQLType sql_type, Optional const& max_size = {}) + : Impl(sql_type) + , m_max_size(max_size) + { + } + + Optional m_max_size {}; +}; + +class TupleImpl : public ContainerValueImpl { +public: + explicit TupleImpl(NonnullRefPtr const& descriptor, bool is_null = true) + : ContainerValueImpl(SQLType::Tuple, is_null) + , m_descriptor(descriptor) + { + m_max_size = m_descriptor->size(); + } + + explicit TupleImpl() + : ContainerValueImpl(SQLType::Tuple, {}) + { + } + + void assign(Value const&); + [[nodiscard]] size_t length() const; + [[nodiscard]] bool can_cast(Value const&) const; + [[nodiscard]] int compare(Value const& other) const; + + virtual bool validate(BaseTypeImpl const&) override; + virtual bool validate_after_assignment() override; + void serialize(ByteBuffer& buffer) const; + void deserialize(ByteBuffer& buffer, size_t&); + +private: + RefPtr m_descriptor; +}; + +class ArrayImpl : public ContainerValueImpl { +public: + explicit ArrayImpl(SQLType element_type, Optional const& max_size = {}) + : ContainerValueImpl(SQLType::Array, max_size) + , m_element_type(element_type) + { + } + + explicit ArrayImpl() + : ContainerValueImpl(SQLType::Array, {}) + , m_element_type(SQLType::Null) + { + } + + void assign(Value const&); + [[nodiscard]] size_t length() const; + [[nodiscard]] bool can_cast(Value const&) const; + [[nodiscard]] int compare(Value const& other) const; + void serialize(ByteBuffer& buffer) const; + void deserialize(ByteBuffer& buffer, size_t&); + virtual bool validate(BaseTypeImpl const&) override; + +private: + SQLType m_element_type { SQLType::Text }; +}; + +using ValueTypeImpl = Variant; + /** * 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 @@ -21,88 +297,105 @@ namespace SQL { */ class Value { public: - explicit Value(SQLType sql_type = SQLType::Text); - Value(SQLType sql_type, ByteBuffer& buffer, size_t& offset); - Value(Value const& other); - ~Value(); + Value(Value&) = default; + Value(Value const&) = default; - static Value const& null(); + explicit Value(SQLType sql_type = SQLType::Null); - Value& operator=(Value&& other) noexcept + template + explicit Value(Variant impl) + : m_impl(impl) { - (*this) = other; - return (*this); } + + enum SetImplementation { + SetImplementationSingleton + }; + + template + Value(SetImplementation, I&& impl) + { + m_impl.set(forward(impl)); + } + + Value(SQLType, Value const&); + Value(SQLType, String const&); + Value(SQLType, char const*); + Value(SQLType, int); + Value(SQLType, double); + Value(SQLType, bool); + explicit Value(String const&); + explicit Value(char const*); + explicit Value(int); + explicit Value(double); + explicit Value(bool); + + ~Value() = default; + + [[nodiscard]] bool is_null() const; + [[nodiscard]] SQLType type() const; + [[nodiscard]] String type_name() const; + [[nodiscard]] BaseTypeImpl downcast_to_basetype() const; + + template + Impl const& get_impl(Badge) const { return m_impl.get(); } + + [[nodiscard]] String to_string() const; + [[nodiscard]] Optional to_int() const; + [[nodiscard]] Optional to_u32() const; + [[nodiscard]] Optional to_double() const; + [[nodiscard]] Optional to_bool() const; + [[nodiscard]] Optional> to_vector() const; + + explicit operator String() const; + explicit operator int() const; + explicit operator u32() const; + explicit operator double() const; + explicit operator bool() const; + + void assign(Value const& other_value); + void assign(String const& string_value); + void assign(int const& int_value); + void assign(double const& double_value); + void assign(bool const& bool_value); + void assign(Vector const& values); + Value& operator=(Value const& other); + Value& operator=(String const&); - Value& operator=(String&& string) - { - operator=(string); - return *this; - } + Value& operator=(char const*); Value& operator=(int); Value& operator=(u32); Value& operator=(double); - Value& set_null(); + Value& operator=(bool); + Value& operator=(Vector const&); - Optional to_string() const; - explicit operator String() const; - Optional to_int() const; - explicit operator int() const; - Optional to_double() const; - explicit operator double() const; - Optional to_u32() const; - explicit operator u32() const; - - [[nodiscard]] SQLType type() const { return m_type; } - [[nodiscard]] const char* type_name() const { return m_type_name(); } - [[nodiscard]] size_t size() const { return m_size(); } - [[nodiscard]] int compare(Value const& other) const { return m_compare(other); } - [[nodiscard]] bool is_null() const { return m_is_null; } + [[nodiscard]] size_t length() const; + [[nodiscard]] u32 hash() const; [[nodiscard]] bool can_cast(Value const&) const; - [[nodiscard]] u32 hash() const { return (is_null()) ? 0 : m_hash(); } + void serialize_to(ByteBuffer&) const; + void deserialize(ByteBuffer&, size_t&); - bool operator==(Value const& other) const { return m_compare(other) == 0; } - bool operator==(String const& other) const; - bool operator==(int other) const; - bool operator==(double other) const; - bool operator!=(Value const& other) const { return m_compare(other) != 0; } - bool operator<(Value const& other) const { return m_compare(other) < 0; } - bool operator<=(Value const& other) const { return m_compare(other) <= 0; } - bool operator>(Value const& other) const { return m_compare(other) > 0; } - bool operator>=(Value const& other) const { return m_compare(other) >= 0; } + [[nodiscard]] int compare(Value const&) const; + bool operator==(Value const&) const; + bool operator==(String const&) const; + bool operator==(int) const; + bool operator==(double) const; + bool operator!=(Value const&) const; + bool operator<(Value const&) const; + bool operator<=(Value const&) const; + bool operator>(Value const&) const; + bool operator>=(Value const&) const; - void serialize(ByteBuffer& buffer) const - { - VERIFY(!is_null()); - m_serialize(buffer); - } + static Value const& null(); + static Value create_tuple(NonnullRefPtr const&); + static Value create_array(SQLType element_type, Optional const& max_size = {}); + static Value deserialize_from(ByteBuffer&, size_t&); private: - void setup(SQLType sql_type); - void setup_text(); - void setup_int(); - void setup_float(); + void setup(SQLType type); - Function()> m_to_string; - Function()> m_to_int; - Function()> m_to_double; - Function m_assign_value; - Function m_assign_string; - Function m_assign_int; - Function m_assign_double; - Function m_compare; - Function m_serialize; - Function m_deserialize; - Function m_size; - Function m_type_name; - Function m_can_cast; - Function m_hash; - - SQLType m_type { SQLType::Text }; - bool m_is_null { true }; - - Variant m_impl; + ValueTypeImpl m_impl {}; }; }