refactor(storage): establish canonical Bun schema and DuckDB dialect - #1343
refactor(storage): establish canonical Bun schema and DuckDB dialect#1343mariusvniekerk wants to merge 19 commits into
Conversation
roborev: Combined Review (
|
roborev: Combined Review (
|
7899144 to
0a688f7
Compare
roborev: Combined Review (
|
7b0173e to
95a5c2b
Compare
roborev: Combined Review (
|
roborev: Combined Review (
|
6d0f950 to
6791f37
Compare
roborev: Combined Review (
|
The three database stores currently duplicate most query and scanning behavior, so schema parity depends on maintaining independent implementations. Record the approved cutover to one Bun-backed store and one canonical model registry before the large refactor begins.\n\nThe design preserves SQLite as the archive, PostgreSQL as an in-place-migrated sync target, and DuckDB as a rebuildable mirror. It also fixes the allowed backend seams to lifecycle, operational metadata, and small FTS/vector capabilities, avoiding a permanent legacy path during the transition.
The storage cutover spans schema, lifecycle, query, search, replication, and migration boundaries that must land in a dependency-safe order. Record executable TDD slices and verification gates so the three stores converge without weakening archive safety, PostgreSQL write capabilities, DuckDB rebuild semantics, or performance checks.
DuckDB needs a first-class Bun identity so the unified store can generate portable schema and queries without inheriting SQLite sequence or type behavior. Keep the advertised feature set conservative and tied to real driver execution, including rollback, conflict, returning, CTE, and update-from behavior.
The cutover plan needs executable decisions for identity ownership, replication conflicts, pricing state, search hit identity, Quack coverage, and performance enforcement before later slices can safely remove backend-specific paths. Record those boundaries and make the DuckDB execution suite portable to platforms without a Windows ARM64 driver binary.
DuckDB must remain distinguishable from both current and future built-in Bun dialects, and mirror IDs must stay source-assigned even when a canonical model carries an autoincrement tag for another engine. Prove the generated catalog has neither a default nor a sequence before later schema work depends on this invariant.
A unified schema still needs explicit compatibility rules for SQLite's shipped message rowid and a non-empty provenance source for parser-created sessions. Fix the logical message key, preserve the archive's physical alias without rebuilding it, and require archive/database identity to flow through migration and batch writes.
The storage cutover needs one durable row contract before adapter migration can replace three independent query paths. Define the common serving tables, source-scoped identity shapes, UTC timestamp boundary, and lossless session/message conversions so later slices can converge schema and queries against a single Bun model registry.\n\nKeep SQLite's shipped message row ID as an accepted physical alias while making session/ordinal the generated logical key, avoiding a destructive archive rebuild.
The shared Bun store must not retain raw pools across SQLite archive reopens or DuckDB mirror replacements, and PostgreSQL's coarse remote-mode flag cannot describe its narrower curation capabilities. Bind Bun execution to each adapter's existing lifetime guard, authorize writes by operation family, and route Quack SELECTs through its query table function. Keep handle ownership with the native pools so the cutover does not introduce a second close lifecycle.
The unified store cannot cut queries over safely while canonical schemas omit deletion and dedup semantics, row conversion can silently erase malformed timestamps, or guarded backends disagree about cursor and recovery state. Make those contracts explicit before schema convergence depends on them.\n\nPreserve SQLite's shipped row-ID relationships as physical aliases while defining ordinal-based common keys, and keep DuckDB's only constraint difference to its unsupported cascade syntax with explicit child-first mirror deletion.
A shared Bun store can only replace the three query paths once every backend exposes the same durable row contract without risking persistent data. Converge SQLite and PostgreSQL transactionally in place, stamp source provenance before new archive writes, and rebuild DuckDB from the canonical registry at schema version 10. Keep shipped SQLite row-id aliases intact and let DuckDB omit only foreign-key DDL that conflicts with atomic mirror replacement; canonical ordinal relationships and explicit replacement ordering preserve the common behavior.
A stamped common schema must not replay stale identity inputs, accept empty session provenance, or preserve PostgreSQL constraints that reject canonical ordinal writes. Make the cutover fail closed while keeping SQLite upgrades additive and DuckDB rebuild-only.\n\nRoute local identity, snapshot, mapping, resync, and backfill behavior through the source-scoped tables so the legacy SQLite inputs become inert after their one-time migration.
The unified storage plan overstated fresh SQLite constraints, treated pricing metadata as timestamps, and split write ownership across stack layers. Those contradictions obscured which guarantees the foundation can enforce and where the final write cutover belongs.\n\nRecord one SQLite physical relationship matrix, real pricing timestamps with separate refresh metadata, workflow-owned provenance completeness, lock-bound PostgreSQL validation, and unsupported downgrade recovery. Assign trigger removal to the final stack layer after application writes are centralized.
Canonical pricing rows must carry real timestamps, workflow staging must remain valid before publication, and stamped PostgreSQL schemas must be validated under the migration lock. Keeping these invariants in the foundation prevents later stack layers from inheriting dialect-specific repair paths. Move SQLite pricing refresh state into dedicated metadata, normalize PostgreSQL pricing columns in the existing convergence transaction, and require explicit DuckDB mapping timestamps. Trigger removal remains owned by the final cutover in PR #1347 and kata vzty.
The common schema upgrade treated every underscore-prefixed model as internal metadata and accepted legacy text timestamp columns after stamping. That could delete valid private model pricing and let read-only or push startup use a schema that no longer matches the canonical timestamp contract. Limit migration to the three reserved metadata keys and reject non-native PostgreSQL timestamp columns. Unstamped writable schemas can still converge under the advisory lock, while stamped or read-only drift now fails closed.
Stamped SQLite archives reinstalled source identity triggers and reran table-wide invariants on every open, masking drift and making startup scale with archive size. Validate non-conflicting source-owned triggers and canonical indexes after the one-time convergence stamp, while leaving row invariants in the convergence transaction. Keep the shared timestamp boundary compatible with SQLite's empty sentinel and persist canonical UTC RFC3339Nano text instead of driver-native time values.
The Quack Bun resolver receives fully formatted SQL, so silently discarding direct driver arguments can execute a different query than the caller intended. Reject argument-bearing calls before forwarding while preserving Bun's zero-argument path and surfacing QueryRow failures through a recoverable row.
A compatibility stamp attests row invariants at convergence time. Rechecking foreign-key and duplicate-row invariants on every stamped open rejects legacy ownerless rows that replication intentionally adopts and turns startup into an unbounded data scan. Keep stamped PostgreSQL validation structural while retaining the complete row checks before the one-time stamp is written.
The screenshot compatibility fixture started from a current foundation database but removed only the legacy mapping journals. Leaving source-scoped journal triggers and the common-schema stamp described an impossible historical archive and made the test depend on stale schema state.
The pre-journal archive fixture can be replayed before or after the top-layer legacy-trigger removal. Treat both legacy and source-scoped trigger names as optional so the fixture removes whichever schema generation exists instead of failing during setup.
f2df2ed to
6791f37
Compare
6791f37 to
f2df2ed
Compare
roborev: Combined Review (
|
SQLite, PostgreSQL, and DuckDB previously evolved independent schema and query foundations, so parity depended on duplicated declarations and backend-specific plumbing. This first stack layer establishes one Bun model registry, guarded backend handles, and a dedicated DuckDB dialect while converging SQLite and PostgreSQL in place under their existing serialization boundaries.
Stamped schemas now validate structural, index, and trigger drift without repair; expensive row invariants remain limited to one-time convergence. Shared timestamps preserve the shipped SQLite empty sentinel and RFC3339Nano text contract, and Quack rejects direct driver arguments that would otherwise be discarded.
Direct database/sql access remains limited to lifecycle, connection-local, transport, and compatibility seams. DuckDB remains a disposable mirror, while narrowly scoped FTS and vector behavior stays adapter-specific. Stack 1 of 5.
generated by a clanker