summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Tests/LibSQL/TestSqlBtreeIndex.cpp34
-rw-r--r--Tests/LibSQL/TestSqlHashIndex.cpp30
-rw-r--r--Tests/LibSQL/TestSqlValueAndTuple.cpp57
-rw-r--r--Userland/Libraries/LibSQL/BTree.cpp18
-rw-r--r--Userland/Libraries/LibSQL/BTree.h14
-rw-r--r--Userland/Libraries/LibSQL/BTreeIterator.cpp2
-rw-r--r--Userland/Libraries/LibSQL/CMakeLists.txt1
-rw-r--r--Userland/Libraries/LibSQL/Database.cpp22
-rw-r--r--Userland/Libraries/LibSQL/Database.h4
-rw-r--r--Userland/Libraries/LibSQL/Forward.h5
-rw-r--r--Userland/Libraries/LibSQL/HashIndex.cpp149
-rw-r--r--Userland/Libraries/LibSQL/HashIndex.h27
-rw-r--r--Userland/Libraries/LibSQL/Heap.cpp20
-rw-r--r--Userland/Libraries/LibSQL/Heap.h14
-rw-r--r--Userland/Libraries/LibSQL/Index.cpp26
-rw-r--r--Userland/Libraries/LibSQL/Index.h17
-rw-r--r--Userland/Libraries/LibSQL/Key.cpp13
-rw-r--r--Userland/Libraries/LibSQL/Key.h8
-rw-r--r--Userland/Libraries/LibSQL/Row.cpp27
-rw-r--r--Userland/Libraries/LibSQL/Row.h10
-rw-r--r--Userland/Libraries/LibSQL/Serialize.h81
-rw-r--r--Userland/Libraries/LibSQL/Serializer.cpp28
-rw-r--r--Userland/Libraries/LibSQL/Serializer.h176
-rw-r--r--Userland/Libraries/LibSQL/TreeNode.cpp116
-rw-r--r--Userland/Libraries/LibSQL/Tuple.cpp54
-rw-r--r--Userland/Libraries/LibSQL/Tuple.h15
-rw-r--r--Userland/Libraries/LibSQL/TupleDescriptor.h61
-rw-r--r--Userland/Libraries/LibSQL/Value.cpp156
-rw-r--r--Userland/Libraries/LibSQL/Value.h285
-rw-r--r--Userland/Libraries/LibSQL/ValueImpl.h297
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>;
+
+}