Serenity Operating System
1/*
2 * Copyright (c) 2021, Jan de Visser <jan@de-visser.net>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <unistd.h>
8
9#include <AK/ScopeGuard.h>
10#include <LibSQL/BTree.h>
11#include <LibSQL/Database.h>
12#include <LibSQL/Heap.h>
13#include <LibSQL/Meta.h>
14#include <LibSQL/Row.h>
15#include <LibSQL/Value.h>
16#include <LibTest/TestCase.h>
17
18NonnullRefPtr<SQL::SchemaDef> setup_schema(SQL::Database&);
19NonnullRefPtr<SQL::TableDef> setup_table(SQL::Database&);
20void insert_into_table(SQL::Database&, int);
21void verify_table_contents(SQL::Database&, int);
22void insert_and_verify(int);
23void commit(SQL::Database&);
24
25NonnullRefPtr<SQL::SchemaDef> setup_schema(SQL::Database& db)
26{
27 auto schema = SQL::SchemaDef::construct("TestSchema");
28 auto maybe_error = db.add_schema(schema);
29 EXPECT(!maybe_error.is_error());
30 return schema;
31}
32
33NonnullRefPtr<SQL::TableDef> setup_table(SQL::Database& db)
34{
35 auto schema = setup_schema(db);
36 auto table = SQL::TableDef::construct(schema, "TestTable");
37 table->append_column("TextColumn", SQL::SQLType::Text);
38 table->append_column("IntColumn", SQL::SQLType::Integer);
39 EXPECT_EQ(table->num_columns(), 2u);
40 auto maybe_error = db.add_table(table);
41 EXPECT(!maybe_error.is_error());
42 return table;
43}
44
45void insert_into_table(SQL::Database& db, int count)
46{
47 auto table = MUST(db.get_table("TestSchema", "TestTable"));
48
49 for (int ix = 0; ix < count; ix++) {
50 SQL::Row row(*table);
51 StringBuilder builder;
52 builder.appendff("Test{}", ix);
53
54 row["TextColumn"] = builder.to_deprecated_string();
55 row["IntColumn"] = ix;
56 auto maybe_error = db.insert(row);
57 EXPECT(!maybe_error.is_error());
58 }
59}
60
61void verify_table_contents(SQL::Database& db, int expected_count)
62{
63 auto table = MUST(db.get_table("TestSchema", "TestTable"));
64
65 int sum = 0;
66 int count = 0;
67 auto rows_or_error = db.select_all(*table);
68 EXPECT(!rows_or_error.is_error());
69 for (auto& row : rows_or_error.value()) {
70 StringBuilder builder;
71 builder.appendff("Test{}", row["IntColumn"].to_int<i32>().value());
72 EXPECT_EQ(row["TextColumn"].to_deprecated_string(), builder.to_deprecated_string());
73 count++;
74 sum += row["IntColumn"].to_int<i32>().value();
75 }
76 EXPECT_EQ(count, expected_count);
77 EXPECT_EQ(sum, (expected_count * (expected_count - 1)) / 2);
78}
79
80void commit(SQL::Database& db)
81{
82 auto maybe_error = db.commit();
83 EXPECT(!maybe_error.is_error());
84}
85
86void insert_and_verify(int count)
87{
88 ScopeGuard guard([]() { unlink("/tmp/test.db"); });
89 {
90 auto db = SQL::Database::construct("/tmp/test.db");
91 EXPECT(!db->open().is_error());
92 (void)setup_table(db);
93 commit(db);
94 }
95 {
96 auto db = SQL::Database::construct("/tmp/test.db");
97 EXPECT(!db->open().is_error());
98 insert_into_table(db, count);
99 commit(db);
100 }
101 {
102 auto db = SQL::Database::construct("/tmp/test.db");
103 EXPECT(!db->open().is_error());
104 verify_table_contents(db, count);
105 }
106}
107
108TEST_CASE(create_heap)
109{
110 ScopeGuard guard([]() { unlink("/tmp/test.db"); });
111 auto heap = SQL::Heap::construct("/tmp/test.db");
112 EXPECT(!heap->open().is_error());
113 EXPECT_EQ(heap->version(), SQL::Heap::current_version);
114}
115
116TEST_CASE(create_from_dev_random)
117{
118 auto heap = SQL::Heap::construct("/dev/random");
119 auto should_be_error = heap->open();
120 EXPECT(should_be_error.is_error());
121}
122
123TEST_CASE(create_from_unreadable_file)
124{
125 auto heap = SQL::Heap::construct("/etc/shadow");
126 auto should_be_error = heap->open();
127 EXPECT(should_be_error.is_error());
128}
129
130TEST_CASE(create_in_non_existing_dir)
131{
132 auto heap = SQL::Heap::construct("/tmp/bogus/test.db");
133 auto should_be_error = heap->open();
134 EXPECT(should_be_error.is_error());
135}
136
137TEST_CASE(create_database)
138{
139 ScopeGuard guard([]() { unlink("/tmp/test.db"); });
140 auto db = SQL::Database::construct("/tmp/test.db");
141 auto should_not_be_error = db->open();
142 EXPECT(!should_not_be_error.is_error());
143 commit(db);
144}
145
146TEST_CASE(add_schema_to_database)
147{
148 ScopeGuard guard([]() { unlink("/tmp/test.db"); });
149 auto db = SQL::Database::construct("/tmp/test.db");
150 EXPECT(!db->open().is_error());
151 (void)setup_schema(db);
152 commit(db);
153}
154
155TEST_CASE(get_schema_from_database)
156{
157 ScopeGuard guard([]() { unlink("/tmp/test.db"); });
158 {
159 auto db = SQL::Database::construct("/tmp/test.db");
160 EXPECT(!db->open().is_error());
161 (void)setup_schema(db);
162 commit(db);
163 }
164 {
165 auto db = SQL::Database::construct("/tmp/test.db");
166 EXPECT(!db->open().is_error());
167 auto schema_or_error = db->get_schema("TestSchema");
168 EXPECT(!schema_or_error.is_error());
169 }
170}
171
172TEST_CASE(add_table_to_database)
173{
174 ScopeGuard guard([]() { unlink("/tmp/test.db"); });
175 auto db = SQL::Database::construct("/tmp/test.db");
176 EXPECT(!db->open().is_error());
177 (void)setup_table(db);
178 commit(db);
179}
180
181TEST_CASE(get_table_from_database)
182{
183 ScopeGuard guard([]() { unlink("/tmp/test.db"); });
184 {
185 auto db = SQL::Database::construct("/tmp/test.db");
186 EXPECT(!db->open().is_error());
187 (void)setup_table(db);
188 commit(db);
189 }
190 {
191 auto db = SQL::Database::construct("/tmp/test.db");
192 EXPECT(!db->open().is_error());
193
194 auto table = MUST(db->get_table("TestSchema", "TestTable"));
195 EXPECT_EQ(table->name(), "TestTable");
196 EXPECT_EQ(table->num_columns(), 2u);
197 }
198}
199
200TEST_CASE(insert_one_into_and_select_from_table)
201{
202 insert_and_verify(1);
203}
204
205TEST_CASE(insert_two_into_table)
206{
207 insert_and_verify(2);
208}
209
210TEST_CASE(insert_10_into_table)
211{
212 insert_and_verify(10);
213}
214
215TEST_CASE(insert_100_into_table)
216{
217 insert_and_verify(100);
218}