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