@@ -86,6 +86,8 @@ typedef struct SeqTableData
8686
8787typedef SeqTableData * SeqTable ;
8888
89+ seq_nextval_hook_t SeqNextvalHook ;
90+
8991static HTAB * seqhashtab = NULL ; /* hash table for SeqTable items */
9092
9193/*
@@ -628,6 +630,10 @@ nextval_internal(Oid relid, bool check_permissions)
628630 elm -> last += elm -> increment ;
629631 relation_close (seqrel , NoLock );
630632 last_used_seq = elm ;
633+
634+ if (SeqNextvalHook )
635+ SeqNextvalHook (relid , elm -> last );
636+
631637 return elm -> last ;
632638 }
633639
@@ -823,6 +829,9 @@ nextval_internal(Oid relid, bool check_permissions)
823829
824830 relation_close (seqrel , NoLock );
825831
832+ if (SeqNextvalHook )
833+ SeqNextvalHook (relid , result );
834+
826835 return result ;
827836}
828837
@@ -892,6 +901,139 @@ lastval(PG_FUNCTION_ARGS)
892901 PG_RETURN_INT64 (result );
893902}
894903
904+ /*
905+ * Bump last value to next iff next > value.
906+ * Support routine for multimaster's monotonic sequences.
907+ */
908+ void
909+ AdjustSequence (Oid relid , int64 next )
910+ {
911+ SeqTable elm ;
912+ Relation seqrel ;
913+ Buffer buf ;
914+ HeapTupleData seqdatatuple ;
915+ Form_pg_sequence_data seq ;
916+ HeapTuple pgstuple ;
917+ Form_pg_sequence pgsform ;
918+ int64 maxv ,
919+ minv ,
920+ incby ,
921+ cache ;
922+ int64 last ;
923+
924+ /* open and lock sequence */
925+ init_sequence (relid , & elm , & seqrel );
926+
927+ if (pg_class_aclcheck (elm -> relid , GetUserId (), ACL_UPDATE ) != ACLCHECK_OK )
928+ ereport (ERROR ,
929+ (errcode (ERRCODE_INSUFFICIENT_PRIVILEGE ),
930+ errmsg ("permission denied for sequence %s" ,
931+ RelationGetRelationName (seqrel ))));
932+
933+ pgstuple = SearchSysCache1 (SEQRELID , ObjectIdGetDatum (relid ));
934+ if (!HeapTupleIsValid (pgstuple ))
935+ elog (ERROR , "cache lookup failed for sequence %u" , relid );
936+ pgsform = (Form_pg_sequence ) GETSTRUCT (pgstuple );
937+ maxv = pgsform -> seqmax ;
938+ minv = pgsform -> seqmin ;
939+ incby = pgsform -> seqincrement ;
940+ cache = pgsform -> seqcache ;
941+ ReleaseSysCache (pgstuple );
942+
943+ /* cached number is greater than received */
944+ if (elm -> last != cache && elm -> last + incby > next )
945+ {
946+ relation_close (seqrel , NoLock );
947+ return ;
948+ }
949+
950+ /* read-only transactions may only modify temp sequences */
951+ if (!seqrel -> rd_islocaltemp )
952+ PreventCommandIfReadOnly ("setval()" );
953+
954+ /*
955+ * Forbid this during parallel operation because, to make it work, the
956+ * cooperating backends would need to share the backend-local cached
957+ * sequence information. Currently, we don't support that.
958+ */
959+ PreventCommandIfParallelMode ("setval()" );
960+
961+ /* lock page' buffer and read tuple */
962+ seq = read_seq_tuple (seqrel , & buf , & seqdatatuple );
963+
964+ if ((next < minv ) || (next > maxv ))
965+ {
966+ char bufv [100 ],
967+ bufm [100 ],
968+ bufx [100 ];
969+
970+ snprintf (bufv , sizeof (bufv ), INT64_FORMAT , next );
971+ snprintf (bufm , sizeof (bufm ), INT64_FORMAT , minv );
972+ snprintf (bufx , sizeof (bufx ), INT64_FORMAT , maxv );
973+ ereport (ERROR ,
974+ (errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
975+ errmsg ("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)" ,
976+ bufv , RelationGetRelationName (seqrel ),
977+ bufm , bufx )));
978+ }
979+
980+ last = seq -> last_value ;
981+ if (seq -> is_called )
982+ {
983+ last += incby ;
984+ }
985+ if (last <= next )
986+ {
987+ next = last + incby * ((next - last + incby )/incby );
988+
989+ /* Set the currval() state only if iscalled = true */
990+ if (seq -> is_called )
991+ {
992+ elm -> last = next ; /* last returned number */
993+ elm -> last_valid = true;
994+ }
995+
996+ /* In any case, forget any future cached numbers */
997+ elm -> cached = elm -> last ;
998+
999+ /* check the comment above nextval_internal()'s equivalent call. */
1000+ if (RelationNeedsWAL (seqrel ))
1001+ GetTopTransactionId ();
1002+
1003+ START_CRIT_SECTION ();
1004+
1005+ seq -> last_value = next ; /* last fetched number */
1006+ seq -> log_cnt = 0 ;
1007+
1008+ MarkBufferDirty (buf );
1009+
1010+ /* XLOG stuff */
1011+ if (RelationNeedsWAL (seqrel ))
1012+ {
1013+ xl_seq_rec xlrec ;
1014+ XLogRecPtr recptr ;
1015+ Page page = BufferGetPage (buf );
1016+
1017+ XLogBeginInsert ();
1018+ XLogRegisterBuffer (0 , buf , REGBUF_WILL_INIT );
1019+
1020+ xlrec .node = seqrel -> rd_node ;
1021+ XLogRegisterData ((char * ) & xlrec , sizeof (xl_seq_rec ));
1022+ XLogRegisterData ((char * ) seqdatatuple .t_data , seqdatatuple .t_len );
1023+
1024+ recptr = XLogInsert (RM_SEQ_ID , XLOG_SEQ_LOG );
1025+
1026+ PageSetLSN (page , recptr );
1027+ }
1028+
1029+ END_CRIT_SECTION ();
1030+ }
1031+
1032+ UnlockReleaseBuffer (buf );
1033+
1034+ relation_close (seqrel , NoLock );
1035+ }
1036+
8951037/*
8961038 * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
8971039 *
0 commit comments