Rendu dialectal d'une table d'infrastructure¶
Ce document décrit core/database/table_ddl.py.
À quoi cela sert¶
Un paquet Forge qui livre sa propre table (sessions, jobs, audit, notifications…) doit pouvoir la décrire une fois et obtenir le DDL correct pour le backend actif.
Sans ce rendu, chaque paquet écrivait son SQL à la main, et l'écrivait pour MariaDB.
L'audit OPTIN-DDL-DIALECT-AUDIT-001 a mesuré le coût sur quatre serveurs réels : douze fichiers SQL livrés par dix opt-ins, aucun exécutable ailleurs que sur MariaDB, alors que le contrat Dialect couvrait déjà toutes les constructions en cause.
Ce que le rendu produit¶
render_create_table(table, dialect) retourne une liste d'instructions à exécuter dans l'ordre : le CREATE TABLE, puis les CREATE INDEX que le dialecte exige hors de la création.
MariaDB porte ses index dans le CREATE TABLE ; PostgreSQL, SQLite et SQL Server les veulent séparés.
Le rendu s'en charge, l'appelant n'a pas à le savoir.
Le SQL reste visible (principe 5) : la commande <opt-in>:init écrit le texte produit dans mvc/migrations/, où l'auteur le relit avant de l'appliquer (ADR-071).
Décrire une table¶
from core.database.table_ddl import Column, Index, TableDefinition, render_create_table
from core.database.backend import get_backend
FORGE_SESSIONS = TableDefinition(
name="forge_sessions",
columns=[
Column("session_id", "char", length=64),
Column("data", "text"),
Column("expire_at", "datetime"),
Column("version", "integer", default=0),
],
primary_key=["session_id"],
indexes=[Index("idx_forge_sessions_expire_at", "expire_at")],
)
statements = render_create_table(FORGE_SESSIONS, get_backend().dialect)
Types acceptés¶
Les types sont exprimés en vocabulaire Forge, jamais en SQL d'un SGBD.
| Type | Rendu par | Remarque |
|---|---|---|
string |
Dialect.string_type(length) |
length requis |
char |
Dialect.char_type(length) |
length requis |
text, integer, big_integer, float, boolean, date, datetime, json |
Dialect.simple_type(...) |
|
identity |
Dialect.auto_increment_column_ddl(...) |
clé primaire auto-incrémentée |
identity_ref |
Dialect.identity_storage_type() |
référence vers une identité, jamais auto-incrémentée |
La distinction identity / identity_ref est celle du ticket FK-IDENTITY-STORAGE-TYPE-001 : une colonne qui stocke un identifiant ne doit pas en générer un.
Valeurs par défaut¶
NO_DEFAULT est la sentinelle d'absence, distincte de DEFAULT NULL : une colonne peut légitimement valoir NULL par défaut.
default_now=True rend DEFAULT CURRENT_TIMESTAMP via le dialecte.
Avec on_update_now=True, la mise à jour automatique est ajoutée là où le dialecte la connaît ; ailleurs le simple DEFAULT est rendu et c'est à l'application de tenir l'horloge.
Clés étrangères¶
ON DELETE RESTRICT est normalisé en NO ACTION : SQL Server ne connaît pas RESTRICT, alors que NO ACTION est compris par les quatre backends et a la même sémantique pratique.
Ce que ce module ne fait pas¶
Il rend des tables d'infrastructure, livrées figées par un paquet.
Les entités de l'application ont leur propre chaîne, forge_mvc_entities.build_entity_sql, qui part d'un contrat JSON utilisateur et gère bien davantage : relations, médias, slugs, horodatages gérés.
Les deux rendus partagent le contrat Dialect mais pas leur entrée.
Les confondre reviendrait à faire dépendre tous les opt-ins du moteur d'entités.
Garde-fou¶
tests/meta/test_optin_ddl_portability_ratchet_001.py est un cliquet : la liste des fichiers SQL encore non portables ne peut que diminuer.
Un paquet neuf qui livrerait du SQL propre à MariaDB fait échouer la suite, et un fichier corrigé mais laissé dans la liste la fait échouer aussi.