摘要:最近,在 PostgreSQL 社区中,一位用户发布了一块 PL/pgSQL 代码(如下所示),惊讶地发现它没有产生语法错误:
最近,在 PostgreSQL 社区中,一位用户发布了一块 PL/pgSQL 代码(如下所示),惊讶地发现它没有产生语法错误:
DO $$DECLARE i INT;BEGIN i = 0; IF i = 0 AND THEN RAISE NOTICE 'i = 0'; END IF;END; $$;乍一看,这个代码块似乎并不完整。请注意IF条件:它在AND运算符后面似乎缺少了一个额外的条件。从逻辑上讲,这应该会出现异常,因为AND后面的条件不完整。
IF i = 0 AND THEN但是,在 PL/pgSQL 执行期间,条件被评估,并没有出现任何语法错误或警告。这就提出了一个关键问题:
• PostgreSQL 在内部如何处理这个条件?• 是什么让这个看似不完整的表达式正常工作的呢?• 条件 “i = 0 AND” 是如何被处理的?在检视此类 PL/pgSQL 代码时,它看起来不完整,假设它应该失败的情况下,会令人惊讶。
在本文中,我们将深入研究 PL/pgSQL 的内部结构,以了解此条件语句的处理方式,以及为什么 PostgreSQL 没有将其标记为错误。
开源软件的一个最大优势是,能够直接检查代码库。这为我们理解事物的运作方式,或在某些情况下为什么它们没有按预期中断,提供了基础。
我们的调查从PLPGSQL_STMT_IF结构开始,通过在pl_exec.c文件中的调用栈进行跟踪。
通过浏览代码,我们发现IF语句及其条件是使用exec_run_select进行求值的,它有效地执行了一条返回一个布尔结果的SELECT语句。
回顾原始的示例,条件i = 0 AND是在一个SELECT子句中处理的。在这里,AND本质上充当一个占位符,允许 PL/pgSQL 引擎在不触发语法错误的情况下计算条件。
-- the condition is evaluated as SQL,-- "and" is treated as column aliasSELECT 0 = 0 AND; and----- t(1 row)这种见解 — 条件表达式作为SELECT语句进行计算 — 开辟了新的可能性。这意味着,我们可以在 PL/pgSQL 的条件中利用各种函数,如以下示例所示。
条件SQLPL/pgSQL计数求值
select count(1) = 1;
do $$
begin
if count(1) = 1 and then
raise notice '%','Matched If Clause';
else raise notice 'Not Match If Clase';
end if;
end;$$;
不区分大小写的匹配
select ('a' ilike 'A');
do $$
begin
if ('a' ilike 'A') and then
end if;
end;$$;
复杂的 unnest – 基于数组的条件
select COUNT(col1) filter(where col1 = 'A') = 2 from (select unnest(ARRAY['A','B','A']) col1)
do $$
begin
if COUNT(col1) filter(where col1 = 'A') = 2 from (select unnest(ARRAY['A','B','A']) col1) then
raise notice '%', 'Matched If Clause';
end if;
end;$$;
行存在性检查
select exists (select 1 from generate_series(1,10000));
do $$
begin
if exists (select 1 from generate_series(1, 10000)) then
end if;
end;$$;
PL/pgSQL 使用SELECT语句处理条件表达式,允许不完整的条件(如IF i = 0 AND)无错误地执行。
条件中的AND并没有出错,而是将其作为了SELECT表达式的一部分,让 PostgreSQL 灵活地计算它。这种方法允许开发者将各种SELECT表达式直接融入到条件计算中,从而提供了其他的方法,可在 PL/pgSQL 中无缝构建和测试复杂逻辑。
来源:散文随风想一点号