1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * pg_controldata.c |
4 | * |
5 | * Routines to expose the contents of the control data file via |
6 | * a set of SQL functions. |
7 | * |
8 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
9 | * Portions Copyright (c) 1994, Regents of the University of California |
10 | * |
11 | * IDENTIFICATION |
12 | * src/backend/utils/misc/pg_controldata.c |
13 | *------------------------------------------------------------------------- |
14 | */ |
15 | |
16 | #include "postgres.h" |
17 | |
18 | #include "access/htup_details.h" |
19 | #include "access/transam.h" |
20 | #include "access/xlog_internal.h" |
21 | #include "access/xlog.h" |
22 | #include "catalog/pg_control.h" |
23 | #include "catalog/pg_type.h" |
24 | #include "common/controldata_utils.h" |
25 | #include "funcapi.h" |
26 | #include "miscadmin.h" |
27 | #include "utils/builtins.h" |
28 | #include "utils/pg_lsn.h" |
29 | #include "utils/timestamp.h" |
30 | |
31 | Datum |
32 | pg_control_system(PG_FUNCTION_ARGS) |
33 | { |
34 | Datum values[4]; |
35 | bool nulls[4]; |
36 | TupleDesc tupdesc; |
37 | HeapTuple htup; |
38 | ControlFileData *ControlFile; |
39 | bool crc_ok; |
40 | |
41 | /* |
42 | * Construct a tuple descriptor for the result row. This must match this |
43 | * function's pg_proc entry! |
44 | */ |
45 | tupdesc = CreateTemplateTupleDesc(4); |
46 | TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pg_control_version" , |
47 | INT4OID, -1, 0); |
48 | TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catalog_version_no" , |
49 | INT4OID, -1, 0); |
50 | TupleDescInitEntry(tupdesc, (AttrNumber) 3, "system_identifier" , |
51 | INT8OID, -1, 0); |
52 | TupleDescInitEntry(tupdesc, (AttrNumber) 4, "pg_control_last_modified" , |
53 | TIMESTAMPTZOID, -1, 0); |
54 | tupdesc = BlessTupleDesc(tupdesc); |
55 | |
56 | /* read the control file */ |
57 | ControlFile = get_controlfile(DataDir, &crc_ok); |
58 | if (!crc_ok) |
59 | ereport(ERROR, |
60 | (errmsg("calculated CRC checksum does not match value stored in file" ))); |
61 | |
62 | values[0] = Int32GetDatum(ControlFile->pg_control_version); |
63 | nulls[0] = false; |
64 | |
65 | values[1] = Int32GetDatum(ControlFile->catalog_version_no); |
66 | nulls[1] = false; |
67 | |
68 | values[2] = Int64GetDatum(ControlFile->system_identifier); |
69 | nulls[2] = false; |
70 | |
71 | values[3] = TimestampTzGetDatum(time_t_to_timestamptz(ControlFile->time)); |
72 | nulls[3] = false; |
73 | |
74 | htup = heap_form_tuple(tupdesc, values, nulls); |
75 | |
76 | PG_RETURN_DATUM(HeapTupleGetDatum(htup)); |
77 | } |
78 | |
79 | Datum |
80 | pg_control_checkpoint(PG_FUNCTION_ARGS) |
81 | { |
82 | Datum values[19]; |
83 | bool nulls[19]; |
84 | TupleDesc tupdesc; |
85 | HeapTuple htup; |
86 | ControlFileData *ControlFile; |
87 | XLogSegNo segno; |
88 | char xlogfilename[MAXFNAMELEN]; |
89 | bool crc_ok; |
90 | |
91 | /* |
92 | * Construct a tuple descriptor for the result row. This must match this |
93 | * function's pg_proc entry! |
94 | */ |
95 | tupdesc = CreateTemplateTupleDesc(18); |
96 | TupleDescInitEntry(tupdesc, (AttrNumber) 1, "checkpoint_lsn" , |
97 | LSNOID, -1, 0); |
98 | TupleDescInitEntry(tupdesc, (AttrNumber) 2, "redo_lsn" , |
99 | LSNOID, -1, 0); |
100 | TupleDescInitEntry(tupdesc, (AttrNumber) 3, "redo_wal_file" , |
101 | TEXTOID, -1, 0); |
102 | TupleDescInitEntry(tupdesc, (AttrNumber) 4, "timeline_id" , |
103 | INT4OID, -1, 0); |
104 | TupleDescInitEntry(tupdesc, (AttrNumber) 5, "prev_timeline_id" , |
105 | INT4OID, -1, 0); |
106 | TupleDescInitEntry(tupdesc, (AttrNumber) 6, "full_page_writes" , |
107 | BOOLOID, -1, 0); |
108 | TupleDescInitEntry(tupdesc, (AttrNumber) 7, "next_xid" , |
109 | TEXTOID, -1, 0); |
110 | TupleDescInitEntry(tupdesc, (AttrNumber) 8, "next_oid" , |
111 | OIDOID, -1, 0); |
112 | TupleDescInitEntry(tupdesc, (AttrNumber) 9, "next_multixact_id" , |
113 | XIDOID, -1, 0); |
114 | TupleDescInitEntry(tupdesc, (AttrNumber) 10, "next_multi_offset" , |
115 | XIDOID, -1, 0); |
116 | TupleDescInitEntry(tupdesc, (AttrNumber) 11, "oldest_xid" , |
117 | XIDOID, -1, 0); |
118 | TupleDescInitEntry(tupdesc, (AttrNumber) 12, "oldest_xid_dbid" , |
119 | OIDOID, -1, 0); |
120 | TupleDescInitEntry(tupdesc, (AttrNumber) 13, "oldest_active_xid" , |
121 | XIDOID, -1, 0); |
122 | TupleDescInitEntry(tupdesc, (AttrNumber) 14, "oldest_multi_xid" , |
123 | XIDOID, -1, 0); |
124 | TupleDescInitEntry(tupdesc, (AttrNumber) 15, "oldest_multi_dbid" , |
125 | OIDOID, -1, 0); |
126 | TupleDescInitEntry(tupdesc, (AttrNumber) 16, "oldest_commit_ts_xid" , |
127 | XIDOID, -1, 0); |
128 | TupleDescInitEntry(tupdesc, (AttrNumber) 17, "newest_commit_ts_xid" , |
129 | XIDOID, -1, 0); |
130 | TupleDescInitEntry(tupdesc, (AttrNumber) 18, "checkpoint_time" , |
131 | TIMESTAMPTZOID, -1, 0); |
132 | tupdesc = BlessTupleDesc(tupdesc); |
133 | |
134 | /* Read the control file. */ |
135 | ControlFile = get_controlfile(DataDir, &crc_ok); |
136 | if (!crc_ok) |
137 | ereport(ERROR, |
138 | (errmsg("calculated CRC checksum does not match value stored in file" ))); |
139 | |
140 | /* |
141 | * Calculate name of the WAL file containing the latest checkpoint's REDO |
142 | * start point. |
143 | */ |
144 | XLByteToSeg(ControlFile->checkPointCopy.redo, segno, wal_segment_size); |
145 | XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID, |
146 | segno, wal_segment_size); |
147 | |
148 | /* Populate the values and null arrays */ |
149 | values[0] = LSNGetDatum(ControlFile->checkPoint); |
150 | nulls[0] = false; |
151 | |
152 | values[1] = LSNGetDatum(ControlFile->checkPointCopy.redo); |
153 | nulls[1] = false; |
154 | |
155 | values[2] = CStringGetTextDatum(xlogfilename); |
156 | nulls[2] = false; |
157 | |
158 | values[3] = Int32GetDatum(ControlFile->checkPointCopy.ThisTimeLineID); |
159 | nulls[3] = false; |
160 | |
161 | values[4] = Int32GetDatum(ControlFile->checkPointCopy.PrevTimeLineID); |
162 | nulls[4] = false; |
163 | |
164 | values[5] = BoolGetDatum(ControlFile->checkPointCopy.fullPageWrites); |
165 | nulls[5] = false; |
166 | |
167 | values[6] = CStringGetTextDatum(psprintf("%u:%u" , |
168 | EpochFromFullTransactionId(ControlFile->checkPointCopy.nextFullXid), |
169 | XidFromFullTransactionId(ControlFile->checkPointCopy.nextFullXid))); |
170 | nulls[6] = false; |
171 | |
172 | values[7] = ObjectIdGetDatum(ControlFile->checkPointCopy.nextOid); |
173 | nulls[7] = false; |
174 | |
175 | values[8] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMulti); |
176 | nulls[8] = false; |
177 | |
178 | values[9] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMultiOffset); |
179 | nulls[9] = false; |
180 | |
181 | values[10] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestXid); |
182 | nulls[10] = false; |
183 | |
184 | values[11] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestXidDB); |
185 | nulls[11] = false; |
186 | |
187 | values[12] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestActiveXid); |
188 | nulls[12] = false; |
189 | |
190 | values[13] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestMulti); |
191 | nulls[13] = false; |
192 | |
193 | values[14] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestMultiDB); |
194 | nulls[14] = false; |
195 | |
196 | values[15] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestCommitTsXid); |
197 | nulls[15] = false; |
198 | |
199 | values[16] = TransactionIdGetDatum(ControlFile->checkPointCopy.newestCommitTsXid); |
200 | nulls[16] = false; |
201 | |
202 | values[17] = TimestampTzGetDatum( |
203 | time_t_to_timestamptz(ControlFile->checkPointCopy.time)); |
204 | nulls[17] = false; |
205 | |
206 | htup = heap_form_tuple(tupdesc, values, nulls); |
207 | |
208 | PG_RETURN_DATUM(HeapTupleGetDatum(htup)); |
209 | } |
210 | |
211 | Datum |
212 | pg_control_recovery(PG_FUNCTION_ARGS) |
213 | { |
214 | Datum values[5]; |
215 | bool nulls[5]; |
216 | TupleDesc tupdesc; |
217 | HeapTuple htup; |
218 | ControlFileData *ControlFile; |
219 | bool crc_ok; |
220 | |
221 | /* |
222 | * Construct a tuple descriptor for the result row. This must match this |
223 | * function's pg_proc entry! |
224 | */ |
225 | tupdesc = CreateTemplateTupleDesc(5); |
226 | TupleDescInitEntry(tupdesc, (AttrNumber) 1, "min_recovery_end_lsn" , |
227 | LSNOID, -1, 0); |
228 | TupleDescInitEntry(tupdesc, (AttrNumber) 2, "min_recovery_end_timeline" , |
229 | INT4OID, -1, 0); |
230 | TupleDescInitEntry(tupdesc, (AttrNumber) 3, "backup_start_lsn" , |
231 | LSNOID, -1, 0); |
232 | TupleDescInitEntry(tupdesc, (AttrNumber) 4, "backup_end_lsn" , |
233 | LSNOID, -1, 0); |
234 | TupleDescInitEntry(tupdesc, (AttrNumber) 5, "end_of_backup_record_required" , |
235 | BOOLOID, -1, 0); |
236 | tupdesc = BlessTupleDesc(tupdesc); |
237 | |
238 | /* read the control file */ |
239 | ControlFile = get_controlfile(DataDir, &crc_ok); |
240 | if (!crc_ok) |
241 | ereport(ERROR, |
242 | (errmsg("calculated CRC checksum does not match value stored in file" ))); |
243 | |
244 | values[0] = LSNGetDatum(ControlFile->minRecoveryPoint); |
245 | nulls[0] = false; |
246 | |
247 | values[1] = Int32GetDatum(ControlFile->minRecoveryPointTLI); |
248 | nulls[1] = false; |
249 | |
250 | values[2] = LSNGetDatum(ControlFile->backupStartPoint); |
251 | nulls[2] = false; |
252 | |
253 | values[3] = LSNGetDatum(ControlFile->backupEndPoint); |
254 | nulls[3] = false; |
255 | |
256 | values[4] = BoolGetDatum(ControlFile->backupEndRequired); |
257 | nulls[4] = false; |
258 | |
259 | htup = heap_form_tuple(tupdesc, values, nulls); |
260 | |
261 | PG_RETURN_DATUM(HeapTupleGetDatum(htup)); |
262 | } |
263 | |
264 | Datum |
265 | pg_control_init(PG_FUNCTION_ARGS) |
266 | { |
267 | Datum values[12]; |
268 | bool nulls[12]; |
269 | TupleDesc tupdesc; |
270 | HeapTuple htup; |
271 | ControlFileData *ControlFile; |
272 | bool crc_ok; |
273 | |
274 | /* |
275 | * Construct a tuple descriptor for the result row. This must match this |
276 | * function's pg_proc entry! |
277 | */ |
278 | tupdesc = CreateTemplateTupleDesc(12); |
279 | TupleDescInitEntry(tupdesc, (AttrNumber) 1, "max_data_alignment" , |
280 | INT4OID, -1, 0); |
281 | TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database_block_size" , |
282 | INT4OID, -1, 0); |
283 | TupleDescInitEntry(tupdesc, (AttrNumber) 3, "blocks_per_segment" , |
284 | INT4OID, -1, 0); |
285 | TupleDescInitEntry(tupdesc, (AttrNumber) 4, "wal_block_size" , |
286 | INT4OID, -1, 0); |
287 | TupleDescInitEntry(tupdesc, (AttrNumber) 5, "bytes_per_wal_segment" , |
288 | INT4OID, -1, 0); |
289 | TupleDescInitEntry(tupdesc, (AttrNumber) 6, "max_identifier_length" , |
290 | INT4OID, -1, 0); |
291 | TupleDescInitEntry(tupdesc, (AttrNumber) 7, "max_index_columns" , |
292 | INT4OID, -1, 0); |
293 | TupleDescInitEntry(tupdesc, (AttrNumber) 8, "max_toast_chunk_size" , |
294 | INT4OID, -1, 0); |
295 | TupleDescInitEntry(tupdesc, (AttrNumber) 9, "large_object_chunk_size" , |
296 | INT4OID, -1, 0); |
297 | TupleDescInitEntry(tupdesc, (AttrNumber) 10, "float4_pass_by_value" , |
298 | BOOLOID, -1, 0); |
299 | TupleDescInitEntry(tupdesc, (AttrNumber) 11, "float8_pass_by_value" , |
300 | BOOLOID, -1, 0); |
301 | TupleDescInitEntry(tupdesc, (AttrNumber) 12, "data_page_checksum_version" , |
302 | INT4OID, -1, 0); |
303 | tupdesc = BlessTupleDesc(tupdesc); |
304 | |
305 | /* read the control file */ |
306 | ControlFile = get_controlfile(DataDir, &crc_ok); |
307 | if (!crc_ok) |
308 | ereport(ERROR, |
309 | (errmsg("calculated CRC checksum does not match value stored in file" ))); |
310 | |
311 | values[0] = Int32GetDatum(ControlFile->maxAlign); |
312 | nulls[0] = false; |
313 | |
314 | values[1] = Int32GetDatum(ControlFile->blcksz); |
315 | nulls[1] = false; |
316 | |
317 | values[2] = Int32GetDatum(ControlFile->relseg_size); |
318 | nulls[2] = false; |
319 | |
320 | values[3] = Int32GetDatum(ControlFile->xlog_blcksz); |
321 | nulls[3] = false; |
322 | |
323 | values[4] = Int32GetDatum(ControlFile->xlog_seg_size); |
324 | nulls[4] = false; |
325 | |
326 | values[5] = Int32GetDatum(ControlFile->nameDataLen); |
327 | nulls[5] = false; |
328 | |
329 | values[6] = Int32GetDatum(ControlFile->indexMaxKeys); |
330 | nulls[6] = false; |
331 | |
332 | values[7] = Int32GetDatum(ControlFile->toast_max_chunk_size); |
333 | nulls[7] = false; |
334 | |
335 | values[8] = Int32GetDatum(ControlFile->loblksize); |
336 | nulls[8] = false; |
337 | |
338 | values[9] = BoolGetDatum(ControlFile->float4ByVal); |
339 | nulls[9] = false; |
340 | |
341 | values[10] = BoolGetDatum(ControlFile->float8ByVal); |
342 | nulls[10] = false; |
343 | |
344 | values[11] = Int32GetDatum(ControlFile->data_checksum_version); |
345 | nulls[11] = false; |
346 | |
347 | htup = heap_form_tuple(tupdesc, values, nulls); |
348 | |
349 | PG_RETURN_DATUM(HeapTupleGetDatum(htup)); |
350 | } |
351 | |