Thread: Best way to create a sequence generator at run time?

Best way to create a sequence generator at run time?

From
Leon Starr
Date:
Hi all,

I am trying to write plpgsql that must create a sequence generators during runtime.  The min/max values are provided as
functionparameters. 

The following isn't working for me and I'm not too surprised, but not sure of the best way to proceed.  I am guessing I
needto use PERFORM, EXECUTE or cursors or something? 

==
create or replace function my_number_assigner(
    p_floor int,
    p_ceiling int
) returns void as
$$
    create sequence num_generator minvalue p_floor maxvalue p_ceiling start with p_floor;
end
$$
language plpgsql;
==

 ERROR:  syntax error at or near "$1"
LINE 1: create sequence num_generator minvalue  $1  maxvalue  $2  s...
                                                 ^
QUERY:  create sequence num_generator minvalue  $1  maxvalue  $2  start with  $1
CONTEXT:  SQL statement in PL/PgSQL function "my_number_assigner" near line 35

I presume that the create sequence expression wants to see literals instead of variables, right?  I knew I was going to
runinto this situation sooner or later.  What should I be doing here? 

- Leon





Re: Best way to create a sequence generator at run time?

From
Tom Lane
Date:
Leon Starr <leon_starr@modelint.com> writes:
> I presume that the create sequence expression wants to see literals instead of variables, right?  I knew I was going
torun into this situation sooner or later.  What should I be doing here? 

You need to construct the CREATE SEQUENCE command as a string then
EXECUTE it.  CREATE SEQUENCE, like most other utility commands, doesn't
handle parameters well.

            regards, tom lane

Re: Best way to create a sequence generator at run time?

From
Leon Starr
Date:
Thanks, Tom!  Makes perfect sense.

I would like to do something now, just a bit more advanced, but I can't help thinking that there must be a standard
solutionand thought maybe you could point me in the right direction. 

I want to number a relvar (table) as a sub sequence of another relvar.  So if I have Department and Document with
Documentsnumbered within each Department, I am wondering what is the best approach.  It's easy enough to just slap a
sequencetype on Department.Number.  But what about Document?  I would need a new sequence object for each relation
(row)in Department, since each Department handles its own document sequence.  It's almost like I should have an
attributeof Department of type 'sequence generator'.  Is that doable? 

Otherwise, I am thinking of just using a naming scheme where each Department's sequence generator would be named
somethinglike this: <dept_<dept_number>_docnumbering_seq 

The question then is simply:  Am I heading down the right road?  Or is there a simpler solution that is commonly
appliedin this case or some cool trick I am missing? 

Thanks.  - Leon


On Sep 21, 2010, at 2:56 PM, Tom Lane wrote:

> Leon Starr <leon_starr@modelint.com> writes:
>> I presume that the create sequence expression wants to see literals instead of variables, right?  I knew I was going
torun into this situation sooner or later.  What should I be doing here? 
>
> You need to construct the CREATE SEQUENCE command as a string then
> EXECUTE it.  CREATE SEQUENCE, like most other utility commands, doesn't
> handle parameters well.
>
>             regards, tom lane
>
> --
> Sent via pgsql-novice mailing list (pgsql-novice@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-novice



Re: Best way to create a sequence generator at run time?

From
Leon Starr
Date:
I'm converging on a good solution, but have just one (hopefully) problem left.
I need to get the nextval for a sequence whose name has been stored, and is thus
not available prior to runtime.  I am trying to use EXECUTE, but can't seem to get
it right.  Suggestions?

Here's the function extract in question:

create or replace function ...
) returns bigint as
$$
declare
    self subsystem%rowtype; -- has an attribute where the sequence name is stored
begin
    select * from subsystem into strict self where (name = p_subsystem and domain = p_domain);

    -- self.cnum_generator is a text value holding the name of the previously created sequence
    -- I've tested to ensure that it is holding the correct value, so no worries there, it is a valid sequence

    -- Now here is the trouble - none of these statements seem to work or parse correctly:

    return execute 'nextval(' || self.cnum_generator || ')';  -- NOPE

    new_num := nextval(self.cnum_generator);  -- I didn't expect this one to work, but might as well try, NOPE

    return query execute 'nextval( $1 )' using self.cnum_generator;  -- no good either

    return query execute 'nextval(' || self.cnum_generator || ')';  -- NOPE

    -- accck!  Phhht!  Help!


Re: Best way to create a sequence generator at run time?

From
Leon Starr
Date:
Well I guess I'll be answering all my own questions today ;)

And the correct answer turns out to be....

    execute 'select nextval(' || quote_literal(self.cnum_generator) || ')' into my_cnum;
    return my_cnum;

Was hoping to avoid the temporary variable (my_cnum), but hey, it works!

And I'm off to the 9.0 release party in SF, see everyone there even though I won't know who anyone is!

- Leon

> I'm converging on a good solution, but have just one (hopefully) problem left.
> I need to get the nextval for a sequence whose name has been stored, and is thus
> not available prior to runtime.  I am trying to use EXECUTE, but can't seem to get
> it right.  Suggestions?
>
> Here's the function extract in question:
>
> create or replace function ...
> ) returns bigint as
> $$
> declare
>     self subsystem%rowtype; -- has an attribute where the sequence name is stored
> begin
>     select * from subsystem into strict self where (name = p_subsystem and domain = p_domain);
>
>     -- self.cnum_generator is a text value holding the name of the previously created sequence
>     -- I've tested to ensure that it is holding the correct value, so no worries there, it is a valid sequence
>
>     -- Now here is the trouble - none of these statements seem to work or parse correctly:
>
>     return execute 'nextval(' || self.cnum_generator || ')';  -- NOPE
>
>     new_num := nextval(self.cnum_generator);  -- I didn't expect this one to work, but might as well try, NOPE
>
>     return query execute 'nextval( $1 )' using self.cnum_generator;  -- no good either
>
>     return query execute 'nextval(' || self.cnum_generator || ')';  -- NOPE
>
>     -- accck!  Phhht!  Help!