<!--
-$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
-->
<indexterm>
<primary>currval</primary>
</indexterm>
+ <indexterm>
+ <primary>lastval</primary>
+ </indexterm>
<indexterm>
<primary>setval</primary>
</indexterm>
<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>
</listitem>
</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>
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/resowner.h"
+#include "utils/syscache.h"
/*
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);
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);
elm->last = result; /* last returned number */
elm->cached = last; /* last fetched number */
+ last_used_seq = elm;
+
START_CRIT_SECTION();
/* XLOG stuff */
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.
*
}
+/*
+ * 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.
init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel)
{
Oid relid = RangeVarGetRelid(relation, false);
- TransactionId thisxid = GetTopTransactionId();
volatile SeqTable elm;
Relation seqrel;
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;