Aller au contenu

Génération SQL

SqlGenerator convertit un MergeResult en instructions SQL paramétrées. Toutes les valeurs sont liées comme paramètres, jamais interpolées dans la chaîne 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']
}

Utilisez generateGuarded() lorsqu'un plan de fusion validé ne doit s'appliquer que si les lignes actives correspondent toujours à un instantané attendu.

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

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

Format des instructions

Chaque instruction est un tableau associatif avec deux clés :

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

Les identifiants (noms de table et de colonne) sont cités selon le style du pilote de base de données. MySQL utilise des accents graves, SQLite utilise des guillemets doubles. En l'absence de pilote, les accents graves sont utilisés par défaut.

Ordre des opérations

Les opérations sont regroupées par type et exécutées dans cet ordre :

  1. INSERTs d'abord : les nouvelles lignes doivent exister avant que les mises à jour puissent les référencer.
  2. UPDATEs ensuite.
  3. DELETEs en dernier : les lignes doivent être dissociées avant leur suppression.
PHP
$statements = SqlGenerator::generate($result);
// All INSERTs, then all UPDATEs, then all DELETEs.

Ordre des clés étrangères

Lorsque les dépendances de clés étrangères sont fournies, les opérations de chaque groupe sont triées topologiquement. Les tables parentes sont traitées avant les tables enfants pour les insertions et les mises à jour. Pour les suppressions, les tables enfants sont traitées avant les tables parentes.

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 effectue un tri topologique du graphe de dépendances. Les dépendances circulaires sont détectées et rompues pour éviter les boucles infinies.

Détection automatique des clés étrangères

Applier lit automatiquement les relations de clés étrangères dans la base de données :

PHP
use Merql\Apply\Applier;

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

Exemples de SQL généré

INSERT

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

Toutes les colonnes des valeurs de l'opération sont incluses.

UPDATE

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

Les colonnes d'identité (clé primaire) sont exclues de la clause SET et utilisées dans la clause WHERE. Les valeurs de la clause WHERE proviennent du décodage de la clé de ligne.

DELETE

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

Seules les colonnes d'identité figurent dans la clause WHERE.

UPDATE protégé

Les mises à jour protégées incluent les clauses d'identité et les clauses correspondant aux lignes actives attendues.

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

Si la ligne n'a plus le status attendu, l'instruction affecte zéro ligne et l'application protégée annule la transaction.

INSERT protégé

Les insertions protégées utilisent NOT EXISTS pour éviter d'insérer une identité apparue depuis la création du plan.

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

Gestion de NULL

Les valeurs NULL sont transmises comme PHP null dans le tableau params. L'adaptateur de connexion gère la liaison :

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

Dans la sortie d'une simulation, les valeurs NULL sont représentées par la chaîne littérale NULL sans guillemets.

Détection des colonnes d'identité

SqlGenerator détermine les colonnes qui constituent l'identité de la ligne en examinant le TableSnapshot::identityColumns de l'instantané de base. Ces colonnes sont utilisées dans les clauses WHERE des instructions UPDATE et DELETE et exclues des clauses SET des UPDATE.

Si aucun instantané de base n'est disponible, la première colonne du tableau values est utilisée comme solution de repli. Si le tableau values est vide, id est supposé.

Fournir un pilote

Pour contrôler la mise entre guillemets des identifiants, transmettez une instance de Driver :

PHP
use Merql\Driver\SqliteDriver;

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