31#include <unordered_set>
35static_assert(SQLITE_VERSION_NUMBER >= 3051003,
"Everett requires SQLite 3.51.3 or later");
72 std::vector<blob_identity>
pairs;
104 int commit(sqlite3 * db)
noexcept {
return sqlite3_exec(db,
"COMMIT",
nullptr,
nullptr,
nullptr); }
107 namespace catalog_detail {
108 using bytes = std::vector<std::byte>;
110 for (
unsigned i = 0; i != 8; ++i) out.push_back(std::byte(value >> (8 * i)));
113 number(out, value.size());
114 auto data = std::as_bytes(std::span(value.data(), value.size()));
115 out.insert(out.end(), data.begin(), data.end());
118 if (value.hex().size() != 32)
throw std::invalid_argument(
"invalid Everett catalog identity");
119 field(out, value.hex());
125 field(out, value.name);
number(out, value.generation);
pair(out, value.head);
field(out, value.owner);
127 inline void binary(
bytes & out, std::span<std::byte const> value) {
128 number(out, value.size()); out.insert(out.end(), value.begin(), value.end());
131 number(out, value.pairs.size());
132 for (
auto const & root : value.pairs)
pair(out, root);
133 number(out, value.natives.size());
140 std::sort(value.pairs.begin(), value.pairs.end(), less);
141 value.pairs.erase(std::unique(value.pairs.begin(), value.pairs.end()), value.pairs.end());
142 std::erase(value.pairs, primary);
143 std::sort(value.natives.begin(), value.natives.end(), [](
auto const & a,
auto const & b) { return a.hex() < b.hex(); });
144 value.natives.erase(std::unique(value.natives.begin(), value.natives.end()), value.natives.end());
153 std::span<std::byte const>
data;
154 [[noreturn]]
static void invalid() {
throw catalog_error(
"invalid Everett timeline outcome", SQLITE_CORRUPT); }
157 std::uint64_t value = 0;
158 for (
unsigned i = 0; i != 8; ++i) value |= std::uint64_t(std::to_integer<unsigned>(
data[i])) << (8 * i);
159 data =
data.subspan(8);
return value;
164 std::string value(
reinterpret_cast<char const *
>(
data.data()),
static_cast<std::size_t
>(size));
165 data =
data.subspan(
static_cast<std::size_t
>(size));
return value;
170 if (
name.empty() || owner.empty() || generation > std::uint64_t(std::numeric_limits<std::int64_t>::max()))
invalid();
172 catch (std::invalid_argument
const &) {
invalid(); }
178 for (std::uint64_t i = 0; i != count; ++i) {
184 for (std::uint64_t i = 0; i != count; ++i) value.natives.emplace_back(
field());
191 bytes checkpoint(
data.begin(),
data.begin() +
static_cast<std::size_t
>(size));
192 data =
data.subspan(
static_cast<std::size_t
>(size));
194 return {std::move(head), std::move(checkpoint), std::move(retained)};
198 inline void name(std::string_view value) {
199 if (value.empty())
throw std::invalid_argument(
"empty Everett catalog name");
201 inline std::int64_t
integer(std::uint64_t value) {
202 if (value > std::uint64_t(std::numeric_limits<std::int64_t>::max()))
203 throw std::length_error(
"Everett catalog count exceeds SQLite integer range");
204 return static_cast<std::int64_t
>(value);
207 switch (code & 255) {
208 case SQLITE_IOERR:
case SQLITE_FULL:
case SQLITE_CORRUPT:
case SQLITE_NOTADB:
209 case SQLITE_NOMEM:
case SQLITE_CANTOPEN:
case SQLITE_PROTOCOL:
return true;
210 default:
return false;
213 [[noreturn]]
inline void fail(sqlite3 * db,
int code) {
214 throw catalog_error(db && sqlite3_errcode(db) != SQLITE_OK ? sqlite3_errmsg(db) : sqlite3_errstr(code), code);
216 inline void exec(sqlite3 * db,
char const * sql) {
217 auto code = sqlite3_exec(db, sql,
nullptr,
nullptr,
nullptr);
218 if (code != SQLITE_OK)
fail(db, code);
224 auto code = sqlite3_prepare_v2(
db, sql, -1, &
value,
nullptr);
225 if (code != SQLITE_OK) {
233 void text(
int index, std::string_view data) {
234 auto code = sqlite3_bind_text64(
value, index, data.data(), data.size(), SQLITE_TRANSIENT, SQLITE_UTF8);
235 if (code != SQLITE_OK)
fail(
db, code);
237 void blob(
int index, std::span<std::byte const> data) {
239 static constexpr std::byte
empty{};
240 auto code = sqlite3_bind_blob64(
value, index, data.empty() ? &
empty : data.data(), data.size(), SQLITE_TRANSIENT);
241 if (code != SQLITE_OK)
fail(
db, code);
243 void key(
int index, std::string_view data) {
244 blob(index, std::as_bytes(std::span(data.data(), data.size())));
247 auto code = sqlite3_bind_int64(
value, index,
number);
248 if (code != SQLITE_OK)
fail(
db, code);
251 auto code = sqlite3_bind_null(
value, index);
252 if (code != SQLITE_OK)
fail(
db, code);
255 auto code = sqlite3_step(
value);
256 if (code == SQLITE_ROW)
return true;
257 if (code == SQLITE_DONE)
return false;
260 void done() {
if (
row())
throw std::logic_error(
"unexpected SQLite result row"); }
261 std::string
text(
int column)
const {
262 auto data = sqlite3_column_text(
value, column);
263 auto size = sqlite3_column_bytes(
value, column);
264 if (!data && (size || sqlite3_errcode(
db) == SQLITE_NOMEM))
fail(
db, SQLITE_NOMEM);
265 return size ? std::string(
reinterpret_cast<char const *
>(data), std::size_t(size)) : std::string{};
267 std::string
key(
int column)
const {
268 auto data =
static_cast<char const *
>(sqlite3_column_blob(
value, column));
269 auto size = sqlite3_column_bytes(
value, column);
270 if (!data && (size || sqlite3_errcode(
db) == SQLITE_NOMEM))
fail(
db, SQLITE_NOMEM);
271 return size ? std::string(data, std::size_t(size)) : std::string{};
274 auto data =
static_cast<std::byte
const *
>(sqlite3_column_blob(
value, column));
275 auto size = sqlite3_column_bytes(
value, column);
276 if (!data && (size || sqlite3_errcode(
db) == SQLITE_NOMEM))
fail(
db, SQLITE_NOMEM);
277 return size ?
bytes(data, data + size) :
bytes{};
279 std::int64_t
integer(
int column)
const {
return sqlite3_column_int64(
value, column); }
280 bool is_null(
int column)
const {
return sqlite3_column_type(
value, column) == SQLITE_NULL; }
283CREATE TABLE catalog_info(singleton INTEGER PRIMARY KEY CHECK(singleton=1), version INTEGER NOT NULL CHECK(version=1), identity TEXT NOT NULL CHECK(length(identity)=32), policy BLOB NOT NULL) STRICT;
284CREATE TABLE operations(id BLOB PRIMARY KEY, kind TEXT NOT NULL, request BLOB NOT NULL, outcome BLOB NOT NULL) STRICT;
285CREATE TABLE owners(kind TEXT NOT NULL CHECK(kind IN('attempt','save','reader')), id BLOB NOT NULL, PRIMARY KEY(kind,id)) STRICT;
286CREATE TABLE attempts(id TEXT PRIMARY KEY CHECK(length(id)=32), owner BLOB NOT NULL UNIQUE) STRICT;
287CREATE TABLE objects(id TEXT PRIMARY KEY CHECK(length(id)=32), kind INTEGER NOT NULL CHECK(kind IN(0,1)), attempt TEXT NOT NULL REFERENCES attempts(id), bytes INTEGER, crc INTEGER, barrier INTEGER,
288 CHECK((bytes IS NULL AND crc IS NULL AND barrier IS NULL) OR (bytes IS NOT NULL AND crc IS NOT NULL AND barrier IS NOT NULL AND bytes>=96 AND crc>=0 AND crc<=4294967295 AND barrier IN(0,1)))) STRICT;
289CREATE TABLE pairs(index_id TEXT PRIMARY KEY REFERENCES objects(id), native_id TEXT NOT NULL REFERENCES objects(id), target_native TEXT, target_index TEXT, native_count INTEGER NOT NULL CHECK(native_count>=0), borrowed_count INTEGER NOT NULL CHECK(borrowed_count>=0), virtual_count INTEGER NOT NULL CHECK(virtual_count>=0),
290 UNIQUE(native_id,index_id), FOREIGN KEY(target_native,target_index) REFERENCES pairs(native_id,index_id), CHECK((target_native IS NULL)=(target_index IS NULL)), CHECK(native_count<=virtual_count AND borrowed_count=virtual_count-native_count)) STRICT;
291CREATE TABLE owner_objects(owner_kind TEXT NOT NULL, owner_id BLOB NOT NULL, object_id TEXT NOT NULL REFERENCES objects(id), PRIMARY KEY(owner_kind,owner_id,object_id), FOREIGN KEY(owner_kind,owner_id) REFERENCES owners(kind,id)) STRICT;
292CREATE TABLE owner_roots(owner_kind TEXT NOT NULL, owner_id BLOB NOT NULL, native_id TEXT NOT NULL, index_id TEXT NOT NULL, PRIMARY KEY(owner_kind,owner_id,native_id,index_id), FOREIGN KEY(owner_kind,owner_id) REFERENCES owners(kind,id), FOREIGN KEY(native_id,index_id) REFERENCES pairs(native_id,index_id)) STRICT;
293CREATE TABLE saves(name BLOB PRIMARY KEY, native_id TEXT NOT NULL, index_id TEXT NOT NULL, FOREIGN KEY(native_id,index_id) REFERENCES pairs(native_id,index_id)) STRICT;
294CREATE TRIGGER sealed_immutable BEFORE UPDATE ON objects WHEN OLD.bytes IS NOT NULL OR NEW.id<>OLD.id OR NEW.kind<>OLD.kind OR NEW.attempt<>OLD.attempt BEGIN SELECT RAISE(ABORT,'immutable object'); END;
299 "CREATE TABLE completed_native_merges(domain BLOB NOT NULL,older TEXT NOT NULL REFERENCES objects(id),newer TEXT NOT NULL REFERENCES objects(id),output TEXT NOT NULL REFERENCES objects(id),owner_kind TEXT NOT NULL CHECK(owner_kind='reader'),owner_id BLOB NOT NULL UNIQUE,PRIMARY KEY(domain,older,newer),FOREIGN KEY(owner_kind,owner_id,output) REFERENCES owner_objects(owner_kind,owner_id,object_id)) STRICT";
301 "CREATE TABLE private_scopes(id TEXT PRIMARY KEY CHECK(length(id)=32)) STRICT";
303 "CREATE TABLE private_attempts(attempt TEXT PRIMARY KEY REFERENCES attempts(id),scope TEXT NOT NULL REFERENCES private_scopes(id)) STRICT";
305 "CREATE TABLE released_private_scopes(scope TEXT PRIMARY KEY REFERENCES private_scopes(id)) STRICT";
309 "CREATE VIEW live_owner_objects AS SELECT p.* FROM owner_objects p WHERE p.owner_kind<>'attempt' OR NOT EXISTS(SELECT 1 FROM attempts a JOIN private_attempts s ON s.attempt=a.id JOIN released_private_scopes r ON r.scope=s.scope WHERE a.owner=p.owner_id)";
311 "CREATE VIEW live_owner_roots AS SELECT p.* FROM owner_roots p WHERE p.owner_kind<>'attempt' OR NOT EXISTS(SELECT 1 FROM attempts a JOIN private_attempts s ON s.attempt=a.id JOIN released_private_scopes r ON r.scope=s.scope WHERE a.owner=p.owner_id)";
315 std::string result =
schema;
316 if (version == 1)
return result;
317 if (version < 2 || version > 4)
throw std::invalid_argument(
"unsupported Everett catalog version");
318 auto replace = [&](std::string_view before, std::string_view after) {
319 result.replace(result.find(before), before.size(), after);
321 replace(
"CHECK(version=1)",
"CHECK(version=" + std::to_string(version) +
")");
322 replace(
"IN('attempt','save','reader')",
"IN('attempt','save','reader','timeline')");
323 result.insert(result.find(
"CREATE TRIGGER sealed_immutable"), R
"sql(CREATE TABLE timelines(name BLOB PRIMARY KEY, source_name BLOB, source_generation INTEGER,
324 CHECK((source_name IS NULL)=(source_generation IS NULL)), FOREIGN KEY(source_name,source_generation) REFERENCES timeline_generations(name,generation)) STRICT;
325CREATE TABLE timeline_generations(name BLOB NOT NULL REFERENCES timelines(name), generation INTEGER NOT NULL CHECK(generation>=0), native_id TEXT NOT NULL, index_id TEXT NOT NULL, owner_kind TEXT NOT NULL CHECK(owner_kind='timeline'), owner_id BLOB NOT NULL UNIQUE,
326 PRIMARY KEY(name,generation), FOREIGN KEY(owner_kind,owner_id,native_id,index_id) REFERENCES owner_roots(owner_kind,owner_id,native_id,index_id)) STRICT;
329 auto first = result.find(
"CREATE TABLE pairs(");
330 auto last = result.find(
';', first);
331 result.replace(first, last + 1 - first, R
"sql(CREATE TABLE pairs(index_id TEXT PRIMARY KEY REFERENCES objects(id), native_id TEXT NOT NULL REFERENCES objects(id), target_native TEXT, target_index TEXT, native_count INTEGER NOT NULL CHECK(native_count>=0), borrowed_count INTEGER NOT NULL CHECK(borrowed_count>=0), virtual_count INTEGER NOT NULL CHECK(virtual_count>=0),
332 layout INTEGER NOT NULL DEFAULT 2 CHECK(layout IN(2,3)), secondary_native TEXT REFERENCES objects(id), secondary_native_count INTEGER NOT NULL DEFAULT 0 CHECK(secondary_native_count>=0), secondary_borrowed_count INTEGER NOT NULL DEFAULT 0 CHECK(secondary_borrowed_count>=0),
333 UNIQUE(native_id,index_id), FOREIGN KEY(target_native,target_index) REFERENCES pairs(native_id,index_id), CHECK((target_native IS NULL)=(target_index IS NULL)),
334 CHECK(native_count<=virtual_count AND secondary_borrowed_count<=virtual_count-native_count AND borrowed_count=virtual_count-native_count-secondary_borrowed_count),
335 CHECK(secondary_native IS NOT NULL OR (secondary_native_count=0 AND secondary_borrowed_count=0)), CHECK(layout=3 OR (secondary_native IS NULL AND secondary_native_count=0 AND secondary_borrowed_count=0))) STRICT;)sql");
338 result.insert(result.find(
"CREATE TRIGGER sealed_immutable"), R
"sql(CREATE TABLE session_checkpoints(name BLOB NOT NULL, generation INTEGER NOT NULL, checkpoint BLOB NOT NULL,
339PRIMARY KEY(name,generation), FOREIGN KEY(name,generation) REFERENCES timeline_generations(name,generation)) STRICT;
340CREATE TABLE session_saves(name BLOB PRIMARY KEY REFERENCES saves(name), session_name BLOB NOT NULL, generation INTEGER NOT NULL,
341 FOREIGN KEY(session_name,generation) REFERENCES session_checkpoints(name,generation)) STRICT;
352 static_assert(std::is_nothrow_move_constructible_v<Ops>);
353 static_assert(
noexcept(std::declval<Ops &>().commit(std::declval<sqlite3 *>())));
359 :
db_(std::exchange(other.db_,
nullptr)),
root_(std::move(other.root_)),
380 auto result =
connect(std::filesystem::canonical(
root), options, std::move(ops));
381 result.schema_version_ = result.detect_schema_version();
382 result.validate_schema();
383 catalog_detail::statement info(result.db_,
"SELECT version,identity,policy FROM catalog_info WHERE singleton=1");
384 if (!info.row() || info.integer(0) != result.schema_version_ || !
compatible_policy(info.blob(2)))
385 throw std::invalid_argument(
"Everett catalog schema or policy mismatch");
386 result.identity_.emplace(info.text(1));
387 if (info.row())
throw std::invalid_argument(
"multiple Everett catalog identities");
388 if (options.private_scope) result.set_private_scope(*options.private_scope);
395 static char const *
source_id() noexcept {
return sqlite3_sourceid(); }
396 std::filesystem::path
const &
root() const & noexcept {
return root_; }
397 std::filesystem::path
const &
root() const && = delete;
400 if (!
identity_)
throw std::logic_error(
"uninitialized Everett catalog identity");
411 insert.
text(1,
id.hex()); insert.
done();
428 exists.
text(1,
id.hex());
429 if (!exists.
row())
throw std::invalid_argument(
"unknown private construction scope");
431 insert.
text(1,
id.hex()); insert.
done();
441 catalog_detail::statement query(
db_,
"SELECT EXISTS(SELECT 1 FROM released_private_scopes WHERE scope=id) FROM private_scopes WHERE id=?");
442 query.
text(1,
id.hex());
450 std::vector<object_id> result;
451 if (!
has_table(
"private_scopes"))
return result;
453 catalog_detail::statement query(
db_,
"SELECT id FROM private_scopes WHERE NOT EXISTS(SELECT 1 FROM released_private_scopes WHERE scope=id) ORDER BY id");
454 while (query.
row()) result.emplace_back(query.
text(0));
466 query.
text(1,
id.hex());
468 throw std::invalid_argument(
"object has no completed seal in this catalog");
470 if (size < 96 || crc < 0 || crc > 0xffffffffLL || barrier < 0 || barrier > 1)
471 throw std::invalid_argument(
"invalid completed seal metadata");
484 auto barrier =
static_cast<unsigned>(receipt.
barrier);
485 if (barrier > 1)
throw std::invalid_argument(
"unsupported seal barrier");
487 throw std::invalid_argument(
"seal receipt names another catalog path");
494 throw std::invalid_argument(
"seal receipt disagrees with catalog reservation");
501 return read([&]() -> std::optional<catalog_operation> {
504 if (!query.
row())
return std::nullopt;
508 std::optional<blob_identity>
find_save(std::string_view name)
const {
510 return read([&]() -> std::optional<blob_identity> {
513 if (!query.
row())
return std::nullopt;
519 std::span<blob_identity const> inputs, std::span<catalog_object_reservation const> outputs) {
521 if (outputs.empty())
throw std::invalid_argument(
"reservation has no outputs");
527 for (
auto const & output : outputs) {
541 for (
auto const & input : inputs)
add_root(
"attempt", owner, input);
542 for (
auto const & output : outputs) {
546 pin.
key(1, owner); pin.
text(2, output.object.hex()); pin.
done();
554 auto path = std::filesystem::canonical(receipt.
path);
572 auto path = std::filesystem::canonical(receipt.
path);
575 transaction(op,
"remember_native_merge", request, [&] {
579 if (!
has_table(
"completed_native_merges")) {
581 for (
auto action : {
"UPDATE",
"DELETE"}) {
582 std::string sql =
"CREATE TRIGGER immutable_completed_native_merges_" + std::string(action) +
583 " BEFORE " + action +
" ON completed_native_merges BEGIN SELECT RAISE(ABORT,'immutable catalog row'); END";
593 row.
blob(1, domain); row.
text(2, key.older.hex()); row.
text(3, key.newer.hex());
613 if (!
has_table(
"completed_native_merges"))
return false;
617 auto outcome =
transaction(op,
"acquire_native_merge", request, [&] {
619 if (!output)
throw std::invalid_argument(
"completed native merge disappeared");
625 object_id output(reader.field()); reader.end();
636 throw std::invalid_argument(
"unsupported catalog admission mode");
637 auto head = source.
head();
638 if (!head)
throw std::invalid_argument(
"empty catalog query root");
639 auto opened = open_mapped_query<P>(
root_, head->identity());
641 std::vector<typename mapped_blob<P>::pair_type> chain;
644 for (
auto current = opened.head(); current; current = current->target()) {
645 chain.push_back(current);
652 for (
auto entry = chain.rbegin(); entry != chain.rend(); ++entry) {
653 auto const & pair = **entry;
656 auto target = pair.target();
658 "SELECT native_id,target_native,target_index,native_count,borrowed_count,virtual_count,layout FROM pairs WHERE index_id=?" :
659 "SELECT native_id,target_native,target_index,native_count,borrowed_count,virtual_count,2 FROM pairs WHERE index_id=?");
660 old.
text(1, pair.identity().index.hex());
662 if (old.
integer(6) != 2 || old.
text(0) != pair.identity().native.hex() || old.
is_null(1) != !target ||
663 (target && (old.
text(1) != target->identity().native.hex() || old.
text(2) != target->identity().index.hex())) ||
667 throw std::invalid_argument(
"registered index identity already has different contents");
670 catalog_detail::statement insert(
db_,
"INSERT INTO pairs(index_id,native_id,target_native,target_index,native_count,borrowed_count,virtual_count) VALUES(?,?,?,?,?,?,?)");
671 insert.
text(1, pair.identity().index.hex()); insert.
text(2, pair.identity().native.hex());
672 if (target) { insert.
text(3, target->identity().native.hex()); insert.
text(4, target->identity().index.hex()); }
673 else { insert.
null(3); insert.
null(4); }
691 template <
class Mapped>
void register_graph(std::string_view op, std::shared_ptr<Mapped const>
const & source,
693 std::array roots{source}; register_graphs<Mapped>(op, roots, admission);
698 std::span<std::shared_ptr<Mapped const>
const> roots,
700 static_assert(std::is_same_v<P, typename Mapped::policy_type>);
703 throw std::logic_error(
"COLA admission requires catalog version 3; no automatic migration");
705 throw std::invalid_argument(
"unsupported catalog admission mode");
706 if (roots.empty())
throw std::invalid_argument(
"empty COLA graph set");
707 std::vector<std::shared_ptr<Mapped const>> canonical(roots.begin(), roots.end());
708 for (
auto const &
root : canonical)
if (!
root)
throw std::invalid_argument(
"empty COLA catalog graph");
709 std::sort(canonical.begin(), canonical.end(), [](
auto const & a,
auto const & b) {
710 return a->identity().index.hex() < b->identity().index.hex();
712 for (std::size_t i = 1; i != canonical.size(); ++i)
715 throw std::invalid_argument(
"conflicting COLA graph root identities");
716 canonical.erase(std::unique(canonical.begin(), canonical.end(), [](
auto const & a,
auto const & b) {
717 return a->identity() == b->identity();
718 }), canonical.end());
721 std::vector<std::shared_ptr<Mapped const>> chain;
722 std::unordered_set<std::string> seen;
726 for (
auto const &
root : canonical) {
728 auto opened = resolver.
pair(
root->identity());
730 std::vector<std::shared_ptr<Mapped const>> pending;
731 for (
auto current = opened; current && seen.insert(current->identity().index.hex()).second;
732 current = current->main_target()) pending.push_back(current);
733 chain.insert(chain.end(), pending.rbegin(), pending.rend());
735 for (
auto const & current : chain) {
736 auto view = current->view();
737 auto main = current->main_target();
738 auto secondary = current->secondary_target();
744 for (
auto count : {view.native().size(), view.borrowed(0).size(), view.borrowed(1).size(),
748 transaction(op,
"register_cola_graphs", request, [&] {
749 for (
auto const & entry : chain) {
750 auto const & pair = *entry;
751 auto view = pair.view();
752 auto main = pair.main_target();
753 auto secondary = pair.secondary_target();
754 auto const & secondary_id = pair.index_object()->secondary_id();
757 view.native().size(), view.borrowed(0).size(), view.borrowed(1).size(), secondary_count, view.virtual_size()});
771 auto prepared = prepare_cola_pair<Mapped>(
id);
773 transaction(op,
"register_cola_pair", request, [&] {
785 if (receipt.
object !=
id.index)
throw std::invalid_argument(
"pair receipt names another index");
786 auto path = std::filesystem::canonical(receipt.
path);
789 throw std::invalid_argument(
"seal receipt names another catalog path");
790 auto prepared = prepare_cola_pair<Mapped>(
id, &receipt);
797 return std::move(prepared.index);
806 if (native_receipt.
object !=
id.native || index_receipt.
object !=
id.index ||
id.native ==
id.index)
807 throw std::invalid_argument(
"pair receipts name different objects");
808 auto native_path = std::filesystem::canonical(native_receipt.
path);
809 auto index_path = std::filesystem::canonical(index_receipt.
path);
812 throw std::invalid_argument(
"seal receipt names another catalog path");
816 auto prepared = prepare_cola_pair<Mapped>(
id, &index_receipt, &native_receipt);
818 auto native = std::make_shared<typename Mapped::native_type const>(std::move(prepared.native));
819 transaction(op,
"seal_native_cola_pair", request, [&] {
825 return std::pair{std::move(
native), std::move(prepared.index)};
834 if (!prepared.
row() || std::uint64_t(prepared.
integer(0)) > P::group_size)
835 throw std::invalid_argument(
"save requires a registered prepared head");
847 if (!head)
throw std::invalid_argument(
"unknown Everett save");
853 if (!head)
throw std::logic_error(
"committed save disappeared");
854 return {*head, std::string(reader_owner)};
857 std::optional<catalog_timeline_head>
find_timeline(std::string_view name)
const {
866 auto outcome =
transaction(op,
"create_timeline", request, [&] {
869 insert.
key(1, name); insert.
done();
883 auto outcome =
transaction(op,
"fork_timeline", request, [&] {
885 if (!actual || *actual != source)
throw std::invalid_argument(
"fork source is not this exact timeline generation");
902 auto outcome =
transaction(op,
"publish_timeline", request, [&] {
904 if (!current)
throw std::invalid_argument(
"unknown Everett timeline");
906 throw std::invalid_argument(
"publish a named session together with its checkpoint");
910 if (expected.
generation == std::uint64_t(std::numeric_limits<std::int64_t>::max()))
911 throw std::length_error(
"Everett timeline generation exhausted");
920 auto head = reader.timeline(); reader.end();
925 std::optional<catalog_session_head>
find_session(std::string_view name)
const {
927 return read([&]() -> std::optional<catalog_session_head> {
929 if (!head)
return std::nullopt;
940 auto outcome =
transaction(op,
"create_session", request, [&] {
943 insert.key(1, name); insert.done();
956 auto outcome =
transaction(op,
"publish_session", request, [&] {
958 if (!
timeline)
throw std::invalid_argument(
"unknown Everett session");
963 if (current.timeline.generation == std::uint64_t(std::numeric_limits<std::int64_t>::max()))
964 throw std::length_error(
"Everett session generation exhausted");
966 candidate, checkpoint, auxiliary);
972 catalog_detail::outcome_reader reader{outcome};
975 auto head = reader.session(); reader.end();
976 return catalog_session_publication{
published != 0, std::move(head)};
985 auto outcome =
transaction(op,
"fork_session", request, [&] {
988 throw std::invalid_argument(
"fork source is not this exact session generation");
1006 throw std::invalid_argument(
"save source is not this exact session generation");
1020 return read([&]() -> std::optional<catalog_session_head> {
1023 if (!saved.
row())
return std::nullopt;
1025 if (!head)
throw catalog_error(
"saved session generation disappeared", SQLITE_CORRUPT);
1039 std::shared_ptr<typename Mapped::index_type const>
index;
1046 return mapping.slice(0, mapping.size());
1051 static_assert(std::is_same_v<P, typename Mapped::policy_type>);
1052 if (
schema_version_ < 3)
throw std::logic_error(
"COLA admission requires catalog version 3");
1053 using native_type =
typename Mapped::native_type;
1054 using index_type =
typename Mapped::index_type;
1055 using view_type =
decltype(std::declval<Mapped const &>().view());
1059 if (index->native_id() !=
id.native)
throw std::invalid_argument(
"COLA pair native identity mismatch");
1068 if (index->secondary_id()) {
1073 auto main_samples =
read([&]() -> std::uint64_t {
1074 if (!index->main_id())
return 0;
1075 auto const &
main = *index->main_id();
1079 throw std::invalid_argument(
"COLA main target is not this registered pair");
1080 auto count = std::uint64_t(row.
integer(1));
1081 return count / P::group_size + (count % P::group_size != 0);
1084 if (index->borrowed(0).size() != main_samples || index->borrowed(1).size() !=
1085 secondary_count / P::group_size + (secondary_count % P::group_size != 0))
1086 throw std::invalid_argument(
"COLA pair sample count disagrees with registered target");
1087 view_type view{
native.view(), {index->borrowed(0), index->borrowed(1)},
1088 {index->interleave(0), index->interleave(1)}, {index->false_borrow_bits(0), index->false_borrow_bits(1)},
1089 {index->cut_lcps(0), index->cut_lcps(1)}, index->virtual_size()};
1091 view.borrowed(0).size(), view.borrowed(1).size(), secondary_count, view.virtual_size()};
1092 return {std::move(index), std::move(files), std::move(descriptor), std::move(
native)};
1104 std::filesystem::path
const & path)
const {
1108 auto barrier =
static_cast<unsigned>(receipt.
barrier);
1109 if (barrier > 1)
throw std::invalid_argument(
"unsupported seal barrier");
1119 throw std::invalid_argument(
"object is not this attempt's unsealed reservation");
1122 throw std::invalid_argument(
"seal receipt names another catalog path");
1135 throw std::invalid_argument(
"object is not this attempt's unsealed reservation");
1152 catalog_detail::statement old(
db_,
"SELECT native_id,target_native,target_index,native_count,borrowed_count,virtual_count,layout,secondary_native,secondary_native_count,secondary_borrowed_count FROM pairs WHERE index_id=?");
1153 old.
text(1, value.id.index.hex());
1155 if (old.
integer(6) != 3 || old.
text(0) != value.id.native.hex() ||
1156 old.
is_null(1) != !value.main || old.
is_null(2) != !value.main ||
1157 (value.main && (old.
text(1) != value.main->native.hex() || old.
text(2) != value.main->index.hex())) ||
1158 old.
is_null(7) != !value.secondary || (value.secondary && old.
text(7) != value.secondary->hex()) ||
1164 throw std::invalid_argument(
"registered COLA index identity already has different contents");
1167 catalog_detail::statement insert(
db_,
"INSERT INTO pairs(index_id,native_id,target_native,target_index,native_count,borrowed_count,virtual_count,layout,secondary_native,secondary_native_count,secondary_borrowed_count) VALUES(?,?,?,?,?,?,?,3,?,?,?)");
1168 insert.
text(1, value.id.index.hex()); insert.
text(2, value.id.native.hex());
1169 if (value.main) { insert.
text(3, value.main->native.hex()); insert.
text(4, value.main->index.hex()); }
1170 else { insert.
null(3); insert.
null(4); }
1174 if (value.secondary) insert.
text(8, value.secondary->hex());
else insert.
null(8);
1187 "catalog_info",
"operations",
"owners",
"attempts",
"pairs",
"owner_objects",
"owner_roots",
"saves",
1188 "timelines",
"timeline_generations",
"session_checkpoints",
"session_saves",
"completed_native_merges",
1189 "private_scopes",
"private_attempts",
"released_private_scopes"
1197 auto location = std::filesystem::canonical(
root);
1198#if defined(__APPLE__) || defined(__linux__)
1200 auto path = location /
"catalog.sqlite3";
1201 int fd =
::open(path.c_str(), O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC | O_NOFOLLOW, 0600);
1202 if (fd < 0)
throw std::system_error(errno, std::generic_category(),
"create Everett catalog");
1203 if (::close(fd))
throw std::system_error(errno, std::generic_category(),
"close new Everett catalog");
1204 auto result =
connect(location, options, std::move(ops),
true);
1205 result.schema_version_ = version;
1206 result.transaction(
"",
"initialize", {}, [&] {
1212 if (!result.has_table(table))
continue;
1213 for (
auto action : {
"UPDATE",
"DELETE"}) {
1214 std::string sql =
"CREATE TRIGGER immutable_" + std::string(table) +
"_" + action +
1215 " BEFORE " + action +
" ON " + table +
" BEGIN SELECT RAISE(ABORT,'immutable catalog row'); END";
1219 catalog_detail::exec(result.db_,
"CREATE TRIGGER objects_no_delete BEFORE DELETE ON objects BEGIN SELECT RAISE(ABORT,'retained object'); END");
1225 int directory = barriers.open_root(location);
1226 if (directory < 0)
throw std::system_error(errno, std::generic_category(),
"open catalog directory");
1227 if (barriers.sync_directory(directory)) {
1228 int error = errno; barriers.close(directory);
1229 throw std::system_error(error, std::generic_category(),
"sync catalog directory; initialization outcome unknown");
1231 if (barriers.close(directory))
1232 throw std::system_error(errno, std::generic_category(),
"close catalog directory; initialization outcome unknown");
1235 (void)location; (void)options; (void)ops; (void)version;
1236 throw std::system_error(std::make_error_code(std::errc::operation_not_supported),
"Everett catalog creation requires POSIX directory barriers");
1240 if (!
db_ ||
poisoned_)
throw std::logic_error(
"Everett catalog handle is inactive or poisoned");
1244 if (
schema_version_ < 2)
throw std::logic_error(
"Everett timelines require catalog version 2 or later; no automatic migration");
1249 throw std::logic_error(
"Everett named sessions require catalog version 4; no automatic migration");
1252 if (table ==
"completed_native_merges" || table ==
"private_scopes" ||
1253 table ==
"private_attempts" || table ==
"released_private_scopes") {
1257 query.
text(1, table);
return query.
row();
1259 if (table ==
"session_checkpoints" || table ==
"session_saves")
return schema_version_ >= 4;
1260 if (table ==
"timelines" || table ==
"timeline_generations")
return schema_version_ >= 2;
1270 auto head = reader.timeline(); reader.end();
return head;
1276 auto head = reader.session(); reader.end();
return head;
1279 std::optional<catalog_detail::bytes>
checkpoint_at(std::string_view name, std::uint64_t generation)
const {
1282 if (!query.
row())
return std::nullopt;
1283 return query.
blob(0);
1287 if (!checkpoint)
throw std::invalid_argument(
"timeline is not a named Everett session");
1289 return {std::move(head), std::move(*checkpoint), std::move(retained)};
1293 catalog_detail::statement pairs(
db_,
"SELECT native_id,index_id FROM owner_roots WHERE owner_kind='timeline' AND owner_id=? ORDER BY native_id,index_id");
1295 while (pairs.
row()) {
1297 if (pair != head.
head) value.pairs.push_back(std::move(pair));
1299 catalog_detail::statement natives(
db_,
"SELECT object_id FROM owner_objects WHERE owner_kind='timeline' AND owner_id=? ORDER BY object_id");
1301 while (natives.
row()) value.natives.emplace_back(natives.
text(0));
1305 for (
auto const & pair : value.pairs)
add_root(
kind, owner, pair);
1306 for (
auto const &
native : value.natives) {
1312 std::optional<catalog_timeline_head>
timeline_at(std::string_view name,
1313 std::optional<std::uint64_t> generation = {})
const {
1315 "SELECT generation,native_id,index_id,owner_id FROM timeline_generations WHERE name=? AND generation=?" :
1316 "SELECT generation,native_id,index_id,owner_id FROM timeline_generations WHERE name=? ORDER BY generation DESC LIMIT 1");
1319 if (!query.
row())
return std::nullopt;
1326 if (!query.
row() || std::uint64_t(query.
integer(0)) > P::group_size)
1327 throw std::invalid_argument(
"timeline requires a registered prepared head");
1331 std::string owner(
reinterpret_cast<char const *
>(encoded.data()), encoded.size());
1336 return {std::string(name), generation, head, std::move(owner)};
1344 insert.
blob(3, checkpoint); insert.
done();
1345 return {std::move(
timeline), {checkpoint.begin(), checkpoint.end()}, auxiliary};
1347 template<
class F>
auto read(F && action)
const {
1348 try {
return action(); }
1357 for (
auto value : {std::uint64_t(P::unit), P::group_size, P::codec_block_size,
1358 std::uint64_t(P::backspace_code), P::backspace_parameter,
1359 std::uint64_t(P::fixed_width), P::value_width.value_or(0)})
1367 if (stored.size() != 7 *
sizeof(std::uint64_t))
return false;
1370 if (fixed > 1 || (!fixed && width))
return false;
1371 auto expected =
policy();
1372 return std::equal(stored.begin(), stored.begin() + 40, expected.begin());
1375 if (sqlite3_libversion_number() < 3051003 || !sqlite3_threadsafe())
1376 throw std::runtime_error(
"Everett requires thread-safe SQLite 3.51.3 or later");
1377 if (options.
busy_timeout_ms < 0)
throw std::invalid_argument(
"negative SQLite busy timeout");
1380 bool initialize =
false) {
1382 sqlite3 * db =
nullptr;
1383 auto path = (
root /
"catalog.sqlite3").
string();
1384 auto code = sqlite3_open_v2(path.c_str(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_EXRESCODE | SQLITE_OPEN_NOFOLLOW,
nullptr);
1387 if (!sqlite3_db_mutex(db))
1388 throw std::runtime_error(
"Everett requires a serialized SQLite connection");
1389 sqlite3_extended_result_codes(db, 1);
1393 code = sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, &setting);
1394 if (code != SQLITE_OK || setting != 1)
catalog_detail::fail(db, code == SQLITE_OK ? SQLITE_ERROR : code);
1395 catalog_detail::exec(db,
"PRAGMA trusted_schema=OFF; PRAGMA foreign_keys=ON; PRAGMA synchronous=FULL; PRAGMA fullfsync=ON; PRAGMA checkpoint_fullfsync=ON;");
1400 if (!journal.
row() || journal.
text(0) !=
"wal")
throw std::runtime_error(
"Everett requires SQLite WAL mode");
1401 for (
auto sql : {
"PRAGMA foreign_keys",
"PRAGMA synchronous",
"PRAGMA fullfsync",
"PRAGMA checkpoint_fullfsync",
"PRAGMA trusted_schema"}) {
1403 auto expected = std::string_view(sql) ==
"PRAGMA synchronous" ? 2 :
1404 std::string_view(sql) ==
"PRAGMA trusted_schema" ? 0 : 1;
1405 if (!check.
row() || check.
integer(0) != expected)
1406 throw std::runtime_error(
"Everett SQLite persistence setting rejected");
1410 void definition(std::string_view type, std::string_view name, std::string_view expected)
const {
1412 query.
text(1, type); query.
text(2, name);
1413 if (!query.
row() || query.
text(0) != expected)
1414 throw std::invalid_argument(
"incompatible Everett catalog schema definition");
1421 auto actual = query.
text(0);
1422 for (
unsigned version : {1, 2, 3, 4}) {
1424 auto first = expected.find(
"CREATE TABLE catalog_info");
1425 auto last = expected.find(
';', first);
1426 if (actual == expected.substr(first, last - first))
return version;
1429 throw std::invalid_argument(
"incompatible Everett catalog version table");
1435 std::string_view source = schema;
1437 auto begin = source.find(
"CREATE TABLE ");
1438 if (begin == std::string_view::npos)
break;
1439 source.remove_prefix(begin);
1440 auto end = source.find(
';');
1441 auto name_end = source.find(
'(');
1442 definition(
"table", source.substr(13, name_end - 13), source.substr(0, end));
1443 source.remove_prefix(end + 1);
1445 auto first = source.find(
"CREATE TRIGGER sealed_immutable");
1446 auto last = source.rfind(
"END;");
1447 definition(
"trigger",
"sealed_immutable", source.substr(first, last + 3 - first));
1450 for (
auto action : {
"UPDATE",
"DELETE"}) {
1451 std::string name =
"immutable_" + std::string(table) +
"_" + action;
1452 std::string sql =
"CREATE TRIGGER " + name +
" BEFORE " + action +
" ON " + table +
1453 " BEGIN SELECT RAISE(ABORT,'immutable catalog row'); END";
1460 definition(
"trigger",
"objects_no_delete",
"CREATE TRIGGER objects_no_delete BEFORE DELETE ON objects BEGIN SELECT RAISE(ABORT,'retained object'); END");
1461 std::string trigger_query =
"SELECT count(*) FROM sqlite_schema WHERE type='trigger' AND tbl_name IN('objects'";
1462 std::int64_t trigger_count = 2;
1464 trigger_query +=
",'" + std::string(table) +
"'"; trigger_count += 2;
1466 trigger_query +=
")";
1468 if (!triggers.
row() || triggers.
integer(0) != trigger_count)
1469 throw std::invalid_argument(
"unexpected trigger on Everett catalog tables");
1479 default:
throw std::invalid_argument(
"unknown native merge kernel");
1482 auto physical =
policy(); result.insert(result.end(), physical.begin(), physical.end());
1493 query.
blob(1, domain); query.
text(2, key.older.hex()); query.
text(3, key.newer.hex());
1494 if (!query.
row())
return {};
1500 for (
auto action : {
"UPDATE",
"DELETE"}) {
1501 auto name =
"immutable_completed_native_merges_" + std::string(action);
1502 definition(
"trigger", name,
"CREATE TRIGGER " + name +
" BEFORE " + action +
1503 " ON completed_native_merges BEGIN SELECT RAISE(ABORT,'immutable catalog row'); END");
1505 catalog_detail::statement count(
db_,
"SELECT count(*) FROM sqlite_schema WHERE type='trigger' AND tbl_name='completed_native_merges'");
1506 if (!count.
row() || count.
integer(0) != 2)
throw std::invalid_argument(
"unexpected native merge hint trigger");
1514 for (
auto table : {
"private_scopes",
"private_attempts",
"released_private_scopes"})
1515 for (
auto action : {
"UPDATE",
"DELETE"}) {
1516 auto name =
"immutable_" + std::string(table) +
"_" + action;
1517 auto sql =
"CREATE TRIGGER " + name +
" BEFORE " + action +
" ON " + table +
1518 " BEGIN SELECT RAISE(ABORT,'immutable catalog row'); END";
1528 for (
auto table : {
"private_scopes",
"private_attempts",
"released_private_scopes"})
1529 for (
auto action : {
"UPDATE",
"DELETE"}) {
1530 auto name =
"immutable_" + std::string(table) +
"_" + action;
1531 definition(
"trigger", name,
"CREATE TRIGGER " + name +
" BEFORE " + action +
" ON " + table +
1532 " BEGIN SELECT RAISE(ABORT,'immutable catalog row'); END");
1537 catalog_detail::statement query(
db_,
"SELECT 1 FROM private_scopes WHERE id=? AND NOT EXISTS(SELECT 1 FROM released_private_scopes WHERE scope=?)");
1538 query.
text(1,
id.hex()); query.
text(2,
id.hex());
1539 if (!query.
row())
throw std::invalid_argument(
"inactive private construction scope");
1552 insert.
text(1,
kind); insert.
key(2, owner); insert.
text(3, pair.native.hex()); insert.
text(4, pair.index.hex()); insert.
done();
1556 query.
text(1,
id.hex());
1558 throw std::invalid_argument(
"chain refers to an unsealed or wrongly typed object");
1567 std::span<std::byte const> bytes) {
1568 auto header = decode_file_header<P>(bytes);
1569 if (header.kind != expected || size != bytes.size() ||
1570 file_detail::total_bytes<P>(header.extent) != bytes.size() || crc !=
file_detail::get(bytes, 64, 4))
1571 throw std::invalid_argument(
"sealed catalog metadata disagrees with object");
1577 bool began =
false, committing =
false;
1582 if (old->kind !=
kind || old->request != request)
1583 throw std::invalid_argument(
"Everett operation identity reused with a different request");
1585 return std::move(old->outcome);
1588 auto result = apply();
1600 if (began && !sqlite3_get_autocommit(
db_) && sqlite3_exec(
db_,
"ROLLBACK",
nullptr,
nullptr,
nullptr) != SQLITE_OK)
1606 if (began && !sqlite3_get_autocommit(
db_) && sqlite3_exec(
db_,
"ROLLBACK",
nullptr,
nullptr,
nullptr) != SQLITE_OK)
Binds immutable mapped blobs to exact dependency chains and validates their samples.
Binds mapped COLA main and secondary targets with exact immutable pins.
void session(bytes &out, catalog_session_head const &value)
Definition sqlite_catalog.h:147
std::string schema_for(unsigned version)
Definition sqlite_catalog.h:314
void number(bytes &out, std::uint64_t value)
Definition sqlite_catalog.h:109
void fail(sqlite3 *db, int code)
Definition sqlite_catalog.h:213
void name(std::string_view value)
Definition sqlite_catalog.h:198
constexpr char schema[]
Definition sqlite_catalog.h:282
constexpr char private_attempts_schema[]
Definition sqlite_catalog.h:302
void pair(bytes &out, blob_identity const &value)
Definition sqlite_catalog.h:121
catalog_auxiliary_roots canonical_auxiliary(catalog_auxiliary_roots value, blob_identity const &primary)
Definition sqlite_catalog.h:136
constexpr char live_owner_roots_schema[]
Definition sqlite_catalog.h:310
void timeline(bytes &out, catalog_timeline_head const &value)
Definition sqlite_catalog.h:124
constexpr char live_owner_objects_schema[]
Definition sqlite_catalog.h:308
void identity(bytes &out, object_id const &value)
Definition sqlite_catalog.h:117
constexpr char native_merges_schema[]
Definition sqlite_catalog.h:298
std::int64_t integer(std::uint64_t value)
Definition sqlite_catalog.h:201
void binary(bytes &out, std::span< std::byte const > value)
Definition sqlite_catalog.h:127
bool storage_error(int code) noexcept
Definition sqlite_catalog.h:206
void auxiliary(bytes &out, catalog_auxiliary_roots const &value)
Definition sqlite_catalog.h:130
void exec(sqlite3 *db, char const *sql)
Definition sqlite_catalog.h:216
constexpr char private_scopes_schema[]
Definition sqlite_catalog.h:300
std::vector< std::byte > bytes
Definition sqlite_catalog.h:108
constexpr char released_private_scopes_schema[]
Definition sqlite_catalog.h:304
void field(bytes &out, std::string_view value)
Definition sqlite_catalog.h:112
std::uint64_t get(std::span< std::byte const > bytes, std::size_t at, unsigned width) noexcept
Definition file.h:65
Definition active_engine.h:18
catalog_admission
Definition sqlite_catalog.h:38
object_sync_barrier
Definition object_writer.h:47
catalog_native_merge_kind
Definition sqlite_catalog.h:47
@ conservative_tombstones
std::string_view file_extension(file_kind kind)
Definition object_path.h:27
std::filesystem::path object_path(object_id const &id, file_kind kind)
Definition object_path.h:52
file_kind
Definition object_path.h:25
Streams and seals immutable object files without catalog publication.
object_id index
Definition sections.h:36
object_id native
Definition sections.h:35
Definition sqlite_catalog.h:71
std::vector< blob_identity > pairs
Definition sqlite_catalog.h:72
bool operator==(catalog_auxiliary_roots const &) const =default
std::vector< object_id > natives
Definition sqlite_catalog.h:73
Definition sqlite_catalog.h:152
std::uint64_t number()
Definition sqlite_catalog.h:155
catalog_session_head session()
Definition sqlite_catalog.h:187
catalog_timeline_head timeline()
Definition sqlite_catalog.h:167
void end() const
Definition sqlite_catalog.h:196
std::span< std::byte const > data
Definition sqlite_catalog.h:153
static void invalid()
Definition sqlite_catalog.h:154
std::string field()
Definition sqlite_catalog.h:161
catalog_auxiliary_roots auxiliary()
Definition sqlite_catalog.h:174
Definition sqlite_catalog.h:220
std::string text(int column) const
Definition sqlite_catalog.h:261
sqlite3_stmt * value
Definition sqlite_catalog.h:222
void key(int index, std::string_view data)
Definition sqlite_catalog.h:243
void text(int index, std::string_view data)
Definition sqlite_catalog.h:233
statement(sqlite3 *db, char const *sql)
Definition sqlite_catalog.h:223
sqlite3 * db
Definition sqlite_catalog.h:221
void integer(int index, std::int64_t number)
Definition sqlite_catalog.h:246
bool is_null(int column) const
Definition sqlite_catalog.h:280
statement(statement const &)=delete
bytes blob(int column) const
Definition sqlite_catalog.h:273
std::string key(int column) const
Definition sqlite_catalog.h:267
bool row()
Definition sqlite_catalog.h:254
statement & operator=(statement const &)=delete
std::int64_t integer(int column) const
Definition sqlite_catalog.h:279
void null(int index)
Definition sqlite_catalog.h:250
void blob(int index, std::span< std::byte const > data)
Definition sqlite_catalog.h:237
void done()
Definition sqlite_catalog.h:260
~statement()
Definition sqlite_catalog.h:230
Definition sqlite_catalog.h:92
std::string operation
Definition sqlite_catalog.h:97
int code
Definition sqlite_catalog.h:96
bool outcome_unknown
Definition sqlite_catalog.h:98
catalog_error(std::string message, int code, std::string operation={}, bool uncertain=false)
Definition sqlite_catalog.h:93
Definition sqlite_catalog.h:50
catalog_native_merge_kind kind
Definition sqlite_catalog.h:53
object_id older
Definition sqlite_catalog.h:52
std::string schema
Definition sqlite_catalog.h:51
object_id newer
Definition sqlite_catalog.h:52
Definition sqlite_catalog.h:45
object_id object
Definition sqlite_catalog.h:45
file_kind kind
Definition sqlite_catalog.h:45
Definition sqlite_catalog.h:87
std::string kind
Definition sqlite_catalog.h:88
std::vector< std::byte > request
Definition sqlite_catalog.h:89
std::vector< std::byte > outcome
Definition sqlite_catalog.h:90
Definition sqlite_catalog.h:39
std::optional< object_id > private_scope
Definition sqlite_catalog.h:43
int busy_timeout_ms
Definition sqlite_catalog.h:40
Definition sqlite_catalog.h:46
std::string owner
Definition sqlite_catalog.h:46
blob_identity head
Definition sqlite_catalog.h:46
Definition sqlite_catalog.h:76
catalog_auxiliary_roots auxiliary
Definition sqlite_catalog.h:79
catalog_timeline_head timeline
Definition sqlite_catalog.h:77
bool operator==(catalog_session_head const &) const =default
std::vector< std::byte > checkpoint
Definition sqlite_catalog.h:78
Definition sqlite_catalog.h:82
bool published
Definition sqlite_catalog.h:83
catalog_session_head head
Definition sqlite_catalog.h:84
bool operator==(catalog_session_publication const &) const =default
Definition sqlite_catalog.h:57
std::string owner
Definition sqlite_catalog.h:61
blob_identity head
Definition sqlite_catalog.h:60
bool operator==(catalog_timeline_head const &) const =default
std::uint64_t generation
Definition sqlite_catalog.h:59
std::string name
Definition sqlite_catalog.h:58
Definition sqlite_catalog.h:64
bool published
Definition sqlite_catalog.h:65
bool operator==(catalog_timeline_publication const &) const =default
catalog_timeline_head head
Definition sqlite_catalog.h:66
Definition cola_query.h:40
pair_type head() const noexcept
Definition cola_query.h:63
Definition mapped_cola.h:214
pair_type pair(blob_identity const &head)
Definition mapped_cola.h:227
Definition mapped_cola.h:91
static mapped_file open(std::filesystem::path const &path)
Definition mapped_file.h:131
Definition mapped_file.h:81
std::span< std::byte const > bytes() const &noexcept
Definition mapped_file.h:99
Definition object_writer.h:39
std::string const & hex() const noexcept
Definition object_writer.h:41
Definition object_path.h:38
std::string const & hex() const noexcept
Definition object_path.h:46
Definition object_writer.h:88
object_attempt_id attempt
Definition object_writer.h:90
std::filesystem::path path
Definition object_writer.h:91
object_id object
Definition object_writer.h:89
std::uint32_t body_crc32c
Definition object_writer.h:93
std::uint64_t bytes
Definition object_writer.h:92
object_sync_barrier barrier
Definition object_writer.h:94
Definition object_writer.h:100
pair_type head() const noexcept
Definition query.h:66
Definition sqlite_catalog.h:1031
std::uint64_t main_samples
Definition sqlite_catalog.h:1035
std::optional< object_id > secondary
Definition sqlite_catalog.h:1034
std::uint64_t secondary_count
Definition sqlite_catalog.h:1035
std::optional< blob_identity > main
Definition sqlite_catalog.h:1033
blob_identity id
Definition sqlite_catalog.h:1032
std::uint64_t native_count
Definition sqlite_catalog.h:1035
std::uint64_t secondary_samples
Definition sqlite_catalog.h:1035
std::uint64_t virtual_count
Definition sqlite_catalog.h:1035
Definition sqlite_catalog.h:1037
mapped_slice secondary
Definition sqlite_catalog.h:1037
mapped_slice index
Definition sqlite_catalog.h:1037
mapped_slice native
Definition sqlite_catalog.h:1037
Definition sqlite_catalog.h:1038
std::shared_ptr< typename Mapped::index_type const > index
Definition sqlite_catalog.h:1039
cola_pair_files files
Definition sqlite_catalog.h:1040
Mapped::native_type native
Definition sqlite_catalog.h:1042
cola_pair_descriptor descriptor
Definition sqlite_catalog.h:1041
Definition sqlite_catalog.h:103
int commit(sqlite3 *db) noexcept
Definition sqlite_catalog.h:104
Definition sqlite_catalog.h:351
catalog_auxiliary_roots auxiliary_at(catalog_timeline_head const &head) const
Definition sqlite_catalog.h:1291
sqlite_catalog & operator=(sqlite_catalog const &)=delete
~sqlite_catalog()
Definition sqlite_catalog.h:363
bool native_merges_validated_
Definition sqlite_catalog.h:1182
std::optional< catalog_detail::bytes > checkpoint_at(std::string_view name, std::uint64_t generation) const
Definition sqlite_catalog.h:1279
sqlite_catalog(sqlite3 *db, std::filesystem::path root, Ops ops)
Definition sqlite_catalog.h:1191
bool has_table(std::string_view table) const
Definition sqlite_catalog.h:1251
std::optional< catalog_operation > lookup_operation(std::string_view op) const
Definition sqlite_catalog.h:499
private_scope_state
Definition sqlite_catalog.h:435
auto seal_pair(std::string_view op, blob_identity const &id, object_seal_receipt const &receipt)
Definition sqlite_catalog.h:782
static void append_native_merge(catalog_detail::bytes &out, std::span< std::byte const > domain, catalog_native_merge const &key)
Definition sqlite_catalog.h:1485
unsigned schema_version_
Definition sqlite_catalog.h:1183
prepared_cola_pair< Mapped > prepare_cola_pair(blob_identity const &id, object_seal_receipt const *receipt=nullptr, object_seal_receipt const *native_receipt=nullptr) const
Definition sqlite_catalog.h:1048
static bool compatible_policy(std::span< std::byte const > stored)
Definition sqlite_catalog.h:1366
static void validate_options(catalog_options options)
Definition sqlite_catalog.h:1374
void verify_sealed(object_seal_receipt const &receipt, file_kind expected) const
Definition sqlite_catalog.h:482
catalog_timeline_head create_timeline(std::string_view op, std::string_view name, blob_identity const &head)
Definition sqlite_catalog.h:862
auto read(F &&action) const
Definition sqlite_catalog.h:1347
void verify_object_envelope(object_id const &id, file_kind expected, std::uint64_t size, std::uint64_t crc) const
Definition sqlite_catalog.h:1562
catalog_detail::bytes transaction(std::string_view op, std::string_view kind, catalog_detail::bytes const &request, F &&apply, bool record=true)
Definition sqlite_catalog.h:1573
catalog_timeline_head add_generation(std::string_view name, std::uint64_t generation, blob_identity const &head)
Definition sqlite_catalog.h:1329
std::optional< object_id > identity_
Definition sqlite_catalog.h:1184
std::vector< object_id > private_scopes() const
Definition sqlite_catalog.h:447
static sqlite_catalog create_version(std::filesystem::path const &root, object_id const &identity, catalog_options options, Ops ops, unsigned version)
Definition sqlite_catalog.h:1193
static sqlite_catalog create_cola(std::filesystem::path const &root, object_id const &identity, catalog_options options={}, Ops ops={})
Definition sqlite_catalog.h:371
void record_native_merge(std::string_view op, catalog_native_merge const &key, object_seal_receipt const &receipt, std::string_view owner)
Definition sqlite_catalog.h:568
void add_native_pin(std::string_view owner, object_id const &native)
Definition sqlite_catalog.h:1541
void require_timelines() const
Definition sqlite_catalog.h:1242
static char const * runtime_version() noexcept
Definition sqlite_catalog.h:394
sqlite_catalog & operator=(sqlite_catalog &&)=delete
void ensure_private_scopes()
Definition sqlite_catalog.h:1509
void save(std::string_view op, std::string_view name, blob_identity const &head)
Definition sqlite_catalog.h:828
Ops ops_
Definition sqlite_catalog.h:1180
std::optional< catalog_session_head > find_session(std::string_view name) const
Definition sqlite_catalog.h:925
catalog_session_head decode_session(catalog_detail::bytes const &bytes) const
Definition sqlite_catalog.h:1273
void write_seal(object_seal_receipt const &receipt)
Definition sqlite_catalog.h:1138
void append_seal_request(catalog_detail::bytes &request, object_seal_receipt const &receipt, std::filesystem::path const &path) const
Definition sqlite_catalog.h:1103
static sqlite_catalog create_sessions(std::filesystem::path const &root, object_id const &identity, catalog_options options={}, Ops ops={})
Definition sqlite_catalog.h:375
catalog_timeline_head fork_timeline(std::string_view op, std::string_view name, catalog_timeline_head const &source)
Definition sqlite_catalog.h:878
static constexpr char const * immutable_tables[]
Definition sqlite_catalog.h:1186
void validate_native_merges() const
Definition sqlite_catalog.h:1497
catalog_timeline_publication publish_timeline(std::string_view op, catalog_timeline_head const &expected, blob_identity const &candidate)
Definition sqlite_catalog.h:898
bool private_construction() const noexcept
Definition sqlite_catalog.h:392
static sqlite_catalog open(std::filesystem::path const &root, catalog_options options={}, Ops ops={})
Definition sqlite_catalog.h:379
void require_private_scope(object_id const &id) const
Definition sqlite_catalog.h:1535
static sqlite_catalog create(std::filesystem::path const &root, object_id const &identity, catalog_options options={}, Ops ops={})
Definition sqlite_catalog.h:365
std::filesystem::path root_
Definition sqlite_catalog.h:1179
catalog_session_head add_session_generation(std::string_view name, std::uint64_t generation, blob_identity const &head, std::span< std::byte const > checkpoint, catalog_auxiliary_roots const &auxiliary)
Definition sqlite_catalog.h:1338
bool poisoned_
Definition sqlite_catalog.h:1181
object_id const & identity() const &&=delete
static char const * source_id() noexcept
Definition sqlite_catalog.h:395
std::filesystem::path const & root() const &noexcept
Definition sqlite_catalog.h:396
std::optional< object_seal_receipt > acquire_native_merge(std::string_view op, catalog_native_merge const &key, std::string_view owner)
Definition sqlite_catalog.h:603
void record_sealed_row(object_seal_receipt const &receipt, std::filesystem::path const &path, mapped_slice const *evidence=nullptr)
Definition sqlite_catalog.h:1114
void validate_private_scopes() const
Definition sqlite_catalog.h:1522
static void append_pair_request(catalog_detail::bytes &request, cola_pair_descriptor const &descriptor)
Definition sqlite_catalog.h:1095
void register_graph(std::string_view op, std::shared_ptr< Mapped const > const &source, catalog_admission admission=catalog_admission::trusted)
Definition sqlite_catalog.h:691
unsigned schema_version() const noexcept
Definition sqlite_catalog.h:393
void record_sealed(std::string_view op, object_seal_receipt const &receipt)
Definition sqlite_catalog.h:552
mapped_slice open_object_bytes(object_id const &id, file_kind expected) const
Definition sqlite_catalog.h:1044
object_seal_receipt sealed_receipt(object_id const &id, file_kind expected) const
Definition sqlite_catalog.h:462
void save_session(std::string_view op, std::string_view name, catalog_session_head const &source)
Definition sqlite_catalog.h:999
void add_root(std::string_view kind, std::string_view owner, blob_identity const &pair)
Definition sqlite_catalog.h:1550
static catalog_detail::bytes policy()
Definition sqlite_catalog.h:1355
private_scope_state scope_state(object_id const &id) const
Definition sqlite_catalog.h:436
bool poisoned() const noexcept
Definition sqlite_catalog.h:391
catalog_session_head create_session(std::string_view op, std::string_view name, blob_identity const &head, std::span< std::byte const > checkpoint, catalog_auxiliary_roots auxiliary={})
Definition sqlite_catalog.h:934
catalog_session_head attach_checkpoint(catalog_timeline_head head) const
Definition sqlite_catalog.h:1285
std::optional< catalog_timeline_head > timeline_at(std::string_view name, std::optional< std::uint64_t > generation={}) const
Definition sqlite_catalog.h:1312
std::optional< catalog_session_head > find_saved_session(std::string_view name) const
Definition sqlite_catalog.h:1018
auto seal_native_pair(std::string_view op, blob_identity const &id, object_seal_receipt const &native_receipt, object_seal_receipt const &index_receipt)
Definition sqlite_catalog.h:803
std::optional< object_id > native_merge_output(std::span< std::byte const > domain, catalog_native_merge const &key) const
Definition sqlite_catalog.h:1490
void require_sealed(object_id const &id, file_kind expected, mapped_slice const *evidence=nullptr) const
Definition sqlite_catalog.h:1554
void register_pair(std::string_view op, blob_identity const &id)
Definition sqlite_catalog.h:769
object_id const & identity() const &
Definition sqlite_catalog.h:398
void reserve(std::string_view op, object_attempt_id const &attempt, std::string_view owner, std::span< blob_identity const > inputs, std::span< catalog_object_reservation const > outputs)
Definition sqlite_catalog.h:518
void set_private_scope(object_id const &id)
Definition sqlite_catalog.h:415
void definition(std::string_view type, std::string_view name, std::string_view expected) const
Definition sqlite_catalog.h:1410
void add_auxiliary(std::string_view kind, std::string_view owner, catalog_auxiliary_roots const &value)
Definition sqlite_catalog.h:1304
std::optional< blob_identity > find_save(std::string_view name) const
Definition sqlite_catalog.h:508
catalog_session_head fork_session(std::string_view op, std::string_view name, catalog_session_head const &source)
Definition sqlite_catalog.h:980
static void validate_timeline(catalog_timeline_head const &value)
Definition sqlite_catalog.h:1263
static sqlite_catalog connect(std::filesystem::path const &root, catalog_options options, Ops ops, bool initialize=false)
Definition sqlite_catalog.h:1379
catalog_saved_root acquire_save(std::string_view op, std::string_view name, std::string_view reader_owner)
Definition sqlite_catalog.h:842
std::optional< catalog_timeline_head > find_timeline(std::string_view name) const
Definition sqlite_catalog.h:857
catalog_timeline_head decode_timeline(catalog_detail::bytes const &bytes) const
Definition sqlite_catalog.h:1267
std::filesystem::path const & root() const &&=delete
static void check_object_envelope(file_kind expected, std::uint64_t size, std::uint64_t crc, std::span< std::byte const > bytes)
Definition sqlite_catalog.h:1566
void register_chain(std::string_view op, mapped_query_root< P > const &source, catalog_admission admission=catalog_admission::trusted)
Definition sqlite_catalog.h:632
void require_sessions() const
Definition sqlite_catalog.h:1246
unsigned detect_schema_version() const
Definition sqlite_catalog.h:1416
P policy_type
Definition sqlite_catalog.h:354
void release_private_scope(std::string_view op, object_id const &id)
Definition sqlite_catalog.h:422
catalog_session_publication publish_session(std::string_view op, catalog_session_head const &expected, blob_identity const &candidate, std::span< std::byte const > checkpoint, catalog_auxiliary_roots auxiliary={})
Definition sqlite_catalog.h:950
void register_graphs(std::string_view op, std::span< std::shared_ptr< Mapped const > const > roots, catalog_admission admission=catalog_admission::trusted)
Definition sqlite_catalog.h:697
static std::int64_t kind(file_kind value)
Definition sqlite_catalog.h:1354
void require_active() const
Definition sqlite_catalog.h:1239
void record_prepared_seal(object_seal_receipt const &receipt, file_kind expected)
Definition sqlite_catalog.h:1130
void validate_schema() const
Definition sqlite_catalog.h:1431
std::optional< object_id > private_scope_
Definition sqlite_catalog.h:1185
void add_owner(std::string_view kind, std::string_view name)
Definition sqlite_catalog.h:1546
sqlite3 * db_
Definition sqlite_catalog.h:1178
void register_chain(std::string_view op, mapped_cola_query_root< P > const &source, catalog_admission admission=catalog_admission::trusted)
Definition sqlite_catalog.h:685
static catalog_detail::bytes native_merge_domain(catalog_native_merge const &key)
Definition sqlite_catalog.h:1471
sqlite_catalog(sqlite_catalog &&other) noexcept
Definition sqlite_catalog.h:358
void require_prepared(blob_identity const &head) const
Definition sqlite_catalog.h:1323
void begin_private_scope(std::string_view op, object_id const &id)
Definition sqlite_catalog.h:405
void register_cola_row(cola_pair_descriptor const &value)
Definition sqlite_catalog.h:1143
sqlite_catalog(sqlite_catalog const &)=delete
void insert_cola_row(cola_pair_descriptor const &value)
Definition sqlite_catalog.h:1151
Definition multiverse.h:42
Definition transaction.h:28