]> granicus.if.org Git - postgresql/blob - src/backend/commands/sequence.c
Minor correction: cause ALTER ROLE role ROLE rolenames to behave
[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.123 2005/06/07 07:08:34 neilc 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 #include "utils/syscache.h"
28
29
30 /*
31  * We don't want to log each fetching of a value from a sequence,
32  * so we pre-log a few fetches in advance. In the event of
33  * crash we can lose as much as we pre-logged.
34  */
35 #define SEQ_LOG_VALS    32
36
37 /*
38  * The "special area" of a sequence's buffer page looks like this.
39  */
40 #define SEQ_MAGIC         0x1717
41
42 typedef struct sequence_magic
43 {
44         uint32          magic;
45 } sequence_magic;
46
47 /*
48  * We store a SeqTable item for every sequence we have touched in the current
49  * session.  This is needed to hold onto nextval/currval state.  (We can't
50  * rely on the relcache, since it's only, well, a cache, and may decide to
51  * discard entries.)
52  *
53  * XXX We use linear search to find pre-existing SeqTable entries.      This is
54  * good when only a small number of sequences are touched in a session, but
55  * would suck with many different sequences.  Perhaps use a hashtable someday.
56  */
57 typedef struct SeqTableData
58 {
59         struct SeqTableData *next;      /* link to next SeqTable object */
60         Oid                     relid;                  /* pg_class OID of this sequence */
61         TransactionId xid;                      /* xact in which we last did a seq op */
62         int64           last;                   /* value last returned by nextval */
63         int64           cached;                 /* last value already cached for nextval */
64         /* if last != cached, we have not used up all the cached values */
65         int64           increment;              /* copy of sequence's increment field */
66 } SeqTableData;
67
68 typedef SeqTableData *SeqTable;
69
70 static SeqTable seqtab = NULL;  /* Head of list of SeqTable items */
71
72 /*
73  * last_used_seq is updated by nextval() to point to the last used
74  * sequence.
75  */
76 static SeqTableData *last_used_seq = NULL;
77
78 static void acquire_share_lock(Relation seqrel, SeqTable seq);
79 static void init_sequence(RangeVar *relation,
80                           SeqTable *p_elm, Relation *p_rel);
81 static Form_pg_sequence read_info(SeqTable elm, Relation rel, Buffer *buf);
82 static void init_params(List *options, Form_pg_sequence new, bool isInit);
83 static void do_setval(RangeVar *sequence, int64 next, bool iscalled);
84
85 /*
86  * DefineSequence
87  *                              Creates a new sequence relation
88  */
89 void
90 DefineSequence(CreateSeqStmt *seq)
91 {
92         FormData_pg_sequence new;
93         CreateStmt *stmt = makeNode(CreateStmt);
94         Oid                     seqoid;
95         Relation        rel;
96         Buffer          buf;
97         PageHeader      page;
98         sequence_magic *sm;
99         HeapTuple       tuple;
100         TupleDesc       tupDesc;
101         Datum           value[SEQ_COL_LASTCOL];
102         char            null[SEQ_COL_LASTCOL];
103         int                     i;
104         NameData        name;
105
106         /* Check and set all option values */
107         init_params(seq->options, &new, true);
108
109         /*
110          * Create relation (and fill *null & *value)
111          */
112         stmt->tableElts = NIL;
113         for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
114         {
115                 ColumnDef  *coldef;
116                 TypeName   *typnam;
117
118                 typnam = makeNode(TypeName);
119                 typnam->setof = FALSE;
120                 typnam->arrayBounds = NIL;
121                 typnam->typmod = -1;
122
123                 coldef = makeNode(ColumnDef);
124                 coldef->typename = typnam;
125                 coldef->inhcount = 0;
126                 coldef->is_local = true;
127                 coldef->is_not_null = true;
128                 coldef->raw_default = NULL;
129                 coldef->cooked_default = NULL;
130                 coldef->constraints = NIL;
131                 coldef->support = NULL;
132
133                 null[i - 1] = ' ';
134
135                 switch (i)
136                 {
137                         case SEQ_COL_NAME:
138                                 typnam->typeid = NAMEOID;
139                                 coldef->colname = "sequence_name";
140                                 namestrcpy(&name, seq->sequence->relname);
141                                 value[i - 1] = NameGetDatum(&name);
142                                 break;
143                         case SEQ_COL_LASTVAL:
144                                 typnam->typeid = INT8OID;
145                                 coldef->colname = "last_value";
146                                 value[i - 1] = Int64GetDatumFast(new.last_value);
147                                 break;
148                         case SEQ_COL_INCBY:
149                                 typnam->typeid = INT8OID;
150                                 coldef->colname = "increment_by";
151                                 value[i - 1] = Int64GetDatumFast(new.increment_by);
152                                 break;
153                         case SEQ_COL_MAXVALUE:
154                                 typnam->typeid = INT8OID;
155                                 coldef->colname = "max_value";
156                                 value[i - 1] = Int64GetDatumFast(new.max_value);
157                                 break;
158                         case SEQ_COL_MINVALUE:
159                                 typnam->typeid = INT8OID;
160                                 coldef->colname = "min_value";
161                                 value[i - 1] = Int64GetDatumFast(new.min_value);
162                                 break;
163                         case SEQ_COL_CACHE:
164                                 typnam->typeid = INT8OID;
165                                 coldef->colname = "cache_value";
166                                 value[i - 1] = Int64GetDatumFast(new.cache_value);
167                                 break;
168                         case SEQ_COL_LOG:
169                                 typnam->typeid = INT8OID;
170                                 coldef->colname = "log_cnt";
171                                 value[i - 1] = Int64GetDatum((int64) 1);
172                                 break;
173                         case SEQ_COL_CYCLE:
174                                 typnam->typeid = BOOLOID;
175                                 coldef->colname = "is_cycled";
176                                 value[i - 1] = BoolGetDatum(new.is_cycled);
177                                 break;
178                         case SEQ_COL_CALLED:
179                                 typnam->typeid = BOOLOID;
180                                 coldef->colname = "is_called";
181                                 value[i - 1] = BoolGetDatum(false);
182                                 break;
183                 }
184                 stmt->tableElts = lappend(stmt->tableElts, coldef);
185         }
186
187         stmt->relation = seq->sequence;
188         stmt->inhRelations = NIL;
189         stmt->constraints = NIL;
190         stmt->hasoids = MUST_NOT_HAVE_OIDS;
191         stmt->oncommit = ONCOMMIT_NOOP;
192         stmt->tablespacename = NULL;
193
194         seqoid = DefineRelation(stmt, RELKIND_SEQUENCE);
195
196         rel = heap_open(seqoid, AccessExclusiveLock);
197         tupDesc = RelationGetDescr(rel);
198
199         /* Initialize first page of relation with special magic number */
200
201         buf = ReadBuffer(rel, P_NEW);
202         Assert(BufferGetBlockNumber(buf) == 0);
203
204         page = (PageHeader) BufferGetPage(buf);
205
206         PageInit((Page) page, BufferGetPageSize(buf), sizeof(sequence_magic));
207         sm = (sequence_magic *) PageGetSpecialPointer(page);
208         sm->magic = SEQ_MAGIC;
209
210         /* hack: ensure heap_insert will insert on the just-created page */
211         rel->rd_targblock = 0;
212
213         /* Now form & insert sequence tuple */
214         tuple = heap_formtuple(tupDesc, value, null);
215         simple_heap_insert(rel, tuple);
216
217         Assert(ItemPointerGetOffsetNumber(&(tuple->t_self)) == FirstOffsetNumber);
218
219         /*
220          * Two special hacks here:
221          *
222          * 1. Since VACUUM does not process sequences, we have to force the tuple
223          * to have xmin = FrozenTransactionId now.      Otherwise it would become
224          * invisible to SELECTs after 2G transactions.  It is okay to do this
225          * because if the current transaction aborts, no other xact will ever
226          * examine the sequence tuple anyway.
227          *
228          * 2. Even though heap_insert emitted a WAL log record, we have to emit
229          * an XLOG_SEQ_LOG record too, since (a) the heap_insert record will
230          * not have the right xmin, and (b) REDO of the heap_insert record
231          * would re-init page and sequence magic number would be lost.  This
232          * means two log records instead of one :-(
233          */
234         LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
235
236         START_CRIT_SECTION();
237
238         {
239                 /*
240                  * Note that the "tuple" structure is still just a local tuple
241                  * record created by heap_formtuple; its t_data pointer doesn't
242                  * point at the disk buffer.  To scribble on the disk buffer we
243                  * need to fetch the item pointer.      But do the same to the local
244                  * tuple, since that will be the source for the WAL log record,
245                  * below.
246                  */
247                 ItemId          itemId;
248                 Item            item;
249
250                 itemId = PageGetItemId((Page) page, FirstOffsetNumber);
251                 item = PageGetItem((Page) page, itemId);
252
253                 HeapTupleHeaderSetXmin((HeapTupleHeader) item, FrozenTransactionId);
254                 ((HeapTupleHeader) item)->t_infomask |= HEAP_XMIN_COMMITTED;
255
256                 HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId);
257                 tuple->t_data->t_infomask |= HEAP_XMIN_COMMITTED;
258         }
259
260         /* XLOG stuff */
261         if (!rel->rd_istemp)
262         {
263                 xl_seq_rec      xlrec;
264                 XLogRecPtr      recptr;
265                 XLogRecData rdata[2];
266                 Form_pg_sequence newseq = (Form_pg_sequence) GETSTRUCT(tuple);
267
268                 /* We do not log first nextval call, so "advance" sequence here */
269                 /* Note we are scribbling on local tuple, not the disk buffer */
270                 newseq->is_called = true;
271                 newseq->log_cnt = 0;
272
273                 xlrec.node = rel->rd_node;
274                 rdata[0].data = (char *) &xlrec;
275                 rdata[0].len = sizeof(xl_seq_rec);
276                 rdata[0].buffer = InvalidBuffer;
277                 rdata[0].next = &(rdata[1]);
278
279                 rdata[1].data = (char *) tuple->t_data;
280                 rdata[1].len = tuple->t_len;
281                 rdata[1].buffer = InvalidBuffer;
282                 rdata[1].next = NULL;
283
284                 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
285
286                 PageSetLSN(page, recptr);
287                 PageSetTLI(page, ThisTimeLineID);
288         }
289
290         END_CRIT_SECTION();
291
292         LockBuffer(buf, BUFFER_LOCK_UNLOCK);
293         WriteBuffer(buf);
294         heap_close(rel, NoLock);
295 }
296
297 /*
298  * AlterSequence
299  *
300  * Modify the definition of a sequence relation
301  */
302 void
303 AlterSequence(AlterSeqStmt *stmt)
304 {
305         SeqTable        elm;
306         Relation        seqrel;
307         Buffer          buf;
308         Page            page;
309         Form_pg_sequence seq;
310         FormData_pg_sequence new;
311
312         /* open and AccessShareLock sequence */
313         init_sequence(stmt->sequence, &elm, &seqrel);
314
315         /* allow ALTER to sequence owner only */
316         if (!pg_class_ownercheck(elm->relid, GetUserId()))
317                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
318                                            stmt->sequence->relname);
319
320         /* lock page' buffer and read tuple into new sequence structure */
321         seq = read_info(elm, seqrel, &buf);
322         page = BufferGetPage(buf);
323
324         /* Copy old values of options into workspace */
325         memcpy(&new, seq, sizeof(FormData_pg_sequence));
326
327         /* Check and set new values */
328         init_params(stmt->options, &new, false);
329
330         /* Now okay to update the on-disk tuple */
331         memcpy(seq, &new, sizeof(FormData_pg_sequence));
332
333         /* Clear local cache so that we don't think we have cached numbers */
334         elm->last = new.last_value; /* last returned number */
335         elm->cached = new.last_value;           /* last cached number (forget
336                                                                                  * cached values) */
337
338         START_CRIT_SECTION();
339
340         /* XLOG stuff */
341         if (!seqrel->rd_istemp)
342         {
343                 xl_seq_rec      xlrec;
344                 XLogRecPtr      recptr;
345                 XLogRecData rdata[2];
346
347                 xlrec.node = seqrel->rd_node;
348                 rdata[0].data = (char *) &xlrec;
349                 rdata[0].len = sizeof(xl_seq_rec);
350                 rdata[0].buffer = InvalidBuffer;
351                 rdata[0].next = &(rdata[1]);
352
353                 rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper;
354                 rdata[1].len = ((PageHeader) page)->pd_special -
355                         ((PageHeader) page)->pd_upper;
356                 rdata[1].buffer = InvalidBuffer;
357                 rdata[1].next = NULL;
358
359                 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
360
361                 PageSetLSN(page, recptr);
362                 PageSetTLI(page, ThisTimeLineID);
363         }
364
365         END_CRIT_SECTION();
366
367         LockBuffer(buf, BUFFER_LOCK_UNLOCK);
368
369         WriteBuffer(buf);
370
371         relation_close(seqrel, NoLock);
372 }
373
374
375 Datum
376 nextval(PG_FUNCTION_ARGS)
377 {
378         text       *seqin = PG_GETARG_TEXT_P(0);
379         RangeVar   *sequence;
380         SeqTable        elm;
381         Relation        seqrel;
382         Buffer          buf;
383         Page            page;
384         Form_pg_sequence seq;
385         int64           incby,
386                                 maxv,
387                                 minv,
388                                 cache,
389                                 log,
390                                 fetch,
391                                 last;
392         int64           result,
393                                 next,
394                                 rescnt = 0;
395         bool            logit = false;
396
397         sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin));
398
399         /* open and AccessShareLock sequence */
400         init_sequence(sequence, &elm, &seqrel);
401
402         if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
403                 ereport(ERROR,
404                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
405                                  errmsg("permission denied for sequence %s",
406                                                 sequence->relname)));
407
408         if (elm->last != elm->cached)           /* some numbers were cached */
409         {
410                 last_used_seq = elm;
411                 elm->last += elm->increment;
412                 relation_close(seqrel, NoLock);
413                 PG_RETURN_INT64(elm->last);
414         }
415
416         /* lock page' buffer and read tuple */
417         seq = read_info(elm, seqrel, &buf);
418         page = BufferGetPage(buf);
419
420         last = next = result = seq->last_value;
421         incby = seq->increment_by;
422         maxv = seq->max_value;
423         minv = seq->min_value;
424         fetch = cache = seq->cache_value;
425         log = seq->log_cnt;
426
427         if (!seq->is_called)
428         {
429                 rescnt++;                               /* last_value if not called */
430                 fetch--;
431                 log--;
432         }
433
434         /*
435          * Decide whether we should emit a WAL log record.      If so, force up
436          * the fetch count to grab SEQ_LOG_VALS more values than we actually
437          * need to cache.  (These will then be usable without logging.)
438          *
439          * If this is the first nextval after a checkpoint, we must force a new
440          * WAL record to be written anyway, else replay starting from the
441          * checkpoint would fail to advance the sequence past the logged
442          * values.      In this case we may as well fetch extra values.
443          */
444         if (log < fetch)
445         {
446                 /* forced log to satisfy local demand for values */
447                 fetch = log = fetch + SEQ_LOG_VALS;
448                 logit = true;
449         }
450         else
451         {
452                 XLogRecPtr      redoptr = GetRedoRecPtr();
453
454                 if (XLByteLE(PageGetLSN(page), redoptr))
455                 {
456                         /* last update of seq was before checkpoint */
457                         fetch = log = fetch + SEQ_LOG_VALS;
458                         logit = true;
459                 }
460         }
461
462         while (fetch)                           /* try to fetch cache [+ log ] numbers */
463         {
464                 /*
465                  * Check MAXVALUE for ascending sequences and MINVALUE for
466                  * descending sequences
467                  */
468                 if (incby > 0)
469                 {
470                         /* ascending sequence */
471                         if ((maxv >= 0 && next > maxv - incby) ||
472                                 (maxv < 0 && next + incby > maxv))
473                         {
474                                 if (rescnt > 0)
475                                         break;          /* stop fetching */
476                                 if (!seq->is_cycled)
477                                 {
478                                         char            buf[100];
479
480                                         snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
481                                         ereport(ERROR,
482                                           (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
483                                            errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
484                                                           sequence->relname, buf)));
485                                 }
486                                 next = minv;
487                         }
488                         else
489                                 next += incby;
490                 }
491                 else
492                 {
493                         /* descending sequence */
494                         if ((minv < 0 && next < minv - incby) ||
495                                 (minv >= 0 && next + incby < minv))
496                         {
497                                 if (rescnt > 0)
498                                         break;          /* stop fetching */
499                                 if (!seq->is_cycled)
500                                 {
501                                         char            buf[100];
502
503                                         snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
504                                         ereport(ERROR,
505                                           (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
506                                            errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
507                                                           sequence->relname, buf)));
508                                 }
509                                 next = maxv;
510                         }
511                         else
512                                 next += incby;
513                 }
514                 fetch--;
515                 if (rescnt < cache)
516                 {
517                         log--;
518                         rescnt++;
519                         last = next;
520                         if (rescnt == 1)        /* if it's first result - */
521                                 result = next;  /* it's what to return */
522                 }
523         }
524
525         log -= fetch;                           /* adjust for any unfetched numbers */
526         Assert(log >= 0);
527
528         /* save info in local cache */
529         elm->last = result;                     /* last returned number */
530         elm->cached = last;                     /* last fetched number */
531
532         last_used_seq = elm;
533
534         START_CRIT_SECTION();
535
536         /* XLOG stuff */
537         if (logit && !seqrel->rd_istemp)
538         {
539                 xl_seq_rec      xlrec;
540                 XLogRecPtr      recptr;
541                 XLogRecData rdata[2];
542
543                 xlrec.node = seqrel->rd_node;
544                 rdata[0].data = (char *) &xlrec;
545                 rdata[0].len = sizeof(xl_seq_rec);
546                 rdata[0].buffer = InvalidBuffer;
547                 rdata[0].next = &(rdata[1]);
548
549                 /* set values that will be saved in xlog */
550                 seq->last_value = next;
551                 seq->is_called = true;
552                 seq->log_cnt = 0;
553
554                 rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper;
555                 rdata[1].len = ((PageHeader) page)->pd_special -
556                         ((PageHeader) page)->pd_upper;
557                 rdata[1].buffer = InvalidBuffer;
558                 rdata[1].next = NULL;
559
560                 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
561
562                 PageSetLSN(page, recptr);
563                 PageSetTLI(page, ThisTimeLineID);
564         }
565
566         /* update on-disk data */
567         seq->last_value = last;         /* last fetched number */
568         seq->is_called = true;
569         seq->log_cnt = log;                     /* how much is logged */
570
571         END_CRIT_SECTION();
572
573         LockBuffer(buf, BUFFER_LOCK_UNLOCK);
574
575         WriteBuffer(buf);
576
577         relation_close(seqrel, NoLock);
578
579         PG_RETURN_INT64(result);
580 }
581
582 Datum
583 currval(PG_FUNCTION_ARGS)
584 {
585         text       *seqin = PG_GETARG_TEXT_P(0);
586         RangeVar   *sequence;
587         SeqTable        elm;
588         Relation        seqrel;
589         int64           result;
590
591         sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin));
592
593         /* open and AccessShareLock sequence */
594         init_sequence(sequence, &elm, &seqrel);
595
596         if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK)
597                 ereport(ERROR,
598                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
599                                  errmsg("permission denied for sequence %s",
600                                                 sequence->relname)));
601
602         if (elm->increment == 0)        /* nextval/read_info were not called */
603                 ereport(ERROR,
604                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
605                                  errmsg("currval of sequence \"%s\" is not yet defined in this session",
606                                                 sequence->relname)));
607
608         result = elm->last;
609
610         relation_close(seqrel, NoLock);
611
612         PG_RETURN_INT64(result);
613 }
614
615 Datum
616 lastval(PG_FUNCTION_ARGS)
617 {
618         Relation        seqrel;
619         int64           result;
620
621         if (last_used_seq == NULL)
622                 ereport(ERROR,
623                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
624                                  errmsg("lastval is not yet defined in this session")));
625
626         /* Someone may have dropped the sequence since the last nextval() */
627         if (!SearchSysCacheExists(RELOID,
628                                                           ObjectIdGetDatum(last_used_seq->relid),
629                                                           0, 0, 0))
630                 ereport(ERROR,
631                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
632                                  errmsg("lastval is not yet defined in this session")));
633
634         seqrel = relation_open(last_used_seq->relid, NoLock);
635         acquire_share_lock(seqrel, last_used_seq);
636
637         /* nextval() must have already been called for this sequence */
638         Assert(last_used_seq->increment != 0);
639
640         if (pg_class_aclcheck(last_used_seq->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK)
641                 ereport(ERROR,
642                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
643                                  errmsg("permission denied for sequence %s",
644                                                 RelationGetRelationName(seqrel))));
645
646         result = last_used_seq->last;
647         relation_close(seqrel, NoLock);
648         PG_RETURN_INT64(result);
649 }
650
651 /*
652  * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
653  *
654  * Note that the 3 arg version (which sets the is_called flag) is
655  * only for use in pg_dump, and setting the is_called flag may not
656  * work if multiple users are attached to the database and referencing
657  * the sequence (unlikely if pg_dump is restoring it).
658  *
659  * It is necessary to have the 3 arg version so that pg_dump can
660  * restore the state of a sequence exactly during data-only restores -
661  * it is the only way to clear the is_called flag in an existing
662  * sequence.
663  */
664 static void
665 do_setval(RangeVar *sequence, int64 next, bool iscalled)
666 {
667         SeqTable        elm;
668         Relation        seqrel;
669         Buffer          buf;
670         Form_pg_sequence seq;
671
672         /* open and AccessShareLock sequence */
673         init_sequence(sequence, &elm, &seqrel);
674
675         if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
676                 ereport(ERROR,
677                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
678                                  errmsg("permission denied for sequence %s",
679                                                 sequence->relname)));
680
681         /* lock page' buffer and read tuple */
682         seq = read_info(elm, seqrel, &buf);
683
684         if ((next < seq->min_value) || (next > seq->max_value))
685         {
686                 char            bufv[100],
687                                         bufm[100],
688                                         bufx[100];
689
690                 snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
691                 snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
692                 snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
693                 ereport(ERROR,
694                                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
695                                  errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
696                                                 bufv, sequence->relname, bufm, bufx)));
697         }
698
699         /* save info in local cache */
700         elm->last = next;                       /* last returned number */
701         elm->cached = next;                     /* last cached number (forget cached
702                                                                  * values) */
703
704         START_CRIT_SECTION();
705
706         /* XLOG stuff */
707         if (!seqrel->rd_istemp)
708         {
709                 xl_seq_rec      xlrec;
710                 XLogRecPtr      recptr;
711                 XLogRecData rdata[2];
712                 Page            page = BufferGetPage(buf);
713
714                 xlrec.node = seqrel->rd_node;
715                 rdata[0].data = (char *) &xlrec;
716                 rdata[0].len = sizeof(xl_seq_rec);
717                 rdata[0].buffer = InvalidBuffer;
718                 rdata[0].next = &(rdata[1]);
719
720                 /* set values that will be saved in xlog */
721                 seq->last_value = next;
722                 seq->is_called = true;
723                 seq->log_cnt = 0;
724
725                 rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper;
726                 rdata[1].len = ((PageHeader) page)->pd_special -
727                         ((PageHeader) page)->pd_upper;
728                 rdata[1].buffer = InvalidBuffer;
729                 rdata[1].next = NULL;
730
731                 recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
732
733                 PageSetLSN(page, recptr);
734                 PageSetTLI(page, ThisTimeLineID);
735         }
736
737         /* save info in sequence relation */
738         seq->last_value = next;         /* last fetched number */
739         seq->is_called = iscalled;
740         seq->log_cnt = (iscalled) ? 0 : 1;
741
742         END_CRIT_SECTION();
743
744         LockBuffer(buf, BUFFER_LOCK_UNLOCK);
745
746         WriteBuffer(buf);
747
748         relation_close(seqrel, NoLock);
749 }
750
751 /*
752  * Implement the 2 arg setval procedure.
753  * See do_setval for discussion.
754  */
755 Datum
756 setval(PG_FUNCTION_ARGS)
757 {
758         text       *seqin = PG_GETARG_TEXT_P(0);
759         int64           next = PG_GETARG_INT64(1);
760         RangeVar   *sequence;
761
762         sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin));
763
764         do_setval(sequence, next, true);
765
766         PG_RETURN_INT64(next);
767 }
768
769 /*
770  * Implement the 3 arg setval procedure.
771  * See do_setval for discussion.
772  */
773 Datum
774 setval_and_iscalled(PG_FUNCTION_ARGS)
775 {
776         text       *seqin = PG_GETARG_TEXT_P(0);
777         int64           next = PG_GETARG_INT64(1);
778         bool            iscalled = PG_GETARG_BOOL(2);
779         RangeVar   *sequence;
780
781         sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin));
782
783         do_setval(sequence, next, iscalled);
784
785         PG_RETURN_INT64(next);
786 }
787
788
789 /*
790  * If we haven't touched the sequence already in this transaction,
791  * we need to acquire AccessShareLock.  We arrange for the lock to
792  * be owned by the top transaction, so that we don't need to do it
793  * more than once per xact.
794  */
795 static void
796 acquire_share_lock(Relation seqrel, SeqTable seq)
797 {
798         TransactionId thisxid = GetTopTransactionId();
799
800         if (seq->xid != thisxid)
801         {
802                 ResourceOwner currentOwner;
803
804                 currentOwner = CurrentResourceOwner;
805                 PG_TRY();
806                 {
807                         CurrentResourceOwner = TopTransactionResourceOwner;
808                         LockRelation(seqrel, AccessShareLock);
809                 }
810                 PG_CATCH();
811                 {
812                         /* Ensure CurrentResourceOwner is restored on error */
813                         CurrentResourceOwner = currentOwner;
814                         PG_RE_THROW();
815                 }
816                 PG_END_TRY();
817                 CurrentResourceOwner = currentOwner;
818
819                 /* Flag that we have a lock in the current xact. */
820                 seq->xid = thisxid;
821         }
822 }
823
824 /*
825  * Given a relation name, open and lock the sequence.  p_elm and p_rel are
826  * output parameters.
827  */
828 static void
829 init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel)
830 {
831         Oid                     relid = RangeVarGetRelid(relation, false);
832         volatile SeqTable elm;
833         Relation        seqrel;
834
835         /*
836          * Open the sequence relation.
837          */
838         seqrel = relation_open(relid, NoLock);
839
840         if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
841                 ereport(ERROR,
842                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
843                                  errmsg("\"%s\" is not a sequence",
844                                                 relation->relname)));
845
846         /* Look to see if we already have a seqtable entry for relation */
847         for (elm = seqtab; elm != NULL; elm = elm->next)
848         {
849                 if (elm->relid == relid)
850                         break;
851         }
852
853         /*
854          * Allocate new seqtable entry if we didn't find one.
855          *
856          * NOTE: seqtable entries remain in the list for the life of a backend.
857          * If the sequence itself is deleted then the entry becomes wasted
858          * memory, but it's small enough that this should not matter.
859          */
860         if (elm == NULL)
861         {
862                 /*
863                  * Time to make a new seqtable entry.  These entries live as long
864                  * as the backend does, so we use plain malloc for them.
865                  */
866                 elm = (SeqTable) malloc(sizeof(SeqTableData));
867                 if (elm == NULL)
868                         ereport(ERROR,
869                                         (errcode(ERRCODE_OUT_OF_MEMORY),
870                                          errmsg("out of memory")));
871                 elm->relid = relid;
872                 elm->xid = InvalidTransactionId;
873                 /* increment is set to 0 until we do read_info (see currval) */
874                 elm->last = elm->cached = elm->increment = 0;
875                 elm->next = seqtab;
876                 seqtab = elm;
877         }
878
879         acquire_share_lock(seqrel, elm);
880
881         *p_elm = elm;
882         *p_rel = seqrel;
883 }
884
885
886 /* Given an opened relation, lock the page buffer and find the tuple */
887 static Form_pg_sequence
888 read_info(SeqTable elm, Relation rel, Buffer *buf)
889 {
890         PageHeader      page;
891         ItemId          lp;
892         HeapTupleData tuple;
893         sequence_magic *sm;
894         Form_pg_sequence seq;
895
896         *buf = ReadBuffer(rel, 0);
897         LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
898
899         page = (PageHeader) BufferGetPage(*buf);
900         sm = (sequence_magic *) PageGetSpecialPointer(page);
901
902         if (sm->magic != SEQ_MAGIC)
903                 elog(ERROR, "bad magic number in sequence \"%s\": %08X",
904                          RelationGetRelationName(rel), sm->magic);
905
906         lp = PageGetItemId(page, FirstOffsetNumber);
907         Assert(ItemIdIsUsed(lp));
908         tuple.t_data = (HeapTupleHeader) PageGetItem((Page) page, lp);
909
910         seq = (Form_pg_sequence) GETSTRUCT(&tuple);
911
912         elm->increment = seq->increment_by;
913
914         return seq;
915 }
916
917 /*
918  * init_params: process the options list of CREATE or ALTER SEQUENCE,
919  * and store the values into appropriate fields of *new.
920  *
921  * If isInit is true, fill any unspecified options with default values;
922  * otherwise, do not change existing options that aren't explicitly overridden.
923  */
924 static void
925 init_params(List *options, Form_pg_sequence new, bool isInit)
926 {
927         DefElem    *last_value = NULL;
928         DefElem    *increment_by = NULL;
929         DefElem    *max_value = NULL;
930         DefElem    *min_value = NULL;
931         DefElem    *cache_value = NULL;
932         DefElem    *is_cycled = NULL;
933         ListCell   *option;
934
935         foreach(option, options)
936         {
937                 DefElem    *defel = (DefElem *) lfirst(option);
938
939                 if (strcmp(defel->defname, "increment") == 0)
940                 {
941                         if (increment_by)
942                                 ereport(ERROR,
943                                                 (errcode(ERRCODE_SYNTAX_ERROR),
944                                                  errmsg("conflicting or redundant options")));
945                         increment_by = defel;
946                 }
947
948                 /*
949                  * start is for a new sequence restart is for alter
950                  */
951                 else if (strcmp(defel->defname, "start") == 0 ||
952                                  strcmp(defel->defname, "restart") == 0)
953                 {
954                         if (last_value)
955                                 ereport(ERROR,
956                                                 (errcode(ERRCODE_SYNTAX_ERROR),
957                                                  errmsg("conflicting or redundant options")));
958                         last_value = defel;
959                 }
960                 else if (strcmp(defel->defname, "maxvalue") == 0)
961                 {
962                         if (max_value)
963                                 ereport(ERROR,
964                                                 (errcode(ERRCODE_SYNTAX_ERROR),
965                                                  errmsg("conflicting or redundant options")));
966                         max_value = defel;
967                 }
968                 else if (strcmp(defel->defname, "minvalue") == 0)
969                 {
970                         if (min_value)
971                                 ereport(ERROR,
972                                                 (errcode(ERRCODE_SYNTAX_ERROR),
973                                                  errmsg("conflicting or redundant options")));
974                         min_value = defel;
975                 }
976                 else if (strcmp(defel->defname, "cache") == 0)
977                 {
978                         if (cache_value)
979                                 ereport(ERROR,
980                                                 (errcode(ERRCODE_SYNTAX_ERROR),
981                                                  errmsg("conflicting or redundant options")));
982                         cache_value = defel;
983                 }
984                 else if (strcmp(defel->defname, "cycle") == 0)
985                 {
986                         if (is_cycled)
987                                 ereport(ERROR,
988                                                 (errcode(ERRCODE_SYNTAX_ERROR),
989                                                  errmsg("conflicting or redundant options")));
990                         is_cycled = defel;
991                 }
992                 else
993                         elog(ERROR, "option \"%s\" not recognized",
994                                  defel->defname);
995         }
996
997         /* INCREMENT BY */
998         if (increment_by != NULL)
999         {
1000                 new->increment_by = defGetInt64(increment_by);
1001                 if (new->increment_by == 0)
1002                         ereport(ERROR,
1003                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1004                                          errmsg("INCREMENT must not be zero")));
1005         }
1006         else if (isInit)
1007                 new->increment_by = 1;
1008
1009         /* CYCLE */
1010         if (is_cycled != NULL)
1011         {
1012                 new->is_cycled = intVal(is_cycled->arg);
1013                 Assert(new->is_cycled == false || new->is_cycled == true);
1014         }
1015         else if (isInit)
1016                 new->is_cycled = false;
1017
1018         /* MAXVALUE (null arg means NO MAXVALUE) */
1019         if (max_value != NULL && max_value->arg)
1020                 new->max_value = defGetInt64(max_value);
1021         else if (isInit || max_value != NULL)
1022         {
1023                 if (new->increment_by > 0)
1024                         new->max_value = SEQ_MAXVALUE;          /* ascending seq */
1025                 else
1026                         new->max_value = -1;    /* descending seq */
1027         }
1028
1029         /* MINVALUE (null arg means NO MINVALUE) */
1030         if (min_value != NULL && min_value->arg)
1031                 new->min_value = defGetInt64(min_value);
1032         else if (isInit || min_value != NULL)
1033         {
1034                 if (new->increment_by > 0)
1035                         new->min_value = 1; /* ascending seq */
1036                 else
1037                         new->min_value = SEQ_MINVALUE;          /* descending seq */
1038         }
1039
1040         /* crosscheck min/max */
1041         if (new->min_value >= new->max_value)
1042         {
1043                 char            bufm[100],
1044                                         bufx[100];
1045
1046                 snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
1047                 snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value);
1048                 ereport(ERROR,
1049                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1050                                  errmsg("MINVALUE (%s) must be less than MAXVALUE (%s)",
1051                                                 bufm, bufx)));
1052         }
1053
1054         /* START WITH */
1055         if (last_value != NULL)
1056         {
1057                 new->last_value = defGetInt64(last_value);
1058                 new->is_called = false;
1059                 new->log_cnt = 1;
1060         }
1061         else if (isInit)
1062         {
1063                 if (new->increment_by > 0)
1064                         new->last_value = new->min_value;       /* ascending seq */
1065                 else
1066                         new->last_value = new->max_value;       /* descending seq */
1067                 new->is_called = false;
1068                 new->log_cnt = 1;
1069         }
1070
1071         /* crosscheck */
1072         if (new->last_value < new->min_value)
1073         {
1074                 char            bufs[100],
1075                                         bufm[100];
1076
1077                 snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
1078                 snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
1079                 ereport(ERROR,
1080                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1081                           errmsg("START value (%s) can't be less than MINVALUE (%s)",
1082                                          bufs, bufm)));
1083         }
1084         if (new->last_value > new->max_value)
1085         {
1086                 char            bufs[100],
1087                                         bufm[100];
1088
1089                 snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
1090                 snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
1091                 ereport(ERROR,
1092                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1093                    errmsg("START value (%s) can't be greater than MAXVALUE (%s)",
1094                                   bufs, bufm)));
1095         }
1096
1097         /* CACHE */
1098         if (cache_value != NULL)
1099         {
1100                 new->cache_value = defGetInt64(cache_value);
1101                 if (new->cache_value <= 0)
1102                 {
1103                         char            buf[100];
1104
1105                         snprintf(buf, sizeof(buf), INT64_FORMAT, new->cache_value);
1106                         ereport(ERROR,
1107                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1108                                          errmsg("CACHE (%s) must be greater than zero",
1109                                                         buf)));
1110                 }
1111         }
1112         else if (isInit)
1113                 new->cache_value = 1;
1114 }
1115
1116
1117 void
1118 seq_redo(XLogRecPtr lsn, XLogRecord *record)
1119 {
1120         uint8           info = record->xl_info & ~XLR_INFO_MASK;
1121         Relation        reln;
1122         Buffer          buffer;
1123         Page            page;
1124         char       *item;
1125         Size            itemsz;
1126         xl_seq_rec *xlrec = (xl_seq_rec *) XLogRecGetData(record);
1127         sequence_magic *sm;
1128
1129         if (info != XLOG_SEQ_LOG)
1130                 elog(PANIC, "seq_redo: unknown op code %u", info);
1131
1132         reln = XLogOpenRelation(xlrec->node);
1133         if (!RelationIsValid(reln))
1134                 return;
1135
1136         buffer = XLogReadBuffer(true, reln, 0);
1137         if (!BufferIsValid(buffer))
1138                 elog(PANIC, "seq_redo: can't read block 0 of rel %u/%u/%u",
1139                    xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);
1140
1141         page = (Page) BufferGetPage(buffer);
1142
1143         /* Always reinit the page and reinstall the magic number */
1144         /* See comments in DefineSequence */
1145         PageInit((Page) page, BufferGetPageSize(buffer), sizeof(sequence_magic));
1146         sm = (sequence_magic *) PageGetSpecialPointer(page);
1147         sm->magic = SEQ_MAGIC;
1148
1149         item = (char *) xlrec + sizeof(xl_seq_rec);
1150         itemsz = record->xl_len - sizeof(xl_seq_rec);
1151         itemsz = MAXALIGN(itemsz);
1152         if (PageAddItem(page, (Item) item, itemsz,
1153                                         FirstOffsetNumber, LP_USED) == InvalidOffsetNumber)
1154                 elog(PANIC, "seq_redo: failed to add item to page");
1155
1156         PageSetLSN(page, lsn);
1157         PageSetTLI(page, ThisTimeLineID);
1158         LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1159         WriteBuffer(buffer);
1160 }
1161
1162 void
1163 seq_desc(char *buf, uint8 xl_info, char *rec)
1164 {
1165         uint8           info = xl_info & ~XLR_INFO_MASK;
1166         xl_seq_rec *xlrec = (xl_seq_rec *) rec;
1167
1168         if (info == XLOG_SEQ_LOG)
1169                 strcat(buf, "log: ");
1170         else
1171         {
1172                 strcat(buf, "UNKNOWN");
1173                 return;
1174         }
1175
1176         sprintf(buf + strlen(buf), "rel %u/%u/%u",
1177                         xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);
1178 }