使用 PostgreSQL 9.3 我一直在尝试定义一个assert
辅助函数来检查空查询结果和类似的事情,如下所示:
CREATE FUNCTION public.assert (
in_assertion boolean,
in_errormessage text
)
RETURNS boolean
IMMUTABLE
LANGUAGE plpgsql
SECURITY INVOKER
AS $function$
BEGIN
IF NOT in_assertion THEN
RAISE EXCEPTION 'assertion failed: %', in_errormessage;
END IF;
RETURN in_assertion;
END;
$function$
;
Run Code Online (Sandbox Code Playgroud)
经过测试,我发现异常没有像我期望的那样抛出。例如,对于CREATE TABLE emptytable (somecolumn text);
和
CREATE FUNCTION public.testassert_buggy (
out somevalue text
)
LANGUAGE sql
SECURITY DEFINER
AS $function$
WITH firstquery AS (
SELECT * FROM emptytable
), nonemptycheck AS (
SELECT assert(count(*) = 42, 'nonemptycheck failed') FROM firstquery
) SELECT * FROM firstquery;
$function$
;
Run Code Online (Sandbox Code Playgroud)
我希望调用SELECT testassert_buggy();
会抛出异常,但结果是
somevalue
-----------
(1 row)
Run Code Online (Sandbox Code Playgroud)
(请注意,firstquery
实际上返回 0 行;第 1 行是由于这是一个带out
参数的函数。)
通过倒数第二行的以下小改动,抛出异常。
CREATE FUNCTION public.testassert (
out somevalue text
)
LANGUAGE sql
SECURITY DEFINER
AS $function$
WITH firstquery AS (
SELECT * FROM emptytable
), nonemptycheck AS (
SELECT assert(count(*) = 42, 'nonemptycheck failed') FROM firstquery
) SELECT firstquery.* FROM nonemptycheck, firstquery;
$function$
;
Run Code Online (Sandbox Code Playgroud)
如果我重写最后一个查询切换表列表(即使用FROM firstquery, nonemptycheck
),则再次没有例外。我很困惑。查询是否以某种方式进行了优化,忽略了异常等副作用?我试图IMMUTABLE
从 的定义中删除assert
,但这并没有什么区别。
根本不执行未引用的 CTE (数据修改CTE除外!)
pgsql-bugs 上的相关线程,Tom Lane 解释了该行为。
在您的第一个示例中,您有:
SELECT * FROM firstquery;
Run Code Online (Sandbox Code Playgroud)
没有提及 CTE nonemptycheck
。所以 CTE 永远不会被执行。
在第二个示例中,您有:
SELECT firstquery.* FROM nonemptycheck, firstquery;
Run Code Online (Sandbox Code Playgroud)
nonemptycheck
被引用,所以它被执行,导致异常。
您在评论中添加的测试用例因类似原因而失败。由于第一个 CTE 不返回任何行,因此外部 SELECT 不返回任何行。不需要执行第二个 CTE,因为不会显示结果。优化器的工作是避免徒劳无功的工作..
附加nonemptycheck
跨接子查询(CROSS JOIN
或逗号后所附),而不是第二CTE并没有帮助,任一。一个类似的优化避免执行:由于firstquery
没有返回一行,则在评价没有意义nonemptycheck
,甚至在一个子查询:
WITH firstquery AS (
SELECT *
FROM emptytable
WHERE FALSE
)
SELECT f.* -- even if you append ", n.*" to SELECT list
FROM firstquery f
, (
SELECT assert(count(*) = 42, 'check failed')
FROM firstquery
) n -- not executed
Run Code Online (Sandbox Code Playgroud)
您可以使用以下命令强制评估FULL OUTER JOIN
:
WITH firstquery AS (
SELECT *
FROM emptytable
WHERE false
)
SELECT f.*
FROM firstquery f
FULL JOIN (
SELECT assert(count(*) = 42, 'check failed')
FROM firstquery
) nonemptycheck ON TRUE; -- always executed
Run Code Online (Sandbox Code Playgroud)
副作用:NULL
当不firstquery
返回任何行时,这将返回一个填充了值的单行。但是,在这种特殊情况下并非如此,因为在这种情况下,您的断言会引发异常。
归档时间: |
|
查看次数: |
1622 次 |
最近记录: |