Skip to documentation
SLOP

tiny.sql.DatabaseSession

Reference tiny.sql DatabaseSession

Defined in tiny.sql.

API (24)

Actions

Public operations.

Fields and members

Public fields and members.

No direct callersNo direct callstiny.sqlDatabaseSession
Static calls · unresolved targets: unknown · external targets: unknown.

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;
Called byCallsDatabaseSessioncommitBranchCheckoutadvanceprivate sourcelib.sql.src.session.database.DatabaseSessionclearPendingEditsversionsameDatabaseSessionadvance
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersDatabaseSessionapplyRootprivate sourcelib.sql.src.session.database.DatabaseSessionrefreshCatalogRootDatabaseSessionanalyzeRelation
Static calls · unresolved targets: 0 · external targets: 4.
Called byCallsDatabaseSessionanalyzeRelationDatabaseSessioncreateIndexDatabaseSessioncreateRelationDatabaseSessiondropRelationprivate sourcelib.sql.src.session.database.DatabaseSessionadoptWorkingRootprivate sourcelib.sql.src.session.database.DatabaseSessionclearPendingEditsDatabaseSessionapplyRoot
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersDatabaseSessionadvanceDatabaseSessioncommit
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callersDatabaseSessionapplyRootprivate sourcelib.sql.src.session.database.DatabaseSessionrefreshCatalogRootDatabaseSessioncreateIndex
Static calls · unresolved targets: 0 · external targets: 4.
Called byCallsNo direct callersDatabaseSessionapplyRootprivate sourcelib.sql.src.session.database.DatabaseSessionrefreshCatalogRootDatabaseSessioncreateRelation
Static calls · unresolved targets: 0 · external targets: 4.
Called byCallsDatabaseSessionreinitWithRootprivate sourcelib.sql.src.session.database.DatabaseSessionclearPendingEditsDatabaseSessiondiscardStagedRelationsprivate sourcelib.sql.src.session.database.DatabaseSessiondiscardWriteDatabaseRootdeinitDatabaseSessiondeinit
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callsDatabaseSessiondeinitprivate sourcelib.sql.src.session.database.DatabaseSessionflushStagedRelationsDatabaseWritediscardDatabaseSessiondiscardStagedRelations
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callersDatabaseSessionapplyRootprivate sourcelib.sql.src.session.database.DatabaseSessionrefreshCatalogRootDatabaseSessiondropRelation
Static calls · unresolved targets: 0 · external targets: 4.
Called byCallsDatabaseSessionreinitWithRoottest sourcelib.sql.src.session.testtest: database session applies catalo...test sourcelib.sql.src.session.testtest: database session assembles stag...test sourcelib.sql.src.session.testtest: database session clears analyze...test sourcelib.sql.src.session.testtest: database session deinit discard...+7 moreversionsameDatabaseSessioninitWithRoot
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersversionsameDatabaseSessionpendingEditRowids
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallsNo direct callsDatabaseWritependingRelationsDatabaseSessionpendingRelations
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersDatabaseSessiondeinitDatabaseSessioninitWithRootDatabaseSessionreinitWithRoot
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersBranchCheckoutstageDatabaseSessionstage
Static calls · unresolved targets: 0 · external targets: 0.

Also reachable as

session.DatabaseSession.

Complete caller list for DatabaseSession.initWithRoot

12 direct callers.

Audit

Definitions17
Public names34
Members8
Version26.7.0
Revisiondaab053ee433