
# Penggabungan Tingkat Kolom

Penggabungan tingkat kolom adalah keunggulan utama merql dibandingkan perbandingan naif pada tingkat baris. Ketika kedua sisi mengubah baris yang sama, merql membandingkan setiap kolom secara terpisah, alih-alih menandai seluruh baris sebagai konflik.

## Masalah penggabungan tingkat baris

Bayangkan sebuah tabel `posts` tempat dua orang mengedit baris yang sama:

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

Penggabungan tingkat baris melihat kedua sisi mengubah baris itu dan menyatakan konflik. Namun, perubahan tersebut sama sekali tidak bertumpang tindih. Ours mengedit `content`, sedangkan theirs mengedit `title` dan `status`.

## Cara penggabungan tingkat kolom menyelesaikannya

merql menerapkan aturan penggabungan tiga arah yang sama pada setiap kolom secara terpisah:

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

Hasil: `{ title: "New Title", content: "Body v2", status: "publish" }`. Penggabungan bersih, tanpa konflik.

## Cara kerjanya

Saat `ThreeWayMerge` mendeteksi bahwa kedua sisi memperbarui baris yang sama, `ColumnMerge::merge()` akan mengambil alih:

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

Aturan yang sama berlaku untuk setiap kolom:

| Base | Ours | Theirs | Hasil |
|---|---|---|---|
| A | A | A | Pertahankan base (tidak berubah) |
| A | A | B | Terima theirs |
| A | B | A | Terima ours |
| A | B | B | Terima (keduanya sepakat) |
| A | B | C | **Konflik** (nilai berbeda) |

## Saat konflik tetap terjadi

Konflik tingkat kolom terjadi ketika kedua sisi mengubah kolom yang sama menjadi nilai yang berbeda:

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

Keduanya mengubah `title` menjadi nilai yang berbeda. Ini merupakan konflik nyata yang memerlukan penyelesaian.

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

## Konflik sebagian

Satu baris dapat berisi penggabungan yang bersih sekaligus konflik. Jika satu kolom bergabung dengan bersih tetapi kolom lain berkonflik, `MergeOperation` untuk baris tersebut berisi nilai gabungan bagi kolom yang bersih dan nilai ours (secara default) bagi kolom yang berkonflik. Konflik tetap dilaporkan secara terpisah.

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

Di sini `title` berkonflik (keduanya mengubahnya secara berbeda), `content` bergabung dengan bersih (hanya ours yang mengubahnya), dan `status` bergabung dengan bersih (hanya theirs yang mengubahnya). Operasi tersebut memuat:

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

## Perbandingan nilai

Nilai kolom dibandingkan sebagai string setelah dikonversi. `NULL` adalah nilai yang berbeda:

- `NULL` ke `NULL` = tidak berubah.
- `NULL` ke `"hello"` = diperbarui.
- `"hello"` ke `NULL` = diperbarui.
- `"1"` ke `1` = tidak berubah (keduanya dikonversi menjadi string `"1"`).

Ini sesuai dengan semantik pengambilan adaptor koneksi MerQL: semua nilai database dikembalikan sebagai string (atau NULL).

## Penggabungan tingkat sel

Ketika penggabungan tingkat kolom masih menghasilkan konflik (kedua sisi mengubah kolom yang sama menjadi nilai berbeda), merql dapat bekerja lebih mendalam dengan [penggabungan tingkat sel](https://merql.inline0.dev/id/docs/merge/cell-level). Fitur ini berlaku untuk kolom TEXT (diff baris demi baris) dan kolom JSON (diff kunci demi kunci), sehingga kasus ketika kedua sisi mengedit bagian berbeda dari nilai kolom yang sama dapat diselesaikan.
