Aller au contenu
Accueil IntoSQL

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.

Votre fichier
sqlshop_2024_postgres.sqlFichier d'exemple, 6,2 ko
Votre base de données

18 lignes converties, 2 corrections, aucune rejetée

Afficher

Tables de shop_2024_postgres.sqlDump PostgreSQL 18.6 · écrit par pg_dump · utf8mb4

  • customersécrite sous le nom CUSTOMERS8 lignes1
    8 colonnes
    IDNUMBER(10) not null
    NAMEVARCHAR2(4000 CHAR) not null
    EMAILVARCHAR2(750 CHAR) not null
    COUNTRYVARCHAR2(2 CHAR) not null
    IS_ACTIVENUMBER(1) not null
    TAGSCLOB not null
    SIGNED_UPDATE
    CREATED_ATTIMESTAMP(6) WITH TIME ZONE not null
  • ordersécrite sous le nom ORDERS10 lignes1
    10 colonnes
    IDNUMBER(10) not null
    CUSTOMER_IDNUMBER(10) not null
    STATUSVARCHAR2(9 CHAR) not null
    TOTALNUMBER(10,2) not null
    ITEMSNUMBER(5) not null
    ORDERED_ATTIMESTAMP(6) not null
    SHIPPED_ATTIMESTAMP(6)
    NOTEVARCHAR2(4000 CHAR)
    DETAILSCLOB
    DELIVERYVARCHAR2(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

  1. "tags" de customers est text[], que Oracle n'a pas ; elle est donc en CLOB avec les mêmes valeurs.
  2. "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 PostgreSQLDans OracleLigne du rapport
booleanNUMBER(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.
textVARCHAR2(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 valuesNULLOracle 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
citextcomparaison 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 precisionNUMBER 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
moneyNUMBER(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
intervalVARCHAR2(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 typesVARCHAR2 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, jsonbCLOBLes fonctions JSON d'Oracle lisent le JSON depuis du texte sur chaque version, et un CLOB garde des valeurs de toute taille.
timestampTIMESTAMPUn 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 zoneTIMESTAMP WITH TIME ZONE en UTCChaque valeur est écrite en UTC, le même instant quel que soit le décalage écrit par le dump.
timeINTERVAL 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 9999NULLLe 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 numericNULLLe 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 NULLNOT 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 typesVARCHAR2 avec un CHECK de ses libellésUn texte avec une contrainte CHECK garde les mêmes valeurs.
enum columns holding values outside their labelsVARCHAR2 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, macaddrVARCHAR2Oracle n'a pas ces types ; le texte garde chaque valeur.
byteaBLOBLes octets restent des octets.
bit(n), bit varyingNUMBER 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, cidNUMBER(10) avec un CHECK qui interdit les négatifsNUMBER contient chaque valeur.
serial, identityGENERATED 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 tableun entier simpleOracle compte une colonne d'identité par table ; toute autre garde ses valeurs sans compteur.counter_dropped
sequences not owned by a columnCREATE SEQUENCE, à la valeur atteinteLes séquences Oracle fonctionnent de la même façon.
generated columnscolonnes 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 lacksune 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 sequenceaucune 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 constraintstraduites, en ligne dans CREATE TABLELes CHECK en ligne sont vérifiés au chargement de chaque ligne.
CHECK constraints with functions Oracle lacksnon crééeUne contrainte CHECK qui ne peut pas être écrite pour Oracle est omise et signalée.check_skipped
CHECK ... NOT VALIDnon 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 columnsnon 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 keysaprè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 DEFAULTsupprimé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 createnon crééeUne clé ne peut pas désigner une table absente.foreign_key_skipped
names over 128 bytesraccourciDepuis Oracle 12.2, les noms font 128 octets au plus, plus que les 63 de PostgreSQL.name_shortened
partitioned tablesune seule table avec les lignes de chaque partitionLes lignes sont les mêmes ; partitionnez à nouveau la table dans Oracle si besoin.partitions_dropped
schemasun 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 typeslisté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 valuesNULLUn convertisseur qui n'exécute pas de base de données ne peut pas les évaluer.expression_value
types IntoSQL doesn't knowCLOBLe texte accepte toute valeur ; changez le type après le chargement.unknown_type
text that isn't valid in its encodingles 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.sql

Les 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.