diff options
30 files changed, 991 insertions, 776 deletions
diff --git a/Tests/LibSQL/TestSqlBtreeIndex.cpp b/Tests/LibSQL/TestSqlBtreeIndex.cpp index c21fcad7ff..9f09560ce6 100644 --- a/Tests/LibSQL/TestSqlBtreeIndex.cpp +++ b/Tests/LibSQL/TestSqlBtreeIndex.cpp @@ -120,23 +120,23 @@ constexpr static u32 pointers[] = { 4, }; -NonnullRefPtr<SQL::BTree> setup_btree(SQL::Heap& heap); -void insert_and_get_to_and_from_btree(int num_keys); -void insert_into_and_scan_btree(int num_keys); +NonnullRefPtr<SQL::BTree> setup_btree(SQL::Serializer&); +void insert_and_get_to_and_from_btree(int); +void insert_into_and_scan_btree(int); -NonnullRefPtr<SQL::BTree> setup_btree(SQL::Heap& heap) +NonnullRefPtr<SQL::BTree> setup_btree(SQL::Serializer& serializer) { NonnullRefPtr<SQL::TupleDescriptor> tuple_descriptor = adopt_ref(*new SQL::TupleDescriptor); tuple_descriptor->append({ "key_value", SQL::SQLType::Integer, SQL::Order::Ascending }); - auto root_pointer = heap.user_value(0); + auto root_pointer = serializer.heap().user_value(0); if (!root_pointer) { - root_pointer = heap.new_record_pointer(); - heap.set_user_value(0, root_pointer); + root_pointer = serializer.heap().new_record_pointer(); + serializer.heap().set_user_value(0, root_pointer); } - auto btree = SQL::BTree::construct(heap, tuple_descriptor, true, root_pointer); + auto btree = SQL::BTree::construct(serializer, tuple_descriptor, true, root_pointer); btree->on_new_root = [&]() { - heap.set_user_value(0, btree->root()); + serializer.heap().set_user_value(0, btree->root()); }; return btree; } @@ -146,7 +146,8 @@ void insert_and_get_to_and_from_btree(int num_keys) ScopeGuard guard([]() { unlink("/tmp/test.db"); }); { auto heap = SQL::Heap::construct("/tmp/test.db"); - auto btree = setup_btree(heap); + SQL::Serializer serializer(heap); + auto btree = setup_btree(serializer); for (auto ix = 0; ix < num_keys; ix++) { SQL::Key k(btree->descriptor()); @@ -161,13 +162,14 @@ void insert_and_get_to_and_from_btree(int num_keys) { auto heap = SQL::Heap::construct("/tmp/test.db"); - auto btree = setup_btree(heap); + SQL::Serializer serializer(heap); + auto btree = setup_btree(serializer); for (auto ix = 0; ix < num_keys; ix++) { SQL::Key k(btree->descriptor()); k[0] = keys[ix]; auto pointer_opt = btree->get(k); - EXPECT(pointer_opt.has_value()); + VERIFY(pointer_opt.has_value()); EXPECT_EQ(pointer_opt.value(), pointers[ix]); } } @@ -178,7 +180,8 @@ void insert_into_and_scan_btree(int num_keys) ScopeGuard guard([]() { unlink("/tmp/test.db"); }); { auto heap = SQL::Heap::construct("/tmp/test.db"); - auto btree = setup_btree(heap); + SQL::Serializer serializer(heap); + auto btree = setup_btree(serializer); for (auto ix = 0; ix < num_keys; ix++) { SQL::Key k(btree->descriptor()); @@ -194,13 +197,14 @@ void insert_into_and_scan_btree(int num_keys) { auto heap = SQL::Heap::construct("/tmp/test.db"); - auto btree = setup_btree(heap); + SQL::Serializer serializer(heap); + auto btree = setup_btree(serializer); int count = 0; SQL::Tuple prev; for (auto iter = btree->begin(); !iter.is_end(); iter++, count++) { auto key = (*iter); - if (prev.length()) { + if (prev.size()) { EXPECT(prev < key); } auto key_value = (int)key[0]; diff --git a/Tests/LibSQL/TestSqlHashIndex.cpp b/Tests/LibSQL/TestSqlHashIndex.cpp index ccf2f5988a..d379f97303 100644 --- a/Tests/LibSQL/TestSqlHashIndex.cpp +++ b/Tests/LibSQL/TestSqlHashIndex.cpp @@ -117,22 +117,22 @@ constexpr static u32 pointers[] = { 4, }; -NonnullRefPtr<SQL::HashIndex> setup_hash_index(SQL::Heap& heap); -void insert_and_get_to_and_from_hash_index(int num_keys); -void insert_into_and_scan_hash_index(int num_keys); +NonnullRefPtr<SQL::HashIndex> setup_hash_index(SQL::Serializer&); +void insert_and_get_to_and_from_hash_index(int); +void insert_into_and_scan_hash_index(int); -NonnullRefPtr<SQL::HashIndex> setup_hash_index(SQL::Heap& heap) +NonnullRefPtr<SQL::HashIndex> setup_hash_index(SQL::Serializer& serializer) { NonnullRefPtr<SQL::TupleDescriptor> tuple_descriptor = adopt_ref(*new SQL::TupleDescriptor); tuple_descriptor->append({ "key_value", SQL::SQLType::Integer, SQL::Order::Ascending }); tuple_descriptor->append({ "text_value", SQL::SQLType::Text, SQL::Order::Ascending }); - auto directory_pointer = heap.user_value(0); + auto directory_pointer = serializer.heap().user_value(0); if (!directory_pointer) { - directory_pointer = heap.new_record_pointer(); - heap.set_user_value(0, directory_pointer); + directory_pointer = serializer.heap().new_record_pointer(); + serializer.heap().set_user_value(0, directory_pointer); } - auto hash_index = SQL::HashIndex::construct(heap, tuple_descriptor, directory_pointer); + auto hash_index = SQL::HashIndex::construct(serializer, tuple_descriptor, directory_pointer); return hash_index; } @@ -141,7 +141,8 @@ void insert_and_get_to_and_from_hash_index(int num_keys) ScopeGuard guard([]() { unlink("/tmp/test.db"); }); { auto heap = SQL::Heap::construct("/tmp/test.db"); - auto hash_index = setup_hash_index(heap); + SQL::Serializer serializer(heap); + auto hash_index = setup_hash_index(serializer); for (auto ix = 0; ix < num_keys; ix++) { SQL::Key k(hash_index->descriptor()); @@ -157,14 +158,15 @@ void insert_and_get_to_and_from_hash_index(int num_keys) { auto heap = SQL::Heap::construct("/tmp/test.db"); - auto hash_index = setup_hash_index(heap); + SQL::Serializer serializer(heap); + auto hash_index = setup_hash_index(serializer); for (auto ix = 0; ix < num_keys; ix++) { SQL::Key k(hash_index->descriptor()); k[0] = keys[ix]; k[1] = String::formatted("The key value is {} and the pointer is {}", keys[ix], pointers[ix]); auto pointer_opt = hash_index->get(k); - EXPECT(pointer_opt.has_value()); + VERIFY(pointer_opt.has_value()); EXPECT_EQ(pointer_opt.value(), pointers[ix]); } } @@ -235,7 +237,8 @@ void insert_into_and_scan_hash_index(int num_keys) ScopeGuard guard([]() { unlink("/tmp/test.db"); }); { auto heap = SQL::Heap::construct("/tmp/test.db"); - auto hash_index = setup_hash_index(heap); + SQL::Serializer serializer(heap); + auto hash_index = setup_hash_index(serializer); for (auto ix = 0; ix < num_keys; ix++) { SQL::Key k(hash_index->descriptor()); @@ -251,7 +254,8 @@ void insert_into_and_scan_hash_index(int num_keys) { auto heap = SQL::Heap::construct("/tmp/test.db"); - auto hash_index = setup_hash_index(heap); + SQL::Serializer serializer(heap); + auto hash_index = setup_hash_index(serializer); Vector<bool> found; for (auto ix = 0; ix < num_keys; ix++) { found.append(false); diff --git a/Tests/LibSQL/TestSqlValueAndTuple.cpp b/Tests/LibSQL/TestSqlValueAndTuple.cpp index cbedb15836..59ebde4b72 100644 --- a/Tests/LibSQL/TestSqlValueAndTuple.cpp +++ b/Tests/LibSQL/TestSqlValueAndTuple.cpp @@ -95,11 +95,11 @@ TEST_CASE(serialize_text_value) SQL::Value v("Test"); EXPECT(v.to_string() == "Test"); - ByteBuffer buffer; - v.serialize_to(buffer); + SQL::Serializer serializer; + serializer.serialize<SQL::Value>(v); - size_t offset = 0; - auto v2 = SQL::Value::deserialize_from(buffer, offset); + serializer.rewind(); + auto v2 = serializer.deserialize<SQL::Value>(); EXPECT((String)v2 == "Test"); } @@ -146,11 +146,11 @@ TEST_CASE(serialize_int_value) EXPECT_EQ(v.type(), SQL::SQLType::Integer); EXPECT_EQ(v.to_int().value(), 42); - ByteBuffer buffer; - v.serialize_to(buffer); + SQL::Serializer serializer; + serializer.serialize<SQL::Value>(v); - size_t offset = 0; - auto v2 = SQL::Value::deserialize_from(buffer, offset); + 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); @@ -201,11 +201,11 @@ TEST_CASE(serialize_float_value) EXPECT_EQ(v.type(), SQL::SQLType::Float); EXPECT(v.to_double().value() - 3.14 < NumericLimits<double>().epsilon()); - ByteBuffer buffer; - v.serialize_to(buffer); + SQL::Serializer serializer; + serializer.serialize<SQL::Value>(v); - size_t offset = 0; - auto v2 = SQL::Value::deserialize_from(buffer, offset); + 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()); @@ -272,11 +272,11 @@ TEST_CASE(serialize_boolean_value) EXPECT_EQ(v.type(), SQL::SQLType::Boolean); EXPECT(bool(v)); - ByteBuffer buffer; - v.serialize_to(buffer); + SQL::Serializer serializer; + serializer.serialize<SQL::Value>(v); - size_t offset = 0; - auto v2 = SQL::Value::deserialize_from(buffer, offset); + serializer.rewind(); + auto v2 = serializer.deserialize<SQL::Value>(); EXPECT(!v2.is_null()); EXPECT_EQ(v2.type(), SQL::SQLType::Boolean); EXPECT(bool(v2)); @@ -374,11 +374,11 @@ TEST_CASE(serialize_tuple_value) values.append(SQL::Value(42)); v = values; - ByteBuffer buffer; - v.serialize_to(buffer); + SQL::Serializer serializer; + serializer.serialize<SQL::Value>(v); - size_t offset = 0; - auto v2 = SQL::Value::deserialize_from(buffer, offset); + serializer.rewind(); + auto v2 = serializer.deserialize<SQL::Value>(); EXPECT(!v2.is_null()); EXPECT_EQ(v2.type(), SQL::SQLType::Tuple); EXPECT_EQ(v, v2); @@ -440,11 +440,11 @@ TEST_CASE(serialize_array_value) values.append(SQL::Value("Test 2")); v = values; - ByteBuffer buffer; - v.serialize_to(buffer); + SQL::Serializer serializer; + serializer.serialize<SQL::Value>(v); - size_t offset = 0; - auto v2 = SQL::Value::deserialize_from(buffer, offset); + serializer.rewind(); + auto v2 = serializer.deserialize<SQL::Value>(); EXPECT(!v2.is_null()); EXPECT_EQ(v2.type(), SQL::SQLType::Array); EXPECT_EQ(v, v2); @@ -497,13 +497,14 @@ TEST_CASE(serialize_tuple) tuple["col1"] = "Test"; tuple["col2"] = 42; - auto buffer = ByteBuffer(); - tuple.serialize(buffer); EXPECT_EQ((String)tuple[0], "Test"); EXPECT_EQ((int)tuple[1], 42); - size_t offset = 0; - SQL::Tuple tuple2(descriptor, buffer, offset); + SQL::Serializer serializer; + serializer.serialize<SQL::Tuple>(tuple); + + serializer.rewind(); + auto tuple2 = serializer.deserialize<SQL::Tuple>(); EXPECT(tuple2[0] == "Test"); EXPECT(tuple2[1] == 42); } diff --git a/Userland/Libraries/LibSQL/BTree.cpp b/Userland/Libraries/LibSQL/BTree.cpp index bb0d07d77b..d83a353e8d 100644 --- a/Userland/Libraries/LibSQL/BTree.cpp +++ b/Userland/Libraries/LibSQL/BTree.cpp @@ -10,14 +10,14 @@ namespace SQL { -BTree::BTree(Heap& heap, NonnullRefPtr<TupleDescriptor> const& descriptor, bool unique, u32 pointer) - : Index(heap, descriptor, unique, pointer) +BTree::BTree(Serializer& serializer, NonnullRefPtr<TupleDescriptor> const& descriptor, bool unique, u32 pointer) + : Index(serializer, descriptor, unique, pointer) , m_root(nullptr) { } -BTree::BTree(Heap& heap, NonnullRefPtr<TupleDescriptor> const& descriptor, u32 pointer) - : BTree(heap, descriptor, true, pointer) +BTree::BTree(Serializer& serializer, NonnullRefPtr<TupleDescriptor> const& descriptor, u32 pointer) + : BTree(serializer, descriptor, true, pointer) { } @@ -37,10 +37,9 @@ BTreeIterator BTree::end() void BTree::initialize_root() { if (pointer()) { - if (pointer() < heap().size()) { - auto buffer = read_block(pointer()); - size_t offset = 0; - m_root = make<TreeNode>(*this, nullptr, pointer(), buffer, offset); + if (serializer().has_block(pointer())) { + serializer().get_block(pointer()); + m_root = serializer().make_and_deserialize<TreeNode>(*this, pointer()); } else { m_root = make<TreeNode>(*this, nullptr, pointer()); } @@ -50,13 +49,14 @@ void BTree::initialize_root() if (on_new_root) on_new_root(); } + m_root->dump_if(0, "initialize_root"); } TreeNode* BTree::new_root() { set_pointer(new_record_pointer()); m_root = make<TreeNode>(*this, nullptr, m_root.leak_ptr(), pointer()); - add_to_write_ahead_log(m_root->as_index_node()); + serializer().serialize_and_write(*m_root.ptr(), m_root->pointer()); if (on_new_root) on_new_root(); return m_root; diff --git a/Userland/Libraries/LibSQL/BTree.h b/Userland/Libraries/LibSQL/BTree.h index 717d5299b0..d02222e38d 100644 --- a/Userland/Libraries/LibSQL/BTree.h +++ b/Userland/Libraries/LibSQL/BTree.h @@ -43,7 +43,7 @@ public: TreeNode* node(); private: - void inflate(); + void deserialize(Serializer&); TreeNode* m_owner; u32 m_pointer { 0 }; @@ -53,27 +53,27 @@ private: class TreeNode : public IndexNode { public: + TreeNode(BTree&, u32 = 0); TreeNode(BTree&, TreeNode*, u32 = 0); TreeNode(BTree&, TreeNode*, TreeNode*, u32 = 0); - TreeNode(BTree&, TreeNode*, u32 pointer, ByteBuffer&, size_t&); ~TreeNode() override = default; [[nodiscard]] BTree& tree() const { return m_tree; } [[nodiscard]] TreeNode* up() const { return m_up; } [[nodiscard]] size_t size() const { return m_entries.size(); } + [[nodiscard]] size_t length() const; [[nodiscard]] Vector<Key> entries() const { return m_entries; } [[nodiscard]] u32 down_pointer(size_t) const; [[nodiscard]] TreeNode* down_node(size_t); [[nodiscard]] bool is_leaf() const { return m_is_leaf; } - [[nodiscard]] size_t max_keys_in_node(); Key const& operator[](size_t) const; bool insert(Key const&); bool update_key_pointer(Key const&); TreeNode* node_for(Key const&); Optional<u32> get(Key&); - void serialize(ByteBuffer&) const override; - IndexNode* as_index_node() override { return dynamic_cast<IndexNode*>(this); } + void deserialize(Serializer&); + void serialize(Serializer&) const; private: TreeNode(BTree&, TreeNode*, DownPointer&, u32 = 0); @@ -111,8 +111,8 @@ public: Function<void(void)> on_new_root; private: - BTree(Heap& heap, NonnullRefPtr<TupleDescriptor> const&, bool unique, u32 pointer); - BTree(Heap& heap, NonnullRefPtr<TupleDescriptor> const&, u32 pointer); + BTree(Serializer&, NonnullRefPtr<TupleDescriptor> const&, bool unique, u32 pointer); + BTree(Serializer&, NonnullRefPtr<TupleDescriptor> const&, u32 pointer); void initialize_root(); TreeNode* new_root(); OwnPtr<TreeNode> m_root { nullptr }; diff --git a/Userland/Libraries/LibSQL/BTreeIterator.cpp b/Userland/Libraries/LibSQL/BTreeIterator.cpp index a926b49c0a..d062287489 100644 --- a/Userland/Libraries/LibSQL/BTreeIterator.cpp +++ b/Userland/Libraries/LibSQL/BTreeIterator.cpp @@ -232,7 +232,7 @@ bool BTreeIterator::update(Key const& new_value) // We are friend of BTree and TreeNode. Don't know how I feel about that. m_current->m_entries[m_index] = new_value; - m_current->tree().add_to_write_ahead_log(m_current); + m_current->tree().serializer().serialize_and_write(*m_current, m_current->pointer()); return true; } diff --git a/Userland/Libraries/LibSQL/CMakeLists.txt b/Userland/Libraries/LibSQL/CMakeLists.txt index d9e8b63c3e..744d7980aa 100644 --- a/Userland/Libraries/LibSQL/CMakeLists.txt +++ b/Userland/Libraries/LibSQL/CMakeLists.txt @@ -17,6 +17,7 @@ set(SOURCES Key.cpp Meta.cpp Row.cpp + Serializer.cpp SQLClient.cpp TreeNode.cpp Tuple.cpp diff --git a/Userland/Libraries/LibSQL/Database.cpp b/Userland/Libraries/LibSQL/Database.cpp index ccbe61cbaf..1854dd4cd7 100644 --- a/Userland/Libraries/LibSQL/Database.cpp +++ b/Userland/Libraries/LibSQL/Database.cpp @@ -19,9 +19,10 @@ namespace SQL { Database::Database(String name) : m_heap(Heap::construct(name)) - , m_schemas(BTree::construct(*m_heap, SchemaDef::index_def()->to_tuple_descriptor(), m_heap->schemas_root())) - , m_tables(BTree::construct(*m_heap, TableDef::index_def()->to_tuple_descriptor(), m_heap->tables_root())) - , m_table_columns(BTree::construct(*m_heap, ColumnDef::index_def()->to_tuple_descriptor(), m_heap->table_columns_root())) + , m_serializer(m_heap) + , m_schemas(BTree::construct(m_serializer, SchemaDef::index_def()->to_tuple_descriptor(), m_heap->schemas_root())) + , m_tables(BTree::construct(m_serializer, TableDef::index_def()->to_tuple_descriptor(), m_heap->tables_root())) + , m_table_columns(BTree::construct(m_serializer, ColumnDef::index_def()->to_tuple_descriptor(), m_heap->table_columns_root())) { m_schemas->on_new_root = [&]() { m_heap->set_schemas_root(m_schemas->root()); @@ -118,10 +119,7 @@ Vector<Row> Database::select_all(TableDef const& table) VERIFY(m_table_cache.get(table.key().hash()).has_value()); Vector<Row> ret; for (auto pointer = table.pointer(); pointer; pointer = ret.last().next_pointer()) { - auto buffer_or_error = m_heap->read_block(pointer); - if (buffer_or_error.is_error()) - VERIFY_NOT_REACHED(); - ret.empend(table, pointer, buffer_or_error.value()); + ret.append(m_serializer.deserialize_block<Row>(pointer, table, pointer)); } return ret; } @@ -134,10 +132,7 @@ Vector<Row> Database::match(TableDef const& table, Key const& key) // TODO Match key against indexes defined on table. If found, // use the index instead of scanning the table. for (auto pointer = table.pointer(); pointer;) { - auto buffer_or_error = m_heap->read_block(pointer); - if (buffer_or_error.is_error()) - VERIFY_NOT_REACHED(); - Row row(table, pointer, buffer_or_error.value()); + auto row = m_serializer.deserialize_block<Row>(pointer, table, pointer); if (row.match(key)) ret.append(row); pointer = ret.last().next_pointer(); @@ -164,9 +159,8 @@ bool Database::insert(Row& row) bool Database::update(Row& tuple) { VERIFY(m_table_cache.get(tuple.table()->key().hash()).has_value()); - ByteBuffer buffer; - tuple.serialize(buffer); - m_heap->add_to_wal(tuple.pointer(), buffer); + m_serializer.reset(); + return m_serializer.serialize_and_write<Tuple>(tuple, tuple.pointer()); // TODO update indexes defined on table. return true; diff --git a/Userland/Libraries/LibSQL/Database.h b/Userland/Libraries/LibSQL/Database.h index 5603dcc2e1..b3bfc0d3af 100644 --- a/Userland/Libraries/LibSQL/Database.h +++ b/Userland/Libraries/LibSQL/Database.h @@ -11,6 +11,7 @@ #include <LibCore/Object.h> #include <LibSQL/Forward.h> #include <LibSQL/Heap.h> +#include <LibSQL/Meta.h> namespace SQL { @@ -42,7 +43,8 @@ public: bool update(Row&); private: - RefPtr<Heap> m_heap; + NonnullRefPtr<Heap> m_heap; + Serializer m_serializer; RefPtr<BTree> m_schemas; RefPtr<BTree> m_tables; RefPtr<BTree> m_table_columns; diff --git a/Userland/Libraries/LibSQL/Forward.h b/Userland/Libraries/LibSQL/Forward.h index 1ef5e733be..273b813b9a 100644 --- a/Userland/Libraries/LibSQL/Forward.h +++ b/Userland/Libraries/LibSQL/Forward.h @@ -21,13 +21,16 @@ class IndexNode; class IndexDef; class Key; class KeyPartDef; +class Relation; class Row; +class SchemaDef; class SQLResult; +class Serializer; class TableDef; class TreeNode; class Tuple; class TupleDescriptor; -struct TupleElement; +struct TupleElementDescriptor; class Value; } diff --git a/Userland/Libraries/LibSQL/HashIndex.cpp b/Userland/Libraries/LibSQL/HashIndex.cpp index b437daa971..2642ada69a 100644 --- a/Userland/Libraries/LibSQL/HashIndex.cpp +++ b/Userland/Libraries/LibSQL/HashIndex.cpp @@ -7,7 +7,7 @@ #include <LibSQL/HashIndex.h> #include <LibSQL/Heap.h> #include <LibSQL/Key.h> -#include <LibSQL/Serialize.h> +#include <LibSQL/Serializer.h> namespace SQL { @@ -19,18 +19,19 @@ HashDirectoryNode::HashDirectoryNode(HashIndex& index, u32 node_number, size_t o { } -HashDirectoryNode::HashDirectoryNode(HashIndex& index, u32 pointer, ByteBuffer& buffer) +HashDirectoryNode::HashDirectoryNode(HashIndex& index, u32 pointer) : IndexNode(pointer) , m_hash_index(index) { +} + +void HashDirectoryNode::deserialize(Serializer& serializer) +{ dbgln_if(SQL_DEBUG, "Deserializing Hash Directory Node"); - size_t offset = 0; - deserialize_from<u32>(buffer, offset, index.m_global_depth); - u32 size; - deserialize_from<u32>(buffer, offset, size); - dbgln_if(SQL_DEBUG, "Global Depth {}, #Bucket pointers {}", index.global_depth(), size); - u32 next_node; - deserialize_from<u32>(buffer, offset, next_node); + m_hash_index.m_global_depth = serializer.deserialize<u32>(); + auto size = serializer.deserialize<u32>(); + dbgln_if(SQL_DEBUG, "Global Depth {}, #Bucket pointers {}", m_hash_index.global_depth(), size); + auto next_node = serializer.deserialize<u32>(); if (next_node) { dbgln_if(SQL_DEBUG, "Next node {}", next_node); m_hash_index.m_nodes.append(next_node); @@ -39,20 +40,18 @@ HashDirectoryNode::HashDirectoryNode(HashIndex& index, u32 pointer, ByteBuffer& m_is_last = true; } for (auto ix = 0u; ix < size; ix++) { - u32 bucket_pointer; - deserialize_from(buffer, offset, bucket_pointer); - u32 local_depth; - deserialize_from(buffer, offset, local_depth); - dbgln_if(SQL_DEBUG, "Bucket pointer {} local depth {}", bucket_pointer, local_depth); - index.append_bucket(ix, local_depth, bucket_pointer); + auto bucket_pointer = serializer.deserialize<u32>(); + auto local_depth = serializer.deserialize<u32>(); + dbgln_if(SQL_DEBUG, "--Index {} bucket pointer {} local depth {}", ix, bucket_pointer, local_depth); + m_hash_index.append_bucket(ix, local_depth, bucket_pointer); } } -void HashDirectoryNode::serialize(ByteBuffer& buffer) const +void HashDirectoryNode::serialize(Serializer& serializer) const { dbgln_if(SQL_DEBUG, "Serializing directory node #{}. Offset {}", m_node_number, m_offset); - serialize_to(buffer, m_hash_index.global_depth()); - serialize_to(buffer, number_of_pointers()); + serializer.serialize<u32>((u32)m_hash_index.global_depth()); + serializer.serialize<u32>(number_of_pointers()); dbgln_if(SQL_DEBUG, "Global depth {}, #bucket pointers {}", m_hash_index.global_depth(), number_of_pointers()); u32 next_node; @@ -64,12 +63,12 @@ void HashDirectoryNode::serialize(ByteBuffer& buffer) const dbgln_if(SQL_DEBUG, "This is the last directory node"); } - serialize_to(buffer, next_node); + serializer.serialize<u32>(next_node); for (auto ix = 0u; ix < number_of_pointers(); ix++) { auto& bucket = m_hash_index.m_buckets[m_offset + ix]; - dbgln_if(SQL_DEBUG, "Bucket pointer {} local depth {}", bucket->pointer(), bucket->local_depth()); - serialize_to(buffer, bucket->pointer()); - serialize_to(buffer, bucket->local_depth()); + dbgln_if(SQL_DEBUG, "Bucket index #{} pointer {} local depth {} size {}", ix, bucket->pointer(), bucket->local_depth(), bucket->size()); + serializer.serialize<u32>(bucket->pointer()); + serializer.serialize<u32>(bucket->local_depth()); } } @@ -81,44 +80,41 @@ HashBucket::HashBucket(HashIndex& hash_index, u32 index, u32 local_depth, u32 po { } -void HashBucket::serialize(ByteBuffer& buffer) const +void HashBucket::serialize(Serializer& serializer) const { dbgln_if(SQL_DEBUG, "Serializing bucket: pointer {}, index #{}, local depth {} size {}", pointer(), index(), local_depth(), size()); - dbgln_if(SQL_DEBUG, "key_length: {} max_entries: {}", m_hash_index.descriptor()->data_length(), max_entries_in_bucket()); - serialize_to(buffer, local_depth()); - serialize_to(buffer, size()); - dbgln_if(SQL_DEBUG, "buffer size after prolog {}", buffer.size()); + serializer.serialize<u32>(local_depth()); + serializer.serialize<u32>(size()); for (auto& key : m_entries) { - key.serialize(buffer); - dbgln_if(SQL_DEBUG, "Key {} buffer size {}", key.to_string(), buffer.size()); + serializer.serialize<Key>(key); } } -void HashBucket::inflate() +void HashBucket::deserialize(Serializer& serializer) { if (m_inflated || !pointer()) return; dbgln_if(SQL_DEBUG, "Inflating Hash Bucket {}", pointer()); - auto buffer = m_hash_index.read_block(pointer()); - size_t offset = 0; - deserialize_from(buffer, offset, m_local_depth); + m_local_depth = serializer.deserialize<u32>(); dbgln_if(SQL_DEBUG, "Bucket Local Depth {}", m_local_depth); - u32 size; - deserialize_from(buffer, offset, size); + auto size = serializer.deserialize<u32>(); dbgln_if(SQL_DEBUG, "Bucket has {} keys", size); for (auto ix = 0u; ix < size; ix++) { - Key key(m_hash_index.descriptor(), buffer, offset); + auto key = serializer.deserialize<Key>(m_hash_index.descriptor()); dbgln_if(SQL_DEBUG, "Key {}: {}", ix, key.to_string()); m_entries.append(key); } m_inflated = true; } -size_t HashBucket::max_entries_in_bucket() const +size_t HashBucket::length() const { - auto key_size = m_hash_index.descriptor()->data_length() + sizeof(u32); - return (BLOCKSIZE - 2 * sizeof(u32)) / key_size; + size_t len = 2 * sizeof(u32); + for (auto& key : m_entries) { + len += key.length(); + } + return len; } Optional<u32> HashBucket::get(Key& key) @@ -134,15 +130,17 @@ Optional<u32> HashBucket::get(Key& key) bool HashBucket::insert(Key const& key) { - inflate(); + if (!m_inflated) + m_hash_index.serializer().deserialize_block_to(pointer(), *this); if (find_key_in_bucket(key).has_value()) { return false; } - if (size() >= max_entries_in_bucket()) { + if ((length() + key.length()) > BLOCKSIZE) { + dbgln_if(SQL_DEBUG, "Adding key {} would make length exceed block size", key.to_string()); return false; } m_entries.append(key); - m_hash_index.add_to_write_ahead_log(this); + m_hash_index.serializer().serialize_and_write(*this, pointer()); return true; } @@ -161,7 +159,7 @@ HashBucket const* HashBucket::next_bucket() { for (auto ix = m_index + 1; ix < m_hash_index.size(); ix++) { auto bucket = m_hash_index.get_bucket_by_index(ix); - bucket->inflate(); + m_hash_index.serializer().deserialize_block_to<HashBucket>(bucket->pointer(), *bucket); if (bucket->size()) return bucket; } @@ -178,13 +176,27 @@ HashBucket const* HashBucket::previous_bucket() return nullptr; } +Vector<Key> const& HashBucket::entries() +{ + if (!m_inflated) + m_hash_index.serializer().deserialize_block_to(pointer(), *this); + return m_entries; +} + Key const& HashBucket::operator[](size_t ix) { - inflate(); + if (!m_inflated) + m_hash_index.serializer().deserialize_block_to(pointer(), *this); VERIFY(ix < size()); return m_entries[ix]; } +Key const& HashBucket::operator[](size_t ix) const +{ + VERIFY(ix < m_entries.size()); + return m_entries[ix]; +} + void HashBucket::list_bucket() { warnln("Bucket #{} size {} local depth {} pointer {}{}", @@ -194,20 +206,19 @@ void HashBucket::list_bucket() } } -HashIndex::HashIndex(Heap& heap, NonnullRefPtr<TupleDescriptor> const& descriptor, u32 first_node) - : Index(heap, descriptor, true, first_node) +HashIndex::HashIndex(Serializer& serializer, NonnullRefPtr<TupleDescriptor> const& descriptor, u32 first_node) + : Index(serializer, descriptor, true, first_node) , m_nodes() , m_buckets() { if (!first_node) { set_pointer(new_record_pointer()); } - if (this->heap().has_block(first_node)) { + if (serializer.has_block(first_node)) { u32 pointer = first_node; do { - VERIFY(this->heap().has_block(pointer)); - auto buffer = read_block(pointer); - auto node = HashDirectoryNode(*this, pointer, buffer); + VERIFY(serializer.has_block(pointer)); + auto node = serializer.deserialize_block<HashDirectoryNode>(pointer, *this, pointer); if (node.is_last()) break; pointer = m_nodes.last(); // FIXME Ugly @@ -215,10 +226,10 @@ HashIndex::HashIndex(Heap& heap, NonnullRefPtr<TupleDescriptor> const& descripto } else { auto bucket = append_bucket(0u, 1u, new_record_pointer()); bucket->m_inflated = true; - add_to_write_ahead_log(bucket); + serializer.serialize_and_write(*bucket, bucket->pointer()); bucket = append_bucket(1u, 1u, new_record_pointer()); bucket->m_inflated = true; - add_to_write_ahead_log(bucket); + serializer.serialize_and_write(*bucket, bucket->pointer()); m_nodes.append(first_node); write_directory_to_write_ahead_log(); } @@ -242,10 +253,12 @@ HashBucket* HashIndex::get_bucket_for_insert(Key const& key) auto key_hash = key.hash(); do { + dbgln_if(SQL_DEBUG, "HashIndex::get_bucket_for_insert({}) bucket {} of {}", key.to_string(), key_hash % size(), size()); auto bucket = get_bucket(key_hash % size()); - if (bucket->size() < bucket->max_entries_in_bucket()) { + if (bucket->length() + key.length() < BLOCKSIZE) { return bucket; } + dbgln_if(SQL_DEBUG, "Bucket is full (bucket size {}/length {} key length {}). Expanding directory", bucket->size(), bucket->length(), key.length()); // We previously doubled the directory but the target bucket is // still at an older depth. Create new buckets at the current global @@ -254,36 +267,47 @@ HashBucket* HashIndex::get_bucket_for_insert(Key const& key) while (bucket->local_depth() < global_depth()) { auto base_index = bucket->index(); auto step = 1 << (global_depth() - bucket->local_depth()); + auto total_moved = 0; for (auto ix = base_index + step; ix < size(); ix += step) { auto& sub_bucket = m_buckets[ix]; sub_bucket->set_local_depth(bucket->local_depth() + 1); + auto moved = 0; for (auto entry_index = (int)bucket->m_entries.size() - 1; entry_index >= 0; entry_index--) { if (bucket->m_entries[entry_index].hash() % size() == ix) { if (!sub_bucket->pointer()) { sub_bucket->set_pointer(new_record_pointer()); } sub_bucket->insert(bucket->m_entries.take(entry_index)); + moved++; } } - if (m_buckets[ix]->pointer()) - add_to_write_ahead_log(m_buckets[ix]); + if (moved > 0) { + dbgln_if(SQL_DEBUG, "Moved {} entries from bucket #{} to #{}", moved, base_index, ix); + serializer().serialize_and_write(*sub_bucket, sub_bucket->pointer()); + } + total_moved += moved; } + if (total_moved) + dbgln_if(SQL_DEBUG, "Redistributed {} entries from bucket #{}", total_moved, base_index); + else + dbgln_if(SQL_DEBUG, "Nothing redistributed from bucket #{}", base_index); bucket->set_local_depth(bucket->local_depth() + 1); - add_to_write_ahead_log(bucket); + serializer().serialize_and_write(*bucket, bucket->pointer()); write_directory_to_write_ahead_log(); auto bucket_after_redistribution = get_bucket(key_hash % size()); - if (bucket_after_redistribution->size() < bucket_after_redistribution->max_entries_in_bucket()) { + if (bucket_after_redistribution->length() + key.length() < BLOCKSIZE) return bucket_after_redistribution; - } } expand(); } while (true); + VERIFY_NOT_REACHED(); } void HashIndex::expand() { auto sz = size(); + dbgln_if(SQL_DEBUG, "Expanding directory from {} to {} buckets", sz, 2 * sz); for (auto i = 0u; i < sz; i++) { auto bucket = get_bucket(i); bucket = append_bucket(sz + i, bucket->local_depth(), 0u); @@ -303,7 +327,7 @@ void HashIndex::write_directory_to_write_ahead_log() size_t num_node = 0u; while (offset < size()) { HashDirectoryNode node(*this, num_node, offset); - add_to_write_ahead_log(node.as_index_node()); + serializer().serialize_and_write(node, node.pointer()); offset += node.number_of_pointers(); } } @@ -325,14 +349,20 @@ Optional<u32> HashIndex::get(Key& key) { auto hash = key.hash(); auto bucket_index = hash % size(); + dbgln_if(SQL_DEBUG, "HashIndex::get({}) bucket_index {}", key.to_string(), bucket_index); auto bucket = get_bucket(bucket_index); + if constexpr (SQL_DEBUG) + bucket->list_bucket(); return bucket->get(key); } bool HashIndex::insert(Key const& key) { + dbgln_if(SQL_DEBUG, "HashIndex::insert({})", key.to_string()); auto bucket = get_bucket_for_insert(key); bucket->insert(key); + if constexpr (SQL_DEBUG) + bucket->list_bucket(); return true; } @@ -369,7 +399,6 @@ void HashIndex::list_hash() bool first_bucket = true; for (auto& bucket : m_buckets) { if (first_bucket) { - warnln("Max. keys in bucket {}", bucket->max_entries_in_bucket()); first_bucket = false; } bucket->list_bucket(); diff --git a/Userland/Libraries/LibSQL/HashIndex.h b/Userland/Libraries/LibSQL/HashIndex.h index 4c56706ee8..b7f55198e8 100644 --- a/Userland/Libraries/LibSQL/HashIndex.h +++ b/Userland/Libraries/LibSQL/HashIndex.h @@ -28,23 +28,16 @@ public: ~HashBucket() override = default; Optional<u32> get(Key&); bool insert(Key const&); - Vector<Key> const& entries() - { - inflate(); - return m_entries; - } + Vector<Key> const& entries(); Key const& operator[](size_t); - Key const& operator[](size_t ix) const - { - VERIFY(ix < m_entries.size()); - return m_entries[ix]; - } + Key const& operator[](size_t ix) const; [[nodiscard]] u32 local_depth() const { return m_local_depth; } [[nodiscard]] u32 size() { return entries().size(); } + [[nodiscard]] size_t length() const; [[nodiscard]] u32 size() const { return m_entries.size(); } [[nodiscard]] u32 index() const { return m_index; } - void serialize(ByteBuffer&) const override; - IndexNode* as_index_node() override { return dynamic_cast<IndexNode*>(this); } + void serialize(Serializer&) const; + void deserialize(Serializer&); [[nodiscard]] HashIndex const& hash_index() const { return m_hash_index; } [[nodiscard]] HashBucket const* next_bucket(); [[nodiscard]] HashBucket const* previous_bucket(); @@ -54,8 +47,6 @@ private: Optional<size_t> find_key_in_bucket(Key const&); void set_index(u32 index) { m_index = index; } void set_local_depth(u32 depth) { m_local_depth = depth; } - [[nodiscard]] size_t max_entries_in_bucket() const; - void inflate(); HashIndex& m_hash_index; u32 m_local_depth { 1 }; @@ -88,7 +79,7 @@ public: void list_hash(); private: - HashIndex(Heap&, NonnullRefPtr<TupleDescriptor> const&, u32); + HashIndex(Serializer&, NonnullRefPtr<TupleDescriptor> const&, u32); void expand(); void write_directory_to_write_ahead_log(); @@ -107,10 +98,10 @@ private: class HashDirectoryNode : public IndexNode { public: HashDirectoryNode(HashIndex&, u32, size_t); - HashDirectoryNode(HashIndex&, u32, ByteBuffer&); + HashDirectoryNode(HashIndex&, u32); HashDirectoryNode(HashDirectoryNode const& other) = default; - void serialize(ByteBuffer&) const override; - IndexNode* as_index_node() override { return dynamic_cast<IndexNode*>(this); } + void deserialize(Serializer&); + void serialize(Serializer&) const; [[nodiscard]] u32 number_of_pointers() const { return min(max_pointers_in_node(), m_hash_index.size() - m_offset); } [[nodiscard]] bool is_last() const { return m_is_last; } static constexpr size_t max_pointers_in_node() { return (BLOCKSIZE - 3 * sizeof(u32)) / (2 * sizeof(u32)); } diff --git a/Userland/Libraries/LibSQL/Heap.cpp b/Userland/Libraries/LibSQL/Heap.cpp index cab2e89611..9b76899ff6 100644 --- a/Userland/Libraries/LibSQL/Heap.cpp +++ b/Userland/Libraries/LibSQL/Heap.cpp @@ -9,7 +9,7 @@ #include <AK/String.h> #include <LibCore/IODevice.h> #include <LibSQL/Heap.h> -#include <LibSQL/Serialize.h> +#include <LibSQL/Serializer.h> #include <sys/stat.h> #include <sys/types.h> @@ -41,6 +41,7 @@ Heap::Heap(String file_name) read_zero_block(); else initialize_zero_block(); + dbgln_if(SQL_DEBUG, "Heap file {} opened. Size = {}", file_name, size()); } Result<ByteBuffer, String> Heap::read_block(u32 block) @@ -56,12 +57,18 @@ Result<ByteBuffer, String> Heap::read_block(u32 block) auto ret = m_file->read(BLOCKSIZE); if (ret.is_empty()) return String("Could not read block"); + dbgln_if(SQL_DEBUG, "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}", + *ret.offset_pointer(0), *ret.offset_pointer(1), + *ret.offset_pointer(2), *ret.offset_pointer(3), + *ret.offset_pointer(4), *ret.offset_pointer(5), + *ret.offset_pointer(6), *ret.offset_pointer(7)); return ret; } bool Heap::write_block(u32 block, ByteBuffer& buffer) { - VERIFY(block < m_next_block); + dbgln_if(SQL_DEBUG, "write_block({}): m_next_block {}", block, m_next_block); + VERIFY(block <= m_next_block); if (!seek_block(block)) VERIFY_NOT_REACHED(); dbgln_if(SQL_DEBUG, "Write heap block {} size {}", block, buffer.size()); @@ -71,6 +78,11 @@ bool Heap::write_block(u32 block, ByteBuffer& buffer) buffer.resize(BLOCKSIZE); memset(buffer.offset_pointer((int)sz), 0, BLOCKSIZE - sz); } + dbgln_if(SQL_DEBUG, "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}", + *buffer.offset_pointer(0), *buffer.offset_pointer(1), + *buffer.offset_pointer(2), *buffer.offset_pointer(3), + *buffer.offset_pointer(4), *buffer.offset_pointer(5), + *buffer.offset_pointer(6), *buffer.offset_pointer(7)); if (m_file->write(buffer.data(), (int)buffer.size())) { if (block == m_end_of_file) m_end_of_file++; @@ -110,8 +122,7 @@ u32 Heap::new_record_pointer() VERIFY_NOT_REACHED(); } auto new_pointer = m_free_list; - size_t offset = 0; - deserialize_from<u32>(block_or_error.value(), offset, m_free_list); + memcpy(&m_free_list, block_or_error.value().offset_pointer(0), sizeof(u32)); update_zero_block(); return new_pointer; } @@ -134,6 +145,7 @@ void Heap::flush() write_block(block, buffer_or_empty.value()); } m_write_ahead_log.clear(); + dbgln_if(SQL_DEBUG, "WAL flushed. Heap size = {}", size()); } constexpr static const char* FILE_ID = "SerenitySQL "; diff --git a/Userland/Libraries/LibSQL/Heap.h b/Userland/Libraries/LibSQL/Heap.h index 9375106f8f..4f83928795 100644 --- a/Userland/Libraries/LibSQL/Heap.h +++ b/Userland/Libraries/LibSQL/Heap.h @@ -12,8 +12,6 @@ #include <AK/Vector.h> #include <LibCore/File.h> #include <LibCore/Object.h> -#include <LibSQL/Meta.h> -#include <LibSQL/Serialize.h> namespace SQL { @@ -81,7 +79,17 @@ public: update_zero_block(); } - void add_to_wal(u32 block, ByteBuffer& buffer) { m_write_ahead_log.set(block, buffer); } + void add_to_wal(u32 block, ByteBuffer& buffer) + { + dbgln_if(SQL_DEBUG, "Adding to WAL: block #{}, size {}", block, buffer.size()); + dbgln_if(SQL_DEBUG, "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}", + *buffer.offset_pointer(0), *buffer.offset_pointer(1), + *buffer.offset_pointer(2), *buffer.offset_pointer(3), + *buffer.offset_pointer(4), *buffer.offset_pointer(5), + *buffer.offset_pointer(6), *buffer.offset_pointer(7)); + m_write_ahead_log.set(block, buffer); + } + void flush(); private: diff --git a/Userland/Libraries/LibSQL/Index.cpp b/Userland/Libraries/LibSQL/Index.cpp index ecd14d5b25..264eadc897 100644 --- a/Userland/Libraries/LibSQL/Index.cpp +++ b/Userland/Libraries/LibSQL/Index.cpp @@ -10,37 +10,19 @@ namespace SQL { -Index::Index(Heap& heap, NonnullRefPtr<TupleDescriptor> const& descriptor, bool unique, u32 pointer) - : m_heap(heap) +Index::Index(Serializer& serializer, NonnullRefPtr<TupleDescriptor> const& descriptor, bool unique, u32 pointer) + : m_serializer(serializer) , m_descriptor(descriptor) , m_unique(unique) , m_pointer(pointer) { } -Index::Index(Heap& heap, NonnullRefPtr<TupleDescriptor> const& descriptor, u32 pointer) - : m_heap(heap) +Index::Index(Serializer& serializer, NonnullRefPtr<TupleDescriptor> const& descriptor, u32 pointer) + : m_serializer(serializer) , m_descriptor(descriptor) , m_pointer(pointer) { } -ByteBuffer Index::read_block(u32 block) -{ - auto ret = m_heap.read_block(block); - if (ret.is_error()) { - warnln("Error reading block {}: {}", block, ret.error()); - VERIFY_NOT_REACHED(); - } - return ret.value(); -} - -void Index::add_to_write_ahead_log(IndexNode* node) -{ - VERIFY(node->pointer()); - ByteBuffer buffer; - node->serialize(buffer); - m_heap.add_to_wal(node->pointer(), buffer); -} - } diff --git a/Userland/Libraries/LibSQL/Index.h b/Userland/Libraries/LibSQL/Index.h index 4805737f3c..fad9cfd245 100644 --- a/Userland/Libraries/LibSQL/Index.h +++ b/Userland/Libraries/LibSQL/Index.h @@ -16,8 +16,7 @@ class IndexNode { public: virtual ~IndexNode() = default; [[nodiscard]] u32 pointer() const { return m_pointer; } - virtual void serialize(ByteBuffer&) const = 0; - virtual IndexNode* as_index_node() = 0; + IndexNode* as_index_node() { return dynamic_cast<IndexNode*>(this); } protected: explicit IndexNode(u32 pointer) @@ -43,18 +42,16 @@ public: [[nodiscard]] u32 pointer() const { return m_pointer; } protected: - Index(Heap& heap, NonnullRefPtr<TupleDescriptor> const&, bool unique, u32 pointer); - Index(Heap& heap, NonnullRefPtr<TupleDescriptor> const&, u32 pointer); + Index(Serializer&, NonnullRefPtr<TupleDescriptor> const&, bool unique, u32 pointer); + Index(Serializer&, NonnullRefPtr<TupleDescriptor> const&, u32 pointer); - [[nodiscard]] Heap const& heap() const { return m_heap; } - [[nodiscard]] Heap& heap() { return m_heap; } + [[nodiscard]] Serializer& serializer() { return m_serializer; } void set_pointer(u32 pointer) { m_pointer = pointer; } - u32 new_record_pointer() { return m_heap.new_record_pointer(); } - ByteBuffer read_block(u32); - void add_to_write_ahead_log(IndexNode*); + u32 new_record_pointer() { return m_serializer.new_record_pointer(); } + // ByteBuffer read_block(u32); private: - Heap& m_heap; + Serializer m_serializer; NonnullRefPtr<TupleDescriptor> m_descriptor; bool m_unique { false }; u32 m_pointer { 0 }; diff --git a/Userland/Libraries/LibSQL/Key.cpp b/Userland/Libraries/LibSQL/Key.cpp index 206462cf19..a31c040403 100644 --- a/Userland/Libraries/LibSQL/Key.cpp +++ b/Userland/Libraries/LibSQL/Key.cpp @@ -9,11 +9,6 @@ namespace SQL { -Key::Key() - : Tuple() -{ -} - Key::Key(NonnullRefPtr<TupleDescriptor> const& descriptor) : Tuple(descriptor) { @@ -25,15 +20,15 @@ Key::Key(NonnullRefPtr<IndexDef> index) { } -Key::Key(NonnullRefPtr<TupleDescriptor> const& descriptor, ByteBuffer& buffer, size_t& offset) - : Tuple(descriptor, buffer, offset) +Key::Key(NonnullRefPtr<TupleDescriptor> const& descriptor, Serializer& serializer) + : Tuple(descriptor, serializer) { } -Key::Key(RefPtr<IndexDef> index, ByteBuffer& buffer, size_t& offset) +Key::Key(RefPtr<IndexDef> index, Serializer& serializer) : Key(index->to_tuple_descriptor()) { - deserialize(buffer, offset); + Tuple::deserialize(serializer); } } diff --git a/Userland/Libraries/LibSQL/Key.h b/Userland/Libraries/LibSQL/Key.h index edc12a40b7..e05b017631 100644 --- a/Userland/Libraries/LibSQL/Key.h +++ b/Userland/Libraries/LibSQL/Key.h @@ -14,14 +14,12 @@ namespace SQL { class Key : public Tuple { public: - Key(); + Key() = default; explicit Key(NonnullRefPtr<TupleDescriptor> const&); explicit Key(NonnullRefPtr<IndexDef>); - Key(NonnullRefPtr<TupleDescriptor> const&, ByteBuffer&, size_t& offset); - Key(RefPtr<IndexDef>, ByteBuffer&, size_t& offset); - Key(Key const&) = default; + Key(NonnullRefPtr<TupleDescriptor> const&, Serializer&); + Key(RefPtr<IndexDef>, Serializer&); RefPtr<IndexDef> index() const { return m_index; } - [[nodiscard]] virtual size_t data_length() const override { return Tuple::data_length() + sizeof(u32); } private: RefPtr<IndexDef> m_index { nullptr }; diff --git a/Userland/Libraries/LibSQL/Row.cpp b/Userland/Libraries/LibSQL/Row.cpp index 9a5c94cc95..bf3b549513 100644 --- a/Userland/Libraries/LibSQL/Row.cpp +++ b/Userland/Libraries/LibSQL/Row.cpp @@ -6,7 +6,7 @@ #include <LibSQL/Meta.h> #include <LibSQL/Row.h> -#include <LibSQL/Serialize.h> +#include <LibSQL/Serializer.h> #include <LibSQL/Tuple.h> namespace SQL { @@ -21,26 +21,29 @@ Row::Row(TupleDescriptor const& descriptor) { } -Row::Row(RefPtr<TableDef> table) +Row::Row(RefPtr<TableDef> table, u32 pointer) : Tuple(table->to_tuple_descriptor()) , m_table(table) { + set_pointer(pointer); } -Row::Row(RefPtr<TableDef> table, u32 pointer, ByteBuffer& buffer) - : Tuple(table->to_tuple_descriptor()) +Row::Row(RefPtr<TableDef> table, u32 pointer, Serializer& serializer) + : Row(move(table), pointer) { - // FIXME Sanitize constructor situation in Tuple so this can be better - size_t offset = 0; - deserialize(buffer, offset); - deserialize_from<u32>(buffer, offset, m_next_pointer); - set_pointer(pointer); + Row::deserialize(serializer); +} + +void Row::deserialize(Serializer& serializer) +{ + Tuple::deserialize(serializer); + m_next_pointer = serializer.deserialize<u32>(); } -void Row::serialize(ByteBuffer& buffer) const +void Row::serialize(Serializer& serializer) const { - Tuple::serialize(buffer); - serialize_to<u32>(buffer, next_pointer()); + Tuple::serialize(serializer); + serializer.serialize<u32>(next_pointer()); } void Row::copy_from(Row const& other) diff --git a/Userland/Libraries/LibSQL/Row.h b/Userland/Libraries/LibSQL/Row.h index 27c98339f0..25c6a8cab7 100644 --- a/Userland/Libraries/LibSQL/Row.h +++ b/Userland/Libraries/LibSQL/Row.h @@ -9,6 +9,7 @@ #include <AK/ByteBuffer.h> #include <AK/RefPtr.h> #include <LibSQL/Forward.h> +#include <LibSQL/Meta.h> #include <LibSQL/Value.h> namespace SQL { @@ -26,16 +27,17 @@ class Row : public Tuple { public: Row(); explicit Row(TupleDescriptor const&); - explicit Row(RefPtr<TableDef>); - Row(RefPtr<TableDef>, u32, ByteBuffer&); + explicit Row(RefPtr<TableDef>, u32 pointer = 0); + Row(RefPtr<TableDef>, u32, Serializer&); Row(Row const&) = default; virtual ~Row() override = default; [[nodiscard]] u32 next_pointer() const { return m_next_pointer; } void next_pointer(u32 ptr) { m_next_pointer = ptr; } RefPtr<TableDef> table() const { return m_table; } - virtual void serialize(ByteBuffer&) const override; - [[nodiscard]] virtual size_t data_length() const override { return Tuple::data_length() + sizeof(u32); } + [[nodiscard]] virtual size_t length() const override { return Tuple::length() + sizeof(u32); } + virtual void serialize(Serializer&) const override; + virtual void deserialize(Serializer&) override; protected: void copy_from(Row const&); diff --git a/Userland/Libraries/LibSQL/Serialize.h b/Userland/Libraries/LibSQL/Serialize.h deleted file mode 100644 index bc03590058..0000000000 --- a/Userland/Libraries/LibSQL/Serialize.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2021, Jan de Visser <jan@de-visser.net> - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include <AK/ByteBuffer.h> -#include <AK/Debug.h> -#include <AK/Format.h> -#include <AK/String.h> -#include <string.h> - -namespace SQL { - -inline void dump(u8 const* ptr, size_t sz, String const& prefix) -{ - StringBuilder builder; - builder.appendff("{0} {1:04x} | ", prefix, sz); - Vector<String> 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<typename T> -void deserialize_from(ByteBuffer& buffer, size_t& at_offset, T& t) -{ - memcpy(&t, read(buffer, at_offset, sizeof(T)), sizeof(T)); -} - -template<typename T> -void serialize_to(ByteBuffer& buffer, T const& t) -{ - write(buffer, (u8 const*)(&t), sizeof(T)); -} - -template<> -inline void deserialize_from<String>(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<String>(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/Serializer.cpp b/Userland/Libraries/LibSQL/Serializer.cpp new file mode 100644 index 0000000000..e4d9e59449 --- /dev/null +++ b/Userland/Libraries/LibSQL/Serializer.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, Jan de Visser <jan@de-visser.net> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibSQL/Serializer.h> + +namespace SQL { + +void Serializer::serialize(String const& text) +{ + serialize<u32>((u32)text.length()); + if (!text.is_empty()) + write((u8 const*)text.characters(), text.length()); +} + +void Serializer::deserialize_to(String& text) +{ + auto length = deserialize<u32>(); + if (length > 0) { + text = String(reinterpret_cast<char const*>(read(length)), length); + } else { + text = ""; + } +} + +} diff --git a/Userland/Libraries/LibSQL/Serializer.h b/Userland/Libraries/LibSQL/Serializer.h new file mode 100644 index 0000000000..65e8841e93 --- /dev/null +++ b/Userland/Libraries/LibSQL/Serializer.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2021, Jan de Visser <jan@de-visser.net> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/ByteBuffer.h> +#include <AK/Debug.h> +#include <AK/Format.h> +#include <AK/ScopeGuard.h> +#include <AK/String.h> +#include <LibSQL/Forward.h> +#include <LibSQL/Heap.h> +#include <string.h> + +namespace SQL { + +class Serializer { +public: + Serializer() = default; + + Serializer(RefPtr<Heap> heap) + : m_heap(heap) + { + } + + void get_block(u32 pointer) + { + VERIFY(m_heap.ptr() != nullptr); + auto buffer_or_error = m_heap->read_block(pointer); + if (buffer_or_error.is_error()) + VERIFY_NOT_REACHED(); + m_buffer = buffer_or_error.value(); + m_current_offset = 0; + } + + void reset() + { + m_buffer.clear(); + m_current_offset = 0; + } + + void rewind() + { + m_current_offset = 0; + } + + template<typename T, typename... Args> + T deserialize_block(u32 pointer, Args&&... args) + { + get_block(pointer); + return deserialize<T>(forward<Args>(args)...); + } + + template<typename T> + void deserialize_block_to(u32 pointer, T& t) + { + get_block(pointer); + return deserialize_to<T>(t); + } + + template<typename T> + void deserialize_to(T& t) + { + if constexpr (IsArithmetic<T>) + memcpy(&t, read(sizeof(T)), sizeof(T)); + else + t.deserialize(*this); + } + + void deserialize_to(String& text); + + template<typename T, typename... Args> + NonnullOwnPtr<T> make_and_deserialize(Args&&... args) + { + auto ptr = make<T>(forward<Args>(args)...); + ptr->deserialize(*this); + return ptr; + } + + template<typename T, typename... Args> + NonnullRefPtr<T> adopt_and_deserialize(Args&&... args) + { + auto ptr = adopt_ref(*new T(forward<Args>(args)...)); + ptr->deserialize(*this); + return ptr; + } + + template<typename T, typename... Args> + T deserialize(Args&&... args) + { + T t(forward<Args>(args)...); + deserialize_to(t); + return t; + } + + template<typename T> + void serialize(T const& t) + { + if constexpr (IsArithmetic<T>) + write((u8 const*)(&t), sizeof(T)); + else + t.serialize(*this); + } + + void serialize(String const&); + + template<typename T> + bool serialize_and_write(T const& t, u32 pointer) + { + VERIFY(m_heap.ptr() != nullptr); + reset(); + serialize<T>(t); + m_heap->add_to_wal(pointer, m_buffer); + return true; + } + + [[nodiscard]] size_t offset() const { return m_current_offset; } + u32 new_record_pointer() + { + VERIFY(m_heap.ptr() != nullptr); + return m_heap->new_record_pointer(); + } + + bool has_block(u32 pointer) const + { + VERIFY(m_heap.ptr() != nullptr); + return pointer < m_heap->size(); + } + + Heap& heap() + { + VERIFY(m_heap.ptr() != nullptr); + return *(m_heap.ptr()); + } + +private: + void write(u8 const* ptr, size_t sz) + { + if constexpr (SQL_DEBUG) + dump(ptr, sz, "(out) =>"); + m_buffer.append(ptr, sz); + m_current_offset += sz; + } + + u8 const* read(size_t sz) + { + auto buffer_ptr = m_buffer.offset_pointer((int)m_current_offset); + if constexpr (SQL_DEBUG) + dump(buffer_ptr, sz, "<= (in)"); + m_current_offset += sz; + return buffer_ptr; + } + + static void dump(u8 const* ptr, size_t sz, String const& prefix) + { + StringBuilder builder; + builder.appendff("{0} {1:04x} | ", prefix, sz); + Vector<String> 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()); + } + + ByteBuffer m_buffer {}; + size_t m_current_offset { 0 }; + RefPtr<Heap> m_heap { nullptr }; +}; + +} diff --git a/Userland/Libraries/LibSQL/TreeNode.cpp b/Userland/Libraries/LibSQL/TreeNode.cpp index 67cc8b779c..6ae7a1f5fb 100644 --- a/Userland/Libraries/LibSQL/TreeNode.cpp +++ b/Userland/Libraries/LibSQL/TreeNode.cpp @@ -9,7 +9,7 @@ #include <AK/NonnullOwnPtr.h> #include <AK/StringBuilder.h> #include <LibSQL/BTree.h> -#include <LibSQL/Serialize.h> +#include <LibSQL/Serializer.h> namespace SQL { @@ -53,17 +53,25 @@ DownPointer::DownPointer(DownPointer const& other) TreeNode* DownPointer::node() { if (!m_node) - inflate(); + deserialize(m_owner->tree().serializer()); return m_node; } -void DownPointer::inflate() +void DownPointer::deserialize(Serializer& serializer) { if (m_node || !m_pointer) return; - auto buffer = m_owner->tree().read_block(m_pointer); - size_t offset = 0; - m_node = make<TreeNode>(m_owner->tree(), m_owner, m_pointer, buffer, offset); + serializer.get_block(m_pointer); + m_node = serializer.make_and_deserialize<TreeNode>(m_owner->tree(), m_owner, m_pointer); +} + +TreeNode::TreeNode(BTree& tree, u32 pointer) + : IndexNode(pointer) + , m_tree(tree) + , m_up(nullptr) + , m_entries() + , m_down() +{ } TreeNode::TreeNode(BTree& tree, TreeNode* up, u32 pointer) @@ -103,36 +111,55 @@ TreeNode::TreeNode(BTree& tree, TreeNode* up, TreeNode* left, u32 pointer) m_is_leaf = left->pointer() == 0; } -TreeNode::TreeNode(BTree& tree, TreeNode* up, u32 pointer, ByteBuffer& buffer, size_t& at_offset) - : IndexNode(pointer) - , m_tree(tree) - , m_up(up) - , m_entries() - , m_down() +void TreeNode::deserialize(Serializer& serializer) { - u32 nodes; - deserialize_from<u32>(buffer, at_offset, nodes); + auto nodes = serializer.deserialize<u32>(); dbgln_if(SQL_DEBUG, "Deserializing node. Size {}", nodes); if (nodes > 0) { for (u32 i = 0; i < nodes; i++) { - u32 left; - deserialize_from<u32>(buffer, at_offset, left); + auto left = serializer.deserialize<u32>(); dbgln_if(SQL_DEBUG, "Down[{}] {}", i, left); if (!m_down.is_empty()) VERIFY((left == 0) == m_is_leaf); else m_is_leaf = (left == 0); - m_entries.append(Key(m_tree.descriptor(), buffer, at_offset)); + m_entries.append(serializer.deserialize<Key>(m_tree.descriptor())); m_down.empend(this, left); } - u32 right; - deserialize_from<u32>(buffer, at_offset, right); + auto right = serializer.deserialize<u32>(); dbgln_if(SQL_DEBUG, "Right {}", right); VERIFY((right == 0) == m_is_leaf); m_down.empend(this, right); } } +void TreeNode::serialize(Serializer& serializer) const +{ + u32 sz = size(); + serializer.serialize<u32>(sz); + if (sz > 0) { + for (auto ix = 0u; ix < size(); ix++) { + auto& entry = m_entries[ix]; + dbgln_if(SQL_DEBUG, "Serializing Left[{}] = {}", ix, m_down[ix].pointer()); + serializer.serialize<u32>(is_leaf() ? 0u : m_down[ix].pointer()); + serializer.serialize<Key>(entry); + } + dbgln_if(SQL_DEBUG, "Serializing Right = {}", m_down[size()].pointer()); + serializer.serialize<u32>(is_leaf() ? 0u : m_down[size()].pointer()); + } +} + +size_t TreeNode::length() const +{ + if (!size()) + return 0; + size_t len = sizeof(u32); + for (auto& key : m_entries) { + len += sizeof(u32) + key.length(); + } + return len; +} + bool TreeNode::insert(Key const& key) { dbgln_if(SQL_DEBUG, "[#{}] INSERT({})", pointer(), key.to_string()); @@ -154,7 +181,7 @@ bool TreeNode::update_key_pointer(Key const& key) if (m_entries[ix].pointer() != key.pointer()) { m_entries[ix].set_pointer(key.pointer()); dump_if(SQL_DEBUG, "To WAL"); - tree().add_to_write_ahead_log(this); + tree().serializer().serialize_and_write<TreeNode>(*this, pointer()); } return true; } @@ -179,16 +206,6 @@ bool TreeNode::insert_in_leaf(Key const& key) return true; } -size_t TreeNode::max_keys_in_node() -{ - auto descriptor = m_tree.descriptor(); - auto key_size = descriptor->data_length() + sizeof(u32); - auto ret = (BLOCKSIZE - 2 * sizeof(u32)) / key_size; - if ((ret % 2) == 0) - --ret; - return ret; -} - Key const& TreeNode::operator[](size_t ix) const { VERIFY(ix < size()); @@ -263,22 +280,6 @@ Optional<u32> TreeNode::get(Key& key) return down_node(size())->get(key); } -void TreeNode::serialize(ByteBuffer& buffer) const -{ - u32 sz = size(); - serialize_to<u32>(buffer, sz); - if (sz > 0) { - for (auto ix = 0u; ix < size(); ix++) { - auto& entry = m_entries[ix]; - dbgln_if(SQL_DEBUG, "Serializing Left[{}] = {}", ix, m_down[ix].pointer()); - serialize_to<u32>(buffer, is_leaf() ? 0u : m_down[ix].pointer()); - entry.serialize(buffer); - } - dbgln_if(SQL_DEBUG, "Serializing Right = {}", m_down[size()].pointer()); - serialize_to<u32>(buffer, is_leaf() ? 0u : m_down[size()].pointer()); - } -} - void TreeNode::just_insert(Key const& key, TreeNode* right) { dbgln_if(SQL_DEBUG, "[#{}] just_insert({}, right = {})", @@ -289,11 +290,11 @@ void TreeNode::just_insert(Key const& key, TreeNode* right) m_entries.insert(ix, key); VERIFY(is_leaf() == (right == nullptr)); m_down.insert(ix + 1, DownPointer(this, right)); - if (size() > max_keys_in_node()) { + if (length() > BLOCKSIZE) { split(); } else { dump_if(SQL_DEBUG, "To WAL"); - tree().add_to_write_ahead_log(this); + tree().serializer().serialize_and_write(*this, pointer()); } return; } @@ -301,11 +302,11 @@ void TreeNode::just_insert(Key const& key, TreeNode* right) m_entries.append(key); m_down.empend(this, right); - if (size() > max_keys_in_node()) { + if (length() > BLOCKSIZE) { split(); } else { dump_if(SQL_DEBUG, "To WAL"); - tree().add_to_write_ahead_log(this); + tree().serializer().serialize_and_write(*this, pointer()); } } @@ -317,15 +318,18 @@ void TreeNode::split() m_up = m_tree.new_root(); // Take the left pointer for the new node: - DownPointer left = m_down.take(max_keys_in_node() / 2 + 1); + auto median_index = size() / 2; + if (!(size() % 2)) + ++median_index; + DownPointer left = m_down.take(median_index); // Create the new right node: auto* new_node = new TreeNode(tree(), m_up, left); // Move the rightmost keys from this node to the new right node: - while (m_entries.size() > max_keys_in_node() / 2 + 1) { - auto entry = m_entries.take(max_keys_in_node() / 2 + 1); - auto down = m_down.take(max_keys_in_node() / 2 + 1); + while (m_entries.size() > median_index) { + auto entry = m_entries.take(median_index); + auto down = m_down.take(median_index); // Reparent to new right node: if (down.m_node != nullptr) { @@ -340,9 +344,9 @@ void TreeNode::split() auto median = m_entries.take_last(); dump_if(SQL_DEBUG, "Split Left To WAL"); - tree().add_to_write_ahead_log(this); + tree().serializer().serialize_and_write(*this, pointer()); new_node->dump_if(SQL_DEBUG, "Split Right to WAL"); - tree().add_to_write_ahead_log(new_node); + tree().serializer().serialize_and_write(*new_node, pointer()); m_up->just_insert(median, new_node); } diff --git a/Userland/Libraries/LibSQL/Tuple.cpp b/Userland/Libraries/LibSQL/Tuple.cpp index 8706a07d50..96d98ae906 100644 --- a/Userland/Libraries/LibSQL/Tuple.cpp +++ b/Userland/Libraries/LibSQL/Tuple.cpp @@ -8,7 +8,7 @@ #include <AK/String.h> #include <AK/StringBuilder.h> -#include <LibSQL/Serialize.h> +#include <LibSQL/Serializer.h> #include <LibSQL/Tuple.h> #include <LibSQL/TupleDescriptor.h> #include <LibSQL/Value.h> @@ -31,44 +31,36 @@ Tuple::Tuple(NonnullRefPtr<TupleDescriptor> const& descriptor, u32 pointer) } } -Tuple::Tuple(NonnullRefPtr<TupleDescriptor> const& descriptor, ByteBuffer& buffer, size_t& offset) +Tuple::Tuple(NonnullRefPtr<TupleDescriptor> const& descriptor, Serializer& serializer) : Tuple(descriptor) { - deserialize(buffer, offset); + deserialize(serializer); } -Tuple::Tuple(NonnullRefPtr<TupleDescriptor> const& descriptor, ByteBuffer& buffer) - : Tuple(descriptor) +void Tuple::deserialize(Serializer& serializer) { - size_t offset = 0; - deserialize(buffer, offset); -} - -void Tuple::deserialize(ByteBuffer& buffer, size_t& offset) -{ - dbgln_if(SQL_DEBUG, "deserialize tuple at offset {}", offset); - deserialize_from<u32>(buffer, offset, m_pointer); + dbgln_if(SQL_DEBUG, "deserialize tuple at offset {}", serializer.offset()); + serializer.deserialize_to<u32>(m_pointer); dbgln_if(SQL_DEBUG, "pointer: {}", m_pointer); + auto sz = serializer.deserialize<u32>(); m_data.clear(); - for (auto& part : *m_descriptor) { - m_data.append(Value::deserialize_from(buffer, offset)); - dbgln_if(SQL_DEBUG, "Deserialized element {} = {}", part.name, m_data.last().to_string()); + m_descriptor->clear(); + for (auto ix = 0u; ix < sz; ++ix) { + m_descriptor->append(serializer.deserialize<TupleElementDescriptor>()); + m_data.append(serializer.deserialize<Value>()); } } -void Tuple::serialize(ByteBuffer& buffer) const +void Tuple::serialize(Serializer& serializer) const { VERIFY(m_descriptor->size() == m_data.size()); dbgln_if(SQL_DEBUG, "Serializing tuple pointer {}", pointer()); - serialize_to<u32>(buffer, pointer()); + serializer.serialize<u32>(pointer()); + serializer.serialize<u32>((u32)m_descriptor->size()); for (auto ix = 0u; ix < m_descriptor->size(); ix++) { auto& key_part = m_data[ix]; - if constexpr (SQL_DEBUG) { - auto key_string = key_part.to_string(); - auto& key_part_definition = (*m_descriptor)[ix]; - dbgln("Serialized part {} = {}", key_part_definition.name, key_string); - } - key_part.serialize_to(buffer); + serializer.serialize<TupleElementDescriptor>((*m_descriptor)[ix]); + serializer.serialize<Value>(key_part); } } @@ -158,6 +150,18 @@ bool Tuple::is_compatible(Tuple const& other) const return true; } +size_t Tuple::length() const +{ + size_t len = 2 * sizeof(u32); + for (auto ix = 0u; ix < m_descriptor->size(); ix++) { + auto& descriptor = (*m_descriptor)[ix]; + auto& value = m_data[ix]; + len += descriptor.length(); + len += value.length(); + } + return len; +} + String Tuple::to_string() const { StringBuilder builder; @@ -186,7 +190,7 @@ void Tuple::copy_from(const Tuple& other) { if (*m_descriptor != *other.m_descriptor) { m_descriptor->clear(); - for (TupleElement const& part : *other.m_descriptor) { + for (TupleElementDescriptor const& part : *other.m_descriptor) { m_descriptor->append(part); } } diff --git a/Userland/Libraries/LibSQL/Tuple.h b/Userland/Libraries/LibSQL/Tuple.h index 5447e9bc6d..8831fe88db 100644 --- a/Userland/Libraries/LibSQL/Tuple.h +++ b/Userland/Libraries/LibSQL/Tuple.h @@ -29,8 +29,7 @@ class Tuple { public: Tuple(); explicit Tuple(NonnullRefPtr<TupleDescriptor> const&, u32 pointer = 0); - Tuple(NonnullRefPtr<TupleDescriptor> const&, ByteBuffer&, size_t&); - Tuple(NonnullRefPtr<TupleDescriptor> const&, ByteBuffer&); + Tuple(NonnullRefPtr<TupleDescriptor> const&, Serializer&); Tuple(Tuple const&); virtual ~Tuple() = default; @@ -62,23 +61,25 @@ public: void set_pointer(u32 ptr) { m_pointer = ptr; } [[nodiscard]] size_t size() const { return m_data.size(); } - [[nodiscard]] size_t length() const { return m_descriptor->size(); } + [[nodiscard]] virtual size_t length() const; + void clear() { m_descriptor->clear(); } [[nodiscard]] NonnullRefPtr<TupleDescriptor> descriptor() const { return m_descriptor; } [[nodiscard]] int compare(Tuple const&) const; [[nodiscard]] int match(Tuple const&) const; [[nodiscard]] u32 hash() const; - virtual void serialize(ByteBuffer&) const; - [[nodiscard]] virtual size_t data_length() const { return descriptor()->data_length(); } protected: [[nodiscard]] Optional<size_t> index_of(String) const; void copy_from(Tuple const&); - void deserialize(ByteBuffer&, size_t&); + virtual void serialize(Serializer&) const; + virtual void deserialize(Serializer&); private: NonnullRefPtr<TupleDescriptor> m_descriptor; Vector<Value> m_data; - u32 m_pointer { 0 }; + u32 m_pointer { 2 * sizeof(u32) }; + + friend Serializer; }; } diff --git a/Userland/Libraries/LibSQL/TupleDescriptor.h b/Userland/Libraries/LibSQL/TupleDescriptor.h index afb93a00f7..ddbb68830b 100644 --- a/Userland/Libraries/LibSQL/TupleDescriptor.h +++ b/Userland/Libraries/LibSQL/TupleDescriptor.h @@ -7,34 +7,44 @@ #pragma once #include <AK/Vector.h> +#include <LibSQL/Serializer.h> #include <LibSQL/Type.h> namespace SQL { -struct TupleElement { +struct TupleElementDescriptor { String name { "" }; SQLType type { SQLType::Text }; Order order { Order::Ascending }; - bool operator==(TupleElement const&) const = default; + bool operator==(TupleElementDescriptor const&) const = default; + + void serialize(Serializer& serializer) const + { + serializer.serialize(name); + serializer.serialize<u8>((u8)type); + serializer.serialize<u8>((u8)order); + } + void deserialize(Serializer& serializer) + { + name = serializer.deserialize<String>(); + type = (SQLType)serializer.deserialize<u8>(); + order = (Order)serializer.deserialize<u8>(); + } + + size_t length() const + { + return (sizeof(u32) + name.length()) + 2 * sizeof(u8); + } }; class TupleDescriptor - : public Vector<TupleElement> + : public Vector<TupleElementDescriptor> , public RefCounted<TupleDescriptor> { public: TupleDescriptor() = default; ~TupleDescriptor() = default; - [[nodiscard]] size_t data_length() const - { - size_t sz = sizeof(u32); - for (auto& part : *this) { - sz += size_of(part.type); - } - return sz; - } - [[nodiscard]] int compare_ignoring_names(TupleDescriptor const& other) const { if (size() != other.size()) @@ -49,7 +59,32 @@ public: return 0; } - using Vector<TupleElement>::operator==; + void serialize(Serializer& serializer) const + { + serializer.serialize<u32>(size()); + for (auto& element : *this) { + serializer.serialize<TupleElementDescriptor>(element); + } + } + + void deserialize(Serializer& serializer) + { + auto sz = serializer.deserialize<u32>(); + for (auto ix = 0u; ix < sz; ix++) { + append(serializer.deserialize<TupleElementDescriptor>()); + } + } + + size_t length() const + { + size_t len = sizeof(u32); + for (auto& element : *this) { + len += element.length(); + } + return len; + } + + using Vector<TupleElementDescriptor>::operator==; }; } diff --git a/Userland/Libraries/LibSQL/Value.cpp b/Userland/Libraries/LibSQL/Value.cpp index c415a130f1..e30e402381 100644 --- a/Userland/Libraries/LibSQL/Value.cpp +++ b/Userland/Libraries/LibSQL/Value.cpp @@ -4,7 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include <LibSQL/Serialize.h> +#include <LibSQL/Serializer.h> #include <LibSQL/Value.h> #include <math.h> #include <string.h> @@ -377,25 +377,19 @@ bool Value::operator>=(Value const& other) const return compare(other) >= 0; } -void Value::serialize_to(ByteBuffer& buffer) const +void Value::serialize(Serializer& serializer) const { u8 type_flags = (u8)type(); if (is_null()) type_flags |= (u8)SQLType::Null; - SQL::serialize_to(buffer, type_flags); + serializer.serialize<u8>(type_flags); if (!is_null()) - m_impl.visit([&](auto& impl) { impl.serialize(buffer); }); + m_impl.visit([&](auto& impl) { serializer.serialize(impl); }); } -void Value::deserialize(ByteBuffer& buffer, size_t& offset_at) +void Value::deserialize(Serializer& serializer) { - 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); + auto type_flags = serializer.deserialize<u8>(); bool is_null = false; if ((type_flags & (u8)SQLType::Null) && (type_flags != (u8)SQLType::Null)) { type_flags &= ~((u8)SQLType::Null); @@ -403,15 +397,12 @@ Value Value::deserialize_from(ByteBuffer& buffer, size_t& at_offset) } auto type = (SQLType)type_flags; VERIFY(!is_null || (type != SQLType::Tuple && type != SQLType::Array)); - Value ret(type); + setup(type); if (!is_null) { - ret.deserialize(buffer, at_offset); + m_impl.visit([&](auto& impl) { impl.deserialize(serializer); }); } - return ret; } -// ----------------------------------------------------------------- - bool NullImpl::can_cast(Value const& value) { return value.is_null(); @@ -422,8 +413,6 @@ int NullImpl::compare(Value const& other) return other.type() == SQLType::Null; } -// ----------------------------------------------------------------- - String TextImpl::to_string() const { return value(); @@ -490,7 +479,7 @@ void TextImpl::assign_bool(bool bool_value) size_t TextImpl::length() const { - return (is_null()) ? 0 : sizeof(u32) + min(value().length(), 64) + 1; + return (is_null()) ? 0 : sizeof(u32) + value().length(); } int TextImpl::compare(Value const& other) const @@ -777,32 +766,38 @@ bool ContainerValueImpl::append(Value const& value) 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 +void ContainerValueImpl::serialize_values(Serializer& serializer) const { - serialize_to(buffer, (u32)size()); - for (auto& value : value()) { - Value(value).serialize_to(buffer); + serializer.serialize((u32)size()); + for (auto& impl : value()) { + serializer.serialize<Value>(Value(impl)); } } -void ContainerValueImpl::deserialize_values(ByteBuffer& buffer, size_t& at_offset) +void ContainerValueImpl::deserialize_values(Serializer& serializer) { - u32 sz; - deserialize_from(buffer, at_offset, sz); + auto sz = serializer.deserialize<u32>(); m_value = Vector<BaseTypeImpl>(); for (auto ix = 0u; ix < sz; ix++) { - append(Value::deserialize_from(buffer, at_offset)); + append(serializer.deserialize<Value>()); } } +size_t ContainerValueImpl::length() const +{ + size_t len = sizeof(u32); + for (auto& impl : value()) { + len += Value(impl).length(); + } + return len; +} + void TupleImpl::assign(Value const& other) { if (other.type() != SQLType::Tuple) { @@ -820,7 +815,7 @@ void TupleImpl::assign(Value const& other) size_t TupleImpl::length() const { - return (m_descriptor) ? m_descriptor->data_length() : 0; + return m_descriptor->length() + ContainerValueImpl::length(); } bool TupleImpl::can_cast(Value const& other_value) const @@ -853,55 +848,61 @@ int TupleImpl::compare(Value const& other) const return 0; } -void TupleImpl::serialize(ByteBuffer& buffer) const +void TupleImpl::serialize(Serializer& serializer) 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); + serializer.serialize<TupleDescriptor>(*m_descriptor); + serialize_values(serializer); +} + +void TupleImpl::deserialize(Serializer& serializer) +{ + m_descriptor = serializer.adopt_and_deserialize<TupleDescriptor>(); + deserialize_values(serializer); +} + +void TupleImpl::infer_descriptor() +{ + if (!m_descriptor) { + m_descriptor = adopt_ref(*new TupleDescriptor); + m_descriptor_inferred = true; } - serialize_values(buffer); } -void TupleImpl::deserialize(ByteBuffer& buffer, size_t& at_offset) +void TupleImpl::extend_descriptor(Value const& value) { - u32 sz; - deserialize_from(buffer, at_offset, sz); - if (sz != (u32)-1) { - NonnullRefPtr<TupleDescriptor> 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 { + VERIFY(m_descriptor_inferred); + m_descriptor->empend("", value.type(), Order::Ascending); +} + +bool TupleImpl::validate_before_assignment(Vector<Value> const& values) +{ + if (m_descriptor_inferred) m_descriptor = nullptr; - m_max_size = {}; + if (!m_descriptor) { + infer_descriptor(); + if (values.size() > m_descriptor->size()) { + for (auto ix = m_descriptor->size(); ix < values.size(); ix++) { + extend_descriptor(values[ix]); + } + } } - deserialize_values(buffer, at_offset); + return true; } bool TupleImpl::validate(BaseTypeImpl const& value) { if (!m_descriptor) - return true; - auto required_type = (*m_descriptor)[size()].type; + infer_descriptor(); + if (m_descriptor_inferred && (this->value().size() == m_descriptor->size())) + extend_descriptor(Value(value)); + if (m_descriptor->size() == this->value().size()) + return false; + auto required_type = (*m_descriptor)[this->value().size()].type; return Value(value).type() == required_type; } bool TupleImpl::validate_after_assignment() { - 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)); @@ -925,11 +926,7 @@ void ArrayImpl::assign(Value const& other) size_t ArrayImpl::length() const { - size_t ret = 0; - for (auto& value : value()) { - ret += Value(value).length(); - } - return ret; + return sizeof(u8) + sizeof(u32) + ContainerValueImpl::length(); } bool ArrayImpl::can_cast(Value const& other_value) const @@ -960,32 +957,31 @@ int ArrayImpl::compare(Value const& other) const return 0; } -void ArrayImpl::serialize(ByteBuffer& buffer) const +void ArrayImpl::serialize(Serializer& serializer) const { - serialize_to(buffer, (u8)m_element_type); + serializer.serialize((u8)m_element_type); if (m_max_size.has_value()) - serialize_to(buffer, (u32)m_max_size.value()); + serializer.serialize((u32)m_max_size.value()); else - serialize_to(buffer, (u32)0); - serialize_values(buffer); + serializer.serialize((u32)0); + serialize_values(serializer); } -void ArrayImpl::deserialize(ByteBuffer& buffer, size_t& at_offset) +void ArrayImpl::deserialize(Serializer& serializer) { - 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); + m_element_type = (SQLType)serializer.deserialize<u8>(); + auto max_sz = serializer.deserialize<u32>(); if (max_sz) m_max_size = max_sz; else m_max_size = {}; - deserialize_values(buffer, at_offset); + deserialize_values(serializer); } bool ArrayImpl::validate(BaseTypeImpl const& impl) { + if (m_max_size.has_value() && (size() >= m_max_size.value())) + return false; return Value(impl).type() == m_element_type; } diff --git a/Userland/Libraries/LibSQL/Value.h b/Userland/Libraries/LibSQL/Value.h index 624e32e4ae..76233c4248 100644 --- a/Userland/Libraries/LibSQL/Value.h +++ b/Userland/Libraries/LibSQL/Value.h @@ -11,287 +11,16 @@ #include <AK/ScopeGuard.h> #include <AK/String.h> #include <AK/Variant.h> -#include <LibSQL/Serialize.h> +#include <LibSQL/Forward.h> #include <LibSQL/TupleDescriptor.h> #include <LibSQL/Type.h> +#include <LibSQL/ValueImpl.h> #include <string.h> 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<int> to_int() { return {}; } - [[nodiscard]] static Optional<double> to_double() { return {}; } - [[nodiscard]] static Optional<bool> to_bool() { return {}; } - [[nodiscard]] static bool to_vector(Vector<Value>&) { 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<Value> 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<typename T> -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<T> m_value {}; -}; - -class TextImpl : public Impl<String> { -public: - explicit TextImpl() - : Impl(SQLType::Text) - { - } - - [[nodiscard]] String to_string() const; - [[nodiscard]] Optional<int> to_int() const; - [[nodiscard]] Optional<double> to_double() const; - [[nodiscard]] Optional<bool> to_bool() const; - [[nodiscard]] static bool to_vector(Vector<Value>&) { 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<Value> 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<int> { -public: - IntegerImpl() - : Impl(SQLType::Integer) - { - } - - [[nodiscard]] String to_string() const; - [[nodiscard]] Optional<int> to_int() const; - [[nodiscard]] Optional<double> to_double() const; - [[nodiscard]] Optional<bool> to_bool() const; - [[nodiscard]] static bool to_vector(Vector<Value>&) { 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<Value> 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<double> { -public: - explicit FloatImpl() - : Impl(SQLType::Float) - { - } - - [[nodiscard]] String to_string() const; - [[nodiscard]] Optional<int> to_int() const; - [[nodiscard]] Optional<double> to_double() const; - [[nodiscard]] static Optional<bool> to_bool() { return {}; } - [[nodiscard]] static bool to_vector(Vector<Value>&) { 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<Value> 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<bool> { -public: - explicit BooleanImpl() - : Impl(SQLType::Boolean) - { - } - - [[nodiscard]] String to_string() const; - [[nodiscard]] Optional<int> to_int() const; - [[nodiscard]] static Optional<double> to_double(); - [[nodiscard]] Optional<bool> to_bool() const; - [[nodiscard]] static bool to_vector(Vector<Value>&) { 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<Value> const&) { m_value = {}; } - [[nodiscard]] static bool can_cast(Value const&); - [[nodiscard]] int compare(Value const& other) const; - [[nodiscard]] u32 hash() const; -}; - -using BaseTypeImpl = Variant<NullImpl, TextImpl, IntegerImpl, FloatImpl, BooleanImpl>; - -class ContainerValueImpl : public Impl<Vector<BaseTypeImpl>> { -public: - virtual ~ContainerValueImpl() = default; - - [[nodiscard]] String to_string() const; - [[nodiscard]] static Optional<int> to_int() { return {}; } - [[nodiscard]] static Optional<double> to_double() { return {}; } - [[nodiscard]] static Optional<bool> to_bool() { return {}; } - [[nodiscard]] bool to_vector(Vector<Value>&) 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<Value> const&); - [[nodiscard]] u32 hash() const; - - virtual bool validate_before_assignment(Vector<Value> const&) { return true; } - virtual bool validate(BaseTypeImpl const&) { return true; } - virtual bool validate_after_assignment() { return true; } - [[nodiscard]] Vector<String> 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<size_t> const& max_size = {}) - : Impl(sql_type) - , m_max_size(max_size) - { - } - - Optional<size_t> m_max_size {}; -}; - -class TupleImpl : public ContainerValueImpl { -public: - explicit TupleImpl(NonnullRefPtr<TupleDescriptor> 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<TupleDescriptor> m_descriptor; -}; - -class ArrayImpl : public ContainerValueImpl { -public: - explicit ArrayImpl(SQLType element_type, Optional<size_t> 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<NullImpl, TextImpl, IntegerImpl, FloatImpl, BooleanImpl, TupleImpl, ArrayImpl>; - /** - * A `Value` is an atomic piece of SQL data. A `Value` has a basic type + * A `Value` is an atomic piece of SQL data`. A `Value` has a basic type * (Text/String, Integer, Float, etc). Richer types are implemented in higher * level layers, but the resulting data is stored in these `Value` objects. */ @@ -373,8 +102,8 @@ public: [[nodiscard]] size_t length() const; [[nodiscard]] u32 hash() const; [[nodiscard]] bool can_cast(Value const&) const; - void serialize_to(ByteBuffer&) const; - void deserialize(ByteBuffer&, size_t&); + void serialize(Serializer&) const; + void deserialize(Serializer&); [[nodiscard]] int compare(Value const&) const; bool operator==(Value const&) const; @@ -390,12 +119,12 @@ public: static Value const& null(); static Value create_tuple(NonnullRefPtr<TupleDescriptor> const&); static Value create_array(SQLType element_type, Optional<size_t> const& max_size = {}); - static Value deserialize_from(ByteBuffer&, size_t&); private: void setup(SQLType type); - ValueTypeImpl m_impl {}; + ValueTypeImpl m_impl { NullImpl() }; + friend Serializer; }; } diff --git a/Userland/Libraries/LibSQL/ValueImpl.h b/Userland/Libraries/LibSQL/ValueImpl.h new file mode 100644 index 0000000000..70fe611ed5 --- /dev/null +++ b/Userland/Libraries/LibSQL/ValueImpl.h @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2021, Jan de Visser <jan@de-visser.net> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/Badge.h> +#include <AK/ByteBuffer.h> +#include <AK/ScopeGuard.h> +#include <AK/String.h> +#include <AK/Variant.h> +#include <LibSQL/Forward.h> +#include <LibSQL/Serializer.h> +#include <LibSQL/TupleDescriptor.h> +#include <LibSQL/Type.h> +#include <string.h> + +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<int> to_int() { return {}; } + [[nodiscard]] static Optional<double> to_double() { return {}; } + [[nodiscard]] static Optional<bool> to_bool() { return {}; } + [[nodiscard]] static bool to_vector(Vector<Value>&) { 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<Value> const&) { } + [[nodiscard]] static size_t length() { return 0; } + [[nodiscard]] static bool can_cast(Value const&); + [[nodiscard]] static int compare(Value const&); + static void serialize(Serializer&) { } + static void deserialize(Serializer&) { } + [[nodiscard]] static u32 hash() { return 0; } +}; + +template<typename T> +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(Serializer& serializer) const + { + serializer.serialize(value()); + } + + void deserialize(Serializer& serializer) + { + T value; + serializer.deserialize_to(value); + m_value = value; + } + +protected: + explicit Impl(SQLType sql_type) + : BaseImpl(sql_type) + { + } + + Optional<T> m_value {}; +}; + +class TextImpl : public Impl<String> { +public: + explicit TextImpl() + : Impl(SQLType::Text) + { + } + + [[nodiscard]] String to_string() const; + [[nodiscard]] Optional<int> to_int() const; + [[nodiscard]] Optional<double> to_double() const; + [[nodiscard]] Optional<bool> to_bool() const; + [[nodiscard]] static bool to_vector(Vector<Value>&) { 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<Value> 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<int> { +public: + IntegerImpl() + : Impl(SQLType::Integer) + { + } + + [[nodiscard]] String to_string() const; + [[nodiscard]] Optional<int> to_int() const; + [[nodiscard]] Optional<double> to_double() const; + [[nodiscard]] Optional<bool> to_bool() const; + [[nodiscard]] static bool to_vector(Vector<Value>&) { 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<Value> 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<double> { +public: + explicit FloatImpl() + : Impl(SQLType::Float) + { + } + + [[nodiscard]] String to_string() const; + [[nodiscard]] Optional<int> to_int() const; + [[nodiscard]] Optional<double> to_double() const; + [[nodiscard]] static Optional<bool> to_bool() { return {}; } + [[nodiscard]] static bool to_vector(Vector<Value>&) { 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<Value> 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<bool> { +public: + explicit BooleanImpl() + : Impl(SQLType::Boolean) + { + } + + [[nodiscard]] String to_string() const; + [[nodiscard]] Optional<int> to_int() const; + [[nodiscard]] static Optional<double> to_double(); + [[nodiscard]] Optional<bool> to_bool() const; + [[nodiscard]] static bool to_vector(Vector<Value>&) { 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<Value> const&) { m_value = {}; } + [[nodiscard]] static bool can_cast(Value const&); + [[nodiscard]] int compare(Value const& other) const; + [[nodiscard]] u32 hash() const; +}; + +using BaseTypeImpl = Variant<NullImpl, TextImpl, IntegerImpl, FloatImpl, BooleanImpl>; + +class ContainerValueImpl : public Impl<Vector<BaseTypeImpl>> { +public: + virtual ~ContainerValueImpl() = default; + + [[nodiscard]] String to_string() const; + [[nodiscard]] static Optional<int> to_int() { return {}; } + [[nodiscard]] static Optional<double> to_double() { return {}; } + [[nodiscard]] static Optional<bool> to_bool() { return {}; } + [[nodiscard]] bool to_vector(Vector<Value>&) 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<Value> const&); + [[nodiscard]] u32 hash() const; + + virtual bool validate_before_assignment(Vector<Value> const&) { return true; } + virtual bool validate(BaseTypeImpl const&) { return true; } + virtual bool validate_after_assignment() { return true; } + [[nodiscard]] Vector<String> to_string_vector() const; + [[nodiscard]] size_t length() const; + [[nodiscard]] size_t size() const { return is_null() ? 0 : value().size(); } + bool append(Value const&); + bool append(BaseTypeImpl const& value); + void serialize_values(Serializer&) const; + void deserialize_values(Serializer&); + +protected: + explicit ContainerValueImpl(SQLType sql_type) + : Impl(sql_type) + { + } +}; + +class TupleImpl : public ContainerValueImpl { +public: + explicit TupleImpl(NonnullRefPtr<TupleDescriptor> const& descriptor) + : ContainerValueImpl(SQLType::Tuple) + , m_descriptor(descriptor) + { + } + + 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_before_assignment(Vector<Value> const&) override; + virtual bool validate(BaseTypeImpl const&) override; + virtual bool validate_after_assignment() override; + void serialize(Serializer&) const; + void deserialize(Serializer&); + +private: + void infer_descriptor(); + void extend_descriptor(Value const&); + RefPtr<TupleDescriptor> m_descriptor; + bool m_descriptor_inferred { false }; +}; + +class ArrayImpl : public ContainerValueImpl { +public: + explicit ArrayImpl(SQLType element_type, Optional<size_t> const& max_size = {}) + : ContainerValueImpl(SQLType::Array) + , m_element_type(element_type) + , m_max_size(max_size) + { + } + + 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(Serializer&) const; + void deserialize(Serializer&); + virtual bool validate(BaseTypeImpl const&) override; + +private: + SQLType m_element_type { SQLType::Text }; + Optional<size_t> m_max_size {}; +}; + +using ValueTypeImpl = Variant<NullImpl, TextImpl, IntegerImpl, FloatImpl, BooleanImpl, TupleImpl, ArrayImpl>; + +} |