Skip to content

feat(repl): Phase 11.11a REPL .spawn for interactive BEGIN CONCURRENT demos (SQLR-22)#131

Merged
joaoh82 merged 1 commit into
mainfrom
worktree-phase-11-11a-repl-spawn
May 11, 2026
Merged

feat(repl): Phase 11.11a REPL .spawn for interactive BEGIN CONCURRENT demos (SQLR-22)#131
joaoh82 merged 1 commit into
mainfrom
worktree-phase-11-11a-repl-spawn

Conversation

@joaoh82
Copy link
Copy Markdown
Owner

@joaoh82 joaoh82 commented May 11, 2026

Summary

  • Lifts the REPL from a single Database to Vec<Connection> so users can mint sibling handles in-session and step through cross-handle MVCC scenarios.
  • New meta-commands .spawn / .use NAME / .conns drive the multi-handle UX; the prompt always shows the active handle (sqlrite[A]> , sqlrite[B]> ).
  • New Connection::execute_with_render lets the REPL keep the prettytable output for SELECT while routing every line through Connection so BEGIN CONCURRENT / COMMIT / ROLLBACK hit the per-connection MVCC state.

Worked demo

sqlrite[A]> PRAGMA journal_mode = mvcc;
sqlrite[A]> CREATE TABLE t (id INTEGER PRIMARY KEY, v INTEGER);
sqlrite[A]> INSERT INTO t (id, v) VALUES (1, 0);
sqlrite[A]> .spawn          -- switches to B
sqlrite[B]> .use A
sqlrite[A]> BEGIN CONCURRENT;
sqlrite[A]> UPDATE t SET v = 100 WHERE id = 1;
sqlrite[A]> .conns
2 handle(s):
  * A (BEGIN CONCURRENT)
    B
sqlrite[A]> .use B
sqlrite[B]> BEGIN CONCURRENT;
sqlrite[B]> UPDATE t SET v = 200 WHERE id = 1;
sqlrite[B]> COMMIT;
sqlrite[B]> .use A
sqlrite[A]> COMMIT;
An error occured: Busy: write-write conflict on t/1: another transaction
committed this row at ts=3 (after our begin_ts=1); transaction rolled
back, retry with a fresh BEGIN CONCURRENT
sqlrite[A]> .use B
sqlrite[B]> SELECT * FROM t;
+----+-----+
| id | v   |
+----+-----+
| 1  | 200 |
+----+-----+

Changes

  • src/repl/mod.rs: new ReplState struct holds Vec<Connection>, parallel Vec<String> of stable letter names, and the active index. Methods: spawn_sibling, use_handle, handles_summary, lock_active, active_conn_mut, collapse_to_active. A next_handle_name(usize) -> String helper produces the A, …, Z, AA, AB sequence.
  • src/meta_command/mod.rs: new MetaCommand::Spawn / Use(String) / Conns variants + parser + handlers. .open now calls state.collapse_to_active() before swapping the underlying Database so siblings can't be left pointing at the stale one.
  • src/main.rs: REPL loop migrated from &mut Database to &mut ReplState. SQL dispatch routes through Connection::execute_with_render. Prompt shows the active handle name.
  • src/connection.rs: new execute_with_render, execute_dispatch_with_render, execute_in_concurrent_tx_with_render — render-aware twins of the existing dispatch tree. concurrent_tx_is_open() promoted to pub so .conns can render the per-handle tx flag.

Why this is split from "Phase 11.11"

The original plan-doc 11.11 bundled the REPL .spawn work with a heavier "N concurrent writers" benchmark workload (extends the benchmarks/ harness, links SQLite + DuckDB drivers, criterion-driven). Splitting keeps each PR reviewable. The bench workload becomes Phase 11.11b in the roadmap.

Test plan

  • cargo build --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs --exclude sqlrite-benchmarks --all-targets
  • cargo test --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs --exclude sqlrite-benchmarks — 615/615 (was 607, +8 new in meta_command::tests)
  • cargo fmt --all -- --check
  • cargo clippy --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs --exclude sqlrite-benchmarks --all-targets
  • cargo doc --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs --exclude sqlrite-benchmarks --no-deps
  • Smoke-tested the BEGIN CONCURRENT cross-handle demo end-to-end
  • CI green

Docs updated

  • docs/roadmap.md — Phase 11.11a promoted to shipped, 11.11b carved out for the bench workload.
  • docs/usage.md — new "Multi-handle mode" section with the worked demo above.
  • docs/smoke-test.md — prompt example refreshed (sqlrite[A]>), command count bumped 5 → 9.
  • docs/design-decisions.md — new §12h on the migration rationale (Vec<Connection> over HashMap, .open collapse semantics, execute_with_render design).

🤖 Generated with Claude Code

… demos (SQLR-22)

Lifts the REPL from a single Database to Vec<Connection> so users can mint
sibling handles in-session and step through cross-handle MVCC scenarios.
The prompt now shows the active handle ('sqlrite[A]> ' / 'sqlrite[B]> ')
so it's obvious which connection will execute the next line.

New meta-commands:
- .spawn      Mint a sibling Connection sharing the same Arc<Mutex<Database>>
              via Connection::connect(), then switch to it. Each handle gets
              a stable letter name (A, B, ..., Z, then AA, AB).
- .use NAME   Switch the active handle (case-insensitive); errors with the
              list of valid names on miss.
- .conns      List every handle, mark active with *, tag handles holding
              an open BEGIN CONCURRENT with (BEGIN CONCURRENT).

Plumbing:
- New ReplState in src/repl/mod.rs owns Vec<Connection>, parallel
  Vec<String> of names, and the active index. Exposes lock_active() for
  meta-commands that mutate the underlying Database directly, and
  active_conn_mut() for SQL dispatch through Connection.
- src/main.rs migrates the REPL loop from &mut Database to
  &mut ReplState. SQL dispatch routes through a new
  Connection::execute_with_render which returns CommandOutput (status +
  optional rendered prettytable) instead of a bare String — so BEGIN
  CONCURRENT / COMMIT / ROLLBACK still hit the per-connection MVCC
  state, while SELECTs come back with the prettytable for the REPL to
  print above the status line.
- Connection::concurrent_tx_is_open() promoted from private to public
  so .conns can render per-handle tx state.
- .open collapses every sibling back to a single handle named A —
  siblings pointing at the previous Database would otherwise be
  stranded with stale MVCC state.

Tests:
- 8 new cases in src/meta_command/mod.rs cover .spawn / .use / .conns
  parse + dispatch behaviour, case-insensitive .use, the error message
  on unknown names, multi-handle shared-database visibility,
  .open-collapse, and the A → Z → AA naming wrap.

Docs:
- roadmap.md: Phase 11.11a promoted to shipped; the heavier
  benchmark workload split out as 11.11b.
- usage.md: new "Multi-handle mode" section with a worked
  BEGIN-CONCURRENT-vs-BEGIN-CONCURRENT demo.
- smoke-test.md: prompt example refreshed (sqlrite[A]>), command
  count bumped from 5 → 9.
- design-decisions.md: new §12h covering the migration rationale
  (why Vec<Connection> over HashMap, why .open collapses siblings,
  why execute_with_render instead of pre-parsing).

Workspace: 615/615 Rust tests pass (was 607, +8 new). fmt + clippy
+ doc all clean. Smoke-tested the BEGIN CONCURRENT demo end-to-end
across A/B handles — A's commit hits Busy as expected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
rust-sqlite Ready Ready Preview, Comment May 11, 2026 9:28am

Request Review

@joaoh82 joaoh82 merged commit 4495a68 into main May 11, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant