dolthub / dolthub/doltlite

cherry-pick, revert and rebase discard the schema-merge actions: phantom unresolvable conflict on every DDL x DDL pair

Closed
#2,903 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C
Stars
268
Forks
18
Avg merge
2h 27m
Merged PRs (30d)
447

Description

Found in a full-repo review at `0ba280f06f`.

`dolt_cherry_pick`, `dolt_revert` and `dolt_rebase` report a conflict that has no rows, on any commit that changes columns when the target branch also changed columns. `dolt_merge` of the same two commits succeeds, and so does Dolt.

Setup: `base` = `t(id,a)`; branch `f` = `ADD COLUMN c INT DEFAULT 5` + `UPDATE t SET c=42 WHERE id=1`; `main` = `ADD COLUMN d INT DEFAULT 1`.

```
-- DoltLite, dolt_merge('f'): succeeds
cols: id,a,d,c
rows: 1,1,1,42 / 2,2,1,5

-- DoltLite, dolt_cherry_pick(fc) -- the same two commits:
Error: cannot merge: conflicts detected, autocommit transaction rolled back.
```

Inside a transaction the conflict turns out to be empty, and there is no way forward:

```
BEGIN;
SELECT dolt_cherry_pick(fc);
-- Error: Cherry-pick has 1 conflict(s). Resolve and then commit with dolt_commit.
SELECT "table"||'/'||num_conflicts FROM dolt_conflicts; -- t/0
SELECT count(*) FROM dolt_conflicts_t; -- 0
SELECT count(*) FROM dolt_schema_conflicts; -- 0
SELECT dolt_commit('-A','-m','try');
-- Error: cannot commit: unresolved schema conflicts. Abort the merge, align the
-- schemas on one side, then rerun the merge.
```

There is nothing to resolve and nothing to align, so `ROLLBACK` is the only exit.

Dolt 2.3.1 cherry-picks the same commit cleanly and produces exactly what DoltLite's own merge produces:

```
CALL dolt_cherry_pick('ev04raqufmi2j3u6hhe5t8hhddbrcd72');
hash,data_conflicts,schema_conflicts,constraint_violations
7m7cs109hfflbhu7gsemtmoenk2q3sq4,0,0,0
id,a,d,c
1,1,1,42
2,2,1,5
```

## Scope

Cherry-pick, source action × target action. Every DDL × DDL pair fails; every DDL × row-change pair is fine:

```
cp\target ADDCOL DROPCOL RENCOL ROWONLY
ADDCOL CONFLICT CONFLICT CONFLICT ok
DROPCOL CONFLICT CONFLICT CONFLICT ok
RENCOL CONFLICT CONFLICT CONFLICT ok
```

A second manifestation of the same cause: `dolt_revert` of a `CREATE INDEX` commit, where a later commit renamed the indexed column, reports `nothing to commit` and leaves the index in place. Dolt reverts it.

A third: cherry-picking a `CREATE INDEX` onto a branch that renamed the indexed column hits the decreed index refusal (`index 'ia' covers column 'a' ... which the other branch renamed`), while `dolt_merge` of the same two commits succeeds and keeps the index.

## Cause

`src/doltlite_cherry_pick.c:191` and `src/doltlite_rebase.c:1003` call `doltliteMergeCatalogs` with NULL schema-action out-params (`dolt_revert` reaches the same helper via `src/doltlite_revert.c:244`):

```c
rc = doltliteMergeCatalogs(db, ancCatHash, ourCatHash, theirCatHash,
&mergedCatHash, pnConflicts, &zMergeErr, 0, 0,
bPreferOurMaster, 0,
&azReindex, &nReindex,
&azRebuild, &nRebuild);
```

`tryResolveSchemaDivergence` (`src/doltlite_merge.c:803` and `:812`) computes the add/drop/rename-column actions and then discards them when those out-params are NULL, while still treating the divergence as resolved and setting `*pSkipRowMerge`:

```c
if( nAddCols==0 && resolvedDivergence
&& ppSchemaActions && pnSchemaActions ){
rc = recordSchemaAddColumns(...);
```

Only the branch-merge path applies them, at `doltliteApplyMergeSchemaActions` (`src/doltlite_merge_cmd.c:754`, called from `mergeRefInstallMergedCatalog` at `:926`). So the rows are relaid out for a merged schema the live catalog never adopts, and the leftover divergence surfaces as a schema conflict with no rows behind it.

## Fix

Thread the real action out-params through the cherry-pick / revert / rebase paths and apply them with the merge path's own applier. `doltliteApplyMergeSchemaActions` is `static` with a self-contained signature — `(db, ancCatHash, theirCatHash, actions, nActions, *mergedCatHash, **err)` — so this is plumbing rather than a redesign: un-`static` it (or move it into the shared merge internals) and call it from the one place that currently has no equivalent.

## Test gap

`test/oracle_schema_change_cherry_pick_revert_test.sh` has 12 scenarios and in **every one the target branch performs no DDL**. The whole broken quadrant is outside the suite named for this feature. The fix wants the 3×4 matrix above, not another example.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the cherry-pick, rebase, and revert call sites in src/doltlite_cherry_pick.c, src/doltlite_rebase.c, and src/doltlite_revert.c, then trace doltliteMergeCatalogs and doltliteApplyMergeSchemaActions in src/doltlite_merge.c and src/doltlite_merge_cmd.c. Extend test/oracle_schema_change_cherry_pick_revert_test.sh with the 3×4 DDL matrix and verify cherry-pick, revert, and rebase produce no phantom conflicts and match merge results.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, sqlite
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
64/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.