dolt_diff_summary calls a drop+create a rename, and dolt_diff splits a rename into a drop plus an add. Both are the same root cause: the catalog has no table identity that survives a rename, so neither surface can tell the two apart.
The dolt_diff_summary half is the one that matters — it is merged and returns a wrong answer today, not a cosmetic difference.
Pre-existing on master (069a59e7d6). Found while looking at the dolt_diff rename pairing. Related: #1999, which is the same trap on dolt_diff_stat.
Repro 1 — dolt_diff_summary reports a rename that never happened
CREATE TABLE t(id INTEGER PRIMARY KEY, v TEXT);
INSERT INTO t VALUES(1,'a');
SELECT dolt_commit('-A','-m','c1');
DROP TABLE t;
CREATE TABLE u(id INTEGER PRIMARY KEY, w TEXT); -- different table, different columns
INSERT INTO u VALUES(5,'z');
SELECT dolt_commit('-A','-m','drop_add');
SELECT * FROM dolt_diff_summary('<c1>','<drop_add>');
|
result |
| doltlite |
t, u, renamed, 1, 1 |
| Dolt 2.2.2 |
t, "", dropped, true, true
"", u, added, true, true |
t was dropped and an unrelated u was created. doltlite claims one was renamed into the other.
Repro 2 — dolt_diff splits a rename into two rows
CREATE TABLE t(id INTEGER PRIMARY KEY, v TEXT);
INSERT INTO t VALUES(1,'a');
SELECT dolt_commit('-A','-m','c1');
ALTER TABLE t RENAME TO u;
INSERT INTO u VALUES(2,'b');
SELECT dolt_commit('-A','-m','rename_and_insert');
SELECT message, data_change, schema_change, table_name FROM dolt_diff;
|
rows for the rename commit |
| doltlite |
rename_and_insert, true, true, u
rename_and_insert, true, true, t |
| Dolt 2.2.2 |
rename_and_insert, true, true, u |
Root cause
struct TableEntry (src/doltlite_catalog_types.h) carries only iTable and zName. Neither is an identity:
zName changes on rename, by definition.
iTable is canonical by sorted table name, assigned positionally at serialization (src/prolly_btree_catalog.c:1104). Creating zebra, apple, mango yields apple=2, mango=3, zebra=4. Renaming a table changes its number, and can change other tables' numbers too.
So after a commit, "renamed t to u" and "dropped t, created u" leave identical catalog state. No amount of comparing the two catalogs can separate them. Dolt distinguishes them because it stores real table identity.
That numbering is not an accident to be fixed. It is what delivers the history-independence contract, quoting prolly_btree_catalog.c:1104:
Canonical numbering: rows sort by type rank then name, and numbering is positional, so the blob is a pure function of the logical catalog (same schema reached through any DDL order hashes identically -- the history-independence contract).
Confirmed live: CREATE t; INSERT; ALTER TABLE t RENAME TO u and CREATE u; INSERT both give dolt_hashof_table('u') = c250395be252b3f7ef4f459887290fd9f229c2ec.
Do not "fix" it by pairing on iTable
dolt_diff_summary already does this (dsRenamePartner, src/doltlite_diff_stat.c:704) and that is exactly why Repro 1 is wrong. Porting the same helper into dolt_diff fixes the rename cases and silently breaks drops. Measured, against Dolt:
| scenario |
master |
with iTable pairing |
Dolt |
| pure rename |
2 rows |
1 row ✔ |
1 row |
| rename + insert |
2 rows |
1 row ✔ |
1 row |
rename t→u + drop unrelated d |
d, t, u |
t, u ✘ lost the d drop |
d, u |
drop t + create u |
t, u |
u ✘ lost the t drop |
t, u |
Both regressions make a dropped table disappear from the diff, which is worse than the extra row being removed. A "pair only when exactly one name is unmatched on each side" guard does not help either: Repro 1 has exactly one unmatched name per side and Dolt still calls it drop+add.
What an actual fix costs
Two options, both real changes:
- Stable table identity in the catalog. Persist a per-table id assigned at creation that survives rename. Fixes all three surfaces at the source. Abandons the history-independence contract by construction (the catalog would have to remember the table was once
t), changes every catalog and table hash, and needs a review of the merge/diff code that assumes canonical numbering.
- Record the rename in the commit rather than the catalog. Commits are already history, so a rename mapping there costs nothing in history-independence and does not rehash existing databases. Requires capturing renames in the session/working set as the DDL runs, plus an additive commit-format change.
Neither is worth a format change purely for rename attribution in diff output, which is why this is filed rather than fixed.
Also found while investigating — unrelated, want their own issues if picked up
Both verified against a pre-change baseline binary, so neither is caused by the work above.
- Table swap.
ALTER TABLE t RENAME TO tmp; ALTER TABLE u RENAME TO t; ALTER TABLE tmp RENAME TO u; — dolt_diff gives schema_change=false for both tables, Dolt gives true.
- Empty table creation. Creating a table and committing without inserting rows gives
data_change=true in dolt_diff, Dolt gives false.
dolt_diff_summarycalls a drop+create a rename, anddolt_diffsplits a rename into a drop plus an add. Both are the same root cause: the catalog has no table identity that survives a rename, so neither surface can tell the two apart.The
dolt_diff_summaryhalf is the one that matters — it is merged and returns a wrong answer today, not a cosmetic difference.Pre-existing on master (
069a59e7d6). Found while looking at thedolt_diffrename pairing. Related: #1999, which is the same trap ondolt_diff_stat.Repro 1 —
dolt_diff_summaryreports a rename that never happenedt, u, renamed, 1, 1t, "", dropped, true, true"", u, added, true, truetwas dropped and an unrelateduwas created. doltlite claims one was renamed into the other.Repro 2 —
dolt_diffsplits a rename into two rowsrename_and_insert, true, true, urename_and_insert, true, true, trename_and_insert, true, true, uRoot cause
struct TableEntry(src/doltlite_catalog_types.h) carries onlyiTableandzName. Neither is an identity:zNamechanges on rename, by definition.iTableis canonical by sorted table name, assigned positionally at serialization (src/prolly_btree_catalog.c:1104). Creatingzebra, apple, mangoyieldsapple=2, mango=3, zebra=4. Renaming a table changes its number, and can change other tables' numbers too.So after a commit, "renamed
ttou" and "droppedt, createdu" leave identical catalog state. No amount of comparing the two catalogs can separate them. Dolt distinguishes them because it stores real table identity.That numbering is not an accident to be fixed. It is what delivers the history-independence contract, quoting
prolly_btree_catalog.c:1104:Confirmed live:
CREATE t; INSERT; ALTER TABLE t RENAME TO uandCREATE u; INSERTboth givedolt_hashof_table('u')=c250395be252b3f7ef4f459887290fd9f229c2ec.Do not "fix" it by pairing on
iTabledolt_diff_summaryalready does this (dsRenamePartner,src/doltlite_diff_stat.c:704) and that is exactly why Repro 1 is wrong. Porting the same helper intodolt_difffixes the rename cases and silently breaks drops. Measured, against Dolt:iTablepairingt→u+ drop unrelateddd,t,ut,u✘ lost theddropd,ut+ createut,uu✘ lost thetdropt,uBoth regressions make a dropped table disappear from the diff, which is worse than the extra row being removed. A "pair only when exactly one name is unmatched on each side" guard does not help either: Repro 1 has exactly one unmatched name per side and Dolt still calls it drop+add.
What an actual fix costs
Two options, both real changes:
t), changes every catalog and table hash, and needs a review of the merge/diff code that assumes canonical numbering.Neither is worth a format change purely for rename attribution in diff output, which is why this is filed rather than fixed.
Also found while investigating — unrelated, want their own issues if picked up
Both verified against a pre-change baseline binary, so neither is caused by the work above.
ALTER TABLE t RENAME TO tmp; ALTER TABLE u RENAME TO t; ALTER TABLE tmp RENAME TO u;—dolt_diffgivesschema_change=falsefor both tables, Dolt givestrue.data_change=trueindolt_diff, Dolt givesfalse.