tiny.sql.DatabaseSession
Defined in tiny.sql.
API (24)
Actions
Public operations.
advanceanalyzeRelationapplyRootbeginWritecommitcreateIndexcreateRelationdeinitdiscardStagedRelationsdropRelationinitWithRootpendingEditRowids: Tells a publication whether it can write one relation as a list of changed rows on top of an earlier root by reporting which rows of that relation have changed since the last commit.pendingRelationsreinitWithRootstageworkingRoot
Fields and members
Public fields and members.
active_writeallocatorcheckoutpending_editspending_edits_basestaged_relationsworking_rootwrite_generation
Source
Source: lib/sql/src/session/database.zig:61
zig
pub const DatabaseSession = struct { allocator: Allocator, checkout: branch.Checkout, staged_relations: std.ArrayList(StagedRelation) = .empty, working_root: version.DatabaseRoot, pending_edits_base: version.Hash, pending_edits: std.StringArrayHashMapUnmanaged(std.ArrayList(i64)) = .empty, active_write: ?ActiveWrite = null, write_generation: u64 = 0, pub fn initWithRoot(session_allocator: Allocator, checkout: branch.Checkout, root: *version.DatabaseRoot) DatabaseSession { std.debug.assert(version.same(root.hash, checkout.working.working)); const working_root = root.*; root.* = undefined; return .{ .allocator = session_allocator, .checkout = checkout, .working_root = working_root, .pending_edits_base = working_root.hash, }; } pub fn deinit(self: *DatabaseSession) void { self.discardWrite(); self.discardStagedRelations(); self.clearPendingEdits(); self.pending_edits.deinit(self.allocator); self.working_root.deinit(); self.* = undefined; } pub fn reinitWithRoot( self: *DatabaseSession, checkout: branch.Checkout, root: *version.DatabaseRoot, ) void { const session_allocator = self.allocator; const write_generation = self.write_generation; self.deinit(); self.* = initWithRoot(session_allocator, checkout, root); self.write_generation = write_generation; } pub fn applyRoot(self: *DatabaseSession, root: *version.DatabaseRoot) void { self.adoptWorkingRoot(root); self.clearPendingEdits(); } pub fn createRelation(self: *DatabaseSession, allocator: Allocator, catalog: *const catalog_mod.Catalog, definition: catalog_mod.RelationDefinition, options: file.CommitOptions) DatabaseError!CatalogFlush { const phase = trace.scope("session.database.create_relation"); defer phase.end(); const commit_value = try catalog.createRelation(allocator, definition, options); var root = try self.refreshCatalogRoot(catalog); var root_live = true; errdefer if (root_live) root.deinit(); const flush = CatalogFlush{ .commit = commit_value, .database = root.hash, }; self.applyRoot(&root); root_live = false; return flush; } pub fn createIndex(self: *DatabaseSession, allocator: Allocator, catalog: *const catalog_mod.Catalog, table_name: []const u8, definition: catalog_mod.IndexDefinition, options: file.CommitOptions) DatabaseError!CatalogFlush { const phase = trace.scope("session.database.create_index"); defer phase.end(); const commit_value = try catalog.createIndex(allocator, table_name, definition, options); var root = try self.refreshCatalogRoot(catalog); var root_live = true; errdefer if (root_live) root.deinit(); const flush = CatalogFlush{ .commit = commit_value, .database = root.hash, }; self.applyRoot(&root); root_live = false; return flush; } pub fn analyzeRelation(self: *DatabaseSession, allocator: Allocator, catalog: *const catalog_mod.Catalog, name: []const u8, options: file.CommitOptions) DatabaseError!CatalogFlush { const phase = trace.scope("session.database.analyze_relation"); defer phase.end(); const commit_value = try catalog.analyzeRelation(allocator, name, options); var root = try self.refreshCatalogRoot(catalog); var root_live = true; errdefer if (root_live) root.deinit(); const flush = CatalogFlush{ .commit = commit_value, .database = root.hash, }; self.applyRoot(&root); root_live = false; return flush; } pub fn dropRelation(self: *DatabaseSession, allocator: Allocator, catalog: *const catalog_mod.Catalog, name: []const u8, options: file.CommitOptions) DatabaseError!CatalogFlush { const phase = trace.scope("session.database.drop_relation"); defer phase.end(); const commit_value = try catalog.dropRelation(allocator, name, options); var root = try self.refreshCatalogRoot(catalog); var root_live = true; errdefer if (root_live) root.deinit(); const flush = CatalogFlush{ .commit = commit_value, .database = root.hash, }; self.applyRoot(&root); root_live = false; return flush; } pub fn beginWrite( self: *DatabaseSession, workspace: *DatabaseWrite.Workspace, flush_allocator: Allocator, limits: DatabaseWrite.Limits, options: file.CommitOptions, ) DatabaseError!DatabaseWrite { const phase = trace.scope("session.database.begin_write"); defer phase.end(); if (self.active_write != null) return error.WriteSessionActive; std.debug.assert(self.staged_relations.items.len == 0); const generation = std.math.add(u64, self.write_generation, 1) catch return error.CapacityOverflow; const storage = try workspace.acquire(limits); self.write_generation = generation; self.active_write = .{ .generation = generation, .workspace = workspace, .flush_allocator = flush_allocator, .options = options, .storage = storage, }; return .{ .session = self, .generation = generation, }; } fn discardWrite(self: *DatabaseSession) void { if (self.active_write) |*active_write| { active_write.workspace.release(&active_write.storage); self.active_write = null; } } fn stageRelation( self: *DatabaseSession, storage: *staging.Storage, relation: *relation_session.RelationSession, ) DatabaseError!void { const phase = trace.scope("session.database.stage_relation"); defer phase.end(); const demand = try staging.Storage.demandForEdits(relation.edits.items); try storage.ensure(demand); if (self.stagedRelationIndex(relation.name)) |relation_index| { const staged = &self.staged_relations.items[relation_index]; if (!version.same(staged.root.hash, relation.root.hash)) { return error.StagedRelationRootMismatch; } storage.appendEditsAssumeCapacity(relation_index, relation.edits.items); relation.deinit(); return; } try storage.ensureRelation(); try self.staged_relations.ensureUnusedCapacity(self.allocator, 1); const staged = StagedRelation.takeMetadata(relation); self.staged_relations.appendAssumeCapacity(staged); const relation_index = storage.registerRelation(); std.debug.assert(relation_index == self.staged_relations.items.len - 1); storage.appendEditsAssumeCapacity(relation_index, relation.edits.items); relation_session.finishStaging(relation); } fn flushStagedRelations( self: *DatabaseSession, flush_allocator: Allocator, options: file.CommitOptions, storage: *staging.Storage, ) DatabaseError!DatabaseFlush { const phase = trace.scope("session.database.flush_staged_relations"); defer phase.end(); if (self.staged_relations.items.len == 0) return error.NoStagedRelation; errdefer self.discardStagedRelations(); const relation_flushes = try flush_allocator.alloc(relation_session.RelationFlush, self.staged_relations.items.len); var flush_count: usize = 0; errdefer flush_allocator.free(relation_flushes); const replacements = try flush_allocator.alloc(version.RelationEntry, self.staged_relations.items.len); defer flush_allocator.free(replacements); storage.prepareOrdered(); for ( self.staged_relations.items, relation_flushes, replacements, 0.., ) |*relation, *flush, *replacement, relation_index| { const edits = storage.orderedFor(relation_index); try self.checkStagedBase(relation); try self.recordPendingEdits(relation.name, edits); const root_flush = try relation.flushRoot(flush_allocator, options, edits); replacement.* = .{ .name = relation.name, .hash = root_flush.hash, }; flush.* = .{ .commit = root_flush.commit, .relation = root_flush.hash, .database = undefined, }; flush_count += 1; } var root = try version.databaseRootReplacingEntries(self.allocator, &self.working_root, replacements[0..flush_count]); var root_live = true; errdefer if (root_live) root.deinit(); const database = root.hash; for (relation_flushes) |*flush| flush.database = database; self.adoptWorkingRoot(&root); root_live = false; self.discardStagedRelations(); return .{ .allocator = flush_allocator, .relations = relation_flushes[0..flush_count], .database = database, }; } pub fn pendingRelations(self: *const DatabaseSession) usize { return self.staged_relations.items.len; } pub fn workingRoot(self: *const DatabaseSession) *const version.DatabaseRoot { return &self.working_root; } pub fn discardStagedRelations(self: *DatabaseSession) void { for (self.staged_relations.items) |*relation| relation.deinit(); self.staged_relations.deinit(self.allocator); self.staged_relations = .empty; } pub fn stage(self: *DatabaseSession) void { self.checkout = self.checkout.stage(); } pub fn commit(self: *DatabaseSession, history: *history_mod.History) DatabaseError!version.Hash { if (!self.checkout.working.hasStaged()) return error.NoStagedRoot; const root = self.checkout.working.staged; const commit_hash = try history.commitBranch(self.checkout.name, root); try self.advance(commit_hash, root); return commit_hash; } pub fn advance(self: *DatabaseSession, commit_hash: version.Hash, root: version.Hash) DatabaseError!void { if (!version.same(self.working_root.hash, root)) return error.InvalidHistory; self.checkout = self.checkout.advance(commit_hash, root); self.clearPendingEdits(); } /// Tells a publication whether it can write one relation as a list of /// changed rows on top of an earlier root by reporting which rows of that /// relation have changed since the last commit. The function answers only /// when the caller's base hash is the root the pending edits were recorded /// against, so the edits are known to cover exactly the distance from that /// base, and it returns nothing when the base differs or when the named /// relation has no recorded edits, and the caller then writes the whole /// relation. The returned rowids borrow the session's own list, which the /// next commit clears. pub fn pendingEditRowids( self: *const DatabaseSession, base: version.Hash, name: []const u8, ) ?[]const i64 { if (!version.same(base, self.pending_edits_base)) return null; const list = self.pending_edits.get(name) orelse return null; return list.items; } fn recordPendingEdits(self: *DatabaseSession, name: []const u8, edits: []const relation_mod.Edit) DatabaseError!void { const list = try self.pendingEditList(name); for (edits) |edit| { const rowid = switch (edit) { .put => |put_edit| put_edit.rowid, .update => |update_edit| update_edit.rowid, .delete => |rowid| rowid, }; try list.append(self.allocator, rowid); } } fn pendingEditList(self: *DatabaseSession, name: []const u8) DatabaseError!*std.ArrayList(i64) { if (self.pending_edits.getPtr(name)) |list| return list; const owned_name = try self.allocator.dupe(u8, name); errdefer self.allocator.free(owned_name); const slot = try self.pending_edits.getOrPut(self.allocator, owned_name); std.debug.assert(!slot.found_existing); slot.key_ptr.* = owned_name; slot.value_ptr.* = .empty; return slot.value_ptr; } fn clearPendingEdits(self: *DatabaseSession) void { self.pending_edits_base = self.working_root.hash; for (self.pending_edits.keys(), self.pending_edits.values()) |name, *list| { self.allocator.free(name); list.deinit(self.allocator); } self.pending_edits.clearRetainingCapacity(); } fn refreshCatalogRoot(self: *DatabaseSession, catalog: *const catalog_mod.Catalog) relation_session.Error!version.DatabaseRoot { return try version.databaseRootMaintained( self.allocator, catalog, self.working_root.conflicts, ); } fn checkStagedBase(self: *const DatabaseSession, relation: *const StagedRelation) DatabaseError!void { const base_hash = self.workingEntryHash(relation.name) orelse return error.StagedRelationRootMismatch; if (!version.same(base_hash, relation.root.hash)) return error.StagedRelationRootMismatch; } fn workingEntryHash(self: *const DatabaseSession, name: []const u8) ?version.Hash { for (self.working_root.entries) |entry| { if (std.mem.eql(u8, entry.name, name)) return entry.hash; } return null; } fn stagedRelation(self: *DatabaseSession, name: []const u8) ?*StagedRelation { const index = self.stagedRelationIndex(name) orelse return null; return &self.staged_relations.items[index]; } fn stagedRelationIndex(self: *const DatabaseSession, name: []const u8) ?usize { for (self.staged_relations.items, 0..) |*relation, index| { if (std.mem.eql(u8, relation.name, name)) return index; } return null; } fn adoptWorkingRoot(self: *DatabaseSession, root: *version.DatabaseRoot) void { const hash = root.hash; self.working_root.deinit(); self.working_root = root.*; root.* = undefined; self.checkout = self.checkout.withWorking(hash); }};Source: lib/sql/src/root.zig:166
zig
pub const DatabaseSession = session.DatabaseSession;Also reachable as
Complete caller list for DatabaseSession.initWithRoot
12 direct callers.
tiny.sql.DatabaseSession.reinitWithRoot[method] atlib/sql/src/session/database.zig:92lib.sql.src.session.test.test_database_session_applies_catalog_roots_before_history_commits[function] — test source atlib/sql/src/session/test.zig:806in nearest public ownerlib.sql.src.session.testlib.sql.src.session.test.test_database_session_assembles_staged_flushes_from_working_database_value[function] — test source atlib/sql/src/session/test.zig:657in nearest public ownerlib.sql.src.session.testlib.sql.src.session.test.test_database_session_clears_analyzed_stats_after_relation_edits[function] — test source atlib/sql/src/session/test.zig:905in nearest public ownerlib.sql.src.session.testlib.sql.src.session.test.test_database_session_deinit_discards_queued_relation_edits[function] — test source atlib/sql/src/session/test.zig:750in nearest public ownerlib.sql.src.session.testlib.sql.src.session.test.test_database_session_flushes_queued_relation_edits_into_one_working_root[function] — test source atlib/sql/src/session/test.zig:564in nearest public ownerlib.sql.src.session.testlib.sql.src.session.test.test_database_session_rejects_staged_relations_that_drift_from_working_values[function] — test source atlib/sql/src/session/test.zig:418in nearest public ownerlib.sql.src.session.testlib.sql.src.session.test.test_database_session_stages_flushed_database_roots_before_history_commits[function] — test source atlib/sql/src/session/test.zig:478in nearest public ownerlib.sql.src.session.testlib.sql.src.session.test.test_database_write_appends_staged_edits_to_staged_relations[function] — test source atlib/sql/src/session/test.zig:110in nearest public ownerlib.sql.src.session.testlib.sql.src.session.test.test_database_write_can_trust_staged_relation_indexes[function] — test source atlib/sql/src/session/test.zig:331in nearest public ownerlib.sql.src.session.testlib.sql.src.session.test.test_database_write_limits_bound_staged_edit_storage[function] — test source atlib/sql/src/session/test.zig:185in nearest public ownerlib.sql.src.session.testlib.sql.src.session.test.test_relation_sessions_stage_edits_through_database_flush[function] — test source atlib/sql/src/session/test.zig:39in nearest public ownerlib.sql.src.session.test
Audit
| Definitions | 17 |
|---|---|
| Public names | 34 |
| Members | 8 |
| Version | 26.7.0 |
| Revision | daab053ee433 |