Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 657c098

Browse files
author
Neil Conway
committed
Add a function lastval(), which returns the value returned by the
last nextval() or setval() performed by the current session. Update the docs, add regression tests, and bump the catalog version. Patch from Dennis Björklund, various improvements by Neil Conway.
1 parent c59887f commit 657c098

File tree

7 files changed

+202
-36
lines changed

7 files changed

+202
-36
lines changed

doc/src/sgml/func.sgml

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.251 2005/06/06 16:29:01 tgl Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.252 2005/06/07 07:08:34 neilc Exp $
33
PostgreSQL documentation
44
-->
55

@@ -6487,6 +6487,9 @@ SELECT TIMESTAMP 'now'; -- incorrect for use with DEFAULT
64876487
<indexterm>
64886488
<primary>currval</primary>
64896489
</indexterm>
6490+
<indexterm>
6491+
<primary>lastval</primary>
6492+
</indexterm>
64906493
<indexterm>
64916494
<primary>setval</primary>
64926495
</indexterm>
@@ -6519,6 +6522,12 @@ SELECT TIMESTAMP 'now'; -- incorrect for use with DEFAULT
65196522
<row>
65206523
<entry><literal><function>currval</function>(<type>text</type>)</literal></entry>
65216524
<entry><type>bigint</type></entry>
6525+
<entry>Return value most recently obtained with
6526+
<function>nextval</function> for specified sequence</entry>
6527+
</row>
6528+
<row>
6529+
<entry><literal><function>lastval</function>()</literal></entry>
6530+
<entry><type>bigint</type></entry>
65226531
<entry>Return value most recently obtained with <function>nextval</function></entry>
65236532
</row>
65246533
<row>
@@ -6587,6 +6596,22 @@ nextval('foo') <lineannotation>searches search path for <literal>fo
65876596
</listitem>
65886597
</varlistentry>
65896598

6599+
<varlistentry>
6600+
<term><function>lastval</function></term>
6601+
<listitem>
6602+
<para>
6603+
Return the value most recently returned by
6604+
<function>nextval</> in the current session. This function is
6605+
identical to <function>currval</function>, except that instead
6606+
of taking the sequence name as an argument it fetches the
6607+
value of the last sequence that <function>nextval</function>
6608+
was used on in the current session. It is an error to call
6609+
<function>lastval</function> if <function>nextval</function>
6610+
has not yet been called in the current session.
6611+
</para>
6612+
</listitem>
6613+
</varlistentry>
6614+
65906615
<varlistentry>
65916616
<term><function>setval</function></term>
65926617
<listitem>

src/backend/commands/sequence.c

Lines changed: 83 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.122 2005/06/06 20:22:57 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.123 2005/06/07 07:08:34 neilc Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -24,6 +24,7 @@
2424
#include "utils/acl.h"
2525
#include "utils/builtins.h"
2626
#include "utils/resowner.h"
27+
#include "utils/syscache.h"
2728

2829

2930
/*
@@ -68,7 +69,13 @@ typedef SeqTableData *SeqTable;
6869

6970
static SeqTable seqtab = NULL; /* Head of list of SeqTable items */
7071

72+
/*
73+
* last_used_seq is updated by nextval() to point to the last used
74+
* sequence.
75+
*/
76+
static SeqTableData *last_used_seq = NULL;
7177

78+
static void acquire_share_lock(Relation seqrel, SeqTable seq);
7279
static void init_sequence(RangeVar *relation,
7380
SeqTable *p_elm, Relation *p_rel);
7481
static Form_pg_sequence read_info(SeqTable elm, Relation rel, Buffer *buf);
@@ -400,6 +407,7 @@ nextval(PG_FUNCTION_ARGS)
400407

401408
if (elm->last != elm->cached) /* some numbers were cached */
402409
{
410+
last_used_seq = elm;
403411
elm->last += elm->increment;
404412
relation_close(seqrel, NoLock);
405413
PG_RETURN_INT64(elm->last);
@@ -521,6 +529,8 @@ nextval(PG_FUNCTION_ARGS)
521529
elm->last = result; /* last returned number */
522530
elm->cached = last; /* last fetched number */
523531

532+
last_used_seq = elm;
533+
524534
START_CRIT_SECTION();
525535

526536
/* XLOG stuff */
@@ -602,6 +612,42 @@ currval(PG_FUNCTION_ARGS)
602612
PG_RETURN_INT64(result);
603613
}
604614

615+
Datum
616+
lastval(PG_FUNCTION_ARGS)
617+
{
618+
Relation seqrel;
619+
int64 result;
620+
621+
if (last_used_seq == NULL)
622+
ereport(ERROR,
623+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
624+
errmsg("lastval is not yet defined in this session")));
625+
626+
/* Someone may have dropped the sequence since the last nextval() */
627+
if (!SearchSysCacheExists(RELOID,
628+
ObjectIdGetDatum(last_used_seq->relid),
629+
0, 0, 0))
630+
ereport(ERROR,
631+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
632+
errmsg("lastval is not yet defined in this session")));
633+
634+
seqrel = relation_open(last_used_seq->relid, NoLock);
635+
acquire_share_lock(seqrel, last_used_seq);
636+
637+
/* nextval() must have already been called for this sequence */
638+
Assert(last_used_seq->increment != 0);
639+
640+
if (pg_class_aclcheck(last_used_seq->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK)
641+
ereport(ERROR,
642+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
643+
errmsg("permission denied for sequence %s",
644+
RelationGetRelationName(seqrel))));
645+
646+
result = last_used_seq->last;
647+
relation_close(seqrel, NoLock);
648+
PG_RETURN_INT64(result);
649+
}
650+
605651
/*
606652
* Main internal procedure that handles 2 & 3 arg forms of SETVAL.
607653
*
@@ -740,6 +786,41 @@ setval_and_iscalled(PG_FUNCTION_ARGS)
740786
}
741787

742788

789+
/*
790+
* If we haven't touched the sequence already in this transaction,
791+
* we need to acquire AccessShareLock. We arrange for the lock to
792+
* be owned by the top transaction, so that we don't need to do it
793+
* more than once per xact.
794+
*/
795+
static void
796+
acquire_share_lock(Relation seqrel, SeqTable seq)
797+
{
798+
TransactionId thisxid = GetTopTransactionId();
799+
800+
if (seq->xid != thisxid)
801+
{
802+
ResourceOwner currentOwner;
803+
804+
currentOwner = CurrentResourceOwner;
805+
PG_TRY();
806+
{
807+
CurrentResourceOwner = TopTransactionResourceOwner;
808+
LockRelation(seqrel, AccessShareLock);
809+
}
810+
PG_CATCH();
811+
{
812+
/* Ensure CurrentResourceOwner is restored on error */
813+
CurrentResourceOwner = currentOwner;
814+
PG_RE_THROW();
815+
}
816+
PG_END_TRY();
817+
CurrentResourceOwner = currentOwner;
818+
819+
/* Flag that we have a lock in the current xact. */
820+
seq->xid = thisxid;
821+
}
822+
}
823+
743824
/*
744825
* Given a relation name, open and lock the sequence. p_elm and p_rel are
745826
* output parameters.
@@ -748,7 +829,6 @@ static void
748829
init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel)
749830
{
750831
Oid relid = RangeVarGetRelid(relation, false);
751-
TransactionId thisxid = GetTopTransactionId();
752832
volatile SeqTable elm;
753833
Relation seqrel;
754834

@@ -796,35 +876,7 @@ init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel)
796876
seqtab = elm;
797877
}
798878

799-
/*
800-
* If we haven't touched the sequence already in this transaction,
801-
* we need to acquire AccessShareLock. We arrange for the lock to
802-
* be owned by the top transaction, so that we don't need to do it
803-
* more than once per xact.
804-
*/
805-
if (elm->xid != thisxid)
806-
{
807-
ResourceOwner currentOwner;
808-
809-
currentOwner = CurrentResourceOwner;
810-
PG_TRY();
811-
{
812-
CurrentResourceOwner = TopTransactionResourceOwner;
813-
814-
LockRelation(seqrel, AccessShareLock);
815-
}
816-
PG_CATCH();
817-
{
818-
/* Ensure CurrentResourceOwner is restored on error */
819-
CurrentResourceOwner = currentOwner;
820-
PG_RE_THROW();
821-
}
822-
PG_END_TRY();
823-
CurrentResourceOwner = currentOwner;
824-
825-
/* Flag that we have a lock in the current xact. */
826-
elm->xid = thisxid;
827-
}
879+
acquire_share_lock(seqrel, elm);
828880

829881
*p_elm = elm;
830882
*p_rel = seqrel;

src/include/catalog/catversion.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
3838
* Portions Copyright (c) 1994, Regents of the University of California
3939
*
40-
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.272 2005/05/30 20:59:17 momjian Exp $
40+
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.273 2005/06/07 07:08:34 neilc Exp $
4141
*
4242
*-------------------------------------------------------------------------
4343
*/
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/* yyyymmddN */
56-
#define CATALOG_VERSION_NO 200505302
56+
#define CATALOG_VERSION_NO 200506071
5757

5858
#endif

src/include/catalog/pg_proc.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.363 2005/05/20 01:29:55 neilc Exp $
10+
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.364 2005/06/07 07:08:34 neilc Exp $
1111
*
1212
* NOTES
1313
* The script catalog/genbki.sh reads this file and generates .bki
@@ -3644,6 +3644,8 @@ DATA(insert OID = 2557 ( bool PGNSP PGUID 12 f f t f i 1 16 "23" _null_ _
36443644
DESCR("convert int4 to boolean");
36453645
DATA(insert OID = 2558 ( int4 PGNSP PGUID 12 f f t f i 1 23 "16" _null_ _null_ _null_ bool_int4 - _null_ ));
36463646
DESCR("convert boolean to int4");
3647+
DATA(insert OID = 2559 ( lastval PGNSP PGUID 12 f f t f v 0 20 "" _null_ _null_ _null_ lastval - _null_ ));
3648+
DESCR("current value from last used sequence");
36473649

36483650

36493651
/*

src/include/commands/sequence.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
9-
* $PostgreSQL: pgsql/src/include/commands/sequence.h,v 1.31 2005/06/06 17:01:25 tgl Exp $
9+
* $PostgreSQL: pgsql/src/include/commands/sequence.h,v 1.32 2005/06/07 07:08:35 neilc Exp $
1010
*
1111
*-------------------------------------------------------------------------
1212
*/
@@ -82,6 +82,7 @@ typedef struct xl_seq_rec
8282

8383
extern Datum nextval(PG_FUNCTION_ARGS);
8484
extern Datum currval(PG_FUNCTION_ARGS);
85+
extern Datum lastval(PG_FUNCTION_ARGS);
8586
extern Datum setval(PG_FUNCTION_ARGS);
8687
extern Datum setval_and_iscalled(PG_FUNCTION_ARGS);
8788

src/test/regress/expected/sequence.out

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,62 @@ COMMENT ON SEQUENCE asdf IS 'won''t work';
7676
ERROR: relation "asdf" does not exist
7777
COMMENT ON SEQUENCE sequence_test2 IS 'will work';
7878
COMMENT ON SEQUENCE sequence_test2 IS NULL;
79+
-- Test lastval()
80+
CREATE SEQUENCE seq;
81+
SELECT nextval('seq');
82+
nextval
83+
---------
84+
1
85+
(1 row)
86+
87+
SELECT lastval();
88+
lastval
89+
---------
90+
1
91+
(1 row)
92+
93+
SELECT setval('seq', 99);
94+
setval
95+
--------
96+
99
97+
(1 row)
98+
99+
SELECT lastval();
100+
lastval
101+
---------
102+
99
103+
(1 row)
104+
105+
CREATE SEQUENCE seq2;
106+
SELECT nextval('seq2');
107+
nextval
108+
---------
109+
1
110+
(1 row)
111+
112+
SELECT lastval();
113+
lastval
114+
---------
115+
1
116+
(1 row)
117+
118+
DROP SEQUENCE seq2;
119+
-- should fail
120+
SELECT lastval();
121+
ERROR: lastval is not yet defined in this session
122+
CREATE USER seq_user;
123+
BEGIN;
124+
SET LOCAL SESSION AUTHORIZATION seq_user;
125+
CREATE SEQUENCE seq3;
126+
SELECT nextval('seq3');
127+
nextval
128+
---------
129+
1
130+
(1 row)
131+
132+
REVOKE ALL ON seq3 FROM seq_user;
133+
SELECT lastval();
134+
ERROR: permission denied for sequence seq3
135+
ROLLBACK;
136+
DROP USER seq_user;
137+
DROP SEQUENCE seq;

src/test/regress/sql/sequence.sql

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,30 @@ COMMENT ON SEQUENCE asdf IS 'won''t work';
4242
COMMENT ON SEQUENCE sequence_test2 IS 'will work';
4343
COMMENT ON SEQUENCE sequence_test2 IS NULL;
4444

45+
-- Test lastval()
46+
CREATE SEQUENCE seq;
47+
SELECT nextval('seq');
48+
SELECT lastval();
49+
SELECT setval('seq', 99);
50+
SELECT lastval();
51+
52+
CREATE SEQUENCE seq2;
53+
SELECT nextval('seq2');
54+
SELECT lastval();
55+
56+
DROP SEQUENCE seq2;
57+
-- should fail
58+
SELECT lastval();
59+
60+
CREATE USER seq_user;
61+
62+
BEGIN;
63+
SET LOCAL SESSION AUTHORIZATION seq_user;
64+
CREATE SEQUENCE seq3;
65+
SELECT nextval('seq3');
66+
REVOKE ALL ON seq3 FROM seq_user;
67+
SELECT lastval();
68+
ROLLBACK;
69+
70+
DROP USER seq_user;
71+
DROP SEQUENCE seq;

0 commit comments

Comments
 (0)