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.
Source
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.
lib.sql.src.diff.decodeLower[function] — private source atlib/sql/src/diff.zig:276in nearest public ownertiny.sql.difftiny.sql.key.decodeIndex[function] atlib/sql/src/key.zig:75tiny.sql.key.decodeIndexRowId[function] atlib/sql/src/key.zig:70lib.sql.src.key.decodeValue[function] — private source atlib/sql/src/key.zig:112in nearest public ownertiny.sql.keylib.sql.src.key.test_rowid_keys_preserve_signed_integer_ordering[function] — test source atlib/sql/src/key.zig:262in nearest public ownertiny.sql.keylib.sql.src.key.test_rowid_keys_reject_malformed_buffers[function] — test source atlib/sql/src/key.zig:283in nearest public ownertiny.sql.keytiny.sql.table.Reader.lastRowId[method] atlib/sql/src/table.zig:200tiny.sql.TableScan.next[method] atlib/sql/src/table.zig:410
Complete caller list for key.encodeIndex
8 direct callers.
tiny.sql.Index.deleteIn[method] atlib/sql/src/index.zig:228tiny.sql.Index.putPayloadIn[method] atlib/sql/src/index.zig:209lib.sql.src.catalog.prepareDistributionKeys[function] — private source atlib/sql/src/catalog.zig:2228in nearest public ownertiny.sql.cataloglib.sql.src.key.expectIndexOrder[function] — private source atlib/sql/src/key.zig:413in nearest public ownertiny.sql.keylib.sql.src.key.test_index_key_decoder_exposes_collation_stored_bytes[function] — test source atlib/sql/src/key.zig:389in nearest public ownertiny.sql.keylib.sql.src.key.test_index_key_decoder_recovers_stored_values_and_rowid_suffix[function] — test source atlib/sql/src/key.zig:364in nearest public ownertiny.sql.keylib.sql.src.key.test_index_prefix_end_bounds_exact_value_prefix[function] — test source atlib/sql/src/key.zig:340in nearest public ownertiny.sql.keylib.sql.src.version.indexRootFromRows[function] — private source atlib/sql/src/version.zig:1090in nearest public ownertiny.sql.version
Complete caller list for key.encodeIndexPrefix
8 direct callers.
lib.sql.src.Reader.lookupProjection[method] — private source atlib/sql/src/index.zig:152in nearest public ownertiny.sql.indexlib.sql.src.Reader.rangeProjection[method] — private source atlib/sql/src/index.zig:123in nearest public ownertiny.sql.indexlib.sql.src.catalog.indexPrefixKey[function] — private source atlib/sql/src/catalog.zig:2404in nearest public ownertiny.sql.catalogtiny.sql.key.encodeIndex[function] atlib/sql/src/key.zig:56lib.sql.src.key.test_index_prefix_end_bounds_exact_value_prefix[function] — test source atlib/sql/src/key.zig:340in nearest public ownertiny.sql.keylib.sql.src.key.test_index_rowid_decoder_rejects_keys_without_rowid_suffix[function] — test source atlib/sql/src/key.zig:404in nearest public ownertiny.sql.keylib.sql.src.statement.access.sampleRankPrefix[function] — private source atlib/sql/src/statement/access.zig:486in nearest public ownerlib.sql.src.statement.accesslib.sql.src.statement.access.test_sample_estimates_use_local_distinct_density[function] — test source atlib/sql/src/statement/access.zig:612in nearest public ownerlib.sql.src.statement.access
Complete caller list for key.encodeRowId
14 direct callers.
lib.sql.src.diff.rowLower[function] — private source atlib/sql/src/diff.zig:594in nearest public ownertiny.sql.difflib.sql.src.diff.rowUpper[function] — private source atlib/sql/src/diff.zig:602in nearest public ownertiny.sql.difflib.sql.src.key.encodeRowIdSegment[function] — private source atlib/sql/src/key.zig:188in nearest public ownertiny.sql.keylib.sql.src.key.test_rowid_keys_preserve_signed_integer_ordering[function] — test source atlib/sql/src/key.zig:262in nearest public ownertiny.sql.keylib.sql.src.key.test_rowid_keys_reject_malformed_buffers[function] — test source atlib/sql/src/key.zig:283in nearest public ownertiny.sql.keylib.sql.src.key.writeInteger[function] — private source atlib/sql/src/key.zig:181in nearest public ownertiny.sql.keytiny.sql.table.Reader.get[method] atlib/sql/src/table.zig:206tiny.sql.table.Reader.getInto[method] atlib/sql/src/table.zig:219tiny.sql.table.Reader.scanProjected[method] atlib/sql/src/table.zig:235tiny.sql.table.Reader.valueLength[method] atlib/sql/src/table.zig:214tiny.sql.RowIdTable.deleteIn[method] atlib/sql/src/table.zig:350tiny.sql.RowIdTable.putEncodedIn[method] atlib/sql/src/table.zig:313tiny.sql.RowIdTable.putIn[method] atlib/sql/src/table.zig:295lib.sql.src.version.tableRootFromRows[function] — private source atlib/sql/src/version.zig:1073in nearest public ownertiny.sql.version
Audit
| Definitions | 11 |
|---|---|
| Public names | 11 |
| Members | 4 |
| Version | 26.7.0 |
| Revision | daab053ee433 |