我遇到了 PL/pgSQL 函数的问题,它根据条件返回不同的数据类型,并导致类型不匹配错误。这是该函数的简化版本:
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$;
请注意,这是我的完整函数的非常简化的版本。我有兴趣了解为什么以及如何解决它。
当使用 调用此函数时lever = 0
,它会正确返回文本uuid
。然而,当用 with 调用它lever = 1
来强制ELSE
执行该语句时,它会抛出一个错误:
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
无论数据类型如何,ELSE
块总是会失败。
实际上,块中的分配
ELSE
工作正常。错误信息指:
类型(匿名)
record
的变量可以包含任何行类型,您甚至可以在同一代码块中将不兼容的行类型重复分配给同一变量。但是PL/pgSQL 函数(与 SQL 函数不同)将所有嵌套 SQL 语句视为准备好的语句。即使
record
类型在重复调用中也必须相同(或匹配)。所以你的函数的第一次调用总是成功的。但是同一会话中的后续调用必须使用相同的准备好的语句。
如果问题仅由特定查询计划触发,我们可以使用动态 SQL 来避免它
EXECUTE
,其中该计划永远不会保存和重用。但测试表明准备好的语句本身不允许数据类型改变。可能的解决方法(如Laurenz所暗示的):为每个分支单独分配。每个语句表面上看起来相同,但由于数据类型不同,准备方式也不同:
小提琴
有关的:
EXECUTE
EXECUTE
PostgreSQL 函数中的语句与无语句无论如何,您展示的示例可以简化为这个简单的 SQL 函数(不会遇到同样的问题):
但你已经暗示你的案子涉及更多。