Skip to documentation
SLOP

tiny.sql.FileDatabase

Reference tiny.sql FileDatabase

Defined in tiny.sql.

An open database over its two files, held for as long as a caller uses the database to read and write through the scopes it hands out.

API (57)

Actions

Public operations.

Types and contracts

Public types and contracts.

Fields and members

Public fields and members.

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

Source

Source: lib/sql/src/file.zig:1746

zig
/// An open database over its two files, held for as long as a caller uses the/// database to read and write through the scopes it hands out. The database/// owns its own directory handle, the base file, the write-ahead log file, and/// the copies of both paths it took at open time, while holding the pager, the/// read cache, digest memo, transaction staging area, and registry of live read/// leases, all borrowed from the workspace. The database allows one write/// transaction at a time, tracks whether one is open, and carries a recovery/// flag that a failed write sets, after which every operation refuses with/// `RecoveryRequired`. Releasing the database with `deinit` is correct only/// once every read lease and the write transaction have ended, which `deinit`/// asserts.pub const Database = struct {    pub const Workspace = DatabaseWorkspace;    allocator: Allocator,    workspace: *Workspace,    io: std.Io,    dir: std.Io.Dir,    path_storage: PathStorage.Loan,    paths: Paths,    base_file: std.Io.File,    wal_file: std.Io.File,    pager: pager.Pager,    /// The length of the log file. Every write, truncation and    /// replacement of the file sets it, so an append that starts here    /// ends the file where its write stops.    wal_written: usize,    wal_synced: bool = true,    max_wal_bytes: usize,    wal_io: WalIo = .{},    base_loaded: bool,    read_cache: ReadCache,    digest_memo: DigestMemo,    read_leases: ReadLeaseRegistry,    base_epoch: u64 = 1,    tree_roots: tree.RootCache = .{},    transaction_staging: TransactionStaging,    write_capacity: pager.Capacity = .{},    write_reserved: bool = false,    write_transaction_open: bool = false,    coordinator_active: bool = false,    recovery_required: bool = false,    writable: bool = true,    state_serial: u64 = 0,    write_serial: u64 = 0,    publication_state: ?PublicationState = null,    testing_workspace_owned: if (builtin.is_test) bool else void = if (builtin.is_test) false else {},    pub fn openForTesting(        allocator: Allocator,        dir: std.Io.Dir,        options: OpenOptions,    ) Error!Database {        if (!builtin.is_test) @compileError("openForTesting is available only in tests");        const workspace = try allocator.create(Workspace);        errdefer allocator.destroy(workspace);        const path_storage_limits = try PathStorage.Limits.forOpen(options);        workspace.* = try Workspace.allocate(allocator, .{            .header = options.header,            .max_wal_bytes = options.wal_capacity_bytes orelse options.max_wal_bytes,            .path_storage = path_storage_limits,            .read_cache_pages = options.read_cache_capacity,        });        errdefer workspace.deallocate(allocator);        var database = try open(allocator, workspace, dir, options);        database.testing_workspace_owned = true;        return database;    }    pub fn ownWorkspaceForTesting(self: *Database) void {        if (!builtin.is_test) @compileError("ownWorkspaceForTesting is available only in tests");        std.debug.assert(!self.testing_workspace_owned);        self.testing_workspace_owned = true;    }    /// Opens the configured database so that a caller gets a usable database    /// whether the files are new, intact, or left mid-write by an earlier run,    /// recovering whatever the last run left behind by replaying the    /// write-ahead log and rewriting the base file or the log when recovery    /// calls for it. The call borrows the path storage, the read cache, and the    /// transaction staging area from the workspace, and takes its own handle on    /// the directory and its own copies of both paths so that the caller's    /// handle and path strings can go. A failure at any step gives every    /// borrowed region and every opened handle back before it returns, so the    /// workspace is reusable, while a failure to read or write the files after    /// the database is open poisons it with `RecoveryRequired`, and the caller    /// reopens.    pub fn open(        allocator: Allocator,        workspace: *Workspace,        dir: std.Io.Dir,        options: OpenOptions,    ) Error!Database {        const phase = trace.scope("file.database.open");        defer phase.end();        const wal_capacity_bytes = options.wal_capacity_bytes orelse options.max_wal_bytes;        if (wal_capacity_bytes > options.max_wal_bytes) return error.InvalidWalLimit;        const path_storage_limits = try PathStorage.Limits.forOpen(options);        var path_storage = try workspace.acquirePathStorage(path_storage_limits);        errdefer workspace.releasePathStorage(&path_storage) catch unreachable;        var read_cache = try workspace.acquireReadCache(.{            .pages = options.read_cache_capacity,        });        errdefer workspace.releaseReadCache(&read_cache);        read_cache.activate();        var digest_memo = try workspace.acquireDigestMemo();        errdefer workspace.releaseDigestMemo(&digest_memo);        try options.control.check();        var publication_state = try PublicationState.init(            &path_storage,            options.publication,        );        errdefer if (publication_state) |*state| state.deinit();        var active_paths = options.paths;        var active_lane: ?publication.Lane = null;        if (publication_state) |*state| {            try options.control.check();            if (state.selected) |token| {                var source = try publication.openSelected(                    options.io,                    dir,                    state.basePair(),                    token,                );                defer source.deinit(options.io);                state.candidate_lane = source.lane ^ 1;                active_lane = try publication.Lane.init(                    state.basePair(),                    state.candidate_lane,                );                try publication.prepareOpened(options.io, dir, &source, &active_lane.?);            } else {                try publication.reset(options.io, dir, state.basePair());                active_lane = try publication.Lane.init(                    state.basePair(),                    state.candidate_lane,                );            }            const pair = active_lane.?.pair();            active_paths = .{ .database = pair.database, .wal = pair.wal };            state.phase = .candidate;        }        try options.control.check();        var recovered = try recoverState(allocator, workspace, dir, .{            .io = options.io,            .paths = active_paths,            .header = options.header,            .max_wal_bytes = options.max_wal_bytes,            .wal_capacity_bytes = wal_capacity_bytes,            .control = options.control,        });        errdefer recovered.deinit();        try options.control.check();        if (recovered.rewrite_base) {            try writeBase(options.io, dir, active_paths.database, &recovered.pager);            recovered.pager.releaseDurableBase();            recovered.base_loaded = false;        }        if (recovered.rewrite_wal) try writeWal(options.io, dir, active_paths.wal, &recovered.pager);        try options.control.check();        var owned_dir = try dir.openDir(options.io, ".", .{});        errdefer owned_dir.close(options.io);        var base_file = try dir.createFile(options.io, active_paths.database, .{ .read = true, .truncate = false });        errdefer base_file.close(options.io);        var wal_file = try dir.createFile(options.io, active_paths.wal, .{ .read = true, .truncate = false });        errdefer wal_file.close(options.io);        const stored_paths = path_storage.storeCurrent(active_paths) catch unreachable;        var transaction_staging = try workspace.acquireTransactionStaging(.{            .frames = try walFrameCapacity(wal_capacity_bytes),        });        errdefer workspace.releaseTransactionStaging(&transaction_staging);        transaction_staging.activate();        trace.progress("file.database.open.complete");        return .{            .allocator = allocator,            .workspace = workspace,            .io = options.io,            .dir = owned_dir,            .path_storage = path_storage,            .paths = stored_paths,            .base_file = base_file,            .wal_file = wal_file,            .pager = recovered.pager,            .wal_written = recovered.pager.walBytes().len,            .max_wal_bytes = options.max_wal_bytes,            .base_loaded = recovered.base_loaded,            .read_cache = read_cache,            .digest_memo = digest_memo,            .read_leases = try ReadLeaseRegistry.init(.{                .leases = options.read_lease_limit,            }),            .transaction_staging = transaction_staging,            .write_capacity = options.write_capacity,            .publication_state = publication_state,        };    }    pub fn openPublishedReadOnly(        allocator: Allocator,        workspace: *Workspace,        dir: std.Io.Dir,        options: PublishedReadOpenOptions,    ) Error!Database {        const path_storage_limits = try PathStorage.Limits.forPublished(options.base_paths);        var path_storage = try workspace.acquirePathStorage(path_storage_limits);        errdefer workspace.releasePathStorage(&path_storage) catch unreachable;        var read_cache = try workspace.acquireReadCache(.{            .pages = options.read_cache_capacity,        });        errdefer workspace.releaseReadCache(&read_cache);        read_cache.activate();        var digest_memo = try workspace.acquireDigestMemo();        errdefer workspace.releaseDigestMemo(&digest_memo);        var opened = try publication.openSelected(options.io, dir, .{            .database = options.base_paths.database,            .wal = options.base_paths.wal,        }, options.token);        var opened_owned = true;        errdefer if (opened_owned) opened.deinit(options.io);        const lane = try publication.Lane.init(.{            .database = options.base_paths.database,            .wal = options.base_paths.wal,        }, opened.lane);        const pair = lane.pair();        var recovered = switch (try recoverReadOnlyFiles(            allocator,            workspace,            opened.database,            opened.wal,            .{                .io = options.io,                .paths = .{ .database = pair.database, .wal = pair.wal },                .header = options.header,                .max_wal_bytes = options.max_wal_bytes,                .read_cache_capacity = options.read_cache_capacity,            },        )) {            .ready => |ready| ready,            .repair_required => return error.InvalidPublication,        };        errdefer workspace.release(&recovered);        var owned_dir = try dir.openDir(options.io, ".", .{});        errdefer owned_dir.close(options.io);        const stored_paths = path_storage.storeCurrent(.{            .database = pair.database,            .wal = pair.wal,        }) catch unreachable;        var transaction_staging = try workspace.acquireTransactionStaging(.{ .frames = 0 });        errdefer workspace.releaseTransactionStaging(&transaction_staging);        transaction_staging.activate();        opened_owned = false;        return .{            .allocator = allocator,            .workspace = workspace,            .io = options.io,            .dir = owned_dir,            .path_storage = path_storage,            .paths = stored_paths,            .base_file = opened.database,            .wal_file = opened.wal,            .pager = recovered,            .wal_written = recovered.walBytes().len,            .max_wal_bytes = options.max_wal_bytes,            .base_loaded = false,            .read_cache = read_cache,            .digest_memo = digest_memo,            .read_leases = try ReadLeaseRegistry.init(.{                .leases = options.read_lease_limit,            }),            .transaction_staging = transaction_staging,            .writable = false,        };    }    pub fn openPublishedReadOnlyForTesting(        allocator: Allocator,        dir: std.Io.Dir,        options: PublishedReadOpenOptions,    ) Error!Database {        if (!builtin.is_test) @compileError("openPublishedReadOnlyForTesting is available only in tests");        const workspace = try allocator.create(Workspace);        errdefer allocator.destroy(workspace);        const path_storage_limits = try PathStorage.Limits.forPublished(options.base_paths);        workspace.* = try Workspace.allocate(allocator, .{            .header = options.header,            .max_wal_bytes = options.max_wal_bytes,            .path_storage = path_storage_limits,            .read_cache_pages = options.read_cache_capacity,        });        errdefer workspace.deallocate(allocator);        var database = try openPublishedReadOnly(allocator, workspace, dir, options);        database.ownWorkspaceForTesting();        return database;    }    pub fn openReadOnly(        allocator: Allocator,        workspace: *Workspace,        dir: std.Io.Dir,        options: ReadOpenOptions,    ) Error!Database {        const path_storage_limits = PathStorage.Limits.forDirect(options.paths);        var path_storage = try workspace.acquirePathStorage(path_storage_limits);        errdefer workspace.releasePathStorage(&path_storage) catch unreachable;        var read_cache = try workspace.acquireReadCache(.{            .pages = options.read_cache_capacity,        });        errdefer workspace.releaseReadCache(&read_cache);        read_cache.activate();        var digest_memo = try workspace.acquireDigestMemo();        errdefer workspace.releaseDigestMemo(&digest_memo);        var base_file = try dir.openFile(options.io, options.paths.database, .{            .allow_directory = false,        });        errdefer base_file.close(options.io);        var wal_file = try dir.openFile(options.io, options.paths.wal, .{            .allow_directory = false,        });        errdefer wal_file.close(options.io);        var recovered = switch (try recoverReadOnlyFiles(            allocator,            workspace,            base_file,            wal_file,            options,        )) {            .ready => |ready| ready,            .repair_required => return error.InvalidDatabaseFile,        };        errdefer workspace.release(&recovered);        var owned_dir = try dir.openDir(options.io, ".", .{});        errdefer owned_dir.close(options.io);        const stored_paths = path_storage.storeCurrent(options.paths) catch unreachable;        var transaction_staging = try workspace.acquireTransactionStaging(.{ .frames = 0 });        errdefer workspace.releaseTransactionStaging(&transaction_staging);        transaction_staging.activate();        return .{            .allocator = allocator,            .workspace = workspace,            .io = options.io,            .dir = owned_dir,            .path_storage = path_storage,            .paths = stored_paths,            .base_file = base_file,            .wal_file = wal_file,            .pager = recovered,            .wal_written = recovered.walBytes().len,            .max_wal_bytes = options.max_wal_bytes,            .base_loaded = false,            .read_cache = read_cache,            .digest_memo = digest_memo,            .read_leases = try ReadLeaseRegistry.init(.{                .leases = options.read_lease_limit,            }),            .transaction_staging = transaction_staging,            .writable = false,        };    }    pub fn openReadOnlyForTesting(        allocator: Allocator,        dir: std.Io.Dir,        options: ReadOpenOptions,    ) Error!Database {        if (!builtin.is_test) @compileError("openReadOnlyForTesting is available only in tests");        const workspace = try allocator.create(Workspace);        errdefer allocator.destroy(workspace);        workspace.* = try Workspace.allocate(allocator, .{            .header = options.header,            .max_wal_bytes = options.max_wal_bytes,            .path_storage = PathStorage.Limits.forDirect(options.paths),            .read_cache_pages = options.read_cache_capacity,        });        errdefer workspace.deallocate(allocator);        var database = try openReadOnly(allocator, workspace, dir, options);        database.ownWorkspaceForTesting();        return database;    }    /// Closes the database and returns everything it borrowed back to the    /// workspace, releasing the read lease registry, closing the base file, the    /// log file, and the directory handle, and returning the pager, the read    /// cache, the transaction staging area, and the path storage. The call    /// frees the cached tree roots through the allocator that `open` was given,    /// and requires that no write transaction is open, which it asserts.    pub fn deinit(self: *Database) void {        const allocator = self.allocator;        const workspace = self.workspace;        const testing_workspace_owned = if (builtin.is_test)            self.testing_workspace_owned        else            false;        std.debug.assert(!self.write_transaction_open);        self.read_leases.deinit();        self.base_file.close(self.io);        self.wal_file.close(self.io);        self.dir.close(self.io);        self.workspace.release(&self.pager);        self.workspace.releaseReadCache(&self.read_cache);        self.workspace.releaseDigestMemo(&self.digest_memo);        self.workspace.releaseTransactionStaging(&self.transaction_staging);        self.tree_roots.deinit(self.allocator);        if (self.publication_state) |*state| state.deinit();        self.workspace.releasePathStorage(&self.path_storage) catch            @panic("invalid database path-storage loan");        self.* = undefined;        if (testing_workspace_owned) {            workspace.deallocate(allocator);            allocator.destroy(workspace);        }    }    pub fn reserve(self: *Database, capacity: pager.Capacity) Error!void {        try self.ensureUsable();        try self.ensureMutable();        try self.pager.reserve(capacity);    }    pub fn beginRead(self: *Database) Error!ReadLease {        try self.ensureUsable();        const index = try self.read_leases.available();        var base_file = try self.leaseBaseFile();        errdefer base_file.deinit(self.io);        const view = (try self.pager.beginRead()).view;        const serial = try self.read_leases.install(            index,            base_file,            view,            self.base_epoch,        );        return .{ .database = self, .index = index, .serial = serial };    }    /// Returns the base file handle for a new read lease. A lane switch    /// closes `base_file` while older leases still read, so a database that    /// publishes through lanes gives each lease its own handle. Any other    /// database keeps `base_file` open until `deinit`, which every lease ends    /// before, so its leases share it.    fn leaseBaseFile(self: *Database) Error!ReadLeaseFile {        if (self.publication_state == null) return .{ .borrowed = &self.base_file };        return .{ .owned = try self.dir.openFile(self.io, self.paths.database, .{}) };    }    pub fn savepoint(self: *Database) Error!Savepoint {        try self.ensureUsable();        if (self.write_transaction_open) return error.WriteTransactionOpen;        return .{            .database = self,            .position = self.pager.position(),            .epoch = self.pager.restoreEpoch(),            .wal_written = self.wal_written,            .state_serial = self.state_serial,            .write_serial = self.write_serial,        };    }    pub fn restore(self: *Database, point: Savepoint) Error!void {        try self.ensureUsable();        try self.ensureMutable();        if (point.database != self) return error.TransactionConflict;        if (self.write_transaction_open) return error.WriteTransactionOpen;        if (self.state_serial == point.state_serial) {            if (self.write_serial != point.write_serial) {                return error.TransactionConflict;            }            if (!std.meta.eql(self.pager.position(), point.position)) {                return error.TransactionConflict;            }            if (self.wal_written != point.wal_written) return error.TransactionConflict;            return;        }        const expected_serial = std.math.add(u64, point.state_serial, 1) catch            return error.TransactionConflict;        const expected_write = std.math.add(u64, point.write_serial, 1) catch            return error.TransactionConflict;        if (self.state_serial != expected_serial) return error.TransactionConflict;        if (self.write_serial != expected_write) return error.TransactionConflict;        if (!self.pager.canRestore(point.position, point.epoch)) {            return error.TransactionConflict;        }        try self.restoreTo(point);    }    pub fn restoreWrites(        self: *Database,        before: Savepoint,        after: Savepoint,    ) Error!void {        try self.ensureUsable();        try self.ensureMutable();        if (before.database != self or after.database != self) {            return error.TransactionConflict;        }        if (self.write_transaction_open) return error.WriteTransactionOpen;        if (self.state_serial != after.state_serial or            self.write_serial != after.write_serial or            !std.meta.eql(self.pager.position(), after.position) or            !std.meta.eql(self.pager.restoreEpoch(), after.epoch) or            self.wal_written != after.wal_written)        {            return error.TransactionConflict;        }        const state_delta = std.math.sub(            u64,            after.state_serial,            before.state_serial,        ) catch return error.TransactionConflict;        const write_delta = std.math.sub(            u64,            after.write_serial,            before.write_serial,        ) catch return error.TransactionConflict;        if (state_delta == 0 or state_delta != write_delta) {            return error.TransactionConflict;        }        if (!self.pager.canRestore(before.position, before.epoch)) {            return error.TransactionConflict;        }        try self.restoreTo(before);    }    fn restoreTo(self: *Database, point: Savepoint) Error!void {        if (self.read_leases.active != 0) return error.ActiveReaders;        try self.ensurePublicationCandidate();        self.wal_synced = false;        self.wal_file.setLength(self.io, point.position.journal.len) catch |err|            return self.ioFailure(err);        self.wal_io.resizes += 1;        self.wal_file.sync(self.io) catch |err|            return self.ioFailure(err);        self.wal_synced = true;        self.pager.restore(point.position);        self.wal_written = point.wal_written;        self.state_serial = point.state_serial;        self.write_serial = point.write_serial;        self.read_cache.clearRetainingCapacity();    }    pub fn walCapacityBytes(self: *const Database) usize {        return self.pager.walCapacityBytes();    }    pub fn walIo(self: *const Database) WalIo {        return self.wal_io;    }    /// Opens the database's single write transaction so that a caller can stage    /// pages inside that write scope, reserving the configured write capacity    /// on first use and giving the transaction its staging area. The call    /// reports `WriteTransactionOpen` when one is already open, and reports    /// `UncommittedWalTail` when the log holds frames past the committed end,    /// which an interrupted write leaves. A refusal for capacity or for an    /// uncommitted tail leaves no transaction open, so the caller is free to    /// checkpoint, raise the configured capacity, or reopen and try again.    pub fn beginWrite(self: *Database) Error!Transaction {        const phase = trace.scope("file.database.begin_write");        defer phase.end();        try self.ensureUsable();        try self.ensureMutable();        try self.ensurePublicationCandidate();        if (self.write_transaction_open) return error.WriteTransactionOpen;        if (!self.write_reserved) {            try self.pager.reserve(self.write_capacity);            self.write_reserved = true;        }        const view = try self.pager.currentView();        if (view.end_mark != self.pager.frameCount()) return error.UncommittedWalTail;        const position = self.pager.position();        self.transaction_staging.begin(self.pager.walStagingCapacity(position));        errdefer self.transaction_staging.end();        self.write_transaction_open = true;        return .{            .database = self,            .start_position = position,            .start_generation = self.pager.baseGeneration(),            .staging = &self.transaction_staging,        };    }    fn appendWal(self: *Database, page_id: u32, db_page_count: u32, image: *const [page.size]u8) Error!void {        const phase = trace.scope("file.database.append_wal");        defer phase.end();        try self.ensureWalFrames(1);        const position = self.pager.position();        const wal_written = self.wal_written;        errdefer self.restoreAppendState(position, wal_written) catch self.poison();        try self.pager.appendWal(page_id, db_page_count, image);        self.persistWal(position.journal.len) catch |err|            return self.ioFailure(err);        trace.progress("file.database.append_wal.complete");    }    fn appendWalSync(self: *Database, page_id: u32, db_page_count: u32, image: *const [page.size]u8) Error!void {        try self.appendWal(page_id, db_page_count, image);        try self.syncWal();    }    pub fn syncWal(self: *Database) Error!void {        const phase = trace.scope("file.database.sync_wal");        defer phase.end();        try self.ensureUsable();        if (self.wal_synced) return;        try self.ensureMutable();        self.wal_file.sync(self.io) catch |err|            return self.ioFailure(err);        self.wal_io.syncs += 1;        self.wal_synced = true;        trace.progress("file.database.sync_wal.complete");    }    pub fn walSynced(self: *const Database) bool {        return self.wal_synced;    }    /// Copies committed pages from the write-ahead log into the base file,    /// which keeps the log from growing without bound. The call reports    /// `RecoveryRequired` while a coordinator holds the database, and reports    /// `ActiveReaders` when a read lease is still holding the current base    /// generation, checking that condition before anything is written. When    /// leases from earlier generations are live, the checkpoint keeps older    /// readers working by holding back the pages they still need. The operation    /// rewrites the log when the checkpoint restarted it. A failure to write    /// the files poisons the database with `RecoveryRequired`.    pub fn checkpoint(self: *Database, options: CheckpointOptions) Error!pager.Checkpoint {        const phase = trace.scope("file.database.checkpoint");        defer phase.end();        try self.ensureUsable();        try options.control.check();        try self.ensureMutable();        try self.ensurePublicationCandidate();        if (self.coordinator_active) return error.RecoveryRequired;        if (self.read_leases.hasEpoch(self.base_epoch)) return error.ActiveReaders;        const oldest = self.read_leases.oldest();        try self.advanceStateSerial();        var prepared = try self.pager.prepareCheckpoint(.{            .readers = if (oldest) |view| .{ .oldest = view } else .none,            .restart_header = options.restart_header,        });        defer prepared.deinit();        try options.control.check();        const has_readers = oldest != null;        const result = if (has_readers) result: {            const committed = try self.pager.commitCheckpoint(prepared);            self.writePreparedCheckpoint(prepared) catch |err|                return self.ioFailure(err);            try options.control.check();            break :result committed;        } else result: {            self.writePreparedCheckpoint(prepared) catch |err|                return self.ioFailure(err);            try options.control.check();            const committed = self.pager.commitDurableCheckpoint(prepared) catch |err|                return self.ioFailure(err);            self.base_loaded = false;            break :result committed;        };        try options.control.check();        if (result.restarted) {            self.rewriteWal() catch |err|                return self.ioFailure(err);        }        try options.control.check();        trace.progress("file.database.checkpoint.complete");        return result;    }    fn writePreparedCheckpoint(self: *Database, prepared: pager.PreparedCheckpoint) Error!void {        const result = prepared.result();        if (result.pages == 0) return;        std.debug.assert(prepared.checkpointPageCount() == result.pages);        try writeCheckpointPages(self.io, self.base_file, prepared, self.pager.databasePageCount());        self.read_cache.clearRetainingCapacity();    }    pub fn flush(self: *Database) Error!void {        const phase = trace.scope("file.database.flush");        defer phase.end();        try self.ensureUsable();        try self.ensureMutable();        if (self.read_leases.active != 0) return error.ActiveReaders;        try self.ensurePublicationCandidate();        if (self.coordinator_active) return error.RecoveryRequired;        try self.advanceStateSerial();        try self.ensureBaseLoaded();        writeBaseFile(self.io, self.base_file, &self.pager) catch |err|            return self.ioFailure(err);        self.rewriteWal() catch |err|            return self.ioFailure(err);        self.pager.releaseDurableBase();        self.base_loaded = false;        self.read_cache.clearRetainingCapacity();        trace.progress("file.database.flush.complete");    }    pub fn beginCoordinator(self: *Database) Error!void {        try self.ensureUsable();        try self.ensureMutable();        if (self.coordinator_active or self.write_transaction_open) {            return error.RecoveryRequired;        }        try self.ensurePublicationCandidate();        self.coordinator_active = true;    }    pub fn endCoordinator(self: *Database) void {        std.debug.assert(self.coordinator_active);        self.coordinator_active = false;    }    pub fn poison(self: *Database) void {        self.recovery_required = true;    }    pub fn requiresRecovery(self: *const Database) bool {        return self.recovery_required;    }    pub fn preparePublication(self: *Database) Error!publication.Token {        try self.ensureUsable();        try self.ensureMutable();        if (self.write_transaction_open or self.coordinator_active) {            return error.RecoveryRequired;        }        const state = if (self.publication_state) |*value|            value        else            return error.InvalidPublication;        if (state.phase != .candidate) return error.InvalidPublication;        self.syncWal() catch |err| return self.ioFailure(err);        self.base_file.sync(self.io) catch |err|            return self.ioFailure(err);        const lane = try publication.Lane.init(state.basePair(), state.candidate_lane);        if (!std.mem.eql(u8, self.paths.database, lane.pair().database) or            !std.mem.eql(u8, self.paths.wal, lane.pair().wal))        {            return error.InvalidPublication;        }        publication.seal(self.io, self.dir, &lane, state.candidate) catch |err|            return self.ioFailure(err);        state.phase = .sealed;        return state.candidate;    }    pub fn publicationCommitted(self: *Database, next: publication.Token) void {        std.debug.assert(self.writable);        std.debug.assert(!self.recovery_required);        const state = if (self.publication_state) |*value| value else unreachable;        std.debug.assert(state.phase == .sealed);        std.debug.assert(next != 0);        std.debug.assert(next != state.candidate);        state.selected = state.candidate;        state.candidate = next;        state.candidate_lane ^= 1;        state.phase = .selected;    }    fn ensureUsable(self: *const Database) Error!void {        if (self.recovery_required) return error.RecoveryRequired;    }    fn ensureMutable(self: *const Database) Error!void {        if (!self.writable) return error.ReadOnlyDatabase;    }    fn ioFailure(self: *Database, err: Error) Error {        if (!self.coordinator_active) self.poison();        return err;    }    fn ensurePublicationCandidate(self: *Database) Error!void {        const state = if (self.publication_state) |*value| value else return;        switch (state.phase) {            .candidate => return,            .sealed => return error.RecoveryRequired,            .selected => {},        }        if (self.write_transaction_open or self.coordinator_active) {            return error.RecoveryRequired;        }        const lane = try publication.Lane.init(state.basePair(), state.candidate_lane);        const pair = lane.pair();        std.debug.assert(self.path_storage.admitsCurrent(.{            .database = pair.database,            .wal = pair.wal,        }));        publication.prepare(self.io, self.dir, self.currentPair(), &lane) catch |err|            return self.ioFailure(err);        self.openPublicationLane(&lane) catch |err| return self.ioFailure(err);        state.phase = .candidate;    }    fn openPublicationLane(self: *Database, lane: *const publication.Lane) Error!void {        const next_epoch = std.math.add(u64, self.base_epoch, 1) catch            return error.GenerationOverflow;        const pair = lane.pair();        const base_file = try self.dir.openFile(self.io, pair.database, .{ .mode = .read_write });        errdefer base_file.close(self.io);        const wal_file = try self.dir.openFile(self.io, pair.wal, .{ .mode = .read_write });        const stored_paths = self.path_storage.storeCurrent(.{            .database = pair.database,            .wal = pair.wal,        }) catch unreachable;        self.base_file.close(self.io);        self.wal_file.close(self.io);        self.paths = stored_paths;        self.base_file = base_file;        self.wal_file = wal_file;        self.base_epoch = next_epoch;        self.read_cache.clearRetainingCapacity();    }    fn currentPair(self: *const Database) publication.Pair {        return .{ .database = self.paths.database, .wal = self.paths.wal };    }    fn rewriteWal(self: *Database) Error!void {        if (self.pager.walBytes().len > self.max_wal_bytes) return error.WalLimitExceeded;        self.wal_synced = false;        try writeWal(self.io, self.dir, self.paths.wal, &self.pager);        const reopened = try self.dir.createFile(self.io, self.paths.wal, .{ .read = true, .truncate = false });        self.wal_file.close(self.io);        self.wal_file = reopened;        self.wal_written = self.pager.walBytes().len;        self.wal_synced = true;    }    fn appendStagedTransactionWal(self: *Database, position: pager.Pager.Position, count: usize, database_page_count: u32) Error!void {        try self.ensureWalFrames(count);        const wal_written = self.wal_written;        errdefer self.restoreAppendState(position, wal_written) catch self.poison();        try self.pager.commitStagedWal(position, count, database_page_count);        self.persistWal(position.journal.len) catch |err|            return self.ioFailure(err);    }    fn ensureWalFrames(self: *const Database, count: usize) Error!void {        const appended = std.math.mul(usize, count, wal.frame_size) catch return error.TransactionTooLarge;        const minimum = std.math.add(usize, wal.header_size, appended) catch return error.TransactionTooLarge;        if (minimum > self.max_wal_bytes) return error.TransactionTooLarge;        const projected = std.math.add(usize, self.pager.walBytes().len, appended) catch return error.TransactionTooLarge;        if (projected > self.max_wal_bytes) return error.WalLimitExceeded;    }    fn persistWal(self: *Database, start: usize) Error!void {        const bytes = self.pager.walBytes();        std.debug.assert(start == self.wal_written);        std.debug.assert(start <= bytes.len);        self.wal_synced = false;        try self.wal_file.writePositionalAll(self.io, bytes[start..], start);        self.wal_io.writes += 1;        self.wal_written = bytes.len;    }    fn restoreAppendState(        self: *Database,        position: pager.Pager.Position,        wal_written: usize,    ) Error!void {        self.pager.restore(position);        try self.wal_file.setLength(self.io, position.journal.len);        self.wal_io.resizes += 1;        self.wal_written = wal_written;    }    fn advanceStateSerial(self: *Database) Error!void {        self.state_serial = std.math.add(u64, self.state_serial, 1) catch            return error.GenerationOverflow;    }    fn advanceWriteSerial(self: *Database) Error!void {        const state_serial = std.math.add(u64, self.state_serial, 1) catch            return error.GenerationOverflow;        const write_serial = std.math.add(u64, self.write_serial, 1) catch            return error.GenerationOverflow;        self.state_serial = state_serial;        self.write_serial = write_serial;    }    fn ensureBaseLoaded(self: *Database) Error!void {        if (self.base_loaded) return;        const count = self.pager.basePageCount();        const generation = self.pager.baseGeneration();        var page_id: u32 = 1;        while (page_id <= count) : (page_id += 1) _ = try self.loadBasePage(page_id, generation);        self.base_loaded = true;    }    fn loadBasePage(self: *Database, page_id: u32, generation: u64) Error!?[]const u8 {        if (page_id == 0 or generation == 0 or page_id > self.pager.basePageCount()) return null;        if (try self.pager.pageAt(page_id, .{ .base_generation = generation, .end_mark = 0 })) |bytes| return bytes;        var image: [page.size]u8 = undefined;        if (!(try self.copyBasePage(page_id, generation, &image))) return null;        try self.pager.installBaseAtGeneration(page_id, &image, generation);        return try self.pager.pageAt(page_id, .{ .base_generation = generation, .end_mark = 0 });    }    fn copyBasePage(self: *Database, page_id: u32, generation: u64, image: *[page.size]u8) Error!bool {        if (page_id == 0) return error.InvalidPageId;        if (generation == 0 or page_id > self.pager.basePageCount()) return false;        const key = ReadCacheKey{ .generation = generation, .page_id = page_id };        if (self.read_cache.get(key, image)) return true;        const n = try self.base_file.readPositionalAll(self.io, image[0..], pageOffset(page_id));        if (n != page.size) return error.InvalidDatabaseFile;        self.read_cache.put(key, image) catch |err| switch (err) {            error.CacheDisabled => {},        };        return true;    }};

Source: lib/sql/src/root.zig:178

zig
pub const FileDatabase = file.Database;
Called byCallsNo direct callersprivate sourcelib.sql.src.file.DatabaseensureMutableprivate sourcelib.sql.src.file.DatabaseensurePublicationCandidateprivate sourcelib.sql.src.file.DatabaseensureUsableFileDatabasebeginCoordinator
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsTreecountTreegetTreegetIntoTreeidentityTreelastKey+5 moreprivate sourcelib.sql.src.file.DatabaseensureUsableprivate sourcelib.sql.src.file.DatabaseleaseBaseFileprivate sourcelib.sql.src.file.ReadLeaseRegistryavailableprivate sourcelib.sql.src.file.ReadLeaseRegistryinstallPagerbeginReadFileDatabasebeginRead
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callersprivate sourcelib.sql.src.file.DatabaseensureMutableprivate sourcelib.sql.src.file.DatabaseensurePublicationCandidateprivate sourcelib.sql.src.file.DatabaseensureUsableFileTransactionStagingbeginFileTransactionStagingend+7 moreFileDatabasebeginWrite
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callersprivate sourcelib.sql.src.file.DatabaseadvanceStateSerialprivate sourcelib.sql.src.file.DatabaseensureMutableprivate sourcelib.sql.src.file.DatabaseensurePublicationCandidateprivate sourcelib.sql.src.file.DatabaseensureUsableprivate sourcelib.sql.src.file.DatabaseioFailure+9 moreFileDatabasecheckpoint
Static calls · unresolved targets: 0 · external targets: 3.
Called byCallsprivate sourcelib.sql.src.connection.TestingConnectiondeinitprivate sourcelib.sql.src.connection.TestingConnectioninitprivate sourcelib.sql.src.file.ReadLeaseRegistrydeinitprivate sourcelib.sql.src.session.staging.workspace.Workspacereleasetree.RootCachedeinittiny.tldrformats.elf.liveness.statedeinitFileDatabasedeinit
Static calls · unresolved targets: 6 · external targets: 2.
Called byCallsNo direct callersprivate sourcelib.sql.src.file.DatabaseadvanceStateSerialprivate sourcelib.sql.src.file.DatabaseensureBaseLoadedprivate sourcelib.sql.src.file.DatabaseensureMutableprivate sourcelib.sql.src.file.DatabaseensurePublicationCandidateprivate sourcelib.sql.src.file.DatabaseensureUsable+7 moreFileDatabaseflush
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.sql.src.filetest: file database rejects path over...test sourcelib.sql.src.filetest: file database rejects read cach...private sourcelib.sql.src.file.PathStorage.LimitsforOpenprivate sourcelib.sql.src.file.PublicationStateinitprivate sourcelib.sql.src.file.ReadLeaseRegistryinitprivate sourcelib.sql.src.filerecoverStateprivate sourcelib.sql.src.filewalFrameCapacity+9 moreFileDatabaseopen
Static calls · unresolved targets: 3 · external targets: 20.
Called byCallstest sourcelib.sql.src.catalogtest: catalog allocates distinct root...test sourcelib.sql.src.catalogtest: catalog analyzes composite inde...test sourcelib.sql.src.catalogtest: catalog analyzes relation stats...test sourcelib.sql.src.catalogtest: catalog clears relation stats w...test sourcelib.sql.src.catalogtest: catalog diagnoses pre identity ...+252 moreprivate sourcelib.sql.src.file.PathStorage.LimitsforOpenFileDatabaseopenForTesting
Static calls · unresolved targets: 2 · external targets: 3.
Called byCallsFileDatabaseopenPublishedReadOnlyForTestingprivate sourcelib.sql.src.file.PathStorage.LimitsforPublishedprivate sourcelib.sql.src.file.ReadLeaseRegistryinitprivate sourcelib.sql.src.filerecoverReadOnlyFilesprivate sourcelib.sql.src.publication.Laneinitprivate sourcelib.sql.src.publication.Lanepairprivate sourcelib.sql.src.publicationopenSelectedFileDatabaseopenPublishedReadOnly
Static calls · unresolved targets: 1 · external targets: 15.
Called byCallstest sourcelib.sql.src.filetest: file published reader pins a la...FileDatabaseopenPublishedReadOnlyprivate sourcelib.sql.src.file.PathStorage.LimitsforPublishedFileDatabaseopenPublishedReadOnlyForTesting
Static calls · unresolved targets: 1 · external targets: 4.
Called byCallsNo direct callersprivate sourcelib.sql.src.file.PathStorage.LimitsforDirectprivate sourcelib.sql.src.file.ReadLeaseRegistryinitprivate sourcelib.sql.src.filerecoverReadOnlyFilesFileDatabaseopenReadOnly
Static calls · unresolved targets: 3 · external targets: 15.
Called byCallstest sourcelib.sql.src.filetest: file ordinary reader pins files...private sourcelib.sql.src.file.PathStorage.LimitsforDirectFileDatabaseopenReadOnlyForTesting
Static calls · unresolved targets: 2 · external targets: 4.
Called byCallsNo direct callsprivate sourcelib.sql.src.file.DatabaseappendStagedTransactionWalprivate sourcelib.sql.src.file.DatabaseappendWalprivate sourcelib.sql.src.file.DatabaseioFailureFileDatabasepoison
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.sql.src.file.DatabaseensureMutableprivate sourcelib.sql.src.file.DatabaseensureUsableprivate sourcelib.sql.src.file.DatabaseioFailureFileDatabasesyncWalprivate sourcelib.sql.src.publication.Laneinit+2 moreFileDatabasepreparePublication
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsprivate sourcelib.sql.src.connection.TestingConnectioninitprivate sourcelib.sql.src.file.DatabaseensureMutableprivate sourcelib.sql.src.file.DatabaseensureUsablePagerreserveFileDatabasereserve
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.sql.src.file.DatabaseensureMutableprivate sourcelib.sql.src.file.DatabaseensureUsableprivate sourcelib.sql.src.file.DatabaserestoreToPagercanRestorePagerpositionFileDatabaserestore
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.sql.src.file.DatabaseensureMutableprivate sourcelib.sql.src.file.DatabaseensureUsableprivate sourcelib.sql.src.file.DatabaserestoreToPagercanRestorePagerpositionPagerrestoreEpochFileDatabaserestoreWrites
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.sql.src.file.DatabaseensureUsablePagerpositionPagerrestoreEpochFileDatabasesavepoint
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate sourcelib.sql.src.file.DatabaseappendWalSyncFileDatabasepreparePublicationFileTransactioncommitprivate sourcelib.sql.src.file.DatabaseensureMutableprivate sourcelib.sql.src.file.DatabaseensureUsableprivate sourcelib.sql.src.file.DatabaseioFailuretraceprogresstracescopeFileDatabasesyncWal
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callersPagerwalCapacityBytesFileDatabasewalCapacityBytes
Static calls · unresolved targets: 0 · external targets: 0.

Complete caller list for FileDatabase.beginRead

10 direct callers.

Complete call list for FileDatabase.beginWrite

12 direct calls.

Complete call list for FileDatabase.checkpoint

14 direct calls.

Complete call list for FileDatabase.flush

12 direct calls.

Complete call list for FileDatabase.open

14 direct calls.

Complete caller list for FileDatabase.openForTesting

257 direct callers.

Complete call list for FileDatabase.preparePublication

7 direct calls.

Audit

Definitions28
Public names28
Members30
Version26.7.0
Revisiondaab053ee433