30. Langage PostgreSQL
Une instance démarrée avec l'élément POSTGRESQL de sql_mode (voir 16.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.
Ce chapitre est une liste condensée. Le manuel du langage PostgreSQL décrit le même langage en détail, chapitre par chapitre (prise en main, types, DDL, DML, requêtes, fonctions, transactions, comptes, PL/pgSQL, catalogue et outils), avec des exemples complets.
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).
30.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.
Un nom de colonne (ou d'index, ou un alias) admet les lettres et chiffres non ASCII. Écrit sans guillemets, un nom qui porte une lettre non ASCII est replié en minuscules, comme PostgreSQL en UTF-8 (PRÉNOM nomme prénom) ; entre guillemets il est gardé, et la casse de ses lettres non ASCII compte ("Âge" n'est pas âge). Les noms de tables, de vues, de séquences, de schémas et de routines restent ASCII (42000), ainsi que tout nom portant un signe combinant ou une ponctuation.
30.2 Expressions et requêtes#
| Forme PostgreSQL | Lue comme |
|---|---|
a || b | concaténation (CONCAT(a, b) : NULL si l'un est NULL ; concat(…) écrit en fonction ignore les NULL, comme concat_ws) ; un booléen s'y écrit true / false, et t / f dans concat(…) et concat_ws(…) écrits en fonctions |
a ~ 'motif', ~*, !~, !~*, a OPERATOR(pg_catalog.~) b | REGEXP / NOT REGEXP |
a ILIKE 'x%', NOT ILIKE, ~~* | LIKE sous la collation utf8mb4_general_ci : la casse et les accents sont ignorés, alors que LIKE et ~ les respectent (collation « C », voir 30.3) |
x::type, CAST(x AS type) | conversion, types du tableau 30.1 |
int4 '5', timestamptz '…', bytea '\x00', boolean 't' | littéral typé |
INTERVAL '1 day', '2 hours 30 minutes'::interval, INTERVAL '3' DAY | intervalle : mois, jours et durée, écrit comme PostgreSQL (1 mon 2 days 03:00:00) |
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 | un interval, écrit 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) |
doc ? 'clé', doc ?| '{a,b}', doc ?& '{a,b}' | existence d'une clé d'objet, d'un élément chaîne d'un tableau ou d'un scalaire chaîne égal (jsonb seulement : 42883 sur un json) ; ?| : l'une des clés, ?& : toutes. ? n'est l'opérateur qu'écrit après un opérande (nom, valeur, ), ], paramètre) ; là où une valeur est attendue (début d'expression, après un opérateur, une virgule, ( ou un mot-clé comme WHERE, AND, LIMIT, THEN), il reste le paramètre de l'API embarquée : d ? ? compare d au premier paramètre |
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 "POSIX", COLLATE ucs_basic ; COLLATE pg_catalog.default | comparaison octet par octet (utf8mb4_bin) ; 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 30.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 :
/entre deux entiers est une division entière tronquée vers zéro (7 / 2vaut 3,-7 / 2vaut -3) ; avec unnumeric, le quotient a l'échelle de PostgreSQL (7 / 2.0vaut3.5000000000000000) ;- une division ou un modulo par zéro (
1 / 0,5 % 0,x / 0.0,mod(x, 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 ; le texte d'un entier peut s'écrire'0x1F','0o17','0b101','1_000'('0x1F'::intvaut 31), comme le nombre écrit sans guillemets :0x1F,0o17,0b101et1_000y sont des entiers (31, 15, 5 et 1000, et non une suite d'octets comme en langage MIRAJ), le souligné sépare aussi les chiffres d'un décimal ou d'un exposant (1_000.5,1e1_0), une forme invalide (1__0,1_,0x) est une erreur de syntaxe (42601) ;'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 ;trim,btrim,ltrim,rtrim: pour le texte et pourbyteaseulement (trim(42):42883,function btrim(integer) does not exist) ; sur unbytea(btrim(octets, octets),trim(LEADING octets FROM octets)), des octets sont retirés et le résultat est unbytea;- mauvais nombre d'arguments :
42883, au message de PostgreSQL (function btrim(unknown, unknown, unknown) does not exist).
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) ; une colonne jsonb range le document normalisé (voir 30.3) ; une colonne bpchar sans longueur range le texte sans ses espaces finaux. Une valeur par défaut écrite en chaîne est lue de même dans le type de la colonne : boolean DEFAULT 'yes', jsonb DEFAULT '{"b": 1, "a": 2}' (normalisée, relue '…'::jsonb).
Appels et priorités :
::lie plus fort que le moins unaire :-3.7::intvaut-(3.7::int), soit-4(de même en langage MIRAJ, où::est une extension) ;- arguments nommés d'une fonction stockée,
f(a => 1, b => 2)ouf(a := 1), après d'éventuels arguments positionnels ; un paramètre omis prend sa valeur par défaut (nom inconnu ou donné deux fois :42883), de même dansCALL p(a => 1); refusés pour une fonction du serveur (0A000) ; - nom de fonction entre guillemets, avec ou sans schéma :
"f"(1); lastval(): dernière valeur servie à la session parnextvalou par une colonneserial/IDENTITY(55000si aucune) ;currval('s')avant toutnextval('s')de la session :55000aussi (NULL en langage MIRAJ) ;- fonction dans un
FROM,SELECT * FROM f(…) [[AS] alias [(colonne)]](ce qu'envoiecursor.callprocde psycopg2) : une ligne d'une colonne, nommée par l'alias, sinon par la fonction ; elle voit les tables qui la précèdent (LATERALadmis devant elle, en première position aussi). Plusieurs lignes :generate_series,unnest(ARRAY[a, b, …], tableau en texte'{1,2}'::int[], paramètre lié),regexp_matches,regexp_split_to_table,json[b]_each,json[b]_each_text,json[b]_array_elements[_text]etjson[b]_object_keys(ces fonctions JSON pouvant citer les tables qui les précèdent ; les bornes degenerate_series, non :42703), avecWITH ORDINALITY [AS alias (colonne, rang)], et les fonctions stockées ensemblistes (RETURNS SETOF,RETURNS TABLE, voir 30.13 « Fonctions qui rendent des lignes ») à arguments constants (liste de définitionsAS t (a int, …),WITH ORDINALITYaprès une fonction stockée :0A000) ; - fonctions ensemblistes dans la liste du
SELECT(SELECT generate_series(1, 3),unnest(ARRAY[…]),regexp_matches,regexp_split_to_table,json[b]_array_elements[_text],json[b]_object_keys) : une ligne par valeur, comme PostgreSQL 10 et suivants ; plusieurs fonctions d'une même liste avancent ensemble, la plus longue fixant le nombre de lignes ; pas dans une liste regroupée, agrégée ou fenêtrée (0A000) ; - alias de colonnes
AS x (a, b)après toute source duFROM(table, vue, expressionWITH, table dérivée,VALUES, fonction) : les premières colonnes sont renommées, les suivantes gardent leur nom ; trop d'alias :42P10; - tableaux d'une expression :
ARRAY[…],'{…}'::type[],tableau[n],array_agg, rendus sous la forme texte de PostgreSQL ({1,2,NULL}) et annoncés sous le type tableau de leurs éléments (int4[],text[]…) ; une colonne ne peut pas être de type tableau (30.8) ; ONLY table,ONLY (table)ettable *dansFROM,UPDATE,DELETE,TRUNCATE,LOCKetALTER TABLE(la forme depg_dump) : sans effet, MIRAJ n'ayant pas d'héritage de tables ;GROUP BYsur la clé primaire entière d'une table : ses autres colonnes sont admises hors agrégat (dépendance fonctionnelle),t.*compris ; pas pour une vue, une table dérivée, une contrainteUNIQUEni une clé citée en partie (42803).
Clauses de SELECT :
SELECT DISTINCT ON (expr, …): première ligne de chaque groupe, dans l'ordre de l'ORDER BY, qui doit commencer par ces expressions (42P10sinon) ;LIMIT n,LIMIT ALL,OFFSET n [ROWS](seul ou avantLIMIT),FETCH {FIRST | NEXT} [n] {ROW | ROWS} {ONLY | WITH TIES}(WITH TIESsansORDER BYau même niveau :42601) ;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 ; 22P02 sinon), y compris un texte calculé ((doc ->> 'actif')::boolean) |
float4, real, float8, double precision | DOUBLE |
numeric(p,s), decimal(p,s) ; numeric seul | DECIMAL(p,s) ; DECIMAL(38,18), valeur écrite sans zéros finals ('3.5'::numeric : 3.5) |
text, varchar[(n)], char[(n)], bpchar, name, uuid, regclass, regtype… | texte (CHAR[(n)]) ; char sans longueur coupe à un caractère, bpchar garde tout le texte ; un booléen s'y écrit true / false (true::text) |
boolean, bool | booléen : un texte ('yes', 'off'… ; 22P02 sinon) ou un entier ; un numeric, un flottant, une date ou une heure ne se convertissent pas (2.5::boolean : 42846) |
date, time[(p)], timetz | DATE, TIME ; le décalage d'un texte converti ('10:00:00+02'::timetz) ou écrit dans une colonne time ou timetz est retiré ; les formes libres de date du langage MIRAJ ('2020_01_01', '2020.01.01 10.00.00', '20200101100000') restent refusées (22007), '20200101' est lu |
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 ; ::jsonb normalise le document, ::json garde le texte écrit |
interval | intervalle ; colonne rangée en texte réécrit, annoncée interval |
vector(n) | vecteur |
Les noms de MIRAJ (SIGNED, DATETIME, BINARY…) restent acceptés, et le préfixe pg_catalog. est ignoré.
30.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 (START WITH n : premier numéro ; autres options de séquence sans effet ; une valeur écrite n'avance pas le compteur) ; ALWAYS est gardé : une valeur écrite y est refusée (428C9) hors OVERRIDING SYSTEM VALUE et COPY |
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,18), valeurs écrites sans zéros finals (1.5, 2) |
text, varchar sans longueur | LONGTEXT |
bpchar sans longueur | LONGTEXT qui garde le nom de son type : bpchar dans SHOW CREATE TABLE et COLUMN_TYPE (dans les deux langages), bpchar (OID 1042, sans modificateur) dans pg_attribute et pour le client, character pour pg_typeof et information_schema.columns ; espaces finaux retirés à l'écriture en langage PostgreSQL |
varchar(n), character varying(n), char(n) et bpchar(n), char et "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 ; un texte portant un décalage, '10:00:00+02', y est refusé : 22007) |
date, json, jsonb, uuid | DATE, JSON, JSONB (colonne JSON qui garde le nom jsonb), UUID |
bit(n), bit | BIT(n), BIT(1) (64 positions au plus) |
bit varying(n), varbit(n) ; bit varying, varbit | VARCHAR(n) ; LONGTEXT, qui gardent leur type : texte de chiffres binaires, varbit(n) / varbit dans SHOW CREATE TABLE et COLUMN_TYPE (dans les deux langages), bit varying (OID 1562, modificateur n) dans pg_attribute et pour le client |
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é). Comme une séquence de PostgreSQL, son compteur n'avance pas pour une valeur écrite, et un numéro tiré par une insertion qui échoue (23505) ou qu'annule un ROLLBACK est consommé.
Collation « C ». Les textes sont comparés et triés octet par octet, comme PostgreSQL en C.UTF-8 (SHOW lc_collate : C) : une colonne créée sans COLLATE dans une base sans interclassement propre est en utf8mb4_bin, de même que la connexion (collation_connection), les conversions ::text et les textes des routines PL/pgSQL ou SQL. 'a' et 'A' sont donc deux valeurs distinctes d'une clé primaire, d'une contrainte UNIQUE, d'un GROUP BY ou d'un DISTINCT, et ORDER BY place les majuscules d'abord ; seuls les espaces de fin sont ignorés par =. ILIKE et ~* ignorent la casse. Une collation de MIRAJ écrite (COLLATE utf8mb4_general_ci, sur une colonne ou dans une expression) reste appliquée. Les tables, routines et déclencheurs créés avant l'adoption de cette collation, et les colonnes des catalogues (sauf pg_settings), restent en utf8mb4_general_ci, insensibles à la casse.
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 6).
Un jsonb est normalisé comme dans PostgreSQL, à l'écriture dans une colonne jsonb (INSERT, UPDATE, COPY), par ::jsonb et dans le résultat des fonctions qui rendent un jsonb : dans chaque objet, une clé répétée ne garde que sa dernière valeur et les clés sont triées par longueur puis par ordre binaire ; l'espacement est canonique ({"a": 1, "b": [1, 2]}) et un nombre écrit avec un exposant est développé (1e2 : 100). '{"a":1,"b":2}'::jsonb = '{"b":2,"a":1}'::jsonb est donc vrai, et un objet ou un tableau écrit en texte face à un jsonb (doc = '{"b":2,"a":1}', doc || '{"c": 3}') est lu en jsonb. Un json garde le texte écrit. Deux jsonb se comparent par valeur, dans l'ordre propre au type (null, chaînes, nombres, booléens, tableaux, objets ; nombres par leur valeur) : '1.0'::jsonb = '1'::jsonb est vrai, '2'::jsonb < '10'::jsonb aussi, et ORDER BY, DISTINCT, GROUP BY, UNION, IN, BETWEEN et une clé de jointure suivent cette comparaison, ainsi qu'une clé primaire et une contrainte UNIQUE (1.0 et 1 y sont un doublon). ||, -, #- et jsonb_set n'existent que pour un jsonb : sur un json, ils sont refusés (42883). Un document rangé avant cette normalisation, ou écrit en langage MIRAJ (où la colonne se comporte comme un JSON et se compare par son texte), garde son texte jusqu'à sa prochaine écriture en langage PostgreSQL ; UPDATE t SET doc = doc::jsonb le normalise.
Une colonne ENUM est annoncée au client sous l'OID de son type énuméré <table>_<colonne>_enum, celui de pg_attribute ; ce type suit la valeur au travers d'une vue, d'une table dérivée, de CASE, de coalesce et de max / min (RowDescription, pg_attribute d'une vue, pg_typeof).
Une colonne de table reprise telle quelle est annoncée dans RowDescription (requête simple et Describe) sous son type déclaré, avec son modificateur, l'OID de sa table et son numéro de colonne : en lecture directe, après DISTINCT, après GROUP BY (clé de groupement, colonne que la clé primaire groupée détermine, *), au travers d'une table dérivée ou d'une expression de table ; au travers d'une vue, la relation annoncée est la vue. UNION, INTERSECT et EXCEPT gardent le type et le modificateur que partagent tous leurs membres, sans relation ; des varchar(n) de longueurs différentes, ou un varchar(n) uni à un texte écrit sans type, sont annoncés varchar sans modificateur, des char(n) de longueurs différentes bpchar sans modificateur. pg_typeof nomme le type déclaré d'une colonne après GROUP BY comme avant. Le langage MIRAJ n'annonce toujours aucune table d'origine après une agrégation ou une union.
Une colonne de vue est annoncée comme la requête qui définit la vue l'annonce quand on l'exécute directement (type, modificateur, pg_typeof ; la relation annoncée est la vue), que le résultat gardé de la vue soit servi ou sa requête rejouée, quel que soit le fuseau de la session ; pg_attribute et information_schema.columns décrivent la vue de même. Une colonne calculée suit le type de son expression (v || 'z' : text), une conversion écrite garde son type (::int2, ::real, ::varchar(5), ::char(3), ::timestamptz(0)…, que pg_get_viewdef rend sous son nom), une vue lue par une autre vue garde le type déclaré de la colonne de base. Le modificateur d'une conversion écrite (::numeric(10,2), ::varchar(5)) n'est pas annoncé (PostgreSQL l'annonce), dans une requête comme au travers d'une vue ; celui d'un ::char(n) l'est.
Une valeur annoncée character(n) (colonne char(n) lue directement, après GROUP BY ou DISTINCT, au travers d'une vue, d'une table dérivée ou d'une CTE ; conversion ::char(n)) est envoyée au client complétée par des espaces jusqu'à n caractères, en texte, en binaire et par COPY … TO STDOUT, comme PostgreSQL l'affiche. Le moteur la range et la calcule sans ses espaces finaux : length, ||, ::text et les comparaisons les ignorent ('ab '::char(5) || '|' vaut ab|), octet_length et LIKE / ILIKE les lisent (octet_length('ab'::char(5)) vaut 5, 'ab'::char(5) LIKE 'ab' est faux, LIKE 'ab ' et LIKE 'ab%' sont vrais). L'interface embarquée rend la valeur sans complément ; max, coalesce et CASE d'un char(n) sont annoncés text, sans complément ; concat, format, to_json, array_agg et ~ lisent la valeur sans ses espaces de remplissage. Un bpchar sans longueur n'est jamais complété. Le langage MIRAJ lit toujours un CHAR(n) sans espaces finaux.
Une colonne bit(n) est annoncée bit (OID 1560, modificateur n) et ses valeurs s'écrivent en chiffres binaires sur n positions, poids fort d'abord (101, 00000101 pour un bit(8)), dans un résultat comme dans COPY … TO. Elle reçoit une chaîne de n chiffres binaires ('101', B'101', X'1F', ou hexadécimale 'x1F' ; un autre nombre de positions est refusé, SQLSTATE 22026), en valeur écrite, en paramètre ou par COPY … FROM, ou un entier qui tient sur n positions (22026 sinon). Une colonne bit varying(n) garde au plus n chiffres (22001 au-delà) et est annoncée bit varying (OID 1562).
Dans une expression, une chaîne de bits est le texte de ses chiffres, annoncé bit ou bit varying : littéraux B'101' et X'1F', conversions ::bit(n), ::bit varying(n), ::integer et ::text, opérateurs &, |, #, ~, <<, >> (même longueur exigée pour les trois premiers, 22026) et ||, fonctions length, bit_length, octet_length, bit_count, get_bit, set_bit, substring, position, overlay. max, min, coalesce, nullif, CASE, greatest, une union, une vue, une table dérivée et pg_typeof gardent le type ; une colonne bit se compare à une chaîne de bits ou à un texte lu de même (b = '101'), position par position. Une colonne bit(n) lue seule, triée, regroupée ou comparée à une chaîne de n positions reste l'entier que range le moteur (les index servent) ; un calcul la lit sur ses n positions. Le texte canonique d'une vue ou d'une colonne générée garde ces formes en appels internes (pg_bit('101'), pg_bit_cast(x, n)), relus par les deux langages et réaffichés B'101', cast(x as bit(n)) en langage PostgreSQL.
Une chaîne de bits n'entre que dans une colonne bit(n) ou bit varying : toute autre colonne (bytea, text, entier, numeric, boolean, date, jsonb, uuid…) la refuse (42804, column "b" is of type bytea but expression is of type bit) par INSERT … VALUES, INSERT … SELECT, UPDATE, ON CONFLICT DO UPDATE et en valeur par défaut ; il faut la convertir (b::text, b::integer). En particulier X'DEADBEEF' n'est pas une suite d'octets en langage PostgreSQL : un bytea s'écrit '\xDEADBEEF' ou decode('DEADBEEF', 'hex').
La valeur par défaut d'une colonne bytea (DEFAULT '\x0aff'::bytea, DEFAULT '\x0aff') est gardée en suite d'octets ((X'0AFF') dans le texte de MIRAJ, '\x0aff'::bytea à l'affichage en langage PostgreSQL) ; une table créée avant cette règle garde son défaut tel qu'il a été rangé, jusqu'à ALTER TABLE … ALTER COLUMN … SET DEFAULT.
Écarts : un bit(n) a 64 positions au plus ; un entier reste admis dans une colonne bit(n) et s'y compare (b = 5) ; une chaîne de bits calculée gardée par CREATE TABLE … AS SELECT ou par une vue fait une colonne bit varying ; bit_and, bit_or, bit_xor et to_json d'une colonne bit(n) rendent un nombre ; une colonne bit varying ne reçoit qu'une chaîne de bits (un nombre, ou une colonne bit(n) lue sans conversion par INSERT … SELECT, est refusé : 42804). En langage MIRAJ, rien ne change : BIT(n) y est un entier, X'0F' une suite d'octets, et une colonne bit varying s'y lit comme le texte de ses chiffres.
30.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 SCHEMA publicrend42P06(publicdésigne le schéma courant, il existe toujours ;IF NOT EXISTS: sans effet). Les éléments de schéma écrits à la suite (CREATE SCHEMA s CREATE TABLE … CREATE VIEW … CREATE INDEX … CREATE SEQUENCE … CREATE TRIGGER … GRANT …) sont créés dans le schéma, tout ou rien ; tout autre élément est refusé (0A000).AUTHORIZATION compteaccorde au compte tous les privilèges sur le schéma (MIRAJ n'a pas de propriétaire) ; sans nom, le schéma porte celui du compte.- Séquences :
DROP SEQUENCEd'une séquence citée par unDEFAULT nextval(…)est refusé (2BP01) sansCASCADE, qui retire ces valeurs par défaut ;ALTER SEQUENCE … INCREMENT BY(et les autres options) vaut dès lenextvalsuivant, qui rend la dernière valeur servie plus le nouveau pas. - Index : un nom d'index écrit est unique dans son schéma (
42P07pour un second, comme pour toute relation de PostgreSQL ; l'index implicite d'une colonneUNIQUE, nommé comme la colonne, ne compte pas) ; un index créé sur la colonne fille d'une clé étrangère se retire comme un autre (la clé reste contrôlée). 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"sans effet (c'est la collation par défaut des textes, voir 30.3). DEFAULT 'texte'::character varying,DEFAULT now(),DEFAULT CURRENT_TIMESTAMP,DEFAULT 'a' || 'b': la valeur est gardée en texte MIRAJ.DEFAULT ma_fonction()(fonction stockée, seule ou dans une expression) : fonction résolue à la création (42883si elle n'existe pas), exécutée pour chaque ligne écrite qui ne fournit pas la colonne (INSERTd'une ou plusieurs lignes,COPY,UPDATE … SET c = DEFAULT,ON CONFLICT DO UPDATE SET c = DEFAULT, lignes existantes d'unALTER TABLE … ADD COLUMN), en lecture seule (une fonction qui écrit :0A000), les tables qu'elle lit verrouillées avec l'instruction ;DROP FUNCTIONrefusé (2BP01) sansCASCADE, qui retire ces valeurs par défaut. Le langage MIRAJ refuse une fonction stockée en valeur par défaut (3770).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 continue, saufRESTART IDENTITY; dans un bloc, les tables sont verrouillées enACCESS EXCLUSIVEjusqu'à la fin de la transaction et vidées en bloc sous elle (ROLLBACKetROLLBACK TO SAVEPOINTrendent les lignes, avec le compteur), table disque et table partitionnée de l'édition Cluster comprises (lignes supprimées une à une pour une table partitionnée dont une partition est tenue par un autre nœud).CREATE TEMP TABLE … ON COMMIT DELETE ROWS: table temporaire vidée à chaque validation (ON COMMIT PRESERVE ROWS: le défaut) ;ON COMMIT DROP: table temporaire supprimée à la fin de la transaction, validée ou annulée (hors d'un bloc, à la fin de l'instruction).CREATE [OR REPLACE] VIEW,CREATE INDEX nom ON t (c)(etUSING btree),DROP TABLE … CASCADE.CREATE INDEX … (c [ASC | DESC] [NULLS FIRST | NULLS LAST]): ordre et place des NULL lus sans effet (l'index est croissant ; l'ordre d'un résultat reste celui que demande la requête).ALTER TABLE ONLY t …,ALTER TABLE t * …: commeALTER TABLE t ….- Comptes : options de rôle de PostgreSQL traduites dans les comptes, rôles et privilèges de MIRAJ —
CREATE USER nom [WITH] PASSWORD '…',CREATE ROLE nom LOGIN PASSWORD '…'(comptenom@'%'),CREATE ROLE nom [NOLOGIN](rôle),ALTER USER | ROLE nom [WITH] PASSWORD '…',LOGIN/NOLOGIN(ACCOUNT UNLOCK | LOCK),IN ROLE(GRANT rôle TO nom),SUPERUSER(GRANT ALL ON *.* … WITH GRANT OPTION),CREATEDB(GRANT CREATE ON *.*),CREATEROLE(GRANT CREATE USER ON *.*),ALTER … NOSUPERUSER | NOCREATEDB | NOCREATEROLE(leREVOKE … ON *.*correspondant),ALTER ROLE r LOGIN(le rôle devient un compte),DROP ROLEd'un compte comme d'un rôle ; une option sans équivalent (VALID UNTIL 'date',CONNECTION LIMIT n,NOINHERIT,REPLICATION,BYPASSRLS, mot de passe déjà haché,ALTER ROLE … RENAME TO,… SET paramètre) est refusée (0A000, option nommée), jamais ignorée. Détail : 39.2 du manuel PostgreSQL.
30.5 Session et transactions#
| Instruction | Effet |
|---|---|
SET [SESSION | LOCAL] nom {TO | =} valeur[, …], SET nom TO DEFAULT | variable de session (lock_wait_timeout, autocommit…) ou paramètre de PostgreSQL ; une liste devient un texte a, b ; défait avec la transaction annulée et par ROLLBACK TO SAVEPOINT ; SET LOCAL : jusqu'à la fin de la transaction (hors transaction : avertissement 25P01, sans effet) ; tous les paramètres de configuration de PostgreSQL sont reconnus (mémorisés sans effet quand ils sont sans objet), un autre nom : 42704 |
SET monappli.x TO 'v', SHOW monappli.x, current_setting('monappli.x') | paramètre personnalisé (nom à point), mémorisé pour la session |
SET default_transaction_read_only, default_transaction_isolation, lock_timeout, idle_in_transaction_session_timeout | mode et niveau des transactions suivantes (transaction_read_only, transaction_isolation de la session) ; attente d'un verrou (55P03, à la place de lock_wait_timeout ; 0 : sans limite) ; session inactive dans une transaction fermée par le serveur réseau (25P03) |
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 des options de la connexion si elles posent le paramètre, sinon valeur par défaut (variable de MIRAJ : valeur courante du serveur) ; RESET ALL : tous les paramètres, schéma courant compris (celui de la connexion ou du dernier USE) |
SET session_replication_role TO replica | déclencheurs de l'utilisateur et clés étrangères suspendus pour la session (comptes SUPER ou SYSTEM_VARIABLES_ADMIN) ; origin, local, RESET : rétablis |
SET shared_buffers, max_connections, fsync… | paramètres du démarrage ou de la configuration du serveur PostgreSQL : 55P02 |
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 par défaut, comme PostgreSQL (SHOW default_transaction_isolation, current_setting, RESET ALL de même), repeatable read… ; le niveau annoncé ne relâche rien : toute transaction lit un instantané fixe (38.3 du manuel PostgreSQL) |
COMMIT [TRANSACTION], END, ROLLBACK [TRANSACTION], ABORT, SAVEPOINT s, RELEASE [SAVEPOINT] s, ROLLBACK TO [SAVEPOINT] s | fin de transaction, points de sauvegarde : ceux de même nom s'empilent, 25P01 hors transaction, 3B001 pour un nom inconnu |
BEGIN; CREATE TABLE …; ROLLBACK | CREATE TABLE (hors AS), CREATE [UNIQUE] INDEX, ALTER TABLE … ADD COLUMN (sans REFERENCES), ADD CONSTRAINT nom UNIQUE, DROP TABLE (sans CASCADE ni vue ou clé étrangère qui la cite) et TRUNCATE d'un bloc de transaction sont défaits par ROLLBACK et ROLLBACK TO SAVEPOINT ; les autres DDL valident d'abord la transaction (détail : 34.11 du manuel PostgreSQL) |
DISCARD ALL, DEALLOCATE [PREPARE] {nom | ALL} | instructions préparées libérées ; DISCARD ALL ferme aussi curseurs, tables temporaires et abonnements LISTEN, et rend aux paramètres leurs valeurs du début de la session, schéma courant compris |
DECLARE c [SCROLL | NO SCROLL] CURSOR [WITH | WITHOUT HOLD] FOR requête, FETCH [sens] [FROM | IN] c, MOVE [sens] c, CLOSE {c | ALL} | curseur SQL (curseurs nommés de psycopg2) ; WITHOUT HOLD exige un bloc de transaction ; tous les sens de PostgreSQL (NEXT, PRIOR, FIRST, LAST, ABSOLUTE n, RELATIVE n, n, ALL, FORWARD / BACKWARD [n | ALL]) ; un curseur NO SCROLL refuse tout recul (55000), SCROLL et NO SCROLL ensemble : 42P11 (38.10 du manuel PostgreSQL) |
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 16.10.4).
30.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 (v [classe]):vector_l2_opspar défaut,vector_cosine_ops,vector_ip_ops), 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(la session fermée reçoit l'erreur fatale57P01de PostgreSQL avant la fin de sa connexion ;57P05au délai d'inactivité),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.
30.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) |
30.8 Pas encore pris en charge#
| Forme | Réponse |
|---|---|
CREATE EVENT, ALTER EVENT | 0A000 : pas d'événement planifié en langage PostgreSQL (30.13) ; routines et déclencheurs : 30.13 |
colonne de type tableau (int[]), tableau à plusieurs dimensions, ARRAY(SELECT …), || entre tableaux | 0A000 (les tableaux d'une expression, ARRAY[…] et '{…}'::type[], sont pris en charge : voir 30.2) |
WITH RECURSIVE dans une vue | 0A000 |
COPY binaire, COPY … FROM un fichier du serveur, COPY … PROGRAM, COPY préparé, ON CONFLICT sur une expression | 0A000 ; voir 30.11 |
alias de colonnes d'une table (FROM t AS x(a, b)) ou d'une requête dont la liste contient *, dans une vue ou une autre définition gardée | 0A000 (admis dans une requête) |
COMMENT ON d'un autre objet qu'une table ou une colonne, DROP INDEX a, b | 0A000 |
30.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).
Vues sur le catalogue : une vue peut lire pg_catalog et information_schema (CREATE VIEW v AS SELECT relname FROM pg_class…), seule, jointe à des tables, au travers d'une autre vue ou d'une expression WITH ; pg_get_viewdef, pg_attribute et information_schema.columns la décrivent (colonnes du type de celles du catalogue : name, oid, "char"). Sa requête est rejouée à chaque lecture, pour le compte qui la lit : aucun résultat n'en est gardé, CREATE CACHED VIEW compris (le catalogue change sans qu'une table soit écrite, et chaque compte n'y voit que ses objets) ; REFRESH VIEW y est sans effet. Une vue sur pg_authid ou pg_shadow reste réservée à qui administre les comptes, à la création comme à la lecture (42501).
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, a pour GENERATED ALWAYS AS IDENTITY, 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) ; rolsuper, rolcreatedb, rolcreaterole d'après ses privilèges globaux (tous sur *.*, CREATE, CREATE USER) ; pg_auth_members : rôles accordés (admin_option ; inherit_option = rôle par défaut) ; rolpassword toujours masqué (******** dans pg_roles et pg_user, NULL dans pg_authid), jamais le vérificateur SCRAM (voir 16.10.2) ; pg_authid réservée au privilège CREATE USER (42501) |
pg_description | commentaires des tables et des colonnes (COMMENT ON), des routines créées en langage MIRAJ avec leur clause COMMENT |
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 <architecture>-<système>, 64-bit |
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 |
pg_cancel_backend(pid), pg_terminate_backend(pid [, délai]) | comme KILL QUERY pid et KILL pid, avec leurs droits : true ; false avec un avertissement pour un pid inconnu, 42501 sans droit |
format_type(oid, typmod), pg_typeof(x) | character varying(80), numeric(12,2), timestamp without time zone… ; pg_typeof rend le type déclaré d'une colonne (uuid, jsonb, character, le type énuméré <table>_<colonne>_enum), boolean pour une condition, jsonb / json / uuid / le type énuméré pour une valeur calculée de ce type (CASE, coalesce, max, min, colonne d'une vue ou d'une table dérivée) |
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_column_privilege, has_any_column_privilege, has_schema_privilege, has_function_privilege | privilèges réels du compte de la session (rôles actifs compris) ; ceux d'un autre compte pour qui administre les comptes (42501 sinon) ; un objet que le compte ne voit pas est inconnu (42P01, 3F000, 42883) ; has_database_privilege, has_sequence_privilege, has_type_privilege : toujours true |
pg_has_role([compte,] rôle, 'MEMBER' | 'USAGE' | 'SET') | le rôle est accordé au compte, directement ou par un autre rôle (USAGE : par les rôles actifs de la session, par les rôles par défaut d'un autre compte) ; rôle inconnu : 42704 |
pg_relation_size, pg_total_relation_size, pg_table_size, pg_indexes_size, pg_database_size, pg_size_pretty | tailles en mémoire, écrites comme PostgreSQL (20 kB) ; pg_indexes_size : taille estimée des index, comptée par pg_total_relation_size ; la relation se donne par son OID ou par son nom en texte (pg_relation_size('t'), inconnu : 42P01) |
pg_encoding_to_char, quote_ident, quote_literal, quote_nullable | |
current_setting(nom [, absent_ok]), set_config(nom, valeur, local) | paramètres ; set_config pose la valeur comme SET (SET LOCAL si local), à la fin de l'instruction qui l'appelle ; 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 ; pour to_char seulement : FF1…FF6, RM, TZ et OF, fuseau de la session pour un timestamptz) et de nombres (9, 0, ., D, ,, G, S, MI, L, FM, et pour to_char PR, RN, EEEE, V) ; to_number(texte, modèle) |
make_interval(…), make_timestamptz(…), timezone(zone, t), date_bin, date_add, date_subtract, isfinite, timeofday | dates et intervalles ; paramètres nommés admis (make_interval(days => 3)) |
age(a, b), age(t), justify_days, justify_hours, justify_interval | intervalles (2 years 7 mons 6 days) |
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 |
regexp_match, regexp_matches, regexp_split_to_array, regexp_split_to_table, regexp_count, regexp_instr, regexp_substr, regexp_like | sémantique de PostgreSQL 16 ; tableaux rendus en text[] ; regexp_matches et regexp_split_to_table rendent des lignes (30.2) |
sha224, sha256, sha384, sha512, get_byte, set_byte, convert_to, convert_from, unistr, normalize, parse_ident | octets, encodages (UTF8, LATIN1, WIN1252, SQL_ASCII), textes Unicode |
uuid_generate_v4, digest, crypt, gen_salt, gen_random_bytes, unaccent, similarity, levenshtein, levenshtein_less_equal, soundex, difference | fonctions des extensions uuid-ossp, pgcrypto, unaccent, pg_trgm et fuzzystrmatch, toujours présentes (CREATE EXTENSION accepté sans effet) |
jsonb_extract_path[_text], jsonb_insert, jsonb_strip_nulls, jsonb_pretty, jsonb_path_exists, jsonb_path_match, jsonb_path_query_array, jsonb_path_query_first | JSON, chemins SQL/JSON (lax, strict, variables, filtres) |
pg_notify, pg_advisory_xact_lock, pg_column_size, pg_input_is_valid, txid_current_snapshot, pg_postmaster_start_time, pg_reload_conf, to_regrole | fonctions du système |
cbrt, scale, min_scale, trim_scale, setseed, random_normal | nombres |
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 ; les formes jsonb rendent un jsonb normalisé (OID 3802), les formes json un json dans l'ordre écrit |
jsonb_set(d, chemin, valeur [, créer]), jsonb_array_length, jsonb_typeof, json_typeof | chemin écrit en tableau ('{a,0}') ; jsonb_set rend un jsonb et n'existe pas pour un json (42883) |
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) ; jsonb_agg et jsonb_object_agg rendent un jsonb, bool_and, bool_or et every un boolean, même sur une condition |
array_agg(x [ORDER BY …]) | tableau des valeurs du groupe (DISTINCT, FILTER admis), annoncé du type tableau de ses valeurs ; array_agg(…) OVER (…) : 0A000 |
corr, covar_pop, covar_samp, regr_slope, regr_intercept, regr_r2, regr_count, regr_avgx, regr_avgy, regr_sxx, regr_syy, regr_sxy | agrégats statistiques sur les paires non NULL |
percentile_cont, percentile_disc, mode(), rank, dense_rank, percent_rank, cume_dist avec WITHIN GROUP (ORDER BY …) | agrégats à tri interne ; fraction hors de 0..1 : 22023 ; fractions en tableau : un tableau de quantiles |
mod(a, b), ascii(texte) | mod(a, 0) : 22012 ; ascii rend le point de code Unicode du premier caractère (ascii('é') : 233) |
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. ::regproc ne connaît que les routines stockées, seules lignes de pg_proc : le nom d'une fonction du serveur ('length'::regproc) donne 42883, et to_regproc('length') NULL.
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. L'index au nom écrit l'emporte sur un index implicite de même nom ; un nom porté par deux tables au même titre (index créés en langage MIRAJ) est refusé comme ambigu (42710) : écrireDROP INDEX nom ON table.COMMENT ONporte sur une table ou une colonne ; un commentaire de colonne redéfinit la colonne (la table est réécrite). Sur un autre objet (COMMENT ON FUNCTION,ON INDEX…) :0A000.
30.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. Les écritures au travers de la vue suivent aussi cette règle : dans un UPDATE ou un DELETE, la condition de la vue et ses colonnes calculées gardent le langage de la vue, tandis que ce que l'instruction écrit elle-même (SET, WHERE) suit le langage de l'instance. Avec une vue v créée en langage PostgreSQL par SELECT id, a, n, length(a) AS l FROM t WHERE strpos(a, 'x') = 0, l'instruction UPDATE v SET n = length(a) WHERE l = 3, exécutée dans l'instance rouverte en langage MIRAJ, ne touche que les lignes sans x de trois caractères (strpos et l de la vue), et y range un nombre d'octets (length de l'instruction). Il en va de même d'une vue qui lit une vue créée dans l'autre langage : chacune garde le sien.
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. La clause PARTITION BY d'une table partitionnée suit la définition, dans la syntaxe de MIRAJ (identifiants entre guillemets doubles, sans commentaire versionné ni mention du moteur). SHOW CREATE SEQUENCE, SHOW CREATE ENDPOINT, SHOW CREATE USER et SHOW GRANTS rendent leur texte avec la lexicographie de PostgreSQL (identifiants "…", chaînes standard ; le compte de SHOW CREATE USER s'écrit "app"@"%", comme celui de SHOW GRANTS) : ces lignes se rejouent telles quelles sur une instance PostgreSQL (miraj-dump --users). SHOW TRIGGERS et information_schema.triggers montrent un déclencheur écrit en PL/pgSQL sous son nom, avec EXECUTE FUNCTION f(…) pour action. Une routine ou un déclencheur écrit en PL/pgSQL est gardé en texte MIRAJ et son texte d'origine à côté (30.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).
30.11 Écritures : RETURNING, ON CONFLICT et COPY#
Formes de PostgreSQL admises dans les écritures :
WITH … INSERT | UPDATE | DELETE: les expressions de table écrites en tête valent pour toute l'écriture (WITH RECURSIVEcompris) ;- expression de table modifiante,
WITH x AS (INSERT | UPDATE | DELETE … [RETURNING …]) [, …]devant unSELECT, unINSERT, unUPDATEou unDELETE: chaque écriture s'exécute une fois, dans l'ordre écrit, par son chemin ordinaire (droits, déclencheurs, journal) ; son nom désigne les lignes de sonRETURNING, pour l'instruction principale et les écritures suivantes. Atomique hors d'un bloc de transaction (transaction implicite). Seulement dans la clauseWITHqui ouvre l'instruction (sous-requête, vue,CREATE TABLE … AS,EXPLAIN,SELECT … INTO:0A000) ; écart : l'instruction principale voit les tables après les écritures, pas leur état d'avant l'instruction ; INSERT INTO t VALUES (…)ouINSERT INTO t SELECT …sans liste de colonnes, avec moins de valeurs que de colonnes : elles vont aux premières colonnes, les autres prennent leur valeur par défaut ; trop de valeurs, ou des lignes de longueurs différentes :42601;INSERT INTO t AS alias …: alias de la table visée, avec ou sansON CONFLICTetRETURNING;INSERT INTO t [(colonnes)] OVERRIDING {SYSTEM | USER} VALUE …: une colonneGENERATED ALWAYS AS IDENTITYn'admet queDEFAULT(INSERT,UPDATE,ON CONFLICT … DO UPDATE:428C9pour une autre valeur,NULLcompris) ;OVERRIDING SYSTEM VALUEy écrit la valeur donnée,OVERRIDING USER VALUEignore la valeur donnée à une colonne d'identité (pas à une colonneserial, qui n'en est pas une) ;COPY … FROM STDINécrit les valeurs du fichier ;UPDATE … SET id = DEFAULTd'une colonne d'identité lui attribue le numéro suivant ; clause admise aussi dans le corps d'une routine PL/pgSQL ou SQL (le texte MIRAJ gardé ne la lit que dans ce corps traduit, blocpg$body) ; le refus428C9porte sur le protocole PostgreSQL le détail et le conseil dans leurs champs (D,H) ;UPDATE ONLY t,DELETE FROM ONLY t,t *: sans effet.
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.SET (a, b) = (expr, expr)affecte plusieurs colonnes d'une liste de valeurs. Les déclencheursUPDATEde la table s'exécutent,BEFOREetAFTER, même quand aucune valeur ne change ; unWHEREfaux n'en lance aucun.- 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) = (SELECT …); 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'etPROGRAMsont refusés (0A000) ; pour charger un fichier du serveur,LOAD DATA INFILEreste disponible en langage MIRAJ. COPY t | (requête) TO '/chemin'écrit les lignes, au même format queTO STDOUT, dans un fichier du serveur (étiquetteCOPY n, rien n'est envoyé au client), sous les garde-fous deSELECT … INTO OUTFILE: le serveur doit fixer un dossier--secure-file-priv(aucun par défaut : refus42501, même pourroot) ; le compte doit avoir le privilègeFILEau niveau global (GRANT FILE ON *.* TO …), l'équivalent du rôlepg_write_server_files(sans lui :42501« permission denied to COPY to a file », comme PostgreSQL) ; le chemin est absolu (42602sinon) et son dossier, rendu canonique (..et liens résolus), se trouve danssecure_file_priv(42501sinon). Écarts voulus : un fichier existant n'est jamais remplacé (58P02, PostgreSQL l'écrase), le fichier est créé en0600et retiré si l'instruction échoue.
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).
30.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 26) | instructions et fichiers en langage PostgreSQL ; invite base=> ; erreurs avec leur SQLSTATE |
miraj-dump (19.3) | script écrit en langage PostgreSQL, rechargeable par psql -f, miraj-cli ou miraj-dump restore |
miraj-backup (19.2) | BACKUP / RESTORE (extensions de MIRAJ) écrits dans ce langage ; avertissements reçus en avis |
miraj-migrate (chapitre 18) | cible PostgreSQL : définitions récrites dans ce langage, lignes chargées par COPY … FROM STDIN, routines et déclencheurs traduits en PL/pgSQL (30.13) |
| Miraj Server Manager (chapitre 22) | 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 23) | 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 (30.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 (16.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 17.12.
30.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 30.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.
DO [LANGUAGE plpgsql] $$ … $$ exécute aussitôt un bloc PL/pgSQL anonyme, comme le corps d'une procédure sans paramètre, avec les droits de la session et dans la base courante, puis l'oublie : étiquette DO, avis de RAISE, erreur avec son SQLSTATE, bloc entier défait en cas d'erreur hors bloc de transaction du client ; LANGUAGE sql ou un autre langage : 0A000.
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, et avant l'erreur d'une instruction qui échoue ; %% é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.
Fonctions qui rendent des lignes#
RETURNS SETOF type (type de base ou table), RETURNS TABLE (colonne type, …) et les paramètres OUT avec RETURNS SETOF record s'appellent dans un FROM : SELECT * FROM f(1), jointures et sous-requêtes comprises, dans le corps d'une autre routine aussi. Le corps ajoute des lignes par RETURN NEXT expr (une colonne, ou un enregistrement de la table), RETURN NEXT sans expression (valeurs courantes des colonnes de RETURNS TABLE ou des paramètres OUT) et RETURN QUERY requête ; RETURN ou la fin du corps terminent le résultat. Une fonction LANGUAGE sql rend les lignes de sa dernière requête. Les valeurs sont converties aux types déclarés ; une requête dont le nombre de colonnes diffère : 42804, comme RETURN expr, et RETURN NEXT hors d'une fonction ensembliste.
Les lignes sont calculées avant l'instruction, gardées en mémoire. Les arguments doivent être constants (paramètres liés compris) : une colonne d'une autre table du FROM (LATERAL) est refusée (0A000), comme l'appel dans la liste d'un SELECT (SELECT f()), dans une vue, RETURN QUERY EXECUTE et RETURNS SETOF record sans paramètre OUT.
Refusé (0A000)#
Fonction scalaire à 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.
É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, 30.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(routines stockées seulement, voir 30.9).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
EXCEPTIONannule les écritures faites par le bloc avant l'erreur (point de sauvegarde implicite, comme PostgreSQL), sauf dans un déclencheur et pour une routine appelée hors transaction par une session en langage MIRAJ, où seule l'instruction en faute 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,18)(30.3) : la valeur rendue par une fonction (RETURNS numeric) s'écrit, comme une colonnenumeric, sans les zéros finals de sa partie décimale.- Les textes d'une routine ou d'un déclencheur (paramètres, variables, littéraux) sont en collation « C », comme ailleurs en langage PostgreSQL (30.3) : leurs comparaisons tiennent compte de la casse.
- 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.
30.14 Pilote ODBC et bibliothèque Python#
Le pilote MIRAJ ODBC Driver (chapitre 29) et la bibliothèque Python miraj (27.12) se connectent aussi à une instance PostgreSQL, sans changement d'API, par le même client PostgreSQL que les outils (30.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.