@@ -68,6 +68,7 @@ typedef struct SeqTableData
68
68
{
69
69
struct SeqTableData *next; /* link to next SeqTable object */
70
70
Oid relid; /* pg_class OID of this sequence */
71
+ Oid filenode; /* last seen relfilenode of this sequence */
71
72
LocalTransactionId lxid; /* xact in which we last did a seq op */
72
73
bool last_valid; /* do we have a valid "last" value? */
73
74
int64 last; /* value last returned by nextval */
@@ -87,6 +88,7 @@ static SeqTable seqtab = NULL; /* Head of list of SeqTable items */
87
88
*/
88
89
static SeqTableData *last_used_seq = NULL;
89
90
91
+ static void fill_seq_with_data(Relation rel, HeapTuple tuple);
90
92
static int64 nextval_internal(Oid relid);
91
93
static Relation open_share_lock(SeqTable seq);
92
94
static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
@@ -109,9 +111,6 @@ DefineSequence(CreateSeqStmt *seq)
109
111
CreateStmt *stmt = makeNode(CreateStmt);
110
112
Oid seqoid;
111
113
Relation rel;
112
- Buffer buf;
113
- Page page;
114
- sequence_magic *sm;
115
114
HeapTuple tuple;
116
115
TupleDesc tupDesc;
117
116
Datum value[SEQ_COL_LASTCOL];
@@ -211,6 +210,100 @@ DefineSequence(CreateSeqStmt *seq)
211
210
rel = heap_open(seqoid, AccessExclusiveLock);
212
211
tupDesc = RelationGetDescr(rel);
213
212
213
+ /* now initialize the sequence's data */
214
+ tuple = heap_form_tuple(tupDesc, value, null);
215
+ fill_seq_with_data(rel, tuple);
216
+
217
+ /* process OWNED BY if given */
218
+ if (owned_by)
219
+ process_owned_by(rel, owned_by);
220
+
221
+ heap_close(rel, NoLock);
222
+ }
223
+
224
+ /*
225
+ * Reset a sequence to its initial value.
226
+ *
227
+ * The change is made transactionally, so that on failure of the current
228
+ * transaction, the sequence will be restored to its previous state.
229
+ * We do that by creating a whole new relfilenode for the sequence; so this
230
+ * works much like the rewriting forms of ALTER TABLE.
231
+ *
232
+ * Caller is assumed to have acquired AccessExclusiveLock on the sequence,
233
+ * which must not be released until end of transaction. Caller is also
234
+ * responsible for permissions checking.
235
+ */
236
+ void
237
+ ResetSequence(Oid seq_relid)
238
+ {
239
+ Relation seq_rel;
240
+ SeqTable elm;
241
+ Form_pg_sequence seq;
242
+ Buffer buf;
243
+ Page page;
244
+ HeapTuple tuple;
245
+ HeapTupleData tupledata;
246
+ ItemId lp;
247
+
248
+ /*
249
+ * Read the old sequence. This does a bit more work than really
250
+ * necessary, but it's simple, and we do want to double-check that it's
251
+ * indeed a sequence.
252
+ */
253
+ init_sequence(seq_relid, &elm, &seq_rel);
254
+ seq = read_info(elm, seq_rel, &buf);
255
+
256
+ /*
257
+ * Copy the existing sequence tuple.
258
+ */
259
+ page = BufferGetPage(buf);
260
+ lp = PageGetItemId(page, FirstOffsetNumber);
261
+ Assert(ItemIdIsNormal(lp));
262
+
263
+ tupledata.t_data = (HeapTupleHeader) PageGetItem(page, lp);
264
+ tupledata.t_len = ItemIdGetLength(lp);
265
+ tuple = heap_copytuple(&tupledata);
266
+
267
+ /* Now we're done with the old page */
268
+ UnlockReleaseBuffer(buf);
269
+
270
+ /*
271
+ * Modify the copied tuple to execute the restart (compare the RESTART
272
+ * action in AlterSequence)
273
+ */
274
+ seq = (Form_pg_sequence) GETSTRUCT(tuple);
275
+ seq->last_value = seq->start_value;
276
+ seq->is_called = false;
277
+ seq->log_cnt = 1;
278
+
279
+ /*
280
+ * Create a new storage file for the sequence. We want to keep the
281
+ * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
282
+ */
283
+ RelationSetNewRelfilenode(seq_rel, InvalidTransactionId);
284
+
285
+ /*
286
+ * Insert the modified tuple into the new storage file.
287
+ */
288
+ fill_seq_with_data(seq_rel, tuple);
289
+
290
+ /* Clear local cache so that we don't think we have cached numbers */
291
+ /* Note that we do not change the currval() state */
292
+ elm->cached = elm->last;
293
+
294
+ relation_close(seq_rel, NoLock);
295
+ }
296
+
297
+ /*
298
+ * Initialize a sequence's relation with the specified tuple as content
299
+ */
300
+ static void
301
+ fill_seq_with_data(Relation rel, HeapTuple tuple)
302
+ {
303
+ Buffer buf;
304
+ Page page;
305
+ sequence_magic *sm;
306
+
214
307
/* Initialize first page of relation with special magic number */
215
308
216
309
buf = ReadBuffer(rel, P_NEW);
@@ -225,8 +318,7 @@ DefineSequence(CreateSeqStmt *seq)
225
318
/* hack: ensure heap_insert will insert on the just-created page */
226
319
RelationSetTargetBlock(rel, 0);
227
320
228
- /* Now form & insert sequence tuple */
229
- tuple = heap_form_tuple(tupDesc, value, null);
321
+ /* Now insert sequence tuple */
230
322
simple_heap_insert(rel, tuple);
231
323
232
324
Assert(ItemPointerGetOffsetNumber(&(tuple->t_self)) == FirstOffsetNumber);
@@ -306,12 +398,6 @@ DefineSequence(CreateSeqStmt *seq)
306
398
END_CRIT_SECTION();
307
399
308
400
UnlockReleaseBuffer(buf);
309
-
310
- /* process OWNED BY if given */
311
- if (owned_by)
312
- process_owned_by(rel, owned_by);
313
-
314
- heap_close(rel, NoLock);
315
401
}
316
402
317
403
/*
@@ -323,29 +409,6 @@ void
323
409
AlterSequence(AlterSeqStmt *stmt)
324
410
{
325
411
Oid relid;
326
-
327
- /* find sequence */
328
- relid = RangeVarGetRelid(stmt->sequence, false);
329
-
330
- /* allow ALTER to sequence owner only */
331
- /* if you change this, see also callers of AlterSequenceInternal! */
332
- if (!pg_class_ownercheck(relid, GetUserId()))
333
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
334
- stmt->sequence->relname);
335
-
336
- /* do the work */
337
- AlterSequenceInternal(relid, stmt->options);
338
- }
339
-
340
- /*
341
- * AlterSequenceInternal
342
- *
343
- * Same as AlterSequence except that the sequence is specified by OID
344
- * and we assume the caller already checked permissions.
345
- */
346
- void
347
- AlterSequenceInternal(Oid relid, List *options)
348
- {
349
412
SeqTable elm;
350
413
Relation seqrel;
351
414
Buffer buf;
@@ -355,8 +418,14 @@ AlterSequenceInternal(Oid relid, List *options)
355
418
List *owned_by;
356
419
357
420
/* open and AccessShareLock sequence */
421
+ relid = RangeVarGetRelid(stmt->sequence, false);
358
422
init_sequence(relid, &elm, &seqrel);
359
423
424
+ /* allow ALTER to sequence owner only */
425
+ if (!pg_class_ownercheck(relid, GetUserId()))
426
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
427
+ stmt->sequence->relname);
428
+
360
429
/* lock page' buffer and read tuple into new sequence structure */
361
430
seq = read_info(elm, seqrel, &buf);
362
431
page = BufferGetPage(buf);
@@ -365,7 +434,7 @@ AlterSequenceInternal(Oid relid, List *options)
365
434
memcpy(&new, seq, sizeof(FormData_pg_sequence));
366
435
367
436
/* Check and set new values */
368
- init_params(options, false, &new, &owned_by);
437
+ init_params(stmt-> options, false, &new, &owned_by);
369
438
370
439
/* Clear local cache so that we don't think we have cached numbers */
371
440
/* Note that we do not change the currval() state */
@@ -937,6 +1006,7 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
937
1006
(errcode(ERRCODE_OUT_OF_MEMORY),
938
1007
errmsg("out of memory")));
939
1008
elm->relid = relid;
1009
+ elm->filenode = InvalidOid;
940
1010
elm->lxid = InvalidLocalTransactionId;
941
1011
elm->last_valid = false;
942
1012
elm->last = elm->cached = elm->increment = 0;
@@ -955,6 +1025,18 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
955
1025
errmsg("\"%s\" is not a sequence",
956
1026
RelationGetRelationName(seqrel))));
957
1027
1028
+ /*
1029
+ * If the sequence has been transactionally replaced since we last saw it,
1030
+ * discard any cached-but-unissued values. We do not touch the currval()
1031
+ * state, however.
1032
+ */
1033
+ if (seqrel->rd_rel->relfilenode != elm->filenode)
1034
+ {
1035
+ elm->filenode = seqrel->rd_rel->relfilenode;
1036
+ elm->cached = elm->last;
1037
+ }
1038
+
1039
+ /* Return results */
958
1040
*p_elm = elm;
959
1041
*p_rel = seqrel;
960
1042
}
0 commit comments