]> granicus.if.org Git - postgresql/blob - src/backend/commands/sequence.c
For some reason access/tupmacs.h has been #including utils/memutils.h,
[postgresql] / src / backend / commands / sequence.c
1 /*-------------------------------------------------------------------------
2  *
3  * sequence.c
4  *        PostgreSQL sequences support code.
5  *
6  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.119 2004/12/31 21:59:41 pgsql Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
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
28
29 /*
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.
33  */
34 #define SEQ_LOG_VALS    32
35
36 /*
37  * The "special area" of a sequence's buffer page looks like this.
38  */
39 #define SEQ_MAGIC         0x1717
40
41 typedef struct sequence_magic
42 {
43         uint32          magic;
44 } sequence_magic;
45
46 /*
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
50  * discard entries.)
51  *
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.
55  */
56 typedef struct SeqTableData
57 {
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 */
65 } SeqTableData;
66
67 typedef SeqTableData *SeqTable;
68
69 static SeqTable seqtab = NULL;  /* Head of list of SeqTable items */
70
71
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);
77
78 /*
79  * DefineSequence
80  *                              Creates a new sequence relation
81  */
82 void
83 DefineSequence(CreateSeqStmt *seq)
84 {
85         FormData_pg_sequence new;
86         CreateStmt *stmt = makeNode(CreateStmt);
87         Oid                     seqoid;
88         Relation        rel;
89         Buffer          buf;
90         PageHeader      page;
91         sequence_magic *sm;
92         HeapTuple       tuple;
93         TupleDesc       tupDesc;
94         Datum           value[SEQ_COL_LASTCOL];
95         char            null[SEQ_COL_LASTCOL];
96         int                     i;
97         NameData        name;
98
99         /* Check and set all option values */
100         init_params(seq->options, &new, true);
101
102         /*
103          * Create relation (and fill *null & *value)
104          */
105         stmt->tableElts = NIL;
106         for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
107         {
108                 ColumnDef  *coldef;
109                 TypeName   *typnam;
110
111                 typnam = makeNode(TypeName);
112                 typnam->setof = FALSE;
113                 typnam->arrayBounds = NIL;
114                 typnam->typmod = -1;
115
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;
125
126                 null[i - 1] = ' ';
127
128                 switch (i)
129                 {
130                         case SEQ_COL_NAME:
131                                 typnam->typeid = NAMEOID;
132                                 coldef->colname = "sequence_name";
133                                 namestrcpy(&name, seq->sequence->relname);
134                                 value[i - 1] = NameGetDatum(&name);
135                                 break;
136                         case SEQ_COL_LASTVAL:
137                                 typnam->typeid = INT8OID;
138                                 coldef->colname = "last_value";
139                                 value[i - 1] = Int64GetDatumFast(new.last_value);
140                                 break;
141                         case SEQ_COL_INCBY:
142                                 typnam->typeid = INT8OID;
143                                 coldef->colname = "increment_by";
144                                 value[i - 1] = Int64GetDatumFast(new.increment_by);
145                                 break;
146                         case SEQ_COL_MAXVALUE:
147                                 typnam->typeid = INT8OID;
148                                 coldef->colname = "max_value";
149                                 value[i - 1] = Int64GetDatumFast(new.max_value);
150                                 break;
151                         case SEQ_COL_MINVALUE:
152                                 typnam->typeid = INT8OID;
153                                 coldef->colname = "min_value";
154                                 value[i - 1] = Int64GetDatumFast(new.min_value);
155                                 break;
156                         case SEQ_COL_CACHE:
157                                 typnam->typeid = INT8OID;
158                                 coldef->colname = "cache_value";
159                                 value[i - 1] = Int64GetDatumFast(new.cache_value);
160                                 break;
161                         case SEQ_COL_LOG:
162                                 typnam->typeid = INT8OID;
163                                 coldef->colname = "log_cnt";
164                                 value[i - 1] = Int64GetDatum((int64) 1);
165                                 break;
166                         case SEQ_COL_CYCLE:
167                                 typnam->typeid = BOOLOID;
168                                 coldef->colname = "is_cycled";
169                                 value[i - 1] = BoolGetDatum(new.is_cycled);
170                                 break;
171                         case SEQ_COL_CALLED:
172                                 typnam->typeid = BOOLOID;
173                                 coldef->colname = "is_called";
174                                 value[i - 1] = BoolGetDatum(false);
175                                 break;
176                 }
177                 stmt->tableElts = lappend(stmt->tableElts, coldef);
178         }
179
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;
186
187         seqoid = DefineRelation(stmt, RELKIND_SEQUENCE);
188
189         rel = heap_open(seqoid, AccessExclusiveLock);
190         tupDesc = RelationGetDescr(rel);
191
192         /* Initialize first page of relation with special magic number */
193
194         buf = ReadBuffer(rel, P_NEW);
195         Assert(BufferGetBlockNumber(buf) == 0);
196
197         page = (PageHeader) BufferGetPage(buf);
198
199         PageInit((Page) page, BufferGetPageSize(buf), sizeof(sequence_magic));
200         sm = (sequence_magic *) PageGetSpecialPointer(page);
201         sm->magic = SEQ_MAGIC;
202
203         /* hack: ensure heap_insert will insert on the just-created page */
204         rel->rd_targblock = 0;
205
206         /* Now form & insert sequence tuple */
207         tuple = heap_formtuple(tupDesc, value, null);
208         simple_heap_insert(rel, tuple);
209
210         Assert(ItemPointerGetOffsetNumber(&(tuple->t_self)) == FirstOffsetNumber);
211
212         /*
213          * Two special hacks here:
214          *
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.
220          *
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 :-(
226          */
227         LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
228
229         START_CRIT_SECTION();
230
231         {
232                 /*
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,
238                  * below.
239                  */
240                 ItemId          itemId;
241                 Item            item;
242
243                 itemId = PageGetItemId((Page) page, FirstOffsetNumber);
244                 item = PageGetItem((Page) page, itemId);
245
246                 HeapTupleHeaderSetXmin((HeapTupleHeader) item, FrozenTransactionId);
247                 ((HeapTupleHeader) item)->t_infomask |= HEAP_XMIN_COMMITTED;
248
249                 HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId);
250                 tuple->t_data->t_infomask |= HEAP_XMIN_COMMITTED;
251         }
252
253         /* XLOG stuff */
254         if (!rel->rd_istemp)
255         {
256                 xl_seq_rec      xlrec;
257                 XLogRecPtr      recptr;
258                 XLogRecData rdata[2];
259                 Form_pg_sequence newseq = (Form_pg_sequence) GETSTRUCT(tuple);
260
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;
264                 newseq->log_cnt = 0;
265
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]);
271
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;
276
277                 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
278
279                 PageSetLSN(page, recptr);
280                 PageSetTLI(page, ThisTimeLineID);
281         }
282
283         END_CRIT_SECTION();
284
285         LockBuffer(buf, BUFFER_LOCK_UNLOCK);
286         WriteBuffer(buf);
287         heap_close(rel, NoLock);
288 }
289
290 /*
291  * AlterSequence
292  *
293  * Modify the definition of a sequence relation
294  */
295 void
296 AlterSequence(AlterSeqStmt *stmt)
297 {
298         SeqTable        elm;
299         Relation        seqrel;
300         Buffer          buf;
301         Page            page;
302         Form_pg_sequence seq;
303         FormData_pg_sequence new;
304
305         /* open and AccessShareLock sequence */
306         init_sequence(stmt->sequence, &elm, &seqrel);
307
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);
312
313         /* lock page' buffer and read tuple into new sequence structure */
314         seq = read_info(elm, seqrel, &buf);
315         page = BufferGetPage(buf);
316
317         /* Copy old values of options into workspace */
318         memcpy(&new, seq, sizeof(FormData_pg_sequence));
319
320         /* Check and set new values */
321         init_params(stmt->options, &new, false);
322
323         /* Now okay to update the on-disk tuple */
324         memcpy(seq, &new, sizeof(FormData_pg_sequence));
325
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
329                                                                                  * cached values) */
330
331         START_CRIT_SECTION();
332
333         /* XLOG stuff */
334         if (!seqrel->rd_istemp)
335         {
336                 xl_seq_rec      xlrec;
337                 XLogRecPtr      recptr;
338                 XLogRecData rdata[2];
339
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]);
345
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;
351
352                 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
353
354                 PageSetLSN(page, recptr);
355                 PageSetTLI(page, ThisTimeLineID);
356         }
357
358         END_CRIT_SECTION();
359
360         LockBuffer(buf, BUFFER_LOCK_UNLOCK);
361
362         WriteBuffer(buf);
363
364         relation_close(seqrel, NoLock);
365 }
366
367
368 Datum
369 nextval(PG_FUNCTION_ARGS)
370 {
371         text       *seqin = PG_GETARG_TEXT_P(0);
372         RangeVar   *sequence;
373         SeqTable        elm;
374         Relation        seqrel;
375         Buffer          buf;
376         Page            page;
377         Form_pg_sequence seq;
378         int64           incby,
379                                 maxv,
380                                 minv,
381                                 cache,
382                                 log,
383                                 fetch,
384                                 last;
385         int64           result,
386                                 next,
387                                 rescnt = 0;
388         bool            logit = false;
389
390         sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
391                                                                                                                          "nextval"));
392
393         /* open and AccessShareLock sequence */
394         init_sequence(sequence, &elm, &seqrel);
395
396         if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
397                 ereport(ERROR,
398                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
399                                  errmsg("permission denied for sequence %s",
400                                                 sequence->relname)));
401
402         if (elm->last != elm->cached)           /* some numbers were cached */
403         {
404                 elm->last += elm->increment;
405                 relation_close(seqrel, NoLock);
406                 PG_RETURN_INT64(elm->last);
407         }
408
409         /* lock page' buffer and read tuple */
410         seq = read_info(elm, seqrel, &buf);
411         page = BufferGetPage(buf);
412
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;
418         log = seq->log_cnt;
419
420         if (!seq->is_called)
421         {
422                 rescnt++;                               /* last_value if not called */
423                 fetch--;
424                 log--;
425         }
426
427         /*
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.)
431          *
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.
436          */
437         if (log < fetch)
438         {
439                 /* forced log to satisfy local demand for values */
440                 fetch = log = fetch + SEQ_LOG_VALS;
441                 logit = true;
442         }
443         else
444         {
445                 XLogRecPtr      redoptr = GetRedoRecPtr();
446
447                 if (XLByteLE(PageGetLSN(page), redoptr))
448                 {
449                         /* last update of seq was before checkpoint */
450                         fetch = log = fetch + SEQ_LOG_VALS;
451                         logit = true;
452                 }
453         }
454
455         while (fetch)                           /* try to fetch cache [+ log ] numbers */
456         {
457                 /*
458                  * Check MAXVALUE for ascending sequences and MINVALUE for
459                  * descending sequences
460                  */
461                 if (incby > 0)
462                 {
463                         /* ascending sequence */
464                         if ((maxv >= 0 && next > maxv - incby) ||
465                                 (maxv < 0 && next + incby > maxv))
466                         {
467                                 if (rescnt > 0)
468                                         break;          /* stop fetching */
469                                 if (!seq->is_cycled)
470                                 {
471                                         char            buf[100];
472
473                                         snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
474                                         ereport(ERROR,
475                                           (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
476                                            errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
477                                                           sequence->relname, buf)));
478                                 }
479                                 next = minv;
480                         }
481                         else
482                                 next += incby;
483                 }
484                 else
485                 {
486                         /* descending sequence */
487                         if ((minv < 0 && next < minv - incby) ||
488                                 (minv >= 0 && next + incby < minv))
489                         {
490                                 if (rescnt > 0)
491                                         break;          /* stop fetching */
492                                 if (!seq->is_cycled)
493                                 {
494                                         char            buf[100];
495
496                                         snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
497                                         ereport(ERROR,
498                                           (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
499                                            errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
500                                                           sequence->relname, buf)));
501                                 }
502                                 next = maxv;
503                         }
504                         else
505                                 next += incby;
506                 }
507                 fetch--;
508                 if (rescnt < cache)
509                 {
510                         log--;
511                         rescnt++;
512                         last = next;
513                         if (rescnt == 1)        /* if it's first result - */
514                                 result = next;  /* it's what to return */
515                 }
516         }
517
518         log -= fetch;                           /* adjust for any unfetched numbers */
519         Assert(log >= 0);
520
521         /* save info in local cache */
522         elm->last = result;                     /* last returned number */
523         elm->cached = last;                     /* last fetched number */
524
525         START_CRIT_SECTION();
526
527         /* XLOG stuff */
528         if (logit && !seqrel->rd_istemp)
529         {
530                 xl_seq_rec      xlrec;
531                 XLogRecPtr      recptr;
532                 XLogRecData rdata[2];
533
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]);
539
540                 /* set values that will be saved in xlog */
541                 seq->last_value = next;
542                 seq->is_called = true;
543                 seq->log_cnt = 0;
544
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;
550
551                 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
552
553                 PageSetLSN(page, recptr);
554                 PageSetTLI(page, ThisTimeLineID);
555         }
556
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 */
561
562         END_CRIT_SECTION();
563
564         LockBuffer(buf, BUFFER_LOCK_UNLOCK);
565
566         WriteBuffer(buf);
567
568         relation_close(seqrel, NoLock);
569
570         PG_RETURN_INT64(result);
571 }
572
573 Datum
574 currval(PG_FUNCTION_ARGS)
575 {
576         text       *seqin = PG_GETARG_TEXT_P(0);
577         RangeVar   *sequence;
578         SeqTable        elm;
579         Relation        seqrel;
580         int64           result;
581
582         sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
583                                                                                                                          "currval"));
584
585         /* open and AccessShareLock sequence */
586         init_sequence(sequence, &elm, &seqrel);
587
588         if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK)
589                 ereport(ERROR,
590                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
591                                  errmsg("permission denied for sequence %s",
592                                                 sequence->relname)));
593
594         if (elm->increment == 0)        /* nextval/read_info were not called */
595                 ereport(ERROR,
596                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
597                                  errmsg("currval of sequence \"%s\" is not yet defined in this session",
598                                                 sequence->relname)));
599
600         result = elm->last;
601
602         relation_close(seqrel, NoLock);
603
604         PG_RETURN_INT64(result);
605 }
606
607 /*
608  * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
609  *
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).
614  *
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
618  * sequence.
619  */
620 static void
621 do_setval(RangeVar *sequence, int64 next, bool iscalled)
622 {
623         SeqTable        elm;
624         Relation        seqrel;
625         Buffer          buf;
626         Form_pg_sequence seq;
627
628         /* open and AccessShareLock sequence */
629         init_sequence(sequence, &elm, &seqrel);
630
631         if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
632                 ereport(ERROR,
633                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
634                                  errmsg("permission denied for sequence %s",
635                                                 sequence->relname)));
636
637         /* lock page' buffer and read tuple */
638         seq = read_info(elm, seqrel, &buf);
639
640         if ((next < seq->min_value) || (next > seq->max_value))
641         {
642                 char            bufv[100],
643                                         bufm[100],
644                                         bufx[100];
645
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);
649                 ereport(ERROR,
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)));
653         }
654
655         /* save info in local cache */
656         elm->last = next;                       /* last returned number */
657         elm->cached = next;                     /* last cached number (forget cached
658                                                                  * values) */
659
660         START_CRIT_SECTION();
661
662         /* XLOG stuff */
663         if (!seqrel->rd_istemp)
664         {
665                 xl_seq_rec      xlrec;
666                 XLogRecPtr      recptr;
667                 XLogRecData rdata[2];
668                 Page            page = BufferGetPage(buf);
669
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]);
675
676                 /* set values that will be saved in xlog */
677                 seq->last_value = next;
678                 seq->is_called = true;
679                 seq->log_cnt = 0;
680
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;
686
687                 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
688
689                 PageSetLSN(page, recptr);
690                 PageSetTLI(page, ThisTimeLineID);
691         }
692
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;
697
698         END_CRIT_SECTION();
699
700         LockBuffer(buf, BUFFER_LOCK_UNLOCK);
701
702         WriteBuffer(buf);
703
704         relation_close(seqrel, NoLock);
705 }
706
707 /*
708  * Implement the 2 arg setval procedure.
709  * See do_setval for discussion.
710  */
711 Datum
712 setval(PG_FUNCTION_ARGS)
713 {
714         text       *seqin = PG_GETARG_TEXT_P(0);
715         int64           next = PG_GETARG_INT64(1);
716         RangeVar   *sequence;
717
718         sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
719                                                                                                                           "setval"));
720
721         do_setval(sequence, next, true);
722
723         PG_RETURN_INT64(next);
724 }
725
726 /*
727  * Implement the 3 arg setval procedure.
728  * See do_setval for discussion.
729  */
730 Datum
731 setval_and_iscalled(PG_FUNCTION_ARGS)
732 {
733         text       *seqin = PG_GETARG_TEXT_P(0);
734         int64           next = PG_GETARG_INT64(1);
735         bool            iscalled = PG_GETARG_BOOL(2);
736         RangeVar   *sequence;
737
738         sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
739                                                                                                                           "setval"));
740
741         do_setval(sequence, next, iscalled);
742
743         PG_RETURN_INT64(next);
744 }
745
746
747 /*
748  * Given a relation name, open and lock the sequence.  p_elm and p_rel are
749  * output parameters.
750  */
751 static void
752 init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel)
753 {
754         Oid                     relid = RangeVarGetRelid(relation, false);
755         TransactionId thisxid = GetTopTransactionId();
756         volatile SeqTable elm;
757         Relation        seqrel;
758
759         /*
760          * Open the sequence relation.
761          */
762         seqrel = relation_open(relid, NoLock);
763
764         if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
765                 ereport(ERROR,
766                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
767                                  errmsg("\"%s\" is not a sequence",
768                                                 relation->relname)));
769
770         /* Look to see if we already have a seqtable entry for relation */
771         for (elm = seqtab; elm != NULL; elm = elm->next)
772         {
773                 if (elm->relid == relid)
774                         break;
775         }
776
777         /*
778          * Allocate new seqtable entry if we didn't find one.
779          *
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.
783          */
784         if (elm == NULL)
785         {
786                 /*
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.
789                  */
790                 elm = (SeqTable) malloc(sizeof(SeqTableData));
791                 if (elm == NULL)
792                         ereport(ERROR,
793                                         (errcode(ERRCODE_OUT_OF_MEMORY),
794                                          errmsg("out of memory")));
795                 elm->relid = relid;
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;
799                 elm->next = seqtab;
800                 seqtab = elm;
801         }
802
803         /*
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.
808          */
809         if (elm->xid != thisxid)
810         {
811                 ResourceOwner currentOwner;
812
813                 currentOwner = CurrentResourceOwner;
814                 PG_TRY();
815                 {
816                         CurrentResourceOwner = TopTransactionResourceOwner;
817
818                         LockRelation(seqrel, AccessShareLock);
819                 }
820                 PG_CATCH();
821                 {
822                         /* Ensure CurrentResourceOwner is restored on error */
823                         CurrentResourceOwner = currentOwner;
824                         PG_RE_THROW();
825                 }
826                 PG_END_TRY();
827                 CurrentResourceOwner = currentOwner;
828
829                 /* Flag that we have a lock in the current xact. */
830                 elm->xid = thisxid;
831         }
832
833         *p_elm = elm;
834         *p_rel = seqrel;
835 }
836
837
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)
841 {
842         PageHeader      page;
843         ItemId          lp;
844         HeapTupleData tuple;
845         sequence_magic *sm;
846         Form_pg_sequence seq;
847
848         *buf = ReadBuffer(rel, 0);
849         LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
850
851         page = (PageHeader) BufferGetPage(*buf);
852         sm = (sequence_magic *) PageGetSpecialPointer(page);
853
854         if (sm->magic != SEQ_MAGIC)
855                 elog(ERROR, "bad magic number in sequence \"%s\": %08X",
856                          RelationGetRelationName(rel), sm->magic);
857
858         lp = PageGetItemId(page, FirstOffsetNumber);
859         Assert(ItemIdIsUsed(lp));
860         tuple.t_data = (HeapTupleHeader) PageGetItem((Page) page, lp);
861
862         seq = (Form_pg_sequence) GETSTRUCT(&tuple);
863
864         elm->increment = seq->increment_by;
865
866         return seq;
867 }
868
869 /*
870  * init_params: process the options list of CREATE or ALTER SEQUENCE,
871  * and store the values into appropriate fields of *new.
872  *
873  * If isInit is true, fill any unspecified options with default values;
874  * otherwise, do not change existing options that aren't explicitly overridden.
875  */
876 static void
877 init_params(List *options, Form_pg_sequence new, bool isInit)
878 {
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;
885         ListCell   *option;
886
887         foreach(option, options)
888         {
889                 DefElem    *defel = (DefElem *) lfirst(option);
890
891                 if (strcmp(defel->defname, "increment") == 0)
892                 {
893                         if (increment_by)
894                                 ereport(ERROR,
895                                                 (errcode(ERRCODE_SYNTAX_ERROR),
896                                                  errmsg("conflicting or redundant options")));
897                         increment_by = defel;
898                 }
899
900                 /*
901                  * start is for a new sequence restart is for alter
902                  */
903                 else if (strcmp(defel->defname, "start") == 0 ||
904                                  strcmp(defel->defname, "restart") == 0)
905                 {
906                         if (last_value)
907                                 ereport(ERROR,
908                                                 (errcode(ERRCODE_SYNTAX_ERROR),
909                                                  errmsg("conflicting or redundant options")));
910                         last_value = defel;
911                 }
912                 else if (strcmp(defel->defname, "maxvalue") == 0)
913                 {
914                         if (max_value)
915                                 ereport(ERROR,
916                                                 (errcode(ERRCODE_SYNTAX_ERROR),
917                                                  errmsg("conflicting or redundant options")));
918                         max_value = defel;
919                 }
920                 else if (strcmp(defel->defname, "minvalue") == 0)
921                 {
922                         if (min_value)
923                                 ereport(ERROR,
924                                                 (errcode(ERRCODE_SYNTAX_ERROR),
925                                                  errmsg("conflicting or redundant options")));
926                         min_value = defel;
927                 }
928                 else if (strcmp(defel->defname, "cache") == 0)
929                 {
930                         if (cache_value)
931                                 ereport(ERROR,
932                                                 (errcode(ERRCODE_SYNTAX_ERROR),
933                                                  errmsg("conflicting or redundant options")));
934                         cache_value = defel;
935                 }
936                 else if (strcmp(defel->defname, "cycle") == 0)
937                 {
938                         if (is_cycled)
939                                 ereport(ERROR,
940                                                 (errcode(ERRCODE_SYNTAX_ERROR),
941                                                  errmsg("conflicting or redundant options")));
942                         is_cycled = defel;
943                 }
944                 else
945                         elog(ERROR, "option \"%s\" not recognized",
946                                  defel->defname);
947         }
948
949         /* INCREMENT BY */
950         if (increment_by != NULL)
951         {
952                 new->increment_by = defGetInt64(increment_by);
953                 if (new->increment_by == 0)
954                         ereport(ERROR,
955                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
956                                          errmsg("INCREMENT must not be zero")));
957         }
958         else if (isInit)
959                 new->increment_by = 1;
960
961         /* CYCLE */
962         if (is_cycled != NULL)
963         {
964                 new->is_cycled = intVal(is_cycled->arg);
965                 Assert(new->is_cycled == false || new->is_cycled == true);
966         }
967         else if (isInit)
968                 new->is_cycled = false;
969
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)
974         {
975                 if (new->increment_by > 0)
976                         new->max_value = SEQ_MAXVALUE;          /* ascending seq */
977                 else
978                         new->max_value = -1;    /* descending seq */
979         }
980
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)
985         {
986                 if (new->increment_by > 0)
987                         new->min_value = 1; /* ascending seq */
988                 else
989                         new->min_value = SEQ_MINVALUE;          /* descending seq */
990         }
991
992         /* crosscheck min/max */
993         if (new->min_value >= new->max_value)
994         {
995                 char            bufm[100],
996                                         bufx[100];
997
998                 snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
999                 snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value);
1000                 ereport(ERROR,
1001                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1002                                  errmsg("MINVALUE (%s) must be less than MAXVALUE (%s)",
1003                                                 bufm, bufx)));
1004         }
1005
1006         /* START WITH */
1007         if (last_value != NULL)
1008         {
1009                 new->last_value = defGetInt64(last_value);
1010                 new->is_called = false;
1011                 new->log_cnt = 1;
1012         }
1013         else if (isInit)
1014         {
1015                 if (new->increment_by > 0)
1016                         new->last_value = new->min_value;       /* ascending seq */
1017                 else
1018                         new->last_value = new->max_value;       /* descending seq */
1019                 new->is_called = false;
1020                 new->log_cnt = 1;
1021         }
1022
1023         /* crosscheck */
1024         if (new->last_value < new->min_value)
1025         {
1026                 char            bufs[100],
1027                                         bufm[100];
1028
1029                 snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
1030                 snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
1031                 ereport(ERROR,
1032                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1033                           errmsg("START value (%s) can't be less than MINVALUE (%s)",
1034                                          bufs, bufm)));
1035         }
1036         if (new->last_value > new->max_value)
1037         {
1038                 char            bufs[100],
1039                                         bufm[100];
1040
1041                 snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
1042                 snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
1043                 ereport(ERROR,
1044                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1045                    errmsg("START value (%s) can't be greater than MAXVALUE (%s)",
1046                                   bufs, bufm)));
1047         }
1048
1049         /* CACHE */
1050         if (cache_value != NULL)
1051         {
1052                 new->cache_value = defGetInt64(cache_value);
1053                 if (new->cache_value <= 0)
1054                 {
1055                         char            buf[100];
1056
1057                         snprintf(buf, sizeof(buf), INT64_FORMAT, new->cache_value);
1058                         ereport(ERROR,
1059                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1060                                          errmsg("CACHE (%s) must be greater than zero",
1061                                                         buf)));
1062                 }
1063         }
1064         else if (isInit)
1065                 new->cache_value = 1;
1066 }
1067
1068
1069 void
1070 seq_redo(XLogRecPtr lsn, XLogRecord *record)
1071 {
1072         uint8           info = record->xl_info & ~XLR_INFO_MASK;
1073         Relation        reln;
1074         Buffer          buffer;
1075         Page            page;
1076         char       *item;
1077         Size            itemsz;
1078         xl_seq_rec *xlrec = (xl_seq_rec *) XLogRecGetData(record);
1079         sequence_magic *sm;
1080
1081         if (info != XLOG_SEQ_LOG)
1082                 elog(PANIC, "seq_redo: unknown op code %u", info);
1083
1084         reln = XLogOpenRelation(true, RM_SEQ_ID, xlrec->node);
1085         if (!RelationIsValid(reln))
1086                 return;
1087
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);
1092
1093         page = (Page) BufferGetPage(buffer);
1094
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;
1100
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");
1107
1108         PageSetLSN(page, lsn);
1109         PageSetTLI(page, ThisTimeLineID);
1110         LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1111         WriteBuffer(buffer);
1112 }
1113
1114 void
1115 seq_undo(XLogRecPtr lsn, XLogRecord *record)
1116 {
1117 }
1118
1119 void
1120 seq_desc(char *buf, uint8 xl_info, char *rec)
1121 {
1122         uint8           info = xl_info & ~XLR_INFO_MASK;
1123         xl_seq_rec *xlrec = (xl_seq_rec *) rec;
1124
1125         if (info == XLOG_SEQ_LOG)
1126                 strcat(buf, "log: ");
1127         else
1128         {
1129                 strcat(buf, "UNKNOWN");
1130                 return;
1131         }
1132
1133         sprintf(buf + strlen(buf), "rel %u/%u/%u",
1134                         xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);
1135 }