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