Converta PostgreSQL para Oracle
Solte um dump simples do pg_dump ou pg_dumpall, em .sql ou .sql.gz, e baixe um script do Oracle com as mesmas tabelas, chaves, índices e dados. Cada valor que o Oracle não aceita como está, e cada objeto que ele não traz, aparece no relatório.
18 linhas convertidas, 2 correções, nenhuma rejeitada
Tabelas em shop_2024_postgres.sqlDump do PostgreSQL 18.6 · gerado por pg_dump · utf8mb4
customersescrita como CUSTOMERS8 linhas1
8 colunas 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 ordersescrita como ORDERS10 linhas1
10 colunas 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)
Não convertidos
- Visão paid_orders
Cada banco de dados escreve estes objetos na sua própria linguagem. Reescreva-os para o 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;
Correções
- "tags" de customers é text[], que o Oracle não tem, então vira CLOB com os mesmos valores.
- "delivery" de orders é interval, que o Oracle não tem, então vira VARCHAR2(100 CHAR) com os mesmos valores.
Para revisar
- 1 visão, gatilho, rotina ou evento não foi convertido; o relatório lista cada um. Recrie-os manualmente no Oracle.
- O valor padrão de "tags" em customers não pode ser escrito para o Oracle (a value its type can't hold), então a coluna fica sem valor padrão. Adicione o valor padrão manualmente se novas linhas precisarem dele.
- O índice idx_orders_details de orders usa uma expressão que não pode ser escrita para o Oracle (a gin index), então não é criado. Crie o índice manualmente se as consultas precisarem dele.
- 1 valor em "shipped_at" de orders é uma data que o Oracle não consegue guardar, como infinity, então ficou NULL. O Oracle guarda os anos de 1 a 9999 sem infinito; substitua essas datas na origem se elas forem importantes.
Seu arquivo é convertido neste computador e nunca é enviado.
Outros exemplos:
O que muda do PostgreSQL para o Oracle
Cada linha é uma regra que o conversor aplica, com a linha do relatório que ele escreve quando a regra muda algo nos seus dados.
boolean
NUMBER(1) com um CHECK de 0 e 1
O Oracle 21c não tem tipo de coluna booleano; 1 e 0 mantêm true e false em todas as versões.
varchar(n), char(n)
VARCHAR2(n CHAR), contando caracteres
Os tamanhos contam caracteres como os do PostgreSQL.
text
VARCHAR2(4000 CHAR) quando todo valor cabe em 4000 bytes (750 caracteres em uma chave), senão CLOB
Um VARCHAR2 comporta 4000 bytes por padrão e o Oracle não indexa um CLOB, então o texto fica VARCHAR2 quando seus valores cabem e vira CLOB quando não cabem.
Linha do relatório: text_as_lob
empty strings and empty bytea values
NULL
O Oracle guarda uma string vazia como NULL, então um valor que era ' é lido de volta como NULL. O relatório os conta por coluna.
Linha do relatório: empty_string_null
citext
comparação exata
O Oracle compara texto de forma exata, a menos que se indique outra coisa, como o text do PostgreSQL; as colunas citext deixam de ignorar maiúsculas.
Linha do relatório: case_insensitive_collation
numeric(p,s) past 38 digits; numeric without a precision
NUMBER quando seus valores cabem em 38 dígitos, senão VARCHAR2
O NUMBER do Oracle comporta 38 dígitos em qualquer escala. Números mais largos ficam como texto, para que cada dígito continue exato.
Linha do relatório: decimal_as_text
money
NUMBER(19,2)
O Oracle não tem tipo money. O valor fica como número, sem o símbolo de moeda que a localidade do servidor adicionou.
Linha do relatório: type_replaced
interval
VARCHAR2(100 CHAR) com o texto do PostgreSQL
Um interval mistura meses com dias e segundos, que o Oracle guarda em dois tipos diferentes; o texto mantém cada parte.
Linha do relatório: type_replaced
arrays (integer[], text[]...)
CLOB com um array JSON
O Oracle não tem arrays. Um array JSON mantém cada item e NULL, e o JSON_TABLE o lê.
Linha do relatório: type_replaced
ranges, geometric types, tsvector, xml, hstore, PostGIS, composite and extension types
VARCHAR2 ou CLOB com o texto do PostgreSQL
O Oracle não tem um tipo equivalente, então cada valor mantém o texto que o PostgreSQL escreveu.
Linha do relatório: type_replaced
json, jsonb
CLOB
As funções JSON do Oracle leem JSON a partir de texto em todas as versões, e um CLOB mantém valores de qualquer tamanho.
timestamp
TIMESTAMP
Um timestamp sem fuso horário é a leitura de um relógio de parede, como o TIMESTAMP do Oracle. date vira DATE, que também guarda uma hora do dia, que fica em meia-noite.
timestamp with time zone
TIMESTAMP WITH TIME ZONE em UTC
Cada valor é escrito em UTC, o mesmo instante qualquer que seja o fuso que o dump escreveu.
time
INTERVAL DAY TO SECOND desde a meia-noite
O Oracle não tem tipo de hora do dia; um intervalo a partir da meia-noite guarda todo valor, incluindo 24:00:00.
infinity, -infinity, BC dates and years past 9999
NULL
O script escreve os anos de 1 a 9999, e o Oracle não tem infinito. Esses valores viram NULL e o relatório os conta.
Linha do relatório: date_out_of_range
NaN and Infinity in numeric
NULL
O NUMBER do Oracle não tem NaN nem Infinity, então esses valores viram NULL e o relatório os conta.
Linha do relatório: invalid_value_null
NOT NULL columns with values that become NULL
NOT NULL removido
Uma coluna cujos valores tiveram de virar NULL não pode manter NOT NULL, senão a carga pararia.
Linha do relatório: not_null_relaxed
enum types
VARCHAR2 com um CHECK dos rótulos
Texto com um CHECK mantém os mesmos valores.
enum columns holding values outside their labels
VARCHAR2 sem CHECK
Valores que os rótulos não nomeiam violariam o CHECK, então ele não é criado.
Linha do relatório: enum_check_skipped
uuid, inet, cidr, macaddr
VARCHAR2
O Oracle não tem esses tipos; o texto mantém cada valor.
bytea
BLOB
Bytes continuam bytes.
bit(n), bit varying
NUMBER com o valor dos bits até 63 bits; dígitos em texto acima disso
Strings de bits são guardadas como o número que seus bits formam.
oid, xid, cid
NUMBER(10) com um CHECK de que não é negativo
NUMBER comporta todo valor.
serial, identity
GENERATED BY DEFAULT ON NULL AS IDENTITY, continuando depois do maior id
Os ids copiados carregam como estão, e o contador continua depois do maior deles ou do próximo valor da sequência, o que for maior.
a second serial or identity column in a table
um inteiro simples
O Oracle conta uma coluna identity por tabela; qualquer outra mantém seus valores sem contador.
Linha do relatório: counter_dropped
sequences not owned by a column
CREATE SEQUENCE, no valor que tinha alcançado
As sequências do Oracle funcionam do mesmo jeito.
generated columns
colunas virtuais
O Oracle só calcula colunas geradas na leitura; colunas STORED viram virtuais com os mesmos valores.
Linha do relatório: stored_as_virtual
generated columns with functions Oracle lacks
uma coluna simples com os valores
Uma fórmula que não pode ser escrita para o Oracle deixa uma coluna simples com os valores do dump e uma linha no relatório.
Linha do relatório: generated_not_translated
defaults with functions Oracle lacks, and defaults from a sequence
sem valor padrão
Um valor padrão que não pode ser escrito para o Oracle fica de fora e é informado.
Linha do relatório: default_not_translated
CHECK constraints
traduzido, dentro do CREATE TABLE
Os CHECKs são testados à medida que cada linha carrega.
CHECK constraints with functions Oracle lacks
não criado
Um CHECK que não pode ser escrito para o Oracle fica de fora e é informado.
Linha do relatório: check_skipped
CHECK ... NOT VALID
não criado
O PostgreSQL nunca verificou as linhas que já existiam, então elas podem violá-lo.
Linha do relatório: check_not_valid
gin, gist, brin and hash indexes, partial and expression indexes, and indexes on CLOB columns
não criado
O IntoSQL escreve índices b-tree sobre colunas para o Oracle, que não indexa um CLOB; crie os outros de novo depois da carga.
Linha do relatório: index_skipped
foreign keys
depois dos dados ENABLE NOVALIDATE, e então validada com um aviso sobre órfãos
A chave verifica as linhas novas na hora e é validada por último; linhas sem pai a deixam sem validação com um aviso, em vez de parar a carga.
ON UPDATE CASCADE, SET NULL or SET DEFAULT; ON DELETE SET DEFAULT
descartado
As chaves estrangeiras do Oracle só agem na exclusão, com CASCADE ou SET NULL.
Linha do relatório: foreign_key_action_unsupported
foreign keys to tables the dump doesn't create
não criada
Uma chave não pode apontar para uma tabela que não existe.
Linha do relatório: foreign_key_skipped
names over 128 bytes
encurtados
Os nomes no Oracle 12.2 e posteriores comportam 128 bytes, mais que os 63 do PostgreSQL.
Linha do relatório: name_shortened
partitioned tables
uma tabela com as linhas de todas as partições
As linhas são as mesmas; particione a tabela de novo no Oracle se precisar.
Linha do relatório: partitions_dropped
schemas
um schema, tabelas com o nome do schema na frente
O script carrega no schema do usuário que o executa, então as tabelas de schemas diferentes recebem o nome do seu schema na frente; as tabelas de public mantêm seus nomes.
Linha do relatório: schemas_merged
views, functions, procedures, triggers, rules, policies, extensions and types
listados no relatório
O código deles é escrito na linguagem do PostgreSQL, então eles são listados para você recriá-los.
Linha do relatório: not_converted_objects
dumps without CREATE TABLE (--data-only)
tipos lidos dos valores
Um dump só de dados tem apenas linhas, então os tipos vêm dos valores.
Linha do relatório: no_schema
expressions in INSERT values
NULL
Um conversor que não executa um banco de dados não consegue avaliá-las.
Linha do relatório: expression_value
types IntoSQL doesn't know
CLOB
O texto aceita qualquer valor; mude o tipo depois da carga.
Linha do relatório: unknown_type
text that isn't valid in its encoding
os bytes inválidos como o caractere de substituição
O script é UTF-8 e carrega com NLS_LANG=.AL32UTF8.
Linha do relatório: invalid_utf8_replaced
text decoded twice (ñ)
copiado como está
O dump contém exatamente esses caracteres, então eles são copiados; o relatório os aponta.
Linha do relatório: mojibake_suspected
| No PostgreSQL | No Oracle | Linha do relatório |
|---|---|---|
| boolean | NUMBER(1) com um CHECK de 0 e 1O Oracle 21c não tem tipo de coluna booleano; 1 e 0 mantêm true e false em todas as versões. | |
| varchar(n), char(n) | VARCHAR2(n CHAR), contando caracteresOs tamanhos contam caracteres como os do PostgreSQL. | |
| text | VARCHAR2(4000 CHAR) quando todo valor cabe em 4000 bytes (750 caracteres em uma chave), senão CLOBUm VARCHAR2 comporta 4000 bytes por padrão e o Oracle não indexa um CLOB, então o texto fica VARCHAR2 quando seus valores cabem e vira CLOB quando não cabem. | text_as_lob |
| empty strings and empty bytea values | NULLO Oracle guarda uma string vazia como NULL, então um valor que era ' é lido de volta como NULL. O relatório os conta por coluna. | empty_string_null |
| citext | comparação exataO Oracle compara texto de forma exata, a menos que se indique outra coisa, como o text do PostgreSQL; as colunas citext deixam de ignorar maiúsculas. | case_insensitive_collation |
| numeric(p,s) past 38 digits; numeric without a precision | NUMBER quando seus valores cabem em 38 dígitos, senão VARCHAR2O NUMBER do Oracle comporta 38 dígitos em qualquer escala. Números mais largos ficam como texto, para que cada dígito continue exato. | decimal_as_text |
| money | NUMBER(19,2)O Oracle não tem tipo money. O valor fica como número, sem o símbolo de moeda que a localidade do servidor adicionou. | type_replaced |
| interval | VARCHAR2(100 CHAR) com o texto do PostgreSQLUm interval mistura meses com dias e segundos, que o Oracle guarda em dois tipos diferentes; o texto mantém cada parte. | type_replaced |
| arrays (integer[], text[]...) | CLOB com um array JSONO Oracle não tem arrays. Um array JSON mantém cada item e NULL, e o JSON_TABLE o lê. | type_replaced |
| ranges, geometric types, tsvector, xml, hstore, PostGIS, composite and extension types | VARCHAR2 ou CLOB com o texto do PostgreSQLO Oracle não tem um tipo equivalente, então cada valor mantém o texto que o PostgreSQL escreveu. | type_replaced |
| json, jsonb | CLOBAs funções JSON do Oracle leem JSON a partir de texto em todas as versões, e um CLOB mantém valores de qualquer tamanho. | |
| timestamp | TIMESTAMPUm timestamp sem fuso horário é a leitura de um relógio de parede, como o TIMESTAMP do Oracle. date vira DATE, que também guarda uma hora do dia, que fica em meia-noite. | |
| timestamp with time zone | TIMESTAMP WITH TIME ZONE em UTCCada valor é escrito em UTC, o mesmo instante qualquer que seja o fuso que o dump escreveu. | |
| time | INTERVAL DAY TO SECOND desde a meia-noiteO Oracle não tem tipo de hora do dia; um intervalo a partir da meia-noite guarda todo valor, incluindo 24:00:00. | |
| infinity, -infinity, BC dates and years past 9999 | NULLO script escreve os anos de 1 a 9999, e o Oracle não tem infinito. Esses valores viram NULL e o relatório os conta. | date_out_of_range |
| NaN and Infinity in numeric | NULLO NUMBER do Oracle não tem NaN nem Infinity, então esses valores viram NULL e o relatório os conta. | invalid_value_null |
| NOT NULL columns with values that become NULL | NOT NULL removidoUma coluna cujos valores tiveram de virar NULL não pode manter NOT NULL, senão a carga pararia. | not_null_relaxed |
| enum types | VARCHAR2 com um CHECK dos rótulosTexto com um CHECK mantém os mesmos valores. | |
| enum columns holding values outside their labels | VARCHAR2 sem CHECKValores que os rótulos não nomeiam violariam o CHECK, então ele não é criado. | enum_check_skipped |
| uuid, inet, cidr, macaddr | VARCHAR2O Oracle não tem esses tipos; o texto mantém cada valor. | |
| bytea | BLOBBytes continuam bytes. | |
| bit(n), bit varying | NUMBER com o valor dos bits até 63 bits; dígitos em texto acima dissoStrings de bits são guardadas como o número que seus bits formam. | |
| oid, xid, cid | NUMBER(10) com um CHECK de que não é negativoNUMBER comporta todo valor. | |
| serial, identity | GENERATED BY DEFAULT ON NULL AS IDENTITY, continuando depois do maior idOs ids copiados carregam como estão, e o contador continua depois do maior deles ou do próximo valor da sequência, o que for maior. | |
| a second serial or identity column in a table | um inteiro simplesO Oracle conta uma coluna identity por tabela; qualquer outra mantém seus valores sem contador. | counter_dropped |
| sequences not owned by a column | CREATE SEQUENCE, no valor que tinha alcançadoAs sequências do Oracle funcionam do mesmo jeito. | |
| generated columns | colunas virtuaisO Oracle só calcula colunas geradas na leitura; colunas STORED viram virtuais com os mesmos valores. | stored_as_virtual |
| generated columns with functions Oracle lacks | uma coluna simples com os valoresUma fórmula que não pode ser escrita para o Oracle deixa uma coluna simples com os valores do dump e uma linha no relatório. | generated_not_translated |
| defaults with functions Oracle lacks, and defaults from a sequence | sem valor padrãoUm valor padrão que não pode ser escrito para o Oracle fica de fora e é informado. | default_not_translated |
| CHECK constraints | traduzido, dentro do CREATE TABLEOs CHECKs são testados à medida que cada linha carrega. | |
| CHECK constraints with functions Oracle lacks | não criadoUm CHECK que não pode ser escrito para o Oracle fica de fora e é informado. | check_skipped |
| CHECK ... NOT VALID | não criadoO PostgreSQL nunca verificou as linhas que já existiam, então elas podem violá-lo. | check_not_valid |
| gin, gist, brin and hash indexes, partial and expression indexes, and indexes on CLOB columns | não criadoO IntoSQL escreve índices b-tree sobre colunas para o Oracle, que não indexa um CLOB; crie os outros de novo depois da carga. | index_skipped |
| foreign keys | depois dos dados ENABLE NOVALIDATE, e então validada com um aviso sobre órfãosA chave verifica as linhas novas na hora e é validada por último; linhas sem pai a deixam sem validação com um aviso, em vez de parar a carga. | |
| ON UPDATE CASCADE, SET NULL or SET DEFAULT; ON DELETE SET DEFAULT | descartadoAs chaves estrangeiras do Oracle só agem na exclusão, com CASCADE ou SET NULL. | foreign_key_action_unsupported |
| foreign keys to tables the dump doesn't create | não criadaUma chave não pode apontar para uma tabela que não existe. | foreign_key_skipped |
| names over 128 bytes | encurtadosOs nomes no Oracle 12.2 e posteriores comportam 128 bytes, mais que os 63 do PostgreSQL. | name_shortened |
| partitioned tables | uma tabela com as linhas de todas as partiçõesAs linhas são as mesmas; particione a tabela de novo no Oracle se precisar. | partitions_dropped |
| schemas | um schema, tabelas com o nome do schema na frenteO script carrega no schema do usuário que o executa, então as tabelas de schemas diferentes recebem o nome do seu schema na frente; as tabelas de public mantêm seus nomes. | schemas_merged |
| views, functions, procedures, triggers, rules, policies, extensions and types | listados no relatórioO código deles é escrito na linguagem do PostgreSQL, então eles são listados para você recriá-los. | not_converted_objects |
| dumps without CREATE TABLE (--data-only) | tipos lidos dos valoresUm dump só de dados tem apenas linhas, então os tipos vêm dos valores. | no_schema |
| expressions in INSERT values | NULLUm conversor que não executa um banco de dados não consegue avaliá-las. | expression_value |
| types IntoSQL doesn't know | CLOBO texto aceita qualquer valor; mude o tipo depois da carga. | unknown_type |
| text that isn't valid in its encoding | os bytes inválidos como o caractere de substituiçãoO script é UTF-8 e carrega com NLS_LANG=.AL32UTF8. | invalid_utf8_replaced |
| text decoded twice (ñ) | copiado como estáO dump contém exatamente esses caracteres, então eles são copiados; o relatório os aponta. | mojibake_suspected |
Carregue o script no Oracle
O script cria as tabelas, copia as linhas e depois adiciona chaves, índices e chaves estrangeiras, então carrega rápido e para no primeiro erro.
NLS_LANG=.AL32UTF8 sqlplus -S -L user@//localhost/mydb @postgresql.oracle.sqlAs chaves estrangeiras são adicionadas ENABLE NOVALIDATE, então as linhas novas são verificadas na hora, e depois validadas. Se uma linha antiga aponta para uma linha que não existe, o script informa qual restrição e a deixa sem validação, em vez de falhar no meio do caminho.
Para um dump grande, execute o SQL*Plus na mesma máquina do banco de dados. O script faz commit a cada 100 linhas. O usuário que o executa precisa de CREATE TABLE, CREATE SEQUENCE, CREATE TRIGGER e CREATE PROCEDURE, e de cota no seu tablespace.
Gere o dump do PostgreSQL para o melhor resultado
O conversor lê o que o dump contém. Estas opções mantêm mais dos seus dados nele:
- --format=plain, o padrão do pg_dump, escreve SQL. Um arquivo custom ou de diretório (-Fc, -Fd) vira SQL com pg_restore -f dump.sql, sem servidor de banco de dados.
- --no-owner --no-privileges deixam de fora papéis e permissões, que o conversor ignora de qualquer forma, então o dump fica menor.
- O pg_dumpall gera o dump de todos os bancos de dados em um arquivo; as tabelas de cada um recebem o nome dele, então nada colide.
O que não é convertido
Visões, visões materializadas, funções, procedimentos, gatilhos, regras, políticas de segurança de linha, extensões e tipos são escritos na linguagem própria do PostgreSQL. Eles aparecem no relatório pelo nome, nunca são descartados em silêncio, para que você possa reescrevê-los para o Oracle.
Perguntas sobre PostgreSQL para Oracle
Não encontrou a resposta? Escreva para nós pela página de contato e uma pessoa responde. Página de contato
Meu dump é enviado?
Não. O conversor roda no seu navegador e lê o dump do seu disco em partes, então ele nunca sai do seu computador. O app para desktop e a linha de comando também convertem no seu computador.
Qual o tamanho máximo do dump?
O conversor nunca guarda o dump inteiro na memória: ele lê o arquivo duas vezes, por intervalos, e escreve o script à medida que avança. Dumps de vários gigabytes são convertidos no navegador, no app para desktop e com a linha de comando.
Ele lê arquivos do pg_dump -Fc?
Não diretamente: um arquivo custom é um formato binário. pg_restore -f dump.sql dump.backup o transforma no SQL simples que o conversor lê, sem se conectar a um servidor. O conversor avisa quando recebe um arquivo desses.
De quais versões do PostgreSQL o dump pode vir?
Dumps escritos pelo pg_dump e pg_dumpall do PostgreSQL 10 em diante, com linhas em COPY ou INSERT. Cada dump de teste vem do PostgreSQL 13 e 18 e é comparado com a origem célula por célula.
Os valores serial e identity são mantidos?
Sim. A coluna serial ou identity de cada tabela vira uma coluna identity, cada linha mantém seu id, e o próximo id continua depois do maior. Sequências que não pertencem a uma coluna viram sequências do Oracle no valor que tinham alcançado.
O que acontece com arrays, enums e outros tipos do PostgreSQL?
Arrays viram arrays JSON, booleanos NUMBER(1), timestamps com fuso horário TIMESTAMP WITH TIME ZONE, e texto VARCHAR2 quando seus valores cabem, senão CLOB. Strings vazias viram NULL, pois é assim que o Oracle as guarda, e o relatório as conta.
E as funções, gatilhos e visões?
Eles aparecem no relatório pelo nome e não são convertidos, porque cada banco de dados os escreve na sua própria linguagem.
Em quais versões do Oracle o script carrega?
Oracle 19c e posteriores. Cada dump de teste carrega no 21c e no 23ai e para no primeiro erro se algo der errado.