Skip to content

Commit a7dfb82

Browse files
README: transactions, limitations, and layering sections
ISSUE-30 — document the transaction model: implicit-transaction, no autocommit, commit/rollback-before-use is a silent no-op, commit errors now propagate (reversing the previously-silent __exit__ behavior). ISSUE-29 — add a Layering section explaining the relationship between dqliteclient (wire), dqlitedbapi (PEP 249 sync/async), and sqlalchemy-dqlite (dialect). Also document the dqlite-specific limitations vs stdlib sqlite3: multi-statement rejection, no autocommit, SERIALIZABLE-only isolation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ac952d4 commit a7dfb82

1 file changed

Lines changed: 42 additions & 0 deletions

File tree

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,48 @@ asyncio.run(main())
4242
- `threadsafety = 1`
4343
- `paramstyle = "qmark"`
4444

45+
## Transactions
46+
47+
`dqlite-dbapi` follows PEP 249's implicit-transaction model.
48+
49+
- The first DML statement on a connection starts a transaction; call
50+
`commit()` to flush it or `rollback()` to discard it.
51+
- There is no autocommit mode. Every write goes through Raft consensus
52+
and every read is serializable.
53+
- Calling `commit()` / `rollback()` before any query has run is a silent
54+
no-op (preserves the "no spurious connect" contract — we don't open
55+
a TCP connection just to send COMMIT).
56+
- Calling `commit()` / `rollback()` with no active transaction
57+
(e.g. right after a DDL statement) is also silently successful, matching
58+
stdlib `sqlite3` semantics.
59+
- The context-manager exit (`with conn: ...`) commits on clean exit and
60+
attempts rollback on exception. **Commit failures now propagate** — an
61+
earlier version silently swallowed them, which could hide data loss.
62+
63+
## Limitations vs. stdlib `sqlite3`
64+
65+
- **Multi-statement SQL is rejected.** `cursor.execute("SELECT 1;
66+
SELECT 2;")` raises `OperationalError` with "nonempty statement tail".
67+
Split into separate `execute()` calls.
68+
- **No autocommit mode** — see above.
69+
- **SERIALIZABLE isolation only.** Every statement is ordered by Raft;
70+
weaker isolation levels aren't exposed.
71+
72+
## Layering
73+
74+
Three related packages play different roles:
75+
76+
- `dqliteclient.DqliteConnection` — the low-level async wire client.
77+
Directly speaks the dqlite wire protocol.
78+
- `dqlitedbapi.Connection` — a sync PEP 249 wrapper built on top, runs a
79+
dedicated event-loop thread so sync code can use the async client
80+
transparently.
81+
- `dqlitedbapi.aio.AsyncConnection` — the PEP 249–shaped async
82+
counterpart for code already running inside an event loop.
83+
84+
Use `dqlitedbapi` (sync or async) unless you specifically need
85+
wire-level control.
86+
4587
## Development
4688

4789
See [DEVELOPMENT.md](DEVELOPMENT.md) for setup and contribution guidelines.

0 commit comments

Comments
 (0)