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