Formation MIGORPG
Dalibo SCOP
24.09
29 août 2024
Formation | Formation MIGORPG |
Titre | Migrer d’Oracle à PostgreSQL |
Révision | 24.09 |
ISBN | N/A |
https://dali.bo/migorpg_pdf | |
EPUB | https://dali.bo/migorpg_epub |
HTML | https://dali.bo/migorpg_html |
Slides | https://dali.bo/migorpg_slides |
Cette formation est sous licence CC-BY-NC-SA. Vous êtes libre de la redistribuer et/ou modifier aux conditions suivantes :
PostgreSQL® Postgres® et le logo Slonik sont des marques déposées par PostgreSQL Community Association of Canada.
Ce document ne couvre que les versions supportées de PostgreSQL au moment de sa rédaction, soit les versions 12 à 16.
Ce module est organisé en quatre parties :
La première migration est importante :
Le projet doit être choisi avec soin :
Souvent moins important :
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 :
Lors de la migration, certaines fonctionnalités d’Oracle auront peu ou pas d’équivalent :
Suivant les réponses aux questions précédentes, vous choisirez :
COPY
)blob
et clob
-> bytea
et
text
number
-> int
, bigint
,
real
, double
, decimal
Étapes :
ORACLE_HOME
et
PATH
comprenant sqlplus
)libaio
(Redhat) ou libaio1
(Debian)Time::HiRes
Perl DBI > v1.614
et DBD::Oracle
En option :
DBD::Pg
pour l’import direct dans PostgreSQLCompress::Zlib
: compression des fichiers en
sortieDBD::MySQL
pour migrer les bases MySQLDBD::ODBC
pour migrer les bases Microsoft
SQL ServerPoints essentiels :
N’hésitez pas, c’est le moment !
Ce module est organisé en trois parties :
É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
dbi:Oracle:TNSNAME
dbi:Oracle://serveur:1521/service
ORACLE_USER
(system
par défaut)ORACLE_PWD
(manager
par défaut)SCHEMA
NOM_SCHEMA
versus SYSUSERS
USER_GRANTS [0|1]
Cette étape d’exploration 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
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_FKEYS
1FILE_PER_TABLE
1FILE_PER_FUNCTION
1DISABLE_TRIGGERS
1TRUNCATE_TABLE
1DISABLE_SEQUENCE
1COMPILE_SCHEMA
[0|1]PLSQL_PGSQL
0Utilisation d’un schéma sous PostgreSQL ?
EXPORT_SCHEMA
[0|1]PG_SCHEMA
nom_du_shemaCREATE_SCHEMA
0PG_VERSION
9.6PG_VERSION
11BITMAP_AS_GIN
: export des index bitmap en
btree_gin
STANDARD_CONFORMING_STRINGS
échappement dans les
chaines de caractèresLa base contient des champs de type SDO_GEOMETRY
.
AUTODETECT_SPATIAL_TYPE
[0|1]DEFAULT_SRID
4326CONVERT_SRID
[0|1|N]POSTGIS_SCHEMA
schema_nameGEOMETRY_EXTRACT_TYPE
[INTERNAL|WKT|WKB]L’export des champs CLOB et BLOB sont contrôlés par :
LONGREADLEN
1047552LONGTRUNCOK
0NO_LOB_LOCATOR
0BLOB_LIMIT
500Étapes :
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 codeUSE_TABLESPACE
REPLACE_TABLES ORIG_TABLE1:DEST_TABLE1
REPLACE_COLS TABLE1(ORIG_COL1:DEST_COL1, [..])
INDEXES_SUFFIX _idx
INDEXES_RENAMING
DATA_TYPE NUMBER(*\,0):bigint
MODIFY_TYPE TABLE1:COL1:integer TABLE1:COL2:timestamptz
REPLACE_AS_BOOLEAN TABLE1:COL1 [..]
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
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 MVIEW -o mviews.sql -b sources/mviews -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
PostgreSQL ne possède pas d’objet de type SYNONYM
search_path
PostgreSQL ne possède pas d’objets de type DIRECTORY
ni
de tables EXTERNAL
file_fdw
COPY
PostgreSQL ne possède pas d’objets de type
DATABASE LINK
oracle_fdw
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
EXPORT_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
Chargement 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 :
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]
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
COPY
CLIENT_ENCODING
DEFAULT_NUMERIC
ou
ALTER TABLE
CLOB
, BLOB
et XML
:
LONGREADLEN
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]
VACUUM FREEZE
préventifN’hésitez pas, c’est le moment !
Ce module est organisé en quatre parties :
Oracle et PostgreSQL sont assez proches :
PostgreSQL et Oracle :
USER.OBJECT
fillfactor
" "
autour des identifiantsCREATE TYPE
) nécessitent une
réécritureDBLINK
) n’existent pas sauf sous
forme d’extensions (dblink
ou fdw
)smallint
, integer
,
bigint
numeric
du standard SQL est appelé
number
sous Oraclevarchar2
dans PostgreSQL
varchar
varchar (n)
: taille en nombre de caractèrestext
équivalent à varchar
sans
taille (1 Go maximum)''
équivaut à NULL
''
et NULL
sont
distinctsboolean
boolean
sous PostgreSQL alors que
vous aurez migré un int
large objects
et fonctions lo_*
bytea
date
: jourtime
: heure seuletimestamp
: date + heure (alias
timestamptz
)YYYY-MM-DD HH24:MI:SS.mmmmmmm+TZ
(conforme SQL)interval
sous PostgreSQLSYSDATE
localtimestamp
add_months
,
etc.NLS_DATE_FORMAT
(TO_CHAR
et
TO_DATE
)
DateStyle
PostgreSQL fournit de nombreux types de données spécialisés :
PERIOD
ip4r
…DECODE
et
NVL
NULL
ROWNUM
ROWNUM
n’existe pas dans PostgreSQL
row_number() OVER ()
ORDER BY
ROWNUM
FROM tab1, tab2 WHERE tab1.col = tab2.col
FROM tab1 JOIN tab2 ON (tab1.col = tab2.col)
(+)
d’Oracle historiqueLEFT JOIN
RIGHT JOIN
FULL OUTER JOIN
FROM t1, t2;
FROM t1 CROSS JOIN t2
UNION
/ UNION ALL
INTERSECT
EXCEPT
MINUS
CONNECT BY
OracleWITH RECURSIVE
PostgreSQLniveau 1/niveau 2/niveau 3
niveau initial AS path
prior.path || niveau courant
NOCYCLE
cycle
element = ANY (tableau) AS cycle
WHERE cycle = false
WITH RECURSIVE
obligatoire dans PostgreSQLBEGIN
BEGIN
caché)BEGIN TRANSACTION ISOLATION LEVEL xxxx
READ COMMITTED
REPEATABLE READ
SERIALIZABLE
SAVEPOINT
RELEASE SAVEPOINT
ROLLBACK TO SAVEPOINT
SELECT FOR SHARE/UPDATE
LOCK TABLE
N’hésitez pas, c’est le moment !
Oracle et PostgreSQL n’ont pas le même langage PL :
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
nom_sequence.nextval
=>
nextval('nom_sequence')
dblink
ou pg_background
RETURN
=> RETURNS
EXECUTE IMMEDIATE
=> EXECUTE
SELECT
sans INTO
=>
PERFORM
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 OracleRECORD
,
correspondant à un enregistrement
TABLE FUNCTION
nom_paquet
.nom_fonction
PACKAGES
ou paquets de procédures stockéesLes noms diffèrent :
NVL()
=> coalesce()
SYSDATE
=> LOCALTIMESTAMP
CURRENT_TIMESTAMP
sans le fuseau
horaireNLSSORT(colname, 'nls_sort=GERMAN')
=>
colname COLLATE "de_DE"
Les 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, ' ')
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 fonctions de
trigger:
devant les noms de variable
OracleDBMS_OUTPUT.(put_line|put|new_line)(...)
en
RAISE NOTICE '...'
FOR … IN … REVERSE min .. max
RAISE EXCEPTION
avec concaténation
||
par le format à la 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
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
aux SELECT … INTO
lorsqu’il y a EXCEPTION … NO_DATA_FOUND
ou
TOO_MANY_ROWS
REGEX_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 fonctions 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 dblinkpg_background
à
partir de PostgreSQL 9.5 et Ora2Pg 17.5Absence 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 :
Deux actions permettent de tester à minima :
TEST
: compare le nombre d’objets et de lignes des deux
bases.
ora2pg -c config/ora2pg.conf -t TEST
TEST_VIEW
: compare le nombre de lignes retounées par
les vues.
ora2pg -c config/ora2pg.conf -t TEST_VIEW
PG_DSN
doit être positionné.N’hésitez pas, c’est le moment !