Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * pg_depend.c
4 : : * routines to support manipulation of the pg_depend relation
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/catalog/pg_depend.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include "access/genam.h"
18 : : #include "access/htup_details.h"
19 : : #include "access/table.h"
20 : : #include "catalog/catalog.h"
21 : : #include "catalog/dependency.h"
22 : : #include "catalog/indexing.h"
23 : : #include "catalog/pg_constraint.h"
24 : : #include "catalog/pg_depend.h"
25 : : #include "catalog/pg_extension.h"
26 : : #include "catalog/partition.h"
27 : : #include "commands/extension.h"
28 : : #include "miscadmin.h"
29 : : #include "utils/fmgroids.h"
30 : : #include "utils/lsyscache.h"
31 : : #include "utils/rel.h"
32 : :
33 : :
34 : : static bool isObjectPinned(const ObjectAddress *object);
35 : :
36 : :
37 : : /*
38 : : * Record a dependency between 2 objects via their respective ObjectAddress.
39 : : * The first argument is the dependent object, the second the one it
40 : : * references.
41 : : *
42 : : * This simply creates an entry in pg_depend, without any other processing.
43 : : */
44 : : void
45 : 49526 : recordDependencyOn(const ObjectAddress *depender,
46 : : const ObjectAddress *referenced,
47 : : DependencyType behavior)
48 : : {
49 : 49526 : recordMultipleDependencies(depender, referenced, 1, behavior);
50 : 49526 : }
51 : :
52 : : /*
53 : : * Record multiple dependencies (of the same kind) for a single dependent
54 : : * object. This has a little less overhead than recording each separately.
55 : : */
56 : : void
57 : 86166 : recordMultipleDependencies(const ObjectAddress *depender,
58 : : const ObjectAddress *referenced,
59 : : int nreferenced,
60 : : DependencyType behavior)
61 : : {
62 : 86166 : Relation dependDesc;
63 : 86166 : CatalogIndexState indstate;
64 : 86166 : TupleTableSlot **slot;
65 : 86166 : int i,
66 : : max_slots,
67 : : slot_init_count,
68 : : slot_stored_count;
69 : :
70 [ + + ]: 86166 : if (nreferenced <= 0)
71 : 3647 : return; /* nothing to do */
72 : :
73 : : /*
74 : : * During bootstrap, do nothing since pg_depend may not exist yet.
75 : : *
76 : : * Objects created during bootstrap are most likely pinned, and the few
77 : : * that are not do not have dependencies on each other, so that there
78 : : * would be no need to make a pg_depend entry anyway.
79 : : */
80 [ + + ]: 82519 : if (IsBootstrapProcessingMode())
81 : 677 : return;
82 : :
83 : 81842 : dependDesc = table_open(DependRelationId, RowExclusiveLock);
84 : :
85 : : /*
86 : : * Allocate the slots to use, but delay costly initialization until we
87 : : * know that they will be used.
88 : : */
89 [ + - ]: 81842 : max_slots = Min(nreferenced,
90 : : MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_depend));
91 : 81842 : slot = palloc_array(TupleTableSlot *, max_slots);
92 : :
93 : : /* Don't open indexes unless we need to make an update */
94 : 81842 : indstate = NULL;
95 : :
96 : : /* number of slots currently storing tuples */
97 : 81842 : slot_stored_count = 0;
98 : : /* number of slots currently initialized */
99 : 81842 : slot_init_count = 0;
100 [ + + ]: 232237 : for (i = 0; i < nreferenced; i++, referenced++)
101 : : {
102 : : /*
103 : : * If the referenced object is pinned by the system, there's no real
104 : : * need to record dependencies on it. This saves lots of space in
105 : : * pg_depend, so it's worth the time taken to check.
106 : : */
107 [ + + ]: 150395 : if (isObjectPinned(referenced))
108 : 102860 : continue;
109 : :
110 [ - + ]: 47535 : if (slot_init_count < max_slots)
111 : : {
112 : 47535 : slot[slot_stored_count] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc),
113 : : &TTSOpsHeapTuple);
114 : 47535 : slot_init_count++;
115 : 47535 : }
116 : :
117 : 47535 : ExecClearTuple(slot[slot_stored_count]);
118 : :
119 : : /*
120 : : * Record the dependency. Note we don't bother to check for duplicate
121 : : * dependencies; there's no harm in them.
122 : : */
123 : 47535 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
124 : 47535 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
125 : 47535 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
126 : 47535 : slot[slot_stored_count]->tts_values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
127 : 47535 : slot[slot_stored_count]->tts_values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
128 : 47535 : slot[slot_stored_count]->tts_values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
129 : 47535 : slot[slot_stored_count]->tts_values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
130 : :
131 : 47535 : memset(slot[slot_stored_count]->tts_isnull, false,
132 : : slot[slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));
133 : :
134 : 47535 : ExecStoreVirtualTuple(slot[slot_stored_count]);
135 : 47535 : slot_stored_count++;
136 : :
137 : : /* If slots are full, insert a batch of tuples */
138 [ + + ]: 47535 : if (slot_stored_count == max_slots)
139 : : {
140 : : /* fetch index info only when we know we need it */
141 [ - + ]: 34005 : if (indstate == NULL)
142 : 34005 : indstate = CatalogOpenIndexes(dependDesc);
143 : :
144 : 68010 : CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
145 : 34005 : indstate);
146 : 34005 : slot_stored_count = 0;
147 : 34005 : }
148 : 47535 : }
149 : :
150 : : /* Insert any tuples left in the buffer */
151 [ + + ]: 81842 : if (slot_stored_count > 0)
152 : : {
153 : : /* fetch index info only when we know we need it */
154 [ - + ]: 7802 : if (indstate == NULL)
155 : 7802 : indstate = CatalogOpenIndexes(dependDesc);
156 : :
157 : 15604 : CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
158 : 7802 : indstate);
159 : 7802 : }
160 : :
161 [ + + ]: 81842 : if (indstate != NULL)
162 : 41807 : CatalogCloseIndexes(indstate);
163 : :
164 : 81842 : table_close(dependDesc, RowExclusiveLock);
165 : :
166 : : /* Drop only the number of slots used */
167 [ + + ]: 129377 : for (i = 0; i < slot_init_count; i++)
168 : 47535 : ExecDropSingleTupleTableSlot(slot[i]);
169 : 81842 : pfree(slot);
170 [ - + ]: 86166 : }
171 : :
172 : : /*
173 : : * If we are executing a CREATE EXTENSION operation, mark the given object
174 : : * as being a member of the extension, or check that it already is one.
175 : : * Otherwise, do nothing.
176 : : *
177 : : * This must be called during creation of any user-definable object type
178 : : * that could be a member of an extension.
179 : : *
180 : : * isReplace must be true if the object already existed, and false if it is
181 : : * newly created. In the former case we insist that it already be a member
182 : : * of the current extension. In the latter case we can skip checking whether
183 : : * it is already a member of any extension.
184 : : *
185 : : * Note: isReplace = true is typically used when updating an object in
186 : : * CREATE OR REPLACE and similar commands. We used to allow the target
187 : : * object to not already be an extension member, instead silently absorbing
188 : : * it into the current extension. However, this was both error-prone
189 : : * (extensions might accidentally overwrite free-standing objects) and
190 : : * a security hazard (since the object would retain its previous ownership).
191 : : */
192 : : void
193 : 20322 : recordDependencyOnCurrentExtension(const ObjectAddress *object,
194 : : bool isReplace)
195 : : {
196 : : /* Only whole objects can be extension members */
197 [ + - ]: 20322 : Assert(object->objectSubId == 0);
198 : :
199 [ + + ]: 20322 : if (creating_extension)
200 : : {
201 : 12 : ObjectAddress extension;
202 : :
203 : : /* Only need to check for existing membership if isReplace */
204 [ + - ]: 12 : if (isReplace)
205 : : {
206 : 0 : Oid oldext;
207 : :
208 : : /*
209 : : * Side note: these catalog lookups are safe only because the
210 : : * object is a pre-existing one. In the not-isReplace case, the
211 : : * caller has most likely not yet done a CommandCounterIncrement
212 : : * that would make the new object visible.
213 : : */
214 : 0 : oldext = getExtensionOfObject(object->classId, object->objectId);
215 [ # # ]: 0 : if (OidIsValid(oldext))
216 : : {
217 : : /* If already a member of this extension, nothing to do */
218 [ # # ]: 0 : if (oldext == CurrentExtensionObject)
219 : 0 : return;
220 : : /* Already a member of some other extension, so reject */
221 [ # # # # ]: 0 : ereport(ERROR,
222 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
223 : : errmsg("%s is already a member of extension \"%s\"",
224 : : getObjectDescription(object, false),
225 : : get_extension_name(oldext))));
226 : 0 : }
227 : : /* It's a free-standing object, so reject */
228 [ # # # # ]: 0 : ereport(ERROR,
229 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
230 : : errmsg("%s is not a member of extension \"%s\"",
231 : : getObjectDescription(object, false),
232 : : get_extension_name(CurrentExtensionObject)),
233 : : errdetail("An extension is not allowed to replace an object that it does not own.")));
234 [ # # ]: 0 : }
235 : :
236 : : /* OK, record it as a member of CurrentExtensionObject */
237 : 12 : extension.classId = ExtensionRelationId;
238 : 12 : extension.objectId = CurrentExtensionObject;
239 : 12 : extension.objectSubId = 0;
240 : :
241 : 12 : recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION);
242 [ - - + ]: 12 : }
243 : 20322 : }
244 : :
245 : : /*
246 : : * If we are executing a CREATE EXTENSION operation, check that the given
247 : : * object is a member of the extension, and throw an error if it isn't.
248 : : * Otherwise, do nothing.
249 : : *
250 : : * This must be called whenever a CREATE IF NOT EXISTS operation (for an
251 : : * object type that can be an extension member) has found that an object of
252 : : * the desired name already exists. It is insecure for an extension to use
253 : : * IF NOT EXISTS except when the conflicting object is already an extension
254 : : * member; otherwise a hostile user could substitute an object with arbitrary
255 : : * properties.
256 : : */
257 : : void
258 : 16 : checkMembershipInCurrentExtension(const ObjectAddress *object)
259 : : {
260 : : /*
261 : : * This is actually the same condition tested in
262 : : * recordDependencyOnCurrentExtension; but we want to issue a
263 : : * differently-worded error, and anyway it would be pretty confusing to
264 : : * call recordDependencyOnCurrentExtension in these circumstances.
265 : : */
266 : :
267 : : /* Only whole objects can be extension members */
268 [ + - ]: 16 : Assert(object->objectSubId == 0);
269 : :
270 [ + - ]: 16 : if (creating_extension)
271 : : {
272 : 0 : Oid oldext;
273 : :
274 : 0 : oldext = getExtensionOfObject(object->classId, object->objectId);
275 : : /* If already a member of this extension, OK */
276 [ # # ]: 0 : if (oldext == CurrentExtensionObject)
277 : 0 : return;
278 : : /* Else complain */
279 [ # # # # ]: 0 : ereport(ERROR,
280 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
281 : : errmsg("%s is not a member of extension \"%s\"",
282 : : getObjectDescription(object, false),
283 : : get_extension_name(CurrentExtensionObject)),
284 : : errdetail("An extension may only use CREATE ... IF NOT EXISTS to skip object creation if the conflicting object is one that it already owns.")));
285 [ # # # ]: 0 : }
286 : 16 : }
287 : :
288 : : /*
289 : : * deleteDependencyRecordsFor -- delete all records with given depender
290 : : * classId/objectId. Returns the number of records deleted.
291 : : *
292 : : * This is used when redefining an existing object. Links leading to the
293 : : * object do not change, and links leading from it will be recreated
294 : : * (possibly with some differences from before).
295 : : *
296 : : * If skipExtensionDeps is true, we do not delete any dependencies that
297 : : * show that the given object is a member of an extension. This avoids
298 : : * needing a lot of extra logic to fetch and recreate that dependency.
299 : : */
300 : : long
301 : 684 : deleteDependencyRecordsFor(Oid classId, Oid objectId,
302 : : bool skipExtensionDeps)
303 : : {
304 : 684 : long count = 0;
305 : 684 : Relation depRel;
306 : 684 : ScanKeyData key[2];
307 : 684 : SysScanDesc scan;
308 : 684 : HeapTuple tup;
309 : :
310 : 684 : depRel = table_open(DependRelationId, RowExclusiveLock);
311 : :
312 : 1368 : ScanKeyInit(&key[0],
313 : : Anum_pg_depend_classid,
314 : : BTEqualStrategyNumber, F_OIDEQ,
315 : 684 : ObjectIdGetDatum(classId));
316 : 1368 : ScanKeyInit(&key[1],
317 : : Anum_pg_depend_objid,
318 : : BTEqualStrategyNumber, F_OIDEQ,
319 : 684 : ObjectIdGetDatum(objectId));
320 : :
321 : 1368 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
322 : 684 : NULL, 2, key);
323 : :
324 [ + + ]: 1535 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
325 : : {
326 [ + + + - ]: 851 : if (skipExtensionDeps &&
327 : 387 : ((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION)
328 : 0 : continue;
329 : :
330 : 851 : CatalogTupleDelete(depRel, &tup->t_self);
331 : 851 : count++;
332 : : }
333 : :
334 : 684 : systable_endscan(scan);
335 : :
336 : 684 : table_close(depRel, RowExclusiveLock);
337 : :
338 : 1368 : return count;
339 : 684 : }
340 : :
341 : : /*
342 : : * deleteDependencyRecordsForClass -- delete all records with given depender
343 : : * classId/objectId, dependee classId, and deptype.
344 : : * Returns the number of records deleted.
345 : : *
346 : : * This is a variant of deleteDependencyRecordsFor, useful when revoking
347 : : * an object property that is expressed by a dependency record (such as
348 : : * extension membership).
349 : : */
350 : : long
351 : 640 : deleteDependencyRecordsForClass(Oid classId, Oid objectId,
352 : : Oid refclassId, char deptype)
353 : : {
354 : 640 : long count = 0;
355 : 640 : Relation depRel;
356 : 640 : ScanKeyData key[2];
357 : 640 : SysScanDesc scan;
358 : 640 : HeapTuple tup;
359 : :
360 : 640 : depRel = table_open(DependRelationId, RowExclusiveLock);
361 : :
362 : 1280 : ScanKeyInit(&key[0],
363 : : Anum_pg_depend_classid,
364 : : BTEqualStrategyNumber, F_OIDEQ,
365 : 640 : ObjectIdGetDatum(classId));
366 : 1280 : ScanKeyInit(&key[1],
367 : : Anum_pg_depend_objid,
368 : : BTEqualStrategyNumber, F_OIDEQ,
369 : 640 : ObjectIdGetDatum(objectId));
370 : :
371 : 1280 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
372 : 640 : NULL, 2, key);
373 : :
374 [ + + ]: 1909 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
375 : : {
376 : 1269 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
377 : :
378 [ + + + + ]: 1269 : if (depform->refclassid == refclassId && depform->deptype == deptype)
379 : : {
380 : 291 : CatalogTupleDelete(depRel, &tup->t_self);
381 : 291 : count++;
382 : 291 : }
383 : 1269 : }
384 : :
385 : 640 : systable_endscan(scan);
386 : :
387 : 640 : table_close(depRel, RowExclusiveLock);
388 : :
389 : 1280 : return count;
390 : 640 : }
391 : :
392 : : /*
393 : : * deleteDependencyRecordsForSpecific -- delete all records with given depender
394 : : * classId/objectId, dependee classId/objectId, of the given deptype.
395 : : * Returns the number of records deleted.
396 : : */
397 : : long
398 : 13 : deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype,
399 : : Oid refclassId, Oid refobjectId)
400 : : {
401 : 13 : long count = 0;
402 : 13 : Relation depRel;
403 : 13 : ScanKeyData key[2];
404 : 13 : SysScanDesc scan;
405 : 13 : HeapTuple tup;
406 : :
407 : 13 : depRel = table_open(DependRelationId, RowExclusiveLock);
408 : :
409 : 26 : ScanKeyInit(&key[0],
410 : : Anum_pg_depend_classid,
411 : : BTEqualStrategyNumber, F_OIDEQ,
412 : 13 : ObjectIdGetDatum(classId));
413 : 26 : ScanKeyInit(&key[1],
414 : : Anum_pg_depend_objid,
415 : : BTEqualStrategyNumber, F_OIDEQ,
416 : 13 : ObjectIdGetDatum(objectId));
417 : :
418 : 26 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
419 : 13 : NULL, 2, key);
420 : :
421 [ + + ]: 73 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
422 : : {
423 : 60 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
424 : :
425 [ + + ]: 60 : if (depform->refclassid == refclassId &&
426 [ + - - + ]: 13 : depform->refobjid == refobjectId &&
427 : 13 : depform->deptype == deptype)
428 : : {
429 : 13 : CatalogTupleDelete(depRel, &tup->t_self);
430 : 13 : count++;
431 : 13 : }
432 : 60 : }
433 : :
434 : 13 : systable_endscan(scan);
435 : :
436 : 13 : table_close(depRel, RowExclusiveLock);
437 : :
438 : 26 : return count;
439 : 13 : }
440 : :
441 : : /*
442 : : * Adjust dependency record(s) to point to a different object of the same type
443 : : *
444 : : * classId/objectId specify the referencing object.
445 : : * refClassId/oldRefObjectId specify the old referenced object.
446 : : * newRefObjectId is the new referenced object (must be of class refClassId).
447 : : *
448 : : * Note the lack of objsubid parameters. If there are subobject references
449 : : * they will all be readjusted. Also, there is an expectation that we are
450 : : * dealing with NORMAL dependencies: if we have to replace an (implicit)
451 : : * dependency on a pinned object with an explicit dependency on an unpinned
452 : : * one, the new one will be NORMAL.
453 : : *
454 : : * Returns the number of records updated -- zero indicates a problem.
455 : : */
456 : : long
457 : 54 : changeDependencyFor(Oid classId, Oid objectId,
458 : : Oid refClassId, Oid oldRefObjectId,
459 : : Oid newRefObjectId)
460 : : {
461 : 54 : long count = 0;
462 : 54 : Relation depRel;
463 : 54 : ScanKeyData key[2];
464 : 54 : SysScanDesc scan;
465 : 54 : HeapTuple tup;
466 : 54 : ObjectAddress objAddr;
467 : 54 : ObjectAddress depAddr;
468 : 54 : bool oldIsPinned;
469 : 54 : bool newIsPinned;
470 : :
471 : : /*
472 : : * Check to see if either oldRefObjectId or newRefObjectId is pinned.
473 : : * Pinned objects should not have any dependency entries pointing to them,
474 : : * so in these cases we should add or remove a pg_depend entry, or do
475 : : * nothing at all, rather than update an entry as in the normal case.
476 : : */
477 : 54 : objAddr.classId = refClassId;
478 : 54 : objAddr.objectId = oldRefObjectId;
479 : 54 : objAddr.objectSubId = 0;
480 : :
481 : 54 : oldIsPinned = isObjectPinned(&objAddr);
482 : :
483 : 54 : objAddr.objectId = newRefObjectId;
484 : :
485 : 54 : newIsPinned = isObjectPinned(&objAddr);
486 : :
487 [ + + ]: 54 : if (oldIsPinned)
488 : : {
489 : : /*
490 : : * If both are pinned, we need do nothing. However, return 1 not 0,
491 : : * else callers will think this is an error case.
492 : : */
493 [ - + ]: 9 : if (newIsPinned)
494 : 0 : return 1;
495 : :
496 : : /*
497 : : * There is no old dependency record, but we should insert a new one.
498 : : * Assume a normal dependency is wanted.
499 : : */
500 : 9 : depAddr.classId = classId;
501 : 9 : depAddr.objectId = objectId;
502 : 9 : depAddr.objectSubId = 0;
503 : 9 : recordDependencyOn(&depAddr, &objAddr, DEPENDENCY_NORMAL);
504 : :
505 : 9 : return 1;
506 : : }
507 : :
508 : 45 : depRel = table_open(DependRelationId, RowExclusiveLock);
509 : :
510 : : /* There should be existing dependency record(s), so search. */
511 : 90 : ScanKeyInit(&key[0],
512 : : Anum_pg_depend_classid,
513 : : BTEqualStrategyNumber, F_OIDEQ,
514 : 45 : ObjectIdGetDatum(classId));
515 : 90 : ScanKeyInit(&key[1],
516 : : Anum_pg_depend_objid,
517 : : BTEqualStrategyNumber, F_OIDEQ,
518 : 45 : ObjectIdGetDatum(objectId));
519 : :
520 : 90 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
521 : 45 : NULL, 2, key);
522 : :
523 [ + + ]: 114 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
524 : : {
525 : 69 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
526 : :
527 [ + + - + ]: 69 : if (depform->refclassid == refClassId &&
528 : 45 : depform->refobjid == oldRefObjectId)
529 : : {
530 [ + + ]: 45 : if (newIsPinned)
531 : 11 : CatalogTupleDelete(depRel, &tup->t_self);
532 : : else
533 : : {
534 : : /* make a modifiable copy */
535 : 34 : tup = heap_copytuple(tup);
536 : 34 : depform = (Form_pg_depend) GETSTRUCT(tup);
537 : :
538 : 34 : depform->refobjid = newRefObjectId;
539 : :
540 : 34 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
541 : :
542 : 34 : heap_freetuple(tup);
543 : : }
544 : :
545 : 45 : count++;
546 : 45 : }
547 : 69 : }
548 : :
549 : 45 : systable_endscan(scan);
550 : :
551 : 45 : table_close(depRel, RowExclusiveLock);
552 : :
553 : 45 : return count;
554 : 54 : }
555 : :
556 : : /*
557 : : * Adjust all dependency records to come from a different object of the same type
558 : : *
559 : : * classId/oldObjectId specify the old referencing object.
560 : : * newObjectId is the new referencing object (must be of class classId).
561 : : *
562 : : * Returns the number of records updated.
563 : : */
564 : : long
565 : 96 : changeDependenciesOf(Oid classId, Oid oldObjectId,
566 : : Oid newObjectId)
567 : : {
568 : 96 : long count = 0;
569 : 96 : Relation depRel;
570 : 96 : ScanKeyData key[2];
571 : 96 : SysScanDesc scan;
572 : 96 : HeapTuple tup;
573 : :
574 : 96 : depRel = table_open(DependRelationId, RowExclusiveLock);
575 : :
576 : 192 : ScanKeyInit(&key[0],
577 : : Anum_pg_depend_classid,
578 : : BTEqualStrategyNumber, F_OIDEQ,
579 : 96 : ObjectIdGetDatum(classId));
580 : 192 : ScanKeyInit(&key[1],
581 : : Anum_pg_depend_objid,
582 : : BTEqualStrategyNumber, F_OIDEQ,
583 : 96 : ObjectIdGetDatum(oldObjectId));
584 : :
585 : 192 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
586 : 96 : NULL, 2, key);
587 : :
588 [ + + ]: 268 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
589 : : {
590 : 172 : Form_pg_depend depform;
591 : :
592 : : /* make a modifiable copy */
593 : 172 : tup = heap_copytuple(tup);
594 : 172 : depform = (Form_pg_depend) GETSTRUCT(tup);
595 : :
596 : 172 : depform->objid = newObjectId;
597 : :
598 : 172 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
599 : :
600 : 172 : heap_freetuple(tup);
601 : :
602 : 172 : count++;
603 : 172 : }
604 : :
605 : 96 : systable_endscan(scan);
606 : :
607 : 96 : table_close(depRel, RowExclusiveLock);
608 : :
609 : 192 : return count;
610 : 96 : }
611 : :
612 : : /*
613 : : * Adjust all dependency records to point to a different object of the same type
614 : : *
615 : : * refClassId/oldRefObjectId specify the old referenced object.
616 : : * newRefObjectId is the new referenced object (must be of class refClassId).
617 : : *
618 : : * Returns the number of records updated.
619 : : */
620 : : long
621 : 96 : changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
622 : : Oid newRefObjectId)
623 : : {
624 : 96 : long count = 0;
625 : 96 : Relation depRel;
626 : 96 : ScanKeyData key[2];
627 : 96 : SysScanDesc scan;
628 : 96 : HeapTuple tup;
629 : 96 : ObjectAddress objAddr;
630 : 96 : bool newIsPinned;
631 : :
632 : 96 : depRel = table_open(DependRelationId, RowExclusiveLock);
633 : :
634 : : /*
635 : : * If oldRefObjectId is pinned, there won't be any dependency entries on
636 : : * it --- we can't cope in that case. (This isn't really worth expending
637 : : * code to fix, in current usage; it just means you can't rename stuff out
638 : : * of pg_catalog, which would likely be a bad move anyway.)
639 : : */
640 : 96 : objAddr.classId = refClassId;
641 : 96 : objAddr.objectId = oldRefObjectId;
642 : 96 : objAddr.objectSubId = 0;
643 : :
644 [ + - ]: 96 : if (isObjectPinned(&objAddr))
645 [ # # # # ]: 0 : ereport(ERROR,
646 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
647 : : errmsg("cannot remove dependency on %s because it is a system object",
648 : : getObjectDescription(&objAddr, false))));
649 : :
650 : : /*
651 : : * We can handle adding a dependency on something pinned, though, since
652 : : * that just means deleting the dependency entry.
653 : : */
654 : 96 : objAddr.objectId = newRefObjectId;
655 : :
656 : 96 : newIsPinned = isObjectPinned(&objAddr);
657 : :
658 : : /* Now search for dependency records */
659 : 192 : ScanKeyInit(&key[0],
660 : : Anum_pg_depend_refclassid,
661 : : BTEqualStrategyNumber, F_OIDEQ,
662 : 96 : ObjectIdGetDatum(refClassId));
663 : 192 : ScanKeyInit(&key[1],
664 : : Anum_pg_depend_refobjid,
665 : : BTEqualStrategyNumber, F_OIDEQ,
666 : 96 : ObjectIdGetDatum(oldRefObjectId));
667 : :
668 : 192 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
669 : 96 : NULL, 2, key);
670 : :
671 [ + + ]: 98 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
672 : : {
673 [ - + ]: 2 : if (newIsPinned)
674 : 0 : CatalogTupleDelete(depRel, &tup->t_self);
675 : : else
676 : : {
677 : 2 : Form_pg_depend depform;
678 : :
679 : : /* make a modifiable copy */
680 : 2 : tup = heap_copytuple(tup);
681 : 2 : depform = (Form_pg_depend) GETSTRUCT(tup);
682 : :
683 : 2 : depform->refobjid = newRefObjectId;
684 : :
685 : 2 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
686 : :
687 : 2 : heap_freetuple(tup);
688 : 2 : }
689 : :
690 : 2 : count++;
691 : : }
692 : :
693 : 96 : systable_endscan(scan);
694 : :
695 : 96 : table_close(depRel, RowExclusiveLock);
696 : :
697 : 192 : return count;
698 : 96 : }
699 : :
700 : : /*
701 : : * isObjectPinned()
702 : : *
703 : : * Test if an object is required for basic database functionality.
704 : : *
705 : : * The passed subId, if any, is ignored; we assume that only whole objects
706 : : * are pinned (and that this implies pinning their components).
707 : : */
708 : : static bool
709 : 150695 : isObjectPinned(const ObjectAddress *object)
710 : : {
711 : 150695 : return IsPinnedObject(object->classId, object->objectId);
712 : : }
713 : :
714 : :
715 : : /*
716 : : * Various special-purpose lookups and manipulations of pg_depend.
717 : : */
718 : :
719 : :
720 : : /*
721 : : * Find the extension containing the specified object, if any
722 : : *
723 : : * Returns the OID of the extension, or InvalidOid if the object does not
724 : : * belong to any extension.
725 : : *
726 : : * Extension membership is marked by an EXTENSION dependency from the object
727 : : * to the extension. Note that the result will be indeterminate if pg_depend
728 : : * contains links from this object to more than one extension ... but that
729 : : * should never happen.
730 : : */
731 : : Oid
732 : 0 : getExtensionOfObject(Oid classId, Oid objectId)
733 : : {
734 : 0 : Oid result = InvalidOid;
735 : 0 : Relation depRel;
736 : 0 : ScanKeyData key[2];
737 : 0 : SysScanDesc scan;
738 : 0 : HeapTuple tup;
739 : :
740 : 0 : depRel = table_open(DependRelationId, AccessShareLock);
741 : :
742 : 0 : ScanKeyInit(&key[0],
743 : : Anum_pg_depend_classid,
744 : : BTEqualStrategyNumber, F_OIDEQ,
745 : 0 : ObjectIdGetDatum(classId));
746 : 0 : ScanKeyInit(&key[1],
747 : : Anum_pg_depend_objid,
748 : : BTEqualStrategyNumber, F_OIDEQ,
749 : 0 : ObjectIdGetDatum(objectId));
750 : :
751 : 0 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
752 : 0 : NULL, 2, key);
753 : :
754 [ # # ]: 0 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
755 : : {
756 : 0 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
757 : :
758 [ # # # # ]: 0 : if (depform->refclassid == ExtensionRelationId &&
759 : 0 : depform->deptype == DEPENDENCY_EXTENSION)
760 : : {
761 : 0 : result = depform->refobjid;
762 : 0 : break; /* no need to keep scanning */
763 : : }
764 [ # # # ]: 0 : }
765 : :
766 : 0 : systable_endscan(scan);
767 : :
768 : 0 : table_close(depRel, AccessShareLock);
769 : :
770 : 0 : return result;
771 : 0 : }
772 : :
773 : : /*
774 : : * Return (possibly NIL) list of extensions that the given object depends on
775 : : * in DEPENDENCY_AUTO_EXTENSION mode.
776 : : */
777 : : List *
778 : 0 : getAutoExtensionsOfObject(Oid classId, Oid objectId)
779 : : {
780 : 0 : List *result = NIL;
781 : 0 : Relation depRel;
782 : 0 : ScanKeyData key[2];
783 : 0 : SysScanDesc scan;
784 : 0 : HeapTuple tup;
785 : :
786 : 0 : depRel = table_open(DependRelationId, AccessShareLock);
787 : :
788 : 0 : ScanKeyInit(&key[0],
789 : : Anum_pg_depend_classid,
790 : : BTEqualStrategyNumber, F_OIDEQ,
791 : 0 : ObjectIdGetDatum(classId));
792 : 0 : ScanKeyInit(&key[1],
793 : : Anum_pg_depend_objid,
794 : : BTEqualStrategyNumber, F_OIDEQ,
795 : 0 : ObjectIdGetDatum(objectId));
796 : :
797 : 0 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
798 : 0 : NULL, 2, key);
799 : :
800 [ # # ]: 0 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
801 : : {
802 : 0 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
803 : :
804 [ # # # # ]: 0 : if (depform->refclassid == ExtensionRelationId &&
805 : 0 : depform->deptype == DEPENDENCY_AUTO_EXTENSION)
806 : 0 : result = lappend_oid(result, depform->refobjid);
807 : 0 : }
808 : :
809 : 0 : systable_endscan(scan);
810 : :
811 : 0 : table_close(depRel, AccessShareLock);
812 : :
813 : 0 : return result;
814 : 0 : }
815 : :
816 : : /*
817 : : * Detect whether a sequence is marked as "owned" by a column
818 : : *
819 : : * An ownership marker is an AUTO or INTERNAL dependency from the sequence to the
820 : : * column. If we find one, store the identity of the owning column
821 : : * into *tableId and *colId and return true; else return false.
822 : : *
823 : : * Note: if there's more than one such pg_depend entry then you get
824 : : * a random one of them returned into the out parameters. This should
825 : : * not happen, though.
826 : : */
827 : : bool
828 : 103 : sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
829 : : {
830 : 103 : bool ret = false;
831 : 103 : Relation depRel;
832 : 103 : ScanKeyData key[2];
833 : 103 : SysScanDesc scan;
834 : 103 : HeapTuple tup;
835 : :
836 : 103 : depRel = table_open(DependRelationId, AccessShareLock);
837 : :
838 : 206 : ScanKeyInit(&key[0],
839 : : Anum_pg_depend_classid,
840 : : BTEqualStrategyNumber, F_OIDEQ,
841 : 103 : ObjectIdGetDatum(RelationRelationId));
842 : 206 : ScanKeyInit(&key[1],
843 : : Anum_pg_depend_objid,
844 : : BTEqualStrategyNumber, F_OIDEQ,
845 : 103 : ObjectIdGetDatum(seqId));
846 : :
847 : 206 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
848 : 103 : NULL, 2, key);
849 : :
850 [ + + ]: 205 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
851 : : {
852 : 104 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
853 : :
854 [ + + - + ]: 104 : if (depform->refclassid == RelationRelationId &&
855 : 2 : depform->deptype == deptype)
856 : : {
857 : 2 : *tableId = depform->refobjid;
858 : 2 : *colId = depform->refobjsubid;
859 : 2 : ret = true;
860 : 2 : break; /* no need to keep scanning */
861 : : }
862 [ - + + ]: 104 : }
863 : :
864 : 103 : systable_endscan(scan);
865 : :
866 : 103 : table_close(depRel, AccessShareLock);
867 : :
868 : 206 : return ret;
869 : 103 : }
870 : :
871 : : /*
872 : : * Collect a list of OIDs of all sequences owned by the specified relation,
873 : : * and column if specified. If deptype is not zero, then only find sequences
874 : : * with the specified dependency type.
875 : : */
876 : : static List *
877 : 120 : getOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype)
878 : : {
879 : 120 : List *result = NIL;
880 : 120 : Relation depRel;
881 : 120 : ScanKeyData key[3];
882 : 120 : SysScanDesc scan;
883 : 120 : HeapTuple tup;
884 : :
885 : 120 : depRel = table_open(DependRelationId, AccessShareLock);
886 : :
887 : 240 : ScanKeyInit(&key[0],
888 : : Anum_pg_depend_refclassid,
889 : : BTEqualStrategyNumber, F_OIDEQ,
890 : 120 : ObjectIdGetDatum(RelationRelationId));
891 : 240 : ScanKeyInit(&key[1],
892 : : Anum_pg_depend_refobjid,
893 : : BTEqualStrategyNumber, F_OIDEQ,
894 : 120 : ObjectIdGetDatum(relid));
895 [ + + ]: 120 : if (attnum)
896 : 210 : ScanKeyInit(&key[2],
897 : : Anum_pg_depend_refobjsubid,
898 : : BTEqualStrategyNumber, F_INT4EQ,
899 : 105 : Int32GetDatum(attnum));
900 : :
901 : 240 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
902 : 120 : NULL, attnum ? 3 : 2, key);
903 : :
904 [ + + ]: 400 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
905 : : {
906 : 280 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
907 : :
908 : : /*
909 : : * We assume any auto or internal dependency of a sequence on a column
910 : : * must be what we are looking for. (We need the relkind test because
911 : : * indexes can also have auto dependencies on columns.)
912 : : */
913 [ + + ]: 280 : if (deprec->classid == RelationRelationId &&
914 [ + - ]: 122 : deprec->objsubid == 0 &&
915 [ + + ]: 122 : deprec->refobjsubid != 0 &&
916 [ + + - + ]: 117 : (deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) &&
917 : 117 : get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
918 : : {
919 [ + + + + ]: 117 : if (!deptype || deprec->deptype == deptype)
920 : 116 : result = lappend_oid(result, deprec->objid);
921 : 117 : }
922 : 280 : }
923 : :
924 : 120 : systable_endscan(scan);
925 : :
926 : 120 : table_close(depRel, AccessShareLock);
927 : :
928 : 240 : return result;
929 : 120 : }
930 : :
931 : : /*
932 : : * Collect a list of OIDs of all sequences owned (identity or serial) by the
933 : : * specified relation.
934 : : */
935 : : List *
936 : 15 : getOwnedSequences(Oid relid)
937 : : {
938 : 15 : return getOwnedSequences_internal(relid, 0, 0);
939 : : }
940 : :
941 : : /*
942 : : * Get owned identity sequence, error if not exactly one.
943 : : */
944 : : Oid
945 : 105 : getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
946 : : {
947 : 105 : Oid relid = RelationGetRelid(rel);
948 : 105 : List *seqlist;
949 : :
950 : : /*
951 : : * The identity sequence is associated with the topmost partitioned table,
952 : : * which might have column order different than the given partition.
953 : : */
954 [ + + ]: 105 : if (RelationGetForm(rel)->relispartition)
955 : : {
956 : 9 : List *ancestors = get_partition_ancestors(relid);
957 : 9 : const char *attname = get_attname(relid, attnum, false);
958 : :
959 : 9 : relid = llast_oid(ancestors);
960 : 9 : attnum = get_attnum(relid, attname);
961 [ + - ]: 9 : if (attnum == InvalidAttrNumber)
962 [ # # # # ]: 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
963 : : attname, relid);
964 : 9 : list_free(ancestors);
965 : 9 : }
966 : :
967 : 105 : seqlist = getOwnedSequences_internal(relid, attnum, DEPENDENCY_INTERNAL);
968 [ + - ]: 105 : if (list_length(seqlist) > 1)
969 [ # # # # ]: 0 : elog(ERROR, "more than one owned sequence found");
970 [ + + ]: 105 : else if (seqlist == NIL)
971 : : {
972 [ + - ]: 2 : if (missing_ok)
973 : 2 : return InvalidOid;
974 : : else
975 [ # # # # ]: 0 : elog(ERROR, "no owned sequence found");
976 : 0 : }
977 : :
978 : 103 : return linitial_oid(seqlist);
979 : 105 : }
980 : :
981 : : /*
982 : : * get_index_constraint
983 : : * Given the OID of an index, return the OID of the owning unique,
984 : : * primary-key, or exclusion constraint, or InvalidOid if there
985 : : * is no owning constraint.
986 : : */
987 : : Oid
988 : 543 : get_index_constraint(Oid indexId)
989 : : {
990 : 543 : Oid constraintId = InvalidOid;
991 : 543 : Relation depRel;
992 : 543 : ScanKeyData key[3];
993 : 543 : SysScanDesc scan;
994 : 543 : HeapTuple tup;
995 : :
996 : : /* Search the dependency table for the index */
997 : 543 : depRel = table_open(DependRelationId, AccessShareLock);
998 : :
999 : 1086 : ScanKeyInit(&key[0],
1000 : : Anum_pg_depend_classid,
1001 : : BTEqualStrategyNumber, F_OIDEQ,
1002 : 543 : ObjectIdGetDatum(RelationRelationId));
1003 : 1086 : ScanKeyInit(&key[1],
1004 : : Anum_pg_depend_objid,
1005 : : BTEqualStrategyNumber, F_OIDEQ,
1006 : 543 : ObjectIdGetDatum(indexId));
1007 : 1086 : ScanKeyInit(&key[2],
1008 : : Anum_pg_depend_objsubid,
1009 : : BTEqualStrategyNumber, F_INT4EQ,
1010 : 543 : Int32GetDatum(0));
1011 : :
1012 : 1086 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
1013 : 543 : NULL, 3, key);
1014 : :
1015 [ + + ]: 903 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
1016 : : {
1017 : 600 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
1018 : :
1019 : : /*
1020 : : * We assume any internal dependency on a constraint must be what we
1021 : : * are looking for.
1022 : : */
1023 [ + + ]: 600 : if (deprec->refclassid == ConstraintRelationId &&
1024 [ + - - + ]: 240 : deprec->refobjsubid == 0 &&
1025 : 240 : deprec->deptype == DEPENDENCY_INTERNAL)
1026 : : {
1027 : 240 : constraintId = deprec->refobjid;
1028 : 240 : break;
1029 : : }
1030 [ - + + ]: 600 : }
1031 : :
1032 : 543 : systable_endscan(scan);
1033 : 543 : table_close(depRel, AccessShareLock);
1034 : :
1035 : 1086 : return constraintId;
1036 : 543 : }
1037 : :
1038 : : /*
1039 : : * get_index_ref_constraints
1040 : : * Given the OID of an index, return the OID of all foreign key
1041 : : * constraints which reference the index.
1042 : : */
1043 : : List *
1044 : 48 : get_index_ref_constraints(Oid indexId)
1045 : : {
1046 : 48 : List *result = NIL;
1047 : 48 : Relation depRel;
1048 : 48 : ScanKeyData key[3];
1049 : 48 : SysScanDesc scan;
1050 : 48 : HeapTuple tup;
1051 : :
1052 : : /* Search the dependency table for the index */
1053 : 48 : depRel = table_open(DependRelationId, AccessShareLock);
1054 : :
1055 : 96 : ScanKeyInit(&key[0],
1056 : : Anum_pg_depend_refclassid,
1057 : : BTEqualStrategyNumber, F_OIDEQ,
1058 : 48 : ObjectIdGetDatum(RelationRelationId));
1059 : 96 : ScanKeyInit(&key[1],
1060 : : Anum_pg_depend_refobjid,
1061 : : BTEqualStrategyNumber, F_OIDEQ,
1062 : 48 : ObjectIdGetDatum(indexId));
1063 : 96 : ScanKeyInit(&key[2],
1064 : : Anum_pg_depend_refobjsubid,
1065 : : BTEqualStrategyNumber, F_INT4EQ,
1066 : 48 : Int32GetDatum(0));
1067 : :
1068 : 96 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
1069 : 48 : NULL, 3, key);
1070 : :
1071 [ + + ]: 50 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
1072 : : {
1073 : 2 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
1074 : :
1075 : : /*
1076 : : * We assume any normal dependency from a constraint must be what we
1077 : : * are looking for.
1078 : : */
1079 [ + - ]: 2 : if (deprec->classid == ConstraintRelationId &&
1080 [ + - - + ]: 2 : deprec->objsubid == 0 &&
1081 : 2 : deprec->deptype == DEPENDENCY_NORMAL)
1082 : : {
1083 : 2 : result = lappend_oid(result, deprec->objid);
1084 : 2 : }
1085 : 2 : }
1086 : :
1087 : 48 : systable_endscan(scan);
1088 : 48 : table_close(depRel, AccessShareLock);
1089 : :
1090 : 96 : return result;
1091 : 48 : }
|