29. Langage PostgreSQL
Une instance démarrée avec l'élément POSTGRESQL de sql_mode (voir 15.10) parle le protocole et la grammaire de PostgreSQL : les requêtes de psql, de psycopg2, de Npgsql ou de pgjdbc s'écrivent comme pour un serveur PostgreSQL. Ce chapitre liste ce que cette grammaire accepte, les extensions de MIRAJ qui y restent disponibles, la syntaxe propre à MIRAJ qui y est refusée et ce qui n'est pas encore pris en charge.
Le moteur, lui, est le même : une requête PostgreSQL est ramenée aux instructions de MIRAJ, avec leurs règles (types, conversions, collations).
29.1 Lexique#
| Écriture | Sens |
|---|---|
"Nom" | identifiant (table, colonne, alias) ; "" dans le nom pour un guillemet |
'texte' | chaîne standard : la barre oblique \ est un caractère ordinaire, '' écrit une apostrophe |
E'a\tb\n' | chaîne avec échappements : \b \f \n \r \t, \\, \', octal \101, hexadécimal \x41, Unicode é et \U0001F600 |
$$texte$$, $fn$texte$fn$ | chaîne sans aucun échappement (corps de fonction, texte contenant des apostrophes) |
$1, $2… | paramètres d'une requête préparée ; un même numéro peut revenir |
-- … | commentaire jusqu'à la fin de la ligne (aussi --5) |
/* … /* … */ … */ | commentaire, éventuellement imbriqué ; /*! … */ n'y est qu'un commentaire |
# | opérateur OU exclusif bit à bit (pas un commentaire) |
Un identifiant entre guillemets reste insensible à la casse : "Articles" et articles désignent la même table (les noms de MIRAJ sont tous insensibles à la casse). Le nom écrit est gardé tel quel à la création.
29.2 Expressions et requêtes#
| Forme PostgreSQL | Lue comme |
|---|---|
a || b | concaténation (CONCAT(a, b) : NULL si l'un est NULL) |
a ~ 'motif', ~*, !~, !~*, a OPERATOR(pg_catalog.~) b | REGEXP / NOT REGEXP |
a ILIKE 'x%', NOT ILIKE | LIKE (les collations par défaut ignorent déjà la casse) |
x::type, CAST(x AS type) | conversion, types du tableau 29.1 |
int4 '5', timestamptz '…', bytea '\x00', boolean 't' | littéral typé |
INTERVAL '1 day', '2 hours 30 minutes'::interval, INTERVAL '3' DAY | intervalle (d + INTERVAL '1 day') |
TRUE, FALSE | booléens, rendus t / f |
a IS [NOT] DISTINCT FROM b | comparaison qui traite NULL comme une valeur |
a = ANY (ARRAY[1, 2]), a <> ALL (ARRAY[…]) | a IN (1, 2), a NOT IN (…) ; a > ANY (ARRAY[…]) : disjonction |
position('a' in b), substring(b from 2 for 3), substring(b for 3) | LOCATE, SUBSTRING |
extract(year from d), extract('month' from d), date_part('year', d) | valeur numeric (date_part : double precision) : YEAR(d), MONTH(d)… ; aussi epoch, dow, isodow, doy, week, quarter, isoyear, decade, century, millennium, julian, milliseconds, microseconds |
extract(epoch from t) | secondes depuis le 01/01/1970 à 00:00 UTC de l'heure écrite (une date ou un timestamp sans fuseau est lu en UTC, comme PostgreSQL) ; entier pour une date, six décimales sinon |
extract(second from t) | secondes avec leur fraction (30.500000) |
date - date, date ± n, date + time | nombre de jours, date, timestamp |
date ± INTERVAL '…', timestamp ± INTERVAL '…' | timestamp |
timestamp - timestamp | intervalle écrit en texte comme PostgreSQL (2 days 02:00:00) |
timestamptz ± INTERVAL '…', timestamptz - timestamptz | en jours, semaines, mois ou années : l'heure civile de la session est gardée (+ interval '1 day') ; en heures, minutes ou secondes : temps écoulé au travers d'un changement d'heure (+ interval '24 hours') ; différence de deux timestamptz : durée réelle entre les instants (1 day 01:00:00 de part et d'autre du passage à l'heure d'hiver) |
t AT TIME ZONE 'zone' | fonction pg_at_time_zone : un timestamptz (now(), to_timestamp(…), colonne timestamptz…) devient le timestamp de l'heure civile de zone ; un timestamp est lu dans zone et devient un timestamptz ; un décalage écrit ('+02') se lit à l'ouest de Greenwich (notation POSIX), comme PostgreSQL |
doc -> 'clé', doc ->> 'clé', doc -> 0, doc -> 'a' ->> 'b', '{…}'::jsonb -> 'a' | JSON_EXTRACT / JSON_UNQUOTE (->> d'un null JSON : NULL) |
doc #> '{a,0}', doc #>> '{a,0}' | élément au chemin écrit en tableau (document, texte) |
a @> b, a <@ b | inclusion de documents JSON (t / f) |
a || b, doc - 'clé', doc - 0 entre documents JSON | fusion (objets) ou mise bout à bout (tableaux) ; membre ou élément retiré |
x COLLATE "C", COLLATE pg_catalog.default | collation ignorée (une collation de MIRAJ, utf8mb4_bin, reste appliquée) |
pg_catalog.version(), public.f(1), public.t | fonction de PostgreSQL ou de MIRAJ, table de la base courante (pg_catalog.pg_class : table du catalogue, voir 29.9) |
current_schema, current_schema() | DATABASE() |
2 ^ 3 | puissance (POW) |
~5, a & b, a | b, a # b, a << n, a >> n | entiers signés (~5 vaut -6, -8 >> 1 vaut -4) |
Calculs et conversions suivent le serveur de référence, et non le langage MIRAJ :
- une division ou un modulo par zéro (
1 / 0,5 % 0,x / 0.0) est une erreur22012(division_by_zero, queEXCEPTION WHEN division_by_zeroattrape), pas un NULL ; - un texte converti en nombre (
'abc'::int,CAST('1.5' AS integer),'x'::numeric) qui n'en est pas un donne22P02au lieu de son préfixe numérique ; un entier peut s'écrire0x1F,0o17,0b101,1_000;'Infinity','-Infinity'et'NaN'sont desfloat8; - un entier écrit qui tient sur 32 bits est un
integer(pg_typeof(1), type annoncé au client) ; sumd'unintegerest unbigint,sumd'unbigintunnumeric(sans dépassement) ;avgd'entiers ou de décimaux est unnumericà 16 décimales au moins (2.3333333333333333) ;stddevetvariancesont l'écart-type et la variance corrigés (stddev_samp,var_samp) ; avecstddev_popetvar_pop, ils rendent sur des entiers ou des décimaux unnumericexact à 16 décimales au moins (stddevde 1 à 5 :1.5811388300841897), undouble precisionsur des flottants, et s'emploient aussi avecOVER;x ^ yetpower(x, y)rendent undouble precisionentre entiers ou avec un flottant, unnumericexact dès qu'un opérande est un décimal (2 ^ 0.5:1.4142135623730950), à 16 chiffres significatifs et pas moins de décimales qu'un opérande ; zéro à une puissance négative ou un négatif à une puissance non entière :2201F;lower(1),upper(1.5):42883, ces fonctions n'existant que pour le texte.
Valeurs écrites dans une colonne (INSERT, UPDATE) : une colonne boolean lit 't', 'yes', 'on', '1', leurs contraires et préfixes (22P02 pour 'maybe') ; une colonne uuid garde la forme canonique en minuscules (22P02 pour un texte qui n'est pas un UUID) ; une colonne json ou jsonb refuse un document illisible (22P02).
Clauses de SELECT :
LIMIT n,LIMIT ALL,OFFSET n [ROWS](seul ou avantLIMIT),FETCH {FIRST | NEXT} [n] {ROW | ROWS} ONLY;ORDER BY x [ASC | DESC] [NULLS FIRST | NULLS LAST](MIRAJ classe les NULL en tête dans l'ordre croissant ; l'ordre demandé est obtenu par une clé de tri ajoutée),xpouvant être un alias de la liste ; une colonnejsonbse trie dans l'ordre de PostgreSQL (null< chaînes < nombres < booléens < tableaux < objets) ;FOR UPDATE,FOR NO KEY UPDATE,FOR SHARE,FOR KEY SHARE, avecNOWAITouSKIP LOCKED.
Tableau 29.1. Types de conversion#
| Type PostgreSQL | Conversion MIRAJ |
|---|---|
int2, smallint, int4, int, integer, int8, bigint | entier signé |
oid | entier sans signe |
bool, boolean | booléen ; texte lu comme PostgreSQL (t, true, yes, on, 1, leurs contraires et préfixes) |
float4, real, float8, double precision | DOUBLE |
numeric(p,s), decimal(p,s) ; numeric seul | DECIMAL(p,s) ; DECIMAL(38,10) |
text, varchar[(n)], char[(n)], bpchar, name, uuid, regclass, regtype… | texte (CHAR[(n)]) |
date, time[(p)], timetz | DATE, TIME |
timestamp[(p)] ; timestamptz, timestamp with time zone | DATETIME(p), à la microseconde sans précision écrite, un décalage écrit (+02, Z) est retiré ; timestamptz : instant ramené au fuseau de la session ('2026-10-08 10:15:00+02'::timestamptz), envoyé avec son décalage (OID 1184) |
bytea | octets ; '\x0001ff'::bytea et le format d'échappement ('a\\b\001') sont décodés |
json, jsonb | JSON |
interval | intervalle (littéral seulement) |
vector(n) | vecteur |
Les noms de MIRAJ (SIGNED, DATETIME, BINARY…) restent acceptés, et le préfixe pg_catalog. est ignoré.
29.3 Types de colonnes#
| Type PostgreSQL | Colonne MIRAJ |
|---|---|
serial, serial4 ; bigserial, serial8 ; smallserial, serial2 | INT / BIGINT / SMALLINT AUTO_INCREMENT NOT NULL |
GENERATED {ALWAYS | BY DEFAULT} AS IDENTITY [(…)] | AUTO_INCREMENT NOT NULL (options de séquence sans effet) |
int2, int4, int8, smallint, integer, bigint | SMALLINT, INT, BIGINT |
float4, real ; float8, double precision | FLOAT ; DOUBLE |
boolean, bool | BOOLEAN |
numeric(p,s) ; numeric | DECIMAL(p,s) ; DECIMAL(38,10) |
text, varchar sans longueur | LONGTEXT |
varchar(n), character varying(n), char(n), "char", name | VARCHAR(n), CHAR(n), CHAR(1), VARCHAR(63) |
bytea | LONGBLOB |
timestamp[(p)] ; timestamptz ; time[(p)], timetz | DATETIME(p) ; TIMESTAMP(p) (un instant rangé en UTC, lu et écrit dans le fuseau de la session : changer le fuseau de la machine ne le déplace pas, et les deux passages de l'heure répétée au passage à l'heure d'hiver restent distincts) ; TIME(p) (à la microseconde par défaut, comme PostgreSQL) |
date, json, jsonb, uuid | DATE, JSON, JSONB (colonne JSON qui garde le nom jsonb), UUID |
Une colonne serial qui n'est pas à elle seule la clé primaire reçoit une contrainte d'unicité (une colonne AUTO_INCREMENT de MIRAJ doit être une clé).
Une colonne jsonb se comporte comme une colonne json (même stockage, mêmes fonctions), mais garde son type : elle est annoncée jsonb (OID 3802) dans RowDescription, pg_attribute.atttypid, format_type, pg_typeof et information_schema.columns.data_type, au travers d'une vue ou d'une table dérivée, et col -> 'clé', col #> '{a,b}' sur elle rendent du jsonb ; une colonne json reste json (OID 114). ALTER COLUMN … TYPE jsonb (ou json) change le type annoncé. En langage MIRAJ, la colonne s'écrit JSONB (voir 5).
29.4 Définition des données#
CREATE SCHEMA [IF NOT EXISTS] nomcrée une base,DROP SCHEMA nom [CASCADE | RESTRICT]la supprime avec tout son contenu : chaque base MIRAJ est un schéma PostgreSQL.CREATE TEMP TABLE,CREATE TEMPORARY TABLE: table temporaire de la session ;CREATE UNLOGGED TABLE: table ordinaire.- Contraintes de colonne :
NOT NULL,NULL,DEFAULT expr,PRIMARY KEY,UNIQUE,CHECK (…)(qui peut citer d'autres colonnes),REFERENCES t (c), précédées ou non deCONSTRAINT nom;DEFERRABLE,INITIALLY DEFERREDsans effet ;COLLATE "C"ignoré. DEFAULT 'texte'::character varying,DEFAULT now(),DEFAULT CURRENT_TIMESTAMP,DEFAULT 'a' || 'b': la valeur est gardée en texte MIRAJ.GENERATED ALWAYS AS (expr) STORED: colonne générée.TRUNCATE [TABLE] [ONLY] t [RESTART IDENTITY | CONTINUE IDENTITY] [CASCADE | RESTRICT]: une table à la fois ; le compteur d'auto-incrément repart de 1.CREATE [OR REPLACE] VIEW,CREATE INDEX nom ON t (c)(etUSING btree),DROP TABLE … CASCADE.
29.5 Session et transactions#
| Instruction | Effet |
|---|---|
SET [SESSION | LOCAL] nom {TO | =} valeur[, …], SET nom TO DEFAULT | variable de session (lock_wait_timeout, autocommit…) ; une liste devient un texte a, b |
SET TIME ZONE 'Europe/Paris', SET TIME ZONE LOCAL, SET TIME ZONE -5 | time_zone de la session : now(), SHOW TIME ZONE, current_setting('TimeZone') et AT TIME ZONE le suivent, le paramètre TimeZone est renvoyé au client ; fuseau inconnu : 22023 |
SET statement_timeout TO 10000, '10s', '1min' | max_statement_time (secondes) ; SHOW statement_timeout rend 10s |
RESET nom, RESET ALL | valeur par défaut ; RESET ALL sans effet |
SHOW nom, SHOW TIME ZONE | une ligne, une colonne nommée comme le paramètre, étiquette SHOW ; les paramètres propres à PostgreSQL (statement_timeout, server_version_num…) sont lus par current_setting |
SHOW ALL | tous les paramètres (name, setting, description), comme pg_settings |
SET search_path TO a, b, public | la base courante devient la première base nommée qui existe et que le compte peut lire ; public garde la base courante ; SHOW search_path rend la base courante |
BEGIN [WORK | TRANSACTION] [ISOLATION LEVEL …] [READ ONLY | READ WRITE] [[NOT] DEFERRABLE], START TRANSACTION … | transaction ; le niveau d'isolement vaut pour elle seule |
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL … | niveau des transactions suivantes |
SHOW TRANSACTION ISOLATION LEVEL, SHOW transaction_isolation | read committed, repeatable read… |
COMMIT [TRANSACTION], END, ROLLBACK [TRANSACTION], ABORT, SAVEPOINT s, RELEASE [SAVEPOINT] s, ROLLBACK TO [SAVEPOINT] s | fin de transaction, points de sauvegarde |
DISCARD ALL, DEALLOCATE [PREPARE] {nom | ALL} | instructions préparées libérées |
DECLARE c [NO SCROLL] CURSOR [WITH | WITHOUT HOLD] FOR requête, FETCH [FORWARD] {n | ALL} [FROM] c, MOVE …, CLOSE {c | ALL} | curseur SQL (curseurs nommés de psycopg2) ; WITHOUT HOLD exige un bloc de transaction ; lecture en avant seulement |
Les paramètres que PostgreSQL annonce aux clients (application_name, client_encoding, DateStyle, TimeZone, extra_float_digits…) sont tenus par le serveur réseau (voir 15.10.4).
29.6 Extensions MIRAJ disponibles#
Elles s'écrivent avec la lexicographie PostgreSQL (identifiants entre guillemets, chaînes standard) :
- index vectoriels (
CREATE VECTOR INDEX,CREATE INDEX … USING hnsw), distances<->,<#>,<+>; index plein texte (CREATE FULLTEXT INDEX),MATCH (…) AGAINST (…); - vues en cache (
CREATE CACHED VIEW,REFRESH VIEW) ; - points d'accès REST (
CREATE ENDPOINT, corps limité à un seulSELECTen langage PostgreSQL), événements de changement (LISTEN,UNLISTEN,NOTIFY,WAIT FOR CHANGES) ; BACKUP DATABASE,RESTORE DATABASE, transactions XA (XA START…),KILL,USE;SHOWde MIRAJ (SHOW TABLES,SHOW DATABASES,SHOW CREATE TABLE,SHOW PROCESSLIST,SHOW VARIABLES,SHOW CLUSTER STATUS…), variables@@nomet@v;- partitionnement, tables temporelles (
FOR SYSTEM_TIME),QUALIFY,GROUP BY … WITH ROLLUP.
29.7 Syntaxe MIRAJ refusée#
Ces formes ont un autre sens en PostgreSQL, ou n'y existent pas : elles reçoivent une erreur de syntaxe (42601) dont le message nomme la construction.
| Syntaxe MIRAJ | En langage PostgreSQL |
|---|---|
`nom` (accents graves) | "nom" |
'l\'été', 'a\nb' (échappements dans '…') | 'l''été', E'a\nb' |
# commentaire | -- commentaire (# est le OU exclusif) |
LIMIT 10, 5 | LIMIT 5 OFFSET 10 |
a || b comme OU logique | a OR b (|| concatène) |
a && b | a AND b |
!a | NOT a |
"texte" comme chaîne | 'texte' ("…" est un identifiant) |
29.8 Pas encore pris en charge#
| Forme | Réponse |
|---|---|
CREATE EVENT, ALTER EVENT | 0A000 : pas d'événement planifié en langage PostgreSQL (29.13) ; routines et déclencheurs : 29.13 |
tableaux (int[], ARRAY[…] hors de = ANY), type interval en colonne | 0A000 (les tableaux du catalogue se lisent, voir 29.9) |
SELECT DISTINCT ON, FETCH … WITH TIES, WITH RECURSIVE dans une vue | 0A000 |
COPY binaire, COPY d'un fichier du serveur, COPY préparé, ON CONFLICT sur une expression | 0A000 ; voir 29.11 |
alias de colonnes d'une table (FROM t AS x(a, b)) ou d'une requête dont la liste contient * | erreur de syntaxe, 0A000 |
COMMENT ON d'un autre objet qu'une table ou une colonne, DROP INDEX a, b | 0A000 |
doc ? 'clé', ?|, ?& (existence d'une clé JSON) | erreur de syntaxe : ? est le paramètre de l'API embarquée ; doc -> 'clé' IS NOT NULL le remplace |
29.9 Catalogue pg_catalog#
Les clients PostgreSQL décrivent la base en lisant les tables du système (pg_class, pg_attribute…). Une instance en langage PostgreSQL les tient dans une base virtuelle pg_catalog, construite depuis le catalogue de MIRAJ à chaque instruction qui la lit, comme information_schema : rien n'y est écrit (une écriture reçoit 42501), et une instance en langage MIRAJ ne la voit pas (ni ses tables, ni les fonctions de ce chapitre).
Modèle : une seule base PostgreSQL, miraj (pg_database) ; chaque base de MIRAJ est un schéma (pg_namespace). La base courante est en tête du chemin de recherche : ses tables sont « visibles » (pg_table_is_visible), celles des autres bases se nomment base.table. Comme dans PostgreSQL, pg_catalog passe avant tout schéma : SELECT * FROM pg_class lit la table du catalogue (une expression WITH du même nom l'emporte).
OID : les types de base gardent ceux de PostgreSQL (int4 23, text 25, numeric 1700, tableaux _int4 1007…), les tables du catalogue aussi ('pg_class'::regclass vaut 1259). Les objets de MIRAJ reçoivent un OID synthétique et stable : un hachage de leur genre, de leur base et de leur nom, supérieur à 16 384, qui ne change pas d'un redémarrage à l'autre (renommer un objet change son OID). Le type vector est celui de l'extension pgvector (OID 16385, extension vector dans pg_extension).
Tableau 29.2. Tables de pg_catalog#
| Table | Contenu |
|---|---|
pg_namespace | les bases de MIRAJ visibles du compte, pg_catalog, information_schema |
pg_class | tables (r), vues (v), index (i) et séquences (S), et les tables du catalogue |
pg_attribute, pg_attrdef | colonnes (type et modificateur PostgreSQL, NOT NULL, identité d pour un auto-incrément, colonne générée s), valeurs par défaut en langage PostgreSQL ; colonnes des vues |
pg_index, pg_constraint | index (clé primaire <table>_pkey, unicité, secondaires btree, plein texte gin, spatiaux gist, vectoriel hnsw) ; contraintes p, u, f (actions, colonnes référencées), c |
pg_type, pg_enum, pg_range | types de base et leurs tableaux, un type énuméré <table>_<colonne>_enum par colonne ENUM et ses valeurs ; pg_range vide |
pg_proc | routines stockées (fonctions f, procédures p) et array_in / array_recv |
pg_database, pg_roles, pg_user, pg_authid, pg_auth_members | la base miraj ; un rôle par nom de compte (root superutilisateur, OID 10) ; rolpassword toujours masqué (******** dans pg_roles et pg_user, NULL dans pg_authid), jamais le vérificateur SCRAM (voir 15.10.2) |
pg_description | commentaires des tables, colonnes et routines (COMMENT ON) |
pg_settings | paramètres annoncés aux clients (server_version, DateStyle, password_encryption = scram-sha-256…) et variables de la session (SHOW ALL) |
pg_am, pg_collation, pg_tablespace, pg_extension | méthodes d'accès, collations default/C/POSIX, pg_default/pg_global, extension vector |
pg_trigger, pg_sequence | déclencheurs et séquences |
pg_tables, pg_views, pg_indexes, pg_matviews, pg_stat_activity | vues usuelles ; pg_stat_activity reprend les sessions (SHOW PROCESSLIST) |
pg_inherits, pg_partitioned_table, pg_depend, pg_locks, pg_stat_user_tables, pg_language, pg_shdescription, pg_policy, pg_publication*, pg_statistic_ext, pg_rewrite, pg_foreign_*, pg_opclass, pg_event_trigger, pg_init_privs | présentes et vides, pour que les requêtes des clients qui les joignent s'exécutent |
Les colonnes sont celles de PostgreSQL 16, dans leur ordre, avec leur type (oid, name, "char", int2, bool…) ; les tableaux du catalogue (conkey, proargnames, indkey…) sont écrits en texte, {1,3} ou 1 3 pour int2vector.
Fonctions de PostgreSQL#
Elles n'existent qu'en langage PostgreSQL, et passent avant une fonction de MIRAJ de même nom (version(), length()…).
| Fonctions | Rôle |
|---|---|
version() | PostgreSQL 16.4 (MIRAJ 1.0.0) on … |
current_database(), current_catalog, current_schema, current_schemas(bool) | miraj, base courante, {pg_catalog,base} |
current_user, session_user, user, pg_backend_pid(), txid_current() | compte sans son hôte, numéro de connexion, transaction |
format_type(oid, typmod), pg_typeof(x) | character varying(80), numeric(12,2), timestamp without time zone… |
pg_get_indexdef(oid [, colonne]), pg_get_constraintdef(oid), pg_get_viewdef(oid), pg_get_expr(texte, oid), pg_get_triggerdef(oid) | définitions, en langage PostgreSQL |
pg_get_function_result(oid), pg_get_function_arguments(oid), pg_get_userbyid(oid) | routines, rôles |
obj_description(oid [, catalogue]), col_description(oid, n), shobj_description | commentaires |
pg_table_is_visible, pg_type_is_visible, pg_function_is_visible | objet de la base courante ou de pg_catalog |
has_table_privilege, has_schema_privilege… , pg_has_role | true : le catalogue ne montre que ce que le compte peut voir |
pg_relation_size, pg_total_relation_size, pg_table_size, pg_database_size, pg_size_pretty | tailles en mémoire, écrites comme PostgreSQL (20 kB) |
pg_encoding_to_char, quote_ident, quote_literal, quote_nullable | |
current_setting(nom [, absent_ok]), set_config(nom, valeur, local) | paramètres ; set_config rend la valeur sans l'appliquer ; TimeZone, transaction_isolation et statement_timeout suivent la session |
date_trunc(champ, t), make_date(a, m, j), make_timestamp(…), make_time(h, m, s) | dates et heures ; date hors du calendrier : 22008 |
to_char(t | n, modèle), to_date(texte, modèle), to_timestamp(texte, modèle), to_timestamp(secondes) | modèles de dates (YYYY, MM, DD, HH24, HH12, MI, SS, MS, US, AM, Month, Mon, Day, Dy, DDD, IW, Q, J, FM, TH, texte entre guillemets) et de nombres (9, 0, ., D, ,, G, S, MI, FM) |
age(a, b), age(t), justify_days, justify_hours, justify_interval | intervalles écrits en texte comme PostgreSQL (2 years 7 mons 6 days) ; INTERVAL '45 days' écrit seul rend aussi son texte |
to_regclass, to_regtype, to_regproc, to_regnamespace | OID d'un nom, NULL s'il n'existe pas |
array_to_string, array_length, array_upper, array_lower, array_position, cardinality | sur les tableaux écrits en texte |
pg_is_in_recovery(), pg_column_is_updatable, pg_relation_is_publishable | false, true, false |
clock_timestamp(), statement_timestamp(), transaction_timestamp(), random(), gen_random_uuid(), pg_sleep(s), length(t) | fonctions de MIRAJ sous leur nom PostgreSQL (length compte des caractères) |
strpos(t, s), to_hex(n), encode(octets, f), decode(t, f) | formats hex, base64, escape |
format(gabarit, …) | %s, %I (identifiant cité), %L (littéral cité, NULL), %%, position %2$s, largeur %10s, %-10s, %*s |
regexp_replace(t, motif, remplacement [, début [, n]] [, drapeaux]) | première correspondance, toutes avec g, la n-ième avec n ; sensible à la casse sauf i ; \1…\9, \& dans le remplacement |
log(x), log(b, x), trunc(x [, n]), gcd, lcm, factorial, width_bucket(x, bas, haut, n) | log(x) est décimal (ln : népérien) |
jsonb_build_object, json_build_object, jsonb_build_array, json_build_array, to_jsonb, to_json | documents JSON ; un booléen devient true / false |
jsonb_set(d, chemin, valeur [, créer]), jsonb_array_length, jsonb_typeof, json_typeof | chemin écrit en tableau ('{a,0}') |
string_agg(x, séparateur [ORDER BY …]), json_agg, jsonb_agg, json_object_agg, jsonb_object_agg, bool_and, bool_or, every | agrégats (GROUP_CONCAT, JSON_ARRAYAGG, JSON_OBJECTAGG, MIN, MAX) |
pg_advisory_lock(clé), pg_try_advisory_lock(clé), pg_advisory_unlock(clé) | verrous consultatifs de session (clé bigint ou deux integer), cumulables, rendus à la fin de la session ; verrous nommés de MIRAJ (GET_LOCK) de nom pg_advisory:<clé> |
Conversions : 'clients'::regclass donne l'OID de la table (42P01 si elle n'existe pas), c.oid::regclass son nom (qualifié par sa base hors de la base courante) ; de même ::regtype (format_type), ::regproc, ::regnamespace, ::regrole.
Formes reconnues pour les requêtes de catalogue : tableau[n] (i.indkey[0], (current_schemas(true))[1]), x = ANY (tableau) et x <> ALL (tableau) sur un tableau du catalogue, generate_series(début, fin [, pas]) dans un FROM (avec AS s(n)), alias de colonnes d'une table dérivée ((SELECT …) AS x(a, b)), alias après AS qui est un mot réservé (AS default). Une condition (a = b, x IS NULL, EXISTS (…)…) placée dans la liste d'un SELECT est un booléen (t / f).
Quelques requêtes figées des clients usent de formes que le moteur n'exécute pas (fonctions qui rendent des lignes comme information_schema._pg_expandarray, pg_partition_ancestors, tableaux construits par ARRAY(SELECT …) corrélé) : le serveur en reconnaît le texte (psql 10 à 18, pgjdbc 42.x, Npgsql 6 à 10) et les réécrit en requêtes équivalentes sur le même catalogue.
Définitions et commentaires#
COMMENT ON TABLE clients IS 'Les clients';
COMMENT ON COLUMN clients.nom IS 'Nom complet'; -- IS NULL retire le commentaire
CREATE TABLE lignes (id serial PRIMARY KEY, commande bigint REFERENCES commandes, qte int);
CREATE INDEX lignes_qte ON lignes (qte);
DROP INDEX lignes_qte; -- sans table, comme PostgreSQL
SHOW ALL;
SHOW statement_timeout;REFERENCES tsans colonnes désigne la clé primaire det(42830si elle n'en a pas).DROP INDEX [IF EXISTS] [base.]nomcherche la table qui porte l'index dans la base courante (ou celle écrite) ; un seul index par instruction.COMMENT ONporte sur une table ou une colonne ; un commentaire de colonne redéfinit la colonne (la table est réécrite).
29.10 Définitions gardées et changement de langage#
Une vue, une valeur par défaut, une contrainte CHECK, une colonne générée, un partitionnement, un point d'accès REST ou un filtre LISTEN écrits en langage PostgreSQL sont gardés en texte MIRAJ par le catalogue (la requête est réécrite depuis son arbre). Une instance peut donc changer de langage (arrêt, modification de sql_mode, redémarrage) : ses objets restent lisibles et utilisables dans l'autre langage.
Les expressions gardées avec une table (colonne générée, index sur expression, valeur par défaut, contrainte CHECK) gardent aussi le langage de leur création : elles se calculent toujours selon ses règles, quel que soit le langage de l'instance qui écrit ou lit la table. Une colonne GENERATED ALWAYS AS (split_part(code, '-', 1)) STORED ou un index sur (doc ->> 'k') créés en langage PostgreSQL donnent la même valeur dans une instance rouverte en langage MIRAJ (split_part, strpos, jsonb_typeof… y restent trouvées, length compte des caractères, ->> rend NULL pour null), et une table créée en langage MIRAJ garde les règles de MIRAJ. Le langage MIRAJ lui-même ne gagne pas les fonctions de PostgreSQL : une requête écrite en langage MIRAJ ne peut pas appeler split_part (1305).
La fonction de partitionnement (et de sous-partitionnement) et la requête d'une vue suivent la même règle : une table PARTITION BY LIST (length(a)) créée en langage PostgreSQL range ses lignes selon le nombre de caractères dans toute instance (ADD / REORGANIZE PARTITION compris), et une vue créée en langage PostgreSQL se lit dans ce langage — sa condition WITH CHECK OPTION aussi —, une vue créée en langage MIRAJ dans celui de MIRAJ. Seul un UPDATE ou un DELETE au travers d'une vue, réécrit en une instruction sur la table, compile la condition de la vue dans le langage de la session : une fonction propre à l'autre langage y est refusée (1305).
L'affichage suit le langage de l'instance : SHOW CREATE TABLE, SHOW CREATE VIEW et information_schema.VIEWS.VIEW_DEFINITION rendent, en langage PostgreSQL, une définition écrite en PostgreSQL (identifiants entre guillemets, ||, LIMIT ALL, types PostgreSQL, index en CREATE INDEX), quel que soit le langage dans lequel l'objet a été créé. Une colonne BOOLEAN (gardée en TINYINT(1)) s'y écrit boolean ; ENUM(…) et SET(…) restent écrits comme MIRAJ les lit (extension, valeurs en chaînes standard), pour que le texte recrée la même colonne. SHOW CREATE SEQUENCE, SHOW CREATE ENDPOINT, SHOW CREATE USER et SHOW GRANTS rendent leur texte avec la lexicographie de PostgreSQL (identifiants "…", chaînes standard) : ces lignes se rejouent telles quelles sur une instance PostgreSQL (miraj-dump --users). Une routine ou un déclencheur écrit en PL/pgSQL est gardé en texte MIRAJ et son texte d'origine à côté (29.13) ; écrit en langage MIRAJ, son corps reste affiché en langage MIRAJ, comme celui d'un événement.
CREATE VIEW resume AS
SELECT nom || ' (' || etat || ')' AS libelle FROM articles WHERE nom ILIKE 'st%' FETCH FIRST 5 ROWS ONLY;
SHOW CREATE VIEW resume;
-- CREATE VIEW "resume" AS select ((("nom" || ' (') || "etat") || ')') as "libelle" from "articles"
-- where ("nom" like 'st%') limit 5Une colonne calculée sans alias garde son nom écrit (SELECT a + 1 FROM t nomme la colonne a + 1).
29.11 Écritures : RETURNING, ON CONFLICT et COPY#
RETURNING#
INSERT, UPDATE et DELETE acceptent une clause RETURNING, écrite comme la liste d'un SELECT (*, t.*, expressions, alias) : l'instruction rend une ligne par ligne écrite, dans l'ordre des écritures, puis son étiquette habituelle (INSERT 0 2, UPDATE 1, DELETE 3).
INSERT INTO clients (nom) VALUES ('Dupont'), ('Durand') RETURNING id, nom, cree_le;
UPDATE stock SET quantite = quantite - 1 WHERE article = 12 RETURNING quantite;
DELETE FROM paniers WHERE expire < now() RETURNING *;
INSERT INTO t AS x (code, n) VALUES ('a', 1) ON CONFLICT (code) DO UPDATE SET n = x.n + 1 RETURNING x.n;INSERTetUPDATErendent les valeurs après l'écriture : valeur d'auto-incrément (SERIAL, identité),DEFAULT, colonnes générées, et ce qu'un déclencheurBEFOREa changé ;DELETErend les valeurs d'avant la suppression. Les lignes rendues sont lues dans la même transaction que l'écriture, verrous encore tenus.- Les expressions sont vérifiées avant toute écriture (une colonne inconnue,
42703, ne modifie rien) ; une sous-requête de la clause voit l'état d'avant l'instruction, comme PostgreSQL. - Protocole étendu : Describe de l'instruction décrit les colonnes de la clause (RowDescription), ce qu'attendent
getGeneratedKeysde pgjdbc etExecuteReaderde Npgsql. - Droits :
SELECTsur la table, en plus du droit d'écriture. - Un
UPDATE … RETURNINGn'est jamais différé (deferred_update) : sa ligne est écrite tout de suite pour être relue. - Non pris en charge (
0A000) : au travers d'une vue, sur une table partitionnée (édition Cluster), dans unUPDATEou unDELETEmulti-tables (DELETE … USING), sous-requête corrélée à la ligne écrite.
RETURNING n'existe qu'en langage PostgreSQL : le langage MIRAJ reste inchangé.
ON CONFLICT#
INSERT INTO t (id, code) VALUES (1, 'a') ON CONFLICT DO NOTHING;
INSERT INTO t (id, code) VALUES (1, 'a') ON CONFLICT (code) DO NOTHING;
INSERT INTO t (id, code) VALUES (1, 'a') ON CONFLICT ON CONSTRAINT t_pkey DO NOTHING;
INSERT INTO t AS x (code, n) VALUES ('a', 5), ('b', 2)
ON CONFLICT (code) DO UPDATE SET n = x.n + EXCLUDED.n WHERE x.n < 100;- La cible désigne la contrainte d'unicité arbitre : liste de colonnes (la contrainte dont les colonnes sont exactement celles-ci, sinon
42P10) ouON CONSTRAINT nom(clé primaire nommée<table>_pkeycomme danspg_catalog, ou nom d'une contrainteUNIQUE; inconnue :42704). Sans cible (DO NOTHINGseulement), toute contrainte d'unicité est arbitre. - Seul l'arbitre est traité : une ligne qui viole une autre contrainte d'unicité reçoit l'erreur
23505, comme dans PostgreSQL (alors queON DUPLICATE KEY UPDATEréagit à toute clé). DO NOTHINGécarte la ligne proposée sans avertissement ; les autres erreurs (NOT NULL,CHECK, clés étrangères) restent des erreurs.DO UPDATE SET col = expr, …modifie la ligne existante : une colonne seule ou qualifiée par la table (ou son alias,INSERT INTO t AS x) désigne la ligne existante,EXCLUDED.colla ligne proposée (valeurs par défaut et déclencheurBEFORE INSERTcompris),DEFAULTla valeur par défaut.WHERE condition(sur la ligne existante etEXCLUDED) laisse la ligne telle quelle quand elle n'est pas vraie. Les déclencheursUPDATEde la table s'exécutent.- Une ligne insérée ou modifiée par l'instruction ne peut pas être modifiée une seconde fois par elle (deux lignes proposées de même clé) :
21000, rien n'est écrit. - Compte : une ligne par ligne insérée ou modifiée (
INSERT 0 2), aucune pourDO NOTHINGou unWHEREfaux ; une ligne en conflit modifiée sans changement de valeur compte aussi. - Non pris en charge (
0A000) : cible faite d'expressions (ON CONFLICT (lower(code))), d'index partiel (WHERE), avecCOLLATEou classe d'opérateurs ;DO UPDATE SET (a, b) = (…); au travers d'une vue ou sur une table partitionnée.
COPY#
| Forme | Effet |
|---|---|
COPY t [(col, …)] FROM STDIN [[WITH] (options)] | lignes envoyées par le client (CopyData), chargées à la fin des données ; étiquette COPY n |
COPY t [(col, …)] TO STDOUT [[WITH] (options)] | lignes de la table envoyées au client, une par message ; COPY n |
COPY (requête) TO STDOUT [[WITH] (options)] | lignes d'une requête (SELECT, ou écriture à clause RETURNING) |
Options : FORMAT text (défaut) ou FORMAT csv, DELIMITER 'c', NULL 'texte', HEADER (sautée à la lecture, écrite en tête à l'écriture), QUOTE 'c', ESCAPE 'c' (CSV), ENCODING 'UTF8', FREEZE (sans effet). La forme ancienne des options est acceptée (WITH DELIMITER AS '|' NULL AS '', WITH CSV HEADER), telle que l'envoient psycopg2 (copy_from, copy_to) et les vieux scripts.
- Format texte : champs séparés par une tabulation, NULL écrit
\N, échappements\t,\n,\r,\\,\b,\f,\v, octal (\101) et hexadécimal (\x41). CSV : virgule, guillemets doubles,""pour un guillemet dans un champ cité, NULL = champ vide non cité,""= chaîne vide ; un champ cité peut contenir des fins de ligne. - Valeurs reçues : texte en UTF-8 (
22021sinon) ; booléens écrits comme PostgreSQL (t,true,yes,on,1…, sinon22P02) ;byteaen hexadécimal\x…ou forme échappée ; date-heure avec décalage (2026-10-06 10:00:00+02) chargée sans le décalage, comme un littéral ; les autres conversions sont celles deLOAD DATAdans lesql_modede la session (mode strict : une valeur invalide est une erreur). Valeurs écrites : celles qu'unSELECTrend en texte (t/f,byteaen\x…, dates ISO). - Une ligne qui n'a pas autant de champs que de colonnes :
22P04(missing data for column "nom",extra data after last expected column), avec la ligne en contexte (COPY t, line 3). COPY … FROM STDINs'exécute comme unINSERT(droitINSERT, vérifié avant que le client n'envoie les données ; déclencheurs, contraintes, clés étrangères) et en une seule instruction : une erreur, un abandon du client (CopyFail,57014) ou une annulation (Ctrl-C,57014) n'écrivent rien. Après une erreur au milieu des données, le serveur lit et ignore la suite jusqu'à la fin de l'envoi, puis rend l'erreur. Le serveur lit d'abord les colonnes de la table : le droitSELECTest aussi exigé.COPY … TO STDOUTexige le droitSELECT, comme la requête correspondante.- Sans liste de colonnes, les colonnes générées sont exclues, dans les deux sens.
psql:\copy t from 'fichier.csv' with (format csv, header)et\copy t to 'sortie.txt'lisent et écrivent le fichier sur le poste du client, parCOPY … FROM STDIN/TO STDOUT.- Non pris en charge (
0A000) :FORMAT binary,HEADER MATCH,FORCE_QUOTE,FORCE_NOT_NULL,FORCE_NULL,DEFAULT,ON_ERROR,COPY … FROM … WHERE, un autre encodage qu'UTF-8, etCOPYdans le protocole étendu (instruction préparée). Fichiers du serveur :COPY t FROM '/chemin',COPY t TO '/chemin'etPROGRAMsont refusés (0A000) ; pour charger ou écrire un fichier du serveur,LOAD DATA INFILEetSELECT … INTO OUTFILErestent disponibles en langage MIRAJ, soumis àsecure_file_privet au droitFILE.
Les données d'un COPY … FROM STDIN sont gardées en mémoire jusqu'à la fin de l'envoi, puis chargées par le chemin de LOAD DATA LOCAL INFILE (sans fichier temporaire).
29.12 Outils MIRAJ sur une instance PostgreSQL#
Les outils de MIRAJ se connectent aux deux langages par le même client (miraj-connect) : ils reconnaissent le langage de l'instance à la connexion et lui envoient leurs instructions dans ce langage.
Choix du protocole : option --protocol auto | miraj | postgresql de chaque outil (clé protocol de la table [monitor] de proxy.toml), auto par défaut.
autoenvoie d'abord une demande de chiffrement PostgreSQL (SSLRequest, ou GSSENCRequest avec--no-ssl) et lit un octet.SouN: instance PostgreSQL, l'échange continue sur la même connexion (TLS, puis SCRAM-SHA-256 ou mot de passe en clair). Tout autre octet est le premier de la salutation du protocole hérité : la connexion est refermée et rouverte en langage MIRAJ. Le serveur MIRAJ reconnaît cette sonde et la ferme sans la compter dansAborted_connects(une ligne « client PostgreSQL sur une instance en langage MIRAJ » dans son journal).- Le langage reconnu est retenu par point d'accès (
hôte:port) pour la durée du processus : une seule connexion de plus, une fois, vers une instance MIRAJ. mirajetpostgresqlimposent le protocole (un client hérité sur une instance PostgreSQL attend le délai de connexion du serveur avant d'être fermé).- Les options TLS sont les mêmes dans les deux protocoles (
--ssl,--ssl-ca,--ssl-insecure,--no-ssl) ; le proxy relaie la sonde comme les autres octets.
| Outil | Sur une instance PostgreSQL |
|---|---|
miraj-cli (mode réseau, chapitre 25) | instructions et fichiers en langage PostgreSQL ; invite base=> ; erreurs avec leur SQLSTATE |
miraj-dump (18.3) | script écrit en langage PostgreSQL, rechargeable par psql -f, miraj-cli ou miraj-dump restore |
miraj-backup (18.2) | BACKUP / RESTORE (extensions de MIRAJ) écrits dans ce langage ; avertissements reçus en avis |
miraj-migrate (chapitre 17) | cible PostgreSQL : définitions récrites dans ce langage, lignes chargées par COPY … FROM STDIN, routines et déclencheurs traduits en PL/pgSQL (29.13) |
| Miraj Server Manager (chapitre 21) | mesures et réglages par des requêtes valables dans les deux langages ; langage de l'instance affiché (« Langage SQL ») |
| installable | vérification du mot de passe de root dans les deux protocoles |
miraj-proxy (chapitre 22) | sonde dans le protocole des nœuds ; refus 9043 en ErrorResponse 08004 pour un client PostgreSQL |
Export d'une instance PostgreSQL (miraj-dump) :
- tables par
SHOW CREATE TABLE(déjà en PostgreSQL), lignes enINSERTmulti-lignes : identifiants"…", chaînes standard, booléenstrue/false,byteaen'\x…'::bytea, flottants au plus court (-1.5e300) ; séquences, vues, points d'accès REST, comptes et droits dans la même lexicographie, sansDELIMITER; - en tête :
SET client_encoding = 'UTF8'etSET foreign_key_checks = 0; la lecture est faite dans une transactionREPEATABLE READ, READ ONLY; - les procédures, fonctions et déclencheurs écrits en PL/pgSQL sont écrits dans ce langage (29.13) ; ceux écrits en langage MIRAJ (créés avant un changement de langage) sont traduits au mieux en PL/pgSQL, sinon cités en commentaire avec la raison, comme les événements ;
--users: le mot de passe est repris par son empreinte (IDENTIFIED BY PASSWORD '*…'). Un compte rechargé n'a pas de vérificateur SCRAM-SHA-256 : il se connecte en mot de passe en clair (sous TLS ou en boucle locale) jusqu'à ce que son mot de passe soit redéfini (ALTER USER … IDENTIFIED BY …), comme un compte créé avant SCRAM (15.10.2). L'outil le rappelle ;- un export d'une instance MIRAJ ne se recharge pas tel quel dans une instance PostgreSQL, ni l'inverse : l'export suit le langage de l'instance lue.
miraj-dump --db shop --users -r shop.sql --port 7007
psql -h 127.0.0.1 -p 7008 -U root -d miraj -v ON_ERROR_STOP=1 -f shop.sql(sous Windows, PGCLIENTENCODING=UTF8 ou chcp 65001 pour les textes accentués.)
Cluster : tous les nœuds d'un cluster ont le même langage. Un nœud d'un autre langage est refusé à l'adhésion (erreur 9054, consignée par les deux nœuds) : voir le 16.12.
29.13 Routines et déclencheurs (PL/pgSQL)#
Une instance en langage PostgreSQL accepte CREATE FUNCTION et CREATE PROCEDURE écrits en LANGUAGE plpgsql ou LANGUAGE sql, et les déclencheurs CREATE TRIGGER … EXECUTE FUNCTION f(). Ils sont traduits à la création en routines et déclencheurs de MIRAJ, exécutés par le même interpréteur que ceux écrits en langage MIRAJ. Ce qui sort du sous-ensemble décrit ici est refusé à la création (0A000, la construction est nommée dans le message) : rien n'est gardé de travers.
CREATE FUNCTION add_tax(price numeric(10,2), rate numeric(4,2) DEFAULT 0.2)
RETURNS numeric(10,2) LANGUAGE plpgsql IMMUTABLE AS $$
DECLARE
result numeric(10,2);
BEGIN
result := price * (1 + rate);
RAISE NOTICE 'tax of % is %', price, result - price;
RETURN round(result, 2);
END;
$$;
SELECT add_tax(10); -- 12.00, avec l'avis « NOTICE: tax of 10.00 is 2.00 »
CREATE PROCEDURE bump(INOUT n int, step int DEFAULT 1) LANGUAGE plpgsql AS $$
BEGIN
n := n + step;
END $$;
CALL bump(41); -- une ligne : n = 42
CREATE FUNCTION set_updated_at() RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
NEW.updated_at := now();
RETURN NEW;
END $$;
CREATE TRIGGER set_updated_at BEFORE UPDATE ON products
FOR EACH ROW EXECUTE FUNCTION set_updated_at();En-tête de CREATE FUNCTION et CREATE PROCEDURE#
| Élément | Pris en charge |
|---|---|
CREATE [OR REPLACE], [schéma.]nom | le schéma est une base de MIRAJ ; une seule routine par nom et par nature (pas de surcharge : OR REPLACE remplace la routine de même nom, quels que soient ses paramètres) |
| paramètres | [IN | OUT | INOUT] [nom] type [DEFAULT expr | = expr] ; sans nom, le paramètre se lit $1, $2… ; types de base de 29.3, table.colonne%TYPE ; les derniers arguments d'un appel peuvent être omis s'ils ont une valeur par défaut |
| résultat | RETURNS type, RETURNS void, RETURNS trigger ; une fonction à un paramètre OUT (ou INOUT) rend sa valeur finale |
LANGUAGE plpgsql | sql | avant ou après AS |
| corps | AS $$…$$, AS $etiquette$…$etiquette$, AS '…' ; forme du standard RETURN expr (langage sql) |
IMMUTABLE | STABLE | VOLATILE | gardé (provolatile) ; IMMUTABLE rend la routine DETERMINISTIC |
STRICT, RETURNS NULL ON NULL INPUT, CALLED ON NULL INPUT | STRICT : NULL rendu sans exécuter le corps si un argument est NULL |
SECURITY INVOKER | DEFINER | INVOKER par défaut, comme PostgreSQL (MIRAJ : SQL SECURITY DEFINER par défaut) |
[NOT] LEAKPROOF, PARALLEL …, COST n, ROWS n | acceptés sans effet |
CALL p(…) d'une procédure à paramètres OUT / INOUT | rend une ligne de leurs valeurs finales (l'argument d'un OUT est ignoré, souvent NULL), comme PostgreSQL |
DROP FUNCTION | PROCEDURE [IF EXISTS] nom [(types)] [CASCADE | RESTRICT] | les types sont sans effet ; une fonction appelée par des déclencheurs n'est supprimée qu'avec CASCADE (qui les supprime ; sinon erreur 9058, SQLSTATE 2BP01) |
Une fonction LANGUAGE sql rend le résultat de son dernier SELECT (première ligne, NULL sans ligne) ; ses instructions précédentes sont des écritures. Une procédure LANGUAGE sql n'exécute que des écritures.
Sous-ensemble PL/pgSQL#
| Construction | Traduction, remarques |
|---|---|
bloc [<<étiquette>>] [DECLARE …] BEGIN … [EXCEPTION …] END [étiquette] | bloc de MIRAJ ; blocs imbriqués, masquage des variables |
DECLARE nom [CONSTANT] type [NOT NULL] [{DEFAULT | := | =} expr] | types de base, table.colonne%TYPE, variable%TYPE, table%ROWTYPE, RECORD ; CONSTANT et NOT NULL exigent une valeur, sans contrôle ensuite |
nom ALIAS FOR $n, nom CURSOR FOR requête | curseur sans argument |
cible := expr (ou =) | variable, champ d'enregistrement r.champ, NEW.colonne |
IF … ELSIF … ELSE … END IF, CASE [valeur] WHEN v1, v2 THEN … END CASE | CASE sans branche ni ELSE : 20000 |
LOOP, WHILE … LOOP, EXIT [étiquette] [WHEN c], CONTINUE [étiquette] [WHEN c] | boucles étiquetées |
FOR i IN [REVERSE] a .. b [BY s] LOOP | bornes évaluées une fois ; i entier, propre à la boucle |
FOR r IN requête LOOP, FOR r IN curseur LOOP | r : enregistrement ou liste de variables |
SELECT … INTO [STRICT] cibles | première ligne, cibles à NULL sans ligne ; STRICT : P0002 sans ligne, P0003 à plusieurs |
INSERT … VALUES (…) RETURNING colonnes INTO cibles | une ligne ; colonne écrite par la requête ou colonne d'identité générée |
FOUND, GET DIAGNOSTICS n = ROW_COUNT | tenus après SELECT … INTO, PERFORM, INSERT, UPDATE, DELETE, FETCH et les boucles FOR |
PERFORM requête | requête exécutée, résultat ignoré, FOUND |
OPEN, FETCH [NEXT | FORWARD] [FROM] curseur INTO cibles, CLOSE | curseurs déclarés, lus en avant |
RAISE NOTICE | WARNING | INFO 'format %', args | avis rendu au client (NOTICE / WARNING / INFO, numéros 9055 à 9057 dans SHOW WARNINGS), y compris depuis une fonction ou un déclencheur ; %% écrit % ; un argument NULL s'écrit <NULL> ; DEBUG et LOG : rien |
RAISE [EXCEPTION] 'format', args [USING ERRCODE = …, MESSAGE = …], RAISE condition, RAISE SQLSTATE 'xxxxx', RAISE; | erreur de SQLSTATE P0001 par défaut, ou celui donné (nom de condition ou code) ; RAISE; relance l'erreur traitée ; DETAIL, HINT, COLUMN, CONSTRAINT, TABLE… acceptés sans effet |
ASSERT condition [, message] | P0004 si la condition est fausse ou NULL |
EXCEPTION WHEN condition [OR …] THEN … | OTHERS, noms de conditions de PostgreSQL (unique_violation, no_data_found, too_many_rows, division_by_zero, check_violation, foreign_key_violation, not_null_violation, raise_exception… et les classes comme integrity_constraint_violation), SQLSTATE 'xxxxx' ; SQLERRM, SQLSTATE et GET STACKED DIAGNOSTICS … = MESSAGE_TEXT | RETURNED_SQLSTATE dans le gestionnaire (SQLSTATE de PostgreSQL : 23505 pour un doublon) |
NULL;, CALL, INSERT, UPDATE, DELETE, TRUNCATE | instructions du corps |
Déclencheurs#
CREATE [OR REPLACE] TRIGGER nom {BEFORE | AFTER} {INSERT | UPDATE [OF col, …] | DELETE} [OR …]
ON table FOR EACH ROW [WHEN (condition)] EXECUTE {FUNCTION | PROCEDURE} f([arguments]);
DROP TRIGGER [IF EXISTS] nom ON table [CASCADE | RESTRICT];- La fonction
fest une fonctionRETURNS triggerécrite en PL/pgSQL, sans paramètre. Elle litNEW,OLD(NULL quand la ligne n'existe pas :OLDd'unINSERT,NEWd'unDELETE),TG_OP,TG_NAME,TG_WHEN,TG_LEVEL,TG_TABLE_NAME,TG_RELNAME,TG_TABLE_SCHEMA,TG_NARGS,TG_ARGV[i]; elle modifieNEWdans un déclencheurBEFORE. RETURN NEW(ouOLDpour unDELETE) laisse la ligne s'écrire ;RETURN NULLdans un déclencheurBEFOREécarte la ligne : elle n'est ni insérée, ni modifiée, ni supprimée, n'est pas comptée, et les déclencheurs suivants ne s'exécutent pas.RETURN OLDd'unBEFORE UPDATEgarde l'ancienne ligne. La valeur rendue par un déclencheurAFTERest ignorée. Atteindre la fin de la fonction sansRETURN:2F005.- Une même fonction peut servir à plusieurs déclencheurs, sur plusieurs tables ; deux tables peuvent avoir un déclencheur de même nom. Remplacer la fonction (
CREATE OR REPLACE FUNCTION) met à jour les déclencheurs qui l'appellent. - Les déclencheurs d'une table, d'un moment et d'un événement s'exécutent dans l'ordre alphabétique de leurs noms, comme dans PostgreSQL.
- Appeler directement une fonction de déclencheur (
SELECT f()) :0A000.
Un déclencheur PostgreSQL devient un déclencheur MIRAJ par événement, nommé nom$table$événement (set_updated_at$products$update), dont le corps est la traduction de la fonction pour cet événement. Une instance en langage MIRAJ les voit sous ces noms (SHOW TRIGGERS) ; en langage PostgreSQL, pg_trigger, \d table et DROP TRIGGER nom ON table les voient sous leur nom PostgreSQL, une ligne par déclencheur.
Refusé (0A000)#
RETURNS SETOF, RETURNS TABLE, RETURN NEXT, RETURN QUERY (fonctions qui rendent des lignes) ; fonction à plusieurs paramètres OUT, paramètre ou résultat de type composite (RECORD, table) ; EXECUTE (SQL dynamique), FOR … IN EXECUTE ; FOREACH et tableaux ; curseurs à arguments, OPEN … FOR, refcursor, FETCH autre que NEXT, MOVE ; COMMIT / ROLLBACK dans une procédure ; VARIADIC, types polymorphes (anyelement…) ; INSERT … ON CONFLICT, UPDATE | DELETE … RETURNING, RETURNING d'une autre ligne qu'un INSERT … VALUES dans le corps ; un SELECT sans INTO dans le corps (42601, comme PostgreSQL) ; autres instructions (DDL) dans le corps ; LANGUAGE c, internal, plpython… ; clauses SET, WINDOW, TRANSFORM, SUPPORT ; BEGIN ATOMIC ; déclencheurs FOR EACH STATEMENT (ou sans FOR EACH ROW), INSTEAD OF, TRUNCATE, CONSTRAINT TRIGGER, REFERENCING ; ALTER FUNCTION | PROCEDURE | TRIGGER ; blocs anonymes DO $$ … $$.
Événements planifiés (CREATE | ALTER EVENT) : refusés en langage PostgreSQL. PostgreSQL n'en a pas (pg_cron est une extension) et un événement de MIRAJ écrit en PL/pgSQL serait une syntaxe inventée. Les événements créés en langage MIRAJ, avant un changement de langage, continuent de s'exécuter.
Stockage, affichage et changement de langage#
- Le catalogue garde le texte MIRAJ de la traduction (comme une vue, 29.10) : c'est lui qui s'exécute, et une instance redémarrée en langage MIRAJ exécute et affiche (
SHOW CREATE FUNCTION, en langage MIRAJ) les routines et déclencheurs créés en PL/pgSQL. - Le texte d'origine (
CREATE FUNCTION … AS $$…$$,CREATE TRIGGER …) est gardé à côté : l'affichage en langage PostgreSQL en vient, au caractère près pour le corps (prosrc,pg_get_functiondef,\sf), et une fonction de déclencheur remplacée est retraduite depuis lui. De retour en langage PostgreSQL, les définitions affichées sont les mêmes qu'avant le changement. - Une routine ou un déclencheur écrit en langage MIRAJ reste affiché en langage MIRAJ (
SHOW CREATE,pg_get_functiondef).
SHOW CREATE FUNCTION | PROCEDURE rend, pour une routine écrite en PostgreSQL, le texte de pg_get_functiondef ; SHOW CREATE TRIGGER nom (nom PostgreSQL ou nom MIRAJ) celui de pg_get_triggerdef. Comme MIRAJ applique la précision d'un paramètre ou du résultat (une valeur rendue est convertie au type déclaré), l'en-tête affiché la garde (numeric(10,2) ; PostgreSQL l'oublie et écrit numeric).
Catalogue, psql et outils#
pg_proc:prosrc(corps tel qu'écrit),prolang(sql14,plpgsql13600, voirpg_language),prokind,provolatile,proisstrict,prosecdef,pronargs,pronargdefaults,prorettype(void2278,trigger2279),proargtypes,proallargtypes,proargmodes,proargnames;pg_get_function_result,pg_get_function_arguments,pg_get_function_identity_arguments,pg_get_functiondef,'nom'::regproc,'nom(types)'::regprocedure.pg_trigger: une ligne par déclencheur PostgreSQL (tgtypede ses événements,tgfoid,tgargs,tgattr,tgqual) ;pg_get_triggerdef(oid [, pretty]).- psql 18 :
\df,\df+(volatilité, sécurité, langage),\sf,\sf+,\d table(section « Triggers »). miraj-dumpécrit les routines et déclencheurs en PL/pgSQL (CREATE OR REPLACE FUNCTION … AS $function$…$function$,DROP … CASCADEd'abord ; un déclencheur par nom et par table, après les lignes), sansDELIMITER: le script se recharge parpsql -fetmiraj-dump restore. Une routine écrite en langage MIRAJ y est traduite au mieux en PL/pgSQL, sinon citée en commentaire.miraj-migratevers une instance en langage PostgreSQL traduit les routines et déclencheurs de la source en PL/pgSQL (17) ; ce qui ne se traduit pas est cité avec un avertissement.
Écarts#
- Un gestionnaire
EXCEPTIONn'annule pas les écritures faites par le bloc avant l'erreur (PostgreSQL les annule par un point de sauvegarde implicite) ; l'instruction en faute, elle, est annulée. Le gestionnaire choisi est le plus précis qui couvre l'erreur (numéro, SQLSTATE, puisOTHERS), et non le premier écrit. - Une fonction ne peut pas s'appeler elle-même, directement ou non (1424 : récursion refusée, comme pour les routines de MIRAJ).
UPDATE OF coldéclenche quand la valeur d'une de ces colonnes change (casse comprise), et non quand la colonne est citée parSETsans changer.- Les types
%TYPE/%ROWTYPEet les champs d'unRECORDsont résolus à la création : les tables (et les requêtes d'unFOR r IN requête) doivent exister ; une table modifiée ensuite n'est pas relue (recréer la routine). De même,TG_TABLE_NAMEest fixé à la création du déclencheur. numericsans précision vautnumeric(38,10)(29.3) : un résultat s'affiche avec dix décimales.- Les comparaisons de textes ignorent la casse (collations de MIRAJ), dans le corps comme ailleurs.
- Les fonctions propres à PostgreSQL (
pg_sleep,format…) citées dans un corps ne s'exécutent que sur une instance en langage PostgreSQL ; une instance repassée en langage MIRAJ les refuse à l'exécution. - Un avis
RAISEne remonte pas les champsDETAIL/HINT. - Les variables d'une routine masquent une colonne de même nom dans ses requêtes (PostgreSQL signale l'ambiguïté).
- Pas de comparaison avec un serveur PostgreSQL de référence pour ce corpus (le serveur local n'était pas joignable sans modifier sa configuration) : les comportements décrits suivent la documentation de PostgreSQL 16.
29.14 Pilote ODBC et bibliothèque Python#
Le pilote MIRAJ ODBC Driver (chapitre 28) et la bibliothèque Python miraj (26.12) se connectent aussi à une instance PostgreSQL, sans changement d'API, par le même client PostgreSQL que les outils (29.12) :
- Choix du protocole : mot-clé
PROTOCOLde la chaîne de connexion ou de la source ODBC, paramètreprotocoldemiraj.connect();auto(défaut),mirajoupostgresql, avec la même détection que les outils. - Paramètres : les marqueurs de l'application (
?d'ODBC,%s/%(nom)sde Python) deviennent$1,$2… d'une instruction préparée nommée du serveur, réutilisée (SQLPreparepuisSQLExecuterépétés,executemany) ; valeurs envoyées en texte avec leur type. - Résultats : colonnes décrites avec leur type PostgreSQL, leur précision et leur échelle, leur table et leur colonne d'origine, leur nullabilité et l'auto-incrément (lus dans
pg_attribute) ;SQL_ATTR_MAX_ROWSlimite les lignes envoyées par le serveur. - ODBC :
SQLGetInfoannoncePostgreSQLet la citation"; les fonctions de catalogue rendentTABLE_CAT=mirajet la base MIRAJ enTABLE_SCHEM;SQLCancelenvoie une demande d'annulation (CancelRequest), l'instruction échoue enHY008. - Python :
mogrifyécrit les littéraux de ce langage ;copy_from,copy_toetcopy_expertpassent parCOPY … FROM STDIN/TO STDOUT;booletjsondécodés, vecteur en texte[x,y,…];lastrowidvautNone(INSERT … RETURNING). - Erreurs : SQLSTATE de PostgreSQL, sans code MIRAJ.
Les pilotes PostgreSQL tiers (psqlODBC, psycopg2/psycopg) fonctionnent aussi.