Estou encontrando um problema com uma função PL/pgSQL em que ela retorna diferentes tipos de dados com base em uma condição e resulta em um erro de incompatibilidade de tipo. Aqui está uma versão simplificada da função:
CREATE OR REPLACE FUNCTION public.test(lever int)
RETURNS VARCHAR
LANGUAGE plpgsql
AS $function$
DECLARE
_new_record RECORD;
BEGIN
-- Evaluating the random condition and returning different strings
IF (lever = 0) THEN
SELECT * FROM
(VALUES(uuid_generate_anura()))x(id)
INTO _new_record;
ELSE
SELECT * FROM
(VALUES(10))x(id)
INTO _new_record;
END IF;
RETURN pg_typeof(_new_record.id)::varchar;
END;
$function$;
Observe que esta é uma versão muito simplificada da minha função completa. Estou interessado em entender por que e como posso contornar isso.
Ao chamar esta função com lever = 0
, ela retorna corretamente o texto uuid
. No entanto, ao chamá-lo lever = 1
para forçar a ELSE
execução da instrução, ocorre um erro:
postgres=# select test(0);
test
------
uuid
(1 row)
postgres=# select test(1);
ERROR: type of parameter 4 (integer) does not match that when preparing the plan (uuid)
CONTEXT: PL/pgSQL function test(integer) line 15 at RETURN
Não importa o tipo de dados, o ELSE
bloco sempre falhará.
Na verdade, a atribuição no
ELSE
bloco funciona bem. A mensagem de errorefere-se a:
Uma variável do tipo (anônima)
record
pode conter qualquer tipo de linha , você pode até atribuir repetidamente tipos de linha incompatíveis à mesma variável dentro do mesmo bloco de código.Mas as funções PL/pgSQL (diferentemente das funções SQL) tratam todas as instruções SQL aninhadas como instruções preparadas. Mesmo
record
os tipos devem ser iguais (ou correspondentes) em chamadas repetidas.Portanto, a primeira chamada da sua função sempre é bem-sucedida. Mas as chamadas subsequentes na mesma sessão deverão funcionar com a mesma declaração preparada.
Se o problema for desencadeado apenas por um plano de consulta específico, podemos evitá-lo usando SQL dinâmico com
EXECUTE
, onde o plano nunca é salvo e reutilizado. Mas os testes indicam que a instrução preparada em si não permite a alteração do tipo de dados.Possível solução alternativa (conforme sugerido por Laurenz ): uma atribuição separada para cada filial. Cada instrução parece a mesma superficialmente, mas é preparada de forma diferente devido aos diferentes tipos de dados:
violino
Relacionado:
EXECUTE
instrução VS nenhumaEXECUTE
instrução em uma função no PostgreSQLPelo que vale, o exemplo que você mostra pode ser reduzido a esta simples função SQL (que não apresenta o mesmo problema):
Mas você já deu a entender que seu caso é mais complicado.