Coverage Report

Created: 2025-09-05 06:52

/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
}