tiny.sql.space
Defined in tiny.sql.
API (11)
Actions
Public operations.
Types and contracts
Public types and contracts.
Values and defaults
Public values and defaults.
Source
Source: lib/sql/src/root.zig:42
zig
pub const space = @import("space.zig");Source: lib/sql/src/space.zig
zig
const std = @import("std");const file = @import("file.zig");const index_mod = @import("index.zig");const page = @import("page.zig");const row = @import("row.zig");const table = @import("table.zig");const tree_mod = @import("tree.zig");const wal = @import("wal.zig");const SpaceError = error{ TooManyRoots,};pub const Error = table.Error || index_mod.Error || tree_mod.Error || page.Error || SpaceError;pub const max_roots: usize = 32;pub const RootSpec = struct { root_page: u32, identity_page: u32 = 0,};pub const Options = struct { meta_page: u32 = 1, roots: []const RootSpec = &.{.{ .root_page = 2 }}, reserved_page_max: u32 = 0,};const Layout = struct { meta_page: u32, roots: [max_roots]RootSpec, root_count: usize, reserved_page_max: u32, fn init(options: Options) Error!Layout { if (options.meta_page == 0) return error.InvalidPageId; if (options.roots.len == 0) return error.InvalidPageId; if (options.roots.len > max_roots) return error.TooManyRoots; var layout = Layout{ .meta_page = options.meta_page, .roots = undefined, .root_count = options.roots.len, .reserved_page_max = @max(options.meta_page, options.reserved_page_max), }; for (options.roots, 0..) |spec, offset| { try layout.addRoot(spec, offset); } return layout; } fn addRoot(self: *Layout, spec: RootSpec, offset: usize) Error!void { if (spec.root_page == 0 or spec.root_page == self.meta_page) { return error.InvalidPageId; } if (spec.identity_page != 0) { if (spec.identity_page == self.meta_page) return error.InvalidPageId; if (spec.identity_page == spec.root_page) return error.InvalidPageId; } for (self.roots[0..offset]) |previous| { if (previous.root_page == spec.root_page) return error.InvalidPageId; if (spec.identity_page != 0 and previous.identity_page == spec.identity_page) { return error.InvalidPageId; } if (previous.root_page == spec.identity_page) return error.InvalidPageId; if (previous.identity_page == spec.root_page) return error.InvalidPageId; } self.roots[offset] = spec; self.reserved_page_max = @max( self.reserved_page_max, @max(spec.root_page, spec.identity_page), ); }};fn optionsForRoot( meta_page: u32, roots: []const RootSpec, reserved_page_max: u32, root_page: u32,) Error!tree_mod.Options { for (roots) |spec| { if (spec.root_page == root_page) return .{ .meta_page = meta_page, .root_page = spec.root_page, .identity_page = spec.identity_page, .reserved_page_max = reserved_page_max, }; } return error.InvalidPageId;}pub const Reader = struct { snapshot: file.Snapshot, meta_page: u32, roots: [max_roots]RootSpec, root_count: usize, reserved_page_max: u32, pub fn open(snapshot: file.Snapshot, options: Options) Error!Reader { const layout = try Layout.init(options); return .{ .snapshot = snapshot, .meta_page = layout.meta_page, .roots = layout.roots, .root_count = layout.root_count, .reserved_page_max = layout.reserved_page_max, }; } pub fn treeOptions(self: *const Reader, root_page: u32) Error!tree_mod.Options { return try optionsForRoot( self.meta_page, self.roots[0..self.root_count], self.reserved_page_max, root_page, ); } pub fn tree(self: *const Reader, root_page: u32) Error!tree_mod.Reader { return try tree_mod.Reader.open(self.snapshot, try self.treeOptions(root_page)); } pub fn rowidTable(self: *const Reader, root_page: u32) Error!table.Reader { return try table.Reader.open(self.snapshot, .{ .tree = try self.treeOptions(root_page) }); } pub fn index( self: *const Reader, root_page: u32, columns: []const row.Column, ) Error!index_mod.Reader { return try index_mod.Reader.open(self.snapshot, .{ .tree = try self.treeOptions(root_page), .columns = columns, }); }};pub const Space = struct { database: *file.Database, meta_page: u32, roots: [max_roots]RootSpec, root_count: usize, reserved_page_max: u32, pub fn open(database: *file.Database, options: Options) Error!Space { const layout = try Layout.init(options); return .{ .database = database, .meta_page = layout.meta_page, .roots = layout.roots, .root_count = layout.root_count, .reserved_page_max = layout.reserved_page_max, }; } pub fn reader(self: *const Space, snapshot: file.Snapshot) Error!Reader { return .{ .snapshot = snapshot, .meta_page = self.meta_page, .roots = self.roots, .root_count = self.root_count, .reserved_page_max = self.reserved_page_max, }; } pub fn treeOptions(self: *const Space, root_page: u32) Error!tree_mod.Options { return try optionsForRoot( self.meta_page, self.roots[0..self.root_count], self.reserved_page_max, root_page, ); } pub fn tree(self: *const Space, root_page: u32) Error!tree_mod.Tree { return try tree_mod.Tree.open(self.database, try self.treeOptions(root_page)); } pub fn rowidTable(self: *const Space, root_page: u32) Error!table.Table { return try table.Table.open(self.database, .{ .tree = try self.treeOptions(root_page) }); } pub fn index(self: *const Space, root_page: u32, columns: []const row.Column) Error!index_mod.Index { return try index_mod.Index.open(self.database, .{ .tree = try self.treeOptions(root_page), .columns = columns, }); } pub fn beginWrite(self: *const Space) Error!tree_mod.Write { return try tree_mod.Write.begin(self.database, .{ .meta_page = self.meta_page, .reserved_page_max = self.reserved_page_max, }); }};test "space rejects invalid roots" { var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); var database = try file.Database.openForTesting(std.testing.allocator, tmp.dir, .{ .paths = .{ .database = "space.db", .wal = "space.wal" }, .header = testingHeader(), }); defer database.deinit(); try std.testing.expectError(error.InvalidPageId, Space.open(&database, .{ .roots = &.{} })); try std.testing.expectError(error.InvalidPageId, Space.open(&database, .{ .meta_page = 1, .roots = &.{ .{ .root_page = 2 }, .{ .root_page = 2 } } })); const space = try Space.open(&database, .{ .meta_page = 1, .roots = &.{ .{ .root_page = 2 }, .{ .root_page = 3 } } }); try std.testing.expectError(error.InvalidPageId, space.treeOptions(4));}test "space keeps table growth away from reserved index root" { var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); { var database = try file.Database.openForTesting(std.testing.allocator, tmp.dir, .{ .paths = .{ .database = "space.db", .wal = "space.wal" }, .header = testingHeader(), }); defer database.deinit(); try database.reserve(.{ .wal_frames = 480 }); const space = try Space.open(&database, .{ .meta_page = 1, .roots = &.{ .{ .root_page = 2 }, .{ .root_page = 3 } } }); var rows = try space.rowidTable(2); var rowid: i64 = 0; while (rowid < 180) : (rowid += 1) { _ = try rows.put(rowid, &.{ .{ .integer = rowid }, .{ .text = "payload" } }, .{ .durability = .buffered }); } { var read = try database.beginRead(); defer read.deinit(); var index_root_image: [page.size]u8 = undefined; try std.testing.expect(!(try read.snapshot().copyPage( 3, &index_root_image, ))); } var secondary = try space.index(3, &.{}); rowid = 0; while (rowid < 180) : (rowid += 1) { _ = try secondary.put(rowid, &.{.{ .integer = @mod(rowid, 9) }}, .{ .durability = .buffered }); } try database.syncWal(); } var reopened = try file.Database.openForTesting(std.testing.allocator, tmp.dir, .{ .paths = .{ .database = "space.db", .wal = "space.wal" }, .header = recoveredHeader(), }); defer reopened.deinit(); const space = try Space.open(&reopened, .{ .meta_page = 1, .roots = &.{ .{ .root_page = 2 }, .{ .root_page = 3 } } }); var rows = try space.rowidTable(2); var secondary = try space.index(3, &.{}); var lookup: index_mod.Scan = undefined; try secondary.lookup(&lookup, std.testing.allocator, &.{.{ .integer = 4 }}); defer lookup.deinit(); var seen: usize = 0; while (try lookup.next()) |entry| { const bytes = (try rows.get(std.testing.allocator, entry.rowid)).?; defer std.testing.allocator.free(bytes); const view = try row.View.init(bytes); try std.testing.expectEqual(entry.rowid, (try view.column(0)).integer); try std.testing.expectEqual(@as(i64, 4), @mod(entry.rowid, 9)); seen += 1; } try std.testing.expect(seen > 0);}fn testingHeader() wal.Header { return .{ .sequence = 901, .salt = .{ .first = 0x9191_7272, .second = 0x6363_5454 }, };}fn recoveredHeader() wal.Header { return .{ .sequence = 902, .salt = .{ .first = 0xaaaa_8888, .second = 0x9999_bbbb }, };}Audit
| Definitions | 9 |
|---|---|
| Public names | 9 |
| Members | 5 |
| Version | 26.7.0 |
| Revision | daab053ee433 |