From a83e6a3c817ffab0eca8cc0d15a21649e82d2aa6 Mon Sep 17 00:00:00 2001 From: Vignesh C Date: Fri, 20 Sep 2024 08:45:21 +0530 Subject: [PATCH v20241118 1/5] Introduce pg_sequence_state function for enhanced sequence management This patch introduces a new function: pg_sequence_state function allows retrieval of sequence values including LSN. --- doc/src/sgml/func.sgml | 26 ++++++++++ src/backend/commands/sequence.c | 70 ++++++++++++++++++++++++++ src/include/catalog/pg_proc.dat | 8 +++ src/test/regress/expected/sequence.out | 12 +++++ src/test/regress/sql/sequence.sql | 2 + 5 files changed, 118 insertions(+) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 73979f20ff..406c108845 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -19632,6 +19632,32 @@ SELECT setval('myseq', 42, false); Next nextvalSELECT privilege on the last used sequence. + + + + + pg_sequence_state + + pg_sequence_state ( regclass ) + record + ( page_lsn pg_lsn, + last_value bigint, + log_cnt bigint, + is_called bool ) + + + Returns information about the sequence. page_lsn is + the page LSN of the sequence, last_value is the + current value of the sequence, log_cnt shows how + many fetches remain before a new WAL record has to be written, and + is_called indicates whether the sequence has been + used. + + + This function requires USAGE + or SELECT privilege on the sequence. + + diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0188e8bbd5..8b6c34a2c1 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -45,6 +45,7 @@ #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" +#include "utils/pg_lsn.h" #include "utils/resowner.h" #include "utils/syscache.h" #include "utils/varlena.h" @@ -1885,6 +1886,75 @@ pg_sequence_last_value(PG_FUNCTION_ARGS) PG_RETURN_NULL(); } +/* + * Return the current on-disk state of the sequence. + * + * Note: This is roughly equivalent to selecting the data from the sequence, + * except that it also returns the page LSN. + */ +Datum +pg_sequence_state(PG_FUNCTION_ARGS) +{ + Oid seq_relid = PG_GETARG_OID(0); + SeqTable elm; + Relation seqrel; + Buffer buf; + Page page; + HeapTupleData seqtuple; + Form_pg_sequence_data seq; + Datum result; + + XLogRecPtr lsn; + int64 last_value; + int64 log_cnt; + bool is_called; + + TupleDesc tupdesc; + HeapTuple tuple; + Datum values[4]; + bool nulls[4] = {false, false, false, false}; + + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + /* open and lock sequence */ + init_sequence(seq_relid, &elm, &seqrel); + + if (pg_class_aclcheck(elm->relid, GetUserId(), + ACL_SELECT | ACL_USAGE) != ACLCHECK_OK) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + + seq = read_seq_tuple(seqrel, &buf, &seqtuple); + page = BufferGetPage(buf); + lsn = PageGetLSN(page); + + last_value = seq->last_value; + log_cnt = seq->log_cnt; + is_called = seq->is_called; + + UnlockReleaseBuffer(buf); + relation_close(seqrel, NoLock); + + /* Page LSN for the sequence */ + values[0] = LSNGetDatum(lsn); + + /* The value most recently returned by nextval in the current session */ + values[1] = Int64GetDatum(last_value); + + /* How many fetches remain before a new WAL record has to be written */ + values[2] = Int64GetDatum(log_cnt); + + /* Indicates whether the sequence has been used */ + values[3] = BoolGetDatum(is_called); + + tuple = heap_form_tuple(tupdesc, values, nulls); + result = HeapTupleGetDatum(tuple); + + PG_RETURN_DATUM(result); +} void seq_redo(XLogReaderState *record) diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index cbbe8acd38..1410fe15d8 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -3375,6 +3375,14 @@ proname => 'pg_sequence_last_value', provolatile => 'v', proparallel => 'u', prorettype => 'int8', proargtypes => 'regclass', prosrc => 'pg_sequence_last_value' }, +{ oid => '6313', + descr => 'current on-disk sequence state', + proname => 'pg_sequence_state', provolatile => 'v', + prorettype => 'record', proargtypes => 'regclass', + proallargtypes => '{regclass,pg_lsn,int8,int8,bool}', + proargmodes => '{i,o,o,o,o}', + proargnames => '{seq_oid,page_lsn,last_value,log_cnt,is_called}', + prosrc => 'pg_sequence_state' }, { oid => '9876', descr => 'return sequence tuple, for use by pg_dump', proname => 'pg_get_sequence_data', provolatile => 'v', proparallel => 'u', prorettype => 'record', proargtypes => 'regclass', diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 15925d99c8..c2d6c78827 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -161,6 +161,12 @@ SELECT nextval('serialTest2_f6_seq'); CREATE SEQUENCE sequence_test; CREATE SEQUENCE IF NOT EXISTS sequence_test; NOTICE: relation "sequence_test" already exists, skipping +SELECT last_value, log_cnt, is_called FROM pg_sequence_state('sequence_test'); + last_value | log_cnt | is_called +------------+---------+----------- + 1 | 0 | f +(1 row) + SELECT nextval('sequence_test'::text); nextval --------- @@ -233,6 +239,12 @@ SELECT nextval('sequence_test'::text); 99 (1 row) +SELECT last_value, log_cnt, is_called FROM pg_sequence_state('sequence_test'); + last_value | log_cnt | is_called +------------+---------+----------- + 99 | 32 | t +(1 row) + DISCARD SEQUENCES; SELECT currval('sequence_test'::regclass); ERROR: currval of sequence "sequence_test" is not yet defined in this session diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 2c220b6074..46054527df 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -112,6 +112,7 @@ SELECT nextval('serialTest2_f6_seq'); CREATE SEQUENCE sequence_test; CREATE SEQUENCE IF NOT EXISTS sequence_test; +SELECT last_value, log_cnt, is_called FROM pg_sequence_state('sequence_test'); SELECT nextval('sequence_test'::text); SELECT nextval('sequence_test'::regclass); SELECT currval('sequence_test'::text); @@ -124,6 +125,7 @@ SELECT setval('sequence_test'::regclass, 32); SELECT nextval('sequence_test'::text); SELECT setval('sequence_test'::regclass, 99, false); SELECT nextval('sequence_test'::text); +SELECT last_value, log_cnt, is_called FROM pg_sequence_state('sequence_test'); DISCARD SEQUENCES; SELECT currval('sequence_test'::regclass); -- 2.34.1