The Parse and Evaluation of PL/pgSQL Conditional Expressions

By John Doe December 26, 2024

Summary: In this article, we’ll learn how PL/pgSQL conditional expressions are parsed and evaluated in PostgreSQL.

Table of Contents

Introduction

Recently, in the PostgreSQL community, a user posted a PL/pgSQL block (shown below) and was surprised that it did not generate a syntax error:

DO $$
DECLARE i INT;
BEGIN
  i = 0;
  IF i = 0 AND THEN
    RAISE NOTICE 'i = 0';
  END IF;
END; $$;

At first glance, this code block seems incomplete. Notice the IF condition: it appears to be missing an additional condition after the AND operator. Logically, this should cause an exception due to the incomplete condition following AND.

  IF i = 0 AND THEN 

However, during PL/pgSQL execution, the condition is evaluated without any syntax errors or warnings. This raises a critical question:

  • How does PostgreSQL internally process this condition?
  • What allows this seemingly incomplete expression to work?
  • How the condition “i = 0 AND” is processed?

While reviewing such PL/pgSQL it looks incomplete and assuming that it should fail will be surprising.

In this article, we’ll dive into the internals of PL/pgSQL to understand how this conditional statement is processed and why PostgreSQL does not flag it as erroneous.

Uncover PL/pgSQL Internal Code

One of the greatest advantages of open-source software is the ability to examine the code base directly. This gives us a foundation for understanding how things work or, in some cases, why they don’t break as expected.

Our investigation begins with the PLPGSQL_STMT_IF structure, tracing through the call stack in the pl_exec.c file.

By exploring the code, we find that IF statements and their conditions are evaluated using exec_run_select, which effectively executes a SELECT statement that returns a boolean result.

PL/pgSQL conditions as SELECT clause

Looking back at the original example, the condition i = 0 AND is processed within a SELECT clause. Here, AND essentially acts as a placeholder, allowing the PL/pgSQL engine to evaluate the condition without triggering syntax errors.

-- the condition is evaluated as SQL,
-- "and" is treated as column alias

SELECT 0 = 0 AND;
 and
-----
 t
(1 row)

This insight — that conditional expressions are evaluated as SELECT statements — opens up new possibilities. It means we can leverage various functions within conditions in PL/pgSQL, as demonstrated in the examples below.

The following snippets illustrate different ways conditional expressions can be evaluated in PL/pgSQL:

Conditions SQL PL/pgSQL
Count evaluation 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;$$;
Case-insensitive match select (‘a’ ilike ‘A’); do $$
begin
if (‘a’ ilike ‘A’) and then
raise notice ‘%’,‘Matched If Clause’;
else raise notice ‘Not Match If Clase’;
end if;
end;$$;
Complex unnest – Array based Condition. 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’;
else raise notice ‘Not Match If Clase’;
end if;
end;$$;
Row Existence check select exists (select 1 from generate_series(1,10000)); do $$
begin
if exists (select 1 from generate_series(1, 10000)) then
raise notice ‘%’, ‘Matched If Clause’;
else raise notice ‘Not Match If Clase’;
end if;
end;$$;

Conclusion

PL/pgSQL processes conditional expressions using SELECT statements, allowing incomplete conditions like IF i = 0 AND to execute without errors.

Instead of failing, the AND is treated as part of a SELECT expression, letting PostgreSQL evaluate it flexibly. This approach lets developers incorporate various SELECT expressions directly into conditional evaluations, offering additional ways to build and test complex logic seamlessly in PL/pgSQL.