feat(storage): route direct pool execution through rebind-aware DB wrapper - #949
Conversation
…ore core Mechanical move (rename-detected) of the store implementation and its white-box tests; mysqlstore becomes a thin public constructor over the shared core so a second dialect backend can assemble the same store logic with its own dependencies. No SQL or behavior changes.
…apper Stores hold a rebindDB instead of a raw *sql.DB, so every statement executed directly on the pool passes through the dialect's placeholder binder exactly once (identity for MySQL). This is the execution seam a Postgres backend needs to rewrite "?" placeholders to "$n" without touching store SQL. Transaction and pinned-connection handles remain raw passthroughs for a follow-up.
|
🤖 Adversarial correctness review, requested by Armand and performed by his agent. Reviewed at head Verdict: clean — no fix-before-merge findings. The seam is in the right place, the boundary is actually sealed (I went looking for bypasses and found none), and the identity transform provably changes nothing for MySQL. Verified solid:
One forward-looking note for the follow-up (nothing to change here): the This review was generated by Claude Code (claude-fable-5). |
aparajon
left a comment
There was a problem hiding this comment.
🤖 Approving per the adversarial review above — clean, no blocking findings. Stamped by Armand's agent (claude-fable-5).
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…re-rebind-wrapper
…nce (#950) * refactor(storage): depend on storage.Storage instead of *mysqlstore.Storage Callers only use the interface method set; widening the server field, buildGRPCTernClient, and test-helper signatures removes the concrete-type coupling so a second storage backend can be wired without touching callers. * refactor(storage): address review suggestions on storage interface decoupling Rename locals that shadowed the storage package import and add a compile-time storage.Storage conformance assertion in mysqlstore. * refactor(storage): move mysqlstore internals to shared internal/sqlstore core Mechanical move (rename-detected) of the store implementation and its white-box tests; mysqlstore becomes a thin public constructor over the shared core so a second dialect backend can assemble the same store logic with its own dependencies. No SQL or behavior changes. * feat(storage): route direct pool execution through rebind-aware DB wrapper Stores hold a rebindDB instead of a raw *sql.DB, so every statement executed directly on the pool passes through the dialect's placeholder binder exactly once (identity for MySQL). This is the execution seam a Postgres backend needs to rewrite "?" placeholders to "$n" without touching store SQL. Transaction and pinned-connection handles remain raw passthroughs for a follow-up. * feat(storage): rebind transaction and pinned-connection SQL exactly once Extend the rebind boundary from direct pool execution to transactions and pinned connections: BeginTx/Conn now return rebind-aware wrappers, so every store statement rebinds its placeholders exactly once at execution time. The advisory-lock flow keeps a sanctioned raw() escape because namedlock.Locker emits engine-native SQL on *sql.Conn. * refactor(storage): move mysqlstore internals to shared internal/sqlstore core (#948) Mechanical move (rename-detected) of the store implementation and its white-box tests; mysqlstore becomes a thin public constructor over the shared core so a second dialect backend can assemble the same store logic with its own dependencies. No SQL or behavior changes. * feat(storage): route direct pool execution through rebind-aware DB wrapper (#949) * refactor(storage): move mysqlstore internals to shared internal/sqlstore core Mechanical move (rename-detected) of the store implementation and its white-box tests; mysqlstore becomes a thin public constructor over the shared core so a second dialect backend can assemble the same store logic with its own dependencies. No SQL or behavior changes. * feat(storage): route direct pool execution through rebind-aware DB wrapper Stores hold a rebindDB instead of a raw *sql.DB, so every statement executed directly on the pool passes through the dialect's placeholder binder exactly once (identity for MySQL). This is the execution seam a Postgres backend needs to rewrite "?" placeholders to "$n" without touching store SQL. Transaction and pinned-connection handles remain raw passthroughs for a follow-up.
Summary
Routes all direct connection-pool execution in the shared
sqlstorecore through a rebind-aware DB wrapper. Every statement executed on the pool now passes through the dialect's placeholder binder exactly once before reaching the SQL driver — the identity transform for MySQL, and the seam a Postgres backend needs to rewrite?placeholders to$nwithout touching store SQL.What
rebindDBwrapper around*sql.DB:ExecContext/QueryContext/QueryRowContextrebind the query via abinderinterface;BeginTx/Conn/PingContext/Closeare raw passthroughs.MySQLDialectgains an identityRebind.Storage) hold*rebindDBinstead of*sql.DB, so no store can bypass the rebind boundary.New(db *sql.DB)is unchanged; no SQL or behavior edits.driver.Connectorstub, proving each direct execution method rebinds exactly once and forwards the rebound SQL and args, and that Tx/pinned-conn handles bypass the binder.Why
The store core emits MySQL-style
?placeholders throughout. Postgres requires$nplaceholders at the wire, and rewriting ~120 hand-written statements would be invasive and error-prone. Centralizing the rewrite at the execution boundary keeps store SQL dialect-neutral: a Postgres constructor only needs to supply a?-to-$nbinder. Transaction and pinned-connection statement rebinding is intentionally deferred to a follow-up to keep this change narrow.Before / after