Le schéma CO (Customer Orders) d'Oracle est le remplacement moderne du schéma OE plus ancien.
Il est livré avec Oracle 19c, il est activement maintenu, et il est construit comme la plupart des applications Oracle réelles sont construites aujourd'hui : colonnes IDENTITY au lieu de paires séquence-déclencheur, JSON stocké dans des colonnes BLOB, et vues qui utilisent des fonctions SQL spécifiques à Oracle.
J'ai exécuté la migration complète en utilisant ora2pg 25.0 avec Oracle 19c comme source et PostgreSQL 18 comme cible.
Cet article aborde les cinq problèmes qui ont nécessité une intervention manuelle — et pourquoi chacun d'eux apparaîtra dans presque tous les schémas de production que vous migrez.
Ceci est le troisième article de la série.
Le Schéma RH post modèles de déclenchement de séquence couverts, %TYPE paramètres, et le bug de réapplication FK.
Le Schéma de publication SH tables partitionnées couvertes, index bitmap et vues matérialisées.
CO introduit trois nouvelles catégories de problèmes qui n'apparaissent dans aucune de celles-ci.
Table des matières
CN est contenu dans
Le Schéma CO le schéma de référence de gestion des commandes d'Oracle pour le commerce de détail comprend : les clients, les magasins, les produits, les commandes, les expéditions, les articles de commande et les stocks.
| Type d'objet | Compter |
|---|---|
| Tableaux | 7 |
| Vues | 4 |
| Séquences | 0 (colonnes d'identité uniquement) |
| Procédures stockées | 0 |
| Déclencheurs | 0 |
| Lignes (total) | ~8,800 |
L'absence de procédures stockées et de déclencheurs ramène l'effort de conversion PL/SQL à zéro. La complexité provient de trois endroits : la manière dont Oracle gère les colonnes IDENTITY, une colonne BLOB qui stocke du JSON, et quatre vues — dont trois utilisent des fonctions SQL spécifiques à Oracle que PostgreSQL ne prend pas en charge.
ora2pg évalue le schéma CO A-3: migration directe, estimée à trois heures-personne. Les principaux facteurs de coût sont les trois vues non portables.
Problème 1 : Les colonnes d'identité n'ont pas de séquences explicites
CO utilise GÉNÉRÉ PAR DÉFAUT SUR NULL COMME IDENTITÉ sur chaque colonne de clé primaire. Oracle gère les séquences sous-jacentes en interne — il n'y a pas CRÉER UNE SÉQUENCE des instructions dans le schéma, et aucune paire séquence-déclencheur comme le schéma RH utilisé.
ora2pg convertit correctement la syntaxe Oracle : GÉNÉRÉ PAR DÉFAUT SUR NULL COMME IDENTITÉ devient GÉNÉRÉ PAR DÉFAUT COMME IDENTITÉ dans PostgreSQL. Cette partie ne demande aucun travail manuel.
Le problème survient après le chargement des données.
Lorsque ora2pg exporte des données via COPY, il exporte les identifiants de ligne réels d'Oracle — 1, 2, 3, … jusqu'à l'identifiant le plus élevé. Le protocole COPY de PostgreSQL accepte ces valeurs et les insère directement dans les colonnes IDENTITY. La séquence IDENTITY n'est pas appelée pendant un chargement COPY. Une fois le chargement terminé, chaque séquence IDENTITY reste à sa valeur initiale de 1.
La première INSERT dans clients après la migration, tentera d'utiliser id_client = 1. Cette ligne existe déjà. L'insertion échoue avec la valeur de clé en double viole la contrainte unique.
La correction consiste à faire avancer chaque séquence IDENTITY au-delà de la valeur la plus élevée chargée :
SET search_path = co, public;
SELECT setval(pg_get_serial_sequence('co.customers', 'customer_id'), (SELECT MAX(customer_id) FROM co.customers));
SELECT setval(pg_get_serial_sequence('co.orders', 'order_id'), (SELECT MAX(order_id) FROM co.orders));
SELECT setval(pg_get_serial_sequence('co.shipments', 'shipment_id'), (SELECT MAX(shipment_id) FROM co.shipments));
SELECT setval(pg_get_serial_sequence('co.stores', 'store_id'), (SELECT MAX(store_id) FROM co.stores));
SELECT setval(pg_get_serial_sequence('co.products', 'product_id'), (SELECT MAX(product_id) FROM co.products));
pg_get_serial_sequence résout le nom de la séquence à partir des noms de table et de colonne. Cela évite de coder en dur les noms de séquence, qui varient entre les versions d'ora2pg.
Exécutez ce bloc immédiatement après le chargement des données, avant que toute application n'accède à la base de données migrée. Sur un schéma de production, les séquences manquantes provoquent des échecs d'INSERT qui sont faciles à manquer lors des tests si l'ensemble de données de test utilise des identifiants différents de ceux de la production.
Problème 2 : Stocker du JSON en BLOB doit devenir JSONB, pas BYTEA
produits.détails_produit est déclaré comme BLOB dans Oracle avec un VÉRIFIER (product_details IS JSON) contrainte.
Les bases de données Oracle antérieures à la version 21c stockent le JSON sous forme de BLOB ou de CLOB — il n'existe pas de type JSON natif.
ora2pg mappe toutes les colonnes BLOB à BYTEA par défaut.
Pour image_produit et magasins.logo, c'est exact — ils stockent des données d'image binaires.
Pour détails_produit, c'est faux.
BYTEA Dans PostgreSQL, il stocke les octets bruts. Il n'y a pas d'opérateurs JSON, pas de fonctions JSON, pas de validation à l'insertion. Le avis_de_produit voir les requêtes détails_produit en JSON en utilisant tableau_elements_jsonb après la réécriture (voir le Problème 5 ci-dessous). Cette fonction nécessite un JSONB argument — il échouera avec function jsonb_array_elements(bytea) does not exist si la colonne est BYTEA.
La correction est un MODIFICATION_TYPE annuler avant d'exécuter l'exportation du TABLEAU :
MODIFY_TYPE PRODUCTS:PRODUCT_DETAILS:jsonb
Ceci fait partie d'un plus long MODIFICATION_TYPE ligne — voir l'étape 6 ci-dessous pour la directive complète.
JSONB applique automatiquement JSON valide à chaque insertion. Le VÉRIFIER (product_details IS JSON) la contrainte est donc redondante après le changement de type de colonne. Elle utilise également la syntaxe Oracle — PostgreSQL n'a pas EST JSON prédicat — il doit donc être supprimé du DDL exporté avant le chargement. Voir Problème 4.
Problème 3 : Les colonnes FK sont NUMBER(38), les PK sont bigint — Inadéquation de type
FORCE_IDENTITY_BIGINT 1 causes ora2pg à exporter les colonnes de clé primaire IDENTITY comme grand entier. PG_INTEGER_TYPE 1 couvre brut NOMBRE colonnes vers grand entier.
Les colonnes FK dans CO — commandes.client_id, commandes.id_magasin, order_items.order_id, et d'autres — sont déclarés comme NOMBRE(38) Dans Oracle. C'est la représentation interne d'Oracle d'une colonne entière déclarée sans précision. Parce que la précision est explicitement 38, PG_INTEGER_TYPE 1 ne s'applique pas. ora2pg les mappe à numérique(38).
Le résultat : les colonnes de clé primaire sont grand entier, les colonnes de clé étrangère s'y référant sont numérique(38). PostgreSQL exige que les colonnes FK et PK aient le même type. Le chargement du DDL réussit, mais la création des contraintes FK échoue :
ERROR: foreign key constraint "orders_customer_id_fk" cannot be implemented
DETAIL: Key columns "customer_id" and "customer_id" are of incompatible types: numeric and bigint.
La correction consiste à ajouter toutes les colonnes de clés étrangères et les autres colonnes entières à MODIFICATION_TYPE:
MODIFY_TYPE INVENTORY:STORE_ID:bigint,INVENTORY:PRODUCT_ID:bigint,INVENTORY:PRODUCT_INVENTORY:integer,ORDERS:CUSTOMER_ID:bigint,ORDERS:STORE_ID:bigint,ORDER_ITEMS:ORDER_ID:bigint,ORDER_ITEMS:LINE_ITEM_ID:integer,ORDER_ITEMS:PRODUCT_ID:bigint,ORDER_ITEMS:QUANTITY:integer,ORDER_ITEMS:SHIPMENT_ID:bigint,SHIPMENTS:STORE_ID:bigint,SHIPMENTS:CUSTOMER_ID:bigint
MODIFICATION_TYPE lit seulement la première ligne de la directive. Toutes les substitutions — y compris la substitution JSONB du problème 2 et la numérique surtensions depuis le bas — doivent être sur une seule ligne séparée par des virgules.
Aussi : n'incluez pas la précision du type à l'intérieur MODIFICATION_TYPE écrire numérique, pas numérique(10,2)).
La virgule entre parenthèses est traitée comme un délimiteur et interrompt l'analyse.
Problème 4 : La contrainte de vérification IS JSON utilise la syntaxe Oracle
ora2pg exporte les produits_json_c contrainte comme :
CONSTRAINT products_json_c CHECK (product_details IS JSON)
PostgreSQL n'a pas EST JSON prédicat. Le chargement du DDL de la table échoue immédiatement avec une erreur de syntaxe.
La contrainte est également redondante. JSONB dans PostgreSQL valide le JSON à chaque insertion. Toute ligne qui échoue à la validation JSON est rejetée avant d'atteindre le stockage. Le EST JSON la contrainte ne fait rien que le type de colonne ne fasse déjà.
La correction : supprimez la ligne de contrainte du DDL exporté avant le chargement.
sed -i '/products_json_c/d' /home/fernando/ora2pg-co/output/CO_tables_fixed.sql
Vérifier :
grep 'products_json_c' /home/fernando/ora2pg-co/output/CO_tables_fixed.sql
# Expected: no output
Le rapport TEST affichera un DIFF sur les CONTRAINTES DE VÉRIFICATION pour le produits table — une contrainte dans Oracle, zéro dans PostgreSQL. Ceci est attendu et correct.
Problème 5 : Trois vues utilisent des fonctions SQL spécifiques à Oracle
CO a quatre vues. Une (commandes_produits) utilise le SQL standard et se convertit sans modification. Trois utilisent des fonctions n'ayant pas d'équivalent direct en PostgreSQL :
produits_commande_client — LISTAGG avec ON OVERFLOW
Oracle's LISTAGG(expr, separateur ON OVERFLOW TRONQUER '...' AVEC COMPTE) concatène les valeurs dans une chaîne délimitée avec gestion du dépassement.
PostgreSQL STRING_AGG(expr, séparateur ORDER BY ...) est l'équivalent — aucune option de dépassement.
-- Oracle
LISTAGG(p.product_name, ', ' ON OVERFLOW TRUNCATE '...' WITH COUNT)
WITHIN GROUP (ORDER BY oi.line_item_id) items
-- PostgreSQL
STRING_AGG(p.product_name, ', ' ORDER BY oi.line_item_id) items
commandes_magasin — GROUPING_ID
Oracle's GROUPING_ID(col1, col2) retourne un vecteur de bits entier codant quelles colonnes sont agrégées dans un ENSEMBLES DE GROUPEMENT requête.
PostgreSQL REGROUPEMENT(col1, col2) fait exactement la même chose — le nom de la fonction diffère, la sémantique est identique.
Le remplacement est un pour un.
-- Oracle
CASE grouping_id(store_name, order_status)
WHEN 1 THEN 'STORE TOTAL'
...
-- PostgreSQL
CASE grouping(s.store_name, o.order_status)
WHEN 1 THEN 'STORE TOTAL'
...
avis_de_produit — TABLEAU_JSON
Oracle's JSON_TABLE étend un tableau JSON en lignes à l'aide d'une projection relationnelle.
PostgreSQL n'a pas JSON_TABLE équivalent avant la version 15 (il a été ajouté dans PostgreSQL 15 dans le cadre de la norme SQL/JSON, mais avec une syntaxe différente).
Pour PostgreSQL 14 et versions antérieures — ou pour un comportement inter-versions cohérent — réécrire en utilisant tableau_elements_jsonb:
-- Oracle
FROM products p,
JSON_TABLE(p.product_details, '$'
COLUMNS (NESTED PATH '$.reviews[*]'
COLUMNS (rating INTEGER PATH '$.rating',
review VARCHAR2(4000) PATH '$.review'))) r
-- PostgreSQL
FROM products p,
jsonb_array_elements(p.product_details->'reviews') AS review_row
Dans la version PostgreSQL, chaque élément de tableau JSON est exposé en tant que jsonb valeur nommée ligne_examen.
Les valeurs de colonnes sont extraites avec le ->> opérateur : review_row->>'rating' et review_row->>'review'.
La distribution Entier sur le champ de notation est obligatoire car ->> revient toujours texte.
Cette réécriture dépend de détails_produit être JSONB — si la colonne est BYTEA (Le problème 2 n'est pas résolu), la vue échoue immédiatement.
Exécution de la migration : chaque commande dans l'ordre
Environnement
Deux VM sur le même réseau. srv1 (192.168.0.180) exécute Oracle 19c avec le schéma CO dans PDB pdb1. srv2 (192.168.0.181) exécute Ubuntu avec PostgreSQL 18 et ora2pg installés. Toutes les commandes ci-dessous sont exécutées sur srv2 sauf indication contraire.
Étape 1 — Collecter les statistiques Oracle
Courir sur srv1 Avant de générer le rapport, les estimations d'effort de ora2pg utilisent les statistiques stockées d'Oracle ; des statistiques obsolètes produisent des comptages inexacts.
-- On Oracle (srv1)
BEGIN
DBMS_STATS.GATHER_SCHEMA_STATS('CO');
DBMS_STATS.GATHER_DICTIONARY_STATS;
END;
/
Étape 2 — Créez le répertoire de sortie
mkdir -p /home/fernando/ora2pg-co/output
Étape 3 — Configuration de base ora2pg.conf
# /etc/ora2pg/ora2pg.conf
ORACLE_DSN dbi:Oracle:host=192.168.0.180;service_name=pdb1;port=1521
ORACLE_USER system
ORACLE_PWD <system password>
SCHEMA CO
EXPORT_SCHEMA 1
CREATE_SCHEMA 1
OUTPUT_DIR /home/fernando/ora2pg-co/output
Utilisation système, pas co — l'exportation COPY se lit v$base de données, ce qui requiert des privilèges DBA.
Étape 4 — Générer le rapport d'évaluation
ora2pg -t SHOW_REPORT --estimate_cost --dump_as_html \
> /home/fernando/ora2pg-co/output/report-co.html
Ouvrir le HTML dans un navigateur. CO devrait marquer A-3. Les inducteurs de coûts sont les trois vues non portables.
Étape 5 — Analyser les types de colonnes
ora2pg -t SHOW_COLUMN > /home/fernando/ora2pg-co/output/columns-co.txt
Colonnes clés à vérifier :
| Colonne | ora2pg par défaut | Type correct | Raison |
|---|---|---|---|
PRIX.UNITAIRE_PRODUIT | double précision | numérique | Monétaire — les nombres à virgule flottante provoquent des erreurs d'arrondi |
PRIX_UNITAIRE_Article_COMMANDE | double précision | numérique | Idem |
PRODUITS.DETAILS_PRODUIT | bytea | jsonb | Magasins JSON |
COMMANDES.ID_CLIENT | numérique(38) | grand entier | FK à grand entier PK — Incompatibilité de type |
Tous les autres NOMBRE(38) colonnes FK/entier | numérique(38) | grand entier ou entier | Même raison |
MAGASINS.LATITUDE, MAGASINS.LONGITUDE | double précision | double précision | Coordonnées géographiques — flottant est correct |
Étape 6 — Ajouter MODIFY_TYPE et réenregistrer la configuration
Tous les remplacements sur une seule ligne — MODIFICATION_TYPE ignore tout ce qui suit la première ligne.
Ne pas utiliser la précision du type (écrire numérique, pas numérique(10,2)).
MODIFY_TYPE PRODUCTS:PRODUCT_DETAILS:jsonb,PRODUCTS:UNIT_PRICE:numeric,ORDER_ITEMS:UNIT_PRICE:numeric,INVENTORY:STORE_ID:bigint,INVENTORY:PRODUCT_ID:bigint,INVENTORY:PRODUCT_INVENTORY:integer,ORDERS:CUSTOMER_ID:bigint,ORDERS:STORE_ID:bigint,ORDER_ITEMS:ORDER_ID:bigint,ORDER_ITEMS:LINE_ITEM_ID:integer,ORDER_ITEMS:PRODUCT_ID:bigint,ORDER_ITEMS:QUANTITY:integer,ORDER_ITEMS:SHIPMENT_ID:bigint,SHIPMENTS:STORE_ID:bigint,SHIPMENTS:CUSTOMER_ID:bigint
Étape 7 — Exécutez les exports de schéma
CO n'a pas de séquences explicites, ni de procédures stockées ou de déclencheurs.
Seules les exportations de TABLE et de VIEW sont nécessaires.
ora2pg -t TABLE -o CO_tables.sql 2>&1 | tee /home/fernando/ora2pg-co/output/table-export-co.log
ora2pg -t VIEW -o CO_views.sql 2>&1 | tee /home/fernando/ora2pg-co/output/view-export-co.log
Après l'exportation du TABLEAU, vérifiez les colonnes critiques :
grep -E 'product_details|UNIT_PRICE|IDENTITY' /home/fernando/ora2pg-co/output/CO_tables.sql
détails_produit doit montrer JSONB. Les deux PRIX_UNITAIRE les colonnes doivent apparaître NUMÉRIQUE. Les colonnes d'identité doivent afficher GÉNÉRÉ PAR DÉFAUT COMME IDENTITÉ.
Si une colonne affiche le mauvais type, la MODIFICATION_TYPE la directive n'a pas pris effet — vérifiez la configuration.
Étape 8 — Rédiger les fichiers de correctifs manuels
Deux fichiers de correction ne peuvent pas être générés à partir de la sortie ora2pg.
CO_tables_fixed.sql — supprime la contrainte IS JSON :
cp /home/fernando/ora2pg-co/output/CO_tables.sql \
/home/fernando/ora2pg-co/output/CO_tables_fixed.sql
sed -i '/products_json_c/d' /home/fernando/ora2pg-co/output/CO_tables_fixed.sql
CO_views_fixed.sql — réécrit les trois vues spécifiques à Oracle :
SET search_path = co,public;
-- product_orders: standard SQL, no changes needed
CREATE OR REPLACE VIEW product_orders AS
SELECT p.product_name, o.order_status,
SUM(oi.quantity * oi.unit_price) total_sales,
COUNT(*) order_count
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN customers c ON o.customer_id = c.customer_id
JOIN products p ON oi.product_id = p.product_id
GROUP BY p.product_name, o.order_status;
-- customer_order_products: LISTAGG → STRING_AGG
CREATE OR REPLACE VIEW customer_order_products AS
SELECT o.order_id, o.order_tms, o.order_status,
c.customer_id, c.email_address, c.full_name,
SUM(oi.quantity * oi.unit_price) order_total,
STRING_AGG(p.product_name, ', ' ORDER BY oi.line_item_id) items
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN customers c ON o.customer_id = c.customer_id
JOIN products p ON oi.product_id = p.product_id
GROUP BY o.order_id, o.order_tms, o.order_status,
c.customer_id, c.email_address, c.full_name;
-- store_orders: GROUPING_ID() → GROUPING()
CREATE OR REPLACE VIEW store_orders AS
SELECT CASE grouping(s.store_name, o.order_status)
WHEN 1 THEN 'STORE TOTAL'
WHEN 2 THEN 'STATUS TOTAL'
WHEN 3 THEN 'GRAND TOTAL'
END total,
s.store_name,
COALESCE(s.web_address, s.physical_address) address,
s.latitude, s.longitude,
o.order_status,
COUNT(DISTINCT o.order_id) order_count,
SUM(oi.quantity * oi.unit_price) total_sales
FROM stores s
JOIN orders o ON s.store_id = o.store_id
JOIN order_items oi ON o.order_id = oi.order_id
GROUP BY GROUPING SETS (
(s.store_name, COALESCE(s.web_address, s.physical_address), s.latitude, s.longitude),
(s.store_name, COALESCE(s.web_address, s.physical_address), s.latitude, s.longitude, o.order_status),
o.order_status,
()
);
-- product_reviews: JSON_TABLE → jsonb_array_elements
CREATE OR REPLACE VIEW product_reviews AS
SELECT p.product_name,
(review_row->>'rating')::INTEGER AS rating,
ROUND(
AVG((review_row->>'rating')::INTEGER)
OVER (PARTITION BY p.product_name),
2
) avg_rating,
review_row->>'review' AS review
FROM products p,
jsonb_array_elements(p.product_details->'reviews') AS review_row;
Étape 9 - Exporter les données
ora2pg -t COPY -o CO_data.sql 2>&1 | tee /home/fernando/ora2pg-co/output/copy-export-co.log
Après l'exportation, vérifiez les nouvelles lignes intégrées dans les données JSON. ora2pg n'échappe pas les caractères de nouvelle ligne littéraux à l'intérieur des valeurs de chaîne JSON.
Si applicable détails_produit la valeur contient un nouveau saut de ligne, le parseur COPY le traite comme un séparateur de ligne et échoue avec syntaxe d'entrée invalide pour le type json.
Corriger les nouvelles lignes intégrées avant le chargement :
python3 << 'EOF'
import re
with open('/home/fernando/ora2pg-co/output/CO_data.sql', 'r') as f:
lines = f.read().split('\n')
result = []
in_products = False
pending = None
for line in lines:
if re.match(r'COPY products\b', line):
in_products = True
result.append(line)
continue
if in_products:
if line == '\\.':
if pending is not None:
result.append(pending)
pending = None
result.append(line)
in_products = False
continue
if re.match(r'^\d+\t', line):
if pending is not None:
result.append(pending)
pending = line
else:
pending = (pending + '\\n' + line) if pending else line
else:
result.append(line)
with open('/home/fernando/ora2pg-co/output/CO_data_fixed.sql', 'w') as f:
f.write('\n'.join(result))
print("Done")
EOF
Vérifier que le nombre de lignes est correct :
grep -c $'\t' /home/fernando/ora2pg-co/output/CO_data_fixed.sql
# Expected: 8783
Étape 10 — Créer la base de données
sudo -u postgres psql -c "CREATE ROLE co WITH LOGIN PASSWORD 'co';"
sudo -u postgres psql -c "CREATE DATABASE codb OWNER co;"
PGPASSWORD=co psql -U co -d codb -h localhost -c "CREATE SCHEMA co;"
Étape 11 — Charger dans l'ordre des dépendances
# Tables first
PGPASSWORD=co psql -U co -d codb -h localhost \
-f /home/fernando/ora2pg-co/output/CO_tables_fixed.sql
# Data — FKs are dropped inside the file before load, not re-added after
PGPASSWORD=co psql -U co -d codb -h localhost \
-f /home/fernando/ora2pg-co/output/CO_data_fixed.sql
Étape 12 — Réappliquer les contraintes FK
ora2pg's COPY file supprime toutes les contraintes de clé étrangère avant le chargement des données et ne les réintègre jamais.
C'est le même bug que dans le laboratoire RH (numéro #1960).
(echo "SET search_path TO co;"; \
grep 'ADD CONSTRAINT.*FOREIGN KEY' \
/home/fernando/ora2pg-co/output/CO_tables_fixed.sql) | \
PGPASSWORD=co psql -U co -d codb -h localhost
Vérifier le compte :
PGPASSWORD=co psql -U co -d codb -h localhost -c "
SELECT COUNT(*) FROM information_schema.table_constraints
WHERE constraint_schema = 'co' AND constraint_type = 'FOREIGN KEY';"
# Expected: 9
Étape 13 — Faire avancer les séquences d'IDENTITÉ
PGPASSWORD=co psql -U co -d codb -h localhost << 'EOF'
SET search_path = co, public;
SELECT setval(pg_get_serial_sequence('co.customers', 'customer_id'), (SELECT MAX(customer_id) FROM co.customers));
SELECT setval(pg_get_serial_sequence('co.orders', 'order_id'), (SELECT MAX(order_id) FROM co.orders));
SELECT setval(pg_get_serial_sequence('co.shipments', 'shipment_id'), (SELECT MAX(shipment_id) FROM co.shipments));
SELECT setval(pg_get_serial_sequence('co.stores', 'store_id'), (SELECT MAX(store_id) FROM co.stores));
SELECT setval(pg_get_serial_sequence('co.products', 'product_id'), (SELECT MAX(product_id) FROM co.products));
EOF
Étape 14 — Charger les vues
PGPASSWORD=co psql -U co -d codb -h localhost \
-f /home/fernando/ora2pg-co/output/CO_views_fixed.sql
# Expected: CREATE VIEW × 4 — no errors
Étape 15 — Exécutez la comparaison TEST
Ajouter la connexion PostgreSQL à ora2pg.conf:
PG_DSN dbi:Pg:dbname=codb;host=localhost;port=5432
PG_USER co
PG_PWD co
ora2pg -t TEST 2>&1 | tee /home/fernando/ora2pg-co/output/CO_test.txt
À quoi ressemble le rapport final de TEST
Après que les cinq correctifs sont appliqués, ora2pg -t TEST doit montrer un DIFF et tout le reste OK.
VÉRIFIER LES CONTRAINTES DIFF sur produits — Oracle a une contrainte de vérification (produits_json_c), PostgreSQL a zéro.
Ceci est attendu : la contrainte a été délibérément supprimée car elle utilise la syntaxe Oracle et est redondante avec JSONB validation du type de colonne.
Toutes les autres sections — colonnes, index, clés primaires, clés étrangères, tables, vues, séquences, décomptes de lignes — devraient afficher OK.
Toute autre ligne DIFF est un réel problème qui doit être résolu avant le basculement.
En résumé
CO est une migration plus propre que HR ou SH à certains égards — pas de procédures stockées, pas de déclencheurs, pas d'index bitmap, pas de tables partitionnées. Les problèmes qu'elle introduit sont d'une nature différente.
L'échec des séquences d'IDENTITÉ après un chargement de données est invisible jusqu'au premier INSERT en production - il n'y a pas d'avertissement pendant la migration elle-même. Les séquences setval l'étape est facile à manquer car le chargement réussit proprement sans elle.
Le modèle BLOB-as-JSON est courant dans les bases de données Oracle antérieures à la version 21c, où il n'existait pas de type JSON natif. Chaque schéma Oracle qui stocke du JSON nécessite la même surcharge MODIFY_TYPE et la même suppression de contrainte IS JSON. Les réécritures de vues sont un travail manuel unique, mais le STRING_AGG, REGROUPEMENTet tableau_elements_jsonb sont des schémas que vous retrouverez dans tout schéma Oracle comportant des vues analytiques ou des vues avec requête JSON.
La discordance de type FK à partir de NOMBRE(38) Les colonnes sont un problème systématique. PG_INTEGER_TYPE 1 manipule nu NOMBRE correctement, mais tout ce qui est déclaré avec précision — même NOMBRE(38), ce qui est la façon dont Oracle représente un entier simple dans son dictionnaire de données — sort du cadre de la règle. Sur un schéma de production, la sortie de SHOW_COLUMN contiendra de nombreuses colonnes de ce type. Chacune d'elles nécessite une entrée MODIFY_TYPE avant l'exportation de la TABLE.
Si vous prévoyez une migration d'Oracle vers PostgreSQL et souhaitez une évaluation indépendante de la complexité, de l'effort et des risques avant de vous engager sur un calendrier, Je propose un audit de migration à prix fixe qui produit exactement cela.
