Treci la conținut

Generarea SQL

SqlGenerator convertește un MergeResult în instrucțiuni SQL parametrizate. Toate valorile sunt transmise ca parametri, niciodată interpolate în șirul SQL.

PHP
use Merql\Apply\SqlGenerator;

$statements = SqlGenerator::generate($result);

foreach ($statements as $stmt) {
    echo $stmt['sql'] . "\n";
    // INSERT INTO `posts` (`id`, `title`, `status`) VALUES (?, ?, ?)
    print_r($stmt['params']);
    // [42, 'New Title', 'publish']
}

Folosiți generateGuarded() când un plan de îmbinare revizuit ar trebui aplicat doar dacă rândurile live corespund în continuare unui instantaneu așteptat.

PHP
use Merql\Apply\SqlGenerator;
use Merql\Snapshot\SnapshotStore;

$statements = SqlGenerator::generateGuarded(
    $selectedResult,
    SnapshotStore::load('theirs'),
);

Formatul instrucțiunilor

Fiecare instrucțiune este un tablou asociativ cu două chei:

PHP
[
    'sql'    => 'INSERT INTO `posts` (`id`, `title`) VALUES (?, ?)',
    'params' => [42, 'New Title'],
]

Identificatorii (numele tabelelor și coloanelor) sunt citați folosind convenția driverului bazei de date. MySQL folosește backtick-uri, iar SQLite folosește ghilimele duble. Dacă nu este furnizat niciun driver, citarea cu backtick-uri este folosită implicit.

Ordonarea operațiilor

  1. Mai întâi INSERTs -- rândurile noi trebuie să existe înainte ca actualizările să le poată referi.

  2. Apoi UPDATEs.

  3. La final DELETEs -- referințele către rânduri trebuie eliminate înainte de ștergere.

  4. INSERĂRILE mai întâi -- rândurile noi trebuie să existe înainte ca actualizările să le poată referi.

  5. ACTUALIZĂRILE apoi.

  6. ȘTERGERILE la final -- referințele către rânduri trebuie eliminate înainte de ștergere.

PHP
$statements = SqlGenerator::generate($result);
// All INSERTs, then all UPDATEs, then all DELETEs.

Ordonarea cheilor străine

Când sunt furnizate dependențele dintre cheile străine, operațiile din fiecare grup sunt ordonate topologic. Tabelele părinte sunt procesate înaintea tabelelor copil pentru inserări și actualizări. Tabelele copil sunt procesate înaintea tabelelor părinte pentru ștergeri.

PHP
$fkDependencies = [
    'comments' => ['posts'],        // comments references posts
    'post_meta' => ['posts'],       // post_meta references posts
];

$statements = SqlGenerator::generate($result, fkDependencies: $fkDependencies);
// INSERTs: posts first, then comments and post_meta
// DELETEs: comments and post_meta first, then posts

ForeignKeyResolver efectuează o sortare topologică a grafului dependențelor. Dependențele circulare sunt detectate și întrerupte pentru a preveni buclele infinite.

Detectarea automată a cheilor străine

Applier citește automat relațiile cheilor străine din baza de date:

PHP
use Merql\Apply\Applier;

$applier = new Applier($connection);
$applied = $applier->apply($result);
// FK dependencies are read from the database via the driver.

Exemple generate de SQL

INSERT

Sql
INSERT INTO `posts` (`id`, `title`, `content`, `status`) VALUES (?, ?, ?, ?)
-- params: [42, 'New Post', 'Body text', 'draft']

Coloanele de identitate (cheia primară) sunt excluse din clauza SET și folosite în clauza WHERE. Valorile clauzei WHERE provin din decodificarea cheii rândului.

UPDATE

Sql
UPDATE `posts` SET `title` = ?, `status` = ? WHERE `id` = ?
-- params: ['New Title', 'publish', 42]

Doar coloanele de identitate apar în clauza WHERE.

DELETE

Sql
DELETE FROM `posts` WHERE `id` = ?
-- params: [42]

Doar coloanele de identitate apar în clauza WHERE.

ACTUALIZARE cu protecție

Actualizările cu protecție includ atât clauze de identitate, cât și clauze pentru rândul live așteptat.

Sql
UPDATE `posts` SET `title` = ? WHERE `id` = ? AND `status` = ?
-- params: ['New Title', 42, 'draft']

Dacă rândul nu mai are identitatea așteptată status, instrucțiunea nu afectează niciun rând, iar aplicarea cu protecție anulează tranzacția.

INSERARE cu protecție

Inserările cu protecție folosesc NOT EXISTS pentru a evita inserarea într-o identitate apărută după crearea planului.

Sql
INSERT INTO `posts` (`id`, `title`) SELECT ?, ? WHERE NOT EXISTS (
  SELECT 1 FROM `posts` WHERE `id` = ?
)
-- params: [42, 'New Post', 42]

Gestionarea valorilor NULL

Valorile NULL sunt transmise ca PHP null în tabloul params. Adaptorul conexiunii gestionează legarea:

PHP
$statements = SqlGenerator::generate($result);
// params might contain: [42, null, 'draft']
// The adapter binds null as SQL NULL

În rezultatul rulării de probă, valorile NULL sunt redate ca șirul literal NULL fără ghilimele.

Detectarea coloanelor de identitate

SqlGenerator determină coloanele care alcătuiesc identitatea rândului, examinând TableSnapshot::identityColumns din instantaneul de bază. Aceste coloane sunt folosite în clauzele WHERE pentru instrucțiunile UPDATE și DELETE și sunt excluse din clauzele SET ale instrucțiunilor UPDATE.

Dacă nu este disponibil niciun instantaneu de bază, prima coloană din tabloul de valori este folosită ca soluție de rezervă. Dacă tabloul de valori este gol, se presupune id.

Transmiterea unui driver

Pentru a controla citarea identificatorilor, transmiteți o instanță Driver:

PHP
use Merql\Driver\SqliteDriver;

$statements = SqlGenerator::generate(
    $result,
    driver: new SqliteDriver(),
);
// Uses double-quote quoting: "posts", "title"