Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * walsummaryfuncs.c
4 : * SQL-callable functions for accessing WAL summary data.
5 : *
6 : * Portions Copyright (c) 2010-2026, PostgreSQL Global Development Group
7 : *
8 : * src/backend/backup/walsummaryfuncs.c
9 : *
10 : *-------------------------------------------------------------------------
11 : */
12 :
13 : #include "postgres.h"
14 :
15 : #include "access/htup_details.h"
16 : #include "backup/walsummary.h"
17 : #include "common/blkreftable.h"
18 : #include "funcapi.h"
19 : #include "miscadmin.h"
20 : #include "postmaster/walsummarizer.h"
21 : #include "utils/fmgrprotos.h"
22 : #include "utils/pg_lsn.h"
23 : #include "utils/tuplestore.h"
24 :
25 : #define NUM_WS_ATTS 3
26 : #define NUM_SUMMARY_ATTS 6
27 : #define NUM_STATE_ATTS 4
28 : #define MAX_BLOCKS_PER_CALL 256
29 :
30 : /*
31 : * List the WAL summary files available in pg_wal/summaries.
32 : */
33 : Datum
34 4 : pg_available_wal_summaries(PG_FUNCTION_ARGS)
35 : {
36 : ReturnSetInfo *rsi;
37 : List *wslist;
38 : ListCell *lc;
39 : Datum values[NUM_WS_ATTS];
40 : bool nulls[NUM_WS_ATTS];
41 :
42 4 : InitMaterializedSRF(fcinfo, 0);
43 4 : rsi = (ReturnSetInfo *) fcinfo->resultinfo;
44 :
45 4 : memset(nulls, 0, sizeof(nulls));
46 :
47 4 : wslist = GetWalSummaries(0, InvalidXLogRecPtr, InvalidXLogRecPtr);
48 38 : foreach(lc, wslist)
49 : {
50 34 : WalSummaryFile *ws = (WalSummaryFile *) lfirst(lc);
51 : HeapTuple tuple;
52 :
53 34 : CHECK_FOR_INTERRUPTS();
54 :
55 34 : values[0] = Int64GetDatum((int64) ws->tli);
56 34 : values[1] = LSNGetDatum(ws->start_lsn);
57 34 : values[2] = LSNGetDatum(ws->end_lsn);
58 :
59 34 : tuple = heap_form_tuple(rsi->setDesc, values, nulls);
60 34 : tuplestore_puttuple(rsi->setResult, tuple);
61 : }
62 :
63 4 : return (Datum) 0;
64 : }
65 :
66 : /*
67 : * List the contents of a WAL summary file identified by TLI, start LSN,
68 : * and end LSN.
69 : */
70 : Datum
71 9 : pg_wal_summary_contents(PG_FUNCTION_ARGS)
72 : {
73 : ReturnSetInfo *rsi;
74 : Datum values[NUM_SUMMARY_ATTS];
75 : bool nulls[NUM_SUMMARY_ATTS];
76 : WalSummaryFile ws;
77 : WalSummaryIO io;
78 : BlockRefTableReader *reader;
79 : int64 raw_tli;
80 : RelFileLocator rlocator;
81 : ForkNumber forknum;
82 : BlockNumber limit_block;
83 :
84 9 : InitMaterializedSRF(fcinfo, 0);
85 9 : rsi = (ReturnSetInfo *) fcinfo->resultinfo;
86 9 : memset(nulls, 0, sizeof(nulls));
87 :
88 : /*
89 : * Since the timeline could at least in theory be more than 2^31, and
90 : * since we don't have unsigned types at the SQL level, it is passed as a
91 : * 64-bit integer. Test whether it's out of range.
92 : */
93 9 : raw_tli = PG_GETARG_INT64(0);
94 9 : if (raw_tli < 1 || raw_tli > PG_INT32_MAX)
95 0 : ereport(ERROR,
96 : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
97 : errmsg("invalid timeline %" PRId64, raw_tli));
98 :
99 : /* Prepare to read the specified WAL summary file. */
100 9 : ws.tli = (TimeLineID) raw_tli;
101 9 : ws.start_lsn = PG_GETARG_LSN(1);
102 9 : ws.end_lsn = PG_GETARG_LSN(2);
103 9 : io.filepos = 0;
104 9 : io.file = OpenWalSummaryFile(&ws, false);
105 9 : reader = CreateBlockRefTableReader(ReadWalSummary, &io,
106 : FilePathName(io.file),
107 : ReportWalSummaryError, NULL);
108 :
109 : /* Loop over relation forks. */
110 346 : while (BlockRefTableReaderNextRelation(reader, &rlocator, &forknum,
111 : &limit_block))
112 : {
113 : BlockNumber blocks[MAX_BLOCKS_PER_CALL];
114 : HeapTuple tuple;
115 :
116 337 : CHECK_FOR_INTERRUPTS();
117 :
118 337 : values[0] = ObjectIdGetDatum(rlocator.relNumber);
119 337 : values[1] = ObjectIdGetDatum(rlocator.spcOid);
120 337 : values[2] = ObjectIdGetDatum(rlocator.dbOid);
121 337 : values[3] = Int16GetDatum((int16) forknum);
122 :
123 : /*
124 : * If the limit block is not InvalidBlockNumber, emit an extra row
125 : * with that block number and limit_block = true.
126 : *
127 : * There is no point in doing this when the limit_block is
128 : * InvalidBlockNumber, because no block with that number or any higher
129 : * number can ever exist.
130 : */
131 337 : if (BlockNumberIsValid(limit_block))
132 : {
133 17 : values[4] = Int64GetDatum((int64) limit_block);
134 17 : values[5] = BoolGetDatum(true);
135 :
136 17 : tuple = heap_form_tuple(rsi->setDesc, values, nulls);
137 17 : tuplestore_puttuple(rsi->setResult, tuple);
138 : }
139 :
140 : /* Loop over blocks within the current relation fork. */
141 : while (1)
142 330 : {
143 : unsigned nblocks;
144 : unsigned i;
145 :
146 667 : CHECK_FOR_INTERRUPTS();
147 :
148 667 : nblocks = BlockRefTableReaderGetBlocks(reader, blocks,
149 : MAX_BLOCKS_PER_CALL);
150 667 : if (nblocks == 0)
151 337 : break;
152 :
153 : /*
154 : * For each block that we specifically know to have been modified,
155 : * emit a row with that block number and limit_block = false.
156 : */
157 330 : values[5] = BoolGetDatum(false);
158 1366 : for (i = 0; i < nblocks; ++i)
159 : {
160 1036 : values[4] = Int64GetDatum((int64) blocks[i]);
161 :
162 1036 : tuple = heap_form_tuple(rsi->setDesc, values, nulls);
163 1036 : tuplestore_puttuple(rsi->setResult, tuple);
164 : }
165 : }
166 : }
167 :
168 : /* Cleanup */
169 9 : DestroyBlockRefTableReader(reader);
170 9 : FileClose(io.file);
171 :
172 9 : return (Datum) 0;
173 : }
174 :
175 : /*
176 : * Returns information about the state of the WAL summarizer process.
177 : */
178 : Datum
179 0 : pg_get_wal_summarizer_state(PG_FUNCTION_ARGS)
180 : {
181 : Datum values[NUM_STATE_ATTS];
182 : bool nulls[NUM_STATE_ATTS];
183 : TimeLineID summarized_tli;
184 : XLogRecPtr summarized_lsn;
185 : XLogRecPtr pending_lsn;
186 : int summarizer_pid;
187 : TupleDesc tupdesc;
188 : HeapTuple htup;
189 :
190 0 : GetWalSummarizerState(&summarized_tli, &summarized_lsn, &pending_lsn,
191 : &summarizer_pid);
192 :
193 0 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
194 0 : elog(ERROR, "return type must be a row type");
195 :
196 0 : memset(nulls, 0, sizeof(nulls));
197 :
198 0 : values[0] = Int64GetDatum((int64) summarized_tli);
199 0 : values[1] = LSNGetDatum(summarized_lsn);
200 0 : values[2] = LSNGetDatum(pending_lsn);
201 :
202 0 : if (summarizer_pid < 0)
203 0 : nulls[3] = true;
204 : else
205 0 : values[3] = Int32GetDatum(summarizer_pid);
206 :
207 0 : htup = heap_form_tuple(tupdesc, values, nulls);
208 :
209 0 : PG_RETURN_DATUM(HeapTupleGetDatum(htup));
210 : }
|