feat(sqlite): navigational write (append/set/flush/delete) for the SQLite backend#122
feat(sqlite): navigational write (append/set/flush/delete) for the SQLite backend#122Admnwk wants to merge 1 commit into
Conversation
…Lite backend The SQLite backend exposed read + SQL passthrough only; navigational write (AdsAppendRecord / AdsSetString / AdsWriteRecord / AdsDeleteRecord) returned an error, while MariaDB/PostgreSQL/Firebird/ODBC already supported it. This closes that gap so the in-process SQLite driver matches the other SQL backends' write contract. - SqliteTable: add staging buffer (staging_row/nulls, pending_append, row_dirty), mirroring MariaTable/FirebirdTable. - SqliteConnection: implement append_blank / set_field / flush_record / delete_record. flush emits a parameterized INSERT (pending_append) or a rowid-keyed UPDATE; delete a rowid-keyed DELETE. SQLite rowid is the implicit key, so no multi-column PK snapshot is needed; values bound via sqlite3_bind_* (no string escaping), new rows located via sqlite3_last_insert_rowid + rowid-list reload. - ace_exports: dispatch the four ABI write entry points to the SQLite backend, following the existing per-backend pattern. - Self-contained doctest (abi_plus_sqlite_write_test): seeds a temp .db via the sqlite3 C API, then drives append/update/delete purely through the ACE ABI. No external server needed. Suite: 876/876 pass, /WX (-Werror) clean, MSVC x64. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request implements navigational write operations (append, set field, flush, and delete) for the SQLite backend, aligning it with other supported database backends. It adds write staging fields to SqliteTable, implements the corresponding execution logic in SqliteConnection, and includes unit tests. Feedback on the changes highlights a critical issue in SqliteConnection::delete_record where deleting a record can leave the cursor in an inconsistent state, potentially causing stale data reads or out-of-bounds access if the deleted record was the last one.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| tbl->row_dirty = false; | ||
| tbl->pending_append = false; | ||
| return load_rowids(db, tbl); |
There was a problem hiding this comment.
After deleting the current record, the cursor state is left in an inconsistent and potentially dangerous state:
- Stale Data Read:
tbl->row_validremainstrueandtbl->current_rowis not cleared, meaning subsequent read operations will return stale data from the deleted record. - Out-of-Bounds Access: Since the record is deleted, the list of rowids returned by
load_rowidswill shrink by 1. If the deleted record was the last record in the table,tbl->poswill now be equal totbl->rowids.size(). Becausetbl->row_validis stilltrue, any subsequent operation attempting to accesstbl->rowids[tbl->pos]will result in an out-of-bounds access and potential crash.
Setting tbl->row_valid = false and checking if tbl->pos is out of bounds to clear tbl->positioned resolves these issues.
tbl->row_dirty = false;
tbl->pending_append = false;
tbl->row_valid = false;
if (auto r = load_rowids(db, tbl); !r) return r.error();
if (tbl->pos >= tbl->rowids.size()) {
tbl->positioned = false;
}
return util::Result<void>{};|
Withdrawing to re-verify against real-world data before resubmitting. |
Summary
The SQLite backend supported read + SQL passthrough only; the navigational
write path (
AdsAppendRecord/AdsSetString/AdsWriteRecord/AdsDeleteRecord) returned an error, while the MariaDB / PostgreSQL /Firebird / ODBC backends already implement it. This closes that gap so the
in-process SQLite driver matches the other SQL backends' write contract.
Changes
SqliteTable: add a staging buffer (staging_row/staging_nulls,pending_append,row_dirty), mirroringMariaTable/FirebirdTable.SqliteConnection: implementappend_blank/set_field/flush_record/delete_record.flush_recordemits a parameterizedINSERT(pending append) or a rowid-keyedUPDATE;delete_recordarowid-keyed
DELETE. SQLite's implicitrowidis the key, so nomulti-column PK snapshot is needed; values are bound via
sqlite3_bind_*(no string escaping), and freshly-inserted rows are located with
sqlite3_last_insert_rowid+ a rowid-list reload.ace_exports.cpp: dispatch the four ABI write entry points to theSQLite backend, following the existing per-backend dispatch pattern.
abi_plus_sqlite_write_test): seeds a temp.dbvia the sqlite3 C API, then drives append/update/delete purelythrough the ACE ABI — no external server required.
Coordination
This touches
src/abi/ace_exports.cpp(the ABI write dispatch). The additionsare additive and mirror the existing per-backend blocks — happy to adjust
placement or style to your preference.
Testing
/WX(warnings-as-errors) clean, MSVC x64.🤖 Generated with Claude Code (Opus 4.8)