Merge a livello di colonna
Il merge a livello di colonna è il principale vantaggio di merql rispetto al confronto ingenuo a livello di riga. Quando entrambi i lati modificano la stessa riga, merql confronta ogni colonna separatamente invece di segnalare un conflitto per l'intera riga.
Il problema del merge a livello di riga
Considerate una tabella posts in cui due persone modificano la stessa riga:
Base: post #42 { title: "Hello", content: "Body", status: "draft" }
Ours: post #42 { title: "Hello", content: "Body v2", status: "draft" }
Theirs: post #42 { title: "New Title", content: "Body", status: "publish" }
Un merge a livello di riga rileva che entrambi i lati hanno modificato la riga e segnala un conflitto. Ma le modifiche non si sovrappongono. Ours ha modificato content, mentre theirs ha modificato title e status.
Come il merge a livello di colonna risolve il problema
merql applica a ogni colonna, separatamente, le stesse regole di merge a tre vie:
title: base="Hello", ours="Hello", theirs="New Title" -> accept theirs
content: base="Body", ours="Body v2", theirs="Body" -> accept ours
status: base="draft", ours="draft", theirs="publish" -> accept theirs
Risultato: { title: "New Title", content: "Body v2", status: "publish" }. Merge senza conflitti.
Come funziona
Quando ThreeWayMerge rileva che entrambi i lati hanno aggiornato la stessa riga, delega a ColumnMerge::merge():
use Merql\Merge\ColumnMerge;
$result = ColumnMerge::merge(
table: 'posts',
rowKey: '42',
base: ['title' => 'Hello', 'content' => 'Body', 'status' => 'draft'],
ours: ['title' => 'Hello', 'content' => 'Body v2', 'status' => 'draft'],
theirs: ['title' => 'New Title', 'content' => 'Body', 'status' => 'publish'],
);
// $result['values'] -> merged column values
// $result['conflicts'] -> list of Conflict objects (empty if clean)
Per ogni colonna si applicano le stesse regole:
| Base | Ours | Theirs | Risultato |
|---|---|---|---|
| A | A | A | Mantieni la base (nessuna modifica) |
| A | A | B | Accetta theirs |
| A | B | A | Accetta ours |
| A | B | B | Accetta (entrambi concordano) |
| A | B | C | Conflitto (valori diversi) |
Quando si verificano ancora conflitti
Si verifica un conflitto a livello di colonna quando entrambi i lati modificano la stessa colonna assegnandole valori diversi:
Base: post #42 { title: "Hello" }
Ours: post #42 { title: "Welcome" }
Theirs: post #42 { title: "Greetings" }
Entrambi hanno modificato title assegnandole valori diversi. È un conflitto effettivo che richiede una risoluzione.
$result = ColumnMerge::merge(
'posts', '42',
['title' => 'Hello'],
['title' => 'Welcome'],
['title' => 'Greetings'],
);
// $result['conflicts'] contains one Conflict:
// table: "posts", column: "title"
// oursValue: "Welcome", theirsValue: "Greetings", baseValue: "Hello"
Conflitti parziali
Una singola riga può contenere sia merge senza conflitti sia conflitti. Se una colonna viene unita senza conflitti ma un'altra genera un conflitto, il MergeOperation della riga contiene i valori uniti delle colonne senza conflitti e, per impostazione predefinita, il valore ours delle colonne in conflitto. Il conflitto viene comunque segnalato separatamente.
Base: { title: "Hello", content: "Body", status: "draft" }
Ours: { title: "Welcome", content: "Body v2", status: "draft" }
Theirs: { title: "Greetings", content: "Body", status: "publish" }
Qui title è in conflitto (entrambi i lati lo hanno modificato diversamente), content viene unito senza conflitti (è cambiato solo ours) e status viene unito senza conflitti (è cambiato solo theirs). L'operazione contiene:
// values: { title: "Welcome", content: "Body v2", status: "publish" }
// One conflict on the "title" column.
Confronto dei valori
I valori delle colonne vengono confrontati come stringhe dopo la conversione. NULL è un valore distinto:
- Da
NULLaNULL= nessuna modifica. - Da
NULLa"hello"= aggiornamento. - Da
"hello"aNULL= aggiornamento. - Da
"1"a1= nessuna modifica (entrambi diventano la stringa"1").
Questo corrisponde alla semantica di fetch dell'adapter di connessione MerQL: tutti i valori del database vengono restituiti come stringhe (o NULL).
Merge a livello di cella
Quando l’unione a livello di colonna genera ancora un conflitto (entrambi i lati hanno modificato la stessa colonna con valori diversi), merql può scendere di un livello con unione a livello di cella. Si applica alle colonne TEXT (confronto riga per riga) e alle colonne JSON (confronto chiave per chiave), risolvendo i casi in cui entrambi i lati hanno modificato parti diverse del valore della stessa colonna.