Convertir PostgreSQL en Oracle
Déposez un dump texte de pg_dump ou pg_dumpall, en .sql ou .sql.gz, et téléchargez un script Oracle avec les mêmes tables, clés, index et données. Chaque valeur que Oracle ne peut pas contenir telle quelle, et chaque objet qu'il ne reprend pas, est listé dans le rapport.
18 lignes converties, 2 corrections, aucune rejetée
Tables de shop_2024_postgres.sqlDump PostgreSQL 18.6 · écrit par pg_dump · utf8mb4
customersécrite sous le nom CUSTOMERS8 lignes1
8 colonnes ID NUMBER(10) not null NAME VARCHAR2(4000 CHAR) not null EMAIL VARCHAR2(750 CHAR) not null COUNTRY VARCHAR2(2 CHAR) not null IS_ACTIVE NUMBER(1) not null TAGS CLOB not null SIGNED_UP DATE CREATED_AT TIMESTAMP(6) WITH TIME ZONE not null ordersécrite sous le nom ORDERS10 lignes1
10 colonnes ID NUMBER(10) not null CUSTOMER_ID NUMBER(10) not null STATUS VARCHAR2(9 CHAR) not null TOTAL NUMBER(10,2) not null ITEMS NUMBER(5) not null ORDERED_AT TIMESTAMP(6) not null SHIPPED_AT TIMESTAMP(6) NOTE VARCHAR2(4000 CHAR) DETAILS CLOB DELIVERY VARCHAR2(100 CHAR)
Non converti
- Vue paid_orders
Chaque base de données les écrit dans son propre langage. Réécrivez-les pour Oracle.
-- Written by IntoSQL from a PostgreSQL dump. Load it with: NLS_LANG=.AL32UTF8 sqlplus -S -L <user>/<password>@<database> @<file>
WHENEVER SQLERROR EXIT FAILURE ROLLBACK;
SET DEFINE OFF;
SET SQLBLANKLINES ON;
SET FEEDBACK OFF;
SET SERVEROUTPUT ON;
CREATE OR REPLACE FUNCTION "INTOSQL_CLOB" (p SYS.ODCIVARCHAR2LIST) RETURN CLOB IS
r CLOB;
BEGIN
DBMS_LOB.CREATETEMPORARY(r, TRUE);
FOR i IN 1 .. p.COUNT LOOP
IF p(i) IS NOT NULL THEN
DBMS_LOB.WRITEAPPEND(r, LENGTH2(p(i)), p(i));
END IF;
END LOOP;
RETURN r;
END;
/
CREATE OR REPLACE FUNCTION "INTOSQL_BLOB" (p SYS.ODCIVARCHAR2LIST) RETURN BLOB IS
r BLOB;
piece RAW(2000);
BEGIN
DBMS_LOB.CREATETEMPORARY(r, TRUE);
FOR i IN 1 .. p.COUNT LOOP
piece := HEXTORAW(p(i));
DBMS_LOB.WRITEAPPEND(r, UTL_RAW.LENGTH(piece), piece);
END LOOP;
RETURN r;
END;
/
CREATE TABLE "CUSTOMERS" (
"ID" NUMBER(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 1001),
"NAME" VARCHAR2(4000 CHAR) NOT NULL,
"EMAIL" VARCHAR2(750 CHAR) NOT NULL,
"COUNTRY" VARCHAR2(2 CHAR) DEFAULT 'US' NOT NULL,
"IS_ACTIVE" NUMBER(1) DEFAULT 1 NOT NULL,
"TAGS" CLOB NOT NULL,
"SIGNED_UP" DATE,
"CREATED_AT" TIMESTAMP(6) WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL,
CONSTRAINT "CK_CUSTOMERS_IS_ACTIVE" CHECK ("IS_ACTIVE" IN (0, 1))
);
CREATE TABLE "ORDERS" (
"ID" NUMBER(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 5001),
"CUSTOMER_ID" NUMBER(10) NOT NULL,
"STATUS" VARCHAR2(9 CHAR) DEFAULT 'pending' NOT NULL,
"TOTAL" NUMBER(10,2) NOT NULL,
"ITEMS" NUMBER(5) DEFAULT 1 NOT NULL,
"ORDERED_AT" TIMESTAMP(6) NOT NULL,
"SHIPPED_AT" TIMESTAMP(6),
"NOTE" VARCHAR2(4000 CHAR),
"DETAILS" CLOB,
"DELIVERY" VARCHAR2(100 CHAR),
CONSTRAINT "CK_ORDERS_STATUS" CHECK ("STATUS" IN ('pending', 'paid', 'shipped', 'cancelled')),
CONSTRAINT "ORDERS_TOTAL_CHECK" CHECK ("TOTAL" >= (0))
);
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
1,
'Ana Martínez',
'ana@example.com',
'MX',
1,
'["vip"]',
DATE '2023-01-15',
TIMESTAMP '2023-01-15 10:24:00 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
2,
'Zoë Müller',
'zoe@example.de',
'DE',
1,
'[]',
DATE '2023-02-03',
TIMESTAMP '2023-02-03 08:11:42 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
3,
'José Peña',
'jose@example.es',
'ES',
0,
'["wholesale","late payer"]',
DATE '2023-03-21',
TIMESTAMP '2023-03-21 19:05:17 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
4,
'李雷',
'lilei@example.cn',
'CN',
1,
'[]',
DATE '2023-05-09',
TIMESTAMP '2023-05-09 02:40:00 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
5,
'Noah O''Brien',
'noah@example.ie',
'IE',
1,
'[]',
NULL,
TIMESTAMP '2023-06-30 14:00:00 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
6,
'Amélie Laurent',
'amelie@example.fr',
'FR',
1,
'["vip"]',
DATE '2024-01-02',
TIMESTAMP '2024-01-02 09:30:00 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
7,
'Ravi Patel',
'ravi@example.in',
'IN',
0,
'[]',
DATE '2024-02-29',
TIMESTAMP '2024-02-29 23:59:59 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
8,
'Sofia Rossi 🌻',
'sofia@example.it',
'IT',
1,
'[]',
DATE '2024-04-12',
TIMESTAMP '2024-04-12 12:12:12 +00:00'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
1,
1,
'shipped',
129.90,
3,
TIMESTAMP '2024-03-01 10:00:00',
TIMESTAMP '2024-03-02 16:30:00',
NULL,
'{"gift": false}',
'1 day 06:30:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
2,
2,
'paid',
54.00,
1,
TIMESTAMP '2024-03-02 11:15:00',
NULL,
'Gift wrap 🎁',
'{"gift": true, "wrap": "red"}',
NULL
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
3,
1,
'cancelled',
12.50,
1,
TIMESTAMP '2024-03-03 09:45:00',
NULL,
'Customer changed their mind',
NULL,
NULL
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
4,
4,
'shipped',
980.00,
12,
TIMESTAMP '2024-03-05 02:10:00',
TIMESTAMP '2024-03-07 08:00:00',
'请小心轻放',
'{"fragile": true}',
'2 days 05:50:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
5,
5,
'pending',
45.75,
2,
TIMESTAMP '2024-03-08 17:20:00',
NULL,
'Leave at the door; ring twice',
NULL,
NULL
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
6,
6,
'shipped',
310.40,
5,
TIMESTAMP '2024-03-10 13:00:00',
TIMESTAMP '2024-03-11 09:00:00',
'Fragile: "glass"',
'{"fragile": true}',
'20:00:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
7,
8,
'paid',
76.20,
2,
TIMESTAMP '2024-03-11 18:45:00',
NULL,
NULL,
NULL,
NULL
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
8,
2,
'shipped',
19.99,
1,
TIMESTAMP '2024-03-12 08:00:00',
NULL,
'Shipped date set to infinity by an old import',
NULL,
NULL
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
9,
3,
'shipped',
64.00,
2,
TIMESTAMP '2024-03-14 15:30:00',
TIMESTAMP '2024-03-15 10:00:00',
'Two parcels',
'{"parcels": [1, 2]}',
'18:30:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
10,
7,
'paid',
1499.00,
1,
TIMESTAMP '2024-03-15 21:05:00',
NULL,
'C:\Users\ravi\invoice.pdf',
NULL,
NULL
);
COMMIT;
END;
/
-- Keys, indexes, foreign keys, counters and triggers, after the rows.
ALTER TABLE "CUSTOMERS" ADD CONSTRAINT "UQ_CUSTOMERS_CUSTOMERS_EMAIL_KEY" UNIQUE ("EMAIL");
ALTER TABLE "CUSTOMERS" ADD CONSTRAINT "PK_CUSTOMERS" PRIMARY KEY ("ID");
ALTER TABLE "ORDERS" ADD CONSTRAINT "PK_ORDERS" PRIMARY KEY ("ID");
CREATE INDEX "IX_ORDERS_IDX_ORDERS_CUSTOMER" ON "ORDERS" ("CUSTOMER_ID");
CREATE INDEX "IX_ORDERS_IDX_ORDERS_STATUS_DATE" ON "ORDERS" ("STATUS", "ORDERED_AT");
ALTER TABLE "ORDERS" ADD CONSTRAINT "ORDERS_CUSTOMER_ID_FKEY" FOREIGN KEY ("CUSTOMER_ID") REFERENCES "CUSTOMERS" ("ID") ENABLE NOVALIDATE;
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE "ORDERS" ENABLE VALIDATE CONSTRAINT "ORDERS_CUSTOMER_ID_FKEY"';
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Foreign key ' || '"ORDERS_CUSTOMER_ID_FKEY"' || ' has rows without a parent: it stays unvalidated, and new rows are checked.');
END;
/
DECLARE
next_id NUMBER;
BEGIN
SELECT NVL(MAX("ID"), 0) + 1 INTO next_id FROM "CUSTOMERS";
IF next_id < 1001 THEN
next_id := 1001;
END IF;
EXECUTE IMMEDIATE 'ALTER TABLE "CUSTOMERS" MODIFY ("ID" GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH ' || next_id || '))';
END;
/
COMMENT ON TABLE "CUSTOMERS" IS 'People who bought at least once';
DECLARE
next_id NUMBER;
BEGIN
SELECT NVL(MAX("ID"), 0) + 1 INTO next_id FROM "ORDERS";
IF next_id < 5001 THEN
next_id := 5001;
END IF;
EXECUTE IMMEDIATE 'ALTER TABLE "ORDERS" MODIFY ("ID" GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH ' || next_id || '))';
END;
/
DROP FUNCTION "INTOSQL_CLOB";
DROP FUNCTION "INTOSQL_BLOB";
COMMIT;
EXIT;
Corrections
- "tags" de customers est text[], que Oracle n'a pas ; elle est donc en CLOB avec les mêmes valeurs.
- "delivery" de orders est interval, que Oracle n'a pas ; elle est donc en VARCHAR2(100 CHAR) avec les mêmes valeurs.
À vérifier
- 1 objet non converti (vues, déclencheurs, routines ou événements) ; le rapport les liste un par un. Recréez-les à la main dans Oracle.
- La valeur par défaut de "tags" dans customers ne peut pas être écrite pour Oracle (a value its type can't hold) ; la colonne n'a donc pas de valeur par défaut. Ajoutez la valeur par défaut à la main si les nouvelles lignes en ont besoin.
- L'index idx_orders_details sur orders utilise une expression qui ne peut pas être écrite pour Oracle (a gin index) ; il n'est donc pas créé. Créez l'index à la main si les requêtes en ont besoin.
- 1 date de "shipped_at" dans orders, que Oracle ne peut pas stocker (par exemple infinity), devient NULL. Oracle stocke les années 1 à 9999, sans infini ; remplacez ces dates dans la source si elles comptent.
Votre fichier est converti sur cet ordinateur et n'est jamais envoyé.
Autres exemples :
Ce qui change de PostgreSQL à Oracle
Chaque ligne est une règle appliquée par le convertisseur, avec la ligne de rapport qu'il écrit quand la règle change quelque chose dans vos données.
boolean
NUMBER(1) avec un CHECK de 0 et 1
Oracle 21c n'a pas de type de colonne booléen ; 1 et 0 gardent true et false sur chaque version.
varchar(n), char(n)
VARCHAR2(n CHAR), en caractères
Les longueurs comptent les caractères comme celles de PostgreSQL.
text
VARCHAR2(4000 CHAR) si chaque valeur tient en 4000 octets (750 caractères dans une clé), sinon CLOB
Un VARCHAR2 contient 4000 octets par défaut et Oracle ne peut pas indexer un CLOB ; le texte garde donc VARCHAR2 si ses valeurs y tiennent, sinon il devient CLOB.
Ligne du rapport: text_as_lob
empty strings and empty bytea values
NULL
Oracle stocke une chaîne vide comme NULL, donc une valeur qui valait ' se relit comme NULL. Le rapport les compte par colonne.
Ligne du rapport: empty_string_null
citext
comparaison exacte
Oracle compare le texte exactement sauf indication contraire, comme le texte de PostgreSQL ; les colonnes citext deviennent sensibles à la casse.
Ligne du rapport: case_insensitive_collation
numeric(p,s) past 38 digits; numeric without a precision
NUMBER si ses valeurs tiennent en 38 chiffres, sinon VARCHAR2
Le NUMBER d'Oracle contient 38 chiffres à toute échelle. Les nombres plus larges sont gardés en texte pour que chaque chiffre reste exact.
Ligne du rapport: decimal_as_text
money
NUMBER(19,2)
Oracle n'a pas de type monétaire. Le montant est gardé comme nombre, sans le symbole monétaire ajouté par la locale du serveur.
Ligne du rapport: type_replaced
interval
VARCHAR2(100 CHAR) avec le texte de PostgreSQL
Un intervalle mélange les mois avec les jours et les secondes, qu'Oracle garde dans deux types différents ; le texte en garde chaque partie.
Ligne du rapport: type_replaced
arrays (integer[], text[]...)
CLOB avec un tableau JSON
Oracle n'a pas de tableaux. Un tableau JSON garde chaque élément et NULL, et JSON_TABLE le lit.
Ligne du rapport: type_replaced
ranges, geometric types, tsvector, xml, hstore, PostGIS, composite and extension types
VARCHAR2 ou CLOB avec le texte de PostgreSQL
Oracle n'a pas de type équivalent ; chaque valeur garde donc le texte écrit par PostgreSQL.
Ligne du rapport: type_replaced
json, jsonb
CLOB
Les fonctions JSON d'Oracle lisent le JSON depuis du texte sur chaque version, et un CLOB garde des valeurs de toute taille.
timestamp
TIMESTAMP
Un timestamp sans fuseau horaire est une date et une heure locales, comme le TIMESTAMP d'Oracle. date devient DATE, qui contient aussi une heure du jour, laissée à minuit.
timestamp with time zone
TIMESTAMP WITH TIME ZONE en UTC
Chaque valeur est écrite en UTC, le même instant quel que soit le décalage écrit par le dump.
time
INTERVAL DAY TO SECOND depuis minuit
Oracle n'a pas de type heure du jour ; un intervalle depuis minuit contient chaque valeur, 24:00:00 compris.
infinity, -infinity, BC dates and years past 9999
NULL
Le script écrit les années 1 à 9999, et Oracle n'a pas d'infini. Ces valeurs deviennent NULL et le rapport les compte.
Ligne du rapport: date_out_of_range
NaN and Infinity in numeric
NULL
Le NUMBER d'Oracle n'a ni NaN ni Infinity ; ces valeurs deviennent donc NULL et le rapport les compte.
Ligne du rapport: invalid_value_null
NOT NULL columns with values that become NULL
NOT NULL retiré
Une colonne dont des valeurs ont dû devenir NULL ne peut pas garder NOT NULL, sinon le chargement s'arrêterait.
Ligne du rapport: not_null_relaxed
enum types
VARCHAR2 avec un CHECK de ses libellés
Un texte avec une contrainte CHECK garde les mêmes valeurs.
enum columns holding values outside their labels
VARCHAR2 sans CHECK
Les valeurs absentes des libellés feraient échouer la contrainte CHECK, qui n'est donc pas créée.
Ligne du rapport: enum_check_skipped
uuid, inet, cidr, macaddr
VARCHAR2
Oracle n'a pas ces types ; le texte garde chaque valeur.
bytea
BLOB
Les octets restent des octets.
bit(n), bit varying
NUMBER avec la valeur des bits jusqu'à 63 bits ; chiffres en texte au-delà
Les chaînes de bits sont stockées comme le nombre que forment leurs bits.
oid, xid, cid
NUMBER(10) avec un CHECK qui interdit les négatifs
NUMBER contient chaque valeur.
serial, identity
GENERATED BY DEFAULT ON NULL AS IDENTITY, qui reprend après l'identifiant le plus élevé
Les identifiants copiés se chargent tels quels, et le compteur reprend après le plus élevé ou après la prochaine valeur de la séquence, selon le plus grand.
a second serial or identity column in a table
un entier simple
Oracle compte une colonne d'identité par table ; toute autre garde ses valeurs sans compteur.
Ligne du rapport: counter_dropped
sequences not owned by a column
CREATE SEQUENCE, à la valeur atteinte
Les séquences Oracle fonctionnent de la même façon.
generated columns
colonnes virtuelles
Oracle ne calcule les colonnes générées qu'à la lecture ; les colonnes STORED deviennent virtuelles avec les mêmes valeurs.
Ligne du rapport: stored_as_virtual
generated columns with functions Oracle lacks
une colonne simple avec les valeurs
Une formule qui ne peut pas être écrite pour Oracle laisse une colonne simple avec les valeurs du dump et une ligne dans le rapport.
Ligne du rapport: generated_not_translated
defaults with functions Oracle lacks, and defaults from a sequence
aucune valeur par défaut
Une valeur par défaut qui ne peut pas être écrite pour Oracle est omise et signalée.
Ligne du rapport: default_not_translated
CHECK constraints
traduites, en ligne dans CREATE TABLE
Les CHECK en ligne sont vérifiés au chargement de chaque ligne.
CHECK constraints with functions Oracle lacks
non créée
Une contrainte CHECK qui ne peut pas être écrite pour Oracle est omise et signalée.
Ligne du rapport: check_skipped
CHECK ... NOT VALID
non créée
PostgreSQL n'a jamais vérifié les lignes déjà présentes, qui peuvent donc l'enfreindre.
Ligne du rapport: check_not_valid
gin, gist, brin and hash indexes, partial and expression indexes, and indexes on CLOB columns
non créé
IntoSQL écrit des index b-tree sur des colonnes pour Oracle, qui ne peut pas indexer un CLOB ; recréez les autres après le chargement.
Ligne du rapport: index_skipped
foreign keys
après les données ENABLE NOVALIDATE, puis validée avec un avertissement sur les orphelins
La clé vérifie tout de suite les nouvelles lignes et est validée en dernier ; des lignes sans parent la laissent non validée avec un avertissement au lieu d'arrêter le chargement.
ON UPDATE CASCADE, SET NULL or SET DEFAULT; ON DELETE SET DEFAULT
supprimé
Les clés étrangères d'Oracle n'agissent qu'à la suppression, avec CASCADE ou SET NULL.
Ligne du rapport: foreign_key_action_unsupported
foreign keys to tables the dump doesn't create
non créée
Une clé ne peut pas désigner une table absente.
Ligne du rapport: foreign_key_skipped
names over 128 bytes
raccourci
Depuis Oracle 12.2, les noms font 128 octets au plus, plus que les 63 de PostgreSQL.
Ligne du rapport: name_shortened
partitioned tables
une seule table avec les lignes de chaque partition
Les lignes sont les mêmes ; partitionnez à nouveau la table dans Oracle si besoin.
Ligne du rapport: partitions_dropped
schemas
un seul schéma, tables préfixées par le nom de leur schéma
Le script se charge dans le schéma de l'utilisateur qui l'exécute ; les tables de schémas différents reçoivent donc le nom de leur schéma en préfixe, et celles de public gardent leur nom.
Ligne du rapport: schemas_merged
views, functions, procedures, triggers, rules, policies, extensions and types
listés dans le rapport
Leur code est écrit dans le langage de PostgreSQL ; ils sont donc listés pour que vous les recréiez.
Ligne du rapport: not_converted_objects
dumps without CREATE TABLE (--data-only)
types déduits des valeurs
Un dump de données seules ne contient que des lignes, donc les types viennent des valeurs.
Ligne du rapport: no_schema
expressions in INSERT values
NULL
Un convertisseur qui n'exécute pas de base de données ne peut pas les évaluer.
Ligne du rapport: expression_value
types IntoSQL doesn't know
CLOB
Le texte accepte toute valeur ; changez le type après le chargement.
Ligne du rapport: unknown_type
text that isn't valid in its encoding
les octets invalides deviennent le caractère de remplacement
Le script est en UTF-8 et se charge avec NLS_LANG=.AL32UTF8.
Ligne du rapport: invalid_utf8_replaced
text decoded twice (ñ)
copié tel quel
Le dump contient exactement ces caractères, ils sont donc copiés ; le rapport les signale.
Ligne du rapport: mojibake_suspected
| Dans PostgreSQL | Dans Oracle | Ligne du rapport |
|---|---|---|
| boolean | NUMBER(1) avec un CHECK de 0 et 1Oracle 21c n'a pas de type de colonne booléen ; 1 et 0 gardent true et false sur chaque version. | |
| varchar(n), char(n) | VARCHAR2(n CHAR), en caractèresLes longueurs comptent les caractères comme celles de PostgreSQL. | |
| text | VARCHAR2(4000 CHAR) si chaque valeur tient en 4000 octets (750 caractères dans une clé), sinon CLOBUn VARCHAR2 contient 4000 octets par défaut et Oracle ne peut pas indexer un CLOB ; le texte garde donc VARCHAR2 si ses valeurs y tiennent, sinon il devient CLOB. | text_as_lob |
| empty strings and empty bytea values | NULLOracle stocke une chaîne vide comme NULL, donc une valeur qui valait ' se relit comme NULL. Le rapport les compte par colonne. | empty_string_null |
| citext | comparaison exacteOracle compare le texte exactement sauf indication contraire, comme le texte de PostgreSQL ; les colonnes citext deviennent sensibles à la casse. | case_insensitive_collation |
| numeric(p,s) past 38 digits; numeric without a precision | NUMBER si ses valeurs tiennent en 38 chiffres, sinon VARCHAR2Le NUMBER d'Oracle contient 38 chiffres à toute échelle. Les nombres plus larges sont gardés en texte pour que chaque chiffre reste exact. | decimal_as_text |
| money | NUMBER(19,2)Oracle n'a pas de type monétaire. Le montant est gardé comme nombre, sans le symbole monétaire ajouté par la locale du serveur. | type_replaced |
| interval | VARCHAR2(100 CHAR) avec le texte de PostgreSQLUn intervalle mélange les mois avec les jours et les secondes, qu'Oracle garde dans deux types différents ; le texte en garde chaque partie. | type_replaced |
| arrays (integer[], text[]...) | CLOB avec un tableau JSONOracle n'a pas de tableaux. Un tableau JSON garde chaque élément et NULL, et JSON_TABLE le lit. | type_replaced |
| ranges, geometric types, tsvector, xml, hstore, PostGIS, composite and extension types | VARCHAR2 ou CLOB avec le texte de PostgreSQLOracle n'a pas de type équivalent ; chaque valeur garde donc le texte écrit par PostgreSQL. | type_replaced |
| json, jsonb | CLOBLes fonctions JSON d'Oracle lisent le JSON depuis du texte sur chaque version, et un CLOB garde des valeurs de toute taille. | |
| timestamp | TIMESTAMPUn timestamp sans fuseau horaire est une date et une heure locales, comme le TIMESTAMP d'Oracle. date devient DATE, qui contient aussi une heure du jour, laissée à minuit. | |
| timestamp with time zone | TIMESTAMP WITH TIME ZONE en UTCChaque valeur est écrite en UTC, le même instant quel que soit le décalage écrit par le dump. | |
| time | INTERVAL DAY TO SECOND depuis minuitOracle n'a pas de type heure du jour ; un intervalle depuis minuit contient chaque valeur, 24:00:00 compris. | |
| infinity, -infinity, BC dates and years past 9999 | NULLLe script écrit les années 1 à 9999, et Oracle n'a pas d'infini. Ces valeurs deviennent NULL et le rapport les compte. | date_out_of_range |
| NaN and Infinity in numeric | NULLLe NUMBER d'Oracle n'a ni NaN ni Infinity ; ces valeurs deviennent donc NULL et le rapport les compte. | invalid_value_null |
| NOT NULL columns with values that become NULL | NOT NULL retiréUne colonne dont des valeurs ont dû devenir NULL ne peut pas garder NOT NULL, sinon le chargement s'arrêterait. | not_null_relaxed |
| enum types | VARCHAR2 avec un CHECK de ses libellésUn texte avec une contrainte CHECK garde les mêmes valeurs. | |
| enum columns holding values outside their labels | VARCHAR2 sans CHECKLes valeurs absentes des libellés feraient échouer la contrainte CHECK, qui n'est donc pas créée. | enum_check_skipped |
| uuid, inet, cidr, macaddr | VARCHAR2Oracle n'a pas ces types ; le texte garde chaque valeur. | |
| bytea | BLOBLes octets restent des octets. | |
| bit(n), bit varying | NUMBER avec la valeur des bits jusqu'à 63 bits ; chiffres en texte au-delàLes chaînes de bits sont stockées comme le nombre que forment leurs bits. | |
| oid, xid, cid | NUMBER(10) avec un CHECK qui interdit les négatifsNUMBER contient chaque valeur. | |
| serial, identity | GENERATED BY DEFAULT ON NULL AS IDENTITY, qui reprend après l'identifiant le plus élevéLes identifiants copiés se chargent tels quels, et le compteur reprend après le plus élevé ou après la prochaine valeur de la séquence, selon le plus grand. | |
| a second serial or identity column in a table | un entier simpleOracle compte une colonne d'identité par table ; toute autre garde ses valeurs sans compteur. | counter_dropped |
| sequences not owned by a column | CREATE SEQUENCE, à la valeur atteinteLes séquences Oracle fonctionnent de la même façon. | |
| generated columns | colonnes virtuellesOracle ne calcule les colonnes générées qu'à la lecture ; les colonnes STORED deviennent virtuelles avec les mêmes valeurs. | stored_as_virtual |
| generated columns with functions Oracle lacks | une colonne simple avec les valeursUne formule qui ne peut pas être écrite pour Oracle laisse une colonne simple avec les valeurs du dump et une ligne dans le rapport. | generated_not_translated |
| defaults with functions Oracle lacks, and defaults from a sequence | aucune valeur par défautUne valeur par défaut qui ne peut pas être écrite pour Oracle est omise et signalée. | default_not_translated |
| CHECK constraints | traduites, en ligne dans CREATE TABLELes CHECK en ligne sont vérifiés au chargement de chaque ligne. | |
| CHECK constraints with functions Oracle lacks | non crééeUne contrainte CHECK qui ne peut pas être écrite pour Oracle est omise et signalée. | check_skipped |
| CHECK ... NOT VALID | non crééePostgreSQL n'a jamais vérifié les lignes déjà présentes, qui peuvent donc l'enfreindre. | check_not_valid |
| gin, gist, brin and hash indexes, partial and expression indexes, and indexes on CLOB columns | non crééIntoSQL écrit des index b-tree sur des colonnes pour Oracle, qui ne peut pas indexer un CLOB ; recréez les autres après le chargement. | index_skipped |
| foreign keys | après les données ENABLE NOVALIDATE, puis validée avec un avertissement sur les orphelinsLa clé vérifie tout de suite les nouvelles lignes et est validée en dernier ; des lignes sans parent la laissent non validée avec un avertissement au lieu d'arrêter le chargement. | |
| ON UPDATE CASCADE, SET NULL or SET DEFAULT; ON DELETE SET DEFAULT | suppriméLes clés étrangères d'Oracle n'agissent qu'à la suppression, avec CASCADE ou SET NULL. | foreign_key_action_unsupported |
| foreign keys to tables the dump doesn't create | non crééeUne clé ne peut pas désigner une table absente. | foreign_key_skipped |
| names over 128 bytes | raccourciDepuis Oracle 12.2, les noms font 128 octets au plus, plus que les 63 de PostgreSQL. | name_shortened |
| partitioned tables | une seule table avec les lignes de chaque partitionLes lignes sont les mêmes ; partitionnez à nouveau la table dans Oracle si besoin. | partitions_dropped |
| schemas | un seul schéma, tables préfixées par le nom de leur schémaLe script se charge dans le schéma de l'utilisateur qui l'exécute ; les tables de schémas différents reçoivent donc le nom de leur schéma en préfixe, et celles de public gardent leur nom. | schemas_merged |
| views, functions, procedures, triggers, rules, policies, extensions and types | listés dans le rapportLeur code est écrit dans le langage de PostgreSQL ; ils sont donc listés pour que vous les recréiez. | not_converted_objects |
| dumps without CREATE TABLE (--data-only) | types déduits des valeursUn dump de données seules ne contient que des lignes, donc les types viennent des valeurs. | no_schema |
| expressions in INSERT values | NULLUn convertisseur qui n'exécute pas de base de données ne peut pas les évaluer. | expression_value |
| types IntoSQL doesn't know | CLOBLe texte accepte toute valeur ; changez le type après le chargement. | unknown_type |
| text that isn't valid in its encoding | les octets invalides deviennent le caractère de remplacementLe script est en UTF-8 et se charge avec NLS_LANG=.AL32UTF8. | invalid_utf8_replaced |
| text decoded twice (ñ) | copié tel quelLe dump contient exactement ces caractères, ils sont donc copiés ; le rapport les signale. | mojibake_suspected |
Charger le script dans Oracle
Le script crée les tables, copie les lignes, puis ajoute les clés, les index et les clés étrangères ; il se charge donc vite et s'arrête à la première erreur.
NLS_LANG=.AL32UTF8 sqlplus -S -L user@//localhost/mydb @postgresql.oracle.sqlLes clés étrangères sont ajoutées ENABLE NOVALIDATE, donc les nouvelles lignes sont vérifiées tout de suite, puis validées. Si une ancienne ligne pointe vers une ligne qui n'existe pas, le script indique quelle contrainte et la laisse non validée, au lieu d'échouer à mi-chemin.
Pour un gros dump, exécutez SQL*Plus sur la machine de la base de données. Le script valide toutes les 100 lignes. L'utilisateur qui l'exécute a besoin de CREATE TABLE, CREATE SEQUENCE, CREATE TRIGGER et CREATE PROCEDURE, et d'un quota sur son tablespace.
Faire le dump PostgreSQL pour un meilleur résultat
Le convertisseur lit ce que contient le dump. Ces options y gardent davantage de vos données :
- --format=plain, le format par défaut de pg_dump, écrit du SQL. Une archive custom ou directory (-Fc, -Fd) devient du SQL avec pg_restore -f dump.sql, sans serveur de base de données.
- --no-owner --no-privileges omettent les rôles et les droits, que le convertisseur ignore de toute façon ; le dump est donc plus petit.
- pg_dumpall met toutes les bases de données dans un seul fichier ; les tables de chacune sont préfixées par son nom, donc rien n'entre en collision.
Ce qui n'est pas converti
Les vues, vues matérialisées, fonctions, procédures, déclencheurs, règles, politiques de sécurité des lignes, extensions et types sont écrits dans le langage propre à PostgreSQL. Ils sont listés par nom dans le rapport, jamais supprimés en silence, pour que vous puissiez les réécrire pour Oracle.
Questions sur PostgreSQL vers Oracle
Pas de réponse ici ? Écrivez-nous depuis la page de contact, une personne vous répond. Page de contact
Mon dump est-il envoyé ?
Non. Le convertisseur s'exécute dans votre navigateur et lit le dump depuis votre disque par morceaux ; il ne quitte donc jamais votre ordinateur. L'application de bureau et la ligne de commande convertissent aussi sur votre ordinateur.
Quelle taille peut avoir le dump ?
Le convertisseur ne garde jamais tout le dump en mémoire : il lit le fichier deux fois, par plages, et écrit le script au fur et à mesure. Les dumps de plusieurs gigaoctets se convertissent dans le navigateur, dans l'application de bureau et avec la ligne de commande.
Lit-il les archives pg_dump -Fc ?
Pas directement : une archive custom est un format binaire. pg_restore -f dump.sql dump.backup la transforme en SQL texte que le convertisseur lit, sans se connecter à un serveur. Le convertisseur le signale quand il reçoit une archive.
De quelles versions de PostgreSQL le dump peut-il venir ?
Les dumps écrits par pg_dump et pg_dumpall depuis PostgreSQL 10, avec des lignes COPY ou INSERT. Chaque dump de test vient de PostgreSQL 13 et 18 et est comparé à sa source cellule par cellule.
Les valeurs serial et identity sont-elles conservées ?
Oui. La colonne serial ou identity de chaque table devient une colonne d'identité, chaque ligne garde son identifiant, et le prochain identifiant reprend après le plus élevé. Les séquences qui n'appartiennent à aucune colonne deviennent des séquences Oracle à la valeur atteinte.
Que deviennent les tableaux, les enums et les autres types PostgreSQL ?
Les tableaux deviennent des tableaux JSON, les booléens NUMBER(1), les timestamps avec fuseau horaire TIMESTAMP WITH TIME ZONE, et le texte VARCHAR2 si ses valeurs y tiennent, sinon CLOB. Les chaînes vides deviennent NULL, car Oracle les stocke ainsi, et le rapport les compte.
Et les fonctions, les déclencheurs et les vues ?
Ils sont listés par nom dans le rapport et ne sont pas convertis, car chaque base de données les écrit dans son propre langage.
Sur quelles versions de Oracle le script se charge-t-il ?
Oracle 19c et versions ultérieures. Chaque dump de test se charge sur 21c et 23ai, et s'arrête à la première erreur en cas de problème.