aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeil Conway <neilc@samurai.com>2005-06-07 07:08:35 +0000
committerNeil Conway <neilc@samurai.com>2005-06-07 07:08:35 +0000
commit657c098e41b0bb29d30d13d9aa1ac858a07f3493 (patch)
tree6d55885d0e4fd2dbeec8edde846fc33dcebdcebe
parentc59887f91618b95f42a33d4c62dac35165a7910a (diff)
downloadpostgresql-657c098e41b0bb29d30d13d9aa1ac858a07f3493.tar.gz
postgresql-657c098e41b0bb29d30d13d9aa1ac858a07f3493.zip
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.
-rw-r--r--doc/src/sgml/func.sgml27
-rw-r--r--src/backend/commands/sequence.c114
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/catalog/pg_proc.h4
-rw-r--r--src/include/commands/sequence.h3
-rw-r--r--src/test/regress/expected/sequence.out59
-rw-r--r--src/test/regress/sql/sequence.sql27
7 files changed, 202 insertions, 36 deletions
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 5bd29793e81..7eb2c4c6b13 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -1,5 +1,5 @@
<!--
-$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.251 2005/06/06 16:29:01 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.252 2005/06/07 07:08:34 neilc Exp $
PostgreSQL documentation
-->
@@ -6488,6 +6488,9 @@ SELECT TIMESTAMP 'now'; -- incorrect for use with DEFAULT
<primary>currval</primary>
</indexterm>
<indexterm>
+ <primary>lastval</primary>
+ </indexterm>
+ <indexterm>
<primary>setval</primary>
</indexterm>
@@ -6519,6 +6522,12 @@ SELECT TIMESTAMP 'now'; -- incorrect for use with DEFAULT
<row>
<entry><literal><function>currval</function>(<type>text</type>)</literal></entry>
<entry><type>bigint</type></entry>
+ <entry>Return value most recently obtained with
+ <function>nextval</function> for specified sequence</entry>
+ </row>
+ <row>
+ <entry><literal><function>lastval</function>()</literal></entry>
+ <entry><type>bigint</type></entry>
<entry>Return value most recently obtained with <function>nextval</function></entry>
</row>
<row>
@@ -6588,6 +6597,22 @@ nextval('foo') <lineannotation>searches search path for <literal>fo
</varlistentry>
<varlistentry>
+ <term><function>lastval</function></term>
+ <listitem>
+ <para>
+ Return the value most recently returned by
+ <function>nextval</> in the current session. This function is
+ identical to <function>currval</function>, except that instead
+ of taking the sequence name as an argument it fetches the
+ value of the last sequence that <function>nextval</function>
+ was used on in the current session. It is an error to call
+ <function>lastval</function> if <function>nextval</function>
+ has not yet been called in the current session.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><function>setval</function></term>
<listitem>
<para>
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 78b9225b83c..5af9ba55b6e 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.122 2005/06/06 20:22:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.123 2005/06/07 07:08:34 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@@ -24,6 +24,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/resowner.h"
+#include "utils/syscache.h"
/*
@@ -68,7 +69,13 @@ typedef SeqTableData *SeqTable;
static SeqTable seqtab = NULL; /* Head of list of SeqTable items */
+/*
+ * last_used_seq is updated by nextval() to point to the last used
+ * sequence.
+ */
+static SeqTableData *last_used_seq = NULL;
+static void acquire_share_lock(Relation seqrel, SeqTable seq);
static void init_sequence(RangeVar *relation,
SeqTable *p_elm, Relation *p_rel);
static Form_pg_sequence read_info(SeqTable elm, Relation rel, Buffer *buf);
@@ -400,6 +407,7 @@ nextval(PG_FUNCTION_ARGS)
if (elm->last != elm->cached) /* some numbers were cached */
{
+ last_used_seq = elm;
elm->last += elm->increment;
relation_close(seqrel, NoLock);
PG_RETURN_INT64(elm->last);
@@ -521,6 +529,8 @@ nextval(PG_FUNCTION_ARGS)
elm->last = result; /* last returned number */
elm->cached = last; /* last fetched number */
+ last_used_seq = elm;
+
START_CRIT_SECTION();
/* XLOG stuff */
@@ -602,6 +612,42 @@ currval(PG_FUNCTION_ARGS)
PG_RETURN_INT64(result);
}
+Datum
+lastval(PG_FUNCTION_ARGS)
+{
+ Relation seqrel;
+ int64 result;
+
+ if (last_used_seq == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("lastval is not yet defined in this session")));
+
+ /* Someone may have dropped the sequence since the last nextval() */
+ if (!SearchSysCacheExists(RELOID,
+ ObjectIdGetDatum(last_used_seq->relid),
+ 0, 0, 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("lastval is not yet defined in this session")));
+
+ seqrel = relation_open(last_used_seq->relid, NoLock);
+ acquire_share_lock(seqrel, last_used_seq);
+
+ /* nextval() must have already been called for this sequence */
+ Assert(last_used_seq->increment != 0);
+
+ if (pg_class_aclcheck(last_used_seq->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied for sequence %s",
+ RelationGetRelationName(seqrel))));
+
+ result = last_used_seq->last;
+ relation_close(seqrel, NoLock);
+ PG_RETURN_INT64(result);
+}
+
/*
* Main internal procedure that handles 2 & 3 arg forms of SETVAL.
*
@@ -741,6 +787,41 @@ setval_and_iscalled(PG_FUNCTION_ARGS)
/*
+ * If we haven't touched the sequence already in this transaction,
+ * we need to acquire AccessShareLock. We arrange for the lock to
+ * be owned by the top transaction, so that we don't need to do it
+ * more than once per xact.
+ */
+static void
+acquire_share_lock(Relation seqrel, SeqTable seq)
+{
+ TransactionId thisxid = GetTopTransactionId();
+
+ if (seq->xid != thisxid)
+ {
+ ResourceOwner currentOwner;
+
+ currentOwner = CurrentResourceOwner;
+ PG_TRY();
+ {
+ CurrentResourceOwner = TopTransactionResourceOwner;
+ LockRelation(seqrel, AccessShareLock);
+ }
+ PG_CATCH();
+ {
+ /* Ensure CurrentResourceOwner is restored on error */
+ CurrentResourceOwner = currentOwner;
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ CurrentResourceOwner = currentOwner;
+
+ /* Flag that we have a lock in the current xact. */
+ seq->xid = thisxid;
+ }
+}
+
+/*
* Given a relation name, open and lock the sequence. p_elm and p_rel are
* output parameters.
*/
@@ -748,7 +829,6 @@ static void
init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel)
{
Oid relid = RangeVarGetRelid(relation, false);
- TransactionId thisxid = GetTopTransactionId();
volatile SeqTable elm;
Relation seqrel;
@@ -796,35 +876,7 @@ init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel)
seqtab = elm;
}
- /*
- * If we haven't touched the sequence already in this transaction,
- * we need to acquire AccessShareLock. We arrange for the lock to
- * be owned by the top transaction, so that we don't need to do it
- * more than once per xact.
- */
- if (elm->xid != thisxid)
- {
- ResourceOwner currentOwner;
-
- currentOwner = CurrentResourceOwner;
- PG_TRY();
- {
- CurrentResourceOwner = TopTransactionResourceOwner;
-
- LockRelation(seqrel, AccessShareLock);
- }
- PG_CATCH();
- {
- /* Ensure CurrentResourceOwner is restored on error */
- CurrentResourceOwner = currentOwner;
- PG_RE_THROW();
- }
- PG_END_TRY();
- CurrentResourceOwner = currentOwner;
-
- /* Flag that we have a lock in the current xact. */
- elm->xid = thisxid;
- }
+ acquire_share_lock(seqrel, elm);
*p_elm = elm;
*p_rel = seqrel;
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index f10bdab7708..a58ec7b5a57 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.272 2005/05/30 20:59:17 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.273 2005/06/07 07:08:34 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200505302
+#define CATALOG_VERSION_NO 200506071
#endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index ebf15f8c199..9f5c2d8e0e3 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.363 2005/05/20 01:29:55 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.364 2005/06/07 07:08:34 neilc Exp $
*
* NOTES
* 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_ _
DESCR("convert int4 to boolean");
DATA(insert OID = 2558 ( int4 PGNSP PGUID 12 f f t f i 1 23 "16" _null_ _null_ _null_ bool_int4 - _null_ ));
DESCR("convert boolean to int4");
+DATA(insert OID = 2559 ( lastval PGNSP PGUID 12 f f t f v 0 20 "" _null_ _null_ _null_ lastval - _null_ ));
+DESCR("current value from last used sequence");
/*
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 48017538869..38538236c7a 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/commands/sequence.h,v 1.31 2005/06/06 17:01:25 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/sequence.h,v 1.32 2005/06/07 07:08:35 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@@ -82,6 +82,7 @@ typedef struct xl_seq_rec
extern Datum nextval(PG_FUNCTION_ARGS);
extern Datum currval(PG_FUNCTION_ARGS);
+extern Datum lastval(PG_FUNCTION_ARGS);
extern Datum setval(PG_FUNCTION_ARGS);
extern Datum setval_and_iscalled(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 040506d4deb..6e919d1f1de 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -76,3 +76,62 @@ COMMENT ON SEQUENCE asdf IS 'won''t work';
ERROR: relation "asdf" does not exist
COMMENT ON SEQUENCE sequence_test2 IS 'will work';
COMMENT ON SEQUENCE sequence_test2 IS NULL;
+-- Test lastval()
+CREATE SEQUENCE seq;
+SELECT nextval('seq');
+ nextval
+---------
+ 1
+(1 row)
+
+SELECT lastval();
+ lastval
+---------
+ 1
+(1 row)
+
+SELECT setval('seq', 99);
+ setval
+--------
+ 99
+(1 row)
+
+SELECT lastval();
+ lastval
+---------
+ 99
+(1 row)
+
+CREATE SEQUENCE seq2;
+SELECT nextval('seq2');
+ nextval
+---------
+ 1
+(1 row)
+
+SELECT lastval();
+ lastval
+---------
+ 1
+(1 row)
+
+DROP SEQUENCE seq2;
+-- should fail
+SELECT lastval();
+ERROR: lastval is not yet defined in this session
+CREATE USER seq_user;
+BEGIN;
+SET LOCAL SESSION AUTHORIZATION seq_user;
+CREATE SEQUENCE seq3;
+SELECT nextval('seq3');
+ nextval
+---------
+ 1
+(1 row)
+
+REVOKE ALL ON seq3 FROM seq_user;
+SELECT lastval();
+ERROR: permission denied for sequence seq3
+ROLLBACK;
+DROP USER seq_user;
+DROP SEQUENCE seq;
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 07f5765faf2..a8b73c02bf8 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -42,3 +42,30 @@ COMMENT ON SEQUENCE asdf IS 'won''t work';
COMMENT ON SEQUENCE sequence_test2 IS 'will work';
COMMENT ON SEQUENCE sequence_test2 IS NULL;
+-- Test lastval()
+CREATE SEQUENCE seq;
+SELECT nextval('seq');
+SELECT lastval();
+SELECT setval('seq', 99);
+SELECT lastval();
+
+CREATE SEQUENCE seq2;
+SELECT nextval('seq2');
+SELECT lastval();
+
+DROP SEQUENCE seq2;
+-- should fail
+SELECT lastval();
+
+CREATE USER seq_user;
+
+BEGIN;
+SET LOCAL SESSION AUTHORIZATION seq_user;
+CREATE SEQUENCE seq3;
+SELECT nextval('seq3');
+REVOKE ALL ON seq3 FROM seq_user;
+SELECT lastval();
+ROLLBACK;
+
+DROP USER seq_user;
+DROP SEQUENCE seq; \ No newline at end of file