
# Sloučení na úrovni sloupců

Sloučení na úrovni sloupců je hlavní výhodou merql oproti naivnímu porovnávání na úrovni řádků. Když obě strany upraví stejný řádek, merql porovná každý sloupec samostatně místo toho, aby celý řádek označil jako konflikt.

## Problém se slučováním na úrovni řádků

Představte si tabulku `posts`, ve které dva lidé upravují stejný řádek:

```
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" }
```

Sloučení na úrovni řádků zjistí, že obě strany změnily řádek, a označí jej jako konflikt. Změny se ale vůbec nepřekrývají. My jsme upravili `content`, oni upravili `title` a `status`.

## Jak sloučení na úrovni sloupců problém řeší

merql použije stejná pravidla třícestného sloučení na každý sloupec zvlášť:

```
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
```

Výsledek: `{ title: "New Title", content: "Body v2", status: "publish" }`. Čisté sloučení bez konfliktu.

## Jak to funguje

Když `ThreeWayMerge` zjistí, že obě strany aktualizovaly stejný řádek, deleguje na `ColumnMerge::merge()`:

```php
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)
```

Pro každý sloupec platí stejná pravidla:

| Základ | Naše | Jejich | Výsledek |
|---|---|---|---|
| A | A | A | Ponechat základ (beze změny) |
| A | A | B | Přijmout jejich |
| A | B | A | Přijmout naši verzi |
| A | B | B | Přijmout (obě strany souhlasí) |
| A | B | C | **Konflikt** (odlišné hodnoty) |

## Kdy ke konfliktům stále dochází

Ke konfliktu na úrovni sloupce dojde, když obě strany změní stejný sloupec na různé hodnoty:

```
Base:    post #42  { title: "Hello" }
Ours:    post #42  { title: "Welcome" }
Theirs:  post #42  { title: "Greetings" }
```

Obě strany změnily `title` na různé hodnoty. Jde o skutečný konflikt, který je třeba vyřešit.

```php
$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"
```

## Částečné konflikty

Jeden řádek může obsahovat čistá sloučení i konflikty. Pokud se jeden sloupec sloučí bez konfliktu, ale u jiného konflikt nastane, `MergeOperation` daného řádku obsahuje sloučené hodnoty čistých sloupců a hodnotu naší strany (ve výchozím nastavení) u konfliktních sloupců. Konflikt se nahlásí samostatně.

```
Base:    { title: "Hello", content: "Body",    status: "draft" }
Ours:    { title: "Welcome", content: "Body v2", status: "draft" }
Theirs:  { title: "Greetings", content: "Body",    status: "publish" }
```

Zde je u `title` konflikt (obě strany jej změnily odlišně), `content` se sloučí čistě (změnila jej pouze naše strana) a `status` se sloučí čistě (změnila jej pouze jejich strana). Operace obsahuje:

```php
// values: { title: "Welcome", content: "Body v2", status: "publish" }
// One conflict on the "title" column.
```

## Porovnávání hodnot

Hodnoty sloupců se po převodu porovnávají jako řetězce. `NULL` je samostatná hodnota:

- `NULL` na `NULL` = beze změny.
- `NULL` na `"hello"` = aktualizace.
- `"hello"` na `NULL` = aktualizace.
- `"1"` na `1` = beze změny (obojí se převede na řetězec `"1"`).

To odpovídá sémantice načítání hodnot adaptéru připojení MerQL: všechny databázové hodnoty se vracejí jako řetězce (nebo NULL).

## Sloučení na úrovni buněk

Pokud sloučení na úrovni sloupců přesto skončí konfliktem (obě strany změnily stejný sloupec na různé hodnoty), merql může jít o úroveň hlouběji pomocí [sloučení na úrovni buněk](https://merql.inline0.dev/cs/docs/merge/cell-level). To se používá u sloupců TEXT (rozdíl po řádcích) a sloupců JSON (rozdíl po klíčích) a řeší případy, kdy obě strany upravily různé části hodnoty stejného sloupce.
