Skip to documentation
SLOP

tiny.sql.TreeScan

Reference tiny.sql TreeScan

Defined in tree.

API (17)

Actions

Public operations.

Fields and members

Public fields and members.

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

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;
Called byCallsNo direct callsIndexScandeinitTableScandeinitTreeRangedeinittree.Readercountprivate sourcelib.sql.src.treeexpectScansMatchModel+3 moreTreeScandeinit
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsIndexScannextTableScannextTreeRangenexttree.Readercounttest sourcelib.sql.src.treetest: tree reader keeps a fixed read ...+2 moreprivate sourcelib.sql.src.tree.ScannextEntryTreeScannext
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsTreeRangestatsprivate sourcelib.sql.src.treeexpectScansMatchModelTreeScanstats
Static calls · unresolved targets: 0 · external targets: 0.

Complete caller list for TreeScan.deinit

8 direct callers.

Complete caller list for TreeScan.next

7 direct callers.

Audit

Definitions4
Public names8
Members14
Version26.7.0
Revisiondaab053ee433