feat: add appendSegment and rowPrefix

This commit is contained in:
dualfroz committed 2026-09-04 15:29:11 +02:00
1 parent f36fa7f122
commit 4784acc242
1 file changed
+155
+155
View File
@@ -0,0 +1,155 @@
const std = @import("std");
const dbmod = @import("db.zig");
const row = @import("row.zig");
const Allocator = std.mem.Allocator;
const Db = dbmod.Db;
const Value = row.Value;
/// Keyspace layout shared by every table and index living inside a single
/// Db instance:
/// "R\x00" table "\x00" orderedPk -> encoded Row
/// "I\x00" table "\x00" field "\x00" orderedValue orderedPk -> encoded pk Value
/// "J\x00" table "\x00" indexName "\x00" orderedValue1 .. orderedValueN orderedPk -> encoded pk Value
/// "M\x00INDEXES\x00" table -> encoded list of indexed field names
/// "M\x00CINDEXES\x00" table -> encoded list of composite index specs
/// "M\x00UNIQUE\x00" table -> encoded list of UNIQUE-constrained field names
/// "M\x00FKEYS\x00" table -> encoded list of FOREIGN KEY specs (field, refTable, refField)
/// "M\x00CHECKS\x00" table -> encoded list of CHECK constraint specs (name, expr source)
/// "M\x00REFBY\x00" table -> encoded list of reverse FK specs (childTable, childField, refField),
/// one entry per OTHER table's FOREIGN KEY that names this table+refField
///
/// All namespaces share one flat LSM keyspace (there is only one Db), so the
/// leading tag byte plus NUL separators keep tables, single-field indexes,
/// composite indexes, and metadata from ever colliding or shadowing each
/// other during a scan. A composite index entry's value columns are just N
/// single-column `row.appendOrdered` encodings concatenated in the index's
/// declared field order ahead of the primary key's - each encoding is
/// already self-delimiting (fixed width for none/boolean/int/float, a
/// 0x00 0x00 terminator for text/bytes with 0x00 bytes escaped), so the
/// concatenation compares byte-for-byte in exactly the same order as
/// comparing the value tuples component by component. That is what makes a
/// prefix scan over the leading K of N components ("leftmost prefix" rule,
/// same as a real multi-column B-tree index) a correct equality lookup on
/// just those K columns, unconstrained on the rest.
pub const rowTag: u8 = 'R';
pub const indexTag: u8 = 'I';
pub const compositeIndexTag: u8 = 'J';
pub const metaTag: u8 = 'M';
const sep: u8 = 0x00;
fn appendSegment(out: *std.ArrayList(u8), segment: []const u8) !void {
try out.appendSlice(segment);
try out.append(sep);
}
pub fn rowPrefix(allocator: Allocator, table: []const u8) ![]u8 {
var out = std.ArrayList(u8).init(allocator);
errdefer out.deinit();
try out.append(rowTag);
try appendSegment(&out, table);
return out.toOwnedSlice();
}
pub fn rowKey(allocator: Allocator, table: []const u8, pk: Value) ![]u8 {
var out = std.ArrayList(u8).init(allocator);
errdefer out.deinit();
try out.append(rowTag);
try appendSegment(&out, table);
try row.appendOrdered(&out, pk);
return out.toOwnedSlice();
}
pub fn indexPrefix(allocator: Allocator, table: []const u8, field: []const u8) ![]u8 {
var out = std.ArrayList(u8).init(allocator);
errdefer out.deinit();
try out.append(indexTag);
try appendSegment(&out, table);
try appendSegment(&out, field);
return out.toOwnedSlice();
}
pub fn indexEntryKey(allocator: Allocator, table: []const u8, field: []const u8, value: Value, pk: Value) ![]u8 {
var out = std.ArrayList(u8).init(allocator);
errdefer out.deinit();
try out.append(indexTag);
try appendSegment(&out, table);
try appendSegment(&out, field);
try row.appendOrdered(&out, value);
try row.appendOrdered(&out, pk);
return out.toOwnedSlice();
}
pub fn indexValueBound(allocator: Allocator, table: []const u8, field: []const u8, value: Value) ![]u8 {
var out = std.ArrayList(u8).init(allocator);
errdefer out.deinit();
try out.append(indexTag);
try appendSegment(&out, table);
try appendSegment(&out, field);
try row.appendOrdered(&out, value);
return out.toOwnedSlice();
}
fn metaKey(allocator: Allocator, table: []const u8) ![]u8 {
var out = std.ArrayList(u8).init(allocator);
errdefer out.deinit();
try out.append(metaTag);
try appendSegment(&out, "INDEXES");
try out.appendSlice(table);
return out.toOwnedSlice();
}
fn compositeMetaKey(allocator: Allocator, table: []const u8) ![]u8 {
var out = std.ArrayList(u8).init(allocator);
errdefer out.deinit();
try out.append(metaTag);
try appendSegment(&out, "CINDEXES");
try out.appendSlice(table);
return out.toOwnedSlice();
}
fn uniqueMetaKey(allocator: Allocator, table: []const u8) ![]u8 {
var out = std.ArrayList(u8).init(allocator);
errdefer out.deinit();
try out.append(metaTag);
try appendSegment(&out, "UNIQUE");
try out.appendSlice(table);
return out.toOwnedSlice();
}
fn foreignKeyMetaKey(allocator: Allocator, table: []const u8) ![]u8 {
var out = std.ArrayList(u8).init(allocator);
errdefer out.deinit();
try out.append(metaTag);
try appendSegment(&out, "FKEYS");
try out.appendSlice(table);
return out.toOwnedSlice();
}
fn checkMetaKey(allocator: Allocator, table: []const u8) ![]u8 {
var out = std.ArrayList(u8).init(allocator);
errdefer out.deinit();
try out.append(metaTag);
try appendSegment(&out, "CHECKS");
try out.appendSlice(table);
return out.toOwnedSlice();
}
fn referencedByMetaKey(allocator: Allocator, table: []const u8) ![]u8 {
var out = std.ArrayList(u8).init(allocator);
errdefer out.deinit();
try out.append(metaTag);
try appendSegment(&out, "REFBY");
try out.appendSlice(table);
return out.toOwnedSlice();
}
pub fn compositeIndexPrefix(allocator: Allocator, table: []const u8, indexName: []const u8) ![]u8 {
var out = std.ArrayList(u8).init(allocator);
errdefer out.deinit();
try out.append(compositeIndexTag);
try appendSegment(&out, table);
try appendSegment(&out, indexName);
return out.toOwnedSlice();
}