Introduce pg_sequence_read_tuple().

This new function returns the data for the given sequence, i.e.,
the values within the sequence tuple.  Since this function is a
substitute for SELECT from the sequence, the SELECT privilege is
required on the sequence in question.  It returns all NULLs for
sequences for which we lack privileges, other sessions' temporary
sequences, and unlogged sequences on standbys.

This function is primarily intended for use by pg_dump in a
follow-up commit that will use it to optimize dumpSequenceData().
Like pg_sequence_last_value(), which is a support function for the
pg_sequences system view, pg_sequence_read_tuple() is left
undocumented.

Bumps catversion.

Reviewed-by: Michael Paquier, Tom Lane
Discussion: https://postgr.es/m/20240503025140.GA1227404%40nathanxps13
This commit is contained in:
Nathan Bossart 2024-07-31 10:12:42 -05:00
parent 68e9629985
commit c8b06bb969
5 changed files with 78 additions and 1 deletions

View File

@ -1773,6 +1773,67 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
}
/*
* Return the sequence tuple.
*
* This is primarily intended for use by pg_dump to gather sequence data
* without needing to individually query each sequence relation.
*/
Datum
pg_sequence_read_tuple(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
SeqTable elm;
Relation seqrel;
Datum values[SEQ_COL_LASTCOL] = {0};
bool isnull[SEQ_COL_LASTCOL] = {0};
TupleDesc resultTupleDesc;
HeapTuple resultHeapTuple;
Datum result;
resultTupleDesc = CreateTemplateTupleDesc(SEQ_COL_LASTCOL);
TupleDescInitEntry(resultTupleDesc, (AttrNumber) 1, "last_value",
INT8OID, -1, 0);
TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "log_cnt",
INT8OID, -1, 0);
TupleDescInitEntry(resultTupleDesc, (AttrNumber) 3, "is_called",
BOOLOID, -1, 0);
resultTupleDesc = BlessTupleDesc(resultTupleDesc);
init_sequence(relid, &elm, &seqrel);
/*
* Return all NULLs for sequences for which we lack privileges, other
* sessions' temporary sequences, and unlogged sequences on standbys.
*/
if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT) == ACLCHECK_OK &&
!RELATION_IS_OTHER_TEMP(seqrel) &&
(RelationIsPermanent(seqrel) || !RecoveryInProgress()))
{
Buffer buf;
HeapTupleData seqtuple;
Form_pg_sequence_data seq;
seq = read_seq_tuple(seqrel, &buf, &seqtuple);
values[0] = Int64GetDatum(seq->last_value);
values[1] = Int64GetDatum(seq->log_cnt);
values[2] = BoolGetDatum(seq->is_called);
UnlockReleaseBuffer(buf);
}
else
memset(isnull, true, sizeof(isnull));
sequence_close(seqrel, NoLock);
resultHeapTuple = heap_form_tuple(resultTupleDesc, values, isnull);
result = HeapTupleGetDatum(resultHeapTuple);
PG_RETURN_DATUM(result);
}
/*
* Return the last value from the sequence
*

View File

@ -57,6 +57,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 202407252
#define CATALOG_VERSION_NO 202407311
#endif

View File

@ -3329,6 +3329,12 @@
proname => 'pg_sequence_last_value', provolatile => 'v', proparallel => 'u',
prorettype => 'int8', proargtypes => 'regclass',
prosrc => 'pg_sequence_last_value' },
{ oid => '9876', descr => 'return sequence tuple, for use by pg_dump',
proname => 'pg_sequence_read_tuple', provolatile => 'v', proparallel => 'u',
prorettype => 'record', proargtypes => 'regclass',
proallargtypes => '{regclass,int8,int8,bool}', proargmodes => '{i,o,o,o}',
proargnames => '{sequence_oid,last_value,log_cnt,is_called}',
prosrc => 'pg_sequence_read_tuple' },
{ oid => '275', descr => 'return the next oid for a system table',
proname => 'pg_nextoid', provolatile => 'v', proparallel => 'u',

View File

@ -839,4 +839,11 @@ SELECT nextval('test_seq1');
3
(1 row)
-- pg_sequence_read_tuple
SELECT * FROM pg_sequence_read_tuple('test_seq1');
last_value | log_cnt | is_called
------------+---------+-----------
10 | 32 | t
(1 row)
DROP SEQUENCE test_seq1;

View File

@ -413,4 +413,7 @@ SELECT nextval('test_seq1');
SELECT nextval('test_seq1');
SELECT nextval('test_seq1');
-- pg_sequence_read_tuple
SELECT * FROM pg_sequence_read_tuple('test_seq1');
DROP SEQUENCE test_seq1;