feat(connection): reject adbc.connection.readonly changes while a manual transaction is active - #280
feat(connection): reject adbc.connection.readonly changes while a manual transaction is active#280fornwall wants to merge 2 commits into
Conversation
…is active Changing adbc.connection.readonly while a manual transaction is active — its kind fixed by a first statement — now fails with InvalidState, in both directions (the JDBC setReadOnly rule; the ADBC spec is silent on toggle timing, and no other ADBC driver implements the option at all). Re-setting the current value is a no-op and stays allowed, and a fresh manual transaction with no statement yet leaves the toggle free. This closes the hole where DML buffered while the connection was writable could still be committed after the connection was flipped read-only: the flip itself is now rejected, so a read-only connection can never have buffered writes to commit. The check-and-store happens under the SharedTxn lock, so a concurrent statement cannot fix the transaction's kind between the check and the store. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATAMk97A9CZw2MX1Xkp9j1
|
CI failure diagnosed and fixed by merging current What broke: every functional check failed with What changed: a plain merge of Local verification (post-merge): 🤖 Generated with Claude Code |
Follow-up to #275.
What
Changing
adbc.connection.readonlywhile a manual transaction is active — a first statement has fixed the transaction's kind (a query opened the shared read-only snapshot, or DML/ingest buffered work) — now fails withInvalidState, in both directions. Two deliberate softenings:readonlycan still be configured afterautocommit=false, before the transaction does anything — soreadonly=true+autocommit=falseremains the way to get declared-read-only transactions.Why
adbc.connection.readonlyis specified in one sentence with no toggle/transaction semantics, and post-initSetOptionsupport is explicitly driver-defined. No other ADBC driver implements the option at all (surveyed apache/arrow-adbc C/Go/Rust and the whole adbc-drivers org: the string appears only in vendoredadbc.hheaders), so there is no convention to follow. The nearest prior art is JDBC'sConnection.setReadOnly, which "cannot be called during a transaction" — this PR adopts that rule.commit()never checked it, so DML buffered while writable would still be committed after the connection was flipped read-only. Rejecting the flip closes that hole by construction: a read-only connection can never be holding buffered writes.How
TxnState::in_active_manual_txn()(manual mode ∧ kind fixed) gates theset_optionarm; the check-and-store happens under theSharedTxnlock, so a concurrent statement cannot fix the transaction's kind between the check and the store.Tests
active_manual_txn_tracks_the_fixed_kind— false in autocommit and in a fresh manual transaction, true once DML buffers, false again after a drained commit.readonly_toggle_is_rejected_in_an_active_manual_transaction— free toggle before any statement; rejection with DML buffered (→true) and inside a query transaction (→false); no-op re-sets allowed; rollback/commit free the toggle again.Docs updated: README,
python/README.md,docs/options.md, lib.rs crate docs,read_onlyfield rustdoc, CLAUDE.md.🤖 Generated with Claude Code
https://claude.ai/code/session_01ATAMk97A9CZw2MX1Xkp9j1