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/pg_type.h"
27 : #include "catalog/partition.h"
28 : #include "commands/extension.h"
29 : #include "miscadmin.h"
30 : #include "storage/lmgr.h"
31 : #include "storage/lock.h"
32 : #include "utils/fmgroids.h"
33 : #include "utils/lsyscache.h"
34 : #include "utils/rel.h"
35 : #include "utils/snapmgr.h"
36 : #include "utils/syscache.h"
37 :
38 :
39 : static bool isObjectPinned(const ObjectAddress *object);
40 : static void dependencyLockAndCheckObject(Oid classId, Oid objectId);
41 :
42 :
43 : /*
44 : * Record a dependency between 2 objects via their respective ObjectAddress.
45 : * The first argument is the dependent object, the second the one it
46 : * references.
47 : *
48 : * This simply creates an entry in pg_depend, without any other processing.
49 : */
50 : void
51 538778 : recordDependencyOn(const ObjectAddress *depender,
52 : const ObjectAddress *referenced,
53 : DependencyType behavior)
54 : {
55 538778 : recordMultipleDependencies(depender, referenced, 1, behavior);
56 538776 : }
57 :
58 : /*
59 : * Record multiple dependencies (of the same kind) for a single dependent
60 : * object. This has a little less overhead than recording each separately.
61 : */
62 : void
63 828013 : recordMultipleDependencies(const ObjectAddress *depender,
64 : const ObjectAddress *referenced,
65 : int nreferenced,
66 : DependencyType behavior)
67 : {
68 : Relation dependDesc;
69 : CatalogIndexState indstate;
70 : TupleTableSlot **slot;
71 : int i,
72 : max_slots,
73 : slot_init_count,
74 : slot_stored_count;
75 :
76 828013 : if (nreferenced <= 0)
77 33446 : return; /* nothing to do */
78 :
79 : /*
80 : * During bootstrap, do nothing since pg_depend may not exist yet.
81 : *
82 : * Objects created during bootstrap are most likely pinned, and the few
83 : * that are not do not have dependencies on each other, so that there
84 : * would be no need to make a pg_depend entry anyway.
85 : */
86 794567 : if (IsBootstrapProcessingMode())
87 41154 : return;
88 :
89 753413 : dependDesc = table_open(DependRelationId, RowExclusiveLock);
90 :
91 : /*
92 : * Allocate the slots to use, but delay costly initialization until we
93 : * know that they will be used.
94 : */
95 753413 : max_slots = Min(nreferenced,
96 : MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_depend));
97 753413 : slot = palloc_array(TupleTableSlot *, max_slots);
98 :
99 : /* Don't open indexes unless we need to make an update */
100 753413 : indstate = NULL;
101 :
102 : /* number of slots currently storing tuples */
103 753413 : slot_stored_count = 0;
104 : /* number of slots currently initialized */
105 753413 : slot_init_count = 0;
106 2186023 : for (i = 0; i < nreferenced; i++, referenced++)
107 : {
108 : /*
109 : * If the referenced object is pinned by the system, there's no real
110 : * need to record dependencies on it. This saves lots of space in
111 : * pg_depend, so it's worth the time taken to check.
112 : */
113 1432617 : if (isObjectPinned(referenced))
114 1033488 : continue;
115 :
116 : /*
117 : * Make sure the new referenced object doesn't go away while we record
118 : * the dependency. DROP routines should lock the object exclusively
119 : * before they check dependencies.
120 : */
121 399129 : dependencyLockAndCheckObject(referenced->classId, referenced->objectId);
122 :
123 399122 : if (slot_init_count < max_slots)
124 : {
125 399122 : slot[slot_stored_count] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc),
126 : &TTSOpsHeapTuple);
127 399122 : slot_init_count++;
128 : }
129 :
130 399122 : ExecClearTuple(slot[slot_stored_count]);
131 :
132 : /*
133 : * Record the dependency. Note we don't bother to check for duplicate
134 : * dependencies; there's no harm in them.
135 : */
136 399122 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
137 399122 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
138 399122 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
139 399122 : slot[slot_stored_count]->tts_values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
140 399122 : slot[slot_stored_count]->tts_values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
141 399122 : slot[slot_stored_count]->tts_values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
142 399122 : slot[slot_stored_count]->tts_values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
143 :
144 399122 : memset(slot[slot_stored_count]->tts_isnull, false,
145 399122 : slot[slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));
146 :
147 399122 : ExecStoreVirtualTuple(slot[slot_stored_count]);
148 399122 : slot_stored_count++;
149 :
150 : /* If slots are full, insert a batch of tuples */
151 399122 : if (slot_stored_count == max_slots)
152 : {
153 : /* fetch index info only when we know we need it */
154 281223 : if (indstate == NULL)
155 281223 : indstate = CatalogOpenIndexes(dependDesc);
156 :
157 281223 : CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
158 : indstate);
159 281223 : slot_stored_count = 0;
160 : }
161 : }
162 :
163 : /* Insert any tuples left in the buffer */
164 753406 : if (slot_stored_count > 0)
165 : {
166 : /* fetch index info only when we know we need it */
167 61366 : if (indstate == NULL)
168 61366 : indstate = CatalogOpenIndexes(dependDesc);
169 :
170 61366 : CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
171 : indstate);
172 : }
173 :
174 753406 : if (indstate != NULL)
175 342589 : CatalogCloseIndexes(indstate);
176 :
177 753406 : table_close(dependDesc, RowExclusiveLock);
178 :
179 : /* Drop only the number of slots used */
180 1152528 : for (i = 0; i < slot_init_count; i++)
181 399122 : ExecDropSingleTupleTableSlot(slot[i]);
182 753406 : pfree(slot);
183 : }
184 :
185 : /*
186 : * If we are executing a CREATE EXTENSION operation, mark the given object
187 : * as being a member of the extension, or check that it already is one.
188 : * Otherwise, do nothing.
189 : *
190 : * This must be called during creation of any user-definable object type
191 : * that could be a member of an extension.
192 : *
193 : * isReplace must be true if the object already existed, and false if it is
194 : * newly created. In the former case we insist that it already be a member
195 : * of the current extension. In the latter case we can skip checking whether
196 : * it is already a member of any extension.
197 : *
198 : * Note: isReplace = true is typically used when updating an object in
199 : * CREATE OR REPLACE and similar commands. We used to allow the target
200 : * object to not already be an extension member, instead silently absorbing
201 : * it into the current extension. However, this was both error-prone
202 : * (extensions might accidentally overwrite free-standing objects) and
203 : * a security hazard (since the object would retain its previous ownership).
204 : */
205 : void
206 203136 : recordDependencyOnCurrentExtension(const ObjectAddress *object,
207 : bool isReplace)
208 : {
209 : /* Only whole objects can be extension members */
210 : Assert(object->objectSubId == 0);
211 :
212 203136 : if (creating_extension)
213 : {
214 : ObjectAddress extension;
215 :
216 : /* Only need to check for existing membership if isReplace */
217 5916 : if (isReplace)
218 : {
219 : Oid oldext;
220 :
221 : /*
222 : * Side note: these catalog lookups are safe only because the
223 : * object is a pre-existing one. In the not-isReplace case, the
224 : * caller has most likely not yet done a CommandCounterIncrement
225 : * that would make the new object visible.
226 : */
227 401 : oldext = getExtensionOfObject(object->classId, object->objectId);
228 401 : if (OidIsValid(oldext))
229 : {
230 : /* If already a member of this extension, nothing to do */
231 397 : if (oldext == CurrentExtensionObject)
232 397 : return;
233 : /* Already a member of some other extension, so reject */
234 0 : ereport(ERROR,
235 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
236 : errmsg("%s is already a member of extension \"%s\"",
237 : getObjectDescription(object, false),
238 : get_extension_name(oldext))));
239 : }
240 : /* It's a free-standing object, so reject */
241 4 : ereport(ERROR,
242 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
243 : errmsg("%s is not a member of extension \"%s\"",
244 : getObjectDescription(object, false),
245 : get_extension_name(CurrentExtensionObject)),
246 : errdetail("An extension is not allowed to replace an object that it does not own.")));
247 : }
248 :
249 : /* OK, record it as a member of CurrentExtensionObject */
250 5515 : extension.classId = ExtensionRelationId;
251 5515 : extension.objectId = CurrentExtensionObject;
252 5515 : extension.objectSubId = 0;
253 :
254 5515 : recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION);
255 : }
256 : }
257 :
258 : /*
259 : * If we are executing a CREATE EXTENSION operation, check that the given
260 : * object is a member of the extension, and throw an error if it isn't.
261 : * Otherwise, do nothing.
262 : *
263 : * This must be called whenever a CREATE IF NOT EXISTS operation (for an
264 : * object type that can be an extension member) has found that an object of
265 : * the desired name already exists. It is insecure for an extension to use
266 : * IF NOT EXISTS except when the conflicting object is already an extension
267 : * member; otherwise a hostile user could substitute an object with arbitrary
268 : * properties.
269 : */
270 : void
271 85 : checkMembershipInCurrentExtension(const ObjectAddress *object)
272 : {
273 : /*
274 : * This is actually the same condition tested in
275 : * recordDependencyOnCurrentExtension; but we want to issue a
276 : * differently-worded error, and anyway it would be pretty confusing to
277 : * call recordDependencyOnCurrentExtension in these circumstances.
278 : */
279 :
280 : /* Only whole objects can be extension members */
281 : Assert(object->objectSubId == 0);
282 :
283 85 : if (creating_extension)
284 : {
285 : Oid oldext;
286 :
287 14 : oldext = getExtensionOfObject(object->classId, object->objectId);
288 : /* If already a member of this extension, OK */
289 14 : if (oldext == CurrentExtensionObject)
290 7 : return;
291 : /* Else complain */
292 7 : ereport(ERROR,
293 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
294 : errmsg("%s is not a member of extension \"%s\"",
295 : getObjectDescription(object, false),
296 : get_extension_name(CurrentExtensionObject)),
297 : errdetail("An extension may only use CREATE ... IF NOT EXISTS to skip object creation if the conflicting object is one that it already owns.")));
298 : }
299 : }
300 :
301 : /*
302 : * deleteDependencyRecordsFor -- delete all records with given depender
303 : * classId/objectId. Returns the number of records deleted.
304 : *
305 : * This is used when redefining an existing object. Links leading to the
306 : * object do not change, and links leading from it will be recreated
307 : * (possibly with some differences from before).
308 : *
309 : * If skipExtensionDeps is true, we do not delete any dependencies that
310 : * show that the given object is a member of an extension. This avoids
311 : * needing a lot of extra logic to fetch and recreate that dependency.
312 : */
313 : long
314 10786 : deleteDependencyRecordsFor(Oid classId, Oid objectId,
315 : bool skipExtensionDeps)
316 : {
317 10786 : long count = 0;
318 : Relation depRel;
319 : ScanKeyData key[2];
320 : SysScanDesc scan;
321 : HeapTuple tup;
322 :
323 10786 : depRel = table_open(DependRelationId, RowExclusiveLock);
324 :
325 10786 : ScanKeyInit(&key[0],
326 : Anum_pg_depend_classid,
327 : BTEqualStrategyNumber, F_OIDEQ,
328 : ObjectIdGetDatum(classId));
329 10786 : ScanKeyInit(&key[1],
330 : Anum_pg_depend_objid,
331 : BTEqualStrategyNumber, F_OIDEQ,
332 : ObjectIdGetDatum(objectId));
333 :
334 10786 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
335 : NULL, 2, key);
336 :
337 18848 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
338 : {
339 8062 : if (skipExtensionDeps &&
340 5909 : ((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION)
341 635 : continue;
342 :
343 7427 : CatalogTupleDelete(depRel, &tup->t_self);
344 7427 : count++;
345 : }
346 :
347 10786 : systable_endscan(scan);
348 :
349 10786 : table_close(depRel, RowExclusiveLock);
350 :
351 10786 : return count;
352 : }
353 :
354 : /*
355 : * deleteDependencyRecordsForClass -- delete all records with given depender
356 : * classId/objectId, dependee classId, and deptype.
357 : * Returns the number of records deleted.
358 : *
359 : * This is a variant of deleteDependencyRecordsFor, useful when revoking
360 : * an object property that is expressed by a dependency record (such as
361 : * extension membership).
362 : */
363 : long
364 9510 : deleteDependencyRecordsForClass(Oid classId, Oid objectId,
365 : Oid refclassId, char deptype)
366 : {
367 9510 : long count = 0;
368 : Relation depRel;
369 : ScanKeyData key[2];
370 : SysScanDesc scan;
371 : HeapTuple tup;
372 :
373 9510 : depRel = table_open(DependRelationId, RowExclusiveLock);
374 :
375 9510 : ScanKeyInit(&key[0],
376 : Anum_pg_depend_classid,
377 : BTEqualStrategyNumber, F_OIDEQ,
378 : ObjectIdGetDatum(classId));
379 9510 : ScanKeyInit(&key[1],
380 : Anum_pg_depend_objid,
381 : BTEqualStrategyNumber, F_OIDEQ,
382 : ObjectIdGetDatum(objectId));
383 :
384 9510 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
385 : NULL, 2, key);
386 :
387 15961 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
388 : {
389 6451 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
390 :
391 6451 : if (depform->refclassid == refclassId && depform->deptype == deptype)
392 : {
393 1477 : CatalogTupleDelete(depRel, &tup->t_self);
394 1477 : count++;
395 : }
396 : }
397 :
398 9510 : systable_endscan(scan);
399 :
400 9510 : table_close(depRel, RowExclusiveLock);
401 :
402 9510 : return count;
403 : }
404 :
405 : /*
406 : * deleteDependencyRecordsForSpecific -- delete all records with given depender
407 : * classId/objectId, dependee classId/objectId, of the given deptype.
408 : * Returns the number of records deleted.
409 : */
410 : long
411 59 : deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype,
412 : Oid refclassId, Oid refobjectId)
413 : {
414 59 : long count = 0;
415 : Relation depRel;
416 : ScanKeyData key[2];
417 : SysScanDesc scan;
418 : HeapTuple tup;
419 :
420 59 : depRel = table_open(DependRelationId, RowExclusiveLock);
421 :
422 59 : ScanKeyInit(&key[0],
423 : Anum_pg_depend_classid,
424 : BTEqualStrategyNumber, F_OIDEQ,
425 : ObjectIdGetDatum(classId));
426 59 : ScanKeyInit(&key[1],
427 : Anum_pg_depend_objid,
428 : BTEqualStrategyNumber, F_OIDEQ,
429 : ObjectIdGetDatum(objectId));
430 :
431 59 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
432 : NULL, 2, key);
433 :
434 319 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
435 : {
436 260 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
437 :
438 260 : if (depform->refclassid == refclassId &&
439 60 : depform->refobjid == refobjectId &&
440 59 : depform->deptype == deptype)
441 : {
442 59 : CatalogTupleDelete(depRel, &tup->t_self);
443 59 : count++;
444 : }
445 : }
446 :
447 59 : systable_endscan(scan);
448 :
449 59 : table_close(depRel, RowExclusiveLock);
450 :
451 59 : return count;
452 : }
453 :
454 : /*
455 : * Adjust dependency record(s) to point to a different object of the same type
456 : *
457 : * classId/objectId specify the referencing object.
458 : * refClassId/oldRefObjectId specify the old referenced object.
459 : * newRefObjectId is the new referenced object (must be of class refClassId).
460 : *
461 : * Note the lack of objsubid parameters. If there are subobject references
462 : * they will all be readjusted. Also, there is an expectation that we are
463 : * dealing with NORMAL dependencies: if we have to replace an (implicit)
464 : * dependency on a pinned object with an explicit dependency on an unpinned
465 : * one, the new one will be NORMAL.
466 : *
467 : * Returns the number of records updated -- zero indicates a problem.
468 : */
469 : long
470 251 : changeDependencyFor(Oid classId, Oid objectId,
471 : Oid refClassId, Oid oldRefObjectId,
472 : Oid newRefObjectId)
473 : {
474 251 : long count = 0;
475 : Relation depRel;
476 : ScanKeyData key[2];
477 : SysScanDesc scan;
478 : HeapTuple tup;
479 : ObjectAddress objAddr;
480 : ObjectAddress depAddr;
481 : bool oldIsPinned;
482 : bool newIsPinned;
483 :
484 : /*
485 : * Check to see if either oldRefObjectId or newRefObjectId is pinned.
486 : * Pinned objects should not have any dependency entries pointing to them,
487 : * so in these cases we should add or remove a pg_depend entry, or do
488 : * nothing at all, rather than update an entry as in the normal case.
489 : */
490 251 : objAddr.classId = refClassId;
491 251 : objAddr.objectId = oldRefObjectId;
492 251 : objAddr.objectSubId = 0;
493 :
494 251 : oldIsPinned = isObjectPinned(&objAddr);
495 :
496 251 : objAddr.objectId = newRefObjectId;
497 :
498 251 : newIsPinned = isObjectPinned(&objAddr);
499 :
500 251 : if (oldIsPinned)
501 : {
502 : /*
503 : * If both are pinned, we need do nothing. However, return 1 not 0,
504 : * else callers will think this is an error case.
505 : */
506 37 : if (newIsPinned)
507 0 : return 1;
508 :
509 : /*
510 : * There is no old dependency record, but we should insert a new one.
511 : * Assume a normal dependency is wanted.
512 : */
513 37 : depAddr.classId = classId;
514 37 : depAddr.objectId = objectId;
515 37 : depAddr.objectSubId = 0;
516 37 : recordDependencyOn(&depAddr, &objAddr, DEPENDENCY_NORMAL);
517 :
518 37 : return 1;
519 : }
520 :
521 : /*
522 : * Make sure the new referenced object doesn't go away while we record the
523 : * dependency.
524 : */
525 214 : if (!newIsPinned)
526 170 : dependencyLockAndCheckObject(refClassId, newRefObjectId);
527 :
528 213 : depRel = table_open(DependRelationId, RowExclusiveLock);
529 :
530 : /* There should be existing dependency record(s), so search. */
531 213 : ScanKeyInit(&key[0],
532 : Anum_pg_depend_classid,
533 : BTEqualStrategyNumber, F_OIDEQ,
534 : ObjectIdGetDatum(classId));
535 213 : ScanKeyInit(&key[1],
536 : Anum_pg_depend_objid,
537 : BTEqualStrategyNumber, F_OIDEQ,
538 : ObjectIdGetDatum(objectId));
539 :
540 213 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
541 : NULL, 2, key);
542 :
543 557 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
544 : {
545 344 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
546 :
547 344 : if (depform->refclassid == refClassId &&
548 213 : depform->refobjid == oldRefObjectId)
549 : {
550 213 : if (newIsPinned)
551 44 : CatalogTupleDelete(depRel, &tup->t_self);
552 : else
553 : {
554 : /* make a modifiable copy */
555 169 : tup = heap_copytuple(tup);
556 169 : depform = (Form_pg_depend) GETSTRUCT(tup);
557 :
558 169 : depform->refobjid = newRefObjectId;
559 :
560 169 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
561 :
562 169 : heap_freetuple(tup);
563 : }
564 :
565 213 : count++;
566 : }
567 : }
568 :
569 213 : systable_endscan(scan);
570 :
571 213 : table_close(depRel, RowExclusiveLock);
572 :
573 213 : return count;
574 : }
575 :
576 : /*
577 : * Adjust all dependency records to come from a different object of the same type
578 : *
579 : * classId/oldObjectId specify the old referencing object.
580 : * newObjectId is the new referencing object (must be of class classId).
581 : *
582 : * Returns the number of records updated.
583 : */
584 : long
585 640 : changeDependenciesOf(Oid classId, Oid oldObjectId,
586 : Oid newObjectId)
587 : {
588 640 : long count = 0;
589 : Relation depRel;
590 : ScanKeyData key[2];
591 : SysScanDesc scan;
592 : HeapTuple tup;
593 :
594 640 : depRel = table_open(DependRelationId, RowExclusiveLock);
595 :
596 640 : ScanKeyInit(&key[0],
597 : Anum_pg_depend_classid,
598 : BTEqualStrategyNumber, F_OIDEQ,
599 : ObjectIdGetDatum(classId));
600 640 : ScanKeyInit(&key[1],
601 : Anum_pg_depend_objid,
602 : BTEqualStrategyNumber, F_OIDEQ,
603 : ObjectIdGetDatum(oldObjectId));
604 :
605 640 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
606 : NULL, 2, key);
607 :
608 1808 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
609 : {
610 : Form_pg_depend depform;
611 :
612 : /* make a modifiable copy */
613 1168 : tup = heap_copytuple(tup);
614 1168 : depform = (Form_pg_depend) GETSTRUCT(tup);
615 :
616 1168 : depform->objid = newObjectId;
617 :
618 1168 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
619 :
620 1168 : heap_freetuple(tup);
621 :
622 1168 : count++;
623 : }
624 :
625 640 : systable_endscan(scan);
626 :
627 640 : table_close(depRel, RowExclusiveLock);
628 :
629 640 : return count;
630 : }
631 :
632 : /*
633 : * Adjust all dependency records to point to a different object of the same type
634 : *
635 : * refClassId/oldRefObjectId specify the old referenced object.
636 : * newRefObjectId is the new referenced object (must be of class refClassId).
637 : *
638 : * Returns the number of records updated.
639 : */
640 : long
641 640 : changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
642 : Oid newRefObjectId)
643 : {
644 640 : long count = 0;
645 : Relation depRel;
646 : ScanKeyData key[2];
647 : SysScanDesc scan;
648 : HeapTuple tup;
649 : ObjectAddress objAddr;
650 : bool newIsPinned;
651 :
652 640 : depRel = table_open(DependRelationId, RowExclusiveLock);
653 :
654 : /*
655 : * If oldRefObjectId is pinned, there won't be any dependency entries on
656 : * it --- we can't cope in that case. (This isn't really worth expending
657 : * code to fix, in current usage; it just means you can't rename stuff out
658 : * of pg_catalog, which would likely be a bad move anyway.)
659 : */
660 640 : objAddr.classId = refClassId;
661 640 : objAddr.objectId = oldRefObjectId;
662 640 : objAddr.objectSubId = 0;
663 :
664 640 : if (isObjectPinned(&objAddr))
665 0 : ereport(ERROR,
666 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
667 : errmsg("cannot remove dependency on %s because it is a system object",
668 : getObjectDescription(&objAddr, false))));
669 :
670 : /*
671 : * We can handle adding a dependency on something pinned, though, since
672 : * that just means deleting the dependency entry.
673 : */
674 640 : objAddr.objectId = newRefObjectId;
675 :
676 640 : newIsPinned = isObjectPinned(&objAddr);
677 :
678 : /* Now search for dependency records */
679 640 : ScanKeyInit(&key[0],
680 : Anum_pg_depend_refclassid,
681 : BTEqualStrategyNumber, F_OIDEQ,
682 : ObjectIdGetDatum(refClassId));
683 640 : ScanKeyInit(&key[1],
684 : Anum_pg_depend_refobjid,
685 : BTEqualStrategyNumber, F_OIDEQ,
686 : ObjectIdGetDatum(oldRefObjectId));
687 :
688 640 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
689 : NULL, 2, key);
690 :
691 651 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
692 : {
693 11 : if (newIsPinned)
694 0 : CatalogTupleDelete(depRel, &tup->t_self);
695 : else
696 : {
697 : Form_pg_depend depform;
698 :
699 : /* make a modifiable copy */
700 11 : tup = heap_copytuple(tup);
701 11 : depform = (Form_pg_depend) GETSTRUCT(tup);
702 :
703 11 : depform->refobjid = newRefObjectId;
704 :
705 11 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
706 :
707 11 : heap_freetuple(tup);
708 : }
709 :
710 11 : count++;
711 : }
712 :
713 640 : systable_endscan(scan);
714 :
715 640 : table_close(depRel, RowExclusiveLock);
716 :
717 640 : return count;
718 : }
719 :
720 : /*
721 : * isObjectPinned()
722 : *
723 : * Test if an object is required for basic database functionality.
724 : *
725 : * The passed subId, if any, is ignored; we assume that only whole objects
726 : * are pinned (and that this implies pinning their components).
727 : */
728 : static bool
729 1434399 : isObjectPinned(const ObjectAddress *object)
730 : {
731 1434399 : return IsPinnedObject(object->classId, object->objectId);
732 : }
733 :
734 :
735 : /*
736 : * dependencyLockAndCheckObject
737 : *
738 : * Lock the object that we are about to record a dependency on. After it's
739 : * locked, verify that it hasn't been dropped while we weren't looking. If it
740 : * has been dropped, throw an an error.
741 : *
742 : * If the caller already holds a lock that conflicts with DROP
743 : * (AccessShareLock or stronger), this does nothing. Callers should acquire
744 : * locks already when they look up the referenced objects, but many callers
745 : * currently do not. This is a backstop to make sure that we don't record a
746 : * bogus reference permanently in the catalogs in that case. In the future,
747 : * after we have tightened up all the callers to acquire locks earlier, this
748 : * could just verify that the object is already locked and throw an error if
749 : * not.
750 : */
751 : static void
752 399299 : dependencyLockAndCheckObject(Oid classId, Oid objectId)
753 : {
754 : /*
755 : * Pinned objects cannot be dropped concurrently, and callers checked this
756 : * already.
757 : */
758 : Assert(!IsPinnedObject(classId, objectId));
759 :
760 399299 : if (classId != RelationRelationId)
761 : {
762 : LOCKTAG tag;
763 : SysCacheIdentifier cache;
764 : Relation rel;
765 : SysScanDesc scan;
766 : ScanKeyData skey;
767 : HeapTuple tuple;
768 :
769 213065 : SET_LOCKTAG_OBJECT(tag,
770 : MyDatabaseId,
771 : classId,
772 : objectId,
773 : 0);
774 :
775 213065 : if (LockHeldByMe(&tag, AccessShareLock, true))
776 158783 : return;
777 :
778 : /* Assume we should lock the whole object not a sub-object */
779 97738 : LockDatabaseObject(classId, objectId, 0, AccessShareLock);
780 :
781 : /*
782 : * Check that the object still exists. If the catalog has a suitable
783 : * syscache, check that first.
784 : */
785 97738 : cache = get_object_catcache_oid(classId);
786 97738 : if (cache != SYSCACHEID_INVALID)
787 : {
788 95236 : if (SearchSysCacheExists1(cache, ObjectIdGetDatum(objectId)))
789 43456 : return;
790 : }
791 :
792 : /*
793 : * If it's not found in the syscache, or there's no suitable syscache
794 : * we can use, scan the catalog table using SnapshotSelf. This
795 : * handles the case that it's an object we just created (for example,
796 : * if it's a composite type created as part of creating a table).
797 : */
798 54282 : rel = table_open(classId, AccessShareLock);
799 :
800 108564 : ScanKeyInit(&skey,
801 54282 : get_object_attnum_oid(classId),
802 : BTEqualStrategyNumber, F_OIDEQ,
803 : ObjectIdGetDatum(objectId));
804 :
805 54282 : scan = systable_beginscan(rel, get_object_oid_index(classId),
806 : true, SnapshotSelf, 1, &skey);
807 :
808 54282 : tuple = systable_getnext(scan);
809 54282 : if (!HeapTupleIsValid(tuple))
810 8 : ereport(ERROR,
811 : (errcode(ERRCODE_UNDEFINED_OBJECT),
812 : errmsg("referenced %s was concurrently dropped",
813 : get_object_class_descr(classId))));
814 :
815 54274 : systable_endscan(scan);
816 54274 : table_close(rel, AccessShareLock);
817 : }
818 : else
819 : {
820 : /*
821 : * Same logic for pg_class entries, but locking relations is handled
822 : * by different functions.
823 : *
824 : * Callers are more careful with locking relations than other objects,
825 : * so we should already have a lock on the relation, or on another
826 : * object that indirectly prevents the relation from being dropped.
827 : * For example, we might have a strong lock on a table while adding
828 : * dependency to its index. However, we cannot detect the indirectly
829 : * protected case here easily. To err on the safe side, acquire a
830 : * lock directly on the relation if we're not holding one already.
831 : */
832 :
833 : /* all shared relations are pinned */
834 : Assert(!IsSharedRelation(objectId));
835 :
836 186234 : if (CheckRelationOidLockedByMe(objectId, AccessShareLock, true))
837 184174 : return;
838 2060 : LockRelationOid(objectId, AccessShareLock);
839 :
840 2060 : if (SearchSysCacheExists1(RELOID, ObjectIdGetDatum(objectId)))
841 2060 : return;
842 0 : ereport(ERROR,
843 : (errcode(ERRCODE_UNDEFINED_OBJECT),
844 : errmsg("referenced relation was concurrently dropped")));
845 : }
846 : }
847 :
848 : /*
849 : * Various special-purpose lookups and manipulations of pg_depend.
850 : */
851 :
852 :
853 : /*
854 : * Find the extension containing the specified object, if any
855 : *
856 : * Returns the OID of the extension, or InvalidOid if the object does not
857 : * belong to any extension.
858 : *
859 : * Extension membership is marked by an EXTENSION dependency from the object
860 : * to the extension. Note that the result will be indeterminate if pg_depend
861 : * contains links from this object to more than one extension ... but that
862 : * should never happen.
863 : */
864 : Oid
865 656 : getExtensionOfObject(Oid classId, Oid objectId)
866 : {
867 656 : Oid result = InvalidOid;
868 : Relation depRel;
869 : ScanKeyData key[2];
870 : SysScanDesc scan;
871 : HeapTuple tup;
872 :
873 656 : depRel = table_open(DependRelationId, AccessShareLock);
874 :
875 656 : ScanKeyInit(&key[0],
876 : Anum_pg_depend_classid,
877 : BTEqualStrategyNumber, F_OIDEQ,
878 : ObjectIdGetDatum(classId));
879 656 : ScanKeyInit(&key[1],
880 : Anum_pg_depend_objid,
881 : BTEqualStrategyNumber, F_OIDEQ,
882 : ObjectIdGetDatum(objectId));
883 :
884 656 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
885 : NULL, 2, key);
886 :
887 1562 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
888 : {
889 1483 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
890 :
891 1483 : if (depform->refclassid == ExtensionRelationId &&
892 577 : depform->deptype == DEPENDENCY_EXTENSION)
893 : {
894 577 : result = depform->refobjid;
895 577 : break; /* no need to keep scanning */
896 : }
897 : }
898 :
899 656 : systable_endscan(scan);
900 :
901 656 : table_close(depRel, AccessShareLock);
902 :
903 656 : return result;
904 : }
905 :
906 : /*
907 : * Return (possibly NIL) list of extensions that the given object depends on
908 : * in DEPENDENCY_AUTO_EXTENSION mode.
909 : */
910 : List *
911 227 : getAutoExtensionsOfObject(Oid classId, Oid objectId)
912 : {
913 227 : List *result = NIL;
914 : Relation depRel;
915 : ScanKeyData key[2];
916 : SysScanDesc scan;
917 : HeapTuple tup;
918 :
919 227 : depRel = table_open(DependRelationId, AccessShareLock);
920 :
921 227 : ScanKeyInit(&key[0],
922 : Anum_pg_depend_classid,
923 : BTEqualStrategyNumber, F_OIDEQ,
924 : ObjectIdGetDatum(classId));
925 227 : ScanKeyInit(&key[1],
926 : Anum_pg_depend_objid,
927 : BTEqualStrategyNumber, F_OIDEQ,
928 : ObjectIdGetDatum(objectId));
929 :
930 227 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
931 : NULL, 2, key);
932 :
933 911 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
934 : {
935 684 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
936 :
937 684 : if (depform->refclassid == ExtensionRelationId &&
938 26 : depform->deptype == DEPENDENCY_AUTO_EXTENSION)
939 26 : result = lappend_oid(result, depform->refobjid);
940 : }
941 :
942 227 : systable_endscan(scan);
943 :
944 227 : table_close(depRel, AccessShareLock);
945 :
946 227 : return result;
947 : }
948 :
949 : /*
950 : * Look up a type belonging to an extension.
951 : *
952 : * Returns the type's OID, or InvalidOid if not found.
953 : *
954 : * Notice that the type is specified by name only, without a schema.
955 : * That's because this will typically be used by relocatable extensions
956 : * which can't make a-priori assumptions about which schema their objects
957 : * are in. As long as the extension only defines one type of this name,
958 : * the answer is unique anyway.
959 : *
960 : * We might later add the ability to look up functions, operators, etc.
961 : */
962 : Oid
963 1 : getExtensionType(Oid extensionOid, const char *typname)
964 : {
965 1 : Oid result = InvalidOid;
966 : Relation depRel;
967 : ScanKeyData key[3];
968 : SysScanDesc scan;
969 : HeapTuple tup;
970 :
971 1 : depRel = table_open(DependRelationId, AccessShareLock);
972 :
973 1 : ScanKeyInit(&key[0],
974 : Anum_pg_depend_refclassid,
975 : BTEqualStrategyNumber, F_OIDEQ,
976 : ObjectIdGetDatum(ExtensionRelationId));
977 1 : ScanKeyInit(&key[1],
978 : Anum_pg_depend_refobjid,
979 : BTEqualStrategyNumber, F_OIDEQ,
980 : ObjectIdGetDatum(extensionOid));
981 1 : ScanKeyInit(&key[2],
982 : Anum_pg_depend_refobjsubid,
983 : BTEqualStrategyNumber, F_INT4EQ,
984 : Int32GetDatum(0));
985 :
986 1 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
987 : NULL, 3, key);
988 :
989 1 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
990 : {
991 1 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
992 :
993 1 : if (depform->classid == TypeRelationId &&
994 1 : depform->deptype == DEPENDENCY_EXTENSION)
995 : {
996 1 : Oid typoid = depform->objid;
997 : HeapTuple typtup;
998 :
999 1 : typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid));
1000 1 : if (!HeapTupleIsValid(typtup))
1001 0 : continue; /* should we throw an error? */
1002 1 : if (strcmp(NameStr(((Form_pg_type) GETSTRUCT(typtup))->typname),
1003 : typname) == 0)
1004 : {
1005 1 : result = typoid;
1006 1 : ReleaseSysCache(typtup);
1007 1 : break; /* no need to keep searching */
1008 : }
1009 0 : ReleaseSysCache(typtup);
1010 : }
1011 : }
1012 :
1013 1 : systable_endscan(scan);
1014 :
1015 1 : table_close(depRel, AccessShareLock);
1016 :
1017 1 : return result;
1018 : }
1019 :
1020 : /*
1021 : * Detect whether a sequence is marked as "owned" by a column
1022 : *
1023 : * An ownership marker is an AUTO or INTERNAL dependency from the sequence to the
1024 : * column. If we find one, store the identity of the owning column
1025 : * into *tableId and *colId and return true; else return false.
1026 : *
1027 : * Note: if there's more than one such pg_depend entry then you get
1028 : * a random one of them returned into the out parameters. This should
1029 : * not happen, though.
1030 : */
1031 : bool
1032 557 : sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
1033 : {
1034 557 : bool ret = false;
1035 : Relation depRel;
1036 : ScanKeyData key[2];
1037 : SysScanDesc scan;
1038 : HeapTuple tup;
1039 :
1040 557 : depRel = table_open(DependRelationId, AccessShareLock);
1041 :
1042 557 : ScanKeyInit(&key[0],
1043 : Anum_pg_depend_classid,
1044 : BTEqualStrategyNumber, F_OIDEQ,
1045 : ObjectIdGetDatum(RelationRelationId));
1046 557 : ScanKeyInit(&key[1],
1047 : Anum_pg_depend_objid,
1048 : BTEqualStrategyNumber, F_OIDEQ,
1049 : ObjectIdGetDatum(seqId));
1050 :
1051 557 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
1052 : NULL, 2, key);
1053 :
1054 1115 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
1055 : {
1056 566 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
1057 :
1058 566 : if (depform->refclassid == RelationRelationId &&
1059 8 : depform->deptype == deptype)
1060 : {
1061 8 : *tableId = depform->refobjid;
1062 8 : *colId = depform->refobjsubid;
1063 8 : ret = true;
1064 8 : break; /* no need to keep scanning */
1065 : }
1066 : }
1067 :
1068 557 : systable_endscan(scan);
1069 :
1070 557 : table_close(depRel, AccessShareLock);
1071 :
1072 557 : return ret;
1073 : }
1074 :
1075 : /*
1076 : * Collect a list of OIDs of all sequences owned by the specified relation,
1077 : * and column if specified. If deptype is not zero, then only find sequences
1078 : * with the specified dependency type.
1079 : */
1080 : static List *
1081 495 : getOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype)
1082 : {
1083 495 : List *result = NIL;
1084 : Relation depRel;
1085 : ScanKeyData key[3];
1086 : SysScanDesc scan;
1087 : HeapTuple tup;
1088 :
1089 495 : depRel = table_open(DependRelationId, AccessShareLock);
1090 :
1091 495 : ScanKeyInit(&key[0],
1092 : Anum_pg_depend_refclassid,
1093 : BTEqualStrategyNumber, F_OIDEQ,
1094 : ObjectIdGetDatum(RelationRelationId));
1095 495 : ScanKeyInit(&key[1],
1096 : Anum_pg_depend_refobjid,
1097 : BTEqualStrategyNumber, F_OIDEQ,
1098 : ObjectIdGetDatum(relid));
1099 495 : if (attnum)
1100 429 : ScanKeyInit(&key[2],
1101 : Anum_pg_depend_refobjsubid,
1102 : BTEqualStrategyNumber, F_INT4EQ,
1103 : Int32GetDatum(attnum));
1104 :
1105 495 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
1106 : NULL, attnum ? 3 : 2, key);
1107 :
1108 1656 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
1109 : {
1110 1161 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
1111 :
1112 : /*
1113 : * We assume any auto or internal dependency of a sequence on a column
1114 : * must be what we are looking for. (We need the relkind test because
1115 : * indexes can also have auto dependencies on columns.)
1116 : */
1117 1161 : if (deprec->classid == RelationRelationId &&
1118 502 : deprec->objsubid == 0 &&
1119 502 : deprec->refobjsubid != 0 &&
1120 964 : (deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) &&
1121 482 : get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
1122 : {
1123 479 : if (!deptype || deprec->deptype == deptype)
1124 475 : result = lappend_oid(result, deprec->objid);
1125 : }
1126 : }
1127 :
1128 495 : systable_endscan(scan);
1129 :
1130 495 : table_close(depRel, AccessShareLock);
1131 :
1132 495 : return result;
1133 : }
1134 :
1135 : /*
1136 : * Collect a list of OIDs of all sequences owned (identity or serial) by the
1137 : * specified relation.
1138 : */
1139 : List *
1140 66 : getOwnedSequences(Oid relid)
1141 : {
1142 66 : return getOwnedSequences_internal(relid, 0, 0);
1143 : }
1144 :
1145 : /*
1146 : * Get owned identity sequence, error if not exactly one.
1147 : */
1148 : Oid
1149 429 : getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
1150 : {
1151 429 : Oid relid = RelationGetRelid(rel);
1152 : List *seqlist;
1153 :
1154 : /*
1155 : * The identity sequence is associated with the topmost partitioned table,
1156 : * which might have column order different than the given partition.
1157 : */
1158 429 : if (RelationGetForm(rel)->relispartition)
1159 : {
1160 36 : List *ancestors = get_partition_ancestors(relid);
1161 36 : const char *attname = get_attname(relid, attnum, false);
1162 :
1163 36 : relid = llast_oid(ancestors);
1164 36 : attnum = get_attnum(relid, attname);
1165 36 : if (attnum == InvalidAttrNumber)
1166 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
1167 : attname, relid);
1168 36 : list_free(ancestors);
1169 : }
1170 :
1171 429 : seqlist = getOwnedSequences_internal(relid, attnum, DEPENDENCY_INTERNAL);
1172 429 : if (list_length(seqlist) > 1)
1173 0 : elog(ERROR, "more than one owned sequence found");
1174 429 : else if (seqlist == NIL)
1175 : {
1176 8 : if (missing_ok)
1177 8 : return InvalidOid;
1178 : else
1179 0 : elog(ERROR, "no owned sequence found");
1180 : }
1181 :
1182 421 : return linitial_oid(seqlist);
1183 : }
1184 :
1185 : /*
1186 : * get_index_constraint
1187 : * Given the OID of an index, return the OID of the owning unique,
1188 : * primary-key, or exclusion constraint, or InvalidOid if there
1189 : * is no owning constraint.
1190 : */
1191 : Oid
1192 8813 : get_index_constraint(Oid indexId)
1193 : {
1194 8813 : Oid constraintId = InvalidOid;
1195 : Relation depRel;
1196 : ScanKeyData key[3];
1197 : SysScanDesc scan;
1198 : HeapTuple tup;
1199 :
1200 : /* Search the dependency table for the index */
1201 8813 : depRel = table_open(DependRelationId, AccessShareLock);
1202 :
1203 8813 : ScanKeyInit(&key[0],
1204 : Anum_pg_depend_classid,
1205 : BTEqualStrategyNumber, F_OIDEQ,
1206 : ObjectIdGetDatum(RelationRelationId));
1207 8813 : ScanKeyInit(&key[1],
1208 : Anum_pg_depend_objid,
1209 : BTEqualStrategyNumber, F_OIDEQ,
1210 : ObjectIdGetDatum(indexId));
1211 8813 : ScanKeyInit(&key[2],
1212 : Anum_pg_depend_objsubid,
1213 : BTEqualStrategyNumber, F_INT4EQ,
1214 : Int32GetDatum(0));
1215 :
1216 8813 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
1217 : NULL, 3, key);
1218 :
1219 10743 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
1220 : {
1221 3129 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
1222 :
1223 : /*
1224 : * We assume any internal dependency on a constraint must be what we
1225 : * are looking for.
1226 : */
1227 3129 : if (deprec->refclassid == ConstraintRelationId &&
1228 1199 : deprec->refobjsubid == 0 &&
1229 1199 : deprec->deptype == DEPENDENCY_INTERNAL)
1230 : {
1231 1199 : constraintId = deprec->refobjid;
1232 1199 : break;
1233 : }
1234 : }
1235 :
1236 8813 : systable_endscan(scan);
1237 8813 : table_close(depRel, AccessShareLock);
1238 :
1239 8813 : return constraintId;
1240 : }
1241 :
1242 : /*
1243 : * get_index_ref_constraints
1244 : * Given the OID of an index, return the OID of all foreign key
1245 : * constraints which reference the index.
1246 : */
1247 : List *
1248 320 : get_index_ref_constraints(Oid indexId)
1249 : {
1250 320 : List *result = NIL;
1251 : Relation depRel;
1252 : ScanKeyData key[3];
1253 : SysScanDesc scan;
1254 : HeapTuple tup;
1255 :
1256 : /* Search the dependency table for the index */
1257 320 : depRel = table_open(DependRelationId, AccessShareLock);
1258 :
1259 320 : ScanKeyInit(&key[0],
1260 : Anum_pg_depend_refclassid,
1261 : BTEqualStrategyNumber, F_OIDEQ,
1262 : ObjectIdGetDatum(RelationRelationId));
1263 320 : ScanKeyInit(&key[1],
1264 : Anum_pg_depend_refobjid,
1265 : BTEqualStrategyNumber, F_OIDEQ,
1266 : ObjectIdGetDatum(indexId));
1267 320 : ScanKeyInit(&key[2],
1268 : Anum_pg_depend_refobjsubid,
1269 : BTEqualStrategyNumber, F_INT4EQ,
1270 : Int32GetDatum(0));
1271 :
1272 320 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
1273 : NULL, 3, key);
1274 :
1275 331 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
1276 : {
1277 11 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
1278 :
1279 : /*
1280 : * We assume any normal dependency from a constraint must be what we
1281 : * are looking for.
1282 : */
1283 11 : if (deprec->classid == ConstraintRelationId &&
1284 11 : deprec->objsubid == 0 &&
1285 11 : deprec->deptype == DEPENDENCY_NORMAL)
1286 : {
1287 11 : result = lappend_oid(result, deprec->objid);
1288 : }
1289 : }
1290 :
1291 320 : systable_endscan(scan);
1292 320 : table_close(depRel, AccessShareLock);
1293 :
1294 320 : return result;
1295 : }
|