← Back to Problems

Branchy SQL — Versioned SQLite

Branchy SQL — Versioned SQLite

Build a tiny "Git for relational data" on top of SQLite. Your program manages a single SQLite file with named branches — isolated views of the same tables. Writes on one branch do not affect any other branch until they are merged in.

You implement three interesting operations: branch, diff, and merge (three-way, with conflict detection). Everything else — PUT, DEL, GET, DUMP — is plumbing the evaluator uses to set up and inspect state.

Protocol

Your program is a long-running REPL. It reads commands one per line on stdin and writes responses on stdout, until EOF or QUIT. Flush after every response — the evaluator reads line-by-line and will hang if you buffer.

Commands

Command Response
INIT <db-path> OK — open/create the SQLite file. A branch named main exists from the start.
SCHEMA <table> <col1:type>,<col2:type>,... OK — declare a table. First column is the primary key. Types: INTEGER or TEXT.
PUT <branch> <table> <pk> <col=val>,<col=val>,... OK — insert-or-update a row on <branch>. <pk> is the integer primary key. Non-PK columns listed in any order; omitted columns default to NULL.
DEL <branch> <table> <pk> OK — delete the row with that pk from <branch> (no-op if missing).
GET <branch> <table> <pk> col=val|col=val|... (excluding pk, columns in SCHEMA order) or NULL if the row does not exist on that branch.
BRANCH <new> <from> OK — fork <new> from <from>'s current state.
DIFF <a> <b> One line of JSON, then END. See Diff format below.
MERGE <src> <dst> OK on success, or CONFLICT <table>:<pk> for the first conflicting row (sorted by (table, pk)) — in which case <dst> is left unchanged.
DUMP <branch> All rows on <branch>, one per line as <table> <pk> <col=val>|<col=val>|..., sorted by (table, pk), terminated by END.
QUIT Exit cleanly. No response.

Diff format

DIFF a b emits the changes you would apply to take branch a's state to branch b's state. One line of JSON followed by END:

{"added":[{"table":"users","pk":2,"row":{"name":"Bob","age":30}}],"removed":[{"table":"users","pk":4,"row":{"name":"Dan","age":40}}],"modified":[{"table":"users","pk":3,"before":{"age":29},"after":{"age":30}}]}
  • added — rows present in b but not a. row contains every non-pk column.
  • removed — rows present in a but not b. row contains every non-pk column (taken from a).
  • modified — rows with the same pk in both branches but at least one differing column. before and after contain only the columns that changed.
  • Entries sorted by (table, pk). Use the exact key order: added, removed, modified. No whitespace inside the JSON.

Merge semantics

MERGE src dst performs a three-way merge:

  1. Find the fork point — the state of dst at the moment src was branched off dst's lineage. Branches form a tree rooted at main; the eval guarantees src and dst share a lineage (one is an ancestor of the other, or they share an ancestor on this lineage chain).
  2. Compute src_Δ = changes from fork point to src, and dst_Δ = changes from fork point to dst.
  3. A conflict is a (table, pk) whose final state in src differs from its final state in dst and both sides changed it.
  4. On conflict: reply CONFLICT <table>:<pk> for the first conflict (sorted by (table, pk)) and do not modify dst.
  5. Otherwise: apply src_Δ to dst and reply OK.

Constraints

  • All tables use INTEGER PRIMARY KEY as the first column. No composite keys, no foreign keys, no indexes you need to manage.
  • Column types are INTEGER and TEXT only.
  • Values in commands are alphanumeric plus underscore — no spaces, no quoting, no NULLs in input (omitted columns are NULL).
  • Branches form a tree rooted at main. The evaluator only attempts merges between branches in the same lineage.
  • The DB file path is absolute. You may create auxiliary tables in it as you wish — the evaluator never reads the file directly.

Example

INIT /tmp/branchy.db
OK
SCHEMA users id:INTEGER,name:TEXT,age:INTEGER
OK
PUT main users 1 name=Alice,age=30
OK
PUT main users 2 name=Bob,age=25
OK
BRANCH feature main
OK
PUT feature users 3 name=Carol,age=28
OK
PUT feature users 1 age=31
OK
DIFF main feature
{"added":[{"table":"users","pk":3,"row":{"name":"Carol","age":28}}],"removed":[],"modified":[{"table":"users","pk":1,"before":{"age":30},"after":{"age":31}}]}
END
MERGE feature main
OK
GET main users 1
name=Alice|age=31
GET main users 3
name=Carol|age=28
QUIT

Notes

  • The eval treats your program as a black box. It never inspects your code — only the commands you respond to and (optionally) the SQLite file you persist.
  • You can use any language that can speak stdin/stdout and link a SQLite library.

Scoring

Your solution is evaluated against 10 test cases (100 points total). Point values grow exponentially — later tests are worth disproportionately more than earlier ones, so passing the hardest tier is where the score lives.

Test 1 2 3 4 5 6 7 8 9 10
Points 1 2 3 5 7 9 12 16 20 25

LEADERBOARD

Rank Developer Score Submitted
1 🥇 meetpatel-D3sTr0
100 / 100
May 29, 2025, 10:27 AM UTC
2 🥈 lokesh
100 / 100
May 29, 2026, 11:32 AM UTC
3 🥉 aniket-axy
100 / 100
May 29, 2026, 11:39 AM UTC
4 gAditya2208
100 / 100
May 29, 2026, 11:49 AM UTC
5 parth469
100 / 100
May 29, 2026, 12:06 PM UTC
6 harshwadhwani-10
100 / 100
May 29, 2026, 12:52 PM UTC
7 Anujjj12
100 / 100
May 29, 2026, 12:54 PM UTC
8 HetaDobariya
100 / 100
May 29, 2026, 1:48 PM UTC