Académique Documents
Professionnel Documents
Culture Documents
20.00-preview
Dalibo SCOP
https://dalibo.com/formations
Formation MIGORPG
TITRE : Migrer d’Oracle à PostgreSQL
SOUS-TITRE : Formation MIGORPG
REVISION: 20.00-preview
DATE: 18 décembre 2019
ISBN: Version provisoire de démonstration
COPYRIGHT: © 2005-2019 DALIBO SARL SCOP
LICENCE: Creative Commons BY-NC-SA
Le logo éléphant de PostgreSQL (« Slonik ») est une création sous copyright et le nom
« PostgreSQL » est une marque déposée par PostgreSQL Community Association of
Canada.
À propos de DALIBO :
Dalibo ne peut retirer les autorisations concédées par la licence tant que vous appliquez
les termes de cette licence selon les conditions suivantes :
Attribution : Vous devez créditer l’œuvre, intégrer un lien vers la licence et indiquer si des
modifications ont été effectuées à l’œuvre. Vous devez indiquer ces informations par tous
les moyens raisonnables, sans toutefois suggérer que Dalibo vous soutient ou soutient la
façon dont vous avez utilisé ce document.
Pas d’Utilisation Commerciale: Vous n’êtes pas autorisé à faire un usage commercial de ce
document, tout ou partie du matériel le composant.
Partage dans les Mêmes Conditions : Dans le cas où vous effectuez un remix, que vous
transformez, ou créez à partir du matériel composant le document original, vous devez
diffuser le document modifié dans les même conditions, c’est à dire avec la même licence
avec laquelle le document original a été diffusé.
Pas de restrictions complémentaires : Vous n’êtes pas autorisé à appliquer des conditions
légales ou des mesures techniques qui restreindraient légalement autrui à utiliser le doc-
ument dans les conditions décrites par la licence.
Note : Ceci est un résumé de la licence. Le texte complet est disponible ici :
https://creativecommons.org/licenses/by-nc-sa/2.0/fr/legalcode
Pour toute demande au sujet des conditions d’utilisation de ce document, envoyez vos
questions à contact@dalibo.com !
Chers lectrices & lecteurs,
Au-delà du contenu technique en lui-même, notre intention est de transmettre les valeurs
qui animent et unissent les développeurs de PostgreSQL depuis toujours : partage, ou-
verture, transparence, créativité, dynamisme... Le but premier de nos formations est de
vous aider à mieux exploiter toute la puissance de PostgreSQL mais nous espérons égale-
ment qu’elles vous inciteront à devenir un membre actif de la communauté en partageant
à votre tour le savoir-faire que vous aurez acquis avec nous.
Nous mettons un point d’honneur à maintenir nos manuels à jour, avec des informations
précises et des exemples détaillés. Toutefois malgré nos efforts et nos multiples relec-
tures, il est probable que ce document contienne des oublis, des coquilles, des impréci-
sions ou des erreurs. Si vous constatez un souci, n’hésitez pas à le signaler via l’adresse
formation@dalibo.com !
Table des Matières
Licence Creative Commons BY-NC-SA 2.0 FR 5
1 Plan de migration 11
1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.2 Méthodologie de migration . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.3 Recommandations et pièges à éviter . . . . . . . . . . . . . . . . . . . . 18
1.4 Migration du schéma et des données . . . . . . . . . . . . . . . . . . . 29
1.5 Fonctionnalités problématiques . . . . . . . . . . . . . . . . . . . . . . 35
1.6 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
1.7 Annexe : Installation de PostgreSQL depuis les paquets communautaires 43
1.8 Travaux pratiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
1.9 Travaux pratiques (solutions) . . . . . . . . . . . . . . . . . . . . . . . . 55
2 Schéma et Données 60
2.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
2.2 Installation d’Ora2Pg . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
2.3 Configuration d’Ora2Pg . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
2.4 Validation de la configuration . . . . . . . . . . . . . . . . . . . . . . . . 74
2.5 Configuration générique . . . . . . . . . . . . . . . . . . . . . . . . . . 84
2.6 Migration du schéma . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
2.7 Migration des données . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
2.8 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
2.9 Travaux pratiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
2.10 Travaux pratiques (solutions) . . . . . . . . . . . . . . . . . . . . . . . . 135
10
1. PLAN DE MIGRATION
1 PLAN DE MIGRATION
11
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
1.1 INTRODUCTION
Ce module est une introduction aux migrations de Oracle vers PostgreSQL. Nous y abor-
dons comment gérer sa première migration (quelque soit le SGBD source et destination),
des recommandations sur une migration d’Oracle vers PostgreSQL (où nous détaillons les
pièges à éviter et les principales différences entre les deux SGBD), une réflexion sur le con-
tenu de la migration et sur le choix de l’outil idoine, et nous finissons avec quelques détails
sur des fonctionnalités manquantes ou implémentées différemment côté PostgreSQL qui
rendront la migration plus difficile.
La façon dont la première migration va se dérouler est essentielle. C’est sur cette ex-
périence que les autres migrations seront abordées. Si l’expérience a été mauvaise, il
est même probable que les migrations prévues après soient repoussées fortement, voire
annulées.
Il est donc essentiel de réussir sa première migration. Réussir veut aussi dire bien la docu-
menter car elle servira de base pour les prochaines migrations : les méthodes employées
seront réutilisées, et certainement améliorées. Réussir veut aussi dire la publiciser, au
moins en interne, pour que tout le monde sache que ce type de migration est réalisable
et qu’une expérience est disponible en interne.
De plus, cette migration va influencer fortement la vision des développeurs et des util-
isateurs vis-à-vis de ce SGBD. Réussir la migration veut donc aussi dire réussir à faire
apprécier et accepter ce moteur de bases de données. Sans cela, il y a de fortes chances
12
1. PLAN DE MIGRATION
que les prochaines migrations ne soient pas demandées volontairement, ce qui rendra les
migrations plus difficiles.
S’il est trop simple, il n’aura pas réellement de valeur. Par exemple, dans le cas d’une
migration d’une base de 100 Mo, sans routines stockées, sans fonctionnalités avancées,
cela ne constituera pas une base qui permettra d’ aborder tranquillement une migration
d’une base de plusieurs centaines de Go et utilisant des fonctionnalités avancées.
L’inverse est aussi vrai. Un projet trop gros risque d’être un soucis. Prenez une base
critique de plusieurs To, dotée d’un très grand nombre de routines stockées. C’est un
véritable challenge, y compris pour une personne expérimentée. Il y a de fortes chances
que la migration soit longue, dure, mal vécue... et possiblement annulée à cause de sa
complexité. Ceci aura un retentissement fort sur les prochaines migrations.
Il est préférable de choisir un projet un peu entre les deux : une base conséquence
(plusieurs dizaines de Go), avec quelques routines stockées, de la réplication, etc. Cela
aura une vraie valeur, tout en étant à portée de main pour une première migration.
Une fois une telle migration réussie, il sera plus simple d’aborder correctement et sans
crainte la migration de bases plus volumineuses ou plus complexes.
Il faut aussi ne pas oublier que la migration doit impliquer un groupe entier, pas seulement
une personne. Les développeurs, les administrateurs, les équipes de support doivent
tous être impliqués dans ce projet, pour qu’ils puissent intégrer les changements quant à
l’utilisation de ce nouveau SGBD.
13
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
• Chef de projet
• Équipe hétérogène (pas que des profils techniques)
• Recetteurs et utilisateurs nombreux (validation du projet la plus continue possible)
L’équipe du projet de migration doit être interne, même si une aide externe peut être
sollicitée. Un chef de projet doit être nommé au sein d’une équipe hétérogène, composée
de développeurs, d’administrateurs, de testeurs et d’utilisateurs. Il est à noter que les
testeurs sont une partie essentielle de l’équipe.
• Société de service
• Contrat de support
• Expert PostgreSQL
Même si l’essentiel du projet est porté en interne, il est toujours possible de faire appel à
une société externe spécialisée dans ce genre de migrations. Cela permet de gagner du
temps sur certaines étapes de la migration pour éviter certains pièges, ou mettre en place
l’outil de migration.
14
1. PLAN DE MIGRATION
doivent avoir la volumétrie réelle de la base de production, sinon les tests de perfor-
mances n’auront pas vraiment de valeur.
En fait, il faut vraiment que cette migration soit considérée comme un vrai projet, et pas
comme un projet au rabais, ce qui arrive malheureusement assez fréquemment. C’est une
opération essentielle, et des ressources compétentes et suffisantes doivent être offertes
pour la mener à bien.
En soi, passer à PostgreSQL n’est pas une révolution. C’est un moteur de bases de don-
nées comme les autres, avec un support du SQL (et quelques extensions) et ses fonction-
nalités propres. Ce qui change est plutôt l’implémentation mais, comme nous le verrons
dans cette formation, si une fonctionnalité identique n’existe pas, une solution de con-
tournement est généralement disponible.
La majorité des utilisateurs de PostgreSQL vienne à PostgreSQL pour faire des économies
(sur les coûts de licence). Si jamais une telle migration demandait énormément de change-
ments, ils ne viendraient pas à PostgreSQL. Or la majorité des migrations se passe bien, et
les utilisateurs restent ensuite sur PostgreSQL. Les migrations qui échouent sont générale-
ment celles qui n’ont pas été correctement gérées dès le départ (pas de ressources pour
le projet, un projet trop gros dès le départ, etc.).
1.2.6 MOTIVER
• Formation indispensable
• Divers cursus
– du chef de projet au développeur
• Adoption grandissante de PostgreSQL
– pérennité
Le passage à un nouveau SGBD est un peu un saut dans l’inconnu pour la majorité des
personnes impliquées. Elles connaissent bien un moteur de bases de données et souvent
ne comprennent pas pourquoi on veut les faire passer à un autre moteur. C’est pour cela
qu’il est nécessaire de les impliquer dès le début du projet et, le cas échéant, de les former.
15
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Il est possible d’avoir de nombreuses formations autour de PostgreSQL pour les différents
acteurs : chefs de projet, administrateurs de bases de données, développeurs, etc.
1.2.7 VALORISER
De toute façon, les concepts utilisés par PostgreSQL sont très proches des concepts des
moteurs SGBD propriétaires. La majorité du temps, il suffit d’adapter les compétences. Il
n’est jamais nécessaire de reprendre tout à zéro. La connaissance d’un autre moteur de
bases de données permet de passer très facilement à PostgreSQL, ce qui valorise l’équipe.
Contrairement à d’autres projets, le service existe déjà. Les délais sont donc généralement
moins importants, ce qui permet de donner du temps aux personnes impliquées dans le
projet pour fournir une migration de qualité (et surtout documenter cette opération).
1.2.9 COÛTS
• Budget ?
• Open source <> gratuit
– Coûts humains
– Coûts matériels
Une migration aura un coût important. Ce n’est pas parce que PostgreSQL est un logiciel
libre que tout est gratuit. La mise à disposition de ressources humaines et matérielles
aura un coût. La formation du personnel aura un coût. Mais ce coût sera amoindri par
le fait que, une fois cette migration réalisée, les prochaines migrations n’auront un coût
qu’au niveau matériel principalement.
16
1. PLAN DE MIGRATION
1.2.10 QUALITÉ
• Cruciale
– La réussite est obligatoire
• Le travail effectué doit être réutilisable
• Ou tout du moins l’expérience et les méthodologies
La qualité de la première migration est cruciale. Si le but est de migrer les autres bases
de données de l’entreprise, il est essentiel que la première migration soit une réussite
totale. Il est essentielle qu’elle soit documentée, discutée, pour que le travail effectué
soit réutilisable (soit complètement, soit uniquement l’expérience et les méthodes) et que
la prochaine migration soit ainsi moins coûteuse.
• Privilégier la qualité
• Contrôler les coûts
• N’est souvent pas contrainte par des délais stricts
Pour résumer, la première migration doit être suffisamment simple pour ne pas être un
échec et suffisamment complexe pour être en confiance pour les prochaines migrations.
Il est donc essentiel de bien choisir la base de sa première migration.
Il est aussi essentiel d’avoir des ressources humaines et matérielles suffisantes, tout en
contrôlant les coûts.
Enfin, il est important de ne pas stresser les acteurs de cette migration avec des délais
difficiles à tenir. Le service est déjà présent et fonctionnel, la première migration doit être
un succès si on veut continuer, autant donner du temps aux équipes responsables de la
migration.
17
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Tous les deux sont des systèmes de gestion de bases de données relationnelles. Tous les
deux utilisent le langage SQL (leur support de la norme diffère évidemment). Tous les
deux ont des connecteurs pour la majorité des langages actuels (l’efficacité et le support
des fonctionnalités du moteur dépendent de l’implémentation des connecteurs).
Par contre, les langages autorisés pour les routines stockées sont différents, y compris
ceux disponibles par défaut.
Même si les fonctionnalités majeures sont présentes dans les deux moteurs, les détails
d’implémentation et de mise en place sont le cœur du problème. Par exemple, les ta-
blespaces sont disponibles dans les deux SGBD mais l’implémentation est différente. Pour
Oracle, il s’agit d’un fichier qu’il faut dimensionner. Pour PostgreSQL, il s’agit d’un réper-
toire dont la taille est libre. Il y a des avantages et des inconvénients à chaque implémen-
tation. Au niveau d’Oracle, il suffit de surveiller l’espace libre au niveau du tablespace.
Avec PostgreSQL, la supervision se fait au niveau du système d’exploitation. C’est plutôt
un défaut de PostgreSQL car, de nos jours, les équipes système et base de données sont
séparées. Par contre, le problème au niveau Oracle vient du dimensionnement des ta-
blespaces. Il faut surveiller plus fortement au niveau Oracle car il est nécessaire d’agrandir
le tablespace si jamais ce dernier vient à être utilisé complètement. Il n’y a pas ce prob-
lème avec PostgreSQL, tout du moins tant qu’il reste de l’espace disque.
Cette partie est donc consacrée à la revue des pièges à éviter et à montrer les différences
d’implémentation pouvant susciter des problèmes ou des incompréhensions lors d’une
migration.
18
1. PLAN DE MIGRATION
PostgreSQL et Oracle :
• Ont le même langage d’accès aux données (SQL)
– mais des « variantes » différentes (extensions au standard)
• De nombreux concepts en commun:
– Transactions et savepoints
– MVCC et verrouillage
• Conservation
– des logiques applicative et algorithmique
– de l’architecture applicative
Ils partagent aussi certains concepts, comme les transactions et les points de retourne-
ments (savepoint), MVCC et la gestion des verrous. Cela permet de conserver les logiques
applicative et algorithmique, au moins jusqu’à une certaine mesure.
Pour PostgreSQL, si vous souhaitez pouvoir annuler des modifications, vous devez utiliser
BEGIN avant d’exécuter les requêtes de modification. Toute transaction qui commence
par un BEGIN doit être validée avec COMMIT ou annulée avec ROLLBACK. Si jamais la
19
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Par exemple, si on insère une donnée dans une table, sans faire de BEGIN avant, et qu’on
essaie d’annuler cette insertion, cela ne fonctionnera pas :
dev2=# CREATE TABLE t1(id integer);
CREATE TABLE
dev2=# INSERT INTO t1 VALUES (1);
INSERT 0 1
dev2=# ROLLBACK;
NOTICE: there is no transaction in progress
ROLLBACK
dev2=# SELECT * FROM t1;
id
----
1
(1 row)
Autre différence au niveau transactionnel : il est possible d’intégrer des ordres DDL dans
des transactions. Par exemple :
dev2=# BEGIN;
BEGIN
dev2=# CREATE TABLE t2(id integer);
CREATE TABLE
dev2=# INSERT INTO t2 VALUES (1);
INSERT 0 1
dev2=# ROLLBACK;
ROLLBACK
dev2=# INSERT INTO t2 VALUES (2);
ERROR: relation "t2" does not exist
LINE 1: INSERT INTO t2 VALUES (2);
^
Enfin, quand une transaction est en erreur, vous ne sortez pas de la transaction. Vous
20
1. PLAN DE MIGRATION
Les types smallint, integer, bigint, float, real, double precision sont plus rapides
que le type numeric sous PostgreSQL : ils utilisent directement les fonctions câblées des
processeurs. Il faut donc les privilégier.
21
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Oracle ne dispose pas du type booléen. Du coup, les développeurs utilisent fréquemment
un entier qu’ils mettront à 0 pour FALSE et à 1 pour TRUE. Un système de migration ne
saura pas dire si cette colonne de type numeric est, pour le développeur, un booléen ou
une valeur entière. Du coup, le système de migration utilisera le typage de la colonne, à
savoir entier. Or, les ORM chercheront un booléen parce que le code applicatif indique
un booléen. Cela provoquera une erreur sur PostgreSQL, comme le montre l’exemple
suivant :
dev2=# INSERT INTO t1 VALUES (true);
ERROR: column "id" is of type integer but expression is of type boolean
LINE 1: insert into t1 values (true);
^
HINT: You will need to rewrite or cast the expression.
dev2=# INSERT INTO t1 VALUES ('t');
ERROR: invalid input syntax for integer: "t"
LINE 1: insert into t1 values ('t');
^
dev2=# CREATE TABLE t3 (c1 boolean);
CREATE TABLE
dev2=# INSERT INTO t3 VALUES (true);
INSERT 0 1
dev2=# INSERT INTO t3 VALUES ('f');
INSERT 0 1
dev2=# SELECT * FROM t3;
c1
----
t
f
(2 rows)
22
1. PLAN DE MIGRATION
Au niveau de PostgreSQL, il existe trois types de données pour les chaînes de caractères :
char, varchar et text. Le type varchar2 d’Oracle est l’équivalent du type varchar de
PostgreSQL. Il est possible de ne pas donner de taille à une colonne de type varchar, ce
qui revient à la déclarer de type text. Dans ce cas, la taille maximale est de 1 Go. Suivant
l’encodage, le nombre de caractères intégrables dans la colonne diffère.
La grosse différence entre PostgreSQL et Oracle pour les chaînes de caractères tient
dans la façon dont les chaînes vides sont gérées. Oracle ne fait pas de différence en-
tre une chaîne vide et une chaîne NULL. PostgreSQL fait cette différence. Du coup, tous
les tests de chaînes vides effectuées avec un IS NULL et tous les tests de chaînes NULL
effectués avec une comparaison avec une chaîne vide ne donneront pas le même résultat
avec PostgreSQL. Ces tests doivent être vérifiés systématiquement par les développeurs
d’applications et de routines stockées.
dev2=# SELECT cast('' AS varchar) IS NULL;
?column?
----------
f
(1 row)
23
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
L’implémentation des types binaires sur PostgreSQL est très particulière. De plus, elle est
double, dans le sens où vous avez deux moyens d’importer et d’exporter des données
binaires dans PostgreSQL.
La deuxième implémentation est un type de données appelé bytea. Comme toutes les
colonnes dans PostgreSQL, sa taille maximale est 1 Go, ce qui est inférieur à la taille
maximale d’un Large Object. Cependant, c’est son seul défaut.
Bien que l’implémentation des Large Objects est en perte de vitesse à cause des nombreux
inconvénients inhérents à son implémentation, elle a été l’objet d’améliorations sur les
dernières versions de PostgreSQL : gestion des droits de lecture ou écriture des Large
Objects, notion de propriétaire d’un Large Object, limite de taille relevée à 4 To. Elle n’est
donc pas obsolète.
24
1. PLAN DE MIGRATION
L’un des gros avantages de PostgreSQL est son extensibilité. Mais même sans cela, Post-
greSQL propose de nombreux types natifs qui vont bien au-delà des types habituels. Ce
sont des types métiers, pour le réseau, la géométrie, la géographie, la gestion du temps,
la gestion des intervalles de valeurs, etc.
Il est donc tout à fait possible d’améliorer une application en passant sur des types spé-
cialisés de PostgreSQL.
• Date
– sous Oracle : YYYY/MM/DD HH:MM:SS
– sous PostgreSQL: YYYY-MM-DD (conforme SQL)
• Time
– sous Oracle : YYYY/MM/DD HH:MM:SS
– sous PostgreSQL : HH:MM:SS.mmmmmmm (µs)
• Gestion des fuseaux horaires
– sous PostgreSQL, par défaut
– timestamp sous PostgreSQL : Date+Time (+TZ)
• Format de sortie conforme SQL sous PostgreSQL :
YYYY-MM-DD HH24:MI:SS.mmmmmmm+TZ
• Type interval
– Date1-Date2 => Interval
Oracle a tendance à mélanger un peu tous les types dates. Ce n’est pas le cas au niveau
de PostgreSQL. Une colonne de type date au niveau de PostgreSQL contient seulement
une date, il n’y a pas d’heure ajoutée. Une colonne de type time au niveau de PostgreSQL
contient seulement un horodatage (heure, minute, seconde, milliseconde), mais pas de
date.
25
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Par défaut, PostgreSQL intègre le fuseau horaire dans les types timestamp ( date et heure).
Le stockage est fait en UTC, mais la restitution dépend du fuseau horaire indiqué par le
client.
Une ancienne écriture des jointures utilisait le signe +. Ces requêtes doivent être réécrites
en utilisant la notation standard (LEFT JOIN) du standard SQL.
De nombreuses fonctions ont des noms différents entre Oracle et PostgreSQL. Par exem-
ple, la fonction NVL sous Oracle s’appelle coalesce sous PostgreSQL.
• Casse par défaut du nom des objets différente entre Oracle et PostgreSQL :
• Si casse non spécifiée :
– majuscule sous Oracle
– minuscule sous PostgreSQL
• Forcer la casse
– " " autour des identifiants.
La casse par défaut des objets est différente entre Oracle et PostgreSQL. C’est d’ailleurs
un exemple où Oracle respecte mieux le standard SQL que PostgreSQL. Si la casse n’est
26
1. PLAN DE MIGRATION
pas spécifiée, le nom de l’objet sera en majuscule sous Oracle et en minuscule sous Post-
greSQL. Pour forcer la casse, il faudra utiliser des guillemets doubles, comme le montre
cet exemple :
dev2=# CREATE TABLE toto(id integer);
CREATE TABLE
dev2=# CREATE TABLE TitI(id integer);
CREATE TABLE
dev2=# \d
List of relations
Schema | Name | Type | Owner
--------+------+-------+-----------
public | t1 | table | guillaume
public | t3 | table | guillaume
public | titi | table | guillaume
public | toto | table | guillaume
(4 rows)
Une simple lecture du code source est facilement source d’erreurs. Bien qu’il soit tou-
jours intéressant de faire une première passe pour corriger les cas les plus flagrants, il est
27
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
nécessaire ensuite de tester l’application sur PostgreSQL et de vérifier dans les journaux
applicatifs les messages d’erreurs qui surviennent. Un outil comme pgBadger permet de
récupérer les erreurs, leur fréquence et les requêtes qui ont causé les erreurs.
Les langages de routines stockées sont différents entre Oracle et PostgreSQL. Même si
PL/pgSQL est un langage assez proche de PL/SQL, cela demandera une revue des rou-
tines stockées et un recodage (automatique ou manuel) des routines.
28
1. PLAN DE MIGRATION
Avant de pouvoir traiter le code, qu’il soit applicatif ou issu des routines stockées, il faut
procéder à la migration du schéma et des données. C’est donc ce dont nous allons parler
dans cette partie.
Généralement, il faudra créer un nouveau schéma, et intégrer les objets par étapes : ta-
bles, index, puis contraintes.
29
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Oracle utilise généralement number pour les types entiers. L’équivalent strict au niveau
PostgreSQL est numeric mais il est préférable de passer à d’autres types de données
comme int (un entier sur quatre octets) ou bigint (un entier sur huit octets) qui sont
bien plus performants.
L’outil pour la migration devra être sélectionné suivant ses possibilités au niveau de la
transformation de certains types en d’autres types, si jamais il est décidé de procéder
ainsi.
L’outil de migration doit pouvoir aussi gérer des types particuliers, comme les types spéci-
fiques à la recherche plein texte, ceux spécifiques aux objets binaires, ceux spécifiques à
la couche spatiale, etc. Il est possible qu’un développement soit nécessaire pour faciliter
la migration. Un outil libre est préférable dans ce cas.
30
1. PLAN DE MIGRATION
Pour des raisons de performances, il est toujours préférable de ne déclarer les index et
les contraintes qu’une fois les tables remplies. L’outil de migration doit aussi prendre cela
en compte : création des tables, remplissage des tables et enfin création des index et
contraintes.
Veut-on :
• Migrer en une seule fois les données ? (« Big Bang »)
• Pouvoir réaliser des incréments ?
• Paralléliser sur plusieurs sessions/threads ?
• Modifier des données « au passage » ?
Toujours dans les décisions à prendre avant la migration, il est important de savoir si on
veut tout migrer d’un coup ou le faire en plusieurs étapes. Les deux possibilités ont leurs
avantages et inconvénients.
Enfin, souhaite-t-on modifier des données lors de l’opération de migration ? là-aussi, cela
peut se concevoir, notamment si certains types de données sont modifiés. Mais c’est une
décision à prendre lors des premières étapes du projet.
31
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Ora2Pg est un outil libre développé par Gilles Darold. Le rythme de développement est
rapide. De nouvelles fonctionnalités sont proposées rapidement, suivant les demandes
des utilisateurs, les nouveautés dans PostgreSQL et les découvertes réalisées par son
auteur.
Les ETL sont intéressants pour les possibilités plus importantes. Ora2Pg ne fait que de la
conversion Oracle vers PostgreSQL et MySQL, alors que les ETL autorisent un plus grand
nombre de source de données et de destination, si bien que l’expérience acquise pour
la migration d’Oracle vers PostgreSQL peut être réutilisé pour réaliser la migration d’un
autre moteur vers un autre moteur ou pour l’import ou l’export de données.
Il est aussi possible de développer sa propre solution si les besoins sont vraiment spé-
cifiques au métier, voire de mixer différentes solutions. Par exemple, il était intéressant
d’utiliser Ora2Pg pour la transformation du schéma et un ETL pour un export et import des
données parallélisés (ce n’est plus le cas maintenant qu’Ora2Pg est lui aussi parallélisé).
• En Perl
• Se connecte à Oracle
• Génère un fichier SQL compatible avec PostgreSQL, en optimisant les types
• Conversion automatique d’une partie du code PL/SQL en PL/pgSQL
• Simple de mise en œuvre
• Rapide au chargement (utilise COPY)
Ora2Pg est un outil écrit en Perl. Il se connecte à Oracle via le connecteur Perl pour
Oracle. Après analyse des catalogues systèmes Oracle et lecture de son fichier de con-
32
1. PLAN DE MIGRATION
L’outil est plutôt simple de mise en œuvre et de prise en main. Il est rapide au chargement,
notamment grâce à sa gestion de la commande COPY.
• Big-Bang
– pas d’incrémental
33
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Les ETL sont spécialisées dans la transformation et le chargement des données. Ils per-
mettent la parallélisation pour leur traitement, ils sont très souples au niveau de la trans-
formation de données. Tout cela leur permet d’être très rapide, certaines fois plus que ne
le peut Ora2Pg.
La migration du schéma est au mieux sommaire, voire inexistante. Ce n’est clairement pas
la fonctionnalité visée par les ETL.
Le paramétrage d’un ETL est souvent très long. Si vous devez migrer les données de 200
tables, vous aurez 200 jobs à créer. Dans ce cas, Ora2Pg est bien plus intéressant, vu que
la migration de la totalité des tables est l’option par défaut.
Ce sont des outils riches et, du coup, complexes. Cela demandera un apprentissage bien
plus long que pour Ora2Pg. Cependant, ils sont utilisables dans bien plus de cas que
Ora2Pg.
34
1. PLAN DE MIGRATION
Certaines fonctionnalités Oracle n’ont pas d’équivalents natifs dans PostgreSQL. Nous
allons étudier les deux cas les plus fréquents : les vues matérialisées et le partitionnement.
• Sous Oracle :
– Stocke le résultat d’une vue physiquement (table)
– Permet la création d’index sur cette table
– Est mise à jour au fil de l’eau ou à intervalle régulier
– Réécriture transparente de requêtes
• Sous PostgreSQL :
– Mise à jour uniquement sur demande
– Pas de réécriture
Le but d’une vue matérialisée est de stocker physiquement le résultat de l’exécution d’une
vue et d’utiliser par la suite ce stockage plutôt que le résultat de l’exécution de la requête.
Il est possible de créer des index sur cette vue matérialisée. Elle est mise à jour soit à la
demande soit au fil de l’eau.
Les vues matérialisées ne sont supportées qu’à partir de la version 9.3 pour PostgreSQL.
Elles ne supportent pas toutes les fonctionnalités qu’offre Oracle : pas de mise à jour au
fil de l’eau, pas de réécriture de requête. La situation devrait cependant s’arranger au fil
des versions. La version 9.4 apporte la mise à jour non bloquante des vues matérialisées.
35
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Avant la 9.3, il fallait créer une vraie table pour la vue matérialisée, et un trigger sur la
table source pour alimenter la table destination.
36
1. PLAN DE MIGRATION
En 9.3, on utilisera plutôt la vue matérialisée car, avec ce nouveau type d’objet, on n’est
plus obligé de passer par un trigger :
Si une mise à jour au fil de l’eau est requise, il faudra forcément passer par des triggers.
38
1. PLAN DE MIGRATION
Par contre, ce genre de rafraîchissement au fil de l’eau de vues matérialisées est très
coûteux. En effet, une vue matérialisée recalcule l’ensemble des données. Par ailleurs,
avant la version 9.4, la vue est inaccessible pendant la durée du rafraîchissement.
Oracle dispose de clauses dans la commande CREATE TABLE permettant de définir sim-
plement les partitions et la méthode de partitionnement.
39
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Depuis PostgreSQL v10, il est possible de déclarer une table comme étant partitionnée
et de déclarer des partitions. La spécification d’une table partitionnée consiste en une
méthode de partitionnement et une liste de colonnes ou expressions à utiliser comme la
clé de partitionnement.
Toutes les lignes insérées dans la table partitionnée seront alors redirigées vers une des
partitions en se basant sur la valeur de la clé de partitionnement.
Les partitions peuvent elles-même être définies comme des tables partitionnées, en
utilisant le sous-partitionnement. Les partitions peuvent avoir leurs propres index,
contraintes et valeurs par défaut, différents de ceux des autres partitions. Les index
doivent être créés séparément pour chaque partition. Voir CREATE TABLE pour plus de
détails sur la création des tables partitionnées et des partitions.
40
1. PLAN DE MIGRATION
Avant la version 10, PostgreSQL ne dispose pas de ces clauses. Il est nécessaire de passer
par l’héritage pour la création des partitions, par l’ajout de contraintes CHECK pour que
le planificateur sache qu’il peut ne parcourir que certaines partitions, et par l’ajout de
triggers pour que les insertions, mises à jour et suppressions se passent correctement.
Autrement dit, la mise en place du partitionnement sous PostgreSQL est lourde et peu
intuitive.
En plus d’une mise en place difficile et d’une administration tout autant complexe, un bon
nombre d’autres inconvénients sont présents.
L’unicité globale ne peut pas être assurée. PostgreSQL dépend d’un index pour assurer
ou forcer cette contrainte, et il n’est pas possible de créer un index sur plusieurs tables.
Du coup, une table partitionnée ne peut pas avoir de contrainte unique ou de clé primaire.
De ce fait, il n’est pas non plus possible d’utiliser des clés étrangères vers cette table.
1.6 CONCLUSION
Points essentiels :
• Grande importance de la première migration.
• Même si Oracle et PostgreSQL sont assez similaires, il y a de nombreuses dif-
férences.
• Étude de la migration, ce qui doit ou pas être migré et comment.
• Choix des outils de migration.
• La majorité du temps de migration est imputable à la conversion du PL/SQL
41
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
1.6.1 QUESTIONS
42
1. PLAN DE MIGRATION
ATTENTION : Red Hat et CentOS fournissent par défaut des versions de Post-
greSQL qui ne sont plus supportées. Ne jamais installer les packages postgresql,
postgresql-client et postgresql-server !
Installation de PostgreSQL 12 :
# export PGSETUP_INITDB_OPTIONS='--data-checksums'
# /usr/pgsql-12/bin/postgresql-12-setup initdb
Chemins :
Chemin
Binaires /usr/pgsql-12/bin
Répertoire de l’utilisateur postgres /var/lib/pgsql
PGDATA par défaut /var/lib/pgsql/12/data
Fichiers de configuration dans PGDATA/
Traces dans PGDATA/log
43
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Configuration :
Si des instances de versions majeures différentes doivent être installées, il faudra installer
les binaires pour chacune, et l’instance par défaut de chaque version vivra dans un sous-
répertoire différent de /var/lib/pgsql automatiquement créé à l’installation. Il faudra
juste modifier les ports dans les postgresql.conf.
• Ne pas utiliser de tiret dans le nom d’une instance (problèmes potentiels avec sys-
temd).
44
1. PLAN DE MIGRATION
# cp /lib/systemd/system/postgresql-12.service \
/etc/systemd/system/postgresql-12-secondaire.service
• Commandes de maintenance :
Option : JIT
L’utilisation du JIT (Just In Time compilation) nécessite un autre paquet, qui lui-même né-
cessite des paquets du dépôt EPEL :
Référence : https://apt.postgresql.org/
Installation de PostgreSQL 12 :
45
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
# apt update
# apt install postgresql-12 postgresql-client-12
La première instance est directement créée, démarrée et déclarée comme service à lancer
au boot du système.
Chemins :
Chemin
Binaires /usr/lib/postgresql/12/bin/
Répertoire de l’utilisateur postgres /var/lib/postgresql
PGDATA de l’instance par défaut /var/lib/postgresql/12/main
Fichiers de configuration dans /etc/postgresql/12/main/
Traces dans /var/log/postgresql/
Configuration
Ouverture du firewall :
# pg_lsclusters
# pg_dropcluster 12 main
46
1. PLAN DE MIGRATION
Ce qui suit est valable pour remplacer l’instance par défaut par une autre, par exemple
pour mettre les checksums en place :
# pg_createcluster 12 secondaire \
--port=5433 \
--datadir=/PGDATA/11/basedecisionnelle \
--pgoption shared_buffers='8GB' --pgoption work_mem='50MB' \
-- --data-checksums --waldir=/ssd/postgresql/11/basedecisionnelle/journaux
• démarrage :
Par défaut, l’instance n’est accessible que par l’utilisateur système postgres, qui n’a pas
de mot de passe. Un détour par sudo est nécessaire :
Pour des tests (pas en production !), il suffit de passer à trust le type de la connexion en
local dans le pg_hba.conf :
La connexion en tant qu’utilisateur postgres (ou tout autre) n’est alors plus sécurisée :
• dans pg_hba.conf, mise en place d’une authentification par mot de passe (md5 par
défaut) pour les accès à localhost :
(une authentification scram-sha-256 est plus conseillée mais elle impose que
password_encryption soit à cette valeur dans postgresql.conf avant de définir
les mots de passe).
• pour se connecter sans taper le mot de passe, un fichier .pgpass dans le répertoire
personnel doit contenir les informations sur cette connexion :
localhost:5432:*:postgres:motdepassetrèslong
• pour n’avoir à taper que psql, on peut définir ces variables d’environnement dans
la session voire dans ~/.bashrc :
export PGUSER=postgres
48
1. PLAN DE MIGRATION
export PGDATABASE=postgres
export PGHOST=localhost
Rappels :
49
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
TP1.1
Donner les correspondances pour PostgreSQL des types de données Oracle suivants :
• NUMBER(4)
• NUMBER(10)
• NUMBER(9,3)
• NUMBER
• VARCHAR(25)
• VARCHAR2
• BLOB
• CLOB
TP1.2
Champs NULL
La requête SELECT suivante ne retourne pas le même résultat selon qu’elle est exécutée
sur Oracle ou sur PostgreSQL (2 lignes sous Oracle et 1 sous PostgreSQL), pourquoi ?
CREATE TABLE label (id NUMBER, lbl VARCHAR(25));
INSERT INTO label VALUES (1, 'Label 1');
INSERT INTO label VALUES (2, NULL);
INSERT INTO label VALUES (3, 'Label 3');
INSERT INTO label VALUES (4, '');
Réécrire la requête SELECT pour qu’elle renvoie le même résultat sur PostgreSQL que sur
Oracle.
TP1.3
50
1. PLAN DE MIGRATION
LABEL
-----
Label
TP1.4
Réécrire pour PostgreSQL la requête suivante (NB : seule la date du jour nous intéresse,
pas les heures) :
SELECT SYSDATE + 1 FROM DUAL;
Exemple de valeur retournée sur Oracle (affichage par défaut sans les heures !) :
SYSDATE+1
---------
14-MAR-14
ADD_MONTH
---------
14-MAY-14
TP1.5
Jointures (+)
Même si la notation (+) n’est pas recommandée, il peut rester de nombreux codes util-
isant cette notation.
Puis ce code :
SELECT * FROM appellation a, region r
WHERE r.id (+) = a.region_id;
51
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
TP1.6
ROWNUM
Réécrire la requête suivante utilisant ROWNUM pour numéroter les lignes retournées :
SELECT ROWNUM, country_name, region_name
FROM countries c
JOIN regions r ON (c.region_id = r.region_id);
TP1.7
Portage de DECODE
TP1.8
FUNCTION
52
1. PLAN DE MIGRATION
RETURN DBMS_LOB.GETLENGTH(a);
END;
Conseil : les fonctions sur les chaînes de caractères sont listées ici : http://docs.
postgresql.fr/9.6/functions-string.html
TP1.9
CONNECT BY
1 | A. Boss | DIRECTEUR |
4 | C. Second | SOUS DIRECTEUR | 1
5 | F. Dsi | DSI | 1
3 | C. Secretaire | ASSISTANTE | 1
2 | J.P Devel | DEVELOPPEUR | 5
pour vous aider, le portage de ce type de requête se fait à l’aide d’une requête récur-
sive (WITH RECURSIVE) sous PostgreSQL. Pour plus d’information voir la documentation :
https://docs.postgresql.fr/current/queries-with.html
Voici les ordres SQL permettant de créer cette table et d’y insérer quelques données pour
faciliter les tests de réécriture.
CREATE TABLE employes (
numero integer,
nom text,
fonction text,
manager integer
);
53
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
54
1. PLAN DE MIGRATION
TP1.1
Ici il y a une particularité avec le type NUMBER sans paramètres qui peut aussi bien être
un entier qu’un décimal. Il convient dans ce cas de vérifier les données pour utiliser le
type adéquat. S’il s’agit de données de type monétaire, comme des tarifs ou montants
de facture, ou tout autre donnée pour laquelle une grande précision est demandée, il est
impératif d’utiliser le type numérique pour éviter les effets de bord des arrondis.
TP1.2
Champs NULL
La requête ne retourne pas le même résultat selon si elle est exécutée sur Oracle ou sur
PostgreSQL car sous Oracle, le type de données VARCHAR ou VARCHAR2 assimile la chaîne
vide à la valeur NULL. Ce comportement fait que les id 2 et 4 sont retournés par Oracle,
alors que PostgreSQl ne retournera que l’id 2.
Pour émuler le comportement non standard d’Oracle, voici la requête qui peut être utilisée
à cet effet sur PostgreSQL :
SELECT count(id) FROM label WHERE lbl IS NULL OR (lbl = '');
TP1.3
De même que dans l’exercice précédent, Oracle permet de concaténer une chaîne avec
une valeur NULL sans problèmes. Avec PostgreSQL, la valeur NULL est propagée dans les
opérations : une valeur NULL concaténée à une chaîne de caractère donne NULL.
TP1.4
Si on ne s’intéresse qu’à la date, la première requête peut simplement être remplacée par
:
SELECT CURRENT_DATE + 1;
renvoie :
?column?
---------------------
2014-05-14 00:00:00
(1 ligne)
TP1.5
Jointures (+)
56
1. PLAN DE MIGRATION
SELECT *
FROM employees e
RIGHT OUTER JOIN departments d ON e.employee_id = d.manager_id;
TP1.6
ROWNUM
La clause OVER () devrait comporter à minima un ORDER BY pour spécifier l’ordre dans
lequel on souhaite avoir les résultats. Cela dit, la requête telle qu’elle est écrite ci-dessus
est une transposition fidèle de son équivalent Oracle.
TP1.7
Portage de DECODE
La fonction DECODE d’Oracle est un équivalent propriétaire de la clause CASE, qui est nor-
malisée.
TP1.8
FUNCTION
ou simplement en SQL :
CREATE OR REPLACE FUNCTION text_length (a text) RETURNS integer AS
$$
SELECT char_length(a);
$$
LANGUAGE SQL
IMMUTABLE;
TP1.9
CONNECT BY
La récursion est initialisée dans une première requête qui récupère les lignes qui corre-
spondent à la condition de la clause START WITH de la requête précédente : mgr IS NULL.
La récursion continue ensuite avec la requête suivante qui réalise une jointure entre la ta-
ble emp et la vue virtuelle emp_hierarchy qui est définie par la clause WITH RECURSIVE. La
condition de jointure correspond à la clause CONNECT BY. La vue virtuelle emp_hierarchy
a pour alias prior pour mieux représenter la transposition de la clause CONNECT BY.
58
1. PLAN DE MIGRATION
FROM employes
JOIN employes_hierarchie prior ON (employes.manager = prior.numero)
)
SELECT * FROM employes_hierarchie;
Résultat :
59
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
2 SCHÉMA ET DONNÉES
60
2. SCHÉMA ET DONNÉES
2.1 INTRODUCTION
Étapes :
• Téléchargement
• Pré-requis
• Compilation
• Installation
• Utilisation
Nous allons aborder dans cette première partie les différentes étapes à réaliser pour in-
staller Ora2Pg à partir des sources :
2.2.1 TÉLÉCHARGEMENT
• Disponible via :
– HTTP : https://ora2pg.darold.net/
– Git : https://github.com/darold/ora2pg
• Télécharger le fichier ora2pg-X.Y.tar.bz2
• Version au 18 janvier 2019 : 20.0
61
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Les fichiers sources et les instructions de compilation sont accessibles depuis le site offi-
ciel du projet1 .
Il est très important de toujours télécharger la dernière version car l’ajout de fonction-
nalités et les corrections de bogues sont permanentes. En effet, ce projet bénéficie
d’améliorations et de corrections au fur et à mesure des retours d’expérience des util-
isateurs. Il est constamment mis à jour.
wget http://downloads.sourceforge.net/project/ora2pg/20.0/ora2pg-20.0.tar.bz2
• Aller sur la page d’accueil du projet Ora2Pg et dans la section Latest release cliquer
sur le lien SF Download v20.0.
• Vous arriverez sur la page Sourceforge du projet, cliquez sur le bouton Download.
1
https://ora2pg.darold.net/
2
https://sourceforge.net/projects/ora2pg/
62
2. SCHÉMA ET DONNÉES
Pour obtenir le dernier code en développement, il faut aller sur le dépôt GitHub (https:
//github.com/darold/ora2pg) du projet et cliquer sur le bouton Download ZIP.
Ora2Pg se connecte à Oracle grâce à l’interface de bases de données pour Perl, appelée
DBI.
Dans cette interface, Ora2Pg va utiliser le connecteur Oracle, appelé DBD::Oracle (DBD,
acronyme de DataBase Driver). Tous les modules Perl, s’ils ne sont pas disponibles en
paquet pour votre distribution, peuvent toujours être téléchargés à partir du site CPAN
(http://search.cpan.org/). Il suffit de saisir le nom du module (ex : DBD::Oracle) dans la
case de recherche et la page de téléchargement du module vous sera proposée.
Le client lourd d’Oracle est nécessaire pour utiliser la couche OCI. Cependant, les nou-
velles versions Instant Client (à partir de la version 10g) suffisent amplement à Ora2Pg.
Attention toutefois, s’il est possible d’utiliser un client Oracle 12c pour se connecter à
des bases Oracle de versions inférieures, l’inverse n’est pas vrai.
Dans le monde Oracle, ces variables d’environnement sont très connues (ORACLE_BASE,
ORACLE_HOME, NLS_LANG, etc.). Pour DBD::Oracle, le positionnement de la variable
ORACLE_HOME suffit.
export ORACLE_HOME=/usr/lib/oracle/10.2.0.4/client64
Pour une installation sous Windows, l’utilisation de Strawberry Perl3 nécessitera les outils
de compilation pour l’installation de DBD::Oracle alors que l’utilisation de la distribution
libre d’ActiveState4 permet d’installer directement une version binaire de la bibliothèque.
Pour les anciennes versions de Perl ou certaines distributions il peut être nécessaire
d’installer le module Perl, Time::HiRes.
3
http://strawberryperl.com/
4
https://www.activestate.com/activeperl/downloads
64
2. SCHÉMA ET DONNÉES
En option :
• PostgreSQL >= 8.4 client ou serveur
• DBD::Pg pour l’import direct dans PostgreSQL
• Compress::Zlib : compression des fichiers en sortie
• DBD::MySQL pour migrer les bases MySQL
Le connecteur PostgreSQL pour DBI, DBD::Pg est nécessaire uniquement si l’on veut mi-
grer directement les données depuis Oracle vers PostgreSQL sans avoir à passer par des
fichiers intermédiaires. DBD::Pg nécessite au minimum les bibliothèques du client Post-
greSQL.
On peut se passer de ce module dans la mesure où, par défaut, Ora2Pg va écrire les objets
et données à migrer dans des fichiers. Ces fichiers peuvent alors être chargés à l’aide de
la commande psql ou être transférés sur une autre machine disposant de cet outil.
La bibliothèque Perl Compress::Zlib est nécessaire si vous souhaitez que les fichiers
de sortie soient compressés avec gzip. C’est notamment très utile pour les fichiers de
données volumineux par exemple.
Fort heureusement, on peut aussi utiliser le binaire bzip2 pour compresser le fichier
de sortie. Dans ce cas, il suffit d’indiquer, dans le fichier de configuration d’Ora2Pg,
l’emplacement du binaire bzip2 sur le système si celui-ci n’est pas dans le PATH.
Ora2Pg, depuis la version 16.0, permet de migrer les bases de données MySQL. Tout
comme pour Oracle, Ora2Pg a besoin de se connecter à l’instance MySQL au travers d’un
driver Perl. C’est le rôle du module Perl DBD::MySQL.
dmake est l’équivalent de make pour Windows, il peut être téléchargé depuis cette URL :
http://search.cpan.org/dist/dmake/. Téléchargez-le et installez dmake quelque part dans
le PATH Windows.
cp /etc/ora2pg/ora2pg.conf.dist /etc/ora2pg/ora2pg.conf
Les paramètres de ce fichier sont explicités de manière exhaustive plus loin dans la for-
mation.
Toutes les options courtes ont une version longue, par exemple -q et --quiet.
Voici l’intégralité des options disponibles en ligne de commande pour le script Perl ora2pg
et leur explication :
66
2. SCHÉMA ET DONNÉES
67
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Étapes de la configuration :
• Syntaxe du fichier de configuration
• Connexion et schéma Oracle
• Validation de la configuration
• La base Oracle vue par Ora2Pg
• Estimation de la charge de migration
• Création d’une configuration générique
68
2. SCHÉMA ET DONNÉES
• la configuration en elle-même ;
• comment se connecter à la base Oracle ?
• comment valider la configuration ?
• que contient la base de données et comment Ora2Pg va l’exporter ?
• comment estimer la charge de la migration ?
• comment créer un fichier de configuration générique ?
Structure
• Fichier de configuration simple
• Les lignes en commentaires débutent par un dièse (#)
• Les variables sont en majuscules
• Plusieurs paramètres sont du type binaire : 0 pour désactivé et 1 pour activé
Chaque ligne non commentée doit commencer par l’une des clés de configuration. Il y en
a environ 86.
La valeur de cette clé est variable. La directive de configuration et sa valeur doivent être
séparées par une ou plusieurs tabulations.
Lorsque la valeur est une liste, le séparateur des éléments de la liste est généralement le
caractère espace.
SKIP fkeys pkeys ukeys indexes checks
Toutes les clés dont la valeur peut être une liste peuvent être répétées plusieurs fois,
exemple :
SKIP fkeys pkeys ukeys
SKIP indexes checks
Pour les autres, si elles sont répétées, la dernière valeur indiquée sera la valeur prise en
compte.
69
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
• IMPORT fichier.conf
• ORACLE_HOME /path/.../
• DEBUG [0|1]
• LOGFILE /path/.../migration.log
IMPORT
Par exemple :
IMPORT common.conf
Le fichier de configuration importé est chargé au moment où la directive IMPORT apparaît
dans le fichier de configuration. Si les directives importées se retrouvent aussi plus loin
dans le fichier de configuration, elles seront écrasées.
ORACLE_HOME
Cette variable très connue dans le monde Oracle permet de déterminer où se trouve
le répertoire contenant toutes les bibliothèques Oracle ainsi que les autres fichiers d’un
client (ou d’un serveur) Oracle.
Par exemple, pour un serveur Oracle 10g (10.2.0) Express Edition, le ORACLE_HOME ressem-
ble à cela :
ORACLE_HOME /usr/lib/oracle/xe/app/oracle/product/10.2.0/server
ORACLE_HOME /usr/lib/oracle/xe/app/oracle/product/10.2.0/client
DEBUG
Lorsque DEBUG est positionné à 1, Ora2Pg va envoyer sur la console tous les messages, y
compris d’erreurs, qu’il a à envoyer.
Il est recommandé de le désactiver par défaut et, s’il doit être activé, de rediriger la sortie
standard dans un fichier ou d’utiliser un fichier de traces en donnant le chemin complet
à la directive LOGFILE.
70
2. SCHÉMA ET DONNÉES
LOGFILE
La valeur de cette directive correspond à un fichier dans lequel seront ajoutés tous les
messages retournés par Ora2Pg. Ceci permet notamment de garder la trace complète
des messages de la migration pour s’assurer qu’il n’y a pas eu de messages d’erreur.
• ORACLE_DSN
– dbi:Oracle:host=serveur;sid=INSTANCE
• ORACLE_USER
– system
• ORACLE_PWD
– manager
• SCHEMA
– NOM_SCHEMA versus SYSUSERS
• USER_GRANTS [0|1]
– l’utilisateur Oracle a-t-il les droits DBA ?
ORACLE_DNS
mais ceci implique que l’instance DB_SID (dans cet exemple) soit connue et accessible par
la machine où Ora2Pg va fonctionner. Pour cela, il suffit de le déclarer dans le fichier
$ORACLE_HOME/network/admin/tnsnames.ora :
On peut tester cela simplement avec des outils comme tnsping ou encore sqlplus.
la partie SID propre à Oracle database est remplacée ici par database.
ORACLE_USER et ORACLE_PWD
On définit avec ces variables l’utilisateur et le mot de passe avec lesquels Ora2Pg va se
connecter au serveur Oracle pour en extraire des informations (schéma, données, etc.).
Il est préférable que cet utilisateur soit déclaré comme un SYSDBA dans Oracle, c’est-
à-dire un utilisateur privilégié de type DBA (un peu comme l’utilisateur postgres l’est
généralement pour un serveur PostgreSQL).
L’export des droits (GRANT) sur les objets de la base de données et les TABLESPACES ne
peuvent être réalisés que par un utilisateur privilégié.
SCHEMA
Cette variable permet de déterminer le schéma ou utilisateur Oracle dont les objets ou
données seront exportés. Le paramètre ORACLE_USER défini précédemment dans le fichier
de configuration doit avoir les droits nécessaires sur les objets de ce schéma.
Par exemple, pour exporter les objets du schéma HR de la base de données de démonstra-
tion d’Oracle 10g XE (Express Edition) :
SCHEMA HR
Si aucun schéma n’est précisé, les objets ou données de tous les schémas de l’instance
seront exportés hormis ceux définis dans le paramètre SYSUSERS.
SYSUSERS
Ce paramètre permet d’exclure, à l’origine, tous les utilisateurs système d’Oracle et leur
schéma qui, parfois, contiennent des tables systèmes qui sont superflues pour une migra-
tion vers PostgreSQL.
SYSTEM CTXSYS DBSNMP EXFSYS LBACSYS MDSYS MGMT_VIEW OLAPSYS ORDDATA OWBSYS
ORDPLUGINS ORDSYS OUTLN SI_INFORMTN_SCHEMA SYS SYSMAN WK_TEST WKSYS WKPROXY
WMSYS XDB APEX_PUBLIC_USER DIP FLOWS_020100 FLOWS_030000 FLOWS_040100 FLOWS_010600
FLOWS_FILES MDDATA ORACLE_OCM SPATIAL_CSW_ADMIN_USR SPATIAL_WFS_ADMIN_USR XS$NULL
72
2. SCHÉMA ET DONNÉES
On peut utiliser cette fonctionnalité d’une manière détournée pour ignorer les objets ap-
partenant à d’autres utilisateurs.
Tout utilisateur spécifié dans la clause SYSUSERS sera ignoré, en plus des utilisateurs ig-
norés par défaut (voir liste ci-dessus).
Par exemple, si on veut ignorer les objets des utilisateurs RECETTE et DEV :
SYSUSERS RECETTE,DEV
USER_GRANTS
Ce paramètre est par défaut à 0 car Ora2Pg part du principe que l’on utilise un utilisa-
teur privilégié (membre du groupe DBA, comme SYSTEM) pour, par exemple, exporter la
définition des objets.
En effet, Ora2Pg utilise intensivement les vues de type DBA_.... Or, un utilisateur non
privilégié n’a pas accès à ces vues, réservées aux administrateurs de la base de données.
On peut alors configurer USER_GRANTS à 1 pour utiliser un utilisateur Oracle non DBA.
Dans ce cas, Ora2Pg utilisera les vues de type ALL_... pour récupérer la définition des
objets.
À noter, qu’alors, cela ne fonctionnera pas avec les types d’export GRANT et TABLESPACE
qui doivent impérativement être réalisés par un utilisateur avec les privilèges DBA.
L’analyse de requêtes applicatives dans la table DBA_AUDIT_TRAIL (export type QUERY),
nécessite aussi ce privilège.
Dans la mesure où le fichier ora2pg.conf va contenir des informations sensibles, il est
recommandé de prendre garde aux droits qui sont associés à ce fichier et, si possible,
de positionner des droits à 0 pour tout utilisateur autre que le propriétaire et le groupe
associés au fichier :
$ chown 660 /etc/ora2pg/ora2pg.conf
73
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Pour tester que les paramètres de connexion à l’instance Oracle sont les bons, on peut
utiliser les actions de rapports simples d’Ora2Pg qui ne nécessitent que la configuration
des variables de connexion.
Par exemple, pour l’instance d’exemple fournie par Oracle XE et le schéma HR, la com-
mande :
ora2pg -t SHOW_SCHEMA
Cela permettra de lister tous les schémas de l’instance Oracle pour trouver la bonne valeur
à donner à la directive SCHEMA dans le fichier de configuration.
La commande :
ora2pg -t SHOW_TABLE
donne la liste des tables qui seront exportées et le nombre d’enregistrements pour chaque
table :
74
2. SCHÉMA ET DONNÉES
Si des tables sont non loguées (unlogged tables), correspondent à des tables externes ou
sont partitionnées, Ora2Pg l’affichera à côté du nom de la table.
S’il s’agit d’une table contenant des objets géométriques avec une contrainte sur le type
d’objet, Ora2Pg donnera son équivalent PostGIS :
75
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
• CLIENT_ENCODING
– utf8, latin1, latin9
• BINMODE
– utf8, raw
SHOW_ENCODING
Ceci retournera les valeurs NLS_LANG, NLS_NCHAR et CLIENT_ENCODING, qui seront util-
isées par Ora2Pg, mais aussi l’encodage réel de la base Oracle et de l’encodage corre-
spondant dans PostgreSQL. Par exemple :
NLS_LANG et NLS_CHAR
76
2. SCHÉMA ET DONNÉES
$ sqlplus hr/secret@xe
PARAMETER VALUE
------------------------------ ----------------------------------------
NLS_LANGUAGE FRENCH
NLS_TERRITORY FRANCE
NLS_CURRENCY €
NLS_ISO_CURRENCY FRANCE
NLS_NUMERIC_CHARACTERS ,
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD/MM/RR
NLS_DATE_LANGUAGE FRENCH
NLS_SORT FRENCH
NLS_TIME_FORMAT HH24:MI:SSXFF
NLS_TIMESTAMP_FORMAT DD/MM/RR HH24:MI:SSXFF
NLS_TIME_TZ_FORMAT HH24:MI:SSXFF TZR
NLS_TIMESTAMP_TZ_FORMAT DD/MM/RR HH24:MI:SSXFF TZR
NLS_DUAL_CURRENCY €
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
17 ligne(s) sélectionnée(s).
On peut aussi exécuter une requête pour récupérer le paramétrage de l’instance toute
entière avec :
SELECT * FROM nls_instance_parameters ;
Ce paramétrage au niveau instance se modifie avec l’ordre ALTER SYSTEM, ainsi qu’au
niveau de la base de données :
SELECT * FROM nls_database_parameters;
Ce paramétrage au niveau base de données ne se modifie pas, il est défini lors de la créa-
tion de la base de données avec un SET.
CLIENT_ENCODING
Par défaut la valeur de cette directive est UTF8, c’est celle qui correspond à l’encodage
unicode utilisé pour extraire les données d’Oracle.
77
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Si le NLS_LANG a été modifié dans le fichier de configuration alors pour que la conver-
sion des données en provenance d’Oracle vers PostgreSQL soit exacte, il faut définir
l’encodage à utiliser par le client PostgreSQL. Ainsi, si la variable NLS_LANG côté connex-
ion Oracle est FRENCH_FRANCE.WE8ISO8859P1, il faudra utiliser l’encodage LATIN1 côté
client PostgreSQL pour ne pas avoir de problème de conversion d’encodage des données.
Pour vous aider à trouver le jeu de caractères dans PostgreSQL correspondant à celui
sous Oracle, vous pouvez consulter ce document, 22.3. Character Set Support5 , qui fait
partie de la documentation officielle de PostgreSQL.
BINMODE
Par défaut le paramètre est positionné à utf8 si NLS_LANG utilise un encodage unicode. Il
n’est donc normalement pas nécessaire de modifier cette variable de configuration. Lors
de l’utilisation d’un encodage unicode, il est indispensable de le positionner à la valeur
utf8 pour éviter les erreurs d’écriture Perl de type Wide character in print.
Ora2Pg dispose d’un mode d’analyse du contenu de la base Oracle afin de générer un
rapport sur son contenu et présenter ce qui peut ou ne peut pas être exporté.
L’outil parcourt l’intégralité des objets, les dénombre, extrait les particularités de chacun
d’eux et dresse un bilan exhaustif de ce qu’il a rencontré. Pour activer le mode « analyse
et rapport », il faut utiliser l’export de type SHOW_REPORT par la commande suivante :
ora2pg -t SHOW_REPORT
-------------------------------------------------------------------------------
Ora2Pg v20.0 - Database Migration Report
-------------------------------------------------------------------------------
5
https://www.postgresql.org/docs/current/static/multibyte.html
78
2. SCHÉMA ET DONNÉES
-------------------------------------------------------------------------------
Object Number Invalid Comments Details
-------------------------------------------------------------------------------
DATABASE LINK 2 0 Database links will be exported as SQL/MED PostgreSQL's
Foreign Data Wrapper (FDW) extensions using oracle_fdw.
GLOBAL TEMPORARY TABLE 0 0 Global temporary table are not supported by
PostgreSQL and will not be exported. You will have to
rewrite some application code to match the PostgreSQL
temporary table behavior.
INDEX 28 0 19 index(es) are concerned by the export, others are automatically
generated and will do so on PostgreSQL. Bitmap will be
exported as btree_gin index(es) and hash index(es) will be
exported as b-tree index(es) if any. Domain index are exported
as b-tree but commented to be edited to mainly use FTS.
Cluster, bitmap join and IOT indexes will not be exported at all.
Reverse indexes are not exported too, you may use a trigram-based
index (see pg_trgm) or a reverse() function based index and search.
Use 'varchar_pattern_ops', 'text_pattern_ops' or 'bpchar_pattern_ops'
operators in your indexes to improve search with the LIKE operator
respectively into varchar, text or char columns.
3 function based b-tree index(es).
13 b-tree index(es).
3 spatial index index(es).
INDEX PARTITION 2 0 Only local indexes partition are exported, they are
build on the column used for the partitioning.
JOB 0 0 Job are not exported. You may set external cron job with them.
PROCEDURE 3 1 Total size of procedure code: 1870 bytes.
SEQUENCE 3 0 Sequences are fully supported, but all call to
sequence_name.NEXTVAL or sequence_name.CURRVAL will be
transformed into NEXTVAL('sequence_name') or CURRVAL('sequence_name').
SYNONYM 0 0 SYNONYMs will be exported as views. SYNONYMs do not exists with Postgr
common workaround is to use views or set the PostgreSQL search_path in
your session to access object outside the current schema.
TABLE 22 0 2 check constraint(s). 1 unknown types. Total number of rows: 421.
Top 10 of tables sorted by number of rows:.
79
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
D’autres paramètres ne peuvent pas être analysés par Ora2Pg comme l’usage de
l’application. Il existe aussi d’autres objets qui ne sont pas exportés directement par
Ora2Pg comme les objets DIMENSION des fonctionnalités OLAP d’Oracle dans la mesure
où ils n’ont pas d’équivalent dans PostgreSQL.
Le rapport généré est identique à celui généré par SHOW_REPORT, mais cette fonctionnalité
provoque en plus l’exploration des objets de la base de données, du code source des vues,
triggers et routines stockées (fonctions, procédures et paquets de fonctions), puis donne
un score à chaque objet et à chaque routine suivant le volume de code et la complexité
de réécriture manuelle de ce code. En effet, la réécriture d’une routine comportant un
CONNECT BY ne prend pas le même temps que la réécriture d’une routine comportant des
appels à GOTO.
-------------------------------------------------------------------------------
Ora2Pg v20.0 - Database Migration Report
-------------------------------------------------------------------------------
Version Oracle Database 12c Enterprise Edition Release 12.1.0.2.0
Schema HR
80
2. SCHÉMA ET DONNÉES
Size 28.56 MB
-------------------------------------------------------------------------------
Object Number Invalid Estimated cost Comments Details
-------------------------------------------------------------------------------
DATABASE LINK 2 0 6 Database links will be exported as SQL/MED PostgreSQL's
Foreign Data Wrapper (FDW) extensions using oracle_fdw.
GLOBAL TEMPORARY TABLE 0 0 0 Global temporary table are not supported by
PostgreSQL and will not be exported. You will have to
rewrite some application code to match the PostgreSQL
temporary table behavior.
INDEX 28 0 5.3 19 index(es) are concerned by the export, others are automatically
generated and will do so on PostgreSQL. Bitmap will be
exported as btree_gin index(es) and hash index(es) will be
exported as b-tree index(es) if any. Domain index are exported
as b-tree but commented to be edited to mainly use FTS.
Cluster, bitmap join and IOT indexes will not be exported at all.
Reverse indexes are not exported too, you may use a trigram-based
index (see pg_trgm) or a reverse() function based index and search
Use 'varchar_pattern_ops', 'text_pattern_ops' or 'bpchar_pattern_o
operators in your indexes to improve search with the LIKE operator
respectively into varchar, text or char columns.
3 function based b-tree index(es). 13 b-tree index(es).
3 spatial index index(es).
INDEX PARTITION 2 0 0 Only local indexes partition are exported, they are build
column used for the partitioning.
JOB 0 0 0 Job are not exported. You may set external cron job with them.
PROCEDURE 3 1 16 Total size of procedure code: 1870 bytes.
add_job_history: 3. test_dupl_vazba: 7. secure_dml: 3.
SEQUENCE 3 0 1 Sequences are fully supported, but all call to sequence_name.N
or sequence_name.CURRVAL will be transformed into NEXTVAL('sequenc
or CURRVAL('sequence_name').
SYNONYM 0 0 0 SYNONYMs will be exported as views. SYNONYMs do not exists with Po
common workaround is to use views or set the PostgreSQL search_pat
your session to access object outside the current schema.
TABLE 22 0 2.4 2 check constraint(s). 1 unknown types. Total number of rows: 421
Top 10 of tables sorted by number of rows:. sg_infrastructure_rout
has 188 rows. employees has 107 rows. departments has 27 rows.
countries has 25 rows. locations has 23 rows. jobs has 19 rows.
81
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
-------------------------------------------------------------------------------
Migration level : B-5
-------------------------------------------------------------------------------
Migration levels:
A - Migration that might be run automatically
B - Migration with code rewrite and a human-days cost up to 5 days
C - Migration with code rewrite and a human-days cost above 5 days
Technical levels:
1 = trivial: no stored functions and no triggers
2 = easy: no stored functions but with triggers, no manual rewriting
3 = simple: stored functions and/or triggers, no manual rewriting
4 = manual: no stored functions but with triggers or views with code rewriting
5 = difficult: stored functions and/or triggers with code rewriting
-------------------------------------------------------------------------------
82
2. SCHÉMA ET DONNÉES
TEST => 2
SIZE => 1
-------------------------------------------------------------------------------
En fin de rapport, Ora2Pg affiche le nombre total d’objets rencontrés, les objets invalides
et un nombre correspondant au nombre d’unités de coût de migration qu’il aura estimé
nécessaire en fonction du code détecté (voir l’Annexe 3 : Méthode de valorisation de la
charge de migration). Cette unité vaut par défaut 5 minutes, cela correspond au temps
moyen que mettrait un spécialiste pour porter le code. Dans l’exemple ci-dessus, on a
donc une estimation par Ora2Pg d’une migration ayant un coût de 162,5 unités multi-
pliées par 5 minutes, ce qui correspond en gros à 2 jours.homme.
--cost_unit_value
COST_UNIT_VALUE 10
ou en ligne de commande :
Dans ce mode de rapport, Ora2Pg affiche aussi les détails du coût de migration estimé
par routine.
DUMP_AS_HTML 1
83
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Ora2Pg propose un exemple de rapport en HTML sur son site : Ora2Pg - Database Mi-
gration Report6
Par défaut, Ora2Pg affiche les dix tables les plus volumineuses en terme de nombre de
lignes et le top dix des tables les plus volumineuses en taille (hors partitions). Le nombre
de table affichées peut être contrôlé avec la directive de configuration TOP_MAX.
L’action SHOW_REPORT renvoie le rapport sur la sortie standard (stdout), il est donc con-
seillé de renvoyer la sortie dans un fichier pour pouvoir le consulter dans l’application
adaptée à son format. Par exemple :
ora2pg -t SHOW_REPORT --estimate_cost --dump_as_html > report.html
Le but est d’avoir un fichier de configuration générique qui sera utilisé pour tous les types
d’export et d’utiliser la souplesse des options en ligne de commande du script ora2pg.
On commande d’abord à Ora2Pg de créer des fichiers de sortie différents pour les
contraintes (FILE_PER_CONSTRAINT), les index (FILE_PER_INDEX) et les clé étrangère
6
https://ora2pg.darold.net/report.html
84
2. SCHÉMA ET DONNÉES
(FILE_PER_FKEY). Cela nous permettra de ne les importer qu’à la fin de la migration pour
ne pas être gêné ou ralenti lors de l’import de données.
On peut aussi générer un fichier différent par table (FILE_PER_TABLE) lors de l’export des
données et par routine (FILE_PER_FUNCTION) pour permettre un traitement individualisé.
La désactivation des triggers pour chaque table avant l’import des données est réalisée,
peu importe s’ils ont été importés auparavant ou non. Cela évitera leur déclenchement
s’ils ont été importés et n’aura pas d’effet si ce n’est pas le cas, DISABLE_TRIGGERS doit
donc être activé. Il est toutefois préférable de ne charger les triggers qu’à la fin.
La conversion automatique du code des routines stockées est désactivée pour pouvoir
obtenir les sources du code. On utilisera l’option -p lors de l’exécution d’ora2pg afin de
l’activer.
85
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Le schéma utilisé pour définir le search_path à la création des objets sera celui donné
comme valeur de la variable SCHEMA par défaut ou celui défini par la variable PG_SCHEMA si
vous souhaitez changer de nom de schéma ou que vous devez accéder à d’autres schémas
lors de l’import des objets.
Ora2pg considère toujours que vous utilisez la dernière version officielle de PostgreSQL
disponible à la sortie de la version d’Ora2Pg que vous utilisez. Cependant il est possible
que vous ayez besoin de migrer dans une base PostgreSQL d’une version antérieure, mais
toutes les fonctionnalités supportées par Ora2Pg n’y existent pas forcément encore.
86
2. SCHÉMA ET DONNÉES
dernière version majeur de PostgreSQL. Ora2Pg adaptera l’export en fonction des fonc-
tionnalités développées dans chaque version.
• BITMAP_AS_GIN pour autoriser l’export des index bitmap dans leur équivalent avec
l’extension btree_gin ;
• STANDARD_CONFORMING_STRINGS pour l’échappement dans les chaines de carac-
tères ;
87
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Les colonnes ayant pour type Oracle spatial SDO_GEOMETRY, peuvent contenir n’importe
quel type de géométrie. Le type équivalent pour PostGIS est geometry.
Dans ce cas, elles pourront aussi contenir n’importe quel type de géométrie.
Il peut être intéressant d’avoir une contrainte sur le type des géométries pouvant être
insérées dans la colonne si c’est toujours le même type d’objet géométrique qui doit être
utilisé.
Le système de référence spatial (SRID) utilisé va être la valeur retournée depuis la table
des métadonnées spatial Oracle (ALL_SDO_GEOM_METADATA) ou, si la valeur retournée est
nulle, la valeur donnée à la directive de configuration DEFAULT_SRID. Voici à peu de chose
près la requête utilisée :
88
2. SCHÉMA ET DONNÉES
Si l’extension PostGIS a été installée dans un schéma spécifique, les appels aux fonc-
tions de l’extension devront être préfixées par le nom du schéma. Pour éviter cela, il est
préférable de positionner le nom du schéma PostGIS dans la directive POSTGIS_SCHEMA
et celui-ci sera ajouté au search_path lors de la création des objets.
Pour l’export des géométries, il est préférable d’utiliser le type INTERNAL pour la direc-
tive GEOMETRY_EXTRACT_TYPE. Cela évite d’utiliser les fonctions Oracle pour extraire la
géométrie au format texte (WKT) ou binaire (WKB). Ces modes nécessitent l’utilisation de
fonctions Oracle (SDO_UTIL.TO_WKTGEOMETRY()) et (SDO_UTIL.TO_WKBGEOMETRY()) qui
sont lentes et ont la particularité de planter l’export dès que le volume est important.
Lors de l’export des LOB, si la directive NO_LOB_LOCATOR est activée, il se peut que vous
rencontriez l’erreur Oracle :
Il est conseillé de laisser Ora2Pg gérer l’export des LOB en utilisant des pointeurs sur les
enregistrements (LOB Locator) lui permettant de récupérer les données de ces champs
89
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Lors de l’export de champs LOB, il est important de diminuer très fortement la valeur de
DATA_LIMIT en fonction de la vitesse maximale d’export pour éviter les dépassements
de mémoire. Pour permettre à Ora2Pg d’extraire ces données avec les autres en adap-
tant automatiquement le DATA_LIMIT à une valeur plus faible lorsqu’il s’agit d’un LOB, la
directive BLOB_LIMIT est disponible.
BLOB_LIMIT 500
La valeur de 500, voire moins, n ’est pas rare avec ce type d’objet. Si cette directive n’est
pas définie, par défaut, Ora2Pg est capable de détecter qu’il s’agit d’une table avec un
champ BLOB et de diminuer automatiquement la valeur de DATA_LIMIT en la divisant par
10 jusqu’ à ce qu’elle soit inférieure ou égale à 1000.
Une bonne pratique consiste donc à positionner une valeur à la directive BLOB_LIMIT pour
forcer Ora2Pg à utiliser cette valeur pour les tables avec BLOB et continuer à utiliser la
valeur de DATA_LIMIT pour les tables sans BLOB.
Étapes :
• Organisation de l’espace de travail
• Utilisation de la configuration générique
• Export du schéma Oracle
• Import du schéma dans PostgreSQL
Nous allons aborder ici les différentes étapes à réaliser pour mettre en œuvre de façon
optimale l’export du schéma :
• Comment s’y retrouver dans tous les fichiers générés et ne pas écraser le précédent
export ?
• Comment utiliser la configuration générique ?
• Et enfin l’export complet du schéma Oracle en des ordres DDL PostgreSQL ?
90
2. SCHÉMA ET DONNÉES
Il est important d’organiser l’espace de travail de son projet de migration. Sans cela, on
se retrouve très vite avec une multitude de fichiers dont le contenu devient très vite
énigmatique.
Dans la mesure où, par défaut, Ora2Pg fait tous ses exports dans un même fichier nommé
output.sql, vous pouvez aussi très facilement écraser le précédent export si vous omet-
tez de renommer le fichier.
L’export du code source du code SQL et PL/SQL dans des fichiers dans un espace de
stockage particulier est très important. Cela permet, lors de la phase de migration des
routines stockées, de vérifier qu’Ora2Pg n’a pas corrompu du code et de comparer le
code.
Pour créer une arborescence de travail destinée à recevoir les fichiers du projet de migra-
tion, on peut s’aider d’ora2pg en exécutant la commande suivante :
/opt/ora2pg/mydb_project/
��� config
� ��� ora2pg.conf
��� data
��� export_schema.sh
��� import_all.sh
��� reports
��� schema
� ��� dblinks
91
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
#!/bin/sh
#-------------------------------------------------------------------------------
#
# Generated by Ora2Pg, the Oracle database Schema converter, version 20.0
#
#-------------------------------------------------------------------------------
EXPORT_TYPE="TABLE PACKAGE VIEW GRANT SEQUENCE TRIGGER FUNCTION PROCEDURE
TABLESPACE PARTITION TYPE MVIEW DBLINK SYNONYM DIRECTORY"
SOURCE_TYPE="PACKAGE VIEW TRIGGER FUNCTION PROCEDURE PARTITION TYPE MVIEW"
namespace="."
92
2. SCHÉMA ET DONNÉES
echo
echo
echo "To extract data use the following command:"
echo
echo "ora2pg -t COPY -o data.sql -b $namespace/data -c $namespace/config/ora2pg.conf"
echo
exit 0
Ora2Pg aura aussi créé un script import_all.sh utilisé pour l’import dans PostgreSQL
des divers objets exportés et disponibles sous forme de fichiers dans l’espace de travail
93
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
après exécution du script export_schema.sh. Si les données ont aussi été exportées sous
forme de fichiers dans l’espace de travail, le script permet de les charger dans PostgreSQL,
sinon il permettra de les charger directement depuis Oracle en utilisant les options de
parallélisme d’Ora2Pg.
Pour les bases MySQL, il est nécessaire d’ajouter l’option -m ou --mysql pour indiquer à
Ora2Pg qu’il s’agit d’un projet de migration de base MySQL.
Les options de connexion à Oracle peuvent être données en ligne de commande avec les
options d’ora2pg dédiées à cet effet (-s, -u et -n). Les valeurs de ces paramètres seront
alors appliquées dans le fichier de configuration générique.
Ensuite, le comportement d’ora2pg sera déterminé par les options des lignes de com-
mande utilisées.
L’option -t permet de choisir le type d’action lors de l’exécution du script plutôt que d’aller
modifier le fichier de configuration. Cette option peut prendre exactement les mêmes
valeurs que la variable TYPE, c’est-à-dire TABLE, VIEW, MVIEW, DBLINK, SYNONYM, DIRECTORY,
GRANT, SEQUENCE, TRIGGER, PACKAGE, FUNCTION, PROCEDURE, PARTITION, TYPE, INSERT,
COPY, TABLESPACE, SHOW_SCHEMA, SHOW_TABLE, SHOW_COLUMN, SHOW_ENCODING, KETTLE,
QUERY, LOAD, TEST, TEST_VIEW et FDW.
94
2. SCHÉMA ET DONNÉES
Le nom des fichiers de sortie est défini à partir de l’option -o correspondant à la directive
OUTPUT.
• table.sql
• CONSTRAINTS_table.sql
• INDEXES_table.sql
• FKEYS_table.sql
Le premier utilise le nom donné par l’option -o et contient les ordres CREATE TABLE ....
Le second utilise aussi le nom donné dans l’option -o mais préfixé par le mot CONSTRAINT_
et, pour cause, il contient tous les ordres de création des contraintes : ALTER TABLE
"..." ADD CONSTRAINT ....
Le troisième fichier contient toutes les commandes de création des index (CREATE INDEX
...) définies dans Oracle à l’exception des index implicites sur les clés primaires que
PostgreSQL génère automatiquement et qui n’ont donc pas besoin d’être exportées.
Le quatrième contient les ordres de création des clés étrangères pour pouvoir être créées
facilement après la migration des données.
95
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
L’option de conversion de code -p est utilisée ici uniquement pour les index ou contraintes
CHECK qui peuvent utiliser des fonctions à convertir.
Par défaut Ora2Pg supprime toutes les informations sur les tablespaces associés aux ob-
jets exportés de la base Oracle. Si vous souhaitez préserver ces informations, notamment
pour utiliser des tablespaces différents pour les tables et les index, la directive de config-
uration USE_TABLESPACE doit être activée. Les tablespaces par défaut d’Oracle (TEMP,
USERS et SYSTEM) ne sont pas pris en compte.
Le premier export (type GRANT) va exporter tous les rôles et leurs droits sur les objets
sous forme d’ordres SQL CREATE ROLE ... et GRANT ... ON ... dans le fichier
schema/users/users.sql.
Le troisième type d’export va exporter tous les types définis par les utilisateurs (CREATE
TYPE ...) dans le fichier schema/types/types.sql. La conversion de certains types
utilisateurs Oracle nécessite une réécriture manuelle pour être compatible avec Post-
greSQL. Ce sont les types définis par CREATE TYPE ... AS TABLE OF ... qui néces-
sitent l’écriture de fonctions définissant le comportement du type lors de la lecture et
de l’écriture dans ce type. Il en va de même avec les types objets (CREATE TYPE ... AS
96
2. SCHÉMA ET DONNÉES
OBJECT ... TYPE BODY). Les fonctions doivent être converties à la syntaxe PostgreSQL.
Cette conversion est réalisée grâce à l’emploi de l’option -p (équivalent à l’activation de
la variable PLSQL_PGSQL).
L’étape suivante de la migration du schéma consiste à exporter tous les autres types
d’objets : les vues, les déclencheurs, les fonctions et procédures stockées (les routines).
Tous ces types d’ export nécessitent l’emploi de l’option -p pour provoquer la conversion
automatique du code SQL et PL/SQL.
L’import de ce type d’objet sera évoqué en détail dans le chapitre dédié à la migration du
code PL/SQL.
Dans la mesure où la conversion du code SQL et PL/SQL n’est pas complète, voire im-
parfaite, il est recommandé d’extraire le code brut pour pouvoir le comparer avec le code
converti par Ora2Pg en cas de problème.
L’extraction du code brut d’Oracle se fait en n’utilisant pas l’option -p lors de l’exécution
du script et en désactivant l’option PLSQL_PGSQL dans le fichier de configuration.
97
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Si la base de données Oracle possède des partitions, Ora2Pg va convertir toutes les par-
titions.
98
2. SCHÉMA ET DONNÉES
Les partitions de type HASH (partitionnement automatique) et les partitions par défaut sont
supportées avec le partitionnement déclaratif à partir de la version 11 de PostgreSQL.
);
100
2. SCHÉMA ET DONNÉES
) INHERITS (sales_list);
CREATE TABLE sales_east (
CHECK ( sales_state IN ('New York', 'Virginia', 'Florida'))
) INHERITS (sales_list);
CREATE TABLE sales_other () INHERITS (sales_list);
Les partitions de type HASH (partitionnement automatique) ne sont pas supportées avec
le partitionnement par héritage.
En passant PG_VERSION à une version supérieure ou égale à 9.3 permet d’utiliser le sup-
port natif des vues matérialisées dans PostgreSQL 9.3. Elle est maintenant activée par
défaut et permet d’exporter les vues matérialisées directement avec la syntaxe SQL.
101
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Si une mise à jour au fil de l’eau est requise, il faudra forcément passer par des triggers,
ceci n’est pas encore implémenté nativement dans PostgreSQL.
Si PG_VERSION est inférieur à la valeur 9.3, Ora2Pg générera le nécessaire pour gérer
des vues matérialisées de type snapshot, c’est-à-dire qui sont mises à jour uniquement
lorsqu’elles sont rafraîchies. Les autres types nécessitent l’écriture de code spécifique
différent à chaque vue.
102
2. SCHÉMA ET DONNÉES
AS $$
DECLARE
mview ALIAS FOR $1; -- name of the materialized view to create
vname ALIAS FOR $2; -- name of the related view
iname ALIAS FOR $3; -- name of the colum of mview to used as unique key
entry materialized_views%ROWTYPE;
BEGIN
EXECUTE 'SELECT * FROM materialized_views
WHERE mview_name = ' || quote_literal(mview) INTO entry;
IF entry.iname IS NOT NULL THEN
RAISE EXCEPTION 'Materialized view % already exist.', mview;
END IF;
RETURN;
END
$$
SECURITY DEFINER
LANGUAGE plpgsql;
103
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
RETURN;
END
$$
SECURITY DEFINER
LANGUAGE plpgsql ;
104
2. SCHÉMA ET DONNÉES
RETURN;
END
$$
SECURITY DEFINER
LANGUAGE plpgsql ;
SELECT create_materialized_view('emp_data','emp_data_mview',
change with the name of the colum to used for the index);
On voit que quels que soient le type et la complexité de la vue matérialisée, Ora2Pg la
transforme en vue rafraîchie à la demande. Dans ce cas, obtenir le code source n’a donc
pas un grand intérêt, c’est pourquoi ce n’est pas réalisé par le script d’export.
Vient ensuite la création de la vue emp_data_mview qui servira à la vue matérialisée qui
est créée par l’appel à la fonction create_materialized_view et qui doit remplacer le
commentaire change with the name of the colum to used for the index dans le code de l’appel
de la fonction.
Dans l’exemple, la table EMPLOYEE possède une clé primaire sur le champ EMPLOYEE_ID,
c’est ce champ qui va aussi être utilisé pour créer un index sur la table de la vue matérial-
isée.
Par la suite, pour rafraîchir la vue matérialisée, il suffit d’appeler la routine de rafraîchisse-
ment :
SELECT refresh_full_materialized_view('emp_data');
105
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Un SYNONYM n’est ni plus ni moins qu’un alias vers un objet d’une base de données
Oracle. Ils sont utilisés pour donner les droits d’accès à un objet dans un autre schéma
ou dans une base distante auquel l’utilisateur n’aurait normalement pas accès.
Les SYNONYM n’existent pas sous PostgreSQL, il y a deux méthodes pour les émuler.
Modification du search_path
L’objet est naturellement caché à l’utilisateur car il n’appartient pas à un schéma de son
search_path par défaut et lorsqu’on veut qu’il y ait accès, on modifie le search_path.
Par exemple :
SET search_path TO other_schema,...;
Cette méthode peut s’avérer assez fastidieuse à mettre en place au niveau applicatif mais
évite la création de vues.
Utilisation de vues
L’autre méthode consiste donc à utiliser des vues. C’est ce que générera Ora2Pg lors de
l’export des synonymes.
106
2. SCHÉMA ET DONNÉES
Si le synonyme pointe sur une table distante par un dblink, Ora2Pg créera la vue telle
que précédemment mais ajoutera un message en commentaire pour signifier que la table
distante doit être créée via un Foreign Data Wrapper ou un dblink. Par exemple :
-- You need to create foreign table hr.employees using foreign server:
-- oradblink1 (see DBLINK and FDW export type)
CREATE VIEW public.emp_table AS SELECT * FROM hr.employees;
ALTER VIEW public.emp_table OWNER TO hr;
GRANT ALL ON public.emp_table TO PUBLIC;
Les DIRECTORY et tables externes n’existent pas dans PostgreSQL tels que définis dans
Oracle. Il est possible d’émuler les accès à des tables externes en utilisant le Foreign Data
Wrapper file_fdw mais uniquement en lecture. Ces tables doivent respecter le format
CSV de COPY. Ora2Pg exporte par défaut toute table externe en une table distante basée
sur l’extension file_fdw. Si vous voulez exporter ces tables comme des tables normales,
il suffit de désactiver la directive de configuration EXTERNAL_TO_FDW en lui donnant la
valeur 0.
activite CHAR(1))
ORGANIZATION EXTERNAL (
TYPE oracle_loader
DEFAULT DIRECTORY ext_directory
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
FIELDS TERMINATED BY ','
MISSING FIELD VALUES ARE NULL
REJECT ROWS WITH ALL NULL FIELDS
(id, nom, prenom, activite))
LOCATION ('person.dat')
)
PARALLEL
REJECT LIMIT 0
NOMONITORING;
Puis, il crée la table comme une table distante rattachée au serveur préalablement défini.
Les DATABASE LINK sont des objets Oracle permettant l’accès à des objets de bases de
données distantes. Ils sont créés de la manière suivante :
108
2. SCHÉMA ET DONNÉES
Ce type d’objet n’existe pas nativement dans PostgreSQL et nécessite l’utilisation d’une
extension Foreign Data Wrapper en fonction du type du SGBD distant.
Ora2Pg exportera ces DATABASE LINK comme des bases Oracle distantes en utilisant
l’extension Foreign Data Wrapper oracle_fdw par défaut. Il est tout à fait possible de
changer l’extension si la base distante est une base PostgreSQL. Voici un exemple d’export
par Ora2Pg :
CREATE SERVER remote_service_name FOREIGN DATA WRAPPER oracle_fdw
OPTIONS (dbserver 'remote_db_name');
Pour que le lien vers la base distante puisse être utilisé, il est nécessaire de créer les tables
distantes dans la base locale :
Maintenant la table peut être utilisée directement au niveau SQL comme s’il s’agissait
d’une table locale :
SELECT * FROM employees@remote_service_name;
Cela fonctionne en lecture et écriture depuis PostgreSQL 9.3. Le Foreign Data Wrapper
oracle_fdw peut être obtenu sur le site des extensions PostgreSQL pgxn.org8
8
https://pgxn.org/dist/oracle_fdw/
109
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Le type BFILE permet de stocker des données non structurées dans des fichiers externes
en dehors de la base de données (fichiers image, documents pdf, etc.). Le type DIRECTORY
permet lui de définir des chemins sur le système de fichier qui pourront être utilisés pour
le stockage de ces données externes.
Un BFILE est une colonne qui stocke un nom de fichier qui pointe vers un fichier externe
contenant les données et le nom de l’identifiant du répertoire base dans lequel ce fichier
est stocké : (DIRECTORY, FILENAME)
Par défaut Ora2Pg transforme le type BFILE en type bytea en chargeant le contenu du
fichier directement en base sous forme d’objet binaire.
CREATE TABLE bfile_test (id bigint, bfilecol bytea);
COPY bfile_test (id,bfilecol) FROM STDIN;
1
1234,ALBERT,GRANT,21\\0121235,ALFRED,BLUEOS,26\\0121236,BERNY,JOL
YSE,34\\012
\.
Il est possible de demander à Ora2Pg de ne pas importer les données dans le champ
cible, mais seulement le chemin complet (répertoire base + nom de fichier) vers le fichier.
Ceci se fait en modifiant le type PostgreSQL associé au type Oracle dans la directive de
configuration DATA_TYPE : ...,BFILE:TEXT,...
Il existe aussi une extension PostgreSQL nommée external_file9 qui permet d’émuler
les DIRECTORY et BFILE d’Oracle. Si le type PostgreSQL associé au type Oracle dans
9
https://github.com/darold/external_file
110
2. SCHÉMA ET DONNÉES
Voici ce que Ora2Pg générera comme ordre SQL lorsque qu’un champ de type BFILE doit
être converti en type EFILE :
INSERT INTO external_file.directories (directory_name, directory_path)
VALUES ('EXT_DIR', '/data/ext/');
INSERT INTO external_file.directory_roles (directory_name, directory_role,
directory_read, directory_write) VALUES ('EXT_DIR', 'hr', true, false);
INSERT INTO external_file.directories (directory_name, directory_path)
VALUES ('SCOTT_DIR', '/usr/home/scott/');
INSERT INTO external_file.directory_roles(directory_name, directory_role,
directory_read, directory_write) VALUES ('SCOTT_DIR', 'hr', true, true);
Le type EFILE contient lui exactement la même chose que le type BFILE, à savoir
(directory_name, file_name).
111
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
L’extension pg_trgm apporte des classes d’opérateur pour les index GiST et GIN permet-
tant de créer un index sur une colonne texte pour les recherches rapides par similarités.
Ces index permettent notamment la recherche par trigrammes pour les requêtes à base
de LIKE, ILIKE, ~ et ~*.
Exemple :
CREATE TABLE test_trgm (t text);
CREATE INDEX trgm_idx ON test_trgm USING GIN (t gin_trgm_ops);
Ce type d’index peut correspondre aux index Oracle CTXCAT indexant des textes de pe-
tites tailles. Il faut toutefois réécrire les requêtes utilisant l’opérateur CATSEARCH en
requêtes utilisant LIKE ou ILIKE.
L’indexation FTS est un des cas les plus fréquents d’utilisation non-relationnelle d’une
base de données : les utilisateurs ont souvent besoin de pouvoir rechercher une informa-
tion qu’ils ne connaissent pas parfaitement, d’une façon floue :
PostgreSQL doit donc permettre de rechercher de façon efficace dans un champ texte.
L’avantage de cette solution est d’être intégrée au SGBD. Le moteur de recherche est
donc toujours parfaitement à jour avec le contenu de la base, puisqu’il est intégré avec le
reste des transactions.
• Ajout d’une colonne vectorisée à la table depeches, afin de maximiser les perfor-
mances de recherche
112
2. SCHÉMA ET DONNÉES
• Creation du trigger
CREATE TRIGGER trg_depeche before INSERT OR update ON depeche
FOR EACH ROW EXECUTE PROCEDURE to_vectdepeche();
• Utilisation :
113
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
La recherche propose bien sûr d’autres opérateurs que & : | pour « ou », ! pour « non ».
On peut effectuer des recherches de radicaux, etc. L’ensemble des opérations possibles
est détaillée ici : https://docs.postgresql.fr/current/textsearch-controls.html.
Ce type d’indexation plein texte correspond à la recherche de texte Oracle basée sur des
index de type CONTEXT. Il sera aussi nécessaire de réécrire les requêtes Oracle utilisant
l’opérateur CONTAINS avec l’operateur @@ de PostgreSQL.
114
2. SCHÉMA ET DONNÉES
La première chose à faire avant de commencer à migrer réellement la base Oracle dans une
base PostgreSQL est de créer le propriétaire de la base de données, toutes les opérations
se feront ensuite sous cet utilisateur. Voici comment créer le propriétaire de la base :
Si vous avez décidé d’exporter le schéma Oracle avec la variable EXPORT_SCHEMA activée,
il faut créer le schéma sous PostgreSQL :
Pour faciliter ensuite l’utilisation du schéma, il est possible d’affecter un schéma par dé-
faut à un utilisateur de sorte qu’à chaque fois qu’il se connecte à la base, ce sont les
schémas donnés qui seront utilisés :
Si des tablespaces doivent être importés, les chemins doivent exister sur le système. Il faut
donc s’assurer qu’ils sont présents et que PostgreSQL pourra écrire dans ces répertoires.
La base étant créée, il ne reste plus qu’à charger les différents objets en commençant par
les tables, puis les partitions, s’il y en a, les vues et pour finir les tablespaces pour déplacer
les objets dans leur espaces de stockage respectif (l’export des tablespaces contient non
seulement les tablespaces, mais aussi les ALTER TABLE et ALTER INDEX déplaçant les ta-
bles et index dans leur tablespace de destination).
115
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Les objets susceptibles de gêner l’import des données, soit en provoquant des erreurs
comme les contraintes, soit en ralentissant leur chargement comme les index, sont laissés
de côté et ne seront importés qu’à la fin de la migration. Dans ce cas, il faudra lancer deux
fois le script tablespaces.sql, une fois après le chargement des tables, une fois après le
chargement des index, et ignorer les erreurs.
Les types d’erreur pouvant survenir sont souvent des problèmes d’encodage dans les
valeurs des contraintes CHECK et dans les index. Dans ce cas, il faut utiliser les ordres :
SET client_encoding TO autre_encodage;
Avec aussi la possibilité, pour les contraintes et index, de trouver du code SQL utilisant
des fonctions qui ne sont pas convertibles automatiquement par Ora2Pg.
CREATE INDEX idx_userage ON players ( to_number(to_char('1974', user_age)) );
ALTER TABLE "actifs" ADD CONSTRAINT CHECK (WYEAR between 0 and 42);
Ora2Pg exporte les champs Oracle de type NUMBER sans précision en bigint. Ce n’est pas
forcément le bon choix notamment lorsque ce champ contient des valeurs avec décimale.
Une erreur va se produire lors de l’import des données. Il sera nécessaire alors de modifier
le type de la colonne à posteriori.
116
2. SCHÉMA ET DONNÉES
On trouve de temps en temps des objets comportant des accents, sans compter qu’il
faudra que le nom de l’objet soit toujours placé entre guillemets doubles. Il faudra aussi
utiliser le bon encodage lors de la création et des appels à l’objet. Ceci génère énormé-
ment d’erreurs et il est fortement conseillé de les supprimer.
Ora2Pg ne détecte pas les noms d’objets correspondants à des mots réservés PostgreSQL.
Il vous faudra, dans ce cas, modifier manuellement le code SQL en les incluant entre
guillemets doubles.
CREATE INDEX idx_userage ON user WHERE age > 16;
Oracle autorise certaines conversions implicites qui ne sont plus autorisées dans Post-
greSQL depuis la version 8.3 (principalement les conversions implicites entre numériques
et chaînes de caractères). Dans l’exemple, WYEAR est une colonne de type VARCHAR dans
Oracle et exportée comme telle dans PostgreSQL. Il faudra donc forcer sa transforma-
tion en integer pour que la contrainte fonctionne, sinon vous obtiendrez une erreur du
genre :
Étapes :
• Export / import des données
• Problèmes rencontrés
• Restauration des séquences, contraintes, triggers et index
• Performances de l’import des données
• Utilisation du parallélisme
• Limitation des données à importer
117
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Nous allons aborder ici les différentes étapes pour migrer de façon optimale les données :
Il faut privilégier le premier type d’export à base d’instruction COPY plutôt que le second
à base d’ordre INSERT. Il y a deux raisons à cela : l’import sera beaucoup plus rapide
avec COPY et vous aurez potentiellement moins d’erreurs si vos données contiennent des
caractères d’échappement (\).
L’avantage d’avoir des fichiers de données à disposition est qu’ils peuvent être rechargés
manuellement plusieurs fois en cas de problème jusqu’à trouver le correctif à apporter.
Dans la mesure où l’export de données dans des fichiers peut occuper un volume disque
très important, Ora2Pg vous donne la possibilité de compresser vos données soit avec
gzip soit avec bzip2. Pour le premier type de compression, il faut installer au préalable
le module Perl Compress::Zlib et donner l’extension .gz au fichier de sortie :
118
2. SCHÉMA ET DONNÉES
Pour utiliser la compression avec bzip2, il suffit que le programme bzip2 soit dans le PATH
et il faut donner l’extension .bz2 au fichier de sortie :
La lenteur de l’export des champs de type LOB dans des champs bytea (qui est le type
correspondant sous PostgreSQL) s’explique par la taille habituellement élevée de ces don-
nées et la nécessité d’échapper l’intégralité des données.
Pour accélérer l’échappement des données bytea, il faut activer l’utilisation du paral-
lélisme. Cela permet en général d’aller deux à trois fois plus vite. Pour cela, il faut utiliser
l’option -j en ligne de commande ou la variable JOBS du fichier de configuration. La valeur
est le nombre de cœurs CPU que l’on veut utiliser.
Lorsque les options de parallélisation sont activées, il est important de s’ assurer que
la valeur de DATA_LIMIT corresponde à la vitesse moyenne maximal d’export d’un sim-
ple processus. Par exemple, si Ora2Pg exporte globalement les données à une vitesse
moyenne de 5000 tuples/s, c’est très certainement la valeur à donner :
DATA_LIMIT 5000
Si c’est plutôt 20 000, alors DATA_LIMIT devra avoir cette valeur. Ceci vous permettra
d’être sûr de tirer le meilleur parti de la parallélisation. Une valeur excessive par contre
peut conduire à des dépassement de ressources, une valeur trop faible forcera Ora2Pg à
créer des processus inutilement.
119
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
CONVERT_SRID
Oracle utilise son propre système spatial de référence SRID (Spatial Reference System Iden-
tifier), la norme de fait est maintenant l’ESPG (European Petroleum Survey Group). Ora-
cle fournit la fonction sdo_cs.map_oracle_srid_to_epsg() permettant de le convertir
dans cette norme lorsque c’est possible. Si la directive CONVERT_SRID est activée, la con-
version sera effectuée.
Cette fonction retourne souvent NULL. Dans ce cas, Ora2Pg renvoit la valeur 8307 comme
SRID par défaut ou, si CONVERT_SRID est activée, 4326 converti en ESPG. Il est possible
de changer cette valeur par défaut en donnant la valeur du SRID à utiliser à la directive
CONVERT_SRID. À noter que dans ce cas, DEFAULT_SRID ne sera pas utilisé.
DEFAULT_SRID
La directive DEFAULT_SRID permet de changer la valeur par défaut du SRID EPSG à utiliser
si la valeur retournée est nulle. Elle vaut 4326 par défaut.
GEOMETRY_EXTRACT_TYPE
Cette directive permet d’informer Ora2Pg sur la méthode à utiliser pour extraire les don-
nées. Il existe trois possibilités :
• WKT
• WKB
• INTERNAL
L’utilisation de ces fonctions est intéressante pour obtenir les géométries telle que les
voit Oracle ; le seul problème est qu’elles génèrent souvent des erreurs, sont incapables
d’extraire des géométries en 3D et surtout provoquent des OOM (Out Of Memory) lorsque
il y a un grand nombre de géométries.
120
2. SCHÉMA ET DONNÉES
Pour palier à ce problème, Ora2Pg embarque sa propre librairie Pure Perl, Ora2Pg::GEOM,
permettant d’extraire les données géométriques au format WKT de manière plus rapide
et surtout sans erreur. Pour utiliser cette méthode, il faut donner la valeur INTERNAL à la
directive GEOMETRY_EXTRACT_TYPE.
L’import des fichiers de données se fait simplement avec l’utilisation de la commande psql
en spécifiant l’utilisateur (myuser), la base de données (mydb) et le fichier à charger (option
-f).
L’import direct des données dans la base PostgreSQL n’est activé que si la variable PG_DSN
est définie. Dans ce cas, le chargement se fait directement lors de l’export des données
sans passer par des fichiers intermédiaires.
Une fois que les données sont chargées avec succès, il est temps de créer les contraintes,
index et triggers qui avaient été laissés de côté lors de la création du schéma. Ces créa-
tions se font aussi à l’aide de la commande psql.
121
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Il est possible que l’import de certains codes, notamment les triggers, nécessitent la
présence de certaines fonctions. Dans ce cas, il faudra les intégrer en parallèle.
Action :
• LOAD permet de paralléliser des ordres SQL sur N processus
ora2pg -c config/ora2pg -t LOAD -j 4 -i schema/tables/INDEXES_tables.sql
ora2pg -c config/ora2pg -t LOAD -j 4 -i schema/tables/CONSTRAINTS_tables.sql
La création des contraintes et des index est une phase qui très souvent dure presque aussi
longtemps que le chargement des données, voire plus longtemps en fonction du nombre.
Depuis la version 16.0 d’Ora2Pg, l’action LOAD permet de donner un fichier d’ordre SQL
en entrée (option -i) et de distribuer sur plusieurs processeurs ces requêtes SQL à l’aide
de l’option -j N d’Ora2Pg.
Il suffit dans ce cas de lui donner en entrée les fichiers relatifs à la création des contraintes
et des index pour pouvoir les charger beaucoup plus rapidement.
Si le type d’export INSERT a été choisi, il arrive très souvent que cela conduise à des
erreurs de caractères invalides lors de l’insertion car le caractère backslash n’est pas
échappé si STANDARD_CONFORMING_STRING est activé. Le respect du standard est activé
par défaut dans PostgreSQL v9.1. Dans Ora2Pg, le même comportement survient. De ce
fait, ils doivent être activés ou désactivés en même temps dans les deux configurations.
Le meilleur moyen de corriger ce problème est d’utiliser le type d’export recommandé
pour les données, c’est-à-dire COPY.
Si vous n’avez pas défini correctement les variables NLS_LANG et CLIENT_ENCODING, vous
aurez aussi des erreurs de caractères invalides. Il vous faudra alors trouver les bonnes
valeurs selon la méthode indiquée dans les chapitres précédents. Malgré une définition
correcte de ces variables, il se peut que vous ayez encore des problèmes d’encodage, et
122
2. SCHÉMA ET DONNÉES
même au sein d’une même table: certains enregistrements ne passeront pas par COPY. Il
semble qu’Oracle soit très permissif sur les caractères qu’il est possible d’inclure dans un
même jeu de caractères.
Pour l’essentiel, ces problèmes sont résolus en forçant toutes les communications à
utiliser l’encodage UNICODE, c’est ce qu’Ora2Pg fait par défaut depuis la version 14.0.
Pour résoudre ce problème, il faut évaluer la quantité de champs concernés par ce prob-
lème. Si cela ne concerne que peu de champs et qu’il est possible d’ avoir une valeur
décimale pour ces champs, le mieux est de changer le type directement :
ALTER TABLE employees ALTER COLUMN real_age TYPE real;
Si par contre le problème se pose de manière quasi systématique, il est alors préférable
de modifier le type défini dans DEFAULT_NUMERIC et de recommencer l’import complet.
Lors de l’export des LOB, si vous n’avez pas activé la directive NO_LOB_LOCATOR, il se peut
que vous rencontriez l’erreur Oracle :
123
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
La méthode la plus simple pour gagner en performances est d’utiliser la méthode COPY et
de ne pas passer par des fichiers intermédiaires pour importer ces données. Pour envoyer
directement les données extraites de la base Oracle vers la base PostgreSQL, il suffit de
définir les paramètres de connexion à la base PostgreSQL dans le fichier de configuration
ora2pg.conf.
COPY ou INSERT
Préférez toujours l’import des données à l’aide de l’ordre COPY plutôt qu’à base d’INSERT.
Ce dernier est beaucoup trop lent pour les gros volumes de données. Lorsque l’import
direct dans PostgreSQL est utilisé, Ora2Pg va utiliser une requête préparée et passer les
valeurs de chaque ligne en paramètre, mais même avec cette méthode, le chargement
avec l’instruction COPY reste le plus performant.
PG_DSN
PG_USER
Il détermine le nom de l’utilisateur PostgreSQL qui sera utilisé pour se connecter à la base
PostgreSQL désignée par le paramètre PG_DSN.
124
2. SCHÉMA ET DONNÉES
PG_USER prod
PG_PWD
DATA_LIMIT
Par défaut, lorsqu’on demande à Ora2Pg d’extraire les données, il récupère les données
par bloc de 10 000 lignes.
Ceci permet d’écrire dans le fichier en sortie ou de transférer les données vers une base
PostgreSQL toutes les 10 000 lignes et ainsi réduire les entrées/sorties. Cependant, suiv-
ant la configuration matérielle de la machine, il peut être très intéressant de faire varier
cette valeur pour gagner en performance. Par exemple, sur une machine disposant de
beaucoup de mémoire, travailler sur 100 000 enregistrements à chaque fois ne doit pas
poser de problème et permet d’accroître les performances de manière significative.
DATA_LIMIT 100000
Si, par contre, votre machine dispose de très peu de mémoire ou que les enregistrements
sont de très grosse taille, cette valeur devra être diminuée, par exemple :
DATA_LIMIT 1000
Ora2Pg de base n’utilise qu’un seul CPU ou cœur pour le chargement des données. Ceci
est très limitant en terme de vitesse d’importation des données. Pour utiliser le paral-
lélisme sur plusieurs cœurs, Ora2Pg dispose de deux directives de configuration : JOBS
125
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Toutefois, pour que les requêtes d’extraction des données de la base Oracle puissent être
parallélisées, il faut qu’Ora2Pg ait connaissance d’une colonne de la table sur laquelle la
division par processus peut être réalisée. Cette colonne doit être de type numérique et,
de préférence, être une clé unique car Ora2Pg va scinder les données en fonction du
nombre de processus demandés selon le principe de la requête suivante :
SELECT * FROM matable WHERE MOD(colonne, ORACLE_COPIES) = #PROCESSUS;
où colonne est la clé unique, ORACLE_COPIES est la valeur de la variable du même nom
ou de l’option -J et #PROCESSUS est le numéro du processus parallélisé en commençant
par 0.
L’utilisation de la fonction ROUND() est impérative lorsque le champ n’est pas un entier. Il
est à noter que l’option -J est sans effet si la table exportée n’a pas de colonne définie
dans la directive DEFINED_PKEY.
En affinant les valeurs données à -j et -J, il est possible de multiplier par 6 à 10 la vitesse
de chargement des données par rapport à un chargement n’ utilisant pas la parallélisation.
Les valeurs de -j et -J se multiplient entre elles. Il faut donc faire attention à ne pas
dépasser le nombre de cœurs disponible sur la machine, par exemple :
ouvrira 8 connexions à Oracle pour extraire les données en parallèle et, pour chacune de
ces connexions, 3 processus supplémentaires seront utilisés pour enregistrer les données
dans PostgreSQL, ce qui donne 24 cœurs utilisés par Ora2Pg.
Ce type de parallélisme est contraignant à mettre en œuvre et peut être mis en oeuvre
par exemple pour extraire des données d’une table avec de nombreux CLOB ou BLOB pour
tenter d’accélérer son export.
Pour paralléliser l’export de plusieurs tables en simultané on peut aussi utiliser la direc-
tive PARALLEL_TABLES. Cette variable prend comme valeur le nombre de connexions à
126
2. SCHÉMA ET DONNÉES
Oracle qui devront être ouvertes pour extraire les données des différentes tables en si-
multané. Lorsque cette directive a une valeur supérieur à 1, la variable FILE_PER_TABLE
est automatiquement activée.
Suivant la structure d’une table, il peut être aussi nécessaire de faire bouger la valeur de
la directive DATA_LIMIT qui, par défaut, est à 10000. Pour les tables dont l’export est très
rapide, une valeur à 100000 est préférable, alors que pour les tables avec LOB et poten-
tiellement des enregistrements de très grande taille, une valeur à 100 sera probablement
nécessaire. Cette valeur est aussi relative aux performances du système. Une bonne
démarche est de tester la vitesse d’export sur des tables moyennes et de positionner la
valeur de DATA_LIMIT à ce niveau, par exemple :
DATA_LIMIT 60000
Puis, sur les tables à très faible débit, utiliser l’option de ligne de commande -L :
La plupart du temps, 90 % des tables peuvent être exportées avec la même configuration
du DATA_LIMIT et du parallélisme pour les insertions dans PostgreSQL seul. Par exemple,
sur un serveur avec 24 cœurs et 64 Go de RAM, la commande suivante (PostgreSQL
tournant sur ce même serveur) :
traitera parfaitement la très grande majorité des tables. Il est à noter que l’option -j est
sans effet si le nombre de lignes de la table en cours d’export divisé par la valeur de -j
(dans l’exemple au dessus : 16) est inférieur à la valeur donnée dans le DATA_LIMIT.
Pour les autres, il faut identifier les tables avec des CLOB et BLOB, les tables avec le plus
grand nombre de lignes et celles avec les plus gros volumes de données. Ensuite, il faut
voir s’il est possible de multiplexer les connexions à Oracle pour accélérer l’export ainsi
que la valeur qui sera le mieux adaptée au DATA_LIMIT en faisant des tests d’import de
données.
127
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
ALLOW
Par défaut, Ora2Pg exporte toutes les tables qu’il trouve, au moins dans le schéma désigné
avec la directive SCHEMA.
On peut cependant limiter l’export à certaines objets, grâce à la directive ALLOW. Il suffit ici
de donner une liste de noms d’objets, séparées par un espace. Les expressions régulières
sont aussi permises.
Exemple :
ALLOW EMPLOYEES SALE_.* COUNTRIES .*_GEOM_SEQ
EXCLUDE
Exemple:
EXCLUDE EMPLOYEES TMP_.* COUNTRIES EMPLOYEES_COPIE_2010.* TEST[0-9]+
Attention, les expressions régulières ne fonctionnent pas avec les versions Oracle 8i, vous
devez utiliser le caractère % à la place, Ora2Pg utilise l’opérateur LIKE dans ce cas.
Les objets filtrés par ces directives dépendent du type d’export. Les exemples précé-
dents montrent la manière dont sont déclarés les filtres globaux, ceux qui vont s’appliquer
quelque soit le type d’export utilisé. Il est possible d’ utiliser un filtre sur un type d’objet
uniquement en utilisant la syntaxe : OBJECT_TYPE[FILTER]. Par exemple :
limitera l’export des triggers à ceux définis sur la table EMPLOYEES. Si vous voulez exporter
certains triggers mais pas ceux qui on une clause INSTEAD OF (liés à des vues) :
128
2. SCHÉMA ET DONNÉES
Ou, par exemple, une forme plus complexe avec inclusion / exclusion d’éléments :
Cette commande va exporter la définition de la table EMPLOYEES tout en excluant tous les
index commençant par emp_ et la contrainte CHECK nommée emp_salary_min.
Autre exemple, lors de l’export des partitions on peut vouloir exclure certaines tables :
Ceci va exclure de l’export les tables partitionnées concernant les années 1980 à 1999
mais pas la table principale ni les autres partitions.
Avec l’export des privilèges (GRANT) il est possible d’utiliser cette forme étendue pour
exclure certains utilisateur de l’export ou limité l’export à certains autres :
ou bien
qui limitera l’export des privilèges aux utilisateurs USER1 et USER2. Mais si vous ne voulez
pas exporter leurs privilèges sur certaines fonctions, alors :
L’utilisation des filtres étendus en fonction de leur complexité peut nécessiter un certain
temps d’apprentissage.
WHERE
Ce paramètre permet d’ajouter des filtres dans les requêtes d’extraction de don-
nées. Il n’est donc utilisé que dans le cadre d’un export de données, soit avec TYPE
[INSERT|COPY].
Ora2Pg ajoutera tous les filtres déclarés dans cette variable et/ou correspondant à une
table donnée, lorsque cela est possible, .
Il convient de créer plusieurs fichiers ora2pg.conf si on doit ajouter des filtres sur de
nombreuses tables, car la configuration de WHERE peut en effet rapidement devenir illisible
si elle est complexe !
• WHERE 1=1
129
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Cet exemple trivial est là pour illustrer le fait que si aucune table n’est mentionnée, la
clause WHERE sera appliquée à toutes les requêtes d’extraction. Si le champ n’existe pas
pour une table donnée, il sera ignoré. Autrement dit, Ora2Pg ne s’attend pas à ce que
le(s) champ(s) mentionnés sans nom existent dans toutes les tables.
Exemple:
WHERE DATE_CREATION > '2001-01-01'
Si, pour une table donnée, il existe des conditions sur ses champs (voir plus bas), alors
cela prévaut sur un champ qui aurait été configuré sans spécification du nom de table.
• WHERE TABLE_TEST[ID1='001']
On peut bien sûr préciser une expression pour une ou plusieurs colonnes d’une table
donnée.
Par exemple, si on ne veut sélectionner que les départements dans la table DEPARTMENTS
dont le champ ID est strictement inférieur à 100 :
WHERE departments[DEPARTMENT_ID<100]
Cela donne :
COPY "departments" ("department_id","department_name",[...])
FROM stdin;
10 Administration 200 1700
20 Marketing 201 1800
30 Purchasing 114 1700
40 Human Resources 203 2400
50 Shipping 121 1500
60 IT 103 1400
70 Public Relations 204 2700
80 Sales 145 2500
90 Executive 100 1700
\.
On peut ainsi composer sur plusieurs champs d’une même table, et ainsi de suite
pour plusieurs tables à la fois. Il suffit pour cela de respecter la convention
NOM_DE_TABLE[COLONNE... etc.] et de séparer chaque élément par un espace.
Par exemple, si on veut restreindre les données ci-dessus aux MANAGER_ID strictement
supérieurs à 200, on écrira :
WHERE DEPARTMENTS[DEPARTMENT_ID<100 AND MANAGER_ID>200]
130
2. SCHÉMA ET DONNÉES
REPLACE_QUERY
Quelquefois cela n’est pas suffisant, par exemple si l’on souhaite faire une jointure sur
une table d’identifiants à migrer ou tout autre requête plus complexe que ce que ne peut
produire Ora2Pg. Dans ce cas il est possible de forcer Ora2Pg a utiliser la requête SQL
qui lui sera donné par la directive REPLACE_QUERY. Par exemple :
REPLACE_QUERY EMPLOYEES[
SELECT e.id,e.fisrtname,lastname
FROM EMPLOYEES e
JOIN EMP_UPDT u
ON (e.id=u.id AND u.cdate>'2014-08-01 00:00:00')
]
Cette requête permet de n’extraire que les enregistrements de la table employees qui
ont été créés depuis le 1er août 2014 sachant que l’information se trouve dans la table
emp_updt.
2.8 CONCLUSION
Le temps de migration du schéma et des données est rapide. Même avec une grosse
volumétrie de données, le plus long concerne généralement le code, au niveau applicatif
comme au niveau des routines stockées.
• Documentation officielle
• Autres sources d’informations
Vous pouvez retrouver la documentation en ligne en anglais sur le site officiel https://
ora2pg.darold.net/.
Une série de documents concernant la migration Oracle vers PostgreSQL est disponible
sur le wiki PostgreSQL : https://wiki.postgresql.org/wiki/Converting_from_other_
Databases_to_PostgreSQL#Oracle
2.8.2 QUESTIONS
132
2. SCHÉMA ET DONNÉES
Environnement
Installer DBD::Oracle.
Installer DBD::Pg.
Téléchargement d’Ora2Pg
Télécharger les fichiers sources de la dernière version et les placer dans /opt/ora2pg/src.
Créer une arborescence de travail destinée à recevoir les fichiers du projet de migration
sous /opt/ora2pg/tp_migration.
Configuration
Export/import du schéma
Exécuter le script permettant l’exécution chaînée de tous les types d’export du schéma
et des procédures stockées. Pour ces dernières l’export du code sera fait dans la version
source Oracle et dans la version transformée par Ora2Pg avec la syntaxe PostgreSQL.
133
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Import du schéma
Importer uniquement les tables, les autres objets du schéma seront importés après
l’import des données.
134
2. SCHÉMA ET DONNÉES
Les opérations d’installation de paquet (yum, make install) sont à exécuter en tant que
root ou plutôt sudo. Le reste, dont les étapes de compilation, sont à effectuer avec un
utilisateur normal.
Environnement
http://www.oracle.com/ -> Download -> Instant Client -> Downloads -> Instant Client for Linux x86-
64 ou Instant Client for Linux x86
Vous devez auparavant accepter la licence en cochant la case Accept License Agree-
ment et posséder un compte Oracle.
La version de l’Instant Client doit être au moins v12 pour accéder aux bases en 12c ; au
plus en v10 pour accéder aux bases Oracle 8 ou 9.
• en 32 bits :
instantclient-basic-linux-11.2.0.3.0.zip
instantclient-sqlplus-linux-11.2.0.3.0.zip
instantclient-sdk-linux-11.2.0.3.0.zip
• en 64 bits :
instantclient-basic-linux.x64-11.2.0.3.0.zip
instantclient-sqlplus-linux.x64-11.2.0.3.0.zip
instantclient-sdk-linux.x64-11.2.0.3.0.zip
# yum install libaio1
mkdir -p /opt/oracle/11.2/
unzip -x instantclient-basic-linux-11.2.0.3.0.zip
sudo mv instantclient_11_2/ /opt/oracle/11.2/
unzip -x instantclient-sdk-linux-11.2.0.3.0.zip
mv instantclient_11_2/* /opt/oracle/11.2/instantclient_11_2/
unzip -x instantclient-sqlplus-linux-11.2.0.3.0.zip
mv instantclient_11_2/* /opt/oracle/11.2/instantclient_11_2/
Le driver Oracle pour Perl DBI peut être trouvé à partir du module de recherche du site CPAN :
http://search.cpan.org/search?query=DBD::Oracle
wget http://search.cpan.org/CPAN/authors/id/P/PY/PYTHIAN/DBD-Oracle-1.74.tar.gz
tar xzf DBD-Oracle-1.74.tar.gz
cd DBD-Oracle-1.74/
export ORACLE_HOME=/opt/oracle/11.2/instantclient_11_2/
LD_LIBRARY_PATH=/opt/oracle/11.2/instantclient_11_2/ perl Makefile.PL
make
sudo make install
Le driver PostgreSQL pour Perl DBI peut être trouvé à partir du module de recherche du site CPAN :
http://search.cpan.org/search?query=DBD::Pg
wget http://search.cpan.org/CPAN/authors/id/T/TU/TURNSTEP/DBD-Pg-2.19.3.tar.gz
tar xzf DBD-Pg-2.19.3.tar.gz
cd DBD-Pg-2.19.3/
export POSTGRES_LIB="/usr/pgsql-9.6/lib -lssl -lcrypto"
perl Makefile.PL
make
sudo make install
Téléchargement d’Ora2Pg
136
2. SCHÉMA ET DONNÉES
mkdir -p /opt/ora2pg/src
cd /opt/ora2pg/src/
wget http://downloads.sourceforge.net/project/ora2pg/20.0/ora2pg-20.0.tar.bz2
export ORACLE_HOME=/opt/oracle/11.2/instantclient_11_2/
tar xjf ora2pg-20.0.tar.bz2
cd ora2pg-20.0/
perl Makefile.PL
make
sudo make install
Pour créer une arborescence de travail destinée à recevoir les fichiers du projet de migra-
tion on peut s’aider d’ora2pg en exécutant la commande suivante :
ora2pg --init_project tp_migration --project_base /opt/ora2pg
tp_migration/
��� config
� ��� ora2pg.conf
��� data
��� export_schema.sh
��� import_all.sh
��� reports
��� schema
� ��� dblinks
� ��� directories
� ��� functions
� ��� grants
� ��� mviews
� ��� packages
137
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
� ��� partitions
� ��� procedures
� ��� sequences
� ��� synonyms
� ��� tables
� ��� tablespaces
� ��� triggers
� ��� types
� ��� views
��� sources
��� functions
��� mviews
��� packages
��� partitions
��� procedures
��� triggers
��� types
��� views
Ora2Pg a aussi créé un script pour l’export automatique nommé : export_schema.sh, un script pour au-
tomatiser l’import dans PostgreSQL, import_all.sh et un fichier de configura-
tion générique config/ora2pg.conf.
Configuration
export ORACLE_HOME=/opt/oracle/11.2/instantclient_11_2/
export LD_LIBRARY_PATH=$ORACLE_HOME
sqlplus hr/phoenix@192.168.1.109:1521/xe
ORACLE_HOME /usr/local/instantclient_11_2
Il reste donc à configurer les paramètres de connexion à l’instance XE d’Oracle avec l’utilisateur HR :
138
2. SCHÉMA ET DONNÉES
ORACLE_DSN dbi:Oracle://192.168.1.109:1521/XE
ORACLE_USER hr
ORACLE_PWD phoenix
Dans la mesure où l’utilisateur hr n’a pas les privilèges DBA, il faut aussi activer la direc-
tive USER_GRANTS :
USER_GRANTS 1
ora2pg -d -c config/ora2pg.conf -t SHOW_SCHEMA
Using character set: NLS_LANG=AMERICAN_AMERICA.AL32UTF8, NLS_NCHAR=AL32UTF8.
Using Perl output encoding :utf8.
Using PostgreSQL client encoding UTF8.
Trying to connect to database: dbi:Oracle:host=192.168.1.150;sid=XE;port=1521
Isolation level: SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
Found Package: EMP_ACTIONS
Found Package: EMP_MGMT
Looking at package emp_mgmt...
Looking at package emp_actions...
Showing all schema...
SCHEMA HR
On veut exporter le schema HR, il faut donc le spécifier dans la configuration et remplacer :
SCHEMA CHANGE_THIS_SCHEMA_NAME
par
SCHEMA HR
[1] TABLE COUNTRIES (25 rows)
[2] TABLE DEPARTMENTS (27 rows)
[3] TABLE EMPLOYEES (107 rows)
[4] TABLE JOBS (19 rows)
[5] TABLE JOB_HISTORY (10 rows)
[6] TABLE LOCATIONS (23 rows)
[7] TABLE PHONE (1 rows)
[8] TABLE REGIONS (4 rows)
[9] TABLE SSN (1 rows)
[10] TABLE TEST (1 rows)
139
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
----------------------------------------------------------
Total number of rows: 218
Top 10 of tables sorted by number of rows:
[1] TABLE EMPLOYEES has 107 rows
[2] TABLE DEPARTMENTS has 27 rows
[3] TABLE COUNTRIES has 25 rows
[4] TABLE LOCATIONS has 23 rows
[5] TABLE JOBS has 19 rows
[6] TABLE JOB_HISTORY has 10 rows
[7] TABLE REGIONS has 4 rows
[8] TABLE SSN has 1 rows
[9] TABLE TEST has 1 rows
[10] TABLE PHONE has 1 rows
Cette commande affiche aussi le top 10 des tables avec le plus d’enregistrements et, si l’utilisateur de con-
nexion a les droits suffisants, le top 10 des tables de plus gros volume.
Export/import du schéma
cd /opt/ora2pg/tp_migration
sh export_schema.sh
Running: ora2pg -p -t TABLE \
-o table.sql -b ./schema/tables -c ./config/ora2pg.conf
Running: ora2pg -p -t PACKAGE \
-o package.sql -b ./schema/packages -c ./config/ora2pg.conf
Running: ora2pg -p -t VIEW \
-o view.sql -b ./schema/views -c ./config/ora2pg.conf
Running: ora2pg -p -t GRANT \
-o grant.sql -b ./schema/grants -c ./config/ora2pg.conf
Running: ora2pg -p -t SEQUENCE \
-o sequence.sql -b ./schema/sequences -c ./config/ora2pg.conf
Running: ora2pg -p -t TRIGGER \
-o trigger.sql -b ./schema/triggers -c ./config/ora2pg.conf
Running: ora2pg -p -t FUNCTION \
140
2. SCHÉMA ET DONNÉES
-o function.sql -b ./schema/functions -c ./config/ora2pg.conf
Running: ora2pg -p -t PROCEDURE \
-o procedure.sql -b ./schema/procedures -c ./config/ora2pg.conf
Running: ora2pg -p -t TABLESPACE \
-o tablespace.sql -b ./schema/tablespaces -c ./config/ora2pg.conf
Running: ora2pg -p -t PARTITION \
-o partition.sql -b ./schema/partitions -c ./config/ora2pg.conf
Running: ora2pg -p -t TYPE \
-o type.sql -b ./schema/types -c ./config/ora2pg.conf
Running: ora2pg -p -t MVIEW \
-o mview.sql -b ./schema/mviews -c ./config/ora2pg.conf
Running: ora2pg -p -t DBLINK \
-o dblink.sql -b ./schema/dblinks -c ./config/ora2pg.conf
Running: ora2pg -p -t SYNONYM \
-o synonym.sql -b ./schema/synonyms -c ./config/ora2pg.conf
Running: ora2pg -p -t DIRECTORY \
-o directorie.sql -b ./schema/directories -c ./config/ora2pg.conf
Généralement l’extraction des GRANT et TABLESPACE génère une erreur si l’utilisateur n’a pas les droits DBA.
Running: ora2pg -t PACKAGE \
-o package.sql -b ./sources/packages -c ./config/ora2pg.conf
Running: ora2pg -t VIEW \
-o view.sql -b ./sources/views -c ./config/ora2pg.conf
Running: ora2pg -t TRIGGER \
-o trigger.sql -b ./sources/triggers -c ./config/ora2pg.conf
Running: ora2pg -t FUNCTION \
-o function.sql -b ./sources/functions -c ./config/ora2pg.conf
Running: ora2pg -t PROCEDURE \
-o procedure.sql -b ./sources/procedures -c ./config/ora2pg.conf
Running: ora2pg -t PARTITION \
-o partition.sql -b ./sources/partitions -c ./config/ora2pg.conf
Running: ora2pg -t TYPE \
-o type.sql -b ./sources/types -c ./config/ora2pg.conf
Running: ora2pg -t MVIEW \
-o mview.sql -b ./sources/mviews -c ./config/ora2pg.conf
tp_migration/
141
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
��� config
� ��� ora2pg.conf
��� data
��� export_schema.sh
��� reports
� ��� columns.txt
� ��� report.html
� ��� tables.txt
��� schema
� ��� functions
� � ��� function.sql
� ��� grants
� � ��� grant.sql
� ��� mviews
� � ��� DEPARTMENTS_MV1_mview.sql
� � ��� MV_DEPARTMENTS_mview.sql
� � ��� mview.sql
� ��� packages
� � ��� package.sql
� ��� partitions
� � ��� partition.sql
� ��� procedures
� � ��� ADD_JOB_HISTORY_procedure.sql
� � ��� procedure.sql
� � ��� SECURE_DML_procedure.sql
� ��� sequences
� � ��� sequence.sql
� ��� tables
� � ��� CONSTRAINTS_table.sql
� � ��� INDEXES_table.sql
� � ��� table.sql
� ��� tablespaces
� � ��� tablespace.sql
� � ��� TBSP_INDEXES_tablespace.sql
� ��� triggers
� � ��� CHECK_RAISE_ON_AVG_trigger.sql
� � ��� trigger.sql
� � ��� UPDATE_JOB_HISTORY_trigger.sql
� ��� types
142
2. SCHÉMA ET DONNÉES
� � ��� type.sql
� ��� views
� ��� EMP_DETAILS_VIEW_view.sql
� ��� view.sql
��� sources
��� functions
� ��� function.sql
��� mviews
� ��� DEPARTMENTS_MV1_mview.sql
� ��� MV_DEPARTMENTS_mview.sql
� ��� mview.sql
��� packages
� ��� package.sql
��� partitions
� ��� partition.sql
��� procedures
� ��� ADD_JOB_HISTORY_procedure.sql
� ��� procedure.sql
� ��� SECURE_DML_procedure.sql
��� triggers
� ��� CHECK_RAISE_ON_AVG_trigger.sql
� ��� trigger.sql
� ��� UPDATE_JOB_HISTORY_trigger.sql
��� types
� ��� type.sql
��� views
��� EMP_DETAILS_VIEW_view.sql
��� view.sql
Import du schéma
Pour créer la base de données pghr sous l’utilisateur migration, il faut déjà créer l’utilisateur.
createuser --no-superuser --no-createrole --no-createdb migration
createdb -E UTF-8 --owner migration pghr
Et on importe les tables et les vues dans la base ; les autres objets seront importés à la fin.
psql -U migration pghr -f ./schema/types/type.sql
143
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
psql -U migration pghr -f ./schema/tables/table.sql
psql -U migration pghr -f ./schema/views/views.sql
psql -U migration pghr -c "\d+"
Liste des relations
Schéma| Nom |Type | Propriét.| Taille | Description
------+------------+-----+----------+--------+----------------------------------
public| countries |table| migration| 0 bytes| country table. Contains 25 rows.
| | | | | References with locations table.
public| departments|table| migration| 0 bytes| Departments table that shows
| | | | | details of departments where
| | | | | employees work. Contains 27 rows;
| | | | | references with locations,
| | | | | employees, and job_history tables.
public| emp_details|vue | migration| 0 bytes|
| _view | | | |
public| employees |table| migration| 0 bytes| employees table. Contains 107 rows.
| | | | | References with departments,
| | | | | jobs, job_history tables.
| | | | | Contains a self reference.
public| job_history|table| migration| 0 bytes| Table that stores job history of
| | | | | the employees. If an employee
| | | | | changes departments within the job
| | | | | or changes jobs within the department,
| | | | | new rows get inserted into this
| | | | | table with old job information of
| | | | | the employee. Contains a complex
| | | | | primary key: employee_id+start_date.
| | | | | Contains 25 rows. References with
| | | | | jobs, employees, and departments
| | | | | tables.
public| jobs |table| migration| 0 bytes| jobs table with job titles and
| | | | | salary ranges. Contains 19 rows.
| | | | | References with employees and
| | | | | job_history table.
public| locations |table| migration| 0 bytes| Locations table that contains
| | | | | specific address of a specific
| | | | | office, warehouse, and/or
144
2. SCHÉMA ET DONNÉES
| | | | | production site of a company.
| | | | | Does not store addresses /
| | | | | locations of customers. Contains
| | | | | 23 rows; references with the
| | | | | departments and countries tables.
public| person_typ |table| migration| 0 bytes|
public| phone |table| migration| 8192 b |
public| regions |table| migration| 0 bytes|
public| ssn |table| migration| 8192 b |
public| student_typ|table| migration| 0 bytes|
public| test |table| migration| 8192 b |
(13 lignes)
L’export de toutes les données de la base Oracle se fait en une seule commande :
[========================>] 10/10 tables (100.0%) end of scanning.
[========================>] 25/25 rows (100.0%) Table COUNTRIES (25.0 recs/sec)
[==> ] 25/219 rows (11.4%) on total data (avg: 25.0 recs/sec)
[========================>] 27/27 rows (100.0%) Table DEPARTMENTS (27.0 recs/sec)
[=====> ] 52/219 rows (23.7%) on total data (avg: 52.0 recs/sec)
[========================>] 107/107 rows (100.0%) Table EMPLOYEES (107.0 recs/sec)
[=================> ] 159/219 rows (72.6%) on total data (avg: 159.0 recs/sec)
[========================>] 19/19 rows (100.0%) Table JOBS (19.0 recs/sec)
[===================> ] 178/219 rows (81.3%) on total data (avg: 178.0 recs/sec)
[========================>] 10/10 rows (100.0%) Table JOB_HISTORY (10.0 recs/sec)
[====================> ] 188/219 rows (85.8%) on total data (avg: 188.0 recs/sec)
[========================>] 23/23 rows (100.0%) Table LOCATIONS (23.0 recs/sec)
[=======================> ] 211/219 rows (96.3%) on total data (avg: 211.0 recs/sec)
[========================>] 2/2 rows (100.0%) Table PHONE (2.0 recs/sec)
[=======================> ] 213/219 rows (97.3%) on total data (avg: 213.0 recs/sec)
[========================>] 4/4 rows (100.0%) Table REGIONS (4.0 recs/sec)
[=======================> ] 217/219 rows (99.1%) on total data (avg: 217.0 recs/sec)
[========================>] 2/2 rows (100.0%) Table SSN (2.0 recs/sec)
[========================>] 219/219 rows (100.0%) on total data (avg: 219.0 recs/sec)
[========================>] 0/0 rows (100.0%) Table TEST (0.0 recs/sec)
145
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
[========================>] 219/219 rows (100.0%) on total data (avg: 219.0 recs/sec)
Cette commande va générer un fichier par table et un fichier data.sql qui pourra être
utilisé pour charger les données en une fois.
data/
��� COUNTRIES_data.sql
��� data.sql
��� DEPARTMENTS_data.sql
��� EMPLOYEES_data.sql
��� JOB_HISTORY_data.sql
��� JOBS_data.sql
��� LOCATIONS_data.sql
��� PHONE_data.sql
��� REGIONS_data.sql
��� SSN_data.sql
��� TEST_data.sql
Pour importer les données dans la base PostgreSQL, on peut le faire fichier par fichier mais
il est plus simple d’utiliser le fichier de chargement global data.sql. Voici son contenu :
\set ON_ERROR_STOP ON
BEGIN;
\i data/COUNTRIES_data.sql
\i data/DEPARTMENTS_data.sql
\i data/EMPLOYEES_data.sql
\i data/JOBS_data.sql
\i data/JOB_HISTORY_data.sql
\i data/LOCATIONS_data.sql
\i data/PHONE_data.sql
\i data/REGIONS_data.sql
\i data/SSN_data.sql
\i data/TEST_data.sql
COMMIT;
146
2. SCHÉMA ET DONNÉES
Exécutons le chargement :
Vérification :
psql:./schema/triggers/UPDATE_JOB_HISTORY_trigger.sql:17:
ERROR: syntax error at or near "add_job_history"
LIGNE 3 : add_job_history(OLD.employee_id, OLD.hire_date, LOCALTIMES...
Ceci est dû au fait que le trigger utilise la fonction add_job_history() qui n’a pas encore
été créée, il faut donc l’importer avant de créer le trigger
148
3. PROCÉDURES STOCKÉES
3 PROCÉDURES STOCKÉES
3.1 INTRODUCTION
Ce module vous donnera les outils et la méthode pour réussir la migration de ce code. Il
vous expliquera aussi les différences de syntaxe entre ces deux langages avec des exem-
ples de cas concrets.
Cette partie indique les différents outils offrant une aide à la migration du code PL/SQL
vers le PL/pgSQL.
149
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
• Orafce :
– nombreuses fonctions de compatibilité Oracle
– to_char(1 param), add_month(), decode()...
– DBMS_ALERT, DBMS_PIPE, DBMS_OUTPUT, DBMS_RANDOM et UTL_FILE
• Migration Tool Kit :
– réservé à EDB PostgreSQL Plus Advanced Server Migration
– ne convertit pas le code PL/SQL
Librairie Orafce
Pour accélérer la phase de réécriture du code PL/SQL vers PL/pgSQL, il existe une bib-
liothèque de compatibilité nommée Orafce10 . Cette bibliothèque libre sous licence BSD
est développée par Pavel Stehule et émule le comportement de bon nombre de fonctions
et modules Oracle sous PostgreSQL.
• add_months(date, integer)
• last_day(date)
• next_day(date, text)
• next_day(date, integer)
• months_between(date, date)
• trunc(date, text)
• round(date, text)
Inutile sous PostgreSQL, il suffit d’enlever la clause FROM DUAL de toutes les requêtes
l’utilisant.
Module dbms_output
Habituellement, PostgreSQL utilise RAISE NOTICE pour retourner les informations aux
clients. La fonction Oracle dbms_output.put_line() a le même but mais ce module
Oracle permet en plus de gérer une file d’attente des messages.
• enable()
• disable()
• serveroutput()
• put()
10
https://github.com/orafce/orafce
150
3. PROCÉDURES STOCKÉES
• put_line()
• new_line()
• get_line()
• get_lines()
Module utl_file
Ce module permet de lire et d’écrire dans n’importe quel fichier accessible depuis le
serveur à partir du code PL/pgSQL. Ce module contient les fonctions suivantes :
• utl_file.fclose()
• utl_file.fclose_all()
• utl_file.fcopy()
• utl_file.fflush()
• utl_file.fgetattr()
• utl_file.fopen()
• utl_file.fremove()
• utl_file.frename()
• utl_file.get_line()
• utl_file.get_nextline()
• utl_file.is_open()
• utl_file.new_line()
• utl_file.put()
• utl_file.put_line()
• utl_file.putf()
• utl_file.tmpdir()
Module dbms_pipe
Module dbms_alert
Ces modules implémentent la plupart des fonctions définies dans le module PL/Vision
d’Oracle.
Ce module fournit des fonctions permettant de protéger les utilisateurs contre des injec-
tions SQL.
151
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Autres fonctions
• concat()
• nvl()
• nvl2()
• lnnvl()
• decode()
• bitand()
• nanvl()
• sinh()
• cosh()
• tanh()
• substr()
Cet ensemble d’outils de migration est un module propriétaire développé par la société
Enterprise DB et destiné à être mis en œuvre uniquement avec la version propriétaire du
serveur PostgreSQL Plus.
• Ora2pg
– convertisseur de code PL/SQL en PL/pgSQL sous licence GPL
– seul outil libre
Ora2Pg11 est le seul outil libre permettant une migration de la majorité du code PL/SQL.
Couplé à Orafce, il permet de limiter considérablement la retouche du code PL/SQL pour
son portage sous PostgreSQL.
11
https://ora2pg.darold.net/
152
3. PROCÉDURES STOCKÉES
Pour aider lors de la phase de test, vous pouvez utiliser le débogueur PL/SQL d’EDB qui
vous indiquera à quelle ligne du code se trouve le problème et plpgsql_lint, un module
pour PostgreSQL 9.0 et plus permettant de signaler des problèmes de syntaxe PL/pgSQL.
Ce validateur de code SQL embarqué vous alerte si vous faites référence à des tables,
colonnes ou variables inexistantes.
Ces deux modules sont des contributions en langage C et doivent être compilés. Si vous
utilisez pgAdmin, edb-debugger est directement intégré dans la distribution.
Il existe aussi SQLMaestro14 , un outil propriétaire qui permet l’exécution pas à pas du
code PL/pgSQL.
Cette partie dresse une liste exhaustive des différences majeures entre Oracle et Pos-
greSQL
12
https://git.postgresql.org/gitweb/?p=pldebugger.git
13
https://github.com/okbob/plpgsql_lint
14
https://www.sqlmaestro.com/products/postgresql/maestro/tour/pgsql_debugger/
153
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Les schémas
Sous PostgreSQL, les schémas sont de véritables espaces de nommage dont on peut
changer le propriétaire, alors qu’un schéma Oracle n’est ni plus ni moins qu’un utilisateur
auquel des objets seront associés.
Sensibilité à la casse
Lorsque les noms des objets ne sont pas écrits entre guillemets doubles, Oracle les trans-
forme en majuscule alors que PostgreSQL les transforme toujours en minuscule. S’ils sont
écrits entre guillemets doubles, les deux ont le même comportement : le nom est pris tel
qu’écrit.
Si vous avez créé vos objets avec des guillemets doubles sous Oracle et que vous les
exportez aussi avec des guillemets doubles, vous devrez toujours inclure ces guillemets
doubles dans le code de vos requêtes lorsque vous ferez appel à un objet. C’est donc
déconseillé, sous Oracle comme sous PostgreSQL.
Types de données
Synonymes
Les synonymes d’Oracle n’ont pas d’équivalent sous PostgreSQL. Il doit être possible
d’utiliser des vues pour tenter d’émuler cette fonctionnalité dans la mesure où il s’agit
d’accéder à des objets d’autres schémas.
Vues matérialisées
Concernant les vues matérialisées, elles existent sous PostgreSQL depuis la version 9.3.
Cependant, elles ne disposent pas de toutes les fonctionnalités accessibles sous Oracle. Il
est possible de les implémenter de toutes pièces en utilisant des fonctions et triggers. En
154
3. PROCÉDURES STOCKÉES
attendant leur implémentation complète au cœur du code de PostgreSQL, voici des doc-
uments expliquant de manière détaillée comment implémenter des vues matérialisées :
• PostgreSQL/Materialized Views15
• Materialized Views that Really Work16
Création de table
La définition des tables est quasiment identique pour les deux SGBD à la différence près
que PostgreSQL n’a pas de table temporaire dont les données insérées ne persistent que
le temps d’une transaction ou d’une session. Sous PostgreSQL c’est la table elle-même
qui est supprimée à la fin de la session.
Il n’y a pas non plus de notion de réservation de nombre de transactions allouées à chaque
bloc ou d’extents.
PCTFREE qui indique (en pourcentage) l’espace que l’on souhaite conserver dans le bloc
pour les mises à jour, correspond au fillfactor sous PostgreSQL. PCTUSED n’existe pas
(il n’a pas de sens dans l’implémentation de PostgreSQL).
Partitionnement
15
http://tech.jonathangardner.net/wiki/PostgreSQL/Materialized_Views
16
https://www.pgcon.org/2008/schedule/events/69.en.html
155
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Les partitions telles que gérées sous Oracle existent sous PostgreSQL depuis la version
10. Avec une version inférieure il faut utiliser l’héritage et définir des triggers et con-
traintes CHECK. Pour plus de détails, consultez le document 5.9. Partitioning17 ainsi que
le document Partitionnement (dans la KB Dalibo)18 . Les types de partitions supportées
sont List et Range. Les partitions de type Hash sont supportées par PostgreSQL v11. Le
partitionnement par référence n’est pas supporté.
Colonnes virtuelles
Pour remplacer les colonnes virtuelles, les vues sont idéales. Voici un exemple de défini-
tion de colonne virtuelle sous Oracle :
CREATE TABLE employees (
id NUMBER,
first_name VARCHAR2(10),
salary NUMBER(9,2),
commission NUMBER(3),
salary2 NUMBER GENERATED ALWAYS AS
(ROUND(salary*(1+commission/100),2)) VIRTUAL,
);
Vous pouvez aussi utiliser les triggers pour mettre à jour les colonnes et permettre
l’utilisation d’un index sur cette colonne, mais, dans ce cas, la colonne ne sera plus
vraiment « virtuelle ».
17
https://www.postgresql.org/docs/current/static/ddl-partitioning.html
18
https://kb.dalibo.com/formations/partitionnement/presentation
156
3. PROCÉDURES STOCKÉES
Les contraintes
L’ensemble des contraintes fonctionne exactement de la même manière, que ce soit pour
les clés primaires, les clés étrangères et les clés uniques ou pour les contraintes CHECK et
NOT NULL.
Les index
Pour les index, seule la forme BTREE correspond, les autres ne sont pas implémentées
mais PostgreSQL dispose lui aussi d’autres types d’index. Quoiqu’il en soit, la plupart des
index utilisés sont des index de type BTREE.
Les index BITMAP sur disque n’existent pas sous PostgreSQL. Ils sont créés en mémoire si
nécessaire à partir des index de type BTREE.
Les index IOT ne sont pas non plus supportés et peuvent être simulés à l’aide de la com-
mande CLUSTER qui trie une table en fonction de l’index.
Tablespaces
Les tablespaces correspondent, dans leur fonctionnalité principale, à ce qui est fait sur
Oracle, à savoir à définir un espace du système de fichiers où un plusieurs objets de la base
pourront être stockés. Il n’y a pas de notion de taille initiale ni d’extension du tablespace
sous PostgreSQL si ce n’est les limites imposées par le système de fichiers.
Types utilisateur
L’ensemble des types pouvant être défini par un utilisateur sont supportés avec plus
ou moins d’adaptation. Il peut notamment être nécessaire de redéfinir des fonctions
d’entrée/sortie définissant le comportement lors d’une insertion/lecture sur les données
du type. Dans la plupart des cas, il s’agit de types composites ou de tableaux parfaitement
supportés par PostgreSQL.
p_number CHAR(8)
);
et la version PostgreSQL :
CREATE TYPE phone_t AS (
a_code char(3),
p_number char(8)
);
dblink
PostgreSQL ne permet pas d’accéder nativement à une autre base de données à l’intérieur
d’une requête SQL. Il est cependant possible d’utiliser les extensions dblink ou Foreign
Data Wrapper pour accéder à des données à distance mais sans pour autant pouvoir
utiliser une notation à base de @ dans la requête.
Oracle autorise l’ajout ou la soustraction d’un nombre entier à une date. Par exemple :
SELECT SYSDATE + 1 FROM DUAL;
retournera la date de demain. Pour obtenir le même résultat avec PostgreSQL, il faut
utiliser un intervalle :
SELECT now() + interval '1 day';
158
3. PROCÉDURES STOCKÉES
Pour Oracle, le format défini par NLS_DATE_FORMAT détermine le format des dates qui
sera utilisé pour la sortie des fonctions TO_CHAR() et TO_DATE(). Avec PostgreSQL, cela
dépend du format défini par la variable de configuration DateStyle (par défaut ISO,
DMY).
Conversion implicite
Les conversions implicites de et vers un champ de type texte ont été supprimées sous
PostgreSQL depuis la version 8.3. Par exemple, il n’est pas possible de faire ce type de
requête :
create table depts ( numero char(2), nom varchar(25) );
pghr=# select * from depts where numero between 0 AND 42;
ERROR: operator does not exist: character >= integer
LIGNE 1 : select * from depts where numero between 0 AND 42;
Si l’on veut pouvoir faire fonctionner cette requête, il faut réaliser une conversion :
select * from depts where numero::integer between 0 AND 42;
Les séquences
L’appel aux fonctions des séquences se fait de manière différente même si les noms de
fonctions sont identiques. Avec Oracle, l’appel se fait avec nom_sequence.nom_fonction
alors qu’avec PostgreSQL, l’appel se fait en donnant le nom de la séquence en paramètre
de la fonction nom_fonction('nom_sequence').
Transactions autonomes
une autre connexion à la base de données, par exemple avec le module dblink.
Différences de syntaxe
Il y a aussi des différences d’écriture. Dans les déclarations de fonction, RETURN prends
un S. EXECUTE l’est toujours immédiatement, le mot clé IMMEDIATE n’existe donc pas.
Dans une fonction, les SELECT non affectés à une variable (sans INTO) doivent être rem-
placés par PERFORM. C’est exactement la même syntaxe qu’un SELECT normal, c’est sim-
plement le mot SELECT qui est remplacé par PERFORM.
Boucle inversée
Dans les ordres REVERSE LOOP, les bornes minimales et maximales doivent être inversées
sous PostgreSQL, car cela indique qu’à chaque pas la valeur sera décrémentée et non
incrémentée.
Une fonction doit impérativement déclarer le langage qu’elle utilise (SQL, PL/pgSQL, C,
PL/Perl, etc.) :
CREATE FUNCTION add(integer, integer) RETURNS integer
AS $$
select $1 + $2;
$$
LANGUAGE SQL
IMMUTABLE
160
3. PROCÉDURES STOCKÉES
CONNECT BY
Les curseurs
Au niveau des curseurs, leurs références est de type REFCURSOR au lieu de REF CURSOR.
161
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Par exemple la déclaration d’une référence sur un curseur se fait de la façon suivante sous
Oracle :
TYPE return_cur IS REF CURSOR RETURN ma_table%ROWTYPE;
p_retcur return_cur;
BULK COLLECT
La notion de BULK COLLECT n’existe pas sous PostgreSQL. En fait, il s’agit de charger dans
un tableau le résultat d’une requête et de parcourir ensuite ce tableau. Par exemple, ce
code Oracle
CREATE PROCEDURE tousLesAuteurs
IS
TYPE my_array IS varray(100) OF varchar(25);
temp_arr my_array;
BEGIN
SELECT nom BULK COLLECT INTO temp_arr FROM auteurs ORDER BY nom;
FOR i IN temp_arr.first .. temp_arr.last LOOP
DBMS_OUTPUT.put_line(i || ') nom: ' || temp_arr..(i));
END LOOP;
END tousLesAuteurs;
Oracle traite les chaînes vide comme NULL, c’est-à-dire qu’il ne fait pas la différence entre
NULL et ''.
162
3. PROCÉDURES STOCKÉES
La requête suivante sur Oracle renvoie vrai si le champ visan’est pas NULL mais est vide.
SELECT * FROM passeports WHERE visa IS NULL;
Ce comportement n’est absolument pas standard et est dangereux. Il faut vraiment faire
attention à ces parties de code qui, lors de la migration, peuvent provoquer des comporte-
ments aberrants de l’ application.
Les triggers sous PostgreSQL font obligatoirement appel à une fonction. Il y a donc sys-
tématiquement une déclaration de fonction et une déclaration de trigger.
CREATE OR REPLACE FUNCTION log_account_update() RETURNS trigger AS
...code ici...
LANGUAGE 'plpgsql';
Avec PostgreSQL, vous devez retourner les enregistrements dans les triggers avant action.
Dans le cas contraire, NULL est retourné, au contraire d’Oracle pour lequel le retour est
implicite. Par exemple :
CREATE FUNCTION gen_id () RETURNS TRIGGER AS
$$
DECLARE
noitem integer;
BEGIN
select into noitem max(no_produit) from produit;
IF noitem ISNULL THEN
noitem:=0;
163
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
END IF;
NEW.no_produit:=noitem+1;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';
PostgreSQL ne connaît que les déclarations de fonctions. Pour PostgreSQL, une procé-
dure n’est ni plus ni moins qu’une fonction qui retourne VOID.
À la déclaration d’une fonction, s’il n’y a pas de paramètre avec Oracle, il est possible
d’omettre les parenthèses de la section de déclaration des paramètres. Avec PostgreSQL,
ces parenthèses sont obligatoires.
164
3. PROCÉDURES STOCKÉES
Pour retourner un jeu d’enregistrements depuis une procédure stockée sous Oracle, c’est
un peu complexe. Il faut soit utiliser une référence de curseur soit définir une TABLE
FUNCTION. Avec PostgreSQL, il suffit de retourner le pseudo type RECORD. Par exemple :
Pour ne pas avoir à réécrire tous les appels vers les fonctions de ces paquets
(nom_paquet.nom_fonction), la solution est de créer sous PostgreSQL un schéma
portant le même nom que le paquet. L’appel aux fonctions se fera alors de façon
identique : nom_schema.nom_fonction.
De même, la notion de variable globale n’existe pas sous PostgreSQL. Pour pouvoir émuler
le comportement des variables globales, on peut utiliser les variables utilisateurs définies
dans le fichier de configuration postgresql.conf.
custom_variable_classes = 'globalvar'
Pour les versions depuis la 9.2, la variable peut être déclarée directement dans le fichier
de configuration avec une valeur initiale :
globalvar.ma_variable = '12'
ou être utilisée sans déclaration préalable dans le fichier de configuration comme suit.
Par exemple, pour créer une variable globale nommée id_region, il suffit d’utiliser la
commande SET :
SET globalvar.id_region = '38';
ou la fonction current_setting() :
select set_config('globalvar.ma_variable','12',false);
Il est aussi possible d’utiliser une table pour définir ces variables et leurs valeurs.
Certains langages, comme PL/Perl par exemple, disposent quant à eux, de variables glob-
ales.
166
3. PROCÉDURES STOCKÉES
Le SGBD Oracle utilise la notation (+) pour décrire le côté où se trouvent les valeurs NULL.
Pour une jointure à gauche, l’annotation (+) serait placée du côté droit (et inversement
pour une jointure à droite). Cette forme n’est pas supportée par PostgreSQL. Il faut donc
réécrire les jointures avec la notation normalisée : LEFT OUTER JOIN ou LEFT JOIN pour
une jointure à gauche et RIGHT OUTER JOIN ou RIGHT JOIN pour une jointure à droite.
Par exemple :
SELECT nom,prenom,titre
FROM auteurs a , auteurs_livres al, livres l
WHERE a.id_auteur = al.ref_auteur
AND al.ref_livre = l.id_livre(+);
SELECT nom,prenom,titre
FROM auteurs a , auteurs_livres al
LEFT JOIN livres l ON l.id_livre = a.ref_livre
WHERE a.id_auteur = l.red_auteur;
L’une des fonctionnalités les plus puissantes d’Ora2Pg est sa conversion automatique du
code Oracle PL/SQL en code PL/pgSQL pour PostgreSQL. Même s’il y a eu beaucoup
d’effort de développement au niveau de PostgreSQL pour faciliter la compatibilité avec
Oracle, il reste certaines parties qui nécessitent une réécriture :
Les paquets de procédures stockées n’existent pas sous PostgreSQL. Pour éviter la
réécriture complète des appels à ces fonctions, Ora2Pg crée un schéma portant le
même nom que le paquet, permettant ainsi de convertir implicitement les appels à
PACKAGE.FONCTION en SCHEMA.FONCTION.
Pour les triggers par exemple, sous Oracle, ils sont déclarés de la façon suivante :
CREATE TRIGGER trigger_name
BEFORE
DELETE OR INSERT OR UPDATE
ON table_name
pl/sql block
alors que, sous PostgreSQL, le code PL/pgSQL doit être dans une fonction. Ora2Pg le
convertira alors de la sorte :
CREATE OR REPLACE FUNCTION trigger_fct_trigger_name () RETURNS trigger AS
$BODY$
DECLARE
BEGIN
plpgsql block
END;
$BODY$
LANGUAGE 'plpgsql';
168
3. PROCÉDURES STOCKÉES
ON table_name
FOR EACH ROW
EXECUTE PROCEDURE trigger_fct_trigger_name ();
Pour les fonctions, les entêtes sont entièrement réécrites. Par exemple :
CREATE FUNCTION simple_fct RETURN VARCHAR2 IS
BEGIN
RETURN 'Simple Function';
END simple_fct;
deviendra :
CREATE OR REPLACE FUNCTION simple () RETURNS varchar AS $body$
BEGIN
RETURN 'Simple Function';
END simple;
$body$
LANGUAGE PLPGSQL;
Pour les fonctions, les choses se compliquent avec le passage de paramètres. Là encore,
Ora2Pg fait automatiquement la conversion. Par exemple, avec le code Oracle :
CREATE FUNCTION simple2_fct (string_in IN VARCHAR2 := 'No entry')
RETURN VARCHAR2 IS
BEGIN
RETURN string_in;
END simple2_fct;
on obtient :
CREATE OR REPLACE FUNCTION simple2_fct (string_in IN text DEFAULT 'No entry')
RETURNS varchar AS
$body$
BEGIN
RETURN string_in;
END simple2_fct;
$body$
LANGUAGE PLPGSQL;
Comme pour les paramètres de fonctions, les types de toutes les variables déclarées dans
une fonction sont automatiquement convertis dans leurs correspondances sous Post-
greSQL et déplacés dans une section DECLARE. Par exemple :
CREATE PROCEDURE load_file (pdname VARCHAR2, psname VARCHAR2, pfname VARCHAR2)
IS
src_file BFILE;
dst_file BLOB;
lgh_file BINARY_INTEGER;
BEGIN
169
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
pl/sql block
END load_file;
Dans ce cas, la procédure est transformée en fonction car, sous PostgreSQL, une procé-
dure n’est qu’une fonction retournant VOID.
Si certaines fonctions Oracle peuvent être remplacées directement par leur équiv-
alent sous PostgreSQL, comme par exemple NVL par coalesce ou SYSDATE par
170
3. PROCÉDURES STOCKÉES
Il est aussi possible que le nom de la fonction et les paramètres doivent être réécrits :
Il y a aussi les fonctions qui n’ont pas d’équivalent direct mais peuvent être écrites
autrement :
172
3. PROCÉDURES STOCKÉES
• Remplacement des ROWNUM dans la clause where par des clauses LIMIT et/ou
OFFSET
• Réécrit la clause HAVING … GROUP BY (variante acceptée par Oracle mais pas Post-
greSQL) en GROUP BY … HAVING
• Ajout du mot clé STRICT aux SELECT … INTO lorsqu’il y a EXCEPTION …
NO_DATA_FOUND ou TOO_MANY_ROWS
• Remplace les appels à MINUS par EXCEPT
Ces notations sont presque équivalentes, à la différence près qu’Oracle opère les tris
ORDER BY après la limitation du nombre de ligne. Dans l’exemple précédent le tri se fera
sur les 10 lignes retournées, alors que coté PostgreSQL, le tri est opéré avant.
Il faut donc faire très attention au résultat attendu, pour avoir le même résultat sous
Oracle que le LIMIT, il faudrait utiliser la requête suivante :
SELECT * FROM (SELECT * FROM A ORDER BY id) WHERE
ROWNUM <= 10;
La conversion des ROWNUM utilisés pour énumérer les lignes dans les requêtes n’est pas
couverte par Ora2Pg, par exemple :
173
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
SELECT * FROM (
SELECT t.*, ROWNUM AS rn
FROM mytable t
ORDER BY paginator, id
)
WHERE rn BETWEEN :start AND :end
• Déplacement des commentaires dans les CASE entre le WHEN et le THEN, non sup-
porté par PostgreSQL
• Remplacement des conditions IS NULL et IS NOT NULL par des instructions à
base de coalesce (pour Oracle, une chaîne vide est équivalente à NULL)
• Inverse les déclarations de curseur CURSOR moncurseur; pour les rendre compat-
ibles avec PostgreSQL : moncurseur CURSOR;
174
3. PROCÉDURES STOCKÉES
Du coup l’insertion d’une chaîne vide dans un champ avec une contrainte NOT NULL va
remonter une exception sous Oracle, mais pas dans PostgreSQL :
CREATE TABLE tempt (
id NUMBER NOT NULL,
descr VARCHAR2(255) NOT NULL
);
INSERT INTO temp_table (id, descr) VALUES (2, '');
ORA-01400: cannot insert NULL into ("HR"."TEMPT"."DESCR")
Si la directive NULL_EQUAL_EMPTY est activée, Ora2Pg remplace toutes les conditions avec
un test sur NULL par une appel à la fonction coalesce().
(field1 IS NULL)
et
(field2 IS NOT NULL)
Le remplacement est réalisé par défaut pour être sur que vous aurez le même comporte-
ment. Ce mécanisme a ses limites car il n’est pas possible d’insérer une chaîne vide dans
un champ numérique. La substitution n’est donc pas nécessaire, mais Ora2Pg ne sait pas
le détecter. De même si vous êtes assuré de ne pas avoir ce genre de problème alors le
remplacement des tests n’est pas nécessaire.
175
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Remplacement de :
• STORAGE_ERROR par OUT_OF_MEMORY
• ZERO_DIVIDE par DIVISION_BY_ZERO
• INVALID_CURSOR par INVALID_CURSOR_STATE
• SQLCODE par le presque équivalent SQLSTATE sous PostgreSQL
• raise_application_error en RAISE EXCEPTION
Remplacement de :
• SYS_REFCURSOR par REFCURSOR
• SQL%NOTFOUND par NOT FOUND
• SYS_EXTRACT_UTC par AT TIME ZONE 'UTC’
• dup_val_on_index en unique_violation
La liste des conversions est assez limitée, et il ne faut pas hésiter à faire des retours à
l’auteur d’Ora2Pg pour qu’il inclue celles que vous détectez.
Étapes :
• Cas des procédures avec transaction autonomes
• Import des fonctions et paquets de fonctions
• Absence de fonctions ou paquets
176
3. PROCÉDURES STOCKÉES
Non supportées nativement par PostgreSQL, Ora2Pg utilise une fonction de substitution
:
• La fonction d’origine est renommée avec le suffix _atx
• La fonction de substitution prend le nom originel de la fonction.
• La fonction de substitution appelle la fonction _atx au travers d’un dblink.
Voici un exemple de fonction Oracle utilisant une transaction autonome pour tracer
toutes les actions réalisées indépendamment et peu importe le résultat de la transaction.
Code Oracle :
Ora2Pg va donc d’abord transformer cette fonction et la renommer avec le suffix _atx
comme suit :
CREATE OR REPLACE FUNCTION log_action (username text, msg text) RETURNS VOID AS
$body$
DECLARE
-- Change this to reflect the dblink connection string
v_conn_str text := 'port=5432 dbname=testdb host=localhost '
'user=pguser password=pgpass';
v_query text;
BEGIN
v_query := 'SELECT true FROM log_action_atx ( ' || quote_literal(username)
|| ', ' || quote_literal(msg) || ' )';
PERFORM * FROM dblink(v_conn_str, v_query) AS p (ret boolean);
END;
$body$
LANGUAGE plpgsql STRICT SECURITY DEFINER;
177
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Dans le cas où la fonction est fortement utilisée, il est préférable de passer par un pooler
de connexion comme pgbouncer sur les connexions dblink pour éviter les pertes de per-
formances aux reconnexions incessantes.
C’est la même chose pour les paquets de fonctions. Pour simplifier le portage, comme
les packages n’existent pas sous PostgreSQL, Ora2Pg va créer un schéma portant le nom
du paquet et importer les fonctions dans ce schéma. Ceci permet de garder la notation
Oracle : PACKAGE.PROCEDURE qui sera en fait sous PostgreSQL : SCHEMA.FONCTION.
Pour faciliter l’import et l’édition manuelle du code des procédures stockées, l’activation
de la variable FILE_PER_FUNCTION permet d’exporter chaque fonction, procédure et trig-
ger dans un fichier dédié, nommé par exemple NOM_FONCTION_functions.sql, pour les
fonctions. Bien sûr, Ora2Pg crée aussi un fichier de chargement global permettant de
charger tous les fichiers en un seul appel. Ce fichier sera ici nommé functions.sql.
Pour les paquets de procédures stockées, toujours si cette variable est activée, Ora2Pg
va créer un sous répertoire portant le nom du paquet ou schéma. Les fonctions ou procé-
dures du paquet seront exportées dans leurs fichiers respectifs tel qu’au dessus.
Pour permettre la prise en compte immédiate des erreurs et leur traitement au fil de
l’import, les fichiers sont préfixés par l’appel à la commande suivante :
\set ON_ERROR_STOP ON
178
3. PROCÉDURES STOCKÉES
En cas de doute et d’erreur sur le code converti automatiquement par Ora2Pg, vous pou-
vez comparer avec le code source du PL/SQL d’Oracle exporté dans les sous-répertoires
du dossier sources du projet.
Ora2Pg préserve les commentaires définis dans le corps et à l’extérieur des fonctions
d’Oracle. Par contre, lors du chargement dans PostgreSQL, les commentaires définis en
dehors de ces fonctions ne seront pas intégrés.
L’étape des tests unitaires est indispensable pour détecter les erreurs avant la mise en
production et être sûr, en dehors de quelques différences acceptables, d’avoir le même
comportement et les mêmes résultats que ce soit avec Oracle ou PostgreSQL.
Les tests doivent être réalisés unitairement, fonction par fonction lors de la conversion
du code, puis fonctionnalité par fonctionnalité au niveau de l’application.
179
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Il est possible que les résultats diffèrent soit légèrement, par exemple avec le nombre de
décimales après la virgule, soit fortement, bien que le code PL/pgSQL ait été importé sans
erreurs.
Pour vous aider, vous pouvez utiliser le débogueur edb-debugger qui vous indiquera la
ligne problématique dans le code et plpgsql_lint qui vous remontera des problèmes de
référence à des tables, colonnes ou variables inexistantes.
En cas de doute sur le code converti automatiquement par Ora2Pg, vous pouvez comparer
avec le code source du PL/SQL d’Oracle exporté dans les sous-répertoires du dossier
sources du projet.
Ora2Pg dispose d’une action permettant de réaliser une série de tests sur les objets ayant
été migrés.
Cette action nommée TEST permet de savoir si tous les objets de la base Oracle ont été
créés sous PostgreSQL. Pour que cette fonctionnalité puisse être utilisée, il est nécessaire
de configurer les paramètres de connexion à la base PostgreSQL, à savoir PG_DSN, PG_USER
et PG_PWD. Puis, une fois cette connexion définie, exécuter la commande :
Lors de ce test, Ora2Pg va dénombrer les informations suivantes des deux cotés, base
source et base de destination :
180
3. PROCÉDURES STOCKÉES
Pour chaque objet dénombré, une section affichant les erreurs rencontrées permet
d’identifier la source du problème. Voici un exemple de rapport généré :
POSTGRES:countries:1
ORACLEDB:SUPPLIER:1
POSTGRES:supplier:1
ORACLEDB:DEPARTMENTS:1
POSTGRES:departments:1
ORACLEDB:JOB_HISTORY:1
POSTGRES:job_history:1
ORACLEDB:REGIONS:1
POSTGRES:regions:1
ORACLEDB:MYTABLE:1
POSTGRES:mytable:1
ORACLEDB:LOCATIONS:1
POSTGRES:locations:1
ORACLEDB:EMPLOYEES:2
POSTGRES:employees:2
ORACLEDB:JOBS:1
POSTGRES:jobs:1
[ERRORS UNIQUE CONSTRAINTS COUNT]
OK, Oracle and PostgreSQL have the same number of unique constraints.
182
3. PROCÉDURES STOCKÉES
183
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
POSTGRES:departments:2
ORACLEDB:MYTABLE:1
POSTGRES:mytable:1
ORACLEDB:EMPLOYEES:5
POSTGRES:employees:5
ORACLEDB:JOBS:2
POSTGRES:jobs:2
ORACLEDB:VAL_RESULTS:0
POSTGRES:val_results:0
ORACLEDB:JOB_HISTORY:4
POSTGRES:job_history:4
ORACLEDB:TESTA:0
POSTGRES:testa:0
ORACLEDB:REGIONS:1
POSTGRES:regions:1
ORACLEDB:TEST_TZ:0
POSTGRES:test_tz:0
ORACLEDB:MESURE:1
POSTGRES:mesure:1
ORACLEDB:FICHIER_DONNEE:1
POSTGRES:fichier_donnee:1
ORACLEDB:TEST_NUM:0
POSTGRES:test_num:0
ORACLEDB:LOCATIONS:2
POSTGRES:locations:2
[ERRORS NOT NULL CONSTRAINTS COUNT]
OK, Oracle and PostgreSQL have the same number of null constraints.
184
3. PROCÉDURES STOCKÉES
ORACLEDB:MYTABLE:0
POSTGRES:mytable:0
ORACLEDB:EMPLOYEES:0
POSTGRES:employees:0
ORACLEDB:JOBS:0
POSTGRES:jobs:0
ORACLEDB:VAL_RESULTS:0
POSTGRES:val_results:0
ORACLEDB:JOB_HISTORY:0
POSTGRES:job_history:0
ORACLEDB:TESTA:0
POSTGRES:testa:0
ORACLEDB:REGIONS:0
POSTGRES:regions:0
ORACLEDB:TEST_TZ:0
POSTGRES:test_tz:0
ORACLEDB:MESURE:0
POSTGRES:mesure:0
ORACLEDB:FICHIER_DONNEE:0
POSTGRES:fichier_donnee:0
ORACLEDB:TEST_NUM:0
POSTGRES:test_num:0
ORACLEDB:LOCATIONS:0
POSTGRES:locations:0
[ERRORS COLUMN DEFAULT VALUE COUNT]
OK, Oracle and PostgreSQL have the same number of column default value.
185
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
OK, Oracle and PostgreSQL have the same number of foreign keys.
POSTGRES:MVIEW:1
[ERRORS MVIEW COUNT]
OK, Oracle and PostgreSQL have the same number of MVIEW.
186
3. PROCÉDURES STOCKÉES
POSTGRES:SEQUENCE:0
[ERRORS SEQUENCE COUNT]
SEQUENCE does not have the same count in source database (1) and in PostgreSQL (0).
Évidement cela n’a de sens que si la base source n’a pas subi de modification du nombre
de lignes entre temps.
En raison du formatage des données retournées par Oracle il n’est pas possible de com-
parer simplement les données entre les deux bases, cependant on peut déjà s’assurer que
le nombre de lignes renvoyées par les vues est identique. Pour cela l’action TEST_VIEW
peut être utilisée.
• pgTap
– http://www.pgtap.org/
• pgUnit
– http://en.dklab.ru/lib/dklab_pgunit/
• Epic
– http://www.epictest.org/
187
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
pgTAP est une bibliothèque de fonctions pour PostgreSQL développées par David
E. Wheeler permettant d’écrire des tests unitaires au format TAP (Test Anything
Protocol) dans des scripts exécutables par la commande psql.
pgTAP permet de vraiment tester la base de données, non seulement en vérifiant la struc-
ture du schéma, mais aussi en testant les vues, les procédures, les fonctions, les règles,
ou triggers.
-- Start a transaction.
BEGIN;
SELECT plan( 2 );
\set domain_id 1
\set src_id 1
-- Insert stuff.
SELECT ok(
insert_stuff( 'www.foo.com', '{1,2,3}', :domain_id, :src_id ),
'insert_stuff() should return true'
);
BEGIN;
SELECT plan( 18 );
188
3. PROCÉDURES STOCKÉES
SELECT can_ok(
'insert_stuff',
ARRAY[ 'text', 'integer[]', 'integer', 'integer' ]
);
Pour plus d’informations sur le format TAP, consultez le site officiel19 , vous trouverez un
exemple d’implémentation Java avec le projet tap4j20 .
19
https://testanything.org/
20
https://sourceforge.net/projects/tap4j/
189
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Toutes les différentes composantes du projet de migration doivent être testées, pas seule-
ment la base de données et l’application mais aussi les performances et les scripts de
maintenance. Cela peut permettre par exemple de s’apercevoir qu’un index n’a pas été
créé ou que le serveur PostgreSQL n’a pas été optimisé correctement.
3.8 CONCLUSION
La conversion du code fait gagner du temps. Aussi étonnant que cela puisse paraître,
elle est très fonctionnelle. Cependant, tout aussi excellente qu’elle soit, il faudra toujours
vérifier les procédures stockées. Il faudra s’assurer que le résultat produit est le bon, et
que les performances sont au moins tout aussi bonnes. Cela fait que cette partie de la
migration est généralement la plus dure et la plus longue.
3.8.1 QUESTIONS
190
3. PROCÉDURES STOCKÉES
Tests unitaires
Créer un script de test simple des procédures stockées basé sur les commandes psql et
sqlplus.
191
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Commençons par importer la fonction manquante à notre trigger. Comme nous avons
choisi d’exporter les fonctions dans des fichiers séparés, la fonction se trouve dans le
fichier schema/procedures/ADD_JOB_HISTORY_procedure.sql.
SET
psql:schema/procedures/ADD_JOB_HISTORY_procedure.sql:24: NOTICE:
type reference job_history.employee_id%TYPE converted to integer
psql:schema/procedures/ADD_JOB_HISTORY_procedure.sql:24: NOTICE:
type reference job_history.start_date%TYPE converted to timestamp
without time zone
psql:schema/procedures/ADD_JOB_HISTORY_procedure.sql:24: NOTICE:
type reference job_history.end_date%TYPE converted to timestamp
without time zone
psql:schema/procedures/ADD_JOB_HISTORY_procedure.sql:24: NOTICE:
type reference job_history.job_id%TYPE converted to character varying
psql:schema/procedures/ADD_JOB_HISTORY_procedure.sql:24: NOTICE:
type reference job_history.department_id%TYPE converted to smallint
CREATE FUNCTION
Il n’y a eu aucune erreur à la création de la fonction, uniquement des indications sur les
types réellement utilisés.
192
3. PROCÉDURES STOCKÉES
$body$
LANGUAGE PLPGSQL;
psql:./schema/triggers/UPDATE_JOB_HISTORY_trigger.sql:17:
ERROR: syntax error at or near "add_job_history"
LIGNE 3 : add_job_history(OLD.employee_id, OLD.hire_date, LOCALTIMES...
Le problème ici est que la fonction est appelée directement, ce qui n’est pas possible avec
PostgreSQL : il faut l’appeler avec une instruction SELECT. Si l’on fait cette modification,
le trigger est chargé sans erreur.
Le chargement de la deuxième fonction est direct, sans erreur. Aucune retouche n’est à
faire sur le code de la fonction :
Pour l’import de fonction le mieux est d’utiliser le fichier d’import global des fonctions de
la façon suivante :
SET
SET
CREATE FUNCTION
SET
CREATE FUNCTION
ATTENTION, cela ne veut pas dire qu’il n’y a pas d’adaptation à faire. Notamment, si on
exécute la fonction emp_sal_ranking, on obtient ce message d’erreur :
Ceci est normal, la fonction est déclarée comme retournant un bigint alors que le résultat
de l’opération de retour est potentiellement un nombre à virgule. Il est donc nécessaire
ici de retourner un float ou un double.
pghr=> select emp_sal_ranking(105);
emp_sal_ranking
-----------------
0.125
(1 ligne)
194
3. PROCÉDURES STOCKÉES
Ici les variables tot_depts et tot_emps servent à tenir à jour le nombre total de départe-
ments et d’employés, probablement pour éviter l’appel à count(*). Ceci peut être fait
d’une autre manière, notamment par l’usage d’une table de variables mise à jour par trig-
gers.
De manière générale, pour émuler les variables globales on peut utiliser un table où utiliser
un langage procédural permettant ce type de stockage global, comme le PL/Perl.
psql:schema/packages/emp_mgmt/increase_sal_package.sql:26:
ERROR: unrecognized exception condition "no_sal"
CONTEXTE : compilation of PL/pgSQL function "increase_sal" near line 10
Après étude de la fonction, l’erreur est imputable à une variable contenant un message
d’erreur non définie.
...sql
THEN RAISE no_sal;
...
Après avoir résolu un dernier problème identique dans le fichier schema/ packages/
emp_mgmt/ increase_comm_package.sql, le paquet de fonctions a été créé sans erreur.
Tests unitaires Voici un exemple de script utilisant les commandes psql et sqlplus pour
réaliser les tests unitaires des procédures stockées importées:
#!/bin/bash
#------------------------------------------------------------------------------
# script utilisé pour valider les réponses des fonctions
# entre Oracle et PostgreSQL
195
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
#------------------------------------------------------------------------------
#-- Oracle --
export ORACLE_HOME="/opt/oracle/11.2/instantclient_11_2"
export PATH="$PATH:/opt/oracle/11.2/instantclient_11_2"
ORACLE_SID="xe"
ORACLE_HOST="192.168.1.109"
ORACLE_USER="hr"
ORACLE_PWD="phoenix"
SQLPLUS="sqlplus -S
$ORACLE_USER/$ORACLE_PWD@$ORACLE_HOST/$ORACLE_SID @/tmp/oracle.sql"
#-- PostgreSQL --
PGSQL_HOST="localhost"
PGSQL_USER="migration"
PGSQL_DB=pghr
PSQL="psql -tA -U $PGSQL_USER -f /tmp/postgresql.sql $PGSQL_DB"
#PGSQL_PORT=5432
#PSQL="psql -h $PGSQL_HOST -p $PGSQL_PORT -tA -U $PGSQL_USER
-f /tmp/postgresql.sql $PGSQL_DB"
exec_oracle () {
echo "
set verify on
set feedback off
set termout on
set linesize 40
set verify off
set feedback off
set pagesize 0" > /tmp/oracle.sql
echo $1 >> /tmp/oracle.sql
echo "exit;" >> /tmp/oracle.sql
echo -n "\tOracle : \t";
$SQLPLUS
}
exec_postgresql () {
echo $1 > /tmp/postgresql.sql
echo -n "\tPostgreSQL : \t"
$PSQL
}
196
3. PROCÉDURES STOCKÉES
sh tests_unitaires.sh
------------------------------------------------------------------
Test fonction : secure_dml()
Oracle : BEGIN secure_dm(); END;
*
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00201: identifier 'SECURE_DM' must
be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
PostgreSQL : psql:/tmp/postgresql.sql:1:
ERROR: You may only make changes outside normal office hours
------------------------------------------------------------------
Test fonction : emp_sal_ranking()
Oracle : .125
PostgreSQL : 0.125
------------------------------------------------------------------
Test fonction : last_first_name()
Oracle : Employee: 105 - AUSTIN, DAVID
PostgreSQL : Employee: 105 - AUSTIN, DAVID
197
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
4.1 INTRODUCTION
Après avoir migré les données, il faut également retravailler à minima les requêtes de
façon à ce qu’elles puissent s’exécuter sur PostgreSQL. Le langage SQL étant issu d’une
norme ISO qui évolue constamment, le travail n’est pas aussi important que s’il s’agissait
d’une réécriture dans un nouveau langage. Mais certaines formes d’écritures peuvent
poser problème. Elles sont héritées des temps où Oracle offrait ses propres extensions
au langage SQL avant que les fonctionnalités ne soient disponibles dans la norme SQL.
Bien qu’Oracle supporte maintenant les dernières avancées de la norme SQL, de nom-
breuses applications à migrer utilisent encore le dialecte SQL. Ce chapitre a pour objectif
de présenter les principaux éléments qui nécessitent une réécriture.
• Langage SQL
– norme ISO
– dernière version 2011
• La façon d’écrire les requêtes ne change pas
– sauf certains détails
198
4. PORTAGE DES REQUÊTES SQL
• Plusieurs incompatibilités
– Oracle ne supporte pas bien la norme SQL
– types numériques, chaînes, binaires, dates
• PostgreSQL fournit également des types spécialisés
Les types smallint, integer, bigint, float, real, double precision sont plus rapides que le type
numeric sous PostgreSQL: ils utilisent directement les fonctions câblées des processeurs.
Il faut donc les privilégier.
– Par instance (avant la 8.4), par base de données (depuis la 8.4), par colonne
(depuis la 9.1)
Au niveau de PostgreSQL, il existe trois types de données pour les chaînes de caractères :
char, varchar et text. Le type varchar2 d’Oracle est l’équivalent du type varchar de Post-
greSQL. Il est possible de ne pas donner de taille à une colonne de type varchar, ce qui
revient à la déclarer de type text. Dans ce cas, la taille maximale est de 1 Go. Suivant
l’encodage, le nombre de caractères intégrables dans la colonne diffère.
La grosse différence entre PostgreSQL et Oracle pour les chaînes de caractères tient dans
la façon dont les chaînes vides sont gérées. Oracle ne fait pas de différence entre une
chaîne vide et une chaîne NULL. PostgreSQL fait cette différence. Du coup, tous les
tests de chaînes vides effectuées avec un IS NULL et tous les tests de chaînes NULL
effectués avec une comparaison avec une chaîne vide ne donneront pas le même résultat
avec PostgreSQL. Ces tests doivent être vérifiés systématiquement par les développeurs
d’applications et de routines stockées.
dev2=# SELECT cast('' AS varchar) IS NULL;
?column?
----------
f
(1 row)
L’implémentation des types binaires sur PostgreSQL est très particulière. De plus, elle est
double, dans le sens où vous avez deux moyens d’importer et d’exporter des données
binaires dans PostgreSQL.
200
4. PORTAGE DES REQUÊTES SQL
Large Object, vous récupérez un identifiant que vous pouvez stocker dans une table util-
isateur (généralement dans une colonne de type OID). Vous devez utiliser cet identifiant
pour traiter l’objet en question (export, suppression, etc.). Cette implémentation a de
nombreux défauts, qui fait qu’elle est rarement utilisée. Parmi les défauts, notons que la
suppression d’une ligne d’une table utilisateur référençant un Large Object ne supprime
pas le Large Object référencé. Notons aussi qu’il est bien plus difficile d’interagir et de
maintenir une table système. Notons enfin que la sauvegarde avec pg_dump est plus
complexe et plus longue si des Larges Objects sont dans la base à sauvegarder. Son
principal avantage sur la deuxième implémentation est la taille maximale d’un Large
Object : 4 To depuis la 9.3 (2 Go avant).
La deuxième implémentation est un type de données appelé bytea. Comme toutes les
colonnes dans PostgreSQL, sa taille maximale est 1 Go, ce qui est inférieur à la taille
maximale d’un Large Object. Cependant, c’est son seul défaut.
Bien que l’implémentation des Large Objects est en perte de vitesse à cause des nombreux
inconvénients inhérents à son implémentation, elle a été l’objet d’améliorations sur les
dernières versions de PostgreSQL : gestion des droits de lecture ou écriture des Large
Objects, notion de propriétaire d’un Large Object, limite de taille relevée à 4 To. Elle n’est
donc pas obsolète.
L’un des gros avantages de PostgreSQL est son extensibilité. Mais même sans cela, Post-
greSQL propose de nombreux types natifs qui vont bien au-delà des types habituels. Ce
sont des types métiers, pour le réseau, la géométrie, la géographie, la gestion du temps,
la gestion des intervalles de valeurs, etc.
Il est donc tout à fait possible d’améliorer une application en passant sur des types spé-
cialisés de PostgreSQL.
201
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
• Types différents
• Fonctions équivalentes à SYSDATE
• Fonctions de manipulation différentes
• Date
– sous Oracle: YYYY/MM/DD HH:MM:SS
– sous PostgreSQL: YYYY-MM-DD (conforme SQL)
• Time
– sous Oracle: YYYY/MM/DD HH:MM:SS
– sous PostgreSQL: HH:MM:SS.mmmmmmm (µs)
Oracle a tendance à mélanger un peu tous les types dates. Ce n’est pas le cas au niveau
de PostgreSQL. Une colonne de type date au niveau de PostgreSQL contient seulement
une date, il n’y a pas d’heure ajoutée. Une colonne de type time au niveau de PostgreSQL
contient seulement un horodatage (heure, minute, seconde, milliseconde), mais pas de
date.
Par défaut, PostgreSQL intègre le fuseau horaire dans les types timestamp ( date et heure).
Le stockage est fait en UTC, mais la restitution dépend du fuseau horaire indiqué par le
client.
202
4. PORTAGE DES REQUÊTES SQL
4.4.3 SYSDATE
• SYSDATE
– retourne la date et l’heure courante, sans timezone
– équivalent direct :
SELECT localtimestamp;
• PostgreSQL implémente d’autres fonctions :
– current_timestamp
– current_date
– current_time
• DECODE
• NVL
203
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
4.5.1 DECODE
La fonction DECODE d’Oracle est un équivalent propriétaire de la clause CASE, qui est nor-
malisée. Oracle supporte CASE mais DECODE est souvent utilisé par habitude.
Attention aux commentaires entre le WHEN et le THEN qui ne sont pas supportés par Post-
greSQL.
204
4. PORTAGE DES REQUÊTES SQL
4.5.2 NVL
La fonction NVL d’Oracle est encore souvent utilisée, bien que la fonction normalisée
COALESCE soit également implémentée. Ces deux fonctions retournent le premier argu-
ment qui n’est pas NULL. Bien évidemment, PostgreSQL n’implémente que la fonction nor-
malisée COALESCE. Un simple remplacement de l’appel de NVL par un appel à COALESCE
est suffisant.
Un article écrit par Lucas Jellema montre les évolutions d’Oracle 11gR2 concernant les
requêtes récursives. Les différents exemples montrent que les requêtes écrites utilisent
les CTE au lieu du CONNECT BY qui fait partie seulement du dialecte SQL Oracle. L’article
est disponible à cette adresse21 .
Si l’on exécute la seconde requête donnée en exemple (la première employant CONNECT
BY directement sur PostgreSQL, on obtient le message d’erreur suivant :
DÉTAIL : There is a WITH item named "employees", but it cannot be referenced
from this part of the query.
ASTUCE : Use WITH RECURSIVE, or re-order the WITH items to remove forward
references.
205
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
4.6 ROWNUM
• pseudo-colonne Oracle
• numérote les lignes du résultat
– parfois utiliser pour limiter le résultat
Oracle propose une pseudo-colonne ROWNUM qui permet de numéroter les lignes du résul-
tat d’une requête SQL. La clause ROWNUM peut être utilisée soit pour numéroter les lignes
de l’ensemble retourné par la requête. Elle peut aussi être utilisée pour limiter l’ensemble
retourné par une requête.
Dans le premier cas, à savoir numéroter les lignes de l’ensemble retourné par la requête,
il faut réécrire la requête pour utiliser la fonction de fenêtrage row_number(). Bien
qu’Oracle préconise d’utiliser la fonction normalisée row_number(), il est fréquent de
trouver ROWNUM dans une requête issue d’une application s’appuyant sur une ancienne
version d’Oracle :
SELECT ROWNUM, * FROM employees;
Il faut toutefois faire attention à une clause ORDER BY dans une requête employant
ROWNUM pour numéroter les lignes retournées par une requête. En effet, le tri commandé
par ORDER BY est réalisé après l’ajout de la pseudo-colonne ROWNUM. Il faudra vérifier le
résultat de la requête sous Oracle et PostgreSQL pour vérifier qu’elles retourneront des
résultats identiques.
La clause WITH ORDINALITY de PostgreSQL 9.4 permet de numéroter les lignes de résultat
d’un appel de fonction.
206
4. PORTAGE DES REQUÊTES SQL
Pour limiter l’ensemble retourné par une requête, il faut supprimer les prédicats utilisant
ROWNUM dans la clause et les transformer en couple LIMIT/OFFSET.
La requête suivante retourne les 10 premières lignes de la table employees sous Oracle :
SELECT *
FROM employees
WHERE ROWNUM < 11;
Elle sera réécrite de la façon suivante lors du portage de la requête pour PostgreSQL :
SELECT *
FROM employees
LIMIT 10;
De la même façon que précédemment, Oracle effectuera le tri commandé par ORDER BY
après l’ajout de la pseudo-colonne ROWNUM, comme le montre le plan d’exécution d’une
requête similaire à l’exemple donné plus haut :
Si une requête Oracle est écrite de manière aussi simple, il conviendra de la réécrire de la
façon suivante :
SELECT r.*
FROM (SELECT *
FROM t1
LIMIT 10) r
ORDER BY col
Cette requête serait simplifiée de cette façon une fois migrée vers PostgreSQL :
SELECT *
FROM t1
ORDER BY col
LIMIT 10;
208
4. PORTAGE DES REQUÊTES SQL
4.7 JOINTURES
• Jointures internes
– FROM tab1, tab2 WHERE tab1.col = tab2.col
– FROM tab1 JOIN tab2 ON (tab1.col = tab2.col)
Le SGBD Oracle supporte la syntaxe normalisée d’écriture des jointures seulement depuis
la version 9i. Auparavant, les jointures étaient exprimées telle que le définissait la pre-
mière version de la norme SQL, avec une notation propriétaire pour la gestion des join-
tures externes. PostgreSQL ne supporte pas cette notation propriétaire, mais supporte
parfaitement la notation portée par la norme SQL.
SELECT *
FROM t1, t2
WHERE t1.col1 = t2.col1
Cependant, cette syntaxe ne permet pas d’écrire de jointure externe. Il est donc recom-
mandé d’utiliser systématiquement la nouvelle notation, qui est aussi bien plus lisible dans
le cas où des jointures simples et externes sont mélangées :
SELECT *
FROM t1
JOIN t2 ON (t1.col1 = t2.col1)
Le SGBD Oracle utilise la notation (+) pour décrire le côté où se trouvent les valeurs
NULL. Pour une jointure à gauche, l’annotation (+) serait placée du côté droit (et inverse-
ment pour une jointure à droite). Cette forme n’est pas supportée par PostgreSQL. Il faut
donc réécrire les jointures avec la notation normalisée : LEFT OUTER JOIN ou LEFT JOIN
pour une jointure à gauche et RIGHT OUTER JOIN ou RIGHT JOIN pour une jointure à
droite.
La requête suivante, écrite pour Oracle et qui comporte une jointure à gauche :
209
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
SELECT *
FROM t1, t2
WHERE t1.col1 = t2.col3 (+);
Dans les versions précédant la version 9i d’Oracle, une jointure externe complète (FULL
OUTER JOIN) devait être exprimée à l’aide d’un UNION entre une jointure à gauche et une
jointure à droite. L’exemple suivant implémente une jointure externe complète :
SELECT *
FROM t1, t2
WHERE t1.col1 = t2.col3 (+)
UNION ALL
SELECT *
FROM t1, t2
WHERE t1.col1 (+) = t2.col3
AND t1.col IS NULL
Cette requête doit être réécrite et sera par ailleurs simplifiée de la façon suivante :
SELECT *
FROM t1
FULL OUTER JOIN t2 ON (t1.col1 = t2.col3);
210
4. PORTAGE DES REQUÊTES SQL
Un produit cartésien peut être exprimé de la façon suivante dans Oracle et PostgreSQL :
SELECT *
FROM t1, t2;
Bien que la documentation Oracle indique que la clause GROUP BY précède la clause
HAVING , la grammaire Oracle autorise l’inverse. Il faut donc corriger les requêtes écrites
de la façon HAVING … GROUP BY.
211
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
L’opérateur ensembliste MINUS est à transposer en EXCEPT pour PostgreSQL. Les autres
opérateurs ensemblistes UNION, UNION ALL et INSERSECT ne nécessitent pas de transpo-
sition.
Ainsi, la requête suivante retourne les produits de l’inventaire qui n’ont pas fait l’objet
d’une commande. Elle est exprimée ainsi pour Oracle :
SELECT product_id FROM inventories
MINUS
SELECT product_id FROM order_items
ORDER BY product_id;
4.10 TRANSACTIONS
Pour PostgreSQL, si vous souhaitez pouvoir annuler des modifications, vous devez utiliser
BEGIN avant d’exécuter les requêtes de modification. Toute transaction qui commence
par un BEGIN doit être validée avec COMMIT ou annulée avec ROLLBACK. Si jamais la
connexion est perdue entre le serveur et le client, le ROLLBACK est automatique.
Par exemple, si on insère une donnée dans une table, sans faire de BEGIN avant, et qu’on
essaie d’annuler cette insertion, cela ne fonctionnera pas :
212
4. PORTAGE DES REQUÊTES SQL
dev2=# BEGIN;
BEGIN
dev2=# INSERT INTO t1 VALUES (2);
INSERT 0 1
dev2=# ROLLBACK;
ROLLBACK
dev2=# SELECT * FROM t1;
id
----
1
(1 row)
Autre différence au niveau transactionnel : il est possible d’intégrer des ordres DDL dans
des transactions. Par exemple :
dev2=# BEGIN;
BEGIN
dev2=# CREATE TABLE t2(id integer);
CREATE TABLE
dev2=# INSERT INTO t2 VALUES (1);
INSERT 0 1
dev2=# ROLLBACK;
ROLLBACK
dev2=# INSERT INTO t2 VALUES (2);
ERROR: relation "t2" does not exist
LINE 1: INSERT INTO t2 VALUES (2);
^
Enfin, quand une transaction est en erreur, vous ne sortez pas de la transaction. Vous
devez absolument exécuter un ordre de fin de transaction (COMMIT ou ROLLBACK, peu
importe, un ROLLBACK sera exécuté) :
dev2=# BEGIN;
213
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
BEGIN
dev2=# INSERT INTO t2 VALUES (2);
ERROR: relation "t2" does not exist
LINE 1: INSERT INTO t2 VALUES (2);
^
dev2=# INSERT INTO t1 VALUES (2);
ERROR: current transaction is aborted, commands ignored until
end of transaction block
dev2=# SELECT * FROM t1;
ERROR: current transaction is aborted, commands ignored until
end of transaction block
dev2=# ROLLBACK;
ROLLBACK
dev2=# SELECT * FROM t1;
id
----
1
(1 row)
Il est possible d’indiquer le niveau d’isolation d’une transaction en l’indiquant dans l’ordre
d’ouverture d’une transaction :
BEGIN [ WORK | TRANSACTION ] [ mode_transaction [, ...] ]
où mode_transaction est :
ISOLATION LEVEL
{ SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }
READ WRITE | READ ONLY
[ NOT ] DEFERRABLE
READ UNCOMMITTED est un synonyme de READ COMMITTED sous PostgreSQL, tout comme
sous Oracle : les moteurs étant MVCC, le mode READ UNCOMMITTED n’a pas d’intérêt (les
écrivains ne bloquent pas les lecteurs, les lecteurs ne bloquent pas les écrivains).
214
4. PORTAGE DES REQUÊTES SQL
ce niveau d’isolation par le biais de verrous pessimistes, grevant ainsi les performances.
Les versions plus anciennes d’Oracle possédaient d’ailleurs un paramètre non-documenté
SERIALIZABLE pour activer l’emploi de verrous pessimistes, mais il n’est plus supporté
depuis Oracle 8.1.6. Ce paramètre permet donc d’activer ce mode d’isolation de façon
à ce qu’il soit respectueux de la norme, au prix de performances dégradées. Dans les
versions actuelles, Oracle n’utilise pas de verrou et de ce fait, son implémentation du
niveau d’isolation SERIALIZABLE n’est pas respectueuse de la norme, à la différence de
PostgreSQL. Il faut noter également que depuis la version 9.1, PostgreSQL est le premier
SGBD qui implémente un mode d’isolation SERIALIZABLE parfaitement respectueux de
la norme SQL. Cette fonctionnalité, issue de travaux de recherches universitaires22 , est
appelée Serializable Snapshot Isolation et corrige les défauts des implémentations23 précé-
dentes du niveau SERIALIZABLE.
Oracle permet de positionner le niveau d’isolation des transactions pour une session don-
née, c’est-à-dire pour toutes les transactions réalisées dans la même session.
L’ordre SET SESSION ... permet de réaliser la même chose pour PostgreSQL :
SET SESSION TRANSACTION ISOLATION LEVEL ...;
Pour plus de détails sur les niveaux d’isolation, consulter la documentation de PostgreSQL
sur l’isolation des transactions24 .
4.10.2 SAVEPOINT
• SAVEPOINT
• RELEASE SAVEPOINT
• ROLLBACK TO SAVEPOINT
Les SAVEPOINT fonctionnent sans régression par rapport au SGBD Oracle. Les verrous
acquis avant la mise en place d’un SAVEPOINT ne sont pas relâchés si un SAVEPOINT est
relâché par un RELEASE SAVEPOINT ou un ROLLBACK TO SAVEPOINT
215
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Les ordres DML acquièrent des verrous implicites. La différence notable entre Oracle et
PostgreSQL concerne l’ordre SELECT : Oracle n’acquiert aucun verrou, tandis que Post-
greSQL pose un verrou de type ACCESS SHARE. De ce fait, Oracle ne protège en aucun
cas les lecteurs de modifications telles que la suppression d’une table. Une lecture peut
être interrompue suite à un DROP TABLE concurrent. L’acquisition par PostgreSQL d’un
verrou ACCESS SHARE pour la lecture protège de ce genre de problèmes.
Les ordres SELECT FOR UPDATE peuvent nécessiter des adaptations. La syntaxe Oracle
est en effet un peu plus riche que celle de PostgreSQL pour ce qui concerne cet ordre
SQL.
Oracle propose une syntaxe WAIT et NOWAIT. PostgreSQL ne propose que la clause NOWAIT.
La clause WAIT est implicite si NOWAIT n’est pas spécifié, il faudra donc la supprimer. La
requête SELECT ... FOR UPDATE WAIT; devient SELECT ... FOR UPDATE;.
216
4. PORTAGE DES REQUÊTES SQL
Concernant la syntaxe de l’ordre LOCK TABLE d’Oracle est compatible avec celle de Post-
greSQL pour les cas généraux. L’ensemble des modes de verrouillage proposés par Oracle
existent tous dans PostgreSQL. On peut noter que PostgreSQL propose plus de type de
verrous.
Tout comme pour l’ordre SELECT FOR UPDATE, Oracle propose une syntaxe WAIT et
NOWAIT. PostgreSQL ne propose aussi que la clauseNOWAIT. La clause WAIT est implicite
si NOWAIT n’est pas spécifié, il faudra donc la supprimer. La requête LOCK TABLE ...
WAIT; devient LOCK TABLE ...;.
Les clauses PARTITION et SUBPARTITION ne peuvent cependant pas être reprises. Dans
le cas de la mise en œuvre du partitionnement dans PostgreSQL, il faut désigner la table
correspondant à la partition ciblée par l’acquisition d’un verrou.
4.11 HIÉRARCHIES
Il n’existe pas de clause directement équivalente dans PostgreSQL, aussi un travail impor-
tant de portage doit être réalisé pour porter les requêtes utilisant cette clause.
217
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
• START WITH
– condition de départ
• CONNECT BY PRIOR
– lien hiérarchique
SELECT empno, ename, job, mgr
FROM emp
START WITH mgr IS NULL
CONNECT BY PRIOR empno = mgr
Soit la requête SQL suivante qui explore la hiérarchie de la table emp. La colonne mgr de
cette table désigne le responsable hiérarchique d’un employé. Si elle vaut NULL, alors
la personne est au sommet de la hiérarchie (START WITH mgr IS NULL). Le lien avec
l’employé et son responsable hiérarchique est construit avec la clause CONNECT BY PRIOR
empno = mgr qui indique que la valeur de la colonne mgr correspond à l’identifiant empno
du niveau de hiérarchie précédent.
SELECT empno, ename, job, mgr
FROM emp
START WITH mgr IS NULL
CONNECT BY PRIOR empno = mgr
Le portage de cette requête est réalisé à l’aide d’une requête récursive (WITH RECURSIVE).
La récursion est initialisée dans une première requête qui récupère les lignes qui corre-
spondent à la condition de la clause START WITH de la requête précédente : mgr IS NULL.
La récursion continue ensuite avec la requête suivante qui réalise une jointure entre la ta-
ble emp et la vue virtuelle emp_hierarchy qui est définie par la clause WITH RECURSIVE. La
condition de jointure correspond à la clause CONNECT BY. La vue virtuelle emp_hierarchy
a pour alias prior pour mieux représenter la transposition de la clause CONNECT BY.
Il faudra néanmoins faire attention à l’ordre des lignes qui sera différent avec la requête
WITH RECURSIVE. En effet, Oracle utilise un algorithme depth-first dans son implémen-
218
4. PORTAGE DES REQUÊTES SQL
tation du CONNECT BY. Ainsi, il explorera d’abord chaque branche avant de passer à la
suivante. L’implémentation WITH RECURSIVE est de type breadth-first qui explore chaque
niveau de hiérarchie avant de descendre.
Il est possible de retrouver l’ordre de tri d’une requête CONNECT BY pour une version an-
térieure à la 11g d’Oracle en triant sur une colonne path, telle qu’elle est construite pour
émuler la clause SYS_CONNECT_BY_PATH :
WITH RECURSIVE emp_hierarchy (empno, ename, job, mgr, path) AS (
SELECT empno, ename, job, mgr, ARRAY[ename::text] AS path
FROM emp
WHERE mgr IS NULL
UNION ALL
SELECT emp.empno, emp.ename, emp.job, emp.mgr, prior.path
|| emp.ename::text AS path
FROM emp
JOIN emp_hierarchy prior ON (emp.mgr = prior.empno)
)
SELECT empno, ename, job FROM emp_hierarchy AS emp
ORDER BY path
Si vous utilisez Oracle 11g, la requête retournera quoi qu’il en soit les résultats dans un
ordre différent.
219
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
220
4. PORTAGE DES REQUÊTES SQL
Une autre façon de faire est d’utiliser un tableau pour stocker le chemin le temps de la
récursion, puis de construire la représentation textuelle de ces chemins au moment de la
sortie des résultats. À noter la conversion de la valeur de ename en type text pour chaque
élément ajouté dans le tableau path. Cette variante peut être utile pour l’émulation de la
clause NOCYCLE comme vu plus bas :
WITH RECURSIVE emp_hierarchy (empno, ename, job, mgr, path) AS (
SELECT empno, ename, job, mgr, ARRAY[ename::text] AS path
FROM emp
WHERE mgr IS NULL
UNION ALL
SELECT emp.empno, emp.ename, emp.job, emp.mgr, prior.path ||
emp.ename::text AS path
FROM emp
JOIN emp_hierarchy prior ON (emp.mgr = prior.empno)
)
SELECT empno, ename, job, array_to_string(path, '/') AS path
FROM emp_hierarchy AS emp
221
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
• équivalent de NOCYCLE
• tableau contenant les éléments
– pseudo-colonne cycle
– element = ANY (tableau) AS cycle
– WHERE cycle = false
4.12 INCOMPATIBILITÉS
222
4. PORTAGE DES REQUÊTES SQL
4.12.1 HINTS
L’optimiseur Oracle supporte des hints, qui permettent au DBA de tromper l’optimiseur
pour lui faire prendre des chemins que l’optimiseur a jugé trop coûteux. Ces hints sont
exprimés sous la forme de commentaires et ne seront donc pas pris en compte par Post-
greSQL, qui ne gère pas ces hints.
Néanmoins, une requête comportant un hint pour contrôler l’optimiseur Oracle doit faire
l’objet d’une attention particulière, et l’analyse de son plan d’exécution devra être faite
minutieusement, pour s’assurer que, sous PostgreSQL, la requête n’a pas de problème
particulier, et agir en conséquence le cas échéant. C’est notamment vrai lorsque l’une
des tables mises en œuvre est particulièrement volumineuse. Mais, de manière générale,
l’ensemble des requêtes portées devront voir leur plan d’exécution vérifié.
Le plan d’exécution de la requête sera vérifié avec l’ordre EXPLAIN ANALYZE qui fournit
non seulement le plan d’exécution en précisant les estimations de sélectivité réalisées
par l’optimiseur, mais va également exécuter la requête et fournir la sélectivité réelle de
chaque nœud du plan d’exécution. Une forte divergence entre la sélectivité estimée et
réelle permet de détecter un problème. Souvent, il s’agit d’un problème de précision des
statistiques. Il est possible d’agir sur cette précision de plusieurs manières.
Tout d’abord, il est possible d’augmenter le nombre d’échantillons collectés, pour con-
struire notamment les histogrammes. Le paramètre default_statistics_target con-
trôle la précision de cet échantillon. Pour une base de forte volumétrie, ce paramètre
sera augmenté systématiquement dans une proportion raisonnable. Pour une base de
volumétrie normale, ce paramètre sera plutôt augmenté en ciblant une colonne partic-
ulière avec l’ordre SQL ALTER TABLE … ALTER COLUMN … SET STATISTICS …;. De plus,
il est possible de forcer artificiellement le nombre de valeurs distinctes d’une colonne avec
l’ordre SQL ALTER TABLE … SET COLUMN … SET n_distinct = …;. Il est aussi souvent
utile d’envisager une réécriture de la requête : si l’optimiseur, sous Oracle comme sous
PostgreSQL, n’arrive pas à trouver un bon plan, c’est probablement qu’elle est écrite d’une
façon qui empêche ce dernier de travailler correctement.
223
https://dalibo.com/formations
Migrer d’Oracle à PostgreSQL
Dans de très rares cas, des requêtes SQL utilisent la colonne ROWID d’Oracle, par exemple
pour dédoublonner des enregistrements. Le ROWID est la localisation physique d’une ligne
dans une table. L’équivalent dans PostgreSQL est le ctid.
Plus précisément, le ROWID Oracle représente une adresse logique d’une ligne, encodée
sous la forme OOOOOO.FFF.BBBBBB.RRR où O représente le numéro d’objet, F le fichier, B
le numéro de bloc et R la ligne dans le bloc. Le format est différent dans le cas d’une table
stockée dans un BIG FILE TABLESPACE, mais le principe reste identique.
Cette méthode d’accès est donc à proscrire, sauf opération particulière et cadrée.
224
NOTES
NOTES
NOTES
NOTES
NOTES
NOS AUTRES PUBLICATIONS
FORMATIONS
LIVRES BLANCS
• Industrialiser PostgreSQL
TÉLÉCHARGEMENT GRATUIT
Les versions électroniques de nos publica ons sont disponibles gratuitement sous licence
open-source ou sous licence Crea ve Commons. Contactez-nous à l’adresse contact@
dalibo.com pour plus d’informa on.
DALIBO, L’EXPERTISE POSTGRESQL
Depuis 2005, DALIBO met à la disposi on de ses clients son savoir-faire dans le domaine
des bases de données et propose des services de conseil, de forma on et de support aux
entreprises et aux ins tu onnels.