Thread: procedure string constant is parsed at procedure create time.

procedure string constant is parsed at procedure create time.

From
jian he
Date:
hi.
table "test" does not exist.

---1.this will work.
CREATE or replace PROCEDURE template_create() LANGUAGE SQL
AS $proc$ DROP TABLE if exists test cascade; CREATE TABLE test(id
int4range,valid_at tsrange,name text);$proc$;

----2.this will have errors.
CREATE or replace PROCEDURE template_create() LANGUAGE SQL AS $proc$
    DROP TABLE if exists test cascade;
    CREATE TABLE test(id int4range,valid_at tsrange,name text);
    INSERT INTO test VALUES (NULL, tsrange('2018-01-01',
'2019-01-01'), 'null key');
$proc$;

----3.language plpgsql works fine.
CREATE or replace PROCEDURE template_create()
LANGUAGE plpgsql AS $proc$
    begin
        DROP TABLE if exists test101 cascade;
        CREATE TABLE test101(id int4range,valid_at tsrange,name text);
        INSERT INTO test101 VALUES (NULL, tsrange('2018-01-01',
'2019-01-01'), 'null key');
    end
$proc$;

per manual: https://www.postgresql.org/docs/current/sql-createprocedure.html
"....This form only works for LANGUAGE SQL, the string constant form
works for all languages. This form is parsed at procedure definition
time, the string constant form is parsed at execution time...."

Why does the 2nd query fail? What am I missing?



Re: procedure string constant is parsed at procedure create time.

From
Alvaro Herrera
Date:
On 2023-Nov-07, jian he wrote:

> ----2.this will have errors.
> CREATE or replace PROCEDURE template_create() LANGUAGE SQL AS $proc$
>     DROP TABLE if exists test cascade;
>     CREATE TABLE test(id int4range,valid_at tsrange,name text);
>     INSERT INTO test VALUES (NULL, tsrange('2018-01-01',
> '2019-01-01'), 'null key');
> $proc$;

This is because of check_function_bodies.  You can get this procedure
created if you set it to 0.

However, it still won't run:

55493 16.0 1480547=# call template_create();
ERROR:  relation "test" does not exist
LÍNEA 4:     INSERT INTO test VALUES (NULL, tsrange('2018-01-01',
                         ^
CONSULTA:  
    DROP TABLE if exists test cascade;
    CREATE TABLE test(id int4range,valid_at tsrange,name text);
    INSERT INTO test VALUES (NULL, tsrange('2018-01-01',
'2019-01-01'), 'null key');

CONTEXTO:  SQL function "template_create" during startup
Duración: 0,278 ms

The problem seems to be that the procedure uses a single catalog
snapshot during execution, so the INSERT doesn't see the table that
CREATE has made.  If you create the table beforehand, then DROP and
CREATE will both work, but the INSERT will fail claiming that the table
that existed before the DROP no longer exists:

55493 16.0 1480547=# CREATE TABLE test(id int4range,valid_at tsrange,name text);
CREATE TABLE
Duración: 1,251 ms
55493 16.0 1480547=# select 'test'::regclass::oid;
  oid   
────────
 210427
(1 fila)

Duración: 0,461 ms
55493 16.0 1480547=# call template_create();
ERROR:  relation with OID 210427 does not exist
CONTEXTO:  SQL function "template_create" statement 3
Duración: 2,207 ms

Note we no longer get the "LINE 4" context item, but we do get
"statement 3", so we know it's still the insert.

(Of course, the table returns to life because the DROP is part of the
transaction that gets aborted by the failure).

-- 
Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/



Re: procedure string constant is parsed at procedure create time.

From
"David G. Johnston"
Date:
On Tuesday, November 7, 2023, jian he <jian.universality@gmail.com> wrote:

Why does the 2nd query fail? What am I missing?


The language specific implementation detail note on this page.


David J.
 

Re: procedure string constant is parsed at procedure create time.

From
Tom Lane
Date:
Alvaro Herrera <alvherre@alvh.no-ip.org> writes:
> On 2023-Nov-07, jian he wrote:
>> ----2.this will have errors.

> The problem seems to be that the procedure uses a single catalog
> snapshot during execution, so the INSERT doesn't see the table that
> CREATE has made.

It looks to me like fmgr_sql does take a new snapshot per query.
The problem is that we do parse analysis of the entire function
body before we execute any of it.  So (roughly speaking) we can
cope with DML changes between statements, but not DDL changes.

At one time it was possible to argue that this was a bug, or
at least a deficiency we ought to rectify sometime.  However,
with new-style SQL functions the entire function body is
certainly parsed before execution.  So now I'd be against
changing this aspect of old-style functions: it'd mainly have
the result of widening the semantic gap between old style and
new style, which doesn't seem like a good thing.

(The verification done by check_function_bodies likewise can't
work if earlier statements make DDL changes that affect later
ones.)

As per the advice in the fine manual, use plpgsql for this
sort of task.

            regards, tom lane