Depuis 2023, je travaille sur la conversion de code SQL d’Oracle, MySQL et SQL Server vers PostgreSQL. Le problème est sérieux. Voici quelques exemples concrets :
Le T-fighter ou jointure externe:
-- Oracle
SELECT *
FROM a, b
WHERE b.a_id (+) = a.id;
-- PostgreSQL
SELECT *
FROM a
LEFT OUTER JOIN b
ON b.a_id = a.id;
START WITH et les jointures récursives:
-- Oracle
SELECT * FROM a
START WITH a.id = 1
CONNECT BY a.parent = PRIOR a.id;
-- PostgreSQL
WITH RECURSIVE recursive_a(id) AS (
SELECT id
FROM a
WHERE id = 1
UNION
SELECT a.id
FROM a
JOIN recursive_a ON a.parent = recursive_a.id
)
SELECT * FROM recursive_a;
Celle-là est violente !
État des lieux
En 2023, la solution de référence était le moteur d’expressions rationnelles d’ora2pg. Malgré sa maturité, ce moteur présente de nombreuses faiblesses: très fragile par principe sur un langage aussi souple que SQL, aucun test de non-régression, des inversions de sens dans la requête finale, suppression systématique de commentaires de code, performance limitée, difficile à réutiliser en CLI ou API, etc.
C’était l’explosion de l’IA. Que faire ? Faut-il encore concevoir des structures de données et des algorithmes ? Ou plutôt attendre que les IA résolvent tous les problèmes ?
Une solution est peu explorée: la transpilation. C’est une technique très courante dans le monde javascript: TypeScript est un langage transpilé. Nous avons d’abord étudié cette piste.
tree-sitter, GritQL, etc.
Il y a énormément de parser SQL dans la nature: tree-sitter pour les éditeurs, sqlparser-rs et ses dérivés mais essentiellement: des forks du parser de requêtes de PostgreSQL.
tree-sitter est très incomplet sur SQL, son but n’est pas de comprendre les syntaxes. SQL est un gros défi pour le format JSON de définition des grammaires dans tree-sitter.
GritQL est très intéressant fonctionnellement, mais basé sur tree-sitter et dépend de JS. De plus, Grit ne permet pas d’enrichir le moteur avec le modèle de donnée pour connaître les types des colonnes, des opérateurs, etc. Cela le rend plus fragile.
[sqlglot] a le même problème: il ne sait traduire que des syntaxes simples, et encore.
Par exemple, sqlglot convertit SELECT SYSDATE FROM DUAL en SELECT CURRENT_TIMESTAMP FROM DUAL.
La clause FROM DUAL n’est pas supprimée.
Pire, l’opérateur joinmark est tout simplement supprimé sans autre conversion!
Ensuite, vous avez une très longue liste de parser de requêtes SQL,
pour beaucoup basés sur le parser de PostgreSQL.
À l’exception notable de sqlparser-rs.
Ces bibliothèques sont orientées interprétation de requêtes.
Toutes perdent les commentaires, la casse et tous le style du code.
Par exemple, CAST(a AS real) devient a::float4,
le code est bien valide, mais la syntaxe original est perdue.
Perdre les commentaires est inacceptable sur du code historique,
parfois simplement pour des questions juridiques.
Toutes ces bibliothèques sont totalement à côté pour le code procédural.
Bref, il faut se retrousser les manches.
Parser manuel ou généré
Au début, écrire un parser est impressionnant, surtout pour le langage SQL. J’ai donc étudié des générateurs de parser: ANTLR4, lark, etc. Ces solutions se cassent souvent les dents sur le SQL, surtout multi-dialectes. Les fichiers de grammaires sont d’obscurs monolithes. Le code généré est très lourd.
Réutiliser un parser n’économise pas de concevoir l’AST, c’est-à-dire la structure de donnée manipulée par la logique de conversion. À Dalibo, j’ai choisi de me lancer dans ce projet ambitieux: un transpilateur SQL universel, basé sur un parser impératif manuel.
Mais le plus intéressant est d’avoir un parser impératif pour contrôler finement l’algorithme. Je reviendrais dans d’autres articles sur l’AST, le parser et les règles de transpilation. Pour faire un rapide bilan, voici un tableau comparatif des solutions:
| Critère | Générateur | Manuel |
|---|---|---|
| Modèle | Déclaratif | Impératif |
| Lignes de code | 300k | 11k |
| Multi-dialecte | non | oui |
| Tests unitaires | à faire | inclus |
| Temps de compilation | très long | instantané |
| Nombres de fichiers | <10 | ~80 |
| Taille de fichier | >10k lignes | entre 100 et 300 lignes |
Pourquoi pas l’IA ?
Certes, les IA sont bien plus performantes en 2026. Néanmoins, l’IA n’est pas une solution sans contre-partie.
L’IA a un coût. L’agent a tendance à faire beaucoup d’essais-erreurs et l’on capitalise peu sur ces échecs, avouons-le. Un schéma SQL contient beaucoup d’expressions: valeurs par défaut, colonnes virtuelles, contraintes CHECK, index fonctionnels, data type complexes, vues, code procédural; sans compter les requêtes applicatives. Plus une application a de code SQL embarqué, plus le projet de migration sera long: audit avant migration effective, multiples itérations, sans compter les rebasages du projet suite aux nouveaux développements. Autant d’occasions d’exécuter le moteur de conversion. Le budget token va exploser !
L’IA a son entropie: tendance à plaire (phénomène du sycophante), hallucination, essais-erreurs et fausses pistes. Une IA a besoin d’informations fiables, déterministes et de qualité. Le contexte pour faire une bonne conversion est très large: quelle version d’Oracle ? Quels types de données ? Selon le RAG disponible, le taux d’hallucination d’un modèle descend au mieux à 3 à 5 %. Sur du code historique avec rarement des tests de non régression, le taux d’hallucination monte jusqu’à 30%. C’est un risque excessif pour du code métier en base de données. HexaCluster, spécialiste de la conversion de code SQL avec LLM, utilise également sur des algorithmes déterministes pour compenser les hallucinations.

En réalité, la question n’est pas avec ou sans IA mais quelle place pour l’IA ? On peut présenter l’utilisation de l’IA sur un spectre. D’un côté la méthode traditionnelle sans IA, de l’autre tout balancer à l’IA et attendre. Entre les deux, on peut utiliser l’IA pour programmer, on peut aussi donner un outil de validation à l’IA pour la guider. C’est plutôt là que se positionne transqlate.
Un choix varié de solutions
Le logiciel libre propose aujourd’hui deux solutions pour convertir du code: le moteur d’expression rationnel d’ora2pg ou le transpilateur déterministe transqlate. Bien que propriétaire, nous allons le comparer à une IA pour le principe:
| Critère | ora2pg | PostgreSQL for VSCode | transqlate |
|---|---|---|---|
| Licence | GPL | souvent propriétaire | PostgreSQL |
| Gouvernance | individuelle | corporation | Entreprise |
| Technique | regexp | Inférence | Édition d’AST |
| Syntaxes complexes | oui | oui | oui |
| Fiabilité | Faible | Modérée | Forte |
| Vitesse | Modérée | Lente | rapide |
| Privé/hors-ligne | oui | non | oui |
| Fichier plat | non | oui | oui |
| SQL dans l’application | non | oui | non |
| API | non | non | Go |
| Intégration IDE | non | VS Code | manuelle |
| Destination | tout PostgreSQL | Azure SQL | tout PostgreSQL |
Il est pertinent d’utiliser l’IA pour implémenter un transpiler. Déléguer toute la logique à l’IA est plus coûteux pour un résultat moins sûr. Le choix final dépendra du projet, de la dette technique, du budget et des compétences disponibles.
D’autres enjeux de la migration demeurent : la stratégie de conversion et le transfert performant des données. Le transpilateur intégrable dans le logiciel de migration ouvre de nombreuses pistes pour migrer efficacement. 2026 est une belle période pour envisager la migration vers PostgreSQL grâce au foisonnement de solutions.