/src/serenity/Userland/Libraries/LibSQL/Database.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2021, Jan de Visser <jan@de-visser.net> |
3 | | * Copyright (c) 2021, Mahmoud Mandour <ma.mandourr@gmail.com> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include <AK/ByteString.h> |
9 | | #include <LibSQL/BTree.h> |
10 | | #include <LibSQL/Database.h> |
11 | | #include <LibSQL/Heap.h> |
12 | | #include <LibSQL/Meta.h> |
13 | | #include <LibSQL/Row.h> |
14 | | #include <LibSQL/Tuple.h> |
15 | | |
16 | | namespace SQL { |
17 | | |
18 | | ErrorOr<NonnullRefPtr<Database>> Database::create(ByteString name) |
19 | 0 | { |
20 | 0 | auto heap = TRY(Heap::create(move(name))); |
21 | 0 | return adopt_nonnull_ref_or_enomem(new (nothrow) Database(move(heap))); |
22 | 0 | } |
23 | | |
24 | | Database::Database(NonnullRefPtr<Heap> heap) |
25 | 0 | : m_heap(move(heap)) |
26 | 0 | , m_serializer(m_heap) |
27 | 0 | { |
28 | 0 | } |
29 | | |
30 | | ResultOr<void> Database::open() |
31 | 0 | { |
32 | 0 | VERIFY(!m_open); |
33 | 0 | TRY(m_heap->open()); |
34 | | |
35 | 0 | m_schemas = TRY(BTree::create(m_serializer, SchemaDef::index_def()->to_tuple_descriptor(), m_heap->schemas_root())); |
36 | 0 | m_schemas->on_new_root = [&]() { |
37 | 0 | m_heap->set_schemas_root(m_schemas->root()); |
38 | 0 | }; |
39 | |
|
40 | 0 | m_tables = TRY(BTree::create(m_serializer, TableDef::index_def()->to_tuple_descriptor(), m_heap->tables_root())); |
41 | 0 | m_tables->on_new_root = [&]() { |
42 | 0 | m_heap->set_tables_root(m_tables->root()); |
43 | 0 | }; |
44 | |
|
45 | 0 | m_table_columns = TRY(BTree::create(m_serializer, ColumnDef::index_def()->to_tuple_descriptor(), m_heap->table_columns_root())); |
46 | 0 | m_table_columns->on_new_root = [&]() { |
47 | 0 | m_heap->set_table_columns_root(m_table_columns->root()); |
48 | 0 | }; |
49 | |
|
50 | 0 | m_open = true; |
51 | |
|
52 | 0 | auto ensure_schema_exists = [&](auto schema_name) -> ResultOr<NonnullRefPtr<SchemaDef>> { |
53 | 0 | if (auto result = get_schema(schema_name); result.is_error()) { |
54 | 0 | if (result.error().error() != SQLErrorCode::SchemaDoesNotExist) |
55 | 0 | return result.release_error(); |
56 | | |
57 | 0 | auto schema_def = TRY(SchemaDef::create(schema_name)); |
58 | 0 | TRY(add_schema(*schema_def)); |
59 | 0 | return schema_def; |
60 | 0 | } else { |
61 | 0 | return result.release_value(); |
62 | 0 | } |
63 | 0 | }; |
64 | |
|
65 | 0 | (void)TRY(ensure_schema_exists("default"sv)); |
66 | 0 | auto master_schema = TRY(ensure_schema_exists("master"sv)); |
67 | | |
68 | 0 | if (auto result = get_table("master"sv, "internal_describe_table"sv); result.is_error()) { |
69 | 0 | if (result.error().error() != SQLErrorCode::TableDoesNotExist) |
70 | 0 | return result.release_error(); |
71 | | |
72 | 0 | auto internal_describe_table = TRY(TableDef::create(master_schema, "internal_describe_table")); |
73 | 0 | internal_describe_table->append_column("Name", SQLType::Text); |
74 | 0 | internal_describe_table->append_column("Type", SQLType::Text); |
75 | 0 | TRY(add_table(*internal_describe_table)); |
76 | 0 | } |
77 | | |
78 | 0 | return {}; |
79 | 0 | } |
80 | | |
81 | 0 | Database::~Database() = default; |
82 | | |
83 | | ErrorOr<void> Database::commit() |
84 | 0 | { |
85 | 0 | VERIFY(is_open()); |
86 | 0 | TRY(m_heap->flush()); |
87 | 0 | return {}; |
88 | 0 | } |
89 | | |
90 | | ResultOr<void> Database::add_schema(SchemaDef const& schema) |
91 | 0 | { |
92 | 0 | VERIFY(is_open()); |
93 | | |
94 | 0 | if (!m_schemas->insert(schema.key())) |
95 | 0 | return Result { SQLCommand::Unknown, SQLErrorCode::SchemaExists, schema.name() }; |
96 | 0 | return {}; |
97 | 0 | } |
98 | | |
99 | | Key Database::get_schema_key(ByteString const& schema_name) |
100 | 0 | { |
101 | 0 | auto key = SchemaDef::make_key(); |
102 | 0 | key["schema_name"] = schema_name; |
103 | 0 | return key; |
104 | 0 | } |
105 | | |
106 | | ResultOr<NonnullRefPtr<SchemaDef>> Database::get_schema(ByteString const& schema) |
107 | 0 | { |
108 | 0 | VERIFY(is_open()); |
109 | | |
110 | 0 | auto schema_name = schema; |
111 | 0 | if (schema.is_empty()) |
112 | 0 | schema_name = "default"sv; |
113 | |
|
114 | 0 | Key key = get_schema_key(schema_name); |
115 | 0 | if (auto it = m_schema_cache.find(key.hash()); it != m_schema_cache.end()) |
116 | 0 | return it->value; |
117 | | |
118 | 0 | auto schema_iterator = m_schemas->find(key); |
119 | 0 | if (schema_iterator.is_end() || (*schema_iterator != key)) |
120 | 0 | return Result { SQLCommand::Unknown, SQLErrorCode::SchemaDoesNotExist, schema_name }; |
121 | | |
122 | 0 | auto schema_def = TRY(SchemaDef::create(*schema_iterator)); |
123 | 0 | m_schema_cache.set(key.hash(), schema_def); |
124 | 0 | return schema_def; |
125 | 0 | } |
126 | | |
127 | | ResultOr<void> Database::add_table(TableDef& table) |
128 | 0 | { |
129 | 0 | VERIFY(is_open()); |
130 | | |
131 | 0 | if (!m_tables->insert(table.key())) |
132 | 0 | return Result { SQLCommand::Unknown, SQLErrorCode::TableExists, table.name() }; |
133 | | |
134 | 0 | for (auto& column : table.columns()) { |
135 | 0 | if (!m_table_columns->insert(column->key())) |
136 | 0 | VERIFY_NOT_REACHED(); |
137 | 0 | } |
138 | | |
139 | 0 | return {}; |
140 | 0 | } |
141 | | |
142 | | Key Database::get_table_key(ByteString const& schema_name, ByteString const& table_name) |
143 | 0 | { |
144 | 0 | auto key = TableDef::make_key(get_schema_key(schema_name)); |
145 | 0 | key["table_name"] = table_name; |
146 | 0 | return key; |
147 | 0 | } |
148 | | |
149 | | ResultOr<NonnullRefPtr<TableDef>> Database::get_table(ByteString const& schema, ByteString const& name) |
150 | 0 | { |
151 | 0 | VERIFY(is_open()); |
152 | | |
153 | 0 | auto schema_name = schema; |
154 | 0 | if (schema.is_empty()) |
155 | 0 | schema_name = "default"sv; |
156 | |
|
157 | 0 | Key key = get_table_key(schema_name, name); |
158 | 0 | if (auto it = m_table_cache.find(key.hash()); it != m_table_cache.end()) |
159 | 0 | return it->value; |
160 | | |
161 | 0 | auto table_iterator = m_tables->find(key); |
162 | 0 | if (table_iterator.is_end() || (*table_iterator != key)) |
163 | 0 | return Result { SQLCommand::Unknown, SQLErrorCode::TableDoesNotExist, ByteString::formatted("{}.{}", schema_name, name) }; |
164 | | |
165 | 0 | auto schema_def = TRY(get_schema(schema)); |
166 | 0 | auto table_def = TRY(TableDef::create(schema_def, name)); |
167 | 0 | table_def->set_block_index((*table_iterator).block_index()); |
168 | 0 | m_table_cache.set(key.hash(), table_def); |
169 | |
|
170 | 0 | auto table_hash = table_def->hash(); |
171 | 0 | auto column_key = ColumnDef::make_key(table_def); |
172 | 0 | for (auto it = m_table_columns->find(column_key); !it.is_end() && ((*it)["table_hash"].to_int<u32>() == table_hash); ++it) |
173 | 0 | table_def->append_column(*it); |
174 | |
|
175 | 0 | return table_def; |
176 | 0 | } |
177 | | |
178 | | ErrorOr<Vector<Row>> Database::select_all(TableDef& table) |
179 | 0 | { |
180 | 0 | VERIFY(m_table_cache.get(table.key().hash()).has_value()); |
181 | 0 | Vector<Row> ret; |
182 | 0 | for (auto block_index = table.block_index(); block_index; block_index = ret.last().next_block_index()) |
183 | 0 | ret.append(m_serializer.deserialize_block<Row>(block_index, table, block_index)); |
184 | 0 | return ret; |
185 | 0 | } |
186 | | |
187 | | ErrorOr<Vector<Row>> Database::match(TableDef& table, Key const& key) |
188 | 0 | { |
189 | 0 | VERIFY(m_table_cache.get(table.key().hash()).has_value()); |
190 | 0 | Vector<Row> ret; |
191 | | |
192 | | // TODO Match key against indexes defined on table. If found, |
193 | | // use the index instead of scanning the table. |
194 | 0 | for (auto block_index = table.block_index(); block_index;) { |
195 | 0 | auto row = m_serializer.deserialize_block<Row>(block_index, table, block_index); |
196 | 0 | if (row.match(key)) |
197 | 0 | ret.append(row); |
198 | 0 | block_index = ret.last().next_block_index(); |
199 | 0 | } |
200 | 0 | return ret; |
201 | 0 | } |
202 | | |
203 | | ErrorOr<void> Database::insert(Row& row) |
204 | 0 | { |
205 | 0 | VERIFY(m_table_cache.get(row.table().key().hash()).has_value()); |
206 | | // TODO: implement table constraints such as unique, foreign key, etc. |
207 | | |
208 | 0 | row.set_block_index(m_heap->request_new_block_index()); |
209 | 0 | row.set_next_block_index(row.table().block_index()); |
210 | 0 | TRY(update(row)); |
211 | | |
212 | | // TODO update indexes defined on table. |
213 | | |
214 | 0 | auto table_key = row.table().key(); |
215 | 0 | table_key.set_block_index(row.block_index()); |
216 | 0 | VERIFY(m_tables->update_key_pointer(table_key)); |
217 | 0 | row.table().set_block_index(row.block_index()); |
218 | 0 | return {}; |
219 | 0 | } |
220 | | |
221 | | ErrorOr<void> Database::remove(Row& row) |
222 | 0 | { |
223 | 0 | auto& table = row.table(); |
224 | 0 | VERIFY(m_table_cache.get(table.key().hash()).has_value()); |
225 | | |
226 | 0 | TRY(m_heap->free_storage(row.block_index())); |
227 | | |
228 | 0 | if (table.block_index() == row.block_index()) { |
229 | 0 | auto table_key = table.key(); |
230 | 0 | table_key.set_block_index(row.next_block_index()); |
231 | 0 | m_tables->update_key_pointer(table_key); |
232 | |
|
233 | 0 | table.set_block_index(row.next_block_index()); |
234 | 0 | return {}; |
235 | 0 | } |
236 | | |
237 | 0 | for (auto block_index = table.block_index(); block_index;) { |
238 | 0 | auto current = m_serializer.deserialize_block<Row>(block_index, table, block_index); |
239 | |
|
240 | 0 | if (current.next_block_index() == row.block_index()) { |
241 | 0 | current.set_next_block_index(row.next_block_index()); |
242 | 0 | TRY(update(current)); |
243 | 0 | break; |
244 | 0 | } |
245 | | |
246 | 0 | block_index = current.next_block_index(); |
247 | 0 | } |
248 | | |
249 | 0 | return {}; |
250 | 0 | } |
251 | | |
252 | | ErrorOr<void> Database::update(Row& tuple) |
253 | 0 | { |
254 | 0 | VERIFY(m_table_cache.get(tuple.table().key().hash()).has_value()); |
255 | | // TODO: implement table constraints such as unique, foreign key, etc. |
256 | | |
257 | 0 | m_serializer.reset(); |
258 | 0 | m_serializer.serialize_and_write<Tuple>(tuple); |
259 | | |
260 | | // TODO update indexes defined on table. |
261 | 0 | return {}; |
262 | 0 | } |
263 | | |
264 | | } |