Initiation à Ora2Pg

Les projets de migration de bases Oracle vers PostgreSQL fleurissant pas mal ces derniers temps, cet article vise à présenter l’outil de migration Ora2Pg sur un cas simple et concret de migration, celui du schéma de démonstration HR.
Pour pouvoir utiliser l’outil Ora2Pg, il vous faut tout d’abord disposer d’un moteur et d’une instance PostgreSQL dans laquelle nous créerons la base dans laquelle le schéma HR cible sera hébergé.
L’outil Ora2Pg s’installe en suivant les quelques étapes mentionnées ci-dessous :
paquets RPM

yum install gcc bzip2 perl-devel unzip
yum install perl-Time-HiRes (si RHEL 6.x)

Installation d’un client Oracle

Le choix de l’instant client 12.2 est proposé dans cet article, pour sa simplicité d’installation et son coté non intrusif.

Il dispose, de plus, dans sa dernière version, des outils datapump (export / import), SQL*Loader et du WRC (Workload Replay Client) pour rejouer une charge de production sur une base de tests.

Les fichiers de l’instant client 12.2 sont téléchargeables sur le lien:  http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html

unzip /tmp/instantclient-basic-linux.x64-12.2.0.1.0.zip -d /opt/oracle
unzip /tmp/instantclient-sdk-linux.x64-12.2.0.1.0.zip -d /opt/oracle
unzip /tmp/instantclient-sqlplus-linux.x64-12.2.0.1.0.zip -d /opt/oracle

Configuration du TNSNAMES.ORA

vi /opt/oracle/instantclient_12_2/tnsnames.ora
BASETEST=
 (DESCRIPTION =
     (ADDRESS=(PROTOCOL=TCP)(HOST=<mon serveur>)(PORT=1521))
     (CONNECT_DATA =
         (SERVER = DEDICATED)
         (SERVICE_NAME = pdb1)
     )
 )

Installation du module DBI

à télécharger sur le site https://metacpan.org/release/DBI et prendre une version supérieure à 1.614.

export ORACLE_HOME=/opt/oracle/instantclient_12_2
export TNS_ADMIN=/opt/oracle/instantclient_12_2
export PATH=$ORACLE_HOME:$PATH
export LD_LIBRARY_PATH=$ORACLE_HOME:$LD_LIBRARY_PATH
tar -zxf DBI-1.636.tar.gz
cd DBI-1.636
perl Makefile.PL
make && make install

Installation du module DBD::Oracle

à télécharger sur le site search.cpan.org/dist/DBD-Oracle

export ORACLE_HOME=/opt/oracle/instantclient_12_2
export TNS_ADMIN=/opt/oracle/instantclient_12_2
export PATH=$ORACLE_HOME:$PATH
export LD_LIBRARY_PATH=$ORACLE_HOME:$LD_LIBRARY_PATH
tar -zxf DBD-Oracle-1.74.tar.gz
cd DBD-Oracle-1.74
perl Makefile.PL
make && make install

Installation de l’outil Ora2Pg

à télécharger sur le site https://sourceforge.net/projects/ora2pg

export ORACLE_HOME=/opt/oracle/instantclient_12_2
export TNS_ADMIN=/opt/oracle/instantclient_12_2
export PATH=$ORACLE_HOME:$PATH
export LD_LIBRARY_PATH=$ORACLE_HOME:$LD_LIBRARY_PATH
cd /tmp
tar -jxf ora2pg-18.1.tar.bz2
cd ora2pg-18.1
perl Makefile.PL
make && make install
chown -R postgres:postgres /etc/ora2pg

L’outil est maintenant installé dans /usr/local/bin et n’attend plus qu’à être paramétré pour pouvoir l’utiliser.
Configuration d’Ora2Pg
Le fichier de paramètres utilisé par défaut par Ora2Pg se nomme /etc/ora2pg/ora2pg.conf.dist, mais peut être écrasé par tout autre nom en utilisant  l’option –conf <nom du fichier de paramètres> de l’outil Ora2Pg.
Dans notre exemple, nous créerons à partir d’une copie de /etc/ora2Pg/ora2pg.conf.dist un fichier /etc/ora2pg/ora2pg.conf.HR contenant les quelques modifications qui suivent et qui suffisent à une grande majorité des situations de migration.

cp /etc/ora2pg/ora2pg.conf.dist /etc/ora2pg/ora2pg.conf.HR
vi /etc/ora2pg/ora2pg.conf.HR
# fournir le chemin d'accès au client Oracle (pour nous, l'instant client 12.2)
ORACLE_HOME /opt/oracle/instantclient_12_2
# fournir les éléments de connexion à la base Oracle
ORACLE_DSN dbi:Oracle:BASETEST
ORACLE_USER system
ORACLE_PWD <mot de passe>
# Le schéma à migrer
SCHEMA HR
Si SCHEMA n'est pas renseigné, Ora2Pg cherchera à migrer tous les schémas de la base à l'exception de ceux fournis par Oracle (SYS, SYSTEM, SYSMAN, etc...)
# Certaines applications utilisent des mots-clés réservés (ex: FROM) comme noms de tables.
USE_RESERVED_WORLDS = 1 (pour permettre l'utilisation de noms d'objets utilisant des mots-clés réservés (ex: from, user, etc...))

Initialisation d’Ora2Pg
Maintenant que notre projet de migration est défini, nous allons initialiser l’arborescence qui lui servira de zone de travail, et qui contiendra les données à migrer en exécutant la commande qui suit, après vous être assuré de disposer de suffisamment de place disques dans ce répertoire.

export ORACLE_HOME=/opt/oracle/instantclient_12_2
export TNS_ADMIN=/opt/oracle/instantclient_12_2
export PATH=$ORACLE_HOME:$PATH
export LD_LIBRARY_PATH=$ORACLE_HOME:$LD_LIBRARY_PATH
ora2pg --project_base <répertoire racine> --init_project HR --conf /etc/ora2pg/ora2pg.conf.HR
Creating project HR.
<répertoire racine>/HR/
 schema/
 dblinks/
 directories/
 functions/
 grants/
 mviews/
 packages/
 partitions/
 procedures/
 sequences/
 synonyms/
 tables/
 tablespaces/
 triggers/
 types/
 views/
 sources/
 functions/
 mviews/
 packages/
 partitions/
 procedures/
 triggers/
 types/
 views/
 data/
 config/
 reports/
Generating generic configuration file
Creating script export_schema.sh to automate all exports.
Creating script import_all.sh to automate all imports.

Evaluation des difficultés de migration
Tout est prêt pour démarrer cette migration, mais avant même de nous lancer dans cet exercice, j’aimerais vous montrer la capacité d’Ora2Pg à évaluer le niveau de difficultés que vous risquez de rencontrer.

export ORACLE_HOME=/opt/oracle/instantclient_12_2
export TNS_ADMIN=/opt/oracle/instantclient_12_2
export PATH=$ORACLE_HOME:$PATH
export LD_LIBRARY_PATH=$ORACLE_HOME:$LD_LIBRARY_PATH
ora2pg -t SHOW_REPORT --estimate_cost --conf /etc/ora2pg/ora2pg.conf.HR
[========================>] 7/7 tables (100.0%) end of scanning.
[========================>] 9/9 objects types (100.0%) end of objects auditing.


L’ajout des options –dump_as_html ou –dump_as_csv à la commande ora2pg permettent la sortie des résultats sous forme de page HTML ou de fichier type EXCEL.
Cette commande fournit le détail des objets à migrer, et surtout un niveau global de difficulté sous la forme d’un code constitué d’une lettre (A,B ou C) et d’un nombre de 1 à 5.
Bien sûr, ce code reste une approximation mais donne tout de même une idée des efforts à fournir pour assurer cette migration.
Notre exemple est d’un niveau A-3, donc relativement simple, ce que vous pourrez d’ailleurs constater par vous-même dans cet article.
Export des données
Passons maintenant à la phase d’export des données à migrer en exécutant le script export_schema.sh généré précédemment lors de l’initialisation du projet.

$ ./export_schema.sh
[========================>] 7/7 tables (100.0%) end of scanning.
[========================>] 9/9 objects types (100.0%) end of objects auditing.
Running: ora2pg -p -t TABLE -o table.sql -b ./schema/tables -c ./config/ora2pg.conf
[========================>] 7/7 tables (100.0%) end of scanning.
[========================>] 7/7 tables (100.0%) end of table export.
Running: ora2pg -p -t PACKAGE -o package.sql -b ./schema/packages -c ./config/ora2pg.conf
[========================>] 0/0 packages (100.0%) end of output.
Running: ora2pg -p -t VIEW -o view.sql -b ./schema/views -c ./config/ora2pg.conf
[========================>] 1/1 views (100.0%) end of output.
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
[========================>] 3/3 sequences (100.0%) end of output.
Running: ora2pg -p -t TRIGGER -o trigger.sql -b ./schema/triggers -c ./config/ora2pg.conf
[========================>] 1/1 triggers (100.0%) end of output.
Running: ora2pg -p -t FUNCTION -o function.sql -b ./schema/functions -c ./config/ora2pg.conf
[========================>] 0/0 functions (100.0%) end of output.
Running: ora2pg -p -t PROCEDURE -o procedure.sql -b ./schema/procedures -c ./config/ora2pg.conf
[========================>] 2/2 procedures (100.0%) end of output.
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
[========================>] 0/0 partitions (100.0%) end of output.
Running: ora2pg -p -t TYPE -o type.sql -b ./schema/types -c ./config/ora2pg.conf
[========================>] 0/0 types (100.0%) end of output.
Running: ora2pg -p -t MVIEW -o mview.sql -b ./schema/mviews -c ./config/ora2pg.conf
[========================>] 0/0 materialized views (100.0%) end of output.
Running: ora2pg -p -t DBLINK -o dblink.sql -b ./schema/dblinks -c ./config/ora2pg.conf
[========================>] 0/0 dblink (100.0%) end of output.
Running: ora2pg -p -t SYNONYM -o synonym.sql -b ./schema/synonyms -c ./config/ora2pg.conf
[========================>] 0/0 synonyms (100.0%) end of output.
Running: ora2pg -p -t DIRECTORY -o directorie.sql -b ./schema/directories -c ./config/ora2pg.conf
[========================>] 0/0 directory (100.0%) end of output.
Running: ora2pg -t PACKAGE -o package.sql -b ./sources/packages -c ./config/ora2pg.conf
[========================>] 0/0 packages (100.0%) end of output.
Running: ora2pg -t VIEW -o view.sql -b ./sources/views -c ./config/ora2pg.conf
[========================>] 1/1 views (100.0%) end of output.
Running: ora2pg -t TRIGGER -o trigger.sql -b ./sources/triggers -c ./config/ora2pg.conf
[========================>] 1/1 triggers (100.0%) end of output.
Running: ora2pg -t FUNCTION -o function.sql -b ./sources/functions -c ./config/ora2pg.conf
[========================>] 0/0 functions (100.0%) end of output.
Running: ora2pg -t PROCEDURE -o procedure.sql -b ./sources/procedures -c ./config/ora2pg.conf
[========================>] 2/2 procedures (100.0%) end of output.
Running: ora2pg -t PARTITION -o partition.sql -b ./sources/partitions -c ./config/ora2pg.conf
[========================>] 0/0 partitions (100.0%) end of output.
Running: ora2pg -t TYPE -o type.sql -b ./sources/types -c ./config/ora2pg.conf
[========================>] 0/0 types (100.0%) end of output.
Running: ora2pg -t MVIEW -o mview.sql -b ./sources/mviews -c ./config/ora2pg.conf
[========================>] 0/0 materialized views (100.0%) end of output.
To extract data use the following command:
ora2pg -t COPY -o data.sql -b ./data -c ./config/ora2pg.conf

Pour effectuer cet export, et pour de grosses volumétries, on peut utiliser l’option de parallélisation comme ci-dessous pour paralléliser cet export sur 4 canaux :

$ ora2pg -t COPY -o data.sql -b ./data -c ./config/ora2pg.conf --parallel 4
[========================>] 7/7 tables (100.0%) end of scanning.
[========================>] 25/25 rows (100.0%) Table COUNTRIES (0 sec., 25 recs/sec)
[========================>] 27/27 rows (100.0%) Table DEPARTMENTS (0 sec., 27 recs/sec)
[========================>] 107/107 rows (100.0%) Table EMPLOYEES (0 sec., 107 recs/sec)
[========================>] 19/19 rows (100.0%) Table JOBS (0 sec., 19 recs/sec)
[========================>] 10/10 rows (100.0%) Table JOB_HISTORY (0 sec., 10 recs/sec)
[========================>] 23/23 rows (100.0%) Table LOCATIONS (0 sec., 23 recs/sec)
[========================>] 4/4 rows (100.0%) Table REGIONS (0 sec., 4 recs/sec)
[========================>] 215/215 rows (100.0%) on total estimated data (5 sec., avg: 43 tuples/sec)

Contrôle des scripts générés
Allons maintenant examiner les scripts générés, et nous assurer que les contraintes référentielles seront correctement gérées lors de l’étape d’import, car il arrive, en fonction des versions de composants installés, que ce ne soit pas toujours le cas.
Pour cela, il suffit de vérifier dans le sous-répertoire schema/tables l’existence des scripts CONSTRAINTS_table.sql et INDEXES_table.sql.
Si ces deux scripts n’ont pas été générés, voici comment procéder :

$ cd schema/tables
$ ls
table.sql
$ mv table.sql table.sav
$ cat table.sav | grep -v "^ALTER" | grep -v INDEX > table.sql
$ cat table.sav | grep INDEX > INDEXES_table.sql
$ cat table.sav | grep "^ALTER" > CONSTRAINTS_table.sql

Import des données (dans la base PostgreSQL)
Passons maintenant à l’étape d’import.
Nous allons importer notre schéma HR dans une base postgreSQL nommée sample, dans laquelle nous allons créer un schéma et un utilisateur HR (sous PostgreSQL, le schéma et l’utilisateur sont deux entités indépendantes alors qu’elles ne sont qu’une sous Oracle).
Dans ce qui suit, nous répondrons oui à l’ensemble des questions à l’exception de celles traitant des indexes et contraintes avant l’import, ainsi que les procédures et triggers qui seront traitées après l’import pour éviter les plantages dus aux contraintes d’intégrités et aux différences de codes entre PL/SQL et PGPLSQL.

$ ./import_all.sh -d sample -o hr -n hr
Would you like to create the owner of the database hr? [y/N/q] y
Would you like to create the database sample? [y/N/q] y
Running: createdb -E UTF8 --owner hr sample
Would you like to create schema hr in database sample? [y/N/q] y
CREATE SCHEMA
Would you like to change search_path of the database owner? [y/N/q] y
Running: psql -d sample -c "ALTER ROLE hr SET search_path TO hr,public;"
ALTER ROLE
Would you like to import TABLE from ./schema/tables/table.sql? [y/N/q] y
SET
CREATE TABLE
COMMENT
COMMENT
...
COMMENT
CREATE TABLE
Would you like to import VIEW from ./schema/views/view.sql? [y/N/q] y
Running: psql --single-transaction -U hr -d sample -f ./schema/views/view.sql
SET
CREATE VIEW
Would you like to import SEQUENCE from ./schema/sequences/sequence.sql? [y/N/q] y
Running: psql --single-transaction -U hr -d sample -f ./schema/sequences/sequence.sql
SET
CREATE SEQUENCE
CREATE SEQUENCE
CREATE SEQUENCE
Would you like to import TRIGGER from ./schema/triggers/trigger.sql? [y/N/q] n
Would you like to import PROCEDURE from ./schema/procedures/procedure.sql? [y/N/q] n
Would you like to process indexes and constraints before loading data? [y/N/q] n
Would you like to import TABLESPACE from ./schema/tablespaces/tablespace.sql? [y/N/q] n
Would you like to import data from ./data/data.sql? [y/N/q] y
Running: psql -U hr -d sample -f ./data/data.sql
SET
BEGIN
COPY 25
COPY 27
COPY 107
COPY 19
COPY 10
COPY 23
COPY 4
ALTER SEQUENCE
ALTER SEQUENCE
ALTER SEQUENCE
COMMIT
Would you like to import indexes from ./schema/tables/INDEXES_table.sql? [y/N/q] y
Running: psql -U hr -d sample -f ./schema/tables/INDEXES_table.sql
CREATE INDEX
CREATE INDEX
...
CREATE INDEX
Would you like to import constraints from ./schema/tables/CONSTRAINTS_table.sql? [y/N/q] y
Running: psql -U hr -d sample -f ./schema/tables/CONSTRAINTS_table.sql
ALTER TABLE
ALTER TABLE
...
ALTER TABLE
ALTER TABLE
Si vous etes en version 18.2 d'Ora2Pg :
Would you like to import foreign keys from ./schema/tables/FKEYS_table.sql? [y/N/q] n

Ce dernier script (FKEYS_tables.sql) qui apparait en version 18.2 d’Ora2Pg, peut parfois se planter, en particulier si les tables concernées sont munies de l’attribut UNLOGGED (l’équivalent de l’option NOLOGGING sous Oracle), c’est pourquoi il vaut mieux l’exécuter manuellement.

cd schema/tables
psql -d sample -U hr -f FKEYS_table.sql
SET
psql:FKEYS_table.sql:9: ERROR: constraints on permanent tables may reference only permanent tables
La ligne 9 contient l'ordre suivant :
ALTER TABLE countries ADD CONSTRAINT countr_reg_fk FOREIGN KEY (region_id) REFERENCES regions(region_id) ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE;

Justement, ça plante dans notre cas, nous allons donc retirer l’attribut UNLOGGED des tables concernées avant de repasser ce script :

psql -d sample -U hr
sample=> alter table countries set logged ;
sample=> alter table regions set logged ;
sample=>
sample=> \i FKEYS_tables.sql
SET
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE

Çà y est, c’est passé !
Il ne nous reste plus qu’à achever l’exercice en créant cette fois les procédures, fonctions et triggers, bref, tout ce qui inclut des ordres de type PL/SQL pour tenter de les adapter au langage PGPLSQL, ce qui parfois peut s’avérer long et fastidieux.
C’est d’ailleurs la plus grosse difficulté rencontrée lors d’une migration d’Oracle vers PostgreSQL.
Pour les procédures :

psql -d sample -U hr -f schema/procedures/procedure.sql
SET
psql:procedure.sql:20: NOTICE: référence de type job_history.employee_id%TYPE convertie en integer
psql:procedure.sql:20: NOTICE: référence de type job_history.start_date%TYPE convertie en timestamp without time zone
psql:procedure.sql:20: NOTICE: référence de type job_history.end_date%TYPE convertie en timestamp without time zone
psql:procedure.sql:20: NOTICE: référence de type job_history.job_id%TYPE convertie en character varying
psql:procedure.sql:20: NOTICE: référence de type job_history.department_id%TYPE convertie en smallint
CREATE FUNCTION
CREATE FUNCTION

Juste un WARNING signalant la conversion des variables de type table.colonne%TYPE par le type de la colonne, je décide donc dans cet article de passer à la suite en créant les triggers.
Pour les triggers :

psql -d sample -U hr -f schema/triggers/trigger.sql
SET
psql:trigger.sql:9: NOTICE: le trigger « update_job_history » de la relation « employees » n'existe pas, poursuite du traitement
DROP TRIGGER
psql:trigger.sql:17: ERREUR: erreur de syntaxe sur ou près de « add_job_history »
LINE 3: add_job_history(OLD.employee_id, OLD.hire_date, LOCALTIMES...

Cette fois, j’ai moins de chance et mon trigger n’a pas été migré.
En version 18.2, Ora2Pg ajoute lui-même la bonne clause et le trigger est correctement créé.
En 18.1, voici ce qu’il faut faire.
Ci-dessous le code de définition de ce trigger :

-- Generated by Ora2Pg, the Oracle database Schema converter, version 18.1
-- Copyright 2000-2017 Gilles DAROLD. All rights reserved.
-- DATASOURCE: dbi:Oracle:BASETEST
SET client_encoding TO 'UTF8';
\set ON_ERROR_STOP ON
DROP TRIGGER IF EXISTS update_job_history ON employees CASCADE;
CREATE OR REPLACE FUNCTION trigger_fct_update_job_history() RETURNS trigger AS $BODY$
BEGIN
 add_job_history(OLD.employee_id, OLD.hire_date, LOCALTIMESTAMP,
 OLD.job_id, OLD.department_id);
RETURN NEW;
END
$BODY$
 LANGUAGE 'plpgsql' SECURITY DEFINER;
-- REVOKE ALL ON FUNCTION trigger_fct_update_job_history() FROM PUBLIC;
CREATE TRIGGER update_job_history
 AFTER UPDATE ON employees FOR EACH ROW
 EXECUTE PROCEDURE trigger_fct_update_job_history();

Ce trigger se déclenche sur un UPDATE réalisé sur la table EMPLOYEES et enregistre ce changement dans la table JOB_HISTORY mais l’appel à ce trigger sous PostgreSQL doit se faire via une commande SELECT <nom de la fonction> ou PERFORM <nom de la fonction>, ce qui n’est pas le cas sous Oracle.
Nous allons donc modifier le script SQL trigger trigger.sql pour y ajouter un PERFORM avant de relancer l’exécution du script.

SET
psql:trigger.sql:9: NOTICE: le trigger « update_job_history » de la relation « employees » n'existe pas, poursuite du traitement
DROP TRIGGER
CREATE FUNCTION
CREATE TRIGGER

Cette fois-ci, ça passe !
Testons maintenant ce trigger pour vérifier qu’il fonctionne correctement, en modifiant une entrée de la table EMPLOYEES, ce qui déclenchera le trigger en question.

psql -d sample -U hr
sample=> select * from employees where employee_id = 101;
 employee_id | first_name | last_name | email | phone_number | hire_date | job_id | salary | commission_pct | manager_id | department_id
-------------+------------+-----------+----------+--------------+---------------------+--------+--------+----------------+------------+---------------
 101 | Neena | Kochhar | NKOCHHAR | 515.123.4568 | 2005-09-21 00:00:00 | AD_VP | 17000 | | 100 | 90
(1 row)
sample=> update employees set job_id = 'AC_MGR' where employee_id = 101 ;
UPDATE 1
sample=> select * from employees where employee_id = 101;
 employee_id | first_name | last_name | email | phone_number | hire_date | job_id | salary | commission_pct | manager_id | department_id
-------------+------------+-----------+----------+--------------+---------------------+--------+--------+----------------+------------+---------------
 101 | Neena | Kochhar | NKOCHHAR | 515.123.4568 | 2005-09-21 00:00:00 | AC_MGR | 17000 | | 100 | 90
(1 row)
sample=> select * from job_history ;
 employee_id | start_date | end_date | job_id | department_id
-------------+---------------------+---------------------------+------------+---------------
 102 | 2001-01-13 00:00:00 | 2006-07-24 00:00:00 | IT_PROG | 60
 101 | 1997-09-21 00:00:00 | 2001-10-27 00:00:00 | AC_ACCOUNT | 110
 101 | 2001-10-28 00:00:00 | 2005-03-15 00:00:00 | AC_MGR | 110
 201 | 2004-02-17 00:00:00 | 2007-12-19 00:00:00 | MK_REP | 20
 114 | 2006-03-24 00:00:00 | 2007-12-31 00:00:00 | ST_CLERK | 50
 122 | 2007-01-01 00:00:00 | 2007-12-31 00:00:00 | ST_CLERK | 50
 200 | 1995-09-17 00:00:00 | 2001-06-17 00:00:00 | AD_ASST | 90
 176 | 2006-03-24 00:00:00 | 2006-12-31 00:00:00 | SA_REP | 80
 176 | 2007-01-01 00:00:00 | 2007-12-31 00:00:00 | SA_MAN | 80
 200 | 2002-07-01 00:00:00 | 2006-12-31 00:00:00 | AC_ACCOUNT | 90
 101 | 2005-09-21 00:00:00 | 2017-08-02 12:12:53.49093 | AD_VP | 90
(11 rows)

Vous remarquerez dans la dernière ligne de la table job_history le format du champs « end_date » qui diffère des autres.
Ce qui montre bien à la fois la relative simplicité de migration d’un schéma Oracle vers une base PostgreSQL, mais aussi que les plus grosses difficultés à gérer se trouvent, d’une part dans le portage des objets utilisant le code PL/SQL, et d’autre part sur le temps à consacrer aux tests du comportement de votre application après migration pour vous assurer de l’identité parfaite avant et après. (ex: le format du champ end_date)
 
Pour les perfectionnistes qui aimeraient aller jusqu’au bout de cette première expérience de migration et connaitre la solution à ce problème de format de champ end_date, en voici une :
Remplacer le code du trigger (trigger.sql)
PERFORM add_job_history(OLD.employee_id, OLD.hire_date, LOCALTIMESTAMP,
OLD.job_id, OLD.department_id);
Par celui-ci
PERFORM add_job_history(OLD.employee_id, OLD.hire_date, date_trunc(‘second’,LOCALTIMESTAMP),
OLD.job_id, OLD.department_id);
Bonne(s) migration(s) !
 
 

1 réflexion sur “Initiation à Ora2Pg”

  1. Oussama CHAOUACHI

    Bravo ! merci ,
    dans les nouvelles versions d’Ora2pg le problème de trigger est résolue .
    – Generated by Ora2Pg, the Oracle database Schema converter, version 21.1
    — Copyright 2000-2020 Gilles DAROLD. All rights reserved.
    — DATASOURCE: dbi:Oracle:mydbfortest

    SET client_encoding TO ‘UTF8’;

    \set ON_ERROR_STOP ON

    SET check_function_bodies = false;

    DROP TRIGGER IF EXISTS update_job_history ON employees CASCADE;
    CREATE OR REPLACE FUNCTION trigger_fct_update_job_history() RETURNS trigger AS $BODY$
    BEGIN
    PERFORM add_job_history(OLD.employee_id, OLD.hire_date, LOCALTIMESTAMP,
    OLD.job_id, OLD.department_id);
    RETURN NEW;
    END
    $BODY$
    LANGUAGE ‘plpgsql’ SECURITY DEFINER;
    — REVOKE ALL ON FUNCTION trigger_fct_update_job_history() FROM PUBLIC;

    CREATE TRIGGER update_job_history
    AFTER UPDATE OF job_id,department_id ON employees FOR EACH ROW
    EXECUTE PROCEDURE trigger_fct_update_job_history();

    Par contre dans l’article il manque des précisions : lors de l’import il faut donner le droit à l’utilisateur postgres pour exécuter import_all.sh
    aussi pour LD_LIBRARY_PATH = $ORACLE_HOME/lib
    Le reste c’est nickel !
    Encore Bravo !

Les commentaires sont fermés.