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);