Skip to documentation
SLOP

tiny.sql.table

Reference tiny.sql table

Defined in tiny.sql.

API (19)

Actions

Public operations.

Types and contracts

Public types and contracts.

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

Source

Called byCallsNo direct callerstree.Readercounttable.Readercount
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callerskeyencodeRowIdtree.Readergettable.Readerget
Static calls · unresolved targets: 1 · external targets: 2.
Called byCallsNo direct callerskeyencodeRowIdtree.ReadergetIntotable.ReadergetInto
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callerskeydecodeRowIdtree.ReaderlastKeytable.ReaderlastRowId
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsCatalogReaderopenspace.ReaderrowidTabletree.Readeropentable.Readeropen
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callerstable.ReaderscanProjectedtable.Readerscan
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstable.ReaderscankeyencodeRowIdtree.Readerscantable.ReaderscanProjected
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callerstree.Readersummarizetable.Readersummarize
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callerskeyencodeRowIdtree.ReadervalueLengthtable.ReadervalueLength
Static calls · unresolved targets: 0 · external targets: 0.

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

zig
pub const table = @import("table.zig");

Source: lib/sql/src/table.zig

zig
const std = @import("std");const alloc_phase = @import("alloc_phase");const file = @import("file.zig");const key = @import("key.zig");const page = @import("page.zig");const row = @import("row.zig");const trace = @import("trace.zig");const tree = @import("tree.zig");const wal = @import("wal.zig");const Allocator = std.mem.Allocator;const reader_get_instruction_page_alignment = 4096;pub const Error = tree.Error || row.Error || key.Error;pub const ValueLimits = struct {    max_value_bytes: usize = 0,    pub fn inspect(table: *const Table, rowid: i64) Error!?ValueLimits {        const value_bytes = (try table.valueLength(rowid)) orelse return null;        return .{ .max_value_bytes = value_bytes };    }    pub fn include(self: *ValueLimits, limits: ValueLimits) void {        self.max_value_bytes = @max(self.max_value_bytes, limits.max_value_bytes);    }};pub const ValueCapacity = struct {    max_value_bytes: usize,    storage_bytes: usize,    pub fn derive(limits: ValueLimits) ValueCapacity {        return .{            .max_value_bytes = limits.max_value_bytes,            .storage_bytes = limits.max_value_bytes,        };    }};pub const ValueStorage = struct {    phase: alloc_phase.capacity.Phase,    capacity: ValueCapacity,    bytes: []u8,    pub const Limits: type = ValueLimits;    pub const Capacity: type = ValueCapacity;    pub const Exhaustion = error{ValueCapacityExceeded};    pub const InitError: type = Allocator.Error;    pub const claim: alloc_phase.capacity.Declaration = .{        .source = .{            .id = "sql.table_value_storage",            .kind = .phase_static,            .limit_source = .caller,            .storage = .{                .covered = &.{                    .{                        .id = "one_reusable_encoded_row_value_region",                        .lifetime = .steady,                        .detail = "one reusable encoded row value region",                    },                },                .excluded = &.{                    "tree traversal, database/WAL bytes, file handles, and operating-system page cache",                    "file read cache, pager history, search hit storage, and decoded result fields",                    "caller-owned keys and trace instrumentation",                },            },            .capacity = .{                .inputs = &.{                    alloc_phase.capacity.bindInput(Limits, "max_value_bytes", "max_value_bytes"),                },                .type_selectors = &.{},                .nodes = &.{                    .{ .input = 0 },                },                .assertions = &.{.{                    .scope = .closure_total,                    .measure = .retained,                    .relation = .exact,                    .expression = 0,                }},            },            .overload = .{                .kind = .reject_before_mutation,                .detail = "oversize values return ValueCapacityExceeded before reusable bytes mutate",            },            .risks = .{                .transitive = .{                    .status = .open,                    .detail = "tree reads use separate file, pager, cache, and trace owners",                },                .foreign = .{                    .status = .open,                    .detail = "base-page reads may enter the host filesystem and OS cache",                },            },            .obligations = &.{                .{ .key = "sql_table_value_capacity", .role = .capacity_model },                .{ .key = "sql_table_value_oom_retry", .role = .custom },                .{ .key = "sql_table_value_sealed", .role = .overload },                .{ .key = "sql_table_value_semantics", .role = .custom },            },        },        .bindings = .{            .owner = @This(),            .seal = .{                .family = alloc_phase.capacity.selector(@This().activate),                .premise = .{                    .class = .checked_semantic_fact,                    .authority = .checker,                },            },            .teardown = .{                .family = alloc_phase.capacity.selector(@This().deinit),                .premise = .{                    .class = .checked_semantic_fact,                    .authority = .checker,                },            },        },    };    pub fn init(allocator: Allocator, limits: ValueLimits) InitError!ValueStorage {        const capacity = ValueCapacity.derive(limits);        const bytes = if (capacity.storage_bytes == 0)            @as([]u8, &.{})        else            try allocator.alloc(u8, capacity.storage_bytes);        return .{            .phase = .initialization,            .capacity = capacity,            .bytes = bytes,        };    }    pub fn activate(self: *ValueStorage) void {        std.debug.assert(self.phase == .initialization);        std.debug.assert(self.bytes.len == self.capacity.storage_bytes);        self.phase = .steady;    }    pub fn get(        self: *ValueStorage,        table: *const Table,        rowid: i64,    ) (Error || Exhaustion)!?[]const u8 {        std.debug.assert(self.phase == .steady);        const value = table.getInto(rowid, self.bytes) catch |err| switch (err) {            error.OutputTooSmall => return error.ValueCapacityExceeded,            else => return err,        };        return value;    }    pub fn deinit(self: *ValueStorage, allocator: Allocator) void {        std.debug.assert(self.phase != .teardown);        std.debug.assert(self.bytes.len == self.capacity.storage_bytes);        self.phase = .teardown;        if (self.bytes.len != 0) allocator.free(self.bytes);        self.bytes = &.{};    }};comptime {    alloc_phase.capacity.requireAllocatorRejectingOwnerShape(ValueStorage);}pub const Options = struct {    tree: tree.Options = .{},};/// Chooses what a table scan carries back for each row, so a scan then pays/// only for the part of each row it will look at. `key` gives the row's key and/// an empty byte slice, `record` gives the record bytes as they are stored,/// which for a large row is the pointer to its overflow pages, and `value`/// gives the decoded row value and reads the overflow pages when the record/// points at them. The projection is chosen once for a whole scan, where `scan`/// picks `value` for callers that do not say, and the type is re-exported from/// the B-tree layer so a table scan and a tree scan take the same values.pub const Projection = tree.Projection;pub const Entry = struct {    rowid: i64,    bytes: []const u8,    pub fn view(self: Entry) row.Error!row.View {        return try row.View.init(self.bytes);    }};pub const Reader = struct {    rows: tree.Reader,    pub fn open(snapshot: file.Snapshot, options: Options) Error!Reader {        return .{ .rows = try tree.Reader.open(snapshot, options.tree) };    }    pub fn lastRowId(self: *const Reader) Error!?i64 {        var buffer: [key.rowid_size]u8 = undefined;        const last = (try self.rows.lastKey(&buffer)) orelse return null;        return try key.decodeRowId(last);    }    pub fn get(self: *const Reader, allocator: Allocator, rowid: i64) align(reader_get_instruction_page_alignment) Error!?[]u8 {        const phase = trace.scope("table.get");        defer phase.end();        var key_bytes: [key.rowid_size]u8 = undefined;        return try self.rows.get(allocator, try key.encodeRowId(&key_bytes, rowid));    }    pub fn valueLength(self: *const Reader, rowid: i64) Error!?usize {        var key_bytes: [key.rowid_size]u8 = undefined;        return try self.rows.valueLength(try key.encodeRowId(&key_bytes, rowid));    }    pub fn getInto(self: *const Reader, rowid: i64, target: []u8) Error!?[]u8 {        var key_bytes: [key.rowid_size]u8 = undefined;        return try self.rows.getInto(try key.encodeRowId(&key_bytes, rowid), target);    }    /// Starts a scan of the rows from `start` up to `end` in `target`.    pub fn scan(        self: *const Reader,        target: *Scan,        allocator: Allocator,        start: ?i64,        end: ?i64,    ) Error!void {        try self.scanProjected(target, allocator, start, end, .value);    }    pub fn scanProjected(        self: *const Reader,        target: *Scan,        allocator: Allocator,        start: ?i64,        end: ?i64,        projection: Projection,    ) Error!void {        const phase = trace.scope("table.scan");        defer phase.end();        var start_bytes: [key.rowid_size]u8 = undefined;        var end_bytes: [key.rowid_size]u8 = undefined;        const start_key = if (start) |rowid| try key.encodeRowId(&start_bytes, rowid) else null;        const end_key = if (end) |rowid| try key.encodeRowId(&end_bytes, rowid) else null;        try self.rows.scan(&target.rows, allocator, start_key, end_key, projection);    }    pub fn summarize(self: *const Reader) Error!tree.Summary {        const phase = trace.scope("table.summarize");        defer phase.end();        return try self.rows.summarize();    }    /// Returns how many rows the table holds in this reader's snapshot. A    /// caller sizes a scan's output by it without walking the table.    pub fn count(self: *const Reader) Error!usize {        return try self.rows.count();    }};pub const Table = struct {    rows: tree.Tree,    pub fn open(database: *file.Database, options: Options) Error!Table {        return .{ .rows = try tree.Tree.open(database, options.tree) };    }    pub fn reader(self: *const Table, snapshot: file.Snapshot) Error!Reader {        return .{ .rows = try self.rows.reader(snapshot) };    }    pub fn lastRowId(self: *const Table) Error!?i64 {        var read = try self.rows.database.beginRead();        defer read.deinit();        const opened = try self.reader(read.snapshot());        return try opened.lastRowId();    }    pub fn put(self: *Table, rowid: i64, values: []const row.Value, options: file.CommitOptions) Error!file.Commit {        const phase = trace.scope("table.put");        defer phase.end();        var write = try tree.Write.beginTree(&self.rows);        defer write.deinit();        try self.putIn(&write, rowid, values);        return try write.commit(options);    }    pub fn putIn(self: *Table, write: *tree.Write, rowid: i64, values: []const row.Value) Error!void {        var key_bytes: [key.rowid_size]u8 = undefined;        var row_bytes: [page.size]u8 = undefined;        const encoded_key = try key.encodeRowId(&key_bytes, rowid);        const encoded_row = try row.encode(&row_bytes, values);        try write.put(&self.rows, encoded_key, encoded_row);    }    pub fn putEncoded(self: *Table, rowid: i64, bytes: []const u8, options: file.CommitOptions) Error!file.Commit {        const phase = trace.scope("table.put_encoded");        defer phase.end();        var write = try tree.Write.beginTree(&self.rows);        defer write.deinit();        try self.putEncodedIn(&write, rowid, bytes);        return try write.commit(options);    }    pub fn putEncodedIn(self: *Table, write: *tree.Write, rowid: i64, bytes: []const u8) Error!void {        _ = try row.View.init(bytes);        var key_bytes: [key.rowid_size]u8 = undefined;        try write.put(&self.rows, try key.encodeRowId(&key_bytes, rowid), bytes);    }    pub fn get(self: *const Table, allocator: Allocator, rowid: i64) Error!?[]u8 {        var read = try self.rows.database.beginRead();        defer read.deinit();        const opened = try self.reader(read.snapshot());        return try opened.get(allocator, rowid);    }    pub fn valueLength(self: *const Table, rowid: i64) Error!?usize {        var read = try self.rows.database.beginRead();        defer read.deinit();        const opened = try self.reader(read.snapshot());        return try opened.valueLength(rowid);    }    pub fn getInto(self: *const Table, rowid: i64, target: []u8) Error!?[]u8 {        var read = try self.rows.database.beginRead();        defer read.deinit();        const opened = try self.reader(read.snapshot());        return try opened.getInto(rowid, target);    }    pub fn delete(self: *Table, rowid: i64, options: file.CommitOptions) Error!file.Commit {        const phase = trace.scope("table.delete");        defer phase.end();        var write = try tree.Write.beginTree(&self.rows);        defer write.deinit();        try self.deleteIn(&write, rowid);        return try write.commit(options);    }    pub fn deleteIn(self: *Table, write: *tree.Write, rowid: i64) Error!void {        var key_bytes: [key.rowid_size]u8 = undefined;        try write.delete(&self.rows, try key.encodeRowId(&key_bytes, rowid));    }    /// Starts a scan of the rows from `start` up to `end` in `target`.    pub fn scan(        self: *const Table,        target: *Scan,        allocator: Allocator,        start: ?i64,        end: ?i64,    ) Error!void {        try self.scanProjected(target, allocator, start, end, .value);    }    pub fn scanProjected(        self: *const Table,        target: *Scan,        allocator: Allocator,        start: ?i64,        end: ?i64,        projection: Projection,    ) Error!void {        var read = try self.rows.database.beginRead();        defer read.deinit();        const opened = try self.reader(read.snapshot());        try opened.scanProjected(target, allocator, start, end, projection);    }    pub fn summarize(self: *const Table) Error!tree.Summary {        var read = try self.rows.database.beginRead();        defer read.deinit();        const opened = try self.reader(read.snapshot());        return try opened.summarize();    }    pub fn count(self: *const Table) Error!usize {        var read = try self.rows.database.beginRead();        defer read.deinit();        const opened = try self.reader(read.snapshot());        return try opened.count();    }    pub fn summarizeIn(self: *const Table, write: *const tree.Write) Error!tree.Summary {        const phase = trace.scope("table.summarize_in");        defer phase.end();        return try self.rows.summarizeIn(write);    }};pub const Scan = struct {    rows: tree.Scan,    pub fn deinit(self: *Scan) void {        self.rows.deinit();        self.* = undefined;    }    pub fn next(self: *Scan) Error!?Entry {        if (try self.rows.next()) |entry| {            return .{                .rowid = try key.decodeRowId(entry.key),                .bytes = entry.bytes,            };        }        return null;    }};test "table value capacity matches the independent maximum model" {    comptime {        @stardustClaim(            @import("alloc_phase").capacity.witness(ValueStorage, "sql_table_value_capacity"),            null,            null,            null,            null,            null,            null,        );    }    for (0..4097) |value_bytes| {        const expected = ValueCapacity{            .max_value_bytes = value_bytes,            .storage_bytes = value_bytes,        };        try std.testing.expectEqual(            expected,            ValueCapacity.derive(.{ .max_value_bytes = value_bytes }),        );    }    try std.testing.expectEqual(        ValueCapacity{            .max_value_bytes = std.math.maxInt(usize),            .storage_bytes = std.math.maxInt(usize),        },        ValueCapacity.derive(.{ .max_value_bytes = std.math.maxInt(usize) }),    );}fn checkValueStorageInitFailures(allocator: Allocator) !void {    var storage = try ValueStorage.init(allocator, .{ .max_value_bytes = 4096 });    storage.deinit(allocator);}test "table value storage initialization cleans allocation failure and retries" {    comptime {        @stardustClaim(            @import("alloc_phase").capacity.witness(ValueStorage, "sql_table_value_oom_retry"),            null,            null,            null,            null,            null,            null,        );    }    try std.testing.checkAllAllocationFailures(        std.testing.allocator,        checkValueStorageInitFailures,        .{},    );    var storage = try ValueStorage.init(std.testing.allocator, .{ .max_value_bytes = 4096 });    defer storage.deinit(std.testing.allocator);    storage.activate();    try std.testing.expectEqual(alloc_phase.capacity.Phase.steady, storage.phase);}fn checkSealedValueReads(    table: *const Table,    storage: *ValueStorage,    rejecting: *ValueStorage,    phase_allocator: *alloc_phase.SealedPhaseAllocator,    large: []const u8,) !void {    const storage_pointer = storage.bytes.ptr;    const storage_capacity = storage.capacity;    phase_allocator.seal();    storage.activate();    rejecting.activate();    const inline_bytes = (try storage.get(table, 1)).?;    try std.testing.expect(storage.bytes.ptr == storage_pointer);    try std.testing.expectEqual(storage_capacity, storage.capacity);    try std.testing.expectEqualStrings(        "inline",        (try (try row.View.init(inline_bytes)).column(0)).text,    );    const overflow_bytes = (try storage.get(table, 2)).?;    try std.testing.expect(storage.bytes.ptr == storage_pointer);    try std.testing.expectEqual(storage_capacity, storage.capacity);    try std.testing.expectEqualSlices(        u8,        large,        (try (try row.View.init(overflow_bytes)).column(0)).blob,    );    try std.testing.expect((try storage.get(table, 3)) == null);    @memset(rejecting.bytes, 0xa5);    try std.testing.expectError(error.ValueCapacityExceeded, rejecting.get(table, 2));    for (rejecting.bytes) |byte| try std.testing.expectEqual(@as(u8, 0xa5), byte);}test "table value storage is sealed before inline overflow and rejection reads" {    comptime {        @stardustClaim(            @import("alloc_phase").capacity.witness(ValueStorage, "sql_table_value_sealed"),            null,            null,            null,            null,            null,            null,        );    }    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    var database = try file.Database.openForTesting(std.testing.allocator, tmp.dir, .{        .paths = .{ .database = "table-value-sealed.db", .wal = "table-value-sealed.wal" },        .header = testingHeader(),    });    defer database.deinit();    try database.reserve(.{ .wal_frames = 64 });    var table = try Table.open(&database, .{});    _ = try table.put(1, &.{.{ .text = "inline" }}, .{ .durability = .buffered });    var large: [page.overflow_capacity + 37]u8 = undefined;    for (&large, 0..) |*byte, index| byte.* = @intCast(index % 251);    var encoded_large: [large.len + 32]u8 = undefined;    _ = try table.putEncoded(        2,        try row.encode(&encoded_large, &.{.{ .blob = &large }}),        .{ .durability = .buffered },    );    const inline_limits = (try ValueLimits.inspect(&table, 1)).?;    var combined_limits = inline_limits;    combined_limits.include((try ValueLimits.inspect(&table, 2)).?);    try std.testing.expect((try ValueLimits.inspect(&table, 3)) == null);    var phase_allocator = try alloc_phase.SealedPhaseAllocator.init(std.testing.allocator);    var storage = ValueStorage.init(        phase_allocator.initializationAllocator(),        combined_limits,    ) catch |err| {        phase_allocator.abortInitialization();        phase_allocator.deinit();        return err;    };    var rejecting = ValueStorage.init(        phase_allocator.initializationAllocator(),        inline_limits,    ) catch |err| {        storage.deinit(phase_allocator.initializationAllocator());        phase_allocator.abortInitialization();        phase_allocator.deinit();        return err;    };    defer {        if (phase_allocator.phase() == .initialization) phase_allocator.abortInitialization();        if (phase_allocator.phase() == .steady) phase_allocator.beginTeardown();        if (rejecting.phase != .teardown) rejecting.deinit(phase_allocator.teardownAllocator());        if (storage.phase != .teardown) storage.deinit(phase_allocator.teardownAllocator());        phase_allocator.deinit();    }    try checkSealedValueReads(&table, &storage, &rejecting, &phase_allocator, &large);}test "table value storage preserves typed row semantics" {    comptime {        @stardustClaim(            @import("alloc_phase").capacity.witness(ValueStorage, "sql_table_value_semantics"),            null,            null,            null,            null,            null,            null,        );    }    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    var database = try file.Database.openForTesting(std.testing.allocator, tmp.dir, .{        .paths = .{ .database = "table-value-semantics.db", .wal = "table-value-semantics.wal" },        .header = testingHeader(),    });    defer database.deinit();    try database.reserve(.{ .wal_frames = 64 });    var table = try Table.open(&database, .{});    _ = try table.put(        7,        &.{ .{ .integer = -42 }, .{ .text = "placed" }, .nil },        .{ .durability = .buffered },    );    const limits = (try ValueLimits.inspect(&table, 7)).?;    var storage = try ValueStorage.init(std.testing.allocator, limits);    defer storage.deinit(std.testing.allocator);    storage.activate();    const view = try row.View.init((try storage.get(&table, 7)).?);    try std.testing.expectEqual(@as(i64, -42), (try view.column(0)).integer);    try std.testing.expectEqualStrings("placed", (try view.column(1)).text);    try std.testing.expectEqual(row.Storage.nil, std.meta.activeTag(try view.column(2)));}test "rowid table stores typed rows and scans in rowid order" {    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    var database = try file.Database.openForTesting(std.testing.allocator, tmp.dir, .{        .paths = .{ .database = "table.db", .wal = "table.wal" },        .header = testingHeader(),    });    defer database.deinit();    try database.reserve(.{ .wal_frames = 64 });    var table = try Table.open(&database, .{});    _ = try table.put(7, &.{ .{ .integer = 7 }, .{ .text = "seven" } }, .{ .durability = .buffered });    _ = try table.put(-2, &.{ .{ .integer = -2 }, .{ .text = "minus-two" } }, .{ .durability = .buffered });    _ = try table.put(0, &.{ .{ .integer = 0 }, .{ .text = "zero" } }, .{ .durability = .buffered });    const found = (try table.get(std.testing.allocator, -2)).?;    defer std.testing.allocator.free(found);    const view = try row.View.init(found);    try std.testing.expectEqual(@as(i64, -2), (try view.column(0)).integer);    try std.testing.expectEqualStrings("minus-two", (try view.column(1)).text);    var scan: Scan = undefined;    try table.scan(&scan, std.testing.allocator, null, null);    defer scan.deinit();    const first = (try scan.next()).?;    const second = (try scan.next()).?;    const third = (try scan.next()).?;    try std.testing.expectEqual(@as(i64, -2), first.rowid);    try std.testing.expectEqual(@as(i64, 0), second.rowid);    try std.testing.expectEqual(@as(i64, 7), third.rowid);    try std.testing.expect(try scan.next() == null);}test "rowid table key projection avoids overflow materialization" {    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    var database = try file.Database.openForTesting(std.testing.allocator, tmp.dir, .{        .paths = .{ .database = "table-projection.db", .wal = "table-projection.wal" },        .header = testingHeader(),    });    defer database.deinit();    try database.reserve(.{ .wal_frames = 64 });    var table = try Table.open(&database, .{});    const large: [2048]u8 = @splat(0xa5);    _ = try table.put(        7,        &.{.{ .blob = &large }},        .{ .durability = .buffered },    );    var key_failing = std.testing.FailingAllocator.init(        std.testing.allocator,        .{ .fail_index = 0 },    );    var key_scan: Scan = undefined;    try table.scanProjected(        &key_scan,        key_failing.allocator(),        null,        null,        .key,    );    defer key_scan.deinit();    const entry = (try key_scan.next()).?;    try std.testing.expectEqual(@as(i64, 7), entry.rowid);    try std.testing.expectEqual(@as(usize, 0), entry.bytes.len);    try std.testing.expect(try key_scan.next() == null);    var value_failing = std.testing.FailingAllocator.init(        std.testing.allocator,        .{ .fail_index = 0 },    );    var value_scan: Scan = undefined;    try table.scanProjected(        &value_scan,        value_failing.allocator(),        null,        null,        .value,    );    defer value_scan.deinit();    try std.testing.expectError(error.OutOfMemory, value_scan.next());}test "rowid table reports the last assigned rowid" {    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    var database = try file.Database.openForTesting(std.testing.allocator, tmp.dir, .{        .paths = .{ .database = "table.db", .wal = "table.wal" },        .header = testingHeader(),    });    defer database.deinit();    try database.reserve(.{ .wal_frames = 256 });    var table = try Table.open(&database, .{});    try std.testing.expectEqual(@as(?i64, null), try table.lastRowId());    _ = try table.put(7, &.{ .{ .integer = 7 }, .{ .text = "seven" } }, .{ .durability = .buffered });    try std.testing.expectEqual(@as(?i64, 7), try table.lastRowId());    _ = try table.put(3, &.{ .{ .integer = 3 }, .{ .text = "three" } }, .{ .durability = .buffered });    try std.testing.expectEqual(@as(?i64, 7), try table.lastRowId());    var rowid: i64 = 8;    while (rowid <= 600) : (rowid += 1) {        _ = try table.put(rowid, &.{ .{ .integer = rowid }, .{ .text = "filler" } }, .{ .durability = .buffered });    }    try std.testing.expectEqual(@as(?i64, 600), try table.lastRowId());}test "rowid table deletes and recovers after reopen" {    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    {        var database = try file.Database.openForTesting(std.testing.allocator, tmp.dir, .{            .paths = .{ .database = "table.db", .wal = "table.wal" },            .header = testingHeader(),        });        defer database.deinit();        try database.reserve(.{ .wal_frames = 64 });        var table = try Table.open(&database, .{});        _ = try table.put(1, &.{.{ .text = "one" }}, .{ .durability = .buffered });        _ = try table.put(2, &.{.{ .text = "two" }}, .{ .durability = .buffered });        _ = try table.delete(1, .{ .durability = .buffered });        try database.syncWal();    }    var reopened = try file.Database.openForTesting(std.testing.allocator, tmp.dir, .{        .paths = .{ .database = "table.db", .wal = "table.wal" },        .header = recoveredHeader(),    });    defer reopened.deinit();    var table = try Table.open(&reopened, .{});    const missing = try table.get(std.testing.allocator, 1);    if (missing) |bytes| std.testing.allocator.free(bytes);    try std.testing.expect(missing == null);    const found = (try table.get(std.testing.allocator, 2)).?;    defer std.testing.allocator.free(found);    const view = try row.View.init(found);    try std.testing.expectEqualStrings("two", (try view.column(0)).text);}fn testingHeader() wal.Header {    return .{        .sequence = 601,        .salt = .{ .first = 0x7171_6262, .second = 0x5353_4444 },    };}fn recoveredHeader() wal.Header {    return .{        .sequence = 602,        .salt = .{ .first = 0x8888_9999, .second = 0xaaaa_bbbb },    };}

Audit

Definitions13
Public names13
Members2
Version26.7.0
Revisiondaab053ee433