Skip to documentation
SLOP

tiny.sql.key

Reference tiny.sql key

Defined in tiny.sql.

API (10)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

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

Source

Called byCallsprivate sourcelib.sql.src.catalogindexPrefixKeytest sourcelib.sql.src.keytest: index key decoder exposes colla...test sourcelib.sql.src.keytest: index key decoder recovers stor...test sourcelib.sql.src.keytest: index rowid decoder rejects key...RelationvalidateIndexesprivate sourcelib.sql.src.statement.predicatecoveredIndexValueskeydecodeRowIdprivate sourcelib.sql.src.keydecodeValueprivate sourcelib.sql.src.keyindexRowIdOffsetkeydecodeIndex
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsIndexScannexttest sourcelib.sql.src.keytest: index prefix end bounds exact v...test sourcelib.sql.src.keytest: index rowid decoder rejects key...keydecodeRowIdprivate sourcelib.sql.src.keyindexRowIdOffsetkeydecodeIndexRowId
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsprivate sourcelib.sql.src.diffdecodeLowerkeydecodeIndexkeydecodeIndexRowIdprivate sourcelib.sql.src.keydecodeValuetest sourcelib.sql.src.keytest: rowid keys preserve signed inte...+3 morekeydecodeRowId
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsIndexdeleteInIndexputPayloadInprivate sourcelib.sql.src.catalogprepareDistributionKeysprivate sourcelib.sql.src.keyexpectIndexOrdertest sourcelib.sql.src.keytest: index key decoder exposes colla...+3 morekeyencodeIndexPrefixprivate sourcelib.sql.src.keyencodeRowIdSegmentkeyencodeIndex
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate sourcelib.sql.src.ReaderlookupProjectionprivate sourcelib.sql.src.ReaderrangeProjectionprivate sourcelib.sql.src.catalogindexPrefixKeykeyencodeIndextest sourcelib.sql.src.keytest: index prefix end bounds exact v...+3 moreprivate sourcelib.sql.src.keyencodeValuekeyencodeIndexPrefix
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsprivate sourcelib.sql.src.ReaderlookupProjectionprivate sourcelib.sql.src.ReaderrangeProjectiontest sourcelib.sql.src.keytest: index prefix end bounds exact v...keyencodePrefixEnd
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsprivate sourcelib.sql.src.diffrowLowerprivate sourcelib.sql.src.diffrowUpperprivate sourcelib.sql.src.keyencodeRowIdSegmenttest sourcelib.sql.src.keytest: rowid keys preserve signed inte...test sourcelib.sql.src.keytest: rowid keys reject malformed buf...+9 morekeyencodeRowId
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/sql/src/key.zig:3

zig
const std = @import("std");const row = @import("row.zig");pub const Error = error{    InvalidKey,    OutputTooSmall,};pub const rowid_size: usize = 8;pub const DecodedIndex = struct {    values: []const row.Value,    rowid: i64,};const DecodedValue = struct {    value: row.Value,    key_len: usize,    scratch_len: usize = 0,};const DecodedBytes = struct {    bytes: []const u8,    key_len: usize,};const sign_bit: u64 = 0x8000_0000_0000_0000;const nil_tag: u8 = 0x00;const integer_tag: u8 = 0x10;const text_tag: u8 = 0x20;const blob_tag: u8 = 0x30;const rowid_tag: u8 = 0x40;pub fn encodeRowId(target: []u8, rowid: i64) Error![]const u8 {    if (target.len < rowid_size) return error.OutputTooSmall;    const sortable = @as(u64, @bitCast(rowid)) ^ sign_bit;    std.mem.writeInt(u64, target[0..rowid_size], sortable, .big);    return target[0..rowid_size];}pub fn decodeRowId(bytes: []const u8) Error!i64 {    if (bytes.len != rowid_size) return error.InvalidKey;    const sortable = std.mem.readInt(u64, bytes[0..rowid_size], .big);    return @bitCast(sortable ^ sign_bit);}pub fn encodeIndexPrefix(target: []u8, values: []const row.Value, columns: []const row.Column) Error![]const u8 {    var cursor: usize = 0;    for (values, 0..) |value, index| {        const column = if (index < columns.len) columns[index] else row.Column{};        cursor += try encodeValue(target[cursor..], value, column);    }    return target[0..cursor];}pub fn encodeIndex(target: []u8, values: []const row.Value, columns: []const row.Column, rowid: i64) Error![]const u8 {    const prefix = try encodeIndexPrefix(target, values, columns);    var cursor = prefix.len;    cursor += try encodeRowIdSegment(target[cursor..], rowid);    return target[0..cursor];}pub fn encodePrefixEnd(target: []u8, prefix: []const u8) Error![]const u8 {    if (target.len < prefix.len + 1) return error.OutputTooSmall;    @memcpy(target[0..prefix.len], prefix);    target[prefix.len] = 0xff;    return target[0 .. prefix.len + 1];}pub fn decodeIndexRowId(bytes: []const u8) Error!i64 {    const offset = try indexRowIdOffset(bytes);    return try decodeRowId(bytes[offset + 1 ..]);}pub fn decodeIndex(target: []row.Value, scratch: []u8, bytes: []const u8) Error!DecodedIndex {    const rowid_offset = try indexRowIdOffset(bytes);    var cursor: usize = 0;    var scratch_cursor: usize = 0;    var count: usize = 0;    while (cursor < rowid_offset) {        if (count >= target.len) return error.OutputTooSmall;        const decoded = try decodeValue(bytes[cursor..rowid_offset], scratch[scratch_cursor..]);        target[count] = decoded.value;        cursor += decoded.key_len;        scratch_cursor += decoded.scratch_len;        count += 1;    }    return .{        .values = target[0..count],        .rowid = try decodeRowId(bytes[rowid_offset + 1 ..]),    };}fn indexRowIdOffset(bytes: []const u8) Error!usize {    if (bytes.len < rowid_size + 1) return error.InvalidKey;    const offset = bytes.len - rowid_size - 1;    if (bytes[offset] != rowid_tag) return error.InvalidKey;    return offset;}fn encodeValue(target: []u8, value: row.Value, column: row.Column) Error!usize {    return switch (value) {        .nil => writeTag(target, nil_tag),        .integer => |integer| writeInteger(target, integer),        .text => |text| writeText(target, text, column.collation),        .blob => |blob| writeBlob(target, blob),    };}fn decodeValue(source: []const u8, scratch: []u8) Error!DecodedValue {    if (source.len == 0) return error.InvalidKey;    return switch (source[0]) {        nil_tag => .{            .value = .nil,            .key_len = 1,        },        integer_tag => integer: {            if (source.len < rowid_size + 1) return error.InvalidKey;            break :integer .{                .value = .{ .integer = try decodeRowId(source[1..][0..rowid_size]) },                .key_len = rowid_size + 1,            };        },        text_tag => text: {            const decoded = try decodeEscaped(source[1..], scratch);            break :text .{                .value = .{ .text = decoded.bytes },                .key_len = decoded.key_len + 1,                .scratch_len = decoded.bytes.len,            };        },        blob_tag => blob: {            const decoded = try decodeEscaped(source[1..], scratch);            break :blob .{                .value = .{ .blob = decoded.bytes },                .key_len = decoded.key_len + 1,                .scratch_len = decoded.bytes.len,            };        },        else => error.InvalidKey,    };}fn decodeEscaped(source: []const u8, scratch: []u8) Error!DecodedBytes {    var cursor: usize = 0;    var scratch_cursor: usize = 0;    while (cursor < source.len) {        const byte = source[cursor];        if (byte == 0) {            if (cursor + 1 >= source.len) return error.InvalidKey;            const next = source[cursor + 1];            if (next == 0) {                return .{                    .bytes = scratch[0..scratch_cursor],                    .key_len = cursor + 2,                };            }            if (next != 0xff) return error.InvalidKey;            if (scratch_cursor >= scratch.len) return error.OutputTooSmall;            scratch[scratch_cursor] = 0;            scratch_cursor += 1;            cursor += 2;            continue;        }        if (scratch_cursor >= scratch.len) return error.OutputTooSmall;        scratch[scratch_cursor] = byte;        scratch_cursor += 1;        cursor += 1;    }    return error.InvalidKey;}fn writeTag(target: []u8, tag: u8) Error!usize {    if (target.len < 1) return error.OutputTooSmall;    target[0] = tag;    return 1;}fn writeInteger(target: []u8, integer: i64) Error!usize {    if (target.len < rowid_size + 1) return error.OutputTooSmall;    target[0] = integer_tag;    _ = try encodeRowId(target[1..], integer);    return rowid_size + 1;}fn encodeRowIdSegment(target: []u8, rowid: i64) Error!usize {    if (target.len < rowid_size + 1) return error.OutputTooSmall;    target[0] = rowid_tag;    _ = try encodeRowId(target[1..], rowid);    return rowid_size + 1;}fn writeText(target: []u8, text: []const u8, collation: row.Collation) Error!usize {    if (target.len < 1) return error.OutputTooSmall;    target[0] = text_tag;    return 1 + try writeEscapedText(target[1..], text, collation);}fn writeBlob(target: []u8, blob: []const u8) Error!usize {    if (target.len < 1) return error.OutputTooSmall;    target[0] = blob_tag;    return 1 + try writeEscapedBytes(target[1..], blob);}fn writeEscapedText(target: []u8, text: []const u8, collation: row.Collation) Error!usize {    const bytes = switch (collation) {        .binary => text,        .nocase => text,        .rtrim => trimRightSpaces(text),    };    var cursor: usize = 0;    for (bytes) |byte| {        const encoded = switch (collation) {            .binary, .rtrim => byte,            .nocase => asciiLower(byte),        };        cursor += try writeEscapedByte(target[cursor..], encoded);    }    cursor += try writeTerminator(target[cursor..]);    return cursor;}fn writeEscapedBytes(target: []u8, bytes: []const u8) Error!usize {    var cursor: usize = 0;    for (bytes) |byte| cursor += try writeEscapedByte(target[cursor..], byte);    cursor += try writeTerminator(target[cursor..]);    return cursor;}fn writeEscapedByte(target: []u8, byte: u8) Error!usize {    if (byte == 0) {        if (target.len < 2) return error.OutputTooSmall;        target[0] = 0;        target[1] = 0xff;        return 2;    }    if (target.len < 1) return error.OutputTooSmall;    target[0] = byte;    return 1;}fn writeTerminator(target: []u8) Error!usize {    if (target.len < 2) return error.OutputTooSmall;    target[0] = 0;    target[1] = 0;    return 2;}fn asciiLower(byte: u8) u8 {    if (byte >= 'A' and byte <= 'Z') return byte + ('a' - 'A');    return byte;}fn trimRightSpaces(bytes: []const u8) []const u8 {    var end = bytes.len;    while (end > 0 and bytes[end - 1] == ' ') end -= 1;    return bytes[0..end];}test "rowid keys preserve signed integer ordering" {    const values = [_]i64{        std.math.minInt(i64),        -1_000_000,        -1,        0,        1,        1_000_000,        std.math.maxInt(i64),    };    var previous: [rowid_size]u8 = undefined;    for (values, 0..) |value, index| {        var encoded: [rowid_size]u8 = undefined;        _ = try encodeRowId(&encoded, value);        try std.testing.expectEqual(value, try decodeRowId(&encoded));        if (index > 0) try std.testing.expect(std.mem.order(u8, &previous, &encoded) == .lt);        previous = encoded;    }}test "rowid keys reject malformed buffers" {    var short: [rowid_size - 1]u8 = undefined;    try std.testing.expectError(error.OutputTooSmall, encodeRowId(&short, 1));    try std.testing.expectError(error.InvalidKey, decodeRowId(&short));}test "index keys sort by storage class value collation and rowid" {    try expectIndexOrder(        &.{row.Value.nil},        9,        &.{.{ .integer = std.math.minInt(i64) }},        0,        &.{},        .lt,    );    try expectIndexOrder(        &.{.{ .integer = -5 }},        9,        &.{.{ .integer = 7 }},        0,        &.{},        .lt,    );    try expectIndexOrder(        &.{.{ .text = "Alpha" }},        9,        &.{.{ .text = "alpha" }},        0,        &.{.{ .collation = .nocase }},        .gt,    );    try expectIndexOrder(        &.{.{ .text = "a " }},        1,        &.{.{ .text = "a" }},        2,        &.{.{ .collation = .rtrim }},        .lt,    );    try expectIndexOrder(        &.{.{ .text = "a\x00b" }},        9,        &.{.{ .text = "a\x00c" }},        0,        &.{},        .lt,    );    try expectIndexOrder(        &.{.{ .text = "z" }},        9,        &.{.{ .blob = "a" }},        0,        &.{},        .lt,    );}test "index prefix end bounds exact value prefix" {    var prefix_buffer: [64]u8 = undefined;    const prefix = try encodeIndexPrefix(&prefix_buffer, &.{.{ .integer = 5 }}, &.{});    var end_buffer: [64]u8 = undefined;    const end = try encodePrefixEnd(&end_buffer, prefix);    var lower_buffer: [64]u8 = undefined;    const lower = try encodeIndex(&lower_buffer, &.{.{ .integer = 4 }}, &.{}, 0);    var first_buffer: [64]u8 = undefined;    const first = try encodeIndex(&first_buffer, &.{.{ .integer = 5 }}, &.{}, -1);    var second_buffer: [64]u8 = undefined;    const second = try encodeIndex(&second_buffer, &.{.{ .integer = 5 }}, &.{}, 2);    var upper_buffer: [64]u8 = undefined;    const upper = try encodeIndex(&upper_buffer, &.{.{ .integer = 6 }}, &.{}, 0);    try std.testing.expect(std.mem.order(u8, lower, prefix) == .lt);    try std.testing.expect(std.mem.order(u8, first, prefix) == .gt);    try std.testing.expect(std.mem.order(u8, first, end) == .lt);    try std.testing.expect(std.mem.order(u8, second, end) == .lt);    try std.testing.expect(std.mem.order(u8, upper, end) == .gt);    try std.testing.expectEqual(@as(i64, -1), try decodeIndexRowId(first));    try std.testing.expectEqual(@as(i64, 2), try decodeIndexRowId(second));}test "index key decoder recovers stored values and rowid suffix" {    var key_buffer: [128]u8 = undefined;    const encoded = try encodeIndex(        &key_buffer,        &.{            .{ .integer = -42 },            .{ .text = "a\x00b" },            .{ .blob = "\x00z" },            row.Value.nil,        },        &.{},        77,    );    var values: [4]row.Value = undefined;    var scratch: [64]u8 = undefined;    const decoded = try decodeIndex(&values, &scratch, encoded);    try std.testing.expectEqual(@as(usize, 4), decoded.values.len);    try std.testing.expectEqual(@as(i64, 77), decoded.rowid);    try std.testing.expectEqual(@as(i64, -42), decoded.values[0].integer);    try std.testing.expectEqualStrings("a\x00b", decoded.values[1].text);    try std.testing.expectEqualSlices(u8, "\x00z", decoded.values[2].blob);    try std.testing.expectEqual(row.Value.nil, decoded.values[3]);}test "index key decoder exposes collation stored bytes" {    var key_buffer: [128]u8 = undefined;    const encoded = try encodeIndex(        &key_buffer,        &.{.{ .text = "Alpha  " }},        &.{.{ .collation = .rtrim }},        1,    );    var values: [1]row.Value = undefined;    var scratch: [32]u8 = undefined;    const decoded = try decodeIndex(&values, &scratch, encoded);    try std.testing.expectEqualStrings("Alpha", decoded.values[0].text);}test "index rowid decoder rejects keys without rowid suffix" {    var prefix_buffer: [64]u8 = undefined;    const prefix = try encodeIndexPrefix(&prefix_buffer, &.{.{ .text = "x" }}, &.{});    try std.testing.expectError(error.InvalidKey, decodeIndexRowId(prefix));    var values: [1]row.Value = undefined;    var scratch: [16]u8 = undefined;    try std.testing.expectError(error.InvalidKey, decodeIndex(&values, &scratch, prefix));}fn expectIndexOrder(    left_values: []const row.Value,    left_rowid: i64,    right_values: []const row.Value,    right_rowid: i64,    columns: []const row.Column,    expected: std.math.Order,) !void {    var left_buffer: [128]u8 = undefined;    var right_buffer: [128]u8 = undefined;    const left = try encodeIndex(&left_buffer, left_values, columns, left_rowid);    const right = try encodeIndex(&right_buffer, right_values, columns, right_rowid);    try std.testing.expectEqual(expected, std.mem.order(u8, left, right));}

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

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

Complete caller list for key.decodeRowId

8 direct callers.

Complete caller list for key.encodeIndex

8 direct callers.

Complete caller list for key.encodeIndexPrefix

8 direct callers.

Complete caller list for key.encodeRowId

14 direct callers.

Audit

Definitions11
Public names11
Members4
Version26.7.0
Revisiondaab053ee433