1 /*-------------------------------------------------------------------------
4 * PostgreSQL sequences support code.
6 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.123 2005/06/07 07:08:34 neilc Exp $
13 *-------------------------------------------------------------------------
17 #include "access/heapam.h"
18 #include "catalog/namespace.h"
19 #include "catalog/pg_type.h"
20 #include "commands/defrem.h"
21 #include "commands/tablecmds.h"
22 #include "commands/sequence.h"
23 #include "miscadmin.h"
24 #include "utils/acl.h"
25 #include "utils/builtins.h"
26 #include "utils/resowner.h"
27 #include "utils/syscache.h"
31 * We don't want to log each fetching of a value from a sequence,
32 * so we pre-log a few fetches in advance. In the event of
33 * crash we can lose as much as we pre-logged.
35 #define SEQ_LOG_VALS 32
38 * The "special area" of a sequence's buffer page looks like this.
40 #define SEQ_MAGIC 0x1717
42 typedef struct sequence_magic
48 * We store a SeqTable item for every sequence we have touched in the current
49 * session. This is needed to hold onto nextval/currval state. (We can't
50 * rely on the relcache, since it's only, well, a cache, and may decide to
53 * XXX We use linear search to find pre-existing SeqTable entries. This is
54 * good when only a small number of sequences are touched in a session, but
55 * would suck with many different sequences. Perhaps use a hashtable someday.
57 typedef struct SeqTableData
59 struct SeqTableData *next; /* link to next SeqTable object */
60 Oid relid; /* pg_class OID of this sequence */
61 TransactionId xid; /* xact in which we last did a seq op */
62 int64 last; /* value last returned by nextval */
63 int64 cached; /* last value already cached for nextval */
64 /* if last != cached, we have not used up all the cached values */
65 int64 increment; /* copy of sequence's increment field */
68 typedef SeqTableData *SeqTable;
70 static SeqTable seqtab = NULL; /* Head of list of SeqTable items */
73 * last_used_seq is updated by nextval() to point to the last used
76 static SeqTableData *last_used_seq = NULL;
78 static void acquire_share_lock(Relation seqrel, SeqTable seq);
79 static void init_sequence(RangeVar *relation,
80 SeqTable *p_elm, Relation *p_rel);
81 static Form_pg_sequence read_info(SeqTable elm, Relation rel, Buffer *buf);
82 static void init_params(List *options, Form_pg_sequence new, bool isInit);
83 static void do_setval(RangeVar *sequence, int64 next, bool iscalled);
87 * Creates a new sequence relation
90 DefineSequence(CreateSeqStmt *seq)
92 FormData_pg_sequence new;
93 CreateStmt *stmt = makeNode(CreateStmt);
101 Datum value[SEQ_COL_LASTCOL];
102 char null[SEQ_COL_LASTCOL];
106 /* Check and set all option values */
107 init_params(seq->options, &new, true);
110 * Create relation (and fill *null & *value)
112 stmt->tableElts = NIL;
113 for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
118 typnam = makeNode(TypeName);
119 typnam->setof = FALSE;
120 typnam->arrayBounds = NIL;
123 coldef = makeNode(ColumnDef);
124 coldef->typename = typnam;
125 coldef->inhcount = 0;
126 coldef->is_local = true;
127 coldef->is_not_null = true;
128 coldef->raw_default = NULL;
129 coldef->cooked_default = NULL;
130 coldef->constraints = NIL;
131 coldef->support = NULL;
138 typnam->typeid = NAMEOID;
139 coldef->colname = "sequence_name";
140 namestrcpy(&name, seq->sequence->relname);
141 value[i - 1] = NameGetDatum(&name);
143 case SEQ_COL_LASTVAL:
144 typnam->typeid = INT8OID;
145 coldef->colname = "last_value";
146 value[i - 1] = Int64GetDatumFast(new.last_value);
149 typnam->typeid = INT8OID;
150 coldef->colname = "increment_by";
151 value[i - 1] = Int64GetDatumFast(new.increment_by);
153 case SEQ_COL_MAXVALUE:
154 typnam->typeid = INT8OID;
155 coldef->colname = "max_value";
156 value[i - 1] = Int64GetDatumFast(new.max_value);
158 case SEQ_COL_MINVALUE:
159 typnam->typeid = INT8OID;
160 coldef->colname = "min_value";
161 value[i - 1] = Int64GetDatumFast(new.min_value);
164 typnam->typeid = INT8OID;
165 coldef->colname = "cache_value";
166 value[i - 1] = Int64GetDatumFast(new.cache_value);
169 typnam->typeid = INT8OID;
170 coldef->colname = "log_cnt";
171 value[i - 1] = Int64GetDatum((int64) 1);
174 typnam->typeid = BOOLOID;
175 coldef->colname = "is_cycled";
176 value[i - 1] = BoolGetDatum(new.is_cycled);
179 typnam->typeid = BOOLOID;
180 coldef->colname = "is_called";
181 value[i - 1] = BoolGetDatum(false);
184 stmt->tableElts = lappend(stmt->tableElts, coldef);
187 stmt->relation = seq->sequence;
188 stmt->inhRelations = NIL;
189 stmt->constraints = NIL;
190 stmt->hasoids = MUST_NOT_HAVE_OIDS;
191 stmt->oncommit = ONCOMMIT_NOOP;
192 stmt->tablespacename = NULL;
194 seqoid = DefineRelation(stmt, RELKIND_SEQUENCE);
196 rel = heap_open(seqoid, AccessExclusiveLock);
197 tupDesc = RelationGetDescr(rel);
199 /* Initialize first page of relation with special magic number */
201 buf = ReadBuffer(rel, P_NEW);
202 Assert(BufferGetBlockNumber(buf) == 0);
204 page = (PageHeader) BufferGetPage(buf);
206 PageInit((Page) page, BufferGetPageSize(buf), sizeof(sequence_magic));
207 sm = (sequence_magic *) PageGetSpecialPointer(page);
208 sm->magic = SEQ_MAGIC;
210 /* hack: ensure heap_insert will insert on the just-created page */
211 rel->rd_targblock = 0;
213 /* Now form & insert sequence tuple */
214 tuple = heap_formtuple(tupDesc, value, null);
215 simple_heap_insert(rel, tuple);
217 Assert(ItemPointerGetOffsetNumber(&(tuple->t_self)) == FirstOffsetNumber);
220 * Two special hacks here:
222 * 1. Since VACUUM does not process sequences, we have to force the tuple
223 * to have xmin = FrozenTransactionId now. Otherwise it would become
224 * invisible to SELECTs after 2G transactions. It is okay to do this
225 * because if the current transaction aborts, no other xact will ever
226 * examine the sequence tuple anyway.
228 * 2. Even though heap_insert emitted a WAL log record, we have to emit
229 * an XLOG_SEQ_LOG record too, since (a) the heap_insert record will
230 * not have the right xmin, and (b) REDO of the heap_insert record
231 * would re-init page and sequence magic number would be lost. This
232 * means two log records instead of one :-(
234 LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
236 START_CRIT_SECTION();
240 * Note that the "tuple" structure is still just a local tuple
241 * record created by heap_formtuple; its t_data pointer doesn't
242 * point at the disk buffer. To scribble on the disk buffer we
243 * need to fetch the item pointer. But do the same to the local
244 * tuple, since that will be the source for the WAL log record,
250 itemId = PageGetItemId((Page) page, FirstOffsetNumber);
251 item = PageGetItem((Page) page, itemId);
253 HeapTupleHeaderSetXmin((HeapTupleHeader) item, FrozenTransactionId);
254 ((HeapTupleHeader) item)->t_infomask |= HEAP_XMIN_COMMITTED;
256 HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId);
257 tuple->t_data->t_infomask |= HEAP_XMIN_COMMITTED;
265 XLogRecData rdata[2];
266 Form_pg_sequence newseq = (Form_pg_sequence) GETSTRUCT(tuple);
268 /* We do not log first nextval call, so "advance" sequence here */
269 /* Note we are scribbling on local tuple, not the disk buffer */
270 newseq->is_called = true;
273 xlrec.node = rel->rd_node;
274 rdata[0].data = (char *) &xlrec;
275 rdata[0].len = sizeof(xl_seq_rec);
276 rdata[0].buffer = InvalidBuffer;
277 rdata[0].next = &(rdata[1]);
279 rdata[1].data = (char *) tuple->t_data;
280 rdata[1].len = tuple->t_len;
281 rdata[1].buffer = InvalidBuffer;
282 rdata[1].next = NULL;
284 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
286 PageSetLSN(page, recptr);
287 PageSetTLI(page, ThisTimeLineID);
292 LockBuffer(buf, BUFFER_LOCK_UNLOCK);
294 heap_close(rel, NoLock);
300 * Modify the definition of a sequence relation
303 AlterSequence(AlterSeqStmt *stmt)
309 Form_pg_sequence seq;
310 FormData_pg_sequence new;
312 /* open and AccessShareLock sequence */
313 init_sequence(stmt->sequence, &elm, &seqrel);
315 /* allow ALTER to sequence owner only */
316 if (!pg_class_ownercheck(elm->relid, GetUserId()))
317 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
318 stmt->sequence->relname);
320 /* lock page' buffer and read tuple into new sequence structure */
321 seq = read_info(elm, seqrel, &buf);
322 page = BufferGetPage(buf);
324 /* Copy old values of options into workspace */
325 memcpy(&new, seq, sizeof(FormData_pg_sequence));
327 /* Check and set new values */
328 init_params(stmt->options, &new, false);
330 /* Now okay to update the on-disk tuple */
331 memcpy(seq, &new, sizeof(FormData_pg_sequence));
333 /* Clear local cache so that we don't think we have cached numbers */
334 elm->last = new.last_value; /* last returned number */
335 elm->cached = new.last_value; /* last cached number (forget
338 START_CRIT_SECTION();
341 if (!seqrel->rd_istemp)
345 XLogRecData rdata[2];
347 xlrec.node = seqrel->rd_node;
348 rdata[0].data = (char *) &xlrec;
349 rdata[0].len = sizeof(xl_seq_rec);
350 rdata[0].buffer = InvalidBuffer;
351 rdata[0].next = &(rdata[1]);
353 rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper;
354 rdata[1].len = ((PageHeader) page)->pd_special -
355 ((PageHeader) page)->pd_upper;
356 rdata[1].buffer = InvalidBuffer;
357 rdata[1].next = NULL;
359 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
361 PageSetLSN(page, recptr);
362 PageSetTLI(page, ThisTimeLineID);
367 LockBuffer(buf, BUFFER_LOCK_UNLOCK);
371 relation_close(seqrel, NoLock);
376 nextval(PG_FUNCTION_ARGS)
378 text *seqin = PG_GETARG_TEXT_P(0);
384 Form_pg_sequence seq;
397 sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin));
399 /* open and AccessShareLock sequence */
400 init_sequence(sequence, &elm, &seqrel);
402 if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
404 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
405 errmsg("permission denied for sequence %s",
406 sequence->relname)));
408 if (elm->last != elm->cached) /* some numbers were cached */
411 elm->last += elm->increment;
412 relation_close(seqrel, NoLock);
413 PG_RETURN_INT64(elm->last);
416 /* lock page' buffer and read tuple */
417 seq = read_info(elm, seqrel, &buf);
418 page = BufferGetPage(buf);
420 last = next = result = seq->last_value;
421 incby = seq->increment_by;
422 maxv = seq->max_value;
423 minv = seq->min_value;
424 fetch = cache = seq->cache_value;
429 rescnt++; /* last_value if not called */
435 * Decide whether we should emit a WAL log record. If so, force up
436 * the fetch count to grab SEQ_LOG_VALS more values than we actually
437 * need to cache. (These will then be usable without logging.)
439 * If this is the first nextval after a checkpoint, we must force a new
440 * WAL record to be written anyway, else replay starting from the
441 * checkpoint would fail to advance the sequence past the logged
442 * values. In this case we may as well fetch extra values.
446 /* forced log to satisfy local demand for values */
447 fetch = log = fetch + SEQ_LOG_VALS;
452 XLogRecPtr redoptr = GetRedoRecPtr();
454 if (XLByteLE(PageGetLSN(page), redoptr))
456 /* last update of seq was before checkpoint */
457 fetch = log = fetch + SEQ_LOG_VALS;
462 while (fetch) /* try to fetch cache [+ log ] numbers */
465 * Check MAXVALUE for ascending sequences and MINVALUE for
466 * descending sequences
470 /* ascending sequence */
471 if ((maxv >= 0 && next > maxv - incby) ||
472 (maxv < 0 && next + incby > maxv))
475 break; /* stop fetching */
480 snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
482 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
483 errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
484 sequence->relname, buf)));
493 /* descending sequence */
494 if ((minv < 0 && next < minv - incby) ||
495 (minv >= 0 && next + incby < minv))
498 break; /* stop fetching */
503 snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
505 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
506 errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
507 sequence->relname, buf)));
520 if (rescnt == 1) /* if it's first result - */
521 result = next; /* it's what to return */
525 log -= fetch; /* adjust for any unfetched numbers */
528 /* save info in local cache */
529 elm->last = result; /* last returned number */
530 elm->cached = last; /* last fetched number */
534 START_CRIT_SECTION();
537 if (logit && !seqrel->rd_istemp)
541 XLogRecData rdata[2];
543 xlrec.node = seqrel->rd_node;
544 rdata[0].data = (char *) &xlrec;
545 rdata[0].len = sizeof(xl_seq_rec);
546 rdata[0].buffer = InvalidBuffer;
547 rdata[0].next = &(rdata[1]);
549 /* set values that will be saved in xlog */
550 seq->last_value = next;
551 seq->is_called = true;
554 rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper;
555 rdata[1].len = ((PageHeader) page)->pd_special -
556 ((PageHeader) page)->pd_upper;
557 rdata[1].buffer = InvalidBuffer;
558 rdata[1].next = NULL;
560 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
562 PageSetLSN(page, recptr);
563 PageSetTLI(page, ThisTimeLineID);
566 /* update on-disk data */
567 seq->last_value = last; /* last fetched number */
568 seq->is_called = true;
569 seq->log_cnt = log; /* how much is logged */
573 LockBuffer(buf, BUFFER_LOCK_UNLOCK);
577 relation_close(seqrel, NoLock);
579 PG_RETURN_INT64(result);
583 currval(PG_FUNCTION_ARGS)
585 text *seqin = PG_GETARG_TEXT_P(0);
591 sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin));
593 /* open and AccessShareLock sequence */
594 init_sequence(sequence, &elm, &seqrel);
596 if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK)
598 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
599 errmsg("permission denied for sequence %s",
600 sequence->relname)));
602 if (elm->increment == 0) /* nextval/read_info were not called */
604 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
605 errmsg("currval of sequence \"%s\" is not yet defined in this session",
606 sequence->relname)));
610 relation_close(seqrel, NoLock);
612 PG_RETURN_INT64(result);
616 lastval(PG_FUNCTION_ARGS)
621 if (last_used_seq == NULL)
623 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
624 errmsg("lastval is not yet defined in this session")));
626 /* Someone may have dropped the sequence since the last nextval() */
627 if (!SearchSysCacheExists(RELOID,
628 ObjectIdGetDatum(last_used_seq->relid),
631 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
632 errmsg("lastval is not yet defined in this session")));
634 seqrel = relation_open(last_used_seq->relid, NoLock);
635 acquire_share_lock(seqrel, last_used_seq);
637 /* nextval() must have already been called for this sequence */
638 Assert(last_used_seq->increment != 0);
640 if (pg_class_aclcheck(last_used_seq->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK)
642 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
643 errmsg("permission denied for sequence %s",
644 RelationGetRelationName(seqrel))));
646 result = last_used_seq->last;
647 relation_close(seqrel, NoLock);
648 PG_RETURN_INT64(result);
652 * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
654 * Note that the 3 arg version (which sets the is_called flag) is
655 * only for use in pg_dump, and setting the is_called flag may not
656 * work if multiple users are attached to the database and referencing
657 * the sequence (unlikely if pg_dump is restoring it).
659 * It is necessary to have the 3 arg version so that pg_dump can
660 * restore the state of a sequence exactly during data-only restores -
661 * it is the only way to clear the is_called flag in an existing
665 do_setval(RangeVar *sequence, int64 next, bool iscalled)
670 Form_pg_sequence seq;
672 /* open and AccessShareLock sequence */
673 init_sequence(sequence, &elm, &seqrel);
675 if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
677 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
678 errmsg("permission denied for sequence %s",
679 sequence->relname)));
681 /* lock page' buffer and read tuple */
682 seq = read_info(elm, seqrel, &buf);
684 if ((next < seq->min_value) || (next > seq->max_value))
690 snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
691 snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
692 snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
694 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
695 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
696 bufv, sequence->relname, bufm, bufx)));
699 /* save info in local cache */
700 elm->last = next; /* last returned number */
701 elm->cached = next; /* last cached number (forget cached
704 START_CRIT_SECTION();
707 if (!seqrel->rd_istemp)
711 XLogRecData rdata[2];
712 Page page = BufferGetPage(buf);
714 xlrec.node = seqrel->rd_node;
715 rdata[0].data = (char *) &xlrec;
716 rdata[0].len = sizeof(xl_seq_rec);
717 rdata[0].buffer = InvalidBuffer;
718 rdata[0].next = &(rdata[1]);
720 /* set values that will be saved in xlog */
721 seq->last_value = next;
722 seq->is_called = true;
725 rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper;
726 rdata[1].len = ((PageHeader) page)->pd_special -
727 ((PageHeader) page)->pd_upper;
728 rdata[1].buffer = InvalidBuffer;
729 rdata[1].next = NULL;
731 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
733 PageSetLSN(page, recptr);
734 PageSetTLI(page, ThisTimeLineID);
737 /* save info in sequence relation */
738 seq->last_value = next; /* last fetched number */
739 seq->is_called = iscalled;
740 seq->log_cnt = (iscalled) ? 0 : 1;
744 LockBuffer(buf, BUFFER_LOCK_UNLOCK);
748 relation_close(seqrel, NoLock);
752 * Implement the 2 arg setval procedure.
753 * See do_setval for discussion.
756 setval(PG_FUNCTION_ARGS)
758 text *seqin = PG_GETARG_TEXT_P(0);
759 int64 next = PG_GETARG_INT64(1);
762 sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin));
764 do_setval(sequence, next, true);
766 PG_RETURN_INT64(next);
770 * Implement the 3 arg setval procedure.
771 * See do_setval for discussion.
774 setval_and_iscalled(PG_FUNCTION_ARGS)
776 text *seqin = PG_GETARG_TEXT_P(0);
777 int64 next = PG_GETARG_INT64(1);
778 bool iscalled = PG_GETARG_BOOL(2);
781 sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin));
783 do_setval(sequence, next, iscalled);
785 PG_RETURN_INT64(next);
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.
796 acquire_share_lock(Relation seqrel, SeqTable seq)
798 TransactionId thisxid = GetTopTransactionId();
800 if (seq->xid != thisxid)
802 ResourceOwner currentOwner;
804 currentOwner = CurrentResourceOwner;
807 CurrentResourceOwner = TopTransactionResourceOwner;
808 LockRelation(seqrel, AccessShareLock);
812 /* Ensure CurrentResourceOwner is restored on error */
813 CurrentResourceOwner = currentOwner;
817 CurrentResourceOwner = currentOwner;
819 /* Flag that we have a lock in the current xact. */
825 * Given a relation name, open and lock the sequence. p_elm and p_rel are
829 init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel)
831 Oid relid = RangeVarGetRelid(relation, false);
832 volatile SeqTable elm;
836 * Open the sequence relation.
838 seqrel = relation_open(relid, NoLock);
840 if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
842 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
843 errmsg("\"%s\" is not a sequence",
844 relation->relname)));
846 /* Look to see if we already have a seqtable entry for relation */
847 for (elm = seqtab; elm != NULL; elm = elm->next)
849 if (elm->relid == relid)
854 * Allocate new seqtable entry if we didn't find one.
856 * NOTE: seqtable entries remain in the list for the life of a backend.
857 * If the sequence itself is deleted then the entry becomes wasted
858 * memory, but it's small enough that this should not matter.
863 * Time to make a new seqtable entry. These entries live as long
864 * as the backend does, so we use plain malloc for them.
866 elm = (SeqTable) malloc(sizeof(SeqTableData));
869 (errcode(ERRCODE_OUT_OF_MEMORY),
870 errmsg("out of memory")));
872 elm->xid = InvalidTransactionId;
873 /* increment is set to 0 until we do read_info (see currval) */
874 elm->last = elm->cached = elm->increment = 0;
879 acquire_share_lock(seqrel, elm);
886 /* Given an opened relation, lock the page buffer and find the tuple */
887 static Form_pg_sequence
888 read_info(SeqTable elm, Relation rel, Buffer *buf)
894 Form_pg_sequence seq;
896 *buf = ReadBuffer(rel, 0);
897 LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
899 page = (PageHeader) BufferGetPage(*buf);
900 sm = (sequence_magic *) PageGetSpecialPointer(page);
902 if (sm->magic != SEQ_MAGIC)
903 elog(ERROR, "bad magic number in sequence \"%s\": %08X",
904 RelationGetRelationName(rel), sm->magic);
906 lp = PageGetItemId(page, FirstOffsetNumber);
907 Assert(ItemIdIsUsed(lp));
908 tuple.t_data = (HeapTupleHeader) PageGetItem((Page) page, lp);
910 seq = (Form_pg_sequence) GETSTRUCT(&tuple);
912 elm->increment = seq->increment_by;
918 * init_params: process the options list of CREATE or ALTER SEQUENCE,
919 * and store the values into appropriate fields of *new.
921 * If isInit is true, fill any unspecified options with default values;
922 * otherwise, do not change existing options that aren't explicitly overridden.
925 init_params(List *options, Form_pg_sequence new, bool isInit)
927 DefElem *last_value = NULL;
928 DefElem *increment_by = NULL;
929 DefElem *max_value = NULL;
930 DefElem *min_value = NULL;
931 DefElem *cache_value = NULL;
932 DefElem *is_cycled = NULL;
935 foreach(option, options)
937 DefElem *defel = (DefElem *) lfirst(option);
939 if (strcmp(defel->defname, "increment") == 0)
943 (errcode(ERRCODE_SYNTAX_ERROR),
944 errmsg("conflicting or redundant options")));
945 increment_by = defel;
949 * start is for a new sequence restart is for alter
951 else if (strcmp(defel->defname, "start") == 0 ||
952 strcmp(defel->defname, "restart") == 0)
956 (errcode(ERRCODE_SYNTAX_ERROR),
957 errmsg("conflicting or redundant options")));
960 else if (strcmp(defel->defname, "maxvalue") == 0)
964 (errcode(ERRCODE_SYNTAX_ERROR),
965 errmsg("conflicting or redundant options")));
968 else if (strcmp(defel->defname, "minvalue") == 0)
972 (errcode(ERRCODE_SYNTAX_ERROR),
973 errmsg("conflicting or redundant options")));
976 else if (strcmp(defel->defname, "cache") == 0)
980 (errcode(ERRCODE_SYNTAX_ERROR),
981 errmsg("conflicting or redundant options")));
984 else if (strcmp(defel->defname, "cycle") == 0)
988 (errcode(ERRCODE_SYNTAX_ERROR),
989 errmsg("conflicting or redundant options")));
993 elog(ERROR, "option \"%s\" not recognized",
998 if (increment_by != NULL)
1000 new->increment_by = defGetInt64(increment_by);
1001 if (new->increment_by == 0)
1003 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1004 errmsg("INCREMENT must not be zero")));
1007 new->increment_by = 1;
1010 if (is_cycled != NULL)
1012 new->is_cycled = intVal(is_cycled->arg);
1013 Assert(new->is_cycled == false || new->is_cycled == true);
1016 new->is_cycled = false;
1018 /* MAXVALUE (null arg means NO MAXVALUE) */
1019 if (max_value != NULL && max_value->arg)
1020 new->max_value = defGetInt64(max_value);
1021 else if (isInit || max_value != NULL)
1023 if (new->increment_by > 0)
1024 new->max_value = SEQ_MAXVALUE; /* ascending seq */
1026 new->max_value = -1; /* descending seq */
1029 /* MINVALUE (null arg means NO MINVALUE) */
1030 if (min_value != NULL && min_value->arg)
1031 new->min_value = defGetInt64(min_value);
1032 else if (isInit || min_value != NULL)
1034 if (new->increment_by > 0)
1035 new->min_value = 1; /* ascending seq */
1037 new->min_value = SEQ_MINVALUE; /* descending seq */
1040 /* crosscheck min/max */
1041 if (new->min_value >= new->max_value)
1046 snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
1047 snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value);
1049 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1050 errmsg("MINVALUE (%s) must be less than MAXVALUE (%s)",
1055 if (last_value != NULL)
1057 new->last_value = defGetInt64(last_value);
1058 new->is_called = false;
1063 if (new->increment_by > 0)
1064 new->last_value = new->min_value; /* ascending seq */
1066 new->last_value = new->max_value; /* descending seq */
1067 new->is_called = false;
1072 if (new->last_value < new->min_value)
1077 snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
1078 snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
1080 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1081 errmsg("START value (%s) can't be less than MINVALUE (%s)",
1084 if (new->last_value > new->max_value)
1089 snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
1090 snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
1092 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1093 errmsg("START value (%s) can't be greater than MAXVALUE (%s)",
1098 if (cache_value != NULL)
1100 new->cache_value = defGetInt64(cache_value);
1101 if (new->cache_value <= 0)
1105 snprintf(buf, sizeof(buf), INT64_FORMAT, new->cache_value);
1107 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1108 errmsg("CACHE (%s) must be greater than zero",
1113 new->cache_value = 1;
1118 seq_redo(XLogRecPtr lsn, XLogRecord *record)
1120 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1126 xl_seq_rec *xlrec = (xl_seq_rec *) XLogRecGetData(record);
1129 if (info != XLOG_SEQ_LOG)
1130 elog(PANIC, "seq_redo: unknown op code %u", info);
1132 reln = XLogOpenRelation(xlrec->node);
1133 if (!RelationIsValid(reln))
1136 buffer = XLogReadBuffer(true, reln, 0);
1137 if (!BufferIsValid(buffer))
1138 elog(PANIC, "seq_redo: can't read block 0 of rel %u/%u/%u",
1139 xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);
1141 page = (Page) BufferGetPage(buffer);
1143 /* Always reinit the page and reinstall the magic number */
1144 /* See comments in DefineSequence */
1145 PageInit((Page) page, BufferGetPageSize(buffer), sizeof(sequence_magic));
1146 sm = (sequence_magic *) PageGetSpecialPointer(page);
1147 sm->magic = SEQ_MAGIC;
1149 item = (char *) xlrec + sizeof(xl_seq_rec);
1150 itemsz = record->xl_len - sizeof(xl_seq_rec);
1151 itemsz = MAXALIGN(itemsz);
1152 if (PageAddItem(page, (Item) item, itemsz,
1153 FirstOffsetNumber, LP_USED) == InvalidOffsetNumber)
1154 elog(PANIC, "seq_redo: failed to add item to page");
1156 PageSetLSN(page, lsn);
1157 PageSetTLI(page, ThisTimeLineID);
1158 LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1159 WriteBuffer(buffer);
1163 seq_desc(char *buf, uint8 xl_info, char *rec)
1165 uint8 info = xl_info & ~XLR_INFO_MASK;
1166 xl_seq_rec *xlrec = (xl_seq_rec *) rec;
1168 if (info == XLOG_SEQ_LOG)
1169 strcat(buf, "log: ");
1172 strcat(buf, "UNKNOWN");
1176 sprintf(buf + strlen(buf), "rel %u/%u/%u",
1177 xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);