Coverage Report

Created: 2026-07-25 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibSQL/Heap.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2021, Jan de Visser <jan@de-visser.net>
3
 * Copyright (c) 2023, Jelle Raaijmakers <jelle@gmta.nl>
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#pragma once
9
10
#include <AK/Array.h>
11
#include <AK/ByteString.h>
12
#include <AK/Debug.h>
13
#include <AK/HashMap.h>
14
#include <AK/RefCounted.h>
15
#include <AK/Vector.h>
16
#include <LibCore/File.h>
17
18
namespace SQL {
19
20
/**
21
 * A Block represents a single discrete chunk of 1024 bytes inside the Heap, and
22
 * acts as the container format for the actual data we are storing. This structure
23
 * is used for everything except block 0, the zero / super block.
24
 *
25
 * If data needs to be stored that is larger than 1016 bytes, Blocks are chained
26
 * together by setting the next block index and the data is reconstructed by
27
 * repeatedly reading blocks until the next block index is 0.
28
 */
29
class Block {
30
public:
31
    typedef u32 Index;
32
33
    static constexpr u32 SIZE = 1024;
34
    static constexpr u32 HEADER_SIZE = sizeof(u32) + sizeof(Index);
35
    static constexpr u32 DATA_SIZE = SIZE - HEADER_SIZE;
36
37
    Block(Index index, u32 size_in_bytes, Index next_block, ByteBuffer data)
38
0
        : m_index(index)
39
0
        , m_size_in_bytes(size_in_bytes)
40
0
        , m_next_block(next_block)
41
0
        , m_data(move(data))
42
0
    {
43
0
        VERIFY(index > 0);
44
0
    }
45
46
0
    Index index() const { return m_index; }
47
0
    u32 size_in_bytes() const { return m_size_in_bytes; }
48
0
    Index next_block() const { return m_next_block; }
49
0
    ByteBuffer const& data() const { return m_data; }
50
51
private:
52
    Index m_index;
53
    u32 m_size_in_bytes;
54
    Index m_next_block;
55
    ByteBuffer m_data;
56
};
57
58
/**
59
 * A Heap is a logical container for database (SQL) data. Conceptually a
60
 * Heap can be a database file, or a memory block, or another storage medium.
61
 * It contains datastructures, like B-Trees, hash_index tables, or tuple stores
62
 * (basically a list of data tuples).
63
 *
64
 * A Heap can be thought of the backing storage of a single database. It's
65
 * assumed that a single SQL database is backed by a single Heap.
66
 */
67
class Heap : public RefCounted<Heap> {
68
public:
69
    static constexpr u32 VERSION = 5;
70
71
    static ErrorOr<NonnullRefPtr<Heap>> create(ByteString);
72
    virtual ~Heap();
73
74
0
    ByteString const& name() const { return m_name; }
75
76
    ErrorOr<void> open();
77
    ErrorOr<size_t> file_size_in_bytes() const;
78
79
    [[nodiscard]] bool has_block(Block::Index) const;
80
    [[nodiscard]] Block::Index request_new_block_index();
81
82
0
    Block::Index schemas_root() const { return m_schemas_root; }
83
84
    void set_schemas_root(Block::Index root)
85
0
    {
86
0
        m_schemas_root = root;
87
0
        update_zero_block().release_value_but_fixme_should_propagate_errors();
88
0
    }
89
90
0
    Block::Index tables_root() const { return m_tables_root; }
91
92
    void set_tables_root(Block::Index root)
93
0
    {
94
0
        m_tables_root = root;
95
0
        update_zero_block().release_value_but_fixme_should_propagate_errors();
96
0
    }
97
98
0
    Block::Index table_columns_root() const { return m_table_columns_root; }
99
100
    void set_table_columns_root(Block::Index root)
101
0
    {
102
0
        m_table_columns_root = root;
103
0
        update_zero_block().release_value_but_fixme_should_propagate_errors();
104
0
    }
105
0
    u32 version() const { return m_version; }
106
107
    u32 user_value(size_t index) const
108
0
    {
109
0
        return m_user_values[index];
110
0
    }
111
112
    void set_user_value(size_t index, u32 value)
113
0
    {
114
0
        m_user_values[index] = value;
115
0
        update_zero_block().release_value_but_fixme_should_propagate_errors();
116
0
    }
117
118
    ErrorOr<ByteBuffer> read_storage(Block::Index);
119
    ErrorOr<void> write_storage(Block::Index, ReadonlyBytes);
120
    ErrorOr<void> free_storage(Block::Index);
121
122
    ErrorOr<void> flush();
123
124
private:
125
    explicit Heap(ByteString);
126
127
    ErrorOr<ByteBuffer> read_raw_block(Block::Index);
128
    ErrorOr<void> write_raw_block(Block::Index, ReadonlyBytes);
129
    ErrorOr<void> write_raw_block_to_wal(Block::Index, ByteBuffer&&);
130
131
    ErrorOr<Block> read_block(Block::Index);
132
    ErrorOr<void> write_block(Block const&);
133
    ErrorOr<void> free_block(Block const&);
134
135
    ErrorOr<void> read_zero_block();
136
    ErrorOr<void> initialize_zero_block();
137
    ErrorOr<void> update_zero_block();
138
139
    ByteString m_name;
140
141
    OwnPtr<Core::InputBufferedFile> m_file;
142
    Block::Index m_highest_block_written { 0 };
143
    Block::Index m_next_block { 1 };
144
    Block::Index m_schemas_root { 0 };
145
    Block::Index m_tables_root { 0 };
146
    Block::Index m_table_columns_root { 0 };
147
    u32 m_version { VERSION };
148
    Array<u32, 16> m_user_values { 0 };
149
    HashMap<Block::Index, ByteBuffer> m_write_ahead_log;
150
    Vector<Block::Index> m_free_block_indices;
151
};
152
153
}