32. PostgreSQL : Prise en main
Ce chapitre vous guide pas à pas : démarrer une instance MIRAJ en langage PostgreSQL, s'y connecter avec un client PostgreSQL (psql, psycopg2, un pilote) ou avec la console miraj-cli, créer un schéma, des tables, y écrire et y lire des données, indexer et inspecter le schéma. À la fin, vous saurez faire tourner un cas d'usage complet sans quitter la syntaxe de PostgreSQL.
Les résultats sont montrés tels qu'un client PostgreSQL les reçoit : un tableau de lignes, puis l'étiquette de l'instruction (SELECT 3, INSERT 0 1, CREATE TABLE…). Les erreurs sont données avec leur SQLSTATE.
32.1 Démarrer le serveur en langage PostgreSQL#
Une instance MIRAJ parle un seul langage SQL, choisi au démarrage par l'élément POSTGRESQL de sql_mode :
miraj-server.exe --root D:\donnees --log --sql-mode "STRICT_TRANS_TABLES,POSTGRESQL"MIRAJ 1.0.0 Entreprise - serveur console sur 127.0.0.1:7007, langage PostgreSQL, dossier "D:\donnees", 12 fils, licence ENT-0001 (Exemple SARL).
Configuration : D:\donnees\miraj_config.xml.La mention langage PostgreSQL confirme le choix ; la fin de la ligne rappelle la licence installée. Le dossier D:\donnees est créé s'il n'existe pas, avec un compte root local dont le mot de passe, tiré au sort, s'affiche une seule fois juste au-dessus : notez-le, puis remplacez-le (32.9). Laissez cette fenêtre ouverte : le serveur y reste actif.
| Point | Valeur |
|---|---|
| Port | le port principal du serveur (--port, 7007 par défaut) : il n'y a pas de second port, et le port 5432 n'est pas utilisé |
| Langage | fixé pour toute l'instance ; le changer demande un redémarrage, les bases et leurs données restent valables |
| Réglage permanent | <sql_mode>STRICT_TRANS_TABLES,POSTGRESQL</sql_mode> dans miraj_config.xml, ou le choix Langage SQL de l'éditeur de configuration |
| Vérification | SHOW sql_mode; rend STRICT_TRANS_TABLES,POSTGRESQL |
Les autres options du serveur (dossier de données, journal, TLS, service Windows) ne dépendent pas du langage : voir l'administration du serveur, et sa section 16.10 pour ce qui est propre à une instance en langage PostgreSQL.
32.2 Ce qu'il faut savoir avant de se connecter#
Un client PostgreSQL voit l'instance ainsi :
| Notion PostgreSQL | Dans MIRAJ |
|---|---|
base de données (dbname) | une seule : miraj |
| schéma | une base MIRAJ ; CREATE SCHEMA ventes et CREATE DATABASE ventes créent la même chose |
schéma courant, search_path | la base courante de la session ; SET search_path TO ventes la choisit |
public | n'est pas un schéma : dans un nom (public.client), c'est un alias de la base courante |
| version annoncée | PostgreSQL 16.4 (MIRAJ 31.0.0) (server_version = 16.4) |
| encodage | UTF-8 seulement (client_encoding = UTF8) |
Le paramètre dbname de la connexion ne choisit donc pas une base PostgreSQL, mais le schéma courant :
dbname donné | Effet |
|---|---|
nom d'une base MIRAJ (ventes) | elle devient le schéma courant |
miraj, postgres, le nom du compte, ou rien | ignoré : la connexion s'ouvre sans schéma courant (current_schema est NULL) |
| un autre nom, inconnu | avis NOTICE: database "inconnue" does not exist; connected without a current database, et la connexion s'ouvre sans schéma courant |
Sans schéma courant, les tables se nomment schéma.table, ou l'on en choisit un par SET search_path TO ventes;.
32.3 Se connecter avec psql#
$env:PGPASSWORD = "mot de passe"
psql -h 127.0.0.1 -p 7007 -U root -d ventes
psql "host=127.0.0.1 port=7007 user=root dbname=ventes"Sous Windows, passez la console en UTF-8 (chcp 65001) ou fixez PGCLIENTENCODING=UTF8 : un texte accentué saisi dans une autre page de codes est refusé. Les commandes de description (\dn, \dt, \d table…) fonctionnent ; elles sont décrites au chapitre 41. Catalogue et outils.
Premier contrôle, valable dans tout client :
SELECT version(); version()
----------------------------------------------------------------------------
PostgreSQL 16.4 (MIRAJ 1.0.0) on x86_64-windows, 64-bit
(1 ligne)SELECT current_database(), current_schema, current_user; current_database() | current_schema | current_user
--------------------+----------------+--------------
miraj | ventes | root
(1 ligne)32.4 Se connecter avec miraj-cli#
La console SQL livrée avec MIRAJ se connecte aussi à une instance en langage PostgreSQL : elle reconnaît le langage à la connexion. Le mot de passe se donne par --password (saisie masquée) ou par la variable d'environnement MIRAJ_PASSWORD.
miraj-cli.exe --port 7007 --user root --password -d ventesMIRAJ 1.0.0 - connecté (langage PostgreSQL) - tapez \help pour l'aide, \q pour quitter.
ventes=>L'invite ventes=> montre la base courante (miraj=> sans base courante) ; chaque instruction se termine par un point-virgule et s'écrit en langage PostgreSQL. Les résultats sont encadrés à la manière de miraj-cli :
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 zeroUn fichier de script s'exécute en le nommant (miraj-cli --port 7007 script.sql), une instruction par -e "…". Détails au chapitre 41.
32.5 Se connecter depuis une application#
Tout pilote PostgreSQL convient ; seuls l'hôte, le port et le nom du schéma changent par rapport à un serveur PostgreSQL.
Python (psycopg2)#
L'exemple lit et écrit la table client créée en 32.7.
import psycopg2
conn = psycopg2.connect(host="127.0.0.1", port=7007, user="root",
password="mot de passe", dbname="ventes")
cur = conn.cursor()
cur.execute("SELECT id, nom FROM client WHERE ville = %s", ("Alger",))
print(cur.fetchall()) # [(1, 'Amel Kaci')]
print(cur.statusmessage) # SELECT 1
cur.execute("INSERT INTO client (nom, email, ville) VALUES (%s, %s, %s) RETURNING id",
("Nadia Haddad", "[email protected]", "Oran"))
print(cur.fetchone()[0]) # 4 : identifiant généré
conn.commit()
conn.close()La forme URI est acceptée aussi : psycopg2.connect("postgresql://root:[email protected]:7007/ventes").
- Les valeurs reviennent dans les types Python habituels :
int,str,boolpour unboolean,decimal.Decimalpour unnumeric,datetime.date,datetime.datetime(avec fuseau pour untimestamptz, commenow()),dictpour unjsonb. - psycopg2 ouvre une transaction à la première instruction : rien n'est gardé sans
conn.commit()(ouconn.autocommit = True). - L'identifiant d'une ligne insérée se lit par
INSERT … RETURNING id;cursor.lastrowidvaut toujours 0. - Après une erreur dans une transaction, les instructions suivantes reçoivent
25P02jusqu'àconn.rollback(), comme sur un serveur PostgreSQL.
Autres pilotes#
| Pilote | Chaîne de connexion |
|---|---|
| Npgsql (.NET) | Host=127.0.0.1;Port=7007;Username=root;Password=…;Database=ventes |
| pgjdbc (Java) | jdbc:postgresql://127.0.0.1:7007/ventes |
| ODBC (MIRAJ ODBC Driver) | DRIVER={MIRAJ ODBC Driver};SERVER=127.0.0.1;PORT=7007;UID=root;PWD=…;DATABASE=ventes |
Le chapitre 41 décrit chaque pilote, ce qu'il prend en charge et ses limites.
Refus de connexion#
| Cas | Ce que reçoit le client |
|---|---|
| mot de passe faux ou compte inconnu | FATAL: Access denied for user 'root'@'localhost' (using password: YES) (SQLSTATE 28P01) ; rien ne distingue les deux cas |
sslmode=require sur un serveur démarré sans certificat | server does not support SSL, but SSL was required (message du client) |
| client du protocole hérité (langage MIRAJ) | la connexion reste muette puis est fermée par le serveur : ce client ne parle pas à une instance PostgreSQL |
L'authentification (SCRAM-SHA-256) et le chiffrement TLS sont décrits au chapitre 39. Comptes et privilèges.
32.6 Créer un schéma#
CREATE SCHEMA ventes;
SET search_path TO ventes;CREATE SCHEMA
SETSET search_path fait de ventes le schéma courant de la session ; SHOW search_path; et SELECT current_schema; rendent alors ventes. Dans une application, donnez plutôt le schéma à la connexion (dbname=ventes).
32.7 Créer des tables#
Quatre tables : des clients, des articles, des commandes et leurs lignes. serial donne une clé auto-incrémentée ; les contraintes NOT NULL, UNIQUE, CHECK, REFERENCES et DEFAULT s'écrivent comme dans PostgreSQL.
CREATE TABLE client (
id serial PRIMARY KEY,
nom varchar(80) NOT NULL,
email varchar(120) NOT NULL UNIQUE,
ville text,
actif boolean NOT NULL DEFAULT true,
cree_le timestamp DEFAULT now()
);
CREATE TABLE article (
id serial PRIMARY KEY,
code varchar(20) NOT NULL UNIQUE,
libelle text NOT NULL,
prix numeric(10,2) NOT NULL CHECK (prix >= 0),
stock integer NOT NULL DEFAULT 0
);
CREATE TABLE commande (
id serial PRIMARY KEY,
client_id integer NOT NULL REFERENCES client (id),
passee_le date NOT NULL DEFAULT current_date,
etat varchar(12) NOT NULL DEFAULT 'ouverte'
);
CREATE TABLE ligne_commande (
commande_id integer NOT NULL REFERENCES commande (id) ON DELETE CASCADE,
article_id integer NOT NULL REFERENCES article (id),
quantite integer NOT NULL CHECK (quantite > 0),
prix_unit numeric(10,2) NOT NULL,
PRIMARY KEY (commande_id, article_id)
);CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLELes types et les contraintes sont détaillés aux chapitres 33. Types de données et 34. Langage DDL.
32.8 Écrire des données#
INSERT INTO client (nom, email, ville) VALUES
('Amel Kaci', '[email protected]', 'Alger'),
('Youssef Ben Ali', '[email protected]', 'Constantine'),
('Claire Dupont', '[email protected]', 'Lyon');INSERT 0 3RETURNING rend les lignes écrites, avec leurs valeurs générées :
INSERT INTO article (code, libelle, prix, stock) VALUES
('STY-01', 'Stylo bille', 1.20, 500),
('CAH-96', 'Cahier 96 pages', 2.50, 200),
('CLA-A4', 'Classeur A4', 4.90, 80)
RETURNING id, code; id | code
----+--------
1 | STY-01
2 | CAH-96
3 | CLA-A4
(3 lignes)
INSERT 0 3INSERT INTO commande (client_id, passee_le) VALUES
(1, '2026-10-01'), (1, '2026-10-05'), (2, '2026-10-06');
INSERT INTO ligne_commande VALUES
(1, 1, 10, 1.20), (1, 2, 4, 2.50), (2, 3, 2, 4.90), (3, 2, 20, 2.30);INSERT 0 3
INSERT 0 4Les contraintes sont appliquées, avec les SQLSTATE de PostgreSQL :
INSERT INTO client (nom, email) VALUES ('Doublon', '[email protected]');
INSERT INTO article (code, libelle, prix) VALUES ('X', 'Prix négatif', -1);
INSERT INTO commande (client_id) VALUES (99);ERREUR 23505 : Duplicate entry '[email protected]' for key 'email'
ERREUR 23514 : new row for relation "article" violates check constraint "CONSTRAINT_1"
ERREUR 23503 : Cannot add or update a child row: a foreign key constraint fails ("ventes"."commande", CONSTRAINT "commande_ibfk_1" FOREIGN KEY ("client_id") REFERENCES "client" ("id"))Le SQLSTATE est celui de PostgreSQL ; le texte du message est celui de MIRAJ, en anglais, et cite les noms à sa manière. Une application doit donc se fier au SQLSTATE, pas au texte.
Modifier une ligne, puis insérer ou mettre à jour selon le cas (ON CONFLICT) :
UPDATE article SET stock = stock - 10 WHERE code = 'STY-01' RETURNING code, stock; code | stock
--------+-------
STY-01 | 490
(1 ligne)
UPDATE 1INSERT INTO article (code, libelle, prix, stock) VALUES ('STY-01', 'Stylo bille bleu', 1.30, 100)
ON CONFLICT (code) DO UPDATE SET prix = EXCLUDED.prix, stock = article.stock + EXCLUDED.stock
RETURNING code, prix, stock; code | prix | stock
--------+------+-------
STY-01 | 1.30 | 590
(1 ligne)
INSERT 0 1Une transaction s'ouvre par BEGIN et se termine par COMMIT ou ROLLBACK. Ici, une suppression et une modification sont annulées :
BEGIN;
DELETE FROM client WHERE id = 3 RETURNING nom;
UPDATE article SET stock = 0;
ROLLBACK;
SELECT code, stock FROM article ORDER BY id;BEGIN
nom
---------------
Claire Dupont
(1 ligne)
DELETE 1
UPDATE 3
ROLLBACK
code | stock
--------+-------
STY-01 | 590
CAH-96 | 200
CLA-A4 | 80
(3 lignes)Voir 35. Langage DML et 38. Transactions et session.
32.9 Changer le mot de passe de root et créer un compte#
Le mot de passe provisoire de root se remplace ainsi :
ALTER USER root@localhost IDENTIFIED BY 'nouveau mot de passe';Une application ne devrait pas se connecter en root. Un compte et ses droits sur le schéma :
CREATE USER app IDENTIFIED BY 'secret123';
GRANT SELECT, INSERT, UPDATE, DELETE ON ventes.* TO app;CREATE USER
GRANTLe compte app lit et écrit alors les tables de ventes, et reçoit 42501 pour le reste (DROP TABLE client : permission denied for table client).
Le compte se crée aussi dans les formes de PostgreSQL, qui rendent leur propre étiquette :
CREATE USER app2 WITH PASSWORD 'secret123';
CREATE ROLE app3 LOGIN PASSWORD 'secret123';CREATE USER
CREATE ROLELes droits sur l'ensemble d'un schéma se donnent par GRANT … ON schéma.*, comme ci-dessus. Voir 39. Comptes et privilèges.
32.10 Interroger les données#
Filtre et tri ; ILIKE ignore la casse, LIKE la respecte, comme dans PostgreSQL :
SELECT id, nom, email FROM client WHERE nom ILIKE '%a%' ORDER BY nom; id | nom | email
----+-----------------+----------------------------
1 | Amel Kaci | [email protected]
3 | Claire Dupont | [email protected]
2 | Youssef Ben Ali | [email protected]
(3 lignes)Conversion :: et booléens (rendus t / f) :
SELECT id, nom, actif, cree_le::date AS jour FROM client ORDER BY id; id | nom | actif | jour
----+-----------------+-------+------------
1 | Amel Kaci | t | 2026-10-11
2 | Youssef Ben Ali | t | 2026-10-11
3 | Claire Dupont | t | 2026-10-11
(3 lignes)Jointure et agrégat, montant de chaque commande :
SELECT c.nom, o.id AS commande, o.passee_le, sum(l.quantite * l.prix_unit) AS montant
FROM commande o
JOIN client c ON c.id = o.client_id
JOIN ligne_commande l ON l.commande_id = o.id
GROUP BY c.nom, o.id, o.passee_le
ORDER BY montant DESC; nom | commande | passee_le | montant
-----------------+----------+------------+---------
Youssef Ben Ali | 3 | 2026-10-06 | 46.00
Amel Kaci | 1 | 2026-10-01 | 22.00
Amel Kaci | 2 | 2026-10-05 | 9.80
(3 lignes)Total par client, clients sans commande compris ; NULLS LAST place le total absent en fin de liste :
SELECT c.nom, count(DISTINCT o.id) AS nb_commandes, sum(l.quantite * l.prix_unit) AS total
FROM client c
LEFT JOIN commande o ON o.client_id = c.id
LEFT JOIN ligne_commande l ON l.commande_id = o.id
GROUP BY c.id, c.nom
ORDER BY total DESC NULLS LAST; nom | nb_commandes | total
-----------------+--------------+-------
Youssef Ben Ali | 1 | 46.00
Amel Kaci | 2 | 31.80
Claire Dupont | 0 |
(3 lignes)Voir 36. Requêtes SELECT et 37. Fonctions SQL.
32.11 Créer un index#
Un index accélère les recherches sur une colonne souvent filtrée qui n'est ni clé primaire ni UNIQUE :
CREATE INDEX idx_commande_date ON commande (passee_le);CREATE INDEXIl se retire sans nommer la table, comme dans PostgreSQL : DROP INDEX idx_commande_date; (étiquette DROP INDEX).
Un index posé sur la colonne d'une clé étrangère (CREATE INDEX … ON commande (client_id)) se retire comme un autre : la contrainte reste contrôlée, plus lentement sans lui. Un nom d'index est unique dans son schéma (42P07 pour un second index du même nom, même sur une autre table).
32.12 Voir le schéma#
Avec psql, \dt liste les tables et \d client décrit une table. Sans psql, les mêmes renseignements se lisent dans pg_catalog et information_schema, ou par les instructions SHOW de MIRAJ, qui restent disponibles.
Lister les tables du schéma courant :
SELECT tablename FROM pg_tables WHERE schemaname = 'ventes' ORDER BY tablename; tablename
----------------
article
client
commande
ligne_commande
(4 lignes)SHOW TABLES; rend la même liste (colonne Tables_in_ventes), et SHOW DATABASES; les schémas.
Décrire les colonnes d'une table :
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'ventes' AND table_name = 'client'
ORDER BY ordinal_position; column_name | data_type | is_nullable | column_default
-------------+-----------------------------+-------------+------------------------------------
id | integer | NO | nextval('client_id_seq'::regclass)
nom | character varying | NO |
email | character varying | NO |
ville | text | YES |
actif | boolean | NO | true
cree_le | timestamp without time zone | YES | now()
(6 lignes)Voir la définition complète d'une table, écrite en langage PostgreSQL :
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")Lister les index :
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)Le chapitre 41. Catalogue et outils décrit pg_catalog et information_schema.
32.13 Différences à connaître dès le départ#
MIRAJ parle la grammaire de PostgreSQL, mais c'est son propre moteur qui exécute. Les écarts que l'on rencontre le plus tôt :
| Sujet | Sur une instance MIRAJ en langage PostgreSQL |
|---|---|
| Base et schéma | une seule base miraj ; un schéma par base MIRAJ ; pas de schéma public (32.2) |
| Casse des noms | tous les identifiants sont insensibles à la casse, même entre guillemets : "Client" et client désignent la même table |
| Casse des textes | les comparaisons de textes respectent la casse (nom = 'AMEL KACI' ne trouve pas Amel Kaci) ; LIKE et ~ la respectent, ILIKE et ~* l'ignorent, comme dans PostgreSQL |
| Division | entre deux entiers, / est une division entière : 7 / 2 vaut 3, comme div(7, 2) ; 7.0 / 2 vaut 3.5000000000000000 |
| Division par zéro | erreur 22012, comme PostgreSQL |
| Noms de contraintes | ceux de MIRAJ : clé primaire <table>_pkey, unicité du nom de la colonne (email), clés étrangères <table>_ibfk_<n>, CHECK CONSTRAINT_<n> |
| Messages d'erreur | SQLSTATE de PostgreSQL, texte de MIRAJ (32.8) |
| Comptes et droits | CREATE USER … IDENTIFIED BY ou WITH PASSWORD, droits sur un schéma par GRANT … ON schéma.* (32.9) |
| Niveau d'isolement | SHOW transaction_isolation rend read committed par défaut |
| Paramètres de démarrage | les options de la connexion (-c statement_timeout=5000, --lock_timeout=2s) sont appliquées comme autant de SET ; une option inconnue (42704) ou réservée au démarrage du serveur PostgreSQL (shared_buffers… : 55P02) refuse la connexion ; les paramètres du message de démarrage des pilotes (TimeZone, DateStyle, extra_float_digits…) le sont aussi, un nom inconnu y étant ignoré |
La syntaxe propre au langage MIRAJ est refusée sur une telle instance, avec une erreur 42601 qui nomme la construction :
SELECT `nom` FROM client;
SELECT * FROM client LIMIT 1, 2;ERREUR 42601 : syntax error at or near "`" at line 1: a backquoted identifier is MIRAJ syntax, not accepted in the PostgreSQL language
ERREUR 42601 : syntax error at or near "," at line 1: LIMIT offset, count is MIRAJ syntax, not accepted in the PostgreSQL languageLa liste complète de ce qui est accepté, refusé ou pas encore pris en charge est au chapitre 30.
32.14 Pour la suite#
Vous savez maintenant démarrer une instance en langage PostgreSQL, vous y connecter, créer un schéma et des tables, écrire, lire, indexer et inspecter le schéma. Les chapitres suivants détaillent chaque sujet :
- 33. Types de données pour choisir le type d'une colonne,
- 34. Langage DDL et 35. Langage DML pour les instructions de définition et d'écriture,
- 36. Requêtes SELECT et 37. Fonctions SQL pour la lecture,
- 38. Transactions et session pour les transactions et les réglages de session,
- 39. Comptes et privilèges pour les comptes, l'authentification et TLS,
- 40. Routines et déclencheurs pour PL/pgSQL,
- 41. Catalogue et outils pour
pg_catalog, les clients, les pilotes et les outils de MIRAJ.