PostgreSQL document says:
The entire body of a SQL function is parsed before any of it is executed. While a SQL function can contain commands that alter the system catalogs (e.g.,
CREATE TABLE
), the effects of such commands will not be visible during parse analysis of later commands in the function. Thus, for example,CREATE TABLE foo (...); INSERT INTO foo VALUES(...);
will not work as desired if packaged up into a single SQL function, since foo won't exist yet when theINSERT
command is parsed.It's recommended to use PL/pgSQL instead of a SQL function in this type of situation.
Why "It's recommended to use PL/pgSQL instead of a SQL function in this type of situation", where the PL/pgSQL or SQL function contains commands that alter the system catalogs, such as CREATE TABLE foo (...); INSERT INTO foo VALUES(...);
?
"The entire body of a SQL function is parsed before any of it is executed". Is it not true for a PL/pgSQL function? What differences are between SQL functions and PL/pgSQL functions, in terms of parsing and executing the commands in their bodies?
You emphasized the key sentence in the manual yourself:
The entire body of a SQL function is parsed before any of it is executed.
Also read about The Parser Stage in the manual.
It consists of two major parts: the parser and the transformation process. The manual:
the transformation process takes the tree handed back by the parser as input and does the semantic interpretation needed to understand which tables, functions, and operators are referenced by the query.
If an SQL function contains these commands:
CREATE TABLE foo (...);
INSERT INTO foo VALUES(...);
Both statements are planned at virtually the same time (based on the same snapshot of the system catalogs). Hence, the INSERT
cannot see the table foo
supposedly created with the preceding CREATE
command. That creates one of the following problems:
If there is no other table named "foo" in your search_patch
(yet), Postgres complains when trying to create the function:
ERROR: relation "foo" does not exist
If another table named "foo" already exists in your search_patch
(and you don't use conflicting column names), Postgres will plan the INSERT
based on that pre-existing table. Typically that results in an error at execution time, if any values cause conflicts in the (wrong!) table. Or, with some bad luck, it might even write to that table without error message! Very sneaky bug.
That cannot happen with a PL/pgSQL function, because it treats SQL commands like prepared statements, planned and executed sequentially. So each statement can see objects created in earlier statements of the same function.
Consequently, statements that are never visited are never even planned - as opposed to SQL functions. And the execution plan for statements can be cached within the same session - also unlike SQL functions. Read about plan caching in PL/pgSQL functions in the manual here.
Each approach has advantages for some use cases. Further reading: