File size: 3,865 Bytes
bf20cb7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include "wayy_db/database.hpp"

#include <filesystem>
#include <mutex>

namespace fs = std::filesystem;

namespace wayy_db {

Database::Database() = default;

Database::Database(const std::string& path) : path_(path) {
    if (!path_.empty()) {
        fs::create_directories(path_);
        scan_tables();

        // Initialize WAL
        wal_ = std::make_unique<WriteAheadLog>(path_);

        // Replay any unprocessed WAL entries from a crash
        if (wal_->has_entries()) {
            wal_->replay(*this);
        }
    }
}

std::vector<std::string> Database::tables() const {
    std::shared_lock lock(mutex_);
    std::vector<std::string> names;
    names.reserve(tables_.size());
    for (const auto& [name, _] : tables_) {
        names.push_back(name);
    }
    // Also include tables on disk that aren't loaded yet
    for (const auto& [name, _] : loaded_) {
        if (!tables_.count(name)) {
            names.push_back(name);
        }
    }
    return names;
}

bool Database::has_table(const std::string& name) const {
    std::shared_lock lock(mutex_);
    return tables_.count(name) > 0 || loaded_.count(name) > 0;
}

Table& Database::table(const std::string& name) {
    // First try with shared lock (read-only)
    {
        std::shared_lock lock(mutex_);
        auto it = tables_.find(name);
        if (it != tables_.end()) {
            return it->second;
        }
    }

    // Need to lazy load - acquire exclusive lock
    std::unique_lock lock(mutex_);

    // Double-check after acquiring exclusive lock (another thread may have loaded it)
    auto it = tables_.find(name);
    if (it != tables_.end()) {
        return it->second;
    }

    // Try to load from disk
    if (is_persistent() && loaded_.count(name)) {
        tables_.emplace(name, Table::mmap(table_path(name)));
        return tables_.at(name);
    }

    throw WayyException("Table not found: " + name);
}

Table& Database::create_table(const std::string& name) {
    std::unique_lock lock(mutex_);

    if (tables_.count(name) > 0 || loaded_.count(name) > 0) {
        throw InvalidOperation("Table already exists: " + name);
    }

    tables_.emplace(name, Table(name));
    if (is_persistent()) {
        loaded_[name] = true;
    }
    return tables_.at(name);
}

void Database::add_table(Table table) {
    const std::string& name = table.name();

    std::unique_lock lock(mutex_);

    if (tables_.count(name) > 0 || loaded_.count(name) > 0) {
        throw InvalidOperation("Table already exists: " + name);
    }

    if (is_persistent()) {
        table.save(table_path(name));
        loaded_[name] = true;
    }
    tables_.emplace(name, std::move(table));
}

void Database::drop_table(const std::string& name) {
    std::unique_lock lock(mutex_);

    tables_.erase(name);
    loaded_.erase(name);

    if (is_persistent()) {
        fs::remove_all(table_path(name));
    }
}

void Database::save() {
    if (!is_persistent()) return;

    std::shared_lock lock(mutex_);
    for (auto& [name, table] : tables_) {
        table.save(table_path(name));
    }
}

void Database::refresh() {
    if (!is_persistent()) return;

    std::unique_lock lock(mutex_);
    scan_tables();
}

void Database::checkpoint() {
    if (!wal_) return;
    wal_->checkpoint(*this);
}

std::string Database::table_path(const std::string& name) const {
    return path_ + "/" + name;
}

void Database::scan_tables() {
    if (!fs::exists(path_)) return;

    for (const auto& entry : fs::directory_iterator(path_)) {
        if (entry.is_directory()) {
            std::string meta_path = entry.path().string() + "/_meta.json";
            if (fs::exists(meta_path)) {
                std::string name = entry.path().filename().string();
                loaded_[name] = false;  // Not loaded into memory yet
            }
        }
    }
}

}  // namespace wayy_db