style: adopt TigerStyle across src/; add docs/TIGER_STYLE.md
Wrap signatures and long expressions to the 100-column limit and make every file zig fmt clean. Semantics-preserving throughout: ignoring whitespace and the trailing commas that wrapping introduces, every file here is byte-identical to its predecessor, and the one apparent exception is a warning string split with `++`, which concatenates at comptime to the same bytes. src/index.zig and src/commands.zig are reformatted in the commits that follow, because their reformat is interleaved with in-flight changes to them and separating the two would need the reformat re-derived rather than moved.
This commit is contained in:
151
src/db.zig
151
src/db.zig
@@ -320,7 +320,13 @@ pub const Engine = struct {
|
||||
/// shared); the collection lock is acquired before the exclusive catalog
|
||||
/// lock is dropped, so a concurrent drop can never free it underneath.
|
||||
/// Returns null when the collection does not exist (and create is off).
|
||||
pub fn lock_collection(self: *Engine, db_name: []const u8, coll_name: []const u8, write: bool, create: bool) !?*Collection {
|
||||
pub fn lock_collection(
|
||||
self: *Engine,
|
||||
db_name: []const u8,
|
||||
coll_name: []const u8,
|
||||
write: bool,
|
||||
create: bool,
|
||||
) !?*Collection {
|
||||
var coll = self.get_collection(db_name, coll_name);
|
||||
if (coll == null and create) {
|
||||
self.catalog_lock.unlockShared(self.io);
|
||||
@@ -425,7 +431,13 @@ pub const Engine = struct {
|
||||
|
||||
/// Log an append (and its seq increment) under the log lock, marking
|
||||
/// the append as in flight so a commit leader's seal covers it.
|
||||
fn log_append(self: *Engine, comptime kind: LogKind, db: []const u8, coll: []const u8, doc: []const u8) !void {
|
||||
fn log_append(
|
||||
self: *Engine,
|
||||
comptime kind: LogKind,
|
||||
db: []const u8,
|
||||
coll: []const u8,
|
||||
doc: []const u8,
|
||||
) !void {
|
||||
_ = self.pending_appends.fetchAdd(1, .acq_rel);
|
||||
defer {
|
||||
// The increment above pairs with this decrement on every return
|
||||
@@ -468,12 +480,24 @@ pub const Engine = struct {
|
||||
|
||||
/// Insert a document. Fails with error.DuplicateKey if the _id exists.
|
||||
/// Generates an ObjectId _id when absent.
|
||||
pub fn insert(self: *Engine, db_name: []const u8, coll_name: []const u8, doc: *const bson.Document, oid_gen: *bson.ObjectIdGen) !void {
|
||||
pub fn insert(
|
||||
self: *Engine,
|
||||
db_name: []const u8,
|
||||
coll_name: []const u8,
|
||||
doc: *const bson.Document,
|
||||
oid_gen: *bson.ObjectIdGen,
|
||||
) !void {
|
||||
return self.upsert(db_name, coll_name, doc, oid_gen, .insert);
|
||||
}
|
||||
|
||||
/// Insert or replace a document by _id (upsert without existence check).
|
||||
pub fn replace(self: *Engine, db_name: []const u8, coll_name: []const u8, doc: *const bson.Document, oid_gen: *bson.ObjectIdGen) !void {
|
||||
pub fn replace(
|
||||
self: *Engine,
|
||||
db_name: []const u8,
|
||||
coll_name: []const u8,
|
||||
doc: *const bson.Document,
|
||||
oid_gen: *bson.ObjectIdGen,
|
||||
) !void {
|
||||
return self.upsert(db_name, coll_name, doc, oid_gen, .replace);
|
||||
}
|
||||
|
||||
@@ -574,7 +598,12 @@ pub const Engine = struct {
|
||||
|
||||
/// Remove a document by its `_id` value. Returns true if it existed.
|
||||
/// The serialized-key encoding stays private to the engine.
|
||||
pub fn remove_by_id(self: *Engine, db_name: []const u8, coll_name: []const u8, id: bson.Value) !bool {
|
||||
pub fn remove_by_id(
|
||||
self: *Engine,
|
||||
db_name: []const u8,
|
||||
coll_name: []const u8,
|
||||
id: bson.Value,
|
||||
) !bool {
|
||||
const id_key = try bson.serialize_value(self.gpa, id);
|
||||
defer self.gpa.free(id_key);
|
||||
return self.remove(db_name, coll_name, id_key);
|
||||
@@ -610,7 +639,12 @@ pub const Engine = struct {
|
||||
return db.collections.get(coll_name);
|
||||
}
|
||||
|
||||
pub fn get_doc(self: *Engine, db_name: []const u8, coll_name: []const u8, id_key: []const u8) ?[]const u8 {
|
||||
pub fn get_doc(
|
||||
self: *Engine,
|
||||
db_name: []const u8,
|
||||
coll_name: []const u8,
|
||||
id_key: []const u8,
|
||||
) ?[]const u8 {
|
||||
const coll = self.get_collection(db_name, coll_name) orelse return null;
|
||||
const off = coll.docs.get(id_key) orelse return null;
|
||||
return coll.doc_bytes(off);
|
||||
@@ -636,7 +670,12 @@ pub const Engine = struct {
|
||||
/// after the index builds over the existing documents and passes
|
||||
/// uniqueness, so a rejected create persists nothing. Returns the new
|
||||
/// index (or the existing one when the spec matches — idempotent).
|
||||
pub fn create_index(self: *Engine, db_name: []const u8, coll_name: []const u8, spec_doc: *const bson.Document) !*index.Index {
|
||||
pub fn create_index(
|
||||
self: *Engine,
|
||||
db_name: []const u8,
|
||||
coll_name: []const u8,
|
||||
spec_doc: *const bson.Document,
|
||||
) !*index.Index {
|
||||
const coll = try self.get_or_create_collection(db_name, coll_name);
|
||||
var ix = try index.parse_spec(self.gpa, spec_doc);
|
||||
var committed = false;
|
||||
@@ -675,7 +714,12 @@ pub const Engine = struct {
|
||||
|
||||
/// Remove a secondary index by name, persisting a drop record first.
|
||||
/// Returns false when no such index exists.
|
||||
pub fn drop_index(self: *Engine, db_name: []const u8, coll_name: []const u8, index_name: []const u8) !bool {
|
||||
pub fn drop_index(
|
||||
self: *Engine,
|
||||
db_name: []const u8,
|
||||
coll_name: []const u8,
|
||||
index_name: []const u8,
|
||||
) !bool {
|
||||
const db = self.dbs.get(db_name) orelse return false;
|
||||
const coll = db.collections.get(coll_name) orelse return false;
|
||||
if (coll.find_index(index_name) == null) return false;
|
||||
@@ -722,7 +766,13 @@ pub const Engine = struct {
|
||||
|
||||
/// Sweep one collection under its write lock; the lock is released on
|
||||
/// every return path. Returns how many documents were removed.
|
||||
fn ttl_sweep_coll(self: *Engine, coll: *Collection, now_ms: i64, db_name: []const u8, coll_name: []const u8) !usize {
|
||||
fn ttl_sweep_coll(
|
||||
self: *Engine,
|
||||
coll: *Collection,
|
||||
now_ms: i64,
|
||||
db_name: []const u8,
|
||||
coll_name: []const u8,
|
||||
) !usize {
|
||||
try coll.lock.lock(self.io);
|
||||
defer coll.lock.unlock(self.io);
|
||||
// Ids are duped rather than aliased: `remove` frees the docs-map key
|
||||
@@ -778,7 +828,11 @@ pub const Engine = struct {
|
||||
while (it.next()) |entry| try out.append(self.gpa, entry.key_ptr.*);
|
||||
}
|
||||
|
||||
pub fn collection_names(self: *Engine, db_name: []const u8, out: *std.ArrayListUnmanaged([]const u8)) !void {
|
||||
pub fn collection_names(
|
||||
self: *Engine,
|
||||
db_name: []const u8,
|
||||
out: *std.ArrayListUnmanaged([]const u8),
|
||||
) !void {
|
||||
const db = self.dbs.get(db_name) orelse return;
|
||||
var it = db.collections.iterator();
|
||||
while (it.next()) |entry| try out.append(self.gpa, entry.key_ptr.*);
|
||||
@@ -786,7 +840,11 @@ pub const Engine = struct {
|
||||
|
||||
// -- internals -----------------------------------------------------------
|
||||
|
||||
pub fn get_or_create_collection(self: *Engine, db_name: []const u8, coll_name: []const u8) !*Collection {
|
||||
pub fn get_or_create_collection(
|
||||
self: *Engine,
|
||||
db_name: []const u8,
|
||||
coll_name: []const u8,
|
||||
) !*Collection {
|
||||
const db = self.dbs.getPtr(db_name) orelse {
|
||||
const db_key = try self.gpa.dupe(u8, db_name);
|
||||
errdefer self.gpa.free(db_key);
|
||||
@@ -808,7 +866,11 @@ pub const Engine = struct {
|
||||
/// generated ObjectId `_id` when absent.
|
||||
/// The canonical bytes of `doc`, with an ObjectId `_id` generated when
|
||||
/// absent. The result is owned by the caller.
|
||||
fn serialize_with_id(self: *Engine, doc: *const bson.Document, oid_gen: *bson.ObjectIdGen) ![]u8 {
|
||||
fn serialize_with_id(
|
||||
self: *Engine,
|
||||
doc: *const bson.Document,
|
||||
oid_gen: *bson.ObjectIdGen,
|
||||
) ![]u8 {
|
||||
if (doc.get("_id") != null) return serialize_doc(self.gpa, doc);
|
||||
var pairs: std.ArrayListUnmanaged(bson.Pair) = .empty;
|
||||
defer pairs.deinit(self.gpa);
|
||||
@@ -994,7 +1056,13 @@ pub const Engine = struct {
|
||||
/// Re-emit one collection's index specs and documents into the compacted
|
||||
/// log, under the collection's write lock (released on every return
|
||||
/// path, including errors).
|
||||
fn compact_snapshot_coll(self: *Engine, coll: *Collection, new_log: *storage.Log, db_name: []const u8, coll_name: []const u8) !void {
|
||||
fn compact_snapshot_coll(
|
||||
self: *Engine,
|
||||
coll: *Collection,
|
||||
new_log: *storage.Log,
|
||||
db_name: []const u8,
|
||||
coll_name: []const u8,
|
||||
) !void {
|
||||
try coll.lock.lock(self.io);
|
||||
defer coll.lock.unlock(self.io);
|
||||
// Re-emit the index definitions first: a compacted log that dropped
|
||||
@@ -1045,7 +1113,13 @@ pub const Engine = struct {
|
||||
while (doc_it.next()) |doc_entry| {
|
||||
ix.append_doc_entries(self.gpa, coll.doc_bytes(doc_entry.value_ptr.*), doc_entry.key_ptr.*) catch |err| switch (err) {
|
||||
error.ParallelArrays => {
|
||||
std.debug.print("multiforadb: WARNING: index '{s}' cannot index an existing document; entry skipped\n", .{ix.name});
|
||||
std.debug.print(
|
||||
"multiforadb: WARNING: index '{s}' cannot index an existing " ++
|
||||
"document; entry skipped\n",
|
||||
.{
|
||||
ix.name,
|
||||
},
|
||||
);
|
||||
continue;
|
||||
},
|
||||
else => return err,
|
||||
@@ -1053,13 +1127,23 @@ pub const Engine = struct {
|
||||
}
|
||||
// Tolerated, not enforced: the database must always open.
|
||||
if (try ix.finish_bulk(self.gpa, false)) {
|
||||
std.debug.print("multiforadb: WARNING: unique index '{s}' has duplicate keys in existing data; duplicates not enforced for existing documents\n", .{ix.name});
|
||||
std.debug.print(
|
||||
"multiforadb: WARNING: unique index '{s}' has duplicate keys in existing " ++
|
||||
"data; duplicates not enforced for existing documents\n",
|
||||
.{
|
||||
ix.name,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Register an (empty) index from a persisted spec document. A repeated
|
||||
/// create record for the same name is an idempotent no-op.
|
||||
fn register_index_from_spec(self: *Engine, coll: *Collection, spec_doc: *const bson.Document) !void {
|
||||
fn register_index_from_spec(
|
||||
self: *Engine,
|
||||
coll: *Collection,
|
||||
spec_doc: *const bson.Document,
|
||||
) !void {
|
||||
var ix = try index.parse_spec(self.gpa, spec_doc);
|
||||
var committed = false;
|
||||
defer if (!committed) ix.deinit(self.gpa);
|
||||
@@ -1103,7 +1187,9 @@ fn apply_record(ctx: *anyopaque, record: storage.Record, doc: *bson.Document) an
|
||||
switch (record.type) {
|
||||
storage.record_type_index_create => {
|
||||
self.register_index_from_spec(coll, doc) catch |err| {
|
||||
std.debug.print("multiforadb: index create record failed to apply: {s}\n", .{@errorName(err)});
|
||||
std.debug.print("multiforadb: index create record failed to apply: {s}\n", .{
|
||||
@errorName(err),
|
||||
});
|
||||
return;
|
||||
};
|
||||
return;
|
||||
@@ -1455,7 +1541,12 @@ test "concurrent readers and writers on a threaded Io" {
|
||||
var remaining = std.atomic.Value(usize).init(@intCast(total));
|
||||
|
||||
const Worker = struct {
|
||||
fn writer(e: *Engine, id_counter: *std.atomic.Value(i32), pending: *std.atomic.Value(usize), alloc: std.mem.Allocator) error{Canceled}!void {
|
||||
fn writer(
|
||||
e: *Engine,
|
||||
id_counter: *std.atomic.Value(i32),
|
||||
pending: *std.atomic.Value(usize),
|
||||
alloc: std.mem.Allocator,
|
||||
) error{Canceled}!void {
|
||||
while (true) {
|
||||
const id = id_counter.fetchAdd(1, .monotonic);
|
||||
if (id > total) return;
|
||||
@@ -1605,7 +1696,11 @@ test "concurrent writers compacting: the log survives a reopen" {
|
||||
}
|
||||
}
|
||||
|
||||
fn writer(e: *Engine, id_counter: *std.atomic.Value(i32), alloc: std.mem.Allocator) error{Canceled}!void {
|
||||
fn writer(
|
||||
e: *Engine,
|
||||
id_counter: *std.atomic.Value(i32),
|
||||
alloc: std.mem.Allocator,
|
||||
) error{Canceled}!void {
|
||||
while (true) {
|
||||
const id = id_counter.fetchAdd(1, .monotonic);
|
||||
if (id > total) return;
|
||||
@@ -1645,7 +1740,14 @@ test "concurrent writers compacting: the log survives a reopen" {
|
||||
|
||||
/// A spec document for a single-path index, built by serializing and
|
||||
/// re-parsing so the pairs are arena-owned.
|
||||
fn index_spec(gpa: std.mem.Allocator, path: []const u8, name: []const u8, unique: bool, sparse: bool, ttl: ?i64) !bson.Document {
|
||||
fn index_spec(
|
||||
gpa: std.mem.Allocator,
|
||||
path: []const u8,
|
||||
name: []const u8,
|
||||
unique: bool,
|
||||
sparse: bool,
|
||||
ttl: ?i64,
|
||||
) !bson.Document {
|
||||
var out: std.ArrayListUnmanaged(u8) = .empty;
|
||||
defer out.deinit(gpa);
|
||||
var pairs: std.ArrayListUnmanaged(bson.Pair) = .empty;
|
||||
@@ -1662,7 +1764,14 @@ fn index_spec(gpa: std.mem.Allocator, path: []const u8, name: []const u8, unique
|
||||
}
|
||||
|
||||
/// Number of entries the named index has for a single-value equality key.
|
||||
fn index_count(gpa: std.mem.Allocator, engine: *Engine, db_name: []const u8, coll_name: []const u8, name: []const u8, key_value: bson.Value) !usize {
|
||||
fn index_count(
|
||||
gpa: std.mem.Allocator,
|
||||
engine: *Engine,
|
||||
db_name: []const u8,
|
||||
coll_name: []const u8,
|
||||
name: []const u8,
|
||||
key_value: bson.Value,
|
||||
) !usize {
|
||||
const coll = engine.get_collection(db_name, coll_name) orelse return 0;
|
||||
for (coll.indexes.items) |*ix| {
|
||||
if (std.mem.eql(u8, ix.name, name)) {
|
||||
|
||||
Reference in New Issue
Block a user