Vous êtes libres de redistribuer et/ou modifier cette création selon les conditions suivantes :
Paternité
Pas d'utilisation commerciale
Partage des conditions initiales à l'identique
Ce module est organisé en quatre parties :
La première migration est importante :
Le projet doit être choisi avec soin :
Souvent moins important:
Oracle et PostgreSQL sont assez proches :
PostgreSQL et Oracle :
BEGIN
BEGIN
caché)smallint
, integer
, bigint
number
sous Oracleboolean
.boolean
sous PostgreSQL alors que vous aurez migré un int
varchar2
dans PostgreSQL
varchar
''
= IS NULL
''
et NULL
sont distinctsvarchar
peut ne pas prendre de taille sous PostgreSQL
text
équivalent à varchar
sans taillelo_*
bytea
PostgreSQL fournit aussi de nombreux types de données spécialisés :
PERIOD
ip4r
YYYY/MM/DD HH:MM:SS
YYYY-MM-DD
(conforme SQL)YYYY/MM/DD HH:MM:SS
HH:MM:SS.mmmmmmm
(µs)timestamp
sous PostgreSQL : Date+Time (+TZ)YYYY-MM-DD HH24:MI:SS.mmmmmmm+TZ
Interval
Oracle : nombreuses extensions incompatibles avec PostgreSQL :
+
)
LEFT JOIN
(standard SQL)CONNECT BY
WITH RECURSIVE
(standard SQL)COALESCE
, CASE
(standard SQL)" "
autour des identifiants.Oracle et PostgreSQL n'ont pas le même langage PL :
Avant de pouvoir porter l'application et le PL :
number(18,0)
, number(4,0)
…int
: -2147483648
à +2147483647
(4 octets, number(9,0)
)
bigint
: -9223372036854775808
à 9223372036854775807
(8 octets, number(18,0)
)
numeric(x,0)
, d'autres en int
/bigint
Veut-on :
Suivant les réponses aux questions précédentes, vous choisirez :
COPY
)blob
et clob
-> bytea
et text
number
-> int
, bigint
, real
, double
, decimal
Lors de la migration, certaines fonctionnalités d'Oracle auront peu ou pas d'équivalent :
Certaines choses peuvent être émulées :
CREATE TABLE AS SELECT
régulièrementcreate table sales (year number(4),
product varchar2(10),
amt number(10,2))
partition by range (year)
partition p1 values less than (1992) tablespace u1,
partition p2 values less than (1993) tablespace u2,
partition p3 values less than (1994) tablespace u3,
partition p4 values less than (1995) tablespace u4,
partition p5 values less than (MAXVALUE) tablespace u5;
Pas d'équivalent simple sous PostgreSQL :
CREATE TABLE sales (year numeric(4),
product varchar(10),
amt numeric(10,2));
CREATE TABLE sales_y1992 (
CHECK ( year = 1992 )
) INHERITS (sales);
CREATE TABLE sales_y1993 (
CHECK ( year = 1993 )
) INHERITS (sales);
…
Points essentiels :
N'hésitez pas, c'est le moment !
Ce module est organisé en quatre parties :
Étapes :
ora2pg-X.Y.tar.bz2
ORACLE_HOME
et PATH
comprenant sqlplus
)libaio1
Time::HiRes
Perl DBI
et DBD::Oracle
En option :
DBD::Pg
pour l'import direct dans PostgreSQLCompress::Zlib
: compression des fichiers en sortieDBD::MySQL
pour migrer les bases MySQLLe script ora2pg
s'utilise de la façon suivante :
ora2pg [-dhpqv --estimate_cost --dump_as_html] [--option value]
Utilisation basique :
ora2pg -t ACTION [-c fichier_de_configuration]
Étapes de la configuration :
Structure
#
)0
pour désactivé et 1
pour activéIMPORT fichier.conf
ORACLE_HOME /path/.../
DEBUG [0|1]
LOGFILE /path/.../migration.log
ORACLE_DSN
dbi:Oracle:host=serveur;sid=INSTANCE
ORACLE_USER
system
ORACLE_PWD
manager
SCHEMA
NOM_SCHEMA
versus SYSUSERS
USER_GRANTS [0|1]
Cette étape de validation de la configuration permet d'obtenir des informations sur la base Oracle :
Certaines informations sont disponibles immédiatement, sans plus de configuration :
SHOW_VERSION
affiche la version de l'instance Oracle.SHOW_SCHEMA
liste les schémas définis sous Oracle.SHOW_TABLE
affiche la liste des tables de la base Oracle.SHOW_COLUMN
affiche la liste des colonnes par table d'une base Oracle.Recherche de l'encodage utilisé par l'instance Oracle :
SHOW_ENCODING
: affiche les valeurs utilisées par Ora2Pg pour
NLS_LANG
CLIENT_ENCODING
NLS_LANG
AMERICAN_AMERICA.AL32UTF8
French_France.WE8ISO8895P1
...NLS_NCHAR
AL32UTF8
...CLIENT_ENCODING
utf8
, latin1
, latin9
BINMODE
utf8
, raw
Rapport exhaustif du contenu de la base Oracle
ora2pg -t SHOW_REPORT
ora2pg -t SHOW_REPORT --dump_as_html
Estimation du coût de migration
ora2pg -t SHOW_REPORT --estimate_cost
ora2pg -t SHOW_REPORT --estimate_cost --dump_as_html
Le but du fichier de configuration générique est multiple :
Utilisation de fichiers de sortie dédiés
FILE_PER_CONSTRAINT
1FILE_PER_INDEX
1FILE_PER_TABLE
1FILE_PER_FUNCTION
1Ajout d'ordres SQL :
DISABLE_TRIGGERS
1TRUNCATE_TABLE
1DISABLE_SEQUENCE
1COMPILE_SCHEMA
[0|1]Désactivation de la conversion automatique du PL/SQL :
PLSQL_PGSQL
0Utilisation d'un schéma sous PostgreSQL ?
EXPORT_SCHEMA [0|1]
PG_SCHEMA nom_du_shema
CREATE_SCHEMA 0
PG_SUPPORTS_INSTEADOF 1
STANDARD_CONFORMING_STRINGS 1
PG_SUPPORTS_IFEXISTS 1
PG_SUPPORTS_MVIEW 1
PG_SUPPORTS_CHECKOPTION 1
BITMAP_AS_GIN 1
La base contient des champs de type SDO_GEOMETRY
.
AUTODETECT_SPATIAL_TYPE [0|1]
DEFAULT_SRID 4326
CONVERT_SRID [0|1|N]
POSTGIS_SCHEMA schema_name
L'export des champs CLOB et BLOB sont contrôlés par :
LONGREADLEN 8192
LONGTRUNCOK 0
NO_LOB_LOCATOR 0
BLOB_LIMIT 500
Étapes :
ora2pg --init_project dirname --project_base dirname
ora2pg.conf
générique
ora2pg
-t
type d'export-b
répertoire de stockage des fichiers-o
nom du fichier de sortie-p
conversion automatique du codeExport des tables, contraintes et index
ora2pg -p -t TABLE -o table.sql -b schema/tables -c config/ora2pg.conf
Export des séquences
ora2pg -t SEQUENCE -o sequences.sql -b schema/sequences -c config/ora2pg.conf
Export des vues
ora2pg -p -t VIEW -o views.sql -b schema/views -c config/ora2pg.conf
USE_TABLESPACE
Les rôles et droits
ora2pg -t GRANT -o users.sql -b schema/users -c config/ora2pg.conf
Les tablespaces
ora2pg -t TABLESPACE -o tablespaces.sql -b schema/tablespaces \
-c config/ora2pg.conf
Les types composites
ora2pg -p -t TYPE -o types.sql -b schema/types -c config/ora2pg.conf
ora2pg -p -t TRIGGER -o triggers.sql -b schema/triggers -c config/ora2pg.conf
ora2pg -p -t FUNCTION -o functions.sql -b schema/functions -c config/ora2pg.conf
ora2pg -p -t PROCEDURE -o procedures.sql -bschema/procedures -c config/ora2pg.conf
ora2pg -p -t PACKAGE -o packages.sql -b schema/packages -c config/ora2pg.conf
Extraction du code brut d'Oracle :
ora2pg -t TYPE -o types.sql -b sources/types -c config/ora2pg.conf
ora2pg -t VIEW -o views.sql -b sources/views -c config/ora2pg.conf
ora2pg -t TRIGGER -o triggers.sql -b sources/triggers -c config/ora2pg.conf
ora2pg -t FUNCTION -o functions.sql -b sources/functions -c config/ora2pg.conf
ora2pg -t PROCEDURE -o procedures.sql -b sources/procedures -c config/ora2pg.conf
ora2pg -t PACKAGE -o packages.sql -b sources/packages -c config/ora2pg.conf
Export des partitions :
ora2pg -t PARTITION -o partitions.sql -b schema/partitions -c config/ora2pg.conf
Depuis PostgreSQL 9.3 :
PG_SUPPORT_MVIEW 1
ora2pg -t MVIEW -o mviews.sql -b schema/mviews -c config/ora2pg.conf
Avant la 9.3 :
PostgreSQL ne possède pas d'objet de type SYNONYM :
search_path
ora2pg -t SYNONYM -o synonyms.sql -b schema/synonyms -c config/ora2pg.conf
PostgreSQL ne possède pas d'objets de type DIRECTORY
ni de tables EXTERNAL
.
file_fdw
.
COPY
ora2pg -t DIRECTORY -o directories.sql -b schema/directories -c config/ora2pg.conf
PostgreSQL ne possède pas d'objets de type DATABASE LINK
.
oracle_fdw
.
ora2pg -t DBLINK -o dblinks.sql -b schema/dblinks -c config/ora2pg.conf
DIRECTORY
et BFILE
BFILE
en donnée bytea
par défautBFILE
est redéfini en TEXT
, stockage du chemin du fichier externeexternal_file
.
EFILE
correspondant au type BFILE
: (directory_name, filename)
BFILE
est redéfini en EFILE
(directive DATA_TYPE
)Oracle Index Texte
CONTAINS
CATSEARCH
PostgreSQL : Full Text Search/Recherche Plein Texte
opérateur @@
équivalent à CONTAINS
SELECT to_tsvector('fat cats ate fat rats') @@ to_tsquery('fat & rat');
pg_trgm
pour les recherches LIKE '%mot%mot%
', équivalent de CTXCATEXPORT_SCHEMA
est activé
Création des objets du schéma :
psql -U myuser -f schema/tables/tables.sql mydb >> create_mydb.log 2>&1
psql -U myuser -f schema/partitions/partitions.sql mydb >> create_mydb.log 2>&1
psql -U myuser -f schema/views/views.sql mydb >> create_mydb.log 2>&1
psql -U myuser -f schema/tablespaces/tablespaces.sql mydb >> create_mydb.log 2>&1
Chargements différé de certains objets :
Bilan de l'export/import du schéma :
CHECK
et conditions des indexCHECK (WYEAR between 0 and 9);
CHECK (wyear::integer between 0 and 9);
Étapes :
Création des fichiers de données :
ora2pg -t COPY -o datas.sql -b data/ -c config/ora2pg.conf
ora2pg -t INSERT -o datas.sql -b data/ -c config/ora2pg.conf
FILE_PER_TABLE 1
bytea
BLOB
et CLOB
en bytea
très lentLOB
SRID
, système spatial de référence
CONVERT_SRID
converti la valeur Oracle dans la norme EPSGDEFAULT_SRID
force la valeur du SRID par défautGEOMETRY_EXTRACT_TYPE [WKT|WKB|INTERNAL]
Import des fichiers de données :
psql -U myuser -f data/datas.sql mydb >> data_mydb.log 2>&1
gunzip -c data/datas.sql.gz | psql -U myuser mydb >> data_mydb.log 2>&1
bunzip2 -c data/datas.sql.bz2 | psql -U myuser mydb >> data_mydb.log 2>&1
Restauration des contraintes, triggers, séquences et index
psql -U myuser -f schema/tables/CONSTRAINTS_tables.sql mygdb >> create_mydb.log 2>&1
psql -U myuser -f schema/tables/INDEXES_tables.sql mygdb >> create_mydb.log 2>&1
psql -U myuser -f schema/sequences/sequences.sql mygdb >> create_mydb.log 2>&1
psql -U myuser -f schema/triggers/triggers.sql mygdb >> create_mydb.log 2>&1
Action :
LOAD
permet de paralléliser des ordres SQL sur N processusora2pg -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
COPY
CLIENT_ENCODING
DEFAULT_NUMERIC
ou ALTER TABLE
CLOB
, BLOB
et XML
: LONGREADLEN
COPY
Import direct dans PostgreSQL
PG_DSN dbi:Pg:dbname=test_db;host=localhost;port=5432
PG_USER [nom_utilisateur]
PG_PWD [mot_de_passe]
DATA_LIMIT
JOBS Ncores
ORACLE_COPIES Ncores
DEFINED_PKEY EMPLOYEE:ID
PARALLEL_TABLES Ncores
JOBS
x ORACLE_COPIES | PARALLEL_TABLES
= Total Nombre cœursALLOW TABLE1 TABLE2 [..] TABLEN
EXCLUDE TABLE1 TABLE2 [..] TABLEN
WHERE TABLE[condition valide] GLOBAL_CONDITION
WHERE TABLE_TEST[ID1='001']
WHERE DATE_CREATION > '2001-01-01
'REPLACE_QUERY TABLENAME[SQL_QUERY]
N'hésitez pas, c'est le moment !
Ce module est organisé en cinq parties :
to_char(1 param)
, add_month()
, decode()
...DBMS_ALERT
, DBMS_PIPE
, DBMS_OUTPUT
, DBMS_RANDOM
et UTL_FILE
fillfactor
DBLINK
) n'existent pas sauf sous forme d'extension (dblink ou fdw).Opérations sur les dates :
DATE + NUMBER
DATE + interval '1 jours'
TIMESTAMP - TIMESTAMP
= NUMBER
interval
NLS_DATE_FORMAT
(TO_CHAR
et TO_DATE
)
DateStyle
Pas de conversion implicite vers et depuis les types chaînes de caractères :
SELECT * FROM depts WHERE numero BETWEEN 0 AND 42;
REVERSE LOOP
=> inversion des bornesCONNECT BY
n'existe pas, utiliser WITH RECURSIVE
REF CURSOR
doit être remplacé par REFCURSOR
nom_curseur%ROWTYPE
doit être remplacé par RECORD
BULK COLLECT
=> Array:NEW
et :OLD
=> NEW
et OLD
UPDATING
, INSERTING
, DELETING
=> TG_OP (UPDATE, INSERT, DELETE)
RETURN NEW
impératif dans les triggers BEFORE
, retour implicite sous Oraclecustom
Left outer join :
SELECT * FROM a, b WHERE a.id = b.id (+)
=> SELECT * FROM a LEFT OUTER JOIN b ON (a.id = b.id)
Right outer join :
SELECT * FROM a, c WHERE a.id (+) = c.id
=> SELECT * FROM a RIGHT OUTER JOIN c ON (a.id = c.id)
Full outer join :
SELECT * FROM a, b WHERE a.id = b.id (+)
UNION ALL SELECT * FROM a, b WHERE a.id (+) = b.id AND a.id = NULL
=> SELECT * FROM a FULL OUTER JOIN b ON (a.id = b.id)
PACKAGES
ou paquets de procédures stockées.Les noms diffèrent :
NVL()
=> coalesce()
SYSDATE
=> LOCALTIMESTAMP
CURRENT_TIMESTAMP
sans le fuseau horaireLes paramètres changent :
to_number(num)
to_number(num, '99...99D99...99')
to_date( string1, format_mask, nls_language)
to_date(text, text)
replace(a, b)
replace(a, b, ' ')
Les noms et les paramètres changent :
trunc(.*date.*)
=> date_trunc('day', ...date...)
substr( string, start_position, length )
=> substring(string from start_position for length)
La réécriture est complète :
add_months
=> ”+ 'N months'::interval”
add_years
=> ”+ 'N year'::interval”
TO_NUMBER(TO_CHAR(…]
=> to_char(…)::integer
decode("user_status",'active',"username",null)
=> (CASE WHEN user_status='active' THEN username ELSE NULL END)
(nom.nextval
=> nextval('nom')
nom.currval
=> currval('nom']
:new.
en NEW.
et :old.
en OLD.
dans les triggersINSERTING|DELETING|UPDATING
en TG_OP = 'INSERT|DELETE|UPDATE
' dans les fonction de triggerDBMS_OUTPUT.(put_line|put|new_line)(...)
en RAISE NOTICE '...'
FOR … IN … REVERSE min .. max
sprintf
utilisé par PostgreSQLROWNUM
dans la clause where par des clauses LIMIT
et/ou OFFSET
HAVING … GROUP BY
(variante acceptée par Oracle mais pas PostgreSQL) en GROUP BY … HAVING
STRICT
aux SELECT … INTO
lorsqu'il y a EXCEPTION … NO_DATA_FOUND
ou TOO_MANY_ROWS
MINUS
par EXCEPT
FROM DUAL
DEFAULT NULL
qui est la valeur par défaut sous PostgreSQL lorsqu'aucune valeur par défaut n'est préciséeEND fct_name;
est réécrit en END;
CASE
entre le WHEN
et le THEN
, non supporté par PostgreSQLIS NULL
et IS NOT NULL
par des instructions à base de coalesce
(pour Oracle, une chaîne vide est équivalente à NULL)CURSOR moncurseur;
pour les rendre compatibles avec PostgreSQL : moncurseur CURSOR;
IN
de la déclaration des curseurs.EXIT WHEN ...%NOTFOUND
par IF NOT FOUND THEN EXIT; END IF;
STRICT
dans les requêtes SELECT ... INTO ...
si une exception sur NO_DATA_FOUND
ou TOO_MANY_ROW
est levéeREGEX_LIKE( string, pattern )
en syntaxe avec l'opérateur PostgreSQL de recherche regex string ~ pattern
.SYS_CONTECT('USERENV', ...)
en équivalent PostgreSQL.SDO_GEOM.*
en appels aux fonction PostGis équivalentes.SDO_*
en opérateurs correspondants PostGis.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 PostgreSQLraise_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
Étapes :
Non supportées nativement par PostgreSQL, Ora2Pg utilise une fonction de substitution :
_atx
_atx
au travers d'un dblink.Chargement des fonctions et procédures :
psql --single-transaction -U myuser -f schema/procedures/procedures.sql mydb
psql --single-transaction -U myuser -f schema/functions/functions.sql mydb
Chargement des paquets de fonctions :
psql --single-transaction -U myuser -f schema/packages/packages.sql mydb
Absence de certaines fonctions ou paquets de fonctions dans l'export
COMPILE_SCHEMA
EXPORT_INVALID
Certains commentaires des paquets de fonctions ne sont pas importés
Valider le portage du code :
N'hésitez pas, c'est le moment !
Table DUAL
pas nécessaire
SELECT fonction();
SELECT current_timestamp;
SELECT 1 = 'a'::text;
'' = IS NULL
''
et NULL
sont distinctslarge objects
et fonctions lo_*
bytea
PostgreSQL fournit aussi de nombreux types de données spécialisés :
SYSDATE
YYYY/MM/DD HH:MM:SS
YYYY-MM-DD
(conforme SQL)YYYY/MM/DD HH:MM:SS
HH:MM:SS.mmmmmmm
(µs)Format de sortie conforme SQL sous PostgreSQL:
YYYY-MM-DD HH24:MI:SS.mmmmmmm+TZ
SYSDATE
équivalent direct :
SELECT localtimestamp;
current_timestamp
current_date
current_time
PostgreSQL ne propose pas de fonctions add_months
, etc.
SELECT current_date + interval '3 days';
SELECT current_date + interval '1 days' * 3;
SELECT (now() - '2014-01-01') * 2 + now()
Équivalent de la clause CASE
du standard
CASE expr
WHEN valeur1 THEN valeur_retour1
WHEN valeur2 THEN valeur_retour2
ELSE valeur_retour3
END
CASE
WHEN expr1 THEN valeur_retour1
WHEN expr2 THEN valeur_retour2
ELSE valeur_retour3
END
Retourne le premier argument non NULL
SELECT NVL(description, description_courte, '(aucune)')FROM articles;
Équivalent de la norme SQL : COALESCE
SELECT COALESCE(description, description_courte, '(aucune)') FROM articles;
WITH RECURSIVE
obligatoire dans PostgreSQLROWNUM
n'existe pas dans PostgreSQL
row_number() OVER ()
ORDER BY
WHERE ROWNUM < 11
LIMIT xx
:SELECT *
FROM employees
LIMIT 10;
FROM tab1, tab2 WHERE tab1.col = tab2.col
FROM tab1 JOIN tab2 ON (tab1.col = tab2.col
LEFT JOIN
RIGHT JOIN
FULL OUTER JOIN
FROM t1, t2;
FROM t1 CROSS JOIN t2
GROUP BY
après HAVING
GROUP BY
avant HAVING
UNION
/ UNION ALL
INTERSECT
EXCEPT
MINUS
BEGIN
BEGIN
caché)BEGIN TRANSACTION ISOLATION LEVEL xxxx
READ COMMITTED
REPEATABLE READ
SERIALIZABLE
SAVEPOINT
RELEASE SAVEPOINT
ROLLBACK TO SAVEPOINT
SELECT
SELECT FOR SHARE/UPDATE
LOCK TABLE
CONNECT BY
OracleWITH RECURSIVE
PostgreSQLSTART WITH
CONNECT BY PRIOR
SELECT empno, ename, job, mgr
FROM emp
START WITH mgr IS NULL
CONNECT BY PRIOR empno = mgr
WITH RECURSIVE hierarchie AS (
condition de départ
UNION ALL
clause de récursion
)
SELECT * FROM hierarchie
LEVEL
donne le niveau de hiérarchiecondition de départ
1 AS level
clause de récursion
prior.level + 1
niveau 1/niveau 2/niveau 3
niveau initial AS path
prior.path || niveau courant
NOCYCLE
cycle
element = ANY (tableau) AS cycle
WHERE cycle = false