ladybird/Tests/LibSQL/TestSqlHashIndex.cpp

334 lines
6.6 KiB
C++
Raw Normal View History

/*
* Copyright (c) 2021, Jan de Visser <jan@de-visser.net>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibSQL/HashIndex.h>
#include <LibSQL/Heap.h>
#include <LibSQL/Meta.h>
#include <LibSQL/Tuple.h>
#include <LibSQL/Value.h>
#include <LibTest/TestCase.h>
#include <unistd.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::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::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 = serializer.heap().user_value(0);
if (!directory_pointer) {
directory_pointer = serializer.heap().new_record_pointer();
serializer.heap().set_user_value(0, directory_pointer);
}
auto hash_index = SQL::HashIndex::construct(serializer, tuple_descriptor, directory_pointer);
return hash_index;
}
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");
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]);
k.set_pointer(pointers[ix]);
hash_index->insert(k);
}
#ifdef LIST_HASH_INDEX
hash_index->list_hash();
#endif
}
{
auto heap = SQL::Heap::construct("/tmp/test.db");
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);
VERIFY(pointer_opt.has_value());
EXPECT_EQ(pointer_opt.value(), pointers[ix]);
}
}
}
TEST_CASE(hash_index_one_key)
{
insert_and_get_to_and_from_hash_index(1);
}
TEST_CASE(hash_index_four_keys)
{
insert_and_get_to_and_from_hash_index(4);
}
TEST_CASE(hash_index_five_keys)
{
insert_and_get_to_and_from_hash_index(5);
}
TEST_CASE(hash_index_10_keys)
{
insert_and_get_to_and_from_hash_index(10);
}
TEST_CASE(hash_index_13_keys)
{
insert_and_get_to_and_from_hash_index(13);
}
TEST_CASE(hash_index_20_keys)
{
insert_and_get_to_and_from_hash_index(20);
}
TEST_CASE(hash_index_25_keys)
{
insert_and_get_to_and_from_hash_index(25);
}
TEST_CASE(hash_index_30_keys)
{
insert_and_get_to_and_from_hash_index(30);
}
TEST_CASE(hash_index_35_keys)
{
insert_and_get_to_and_from_hash_index(35);
}
TEST_CASE(hash_index_40_keys)
{
insert_and_get_to_and_from_hash_index(40);
}
TEST_CASE(hash_index_45_keys)
{
insert_and_get_to_and_from_hash_index(45);
}
TEST_CASE(hash_index_50_keys)
{
insert_and_get_to_and_from_hash_index(50);
}
void insert_into_and_scan_hash_index(int num_keys)
{
ScopeGuard guard([]() { unlink("/tmp/test.db"); });
{
auto heap = SQL::Heap::construct("/tmp/test.db");
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]);
k.set_pointer(pointers[ix]);
hash_index->insert(k);
}
#ifdef LIST_HASH_INDEX
hash_index->list_hash();
#endif
}
{
auto heap = SQL::Heap::construct("/tmp/test.db");
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);
}
int count = 0;
for (auto iter = hash_index->begin(); !iter.is_end(); iter++, count++) {
auto key = (*iter);
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]);
if (found[ix])
FAIL(String::formatted("Key {}, index {} already found previously", key_value, ix));
found[ix] = true;
break;
}
}
}
#ifdef LIST_HASH_INDEX
hash_index->list_hash();
#endif
EXPECT_EQ(count, num_keys);
for (auto ix = 0; ix < num_keys; ix++) {
if (!found[ix])
FAIL(String::formatted("Key {}, index {} not found", keys[ix], ix));
}
}
}
TEST_CASE(hash_index_scan_one_key)
{
insert_into_and_scan_hash_index(1);
}
TEST_CASE(hash_index_scan_four_keys)
{
insert_into_and_scan_hash_index(4);
}
TEST_CASE(hash_index_scan_five_keys)
{
insert_into_and_scan_hash_index(5);
}
TEST_CASE(hash_index_scan_10_keys)
{
insert_into_and_scan_hash_index(10);
}
TEST_CASE(hash_index_scan_15_keys)
{
insert_into_and_scan_hash_index(15);
}
TEST_CASE(hash_index_scan_20_keys)
{
insert_into_and_scan_hash_index(20);
}
TEST_CASE(hash_index_scan_30_keys)
{
insert_into_and_scan_hash_index(30);
}
TEST_CASE(hash_index_scan_40_keys)
{
insert_into_and_scan_hash_index(40);
}
TEST_CASE(hash_index_scan_50_keys)
{
insert_into_and_scan_hash_index(50);
}