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.119 2004/12/31 21:59:41 pgsql 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"
30 * We don't want to log each fetching of a value from a sequence,
31 * so we pre-log a few fetches in advance. In the event of
32 * crash we can lose as much as we pre-logged.
34 #define SEQ_LOG_VALS 32
37 * The "special area" of a sequence's buffer page looks like this.
39 #define SEQ_MAGIC 0x1717
41 typedef struct sequence_magic
47 * We store a SeqTable item for every sequence we have touched in the current
48 * session. This is needed to hold onto nextval/currval state. (We can't
49 * rely on the relcache, since it's only, well, a cache, and may decide to
52 * XXX We use linear search to find pre-existing SeqTable entries. This is
53 * good when only a small number of sequences are touched in a session, but
54 * would suck with many different sequences. Perhaps use a hashtable someday.
56 typedef struct SeqTableData
58 struct SeqTableData *next; /* link to next SeqTable object */
59 Oid relid; /* pg_class OID of this sequence */
60 TransactionId xid; /* xact in which we last did a seq op */
61 int64 last; /* value last returned by nextval */
62 int64 cached; /* last value already cached for nextval */
63 /* if last != cached, we have not used up all the cached values */
64 int64 increment; /* copy of sequence's increment field */
67 typedef SeqTableData *SeqTable;
69 static SeqTable seqtab = NULL; /* Head of list of SeqTable items */
72 static void init_sequence(RangeVar *relation,
73 SeqTable *p_elm, Relation *p_rel);
74 static Form_pg_sequence read_info(SeqTable elm, Relation rel, Buffer *buf);
75 static void init_params(List *options, Form_pg_sequence new, bool isInit);
76 static void do_setval(RangeVar *sequence, int64 next, bool iscalled);
80 * Creates a new sequence relation
83 DefineSequence(CreateSeqStmt *seq)
85 FormData_pg_sequence new;
86 CreateStmt *stmt = makeNode(CreateStmt);
94 Datum value[SEQ_COL_LASTCOL];
95 char null[SEQ_COL_LASTCOL];
99 /* Check and set all option values */
100 init_params(seq->options, &new, true);
103 * Create relation (and fill *null & *value)
105 stmt->tableElts = NIL;
106 for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
111 typnam = makeNode(TypeName);
112 typnam->setof = FALSE;
113 typnam->arrayBounds = NIL;
116 coldef = makeNode(ColumnDef);
117 coldef->typename = typnam;
118 coldef->inhcount = 0;
119 coldef->is_local = true;
120 coldef->is_not_null = true;
121 coldef->raw_default = NULL;
122 coldef->cooked_default = NULL;
123 coldef->constraints = NIL;
124 coldef->support = NULL;
131 typnam->typeid = NAMEOID;
132 coldef->colname = "sequence_name";
133 namestrcpy(&name, seq->sequence->relname);
134 value[i - 1] = NameGetDatum(&name);
136 case SEQ_COL_LASTVAL:
137 typnam->typeid = INT8OID;
138 coldef->colname = "last_value";
139 value[i - 1] = Int64GetDatumFast(new.last_value);
142 typnam->typeid = INT8OID;
143 coldef->colname = "increment_by";
144 value[i - 1] = Int64GetDatumFast(new.increment_by);
146 case SEQ_COL_MAXVALUE:
147 typnam->typeid = INT8OID;
148 coldef->colname = "max_value";
149 value[i - 1] = Int64GetDatumFast(new.max_value);
151 case SEQ_COL_MINVALUE:
152 typnam->typeid = INT8OID;
153 coldef->colname = "min_value";
154 value[i - 1] = Int64GetDatumFast(new.min_value);
157 typnam->typeid = INT8OID;
158 coldef->colname = "cache_value";
159 value[i - 1] = Int64GetDatumFast(new.cache_value);
162 typnam->typeid = INT8OID;
163 coldef->colname = "log_cnt";
164 value[i - 1] = Int64GetDatum((int64) 1);
167 typnam->typeid = BOOLOID;
168 coldef->colname = "is_cycled";
169 value[i - 1] = BoolGetDatum(new.is_cycled);
172 typnam->typeid = BOOLOID;
173 coldef->colname = "is_called";
174 value[i - 1] = BoolGetDatum(false);
177 stmt->tableElts = lappend(stmt->tableElts, coldef);
180 stmt->relation = seq->sequence;
181 stmt->inhRelations = NIL;
182 stmt->constraints = NIL;
183 stmt->hasoids = MUST_NOT_HAVE_OIDS;
184 stmt->oncommit = ONCOMMIT_NOOP;
185 stmt->tablespacename = NULL;
187 seqoid = DefineRelation(stmt, RELKIND_SEQUENCE);
189 rel = heap_open(seqoid, AccessExclusiveLock);
190 tupDesc = RelationGetDescr(rel);
192 /* Initialize first page of relation with special magic number */
194 buf = ReadBuffer(rel, P_NEW);
195 Assert(BufferGetBlockNumber(buf) == 0);
197 page = (PageHeader) BufferGetPage(buf);
199 PageInit((Page) page, BufferGetPageSize(buf), sizeof(sequence_magic));
200 sm = (sequence_magic *) PageGetSpecialPointer(page);
201 sm->magic = SEQ_MAGIC;
203 /* hack: ensure heap_insert will insert on the just-created page */
204 rel->rd_targblock = 0;
206 /* Now form & insert sequence tuple */
207 tuple = heap_formtuple(tupDesc, value, null);
208 simple_heap_insert(rel, tuple);
210 Assert(ItemPointerGetOffsetNumber(&(tuple->t_self)) == FirstOffsetNumber);
213 * Two special hacks here:
215 * 1. Since VACUUM does not process sequences, we have to force the tuple
216 * to have xmin = FrozenTransactionId now. Otherwise it would become
217 * invisible to SELECTs after 2G transactions. It is okay to do this
218 * because if the current transaction aborts, no other xact will ever
219 * examine the sequence tuple anyway.
221 * 2. Even though heap_insert emitted a WAL log record, we have to emit
222 * an XLOG_SEQ_LOG record too, since (a) the heap_insert record will
223 * not have the right xmin, and (b) REDO of the heap_insert record
224 * would re-init page and sequence magic number would be lost. This
225 * means two log records instead of one :-(
227 LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
229 START_CRIT_SECTION();
233 * Note that the "tuple" structure is still just a local tuple
234 * record created by heap_formtuple; its t_data pointer doesn't
235 * point at the disk buffer. To scribble on the disk buffer we
236 * need to fetch the item pointer. But do the same to the local
237 * tuple, since that will be the source for the WAL log record,
243 itemId = PageGetItemId((Page) page, FirstOffsetNumber);
244 item = PageGetItem((Page) page, itemId);
246 HeapTupleHeaderSetXmin((HeapTupleHeader) item, FrozenTransactionId);
247 ((HeapTupleHeader) item)->t_infomask |= HEAP_XMIN_COMMITTED;
249 HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId);
250 tuple->t_data->t_infomask |= HEAP_XMIN_COMMITTED;
258 XLogRecData rdata[2];
259 Form_pg_sequence newseq = (Form_pg_sequence) GETSTRUCT(tuple);
261 /* We do not log first nextval call, so "advance" sequence here */
262 /* Note we are scribbling on local tuple, not the disk buffer */
263 newseq->is_called = true;
266 xlrec.node = rel->rd_node;
267 rdata[0].buffer = InvalidBuffer;
268 rdata[0].data = (char *) &xlrec;
269 rdata[0].len = sizeof(xl_seq_rec);
270 rdata[0].next = &(rdata[1]);
272 rdata[1].buffer = InvalidBuffer;
273 rdata[1].data = (char *) tuple->t_data;
274 rdata[1].len = tuple->t_len;
275 rdata[1].next = NULL;
277 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
279 PageSetLSN(page, recptr);
280 PageSetTLI(page, ThisTimeLineID);
285 LockBuffer(buf, BUFFER_LOCK_UNLOCK);
287 heap_close(rel, NoLock);
293 * Modify the definition of a sequence relation
296 AlterSequence(AlterSeqStmt *stmt)
302 Form_pg_sequence seq;
303 FormData_pg_sequence new;
305 /* open and AccessShareLock sequence */
306 init_sequence(stmt->sequence, &elm, &seqrel);
308 /* allow ALTER to sequence owner only */
309 if (!pg_class_ownercheck(elm->relid, GetUserId()))
310 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
311 stmt->sequence->relname);
313 /* lock page' buffer and read tuple into new sequence structure */
314 seq = read_info(elm, seqrel, &buf);
315 page = BufferGetPage(buf);
317 /* Copy old values of options into workspace */
318 memcpy(&new, seq, sizeof(FormData_pg_sequence));
320 /* Check and set new values */
321 init_params(stmt->options, &new, false);
323 /* Now okay to update the on-disk tuple */
324 memcpy(seq, &new, sizeof(FormData_pg_sequence));
326 /* Clear local cache so that we don't think we have cached numbers */
327 elm->last = new.last_value; /* last returned number */
328 elm->cached = new.last_value; /* last cached number (forget
331 START_CRIT_SECTION();
334 if (!seqrel->rd_istemp)
338 XLogRecData rdata[2];
340 xlrec.node = seqrel->rd_node;
341 rdata[0].buffer = InvalidBuffer;
342 rdata[0].data = (char *) &xlrec;
343 rdata[0].len = sizeof(xl_seq_rec);
344 rdata[0].next = &(rdata[1]);
346 rdata[1].buffer = InvalidBuffer;
347 rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper;
348 rdata[1].len = ((PageHeader) page)->pd_special -
349 ((PageHeader) page)->pd_upper;
350 rdata[1].next = NULL;
352 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
354 PageSetLSN(page, recptr);
355 PageSetTLI(page, ThisTimeLineID);
360 LockBuffer(buf, BUFFER_LOCK_UNLOCK);
364 relation_close(seqrel, NoLock);
369 nextval(PG_FUNCTION_ARGS)
371 text *seqin = PG_GETARG_TEXT_P(0);
377 Form_pg_sequence seq;
390 sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
393 /* open and AccessShareLock sequence */
394 init_sequence(sequence, &elm, &seqrel);
396 if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
398 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
399 errmsg("permission denied for sequence %s",
400 sequence->relname)));
402 if (elm->last != elm->cached) /* some numbers were cached */
404 elm->last += elm->increment;
405 relation_close(seqrel, NoLock);
406 PG_RETURN_INT64(elm->last);
409 /* lock page' buffer and read tuple */
410 seq = read_info(elm, seqrel, &buf);
411 page = BufferGetPage(buf);
413 last = next = result = seq->last_value;
414 incby = seq->increment_by;
415 maxv = seq->max_value;
416 minv = seq->min_value;
417 fetch = cache = seq->cache_value;
422 rescnt++; /* last_value if not called */
428 * Decide whether we should emit a WAL log record. If so, force up
429 * the fetch count to grab SEQ_LOG_VALS more values than we actually
430 * need to cache. (These will then be usable without logging.)
432 * If this is the first nextval after a checkpoint, we must force a new
433 * WAL record to be written anyway, else replay starting from the
434 * checkpoint would fail to advance the sequence past the logged
435 * values. In this case we may as well fetch extra values.
439 /* forced log to satisfy local demand for values */
440 fetch = log = fetch + SEQ_LOG_VALS;
445 XLogRecPtr redoptr = GetRedoRecPtr();
447 if (XLByteLE(PageGetLSN(page), redoptr))
449 /* last update of seq was before checkpoint */
450 fetch = log = fetch + SEQ_LOG_VALS;
455 while (fetch) /* try to fetch cache [+ log ] numbers */
458 * Check MAXVALUE for ascending sequences and MINVALUE for
459 * descending sequences
463 /* ascending sequence */
464 if ((maxv >= 0 && next > maxv - incby) ||
465 (maxv < 0 && next + incby > maxv))
468 break; /* stop fetching */
473 snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
475 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
476 errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
477 sequence->relname, buf)));
486 /* descending sequence */
487 if ((minv < 0 && next < minv - incby) ||
488 (minv >= 0 && next + incby < minv))
491 break; /* stop fetching */
496 snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
498 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
499 errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
500 sequence->relname, buf)));
513 if (rescnt == 1) /* if it's first result - */
514 result = next; /* it's what to return */
518 log -= fetch; /* adjust for any unfetched numbers */
521 /* save info in local cache */
522 elm->last = result; /* last returned number */
523 elm->cached = last; /* last fetched number */
525 START_CRIT_SECTION();
528 if (logit && !seqrel->rd_istemp)
532 XLogRecData rdata[2];
534 xlrec.node = seqrel->rd_node;
535 rdata[0].buffer = InvalidBuffer;
536 rdata[0].data = (char *) &xlrec;
537 rdata[0].len = sizeof(xl_seq_rec);
538 rdata[0].next = &(rdata[1]);
540 /* set values that will be saved in xlog */
541 seq->last_value = next;
542 seq->is_called = true;
545 rdata[1].buffer = InvalidBuffer;
546 rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper;
547 rdata[1].len = ((PageHeader) page)->pd_special -
548 ((PageHeader) page)->pd_upper;
549 rdata[1].next = NULL;
551 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
553 PageSetLSN(page, recptr);
554 PageSetTLI(page, ThisTimeLineID);
557 /* update on-disk data */
558 seq->last_value = last; /* last fetched number */
559 seq->is_called = true;
560 seq->log_cnt = log; /* how much is logged */
564 LockBuffer(buf, BUFFER_LOCK_UNLOCK);
568 relation_close(seqrel, NoLock);
570 PG_RETURN_INT64(result);
574 currval(PG_FUNCTION_ARGS)
576 text *seqin = PG_GETARG_TEXT_P(0);
582 sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
585 /* open and AccessShareLock sequence */
586 init_sequence(sequence, &elm, &seqrel);
588 if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK)
590 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
591 errmsg("permission denied for sequence %s",
592 sequence->relname)));
594 if (elm->increment == 0) /* nextval/read_info were not called */
596 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
597 errmsg("currval of sequence \"%s\" is not yet defined in this session",
598 sequence->relname)));
602 relation_close(seqrel, NoLock);
604 PG_RETURN_INT64(result);
608 * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
610 * Note that the 3 arg version (which sets the is_called flag) is
611 * only for use in pg_dump, and setting the is_called flag may not
612 * work if multiple users are attached to the database and referencing
613 * the sequence (unlikely if pg_dump is restoring it).
615 * It is necessary to have the 3 arg version so that pg_dump can
616 * restore the state of a sequence exactly during data-only restores -
617 * it is the only way to clear the is_called flag in an existing
621 do_setval(RangeVar *sequence, int64 next, bool iscalled)
626 Form_pg_sequence seq;
628 /* open and AccessShareLock sequence */
629 init_sequence(sequence, &elm, &seqrel);
631 if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
633 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
634 errmsg("permission denied for sequence %s",
635 sequence->relname)));
637 /* lock page' buffer and read tuple */
638 seq = read_info(elm, seqrel, &buf);
640 if ((next < seq->min_value) || (next > seq->max_value))
646 snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
647 snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
648 snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
650 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
651 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
652 bufv, sequence->relname, bufm, bufx)));
655 /* save info in local cache */
656 elm->last = next; /* last returned number */
657 elm->cached = next; /* last cached number (forget cached
660 START_CRIT_SECTION();
663 if (!seqrel->rd_istemp)
667 XLogRecData rdata[2];
668 Page page = BufferGetPage(buf);
670 xlrec.node = seqrel->rd_node;
671 rdata[0].buffer = InvalidBuffer;
672 rdata[0].data = (char *) &xlrec;
673 rdata[0].len = sizeof(xl_seq_rec);
674 rdata[0].next = &(rdata[1]);
676 /* set values that will be saved in xlog */
677 seq->last_value = next;
678 seq->is_called = true;
681 rdata[1].buffer = InvalidBuffer;
682 rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper;
683 rdata[1].len = ((PageHeader) page)->pd_special -
684 ((PageHeader) page)->pd_upper;
685 rdata[1].next = NULL;
687 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
689 PageSetLSN(page, recptr);
690 PageSetTLI(page, ThisTimeLineID);
693 /* save info in sequence relation */
694 seq->last_value = next; /* last fetched number */
695 seq->is_called = iscalled;
696 seq->log_cnt = (iscalled) ? 0 : 1;
700 LockBuffer(buf, BUFFER_LOCK_UNLOCK);
704 relation_close(seqrel, NoLock);
708 * Implement the 2 arg setval procedure.
709 * See do_setval for discussion.
712 setval(PG_FUNCTION_ARGS)
714 text *seqin = PG_GETARG_TEXT_P(0);
715 int64 next = PG_GETARG_INT64(1);
718 sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
721 do_setval(sequence, next, true);
723 PG_RETURN_INT64(next);
727 * Implement the 3 arg setval procedure.
728 * See do_setval for discussion.
731 setval_and_iscalled(PG_FUNCTION_ARGS)
733 text *seqin = PG_GETARG_TEXT_P(0);
734 int64 next = PG_GETARG_INT64(1);
735 bool iscalled = PG_GETARG_BOOL(2);
738 sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
741 do_setval(sequence, next, iscalled);
743 PG_RETURN_INT64(next);
748 * Given a relation name, open and lock the sequence. p_elm and p_rel are
752 init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel)
754 Oid relid = RangeVarGetRelid(relation, false);
755 TransactionId thisxid = GetTopTransactionId();
756 volatile SeqTable elm;
760 * Open the sequence relation.
762 seqrel = relation_open(relid, NoLock);
764 if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
766 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
767 errmsg("\"%s\" is not a sequence",
768 relation->relname)));
770 /* Look to see if we already have a seqtable entry for relation */
771 for (elm = seqtab; elm != NULL; elm = elm->next)
773 if (elm->relid == relid)
778 * Allocate new seqtable entry if we didn't find one.
780 * NOTE: seqtable entries remain in the list for the life of a backend.
781 * If the sequence itself is deleted then the entry becomes wasted
782 * memory, but it's small enough that this should not matter.
787 * Time to make a new seqtable entry. These entries live as long
788 * as the backend does, so we use plain malloc for them.
790 elm = (SeqTable) malloc(sizeof(SeqTableData));
793 (errcode(ERRCODE_OUT_OF_MEMORY),
794 errmsg("out of memory")));
796 elm->xid = InvalidTransactionId;
797 /* increment is set to 0 until we do read_info (see currval) */
798 elm->last = elm->cached = elm->increment = 0;
804 * If we haven't touched the sequence already in this transaction,
805 * we need to acquire AccessShareLock. We arrange for the lock to
806 * be owned by the top transaction, so that we don't need to do it
807 * more than once per xact.
809 if (elm->xid != thisxid)
811 ResourceOwner currentOwner;
813 currentOwner = CurrentResourceOwner;
816 CurrentResourceOwner = TopTransactionResourceOwner;
818 LockRelation(seqrel, AccessShareLock);
822 /* Ensure CurrentResourceOwner is restored on error */
823 CurrentResourceOwner = currentOwner;
827 CurrentResourceOwner = currentOwner;
829 /* Flag that we have a lock in the current xact. */
838 /* Given an opened relation, lock the page buffer and find the tuple */
839 static Form_pg_sequence
840 read_info(SeqTable elm, Relation rel, Buffer *buf)
846 Form_pg_sequence seq;
848 *buf = ReadBuffer(rel, 0);
849 LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
851 page = (PageHeader) BufferGetPage(*buf);
852 sm = (sequence_magic *) PageGetSpecialPointer(page);
854 if (sm->magic != SEQ_MAGIC)
855 elog(ERROR, "bad magic number in sequence \"%s\": %08X",
856 RelationGetRelationName(rel), sm->magic);
858 lp = PageGetItemId(page, FirstOffsetNumber);
859 Assert(ItemIdIsUsed(lp));
860 tuple.t_data = (HeapTupleHeader) PageGetItem((Page) page, lp);
862 seq = (Form_pg_sequence) GETSTRUCT(&tuple);
864 elm->increment = seq->increment_by;
870 * init_params: process the options list of CREATE or ALTER SEQUENCE,
871 * and store the values into appropriate fields of *new.
873 * If isInit is true, fill any unspecified options with default values;
874 * otherwise, do not change existing options that aren't explicitly overridden.
877 init_params(List *options, Form_pg_sequence new, bool isInit)
879 DefElem *last_value = NULL;
880 DefElem *increment_by = NULL;
881 DefElem *max_value = NULL;
882 DefElem *min_value = NULL;
883 DefElem *cache_value = NULL;
884 DefElem *is_cycled = NULL;
887 foreach(option, options)
889 DefElem *defel = (DefElem *) lfirst(option);
891 if (strcmp(defel->defname, "increment") == 0)
895 (errcode(ERRCODE_SYNTAX_ERROR),
896 errmsg("conflicting or redundant options")));
897 increment_by = defel;
901 * start is for a new sequence restart is for alter
903 else if (strcmp(defel->defname, "start") == 0 ||
904 strcmp(defel->defname, "restart") == 0)
908 (errcode(ERRCODE_SYNTAX_ERROR),
909 errmsg("conflicting or redundant options")));
912 else if (strcmp(defel->defname, "maxvalue") == 0)
916 (errcode(ERRCODE_SYNTAX_ERROR),
917 errmsg("conflicting or redundant options")));
920 else if (strcmp(defel->defname, "minvalue") == 0)
924 (errcode(ERRCODE_SYNTAX_ERROR),
925 errmsg("conflicting or redundant options")));
928 else if (strcmp(defel->defname, "cache") == 0)
932 (errcode(ERRCODE_SYNTAX_ERROR),
933 errmsg("conflicting or redundant options")));
936 else if (strcmp(defel->defname, "cycle") == 0)
940 (errcode(ERRCODE_SYNTAX_ERROR),
941 errmsg("conflicting or redundant options")));
945 elog(ERROR, "option \"%s\" not recognized",
950 if (increment_by != NULL)
952 new->increment_by = defGetInt64(increment_by);
953 if (new->increment_by == 0)
955 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
956 errmsg("INCREMENT must not be zero")));
959 new->increment_by = 1;
962 if (is_cycled != NULL)
964 new->is_cycled = intVal(is_cycled->arg);
965 Assert(new->is_cycled == false || new->is_cycled == true);
968 new->is_cycled = false;
970 /* MAXVALUE (null arg means NO MAXVALUE) */
971 if (max_value != NULL && max_value->arg)
972 new->max_value = defGetInt64(max_value);
973 else if (isInit || max_value != NULL)
975 if (new->increment_by > 0)
976 new->max_value = SEQ_MAXVALUE; /* ascending seq */
978 new->max_value = -1; /* descending seq */
981 /* MINVALUE (null arg means NO MINVALUE) */
982 if (min_value != NULL && min_value->arg)
983 new->min_value = defGetInt64(min_value);
984 else if (isInit || min_value != NULL)
986 if (new->increment_by > 0)
987 new->min_value = 1; /* ascending seq */
989 new->min_value = SEQ_MINVALUE; /* descending seq */
992 /* crosscheck min/max */
993 if (new->min_value >= new->max_value)
998 snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
999 snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value);
1001 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1002 errmsg("MINVALUE (%s) must be less than MAXVALUE (%s)",
1007 if (last_value != NULL)
1009 new->last_value = defGetInt64(last_value);
1010 new->is_called = false;
1015 if (new->increment_by > 0)
1016 new->last_value = new->min_value; /* ascending seq */
1018 new->last_value = new->max_value; /* descending seq */
1019 new->is_called = false;
1024 if (new->last_value < new->min_value)
1029 snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
1030 snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
1032 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1033 errmsg("START value (%s) can't be less than MINVALUE (%s)",
1036 if (new->last_value > new->max_value)
1041 snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
1042 snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
1044 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1045 errmsg("START value (%s) can't be greater than MAXVALUE (%s)",
1050 if (cache_value != NULL)
1052 new->cache_value = defGetInt64(cache_value);
1053 if (new->cache_value <= 0)
1057 snprintf(buf, sizeof(buf), INT64_FORMAT, new->cache_value);
1059 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1060 errmsg("CACHE (%s) must be greater than zero",
1065 new->cache_value = 1;
1070 seq_redo(XLogRecPtr lsn, XLogRecord *record)
1072 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1078 xl_seq_rec *xlrec = (xl_seq_rec *) XLogRecGetData(record);
1081 if (info != XLOG_SEQ_LOG)
1082 elog(PANIC, "seq_redo: unknown op code %u", info);
1084 reln = XLogOpenRelation(true, RM_SEQ_ID, xlrec->node);
1085 if (!RelationIsValid(reln))
1088 buffer = XLogReadBuffer(true, reln, 0);
1089 if (!BufferIsValid(buffer))
1090 elog(PANIC, "seq_redo: can't read block 0 of rel %u/%u/%u",
1091 xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);
1093 page = (Page) BufferGetPage(buffer);
1095 /* Always reinit the page and reinstall the magic number */
1096 /* See comments in DefineSequence */
1097 PageInit((Page) page, BufferGetPageSize(buffer), sizeof(sequence_magic));
1098 sm = (sequence_magic *) PageGetSpecialPointer(page);
1099 sm->magic = SEQ_MAGIC;
1101 item = (char *) xlrec + sizeof(xl_seq_rec);
1102 itemsz = record->xl_len - sizeof(xl_seq_rec);
1103 itemsz = MAXALIGN(itemsz);
1104 if (PageAddItem(page, (Item) item, itemsz,
1105 FirstOffsetNumber, LP_USED) == InvalidOffsetNumber)
1106 elog(PANIC, "seq_redo: failed to add item to page");
1108 PageSetLSN(page, lsn);
1109 PageSetTLI(page, ThisTimeLineID);
1110 LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1111 WriteBuffer(buffer);
1115 seq_undo(XLogRecPtr lsn, XLogRecord *record)
1120 seq_desc(char *buf, uint8 xl_info, char *rec)
1122 uint8 info = xl_info & ~XLR_INFO_MASK;
1123 xl_seq_rec *xlrec = (xl_seq_rec *) rec;
1125 if (info == XLOG_SEQ_LOG)
1126 strcat(buf, "log: ");
1129 strcat(buf, "UNKNOWN");
1133 sprintf(buf + strlen(buf), "rel %u/%u/%u",
1134 xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);