@@ -68,6 +68,7 @@ typedef struct SeqTableData
6868{
6969 struct SeqTableData * next ; /* link to next SeqTable object */
7070 Oid relid ; /* pg_class OID of this sequence */
71+ Oid filenode ; /* last seen relfilenode of this sequence */
7172 LocalTransactionId lxid ; /* xact in which we last did a seq op */
7273 bool last_valid ; /* do we have a valid "last" value? */
7374 int64 last ; /* value last returned by nextval */
@@ -87,6 +88,7 @@ static SeqTable seqtab = NULL; /* Head of list of SeqTable items */
8788 */
8889static SeqTableData * last_used_seq = NULL ;
8990
91+ static void fill_seq_with_data (Relation rel , HeapTuple tuple );
9092static int64 nextval_internal (Oid relid );
9193static Relation open_share_lock (SeqTable seq );
9294static void init_sequence (Oid relid , SeqTable * p_elm , Relation * p_rel );
@@ -109,9 +111,6 @@ DefineSequence(CreateSeqStmt *seq)
109111 CreateStmt * stmt = makeNode (CreateStmt );
110112 Oid seqoid ;
111113 Relation rel ;
112- Buffer buf ;
113- Page page ;
114- sequence_magic * sm ;
115114 HeapTuple tuple ;
116115 TupleDesc tupDesc ;
117116 Datum value [SEQ_COL_LASTCOL ];
@@ -211,6 +210,100 @@ DefineSequence(CreateSeqStmt *seq)
211210 rel = heap_open (seqoid , AccessExclusiveLock );
212211 tupDesc = RelationGetDescr (rel );
213212
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+
214307 /* Initialize first page of relation with special magic number */
215308
216309 buf = ReadBuffer (rel , P_NEW );
@@ -225,8 +318,7 @@ DefineSequence(CreateSeqStmt *seq)
225318 /* hack: ensure heap_insert will insert on the just-created page */
226319 RelationSetTargetBlock (rel , 0 );
227320
228- /* Now form & insert sequence tuple */
229- tuple = heap_form_tuple (tupDesc , value , null );
321+ /* Now insert sequence tuple */
230322 simple_heap_insert (rel , tuple );
231323
232324 Assert (ItemPointerGetOffsetNumber (& (tuple -> t_self )) == FirstOffsetNumber );
@@ -306,12 +398,6 @@ DefineSequence(CreateSeqStmt *seq)
306398 END_CRIT_SECTION ();
307399
308400 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 );
315401}
316402
317403/*
@@ -323,29 +409,6 @@ void
323409AlterSequence (AlterSeqStmt * stmt )
324410{
325411 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- {
349412 SeqTable elm ;
350413 Relation seqrel ;
351414 Buffer buf ;
@@ -355,8 +418,14 @@ AlterSequenceInternal(Oid relid, List *options)
355418 List * owned_by ;
356419
357420 /* open and AccessShareLock sequence */
421+ relid = RangeVarGetRelid (stmt -> sequence , false);
358422 init_sequence (relid , & elm , & seqrel );
359423
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+
360429 /* lock page' buffer and read tuple into new sequence structure */
361430 seq = read_info (elm , seqrel , & buf );
362431 page = BufferGetPage (buf );
@@ -365,7 +434,7 @@ AlterSequenceInternal(Oid relid, List *options)
365434 memcpy (& new , seq , sizeof (FormData_pg_sequence ));
366435
367436 /* Check and set new values */
368- init_params (options , false, & new , & owned_by );
437+ init_params (stmt -> options , false, & new , & owned_by );
369438
370439 /* Clear local cache so that we don't think we have cached numbers */
371440 /* Note that we do not change the currval() state */
@@ -937,6 +1006,7 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
9371006 (errcode (ERRCODE_OUT_OF_MEMORY ),
9381007 errmsg ("out of memory" )));
9391008 elm -> relid = relid ;
1009+ elm -> filenode = InvalidOid ;
9401010 elm -> lxid = InvalidLocalTransactionId ;
9411011 elm -> last_valid = false;
9421012 elm -> last = elm -> cached = elm -> increment = 0 ;
@@ -955,6 +1025,18 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
9551025 errmsg ("\"%s\" is not a sequence" ,
9561026 RelationGetRelationName (seqrel ))));
9571027
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 */
9581040 * p_elm = elm ;
9591041 * p_rel = seqrel ;
9601042}
0 commit comments