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