Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Typedefs | Functions | Variables

sequence.c File Reference

#include "postgres.h"
#include "access/htup_details.h"
#include "access/multixact.h"
#include "access/transam.h"
#include "access/xlogutils.h"
#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/resowner.h"
#include "utils/syscache.h"
Include dependency graph for sequence.c:

Go to the source code of this file.

Data Structures

struct  sequence_magic
struct  SeqTableData

Defines

#define SEQ_LOG_VALS   32
#define SEQ_MAGIC   0x1717

Typedefs

typedef struct sequence_magic sequence_magic
typedef struct SeqTableData SeqTableData
typedef SeqTableDataSeqTable

Functions

static void fill_seq_with_data (Relation rel, HeapTuple tuple)
static int64 nextval_internal (Oid relid)
static Relation open_share_lock (SeqTable seq)
static void init_sequence (Oid relid, SeqTable *p_elm, Relation *p_rel)
static Form_pg_sequence read_seq_tuple (SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
static void init_params (List *options, bool isInit, Form_pg_sequence new, List **owned_by)
static void do_setval (Oid relid, int64 next, bool iscalled)
static void process_owned_by (Relation seqrel, List *owned_by)
Oid DefineSequence (CreateSeqStmt *seq)
void ResetSequence (Oid seq_relid)
Oid AlterSequence (AlterSeqStmt *stmt)
Datum nextval (PG_FUNCTION_ARGS)
Datum nextval_oid (PG_FUNCTION_ARGS)
Datum currval_oid (PG_FUNCTION_ARGS)
Datum lastval (PG_FUNCTION_ARGS)
Datum setval_oid (PG_FUNCTION_ARGS)
Datum setval3_oid (PG_FUNCTION_ARGS)
Datum pg_sequence_parameters (PG_FUNCTION_ARGS)
void seq_redo (XLogRecPtr lsn, XLogRecord *record)

Variables

static SeqTable seqtab = NULL
static SeqTableDatalast_used_seq = NULL

Define Documentation

#define SEQ_LOG_VALS   32

Definition at line 46 of file sequence.c.

#define SEQ_MAGIC   0x1717

Definition at line 51 of file sequence.c.

Referenced by read_seq_tuple().


Typedef Documentation

Definition at line 82 of file sequence.c.

typedef struct SeqTableData SeqTableData

Function Documentation

Oid AlterSequence ( AlterSeqStmt stmt  ) 

Definition at line 409 of file sequence.c.

References AccessShareLock, ACL_KIND_CLASS, aclcheck_error(), ACLCHECK_NOT_OWNER, buf, XLogRecData::buffer, BufferGetPage, SeqTableData::cached, XLogRecData::data, END_CRIT_SECTION, ereport, errmsg(), GetUserId(), init_params(), init_sequence(), InvalidBuffer, InvalidOid, InvokeObjectPostAlterHook, SeqTableData::last, XLogRecData::len, MarkBufferDirty(), AlterSeqStmt::missing_ok, XLogRecData::next, xl_seq_rec::node, NoLock, NOTICE, NULL, AlterSeqStmt::options, PageSetLSN, pg_class_ownercheck(), process_owned_by(), RangeVarGetRelid, RelationData::rd_node, read_seq_tuple(), relation_close(), RelationNeedsWAL, RelationRelationId, SeqTableData::relid, RangeVar::relname, AlterSeqStmt::sequence, START_CRIT_SECTION, HeapTupleData::t_data, HeapTupleData::t_len, UnlockReleaseBuffer(), XLOG_SEQ_LOG, and XLogInsert().

Referenced by ProcessUtilitySlow().

{
    Oid         relid;
    SeqTable    elm;
    Relation    seqrel;
    Buffer      buf;
    HeapTupleData seqtuple;
    Form_pg_sequence seq;
    FormData_pg_sequence new;
    List       *owned_by;

    /* Open and lock sequence. */
    relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
    if (relid == InvalidOid)
    {
        ereport(NOTICE,
                (errmsg("relation \"%s\" does not exist, skipping",
                        stmt->sequence->relname)));
        return InvalidOid;
    }

    init_sequence(relid, &elm, &seqrel);

    /* allow ALTER to sequence owner only */
    if (!pg_class_ownercheck(relid, GetUserId()))
        aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
                       stmt->sequence->relname);

    /* lock page' buffer and read tuple into new sequence structure */
    seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);

    /* Copy old values of options into workspace */
    memcpy(&new, seq, sizeof(FormData_pg_sequence));

    /* Check and set new values */
    init_params(stmt->options, false, &new, &owned_by);

    /* Clear local cache so that we don't think we have cached numbers */
    /* Note that we do not change the currval() state */
    elm->cached = elm->last;

    /* Now okay to update the on-disk tuple */
    START_CRIT_SECTION();

    memcpy(seq, &new, sizeof(FormData_pg_sequence));

    MarkBufferDirty(buf);

    /* XLOG stuff */
    if (RelationNeedsWAL(seqrel))
    {
        xl_seq_rec  xlrec;
        XLogRecPtr  recptr;
        XLogRecData rdata[2];
        Page        page = BufferGetPage(buf);

        xlrec.node = seqrel->rd_node;
        rdata[0].data = (char *) &xlrec;
        rdata[0].len = sizeof(xl_seq_rec);
        rdata[0].buffer = InvalidBuffer;
        rdata[0].next = &(rdata[1]);

        rdata[1].data = (char *) seqtuple.t_data;
        rdata[1].len = seqtuple.t_len;
        rdata[1].buffer = InvalidBuffer;
        rdata[1].next = NULL;

        recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);

        PageSetLSN(page, recptr);
    }

    END_CRIT_SECTION();

    UnlockReleaseBuffer(buf);

    /* process OWNED BY if given */
    if (owned_by)
        process_owned_by(seqrel, owned_by);

    InvokeObjectPostAlterHook(RelationRelationId, relid, 0);

    relation_close(seqrel, NoLock);

    return relid;
}

Datum currval_oid ( PG_FUNCTION_ARGS   ) 

Definition at line 759 of file sequence.c.

References ACL_SELECT, ACL_USAGE, ACLCHECK_OK, ereport, errcode(), errmsg(), ERROR, GetUserId(), init_sequence(), SeqTableData::last, SeqTableData::last_valid, NoLock, pg_class_aclcheck(), PG_GETARG_OID, PG_RETURN_INT64, relation_close(), RelationGetRelationName, and SeqTableData::relid.

{
    Oid         relid = PG_GETARG_OID(0);
    int64       result;
    SeqTable    elm;
    Relation    seqrel;

    /* open and AccessShareLock sequence */
    init_sequence(relid, &elm, &seqrel);

    if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK &&
        pg_class_aclcheck(elm->relid, GetUserId(), ACL_USAGE) != ACLCHECK_OK)
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 errmsg("permission denied for sequence %s",
                        RelationGetRelationName(seqrel))));

    if (!elm->last_valid)
        ereport(ERROR,
                (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                 errmsg("currval of sequence \"%s\" is not yet defined in this session",
                        RelationGetRelationName(seqrel))));

    result = elm->last;

    relation_close(seqrel, NoLock);

    PG_RETURN_INT64(result);
}

Oid DefineSequence ( CreateSeqStmt seq  ) 

Definition at line 109 of file sequence.c.

References AccessExclusiveLock, Assert, BoolGetDatum, BOOLOID, ColumnDef::collClause, ColumnDef::collOid, ColumnDef::colname, CreateStmt::constraints, ColumnDef::constraints, ColumnDef::cooked_default, DefineRelation(), ereport, errcode(), errmsg(), ERROR, fill_seq_with_data(), heap_close, heap_form_tuple(), heap_open(), i, CreateStmt::if_not_exists, ColumnDef::inhcount, CreateStmt::inhRelations, init_params(), Int64GetDatum(), Int64GetDatumFast, INT8OID, InvalidOid, ColumnDef::is_from_type, ColumnDef::is_local, ColumnDef::is_not_null, lappend(), makeNode, makeTypeNameFromOid(), name, NameGetDatum, NAMEOID, namestrcpy(), NoLock, CreateStmt::oncommit, CreateStmt::options, CreateSeqStmt::options, CreateSeqStmt::ownerId, process_owned_by(), ColumnDef::raw_default, CreateStmt::relation, RelationGetDescr, RELKIND_SEQUENCE, RangeVar::relname, RangeVar::relpersistence, RELPERSISTENCE_UNLOGGED, SEQ_COL_CACHE, SEQ_COL_CALLED, SEQ_COL_CYCLE, SEQ_COL_FIRSTCOL, SEQ_COL_INCBY, SEQ_COL_LASTVAL, SEQ_COL_LOG, SEQ_COL_MAXVALUE, SEQ_COL_MINVALUE, SEQ_COL_NAME, SEQ_COL_STARTVAL, CreateSeqStmt::sequence, ColumnDef::storage, CreateStmt::tableElts, CreateStmt::tablespacename, ColumnDef::typeName, and value.

Referenced by ProcessUtilitySlow().

{
    FormData_pg_sequence new;
    List       *owned_by;
    CreateStmt *stmt = makeNode(CreateStmt);
    Oid         seqoid;
    Relation    rel;
    HeapTuple   tuple;
    TupleDesc   tupDesc;
    Datum       value[SEQ_COL_LASTCOL];
    bool        null[SEQ_COL_LASTCOL];
    int         i;
    NameData    name;

    /* Unlogged sequences are not implemented -- not clear if useful. */
    if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("unlogged sequences are not supported")));

    /* Check and set all option values */
    init_params(seq->options, true, &new, &owned_by);

    /*
     * Create relation (and fill value[] and null[] for the tuple)
     */
    stmt->tableElts = NIL;
    for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
    {
        ColumnDef  *coldef = makeNode(ColumnDef);

        coldef->inhcount = 0;
        coldef->is_local = true;
        coldef->is_not_null = true;
        coldef->is_from_type = false;
        coldef->storage = 0;
        coldef->raw_default = NULL;
        coldef->cooked_default = NULL;
        coldef->collClause = NULL;
        coldef->collOid = InvalidOid;
        coldef->constraints = NIL;

        null[i - 1] = false;

        switch (i)
        {
            case SEQ_COL_NAME:
                coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
                coldef->colname = "sequence_name";
                namestrcpy(&name, seq->sequence->relname);
                value[i - 1] = NameGetDatum(&name);
                break;
            case SEQ_COL_LASTVAL:
                coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
                coldef->colname = "last_value";
                value[i - 1] = Int64GetDatumFast(new.last_value);
                break;
            case SEQ_COL_STARTVAL:
                coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
                coldef->colname = "start_value";
                value[i - 1] = Int64GetDatumFast(new.start_value);
                break;
            case SEQ_COL_INCBY:
                coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
                coldef->colname = "increment_by";
                value[i - 1] = Int64GetDatumFast(new.increment_by);
                break;
            case SEQ_COL_MAXVALUE:
                coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
                coldef->colname = "max_value";
                value[i - 1] = Int64GetDatumFast(new.max_value);
                break;
            case SEQ_COL_MINVALUE:
                coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
                coldef->colname = "min_value";
                value[i - 1] = Int64GetDatumFast(new.min_value);
                break;
            case SEQ_COL_CACHE:
                coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
                coldef->colname = "cache_value";
                value[i - 1] = Int64GetDatumFast(new.cache_value);
                break;
            case SEQ_COL_LOG:
                coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
                coldef->colname = "log_cnt";
                value[i - 1] = Int64GetDatum((int64) 0);
                break;
            case SEQ_COL_CYCLE:
                coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
                coldef->colname = "is_cycled";
                value[i - 1] = BoolGetDatum(new.is_cycled);
                break;
            case SEQ_COL_CALLED:
                coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
                coldef->colname = "is_called";
                value[i - 1] = BoolGetDatum(false);
                break;
        }
        stmt->tableElts = lappend(stmt->tableElts, coldef);
    }

    stmt->relation = seq->sequence;
    stmt->inhRelations = NIL;
    stmt->constraints = NIL;
    stmt->options = NIL;
    stmt->oncommit = ONCOMMIT_NOOP;
    stmt->tablespacename = NULL;
    stmt->if_not_exists = false;

    seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId);
    Assert(seqoid != InvalidOid);

    rel = heap_open(seqoid, AccessExclusiveLock);
    tupDesc = RelationGetDescr(rel);

    /* now initialize the sequence's data */
    tuple = heap_form_tuple(tupDesc, value, null);
    fill_seq_with_data(rel, tuple);

    /* process OWNED BY if given */
    if (owned_by)
        process_owned_by(rel, owned_by);

    heap_close(rel, NoLock);

    return seqoid;
}

static void do_setval ( Oid  relid,
int64  next,
bool  iscalled 
) [static]

Definition at line 838 of file sequence.c.

References ACL_UPDATE, ACLCHECK_OK, buf, XLogRecData::buffer, BufferGetPage, SeqTableData::cached, XLogRecData::data, END_CRIT_SECTION, ereport, errcode(), errmsg(), ERROR, GetUserId(), init_sequence(), InvalidBuffer, FormData_pg_sequence::is_called, SeqTableData::last, SeqTableData::last_valid, FormData_pg_sequence::last_value, XLogRecData::len, FormData_pg_sequence::log_cnt, MarkBufferDirty(), FormData_pg_sequence::max_value, FormData_pg_sequence::min_value, XLogRecData::next, xl_seq_rec::node, NoLock, NULL, PageSetLSN, pg_class_aclcheck(), PreventCommandIfReadOnly(), RelationData::rd_islocaltemp, RelationData::rd_node, read_seq_tuple(), relation_close(), RelationGetRelationName, RelationNeedsWAL, SeqTableData::relid, snprintf(), START_CRIT_SECTION, HeapTupleData::t_data, HeapTupleData::t_len, UnlockReleaseBuffer(), XLOG_SEQ_LOG, and XLogInsert().

Referenced by setval3_oid(), and setval_oid().

{
    SeqTable    elm;
    Relation    seqrel;
    Buffer      buf;
    HeapTupleData seqtuple;
    Form_pg_sequence seq;

    /* open and AccessShareLock sequence */
    init_sequence(relid, &elm, &seqrel);

    if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 errmsg("permission denied for sequence %s",
                        RelationGetRelationName(seqrel))));

    /* read-only transactions may only modify temp sequences */
    if (!seqrel->rd_islocaltemp)
        PreventCommandIfReadOnly("setval()");

    /* lock page' buffer and read tuple */
    seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);

    if ((next < seq->min_value) || (next > seq->max_value))
    {
        char        bufv[100],
                    bufm[100],
                    bufx[100];

        snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
        snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
        snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
        ereport(ERROR,
                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
                        bufv, RelationGetRelationName(seqrel),
                        bufm, bufx)));
    }

    /* Set the currval() state only if iscalled = true */
    if (iscalled)
    {
        elm->last = next;       /* last returned number */
        elm->last_valid = true;
    }

    /* In any case, forget any future cached numbers */
    elm->cached = elm->last;

    /* ready to change the on-disk (or really, in-buffer) tuple */
    START_CRIT_SECTION();

    seq->last_value = next;     /* last fetched number */
    seq->is_called = iscalled;
    seq->log_cnt = 0;

    MarkBufferDirty(buf);

    /* XLOG stuff */
    if (RelationNeedsWAL(seqrel))
    {
        xl_seq_rec  xlrec;
        XLogRecPtr  recptr;
        XLogRecData rdata[2];
        Page        page = BufferGetPage(buf);

        xlrec.node = seqrel->rd_node;
        rdata[0].data = (char *) &xlrec;
        rdata[0].len = sizeof(xl_seq_rec);
        rdata[0].buffer = InvalidBuffer;
        rdata[0].next = &(rdata[1]);

        rdata[1].data = (char *) seqtuple.t_data;
        rdata[1].len = seqtuple.t_len;
        rdata[1].buffer = InvalidBuffer;
        rdata[1].next = NULL;

        recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);

        PageSetLSN(page, recptr);
    }

    END_CRIT_SECTION();

    UnlockReleaseBuffer(buf);

    relation_close(seqrel, NoLock);
}

static void fill_seq_with_data ( Relation  rel,
HeapTuple  tuple 
) [static]

Definition at line 308 of file sequence.c.

References Assert, buf, XLogRecData::buffer, BUFFER_LOCK_EXCLUSIVE, BufferGetBlockNumber(), BufferGetPage, BufferGetPageSize, XLogRecData::data, END_CRIT_SECTION, FirstOffsetNumber, FrozenTransactionId, HeapTupleHeaderSetXmin, InvalidBuffer, ItemPointerGetOffsetNumber, XLogRecData::len, LockBuffer(), sequence_magic::magic, MarkBufferDirty(), XLogRecData::next, xl_seq_rec::node, NULL, P_NEW, PageGetItem, PageGetItemId, PageGetSpecialPointer, PageInit(), PageSetLSN, RelationData::rd_node, ReadBuffer(), RelationNeedsWAL, RelationSetTargetBlock, simple_heap_insert(), START_CRIT_SECTION, HeapTupleData::t_data, HeapTupleHeaderData::t_infomask, HeapTupleData::t_len, HeapTupleData::t_self, UnlockReleaseBuffer(), XLOG_SEQ_LOG, and XLogInsert().

Referenced by DefineSequence(), and ResetSequence().

{
    Buffer      buf;
    Page        page;
    sequence_magic *sm;

    /* Initialize first page of relation with special magic number */

    buf = ReadBuffer(rel, P_NEW);
    Assert(BufferGetBlockNumber(buf) == 0);

    page = BufferGetPage(buf);

    PageInit(page, BufferGetPageSize(buf), sizeof(sequence_magic));
    sm = (sequence_magic *) PageGetSpecialPointer(page);
    sm->magic = SEQ_MAGIC;

    /* hack: ensure heap_insert will insert on the just-created page */
    RelationSetTargetBlock(rel, 0);

    /* Now insert sequence tuple */
    simple_heap_insert(rel, tuple);

    Assert(ItemPointerGetOffsetNumber(&(tuple->t_self)) == FirstOffsetNumber);

    /*
     * Two special hacks here:
     *
     * 1. Since VACUUM does not process sequences, we have to force the tuple
     * to have xmin = FrozenTransactionId now.  Otherwise it would become
     * invisible to SELECTs after 2G transactions.  It is okay to do this
     * because if the current transaction aborts, no other xact will ever
     * examine the sequence tuple anyway.
     *
     * 2. Even though heap_insert emitted a WAL log record, we have to emit an
     * XLOG_SEQ_LOG record too, since (a) the heap_insert record will not have
     * the right xmin, and (b) REDO of the heap_insert record would re-init
     * page and sequence magic number would be lost.  This means two log
     * records instead of one :-(
     */
    LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);

    START_CRIT_SECTION();

    {
        /*
         * Note that the "tuple" structure is still just a local tuple record
         * created by heap_form_tuple; its t_data pointer doesn't point at the
         * disk buffer.  To scribble on the disk buffer we need to fetch the
         * item pointer.  But do the same to the local tuple, since that will
         * be the source for the WAL log record, below.
         */
        ItemId      itemId;
        Item        item;

        itemId = PageGetItemId((Page) page, FirstOffsetNumber);
        item = PageGetItem((Page) page, itemId);

        HeapTupleHeaderSetXmin((HeapTupleHeader) item, FrozenTransactionId);
        ((HeapTupleHeader) item)->t_infomask |= HEAP_XMIN_COMMITTED;

        HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId);
        tuple->t_data->t_infomask |= HEAP_XMIN_COMMITTED;
    }

    MarkBufferDirty(buf);

    /* XLOG stuff */
    if (RelationNeedsWAL(rel))
    {
        xl_seq_rec  xlrec;
        XLogRecPtr  recptr;
        XLogRecData rdata[2];

        xlrec.node = rel->rd_node;
        rdata[0].data = (char *) &xlrec;
        rdata[0].len = sizeof(xl_seq_rec);
        rdata[0].buffer = InvalidBuffer;
        rdata[0].next = &(rdata[1]);

        rdata[1].data = (char *) tuple->t_data;
        rdata[1].len = tuple->t_len;
        rdata[1].buffer = InvalidBuffer;
        rdata[1].next = NULL;

        recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);

        PageSetLSN(page, recptr);
    }

    END_CRIT_SECTION();

    UnlockReleaseBuffer(buf);
}

static void init_params ( List options,
bool  isInit,
Form_pg_sequence  new,
List **  owned_by 
) [static]

Definition at line 1141 of file sequence.c.

References DefElem::arg, Assert, BoolIsValid, buf, defGetInt64(), defGetQualifiedName(), DefElem::defname, elog, ereport, errcode(), errmsg(), ERROR, intVal, lfirst, NULL, and snprintf().

Referenced by AlterSequence(), and DefineSequence().

{
    DefElem    *start_value = NULL;
    DefElem    *restart_value = NULL;
    DefElem    *increment_by = NULL;
    DefElem    *max_value = NULL;
    DefElem    *min_value = NULL;
    DefElem    *cache_value = NULL;
    DefElem    *is_cycled = NULL;
    ListCell   *option;

    *owned_by = NIL;

    foreach(option, options)
    {
        DefElem    *defel = (DefElem *) lfirst(option);

        if (strcmp(defel->defname, "increment") == 0)
        {
            if (increment_by)
                ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("conflicting or redundant options")));
            increment_by = defel;
        }
        else if (strcmp(defel->defname, "start") == 0)
        {
            if (start_value)
                ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("conflicting or redundant options")));
            start_value = defel;
        }
        else if (strcmp(defel->defname, "restart") == 0)
        {
            if (restart_value)
                ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("conflicting or redundant options")));
            restart_value = defel;
        }
        else if (strcmp(defel->defname, "maxvalue") == 0)
        {
            if (max_value)
                ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("conflicting or redundant options")));
            max_value = defel;
        }
        else if (strcmp(defel->defname, "minvalue") == 0)
        {
            if (min_value)
                ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("conflicting or redundant options")));
            min_value = defel;
        }
        else if (strcmp(defel->defname, "cache") == 0)
        {
            if (cache_value)
                ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("conflicting or redundant options")));
            cache_value = defel;
        }
        else if (strcmp(defel->defname, "cycle") == 0)
        {
            if (is_cycled)
                ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("conflicting or redundant options")));
            is_cycled = defel;
        }
        else if (strcmp(defel->defname, "owned_by") == 0)
        {
            if (*owned_by)
                ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("conflicting or redundant options")));
            *owned_by = defGetQualifiedName(defel);
        }
        else
            elog(ERROR, "option \"%s\" not recognized",
                 defel->defname);
    }

    /*
     * We must reset log_cnt when isInit or when changing any parameters
     * that would affect future nextval allocations.
     */
    if (isInit)
        new->log_cnt = 0;

    /* INCREMENT BY */
    if (increment_by != NULL)
    {
        new->increment_by = defGetInt64(increment_by);
        if (new->increment_by == 0)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                     errmsg("INCREMENT must not be zero")));
        new->log_cnt = 0;
    }
    else if (isInit)
        new->increment_by = 1;

    /* CYCLE */
    if (is_cycled != NULL)
    {
        new->is_cycled = intVal(is_cycled->arg);
        Assert(BoolIsValid(new->is_cycled));
        new->log_cnt = 0;
    }
    else if (isInit)
        new->is_cycled = false;

    /* MAXVALUE (null arg means NO MAXVALUE) */
    if (max_value != NULL && max_value->arg)
    {
        new->max_value = defGetInt64(max_value);
        new->log_cnt = 0;
    }
    else if (isInit || max_value != NULL)
    {
        if (new->increment_by > 0)
            new->max_value = SEQ_MAXVALUE;      /* ascending seq */
        else
            new->max_value = -1;    /* descending seq */
        new->log_cnt = 0;
    }

    /* MINVALUE (null arg means NO MINVALUE) */
    if (min_value != NULL && min_value->arg)
    {
        new->min_value = defGetInt64(min_value);
        new->log_cnt = 0;
    }
    else if (isInit || min_value != NULL)
    {
        if (new->increment_by > 0)
            new->min_value = 1; /* ascending seq */
        else
            new->min_value = SEQ_MINVALUE;      /* descending seq */
        new->log_cnt = 0;
    }

    /* crosscheck min/max */
    if (new->min_value >= new->max_value)
    {
        char        bufm[100],
                    bufx[100];

        snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
        snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value);
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("MINVALUE (%s) must be less than MAXVALUE (%s)",
                        bufm, bufx)));
    }

    /* START WITH */
    if (start_value != NULL)
        new->start_value = defGetInt64(start_value);
    else if (isInit)
    {
        if (new->increment_by > 0)
            new->start_value = new->min_value;  /* ascending seq */
        else
            new->start_value = new->max_value;  /* descending seq */
    }

    /* crosscheck START */
    if (new->start_value < new->min_value)
    {
        char        bufs[100],
                    bufm[100];

        snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value);
        snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("START value (%s) cannot be less than MINVALUE (%s)",
                        bufs, bufm)));
    }
    if (new->start_value > new->max_value)
    {
        char        bufs[100],
                    bufm[100];

        snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value);
        snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
              errmsg("START value (%s) cannot be greater than MAXVALUE (%s)",
                     bufs, bufm)));
    }

    /* RESTART [WITH] */
    if (restart_value != NULL)
    {
        if (restart_value->arg != NULL)
            new->last_value = defGetInt64(restart_value);
        else
            new->last_value = new->start_value;
        new->is_called = false;
        new->log_cnt = 0;
    }
    else if (isInit)
    {
        new->last_value = new->start_value;
        new->is_called = false;
    }

    /* crosscheck RESTART (or current value, if changing MIN/MAX) */
    if (new->last_value < new->min_value)
    {
        char        bufs[100],
                    bufm[100];

        snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
        snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
               errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
                      bufs, bufm)));
    }
    if (new->last_value > new->max_value)
    {
        char        bufs[100],
                    bufm[100];

        snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
        snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
            errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
                   bufs, bufm)));
    }

    /* CACHE */
    if (cache_value != NULL)
    {
        new->cache_value = defGetInt64(cache_value);
        if (new->cache_value <= 0)
        {
            char        buf[100];

            snprintf(buf, sizeof(buf), INT64_FORMAT, new->cache_value);
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                     errmsg("CACHE (%s) must be greater than zero",
                            buf)));
        }
        new->log_cnt = 0;
    }
    else if (isInit)
        new->cache_value = 1;
}

static void init_sequence ( Oid  relid,
SeqTable p_elm,
Relation p_rel 
) [static]

Definition at line 1006 of file sequence.c.

References SeqTableData::cached, ereport, errcode(), errmsg(), ERROR, SeqTableData::filenode, SeqTableData::increment, SeqTableData::last, SeqTableData::last_valid, SeqTableData::lxid, malloc, SeqTableData::next, NULL, open_share_lock(), RelationData::rd_rel, RelationGetRelationName, SeqTableData::relid, and RELKIND_SEQUENCE.

Referenced by AlterSequence(), currval_oid(), do_setval(), nextval_internal(), pg_sequence_parameters(), and ResetSequence().

{
    SeqTable    elm;
    Relation    seqrel;

    /* Look to see if we already have a seqtable entry for relation */
    for (elm = seqtab; elm != NULL; elm = elm->next)
    {
        if (elm->relid == relid)
            break;
    }

    /*
     * Allocate new seqtable entry if we didn't find one.
     *
     * NOTE: seqtable entries remain in the list for the life of a backend. If
     * the sequence itself is deleted then the entry becomes wasted memory,
     * but it's small enough that this should not matter.
     */
    if (elm == NULL)
    {
        /*
         * Time to make a new seqtable entry.  These entries live as long as
         * the backend does, so we use plain malloc for them.
         */
        elm = (SeqTable) malloc(sizeof(SeqTableData));
        if (elm == NULL)
            ereport(ERROR,
                    (errcode(ERRCODE_OUT_OF_MEMORY),
                     errmsg("out of memory")));
        elm->relid = relid;
        elm->filenode = InvalidOid;
        elm->lxid = InvalidLocalTransactionId;
        elm->last_valid = false;
        elm->last = elm->cached = elm->increment = 0;
        elm->next = seqtab;
        seqtab = elm;
    }

    /*
     * Open the sequence relation.
     */
    seqrel = open_share_lock(elm);

    if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("\"%s\" is not a sequence",
                        RelationGetRelationName(seqrel))));

    /*
     * If the sequence has been transactionally replaced since we last saw it,
     * discard any cached-but-unissued values.  We do not touch the currval()
     * state, however.
     */
    if (seqrel->rd_rel->relfilenode != elm->filenode)
    {
        elm->filenode = seqrel->rd_rel->relfilenode;
        elm->cached = elm->last;
    }

    /* Return results */
    *p_elm = elm;
    *p_rel = seqrel;
}

Datum lastval ( PG_FUNCTION_ARGS   ) 

Definition at line 790 of file sequence.c.

References ACL_SELECT, ACL_USAGE, ACLCHECK_OK, Assert, ereport, errcode(), errmsg(), ERROR, GetUserId(), SeqTableData::last, SeqTableData::last_valid, NoLock, NULL, ObjectIdGetDatum, open_share_lock(), pg_class_aclcheck(), PG_RETURN_INT64, relation_close(), RelationGetRelationName, SeqTableData::relid, RELOID, and SearchSysCacheExists1.

{
    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 (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(last_used_seq->relid)))
        ereport(ERROR,
                (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                 errmsg("lastval is not yet defined in this session")));

    seqrel = open_share_lock(last_used_seq);

    /* nextval() must have already been called for this sequence */
    Assert(last_used_seq->last_valid);

    if (pg_class_aclcheck(last_used_seq->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK &&
        pg_class_aclcheck(last_used_seq->relid, GetUserId(), ACL_USAGE) != 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);
}

Datum nextval ( PG_FUNCTION_ARGS   ) 

Definition at line 503 of file sequence.c.

References makeRangeVarFromNameList(), nextval_internal(), NoLock, PG_GETARG_TEXT_P, PG_RETURN_INT64, RangeVarGetRelid, SeqTableData::relid, and textToQualifiedNameList().

Referenced by autoinc(), and ttdummy().

{
    text       *seqin = PG_GETARG_TEXT_P(0);
    RangeVar   *sequence;
    Oid         relid;

    sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin));

    /*
     * XXX: This is not safe in the presence of concurrent DDL, but acquiring
     * a lock here is more expensive than letting nextval_internal do it,
     * since the latter maintains a cache that keeps us from hitting the lock
     * manager more than once per transaction.  It's not clear whether the
     * performance penalty is material in practice, but for now, we do it this
     * way.
     */
    relid = RangeVarGetRelid(sequence, NoLock, false);

    PG_RETURN_INT64(nextval_internal(relid));
}

static int64 nextval_internal ( Oid  relid  )  [static]

Definition at line 533 of file sequence.c.

References ACL_UPDATE, ACL_USAGE, ACLCHECK_OK, Assert, buf, XLogRecData::buffer, BufferGetPage, FormData_pg_sequence::cache_value, SeqTableData::cached, XLogRecData::data, END_CRIT_SECTION, ereport, errcode(), errmsg(), ERROR, GetRedoRecPtr(), GetUserId(), SeqTableData::increment, FormData_pg_sequence::increment_by, init_sequence(), InvalidBuffer, FormData_pg_sequence::is_called, FormData_pg_sequence::is_cycled, SeqTableData::last, SeqTableData::last_valid, FormData_pg_sequence::last_value, XLogRecData::len, FormData_pg_sequence::log_cnt, MarkBufferDirty(), FormData_pg_sequence::max_value, FormData_pg_sequence::min_value, XLogRecData::next, SeqTableData::next, xl_seq_rec::node, NoLock, NULL, PageGetLSN, PageSetLSN, pg_class_aclcheck(), PreventCommandIfReadOnly(), RelationData::rd_islocaltemp, RelationData::rd_node, read_seq_tuple(), relation_close(), RelationGetRelationName, RelationNeedsWAL, SeqTableData::relid, snprintf(), START_CRIT_SECTION, HeapTupleData::t_data, HeapTupleData::t_len, UnlockReleaseBuffer(), XLOG_SEQ_LOG, and XLogInsert().

Referenced by nextval(), and nextval_oid().

{
    SeqTable    elm;
    Relation    seqrel;
    Buffer      buf;
    Page        page;
    HeapTupleData seqtuple;
    Form_pg_sequence seq;
    int64       incby,
                maxv,
                minv,
                cache,
                log,
                fetch,
                last;
    int64       result,
                next,
                rescnt = 0;
    bool        logit = false;

    /* open and AccessShareLock sequence */
    init_sequence(relid, &elm, &seqrel);

    if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_USAGE) != ACLCHECK_OK &&
        pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 errmsg("permission denied for sequence %s",
                        RelationGetRelationName(seqrel))));

    /* read-only transactions may only modify temp sequences */
    if (!seqrel->rd_islocaltemp)
        PreventCommandIfReadOnly("nextval()");

    if (elm->last != elm->cached)       /* some numbers were cached */
    {
        Assert(elm->last_valid);
        Assert(elm->increment != 0);
        elm->last += elm->increment;
        relation_close(seqrel, NoLock);
        last_used_seq = elm;
        return elm->last;
    }

    /* lock page' buffer and read tuple */
    seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
    page = BufferGetPage(buf);

    last = next = result = seq->last_value;
    incby = seq->increment_by;
    maxv = seq->max_value;
    minv = seq->min_value;
    fetch = cache = seq->cache_value;
    log = seq->log_cnt;

    if (!seq->is_called)
    {
        rescnt++;               /* return last_value if not is_called */
        fetch--;
    }

    /*
     * Decide whether we should emit a WAL log record.  If so, force up the
     * fetch count to grab SEQ_LOG_VALS more values than we actually need to
     * cache.  (These will then be usable without logging.)
     *
     * If this is the first nextval after a checkpoint, we must force a new
     * WAL record to be written anyway, else replay starting from the
     * checkpoint would fail to advance the sequence past the logged values.
     * In this case we may as well fetch extra values.
     */
    if (log < fetch || !seq->is_called)
    {
        /* forced log to satisfy local demand for values */
        fetch = log = fetch + SEQ_LOG_VALS;
        logit = true;
    }
    else
    {
        XLogRecPtr  redoptr = GetRedoRecPtr();

        if (PageGetLSN(page) <= redoptr)
        {
            /* last update of seq was before checkpoint */
            fetch = log = fetch + SEQ_LOG_VALS;
            logit = true;
        }
    }

    while (fetch)               /* try to fetch cache [+ log ] numbers */
    {
        /*
         * Check MAXVALUE for ascending sequences and MINVALUE for descending
         * sequences
         */
        if (incby > 0)
        {
            /* ascending sequence */
            if ((maxv >= 0 && next > maxv - incby) ||
                (maxv < 0 && next + incby > maxv))
            {
                if (rescnt > 0)
                    break;      /* stop fetching */
                if (!seq->is_cycled)
                {
                    char        buf[100];

                    snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
                    ereport(ERROR,
                          (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                           errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
                                  RelationGetRelationName(seqrel), buf)));
                }
                next = minv;
            }
            else
                next += incby;
        }
        else
        {
            /* descending sequence */
            if ((minv < 0 && next < minv - incby) ||
                (minv >= 0 && next + incby < minv))
            {
                if (rescnt > 0)
                    break;      /* stop fetching */
                if (!seq->is_cycled)
                {
                    char        buf[100];

                    snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
                    ereport(ERROR,
                          (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                           errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
                                  RelationGetRelationName(seqrel), buf)));
                }
                next = maxv;
            }
            else
                next += incby;
        }
        fetch--;
        if (rescnt < cache)
        {
            log--;
            rescnt++;
            last = next;
            if (rescnt == 1)    /* if it's first result - */
                result = next;  /* it's what to return */
        }
    }

    log -= fetch;               /* adjust for any unfetched numbers */
    Assert(log >= 0);

    /* save info in local cache */
    elm->last = result;         /* last returned number */
    elm->cached = last;         /* last fetched number */
    elm->last_valid = true;

    last_used_seq = elm;

    /* ready to change the on-disk (or really, in-buffer) tuple */
    START_CRIT_SECTION();

    /*
     * We must mark the buffer dirty before doing XLogInsert(); see notes in
     * SyncOneBuffer().  However, we don't apply the desired changes just yet.
     * This looks like a violation of the buffer update protocol, but it is
     * in fact safe because we hold exclusive lock on the buffer.  Any other
     * process, including a checkpoint, that tries to examine the buffer
     * contents will block until we release the lock, and then will see the
     * final state that we install below.
     */
    MarkBufferDirty(buf);

    /* XLOG stuff */
    if (logit && RelationNeedsWAL(seqrel))
    {
        xl_seq_rec  xlrec;
        XLogRecPtr  recptr;
        XLogRecData rdata[2];

        /*
         * We don't log the current state of the tuple, but rather the state
         * as it would appear after "log" more fetches.  This lets us skip
         * that many future WAL records, at the cost that we lose those
         * sequence values if we crash.
         */

        /* set values that will be saved in xlog */
        seq->last_value = next;
        seq->is_called = true;
        seq->log_cnt = 0;

        xlrec.node = seqrel->rd_node;
        rdata[0].data = (char *) &xlrec;
        rdata[0].len = sizeof(xl_seq_rec);
        rdata[0].buffer = InvalidBuffer;
        rdata[0].next = &(rdata[1]);

        rdata[1].data = (char *) seqtuple.t_data;
        rdata[1].len = seqtuple.t_len;
        rdata[1].buffer = InvalidBuffer;
        rdata[1].next = NULL;

        recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);

        PageSetLSN(page, recptr);
    }

    /* Now update sequence tuple to the intended final state */
    seq->last_value = last;     /* last fetched number */
    seq->is_called = true;
    seq->log_cnt = log;         /* how much is logged */

    END_CRIT_SECTION();

    UnlockReleaseBuffer(buf);

    relation_close(seqrel, NoLock);

    return result;
}

Datum nextval_oid ( PG_FUNCTION_ARGS   ) 
static Relation open_share_lock ( SeqTable  seq  )  [static]

Definition at line 969 of file sequence.c.

References AccessShareLock, CurrentResourceOwner, LockRelationOid(), SeqTableData::lxid, PGPROC::lxid, MyProc, NoLock, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, relation_open(), SeqTableData::relid, and TopTransactionResourceOwner.

Referenced by init_sequence(), and lastval().

{
    LocalTransactionId thislxid = MyProc->lxid;

    /* Get the lock if not already held in this xact */
    if (seq->lxid != thislxid)
    {
        ResourceOwner currentOwner;

        currentOwner = CurrentResourceOwner;
        PG_TRY();
        {
            CurrentResourceOwner = TopTransactionResourceOwner;
            LockRelationOid(seq->relid, 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->lxid = thislxid;
    }

    /* We now know we have AccessShareLock, and can safely open the rel */
    return relation_open(seq->relid, NoLock);
}

Datum pg_sequence_parameters ( PG_FUNCTION_ARGS   ) 

Definition at line 1499 of file sequence.c.

References ACL_SELECT, ACL_UPDATE, ACL_USAGE, ACLCHECK_OK, BlessTupleDesc(), BoolGetDatum, BOOLOID, buf, CreateTemplateTupleDesc(), ereport, errcode(), errmsg(), ERROR, GetUserId(), heap_form_tuple(), HeapTupleGetDatum, FormData_pg_sequence::increment_by, init_sequence(), Int64GetDatum(), INT8OID, FormData_pg_sequence::is_cycled, FormData_pg_sequence::max_value, FormData_pg_sequence::min_value, NoLock, pg_class_aclcheck(), PG_GETARG_OID, read_seq_tuple(), relation_close(), RelationGetRelationName, SeqTableData::relid, FormData_pg_sequence::start_value, TupleDescInitEntry(), UnlockReleaseBuffer(), and values.

{
    Oid         relid = PG_GETARG_OID(0);
    TupleDesc   tupdesc;
    Datum       values[5];
    bool        isnull[5];
    SeqTable    elm;
    Relation    seqrel;
    Buffer      buf;
    HeapTupleData seqtuple;
    Form_pg_sequence seq;

    /* open and AccessShareLock sequence */
    init_sequence(relid, &elm, &seqrel);

    if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 errmsg("permission denied for sequence %s",
                        RelationGetRelationName(seqrel))));

    tupdesc = CreateTemplateTupleDesc(5, false);
    TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
                       INT8OID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value",
                       INT8OID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber) 3, "maximum_value",
                       INT8OID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber) 4, "increment",
                       INT8OID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option",
                       BOOLOID, -1, 0);

    BlessTupleDesc(tupdesc);

    memset(isnull, 0, sizeof(isnull));

    seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);

    values[0] = Int64GetDatum(seq->start_value);
    values[1] = Int64GetDatum(seq->min_value);
    values[2] = Int64GetDatum(seq->max_value);
    values[3] = Int64GetDatum(seq->increment_by);
    values[4] = BoolGetDatum(seq->is_cycled);

    UnlockReleaseBuffer(buf);
    relation_close(seqrel, NoLock);

    return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
}

static void process_owned_by ( Relation  seqrel,
List owned_by 
) [static]

Definition at line 1410 of file sequence.c.

References AccessShareLock, Assert, ObjectAddress::classId, DEPENDENCY_AUTO, ereport, errcode(), errhint(), errmsg(), ERROR, get_attnum(), InvalidAttrNumber, lfirst, linitial, list_copy(), list_length(), list_tail(), list_truncate(), makeRangeVarFromNameList(), markSequenceUnowned(), NoLock, ObjectAddress::objectId, ObjectAddress::objectSubId, RelationData::rd_rel, recordDependencyOn(), relation_close(), relation_openrv(), RelationGetNamespace, RelationGetRelationName, RelationGetRelid, RELKIND_RELATION, and strVal.

Referenced by AlterSequence(), and DefineSequence().

{
    int         nnames;
    Relation    tablerel;
    AttrNumber  attnum;

    nnames = list_length(owned_by);
    Assert(nnames > 0);
    if (nnames == 1)
    {
        /* Must be OWNED BY NONE */
        if (strcmp(strVal(linitial(owned_by)), "none") != 0)
            ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                     errmsg("invalid OWNED BY option"),
                errhint("Specify OWNED BY table.column or OWNED BY NONE.")));
        tablerel = NULL;
        attnum = 0;
    }
    else
    {
        List       *relname;
        char       *attrname;
        RangeVar   *rel;

        /* Separate relname and attr name */
        relname = list_truncate(list_copy(owned_by), nnames - 1);
        attrname = strVal(lfirst(list_tail(owned_by)));

        /* Open and lock rel to ensure it won't go away meanwhile */
        rel = makeRangeVarFromNameList(relname);
        tablerel = relation_openrv(rel, AccessShareLock);

        /* Must be a regular table */
        if (tablerel->rd_rel->relkind != RELKIND_RELATION)
            ereport(ERROR,
                    (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                     errmsg("referenced relation \"%s\" is not a table",
                            RelationGetRelationName(tablerel))));

        /* We insist on same owner and schema */
        if (seqrel->rd_rel->relowner != tablerel->rd_rel->relowner)
            ereport(ERROR,
                    (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                     errmsg("sequence must have same owner as table it is linked to")));
        if (RelationGetNamespace(seqrel) != RelationGetNamespace(tablerel))
            ereport(ERROR,
                    (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                     errmsg("sequence must be in same schema as table it is linked to")));

        /* Now, fetch the attribute number from the system cache */
        attnum = get_attnum(RelationGetRelid(tablerel), attrname);
        if (attnum == InvalidAttrNumber)
            ereport(ERROR,
                    (errcode(ERRCODE_UNDEFINED_COLUMN),
                     errmsg("column \"%s\" of relation \"%s\" does not exist",
                            attrname, RelationGetRelationName(tablerel))));
    }

    /*
     * OK, we are ready to update pg_depend.  First remove any existing AUTO
     * dependencies for the sequence, then optionally add a new one.
     */
    markSequenceUnowned(RelationGetRelid(seqrel));

    if (tablerel)
    {
        ObjectAddress refobject,
                    depobject;

        refobject.classId = RelationRelationId;
        refobject.objectId = RelationGetRelid(tablerel);
        refobject.objectSubId = attnum;
        depobject.classId = RelationRelationId;
        depobject.objectId = RelationGetRelid(seqrel);
        depobject.objectSubId = 0;
        recordDependencyOn(&depobject, &refobject, DEPENDENCY_AUTO);
    }

    /* Done, but hold lock until commit */
    if (tablerel)
        relation_close(tablerel, NoLock);
}

static Form_pg_sequence read_seq_tuple ( SeqTable  elm,
Relation  rel,
Buffer buf,
HeapTuple  seqtuple 
) [static]

Definition at line 1083 of file sequence.c.

References Assert, BUFFER_LOCK_EXCLUSIVE, BufferGetPage, elog, ERROR, FirstOffsetNumber, GETSTRUCT, HEAP_XMAX_IS_MULTI, HeapTupleHeaderGetRawXmax, HeapTupleHeaderSetXmax, SeqTableData::increment, FormData_pg_sequence::increment_by, InvalidTransactionId, ItemIdGetLength, ItemIdIsNormal, LockBuffer(), sequence_magic::magic, MarkBufferDirtyHint(), PageGetItem, PageGetItemId, PageGetSpecialPointer, ReadBuffer(), RelationGetRelationName, SEQ_MAGIC, HeapTupleData::t_data, HeapTupleHeaderData::t_infomask, and HeapTupleData::t_len.

Referenced by AlterSequence(), do_setval(), nextval_internal(), pg_sequence_parameters(), and ResetSequence().

{
    Page        page;
    ItemId      lp;
    sequence_magic *sm;
    Form_pg_sequence seq;

    *buf = ReadBuffer(rel, 0);
    LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);

    page = BufferGetPage(*buf);
    sm = (sequence_magic *) PageGetSpecialPointer(page);

    if (sm->magic != SEQ_MAGIC)
        elog(ERROR, "bad magic number in sequence \"%s\": %08X",
             RelationGetRelationName(rel), sm->magic);

    lp = PageGetItemId(page, FirstOffsetNumber);
    Assert(ItemIdIsNormal(lp));

    /* Note we currently only bother to set these two fields of *seqtuple */
    seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
    seqtuple->t_len = ItemIdGetLength(lp);

    /*
     * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
     * a sequence, which would leave a non-frozen XID in the sequence tuple's
     * xmax, which eventually leads to clog access failures or worse. If we
     * see this has happened, clean up after it.  We treat this like a hint
     * bit update, ie, don't bother to WAL-log it, since we can certainly do
     * this again if the update gets lost.
     */
    Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
    if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
    {
        HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
        seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
        seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
        MarkBufferDirtyHint(*buf);
    }

    seq = (Form_pg_sequence) GETSTRUCT(seqtuple);

    /* this is a handy place to update our copy of the increment */
    elm->increment = seq->increment_by;

    return seq;
}

void ResetSequence ( Oid  seq_relid  ) 

Definition at line 250 of file sequence.c.

References buf, SeqTableData::cached, fill_seq_with_data(), GETSTRUCT, heap_copytuple(), init_sequence(), InvalidMultiXactId, InvalidTransactionId, FormData_pg_sequence::is_called, SeqTableData::last, FormData_pg_sequence::last_value, FormData_pg_sequence::log_cnt, NoLock, read_seq_tuple(), relation_close(), RelationSetNewRelfilenode(), FormData_pg_sequence::start_value, and UnlockReleaseBuffer().

Referenced by ExecuteTruncate().

{
    Relation    seq_rel;
    SeqTable    elm;
    Form_pg_sequence seq;
    Buffer      buf;
    HeapTupleData seqtuple;
    HeapTuple   tuple;

    /*
     * Read the old sequence.  This does a bit more work than really
     * necessary, but it's simple, and we do want to double-check that it's
     * indeed a sequence.
     */
    init_sequence(seq_relid, &elm, &seq_rel);
    (void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);

    /*
     * Copy the existing sequence tuple.
     */
    tuple = heap_copytuple(&seqtuple);

    /* Now we're done with the old page */
    UnlockReleaseBuffer(buf);

    /*
     * Modify the copied tuple to execute the restart (compare the RESTART
     * action in AlterSequence)
     */
    seq = (Form_pg_sequence) GETSTRUCT(tuple);
    seq->last_value = seq->start_value;
    seq->is_called = false;
    seq->log_cnt = 0;

    /*
     * Create a new storage file for the sequence.  We want to keep the
     * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
     * Same with relminmxid, since a sequence will never contain multixacts.
     */
    RelationSetNewRelfilenode(seq_rel, InvalidTransactionId,
                              InvalidMultiXactId);

    /*
     * Insert the modified tuple into the new storage file.
     */
    fill_seq_with_data(seq_rel, tuple);

    /* Clear local cache so that we don't think we have cached numbers */
    /* Note that we do not change the currval() state */
    elm->cached = elm->last;

    relation_close(seq_rel, NoLock);
}

void seq_redo ( XLogRecPtr  lsn,
XLogRecord record 
)

Definition at line 1552 of file sequence.c.

References Assert, BufferGetPage, BufferGetPageSize, BufferIsValid, elog, InvalidOffsetNumber, sequence_magic::magic, MarkBufferDirty(), xl_seq_rec::node, PageAddItem(), PageGetSpecialPointer, PageInit(), PageSetLSN, palloc(), PANIC, pfree(), UnlockReleaseBuffer(), XLogRecord::xl_info, XLogRecord::xl_len, XLOG_SEQ_LOG, XLogReadBuffer(), XLogRecGetData, and XLR_BKP_BLOCK_MASK.

{
    uint8       info = record->xl_info & ~XLR_INFO_MASK;
    Buffer      buffer;
    Page        page;
    Page        localpage;
    char       *item;
    Size        itemsz;
    xl_seq_rec *xlrec = (xl_seq_rec *) XLogRecGetData(record);
    sequence_magic *sm;

    /* Backup blocks are not used in seq records */
    Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));

    if (info != XLOG_SEQ_LOG)
        elog(PANIC, "seq_redo: unknown op code %u", info);

    buffer = XLogReadBuffer(xlrec->node, 0, true);
    Assert(BufferIsValid(buffer));
    page = (Page) BufferGetPage(buffer);

    /*
     * We must always reinit the page and reinstall the magic number (see
     * comments in fill_seq_with_data).  However, since this WAL record type
     * is also used for updating sequences, it's possible that a hot-standby
     * backend is examining the page concurrently; so we mustn't transiently
     * trash the buffer.  The solution is to build the correct new page
     * contents in local workspace and then memcpy into the buffer.  Then only
     * bytes that are supposed to change will change, even transiently. We
     * must palloc the local page for alignment reasons.
     */
    localpage = (Page) palloc(BufferGetPageSize(buffer));

    PageInit(localpage, BufferGetPageSize(buffer), sizeof(sequence_magic));
    sm = (sequence_magic *) PageGetSpecialPointer(localpage);
    sm->magic = SEQ_MAGIC;

    item = (char *) xlrec + sizeof(xl_seq_rec);
    itemsz = record->xl_len - sizeof(xl_seq_rec);

    if (PageAddItem(localpage, (Item) item, itemsz,
                    FirstOffsetNumber, false, false) == InvalidOffsetNumber)
        elog(PANIC, "seq_redo: failed to add item to page");

    PageSetLSN(localpage, lsn);

    memcpy(page, localpage, BufferGetPageSize(buffer));
    MarkBufferDirty(buffer);
    UnlockReleaseBuffer(buffer);

    pfree(localpage);
}

Datum setval3_oid ( PG_FUNCTION_ARGS   ) 

Definition at line 948 of file sequence.c.

References do_setval(), next(), PG_GETARG_BOOL, PG_GETARG_INT64, PG_GETARG_OID, PG_RETURN_INT64, and SeqTableData::relid.

{
    Oid         relid = PG_GETARG_OID(0);
    int64       next = PG_GETARG_INT64(1);
    bool        iscalled = PG_GETARG_BOOL(2);

    do_setval(relid, next, iscalled);

    PG_RETURN_INT64(next);
}

Datum setval_oid ( PG_FUNCTION_ARGS   ) 

Definition at line 933 of file sequence.c.

References do_setval(), next(), PG_GETARG_INT64, PG_GETARG_OID, PG_RETURN_INT64, and SeqTableData::relid.

{
    Oid         relid = PG_GETARG_OID(0);
    int64       next = PG_GETARG_INT64(1);

    do_setval(relid, next, true);

    PG_RETURN_INT64(next);
}


Variable Documentation

SeqTableData* last_used_seq = NULL [static]

Definition at line 90 of file sequence.c.

SeqTable seqtab = NULL [static]

Definition at line 84 of file sequence.c.