Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * sequence.c
4 : : * PostgreSQL sequences support code.
5 : : *
6 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/commands/sequence.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include "access/bufmask.h"
18 : : #include "access/htup_details.h"
19 : : #include "access/multixact.h"
20 : : #include "access/relation.h"
21 : : #include "access/sequence.h"
22 : : #include "access/table.h"
23 : : #include "access/transam.h"
24 : : #include "access/xact.h"
25 : : #include "access/xlog.h"
26 : : #include "access/xloginsert.h"
27 : : #include "access/xlogutils.h"
28 : : #include "catalog/dependency.h"
29 : : #include "catalog/indexing.h"
30 : : #include "catalog/namespace.h"
31 : : #include "catalog/objectaccess.h"
32 : : #include "catalog/pg_sequence.h"
33 : : #include "catalog/pg_type.h"
34 : : #include "catalog/storage_xlog.h"
35 : : #include "commands/defrem.h"
36 : : #include "commands/sequence.h"
37 : : #include "commands/tablecmds.h"
38 : : #include "funcapi.h"
39 : : #include "miscadmin.h"
40 : : #include "nodes/makefuncs.h"
41 : : #include "parser/parse_type.h"
42 : : #include "storage/lmgr.h"
43 : : #include "storage/proc.h"
44 : : #include "storage/smgr.h"
45 : : #include "utils/acl.h"
46 : : #include "utils/builtins.h"
47 : : #include "utils/lsyscache.h"
48 : : #include "utils/resowner.h"
49 : : #include "utils/syscache.h"
50 : : #include "utils/varlena.h"
51 : :
52 : :
53 : : /*
54 : : * We don't want to log each fetching of a value from a sequence,
55 : : * so we pre-log a few fetches in advance. In the event of
56 : : * crash we can lose (skip over) as many values as we pre-logged.
57 : : */
58 : : #define SEQ_LOG_VALS 32
59 : :
60 : : /*
61 : : * The "special area" of a sequence's buffer page looks like this.
62 : : */
63 : : #define SEQ_MAGIC 0x1717
64 : :
65 : : typedef struct sequence_magic
66 : : {
67 : : uint32 magic;
68 : : } sequence_magic;
69 : :
70 : : /*
71 : : * We store a SeqTable item for every sequence we have touched in the current
72 : : * session. This is needed to hold onto nextval/currval state. (We can't
73 : : * rely on the relcache, since it's only, well, a cache, and may decide to
74 : : * discard entries.)
75 : : */
76 : : typedef struct SeqTableData
77 : : {
78 : : Oid relid; /* pg_class OID of this sequence (hash key) */
79 : : RelFileNumber filenumber; /* last seen relfilenumber of this sequence */
80 : : LocalTransactionId lxid; /* xact in which we last did a seq op */
81 : : bool last_valid; /* do we have a valid "last" value? */
82 : : int64 last; /* value last returned by nextval */
83 : : int64 cached; /* last value already cached for nextval */
84 : : /* if last != cached, we have not used up all the cached values */
85 : : int64 increment; /* copy of sequence's increment field */
86 : : /* note that increment is zero until we first do nextval_internal() */
87 : : } SeqTableData;
88 : :
89 : : typedef SeqTableData *SeqTable;
90 : :
91 : : static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
92 : :
93 : : /*
94 : : * last_used_seq is updated by nextval() to point to the last used
95 : : * sequence.
96 : : */
97 : : static SeqTableData *last_used_seq = NULL;
98 : :
99 : : static void fill_seq_with_data(Relation rel, HeapTuple tuple);
100 : : static void fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum);
101 : : static Relation lock_and_open_sequence(SeqTable seq);
102 : : static void create_seq_hashtable(void);
103 : : static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
104 : : static Form_pg_sequence_data read_seq_tuple(Relation rel,
105 : : Buffer *buf, HeapTuple seqdatatuple);
106 : : static void init_params(ParseState *pstate, List *options, bool for_identity,
107 : : bool isInit,
108 : : Form_pg_sequence seqform,
109 : : Form_pg_sequence_data seqdataform,
110 : : bool *need_seq_rewrite,
111 : : List **owned_by);
112 : : static void do_setval(Oid relid, int64 next, bool iscalled);
113 : : static void process_owned_by(Relation seqrel, List *owned_by, bool for_identity);
114 : :
115 : :
116 : : /*
117 : : * DefineSequence
118 : : * Creates a new sequence relation
119 : : */
120 : : ObjectAddress
2777 peter_e@gmx.net 121 :CBC 876 : DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
122 : : {
123 : : FormData_pg_sequence seqform;
124 : : FormData_pg_sequence_data seqdataform;
125 : : bool need_seq_rewrite;
126 : : List *owned_by;
9715 bruce@momjian.us 127 : 876 : CreateStmt *stmt = makeNode(CreateStmt);
128 : : Oid seqoid;
129 : : ObjectAddress address;
130 : : Relation rel;
131 : : HeapTuple tuple;
132 : : TupleDesc tupDesc;
133 : : Datum value[SEQ_COL_LASTCOL];
134 : : bool null[SEQ_COL_LASTCOL];
135 : : Datum pgs_values[Natts_pg_sequence];
136 : : bool pgs_nulls[Natts_pg_sequence];
137 : : int i;
138 : :
139 : : /*
140 : : * If if_not_exists was given and a relation with the same name already
141 : : * exists, bail out. (Note: we needn't check this when not if_not_exists,
142 : : * because DefineRelation will complain anyway.)
143 : : */
3519 heikki.linnakangas@i 144 [ + + ]: 876 : if (seq->if_not_exists)
145 : : {
146 : 8 : RangeVarGetAndCheckCreationNamespace(seq->sequence, NoLock, &seqoid);
147 [ + + ]: 8 : if (OidIsValid(seqoid))
148 : : {
149 : : /*
150 : : * If we are in an extension script, insist that the pre-existing
151 : : * object be a member of the extension, to avoid security risks.
152 : : */
615 tgl@sss.pgh.pa.us 153 : 5 : ObjectAddressSet(address, RelationRelationId, seqoid);
154 : 5 : checkMembershipInCurrentExtension(&address);
155 : :
156 : : /* OK to skip */
3519 heikki.linnakangas@i 157 [ + + ]: 4 : ereport(NOTICE,
158 : : (errcode(ERRCODE_DUPLICATE_TABLE),
159 : : errmsg("relation \"%s\" already exists, skipping",
160 : : seq->sequence->relname)));
3330 alvherre@alvh.no-ip. 161 : 4 : return InvalidObjectAddress;
162 : : }
163 : : }
164 : :
165 : : /* Check and set all option values */
2498 tgl@sss.pgh.pa.us 166 : 871 : init_params(pstate, seq->options, seq->for_identity, true,
167 : : &seqform, &seqdataform,
168 : : &need_seq_rewrite, &owned_by);
169 : :
170 : : /*
171 : : * Create relation (and fill value[] and null[] for the tuple)
172 : : */
9716 bruce@momjian.us 173 : 835 : stmt->tableElts = NIL;
174 [ + + ]: 3340 : for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
175 : : {
228 peter@eisentraut.org 176 :GNC 2505 : ColumnDef *coldef = NULL;
177 : :
9716 bruce@momjian.us 178 [ + + + - ]:CBC 2505 : switch (i)
179 : : {
9715 180 : 835 : case SEQ_COL_LASTVAL:
229 peter@eisentraut.org 181 :GNC 835 : coldef = makeColumnDef("last_value", INT8OID, -1, InvalidOid);
2672 peter_e@gmx.net 182 :CBC 835 : value[i - 1] = Int64GetDatumFast(seqdataform.last_value);
9715 bruce@momjian.us 183 : 835 : break;
8536 vadim4o@yahoo.com 184 : 835 : case SEQ_COL_LOG:
229 peter@eisentraut.org 185 :GNC 835 : coldef = makeColumnDef("log_cnt", INT8OID, -1, InvalidOid);
4281 tgl@sss.pgh.pa.us 186 :CBC 835 : value[i - 1] = Int64GetDatum((int64) 0);
8536 vadim4o@yahoo.com 187 : 835 : break;
9715 bruce@momjian.us 188 : 835 : case SEQ_COL_CALLED:
229 peter@eisentraut.org 189 :GNC 835 : coldef = makeColumnDef("is_called", BOOLOID, -1, InvalidOid);
8277 tgl@sss.pgh.pa.us 190 :CBC 835 : value[i - 1] = BoolGetDatum(false);
9715 bruce@momjian.us 191 : 835 : break;
192 : : }
193 : :
229 peter@eisentraut.org 194 :GNC 2505 : coldef->is_not_null = true;
195 : 2505 : null[i - 1] = false;
196 : :
9716 bruce@momjian.us 197 :CBC 2505 : stmt->tableElts = lappend(stmt->tableElts, coldef);
198 : : }
199 : :
8060 tgl@sss.pgh.pa.us 200 : 835 : stmt->relation = seq->sequence;
201 : 835 : stmt->inhRelations = NIL;
9716 bruce@momjian.us 202 : 835 : stmt->constraints = NIL;
4020 tgl@sss.pgh.pa.us 203 : 835 : stmt->options = NIL;
7825 204 : 835 : stmt->oncommit = ONCOMMIT_NOOP;
7216 205 : 835 : stmt->tablespacename = NULL;
3519 heikki.linnakangas@i 206 : 835 : stmt->if_not_exists = seq->if_not_exists;
207 : :
2685 rhaas@postgresql.org 208 : 835 : address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL, NULL);
3330 alvherre@alvh.no-ip. 209 : 835 : seqoid = address.objectId;
5012 rhaas@postgresql.org 210 [ - + ]: 835 : Assert(seqoid != InvalidOid);
211 : :
48 michael@paquier.xyz 212 :GNC 835 : rel = sequence_open(seqoid, AccessExclusiveLock);
9357 bruce@momjian.us 213 :CBC 835 : tupDesc = RelationGetDescr(rel);
214 : :
215 : : /* now initialize the sequence's data */
4897 tgl@sss.pgh.pa.us 216 : 835 : tuple = heap_form_tuple(tupDesc, value, null);
217 : 835 : fill_seq_with_data(rel, tuple);
218 : :
219 : : /* process OWNED BY if given */
220 [ + + ]: 835 : if (owned_by)
2565 peter_e@gmx.net 221 : 13 : process_owned_by(rel, owned_by, seq->for_identity);
222 : :
48 michael@paquier.xyz 223 :GNC 823 : sequence_close(rel, NoLock);
224 : :
225 : : /* fill in pg_sequence */
1910 andres@anarazel.de 226 :CBC 823 : rel = table_open(SequenceRelationId, RowExclusiveLock);
2672 peter_e@gmx.net 227 : 823 : tupDesc = RelationGetDescr(rel);
228 : :
229 : 823 : memset(pgs_nulls, 0, sizeof(pgs_nulls));
230 : :
231 : 823 : pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid);
2620 232 : 823 : pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid);
2672 233 : 823 : pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart);
234 : 823 : pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement);
235 : 823 : pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax);
236 : 823 : pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin);
237 : 823 : pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache);
2620 238 : 823 : pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle);
239 : :
2672 240 : 823 : tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls);
2630 alvherre@alvh.no-ip. 241 : 823 : CatalogTupleInsert(rel, tuple);
242 : :
2672 peter_e@gmx.net 243 : 823 : heap_freetuple(tuple);
1910 andres@anarazel.de 244 : 823 : table_close(rel, RowExclusiveLock);
245 : :
3330 alvherre@alvh.no-ip. 246 : 823 : return address;
247 : : }
248 : :
249 : : /*
250 : : * Reset a sequence to its initial value.
251 : : *
252 : : * The change is made transactionally, so that on failure of the current
253 : : * transaction, the sequence will be restored to its previous state.
254 : : * We do that by creating a whole new relfilenumber for the sequence; so this
255 : : * works much like the rewriting forms of ALTER TABLE.
256 : : *
257 : : * Caller is assumed to have acquired AccessExclusiveLock on the sequence,
258 : : * which must not be released until end of transaction. Caller is also
259 : : * responsible for permissions checking.
260 : : */
261 : : void
4897 tgl@sss.pgh.pa.us 262 : 18 : ResetSequence(Oid seq_relid)
263 : : {
264 : : Relation seq_rel;
265 : : SeqTable elm;
266 : : Form_pg_sequence_data seq;
267 : : Buffer buf;
268 : : HeapTupleData seqdatatuple;
269 : : HeapTuple tuple;
270 : : HeapTuple pgstuple;
271 : : Form_pg_sequence pgsform;
272 : : int64 startv;
273 : :
274 : : /*
275 : : * Read the old sequence. This does a bit more work than really
276 : : * necessary, but it's simple, and we do want to double-check that it's
277 : : * indeed a sequence.
278 : : */
279 : 18 : init_sequence(seq_relid, &elm, &seq_rel);
2672 peter_e@gmx.net 280 : 18 : (void) read_seq_tuple(seq_rel, &buf, &seqdatatuple);
281 : :
282 : 18 : pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(seq_relid));
283 [ - + ]: 18 : if (!HeapTupleIsValid(pgstuple))
2672 peter_e@gmx.net 284 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for sequence %u", seq_relid);
2672 peter_e@gmx.net 285 :CBC 18 : pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
286 : 18 : startv = pgsform->seqstart;
287 : 18 : ReleaseSysCache(pgstuple);
288 : :
289 : : /*
290 : : * Copy the existing sequence tuple.
291 : : */
292 : 18 : tuple = heap_copytuple(&seqdatatuple);
293 : :
294 : : /* Now we're done with the old page */
4897 tgl@sss.pgh.pa.us 295 : 18 : UnlockReleaseBuffer(buf);
296 : :
297 : : /*
298 : : * Modify the copied tuple to execute the restart (compare the RESTART
299 : : * action in AlterSequence)
300 : : */
2672 peter_e@gmx.net 301 : 18 : seq = (Form_pg_sequence_data) GETSTRUCT(tuple);
302 : 18 : seq->last_value = startv;
4897 tgl@sss.pgh.pa.us 303 : 18 : seq->is_called = false;
4281 304 : 18 : seq->log_cnt = 0;
305 : :
306 : : /*
307 : : * Create a new storage file for the sequence.
308 : : */
648 rhaas@postgresql.org 309 : 18 : RelationSetNewRelfilenumber(seq_rel, seq_rel->rd_rel->relpersistence);
310 : :
311 : : /*
312 : : * Ensure sequence's relfrozenxid is at 0, since it won't contain any
313 : : * unfrozen XIDs. Same with relminmxid, since a sequence will never
314 : : * contain multixacts.
315 : : */
1844 andres@anarazel.de 316 [ - + ]: 18 : Assert(seq_rel->rd_rel->relfrozenxid == InvalidTransactionId);
317 [ - + ]: 18 : Assert(seq_rel->rd_rel->relminmxid == InvalidMultiXactId);
318 : :
319 : : /*
320 : : * Insert the modified tuple into the new storage file.
321 : : */
4897 tgl@sss.pgh.pa.us 322 : 18 : fill_seq_with_data(seq_rel, tuple);
323 : :
324 : : /* Clear local cache so that we don't think we have cached numbers */
325 : : /* Note that we do not change the currval() state */
326 : 18 : elm->cached = elm->last;
327 : :
48 michael@paquier.xyz 328 :GNC 18 : sequence_close(seq_rel, NoLock);
4897 tgl@sss.pgh.pa.us 329 :CBC 18 : }
330 : :
331 : : /*
332 : : * Initialize a sequence's relation with the specified tuple as content
333 : : *
334 : : * This handles unlogged sequences by writing to both the main and the init
335 : : * fork as necessary.
336 : : */
337 : : static void
338 : 964 : fill_seq_with_data(Relation rel, HeapTuple tuple)
339 : : {
738 peter@eisentraut.org 340 : 964 : fill_seq_fork_with_data(rel, tuple, MAIN_FORKNUM);
341 : :
342 [ + + ]: 964 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
343 : : {
344 : : SMgrRelation srel;
345 : :
42 heikki.linnakangas@i 346 :GNC 50 : srel = smgropen(rel->rd_locator, INVALID_PROC_NUMBER);
738 peter@eisentraut.org 347 :CBC 50 : smgrcreate(srel, INIT_FORKNUM, false);
648 rhaas@postgresql.org 348 : 50 : log_smgrcreate(&rel->rd_locator, INIT_FORKNUM);
738 peter@eisentraut.org 349 : 50 : fill_seq_fork_with_data(rel, tuple, INIT_FORKNUM);
350 : 50 : FlushRelationBuffers(rel);
351 : 50 : smgrclose(srel);
352 : : }
353 : 964 : }
354 : :
355 : : /*
356 : : * Initialize a sequence's relation fork with the specified tuple as content
357 : : */
358 : : static void
359 : 1014 : fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
360 : : {
361 : : Buffer buf;
362 : : Page page;
363 : : sequence_magic *sm;
364 : : OffsetNumber offnum;
365 : :
366 : : /* Initialize first page of relation with special magic number */
367 : :
235 tmunro@postgresql.or 368 : 1014 : buf = ExtendBufferedRel(BMR_REL(rel), forkNum, NULL,
369 : : EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK);
8325 tgl@sss.pgh.pa.us 370 [ - + ]: 1014 : Assert(BufferGetBlockNumber(buf) == 0);
371 : :
2916 kgrittn@postgresql.o 372 : 1014 : page = BufferGetPage(buf);
373 : :
5754 tgl@sss.pgh.pa.us 374 : 1014 : PageInit(page, BufferGetPageSize(buf), sizeof(sequence_magic));
9716 bruce@momjian.us 375 : 1014 : sm = (sequence_magic *) PageGetSpecialPointer(page);
376 : 1014 : sm->magic = SEQ_MAGIC;
377 : :
378 : : /* Now insert sequence tuple */
379 : :
380 : : /*
381 : : * Since VACUUM does not process sequences, we have to force the tuple to
382 : : * have xmin = FrozenTransactionId now. Otherwise it would become
383 : : * invisible to SELECTs after 2G transactions. It is okay to do this
384 : : * because if the current transaction aborts, no other xact will ever
385 : : * examine the sequence tuple anyway.
386 : : */
3645 heikki.linnakangas@i 387 : 1014 : HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId);
388 [ - + ]: 1014 : HeapTupleHeaderSetXminFrozen(tuple->t_data);
389 [ - + ]: 1014 : HeapTupleHeaderSetCmin(tuple->t_data, FirstCommandId);
390 : 1014 : HeapTupleHeaderSetXmax(tuple->t_data, InvalidTransactionId);
391 : 1014 : tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
392 : 1014 : ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
393 : :
394 : : /* check the comment above nextval_internal()'s equivalent call. */
3335 andres@anarazel.de 395 [ + + + + : 1014 : if (RelationNeedsWAL(rel))
+ + - + ]
396 : 540 : GetTopTransactionId();
397 : :
8412 vadim4o@yahoo.com 398 : 1014 : START_CRIT_SECTION();
399 : :
6589 tgl@sss.pgh.pa.us 400 : 1014 : MarkBufferDirty(buf);
401 : :
3645 heikki.linnakangas@i 402 : 1014 : offnum = PageAddItem(page, (Item) tuple->t_data, tuple->t_len,
403 : : InvalidOffsetNumber, false, false);
404 [ - + ]: 1014 : if (offnum != FirstOffsetNumber)
3645 heikki.linnakangas@i 405 [ # # ]:UBC 0 : elog(ERROR, "failed to add sequence tuple to page");
406 : :
407 : : /* XLOG stuff */
738 peter@eisentraut.org 408 [ + + + + :CBC 1014 : if (RelationNeedsWAL(rel) || forkNum == INIT_FORKNUM)
+ + + - +
+ ]
409 : : {
410 : : xl_seq_rec xlrec;
411 : : XLogRecPtr recptr;
412 : :
3433 heikki.linnakangas@i 413 : 590 : XLogBeginInsert();
414 : 590 : XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
415 : :
648 rhaas@postgresql.org 416 : 590 : xlrec.locator = rel->rd_locator;
417 : :
3433 heikki.linnakangas@i 418 : 590 : XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
419 : 590 : XLogRegisterData((char *) tuple->t_data, tuple->t_len);
420 : :
421 : 590 : recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
422 : :
8412 vadim4o@yahoo.com 423 : 590 : PageSetLSN(page, recptr);
424 : : }
425 : :
426 [ - + ]: 1014 : END_CRIT_SECTION();
427 : :
6589 tgl@sss.pgh.pa.us 428 : 1014 : UnlockReleaseBuffer(buf);
9874 vadim4o@yahoo.com 429 : 1014 : }
430 : :
431 : : /*
432 : : * AlterSequence
433 : : *
434 : : * Modify the definition of a sequence relation
435 : : */
436 : : ObjectAddress
2777 peter_e@gmx.net 437 : 689 : AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
438 : : {
439 : : Oid relid;
440 : : SeqTable elm;
441 : : Relation seqrel;
442 : : Buffer buf;
443 : : HeapTupleData datatuple;
444 : : Form_pg_sequence seqform;
445 : : Form_pg_sequence_data newdataform;
446 : : bool need_seq_rewrite;
447 : : List *owned_by;
448 : : ObjectAddress address;
449 : : Relation rel;
450 : : HeapTuple seqtuple;
451 : : HeapTuple newdatatuple;
452 : :
453 : : /* Open and lock sequence, and check for ownership along the way. */
2497 454 : 689 : relid = RangeVarGetRelidExtended(stmt->sequence,
455 : : ShareRowExclusiveLock,
2207 andres@anarazel.de 456 : 689 : stmt->missing_ok ? RVR_MISSING_OK : 0,
457 : : RangeVarCallbackOwnsRelation,
458 : : NULL);
4465 simon@2ndQuadrant.co 459 [ + + ]: 686 : if (relid == InvalidOid)
460 : : {
461 [ + - ]: 3 : ereport(NOTICE,
462 : : (errmsg("relation \"%s\" does not exist, skipping",
463 : : stmt->sequence->relname)));
3330 alvherre@alvh.no-ip. 464 : 3 : return InvalidObjectAddress;
465 : : }
466 : :
6769 tgl@sss.pgh.pa.us 467 : 683 : init_sequence(relid, &elm, &seqrel);
468 : :
1910 andres@anarazel.de 469 : 680 : rel = table_open(SequenceRelationId, RowExclusiveLock);
2510 470 : 680 : seqtuple = SearchSysCacheCopy1(SEQRELID,
471 : : ObjectIdGetDatum(relid));
472 [ - + ]: 680 : if (!HeapTupleIsValid(seqtuple))
2672 peter_e@gmx.net 473 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for sequence %u",
474 : : relid);
475 : :
2510 andres@anarazel.de 476 :CBC 680 : seqform = (Form_pg_sequence) GETSTRUCT(seqtuple);
477 : :
478 : : /* lock page buffer and read tuple into new sequence structure */
479 : 680 : (void) read_seq_tuple(seqrel, &buf, &datatuple);
480 : :
481 : : /* copy the existing sequence data tuple, so it can be modified locally */
482 : 680 : newdatatuple = heap_copytuple(&datatuple);
483 : 680 : newdataform = (Form_pg_sequence_data) GETSTRUCT(newdatatuple);
484 : :
485 : 680 : UnlockReleaseBuffer(buf);
486 : :
487 : : /* Check and set new values */
2498 tgl@sss.pgh.pa.us 488 : 680 : init_params(pstate, stmt->options, stmt->for_identity, false,
489 : : seqform, newdataform,
490 : : &need_seq_rewrite, &owned_by);
491 : :
492 : : /* If needed, rewrite the sequence relation itself */
493 [ + + ]: 665 : if (need_seq_rewrite)
494 : : {
495 : : /* check the comment above nextval_internal()'s equivalent call. */
496 [ + + + + : 81 : if (RelationNeedsWAL(seqrel))
+ + + - ]
497 : 79 : GetTopTransactionId();
498 : :
499 : : /*
500 : : * Create a new storage file for the sequence, making the state
501 : : * changes transactional.
502 : : */
648 rhaas@postgresql.org 503 : 81 : RelationSetNewRelfilenumber(seqrel, seqrel->rd_rel->relpersistence);
504 : :
505 : : /*
506 : : * Ensure sequence's relfrozenxid is at 0, since it won't contain any
507 : : * unfrozen XIDs. Same with relminmxid, since a sequence will never
508 : : * contain multixacts.
509 : : */
1844 andres@anarazel.de 510 [ - + ]: 81 : Assert(seqrel->rd_rel->relfrozenxid == InvalidTransactionId);
511 [ - + ]: 81 : Assert(seqrel->rd_rel->relminmxid == InvalidMultiXactId);
512 : :
513 : : /*
514 : : * Insert the modified tuple into the new storage file.
515 : : */
2498 tgl@sss.pgh.pa.us 516 : 81 : fill_seq_with_data(seqrel, newdatatuple);
517 : : }
518 : :
519 : : /* Clear local cache so that we don't think we have cached numbers */
520 : : /* Note that we do not change the currval() state */
48 michael@paquier.xyz 521 :GNC 665 : elm->cached = elm->last;
522 : :
523 : : /* process OWNED BY if given */
6446 tgl@sss.pgh.pa.us 524 [ + + ]:CBC 665 : if (owned_by)
2565 peter_e@gmx.net 525 : 580 : process_owned_by(seqrel, owned_by, stmt->for_identity);
526 : :
527 : : /* update the pg_sequence tuple (we could skip this in some cases...) */
2510 andres@anarazel.de 528 : 662 : CatalogTupleUpdate(rel, &seqtuple->t_self, seqtuple);
529 : :
4046 rhaas@postgresql.org 530 [ - + ]: 662 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
531 : :
3330 alvherre@alvh.no-ip. 532 : 662 : ObjectAddressSet(address, RelationRelationId, relid);
533 : :
1910 andres@anarazel.de 534 : 662 : table_close(rel, RowExclusiveLock);
48 michael@paquier.xyz 535 :GNC 662 : sequence_close(seqrel, NoLock);
536 : :
3330 alvherre@alvh.no-ip. 537 :CBC 662 : return address;
538 : : }
539 : :
540 : : void
738 peter@eisentraut.org 541 : 30 : SequenceChangePersistence(Oid relid, char newrelpersistence)
542 : : {
543 : : SeqTable elm;
544 : : Relation seqrel;
545 : : Buffer buf;
546 : : HeapTupleData seqdatatuple;
547 : :
548 : 30 : init_sequence(relid, &elm, &seqrel);
549 : :
550 : : /* check the comment above nextval_internal()'s equivalent call. */
551 [ + + + + : 30 : if (RelationNeedsWAL(seqrel))
+ + + - ]
552 : 17 : GetTopTransactionId();
553 : :
554 : 30 : (void) read_seq_tuple(seqrel, &buf, &seqdatatuple);
648 rhaas@postgresql.org 555 : 30 : RelationSetNewRelfilenumber(seqrel, newrelpersistence);
738 peter@eisentraut.org 556 : 30 : fill_seq_with_data(seqrel, &seqdatatuple);
557 : 30 : UnlockReleaseBuffer(buf);
558 : :
48 michael@paquier.xyz 559 :GNC 30 : sequence_close(seqrel, NoLock);
738 peter@eisentraut.org 560 :CBC 30 : }
561 : :
562 : : void
2672 peter_e@gmx.net 563 : 452 : DeleteSequenceTuple(Oid relid)
564 : : {
565 : : Relation rel;
566 : : HeapTuple tuple;
567 : :
1910 andres@anarazel.de 568 : 452 : rel = table_open(SequenceRelationId, RowExclusiveLock);
569 : :
2672 peter_e@gmx.net 570 : 452 : tuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(relid));
571 [ - + ]: 452 : if (!HeapTupleIsValid(tuple))
2672 peter_e@gmx.net 572 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for sequence %u", relid);
573 : :
2629 tgl@sss.pgh.pa.us 574 :CBC 452 : CatalogTupleDelete(rel, &tuple->t_self);
575 : :
2672 peter_e@gmx.net 576 : 452 : ReleaseSysCache(tuple);
1910 andres@anarazel.de 577 : 452 : table_close(rel, RowExclusiveLock);
2672 peter_e@gmx.net 578 : 452 : }
579 : :
580 : : /*
581 : : * Note: nextval with a text argument is no longer exported as a pg_proc
582 : : * entry, but we keep it around to ease porting of C code that may have
583 : : * called the function directly.
584 : : */
585 : : Datum
8708 tgl@sss.pgh.pa.us 586 : 21 : nextval(PG_FUNCTION_ARGS)
587 : : {
2590 noah@leadboat.com 588 : 21 : text *seqin = PG_GETARG_TEXT_PP(0);
589 : : RangeVar *sequence;
590 : : Oid relid;
591 : :
6769 tgl@sss.pgh.pa.us 592 : 21 : sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin));
593 : :
594 : : /*
595 : : * XXX: This is not safe in the presence of concurrent DDL, but acquiring
596 : : * a lock here is more expensive than letting nextval_internal do it,
597 : : * since the latter maintains a cache that keeps us from hitting the lock
598 : : * manager more than once per transaction. It's not clear whether the
599 : : * performance penalty is material in practice, but for now, we do it this
600 : : * way.
601 : : */
4519 rhaas@postgresql.org 602 : 21 : relid = RangeVarGetRelid(sequence, NoLock, false);
603 : :
2565 peter_e@gmx.net 604 : 21 : PG_RETURN_INT64(nextval_internal(relid, true));
605 : : }
606 : :
607 : : Datum
6769 tgl@sss.pgh.pa.us 608 : 190949 : nextval_oid(PG_FUNCTION_ARGS)
609 : : {
610 : 190949 : Oid relid = PG_GETARG_OID(0);
611 : :
2565 peter_e@gmx.net 612 : 190949 : PG_RETURN_INT64(nextval_internal(relid, true));
613 : : }
614 : :
615 : : int64
616 : 191468 : nextval_internal(Oid relid, bool check_permissions)
617 : : {
618 : : SeqTable elm;
619 : : Relation seqrel;
620 : : Buffer buf;
621 : : Page page;
622 : : HeapTuple pgstuple;
623 : : Form_pg_sequence pgsform;
624 : : HeapTupleData seqdatatuple;
625 : : Form_pg_sequence_data seq;
626 : : int64 incby,
627 : : maxv,
628 : : minv,
629 : : cache,
630 : : log,
631 : : fetch,
632 : : last;
633 : : int64 result,
634 : : next,
9715 bruce@momjian.us 635 : 191468 : rescnt = 0;
636 : : bool cycle;
8536 vadim4o@yahoo.com 637 : 191468 : bool logit = false;
638 : :
639 : : /* open and lock sequence */
6769 tgl@sss.pgh.pa.us 640 : 191468 : init_sequence(relid, &elm, &seqrel);
641 : :
2565 peter_e@gmx.net 642 [ + + + + ]: 382438 : if (check_permissions &&
643 : 190970 : pg_class_aclcheck(elm->relid, GetUserId(),
644 : : ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
7574 tgl@sss.pgh.pa.us 645 [ + - ]: 3 : ereport(ERROR,
646 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
647 : : errmsg("permission denied for sequence %s",
648 : : RelationGetRelationName(seqrel))));
649 : :
650 : : /* read-only transactions may only modify temp sequences */
4136 651 [ + + ]: 191465 : if (!seqrel->rd_islocaltemp)
5167 652 : 130597 : PreventCommandIfReadOnly("nextval()");
653 : :
654 : : /*
655 : : * Forbid this during parallel operation because, to make it work, the
656 : : * cooperating backends would need to share the backend-local cached
657 : : * sequence information. Currently, we don't support that.
658 : : */
3272 rhaas@postgresql.org 659 : 191462 : PreventCommandIfParallelMode("nextval()");
660 : :
2489 tgl@sss.pgh.pa.us 661 [ + + ]: 191462 : if (elm->last != elm->cached) /* some numbers were cached */
662 : : {
6016 663 [ - + ]: 6 : Assert(elm->last_valid);
664 [ - + ]: 6 : Assert(elm->increment != 0);
9716 bruce@momjian.us 665 : 6 : elm->last += elm->increment;
48 michael@paquier.xyz 666 :GNC 6 : sequence_close(seqrel, NoLock);
6016 tgl@sss.pgh.pa.us 667 :CBC 6 : last_used_seq = elm;
6769 668 : 6 : return elm->last;
669 : : }
670 : :
2672 peter_e@gmx.net 671 : 191456 : pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(relid));
672 [ - + ]: 191456 : if (!HeapTupleIsValid(pgstuple))
2672 peter_e@gmx.net 673 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for sequence %u", relid);
2672 peter_e@gmx.net 674 :CBC 191456 : pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
675 : 191456 : incby = pgsform->seqincrement;
676 : 191456 : maxv = pgsform->seqmax;
677 : 191456 : minv = pgsform->seqmin;
678 : 191456 : cache = pgsform->seqcache;
2620 679 : 191456 : cycle = pgsform->seqcycle;
2672 680 : 191456 : ReleaseSysCache(pgstuple);
681 : :
682 : : /* lock page buffer and read tuple */
683 : 191456 : seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
2916 kgrittn@postgresql.o 684 : 191456 : page = BufferGetPage(buf);
685 : :
8536 vadim4o@yahoo.com 686 : 191456 : last = next = result = seq->last_value;
2672 peter_e@gmx.net 687 : 191456 : fetch = cache;
8536 vadim4o@yahoo.com 688 : 191456 : log = seq->log_cnt;
689 : :
8277 tgl@sss.pgh.pa.us 690 [ + + ]: 191456 : if (!seq->is_called)
691 : : {
4281 692 : 575 : rescnt++; /* return last_value if not is_called */
8536 vadim4o@yahoo.com 693 : 575 : fetch--;
694 : : }
695 : :
696 : : /*
697 : : * Decide whether we should emit a WAL log record. If so, force up the
698 : : * fetch count to grab SEQ_LOG_VALS more values than we actually need to
699 : : * cache. (These will then be usable without logging.)
700 : : *
701 : : * If this is the first nextval after a checkpoint, we must force a new
702 : : * WAL record to be written anyway, else replay starting from the
703 : : * checkpoint would fail to advance the sequence past the logged values.
704 : : * In this case we may as well fetch extra values.
705 : : */
4281 tgl@sss.pgh.pa.us 706 [ + + + + ]: 191456 : if (log < fetch || !seq->is_called)
707 : : {
708 : : /* forced log to satisfy local demand for values */
8066 709 : 4480 : fetch = log = fetch + SEQ_LOG_VALS;
8536 vadim4o@yahoo.com 710 : 4480 : logit = true;
711 : : }
712 : : else
713 : : {
8066 tgl@sss.pgh.pa.us 714 : 186976 : XLogRecPtr redoptr = GetRedoRecPtr();
715 : :
4125 alvherre@alvh.no-ip. 716 [ + + ]: 186976 : if (PageGetLSN(page) <= redoptr)
717 : : {
718 : : /* last update of seq was before checkpoint */
8066 tgl@sss.pgh.pa.us 719 : 61615 : fetch = log = fetch + SEQ_LOG_VALS;
720 : 61615 : logit = true;
721 : : }
722 : : }
723 : :
8424 bruce@momjian.us 724 [ + + ]: 2496570 : while (fetch) /* try to fetch cache [+ log ] numbers */
725 : : {
726 : : /*
727 : : * Check MAXVALUE for ascending sequences and MINVALUE for descending
728 : : * sequences
729 : : */
8708 tgl@sss.pgh.pa.us 730 [ + + ]: 2305143 : if (incby > 0)
731 : : {
732 : : /* ascending sequence */
9716 bruce@momjian.us 733 [ + - + + : 2304846 : if ((maxv >= 0 && next > maxv - incby) ||
- + ]
9716 bruce@momjian.us 734 [ # # ]:UBC 0 : (maxv < 0 && next + incby > maxv))
735 : : {
9716 bruce@momjian.us 736 [ + + ]:CBC 20 : if (rescnt > 0)
8536 vadim4o@yahoo.com 737 : 12 : break; /* stop fetching */
2672 peter_e@gmx.net 738 [ + + ]: 8 : if (!cycle)
7574 tgl@sss.pgh.pa.us 739 [ + - ]: 5 : ereport(ERROR,
740 : : (errcode(ERRCODE_SEQUENCE_GENERATOR_LIMIT_EXCEEDED),
741 : : errmsg("nextval: reached maximum value of sequence \"%s\" (%lld)",
742 : : RelationGetRelationName(seqrel),
743 : : (long long) maxv)));
9716 bruce@momjian.us 744 : 3 : next = minv;
745 : : }
746 : : else
747 : 2304826 : next += incby;
748 : : }
749 : : else
750 : : {
751 : : /* descending sequence */
752 [ + - + + : 297 : if ((minv < 0 && next < minv - incby) ||
- + ]
9716 bruce@momjian.us 753 [ # # ]:UBC 0 : (minv >= 0 && next + incby < minv))
754 : : {
9716 bruce@momjian.us 755 [ + + ]:CBC 15 : if (rescnt > 0)
8536 vadim4o@yahoo.com 756 : 9 : break; /* stop fetching */
2672 peter_e@gmx.net 757 [ + + ]: 6 : if (!cycle)
7574 tgl@sss.pgh.pa.us 758 [ + - ]: 3 : ereport(ERROR,
759 : : (errcode(ERRCODE_SEQUENCE_GENERATOR_LIMIT_EXCEEDED),
760 : : errmsg("nextval: reached minimum value of sequence \"%s\" (%lld)",
761 : : RelationGetRelationName(seqrel),
762 : : (long long) minv)));
9716 bruce@momjian.us 763 : 3 : next = maxv;
764 : : }
765 : : else
766 : 282 : next += incby;
767 : : }
8536 vadim4o@yahoo.com 768 : 2305114 : fetch--;
769 [ + + ]: 2305114 : if (rescnt < cache)
770 : : {
771 : 190900 : log--;
772 : 190900 : rescnt++;
773 : 190900 : last = next;
8424 bruce@momjian.us 774 [ + + ]: 190900 : if (rescnt == 1) /* if it's first result - */
775 : 190873 : result = next; /* it's what to return */
776 : : }
777 : : }
778 : :
8066 tgl@sss.pgh.pa.us 779 : 191448 : log -= fetch; /* adjust for any unfetched numbers */
780 [ - + ]: 191448 : Assert(log >= 0);
781 : :
782 : : /* save info in local cache */
48 michael@paquier.xyz 783 :GNC 191448 : elm->increment = incby;
9716 bruce@momjian.us 784 :CBC 191448 : elm->last = result; /* last returned number */
8536 vadim4o@yahoo.com 785 : 191448 : elm->cached = last; /* last fetched number */
6016 tgl@sss.pgh.pa.us 786 : 191448 : elm->last_valid = true;
787 : :
6886 neilc@samurai.com 788 : 191448 : last_used_seq = elm;
789 : :
790 : : /*
791 : : * If something needs to be WAL logged, acquire an xid, so this
792 : : * transaction's commit will trigger a WAL flush and wait for syncrep.
793 : : * It's sufficient to ensure the toplevel transaction has an xid, no need
794 : : * to assign xids subxacts, that'll already trigger an appropriate wait.
795 : : * (Have to do that here, so we're outside the critical section)
796 : : */
3335 andres@anarazel.de 797 [ + + + + : 191448 : if (logit && RelationNeedsWAL(seqrel))
+ + + + +
+ ]
798 : 4355 : GetTopTransactionId();
799 : :
800 : : /* ready to change the on-disk (or really, in-buffer) tuple */
8493 tgl@sss.pgh.pa.us 801 : 191448 : START_CRIT_SECTION();
802 : :
803 : : /*
804 : : * We must mark the buffer dirty before doing XLogInsert(); see notes in
805 : : * SyncOneBuffer(). However, we don't apply the desired changes just yet.
806 : : * This looks like a violation of the buffer update protocol, but it is in
807 : : * fact safe because we hold exclusive lock on the buffer. Any other
808 : : * process, including a checkpoint, that tries to examine the buffer
809 : : * contents will block until we release the lock, and then will see the
810 : : * final state that we install below.
811 : : */
6589 812 : 191448 : MarkBufferDirty(buf);
813 : :
814 : : /* XLOG stuff */
4871 rhaas@postgresql.org 815 [ + + + + : 191448 : if (logit && RelationNeedsWAL(seqrel))
+ + + + +
+ ]
816 : : {
817 : : xl_seq_rec xlrec;
818 : : XLogRecPtr recptr;
819 : :
820 : : /*
821 : : * We don't log the current state of the tuple, but rather the state
822 : : * as it would appear after "log" more fetches. This lets us skip
823 : : * that many future WAL records, at the cost that we lose those
824 : : * sequence values if we crash.
825 : : */
3433 heikki.linnakangas@i 826 : 4355 : XLogBeginInsert();
827 : 4355 : XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
828 : :
829 : : /* set values that will be saved in xlog */
8508 vadim4o@yahoo.com 830 : 4355 : seq->last_value = next;
8277 tgl@sss.pgh.pa.us 831 : 4355 : seq->is_called = true;
8508 vadim4o@yahoo.com 832 : 4355 : seq->log_cnt = 0;
833 : :
648 rhaas@postgresql.org 834 : 4355 : xlrec.locator = seqrel->rd_locator;
835 : :
3433 heikki.linnakangas@i 836 : 4355 : XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
2672 peter_e@gmx.net 837 : 4355 : XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);
838 : :
3433 heikki.linnakangas@i 839 : 4355 : recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
840 : :
8508 vadim4o@yahoo.com 841 : 4355 : PageSetLSN(page, recptr);
842 : : }
843 : :
844 : : /* Now update sequence tuple to the intended final state */
8536 845 : 191448 : seq->last_value = last; /* last fetched number */
8277 tgl@sss.pgh.pa.us 846 : 191448 : seq->is_called = true;
8536 vadim4o@yahoo.com 847 : 191448 : seq->log_cnt = log; /* how much is logged */
848 : :
8493 tgl@sss.pgh.pa.us 849 [ - + ]: 191448 : END_CRIT_SECTION();
850 : :
6589 851 : 191448 : UnlockReleaseBuffer(buf);
852 : :
48 michael@paquier.xyz 853 :GNC 191448 : sequence_close(seqrel, NoLock);
854 : :
6769 tgl@sss.pgh.pa.us 855 :CBC 191448 : return result;
856 : : }
857 : :
858 : : Datum
859 : 58 : currval_oid(PG_FUNCTION_ARGS)
860 : : {
861 : 58 : Oid relid = PG_GETARG_OID(0);
862 : : int64 result;
863 : : SeqTable elm;
864 : : Relation seqrel;
865 : :
866 : : /* open and lock sequence */
867 : 58 : init_sequence(relid, &elm, &seqrel);
868 : :
3462 peter_e@gmx.net 869 [ + + ]: 58 : if (pg_class_aclcheck(elm->relid, GetUserId(),
870 : : ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
7574 tgl@sss.pgh.pa.us 871 [ + - ]: 3 : ereport(ERROR,
872 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
873 : : errmsg("permission denied for sequence %s",
874 : : RelationGetRelationName(seqrel))));
875 : :
6016 876 [ + + ]: 55 : if (!elm->last_valid)
7574 877 [ + - ]: 3 : ereport(ERROR,
878 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
879 : : errmsg("currval of sequence \"%s\" is not yet defined in this session",
880 : : RelationGetRelationName(seqrel))));
881 : :
9716 bruce@momjian.us 882 : 52 : result = elm->last;
883 : :
48 michael@paquier.xyz 884 :GNC 52 : sequence_close(seqrel, NoLock);
885 : :
8277 tgl@sss.pgh.pa.us 886 :CBC 52 : PG_RETURN_INT64(result);
887 : : }
888 : :
889 : : Datum
6886 neilc@samurai.com 890 : 24 : lastval(PG_FUNCTION_ARGS)
891 : : {
892 : : Relation seqrel;
893 : : int64 result;
894 : :
895 [ + + ]: 24 : if (last_used_seq == NULL)
896 [ + - ]: 3 : ereport(ERROR,
897 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
898 : : errmsg("lastval is not yet defined in this session")));
899 : :
900 : : /* Someone may have dropped the sequence since the last nextval() */
5173 rhaas@postgresql.org 901 [ + + ]: 21 : if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(last_used_seq->relid)))
6886 neilc@samurai.com 902 [ + - ]: 3 : ereport(ERROR,
903 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
904 : : errmsg("lastval is not yet defined in this session")));
905 : :
2532 peter_e@gmx.net 906 : 18 : seqrel = lock_and_open_sequence(last_used_seq);
907 : :
908 : : /* nextval() must have already been called for this sequence */
6016 tgl@sss.pgh.pa.us 909 [ - + ]: 18 : Assert(last_used_seq->last_valid);
910 : :
3462 peter_e@gmx.net 911 [ + + ]: 18 : if (pg_class_aclcheck(last_used_seq->relid, GetUserId(),
912 : : ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
6886 neilc@samurai.com 913 [ + - ]: 3 : ereport(ERROR,
914 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
915 : : errmsg("permission denied for sequence %s",
916 : : RelationGetRelationName(seqrel))));
917 : :
918 : 15 : result = last_used_seq->last;
48 michael@paquier.xyz 919 :GNC 15 : sequence_close(seqrel, NoLock);
920 : :
6886 neilc@samurai.com 921 :CBC 15 : PG_RETURN_INT64(result);
922 : : }
923 : :
924 : : /*
925 : : * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
926 : : *
927 : : * Note that the 3 arg version (which sets the is_called flag) is
928 : : * only for use in pg_dump, and setting the is_called flag may not
929 : : * work if multiple users are attached to the database and referencing
930 : : * the sequence (unlikely if pg_dump is restoring it).
931 : : *
932 : : * It is necessary to have the 3 arg version so that pg_dump can
933 : : * restore the state of a sequence exactly during data-only restores -
934 : : * it is the only way to clear the is_called flag in an existing
935 : : * sequence.
936 : : */
937 : : static void
6769 tgl@sss.pgh.pa.us 938 : 265 : do_setval(Oid relid, int64 next, bool iscalled)
939 : : {
940 : : SeqTable elm;
941 : : Relation seqrel;
942 : : Buffer buf;
943 : : HeapTupleData seqdatatuple;
944 : : Form_pg_sequence_data seq;
945 : : HeapTuple pgstuple;
946 : : Form_pg_sequence pgsform;
947 : : int64 maxv,
948 : : minv;
949 : :
950 : : /* open and lock sequence */
951 : 265 : init_sequence(relid, &elm, &seqrel);
952 : :
8060 953 [ + + ]: 265 : if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
7574 954 [ + - ]: 3 : ereport(ERROR,
955 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
956 : : errmsg("permission denied for sequence %s",
957 : : RelationGetRelationName(seqrel))));
958 : :
2672 peter_e@gmx.net 959 : 262 : pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(relid));
960 [ - + ]: 262 : if (!HeapTupleIsValid(pgstuple))
2672 peter_e@gmx.net 961 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for sequence %u", relid);
2672 peter_e@gmx.net 962 :CBC 262 : pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
963 : 262 : maxv = pgsform->seqmax;
964 : 262 : minv = pgsform->seqmin;
965 : 262 : ReleaseSysCache(pgstuple);
966 : :
967 : : /* read-only transactions may only modify temp sequences */
4136 tgl@sss.pgh.pa.us 968 [ + + ]: 262 : if (!seqrel->rd_islocaltemp)
5167 969 : 133 : PreventCommandIfReadOnly("setval()");
970 : :
971 : : /*
972 : : * Forbid this during parallel operation because, to make it work, the
973 : : * cooperating backends would need to share the backend-local cached
974 : : * sequence information. Currently, we don't support that.
975 : : */
3272 rhaas@postgresql.org 976 : 259 : PreventCommandIfParallelMode("setval()");
977 : :
978 : : /* lock page buffer and read tuple */
2672 peter_e@gmx.net 979 : 259 : seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
980 : :
981 [ + + + + ]: 259 : if ((next < minv) || (next > maxv))
7574 tgl@sss.pgh.pa.us 982 [ + - ]: 6 : ereport(ERROR,
983 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
984 : : errmsg("setval: value %lld is out of bounds for sequence \"%s\" (%lld..%lld)",
985 : : (long long) next, RelationGetRelationName(seqrel),
986 : : (long long) minv, (long long) maxv)));
987 : :
988 : : /* Set the currval() state only if iscalled = true */
6016 989 [ + + ]: 253 : if (iscalled)
990 : : {
991 : 95 : elm->last = next; /* last returned number */
992 : 95 : elm->last_valid = true;
993 : : }
994 : :
995 : : /* In any case, forget any future cached numbers */
996 : 253 : elm->cached = elm->last;
997 : :
998 : : /* check the comment above nextval_internal()'s equivalent call. */
3335 andres@anarazel.de 999 [ + + + + : 253 : if (RelationNeedsWAL(seqrel))
+ + + - ]
1000 : 92 : GetTopTransactionId();
1001 : :
1002 : : /* ready to change the on-disk (or really, in-buffer) tuple */
8493 tgl@sss.pgh.pa.us 1003 : 253 : START_CRIT_SECTION();
1004 : :
4281 1005 : 253 : seq->last_value = next; /* last fetched number */
1006 : 253 : seq->is_called = iscalled;
1007 : 253 : seq->log_cnt = 0;
1008 : :
6589 1009 : 253 : MarkBufferDirty(buf);
1010 : :
1011 : : /* XLOG stuff */
4871 rhaas@postgresql.org 1012 [ + + + + : 253 : if (RelationNeedsWAL(seqrel))
+ + + - ]
1013 : : {
1014 : : xl_seq_rec xlrec;
1015 : : XLogRecPtr recptr;
2916 kgrittn@postgresql.o 1016 : 92 : Page page = BufferGetPage(buf);
1017 : :
3433 heikki.linnakangas@i 1018 : 92 : XLogBeginInsert();
1019 : 92 : XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
1020 : :
648 rhaas@postgresql.org 1021 : 92 : xlrec.locator = seqrel->rd_locator;
3433 heikki.linnakangas@i 1022 : 92 : XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
2672 peter_e@gmx.net 1023 : 92 : XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);
1024 : :
3433 heikki.linnakangas@i 1025 : 92 : recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
1026 : :
8508 vadim4o@yahoo.com 1027 : 92 : PageSetLSN(page, recptr);
1028 : : }
1029 : :
8493 tgl@sss.pgh.pa.us 1030 [ - + ]: 253 : END_CRIT_SECTION();
1031 : :
6589 1032 : 253 : UnlockReleaseBuffer(buf);
1033 : :
48 michael@paquier.xyz 1034 :GNC 253 : sequence_close(seqrel, NoLock);
8586 pjw@rhyme.com.au 1035 :CBC 253 : }
1036 : :
1037 : : /*
1038 : : * Implement the 2 arg setval procedure.
1039 : : * See do_setval for discussion.
1040 : : */
1041 : : Datum
6769 tgl@sss.pgh.pa.us 1042 : 82 : setval_oid(PG_FUNCTION_ARGS)
1043 : : {
1044 : 82 : Oid relid = PG_GETARG_OID(0);
8277 1045 : 82 : int64 next = PG_GETARG_INT64(1);
1046 : :
6769 1047 : 82 : do_setval(relid, next, true);
1048 : :
8277 1049 : 70 : PG_RETURN_INT64(next);
1050 : : }
1051 : :
1052 : : /*
1053 : : * Implement the 3 arg setval procedure.
1054 : : * See do_setval for discussion.
1055 : : */
1056 : : Datum
6769 1057 : 183 : setval3_oid(PG_FUNCTION_ARGS)
1058 : : {
1059 : 183 : Oid relid = PG_GETARG_OID(0);
8277 1060 : 183 : int64 next = PG_GETARG_INT64(1);
8586 pjw@rhyme.com.au 1061 : 183 : bool iscalled = PG_GETARG_BOOL(2);
1062 : :
6769 tgl@sss.pgh.pa.us 1063 : 183 : do_setval(relid, next, iscalled);
1064 : :
8051 1065 : 183 : PG_RETURN_INT64(next);
1066 : : }
1067 : :
1068 : :
1069 : : /*
1070 : : * Open the sequence and acquire lock if needed
1071 : : *
1072 : : * If we haven't touched the sequence already in this transaction,
1073 : : * we need to acquire a lock. We arrange for the lock to
1074 : : * be owned by the top transaction, so that we don't need to do it
1075 : : * more than once per xact.
1076 : : */
1077 : : static Relation
2532 peter_e@gmx.net 1078 : 192597 : lock_and_open_sequence(SeqTable seq)
1079 : : {
42 heikki.linnakangas@i 1080 :GNC 192597 : LocalTransactionId thislxid = MyProc->vxid.lxid;
1081 : :
1082 : : /* Get the lock if not already held in this xact */
6066 tgl@sss.pgh.pa.us 1083 [ + + ]:CBC 192597 : if (seq->lxid != thislxid)
1084 : : {
1085 : : ResourceOwner currentOwner;
1086 : :
6886 neilc@samurai.com 1087 : 2504 : currentOwner = CurrentResourceOwner;
2377 tgl@sss.pgh.pa.us 1088 : 2504 : CurrentResourceOwner = TopTransactionResourceOwner;
1089 : :
1090 : 2504 : LockRelationOid(seq->relid, RowExclusiveLock);
1091 : :
6886 neilc@samurai.com 1092 : 2504 : CurrentResourceOwner = currentOwner;
1093 : :
1094 : : /* Flag that we have a lock in the current xact */
6066 tgl@sss.pgh.pa.us 1095 : 2504 : seq->lxid = thislxid;
1096 : : }
1097 : :
1098 : : /* We now know we have the lock, and can safely open the rel */
48 michael@paquier.xyz 1099 :GNC 192597 : return sequence_open(seq->relid, NoLock);
1100 : : }
1101 : :
1102 : : /*
1103 : : * Creates the hash table for storing sequence data
1104 : : */
1105 : : static void
3803 heikki.linnakangas@i 1106 :CBC 311 : create_seq_hashtable(void)
1107 : : {
1108 : : HASHCTL ctl;
1109 : :
1110 : 311 : ctl.keysize = sizeof(Oid);
1111 : 311 : ctl.entrysize = sizeof(SeqTableData);
1112 : :
1113 : 311 : seqhashtab = hash_create("Sequence values", 16, &ctl,
1114 : : HASH_ELEM | HASH_BLOBS);
1115 : 311 : }
1116 : :
1117 : : /*
1118 : : * Given a relation OID, open and lock the sequence. p_elm and p_rel are
1119 : : * output parameters.
1120 : : */
1121 : : static void
6769 tgl@sss.pgh.pa.us 1122 : 192579 : init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
1123 : : {
1124 : : SeqTable elm;
1125 : : Relation seqrel;
1126 : : bool found;
1127 : :
1128 : : /* Find or create a hash table entry for this sequence */
3803 heikki.linnakangas@i 1129 [ + + ]: 192579 : if (seqhashtab == NULL)
1130 : 311 : create_seq_hashtable();
1131 : :
1132 : 192579 : elm = (SeqTable) hash_search(seqhashtab, &relid, HASH_ENTER, &found);
1133 : :
1134 : : /*
1135 : : * Initialize the new hash table entry if it did not exist already.
1136 : : *
1137 : : * NOTE: seqhashtab entries are stored for the life of a backend (unless
1138 : : * explicitly discarded with DISCARD). If the sequence itself is deleted
1139 : : * then the entry becomes wasted memory, but it's small enough that this
1140 : : * should not matter.
1141 : : */
1142 [ + + ]: 192579 : if (!found)
1143 : : {
1144 : : /* relid already filled in */
648 rhaas@postgresql.org 1145 : 972 : elm->filenumber = InvalidRelFileNumber;
6066 tgl@sss.pgh.pa.us 1146 : 972 : elm->lxid = InvalidLocalTransactionId;
6016 1147 : 972 : elm->last_valid = false;
2672 peter_e@gmx.net 1148 : 972 : elm->last = elm->cached = 0;
1149 : : }
1150 : :
1151 : : /*
1152 : : * Open the sequence relation.
1153 : : */
2532 1154 : 192579 : seqrel = lock_and_open_sequence(elm);
1155 : :
1156 : : /*
1157 : : * If the sequence has been transactionally replaced since we last saw it,
1158 : : * discard any cached-but-unissued values. We do not touch the currval()
1159 : : * state, however.
1160 : : */
648 rhaas@postgresql.org 1161 [ + + ]: 192576 : if (seqrel->rd_rel->relfilenode != elm->filenumber)
1162 : : {
1163 : 1035 : elm->filenumber = seqrel->rd_rel->relfilenode;
4897 tgl@sss.pgh.pa.us 1164 : 1035 : elm->cached = elm->last;
1165 : : }
1166 : :
1167 : : /* Return results */
7998 1168 : 192576 : *p_elm = elm;
1169 : 192576 : *p_rel = seqrel;
9874 vadim4o@yahoo.com 1170 : 192576 : }
1171 : :
1172 : :
1173 : : /*
1174 : : * Given an opened sequence relation, lock the page buffer and find the tuple
1175 : : *
1176 : : * *buf receives the reference to the pinned-and-ex-locked buffer
1177 : : * *seqdatatuple receives the reference to the sequence tuple proper
1178 : : * (this arg should point to a local variable of type HeapTupleData)
1179 : : *
1180 : : * Function's return value points to the data payload of the tuple
1181 : : */
1182 : : static Form_pg_sequence_data
2672 peter_e@gmx.net 1183 : 192500 : read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple)
1184 : : {
1185 : : Page page;
1186 : : ItemId lp;
1187 : : sequence_magic *sm;
1188 : : Form_pg_sequence_data seq;
1189 : :
7998 tgl@sss.pgh.pa.us 1190 : 192500 : *buf = ReadBuffer(rel, 0);
1191 : 192500 : LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
1192 : :
2916 kgrittn@postgresql.o 1193 : 192500 : page = BufferGetPage(*buf);
7998 tgl@sss.pgh.pa.us 1194 : 192500 : sm = (sequence_magic *) PageGetSpecialPointer(page);
1195 : :
1196 [ - + ]: 192500 : if (sm->magic != SEQ_MAGIC)
7566 tgl@sss.pgh.pa.us 1197 [ # # ]:UBC 0 : elog(ERROR, "bad magic number in sequence \"%s\": %08X",
1198 : : RelationGetRelationName(rel), sm->magic);
1199 : :
7998 tgl@sss.pgh.pa.us 1200 :CBC 192500 : lp = PageGetItemId(page, FirstOffsetNumber);
6059 1201 [ - + ]: 192500 : Assert(ItemIdIsNormal(lp));
1202 : :
1203 : : /* Note we currently only bother to set these two fields of *seqdatatuple */
2672 peter_e@gmx.net 1204 : 192500 : seqdatatuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
1205 : 192500 : seqdatatuple->t_len = ItemIdGetLength(lp);
1206 : :
1207 : : /*
1208 : : * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
1209 : : * a sequence, which would leave a non-frozen XID in the sequence tuple's
1210 : : * xmax, which eventually leads to clog access failures or worse. If we
1211 : : * see this has happened, clean up after it. We treat this like a hint
1212 : : * bit update, ie, don't bother to WAL-log it, since we can certainly do
1213 : : * this again if the update gets lost.
1214 : : */
1215 [ - + ]: 192500 : Assert(!(seqdatatuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
1216 [ - + ]: 192500 : if (HeapTupleHeaderGetRawXmax(seqdatatuple->t_data) != InvalidTransactionId)
1217 : : {
2672 peter_e@gmx.net 1218 :UBC 0 : HeapTupleHeaderSetXmax(seqdatatuple->t_data, InvalidTransactionId);
1219 : 0 : seqdatatuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
1220 : 0 : seqdatatuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
3954 jdavis@postgresql.or 1221 : 0 : MarkBufferDirtyHint(*buf, true);
1222 : : }
1223 : :
2672 peter_e@gmx.net 1224 :CBC 192500 : seq = (Form_pg_sequence_data) GETSTRUCT(seqdatatuple);
1225 : :
7998 tgl@sss.pgh.pa.us 1226 : 192500 : return seq;
1227 : : }
1228 : :
1229 : : /*
1230 : : * init_params: process the options list of CREATE or ALTER SEQUENCE, and
1231 : : * store the values into appropriate fields of seqform, for changes that go
1232 : : * into the pg_sequence catalog, and fields of seqdataform for changes to the
1233 : : * sequence relation itself. Set *need_seq_rewrite to true if we changed any
1234 : : * parameters that require rewriting the sequence's relation (interesting for
1235 : : * ALTER SEQUENCE). Also set *owned_by to any OWNED BY option, or to NIL if
1236 : : * there is none.
1237 : : *
1238 : : * If isInit is true, fill any unspecified options with default values;
1239 : : * otherwise, do not change existing options that aren't explicitly overridden.
1240 : : *
1241 : : * Note: we force a sequence rewrite whenever we change parameters that affect
1242 : : * generation of future sequence values, even if the seqdataform per se is not
1243 : : * changed. This allows ALTER SEQUENCE to behave transactionally. Currently,
1244 : : * the only option that doesn't cause that is OWNED BY. It's *necessary* for
1245 : : * ALTER SEQUENCE OWNED BY to not rewrite the sequence, because that would
1246 : : * break pg_upgrade by causing unwanted changes in the sequence's
1247 : : * relfilenumber.
1248 : : */
1249 : : static void
2565 peter_e@gmx.net 1250 : 1551 : init_params(ParseState *pstate, List *options, bool for_identity,
1251 : : bool isInit,
1252 : : Form_pg_sequence seqform,
1253 : : Form_pg_sequence_data seqdataform,
1254 : : bool *need_seq_rewrite,
1255 : : List **owned_by)
1256 : : {
2620 1257 : 1551 : DefElem *as_type = NULL;
5811 tgl@sss.pgh.pa.us 1258 : 1551 : DefElem *start_value = NULL;
1259 : 1551 : DefElem *restart_value = NULL;
9715 bruce@momjian.us 1260 : 1551 : DefElem *increment_by = NULL;
1261 : 1551 : DefElem *max_value = NULL;
1262 : 1551 : DefElem *min_value = NULL;
1263 : 1551 : DefElem *cache_value = NULL;
7447 tgl@sss.pgh.pa.us 1264 : 1551 : DefElem *is_cycled = NULL;
1265 : : ListCell *option;
2567 peter_e@gmx.net 1266 : 1551 : bool reset_max_value = false;
1267 : 1551 : bool reset_min_value = false;
1268 : :
2498 tgl@sss.pgh.pa.us 1269 : 1551 : *need_seq_rewrite = false;
6446 1270 : 1551 : *owned_by = NIL;
1271 : :
7696 bruce@momjian.us 1272 [ + + + + : 3324 : foreach(option, options)
+ + ]
1273 : : {
9715 1274 : 1773 : DefElem *defel = (DefElem *) lfirst(option);
1275 : :
2620 peter_e@gmx.net 1276 [ + + ]: 1773 : if (strcmp(defel->defname, "as") == 0)
1277 : : {
1278 [ - + ]: 676 : if (as_type)
1004 dean.a.rasheed@gmail 1279 :UBC 0 : errorConflictingDefElem(defel, pstate);
2620 peter_e@gmx.net 1280 :CBC 676 : as_type = defel;
2498 tgl@sss.pgh.pa.us 1281 : 676 : *need_seq_rewrite = true;
1282 : : }
2620 peter_e@gmx.net 1283 [ + + ]: 1097 : else if (strcmp(defel->defname, "increment") == 0)
1284 : : {
7731 bruce@momjian.us 1285 [ - + ]: 112 : if (increment_by)
1004 dean.a.rasheed@gmail 1286 :UBC 0 : errorConflictingDefElem(defel, pstate);
9716 bruce@momjian.us 1287 :CBC 112 : increment_by = defel;
2498 tgl@sss.pgh.pa.us 1288 : 112 : *need_seq_rewrite = true;
1289 : : }
5812 1290 [ + + ]: 985 : else if (strcmp(defel->defname, "start") == 0)
1291 : : {
5811 1292 [ - + ]: 104 : if (start_value)
1004 dean.a.rasheed@gmail 1293 :UBC 0 : errorConflictingDefElem(defel, pstate);
5811 tgl@sss.pgh.pa.us 1294 :CBC 104 : start_value = defel;
2498 1295 : 104 : *need_seq_rewrite = true;
1296 : : }
5812 1297 [ + + ]: 881 : else if (strcmp(defel->defname, "restart") == 0)
1298 : : {
5811 1299 [ - + ]: 42 : if (restart_value)
1004 dean.a.rasheed@gmail 1300 :UBC 0 : errorConflictingDefElem(defel, pstate);
5811 tgl@sss.pgh.pa.us 1301 :CBC 42 : restart_value = defel;
2498 1302 : 42 : *need_seq_rewrite = true;
1303 : : }
8207 bruce@momjian.us 1304 [ + + ]: 839 : else if (strcmp(defel->defname, "maxvalue") == 0)
1305 : : {
7731 1306 [ - + ]: 81 : if (max_value)
1004 dean.a.rasheed@gmail 1307 :UBC 0 : errorConflictingDefElem(defel, pstate);
9716 bruce@momjian.us 1308 :CBC 81 : max_value = defel;
2498 tgl@sss.pgh.pa.us 1309 : 81 : *need_seq_rewrite = true;
1310 : : }
8207 bruce@momjian.us 1311 [ + + ]: 758 : else if (strcmp(defel->defname, "minvalue") == 0)
1312 : : {
7731 1313 [ - + ]: 83 : if (min_value)
1004 dean.a.rasheed@gmail 1314 :UBC 0 : errorConflictingDefElem(defel, pstate);
9716 bruce@momjian.us 1315 :CBC 83 : min_value = defel;
2498 tgl@sss.pgh.pa.us 1316 : 83 : *need_seq_rewrite = true;
1317 : : }
8207 bruce@momjian.us 1318 [ + + ]: 675 : else if (strcmp(defel->defname, "cache") == 0)
1319 : : {
7731 1320 [ - + ]: 61 : if (cache_value)
1004 dean.a.rasheed@gmail 1321 :UBC 0 : errorConflictingDefElem(defel, pstate);
9716 bruce@momjian.us 1322 :CBC 61 : cache_value = defel;
2498 tgl@sss.pgh.pa.us 1323 : 61 : *need_seq_rewrite = true;
1324 : : }
8207 bruce@momjian.us 1325 [ + + ]: 614 : else if (strcmp(defel->defname, "cycle") == 0)
1326 : : {
7447 tgl@sss.pgh.pa.us 1327 [ - + ]: 21 : if (is_cycled)
1004 dean.a.rasheed@gmail 1328 :UBC 0 : errorConflictingDefElem(defel, pstate);
7447 tgl@sss.pgh.pa.us 1329 :CBC 21 : is_cycled = defel;
2498 1330 : 21 : *need_seq_rewrite = true;
1331 : : }
6446 1332 [ + - ]: 593 : else if (strcmp(defel->defname, "owned_by") == 0)
1333 : : {
1334 [ - + ]: 593 : if (*owned_by)
1004 dean.a.rasheed@gmail 1335 :UBC 0 : errorConflictingDefElem(defel, pstate);
6446 tgl@sss.pgh.pa.us 1336 :CBC 593 : *owned_by = defGetQualifiedName(defel);
1337 : : }
2565 peter_e@gmx.net 1338 [ # # ]:UBC 0 : else if (strcmp(defel->defname, "sequence_name") == 0)
1339 : : {
1340 : : /*
1341 : : * The parser allows this, but it is only for identity columns, in
1342 : : * which case it is filtered out in parse_utilcmd.c. We only get
1343 : : * here if someone puts it into a CREATE SEQUENCE.
1344 : : */
1345 [ # # ]: 0 : ereport(ERROR,
1346 : : (errcode(ERRCODE_SYNTAX_ERROR),
1347 : : errmsg("invalid sequence option SEQUENCE NAME"),
1348 : : parser_errposition(pstate, defel->location)));
1349 : : }
1350 : : else
7574 tgl@sss.pgh.pa.us 1351 [ # # ]: 0 : elog(ERROR, "option \"%s\" not recognized",
1352 : : defel->defname);
1353 : : }
1354 : :
1355 : : /*
1356 : : * We must reset log_cnt when isInit or when changing any parameters that
1357 : : * would affect future nextval allocations.
1358 : : */
4281 tgl@sss.pgh.pa.us 1359 [ + + ]:CBC 1551 : if (isInit)
2672 peter_e@gmx.net 1360 : 871 : seqdataform->log_cnt = 0;
1361 : :
1362 : : /* AS type */
2620 1363 [ + + ]: 1551 : if (as_type != NULL)
1364 : : {
2524 bruce@momjian.us 1365 : 676 : Oid newtypid = typenameTypeId(pstate, defGetTypeName(as_type));
1366 : :
2567 peter_e@gmx.net 1367 [ + + + + ]: 673 : if (newtypid != INT2OID &&
1368 [ + + ]: 58 : newtypid != INT4OID &&
1369 : : newtypid != INT8OID)
2620 1370 [ + - + + ]: 12 : ereport(ERROR,
1371 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1372 : : for_identity
1373 : : ? errmsg("identity column type must be smallint, integer, or bigint")
1374 : : : errmsg("sequence type must be smallint, integer, or bigint")));
1375 : :
2567 1376 [ + + ]: 661 : if (!isInit)
1377 : : {
1378 : : /*
1379 : : * When changing type and the old sequence min/max values were the
1380 : : * min/max of the old type, adjust sequence min/max values to
1381 : : * min/max of new type. (Otherwise, the user chose explicit
1382 : : * min/max values, which we'll leave alone.)
1383 : : */
1384 [ + + + + ]: 42 : if ((seqform->seqtypid == INT2OID && seqform->seqmax == PG_INT16_MAX) ||
1385 [ + + + + ]: 30 : (seqform->seqtypid == INT4OID && seqform->seqmax == PG_INT32_MAX) ||
2489 tgl@sss.pgh.pa.us 1386 [ + + + - ]: 15 : (seqform->seqtypid == INT8OID && seqform->seqmax == PG_INT64_MAX))
2567 peter_e@gmx.net 1387 : 30 : reset_max_value = true;
1388 [ + + + + ]: 42 : if ((seqform->seqtypid == INT2OID && seqform->seqmin == PG_INT16_MIN) ||
1389 [ + + + + ]: 33 : (seqform->seqtypid == INT4OID && seqform->seqmin == PG_INT32_MIN) ||
2489 tgl@sss.pgh.pa.us 1390 [ + + - + ]: 30 : (seqform->seqtypid == INT8OID && seqform->seqmin == PG_INT64_MIN))
2567 peter_e@gmx.net 1391 : 12 : reset_min_value = true;
1392 : : }
1393 : :
1394 : 661 : seqform->seqtypid = newtypid;
1395 : : }
2620 1396 [ + + ]: 875 : else if (isInit)
1397 : : {
1398 : 240 : seqform->seqtypid = INT8OID;
1399 : : }
1400 : :
1401 : : /* INCREMENT BY */
7403 neilc@samurai.com 1402 [ + + ]: 1536 : if (increment_by != NULL)
1403 : : {
2672 peter_e@gmx.net 1404 : 112 : seqform->seqincrement = defGetInt64(increment_by);
1405 [ + + ]: 112 : if (seqform->seqincrement == 0)
7574 tgl@sss.pgh.pa.us 1406 [ + - ]: 3 : ereport(ERROR,
1407 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1408 : : errmsg("INCREMENT must not be zero")));
2672 peter_e@gmx.net 1409 : 109 : seqdataform->log_cnt = 0;
1410 : : }
7447 tgl@sss.pgh.pa.us 1411 [ + + ]: 1424 : else if (isInit)
1412 : : {
2672 peter_e@gmx.net 1413 : 769 : seqform->seqincrement = 1;
1414 : : }
1415 : :
1416 : : /* CYCLE */
7403 neilc@samurai.com 1417 [ + + ]: 1533 : if (is_cycled != NULL)
1418 : : {
821 peter@eisentraut.org 1419 : 21 : seqform->seqcycle = boolVal(is_cycled->arg);
2672 peter_e@gmx.net 1420 [ + + - + ]: 21 : Assert(BoolIsValid(seqform->seqcycle));
1421 : 21 : seqdataform->log_cnt = 0;
1422 : : }
7447 tgl@sss.pgh.pa.us 1423 [ + + ]: 1512 : else if (isInit)
1424 : : {
2672 peter_e@gmx.net 1425 : 851 : seqform->seqcycle = false;
1426 : : }
1427 : :
1428 : : /* MAXVALUE (null arg means NO MAXVALUE) */
7403 neilc@samurai.com 1429 [ + + + + ]: 1533 : if (max_value != NULL && max_value->arg)
1430 : : {
2672 peter_e@gmx.net 1431 : 34 : seqform->seqmax = defGetInt64(max_value);
1432 : 34 : seqdataform->log_cnt = 0;
1433 : : }
2567 1434 [ + + + - : 1499 : else if (isInit || max_value != NULL || reset_max_value)
+ + ]
1435 : : {
1436 [ + + + + ]: 867 : if (seqform->seqincrement > 0 || reset_max_value)
1437 : : {
1438 : : /* ascending seq */
2620 1439 [ + + ]: 849 : if (seqform->seqtypid == INT2OID)
1440 : 37 : seqform->seqmax = PG_INT16_MAX;
1441 [ + + ]: 812 : else if (seqform->seqtypid == INT4OID)
1442 : 547 : seqform->seqmax = PG_INT32_MAX;
1443 : : else
1444 : 265 : seqform->seqmax = PG_INT64_MAX;
1445 : : }
1446 : : else
2489 tgl@sss.pgh.pa.us 1447 : 18 : seqform->seqmax = -1; /* descending seq */
2672 peter_e@gmx.net 1448 : 867 : seqdataform->log_cnt = 0;
1449 : : }
1450 : :
1451 : : /* Validate maximum value. No need to check INT8 as seqmax is an int64 */
2620 1452 [ + + + - : 1533 : if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX))
+ + ]
1006 drowley@postgresql.o 1453 [ + + + - : 1527 : || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)))
- + ]
2620 peter_e@gmx.net 1454 [ + - ]: 6 : ereport(ERROR,
1455 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1456 : : errmsg("MAXVALUE (%lld) is out of range for sequence data type %s",
1457 : : (long long) seqform->seqmax,
1458 : : format_type_be(seqform->seqtypid))));
1459 : :
1460 : : /* MINVALUE (null arg means NO MINVALUE) */
7403 neilc@samurai.com 1461 [ + + + + ]: 1527 : if (min_value != NULL && min_value->arg)
1462 : : {
2672 peter_e@gmx.net 1463 : 36 : seqform->seqmin = defGetInt64(min_value);
1464 : 36 : seqdataform->log_cnt = 0;
1465 : : }
2567 1466 [ + + + - : 1491 : else if (isInit || min_value != NULL || reset_min_value)
+ + ]
1467 : : {
1468 [ + + + + ]: 842 : if (seqform->seqincrement < 0 || reset_min_value)
1469 : : {
1470 : : /* descending seq */
2620 1471 [ + + ]: 31 : if (seqform->seqtypid == INT2OID)
1472 : 10 : seqform->seqmin = PG_INT16_MIN;
1473 [ + + ]: 21 : else if (seqform->seqtypid == INT4OID)
1474 : 14 : seqform->seqmin = PG_INT32_MIN;
1475 : : else
1476 : 7 : seqform->seqmin = PG_INT64_MIN;
1477 : : }
1478 : : else
2524 bruce@momjian.us 1479 : 811 : seqform->seqmin = 1; /* ascending seq */
2672 peter_e@gmx.net 1480 : 842 : seqdataform->log_cnt = 0;
1481 : : }
1482 : :
1483 : : /* Validate minimum value. No need to check INT8 as seqmin is an int64 */
2620 1484 [ + + + + : 1527 : if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX))
+ - ]
1006 drowley@postgresql.o 1485 [ + + + - : 1521 : || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)))
- + ]
2620 peter_e@gmx.net 1486 [ + - ]: 6 : ereport(ERROR,
1487 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1488 : : errmsg("MINVALUE (%lld) is out of range for sequence data type %s",
1489 : : (long long) seqform->seqmin,
1490 : : format_type_be(seqform->seqtypid))));
1491 : :
1492 : : /* crosscheck min/max */
2672 1493 [ + + ]: 1521 : if (seqform->seqmin >= seqform->seqmax)
7574 tgl@sss.pgh.pa.us 1494 [ + - ]: 6 : ereport(ERROR,
1495 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1496 : : errmsg("MINVALUE (%lld) must be less than MAXVALUE (%lld)",
1497 : : (long long) seqform->seqmin,
1498 : : (long long) seqform->seqmax)));
1499 : :
1500 : : /* START WITH */
5811 1501 [ + + ]: 1515 : if (start_value != NULL)
1502 : : {
2672 peter_e@gmx.net 1503 : 104 : seqform->seqstart = defGetInt64(start_value);
1504 : : }
7447 tgl@sss.pgh.pa.us 1505 [ + + ]: 1411 : else if (isInit)
1506 : : {
2672 peter_e@gmx.net 1507 [ + + ]: 757 : if (seqform->seqincrement > 0)
2489 tgl@sss.pgh.pa.us 1508 : 745 : seqform->seqstart = seqform->seqmin; /* ascending seq */
1509 : : else
1510 : 12 : seqform->seqstart = seqform->seqmax; /* descending seq */
1511 : : }
1512 : :
1513 : : /* crosscheck START */
2672 peter_e@gmx.net 1514 [ + + ]: 1515 : if (seqform->seqstart < seqform->seqmin)
5812 tgl@sss.pgh.pa.us 1515 [ + - ]: 3 : ereport(ERROR,
1516 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1517 : : errmsg("START value (%lld) cannot be less than MINVALUE (%lld)",
1518 : : (long long) seqform->seqstart,
1519 : : (long long) seqform->seqmin)));
2672 peter_e@gmx.net 1520 [ + + ]: 1512 : if (seqform->seqstart > seqform->seqmax)
5812 tgl@sss.pgh.pa.us 1521 [ + - ]: 3 : ereport(ERROR,
1522 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1523 : : errmsg("START value (%lld) cannot be greater than MAXVALUE (%lld)",
1524 : : (long long) seqform->seqstart,
1525 : : (long long) seqform->seqmax)));
1526 : :
1527 : : /* RESTART [WITH] */
5811 1528 [ + + ]: 1509 : if (restart_value != NULL)
1529 : : {
1530 [ + + ]: 42 : if (restart_value->arg != NULL)
2672 peter_e@gmx.net 1531 : 27 : seqdataform->last_value = defGetInt64(restart_value);
1532 : : else
1533 : 15 : seqdataform->last_value = seqform->seqstart;
1534 : 42 : seqdataform->is_called = false;
1535 : 42 : seqdataform->log_cnt = 0;
1536 : : }
5811 tgl@sss.pgh.pa.us 1537 [ + + ]: 1467 : else if (isInit)
1538 : : {
2672 peter_e@gmx.net 1539 : 838 : seqdataform->last_value = seqform->seqstart;
1540 : 838 : seqdataform->is_called = false;
1541 : : }
1542 : :
1543 : : /* crosscheck RESTART (or current value, if changing MIN/MAX) */
1544 [ + + ]: 1509 : if (seqdataform->last_value < seqform->seqmin)
7574 tgl@sss.pgh.pa.us 1545 [ + - ]: 3 : ereport(ERROR,
1546 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1547 : : errmsg("RESTART value (%lld) cannot be less than MINVALUE (%lld)",
1548 : : (long long) seqdataform->last_value,
1549 : : (long long) seqform->seqmin)));
2672 peter_e@gmx.net 1550 [ + + ]: 1506 : if (seqdataform->last_value > seqform->seqmax)
7574 tgl@sss.pgh.pa.us 1551 [ + - ]: 3 : ereport(ERROR,
1552 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1553 : : errmsg("RESTART value (%lld) cannot be greater than MAXVALUE (%lld)",
1554 : : (long long) seqdataform->last_value,
1555 : : (long long) seqform->seqmax)));
1556 : :
1557 : : /* CACHE */
7403 neilc@samurai.com 1558 [ + + ]: 1503 : if (cache_value != NULL)
1559 : : {
2672 peter_e@gmx.net 1560 : 61 : seqform->seqcache = defGetInt64(cache_value);
1561 [ + + ]: 61 : if (seqform->seqcache <= 0)
7447 tgl@sss.pgh.pa.us 1562 [ + - ]: 3 : ereport(ERROR,
1563 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1564 : : errmsg("CACHE (%lld) must be greater than zero",
1565 : : (long long) seqform->seqcache)));
2672 peter_e@gmx.net 1566 : 58 : seqdataform->log_cnt = 0;
1567 : : }
7447 tgl@sss.pgh.pa.us 1568 [ + + ]: 1442 : else if (isInit)
1569 : : {
2672 peter_e@gmx.net 1570 : 778 : seqform->seqcache = 1;
1571 : : }
9874 vadim4o@yahoo.com 1572 : 1500 : }
1573 : :
1574 : : /*
1575 : : * Process an OWNED BY option for CREATE/ALTER SEQUENCE
1576 : : *
1577 : : * Ownership permissions on the sequence are already checked,
1578 : : * but if we are establishing a new owned-by dependency, we must
1579 : : * enforce that the referenced table has the same owner and namespace
1580 : : * as the sequence.
1581 : : */
1582 : : static void
2565 peter_e@gmx.net 1583 : 593 : process_owned_by(Relation seqrel, List *owned_by, bool for_identity)
1584 : : {
1585 : : DependencyType deptype;
1586 : : int nnames;
1587 : : Relation tablerel;
1588 : : AttrNumber attnum;
1589 : :
1590 [ + + ]: 593 : deptype = for_identity ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO;
1591 : :
6446 tgl@sss.pgh.pa.us 1592 : 593 : nnames = list_length(owned_by);
1593 [ - + ]: 593 : Assert(nnames > 0);
1594 [ + + ]: 593 : if (nnames == 1)
1595 : : {
1596 : : /* Must be OWNED BY NONE */
1597 [ + + ]: 6 : if (strcmp(strVal(linitial(owned_by)), "none") != 0)
1598 [ + - ]: 3 : ereport(ERROR,
1599 : : (errcode(ERRCODE_SYNTAX_ERROR),
1600 : : errmsg("invalid OWNED BY option"),
1601 : : errhint("Specify OWNED BY table.column or OWNED BY NONE.")));
1602 : 3 : tablerel = NULL;
1603 : 3 : attnum = 0;
1604 : : }
1605 : : else
1606 : : {
1607 : : List *relname;
1608 : : char *attrname;
1609 : : RangeVar *rel;
1610 : :
1611 : : /* Separate relname and attr name */
641 drowley@postgresql.o 1612 : 587 : relname = list_copy_head(owned_by, nnames - 1);
1295 tgl@sss.pgh.pa.us 1613 : 587 : attrname = strVal(llast(owned_by));
1614 : :
1615 : : /* Open and lock rel to ensure it won't go away meanwhile */
6446 1616 : 587 : rel = makeRangeVarFromNameList(relname);
1617 : 587 : tablerel = relation_openrv(rel, AccessShareLock);
1618 : :
1619 : : /* Must be a regular or foreign table */
3987 1620 [ + + ]: 587 : if (!(tablerel->rd_rel->relkind == RELKIND_RELATION ||
2685 rhaas@postgresql.org 1621 [ + + ]: 34 : tablerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
2565 peter_e@gmx.net 1622 [ + - ]: 30 : tablerel->rd_rel->relkind == RELKIND_VIEW ||
2685 rhaas@postgresql.org 1623 [ + + ]: 30 : tablerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE))
6446 tgl@sss.pgh.pa.us 1624 [ + - ]: 3 : ereport(ERROR,
1625 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1626 : : errmsg("sequence cannot be owned by relation \"%s\"",
1627 : : RelationGetRelationName(tablerel)),
1628 : : errdetail_relkind_not_supported(tablerel->rd_rel->relkind)));
1629 : :
1630 : : /* We insist on same owner and schema */
1631 [ - + ]: 584 : if (seqrel->rd_rel->relowner != tablerel->rd_rel->relowner)
6446 tgl@sss.pgh.pa.us 1632 [ # # ]:UBC 0 : ereport(ERROR,
1633 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1634 : : errmsg("sequence must have same owner as table it is linked to")));
6446 tgl@sss.pgh.pa.us 1635 [ + + ]:CBC 584 : if (RelationGetNamespace(seqrel) != RelationGetNamespace(tablerel))
1636 [ + - ]: 3 : ereport(ERROR,
1637 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1638 : : errmsg("sequence must be in same schema as table it is linked to")));
1639 : :
1640 : : /* Now, fetch the attribute number from the system cache */
1641 : 581 : attnum = get_attnum(RelationGetRelid(tablerel), attrname);
1642 [ + + ]: 581 : if (attnum == InvalidAttrNumber)
1643 [ + - ]: 3 : ereport(ERROR,
1644 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
1645 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
1646 : : attrname, RelationGetRelationName(tablerel))));
1647 : : }
1648 : :
1649 : : /*
1650 : : * Catch user explicitly running OWNED BY on identity sequence.
1651 : : */
2565 peter_e@gmx.net 1652 [ + + ]: 581 : if (deptype == DEPENDENCY_AUTO)
1653 : : {
1654 : : Oid tableId;
1655 : : int32 colId;
1656 : :
1657 [ + + ]: 410 : if (sequenceIsOwned(RelationGetRelid(seqrel), DEPENDENCY_INTERNAL, &tableId, &colId))
1658 [ + - ]: 3 : ereport(ERROR,
1659 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1660 : : errmsg("cannot change ownership of identity sequence"),
1661 : : errdetail("Sequence \"%s\" is linked to table \"%s\".",
1662 : : RelationGetRelationName(seqrel),
1663 : : get_rel_name(tableId))));
1664 : : }
1665 : :
1666 : : /*
1667 : : * OK, we are ready to update pg_depend. First remove any existing
1668 : : * dependencies for the sequence, then optionally add a new one.
1669 : : */
1670 : 578 : deleteDependencyRecordsForClass(RelationRelationId, RelationGetRelid(seqrel),
1671 : : RelationRelationId, deptype);
1672 : :
6446 tgl@sss.pgh.pa.us 1673 [ + - ]: 578 : if (tablerel)
1674 : : {
1675 : : ObjectAddress refobject,
1676 : : depobject;
1677 : :
1678 : 578 : refobject.classId = RelationRelationId;
1679 : 578 : refobject.objectId = RelationGetRelid(tablerel);
1680 : 578 : refobject.objectSubId = attnum;
1681 : 578 : depobject.classId = RelationRelationId;
1682 : 578 : depobject.objectId = RelationGetRelid(seqrel);
1683 : 578 : depobject.objectSubId = 0;
2565 peter_e@gmx.net 1684 : 578 : recordDependencyOn(&depobject, &refobject, deptype);
1685 : : }
1686 : :
1687 : : /* Done, but hold lock until commit */
6446 tgl@sss.pgh.pa.us 1688 [ + - ]: 578 : if (tablerel)
1689 : 578 : relation_close(tablerel, NoLock);
1690 : 578 : }
1691 : :
1692 : :
1693 : : /*
1694 : : * Return sequence parameters in a list of the form created by the parser.
1695 : : */
1696 : : List *
2565 peter_e@gmx.net 1697 : 3 : sequence_options(Oid relid)
1698 : : {
1699 : : HeapTuple pgstuple;
1700 : : Form_pg_sequence pgsform;
1701 : 3 : List *options = NIL;
1702 : :
269 michael@paquier.xyz 1703 :GNC 3 : pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(relid));
2565 peter_e@gmx.net 1704 [ - + ]:CBC 3 : if (!HeapTupleIsValid(pgstuple))
2565 peter_e@gmx.net 1705 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for sequence %u", relid);
2565 peter_e@gmx.net 1706 :CBC 3 : pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
1707 : :
1708 : : /* Use makeFloat() for 64-bit integers, like gram.y does. */
2230 1709 : 3 : options = lappend(options,
1710 : 3 : makeDefElem("cache", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqcache)), -1));
1711 : 3 : options = lappend(options,
821 peter@eisentraut.org 1712 : 3 : makeDefElem("cycle", (Node *) makeBoolean(pgsform->seqcycle), -1));
2230 peter_e@gmx.net 1713 : 3 : options = lappend(options,
1714 : 3 : makeDefElem("increment", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqincrement)), -1));
1715 : 3 : options = lappend(options,
1716 : 3 : makeDefElem("maxvalue", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqmax)), -1));
1717 : 3 : options = lappend(options,
1718 : 3 : makeDefElem("minvalue", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqmin)), -1));
1719 : 3 : options = lappend(options,
1720 : 3 : makeDefElem("start", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqstart)), -1));
1721 : :
2565 1722 : 3 : ReleaseSysCache(pgstuple);
1723 : :
1724 : 3 : return options;
1725 : : }
1726 : :
1727 : : /*
1728 : : * Return sequence parameters (formerly for use by information schema)
1729 : : */
1730 : : Datum
4851 1731 : 3 : pg_sequence_parameters(PG_FUNCTION_ARGS)
1732 : : {
1733 : 3 : Oid relid = PG_GETARG_OID(0);
1734 : : TupleDesc tupdesc;
1735 : : Datum values[7];
1736 : : bool isnull[7];
1737 : : HeapTuple pgstuple;
1738 : : Form_pg_sequence pgsform;
1739 : :
1740 [ - + ]: 3 : if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
4851 peter_e@gmx.net 1741 [ # # ]:UBC 0 : ereport(ERROR,
1742 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1743 : : errmsg("permission denied for sequence %s",
1744 : : get_rel_name(relid))));
1745 : :
480 michael@paquier.xyz 1746 [ - + ]:CBC 3 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
480 michael@paquier.xyz 1747 [ # # ]:UBC 0 : elog(ERROR, "return type must be a row type");
1748 : :
4851 peter_e@gmx.net 1749 :CBC 3 : memset(isnull, 0, sizeof(isnull));
1750 : :
269 michael@paquier.xyz 1751 :GNC 3 : pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(relid));
2672 peter_e@gmx.net 1752 [ - + ]:CBC 3 : if (!HeapTupleIsValid(pgstuple))
2672 peter_e@gmx.net 1753 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for sequence %u", relid);
2672 peter_e@gmx.net 1754 :CBC 3 : pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
1755 : :
1756 : 3 : values[0] = Int64GetDatum(pgsform->seqstart);
1757 : 3 : values[1] = Int64GetDatum(pgsform->seqmin);
1758 : 3 : values[2] = Int64GetDatum(pgsform->seqmax);
1759 : 3 : values[3] = Int64GetDatum(pgsform->seqincrement);
1760 : 3 : values[4] = BoolGetDatum(pgsform->seqcycle);
1761 : 3 : values[5] = Int64GetDatum(pgsform->seqcache);
2620 1762 : 3 : values[6] = ObjectIdGetDatum(pgsform->seqtypid);
1763 : :
2672 1764 : 3 : ReleaseSysCache(pgstuple);
1765 : :
4851 1766 : 3 : return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
1767 : : }
1768 : :
1769 : : /*
1770 : : * Return the last value from the sequence
1771 : : *
1772 : : * Note: This has a completely different meaning than lastval().
1773 : : */
1774 : : Datum
2704 1775 : 57 : pg_sequence_last_value(PG_FUNCTION_ARGS)
1776 : : {
1777 : 57 : Oid relid = PG_GETARG_OID(0);
1778 : : SeqTable elm;
1779 : : Relation seqrel;
1780 : : Buffer buf;
1781 : : HeapTupleData seqtuple;
1782 : : Form_pg_sequence_data seq;
1783 : : bool is_called;
1784 : : int64 result;
1785 : :
1786 : : /* open and lock sequence */
1787 : 57 : init_sequence(relid, &elm, &seqrel);
1788 : :
1789 [ - + ]: 57 : if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
2704 peter_e@gmx.net 1790 [ # # ]:UBC 0 : ereport(ERROR,
1791 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1792 : : errmsg("permission denied for sequence %s",
1793 : : RelationGetRelationName(seqrel))));
1794 : :
2672 peter_e@gmx.net 1795 :CBC 57 : seq = read_seq_tuple(seqrel, &buf, &seqtuple);
1796 : :
2704 1797 : 57 : is_called = seq->is_called;
1798 : 57 : result = seq->last_value;
1799 : :
1800 : 57 : UnlockReleaseBuffer(buf);
48 michael@paquier.xyz 1801 :GNC 57 : sequence_close(seqrel, NoLock);
1802 : :
2704 peter_e@gmx.net 1803 [ + + ]:CBC 57 : if (is_called)
1804 : 24 : PG_RETURN_INT64(result);
1805 : : else
1806 : 33 : PG_RETURN_NULL();
1807 : : }
1808 : :
1809 : :
1810 : : void
3433 heikki.linnakangas@i 1811 : 2308 : seq_redo(XLogReaderState *record)
1812 : : {
1813 : 2308 : XLogRecPtr lsn = record->EndRecPtr;
1814 : 2308 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
1815 : : Buffer buffer;
1816 : : Page page;
1817 : : Page localpage;
1818 : : char *item;
1819 : : Size itemsz;
8424 bruce@momjian.us 1820 : 2308 : xl_seq_rec *xlrec = (xl_seq_rec *) XLogRecGetData(record);
1821 : : sequence_magic *sm;
1822 : :
8508 vadim4o@yahoo.com 1823 [ - + ]: 2308 : if (info != XLOG_SEQ_LOG)
8079 bruce@momjian.us 1824 [ # # ]:UBC 0 : elog(PANIC, "seq_redo: unknown op code %u", info);
1825 : :
3433 heikki.linnakangas@i 1826 :CBC 2308 : buffer = XLogInitBufferForRedo(record, 0);
2916 kgrittn@postgresql.o 1827 : 2308 : page = (Page) BufferGetPage(buffer);
1828 : :
1829 : : /*
1830 : : * We always reinit the page. However, since this WAL record type is also
1831 : : * used for updating sequences, it's possible that a hot-standby backend
1832 : : * is examining the page concurrently; so we mustn't transiently trash the
1833 : : * buffer. The solution is to build the correct new page contents in
1834 : : * local workspace and then memcpy into the buffer. Then only bytes that
1835 : : * are supposed to change will change, even transiently. We must palloc
1836 : : * the local page for alignment reasons.
1837 : : */
4452 tgl@sss.pgh.pa.us 1838 : 2308 : localpage = (Page) palloc(BufferGetPageSize(buffer));
1839 : :
1840 : 2308 : PageInit(localpage, BufferGetPageSize(buffer), sizeof(sequence_magic));
1841 : 2308 : sm = (sequence_magic *) PageGetSpecialPointer(localpage);
8508 vadim4o@yahoo.com 1842 : 2308 : sm->magic = SEQ_MAGIC;
1843 : :
8424 bruce@momjian.us 1844 : 2308 : item = (char *) xlrec + sizeof(xl_seq_rec);
3433 heikki.linnakangas@i 1845 : 2308 : itemsz = XLogRecGetDataLen(record) - sizeof(xl_seq_rec);
1846 : :
4452 tgl@sss.pgh.pa.us 1847 [ - + ]: 2308 : if (PageAddItem(localpage, (Item) item, itemsz,
1848 : : FirstOffsetNumber, false, false) == InvalidOffsetNumber)
8079 bruce@momjian.us 1849 [ # # ]:UBC 0 : elog(PANIC, "seq_redo: failed to add item to page");
1850 : :
4452 tgl@sss.pgh.pa.us 1851 :CBC 2308 : PageSetLSN(localpage, lsn);
1852 : :
1853 : 2308 : memcpy(page, localpage, BufferGetPageSize(buffer));
6589 1854 : 2308 : MarkBufferDirty(buffer);
1855 : 2308 : UnlockReleaseBuffer(buffer);
1856 : :
4452 1857 : 2308 : pfree(localpage);
8536 vadim4o@yahoo.com 1858 : 2308 : }
1859 : :
1860 : : /*
1861 : : * Flush cached sequence information.
1862 : : */
1863 : : void
3846 rhaas@postgresql.org 1864 : 9 : ResetSequenceCaches(void)
1865 : : {
3803 heikki.linnakangas@i 1866 [ + + ]: 9 : if (seqhashtab)
1867 : : {
1868 : 6 : hash_destroy(seqhashtab);
1869 : 6 : seqhashtab = NULL;
1870 : : }
1871 : :
3842 rhaas@postgresql.org 1872 : 9 : last_used_seq = NULL;
3846 1873 : 9 : }
1874 : :
1875 : : /*
1876 : : * Mask a Sequence page before performing consistency checks on it.
1877 : : */
1878 : : void
2622 rhaas@postgresql.org 1879 :UBC 0 : seq_mask(char *page, BlockNumber blkno)
1880 : : {
2396 1881 : 0 : mask_page_lsn_and_checksum(page);
1882 : :
2622 1883 : 0 : mask_unused_space(page);
1884 : 0 : }
|