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/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
3388 peter_e@gmx.net 112 :CBC 989 : 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;
10326 bruce@momjian.us 120 : 989 : 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 : : */
4130 heikki.linnakangas@i 137 [ + + ]: 989 : if (seq->if_not_exists)
138 : : {
139 : 8 : RangeVarGetAndCheckCreationNamespace(seq->sequence, NoLock, &seqoid);
140 [ + + ]: 8 : 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 : : */
1226 tgl@sss.pgh.pa.us 146 : 5 : ObjectAddressSet(address, RelationRelationId, seqoid);
147 : 5 : checkMembershipInCurrentExtension(&address);
148 : :
149 : : /* OK to skip */
4130 heikki.linnakangas@i 150 [ + + ]: 4 : ereport(NOTICE,
151 : : (errcode(ERRCODE_DUPLICATE_TABLE),
152 : : errmsg("relation \"%s\" already exists, skipping",
153 : : seq->sequence->relname)));
3941 alvherre@alvh.no-ip. 154 : 4 : return InvalidObjectAddress;
155 : : }
156 : : }
157 : :
158 : : /* Check and set all option values */
3109 tgl@sss.pgh.pa.us 159 : 984 : 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 : : */
10327 bruce@momjian.us 166 : 948 : stmt->tableElts = NIL;
167 [ + + ]: 3792 : for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
168 : : {
839 peter@eisentraut.org 169 : 2844 : ColumnDef *coldef = NULL;
170 : :
10327 bruce@momjian.us 171 [ + + + - ]: 2844 : switch (i)
172 : : {
10326 173 : 948 : case SEQ_COL_LASTVAL:
840 peter@eisentraut.org 174 : 948 : coldef = makeColumnDef("last_value", INT8OID, -1, InvalidOid);
120 michael@paquier.xyz 175 :GNC 948 : value[i - 1] = Int64GetDatumFast(last_value);
10326 bruce@momjian.us 176 :CBC 948 : break;
9147 vadim4o@yahoo.com 177 : 948 : case SEQ_COL_LOG:
840 peter@eisentraut.org 178 : 948 : coldef = makeColumnDef("log_cnt", INT8OID, -1, InvalidOid);
4892 tgl@sss.pgh.pa.us 179 : 948 : value[i - 1] = Int64GetDatum((int64) 0);
9147 vadim4o@yahoo.com 180 : 948 : break;
10326 bruce@momjian.us 181 : 948 : case SEQ_COL_CALLED:
840 peter@eisentraut.org 182 : 948 : coldef = makeColumnDef("is_called", BOOLOID, -1, InvalidOid);
8888 tgl@sss.pgh.pa.us 183 : 948 : value[i - 1] = BoolGetDatum(false);
10326 bruce@momjian.us 184 : 948 : break;
185 : : }
186 : :
840 peter@eisentraut.org 187 : 2844 : coldef->is_not_null = true;
188 : 2844 : null[i - 1] = false;
189 : :
10327 bruce@momjian.us 190 : 2844 : stmt->tableElts = lappend(stmt->tableElts, coldef);
191 : : }
192 : :
8671 tgl@sss.pgh.pa.us 193 : 948 : stmt->relation = seq->sequence;
194 : 948 : stmt->inhRelations = NIL;
10327 bruce@momjian.us 195 : 948 : stmt->constraints = NIL;
4631 tgl@sss.pgh.pa.us 196 : 948 : stmt->options = NIL;
8436 197 : 948 : stmt->oncommit = ONCOMMIT_NOOP;
7827 198 : 948 : stmt->tablespacename = NULL;
4130 heikki.linnakangas@i 199 : 948 : stmt->if_not_exists = seq->if_not_exists;
200 : :
3296 rhaas@postgresql.org 201 : 948 : address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL, NULL);
3941 alvherre@alvh.no-ip. 202 : 948 : seqoid = address.objectId;
5623 rhaas@postgresql.org 203 [ - + ]: 948 : Assert(seqoid != InvalidOid);
204 : :
659 michael@paquier.xyz 205 : 948 : rel = sequence_open(seqoid, AccessExclusiveLock);
9968 bruce@momjian.us 206 : 948 : tupDesc = RelationGetDescr(rel);
207 : :
208 : : /* now initialize the sequence's data */
5508 tgl@sss.pgh.pa.us 209 : 948 : tuple = heap_form_tuple(tupDesc, value, null);
210 : 948 : fill_seq_with_data(rel, tuple);
211 : :
212 : : /* process OWNED BY if given */
213 [ + + ]: 948 : if (owned_by)
3176 peter_e@gmx.net 214 : 13 : process_owned_by(rel, owned_by, seq->for_identity);
215 : :
659 michael@paquier.xyz 216 : 936 : sequence_close(rel, NoLock);
217 : :
218 : : /* fill in pg_sequence */
2521 andres@anarazel.de 219 : 936 : rel = table_open(SequenceRelationId, RowExclusiveLock);
3283 peter_e@gmx.net 220 : 936 : tupDesc = RelationGetDescr(rel);
221 : :
222 : 936 : memset(pgs_nulls, 0, sizeof(pgs_nulls));
223 : :
224 : 936 : pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid);
3231 225 : 936 : pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid);
3283 226 : 936 : pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart);
227 : 936 : pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement);
228 : 936 : pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax);
229 : 936 : pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin);
230 : 936 : pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache);
3231 231 : 936 : pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle);
232 : :
3283 233 : 936 : tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls);
3241 alvherre@alvh.no-ip. 234 : 936 : CatalogTupleInsert(rel, tuple);
235 : :
3283 peter_e@gmx.net 236 : 936 : heap_freetuple(tuple);
2521 andres@anarazel.de 237 : 936 : table_close(rel, RowExclusiveLock);
238 : :
3941 alvherre@alvh.no-ip. 239 : 936 : 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
5508 tgl@sss.pgh.pa.us 255 : 18 : 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 : 18 : init_sequence(seq_relid, &elm, &seq_rel);
3283 peter_e@gmx.net 273 : 18 : (void) read_seq_tuple(seq_rel, &buf, &seqdatatuple);
274 : :
275 : 18 : pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(seq_relid));
276 [ - + ]: 18 : if (!HeapTupleIsValid(pgstuple))
3283 peter_e@gmx.net 277 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for sequence %u", seq_relid);
3283 peter_e@gmx.net 278 :CBC 18 : pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
279 : 18 : startv = pgsform->seqstart;
280 : 18 : ReleaseSysCache(pgstuple);
281 : :
282 : : /*
283 : : * Copy the existing sequence tuple.
284 : : */
285 : 18 : tuple = heap_copytuple(&seqdatatuple);
286 : :
287 : : /* Now we're done with the old page */
5508 tgl@sss.pgh.pa.us 288 : 18 : UnlockReleaseBuffer(buf);
289 : :
290 : : /*
291 : : * Modify the copied tuple to execute the restart (compare the RESTART
292 : : * action in AlterSequence)
293 : : */
3283 peter_e@gmx.net 294 : 18 : seq = (Form_pg_sequence_data) GETSTRUCT(tuple);
295 : 18 : seq->last_value = startv;
5508 tgl@sss.pgh.pa.us 296 : 18 : seq->is_called = false;
4892 297 : 18 : seq->log_cnt = 0;
298 : :
299 : : /*
300 : : * Create a new storage file for the sequence.
301 : : */
1259 rhaas@postgresql.org 302 : 18 : 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 : : */
2455 andres@anarazel.de 309 [ - + ]: 18 : Assert(seq_rel->rd_rel->relfrozenxid == InvalidTransactionId);
310 [ - + ]: 18 : Assert(seq_rel->rd_rel->relminmxid == InvalidMultiXactId);
311 : :
312 : : /*
313 : : * Insert the modified tuple into the new storage file.
314 : : */
5508 tgl@sss.pgh.pa.us 315 : 18 : 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 : 18 : elm->cached = elm->last;
320 : :
659 michael@paquier.xyz 321 : 18 : sequence_close(seq_rel, NoLock);
5508 tgl@sss.pgh.pa.us 322 : 18 : }
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 : 1091 : fill_seq_with_data(Relation rel, HeapTuple tuple)
332 : : {
1349 peter@eisentraut.org 333 : 1091 : fill_seq_fork_with_data(rel, tuple, MAIN_FORKNUM);
334 : :
335 [ + + ]: 1091 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
336 : : {
337 : : SMgrRelation srel;
338 : :
653 heikki.linnakangas@i 339 : 58 : srel = smgropen(rel->rd_locator, INVALID_PROC_NUMBER);
1349 peter@eisentraut.org 340 : 58 : smgrcreate(srel, INIT_FORKNUM, false);
1259 rhaas@postgresql.org 341 : 58 : log_smgrcreate(&rel->rd_locator, INIT_FORKNUM);
1349 peter@eisentraut.org 342 : 58 : fill_seq_fork_with_data(rel, tuple, INIT_FORKNUM);
343 : 58 : FlushRelationBuffers(rel);
344 : 58 : smgrclose(srel);
345 : : }
346 : 1091 : }
347 : :
348 : : /*
349 : : * Initialize a sequence's relation fork with the specified tuple as content
350 : : */
351 : : static void
352 : 1149 : 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 : :
846 tmunro@postgresql.or 361 : 1149 : buf = ExtendBufferedRel(BMR_REL(rel), forkNum, NULL,
362 : : EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK);
8936 tgl@sss.pgh.pa.us 363 [ - + ]: 1149 : Assert(BufferGetBlockNumber(buf) == 0);
364 : :
3527 kgrittn@postgresql.o 365 : 1149 : page = BufferGetPage(buf);
366 : :
6365 tgl@sss.pgh.pa.us 367 : 1149 : PageInit(page, BufferGetPageSize(buf), sizeof(sequence_magic));
10327 bruce@momjian.us 368 : 1149 : sm = (sequence_magic *) PageGetSpecialPointer(page);
369 : 1149 : 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 : : */
4256 heikki.linnakangas@i 380 : 1149 : HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId);
381 : 1149 : HeapTupleHeaderSetXminFrozen(tuple->t_data);
382 : 1149 : HeapTupleHeaderSetCmin(tuple->t_data, FirstCommandId);
383 : 1149 : HeapTupleHeaderSetXmax(tuple->t_data, InvalidTransactionId);
384 : 1149 : tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
385 : 1149 : ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
386 : :
387 : : /* check the comment above nextval_internal()'s equivalent call. */
3946 andres@anarazel.de 388 [ + + + + : 1149 : if (RelationNeedsWAL(rel))
+ + - + ]
389 : 612 : GetTopTransactionId();
390 : :
9023 vadim4o@yahoo.com 391 : 1149 : START_CRIT_SECTION();
392 : :
7200 tgl@sss.pgh.pa.us 393 : 1149 : MarkBufferDirty(buf);
394 : :
50 peter@eisentraut.org 395 :GNC 1149 : offnum = PageAddItem(page, tuple->t_data, tuple->t_len, InvalidOffsetNumber, false, false);
4256 heikki.linnakangas@i 396 [ - + ]:CBC 1149 : if (offnum != FirstOffsetNumber)
4256 heikki.linnakangas@i 397 [ # # ]:UBC 0 : elog(ERROR, "failed to add sequence tuple to page");
398 : :
399 : : /* XLOG stuff */
1349 peter@eisentraut.org 400 [ + + + + :CBC 1149 : if (RelationNeedsWAL(rel) || forkNum == INIT_FORKNUM)
+ + + - +
+ ]
401 : : {
402 : : xl_seq_rec xlrec;
403 : : XLogRecPtr recptr;
404 : :
4044 heikki.linnakangas@i 405 : 670 : XLogBeginInsert();
406 : 670 : XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
407 : :
1259 rhaas@postgresql.org 408 : 670 : xlrec.locator = rel->rd_locator;
409 : :
308 peter@eisentraut.org 410 : 670 : XLogRegisterData(&xlrec, sizeof(xl_seq_rec));
411 : 670 : XLogRegisterData(tuple->t_data, tuple->t_len);
412 : :
4044 heikki.linnakangas@i 413 : 670 : recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
414 : :
9023 vadim4o@yahoo.com 415 : 670 : PageSetLSN(page, recptr);
416 : : }
417 : :
418 [ - + ]: 1149 : END_CRIT_SECTION();
419 : :
7200 tgl@sss.pgh.pa.us 420 : 1149 : UnlockReleaseBuffer(buf);
10485 vadim4o@yahoo.com 421 : 1149 : }
422 : :
423 : : /*
424 : : * AlterSequence
425 : : *
426 : : * Modify the definition of a sequence relation
427 : : */
428 : : ObjectAddress
3388 peter_e@gmx.net 429 : 758 : 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;
120 michael@paquier.xyz 443 :GNC 758 : 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. */
3108 peter_e@gmx.net 449 :CBC 758 : relid = RangeVarGetRelidExtended(stmt->sequence,
450 : : ShareRowExclusiveLock,
2818 andres@anarazel.de 451 : 758 : stmt->missing_ok ? RVR_MISSING_OK : 0,
452 : : RangeVarCallbackOwnsRelation,
453 : : NULL);
5076 simon@2ndQuadrant.co 454 [ + + ]: 755 : if (relid == InvalidOid)
455 : : {
456 [ + - ]: 3 : ereport(NOTICE,
457 : : (errmsg("relation \"%s\" does not exist, skipping",
458 : : stmt->sequence->relname)));
3941 alvherre@alvh.no-ip. 459 : 3 : return InvalidObjectAddress;
460 : : }
461 : :
7380 tgl@sss.pgh.pa.us 462 : 752 : init_sequence(relid, &elm, &seqrel);
463 : :
2521 andres@anarazel.de 464 : 749 : rel = table_open(SequenceRelationId, RowExclusiveLock);
3121 465 : 749 : seqtuple = SearchSysCacheCopy1(SEQRELID,
466 : : ObjectIdGetDatum(relid));
467 [ - + ]: 749 : if (!HeapTupleIsValid(seqtuple))
3283 peter_e@gmx.net 468 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for sequence %u",
469 : : relid);
470 : :
3121 andres@anarazel.de 471 :CBC 749 : seqform = (Form_pg_sequence) GETSTRUCT(seqtuple);
472 : :
473 : : /* lock page buffer and read tuple into new sequence structure */
474 : 749 : (void) read_seq_tuple(seqrel, &buf, &datatuple);
475 : :
476 : : /* copy the existing sequence data tuple, so it can be modified locally */
477 : 749 : newdatatuple = heap_copytuple(&datatuple);
478 : 749 : newdataform = (Form_pg_sequence_data) GETSTRUCT(newdatatuple);
120 michael@paquier.xyz 479 :GNC 749 : last_value = newdataform->last_value;
480 : 749 : is_called = newdataform->is_called;
481 : :
3121 andres@anarazel.de 482 :CBC 749 : UnlockReleaseBuffer(buf);
483 : :
484 : : /* Check and set new values */
3109 tgl@sss.pgh.pa.us 485 : 749 : 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 [ + + ]: 734 : if (need_seq_rewrite)
491 : : {
492 : : /* check the comment above nextval_internal()'s equivalent call. */
493 [ + + + + : 89 : if (RelationNeedsWAL(seqrel))
+ + + - ]
494 : 87 : GetTopTransactionId();
495 : :
496 : : /*
497 : : * Create a new storage file for the sequence, making the state
498 : : * changes transactional.
499 : : */
1259 rhaas@postgresql.org 500 : 89 : 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 : : */
2455 andres@anarazel.de 507 [ - + ]: 89 : Assert(seqrel->rd_rel->relfrozenxid == InvalidTransactionId);
508 [ - + ]: 89 : Assert(seqrel->rd_rel->relminmxid == InvalidMultiXactId);
509 : :
510 : : /*
511 : : * Insert the modified tuple into the new storage file.
512 : : */
120 michael@paquier.xyz 513 :GNC 89 : newdataform->last_value = last_value;
514 : 89 : newdataform->is_called = is_called;
515 [ + + ]: 89 : if (reset_state)
516 : 88 : newdataform->log_cnt = 0;
3109 tgl@sss.pgh.pa.us 517 :CBC 89 : 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 */
659 michael@paquier.xyz 522 : 734 : elm->cached = elm->last;
523 : :
524 : : /* process OWNED BY if given */
7057 tgl@sss.pgh.pa.us 525 [ + + ]: 734 : if (owned_by)
3176 peter_e@gmx.net 526 : 638 : process_owned_by(seqrel, owned_by, stmt->for_identity);
527 : :
528 : : /* update the pg_sequence tuple (we could skip this in some cases...) */
3121 andres@anarazel.de 529 : 731 : CatalogTupleUpdate(rel, &seqtuple->t_self, seqtuple);
530 : :
4657 rhaas@postgresql.org 531 [ - + ]: 731 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
532 : :
3941 alvherre@alvh.no-ip. 533 : 731 : ObjectAddressSet(address, RelationRelationId, relid);
534 : :
2521 andres@anarazel.de 535 : 731 : table_close(rel, RowExclusiveLock);
659 michael@paquier.xyz 536 : 731 : sequence_close(seqrel, NoLock);
537 : :
3941 alvherre@alvh.no-ip. 538 : 731 : return address;
539 : : }
540 : :
541 : : void
1349 peter@eisentraut.org 542 : 36 : 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 : : */
537 noah@leadboat.com 555 : 36 : LockRelationOid(relid, AccessExclusiveLock);
1349 peter@eisentraut.org 556 : 36 : init_sequence(relid, &elm, &seqrel);
557 : :
558 : : /* check the comment above nextval_internal()'s equivalent call. */
559 [ + + + + : 36 : if (RelationNeedsWAL(seqrel))
+ + + - ]
560 : 20 : GetTopTransactionId();
561 : :
562 : 36 : (void) read_seq_tuple(seqrel, &buf, &seqdatatuple);
1259 rhaas@postgresql.org 563 : 36 : RelationSetNewRelfilenumber(seqrel, newrelpersistence);
1349 peter@eisentraut.org 564 : 36 : fill_seq_with_data(seqrel, &seqdatatuple);
565 : 36 : UnlockReleaseBuffer(buf);
566 : :
659 michael@paquier.xyz 567 : 36 : sequence_close(seqrel, NoLock);
1349 peter@eisentraut.org 568 : 36 : }
569 : :
570 : : void
3283 peter_e@gmx.net 571 : 512 : DeleteSequenceTuple(Oid relid)
572 : : {
573 : : Relation rel;
574 : : HeapTuple tuple;
575 : :
2521 andres@anarazel.de 576 : 512 : rel = table_open(SequenceRelationId, RowExclusiveLock);
577 : :
3283 peter_e@gmx.net 578 : 512 : tuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(relid));
579 [ - + ]: 512 : if (!HeapTupleIsValid(tuple))
3283 peter_e@gmx.net 580 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for sequence %u", relid);
581 : :
3240 tgl@sss.pgh.pa.us 582 :CBC 512 : CatalogTupleDelete(rel, &tuple->t_self);
583 : :
3283 peter_e@gmx.net 584 : 512 : ReleaseSysCache(tuple);
2521 andres@anarazel.de 585 : 512 : table_close(rel, RowExclusiveLock);
3283 peter_e@gmx.net 586 : 512 : }
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
9319 tgl@sss.pgh.pa.us 594 : 9 : nextval(PG_FUNCTION_ARGS)
595 : : {
3201 noah@leadboat.com 596 : 9 : text *seqin = PG_GETARG_TEXT_PP(0);
597 : : RangeVar *sequence;
598 : : Oid relid;
599 : :
7380 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 : : */
5130 rhaas@postgresql.org 610 : 9 : relid = RangeVarGetRelid(sequence, NoLock, false);
611 : :
3176 peter_e@gmx.net 612 : 9 : PG_RETURN_INT64(nextval_internal(relid, true));
613 : : }
614 : :
615 : : Datum
7380 tgl@sss.pgh.pa.us 616 : 100596 : nextval_oid(PG_FUNCTION_ARGS)
617 : : {
618 : 100596 : Oid relid = PG_GETARG_OID(0);
619 : :
3176 peter_e@gmx.net 620 : 100596 : PG_RETURN_INT64(nextval_internal(relid, true));
621 : : }
622 : :
623 : : int64
624 : 101122 : 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,
10326 bruce@momjian.us 643 : 101122 : rescnt = 0;
644 : : bool cycle;
9147 vadim4o@yahoo.com 645 : 101122 : bool logit = false;
646 : :
647 : : /* open and lock sequence */
7380 tgl@sss.pgh.pa.us 648 : 101122 : init_sequence(relid, &elm, &seqrel);
649 : :
3176 peter_e@gmx.net 650 [ + + + + ]: 201727 : if (check_permissions &&
651 : 100605 : pg_class_aclcheck(elm->relid, GetUserId(),
652 : : ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
8185 tgl@sss.pgh.pa.us 653 [ + - ]: 3 : 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 */
4747 659 [ + + ]: 101119 : if (!seqrel->rd_islocaltemp)
5778 660 : 40242 : 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 : : */
3883 rhaas@postgresql.org 667 : 101116 : PreventCommandIfParallelMode("nextval()");
668 : :
3100 tgl@sss.pgh.pa.us 669 [ + + ]: 101116 : if (elm->last != elm->cached) /* some numbers were cached */
670 : : {
6627 671 [ - + ]: 6 : Assert(elm->last_valid);
672 [ - + ]: 6 : Assert(elm->increment != 0);
10327 bruce@momjian.us 673 : 6 : elm->last += elm->increment;
659 michael@paquier.xyz 674 : 6 : sequence_close(seqrel, NoLock);
6627 tgl@sss.pgh.pa.us 675 : 6 : last_used_seq = elm;
7380 676 : 6 : return elm->last;
677 : : }
678 : :
3283 peter_e@gmx.net 679 : 101110 : pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(relid));
680 [ - + ]: 101110 : if (!HeapTupleIsValid(pgstuple))
3283 peter_e@gmx.net 681 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for sequence %u", relid);
3283 peter_e@gmx.net 682 :CBC 101110 : pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
683 : 101110 : incby = pgsform->seqincrement;
684 : 101110 : maxv = pgsform->seqmax;
685 : 101110 : minv = pgsform->seqmin;
686 : 101110 : cache = pgsform->seqcache;
3231 687 : 101110 : cycle = pgsform->seqcycle;
3283 688 : 101110 : ReleaseSysCache(pgstuple);
689 : :
690 : : /* lock page buffer and read tuple */
691 : 101110 : seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
3527 kgrittn@postgresql.o 692 : 101110 : page = BufferGetPage(buf);
693 : :
9147 vadim4o@yahoo.com 694 : 101110 : last = next = result = seq->last_value;
3283 peter_e@gmx.net 695 : 101110 : fetch = cache;
9147 vadim4o@yahoo.com 696 : 101110 : log = seq->log_cnt;
697 : :
8888 tgl@sss.pgh.pa.us 698 [ + + ]: 101110 : if (!seq->is_called)
699 : : {
4892 700 : 598 : rescnt++; /* return last_value if not is_called */
9147 vadim4o@yahoo.com 701 : 598 : 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 : : */
4892 tgl@sss.pgh.pa.us 714 [ + + + + ]: 101110 : if (log < fetch || !seq->is_called)
715 : : {
716 : : /* forced log to satisfy local demand for values */
8677 717 : 1765 : fetch = log = fetch + SEQ_LOG_VALS;
9147 vadim4o@yahoo.com 718 : 1765 : logit = true;
719 : : }
720 : : else
721 : : {
8677 tgl@sss.pgh.pa.us 722 : 99345 : XLogRecPtr redoptr = GetRedoRecPtr();
723 : :
4736 alvherre@alvh.no-ip. 724 [ + + ]: 99345 : if (PageGetLSN(page) <= redoptr)
725 : : {
726 : : /* last update of seq was before checkpoint */
8677 tgl@sss.pgh.pa.us 727 : 61619 : fetch = log = fetch + SEQ_LOG_VALS;
728 : 61619 : logit = true;
729 : : }
730 : : }
731 : :
9035 bruce@momjian.us 732 [ + + ]: 2229103 : while (fetch) /* try to fetch cache [+ log ] numbers */
733 : : {
734 : : /*
735 : : * Check MAXVALUE for ascending sequences and MINVALUE for descending
736 : : * sequences
737 : : */
9319 tgl@sss.pgh.pa.us 738 [ + + ]: 2128022 : if (incby > 0)
739 : : {
740 : : /* ascending sequence */
10327 bruce@momjian.us 741 [ + - + + : 2127725 : if ((maxv >= 0 && next > maxv - incby) ||
- + ]
10327 bruce@momjian.us 742 [ # # ]:UBC 0 : (maxv < 0 && next + incby > maxv))
743 : : {
10327 bruce@momjian.us 744 [ + + ]:CBC 20 : if (rescnt > 0)
9147 vadim4o@yahoo.com 745 : 12 : break; /* stop fetching */
3283 peter_e@gmx.net 746 [ + + ]: 8 : if (!cycle)
8185 tgl@sss.pgh.pa.us 747 [ + - ]: 5 : ereport(ERROR,
748 : : (errcode(ERRCODE_SEQUENCE_GENERATOR_LIMIT_EXCEEDED),
749 : : errmsg("nextval: reached maximum value of sequence \"%s\" (%" PRId64 ")",
750 : : RelationGetRelationName(seqrel),
751 : : maxv)));
10327 bruce@momjian.us 752 : 3 : next = minv;
753 : : }
754 : : else
755 : 2127705 : next += incby;
756 : : }
757 : : else
758 : : {
759 : : /* descending sequence */
760 [ + - + + : 297 : if ((minv < 0 && next < minv - incby) ||
- + ]
10327 bruce@momjian.us 761 [ # # ]:UBC 0 : (minv >= 0 && next + incby < minv))
762 : : {
10327 bruce@momjian.us 763 [ + + ]:CBC 15 : if (rescnt > 0)
9147 vadim4o@yahoo.com 764 : 9 : break; /* stop fetching */
3283 peter_e@gmx.net 765 [ + + ]: 6 : if (!cycle)
8185 tgl@sss.pgh.pa.us 766 [ + - ]: 3 : ereport(ERROR,
767 : : (errcode(ERRCODE_SEQUENCE_GENERATOR_LIMIT_EXCEEDED),
768 : : errmsg("nextval: reached minimum value of sequence \"%s\" (%" PRId64 ")",
769 : : RelationGetRelationName(seqrel),
770 : : minv)));
10327 bruce@momjian.us 771 : 3 : next = maxv;
772 : : }
773 : : else
774 : 282 : next += incby;
775 : : }
9147 vadim4o@yahoo.com 776 : 2127993 : fetch--;
777 [ + + ]: 2127993 : if (rescnt < cache)
778 : : {
779 : 100531 : log--;
780 : 100531 : rescnt++;
781 : 100531 : last = next;
9035 bruce@momjian.us 782 [ + + ]: 100531 : if (rescnt == 1) /* if it's first result - */
783 : 100504 : result = next; /* it's what to return */
784 : : }
785 : : }
786 : :
8677 tgl@sss.pgh.pa.us 787 : 101102 : log -= fetch; /* adjust for any unfetched numbers */
788 [ - + ]: 101102 : Assert(log >= 0);
789 : :
790 : : /* save info in local cache */
659 michael@paquier.xyz 791 : 101102 : elm->increment = incby;
10327 bruce@momjian.us 792 : 101102 : elm->last = result; /* last returned number */
9147 vadim4o@yahoo.com 793 : 101102 : elm->cached = last; /* last fetched number */
6627 tgl@sss.pgh.pa.us 794 : 101102 : elm->last_valid = true;
795 : :
7497 neilc@samurai.com 796 : 101102 : 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 : : */
3946 andres@anarazel.de 805 [ + + + + : 101102 : if (logit && RelationNeedsWAL(seqrel))
+ + + + +
+ ]
806 : 1635 : GetTopTransactionId();
807 : :
808 : : /* ready to change the on-disk (or really, in-buffer) tuple */
9104 tgl@sss.pgh.pa.us 809 : 101102 : 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 : : */
7200 820 : 101102 : MarkBufferDirty(buf);
821 : :
822 : : /* XLOG stuff */
5482 rhaas@postgresql.org 823 [ + + + + : 101102 : 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 : : */
4044 heikki.linnakangas@i 834 : 1635 : XLogBeginInsert();
835 : 1635 : XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
836 : :
837 : : /* set values that will be saved in xlog */
9119 vadim4o@yahoo.com 838 : 1635 : seq->last_value = next;
8888 tgl@sss.pgh.pa.us 839 : 1635 : seq->is_called = true;
9119 vadim4o@yahoo.com 840 : 1635 : seq->log_cnt = 0;
841 : :
1259 rhaas@postgresql.org 842 : 1635 : xlrec.locator = seqrel->rd_locator;
843 : :
308 peter@eisentraut.org 844 : 1635 : XLogRegisterData(&xlrec, sizeof(xl_seq_rec));
845 : 1635 : XLogRegisterData(seqdatatuple.t_data, seqdatatuple.t_len);
846 : :
4044 heikki.linnakangas@i 847 : 1635 : recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
848 : :
9119 vadim4o@yahoo.com 849 : 1635 : PageSetLSN(page, recptr);
850 : : }
851 : :
852 : : /* Now update sequence tuple to the intended final state */
9147 853 : 101102 : seq->last_value = last; /* last fetched number */
8888 tgl@sss.pgh.pa.us 854 : 101102 : seq->is_called = true;
9147 vadim4o@yahoo.com 855 : 101102 : seq->log_cnt = log; /* how much is logged */
856 : :
9104 tgl@sss.pgh.pa.us 857 [ - + ]: 101102 : END_CRIT_SECTION();
858 : :
7200 859 : 101102 : UnlockReleaseBuffer(buf);
860 : :
659 michael@paquier.xyz 861 : 101102 : sequence_close(seqrel, NoLock);
862 : :
7380 tgl@sss.pgh.pa.us 863 : 101102 : return result;
864 : : }
865 : :
866 : : Datum
867 : 58 : currval_oid(PG_FUNCTION_ARGS)
868 : : {
869 : 58 : Oid relid = PG_GETARG_OID(0);
870 : : int64 result;
871 : : SeqTable elm;
872 : : Relation seqrel;
873 : :
874 : : /* open and lock sequence */
875 : 58 : init_sequence(relid, &elm, &seqrel);
876 : :
4073 peter_e@gmx.net 877 [ + + ]: 58 : if (pg_class_aclcheck(elm->relid, GetUserId(),
878 : : ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
8185 tgl@sss.pgh.pa.us 879 [ + - ]: 3 : ereport(ERROR,
880 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
881 : : errmsg("permission denied for sequence %s",
882 : : RelationGetRelationName(seqrel))));
883 : :
6627 884 [ + + ]: 55 : if (!elm->last_valid)
8185 885 [ + - ]: 3 : 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 : :
10327 bruce@momjian.us 890 : 52 : result = elm->last;
891 : :
659 michael@paquier.xyz 892 : 52 : sequence_close(seqrel, NoLock);
893 : :
8888 tgl@sss.pgh.pa.us 894 : 52 : PG_RETURN_INT64(result);
895 : : }
896 : :
897 : : Datum
7497 neilc@samurai.com 898 : 24 : lastval(PG_FUNCTION_ARGS)
899 : : {
900 : : Relation seqrel;
901 : : int64 result;
902 : :
903 [ + + ]: 24 : if (last_used_seq == NULL)
904 [ + - ]: 3 : 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() */
5784 rhaas@postgresql.org 909 [ + + ]: 21 : if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(last_used_seq->relid)))
7497 neilc@samurai.com 910 [ + - ]: 3 : ereport(ERROR,
911 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
912 : : errmsg("lastval is not yet defined in this session")));
913 : :
3143 peter_e@gmx.net 914 : 18 : seqrel = lock_and_open_sequence(last_used_seq);
915 : :
916 : : /* nextval() must have already been called for this sequence */
6627 tgl@sss.pgh.pa.us 917 [ - + ]: 18 : Assert(last_used_seq->last_valid);
918 : :
4073 peter_e@gmx.net 919 [ + + ]: 18 : if (pg_class_aclcheck(last_used_seq->relid, GetUserId(),
920 : : ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
7497 neilc@samurai.com 921 [ + - ]: 3 : ereport(ERROR,
922 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
923 : : errmsg("permission denied for sequence %s",
924 : : RelationGetRelationName(seqrel))));
925 : :
926 : 15 : result = last_used_seq->last;
659 michael@paquier.xyz 927 : 15 : sequence_close(seqrel, NoLock);
928 : :
7497 neilc@samurai.com 929 : 15 : 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
41 akapila@postgresql.o 946 :GNC 283 : 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 */
7380 tgl@sss.pgh.pa.us 959 :CBC 283 : init_sequence(relid, &elm, &seqrel);
960 : :
8671 961 [ + + ]: 283 : if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
8185 962 [ + - ]: 3 : ereport(ERROR,
963 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
964 : : errmsg("permission denied for sequence %s",
965 : : RelationGetRelationName(seqrel))));
966 : :
3283 peter_e@gmx.net 967 : 280 : pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(relid));
968 [ - + ]: 280 : if (!HeapTupleIsValid(pgstuple))
3283 peter_e@gmx.net 969 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for sequence %u", relid);
3283 peter_e@gmx.net 970 :CBC 280 : pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
971 : 280 : maxv = pgsform->seqmax;
972 : 280 : minv = pgsform->seqmin;
973 : 280 : ReleaseSysCache(pgstuple);
974 : :
975 : : /* read-only transactions may only modify temp sequences */
4747 tgl@sss.pgh.pa.us 976 [ + + ]: 280 : if (!seqrel->rd_islocaltemp)
5778 977 : 151 : 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 : : */
3883 rhaas@postgresql.org 984 : 277 : PreventCommandIfParallelMode("setval()");
985 : :
986 : : /* lock page buffer and read tuple */
3283 peter_e@gmx.net 987 : 277 : seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
988 : :
989 [ + + + + ]: 277 : if ((next < minv) || (next > maxv))
8185 tgl@sss.pgh.pa.us 990 [ + - ]: 6 : 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 */
6627 997 [ + + ]: 271 : if (iscalled)
998 : : {
999 : 105 : elm->last = next; /* last returned number */
1000 : 105 : elm->last_valid = true;
1001 : : }
1002 : :
1003 : : /* In any case, forget any future cached numbers */
1004 : 271 : elm->cached = elm->last;
1005 : :
1006 : : /* check the comment above nextval_internal()'s equivalent call. */
3946 andres@anarazel.de 1007 [ + + + + : 271 : if (RelationNeedsWAL(seqrel))
+ + + - ]
1008 : 128 : GetTopTransactionId();
1009 : :
1010 : : /* ready to change the on-disk (or really, in-buffer) tuple */
9104 tgl@sss.pgh.pa.us 1011 : 271 : START_CRIT_SECTION();
1012 : :
4892 1013 : 271 : seq->last_value = next; /* last fetched number */
1014 : 271 : seq->is_called = iscalled;
1015 : 271 : seq->log_cnt = 0;
1016 : :
7200 1017 : 271 : MarkBufferDirty(buf);
1018 : :
1019 : : /* XLOG stuff */
5482 rhaas@postgresql.org 1020 [ + + + + : 271 : if (RelationNeedsWAL(seqrel))
+ + + - ]
1021 : : {
1022 : : xl_seq_rec xlrec;
1023 : : XLogRecPtr recptr;
3527 kgrittn@postgresql.o 1024 : 128 : Page page = BufferGetPage(buf);
1025 : :
4044 heikki.linnakangas@i 1026 : 128 : XLogBeginInsert();
1027 : 128 : XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
1028 : :
1259 rhaas@postgresql.org 1029 : 128 : xlrec.locator = seqrel->rd_locator;
308 peter@eisentraut.org 1030 : 128 : XLogRegisterData(&xlrec, sizeof(xl_seq_rec));
1031 : 128 : XLogRegisterData(seqdatatuple.t_data, seqdatatuple.t_len);
1032 : :
4044 heikki.linnakangas@i 1033 : 128 : recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
1034 : :
9119 vadim4o@yahoo.com 1035 : 128 : PageSetLSN(page, recptr);
1036 : : }
1037 : :
9104 tgl@sss.pgh.pa.us 1038 [ - + ]: 271 : END_CRIT_SECTION();
1039 : :
7200 1040 : 271 : UnlockReleaseBuffer(buf);
1041 : :
659 michael@paquier.xyz 1042 : 271 : sequence_close(seqrel, NoLock);
9197 pjw@rhyme.com.au 1043 : 271 : }
1044 : :
1045 : : /*
1046 : : * Implement the 2 arg setval procedure.
1047 : : * See SetSequence for discussion.
1048 : : */
1049 : : Datum
7380 tgl@sss.pgh.pa.us 1050 : 85 : setval_oid(PG_FUNCTION_ARGS)
1051 : : {
1052 : 85 : Oid relid = PG_GETARG_OID(0);
8888 1053 : 85 : int64 next = PG_GETARG_INT64(1);
1054 : :
41 akapila@postgresql.o 1055 :GNC 85 : SetSequence(relid, next, true);
1056 : :
8888 tgl@sss.pgh.pa.us 1057 :CBC 73 : PG_RETURN_INT64(next);
1058 : : }
1059 : :
1060 : : /*
1061 : : * Implement the 3 arg setval procedure.
1062 : : * See SetSequence for discussion.
1063 : : */
1064 : : Datum
7380 1065 : 189 : setval3_oid(PG_FUNCTION_ARGS)
1066 : : {
1067 : 189 : Oid relid = PG_GETARG_OID(0);
8888 1068 : 189 : int64 next = PG_GETARG_INT64(1);
9197 pjw@rhyme.com.au 1069 : 189 : bool iscalled = PG_GETARG_BOOL(2);
1070 : :
41 akapila@postgresql.o 1071 :GNC 189 : SetSequence(relid, next, iscalled);
1072 : :
8662 tgl@sss.pgh.pa.us 1073 :CBC 189 : 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
3143 peter_e@gmx.net 1086 : 102983 : lock_and_open_sequence(SeqTable seq)
1087 : : {
653 heikki.linnakangas@i 1088 : 102983 : LocalTransactionId thislxid = MyProc->vxid.lxid;
1089 : :
1090 : : /* Get the lock if not already held in this xact */
6677 tgl@sss.pgh.pa.us 1091 [ + + ]: 102983 : if (seq->lxid != thislxid)
1092 : : {
1093 : : ResourceOwner currentOwner;
1094 : :
7497 neilc@samurai.com 1095 : 3172 : currentOwner = CurrentResourceOwner;
2988 tgl@sss.pgh.pa.us 1096 : 3172 : CurrentResourceOwner = TopTransactionResourceOwner;
1097 : :
1098 : 3172 : LockRelationOid(seq->relid, RowExclusiveLock);
1099 : :
7497 neilc@samurai.com 1100 : 3172 : CurrentResourceOwner = currentOwner;
1101 : :
1102 : : /* Flag that we have a lock in the current xact */
6677 tgl@sss.pgh.pa.us 1103 : 3172 : seq->lxid = thislxid;
1104 : : }
1105 : :
1106 : : /* We now know we have the lock, and can safely open the rel */
659 michael@paquier.xyz 1107 : 102983 : return sequence_open(seq->relid, NoLock);
1108 : : }
1109 : :
1110 : : /*
1111 : : * Creates the hash table for storing sequence data
1112 : : */
1113 : : static void
4414 heikki.linnakangas@i 1114 : 429 : create_seq_hashtable(void)
1115 : : {
1116 : : HASHCTL ctl;
1117 : :
1118 : 429 : ctl.keysize = sizeof(Oid);
1119 : 429 : ctl.entrysize = sizeof(SeqTableData);
1120 : :
1121 : 429 : seqhashtab = hash_create("Sequence values", 16, &ctl,
1122 : : HASH_ELEM | HASH_BLOBS);
1123 : 429 : }
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
7380 tgl@sss.pgh.pa.us 1130 : 102965 : 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 */
4414 heikki.linnakangas@i 1137 [ + + ]: 102965 : if (seqhashtab == NULL)
1138 : 429 : create_seq_hashtable();
1139 : :
1140 : 102965 : 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 [ + + ]: 102965 : if (!found)
1151 : : {
1152 : : /* relid already filled in */
1259 rhaas@postgresql.org 1153 : 1691 : elm->filenumber = InvalidRelFileNumber;
6677 tgl@sss.pgh.pa.us 1154 : 1691 : elm->lxid = InvalidLocalTransactionId;
6627 1155 : 1691 : elm->last_valid = false;
3283 peter_e@gmx.net 1156 : 1691 : elm->last = elm->cached = 0;
1157 : : }
1158 : :
1159 : : /*
1160 : : * Open the sequence relation.
1161 : : */
3143 1162 : 102965 : 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 : : */
1259 rhaas@postgresql.org 1169 [ + + ]: 102962 : if (seqrel->rd_rel->relfilenode != elm->filenumber)
1170 : : {
1171 : 1754 : elm->filenumber = seqrel->rd_rel->relfilenode;
5508 tgl@sss.pgh.pa.us 1172 : 1754 : elm->cached = elm->last;
1173 : : }
1174 : :
1175 : : /* Return results */
8609 1176 : 102962 : *p_elm = elm;
1177 : 102962 : *p_rel = seqrel;
10485 vadim4o@yahoo.com 1178 : 102962 : }
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
3283 peter_e@gmx.net 1191 : 102865 : 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 : :
8609 tgl@sss.pgh.pa.us 1198 : 102865 : *buf = ReadBuffer(rel, 0);
1199 : 102865 : LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
1200 : :
3527 kgrittn@postgresql.o 1201 : 102865 : page = BufferGetPage(*buf);
8609 tgl@sss.pgh.pa.us 1202 : 102865 : sm = (sequence_magic *) PageGetSpecialPointer(page);
1203 : :
1204 [ - + ]: 102865 : if (sm->magic != SEQ_MAGIC)
8177 tgl@sss.pgh.pa.us 1205 [ # # ]:UBC 0 : elog(ERROR, "bad magic number in sequence \"%s\": %08X",
1206 : : RelationGetRelationName(rel), sm->magic);
1207 : :
8609 tgl@sss.pgh.pa.us 1208 :CBC 102865 : lp = PageGetItemId(page, FirstOffsetNumber);
6670 1209 [ - + ]: 102865 : Assert(ItemIdIsNormal(lp));
1210 : :
1211 : : /* Note we currently only bother to set these two fields of *seqdatatuple */
3283 peter_e@gmx.net 1212 : 102865 : seqdatatuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
1213 : 102865 : 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 [ - + ]: 102865 : Assert(!(seqdatatuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
1224 [ - + ]: 102865 : if (HeapTupleHeaderGetRawXmax(seqdatatuple->t_data) != InvalidTransactionId)
1225 : : {
3283 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;
4565 jdavis@postgresql.or 1229 : 0 : MarkBufferDirtyHint(*buf, true);
1230 : : }
1231 : :
3283 peter_e@gmx.net 1232 :CBC 102865 : seq = (Form_pg_sequence_data) GETSTRUCT(seqdatatuple);
1233 : :
8609 tgl@sss.pgh.pa.us 1234 : 102865 : 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
3176 peter_e@gmx.net 1260 : 1733 : 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 : : {
3231 1269 : 1733 : DefElem *as_type = NULL;
6422 tgl@sss.pgh.pa.us 1270 : 1733 : DefElem *start_value = NULL;
1271 : 1733 : DefElem *restart_value = NULL;
10326 bruce@momjian.us 1272 : 1733 : DefElem *increment_by = NULL;
1273 : 1733 : DefElem *max_value = NULL;
1274 : 1733 : DefElem *min_value = NULL;
1275 : 1733 : DefElem *cache_value = NULL;
8058 tgl@sss.pgh.pa.us 1276 : 1733 : DefElem *is_cycled = NULL;
1277 : : ListCell *option;
3178 peter_e@gmx.net 1278 : 1733 : bool reset_max_value = false;
1279 : 1733 : bool reset_min_value = false;
1280 : :
3109 tgl@sss.pgh.pa.us 1281 : 1733 : *need_seq_rewrite = false;
7057 1282 : 1733 : *owned_by = NIL;
1283 : :
8307 bruce@momjian.us 1284 [ + + + + : 3704 : foreach(option, options)
+ + ]
1285 : : {
10326 1286 : 1971 : DefElem *defel = (DefElem *) lfirst(option);
1287 : :
3231 peter_e@gmx.net 1288 [ + + ]: 1971 : if (strcmp(defel->defname, "as") == 0)
1289 : : {
1290 [ - + ]: 749 : if (as_type)
1615 dean.a.rasheed@gmail 1291 :UBC 0 : errorConflictingDefElem(defel, pstate);
3231 peter_e@gmx.net 1292 :CBC 749 : as_type = defel;
3109 tgl@sss.pgh.pa.us 1293 : 749 : *need_seq_rewrite = true;
1294 : : }
3231 peter_e@gmx.net 1295 [ + + ]: 1222 : else if (strcmp(defel->defname, "increment") == 0)
1296 : : {
8342 bruce@momjian.us 1297 [ - + ]: 129 : if (increment_by)
1615 dean.a.rasheed@gmail 1298 :UBC 0 : errorConflictingDefElem(defel, pstate);
10327 bruce@momjian.us 1299 :CBC 129 : increment_by = defel;
3109 tgl@sss.pgh.pa.us 1300 : 129 : *need_seq_rewrite = true;
1301 : : }
6423 1302 [ + + ]: 1093 : else if (strcmp(defel->defname, "start") == 0)
1303 : : {
6422 1304 [ - + ]: 123 : if (start_value)
1615 dean.a.rasheed@gmail 1305 :UBC 0 : errorConflictingDefElem(defel, pstate);
6422 tgl@sss.pgh.pa.us 1306 :CBC 123 : start_value = defel;
3109 1307 : 123 : *need_seq_rewrite = true;
1308 : : }
6423 1309 [ + + ]: 970 : else if (strcmp(defel->defname, "restart") == 0)
1310 : : {
6422 1311 [ - + ]: 42 : if (restart_value)
1615 dean.a.rasheed@gmail 1312 :UBC 0 : errorConflictingDefElem(defel, pstate);
6422 tgl@sss.pgh.pa.us 1313 :CBC 42 : restart_value = defel;
3109 1314 : 42 : *need_seq_rewrite = true;
1315 : : }
8818 bruce@momjian.us 1316 [ + + ]: 928 : else if (strcmp(defel->defname, "maxvalue") == 0)
1317 : : {
8342 1318 [ - + ]: 91 : if (max_value)
1615 dean.a.rasheed@gmail 1319 :UBC 0 : errorConflictingDefElem(defel, pstate);
10327 bruce@momjian.us 1320 :CBC 91 : max_value = defel;
3109 tgl@sss.pgh.pa.us 1321 : 91 : *need_seq_rewrite = true;
1322 : : }
8818 bruce@momjian.us 1323 [ + + ]: 837 : else if (strcmp(defel->defname, "minvalue") == 0)
1324 : : {
8342 1325 [ - + ]: 91 : if (min_value)
1615 dean.a.rasheed@gmail 1326 :UBC 0 : errorConflictingDefElem(defel, pstate);
10327 bruce@momjian.us 1327 :CBC 91 : min_value = defel;
3109 tgl@sss.pgh.pa.us 1328 : 91 : *need_seq_rewrite = true;
1329 : : }
8818 bruce@momjian.us 1330 [ + + ]: 746 : else if (strcmp(defel->defname, "cache") == 0)
1331 : : {
8342 1332 [ - + ]: 71 : if (cache_value)
1615 dean.a.rasheed@gmail 1333 :UBC 0 : errorConflictingDefElem(defel, pstate);
10327 bruce@momjian.us 1334 :CBC 71 : cache_value = defel;
3109 tgl@sss.pgh.pa.us 1335 : 71 : *need_seq_rewrite = true;
1336 : : }
8818 bruce@momjian.us 1337 [ + + ]: 675 : else if (strcmp(defel->defname, "cycle") == 0)
1338 : : {
8058 tgl@sss.pgh.pa.us 1339 [ - + ]: 24 : if (is_cycled)
1615 dean.a.rasheed@gmail 1340 :UBC 0 : errorConflictingDefElem(defel, pstate);
8058 tgl@sss.pgh.pa.us 1341 :CBC 24 : is_cycled = defel;
3109 1342 : 24 : *need_seq_rewrite = true;
1343 : : }
7057 1344 [ + - ]: 651 : else if (strcmp(defel->defname, "owned_by") == 0)
1345 : : {
1346 [ - + ]: 651 : if (*owned_by)
1615 dean.a.rasheed@gmail 1347 :UBC 0 : errorConflictingDefElem(defel, pstate);
7057 tgl@sss.pgh.pa.us 1348 :CBC 651 : *owned_by = defGetQualifiedName(defel);
1349 : : }
3176 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
8185 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 : : */
4892 tgl@sss.pgh.pa.us 1374 [ + + ]:CBC 1733 : if (isInit)
120 michael@paquier.xyz 1375 :GNC 984 : *reset_state = true;
1376 : :
1377 : : /* AS type */
3231 peter_e@gmx.net 1378 [ + + ]:CBC 1733 : if (as_type != NULL)
1379 : : {
3135 bruce@momjian.us 1380 : 749 : Oid newtypid = typenameTypeId(pstate, defGetTypeName(as_type));
1381 : :
3178 peter_e@gmx.net 1382 [ + + + + ]: 746 : if (newtypid != INT2OID &&
1383 [ + + ]: 68 : newtypid != INT4OID &&
1384 : : newtypid != INT8OID)
3231 1385 [ + - + + ]: 12 : 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 : :
3178 1391 [ + + ]: 734 : 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 [ + + + + ]: 45 : if ((seqform->seqtypid == INT2OID && seqform->seqmax == PG_INT16_MAX) ||
1400 [ + + + + ]: 33 : (seqform->seqtypid == INT4OID && seqform->seqmax == PG_INT32_MAX) ||
3100 tgl@sss.pgh.pa.us 1401 [ + + + - ]: 18 : (seqform->seqtypid == INT8OID && seqform->seqmax == PG_INT64_MAX))
3178 peter_e@gmx.net 1402 : 33 : reset_max_value = true;
1403 [ + + + + ]: 45 : if ((seqform->seqtypid == INT2OID && seqform->seqmin == PG_INT16_MIN) ||
1404 [ + + + + ]: 36 : (seqform->seqtypid == INT4OID && seqform->seqmin == PG_INT32_MIN) ||
3100 tgl@sss.pgh.pa.us 1405 [ + + - + ]: 33 : (seqform->seqtypid == INT8OID && seqform->seqmin == PG_INT64_MIN))
3178 peter_e@gmx.net 1406 : 12 : reset_min_value = true;
1407 : : }
1408 : :
1409 : 734 : seqform->seqtypid = newtypid;
1410 : : }
3231 1411 [ + + ]: 984 : else if (isInit)
1412 : : {
1413 : 283 : seqform->seqtypid = INT8OID;
1414 : : }
1415 : :
1416 : : /* INCREMENT BY */
8014 neilc@samurai.com 1417 [ + + ]: 1718 : if (increment_by != NULL)
1418 : : {
3283 peter_e@gmx.net 1419 : 129 : seqform->seqincrement = defGetInt64(increment_by);
1420 [ + + ]: 129 : if (seqform->seqincrement == 0)
8185 tgl@sss.pgh.pa.us 1421 [ + - ]: 3 : ereport(ERROR,
1422 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1423 : : errmsg("INCREMENT must not be zero")));
120 michael@paquier.xyz 1424 :GNC 126 : *reset_state = true;
1425 : : }
8058 tgl@sss.pgh.pa.us 1426 [ + + ]:CBC 1589 : else if (isInit)
1427 : : {
3283 peter_e@gmx.net 1428 : 870 : seqform->seqincrement = 1;
1429 : : }
1430 : :
1431 : : /* CYCLE */
8014 neilc@samurai.com 1432 [ + + ]: 1715 : if (is_cycled != NULL)
1433 : : {
1432 peter@eisentraut.org 1434 : 24 : seqform->seqcycle = boolVal(is_cycled->arg);
3283 peter_e@gmx.net 1435 [ + + - + ]: 24 : Assert(BoolIsValid(seqform->seqcycle));
120 michael@paquier.xyz 1436 :GNC 24 : *reset_state = true;
1437 : : }
8058 tgl@sss.pgh.pa.us 1438 [ + + ]:CBC 1691 : else if (isInit)
1439 : : {
3283 peter_e@gmx.net 1440 : 961 : seqform->seqcycle = false;
1441 : : }
1442 : :
1443 : : /* MAXVALUE (null arg means NO MAXVALUE) */
8014 neilc@samurai.com 1444 [ + + + + ]: 1715 : if (max_value != NULL && max_value->arg)
1445 : : {
3283 peter_e@gmx.net 1446 : 37 : seqform->seqmax = defGetInt64(max_value);
120 michael@paquier.xyz 1447 :GNC 37 : *reset_state = true;
1448 : : }
3178 peter_e@gmx.net 1449 [ + + + - :CBC 1678 : else if (isInit || max_value != NULL || reset_max_value)
+ + ]
1450 : : {
1451 [ + + + + ]: 980 : if (seqform->seqincrement > 0 || reset_max_value)
1452 : : {
1453 : : /* ascending seq */
3231 1454 [ + + ]: 962 : if (seqform->seqtypid == INT2OID)
1455 : 37 : seqform->seqmax = PG_INT16_MAX;
1456 [ + + ]: 925 : else if (seqform->seqtypid == INT4OID)
1457 : 610 : seqform->seqmax = PG_INT32_MAX;
1458 : : else
1459 : 315 : seqform->seqmax = PG_INT64_MAX;
1460 : : }
1461 : : else
3100 tgl@sss.pgh.pa.us 1462 : 18 : seqform->seqmax = -1; /* descending seq */
120 michael@paquier.xyz 1463 :GNC 980 : *reset_state = true;
1464 : : }
1465 : :
1466 : : /* Validate maximum value. No need to check INT8 as seqmax is an int64 */
3231 peter_e@gmx.net 1467 [ + + + - :CBC 1715 : if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX))
+ + ]
1617 drowley@postgresql.o 1468 [ + + + - : 1709 : || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)))
- + ]
3231 peter_e@gmx.net 1469 [ + - ]: 6 : 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) */
8014 neilc@samurai.com 1476 [ + + + + ]: 1709 : if (min_value != NULL && min_value->arg)
1477 : : {
3283 peter_e@gmx.net 1478 : 37 : seqform->seqmin = defGetInt64(min_value);
120 michael@paquier.xyz 1479 :GNC 37 : *reset_state = true;
1480 : : }
3178 peter_e@gmx.net 1481 [ + + + - :CBC 1672 : else if (isInit || min_value != NULL || reset_min_value)
+ + ]
1482 : : {
1483 [ + + + + ]: 954 : if (seqform->seqincrement < 0 || reset_min_value)
1484 : : {
1485 : : /* descending seq */
3231 1486 [ + + ]: 31 : if (seqform->seqtypid == INT2OID)
1487 : 10 : seqform->seqmin = PG_INT16_MIN;
1488 [ + + ]: 21 : else if (seqform->seqtypid == INT4OID)
1489 : 14 : seqform->seqmin = PG_INT32_MIN;
1490 : : else
1491 : 7 : seqform->seqmin = PG_INT64_MIN;
1492 : : }
1493 : : else
3135 bruce@momjian.us 1494 : 923 : seqform->seqmin = 1; /* ascending seq */
120 michael@paquier.xyz 1495 :GNC 954 : *reset_state = true;
1496 : : }
1497 : :
1498 : : /* Validate minimum value. No need to check INT8 as seqmin is an int64 */
3231 peter_e@gmx.net 1499 [ + + + + :CBC 1709 : if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX))
+ - ]
1617 drowley@postgresql.o 1500 [ + + + - : 1703 : || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)))
- + ]
3231 peter_e@gmx.net 1501 [ + - ]: 6 : 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 */
3283 1508 [ + + ]: 1703 : if (seqform->seqmin >= seqform->seqmax)
8185 tgl@sss.pgh.pa.us 1509 [ + - ]: 6 : 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 */
6422 1516 [ + + ]: 1697 : if (start_value != NULL)
1517 : : {
3283 peter_e@gmx.net 1518 : 123 : seqform->seqstart = defGetInt64(start_value);
1519 : : }
8058 tgl@sss.pgh.pa.us 1520 [ + + ]: 1574 : else if (isInit)
1521 : : {
3283 peter_e@gmx.net 1522 [ + + ]: 851 : if (seqform->seqincrement > 0)
3100 tgl@sss.pgh.pa.us 1523 : 839 : seqform->seqstart = seqform->seqmin; /* ascending seq */
1524 : : else
1525 : 12 : seqform->seqstart = seqform->seqmax; /* descending seq */
1526 : : }
1527 : :
1528 : : /* crosscheck START */
3283 peter_e@gmx.net 1529 [ + + ]: 1697 : if (seqform->seqstart < seqform->seqmin)
6423 tgl@sss.pgh.pa.us 1530 [ + - ]: 3 : 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)));
3283 peter_e@gmx.net 1535 [ + + ]: 1694 : if (seqform->seqstart > seqform->seqmax)
6423 tgl@sss.pgh.pa.us 1536 [ + - ]: 3 : 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] */
6422 1543 [ + + ]: 1691 : if (restart_value != NULL)
1544 : : {
1545 [ + + ]: 42 : if (restart_value->arg != NULL)
120 michael@paquier.xyz 1546 :GNC 27 : *last_value = defGetInt64(restart_value);
1547 : : else
1548 : 15 : *last_value = seqform->seqstart;
1549 : 42 : *is_called = false;
1550 : 42 : *reset_state = true;
1551 : : }
6422 tgl@sss.pgh.pa.us 1552 [ + + ]:CBC 1649 : else if (isInit)
1553 : : {
120 michael@paquier.xyz 1554 :GNC 951 : *last_value = seqform->seqstart;
1555 : 951 : *is_called = false;
1556 : : }
1557 : :
1558 : : /* crosscheck RESTART (or current value, if changing MIN/MAX) */
1559 [ + + ]: 1691 : if (*last_value < seqform->seqmin)
8185 tgl@sss.pgh.pa.us 1560 [ + - ]:CBC 3 : 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)));
120 michael@paquier.xyz 1565 [ + + ]:GNC 1688 : if (*last_value > seqform->seqmax)
8185 tgl@sss.pgh.pa.us 1566 [ + - ]:CBC 3 : 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 */
8014 neilc@samurai.com 1573 [ + + ]: 1685 : if (cache_value != NULL)
1574 : : {
3283 peter_e@gmx.net 1575 : 71 : seqform->seqcache = defGetInt64(cache_value);
1576 [ + + ]: 71 : if (seqform->seqcache <= 0)
8058 tgl@sss.pgh.pa.us 1577 [ + - ]: 3 : ereport(ERROR,
1578 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1579 : : errmsg("CACHE (%" PRId64 ") must be greater than zero",
1580 : : seqform->seqcache)));
120 michael@paquier.xyz 1581 :GNC 68 : *reset_state = true;
1582 : : }
8058 tgl@sss.pgh.pa.us 1583 [ + + ]:CBC 1614 : else if (isInit)
1584 : : {
3283 peter_e@gmx.net 1585 : 881 : seqform->seqcache = 1;
1586 : : }
10485 vadim4o@yahoo.com 1587 : 1682 : }
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
3176 peter_e@gmx.net 1598 : 651 : 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 [ + + ]: 651 : deptype = for_identity ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO;
1606 : :
7057 tgl@sss.pgh.pa.us 1607 : 651 : nnames = list_length(owned_by);
1608 [ - + ]: 651 : Assert(nnames > 0);
1609 [ + + ]: 651 : if (nnames == 1)
1610 : : {
1611 : : /* Must be OWNED BY NONE */
1612 [ + + ]: 6 : if (strcmp(strVal(linitial(owned_by)), "none") != 0)
1613 [ + - ]: 3 : 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 : 3 : tablerel = NULL;
1618 : 3 : attnum = 0;
1619 : : }
1620 : : else
1621 : : {
1622 : : List *relname;
1623 : : char *attrname;
1624 : : RangeVar *rel;
1625 : :
1626 : : /* Separate relname and attr name */
1252 drowley@postgresql.o 1627 : 645 : relname = list_copy_head(owned_by, nnames - 1);
1906 tgl@sss.pgh.pa.us 1628 : 645 : attrname = strVal(llast(owned_by));
1629 : :
1630 : : /* Open and lock rel to ensure it won't go away meanwhile */
7057 1631 : 645 : rel = makeRangeVarFromNameList(relname);
1632 : 645 : tablerel = relation_openrv(rel, AccessShareLock);
1633 : :
1634 : : /* Must be a regular or foreign table */
4598 1635 [ + + ]: 645 : if (!(tablerel->rd_rel->relkind == RELKIND_RELATION ||
3296 rhaas@postgresql.org 1636 [ + + ]: 38 : tablerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
3176 peter_e@gmx.net 1637 [ + - ]: 34 : tablerel->rd_rel->relkind == RELKIND_VIEW ||
3296 rhaas@postgresql.org 1638 [ + + ]: 34 : tablerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE))
7057 tgl@sss.pgh.pa.us 1639 [ + - ]: 3 : 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 [ - + ]: 642 : if (seqrel->rd_rel->relowner != tablerel->rd_rel->relowner)
7057 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")));
7057 tgl@sss.pgh.pa.us 1650 [ + + ]:CBC 642 : if (RelationGetNamespace(seqrel) != RelationGetNamespace(tablerel))
1651 [ + - ]: 3 : 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 : 639 : attnum = get_attnum(RelationGetRelid(tablerel), attrname);
1657 [ + + ]: 639 : if (attnum == InvalidAttrNumber)
1658 [ + - ]: 3 : 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 : : */
3176 peter_e@gmx.net 1667 [ + + ]: 639 : if (deptype == DEPENDENCY_AUTO)
1668 : : {
1669 : : Oid tableId;
1670 : : int32 colId;
1671 : :
1672 [ + + ]: 426 : if (sequenceIsOwned(RelationGetRelid(seqrel), DEPENDENCY_INTERNAL, &tableId, &colId))
1673 [ + - ]: 3 : 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 : 636 : deleteDependencyRecordsForClass(RelationRelationId, RelationGetRelid(seqrel),
1686 : : RelationRelationId, deptype);
1687 : :
7057 tgl@sss.pgh.pa.us 1688 [ + - ]: 636 : if (tablerel)
1689 : : {
1690 : : ObjectAddress refobject,
1691 : : depobject;
1692 : :
1693 : 636 : refobject.classId = RelationRelationId;
1694 : 636 : refobject.objectId = RelationGetRelid(tablerel);
1695 : 636 : refobject.objectSubId = attnum;
1696 : 636 : depobject.classId = RelationRelationId;
1697 : 636 : depobject.objectId = RelationGetRelid(seqrel);
1698 : 636 : depobject.objectSubId = 0;
3176 peter_e@gmx.net 1699 : 636 : recordDependencyOn(&depobject, &refobject, deptype);
1700 : : }
1701 : :
1702 : : /* Done, but hold lock until commit */
7057 tgl@sss.pgh.pa.us 1703 [ + - ]: 636 : if (tablerel)
1704 : 636 : relation_close(tablerel, NoLock);
1705 : 636 : }
1706 : :
1707 : :
1708 : : /*
1709 : : * Return sequence parameters in a list of the form created by the parser.
1710 : : */
1711 : : List *
3176 peter_e@gmx.net 1712 : 6 : sequence_options(Oid relid)
1713 : : {
1714 : : HeapTuple pgstuple;
1715 : : Form_pg_sequence pgsform;
1716 : 6 : List *options = NIL;
1717 : :
880 michael@paquier.xyz 1718 : 6 : pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(relid));
3176 peter_e@gmx.net 1719 [ - + ]: 6 : if (!HeapTupleIsValid(pgstuple))
3176 peter_e@gmx.net 1720 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for sequence %u", relid);
3176 peter_e@gmx.net 1721 :CBC 6 : pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
1722 : :
1723 : : /* Use makeFloat() for 64-bit integers, like gram.y does. */
2841 1724 : 6 : options = lappend(options,
1725 : 6 : makeDefElem("cache", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqcache)), -1));
1726 : 6 : options = lappend(options,
1432 peter@eisentraut.org 1727 : 6 : makeDefElem("cycle", (Node *) makeBoolean(pgsform->seqcycle), -1));
2841 peter_e@gmx.net 1728 : 6 : options = lappend(options,
1729 : 6 : makeDefElem("increment", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqincrement)), -1));
1730 : 6 : options = lappend(options,
1731 : 6 : makeDefElem("maxvalue", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqmax)), -1));
1732 : 6 : options = lappend(options,
1733 : 6 : makeDefElem("minvalue", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqmin)), -1));
1734 : 6 : options = lappend(options,
1735 : 6 : makeDefElem("start", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqstart)), -1));
1736 : :
3176 1737 : 6 : ReleaseSysCache(pgstuple);
1738 : :
1739 : 6 : return options;
1740 : : }
1741 : :
1742 : : /*
1743 : : * Return sequence parameters (formerly for use by information schema)
1744 : : */
1745 : : Datum
5462 1746 : 3 : pg_sequence_parameters(PG_FUNCTION_ARGS)
1747 : : {
1748 : 3 : 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 [ - + ]: 3 : if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
5462 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 : :
1091 michael@paquier.xyz 1761 [ - + ]:CBC 3 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1091 michael@paquier.xyz 1762 [ # # ]:UBC 0 : elog(ERROR, "return type must be a row type");
1763 : :
5462 peter_e@gmx.net 1764 :CBC 3 : memset(isnull, 0, sizeof(isnull));
1765 : :
880 michael@paquier.xyz 1766 : 3 : pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(relid));
3283 peter_e@gmx.net 1767 [ - + ]: 3 : if (!HeapTupleIsValid(pgstuple))
3283 peter_e@gmx.net 1768 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for sequence %u", relid);
3283 peter_e@gmx.net 1769 :CBC 3 : pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
1770 : :
1771 : 3 : values[0] = Int64GetDatum(pgsform->seqstart);
1772 : 3 : values[1] = Int64GetDatum(pgsform->seqmin);
1773 : 3 : values[2] = Int64GetDatum(pgsform->seqmax);
1774 : 3 : values[3] = Int64GetDatum(pgsform->seqincrement);
1775 : 3 : values[4] = BoolGetDatum(pgsform->seqcycle);
1776 : 3 : values[5] = Int64GetDatum(pgsform->seqcache);
3231 1777 : 3 : values[6] = ObjectIdGetDatum(pgsform->seqtypid);
1778 : :
3283 1779 : 3 : ReleaseSysCache(pgstuple);
1780 : :
5462 1781 : 3 : 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
473 michael@paquier.xyz 1793 : 638 : pg_get_sequence_data(PG_FUNCTION_ARGS)
1794 : : {
1795 : : #define PG_GET_SEQUENCE_DATA_COLS 3
503 nathan@postgresql.or 1796 : 638 : Oid relid = PG_GETARG_OID(0);
1797 : : SeqTable elm;
1798 : : Relation seqrel;
473 michael@paquier.xyz 1799 : 638 : Datum values[PG_GET_SEQUENCE_DATA_COLS] = {0};
1800 : 638 : bool isnull[PG_GET_SEQUENCE_DATA_COLS] = {0};
1801 : : TupleDesc resultTupleDesc;
1802 : : HeapTuple resultHeapTuple;
1803 : : Datum result;
1804 : :
1805 : 638 : resultTupleDesc = CreateTemplateTupleDesc(PG_GET_SEQUENCE_DATA_COLS);
503 nathan@postgresql.or 1806 : 638 : TupleDescInitEntry(resultTupleDesc, (AttrNumber) 1, "last_value",
1807 : : INT8OID, -1, 0);
473 michael@paquier.xyz 1808 : 638 : TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "is_called",
1809 : : BOOLOID, -1, 0);
71 akapila@postgresql.o 1810 :GNC 638 : TupleDescInitEntry(resultTupleDesc, (AttrNumber) 3, "page_lsn",
1811 : : LSNOID, -1, 0);
503 nathan@postgresql.or 1812 :CBC 638 : resultTupleDesc = BlessTupleDesc(resultTupleDesc);
1813 : :
1814 : 638 : init_sequence(relid, &elm, &seqrel);
1815 : :
1816 : : /*
1817 : : * Return all NULLs for sequences for which we lack privileges, other
1818 : : * sessions' temporary sequences, and unlogged sequences on standbys.
1819 : : */
1820 [ + + ]: 638 : if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT) == ACLCHECK_OK &&
1821 [ - + - - ]: 628 : !RELATION_IS_OTHER_TEMP(seqrel) &&
1822 [ + + + + ]: 628 : (RelationIsPermanent(seqrel) || !RecoveryInProgress()))
1823 : 618 : {
1824 : : Buffer buf;
1825 : : HeapTupleData seqtuple;
1826 : : Form_pg_sequence_data seq;
1827 : : Page page;
1828 : :
1829 : 618 : seq = read_seq_tuple(seqrel, &buf, &seqtuple);
71 akapila@postgresql.o 1830 :GNC 618 : page = BufferGetPage(buf);
1831 : :
503 nathan@postgresql.or 1832 :CBC 618 : values[0] = Int64GetDatum(seq->last_value);
473 michael@paquier.xyz 1833 : 618 : values[1] = BoolGetDatum(seq->is_called);
71 akapila@postgresql.o 1834 :GNC 618 : values[2] = LSNGetDatum(PageGetLSN(page));
1835 : :
503 nathan@postgresql.or 1836 :CBC 618 : UnlockReleaseBuffer(buf);
1837 : : }
1838 : : else
1839 : 20 : memset(isnull, true, sizeof(isnull));
1840 : :
1841 : 638 : sequence_close(seqrel, NoLock);
1842 : :
1843 : 638 : resultHeapTuple = heap_form_tuple(resultTupleDesc, values, isnull);
1844 : 638 : result = HeapTupleGetDatum(resultHeapTuple);
1845 : 638 : PG_RETURN_DATUM(result);
1846 : : #undef PG_GET_SEQUENCE_DATA_COLS
1847 : : }
1848 : :
1849 : :
1850 : : /*
1851 : : * Return the last value from the sequence
1852 : : *
1853 : : * Note: This has a completely different meaning than lastval().
1854 : : */
1855 : : Datum
3315 peter_e@gmx.net 1856 : 58 : pg_sequence_last_value(PG_FUNCTION_ARGS)
1857 : : {
1858 : 58 : Oid relid = PG_GETARG_OID(0);
1859 : : SeqTable elm;
1860 : : Relation seqrel;
582 nathan@postgresql.or 1861 : 58 : bool is_called = false;
1862 : 58 : int64 result = 0;
1863 : :
1864 : : /* open and lock sequence */
3315 peter_e@gmx.net 1865 : 58 : init_sequence(relid, &elm, &seqrel);
1866 : :
1867 : : /*
1868 : : * We return NULL for other sessions' temporary sequences. The
1869 : : * pg_sequences system view already filters those out, but this offers a
1870 : : * defense against ERRORs in case someone invokes this function directly.
1871 : : *
1872 : : * Also, for the benefit of the pg_sequences view, we return NULL for
1873 : : * unlogged sequences on standbys and for sequences for which the current
1874 : : * user lacks privileges instead of throwing an error.
1875 : : */
533 nathan@postgresql.or 1876 [ + - ]: 58 : if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_USAGE) == ACLCHECK_OK &&
1877 [ - + - - ]: 58 : !RELATION_IS_OTHER_TEMP(seqrel) &&
582 1878 [ + + - + ]: 58 : (RelationIsPermanent(seqrel) || !RecoveryInProgress()))
1879 : : {
1880 : : Buffer buf;
1881 : : HeapTupleData seqtuple;
1882 : : Form_pg_sequence_data seq;
1883 : :
1884 : 57 : seq = read_seq_tuple(seqrel, &buf, &seqtuple);
1885 : :
1886 : 57 : is_called = seq->is_called;
1887 : 57 : result = seq->last_value;
1888 : :
1889 : 57 : UnlockReleaseBuffer(buf);
1890 : : }
659 michael@paquier.xyz 1891 : 58 : sequence_close(seqrel, NoLock);
1892 : :
3315 peter_e@gmx.net 1893 [ + + ]: 58 : if (is_called)
1894 : 24 : PG_RETURN_INT64(result);
1895 : : else
1896 : 34 : PG_RETURN_NULL();
1897 : : }
1898 : :
1899 : : /*
1900 : : * Flush cached sequence information.
1901 : : */
1902 : : void
4457 rhaas@postgresql.org 1903 : 9 : ResetSequenceCaches(void)
1904 : : {
4414 heikki.linnakangas@i 1905 [ + + ]: 9 : if (seqhashtab)
1906 : : {
1907 : 6 : hash_destroy(seqhashtab);
1908 : 6 : seqhashtab = NULL;
1909 : : }
1910 : :
4453 rhaas@postgresql.org 1911 : 9 : last_used_seq = NULL;
4457 1912 : 9 : }
|