- wo_row_update_field: encode new value, unique re-check against a shadow BEFORE any mutation (violating update leaves the row untouched, DB_ERR_UNIQUE), index entries moved old-hash -> new-hash, old engine value freed; proven by test_table (unique refusal keeps the row, released key becomes insertable) - WAL UPDATE record: full-row re-log, replay = replace (remove + re-create same id); prefix/suffix delta recorded as later optimization; test_wal replays insert+update to the updated state - builtins 62 DB_UPDATE_FIELD (cid,id,field,value) and 63 DB_DELETE (cid,id), commit-before-ack like insert, WO_T_UNIQUE/WO_T_DB/WO_T_IO mapping; dispatch range 61..63; loader arities; runner mirror - plan Task 5 marked superseded-in-part with the recorded deviation: the language surface (reads, queries, row views, delete statement) is 9b's, where the comprehension design put it -- no interim brace- select grammar to retire later - gates: test_table 839/0, test_wal 102/0, 15 suites, oop-e2e 73/0 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
75 lines
2.5 KiB
C
75 lines
2.5 KiB
C
#include "db.h"
|
|
|
|
#include "table.h"
|
|
#include "wal.h"
|
|
|
|
int wo_builtin_db(wo_vm *vm, uint64_t *R, uint32_t ins, const char **msg) {
|
|
uint32_t A = wo_ins_a(ins), B = wo_ins_b(ins), C = wo_ins_c(ins);
|
|
wo_db *db = (wo_db *)vm->rt.db;
|
|
if (!db) {
|
|
*msg = "database engine not initialized";
|
|
return WO_T_DB;
|
|
}
|
|
switch (C) {
|
|
case WO_B_DB_INSERT: {
|
|
uint32_t cid = (uint32_t)R[B];
|
|
int ek = 0;
|
|
uint64_t id = wo_row_insert(db, cid, &R[B + 1], msg, &ek);
|
|
if (!id)
|
|
return ek == DB_ERR_UNIQUE ? WO_T_UNIQUE
|
|
: ek == DB_ERR_OOM ? WO_T_OOM
|
|
: WO_T_DB;
|
|
wo_wal *w = (wo_wal *)vm->rt.wal;
|
|
if (w) {
|
|
/* RAM applied, record staged, ONE commit before the ack (the
|
|
* builtin's return). A failed commit is a failed write: the
|
|
* row is removed again so RAM never claims what disk never
|
|
* acknowledged, and the statement traps. */
|
|
if (wo_wal_append_insert(w, db, cid, id) != 0 || wo_wal_commit(w) != 0) {
|
|
wo_row_remove(db, cid, id);
|
|
*msg = "wal commit failed";
|
|
return WO_T_IO;
|
|
}
|
|
}
|
|
R[A] = id;
|
|
return 0;
|
|
}
|
|
case WO_B_DB_UPDATE_FIELD: {
|
|
uint32_t cid = (uint32_t)R[B];
|
|
uint64_t id = R[B + 1];
|
|
uint32_t field = (uint32_t)R[B + 2];
|
|
int ek = 0;
|
|
if (wo_row_update_field(db, cid, id, field, R[B + 3], msg, &ek) != 0)
|
|
return ek == DB_ERR_UNIQUE ? WO_T_UNIQUE : ek == DB_ERR_OOM ? WO_T_OOM : WO_T_DB;
|
|
wo_wal *w = (wo_wal *)vm->rt.wal;
|
|
if (w) {
|
|
if (wo_wal_append_update(w, db, cid, id) != 0 || wo_wal_commit(w) != 0) {
|
|
*msg = "wal commit failed"; /* RAM ahead of disk: trap, do not ack */
|
|
return WO_T_IO;
|
|
}
|
|
}
|
|
R[A] = 0;
|
|
return 0;
|
|
}
|
|
case WO_B_DB_DELETE: {
|
|
uint32_t cid = (uint32_t)R[B];
|
|
uint64_t id = R[B + 1];
|
|
if (wo_row_remove(db, cid, id) != 0) {
|
|
*msg = "no such row";
|
|
return WO_T_DB;
|
|
}
|
|
wo_wal *w = (wo_wal *)vm->rt.wal;
|
|
if (w) {
|
|
if (wo_wal_append_remove(w, cid, id) != 0 || wo_wal_commit(w) != 0) {
|
|
*msg = "wal commit failed";
|
|
return WO_T_IO;
|
|
}
|
|
}
|
|
R[A] = 0;
|
|
return 0;
|
|
}
|
|
default:
|
|
*msg = "unknown db builtin";
|
|
return WO_T_DB;
|
|
}
|
|
}
|