Aller au contenu
guide technique

Poser des questions à PostgreSQL en langage naturel

Interroger PostgreSQL en langage naturel : le rôle en lecture seule, la validation du SQL généré avant exécution, les pièges des poolers et des schémas, et les limites.

Par l'équipe Datarelix · Publié le · Mis à jour le · Vérifié le

Vous pouvez poser vos questions à PostgreSQL en langage courant, et ce qui décide de la sûreté de l’opération n’a rien à voir avec le modèle. C’est le rôle en lecture seule que vous accordez, et la validation appliquée au SQL généré avant son exécution. Réglez ces deux points correctement, et l’interrogation en langage naturel n’est qu’un SELECT cadré derrière une meilleure interface. Réglez-les mal, et une instruction générée atteint la production avec des droits d’écriture.

Avant de commencer

Pour les ingénieurs et les analystes qui exploitent PostgreSQL — RDS, Cloud SQL, Azure Database, Supabase, Neon ou une instance auto-hébergée — et qui évaluent s’il est prudent d’y brancher l’interrogation en langage naturel. Ce guide suppose que vous savez créer un rôle et exécuter un GRANT. Pour le formulaire de connexion pas à pas, voyez le guide de connexion PostgreSQL.

Est-il sûr d’interroger PostgreSQL en langage courant ? Les deux frontières qui comptent

Schéma d'une question transmise à un modèle qui ne reçoit que la structure du schéma et renvoie un plan structuré, franchissant une frontière d'identifiants en pointillés vers un validateur en lecture seule, puis un service de requêtes isolé détenant les identifiants chiffrés, puis PostgreSQL exécuté sous un rôle limité au SELECT. Une flèche de retour ramène la réponse et ses preuves à l'utilisateur, tandis qu'une flèche barrée montre que les lignes de résultat ne repartent pas vers le modèle.
Deux frontières indépendantes : le rôle de base de données limite ce qui est atteignable ; le validateur limite ce qui peut s'exécuter.

Première frontière : le rôle PostgreSQL

C’est la frontière que vous contrôlez, et celle qui tient même si tout le reste cède. Un rôle doté de CONNECT, USAGE et SELECT — et de rien d’autre — ne peut pas écrire, quel que soit le SQL qui lui parvient. Les privilèges minimaux :

CREATE USER datarelix_reader WITH PASSWORD 'choose-a-strong-password';
GRANT CONNECT ON DATABASE your_db TO datarelix_reader;
GRANT USAGE ON SCHEMA public TO datarelix_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO datarelix_reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT ON TABLES TO datarelix_reader;

La ligne ALTER DEFAULT PRIVILEGES est celle que l’on oublie, et les nouvelles tables disparaissent alors sans bruit jusqu’à ce que quelqu’un refasse le GRANT. Faites-en un rôle LOGIN qui n’est pas superutilisateur. Le modèle de privilèges complet est décrit dans la référence GRANT de PostgreSQL.

À partir de PostgreSQL 14, il existe un chemin plus court : GRANT pg_read_all_data TO datarelix_reader; accorde la lecture sur toutes les tables de la base. Cette ligne remplace les quatre lignes GRANT/ALTER DEFAULT PRIVILEGES et ne se périme jamais, mais elle porte sur toute la base — les privilèges explicites ci-dessus sont plus stricts si vous voulez restreindre la connexion à un seul schéma.

Vérifiez les privilèges avant de quitter la base. Connecté en tant que datarelix_reader :

-- Should return true for a table you expect to be readable:
SELECT has_table_privilege('datarelix_reader', 'public.orders', 'SELECT');

-- Should return only the tables you intended to expose:
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY 1, 2;

-- Should fail with "permission denied", proving the boundary:
CREATE TABLE should_not_work (id int);

Si la deuxième requête liste des schémas que vous ne vouliez pas exposer, corrigez-le ici plutôt que de compter sur le paramètre de schéma autorisé de la connexion — ce sont les privilèges qui tiennent, quoi qu’il arrive.

Deuxième frontière : la validation avant exécution

Chaque instruction générée est analysée en arbre syntaxique complet et rejetée si ce n’est pas une lecture — uniquement SELECT, CTE et opérations ensemblistes. INSERT, UPDATE, DELETE, MERGE, le DDL et les commandes d’administration n’ont aucun passage, pas plus que les échappatoires propres à PostgreSQL comme COPY, DO, CALL, TRUNCATE, VACUUM ou PREPARE/EXECUTE. C’est important parce qu’une analyse syntaxique n’est pas un filtrage de mots-clés : un DELETE masqué par des commentaires reste un DELETE dans l’arbre syntaxique. L’argument général est développé dans l’analyse de données par IA en lecture seule.

Les deux frontières sont une redondance volontaire. Les privilèges suffiraient à eux seuls ; le validateur intercepte en plus les requêtes coûteuses mais légales, et applique une limite de lignes automatique, pour qu’une question sans bornes ne renvoie pas un résultat sans bornes.

À quoi ressemble le SQL généré

Sur le schéma fictif d’un commerçant — customers, orders, order_items — la question « Quels ont été nos 10 meilleurs clients en chiffre d’affaires au T2 2026 ? » produit quelque chose comme :

SELECT
  c.customer_id,
  c.name,
  SUM(oi.quantity * oi.unit_price) AS revenue
FROM customers AS c
JOIN orders AS o ON o.customer_id = c.customer_id
JOIN order_items AS oi ON oi.order_id = o.order_id
WHERE o.status = 'completed'
  AND o.ordered_at >= DATE '2026-04-01'
  AND o.ordered_at < DATE '2026-07-01'
GROUP BY c.customer_id, c.name
ORDER BY revenue DESC
LIMIT 10;

C’est du PostgreSQL idiomatique : des intervalles de dates semi-ouverts plutôt que BETWEEN, date_trunc('quarter', …) quand il faut une granularité de période, LIMIT/OFFSET pour la pagination, des guillemets droits pour les identifiants qui en ont besoin. Un système qui génère du SQL générique produira TOP 10 ou des identifiants entre accents graves, que PostgreSQL rejette sans appel — le mécanisme général est expliqué dans text-to-SQL expliqué.

Remarquez aussi ce qui est décidé ici : status = 'completed', le brut plutôt que le net des remboursements, un trimestre calendaire plutôt que les 90 derniers jours. Ce sont des interprétations. Voir la requête, c’est ce qui vous permet de les contrôler — tout l’intérêt de vérifier une réponse générée par IA.

Ce qui, sur PostgreSQL, va vous poser problème

RéalitéCe qui se passeQue faire
Poolers en mode transactionLe pooler en mode transaction de Supabase (port 6543) et PgBouncer en mode transaction ne prennent pas en charge les instructions préparées. Vous obtenez prepared statement … already exists.Utilisez une connexion directe ou un pooler en mode session (Supabase : port 5432).
Noms d’utilisateur du pooler SupabaseLe nom d’utilisateur est postgres.<project-ref>, pas postgres, et l’authentification échoue de façon déroutante.Copiez le nom d’utilisateur depuis la chaîne de connexion du pooler, pas depuis la connexion directe.
Azure Flexible ServerPar habitude, on ajoute user@servername. Ce suffixe n’a jamais été nécessaire que pour Single Server, aujourd’hui retiré.Utilisez le nom d’utilisateur simple.
Un schéma par connexionLa portée est un schéma unique. Les tables d’un second schéma sont invisibles, et le modèle répondra avec assurance à partir de ce qu’il peut voir.Créez une connexion par schéma.
Valeurs par défaut du mode SSLrequire chiffre mais ne vérifie pas le certificat du serveur — une protection contre l’écoute passive, pas contre un MITM actif.Utilisez verify-full pour un PostgreSQL managé en production.
Vues et vues matérialiséesElles sont lisibles et souvent de meilleures cibles que les tables de base, car elles portent en général les définitions de votre équipe.Pointez les privilèges vers votre schéma analytique plutôt que vers l’ingestion brute.
Row-Level SecurityLes politiques RLS s’appliquent au rôle qui se connecte. Sur Supabase, où RLS est actif par défaut, un rôle en lecture seule sans politique correspondante ne voit aucune ligne : l’outil signale un résultat vide plutôt qu’une erreur.Écrivez une politique SELECT pour le rôle lecteur sur chaque table que vous exposez. N’accordez pas BYPASSRLS — cela annulerait la frontière que vous venez de construire.

Ce que le modèle reçoit, et ce qu’il ne reçoit pas

Le modèle reçoit les noms de tables, les noms de colonnes, les types de données, les clés primaires et étrangères, ainsi que les descriptions que vous avez rédigées. Il ne reçoit pas les lignes, et il ne reçoit pas la chaîne de connexion — celle-ci reste dans la couche d’exécution, ce qu’explique comment les identifiants restent hors du modèle.

Une exception : une valeur de réponse sur une seule ligne peut être reprise dans le contexte des questions suivantes, pour qu’un « et par rapport à l’an dernier ? » ait du sens — ce qui entre dans le contexte. Les tableaux de résultats, non.

La structure du schéma ne suffit pas non plus à obtenir de bonnes réponses. Les schémas PostgreSQL sont pleins de colonnes status, type et amount dont le sens n’existe que dans la tête de quelqu’un. Y attacher des descriptions d’une ligne comble cet écart — voyez pourquoi le contexte sémantique compte.

Connecter PostgreSQL à Datarelix

Datarelix se connecte à PostgreSQL 13+ avec un nom d’utilisateur et un mot de passe, ou avec une chaîne de connexion postgresql:// ; le mot de passe est stocké chiffré, tous les autres champs comme configuration en clair. L’introspection lit le catalogue à travers votre rôle : ce que le rôle en lecture seule ne voit pas — une table dans un schéma que vous n’avez pas accordé — est tout aussi invisible pour Datarelix. Elle remonte les clés primaires, les clés étrangères, la nullabilité, les valeurs par défaut et les types ; et une analyse IA du schéma, facultative, propose des descriptions et déduit les relations non déclarées à partir des conventions de nommage — utile sur les schémas où les clés étrangères n’ont jamais été déclarées.

La configuration, les modes SSL et le tableau de dépannage complet se trouvent dans le guide de connexion PostgreSQL.

Là où cela s’arrête sur PostgreSQL

Rien ici n’écrit : pas d’INSERT, pas de DDL, pas de migrations, pas d’administration. C’est voulu par conception ; aucune voie d’écriture n’est prévue. La portée est d’un schéma par connexion : les jointures entre schémas dans une même question sont donc exclues. Une limite de lignes s’applique côté serveur, donc « exporter toute la table » n’est pas le bon outil.

La qualité des réponses suit la qualité du schéma. Sur un schéma analytique bien modélisé, aux colonnes décrites, les résultats sont bons ; sur des tables opérationnelles brutes, aux noms énigmatiques et aux codes de statut non documentés, vous obtiendrez du SQL impeccable sur la mauvaise colonne. Les clés étrangères jamais déclarées ne peuvent qu’être déduites, et la déduction se trompe parfois — vérifiez les jointures sur les premières réponses, pour tout schéma que vous n’avez pas modélisé vous-même.

Sources

La syntaxe des privilèges et leur comportement suivent la documentation GRANT de PostgreSQL ; la sémantique des modes SSL suit la prise en charge SSL de libpq. Le comportement des poolers est conforme à la documentation de connexion de Supabase. Les affirmations produit décrivent Datarelix tel que livré en août 2026 et font foi sur la page sécurité. L’exemple SQL s’appuie sur un schéma de démonstration fictif : il est illustratif, ce n’est pas une sortie réelle.

Créez le rôle, puis posez une question dont vous connaissez la réponse

Créez le rôle en lecture seule ci-dessus, suivez le démarrage rapide et posez une question dont vous connaissez déjà la réponse. Lisez le SQL généré avant de lire le chiffre. Vos 14 premiers jours s’exécutent aux limites du plan Pro, sans carte bancaire.