From 4923356f6143ecdb68c4a9ac41c05dcbba1253a3 Mon Sep 17 00:00:00 2001 From: Bharath Rupireddy Date: Tue, 15 Nov 2022 09:25:14 +0000 Subject: [PATCH v3] Introduce pg_walfile_offset_lsn() Author: Nathan Bossart, Bharath Rupireddy --- doc/src/sgml/func.sgml | 16 +++++ src/backend/access/transam/xlogfuncs.c | 62 ++++++++++++++++++++ src/include/access/xlog_internal.h | 8 +++ src/include/catalog/pg_proc.dat | 6 ++ src/test/regress/expected/misc_functions.out | 26 ++++++++ src/test/regress/sql/misc_functions.sql | 12 ++++ 6 files changed, 130 insertions(+) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 6e0425cb3d..26970345fd 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -25900,6 +25900,22 @@ LOG: Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560 + + + + pg_walfile_offset_lsn + + pg_walfile_offset_lsn ( file_name text, file_offset integer ) + record + ( lsn pg_lsn, + timeline_id integer ) + + + Computes write-ahead log location and timline ID from a given WAL file + name and byte offset within that file. + + + diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c index 487d5d9cac..ed2690b49a 100644 --- a/src/backend/access/transam/xlogfuncs.c +++ b/src/backend/access/transam/xlogfuncs.c @@ -339,6 +339,68 @@ pg_last_wal_replay_lsn(PG_FUNCTION_ARGS) PG_RETURN_LSN(recptr); } +/* + * Compute an LSN and timline ID given a WAL file name and decimal byte offset. + */ +Datum +pg_walfile_offset_lsn(PG_FUNCTION_ARGS) +{ + char *fname = text_to_cstring(PG_GETARG_TEXT_PP(0)); + int offset = PG_GETARG_INT32(1); + TimeLineID tli; + XLogSegNo segno; + XLogRecPtr lsn; + uint32 log; + uint32 seg; + Datum values[2] = {0}; + bool isnull[2] = {0}; + TupleDesc resultTupleDesc; + HeapTuple resultHeapTuple; + Datum result; + + if (!IsXLogFileName(fname)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid WAL file name \"%s\"", fname))); + + XLogIdFromFileName(fname, &tli, &segno, &log, &seg, wal_segment_size); + + if (seg >= XLogSegmentsPerXLogId(wal_segment_size) || + (log == 0 && seg == 0) || + segno == 0 || + tli == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid WAL file name \"%s\"", fname))); + + if (offset < 0 || offset >= wal_segment_size) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("offset must not be negative or greater than or equal to WAL segment size"))); + + /* + * Construct a tuple descriptor for the result row. This must match this + * function's pg_proc entry! + */ + resultTupleDesc = CreateTemplateTupleDesc(2); + TupleDescInitEntry(resultTupleDesc, (AttrNumber) 1, "lsn", + PG_LSNOID, -1, 0); + TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "timeline_id", + INT4OID, -1, 0); + + resultTupleDesc = BlessTupleDesc(resultTupleDesc); + + XLogSegNoOffsetToRecPtr(segno, offset, wal_segment_size, lsn); + + values[0] = LSNGetDatum(lsn); + values[1] = UInt32GetDatum(tli); + + resultHeapTuple = heap_form_tuple(resultTupleDesc, values, isnull); + result = HeapTupleGetDatum(resultHeapTuple); + + PG_RETURN_DATUM(result); +} + /* * Compute an xlog file name and decimal byte offset given a WAL location, * such as is returned by pg_backup_stop() or pg_switch_wal(). diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index 44291b337b..0ffe4142be 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -206,6 +206,14 @@ XLogFromFileName(const char *fname, TimeLineID *tli, XLogSegNo *logSegNo, int wa *logSegNo = (uint64) log * XLogSegmentsPerXLogId(wal_segsz_bytes) + seg; } +static inline void +XLogIdFromFileName(const char *fname, TimeLineID *tli, XLogSegNo *logSegNo, + uint32 *log, uint32 *seg, int wal_segsz_bytes) +{ + sscanf(fname, "%08X%08X%08X", tli, log, seg); + *logSegNo = (uint64) (*log) * XLogSegmentsPerXLogId(wal_segsz_bytes) + *seg; +} + static inline void XLogFilePath(char *path, TimeLineID tli, XLogSegNo logSegNo, int wal_segsz_bytes) { diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 9dbe9ec801..f1a732c172 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -6336,6 +6336,12 @@ proargtypes => 'pg_lsn', proallargtypes => '{pg_lsn,text,int4}', proargmodes => '{i,o,o}', proargnames => '{lsn,file_name,file_offset}', prosrc => 'pg_walfile_name_offset' }, +{ oid => '8205', + descr => 'wal location and timeline ID given a wal filename and byte offset', + proname => 'pg_walfile_offset_lsn', prorettype => 'record', + proargtypes => 'text int4', proallargtypes => '{text,int4,pg_lsn,int4}', + proargmodes => '{i,i,o,o}', proargnames => '{file_name,file_offset,lsn,timeline_id}', + prosrc => 'pg_walfile_offset_lsn' }, { oid => '2851', descr => 'wal filename, given a wal location', proname => 'pg_walfile_name', prorettype => 'text', proargtypes => 'pg_lsn', prosrc => 'pg_walfile_name' }, diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out index 88bb696ded..1b2ab4f25b 100644 --- a/src/test/regress/expected/misc_functions.out +++ b/src/test/regress/expected/misc_functions.out @@ -619,3 +619,29 @@ SELECT count(*) > 0 AS ok FROM pg_control_system(); t (1 row) +-- pg_walfile_name_offset +SELECT * FROM pg_walfile_name_offset('1/F'); + file_name | file_offset +--------------------------+------------- + 000000010000000100000000 | 15 +(1 row) + +-- pg_walfile_offset_lsn +SELECT * FROM pg_walfile_offset_lsn('invalid', 15); +ERROR: invalid WAL file name "invalid" +SELECT * FROM pg_walfile_offset_lsn('0000000100000000FFFFFFFF', 15); +ERROR: invalid WAL file name "0000000100000000FFFFFFFF" +SELECT * FROM pg_walfile_offset_lsn('000000010000000000000000', 15); +ERROR: invalid WAL file name "000000010000000000000000" +SELECT * FROM pg_walfile_offset_lsn('000000000000000100000000', 15); +ERROR: invalid WAL file name "000000000000000100000000" +SELECT * FROM pg_walfile_offset_lsn('000000010000000100000000', -1); +ERROR: offset must not be negative or greater than or equal to WAL segment size +SELECT * FROM pg_walfile_offset_lsn('000000010000000100000000', 2000000000); +ERROR: offset must not be negative or greater than or equal to WAL segment size +SELECT * FROM pg_walfile_offset_lsn('000000010000000100000000', 15); + lsn | timeline_id +-----+------------- + 1/F | 1 +(1 row) + diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql index b07e9e8dbb..9cb59cecf7 100644 --- a/src/test/regress/sql/misc_functions.sql +++ b/src/test/regress/sql/misc_functions.sql @@ -229,3 +229,15 @@ SELECT count(*) > 0 AS ok FROM pg_control_checkpoint(); SELECT count(*) > 0 AS ok FROM pg_control_init(); SELECT count(*) > 0 AS ok FROM pg_control_recovery(); SELECT count(*) > 0 AS ok FROM pg_control_system(); + +-- pg_walfile_name_offset +SELECT * FROM pg_walfile_name_offset('1/F'); + +-- pg_walfile_offset_lsn +SELECT * FROM pg_walfile_offset_lsn('invalid', 15); +SELECT * FROM pg_walfile_offset_lsn('0000000100000000FFFFFFFF', 15); +SELECT * FROM pg_walfile_offset_lsn('000000010000000000000000', 15); +SELECT * FROM pg_walfile_offset_lsn('000000000000000100000000', 15); +SELECT * FROM pg_walfile_offset_lsn('000000010000000100000000', -1); +SELECT * FROM pg_walfile_offset_lsn('000000010000000100000000', 2000000000); +SELECT * FROM pg_walfile_offset_lsn('000000010000000100000000', 15); -- 2.34.1