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