Thread: My very first PL/pgSQL procedure...

My very first PL/pgSQL procedure...

From
Philippe Ferreira
Date:
Hi,

I've got a problem with my very first PL/pgSQL procedure !
I created the following procedure, that should reconfigure a sequence :
---------------------------------------------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION seq_min(sequence_name varchar, minval
integer) RETURNS VOID AS $$
DECLARE
   current_seq integer;
BEGIN
   LOCK TABLE sequence_name IN ACCESS EXCLUSIVE MODE;
   current_seq := last_value FROM sequence_name;
   IF current_seq < minval THEN
     ALTER SEQUENCE sequence_name RESTART WITH minval;
   END IF;
END;
$$ LANGUAGE plpgsql;
---------------------------------------------------------------------------------------------------------------------


I call it from the psql interface by :
      SELECT seq_min('seq_mytable', 1029);

But PostgreSQL returns the following error (translated from french) :

ERROR:  syntax error on or near «$1» at character 13
QUERY : LOCK TABLE  $1  IN ACCESS EXCLUSIVE MODE
CONTEXT : PL/pgSQL function "seq_min" line 4 at SQL statement
LINE 1 : LOCK TABLE  $1  IN ACCESS EXCLUSIVE MODE

So it seems that PostgreSQL have troubles handling my variable
"sequence_name"...
Any idea ?

Thank you in advance,
Philippe Ferreira, France.

Re: My very first PL/pgSQL procedure...

From
"Jim Buttafuoco"
Date:
you need to use EXECUTE to do the dynamic lock table.

sql = 'LOCK TABLE ' || sequence_name  || 'IN ACCESS EXCLUSIVE MODE';
EXECUTE sql;

---------- Original Message -----------
From: Philippe Ferreira <phil.f@worldonline.fr>
To: pgsql-general@postgresql.org
Sent: Wed, 25 Jan 2006 18:37:21 +0100
Subject: [GENERAL] My very first PL/pgSQL procedure...

> Hi,
>
> I've got a problem with my very first PL/pgSQL procedure !
> I created the following procedure, that should reconfigure a sequence :
> ---------------------------------------------------------------------------------------------------------------------
>
> CREATE OR REPLACE FUNCTION seq_min(sequence_name varchar, minval
> integer) RETURNS VOID AS $$
> DECLARE
>    current_seq integer;
> BEGIN
>    LOCK TABLE sequence_name IN ACCESS EXCLUSIVE MODE;
>    current_seq := last_value FROM sequence_name;
>    IF current_seq < minval THEN
>      ALTER SEQUENCE sequence_name RESTART WITH minval;
>    END IF;
> END;
> $$ LANGUAGE plpgsql;
> ---------------------------------------------------------------------------------------------------------------------
>
> I call it from the psql interface by :
>       SELECT seq_min('seq_mytable', 1029);
>
> But PostgreSQL returns the following error (translated from french) :
>
> ERROR:  syntax error on or near «$1» at character 13
> QUERY : LOCK TABLE  $1  IN ACCESS EXCLUSIVE MODE
> CONTEXT : PL/pgSQL function "seq_min" line 4 at SQL statement
> LINE 1 : LOCK TABLE  $1  IN ACCESS EXCLUSIVE MODE
>
> So it seems that PostgreSQL have troubles handling my variable
> "sequence_name"...
> Any idea ?
>
> Thank you in advance,
> Philippe Ferreira, France.
>
> ---------------------------(end of broadcast)---------------------------
> TIP 3: Have you checked our extensive FAQ?
>
>                http://www.postgresql.org/docs/faq
------- End of Original Message -------


Re: My very first PL/pgSQL procedure...

From
Philippe Ferreira
Date:
>
>
>you need to use EXECUTE to do the dynamic lock table.
>
>sql = 'LOCK TABLE ' || sequence_name  || 'IN ACCESS EXCLUSIVE MODE';
>EXECUTE sql;
>
>


Thank you for your help  ;-)
I've been able to rewrite my procedure as follows :

---------------------------------------------------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION seq_min(sequence_name varchar, minval
integer) RETURNS VOID AS $$
DECLARE
  current_seq integer;
BEGIN
  EXECUTE 'LOCK TABLE ' || sequence_name || ' IN ACCESS EXCLUSIVE MODE';
  current_seq := last_value FROM sequence_name;
  IF current_seq < minval THEN
    EXECUTE 'ALTER SEQUENCE ' || sequence_name || ' RESTART WITH ' ||
minval;
  END IF;
END;
$$ LANGUAGE plpgsql;
---------------------------------------------------------------------------------------------------------------------

However, when I call : "SELECT seq_min('seq_mytable', 1029);"
I get this other error (translated from french) :

  ERROR:  «seq_mytable» is not a table
  CONTEXT : SQL instruction «LOCK TABLE seq_mytable IN ACCESS EXCLUSIVE
MODE»
  PL/pgSQL function "seq_min" line 4 at execute statement

So, it seems that it is impossible to lock a sequence !
If it is the case, how can I achieve the same result without locking the
sequence ?

Thank you again,
Philippe Ferreira.


Re: My very first PL/pgSQL procedure...

From
Philippe Ferreira
Date:
Hi,

The only solution I've found to get the same reliable result, but
without locking, is the dirty way (loops) :
---------------------------------------------------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION seq_min(sequence_name varchar, minval
integer) RETURNS VOID AS $$
DECLARE
  sequence_record RECORD;
BEGIN
  -- Get the current sequence value :
  FOR sequence_record IN EXECUTE 'SELECT last_value FROM ' ||
sequence_name LOOP
    NULL;
  END LOOP;
  -- Loop to bring the sequence to (at least) minval :
  WHILE sequence_record.last_value < minval LOOP
    -- Increment by 1 the sequence (and get the new value) :
    FOR sequence_record IN EXECUTE 'SELECT nextval(''' || sequence_name
|| ''') AS last_value' LOOP
      NULL;
    END LOOP;
  END LOOP;
  RETURN;
END;
$$ LANGUAGE plpgsql;
---------------------------------------------------------------------------------------------------------------------

It gives the result I expect (and it doesn't interfere with concurrent
uses of the sequence), but it is not very optimized !
So, if someone have a better idea, I'm still open !

Thank you,
Philippe Ferreira.

Re: My very first PL/pgSQL procedure...

From
"Jim Buttafuoco"
Date:
why not just use setval(), see docs for arguments.


---------- Original Message -----------
From: Philippe Ferreira <phil.f@worldonline.fr>
To: pgsql-general@postgresql.org
Sent: Wed, 25 Jan 2006 22:11:11 +0100
Subject: Re: [GENERAL] My very first PL/pgSQL procedure...

> Hi,
>
> The only solution I've found to get the same reliable result, but
> without locking, is the dirty way (loops) :
> ---------------------------------------------------------------------------------------------------------------------
> CREATE OR REPLACE FUNCTION seq_min(sequence_name varchar, minval
> integer) RETURNS VOID AS $$
> DECLARE
>   sequence_record RECORD;
> BEGIN
>   -- Get the current sequence value :
>   FOR sequence_record IN EXECUTE 'SELECT last_value FROM ' ||
> sequence_name LOOP
>     NULL;
>   END LOOP;
>   -- Loop to bring the sequence to (at least) minval :
>   WHILE sequence_record.last_value < minval LOOP
>     -- Increment by 1 the sequence (and get the new value) :
>     FOR sequence_record IN EXECUTE 'SELECT nextval(''' || sequence_name
> || ''') AS last_value' LOOP
>       NULL;
>     END LOOP;
>   END LOOP;
>   RETURN;
> END;
> $$ LANGUAGE plpgsql;
> ---------------------------------------------------------------------------------------------------------------------
>
> It gives the result I expect (and it doesn't interfere with concurrent
> uses of the sequence), but it is not very optimized !
> So, if someone have a better idea, I'm still open !
>
> Thank you,
> Philippe Ferreira.
>
> ---------------------------(end of broadcast)---------------------------
> TIP 1: if posting/reading through Usenet, please send an appropriate
>        subscribe-nomail command to majordomo@postgresql.org so that your
>        message can get through to the mailing list cleanly
------- End of Original Message -------


Re: My very first PL/pgSQL procedure...

From
Philippe Ferreira
Date:
> why not just use setval(), see docs for arguments.


I think that "setval('seq_name', xx)" have the same effect than
"SEQUENCE seq_name RESTART WITH xx" (the instruction I
wanted to use in my first function).

But the problem is that in both cases, the sequence should be
locked in order to prevent problems with concurrent transactions.
For example, if I want to raise the sequence value to "1000", while
its current value is "998", I would call :
     setval('seq_name', 1000);

But because the sequence could not be locked, some concurrent
transactions could have already raised it's current value in the
meantime to, say, "1002", before the effective execution of setval().

So, instead of raising the value to 1000, my function could have
done the opposite (from 1002 to 1000) ! And the two next "INSERT"
using this sequence would then break !!

The only solution I found to prevent this is with my "loops" !!

Best regards,
Philippe Ferreira.

Re: My very first PL/pgSQL procedure...

From
Martijn van Oosterhout
Date:
On Thu, Jan 26, 2006 at 09:04:25PM +0100, Philippe Ferreira wrote:
> But because the sequence could not be locked, some concurrent
> transactions could have already raised it's current value in the
> meantime to, say, "1002", before the effective execution of setval().

Umm, locking sequences won't ever happen. The *whole point* of
sequences is that you get a unique number and you don't have to wait
for it. By locking you'd be telling other people they have to wait.

> So, instead of raising the value to 1000, my function could have
> done the opposite (from 1002 to 1000) ! And the two next "INSERT"
> using this sequence would then break !!

I think you need to provide a rationale why you want to control the
value of the sequence in such a way anyway, because you're trying to do
something that the system isn't likely to support. The numbers are
supposed to be "opaque", the actual values are not supposed to be
relevent.

Have a nice day,
--
Martijn van Oosterhout   <kleptog@svana.org>   http://svana.org/kleptog/
> Patent. n. Genius is 5% inspiration and 95% perspiration. A patent is a
> tool for doing 5% of the work and then sitting around waiting for someone
> else to do the other 95% so you can sue them.

Attachment

Re: My very first PL/pgSQL procedure...

From
Philippe Ferreira
Date:
>I think you need to provide a rationale why you want to control the
>value of the sequence in such a way anyway, because you're trying to do
>something that the system isn't likely to support. The numbers are
>supposed to be "opaque", the actual values are not supposed to be
>relevent.
>
>Have a nice day,
>
>
Hi,

Actually, I'm building a switchover/failover system (based on WAL) for
my own needs.

There is no problem for the switchover, because the data is properly
synched during
this procedure.

However, when I have to start the failover on a secondary server, some
changes can
be "uncommited" at the time of the failover.
So, as soon as the primary PostgreSQL server is up again, I have to resync
(automatically or manually) these "uncommited" changes to the failover
node, before
switching properly to the primary.

Actually, I've got a sequence which generates order numbers. These codes
will be
always unique, even in case of failover, since I alter the definition of the
sequence depending on the server (primary or secondary), with a suffix
of 'A' or 'B'.
For example, imagine the following scenario :

------------------------------------------------

Server A :         Server B :
==========         ==========
1000A
1001A
1002A
    ---[SWITCHOVER]-->
                     1003B
                     1004B
                     1005B
                     1006B
    <--[SWITCHOVER]---
1007A
1008A
1009A
1010A  [FAILOVER]--> 1010B
1011A                1011B
1012A                  -
1013A                  -
        [RESYNC]-->  1010A
        [RESYNC]-->  1011A
        [RESYNC]-->  1012A
        [RESYNC]-->  1013A
    <--[SWITCHOVER]---
1014A
1015A

------------------------------------------------

So, in this example, if I don't increment the last value of my
sequence from 1011 to 1013 (before my last "SWITCHOVER"), it
will output "1012A" (instead of 1014A), and my new record will
conflict with an existing one !!

Thank you for your interest,
Philippe Ferreira.

Re: My very first PL/pgSQL procedure...

From
"Jim Buttafuoco"
Date:
why not have server a start at 1 and server b start at max int8/2.  that will give each server alot of orders before
you have an issue.  They will will not have to play games with the sequence value.

Jim


---------- Original Message -----------
From: Philippe Ferreira <phil.f@worldonline.fr>
To: Martijn van Oosterhout <kleptog@svana.org>
Cc: jim@contactbda.com, pgsql-general@postgresql.org
Sent: Sat, 28 Jan 2006 17:46:55 +0100
Subject: Re: [GENERAL] My very first PL/pgSQL procedure...

> >I think you need to provide a rationale why you want to control the
> >value of the sequence in such a way anyway, because you're trying to do
> >something that the system isn't likely to support. The numbers are
> >supposed to be "opaque", the actual values are not supposed to be
> >relevent.
> >
> >Have a nice day,
> >
> >
> Hi,
>
> Actually, I'm building a switchover/failover system (based on WAL) for
> my own needs.
>
> There is no problem for the switchover, because the data is properly
> synched during
> this procedure.
>
> However, when I have to start the failover on a secondary server, some
> changes can
> be "uncommited" at the time of the failover.
> So, as soon as the primary PostgreSQL server is up again, I have to resync
> (automatically or manually) these "uncommited" changes to the failover
> node, before
> switching properly to the primary.
>
> Actually, I've got a sequence which generates order numbers. These codes
> will be
> always unique, even in case of failover, since I alter the definition of the
> sequence depending on the server (primary or secondary), with a suffix
> of 'A' or 'B'.
> For example, imagine the following scenario :
>
> ------------------------------------------------
>
> Server A :         Server B :
> ==========         ==========
> 1000A
> 1001A
> 1002A
>     ---[SWITCHOVER]-->
>                      1003B
>                      1004B
>                      1005B
>                      1006B
>     <--[SWITCHOVER]---
> 1007A
> 1008A
> 1009A
> 1010A  [FAILOVER]--> 1010B
> 1011A                1011B
> 1012A                  -
> 1013A                  -
>         [RESYNC]-->  1010A
>         [RESYNC]-->  1011A
>         [RESYNC]-->  1012A
>         [RESYNC]-->  1013A
>     <--[SWITCHOVER]---
> 1014A
> 1015A
>
> ------------------------------------------------
>
> So, in this example, if I don't increment the last value of my
> sequence from 1011 to 1013 (before my last "SWITCHOVER"), it
> will output "1012A" (instead of 1014A), and my new record will
> conflict with an existing one !!
>
> Thank you for your interest,
> Philippe Ferreira.
>
> ---------------------------(end of broadcast)---------------------------
> TIP 9: In versions below 8.0, the planner will ignore your desire to
>        choose an index scan if your joining column's datatypes do not
>        match
------- End of Original Message -------


Re: My very first PL/pgSQL procedure...

From
Philippe Ferreira
Date:
>why not have server a start at 1 and server b start at max int8/2.  that will give each server alot of orders before
>you have an issue.  They will will not have to play games with the sequence value.
>
>Jim
>
>
Hi,

Your method is different from mine, but I think that both methods will
face the same constraints in case of failover...

Anyway, my setup is working fine, so I won't change anything !
The only point is that it could have been useful to be able to lock a
sequence...
It seems that it was possible with PostgreSQL 7.0, but was removed in 7.1 :
    http://archives.postgresql.org/pgsql-hackers/2001-10/msg00928.php

Best regards,
Philippe Ferreira.