mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
001949d77a
The handling of filesystem level errors was basically non-existing or consisting of `VERIFY_NOT_REACHED` assertions. Addressed this by * Adding `open` methods to `Heap` and `Database` which return errors. * Changing the interface of methods of these classes and clients downstream to propagate these errors. The constructors of `Heap` and `Database` don't open the underlying filesystem file anymore. The SQL statement handlers return an `SQLErrorCode::InternalError` error code if an error comes back from the lower levels. Note that some of these errors are things like duplicate index entry errors that should be caught before the SQL layer attempts to actually update the database. Added tests to catch attempts to open weird or non-existent files as databases. Finally, in between me writing this patch and submitting the PR the AK::Result<Foo, Bar> template got deprecated in favour of ErrorOr<Foo>. This resulted in more busywork.
320 lines
5.7 KiB
C++
320 lines
5.7 KiB
C++
/*
|
|
* Copyright (c) 2021, Jan de Visser <jan@de-visser.net>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <AK/ScopeGuard.h>
|
|
#include <LibSQL/BTree.h>
|
|
#include <LibSQL/Heap.h>
|
|
#include <LibSQL/Key.h>
|
|
#include <LibSQL/Meta.h>
|
|
#include <LibSQL/TupleDescriptor.h>
|
|
#include <LibSQL/Value.h>
|
|
#include <LibTest/TestCase.h>
|
|
|
|
constexpr static int keys[] = {
|
|
39,
|
|
87,
|
|
77,
|
|
42,
|
|
98,
|
|
40,
|
|
53,
|
|
8,
|
|
37,
|
|
12,
|
|
90,
|
|
72,
|
|
73,
|
|
11,
|
|
88,
|
|
22,
|
|
10,
|
|
82,
|
|
25,
|
|
61,
|
|
97,
|
|
18,
|
|
60,
|
|
68,
|
|
21,
|
|
3,
|
|
58,
|
|
29,
|
|
13,
|
|
17,
|
|
89,
|
|
81,
|
|
16,
|
|
64,
|
|
5,
|
|
41,
|
|
36,
|
|
91,
|
|
38,
|
|
24,
|
|
32,
|
|
50,
|
|
34,
|
|
94,
|
|
49,
|
|
47,
|
|
1,
|
|
6,
|
|
44,
|
|
76,
|
|
};
|
|
constexpr static u32 pointers[] = {
|
|
92,
|
|
4,
|
|
50,
|
|
47,
|
|
68,
|
|
73,
|
|
24,
|
|
28,
|
|
50,
|
|
93,
|
|
60,
|
|
36,
|
|
92,
|
|
72,
|
|
53,
|
|
26,
|
|
91,
|
|
84,
|
|
25,
|
|
43,
|
|
88,
|
|
12,
|
|
62,
|
|
35,
|
|
96,
|
|
27,
|
|
96,
|
|
27,
|
|
99,
|
|
30,
|
|
21,
|
|
89,
|
|
54,
|
|
60,
|
|
37,
|
|
68,
|
|
35,
|
|
55,
|
|
80,
|
|
2,
|
|
33,
|
|
26,
|
|
93,
|
|
70,
|
|
45,
|
|
44,
|
|
3,
|
|
66,
|
|
75,
|
|
4,
|
|
};
|
|
|
|
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::Serializer& serializer)
|
|
{
|
|
NonnullRefPtr<SQL::TupleDescriptor> tuple_descriptor = adopt_ref(*new SQL::TupleDescriptor);
|
|
tuple_descriptor->append({ "schema", "table", "key_value", SQL::SQLType::Integer, SQL::Order::Ascending });
|
|
|
|
auto root_pointer = serializer.heap().user_value(0);
|
|
if (!root_pointer) {
|
|
root_pointer = serializer.heap().new_record_pointer();
|
|
serializer.heap().set_user_value(0, root_pointer);
|
|
}
|
|
auto btree = SQL::BTree::construct(serializer, tuple_descriptor, true, root_pointer);
|
|
btree->on_new_root = [&]() {
|
|
serializer.heap().set_user_value(0, btree->root());
|
|
};
|
|
return btree;
|
|
}
|
|
|
|
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");
|
|
EXPECT(!heap->open().is_error());
|
|
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];
|
|
k.set_pointer(pointers[ix]);
|
|
btree->insert(k);
|
|
}
|
|
#ifdef LIST_TREE
|
|
btree->list_tree();
|
|
#endif
|
|
}
|
|
|
|
{
|
|
auto heap = SQL::Heap::construct("/tmp/test.db");
|
|
EXPECT(!heap->open().is_error());
|
|
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);
|
|
VERIFY(pointer_opt.has_value());
|
|
EXPECT_EQ(pointer_opt.value(), pointers[ix]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void insert_into_and_scan_btree(int num_keys)
|
|
{
|
|
ScopeGuard guard([]() { unlink("/tmp/test.db"); });
|
|
{
|
|
auto heap = SQL::Heap::construct("/tmp/test.db");
|
|
EXPECT(!heap->open().is_error());
|
|
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];
|
|
k.set_pointer(pointers[ix]);
|
|
btree->insert(k);
|
|
}
|
|
|
|
#ifdef LIST_TREE
|
|
btree->list_tree();
|
|
#endif
|
|
}
|
|
|
|
{
|
|
auto heap = SQL::Heap::construct("/tmp/test.db");
|
|
EXPECT(!heap->open().is_error());
|
|
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.size()) {
|
|
EXPECT(prev < key);
|
|
}
|
|
auto key_value = (int)key[0];
|
|
for (auto ix = 0; ix < num_keys; ix++) {
|
|
if (keys[ix] == key_value) {
|
|
EXPECT_EQ(key.pointer(), pointers[ix]);
|
|
break;
|
|
}
|
|
}
|
|
prev = key;
|
|
}
|
|
EXPECT_EQ(count, num_keys);
|
|
}
|
|
}
|
|
|
|
TEST_CASE(btree_one_key)
|
|
{
|
|
insert_and_get_to_and_from_btree(1);
|
|
}
|
|
|
|
TEST_CASE(btree_four_keys)
|
|
{
|
|
insert_and_get_to_and_from_btree(4);
|
|
}
|
|
|
|
TEST_CASE(btree_five_keys)
|
|
{
|
|
insert_and_get_to_and_from_btree(5);
|
|
}
|
|
|
|
TEST_CASE(btree_10_keys)
|
|
{
|
|
insert_and_get_to_and_from_btree(10);
|
|
}
|
|
|
|
TEST_CASE(btree_13_keys)
|
|
{
|
|
insert_and_get_to_and_from_btree(13);
|
|
}
|
|
|
|
TEST_CASE(btree_20_keys)
|
|
{
|
|
insert_and_get_to_and_from_btree(20);
|
|
}
|
|
|
|
TEST_CASE(btree_25_keys)
|
|
{
|
|
insert_and_get_to_and_from_btree(25);
|
|
}
|
|
|
|
TEST_CASE(btree_30_keys)
|
|
{
|
|
insert_and_get_to_and_from_btree(30);
|
|
}
|
|
|
|
TEST_CASE(btree_35_keys)
|
|
{
|
|
insert_and_get_to_and_from_btree(35);
|
|
}
|
|
|
|
TEST_CASE(btree_40_keys)
|
|
{
|
|
insert_and_get_to_and_from_btree(40);
|
|
}
|
|
|
|
TEST_CASE(btree_45_keys)
|
|
{
|
|
insert_and_get_to_and_from_btree(45);
|
|
}
|
|
|
|
TEST_CASE(btree_50_keys)
|
|
{
|
|
insert_and_get_to_and_from_btree(50);
|
|
}
|
|
|
|
TEST_CASE(btree_scan_one_key)
|
|
{
|
|
insert_into_and_scan_btree(1);
|
|
}
|
|
|
|
TEST_CASE(btree_scan_four_keys)
|
|
{
|
|
insert_into_and_scan_btree(4);
|
|
}
|
|
|
|
TEST_CASE(btree_scan_five_keys)
|
|
{
|
|
insert_into_and_scan_btree(5);
|
|
}
|
|
|
|
TEST_CASE(btree_scan_10_keys)
|
|
{
|
|
insert_into_and_scan_btree(10);
|
|
}
|
|
|
|
TEST_CASE(btree_scan_15_keys)
|
|
{
|
|
insert_into_and_scan_btree(15);
|
|
}
|
|
|
|
TEST_CASE(btree_scan_30_keys)
|
|
{
|
|
insert_into_and_scan_btree(15);
|
|
}
|
|
|
|
TEST_CASE(btree_scan_50_keys)
|
|
{
|
|
insert_into_and_scan_btree(50);
|
|
}
|