40. PostgreSQL : Catalogue et outils
Ce chapitre décrit ce qu'un client voit d'une instance MIRAJ en langage PostgreSQL : le catalogue pg_catalog, information_schema et les instructions SHOW, puis les clients et pilotes (psql, psycopg2, pgjdbc, Npgsql, ODBC, bibliothèque Python miraj), les outils de MIRAJ sur une telle instance et la sauvegarde.
Les exemples portent sur le schéma ventes de la prise en main (tables client, article, commande, ligne_commande), complété d'une vue v_total_commande, d'une séquence seq_facture, d'une fonction ttc et d'un déclencheur trg_maj_stock.
40.1 Modèle : une base, un schéma par base MIRAJ#
| Notion PostgreSQL | Dans MIRAJ |
|---|---|
base de données (pg_database, current_database()) | une seule, miraj (OID 16384, encodage UTF8) |
schéma (pg_namespace) | une base MIRAJ ; s'y ajoutent pg_catalog et information_schema |
schéma courant (current_schema, search_path) | la base courante de la session |
public | pas un schéma : dans un nom (public.client), alias de la base courante |
| propriétaire des objets | root (OID 10) |
SELECT oid, nspname FROM pg_namespace ORDER BY nspname; oid | nspname
------------+--------------------
13183 | information_schema
689122271 | miraj
11 | pg_catalog
1505511691 | ventes
(4 lignes)Le schéma miraj est la base système de MIRAJ : seul root le voit. Un compte ordinaire ne voit que les schémas sur lesquels il a un droit (le compte app de la prise en main voit information_schema, pg_catalog et ventes).
SELECT current_database(), current_schema, current_schemas(true), current_schemas(false); current_database() | current_schema | current_schemas(true) | current_schemas(false)
--------------------+----------------+-----------------------+------------------------
miraj | ventes | {pg_catalog,ventes} | {ventes}
(1 ligne)Identifiants d'objets (OID). Les types de base et les tables du catalogue gardent les OID de PostgreSQL (int4 23, text 25, numeric 1700, pg_class 1259). Les objets que vous créez reçoivent un OID calculé depuis leur genre, leur schéma et leur nom : il ne change pas d'un redémarrage à l'autre, mais renommer un objet change son OID.
Résolution des noms. pg_catalog passe avant tout schéma, comme dans PostgreSQL : SELECT * FROM pg_class lit la table du catalogue, sans préfixe. Une table d'un autre schéma se nomme schéma.table.
Vues sur le catalogue. Une vue peut lire les tables de pg_catalog et d'information_schema, seules, jointes, au travers d'une autre vue ou d'une expression WITH :
CREATE VIEW v_tables AS
SELECT n.nspname AS schema, c.relname AS nom
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r' AND n.nspname = 'ventes';Sa requête est rejouée à chaque lecture, pour le compte qui la lit : elle suit le catalogue sans délai (une table créée y paraît aussitôt) et ne montre à chacun que ce que le catalogue lui montre. Aucun résultat n'en est gardé, même pour une vue créée par CREATE CACHED VIEW ; 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).
Lecture seule. Le catalogue est reconstruit à chaque instruction qui le lit ; rien ne s'y écrit :
INSERT INTO pg_class (relname) VALUES ('x');ERREUR 42501 : permission denied for schema pg_catalog40.2 Le catalogue pg_catalog#
Tables et vues présentes#
Les colonnes sont celles de PostgreSQL 16, dans leur ordre et avec leur type.
| Table ou vue | Contenu | |||
|---|---|---|---|---|
pg_namespace | les schémas visibles du compte, pg_catalog, information_schema | |||
pg_database | la base miraj | |||
pg_class | tables (relkind = r), vues (v), index (i), séquences (S), et les tables du catalogue | |||
pg_attribute, pg_attrdef | colonnes des tables et des vues (type, attnotnull, identité attidentity = d pour un serial ou BY DEFAULT, a pour GENERATED ALWAYS, colonne générée), valeurs par défaut | |||
pg_index, pg_indexes | index : clé primaire <table>_pkey, unicité, index secondaires (btree), plein texte (gin), spatiaux (gist), vectoriels (hnsw) | |||
pg_constraint | contraintes p (clé primaire), u (unicité), f (clé étrangère), c (CHECK) | |||
pg_type, pg_enum, pg_range | types de base et leurs tableaux (92 types), un type énuméré par colonne ENUM ; pg_range vide | |||
pg_proc, pg_language | fonctions (prokind = f) et procédures (p) ; langages internal, c, sql, plpgsql | |||
pg_trigger | déclencheurs, sous leur nom PostgreSQL | |||
pg_sequence, pg_sequences | séquences (CREATE SEQUENCE) ; pg_sequences les présente avec leur schéma, leur propriétaire et leurs bornes | |||
pg_views, pg_tables, pg_matviews | vues usuelles ; pg_matviews vide | |||
pg_description | commentaires des tables et des colonnes (COMMENT ON) | |||
pg_roles, pg_user, pg_authid, pg_shadow, pg_auth_members | un rôle par compte, ses attributs (rolsuper, rolcreatedb, rolcreaterole) et les rôles accordés ; mot de passe toujours masqué ; pg_authid et pg_shadow réservées à qui administre les comptes (42501, permission denied for table pg_authid) | |||
pg_settings | paramètres annoncés aux clients et variables de la session (209 lignes) | |||
pg_stat_activity | sessions ouvertes | |||
pg_am, pg_collation, pg_tablespace, pg_extension | méthodes d'accès (heap, btree, hash, gist, gin, hnsw), collations default / C / POSIX, pg_default / pg_global, extension vector 0.8.0 | pg_inherits, pg_partitioned_table, pg_depend, pg_locks, pg_stat_user_tables, pg_shdescription, pg_policy, pg_publication, pg_publication_rel, pg_publication_namespace, pg_statistic_ext, pg_rewrite, pg_foreign_data_wrapper, pg_foreign_server, pg_foreign_table, 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 tableaux du catalogue (conkey, proargnames…) sont rendus en tableaux ({1,2}), indkey en int2vector (1 2).
Extensions. vector est la seule ligne de pg_extension et n'a pas à être installée. CREATE EXTENSION [IF NOT EXISTS] nom [WITH] [SCHEMA s] [VERSION v] [CASCADE] et DROP EXTENSION [IF EXISTS] nom [, …] sont acceptés sans effet pour plpgsql, pgcrypto, uuid-ossp, unaccent, pg_trgm, fuzzystrmatch, btree_gin, btree_gist et vector, dont les fonctions (gen_random_bytes, uuid_generate_v4, unaccent, similarity, levenshtein…) sont toujours disponibles (36. Fonctions SQL) : un script d'installation écrit pour PostgreSQL passe sans changement.
Objets d'un schéma#
SELECT c.relname, c.relkind, c.relnatts, c.reltuples
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'ventes'
ORDER BY c.relkind, c.relname; relname | relkind | relnatts | reltuples
---------------------+---------+----------+-----------
article_pkey | i | 1 | 3.0
client_pkey | i | 1 | 3.0
code | i | 1 | 3.0
commande_pkey | i | 1 | 3.0
email | i | 1 | 3.0
idx_commande_date | i | 1 | 3.0
ligne_commande_pkey | i | 2 | 4.0
article | r | 5 | 3.0
client | r | 6 | 3.0
commande | r | 4 | 3.0
ligne_commande | r | 4 | 4.0
seq_facture | S | 3 | 1.0
v_total_commande | v | 0 | 0.0
(13 lignes)Les noms sont ceux de MIRAJ : la clé primaire s'appelle <table>_pkey, une contrainte UNIQUE de colonne porte le nom de sa colonne (email, code), une clé étrangère <table>_ibfk_<n>, une contrainte CHECK sans nom CONSTRAINT_<n>, numérotée dans sa table (article et ligne_commande ont chacune leur CONSTRAINT_1).
Colonnes d'une table#
SELECT a.attnum, a.attname, format_type(a.atttypid, a.atttypmod) AS type,
a.attnotnull, a.attidentity, pg_get_expr(d.adbin, d.adrelid) AS defaut
FROM pg_attribute a
LEFT JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
WHERE a.attrelid = 'client'::regclass AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum; attnum | attname | type | attnotnull | attidentity | defaut
--------+---------+-----------------------------+------------+-------------+-------------------
1 | id | integer | t | d |
2 | nom | character varying(80) | t | |
3 | email | character varying(120) | t | |
4 | ville | text | f | |
5 | actif | boolean | t | | true
6 | cree_le | timestamp without time zone | f | | CURRENT_TIMESTAMP
(6 lignes)Dans pg_attribute, une colonne serial apparaît comme une colonne d'identité (attidentity = d), sans valeur par défaut dans pg_attrdef ; une colonne GENERATED ALWAYS AS IDENTITY porte a. information_schema.columns la présente, elle, comme PostgreSQL : nextval('client_id_seq'::regclass) (40.6).
Index et contraintes#
SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'commande' ORDER BY indexname; indexname | indexdef
-------------------+---------------------------------------------------------------------------
commande_pkey | CREATE UNIQUE INDEX commande_pkey ON ventes.commande USING btree (id)
idx_commande_date | CREATE INDEX idx_commande_date ON ventes.commande USING btree (passee_le)
(2 lignes)SELECT conname, contype, conkey, confrelid::regclass AS cible, pg_get_constraintdef(oid) AS definition
FROM pg_constraint
WHERE conrelid = 'ligne_commande'::regclass
ORDER BY conname; conname | contype | conkey | cible | definition
-----------------------+---------+--------+----------+---------------------------------------------------------------------
CONSTRAINT_1 | c | | 0 | CHECK (("quantite" > 0))
ligne_commande_ibfk_1 | f | {1} | commande | FOREIGN KEY (commande_id) REFERENCES commande(id) ON DELETE CASCADE
ligne_commande_ibfk_2 | f | {2} | article | FOREIGN KEY (article_id) REFERENCES article(id)
ligne_commande_pkey | p | {1,2} | 0 | PRIMARY KEY (commande_id, article_id)
(4 lignes)Pour une contrainte qui n'est pas une clé étrangère, confrelid::regclass rend 0.
Colonnes de la clé primaire d'une table :
SELECT a.attname
FROM pg_class c
JOIN pg_index i ON i.indrelid = c.oid AND i.indisprimary
JOIN pg_attribute a ON a.attrelid = c.oid AND a.attnum = ANY (i.indkey)
WHERE c.relname = 'ligne_commande'
ORDER BY a.attnum; attname
-------------
commande_id
article_id
(2 lignes)Tables qui référencent une table :
SELECT conrelid::regclass AS table_enfant, conname, pg_get_constraintdef(oid)
FROM pg_constraint
WHERE contype = 'f' AND confrelid = 'client'::regclass; table_enfant | conname | pg_get_constraintdef(oid)
--------------+-----------------+-----------------------------------------------
commande | commande_ibfk_1 | FOREIGN KEY (client_id) REFERENCES client(id)
(1 ligne)Vues, séquences, routines, déclencheurs#
SELECT pg_get_viewdef('v_total_commande'::regclass); select "o"."id", "o"."client_id", sum(("l"."quantite" * "l"."prix_unit")) as "montant" from "commande" as "o" join "ligne_commande" as "l" on ("l"."commande_id" = "o"."id") group by "o"."id","o"."client_id";La définition est celle que MIRAJ garde : la requête est réécrite, identifiants toujours entre guillemets. pg_get_viewdef la rend précédée d'une espace et terminée par ;, comme PostgreSQL ; pg_views.definition et information_schema.views.view_definition rendent le même texte, sans l'espace ni le ;.
SELECT seqrelid::regclass AS sequence, seqstart, seqincrement, seqmin, seqmax, seqcache, seqcycle
FROM pg_sequence; sequence | seqstart | seqincrement | seqmin | seqmax | seqcache | seqcycle
-------------+----------+--------------+--------+---------------------+----------+----------
seq_facture | 1000 | 1 | 1 | 9223372036854775806 | 1000 | f
(1 ligne)SELECT p.proname, p.prokind, l.lanname, p.provolatile,
pg_get_function_arguments(p.oid) AS arguments, pg_get_function_result(p.oid) AS resultat
FROM pg_proc p
JOIN pg_language l ON l.oid = p.prolang
JOIN pg_namespace n ON n.oid = p.pronamespace
WHERE n.nspname = 'ventes'; proname | prokind | lanname | provolatile | arguments | resultat
-----------+---------+---------+-------------+--------------------------------------------------+---------------
maj_stock | f | plpgsql | v | | trigger
ttc | f | plpgsql | i | ht numeric(10,2), taux numeric(4,2) DEFAULT 0.19 | numeric(10,2)
(2 lignes)SELECT pg_get_functiondef('ttc'::regproc);CREATE OR REPLACE FUNCTION ventes.ttc(ht numeric(10,2), taux numeric(4,2) DEFAULT 0.19)
RETURNS numeric(10,2)
LANGUAGE plpgsql
IMMUTABLE
AS $function$
BEGIN
RETURN round(ht * (1 + taux), 2);
END;
$function$SELECT tgname, tgrelid::regclass AS table, tgfoid::regproc AS fonction, pg_get_triggerdef(oid) AS definition
FROM pg_trigger; tgname | table | fonction | definition
---------------+----------------+-----------+--------------------------------------------------------------------------------------------------------------
trg_maj_stock | ligne_commande | maj_stock | CREATE TRIGGER trg_maj_stock AFTER INSERT ON ventes.ligne_commande FOR EACH ROW EXECUTE FUNCTION maj_stock()
(1 ligne)Les routines et déclencheurs sont décrits au chapitre 39.
Commentaires#
COMMENT ON porte sur une table ou une colonne ; IS NULL retire le commentaire. Un commentaire de colonne redéfinit la colonne : la table est réécrite.
COMMENT ON TABLE client IS 'Clients de la boutique';
COMMENT ON COLUMN client.email IS 'Adresse de contact, unique';
SELECT obj_description('client'::regclass, 'pg_class') AS de_table,
col_description('client'::regclass, 3) AS de_colonne; de_table | de_colonne
------------------------+----------------------------
Clients de la boutique | Adresse de contact, unique
(1 ligne)Rôles#
SELECT rolname, rolsuper, rolcanlogin, rolpassword FROM pg_roles ORDER BY rolname; rolname | rolsuper | rolcanlogin | rolpassword
---------+----------+-------------+-------------
app | f | t | ********
root | t | t | ********
(2 lignes)Un rôle par nom de compte (sans son hôte). Le mot de passe n'est jamais rendu : ******** dans pg_roles et pg_user, NULL dans pg_authid. rolsuper, rolcreatedb et rolcreaterole suivent les privilèges globaux du compte, pg_auth_members les rôles accordés, et pg_has_role les interroge (voir 38.2 et 38.5). Les droits détaillés ne sont pas dans le catalogue (relacl est NULL) : SHOW GRANTS FOR compte les donne (voir 38).
Fonctions d'information#
| Fonctions | Résultat |
|---|---|
version() | PostgreSQL 16.4 (MIRAJ 1.0.0) on x86_64-windows, 64-bit (aarch64-macos, x86_64-linux… selon la plate-forme du serveur) |
current_database(), current_catalog | miraj |
current_schema, current_schemas(bool) | schéma courant ; {pg_catalog,ventes} |
current_user, current_role, session_user, pg_get_userbyid(oid) | nom du compte, sans son hôte |
pg_backend_pid() | numéro de la session (celui de pg_stat_activity.pid et de KILL) |
pg_postmaster_start_time(), pg_conf_load_time() | instant du démarrage du serveur, du chargement de sa configuration |
inet_server_addr(), inet_client_addr() | adresses du serveur et du client de la connexion (127.0.0.1) |
pg_reload_conf() | t |
txid_current(), pg_current_xact_id(), txid_current_if_assigned(), txid_current_snapshot() | numéro de la transaction en cours (NULL pour txid_current_if_assigned() hors écriture), instantané (626841:626841:) |
pg_advisory_lock(n), pg_try_advisory_lock(n), pg_advisory_unlock(n), pg_advisory_xact_lock(n), pg_try_advisory_xact_lock(n) | verrous consultatifs, de session ou de transaction ; les formes try rendent t ou f |
pg_column_size(x), pg_input_is_valid(texte, type) | taille d'une valeur en octets (pg_column_size('abc'::text) : 4) ; pg_input_is_valid('x', 'integer') : f |
pg_terminate_backend(pid [, délai]), pg_cancel_backend(pid) | ferme la session pid, interrompt sa requête en cours : t ; f avec un avertissement si pid est inconnu |
format_type(oid, typmod), pg_typeof(x) | character varying(80), numeric(10,2), timestamp without time zone… |
pg_get_indexdef(oid), pg_get_constraintdef(oid), pg_get_viewdef(oid), pg_get_expr(texte, oid) | définitions, en langage PostgreSQL |
pg_get_functiondef(oid), pg_get_function_arguments(oid), pg_get_function_identity_arguments(oid), pg_get_function_result(oid), pg_get_triggerdef(oid) | routines et déclencheurs (pg_get_function_identity_arguments : ht numeric(10,2), taux numeric(4,2), sans valeurs par défaut) |
obj_description(oid [, catalogue]), col_description(oid, n), shobj_description(oid, catalogue) | commentaires |
to_regclass(nom), to_regtype(nom), to_regnamespace(nom), to_regproc(nom), to_regrole(nom) | OID, ou NULL si le nom n'existe pas |
pg_table_is_visible(oid), pg_function_is_visible(oid), pg_type_is_visible(oid) | t pour un objet du schéma courant ou de pg_catalog |
pg_relation_filepath(relation) | chemin de la relation à la manière de PostgreSQL (base/16384/1075441615) |
has_table_privilege([rôle,] table, privilège), has_column_privilege([rôle,] table, colonne, privilège), has_any_column_privilege(…), has_schema_privilege([rôle,] schéma, privilège), has_function_privilege([rôle,] fonction, 'EXECUTE') | privilèges réels du compte de la session, ou d'un autre compte pour qui administre les comptes (voir 38.5) ; un objet que le compte ne voit pas rend 42P01 / 3F000 / 42883 ; has_database_privilege, has_sequence_privilege, has_type_privilege : toujours t |
pg_has_role([compte,] rôle, 'MEMBER' | 'USAGE' | 'SET') | le rôle est-il accordé au compte (voir 38.5) ; rôle inconnu : 42704 |
pg_relation_size(relation), pg_total_relation_size(relation), pg_table_size(relation), pg_indexes_size(relation), pg_database_size(nom), pg_size_pretty(n) | tailles en octets ; la relation se donne par son OID ou par son nom (pg_relation_size('client'), 'ventes.client'), un nom inconnu rend 42P01 (relation "absente" does not exist) ; pg_indexes_size rend la taille estimée des index de la table, que pg_total_relation_size ajoute à celle de la table (client : 456 + 276 = 732) ; pg_size_pretty(20480::bigint) rend 20 kB |
current_setting(nom [, absent_ok]) | valeur d'un paramètre ; inconnu : 42704 (unrecognized configuration parameter "nexiste"), ou NULL avec true |
pg_encoding_to_char(n), quote_ident(t), quote_literal(t), quote_nullable(t) | UTF8 ; "Mon Nom", 'l''été', NULL |
pg_is_in_recovery() | f |
Conversions de noms :
| Écriture | Résultat |
|---|---|
'client'::regclass | OID de la table du schéma courant ('ventes.client'::regclass pour un autre schéma) ; 42P01 (relation "absente" does not exist) si elle n'existe pas |
c.oid::regclass, conrelid::regclass | nom de la table |
'int4'::regtype::oid | 23 ; atttypid::regtype rend le nom (integer, numeric) |
'ttc'::regproc, tgfoid::regproc | OID d'une routine, son nom |
40.3 information_schema et instructions SHOW#
information_schema#
information_schema est celui de MIRAJ, présenté en langage PostgreSQL à la manière de PostgreSQL : les colonnes de catalogue (table_catalog, constraint_catalog, catalog_name…) portent le nom de la base PostgreSQL de l'instance, miraj, celui que rend current_database() (le langage MIRAJ y lit def, comme la référence). Ses tables usuelles répondent aux requêtes portables, avec quelques différences à connaître :
| Vue | Remarques |
|---|---|
schemata | catalog_name vaut miraj ; liste aussi miraj, performance_schema et sys |
tables | table_catalog = miraj ; table_type : BASE TABLE, VIEW, SEQUENCE |
columns | data_type porte le nom du standard (integer, character varying, timestamp without time zone, boolean, numeric) ; une colonne serial est présentée comme dans PostgreSQL : column_default = nextval('<table>_<colonne>_seq'::regclass), is_identity = NO ; une colonne GENERATED … AS IDENTITY porte is_identity = YES, identity_generation ALWAYS ou BY DEFAULT et un column_default NULL ; udt_name porte le nom interne du type (int4, int8, varchar, bpchar, bool, timestamptz, float8… ; <table>_<colonne>_enum pour un type énuméré, comme pg_type.typname), udt_schema son schéma (pg_catalog, ou celui de la table pour un type énuméré), udt_catalog vaut miraj |
routines | data_type porte le nom du standard (trigger, void, record pour ces pseudo-types) ; type_udt_name le nom interne du type du résultat, type_udt_schema vaut pg_catalog, type_udt_catalog miraj (NULL pour une procédure) |
table_constraints | la clé primaire s'y appelle <table>_pkey, comme dans pg_constraint ; porte table_catalog |
constraint_column_usage | colonnes sur lesquelles porte chaque contrainte : celles d'une clé primaire ou UNIQUE, les colonnes référencées d'une clé étrangère, celles que cite un CHECK ; vue du langage PostgreSQL seulement (absente d'une instance en langage MIRAJ, où une vue qui la lit reste lisible) |
key_column_usage | porte aussi referenced_table_name et referenced_column_name pour une clé étrangère |
referential_constraints | update_rule, delete_rule (NO ACTION, CASCADE…) |
check_constraints | check_clause est écrite en langage PostgreSQL, identifiants entre guillemets doubles (("prix" >= 0)) |
views | view_definition en langage PostgreSQL |
routines | data_type porte le nom du standard (integer, text, character varying…), trigger pour une fonction de déclencheur, void, record |
triggers | un déclencheur écrit en langage PostgreSQL y porte son nom, une ligne par événement, action_statement = EXECUTE FUNCTION f(…) et sa condition WHEN dans action_condition |
sequences | séquences créées par CREATE SEQUENCE |
domains | vue présente, vide |
schema_privileges, table_privileges | droits accordés (grantee = 'app'@'%') |
Les noms de ces vues et de leurs colonnes s'écrivent indifféremment en minuscules ou en majuscules.
SELECT constraint_name, table_name, constraint_type
FROM information_schema.table_constraints
WHERE table_schema = 'ventes' AND table_name IN ('client', 'commande')
ORDER BY table_name, constraint_name; constraint_name | table_name | constraint_type
-----------------+------------+-----------------
client_pkey | client | PRIMARY KEY
email | client | UNIQUE
commande_ibfk_1 | commande | FOREIGN KEY
commande_pkey | commande | PRIMARY KEY
(4 lignes)SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name
FROM information_schema.key_column_usage
WHERE table_schema = 'ventes' AND referenced_table_name IS NOT NULL
ORDER BY table_name, constraint_name; constraint_name | table_name | column_name | referenced_table_name | referenced_column_name
-----------------------+----------------+-------------+-----------------------+------------------------
commande_ibfk_1 | commande | client_id | client | id
ligne_commande_ibfk_1 | ligne_commande | commande_id | commande | id
ligne_commande_ibfk_2 | ligne_commande | article_id | article | id
(3 lignes)Pour un outil écrit pour PostgreSQL, préférer pg_catalog, plus fidèle ; information_schema convient aux requêtes simples (liste des tables, des colonnes).
Instructions SHOW#
SHOW nom lit un paramètre, comme dans PostgreSQL ; SHOW ALL rend tous les paramètres (name, setting, description).
SHOW server_version; -- 16.4
SHOW search_path; -- ventes
SHOW sql_mode; -- POSTGRESQL,STRICT_TRANS_TABLES
SHOW transaction_isolation; -- read committed
SHOW statement_timeout; -- 5minLes instructions SHOW de MIRAJ restent disponibles, en extension :
| Instruction | Rend |
|---|---|
SHOW DATABASES | les schémas (bases MIRAJ) |
SHOW TABLES, SHOW FULL TABLES | tables, vues et séquences du schéma courant, avec leur genre |
SHOW CREATE TABLE t, SHOW CREATE VIEW v, SHOW CREATE SEQUENCE s, SHOW CREATE FUNCTION f | la définition, écrite en langage PostgreSQL |
SHOW COLUMNS FROM t, DESCRIBE t, SHOW INDEX FROM t, SHOW TABLE STATUS | description à la manière de MIRAJ : types de MIRAJ (int(11), longtext, tinyint(1), datetime(6)), clé primaire PRIMARY |
SHOW TRIGGERS | colonnes de MIRAJ ; un déclencheur écrit en langage PostgreSQL y porte son nom et, pour Statement, EXECUTE FUNCTION f(…) (une ligne par événement) |
SHOW PROCESSLIST | sessions (voir 40.7) |
SHOW GRANTS FOR compte | droits d'un compte |
SHOW VARIABLES LIKE '…', SHOW STATUS LIKE '…' | variables et compteurs du serveur |
SHOW CREATE TABLE commande;CREATE TABLE "commande" (
"id" integer GENERATED BY DEFAULT AS IDENTITY (START WITH 4) NOT NULL,
"client_id" integer NOT NULL,
"passee_le" date NOT NULL DEFAULT CURRENT_DATE,
"etat" character varying(12) NOT NULL DEFAULT 'ouverte'::character varying,
PRIMARY KEY ("id"),
CONSTRAINT "commande_ibfk_1" FOREIGN KEY ("client_id") REFERENCES "client" ("id")
);
CREATE INDEX "idx_commande_date" ON "commande" ("passee_le")START WITH donne le numéro que recevra la prochaine ligne : un script rejoué ailleurs reprend la numérotation là où elle en était.
EXPLAIN requête rend le plan de MIRAJ, dans sa propre présentation (colonnes id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra) :
EXPLAIN SELECT * FROM client WHERE id = 1; id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
----+-------------+--------+--------+---------------+---------+---------+-------+------+-------
1 | SIMPLE | client | eq_ref | PRIMARY,email | PRIMARY | | const | 1 |
(1 ligne)40.4 psql#
$env:PGPASSWORD = "mot de passe"
psql -h 127.0.0.1 -p 7007 -U root -d ventes
psql "host=serveur.exemple port=7007 user=root dbname=ventes sslmode=require"Le paramètre -d (ou dbname) nomme le schéma courant (voir 31.2). Le serveur ne travaille qu'en UTF-8 : sous Windows, chcp 65001 ou PGCLIENTENCODING=UTF8.
Les commandes de description lisent pg_catalog :
| Commande | Montre |
|---|---|
\l | l'unique base miraj |
\dn | les schémas, c'est-à-dire les bases MIRAJ |
\dt, \dv, \di, \ds, \d | tables, vues, index, séquences du schéma courant ; \dt ventes.* pour un autre schéma |
\d table, \d+ table | colonnes (types PostgreSQL, valeurs par défaut, identité), index, contraintes, clés étrangères, déclencheurs |
\du | comptes |
\df, \df+, \sf fonction | routines, texte d'une fonction |
\dx | extension vector |
\dp | tables, sans droits détaillés |
\x | affichage étendu |
\copy t from 'fichier.csv' with (format csv, header), \copy t to 'sortie.txt' | charge ou écrit un fichier du poste client (voir 34) |
Ces commandes envoient des requêtes ordinaires sur le catalogue. Celle de \d sans argument (tables, vues et séquences du schéma courant) rend ici :
Schema | Name | Type | Owner
--------+------------------+----------+-------
ventes | article | table | root
ventes | client | table | root
ventes | commande | table | root
ventes | ligne_commande | table | root
ventes | seq_facture | sequence | root
ventes | v_total_commande | view | root
(6 lignes)Une requête saisie ou passée par -c peut contenir plusieurs instructions ; chacune rend son résultat et son étiquette. Ctrl-C annule la requête en cours (57014). psql -f script.sql exécute un script, par exemple un export de miraj-dump (40.9).
Non pris en charge : \gdesc. Les requêtes du catalogue sont celles de psql 10 à 18 ; une version qui en changerait le texte peut recevoir une erreur de syntaxe sur une commande \d.
40.5 Pilotes et bibliothèques#
Tout pilote PostgreSQL se connecte au port du serveur, avec le nom d'un schéma pour base. Le pilote ODBC de MIRAJ et la bibliothèque Python miraj parlent aussi les deux langages.
| Pilote | Connexion |
|---|---|
| psycopg2 (Python) | psycopg2.connect(host=…, port=7007, user=…, password=…, dbname="ventes") |
| pgjdbc (Java) | jdbc:postgresql://serveur:7007/ventes |
| Npgsql (.NET) | Host=serveur;Port=7007;Username=…;Password=…;Database=ventes |
| MIRAJ ODBC Driver | DRIVER={MIRAJ ODBC Driver};SERVER=serveur;PORT=7007;UID=…;PWD=…;DATABASE=ventes |
bibliothèque Python miraj | miraj.connect(host=…, port=7007, user=…, password=…, dbname="ventes") |
Paramètres annoncés à la connexion : server_version = 16.4, server_encoding = client_encoding = UTF8, DateStyle = ISO, MDY, integer_datetimes = on, standard_conforming_strings = on, TimeZone = le fuseau du serveur. Un autre client_encoding est refusé (0A000).
psycopg2#
import psycopg2, psycopg2.extras
conn = psycopg2.connect(host="127.0.0.1", port=7007, user="app",
password="secret123", dbname="ventes")
cur = conn.cursor()
cur.execute("SELECT code, prix, stock FROM article WHERE prix > %s ORDER BY id", (2,))
for code, prix, stock in cur:
print(code, prix, stock) # CAH-96 2.50 200 / CLA-A4 4.90 80
cur.execute("INSERT INTO client (nom, email) VALUES (%(nom)s, %(email)s) RETURNING id",
{"nom": "Nadia Haddad", "email": "[email protected]"})
identifiant = cur.fetchone()[0]
conn.commit()| Sujet | Comportement |
|---|---|
| Paramètres | %s et %(nom)s, substitués par psycopg2 ; cursor.mogrify écrit 'l''été', NULL, '2026-01-02'::date |
| Types reçus | int, str, bool, Decimal (numeric), float, date, time, datetime (avec fuseau pour un timestamptz), dict / list (json, jsonb), memoryview (bytea), str (uuid) |
| Types envoyés | psycopg2.extras.Json(…) pour une colonne jsonb, psycopg2.Binary(…) pour un bytea, Decimal, dates et heures |
| Étiquette | cursor.statusmessage : SELECT 3, INSERT 0 1, UPDATE 2… ; cursor.rowcount |
| Identifiant généré | INSERT … RETURNING id ; cursor.lastrowid vaut 0 |
| Transactions | ouvertes à la première instruction ; commit(), rollback(), autocommit, with conn: ; set_session(isolation_level=…, readonly=True) (une écriture reçoit alors 25006) |
| Erreurs | classes de psycopg2 d'après le SQLSTATE : UndefinedTable (42P01), UniqueViolation (23505), InFailedSqlTransaction (25P02), QueryCanceled (57014)… ; e.pgcode, e.diag.message_primary |
| Avis | RAISE NOTICE d'une fonction : conn.notices (NOTICE: bonjour 42) |
| Lots | executemany, psycopg2.extras.execute_values, execute_batch |
| Curseur nommé | conn.cursor(name="grand") : DECLARE … CURSOR côté serveur, lu par fetchmany ; dans une transaction |
| Curseurs à dictionnaire | RealDictCursor, DictCursor |
| Copies | copy_from, copy_to, copy_expert (texte et CSV) |
| Annulation | conn.cancel() depuis un autre fil : la requête échoue en 57014 ; SET statement_timeout TO 1000 aussi |
| Mode asynchrone | psycopg2.connect(async_=1, …) avec wait_select |
Chargement et export en masse :
import io
donnees = io.StringIO("GOM-01\tGomme blanche\t0.80\t300\nREG-30\tRègle 30 cm\t1.90\t120\n")
cur.copy_from(donnees, "article", columns=("code", "libelle", "prix", "stock"))
sortie = io.StringIO()
cur.copy_expert("COPY (SELECT code, libelle, prix FROM article ORDER BY id) "
"TO STDOUT WITH (FORMAT csv, HEADER)", sortie)
print(sortie.getvalue())code,libelle,prix
STY-01,Stylo bille,1.30
CAH-96,Cahier 96 pages,2.50
CLA-A4,Classeur A4,4.90
GOM-01,Gomme blanche,0.80
REG-30,Règle 30 cm,1.90À savoir :
cursor.callproc("f", …)appelle une fonction : psycopg2 envoieSELECT * FROM f(…), qui rend une ligne d'une colonne. Pour une procédure, écrirecur.execute("CALL p(%s)", …).- Les
optionsde la connexion (options="-c statement_timeout=5000", formes-c nom=valeuret--nom=valeur, une barre oblique inverse devant un espace de la valeur) sont appliquées avant la première requête, comme autant deSET:SHOW statement_timeoutrend5s, etRESET statement_timeoutcommeRESET ALLy reviennent. Une option inconnue, une valeur refusée ou un paramètre du démarrage du serveur (shared_buffers…) refuse la connexion (FATAL,42704,22023,55P02…), comme PostgreSQL. Les autres options de la ligne de commande du serveur PostgreSQL (-d,-S…) sont refusées (42601). - Les paramètres qu'un pilote écrit dans le message de démarrage lui-même (pgjdbc et Npgsql :
client_encoding,DateStyle,TimeZone,extra_float_digits,search_path,application_name…) sont posés de la même façon, comme valeurs de départ de la session. Là, un nom inconnu ou une valeur que le serveur ne sait pas honorer (client_encodingautre qu'UTF8,DateStyleautre qu'ISO, fuseau inconnu) est ignoré sans refuser la connexion. - Une instruction
executequi contient plusieurs requêtes ne rend que le résultat de la dernière, comme avec PostgreSQL.
pgjdbc et Npgsql#
Ces pilotes préparent leurs requêtes : ils emploient le protocole étendu (paramètres $1, $2…, valeurs en texte ou en binaire), que le serveur prend en charge. Le chapitre 15 en donne le détail et la liste des fonctions essayées (15.10.4).
| Pilote | Pris en charge |
|---|---|
| pgjdbc 42.7 | PreparedStatement typé, instructions préparées côté serveur, lots (executeBatch), setFetchSize, niveaux d'isolement, getGeneratedKeys (RETURNING), CopyManager en texte et CSV, DatabaseMetaData (getSchemas, getTables, getColumns, getPrimaryKeys, getImportedKeys, getExportedKeys, getIndexInfo, getTypeInfo…), ResultSet modifiable |
| Npgsql 10 | Open (types chargés depuis pg_type), commandes paramétrées typées, commande préparée réutilisée, transactions, NpgsqlBatch, annulation, ExecuteReader sur INSERT … RETURNING, BeginTextImport / BeginTextExport, GetSchema (Tables, Columns, Indexes, ForeignKeys…) |
| DBeaver | passe par pgjdbc : navigateur (schémas, tables, colonnes, clés, index) et édition des lignes |
await using var conn = new NpgsqlConnection("Host=serveur;Port=7007;Username=app;Password=…;Database=ventes");
await conn.OpenAsync();
await using var cmd = new NpgsqlCommand("SELECT libelle, prix FROM article WHERE prix > $1", conn);
cmd.Parameters.Add(new NpgsqlParameter { Value = 2.0m, NpgsqlDbType = NpgsqlDbType.Numeric });Limites communes : pas de COPY binaire (BeginBinaryImport de Npgsql), pas de COPY dans une instruction préparée.
Pilote ODBC de MIRAJ#
MIRAJ ODBC Driver (installation et sources de données : chapitre 28) reconnaît le langage de l'instance à la connexion ; PROTOCOL=postgresql dans la chaîne l'impose.
import pyodbc
cn = pyodbc.connect("DRIVER={MIRAJ ODBC Driver};SERVER=127.0.0.1;PORT=7007;"
"UID=app;PWD=secret123;DATABASE=ventes")
cur = cn.cursor()
cur.execute("SELECT id, nom, actif FROM client WHERE ville = ?", "Alger")
print(cur.fetchall()) # [(1, 'Amel Kaci', True)]
cur.execute("INSERT INTO client (nom, email) VALUES (?, ?) RETURNING id", "Essai", "[email protected]")
print(cur.fetchone()[0])
cn.commit()| Domaine | Sur une instance en langage PostgreSQL |
|---|---|
| SQL | celui de ce manuel ; les marqueurs ? sont liés comme paramètres ; les séquences d'échappement ODBC sont traduites ({fn UCASE('abc')}, {d '2026-10-11'}, {fn CONVERT('12', SQL_INTEGER)}) |
SQLGetInfo | SQL_DBMS_NAME = PostgreSQL, SQL_DBMS_VER = 16.04.0000, guillemet d'identifiant ", SQL_SCHEMA_TERM = schema, SQL_DATABASE_NAME = miraj |
| Catalogue | SQLTables : TABLE_CAT = miraj, TABLE_SCHEM = le schéma (ventes), types TABLE, VIEW, SEQUENCE ; SQLColumns : types nommés int4, varchar, text, numeric… ; SQLPrimaryKeys (clé nommée <table>_pkey), SQLForeignKeys, SQLStatistics |
| Types | bool rendu en booléen, numeric en décimal exact ; SQLGetTypeInfo : text, json, jsonb, varchar, bpchar, uuid, bool, int8, bytea, numeric, int4, int2, float4, float8, date, time, timestamp, timestamptz |
| Résultats | plusieurs instructions dans un texte : un résultat par instruction (SQLMoreResults, cursor.nextset()) |
| Erreurs | SQLSTATE de PostgreSQL (42P01, 23505…), code natif 0 |
| Base inconnue | DATABASE=inconnue : connexion refusée, 3D000 |
| TLS | SSLMODE=required sur un serveur sans certificat : 08001 (« TLS exigé, mais le serveur ne le propose pas ») |
| Identifiant généré | INSERT … RETURNING (le protocole ne transmet pas de dernier identifiant) |
COPY n'est pas disponible par ODBC. Les autres limites du pilote sont au 28.6. Un pilote PostgreSQL tiers (psqlODBC) se connecte aussi à une telle instance.
Bibliothèque Python miraj#
La bibliothèque miraj (26.12) offre l'interface DB-API de psycopg2 vers les deux langages : une même application se connecte à une instance MIRAJ ou PostgreSQL sans changer d'API.
import miraj
conn = miraj.connect(host="127.0.0.1", port=7007, user="app", password="secret123",
dbname="ventes", protocol="postgresql")
cur = conn.cursor()
cur.execute("SELECT libelle, prix FROM article WHERE prix > %s", (2,))protocol:auto(défaut, langage reconnu à la connexion),mirajoupostgresql.dbnamenomme le schéma ; un schéma inconnu est refusé.- Les
%sdeviennent les paramètres$1,$2… d'une instruction préparée gardée par connexion et réutilisée (executemanyprépare une fois). statusmessageporte l'étiquette du serveur ;lastrowidvautNone: utiliserINSERT … RETURNING.copy_from,copy_to,copy_expertetinsert_rowspassent parCOPY.- Erreurs : SQLSTATE de PostgreSQL (
e.sqlstate,e.pgcode) ; avis dansconnection.notices. - Pas de curseur nommé (
cursor(name=…):NotSupportedError) : le résultat est lu en entier à l'exécution.
40.6 Requêtes courantes sur le catalogue#
Tables d'un schéma, avec leur taille et leur commentaire :
SELECT n.nspname AS schema, c.relname AS table,
pg_size_pretty(pg_total_relation_size(c.oid)) AS taille,
obj_description(c.oid, 'pg_class') AS commentaire
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r' AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'miraj')
ORDER BY 1, 2; schema | table | taille | commentaire
--------+----------------+-----------+------------------------
ventes | article | 589 bytes |
ventes | client | 732 bytes | Clients de la boutique
ventes | commande | 344 bytes |
ventes | ligne_commande | 176 bytes |
(4 lignes)Les tailles sont celles des données en mémoire, index compris, et non celles des fichiers.
Existence d'une table, sans erreur si elle manque :
SELECT to_regclass('client') IS NOT NULL AS client, to_regclass('absente') IS NOT NULL AS absente; client | absente
--------+---------
t | f
(1 ligne)Colonnes d'une table par information_schema :
SELECT column_name, data_type, is_nullable, column_default, udt_name, is_identity
FROM information_schema.columns
WHERE table_schema = 'ventes' AND table_name = 'client'
ORDER BY ordinal_position; column_name | data_type | is_nullable | column_default | udt_name | is_identity
-------------+-----------------------------+-------------+------------------------------------+-----------+-------------
id | integer | NO | nextval('client_id_seq'::regclass) | int4 | NO
nom | character varying | NO | | varchar | NO
email | character varying | NO | | varchar | NO
ville | text | YES | | text | NO
actif | boolean | NO | true | bool | NO
cree_le | timestamp without time zone | YES | now() | timestamp | NO
(6 lignes)Types connus du serveur :
SELECT typname, oid, typcategory FROM pg_type
WHERE typname IN ('bool', 'int4', 'numeric', 'text', 'timestamptz', 'jsonb', 'uuid', 'vector')
ORDER BY typname; typname | oid | typcategory
-------------+-------+-------------
bool | 16 | B
int4 | 23 | N
jsonb | 3802 | U
numeric | 1700 | N
text | 25 | S
timestamptz | 1184 | D
uuid | 2950 | U
vector | 16385 | U
(8 lignes)40.7 Sessions et paramètres#
SELECT pid, usename, datname, client_addr, state, query FROM pg_stat_activity ORDER BY pid; pid | usename | datname | client_addr | state | query
-----+---------+---------+-------------+--------+------------------------------------------------------------------------------------
1 | root | miraj | localhost | idle |
28 | root | miraj | localhost | active | SELECT pid, usename, datname, client_addr, state, query FROM pg_stat_activity ORDER BY pid
(2 lignes)pg_stat_activity reprend les sessions de SHOW PROCESSLIST. Un compte ordinaire n'y voit que ses propres sessions. application_name est celui que la session a donné à la connexion ou par SET ; backend_start n'est pas renseignée.
Une session se ferme par pg_terminate_backend(pid), ou par l'instruction KILL de MIRAJ, avec son pid ; pg_cancel_backend(pid) et KILL QUERY pid n'interrompent que la requête en cours (57014). La session fermée reçoit l'erreur fatale 57P01 (terminating connection due to administrator command) avant la fin de sa connexion (voir le chapitre 37).
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND state = 'idle'; pg_terminate_backend
----------------------
t
t
(2 lignes)KILL 53 ferme de même la session 53 et rend l'étiquette KILL.
Les deux fonctions rendent t ; pour un pid inconnu, f avec l'avertissement PID 99999 is not a PostgreSQL backend process (KILL répond 42704, Unknown thread id: 99999). Un compte ne ferme que ses propres sessions, sauf droit d'administration (PROCESS) : 42501, permission denied to terminate process (You are not owner of thread 1 pour KILL).
Paramètres :
SELECT name, setting FROM pg_settings
WHERE name IN ('server_version', 'server_version_num', 'client_encoding', 'DateStyle', 'TimeZone',
'max_connections', 'password_encryption', 'search_path', 'transaction_isolation',
'statement_timeout')
ORDER BY name; name | setting
-----------------------+----------------
DateStyle | ISO, MDY
TimeZone | Etc/GMT-1
client_encoding | UTF8
max_connections | 151
password_encryption | scram-sha-256
search_path | ventes
server_version | 16.4
server_version_num | 160004
statement_timeout | 5min
transaction_isolation | read committed
(10 lignes)pg_settings réunit les paramètres que PostgreSQL annonce aux clients et les variables de session de MIRAJ (autocommit, lock_wait_timeout…). Comme dans PostgreSQL, ses textes se comparent et se trient octet par octet : les noms à majuscules (DateStyle, TimeZone) viennent en tête, et name = 'datestyle' ne trouve rien. statement_timeout vaut par défaut la durée maximale d'une instruction fixée au serveur (5 minutes). current_setting('nom') lit une valeur ; les réglages se font par SET (voir 37).
40.8 Outils MIRAJ#
Les outils de MIRAJ reconnaissent le langage de l'instance à la connexion et lui parlent dans ce langage. L'option --protocol auto | miraj | postgresql (défaut auto) permet de l'imposer ; les options de connexion sont les mêmes dans les deux langages (--host, --port, --user, --password, variable MIRAJ_PASSWORD, --ssl, --ssl-ca, --ssl-insecure, --no-ssl).
| Outil | Sur une instance en langage PostgreSQL |
|---|---|
miraj-cli | console SQL : instructions et fichiers en langage PostgreSQL, invite schéma=> |
miraj-backup | sauvegarde physique à chaud et restauration (40.9) |
miraj-dump | export en un script SQL écrit en langage PostgreSQL (40.9) |
miraj-migrate | migration vers MIRAJ : définitions récrites en langage PostgreSQL, lignes chargées par COPY, routines traduites en PL/pgSQL (chapitre 17) |
| Miraj Server Manager | supervision et réglages ; affiche le langage de l'instance (chapitre 21) |
miraj-proxy | relaie les connexions PostgreSQL sans changement (chapitre 22) |
miraj-cli#
$env:MIRAJ_PASSWORD = "mot de passe"
miraj-cli.exe --port 7007 -d ventesOK, 0 ligne(s) affectée(s)
MIRAJ 1.0.0 - connecté (langage PostgreSQL) - tapez \help pour l'aide, \q pour quitter.
ventes=> SELECT code, prix FROM article ORDER BY id LIMIT 2;
+--------+------+
| code | prix |
+--------+------+
| STY-01 | 1.30 |
| CAH-96 | 2.50 |
+--------+------+
2 ligne(s)
ventes=> SELECT 1/0;
ERREUR 22012 : division by zero| Usage | Commande |
|---|---|
| console interactive | miraj-cli --port 7007 -d ventes |
| une ou plusieurs instructions | miraj-cli --port 7007 -d ventes -e "SELECT 1 AS a; SELECT 2 AS b;" |
| un script | miraj-cli --port 7007 script.sql : instructions exécutées une à une, arrêt à la première erreur (code de sortie 1) |
| résultat en JSON | --json : [ {"code": "STY-01", "prix": 1.30} ], suivi de 1 ligne(s) |
- Avec
-d, la console pose d'abord le schéma courant ; la ligneOK, 0 ligne(s) affectée(s)qui précède la bannière (ou le premier résultat de-e) en est le compte rendu. - Les erreurs s'affichent avec leur SQLSTATE (
ERREUR 42P01 : relation "absente" does not exist). - Avec
-eet plusieurs instructions, les résultats de celles qui précèdent une erreur sont affichés, puis l'erreur ; les suivantes ne sont pas exécutées. - Un script est découpé selon la lexicographie de PostgreSQL : chaînes standard (
'l''été'),E'…',$$…$$. \lliste les schémas (SHOW DATABASES),\dles tables du schéma courant (SHOW TABLES),\d tabledécrit une table à la manière de MIRAJ (DESCRIBE: typesint(11),longtext…). Pour une description en types PostgreSQL, interrogerpg_attribute(40.2) ouSHOW CREATE TABLE.- Ctrl-C interrompt l'instruction en cours (une boucle de procédure, une longue requête) et rend la main à l'invite.
- Code de sortie 2 : connexion impossible (mot de passe faux :
connexion à [email protected]:7007 impossible : erreur SQL 28P01 : Access denied for user 'root'@'localhost' (using password: YES)). - Avec
--protocol mirajsur une instance PostgreSQL, la connexion échoue après une attente (server disconnected) : laisserauto.
Le reste de la console (historique, \timing, source) est décrit au chapitre 25.
40.9 Sauvegarde et restauration#
Deux méthodes, les mêmes que dans le langage MIRAJ (chapitre 18) :
| Méthode | Nature | Outil |
|---|---|---|
| copie physique à chaud | fichiers du schéma, restauration immédiate | BACKUP DATABASE / RESTORE DATABASE, miraj-backup |
| export SQL | script en langage PostgreSQL, lisible et portable | miraj-dump |
BACKUP DATABASE et RESTORE DATABASE#
Ces instructions sont des extensions de MIRAJ, disponibles en langage PostgreSQL. Le serveur doit être démarré avec un dossier de sauvegarde (--backup-dir E:\sauvegardes, ou <backup_dir> dans miraj_config.xml) ; le dossier nommé dans l'instruction est relatif à celui-ci.
BACKUP DATABASE ventes TO 'sauv1'; Database | Directory | Files | Bytes | End_lsn
----------+----------------------+-------+-------+---------
ventes | E:\sauvegardes\sauv1 | 9 | 7247 | 1464
(1 ligne)RESTORE DATABASE ventes_copie FROM 'sauv1';WARNING: base ventes restaurée sous le nom ventes_copie : les références qualifiées par l'ancien nom (vues, routines, clés étrangères) le gardent
Database | Directory | Files | Bytes | End_lsn
--------------+----------------------+-------+-------+---------
ventes_copie | E:\sauvegardes\sauv1 | 9 | 7247 | 1464
(1 ligne)Le schéma restauré est aussitôt utilisable (SELECT count(*) FROM ventes_copie.client;) et apparaît dans pg_namespace. La sauvegarde ne bloque pas les écritures.
Sans --backup-dir, l'instruction est refusée (XX000, the server is running without the --backup-dir option so it cannot execute this statement) ; avec un chemin hors de ce dossier aussi (XX000, The server is running with the --backup-dir option so it cannot execute this statement).
miraj-backup#
$env:MIRAJ_PASSWORD = "mot de passe"
miraj-backup backup --db ventes --to sauv2 --port 7007
miraj-backup restore --from sauv2 --db ventes_copie --replace --port 7007
miraj-backup verify --from E:\sauvegardes\sauv2
miraj-backup list --from E:\sauvegardes\sauv2ventes : 9 fichier(s), 7.1 Ko, position 1464 → E:\sauvegardes\sauv2
ventes_copie : 9 fichier(s), 7.1 Ko, position 1464 → E:\sauvegardes\sauv2
Sauvegarde valide : base ventes, 9 fichier(s), 7.1 Ko, position 1464backup et restore envoient BACKUP / RESTORE au serveur (le dossier est celui du serveur, relatif à son --backup-dir) ; --all sauvegarde tous les schémas, --replace remplace un schéma existant. verify et list lisent un dossier local. Le compte doit avoir le droit BACKUP_ADMIN. Une copie physique ne contient pas les comptes : les garder par miraj-dump --users.
miraj-dump#
miraj-dump --db ventes --users -r ventes.sql --port 7007
miraj-dump restore ventes.sql --port 7008
psql -h 127.0.0.1 -p 7008 -U root -d miraj -v ON_ERROR_STOP=1 -f ventes.sqlSur une instance en langage PostgreSQL, le script est écrit dans ce langage et se recharge par miraj-dump restore, miraj-cli ou psql -f :
- tables telles que
SHOW CREATE TABLEles rend, lignes enINSERTmulti-lignes (identifiants entre guillemets, chaînes standard, booléenstrue/false,byteaen'\x…'::bytea) ; - séquences, vues, routines et déclencheurs en PL/pgSQL (
CREATE OR REPLACE FUNCTION … AS $function$…$function$), sansDELIMITER; - la lecture se fait dans une seule transaction : le script reflète un instant unique, sans bloquer les autres sessions ;
--usersajoute les comptes et leurs droits. Un compte rechargé se connecte par 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 …) ; un tel script est un fichier sensible, à protéger comme un fichier de mots de passe ;- un export suit le langage de l'instance lue : celui d'une instance en langage MIRAJ ne se recharge pas dans une instance en langage PostgreSQL, ni l'inverse.
Les options d'export (--tables, --no-data, --no-routines…) sont décrites au 18.3.
40.10 Non pris en charge et écarts#
| Sujet | Sur une instance MIRAJ |
|---|---|
| Vues du catalogue absentes | pg_stat_database, pg_stat_all_tables, pg_statio_user_tables, pg_stats, pg_prepared_statements, pg_cursors, pg_available_extensions, pg_timezone_names, pg_operator : 42P01 |
| Tables présentes mais vides | pg_locks, pg_stat_user_tables, pg_depend, pg_inherits… (40.2) : pas de verrous ni de statistiques d'accès par ce biais |
| Fonctions absentes | inet_server_port, pg_current_wal_lsn : 42883 |
| Fonctions de taille | tailles en mémoire ; pg_indexes_size est une estimation (largeur moyenne des clés par ligne vivante) |
'nom'::regclass écrit seul | rend l'OID (un nombre) ; oid::regclass rend le nom |
confrelid::regclass d'une contrainte sans table cible | 0 (PostgreSQL : -) |
| Droits | relacl, attacl, nspacl NULL ; has_table_privilege, has_column_privilege, has_any_column_privilege et has_schema_privilege rendent les privilèges réels (8.5), les autres has_*_privilege toujours t ; pg_shadow et pg_authid réservées à qui administre les comptes, mot de passe toujours NULL |
pg_stat_activity | backend_start non renseigné ; client_addr en texte (localhost) ; une transaction ouverte et inactive reste idle |
txid_current() | numéro positif, le même dans toute la transaction, mais propre à MIRAJ : ni croissant d'une transaction à l'autre, ni comparable à ceux de PostgreSQL |
pg_settings | colonnes short_desc, unit vides pour les variables de MIRAJ ; la ligne application_name suit la valeur donnée à la connexion ou par SET |
COMMENT ON | tables et colonnes seulement ; COMMENT ON VIEW, FUNCTION, SCHEMA… : 0A000 |
CREATE EXTENSION, VACUUM | VACUUM : erreur de syntaxe 42601 ; ANALYZE : 0A000 ; CREATE EXTENSION d'une extension autre que celles de 40.2 (postgis…) : 0A000. L'extension vector est toujours présente ; CREATE EXTENSION vector est accepté sans effet |
information_schema | celui de MIRAJ : colonnes propres à MIRAJ (column_type, column_key, extra…) ; les colonnes ajoutées pour PostgreSQL (udt_name, udt_schema, type_udt_name, table_constraints.table_catalog) valent aussi au travers d'une vue |
EXPLAIN | plan de MIRAJ, dans sa présentation |
| Noms de contraintes | ceux de MIRAJ (<table>_pkey, <table>_ibfk_<n>, CONSTRAINT_<n>, nom de la colonne pour un UNIQUE de colonne) |
| OID des objets | calculés depuis le nom : stables, mais changés par un renommage |
pg_get_viewdef, CHECK | identifiants toujours entre guillemets ; requête réécrite |
| psql | \gdesc refusé |
| psycopg2 | callproc d'une procédure : 42883 (écrire CALL) ; client_encoding autre qu'UTF-8 : 0A000 ; lastrowid = 0 |
| ODBC | pas de COPY |
LISTEN / NOTIFY | acceptés, mais le client ne reçoit pas de notification asynchrone : voir 37 |
DROP INDEX a, b | 0A000 : un index par instruction |
La liste condensée de ce que le catalogue contient est au 29.9.