Line data Source code
1 : /*
2 : * pg_upgrade_support.c
3 : *
4 : * server-side functions to set backend global variables
5 : * to control oid and relfilenumber assignment, and do other special
6 : * hacks needed for pg_upgrade.
7 : *
8 : * Copyright (c) 2010-2026, PostgreSQL Global Development Group
9 : * src/backend/utils/adt/pg_upgrade_support.c
10 : */
11 :
12 : #include "postgres.h"
13 :
14 : #include "access/relation.h"
15 : #include "access/table.h"
16 : #include "catalog/binary_upgrade.h"
17 : #include "catalog/heap.h"
18 : #include "catalog/namespace.h"
19 : #include "catalog/pg_subscription_rel.h"
20 : #include "catalog/pg_type.h"
21 : #include "commands/extension.h"
22 : #include "miscadmin.h"
23 : #include "replication/logical.h"
24 : #include "replication/logicallauncher.h"
25 : #include "replication/origin.h"
26 : #include "replication/worker_internal.h"
27 : #include "storage/lmgr.h"
28 : #include "utils/array.h"
29 : #include "utils/builtins.h"
30 : #include "utils/lsyscache.h"
31 : #include "utils/pg_lsn.h"
32 :
33 :
34 : #define CHECK_IS_BINARY_UPGRADE \
35 : do { \
36 : if (!IsBinaryUpgrade) \
37 : ereport(ERROR, \
38 : (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), \
39 : errmsg("function can only be called when server is in binary upgrade mode"))); \
40 : } while (0)
41 :
42 : Datum
43 8 : binary_upgrade_set_next_pg_tablespace_oid(PG_FUNCTION_ARGS)
44 : {
45 8 : Oid tbspoid = PG_GETARG_OID(0);
46 :
47 8 : CHECK_IS_BINARY_UPGRADE;
48 8 : binary_upgrade_next_pg_tablespace_oid = tbspoid;
49 :
50 8 : PG_RETURN_VOID();
51 : }
52 :
53 : Datum
54 1750 : binary_upgrade_set_next_pg_type_oid(PG_FUNCTION_ARGS)
55 : {
56 1750 : Oid typoid = PG_GETARG_OID(0);
57 :
58 1750 : CHECK_IS_BINARY_UPGRADE;
59 1750 : binary_upgrade_next_pg_type_oid = typoid;
60 :
61 1750 : PG_RETURN_VOID();
62 : }
63 :
64 : Datum
65 1748 : binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
66 : {
67 1748 : Oid typoid = PG_GETARG_OID(0);
68 :
69 1748 : CHECK_IS_BINARY_UPGRADE;
70 1748 : binary_upgrade_next_array_pg_type_oid = typoid;
71 :
72 1748 : PG_RETURN_VOID();
73 : }
74 :
75 : Datum
76 12 : binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
77 : {
78 12 : Oid typoid = PG_GETARG_OID(0);
79 :
80 12 : CHECK_IS_BINARY_UPGRADE;
81 12 : binary_upgrade_next_mrng_pg_type_oid = typoid;
82 :
83 12 : PG_RETURN_VOID();
84 : }
85 :
86 : Datum
87 12 : binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
88 : {
89 12 : Oid typoid = PG_GETARG_OID(0);
90 :
91 12 : CHECK_IS_BINARY_UPGRADE;
92 12 : binary_upgrade_next_mrng_array_pg_type_oid = typoid;
93 :
94 12 : PG_RETURN_VOID();
95 : }
96 :
97 : Datum
98 1766 : binary_upgrade_set_next_heap_pg_class_oid(PG_FUNCTION_ARGS)
99 : {
100 1766 : Oid reloid = PG_GETARG_OID(0);
101 :
102 1766 : CHECK_IS_BINARY_UPGRADE;
103 1766 : binary_upgrade_next_heap_pg_class_oid = reloid;
104 :
105 1766 : PG_RETURN_VOID();
106 : }
107 :
108 : Datum
109 1574 : binary_upgrade_set_next_heap_relfilenode(PG_FUNCTION_ARGS)
110 : {
111 1574 : RelFileNumber relfilenumber = PG_GETARG_OID(0);
112 :
113 1574 : CHECK_IS_BINARY_UPGRADE;
114 1574 : binary_upgrade_next_heap_pg_class_relfilenumber = relfilenumber;
115 :
116 1574 : PG_RETURN_VOID();
117 : }
118 :
119 : Datum
120 1114 : binary_upgrade_set_next_index_pg_class_oid(PG_FUNCTION_ARGS)
121 : {
122 1114 : Oid reloid = PG_GETARG_OID(0);
123 :
124 1114 : CHECK_IS_BINARY_UPGRADE;
125 1114 : binary_upgrade_next_index_pg_class_oid = reloid;
126 :
127 1114 : PG_RETURN_VOID();
128 : }
129 :
130 : Datum
131 1234 : binary_upgrade_set_next_index_relfilenode(PG_FUNCTION_ARGS)
132 : {
133 1234 : RelFileNumber relfilenumber = PG_GETARG_OID(0);
134 :
135 1234 : CHECK_IS_BINARY_UPGRADE;
136 1234 : binary_upgrade_next_index_pg_class_relfilenumber = relfilenumber;
137 :
138 1234 : PG_RETURN_VOID();
139 : }
140 :
141 : Datum
142 540 : binary_upgrade_set_next_toast_pg_class_oid(PG_FUNCTION_ARGS)
143 : {
144 540 : Oid reloid = PG_GETARG_OID(0);
145 :
146 540 : CHECK_IS_BINARY_UPGRADE;
147 540 : binary_upgrade_next_toast_pg_class_oid = reloid;
148 :
149 540 : PG_RETURN_VOID();
150 : }
151 :
152 : Datum
153 540 : binary_upgrade_set_next_toast_relfilenode(PG_FUNCTION_ARGS)
154 : {
155 540 : RelFileNumber relfilenumber = PG_GETARG_OID(0);
156 :
157 540 : CHECK_IS_BINARY_UPGRADE;
158 540 : binary_upgrade_next_toast_pg_class_relfilenumber = relfilenumber;
159 :
160 540 : PG_RETURN_VOID();
161 : }
162 :
163 : Datum
164 100 : binary_upgrade_set_next_pg_enum_oid(PG_FUNCTION_ARGS)
165 : {
166 100 : Oid enumoid = PG_GETARG_OID(0);
167 :
168 100 : CHECK_IS_BINARY_UPGRADE;
169 100 : binary_upgrade_next_pg_enum_oid = enumoid;
170 :
171 100 : PG_RETURN_VOID();
172 : }
173 :
174 : Datum
175 34 : binary_upgrade_set_next_pg_authid_oid(PG_FUNCTION_ARGS)
176 : {
177 34 : Oid authoid = PG_GETARG_OID(0);
178 :
179 34 : CHECK_IS_BINARY_UPGRADE;
180 34 : binary_upgrade_next_pg_authid_oid = authoid;
181 34 : PG_RETURN_VOID();
182 : }
183 :
184 : Datum
185 8 : binary_upgrade_create_empty_extension(PG_FUNCTION_ARGS)
186 : {
187 : text *extName;
188 : text *schemaName;
189 : bool relocatable;
190 : text *extVersion;
191 : Datum extConfig;
192 : Datum extCondition;
193 : List *requiredExtensions;
194 :
195 8 : CHECK_IS_BINARY_UPGRADE;
196 :
197 : /* We must check these things before dereferencing the arguments */
198 8 : if (PG_ARGISNULL(0) ||
199 8 : PG_ARGISNULL(1) ||
200 8 : PG_ARGISNULL(2) ||
201 8 : PG_ARGISNULL(3))
202 0 : elog(ERROR, "null argument to binary_upgrade_create_empty_extension is not allowed");
203 :
204 8 : extName = PG_GETARG_TEXT_PP(0);
205 8 : schemaName = PG_GETARG_TEXT_PP(1);
206 8 : relocatable = PG_GETARG_BOOL(2);
207 8 : extVersion = PG_GETARG_TEXT_PP(3);
208 :
209 8 : if (PG_ARGISNULL(4))
210 8 : extConfig = PointerGetDatum(NULL);
211 : else
212 0 : extConfig = PG_GETARG_DATUM(4);
213 :
214 8 : if (PG_ARGISNULL(5))
215 8 : extCondition = PointerGetDatum(NULL);
216 : else
217 0 : extCondition = PG_GETARG_DATUM(5);
218 :
219 8 : requiredExtensions = NIL;
220 8 : if (!PG_ARGISNULL(6))
221 : {
222 8 : ArrayType *textArray = PG_GETARG_ARRAYTYPE_P(6);
223 : Datum *textDatums;
224 : int ndatums;
225 : int i;
226 :
227 8 : deconstruct_array_builtin(textArray, TEXTOID, &textDatums, NULL, &ndatums);
228 8 : for (i = 0; i < ndatums; i++)
229 : {
230 0 : char *extName = TextDatumGetCString(textDatums[i]);
231 0 : Oid extOid = get_extension_oid(extName, false);
232 :
233 0 : requiredExtensions = lappend_oid(requiredExtensions, extOid);
234 : }
235 : }
236 :
237 8 : InsertExtensionTuple(text_to_cstring(extName),
238 : GetUserId(),
239 8 : get_namespace_oid(text_to_cstring(schemaName), false),
240 : relocatable,
241 8 : text_to_cstring(extVersion),
242 : extConfig,
243 : extCondition,
244 : requiredExtensions);
245 :
246 8 : PG_RETURN_VOID();
247 : }
248 :
249 : Datum
250 0 : binary_upgrade_set_record_init_privs(PG_FUNCTION_ARGS)
251 : {
252 0 : bool record_init_privs = PG_GETARG_BOOL(0);
253 :
254 0 : CHECK_IS_BINARY_UPGRADE;
255 0 : binary_upgrade_record_init_privs = record_init_privs;
256 :
257 0 : PG_RETURN_VOID();
258 : }
259 :
260 : Datum
261 4 : binary_upgrade_set_missing_value(PG_FUNCTION_ARGS)
262 : {
263 4 : Oid table_id = PG_GETARG_OID(0);
264 4 : text *attname = PG_GETARG_TEXT_P(1);
265 4 : text *value = PG_GETARG_TEXT_P(2);
266 4 : char *cattname = text_to_cstring(attname);
267 4 : char *cvalue = text_to_cstring(value);
268 :
269 4 : CHECK_IS_BINARY_UPGRADE;
270 4 : SetAttrMissing(table_id, cattname, cvalue);
271 :
272 4 : PG_RETURN_VOID();
273 : }
274 :
275 : /*
276 : * Verify the given slot has already consumed all the WAL changes.
277 : *
278 : * Returns true if there are no decodable WAL records after the
279 : * confirmed_flush_lsn. Otherwise false.
280 : *
281 : * This is a special purpose function to ensure that the given slot can be
282 : * upgraded without data loss.
283 : */
284 : Datum
285 6 : binary_upgrade_check_logical_slot_pending_wal(PG_FUNCTION_ARGS)
286 : {
287 : Name slot_name;
288 : XLogRecPtr end_of_wal;
289 : XLogRecPtr scan_cutoff_lsn;
290 : XLogRecPtr last_pending_wal;
291 :
292 6 : CHECK_IS_BINARY_UPGRADE;
293 :
294 : /*
295 : * Binary upgrades only allowed super-user connections so we must have
296 : * permission to use replication slots.
297 : */
298 : Assert(has_rolreplication(GetUserId()));
299 :
300 6 : slot_name = PG_GETARG_NAME(0);
301 6 : scan_cutoff_lsn = PG_GETARG_LSN(1);
302 :
303 : /* Acquire the given slot */
304 6 : ReplicationSlotAcquire(NameStr(*slot_name), true, true);
305 :
306 : Assert(SlotIsLogical(MyReplicationSlot));
307 :
308 : /* Slots must be valid as otherwise we won't be able to scan the WAL */
309 : Assert(MyReplicationSlot->data.invalidated == RS_INVAL_NONE);
310 :
311 6 : end_of_wal = GetFlushRecPtr(NULL);
312 6 : last_pending_wal = LogicalReplicationSlotCheckPendingWal(end_of_wal,
313 : scan_cutoff_lsn);
314 :
315 : /* Clean up */
316 6 : ReplicationSlotRelease();
317 :
318 6 : if (XLogRecPtrIsValid(last_pending_wal))
319 2 : PG_RETURN_LSN(last_pending_wal);
320 : else
321 4 : PG_RETURN_NULL();
322 : }
323 :
324 : /*
325 : * binary_upgrade_add_sub_rel_state
326 : *
327 : * Add the relation with the specified relation state to pg_subscription_rel
328 : * catalog.
329 : */
330 : Datum
331 6 : binary_upgrade_add_sub_rel_state(PG_FUNCTION_ARGS)
332 : {
333 : Relation subrel;
334 : Relation rel;
335 : Oid subid;
336 : char *subname;
337 : Oid relid;
338 : char relstate;
339 : XLogRecPtr sublsn;
340 :
341 6 : CHECK_IS_BINARY_UPGRADE;
342 :
343 : /* We must check these things before dereferencing the arguments */
344 6 : if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2))
345 0 : elog(ERROR, "null argument to binary_upgrade_add_sub_rel_state is not allowed");
346 :
347 6 : subname = text_to_cstring(PG_GETARG_TEXT_PP(0));
348 6 : relid = PG_GETARG_OID(1);
349 6 : relstate = PG_GETARG_CHAR(2);
350 6 : sublsn = PG_ARGISNULL(3) ? InvalidXLogRecPtr : PG_GETARG_LSN(3);
351 :
352 6 : subrel = table_open(SubscriptionRelationId, RowExclusiveLock);
353 6 : subid = get_subscription_oid(subname, false);
354 6 : rel = relation_open(relid, AccessShareLock);
355 :
356 : /*
357 : * Since there are no concurrent ALTER/DROP SUBSCRIPTION commands during
358 : * the upgrade process, and the apply worker (which builds cache based on
359 : * the subscription catalog) is not running, the locks can be released
360 : * immediately.
361 : */
362 6 : AddSubscriptionRelState(subid, relid, relstate, sublsn, false);
363 6 : relation_close(rel, AccessShareLock);
364 6 : table_close(subrel, RowExclusiveLock);
365 :
366 6 : PG_RETURN_VOID();
367 : }
368 :
369 : /*
370 : * binary_upgrade_replorigin_advance
371 : *
372 : * Update the remote_lsn for the subscriber's replication origin.
373 : */
374 : Datum
375 2 : binary_upgrade_replorigin_advance(PG_FUNCTION_ARGS)
376 : {
377 : Relation rel;
378 : Oid subid;
379 : char *subname;
380 : char originname[NAMEDATALEN];
381 : ReplOriginId node;
382 : XLogRecPtr remote_commit;
383 :
384 2 : CHECK_IS_BINARY_UPGRADE;
385 :
386 : /*
387 : * We must ensure a non-NULL subscription name before dereferencing the
388 : * arguments.
389 : */
390 2 : if (PG_ARGISNULL(0))
391 0 : elog(ERROR, "null argument to binary_upgrade_replorigin_advance is not allowed");
392 :
393 2 : subname = text_to_cstring(PG_GETARG_TEXT_PP(0));
394 2 : remote_commit = PG_ARGISNULL(1) ? InvalidXLogRecPtr : PG_GETARG_LSN(1);
395 :
396 2 : rel = table_open(SubscriptionRelationId, RowExclusiveLock);
397 2 : subid = get_subscription_oid(subname, false);
398 :
399 2 : ReplicationOriginNameForLogicalRep(subid, InvalidOid, originname, sizeof(originname));
400 :
401 : /* Lock to prevent the replication origin from vanishing */
402 2 : LockRelationOid(ReplicationOriginRelationId, RowExclusiveLock);
403 2 : node = replorigin_by_name(originname, false);
404 :
405 : /*
406 : * The server will be stopped after setting up the objects in the new
407 : * cluster and the origins will be flushed during the shutdown checkpoint.
408 : * This will ensure that the latest LSN values for origin will be
409 : * available after the upgrade.
410 : */
411 2 : replorigin_advance(node, remote_commit, InvalidXLogRecPtr,
412 : false /* backward */ ,
413 : false /* WAL log */ );
414 :
415 2 : UnlockRelationOid(ReplicationOriginRelationId, RowExclusiveLock);
416 2 : table_close(rel, RowExclusiveLock);
417 :
418 2 : PG_RETURN_VOID();
419 : }
420 :
421 : /*
422 : * binary_upgrade_create_conflict_detection_slot
423 : *
424 : * Create a replication slot to retain information necessary for conflict
425 : * detection such as dead tuples, commit timestamps, and origins.
426 : */
427 : Datum
428 2 : binary_upgrade_create_conflict_detection_slot(PG_FUNCTION_ARGS)
429 : {
430 2 : CHECK_IS_BINARY_UPGRADE;
431 :
432 2 : CreateConflictDetectionSlot();
433 :
434 2 : ReplicationSlotRelease();
435 :
436 2 : PG_RETURN_VOID();
437 : }
|