Todos os produtos
Search
Central de documentação

AnalyticDB:Migração de aplicações Oracle para o AnalyticDB for PostgreSQL

Última atualização: Sep 15, 2026

O AnalyticDB for PostgreSQL é compatível com a sintaxe Oracle. Este tópico aborda tudo o que você precisa para migrar uma aplicação Oracle: como avaliar o escopo da migração com o Ora2Pg, configure o modo de compatibilidade, usar funções compatíveis com Oracle por meio da extensão Orafce e converter construções PL/SQL para PL/pgSQL.

Principais diferenças em resumo

Analise estas diferenças de alto nível antes de se aprofundar nas regras específicas de conversão. Elas afetam os problemas de migração mais comuns:

Área

Comportamento do Oracle

Comportamento do AnalyticDB for PostgreSQL

Concatenação com NULL

`'abc'

NULL` retorna 'abc'

Retorna NULL (padrão do PostgreSQL); use o modo de compatibilidade Oracle para alterar

ROWNUM

Pseudocoluna

Use LIMIT (limite de linhas) ou row_number() over() (numeração de linhas)

Tabela DUAL

Tabela virtual exigida por algumas consultas

Remova FROM DUAL ou crie uma tabela dual

CONNECT BY

Consulta hierárquica nativa

Sem equivalente direto; reescreva como uma função PL/pgSQL

Pacotes

CREATE PACKAGE

Sem suporte a pacotes; converta para schemas + funções

PRAGMA

Diretivas de compilação

Não suportado; exclua todas as instruções PRAGMA

Controle de transação em funções

BEGIN, COMMIT, ROLLBACK no corpo da função

Não suportado; mova o controle de transação para fora das funções

OUTER JOIN (+)

WHERE a.id = b.id(+)

Use a sintaxe padrão LEFT JOIN / RIGHT JOIN

FOR LOOP REVERSE

Conta regressivamente do 2º número para o 1º

Conta regressivamente do 1º número para o 2º; inverta os limites

Funções com argumentos RETURN + OUT

Ambos permitidos simultaneamente

Não suportado; converta RETURN em um argumento OUT adicional

Arrays associativos

Suportado

Sem equivalente; requer redesenho

Funções definidas pelo usuário em SELECT

Podem conter instruções SQL

Não podem conter instruções SQL; reescreva como subconsultas

Automatize a conversão de schema com o Ora2Pg

O Ora2Pg é uma ferramenta open source que converte instruções DDL do Oracle para tabelas, views e pacotes em sintaxe compatível com PostgreSQL.

Avalie o escopo da migração primeiro

Antes de executar uma conversão completa, gere um relatório de avaliação de migração para identificar quais objetos não podem ser convertidos automaticamente:

ora2pg -t SHOW_REPORT

O relatório lista todos os objetos do banco de dados, suas quantidades e quais exigem trabalho manual. Utilize-o para estimar o escopo antes de iniciar a migração.

O que o Ora2Pg trata automaticamente

  • DDL de tabelas e views

  • Estrutura de pacotes (convertida para schemas)

  • Sintaxe básica de PL/SQL para PL/pgSQL

O que exige correção manual

Após executar o Ora2Pg, revise e corrija a saída para estes problemas antes de executá-la na sua instância:

  • Os scripts convertidos podem ter como alvo uma versão de sintaxe do PostgreSQL posterior à versão secundária do mecanismo da sua instância

  • As regras de conversão podem estar incompletas ou incorretas para construções complexas de PL/SQL

  • Todas as construções listadas na seção Conversão de PL/SQL abaixo

Configure o modo de compatibilidade

O parâmetro adb_compatibility_mode controla como o AnalyticDB for PostgreSQL lida com sintaxes que se comportam de maneira diferente entre bancos de dados.

Parâmetro

Valores válidos

Padrão

adb_compatibility_mode

postgres, oracle

postgres

Verifique o valor atual antes de fazer alterações:

SHOW adb_compatibility_mode;

Para alterar o parâmetro no nível da instância, envie um ticket.Envie um ticket

Concatenação de strings com NULL

A expressão 'abc' || NULL comporta-se de forma diferente dependendo do modo de compatibilidade:

  • Modo PostgreSQL (padrão): retorna NULL

  • Modo Oracle: retorna 'abc' (o Oracle trata NULL como uma string vazia)

Importante

Antes de usar concatenação de strings no modo de compatibilidade Oracle, desative o mecanismo Laser:

SET laser.enable = off;

Inferência de tipo de string constante

O AnalyticDB for PostgreSQL reconhece automaticamente strings constantes como o tipo TEXT (não UNKNOWN) em instruções como CREATE TABLE AS SELECT. Esse comportamento não exige nenhuma configuração de modo de compatibilidade.

Use a extensão Orafce

A extensão Orafce fornece funções compatíveis com Oracle que você pode usar no AnalyticDB for PostgreSQL sem modificá-las ou convertê-las.

Instale o Orafce uma vez por banco de dados:

CREATE EXTENSION orafce;

O Orafce também adiciona suporte ao tipo de dados VARCHAR2 do Oracle.

Funções de data e hora

Função

Descrição

Exemplo

add_months(day date, value int)

Adiciona meses a uma data

SELECT add_months(current_date, 2);2019-08-31

last_day(value date)

Retorna o último dia do mês

SELECT last_day('2018-06-01');2018-06-30

next_day(value date, weekday text)

Retorna a próxima ocorrência de um dia da semana

SELECT next_day(current_date, 'FRIDAY');

next_day(value date, weekday integer)

Dia da semana como inteiro: 1=Domingo, 2=Segunda, ..., 7=Sábado

SELECT next_day('2019-06-22', 1);2019-06-23

months_between(date1 date, date2 date)

Retorna o número de meses entre duas datas (positivo se date1 > date2)

SELECT months_between('2019-01-01', '2018-11-01');2

Truncamento e arredondamento de timestamp

Função

Descrição

Exemplo

trunc(value timestamp with time zone, fmt text)

Trunca para ano (Y), trimestre (Q), mês, dia, semana, hora, minuto ou segundo

SELECT TRUNC(current_date, 'Q');2019-04-01

trunc(value timestamp with time zone)

Trunca horas, minutos e segundos

SELECT TRUNC('2019-12-11'::timestamp);2019-12-11 00:00:00+08

trunc(value date)

Trunca uma data

SELECT TRUNC('2019-12-11'::timestamp, 'Y');2019-01-01 00:00:00+08

round(value timestamp with time zone, fmt text)

Arredonda para o valor mais próximo por unidade (semana, dia, etc.)

SELECT round('2018-10-06 13:11:11'::timestamp, 'YEAR');2019-01-01 00:00:00+08

round(value timestamp with time zone)

Arredonda para o dia mais próximo

SELECT round('2018-10-06 13:11:11'::timestamp);2018-10-07 00:00:00+08

round(value date, fmt text)

Retorna uma data arredondada

SELECT round(TO_DATE('27-OCT-00','DD-MON-YY'), 'YEAR');2001-01-01

round(value date)

Retorna uma data arredondada

SELECT round(TO_DATE('27-FEB-00','DD-MON-YY'));2000-02-27

Funções de tratamento de NULL

Função

Descrição

Exemplo

nvl(anyelement, anyelement)

Retorna o segundo argumento se o primeiro for nulo; caso contrário, retorna o primeiro. Ambos os argumentos devem ser do mesmo tipo.

SELECT nvl(null, 1);1

nvl2(anyelement, anyelement, anyelement)

Retorna o terceiro argumento se o primeiro for nulo; caso contrário, retorna o segundo

SELECT nvl2(null, 1, 2);2

lnnvl(bool)

Retorna true se o argumento for nulo ou falso; retorna false se o argumento for verdadeiro

SELECT lnnvl(null);t

nanvl(float4, float4) / nanvl(numeric, numeric)

Retorna o segundo argumento se o primeiro for NaN; caso contrário, retorna o primeiro

SELECT nanvl('NaN', 1.1);1.1

Funções de string

Função

Descrição

instr(str text, patt text, start int, nth int)

Retorna a posição da enésima ocorrência de patt em str começando em start; retorna 0 se não for encontrado

instr(str text, patt text, start int)

Retorna a posição da primeira ocorrência a partir de start

instr(str text, patt text)

Pesquisa desde o início da string

substr(str text, start int)

Retorna a substring começando em start

substr(str text, start int, len int)

Retorna uma substring; len deve ser >= start e <= o comprimento da string

pg_catalog.substrb(varchar2, integer, integer)

Retorna uma substring de uma string VARCHAR2 por posição inicial e final

pg_catalog.substrb(varchar2, integer)

Retorna uma substring de uma string VARCHAR2 de uma posição até o fim

pg_catalog.lengthb(varchar2)

Retorna a contagem de bytes de uma string VARCHAR2; retorna null para entrada nula, 0 para string vazia

lpad(string char, length int, fill char)

Preenche uma string à esquerda até o comprimento especificado.

Nota

O PostgreSQL remove espaços à direita de valores CHAR; o Oracle não.

lpad(string char, length int)

Preenche à esquerda com espaços

concat(text, text)

Une duas strings

concat(text/anyarray, text/anyarray)

Une valores do mesmo tipo ou de tipos diferentes

nlssort(text, text)

Ordena dados em uma ordem específica de localidade (ex.: 'en_US.UTF-8', 'C')

plvstr.rvrs(str text, start int, end int)

Inverte caracteres de start até end

plvstr.rvrs(str text, start int)

Inverte de start até o final da string

plvstr.rvrs(str text)

Inverte a string inteira

Funções de agregação

Função

Descrição

Exemplo

listagg(text)

Concatena valores em uma única string

SELECT listagg(t) FROM (VALUES('abc'), ('def')) as l(t);abcdef

listagg(text, text)

Concatena com um separador

SELECT listagg(t, '.') FROM (VALUES('abc'), ('def')) as l(t);abc.def

bitand(bigint, bigint)

AND bit a bit em dois valores inteiros

SELECT bitand(2, 6);2

Funções de diagnóstico

Função

Descrição

Exemplo

dump("any")

Retorna código de tipo, comprimento em bytes e representação interna

SELECT dump('adb4pg');Typ=705 Len=7: 97,100,98,52,112,103,0

dump("any", integer)

Especifica o formato de saída: 10 para decimal, 16 para hexadecimal. Passar 2 causa um erro.

SELECT dump('adb4pg', 16);

Funções de expressão regular

Todas as funções regex abaixo aceitam um argumento flags com estes valores: 'i' (insensível a maiúsculas/minúsculas), 'c' (sensível a maiúsculas/minúsculas), 'n' (. corresponde a nova linha), 'm' (multilinha), 'x' (ignorar espaços em branco).

Função

Descrição

regexp_count(string, pattern)

Retorna o número de vezes que pattern ocorre; retorna 0 se não houver correspondência

regexp_count(string, pattern, startPos)

Conta ocorrências a partir de startPos

regexp_count(string, pattern, startPos, flags)

Conta com flags de correspondência personalizadas

regexp_instr(string, pattern)

Retorna a posição inicial do padrão; retorna 0 se não for encontrado

regexp_instr(string, pattern, startPos, occurrence, return_opt, flags, group)

Assinatura completa: return_opt=0 retorna a posição inicial da correspondência, return_opt=1 retorna a posição após a correspondência; group especifica o grupo de captura (0 = correspondência inteira)

regexp_like(string, pattern)

Retorna true se qualquer substring corresponder ao padrão

regexp_like(string, pattern, flags)

Retorna true com flags de correspondência personalizadas

regexp_substr(string, pattern)

Retorna a primeira substring correspondente

regexp_substr(string, pattern, startPos, occurrence, flags)

Retorna a substring correspondente para a ocorrência especificada a partir de startPos

Funções suportadas sem Orafce

Estas funções compatíveis com Oracle estão disponíveis nativamente no AnalyticDB for PostgreSQL sem a instalação do Orafce:

Função

Descrição

Exemplo

sinh(float)

Seno hiperbólico

SELECT sinh(0.1);0.100166750019844

tanh(float)

Tangente hiperbólica

SELECT tanh(3);0.99505475368673

cosh(float)

Cosseno hiperbólico

SELECT cosh(0.2);1.02006675561908

decode(expression, value, return [,value,return]... [,default])

Retorna o valor correspondente ou o padrão se não houver correspondência

Visualize o exemplo abaixo

-- Create sample table
CREATE TABLE t1(id int, name varchar(20));
INSERT INTO t1 values(1,'alibaba');
INSERT INTO t1 values(2,'adb4pg');

-- decode: returns 'alibaba' for id=1, 'adb4pg' for id=2
SELECT decode(id, 1, 'alibaba', 2, 'adb4pg', 'not found') FROM t1;

Mapeamentos de tipos de dados

Tipo Oracle

Tipo AnalyticDB for PostgreSQL

VARCHAR2

varchar ou text

DATE

timestamp

LONG

text

LONG RAW

bytea

CLOB

text

NCLOB

text

BLOB

bytea

RAW

bytea

ROWID

oid

FLOAT

double precision

DEC

decimal

DECIMAL

decimal

DOUBLE PRECISION

double precision

INT

int

INTEGER

integer

REAL

real

SMALLINT

smallint

NUMBER

numeric

BINARY_FLOAT

double precision

BINARY_DOUBLE

double precision

TIMESTAMP

timestamp

XMLTYPE

xml

BINARY_INTEGER

integer

PLS_INTEGER

integer

TIMESTAMP WITH TIME ZONE

timestamp with time zone

TIMESTAMP WITH LOCAL TIME ZONE

timestamp with time zone

Mapeamentos de funções

Função Oracle

Equivalente no AnalyticDB for PostgreSQL

sysdate

current_timestamp

trunc

trunc ou date_trunc

dbms_output.put_line

Instrução RAISE

decode

CASE WHEN ou decode

NVL

coalesce

Conversão de dados em PL/SQL

PL/SQL (Procedural Language/SQL) corresponde a PL/pgSQL no AnalyticDB for PostgreSQL. As seções abaixo abordam as regras de conversão para cada construção PL/SQL.

Pacotes

O PL/pgSQL não suporta pacotes. Converta cada pacote em um schema e converta todos os procedimentos e funções dentro do pacote em funções independentes nesse schema.

Oracle:

CREATE OR REPLACE PACKAGE pkg IS
    ...
END;

AnalyticDB for PostgreSQL:

-- Package becomes a schema
CREATE SCHEMA pkg;

Regras de conversão para conteúdo de pacotes:

  • Variáveis locais em procedimentos e funções: nenhuma alteração necessária

  • Variáveis globais: armazene em uma tabela temporária (visualize Variáveis globais abaixo)

  • Blocos de inicialização de pacote: remova-os. Se a lógica não puder ser removida, encapsule-a em uma função e chame a função explicitamente quando necessário.

  • Procedimentos e funções: converta para funções no schema correspondente. Cada função deve ter o prefixo do nome do schema.

Exemplo — função dentro de um pacote:

Oracle:

FUNCTION test_func (args int) RETURN int is
var number := 10;
BEGIN
    ...
END;

AnalyticDB for PostgreSQL:

-- RETURN -> RETURNS; IS/AS -> AS $$...$$; add LANGUAGE clause
CREATE OR REPLACE FUNCTION pkg.test_func(args int) RETURNS int AS
$$
    ...
$$
LANGUAGE plpgsql;

Procedimentos e funções

Converta procedimentos e funções do Oracle para funções PL/pgSQL. As principais mudanças são:

  • RETURN (na assinatura da função) -> RETURNS

  • Envolva o corpo da função em $$...$$

  • Adicione uma cláusula LANGUAGE plpgsql

  • Converta subprocedimentos em funções independentes

Exemplo:

Oracle:

CREATE OR REPLACE FUNCTION test_func (v_name varchar2, v_version varchar2)
RETURN varchar2 IS
    ret varchar(32);
BEGIN
    IF v_version IS NULL THEN
        ret := v_name;
    ELSE
        ret := v_name || '/' || v_version;
    END IF;
    RETURN ret;
END;

AnalyticDB for PostgreSQL:

-- Changes: varchar2 -> varchar; RETURN -> RETURNS; IS -> AS; body wrapped in $$...$$
CREATE OR REPLACE FUNCTION test_func (v_name varchar, v_version varchar)
RETURNS varchar AS
$$
DECLARE
    ret varchar(32);
BEGIN
    IF v_version IS NULL THEN
        ret := v_name;
    ELSE
        ret := v_name || '/' || v_version;
    END IF;
    RETURN ret;
END;
$$
LANGUAGE plpgsql;

Instruções PL

FOR LOOP com REVERSE

PL/SQL e PL/pgSQL tratam a palavra-chave REVERSE de forma diferente:

  • PL/SQL: FOR i IN REVERSE 1..3 conta regressivamente de 3 a 1

  • PL/pgSQL: FOR i IN REVERSE 1..3 conta regressivamente de 1 a 3 (direção oposta)

Inverta os limites do loop ao converter:

Oracle:

FOR i IN REVERSE 1..3 LOOP
    DBMS_OUTPUT.PUT_LINE(TO_CHAR(i));
END LOOP;

AnalyticDB for PostgreSQL:

-- Boundaries swapped: 1..3 -> 3..1; DBMS_OUTPUT.PUT_LINE -> RAISE
FOR i IN REVERSE 3..1 LOOP
    RAISE '%', i;
END LOOP;

Instruções PRAGMA

Instruções PRAGMA não são suportadas. Exclua todas as instruções PRAGMA.

Controle de transação

Funções no AnalyticDB for PostgreSQL não suportam BEGIN, COMMIT ou ROLLBACK dentro do corpo da função. Aplique uma destas estratégias:

  • Exclua as instruções de controle de transação do corpo da função e inclua-as fora da chamada da função.

  • Divida a função em cada limite de COMMIT ou ROLLBACK em funções separadas.

EXECUTE (SQL dinâmico)

O AnalyticDB for PostgreSQL suporta SQL dinâmico, mas com estas diferenças em relação ao Oracle:

  • A sintaxe USING não é suportada. Concatene parâmetros diretamente nas strings SQL.

  • Envolva identificadores de banco de dados com quote_ident e valores de string com quote_literal.

Oracle:

EXECUTE 'UPDATE employees_temp SET commission_pct = :x' USING a_null;

AnalyticDB for PostgreSQL:

-- USING replaced with string concatenation using quote_literal
EXECUTE 'UPDATE employees_temp SET commission_pct = ' || quote_literal(a_null);

Funções PIPE ROW

Substitua funções PIPE ROW por funções de tabela usando RETURNS SETOF <type> e RETURN NEXT.

Oracle:

TYPE pair IS RECORD(a int, b int);
TYPE numset_t IS TABLE OF pair;

FUNCTION f1(x int) RETURN numset_t PIPELINED IS
DECLARE
    v_p pair;
BEGIN
    FOR i IN 1..x LOOP
        v_p.a := i;
        v_p.b := i+10;
        PIPE ROW(v_p);
    END LOOP;
    RETURN;
END;

SELECT * FROM f1(10);

AnalyticDB for PostgreSQL:

-- RECORD type -> CREATE TYPE; PIPE ROW -> RETURN NEXT; PIPELINED -> RETURNS SETOF
CREATE TYPE pair AS (a int, b int);

CREATE OR REPLACE FUNCTION f1(x int) RETURNS SETOF pair AS
$$
DECLARE
    rec pair;
BEGIN
    FOR i IN 1..x LOOP
        rec := row(i, i+10);
        RETURN NEXT rec;
    END LOOP;
    RETURN;
END
$$
LANGUAGE plpgsql;

SELECT * FROM f1(10);

Tratamento de exceções

  • Use a instrução RAISE para lançar exceções.

  • Após capturar uma exceção, a transação não pode ser revertida dentro da função. O rollback só é permitido fora de funções definidas pelo usuário.

Para códigos de erro suportados, consulte a referência de códigos de erro do PostgreSQL.

Funções com argumentos RETURN e OUT

Uma função não pode usar simultaneamente um argumento RETURN e um argumento OUT. Converta o argumento RETURN em um argumento OUT adicional e recupere os valores de retorno usando SELECT * FROM test_func(...) INTO rec.

Oracle:

CREATE OR REPLACE FUNCTION test_func(id int, name varchar(10), out_id out int) RETURNS varchar(10)
AS $body$
BEGIN
    out_id := id + 1;
    RETURN name;
END
$body$
LANGUAGE PLPGSQL;

AnalyticDB for PostgreSQL:

-- RETURN value converted to an OUT argument (out_name)
CREATE OR REPLACE FUNCTION test_func(id int, name varchar(10), out_id out int, out_name out varchar(10))
AS $body$
BEGIN
    out_id := id + 1;
    out_name := name;
END
$body$
LANGUAGE PLPGSQL;

-- Retrieve both output values
SELECT * FROM test_func(1, '1') INTO rec;

Aspas simples na concatenação de strings SQL dinâmicas

Quando uma variável string contém aspas simples (ex.: adb'-'pg), a concatenação direta causa um erro de análise porque hífens são interpretados como operadores. Use quote_literal para incorporar o valor com segurança.

Oracle:

sql_str := 'SELECT * FROM test1 WHERE col1 = ' || param1 || ' AND col2 = ''' || param2 || ''' AND col3 = 3';

AnalyticDB for PostgreSQL:

-- quote_literal wraps param2 safely, handling embedded quotes
sql_str := 'SELECT * FROM test1 WHERE col1 = ' || param1 || ' AND col2 = ' || quote_literal(param2) || ' AND col3 = 3';

Dias entre dois timestamps

Oracle:

SELECT to_date('2019-06-30 16:16:16') - to_date('2019-06-29 15:15:15') + 1 INTO v_days FROM dual;

AnalyticDB for PostgreSQL:

-- Use extract() to get integer day count from an interval
SELECT extract('days' FROM '2019-06-30 16:16:16'::timestamp - '2019-06-29 15:15:15'::timestamp + '1 days'::interval)::int INTO v_days;

Tipos de dados PL

Tipo RECORD

Converta tipos RECORD do Oracle para tipos compostos:

Oracle:

TYPE rec IS RECORD (a int, b int);

AnalyticDB for PostgreSQL:

CREATE TYPE rec AS (a int, b int);

Tabelas aninhadas e arrays de tamanho variável

Como variáveis PL, tanto NESTED TABLE quanto VARRAY mapeiam para o tipo ARRAY do PostgreSQL.

Oracle:

DECLARE
    TYPE Roster IS TABLE OF VARCHAR2(15);
    names Roster := Roster('D Caruso', 'J Hamil', 'D Piro', 'R Singh');
BEGIN
    FOR i IN names.FIRST .. names.LAST LOOP
        IF names(i) = 'J Hamil' THEN
            DBMS_OUTPUT.PUT_LINE(names(i));
        END IF;
    END LOOP;
END;

AnalyticDB for PostgreSQL:

-- TABLE OF -> varchar[]; array indexing: names(i) -> names[i]; FIRST..LAST -> 1..array_length
CREATE OR REPLACE FUNCTION f1() RETURNS VOID AS
$$
DECLARE
    names varchar(15)[] := '{"D Caruso", "J Hamil", "D Piro", "R Singh"}';
    len int := array_length(names, 1);
BEGIN
    FOR i IN 1..len LOOP
        IF names[i] = 'J Hamil' THEN
            RAISE NOTICE '%', names[i];
        END IF;
    END LOOP;
    RETURN;
END
$$
LANGUAGE plpgsql;

SELECT f1();

Se a tabela aninhada for usada como valor de retorno de função, use uma função de tabela (RETURNS SETOF <type>) em vez disso.

Arrays associativos

Não há substituto para arrays associativos do Oracle no AnalyticDB for PostgreSQL. Esta construção exige redesenho.

Variáveis globais

O AnalyticDB for PostgreSQL não suporta variáveis globais. Armazene variáveis globais de nível de pacote em uma tabela temporária e defina funções de acesso.

-- Store global variables in a temporary table
-- Note: The id column is the distribution key and cannot be modified
CREATE TEMPORARY TABLE global_variables (
    id int,
    g_count int,
    g_set_id varchar(50),
    g_err_code varchar(100)
);

INSERT INTO global_variables VALUES(0, 1, null, null);

-- Getter function
CREATE OR REPLACE FUNCTION get_variable() RETURNS SETOF global_variables AS
$$
DECLARE
    rec global_variables%rowtype;
BEGIN
    EXECUTE 'SELECT * FROM global_variables' INTO rec;
    RETURN NEXT rec;
END;
$$
LANGUAGE plpgsql;

-- Setter function
CREATE OR REPLACE FUNCTION set_variable(IN param varchar(50), IN value anyelement) RETURNS void AS
$$
BEGIN
    EXECUTE 'UPDATE global_variables SET ' || quote_ident(param) || ' = ' || quote_literal(value);
END;
$$
LANGUAGE plpgsql;

Para modificar uma variável global:

-- Declare tmp_rec record; in the calling function
SELECT * FROM set_variable('g_err_code', 'error'::varchar) INTO tmp_rec;

Para ler uma variável global:

SELECT * FROM get_variable() INTO tmp_rec;
error_code := tmp_rec.g_err_code;

Conversões SQL

CONNECT BY

A cláusula CONNECT BY para consultas hierárquicas não tem equivalente SQL direto no AnalyticDB for PostgreSQL. Reescreva-a como uma função PL/pgSQL que percorre a hierarquia iterativamente.

Oracle:

SELECT emp_id, lead_id, emp_name, PRIOR emp_name AS lead_name, salary
FROM employee
START WITH lead_id = 0
CONNECT BY PRIOR emp_id = lead_id;

AnalyticDB for PostgreSQL (função de percurso iterativo):

CREATE OR REPLACE FUNCTION f1(tablename text, lead_id int, nocycle boolean) RETURNS SETOF employee AS
$$
DECLARE
    idx int := 0;
    res_tbl varchar(265) := 'result_table';
    prev_tbl varchar(265) := 'tmp_prev';
    curr_tbl varchar(256) := 'tmp_curr';
    current_result_sql varchar(4000);
    tbl_count int;
    rec record;
BEGIN
    EXECUTE 'TRUNCATE ' || prev_tbl;
    EXECUTE 'TRUNCATE ' || curr_tbl;
    EXECUTE 'TRUNCATE ' || res_tbl;
    LOOP
        -- Query the current hierarchy level and insert into tmp_curr
        current_result_sql := 'INSERT INTO ' || curr_tbl || ' SELECT t1.* FROM ' || tablename || ' t1';

        IF idx > 0 THEN
            current_result_sql := current_result_sql || ', ' || prev_tbl || ' t2 WHERE t1.lead_id = t2.emp_id';
        ELSE
            current_result_sql := current_result_sql || ' WHERE t1.lead_id = ' || lead_id;
        END IF;
        EXECUTE current_result_sql;

        -- If nocycle is false, remove already-traversed rows
        IF nocycle IS FALSE THEN
            EXECUTE 'DELETE FROM ' || curr_tbl || ' WHERE (lead_id, emp_id) IN (SELECT lead_id, emp_id FROM ' || res_tbl || ')';
        END IF;

        -- Exit when no more rows
        EXECUTE 'SELECT count(*) FROM ' || curr_tbl INTO tbl_count;
        EXIT WHEN tbl_count = 0;

        -- Promote current results to result and prev tables
        EXECUTE 'INSERT INTO ' || res_tbl || ' SELECT * FROM ' || curr_tbl;
        EXECUTE 'TRUNCATE ' || prev_tbl;
        EXECUTE 'INSERT INTO ' || prev_tbl || ' SELECT * FROM ' || curr_tbl;
        EXECUTE 'TRUNCATE ' || curr_tbl;
        idx := idx + 1;
    END LOOP;

    FOR rec IN EXECUTE 'SELECT * FROM ' || res_tbl LOOP
        RETURN NEXT rec;
    END LOOP;
    RETURN;
END
$$
LANGUAGE plpgsql;

ROWNUM

Limitar tamanho do resultado:

Oracle:

SELECT * FROM t WHERE rownum < 10;

AnalyticDB for PostgreSQL:

SELECT * FROM t LIMIT 10;

Gerar números de linha:

Oracle:

SELECT rownum, * FROM t;

AnalyticDB for PostgreSQL:

SELECT row_number() OVER() AS rownum, * FROM t;

Tabela DUAL

Opção 1 — Remover FROM DUAL:

Oracle:

SELECT sysdate FROM dual;

AnalyticDB for PostgreSQL:

SELECT current_timestamp;

Opção 2 — Criar uma tabela dual:

CREATE TABLE dual (dummy varchar(1));
INSERT INTO dual VALUES ('X');

Funções definidas pelo usuário em instruções SELECT

O AnalyticDB for PostgreSQL não permite chamar funções definidas pelo usuário que contenham instruções SQL dentro de SELECT. Fazer isso causa um erro de segmento:

ERROR: function cannot execute on segment because it accesses relation "public.t2"

Converta tais funções em expressões SQL ou subconsultas.

Oracle:

CREATE OR REPLACE FUNCTION f1(arg int) RETURN int IS
    v int;
BEGIN
    SELECT b INTO v FROM t2 WHERE a = arg;
    RETURN v;
END;

SELECT a, f1(b) FROM t1;

AnalyticDB for PostgreSQL:

-- Inline the function logic as a join
SELECT t1.a, t2.b FROM t1, t2 WHERE t1.b = t2.a;

OUTER JOIN (+)

Outer join de duas tabelas:

Oracle:

SELECT * FROM a, b WHERE a.id = b.id(+);

AnalyticDB for PostgreSQL:

SELECT * FROM a LEFT JOIN b ON a.id = b.id;

Outer join de três tabelas:

Use uma Common Table Expression (CTE) para unir duas tabelas primeiro e aplique RIGHT OUTER JOIN com coalesce.

Oracle:

SELECT * FROM test1 t1, test2 t2, test3 t3
WHERE t1.col1(+) BETWEEN NVL(t2.col1, t3.col1) AND NVL(t3.col1, t2.col1);

AnalyticDB for PostgreSQL:

WITH cte AS (
    SELECT t2.col1 AS low, t2.col2, t3.col1 AS high, t3.col2 AS c2
    FROM t2, t3
)
SELECT * FROM t1
RIGHT OUTER JOIN cte ON t1.col1 BETWEEN coalesce(cte.low, cte.high) AND coalesce(cte.high, cte.low);

MERGE INTO

Na maioria dos casos, substitua MERGE INTO por INSERT ON CONFLICT. Para casos que INSERT ON CONFLICT não consegue cobrir, use stored procedures.

Sequências

Oracle:

CREATE SEQUENCE seq1;
SELECT seq1.nextval FROM dual;

AnalyticDB for PostgreSQL:

CREATE SEQUENCE seq1;
SELECT nextval('seq1');

Cursores

O percurso básico de cursor é suportado. Use o padrão OPEN/FETCH/CLOSE:

Oracle:

FUNCTION test_func() IS
    CURSOR data_cursor IS SELECT * FROM test1;
BEGIN
    FOR i IN data_cursor LOOP
        -- process i
    END LOOP;
END;

AnalyticDB for PostgreSQL:

CREATE OR REPLACE FUNCTION test_func()
AS $body$
DECLARE
    data_cursor CURSOR FOR SELECT * FROM test1;
    i record;
BEGIN
    OPEN data_cursor;
    LOOP
        FETCH data_cursor INTO i;
        IF NOT FOUND THEN
            EXIT;
        END IF;
        -- process i
    END LOOP;
    CLOSE data_cursor;
END;
$body$
LANGUAGE PLPGSQL;

Cursores com o mesmo nome em funções recursivas não são suportados. Substitua por FOR i IN <query>:

Oracle:

FUNCTION test_func(level IN number) IS
    CURSOR data_cursor IS SELECT * FROM test1;
BEGIN
    IF level > 5 THEN RETURN; END IF;
    FOR i IN data_cursor LOOP
        -- process i
        test_func(level + 1);
    END LOOP;
END;

AnalyticDB for PostgreSQL:

-- Named cursor removed; inline query used instead to support recursion
CREATE OR REPLACE FUNCTION test_func(level int) RETURNS void
AS $body$
DECLARE
    i record;
BEGIN
    IF level > 5 THEN
        RETURN;
    END IF;
    FOR i IN SELECT * FROM test1 LOOP
        -- process i
        PERFORM test_func(level + 1);
    END LOOP;
END;
$body$
LANGUAGE PLPGSQL;

Armadilhas comuns

Essas diferenças de comportamento em tempo de execução são a fonte mais frequente de bugs após a migração. Revise-as antes de testar seu código convertido.

Armadilha

Comportamento do Oracle

Comportamento do AnalyticDB for PostgreSQL

Ação

NULL na concatenação de strings

`'abc'

NULL` -> 'abc'

Retorna NULL

Mude para o modo de compatibilidade Oracle ou reescreva usando coalesce

Direção do FOR LOOP REVERSE

Conta regressivamente do 2º para o 1º número

Conta regressivamente do 1º para o 2º

Inverta os limites do loop

Controle de transação em funções

COMMIT/ROLLBACK permitidos no corpo da função

Não suportado

Mova para fora da função ou divida a função

SQL dinâmico com USING

EXECUTE '...' USING var

USING não suportado

Concatene com quote_literal / quote_ident

Funções com RETURN + OUT

Ambos permitidos

Não suportado

Converta RETURN em um argumento OUT

Funções definidas pelo usuário em SELECT

Podem consultar tabelas

Não podem consultar tabelas

Reescreva como subconsulta ou join

Arrays associativos

Suportado

Sem equivalente

Requer redesenho

Cursores com o mesmo nome em recursão

Suportado

Não suportado

Use FOR i IN <query> em vez disso

Próximos passos