Merql
Advanced

Schema Validation

When schemas evolve between snapshots (columns added, removed, or renamed), merql detects and reports these differences. Schema mismatches do not block the merge, but they are surfaced so the caller can decide how to proceed.

Detection

SchemaValidator::validate() compares the column sets of each table across base, ours, and theirs snapshots. It reports:

  • Columns added in ours (present in ours but not in base)
  • Columns removed in ours (present in base but not in ours)
  • Columns added in theirs
  • Columns removed in theirs
use Merql\Merge\ThreeWayMerge;

$merge = new ThreeWayMerge();
$result = $merge->merge($base, $ours, $theirs);

if ($result->hasSchemaMismatches()) {
    foreach ($result->schemaMismatches() as $mismatch) {
        echo $mismatch->getMessage() . "\n";
        // "Schema mismatch on table 'posts': columns added in theirs: subtitle"
    }
}

What is detected

ChangeDetectedNotes
Column addedYesReported per side (ours/theirs)
Column removedYesReported per side
Column type changedNoTypes are stored but not compared
Column renamedNoAppears as add + remove
Primary key changedNoIdentity is fixed at snapshot time

Behavior during merge

When a column exists in one snapshot but not another, the merge treats the missing column value as null. This means:

  • A column added in theirs with a non-null value appears as null -> value (accepted as theirs change)
  • A column removed in theirs appears as having null for all rows in theirs

The merge proceeds and produces a result. The schema mismatches are informational. The caller should verify that the target database schema matches the merged data before applying.

Accessing mismatches

Schema mismatches are available on both ThreeWayMerge (via schemaMismatches()) and on MergeResult (via schemaMismatches() and hasSchemaMismatches()). They are preserved through ConflictResolver::resolve().

// On the merge result directly
$result->hasSchemaMismatches(); // bool
$result->schemaMismatches();    // list<SchemaException>

// Each mismatch is a SchemaException with a descriptive message
foreach ($result->schemaMismatches() as $e) {
    // $e->getMessage() includes table name and column details
}

On this page