Skip to documentation
SLOP

tiny.sql.FileTransaction

Reference tiny.sql FileTransaction

Defined in tiny.sql.

API (10)

Actions

Public operations.

Fields and members

Public fields and members.

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

Source

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

zig
pub const Transaction = struct {    database: *Database,    start_position: pager.Pager.Position,    start_generation: u64,    staging: *TransactionStaging,    closed: bool = false,    /// Ends the transaction without committing it, usually called through a    /// defer so that an abandoned transaction gives the database's single write    /// slot back and another transaction can open. The call drops the staged    /// pages, so nothing the transaction put reaches the log, and does nothing    /// on a transaction that has already committed or ended.    pub fn deinit(self: *Transaction) void {        self.close();    }    pub fn putPage(self: *Transaction, page_id: u32, image: *const [page.size]u8) Error!void {        if (self.closed) return error.TransactionClosed;        if (page_id == 0) return error.InvalidPageId;        try self.ensureCurrent();        const phase = trace.scope("file.transaction.put_page");        defer phase.end();        switch (self.findSlot(page_id)) {            .existing => |index| try self.database.pager.stageWalPage(self.start_position, index, page_id, image),            .empty => |index| {                const page_index = self.staging.nextPage() catch return self.capacityError();                try self.database.pager.stageWalPage(self.start_position, page_index, page_id, image);                self.staging.occupy(index, page_index);            },            .full => return self.capacityError(),        }    }    pub fn getPage(self: *const Transaction, page_id: u32) Error!?[]const u8 {        if (self.closed) return error.TransactionClosed;        if (page_id == 0) return error.InvalidPageId;        try self.ensureCurrent();        return switch (self.findSlot(page_id)) {            .existing => |index| self.database.pager.stagedWalPage(self.start_position, index),            .empty => null,            .full => null,        };    }    /// Returns the image this transaction staged for `page_id`, or null when    /// it staged none. Edits through the image are staged as they happen,    /// so putting the same image again copies nothing. The image stays    /// valid until the transaction commits or ends.    pub fn editPage(self: *Transaction, page_id: u32) Error!?*[page.size]u8 {        if (self.closed) return error.TransactionClosed;        if (page_id == 0) return error.InvalidPageId;        try self.ensureCurrent();        return switch (self.findSlot(page_id)) {            .existing => |index| self.database.pager.stagedWalPageMut(self.start_position, index),            .empty => null,            .full => null,        };    }    /// Appends every staged page to the write-ahead log as one committed batch    /// to make those pages part of the database, sorting the staged pages by    /// page number before appending them. Callers choose whether to wait for    /// the bytes to reach the disk through durability options: with `synced`    /// durability the call syncs the log before returning and reports the    /// commit as synced, and with `buffered` it returns as soon as the bytes    /// are written and reports the commit as not yet synced. A transaction with    /// nothing staged commits as a no-op, reporting zero frames and zero pages.    /// The call reports `TransactionConflict` when the database moved    /// underneath the transaction, and `TransactionClosed` when the transaction    /// has already ended. The transaction ends whichever way the call goes, so    /// the write slot is free even after a failure.    pub fn commit(self: *Transaction, options: CommitOptions) Error!Commit {        if (self.closed) return error.TransactionClosed;        defer self.close();        const phase = trace.scope("file.transaction.commit");        defer phase.end();        try self.ensureCurrent();        if (self.staging.pages == 0) {            return .{                .view = try self.database.pager.currentView(),                .frames = 0,                .pages = 0,                .synced = false,            };        }        self.sortPages();        var page_count = self.database.pager.databasePageCount();        for (0..self.staging.pages) |index| page_count = @max(page_count, self.pageId(index));        try self.database.advanceWriteSerial();        try self.database.appendStagedTransactionWal(self.start_position, self.staging.pages, page_count);        const view = try self.database.pager.currentView();        const frames = self.staging.pages;        const synced = switch (options.durability) {            .buffered => false,            .synced => synced: {                try self.database.syncWal();                break :synced true;            },        };        trace.progress("file.transaction.commit.complete");        return .{            .view = view,            .frames = frames,            .pages = frames,            .synced = synced,        };    }    fn close(self: *Transaction) void {        if (self.closed) return;        std.debug.assert(self.database.write_transaction_open);        self.staging.end();        self.database.write_transaction_open = false;        self.closed = true;    }    fn ensureCurrent(self: *const Transaction) Error!void {        try self.database.ensureUsable();        if (!std.meta.eql(self.database.pager.position(), self.start_position) or self.database.pager.baseGeneration() != self.start_generation) {            return error.TransactionConflict;        }    }    fn findSlot(self: *const Transaction, page_id: u32) TransactionSlot {        const slots = self.staging.capacity.index_slots;        if (slots == 0) return .full;        var index = std.hash.Wyhash.hash(0x7469_6e79_7371_6c31, std.mem.asBytes(&page_id)) % slots;        for (0..slots) |_| {            const page_index = self.staging.pageIndex(index) orelse return .{ .empty = index };            if (self.database.pager.stagedWalPageId(self.start_position, page_index) == page_id) return .{ .existing = page_index };            index = if (index + 1 == slots) 0 else index + 1;        }        return .full;    }    fn capacityError(self: *const Transaction) DatabaseFileError {        return if (self.staging.limit_frames < self.staging.capacity.frames) error.WalLimitExceeded else error.TransactionTooLarge;    }    fn sortPages(self: *Transaction) void {        if (self.staging.pages < 2) return;        self.quickSortPages(0, self.staging.pages);    }    fn quickSortPages(self: *Transaction, start: usize, end: usize) void {        if (end - start < 16) return self.insertionSortPages(start, end);        const pivot = self.pageId(start + (end - start) / 2);        var left = start;        var right = end - 1;        while (left <= right) {            while (self.pageId(left) < pivot) left += 1;            while (self.pageId(right) > pivot) {                if (right == start) break;                right -= 1;            }            if (left <= right) {                self.database.pager.swapStagedWalFrames(self.start_position, left, right);                left += 1;                if (right == start) break;                right -= 1;            }        }        if (right > start) self.quickSortPages(start, right + 1);        if (left < end) self.quickSortPages(left, end);    }    fn insertionSortPages(self: *Transaction, start: usize, end: usize) void {        var index = start + 1;        while (index < end) : (index += 1) {            var scan = index;            while (scan > start and self.pageId(scan) < self.pageId(scan - 1)) : (scan -= 1) {                self.database.pager.swapStagedWalFrames(self.start_position, scan, scan - 1);            }        }    }    fn pageId(self: *const Transaction, index: usize) u32 {        return self.database.pager.stagedWalPageId(self.start_position, index);    }};

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

zig
pub const FileTransaction = file.Transaction;
Called byCallsTreeWritecommitprivate sourcelib.sql.src.file.DatabaseadvanceWriteSerialprivate sourcelib.sql.src.file.DatabaseappendStagedTransactionWalFileDatabasesyncWalprivate sourcelib.sql.src.file.Transactioncloseprivate sourcelib.sql.src.file.TransactionensureCurrent+4 moreFileTransactioncommit
Static calls · unresolved targets: 2 · external targets: 1.
Called byCallsTreeWritedeinitprivate sourcelib.sql.src.file.TransactioncloseFileTransactiondeinit
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate sourcelib.sql.src.tree.WritereadPageSourceprivate sourcelib.sql.src.file.TransactionensureCurrentprivate sourcelib.sql.src.file.TransactionfindSlotFileTransactioneditPage
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallsprivate sourcelib.sql.src.tree.WritereadPageprivate sourcelib.sql.src.file.TransactionensureCurrentprivate sourcelib.sql.src.file.TransactionfindSlotFileTransactiongetPage
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallsTreeWritecommitprivate sourcelib.sql.src.tree.WriteputPageprivate sourcelib.sql.src.tree.WritespillFreeListprivate sourcelib.sql.src.file.TransactioncapacityErrorprivate sourcelib.sql.src.file.TransactionensureCurrentprivate sourcelib.sql.src.file.TransactionfindSlotFileTransactionStagingnextPageFileTransactionStagingoccupytracescopeFileTransactionputPage
Static calls · unresolved targets: 1 · external targets: 1.

Complete call list for FileTransaction.commit

9 direct calls.

Audit

Definitions6
Public names6
Members5
Version26.7.0
Revisiondaab053ee433