tiny.sql.TreeScan
Defined in tree.
API (17)
Actions
Public operations.
Fields and members
Public fields and members.
allocatordepthend_keyend_lenfailureframesindexleafobservedparentprojectionreadsnapshotvalue
Source
Source: lib/sql/src/tree.zig:1566
zig
pub const Scan = struct { allocator: Allocator, read: ?file.ReadLease, snapshot: file.Snapshot, frames: [max_height]BranchFrame = undefined, depth: usize = 0, /// Image of the branch at `frames[depth - 1]`, validated when read. Leaf /// advances under one parent read only the next leaf. parent: [page.size]u8 = undefined, /// Image of the current leaf, validated when read. leaf: [page.size]u8 = undefined, value: std.ArrayList(u8) = .empty, end_key: [page.size]u8 = undefined, end_len: ?usize, index: usize, projection: Projection, observed: ScanStats = .{}, /// The first error `next` returned. A page that fails to read or validate /// can be left in `leaf` or `parent`, so the scan stops there and returns /// the same error from then on. failure: ?Error = null, /// Fills `self` in place, since a scan holds three page-sized buffers /// that a return by value would copy through each caller. fn init( self: *Scan, allocator: Allocator, snapshot: file.Snapshot, root_page: u32, start: ?[]const u8, end: ?[]const u8, projection: Projection, ) Error!void { const retained = try snapshot.retain(); self.* = .{ .allocator = allocator, .read = retained, .snapshot = snapshot, .end_len = null, .index = 0, .projection = projection, }; errdefer if (self.read) |*read| read.deinit(); if (end) |key| { if (key.len > page.size) return error.KeyTooLarge; @memcpy(self.end_key[0..key.len], key); self.end_len = key.len; } const root_mark = try readRoot(snapshot, root_page, &self.leaf); try self.descend(root_page, root_mark, start); const leaf = page.Leaf.fromValidated(&self.leaf); const leaf_range = try leaf.range(start, self.endSlice()); self.index = leaf_range.index; } pub fn deinit(self: *Scan) void { self.value.deinit(self.allocator); if (self.read) |*read| read.deinit(); self.* = undefined; } pub fn next(self: *Scan) Error!?ScanEntry { if (self.failure) |failure| return failure; return self.nextEntry() catch |err| { self.failure = err; return err; }; } fn nextEntry(self: *Scan) Error!?ScanEntry { while (true) { const leaf = page.Leaf.fromValidated(&self.leaf); var leaf_range = page.Range{ .leaf = &leaf, .end = self.endSlice(), .index = self.index, }; if (leaf_range.next()) |entry| { self.index = leaf_range.index; self.observed.entries_returned += 1; return .{ .key = entry.key, .bytes = try self.valueFor(entry.value), }; } if (!(try self.advanceLeaf())) return null; } } fn valueFor(self: *Scan, bytes: []const u8) Error![]const u8 { return switch (self.projection) { .key => "", .record => bytes, .value => value: { if (bytes.len == 0) return error.InvalidRecord; break :value switch (bytes[0]) { record.inline_tag => bytes[1..], record.overflow_tag => overflow_value: { const overflow = try record.overflow(bytes); const len = try overflowLengthAsUsize(overflow.len); try self.value.resize(self.allocator, len); try readOverflowValueInto(self.snapshot, overflow, self.value.items); break :overflow_value self.value.items; }, else => error.InvalidRecord, }; }, }; } pub fn stats(self: *const Scan) ScanStats { return self.observed; } fn endSlice(self: *const Scan) ?[]const u8 { const len = self.end_len orelse return null; return self.end_key[0..len]; } /// Descends from the page image in `self.leaf`, which is `page_id` with /// mark `page_mark`, to the leaf that holds `start`, or to the leftmost /// leaf. Each page is read into scan storage and loaded once. The last /// branch passed stays in `self.parent`. fn descend(self: *Scan, page_id: u32, page_mark: file.PageMark, start: ?[]const u8) Error!void { var current_page = page_id; var mark = page_mark; while (true) { switch (try loadMarkedTreePage(&self.leaf, mark)) { .leaf => { self.observed.leaf_pages_visited += 1; return; }, .branch => { if (self.depth >= max_height) return error.TreeTooDeep; self.parent = self.leaf; const branch = page.Branch.fromValidated(&self.parent); self.observed.branch_pages_visited += 1; const index = if (start) |key| branch.childIndexFor(key) else 0; self.frames[self.depth] = .{ .page_id = current_page, .index = index, }; self.depth += 1; current_page = branch.childAt(index); mark = try readMarkedPage(self.snapshot, current_page, &self.leaf); }, } } } /// Moves to the next leaf in key order. The branch above the current leaf /// is already in `self.parent`, so an advance to a sibling reads one page. /// Only an advance past the parent's last child rereads a higher branch. fn advanceLeaf(self: *Scan) Error!bool { while (self.depth > 0) { const frame = &self.frames[self.depth - 1]; const branch = page.Branch.fromValidated(&self.parent); const next_index = frame.index + 1; if (next_index < branch.cellCount()) { if (!self.childStartsBeforeEnd(branch.lowerAt(next_index))) { self.observed.separator_children_pruned += branch.cellCount() - next_index; return false; } frame.index = next_index; const child = branch.childAt(next_index); const mark = try readMarkedPage(self.snapshot, child, &self.leaf); try self.descend(child, mark, null); self.index = 0; return true; } self.depth -= 1; if (self.depth > 0) { const parent_page = self.frames[self.depth - 1].page_id; const mark = try readMarkedPage(self.snapshot, parent_page, &self.parent); switch (try loadMarkedTreePage(&self.parent, mark)) { .leaf => return error.InvalidPage, .branch => self.observed.branch_pages_visited += 1, } } } return false; } fn childStartsBeforeEnd(self: *const Scan, lower_key: []const u8) bool { const end = self.endSlice() orelse return true; return simd.order(Bytes, lower_key, end) == .lt; }};Source: lib/sql/src/root.zig:219
zig
pub const TreeScan = tree.Scan;Complete caller list for TreeScan.deinit
8 direct callers.
tiny.sql.IndexScan.deinit[method] atlib/sql/src/index.zig:318tiny.sql.TableScan.deinit[method] atlib/sql/src/table.zig:405tiny.sql.TreeRange.deinit[method] atlib/sql/src/tree.zig:1546tiny.sql.tree.Reader.count[method] atlib/sql/src/tree.zig:771lib.sql.src.tree.expectScansMatchModel[function] — private source atlib/sql/src/tree.zig:4044in nearest public ownertiny.sql.treelib.sql.src.tree.test_tree_reader_keeps_a_fixed_read_view_without_write_declarations[function] — test source atlib/sql/src/tree.zig:2220in nearest public ownertiny.sql.treelib.sql.src.tree.test_tree_reads_mark_the_pages_they_validate[function] — test source atlib/sql/src/tree.zig:3835in nearest public ownertiny.sql.treelib.sql.src.tree.test_tree_scan_projection_controls_overflow_materialization[function] — test source atlib/sql/src/tree.zig:3354in nearest public ownertiny.sql.tree
Complete caller list for TreeScan.next
7 direct callers.
tiny.sql.IndexScan.next[method] atlib/sql/src/index.zig:323tiny.sql.TableScan.next[method] atlib/sql/src/table.zig:410tiny.sql.TreeRange.next[method] atlib/sql/src/tree.zig:1551tiny.sql.tree.Reader.count[method] atlib/sql/src/tree.zig:771lib.sql.src.tree.test_tree_reader_keeps_a_fixed_read_view_without_write_declarations[function] — test source atlib/sql/src/tree.zig:2220in nearest public ownertiny.sql.treelib.sql.src.tree.test_tree_reads_mark_the_pages_they_validate[function] — test source atlib/sql/src/tree.zig:3835in nearest public ownertiny.sql.treelib.sql.src.tree.test_tree_scan_projection_controls_overflow_materialization[function] — test source atlib/sql/src/tree.zig:3354in nearest public ownertiny.sql.tree
Audit
| Definitions | 4 |
|---|---|
| Public names | 8 |
| Members | 14 |
| Version | 26.7.0 |
| Revision | daab053ee433 |