Thread: SQL2003 GENERATED ... AS ... syntax

SQL2003 GENERATED ... AS ... syntax

From
des@des.no (Dag-Erling Smørgrav)
Date:
As previously mentioned, I'm working on implementing [subject].  I
think I've mostly worked it out, but I'm having trouble with the
GENERATED ALWAYS case.

My changes (see attached patch) can be summarized as follows:

 - in backend/utils/adt/misc.c:

   - added a force_default_value() function which takes a string
     argument (the name of the column to force to default) and
     currently does nothing.

 - in backend/parser/gram.y:

   - when GENERATED ... AS ... is encountered in a column definition,
     it adds a node of the new T_Generated type to the constraint
     list.  This node contains a bool that differentiates between BY
     DEFAULT and ALWAYS, and a pointer to a CreateSeqStmt (for
     IDENTITY '(' OptSeqList ')') or a List constructed by the a_expr
     production (for '(' a_expr ')')

 - in backend/parser/analyze.c:

   - factored out the code from transformColumnDefinition() that
     creates the sequence and the DEFAULT constraint into a separate
     CreateSerialColumn() function which takes as one of its arguments
     is a List of sequence options.  The SERIAL code passes in a NIL
     list, while the GENERATED AS IDENTITY code passes in the option
     list from the CreateSeqStmt.

   - added a CreateAlwaysDefaultColumn() function which synthesizes a
     CreateTrigStmt equivalent to CREATE TRIGGER foo BEFORE INSERT ON
     bar FOR EACH ROW EXECUTE PROCEDURE force_default_value ('baz')
     and adds it to the work list.  This function is called by
     transformColumnDefinition() if a Generated node with always set
     to true is encountered.

Now I must be doing something wrong in CreateAlwaysDefaultColumn(),
because the CreateTrigStmt fails to execute:

| des=# create table test ( id int generated always as identity ( minvalue 1000 ), word text );
| NOTICE:  CREATE TABLE will create implicit sequence "test_id_seq" for SERIAL column "test.id"
| NOTICE:  CREATE TABLE will create implicit trigger "test_id_always_default" for column "test.id"
| ERROR:  relation "public.test" does not exist

GENERATED BY DEFAULT AS IDENTITY works fine though, so I must have
done *something* right:

| des=# create table test ( id int generated by default as identity ( minvalue 1000 ), word text );
| NOTICE:  CREATE TABLE will create implicit sequence "test_id_seq" for SERIAL column "test.id"
| CREATE TABLE
| des=# select sequence_name, last_value, min_value, max_value from test_id_seq;
|  sequence_name | last_value | min_value |      max_value
| ---------------+------------+-----------+---------------------
|  test_id_seq   |       1000 |      1000 | 9223372036854775807
| (1 row)
|

On the other hand, I seem to have botched the definition of
force_default_value() in include/catalog/pg_proc.h, because adding the
trigger manually doesn't seem to work either:

| des=# \df force_default_value
|                              List of functions
|  Result data type |   Schema   |        Name         | Argument data types
| ------------------+------------+---------------------+---------------------
|  "trigger"        | pg_catalog | force_default_value | text
| (1 row)
|
| des=# create trigger test_id_always_default before insert on test for each row execute procedure force_default_value
('id');
| ERROR:  function force_default_value() does not exist

Any suggestions?

DES
--
Dag-Erling Smørgrav - des@des.no


Attachment

Re: SQL2003 GENERATED ... AS ... syntax

From
Rod Taylor
Date:
> On the other hand, I seem to have botched the definition of
> force_default_value() in include/catalog/pg_proc.h, because adding the
> trigger manually doesn't seem to work either:
>
> | des=# \df force_default_value
> |                              List of functions
> |  Result data type |   Schema   |        Name         | Argument data types
> | ------------------+------------+---------------------+---------------------
> |  "trigger"        | pg_catalog | force_default_value | text
> | (1 row)
> |
> | des=# create trigger test_id_always_default before insert on test for each row execute procedure
force_default_value('id'); 
> | ERROR:  function force_default_value() does not exist
>
> Any suggestions?

Triggers are strange this way.  You need to create the function without
any arguments. The procedure is expected to find (and enforce) the
arguments through use of the TriggerData struct.

ttdummy() in src/test/regress/regress.c is an example of a Trigger
taking 2 arguments.


Using triggers could cause some interesting side effects when they're
system controlled.  Triggers are fired by naming convention
(alphabetical order).  If the user creates two triggers, firing first
and third (force_default_value() being second) they will see two
different values in that field.

It would be interesting to see what other DBs do with ALWAYS and BEFORE
triggers (whether they see the user supplied or generated value).



I think a longer term solution would be to add a type to pg_attrdef and
a bool for ALWAYS. (Tom?)
- default, identity, and generator being the types.

- SERIAL would be converted into an identity (not the other way around).

- psql would not show defaults in the case of an identity.

- pg_dump could be trained to use the identity syntax on the table which
has the added benefit of not relying on sequence naming convention.

- rewriteTargetList() can be used to enforce the default value from the
client perspective only (triggers, user rules, etc. could all override
ALWAYS default but the spec does not mention otherwise).

Re: SQL2003 GENERATED ... AS ... syntax

From
des@des.no (Dag-Erling Smørgrav)
Date:
Rod Taylor <rbt@rbt.ca> writes:
> I think a longer term solution would be to add a type to pg_attrdef and
> a bool for ALWAYS. (Tom?)

I thought about it, but won't that change the on-disk format?  Since
minor version upgrades aren't supposed to require a dump / restore,
and I understand 7.4 is already in feature freeze, the earliest
opportunity for something like this would be 7.5.

DES
--
Dag-Erling Smørgrav - des@des.no


Re: SQL2003 GENERATED ... AS ... syntax

From
Rod Taylor
Date:
On Sun, 2003-08-03 at 10:31, Dag-Erling Smørgrav wrote:
> Rod Taylor <rbt@rbt.ca> writes:
> > I think a longer term solution would be to add a type to pg_attrdef and
> > a bool for ALWAYS. (Tom?)
>
> I thought about it, but won't that change the on-disk format?  Since
> minor version upgrades aren't supposed to require a dump / restore,
> and I understand 7.4 is already in feature freeze, the earliest
> opportunity for something like this would be 7.5.

Yes it would. The solution you have already requires an initdb (changed
pg_proc.h), as such will probably need to wait until 7.5 for
integration. You might be able to squeeze it in as a contrib module for
7.4 though.

Re: SQL2003 GENERATED ... AS ... syntax

From
Tom Lane
Date:
des@des.no (Dag-Erling Smørgrav) writes:
> I thought about it, but won't that change the on-disk format?  Since
> minor version upgrades aren't supposed to require a dump / restore,
> and I understand 7.4 is already in feature freeze, the earliest
> opportunity for something like this would be 7.5.

The earliest opportunity is 7.5 anyway.  We don't do feature additions
in dot-releases, only bug fixes.

I don't much care for a trigger-based implementation of the
functionality; it seems like a crude way to do it.  Seems like an easier
answer would involve rejecting attempts to set the column explicitly
during the rewriter stage that inserts default values.

I haven't looked at the SQL200x spec so I'm not sure exactly how they
define GENERATED ALWAYS --- is it an error to try to override the
default, or is your attempt silently ignored?  We could do either.
        regards, tom lane


Re: SQL2003 GENERATED ... AS ... syntax

From
Tom Lane
Date:
Rod Taylor <rbt@rbt.ca> writes:
> Yes it would. The solution you have already requires an initdb (changed
> pg_proc.h), as such will probably need to wait until 7.5 for
> integration. You might be able to squeeze it in as a contrib module for
> 7.4 though.

Given that it also requires grammar and parser changes, I don't see how
it could exist as contrib at all.

I recommend dropping any thought of this appearing in any form in any
7.4.* release; it just ain't gonna happen that way.  Design a clean
solution for 7.5, instead.
        regards, tom lane


Re: SQL2003 GENERATED ... AS ... syntax

From
Rod Taylor
Date:
On Sun, 2003-08-03 at 14:35, Tom Lane wrote:
> Rod Taylor <rbt@rbt.ca> writes:
> > Yes it would. The solution you have already requires an initdb (changed
> > pg_proc.h), as such will probably need to wait until 7.5 for
> > integration. You might be able to squeeze it in as a contrib module for
> > 7.4 though.
>
> Given that it also requires grammar and parser changes, I don't see how
> it could exist as contrib at all.
>
> I recommend dropping any thought of this appearing in any form in any
> 7.4.* release; it just ain't gonna happen that way.  Design a clean
> solution for 7.5, instead.

I see.. I was thinking of the userlocks module which has a partial
implementation in the backend, with the 'work' units in contrib.