feat: add appendSegment and rowPrefix
This commit is contained in:
1 parent
f36fa7f122
commit
4784acc242
1 file changed
+155
+155
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user