Age Owner Branch data 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-2025, 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
8457 tgl@sss.pgh.pa.us 45 :CBC 434464 : recordDependencyOn(const ObjectAddress *depender,
46 : : const ObjectAddress *referenced,
47 : : DependencyType behavior)
48 : : {
1583 tmunro@postgresql.or 49 : 434464 : recordMultipleDependencies(depender, referenced, 1, behavior);
8453 tgl@sss.pgh.pa.us 50 : 434464 : }
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 : 650745 : recordMultipleDependencies(const ObjectAddress *depender,
58 : : const ObjectAddress *referenced,
59 : : int nreferenced,
60 : : DependencyType behavior)
61 : : {
62 : : Relation dependDesc;
63 : : CatalogIndexState indstate;
64 : : TupleTableSlot **slot;
65 : : int i,
66 : : max_slots,
67 : : slot_init_count,
68 : : slot_stored_count;
69 : :
70 [ + + ]: 650745 : if (nreferenced <= 0)
71 : 25078 : 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 : : */
8457 80 [ + + ]: 625667 : if (IsBootstrapProcessingMode())
81 : 33550 : return;
82 : :
2420 andres@anarazel.de 83 : 592117 : 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 : : */
1827 michael@paquier.xyz 89 [ + - ]: 592117 : max_slots = Min(nreferenced,
90 : : MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_depend));
91 : 592117 : slot = palloc(sizeof(TupleTableSlot *) * max_slots);
92 : :
93 : : /* Don't open indexes unless we need to make an update */
8433 tgl@sss.pgh.pa.us 94 : 592117 : indstate = NULL;
95 : :
96 : : /* number of slots currently storing tuples */
1827 michael@paquier.xyz 97 : 592117 : slot_stored_count = 0;
98 : : /* number of slots currently initialized */
99 : 592117 : slot_init_count = 0;
8453 tgl@sss.pgh.pa.us 100 [ + + ]: 1683335 : 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 : : */
1514 107 [ + + ]: 1091218 : if (isObjectPinned(referenced))
1827 michael@paquier.xyz 108 : 805671 : continue;
109 : :
110 [ + - ]: 285547 : if (slot_init_count < max_slots)
111 : : {
112 : 285547 : slot[slot_stored_count] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc),
113 : : &TTSOpsHeapTuple);
114 : 285547 : slot_init_count++;
115 : : }
116 : :
117 : 285547 : 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 : 285547 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
124 : 285547 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
125 : 285547 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
126 : 285547 : slot[slot_stored_count]->tts_values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
127 : 285547 : slot[slot_stored_count]->tts_values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
128 : 285547 : slot[slot_stored_count]->tts_values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
129 : 285547 : slot[slot_stored_count]->tts_values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
130 : :
1583 tmunro@postgresql.or 131 : 285547 : memset(slot[slot_stored_count]->tts_isnull, false,
132 : 285547 : slot[slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));
133 : :
1827 michael@paquier.xyz 134 : 285547 : ExecStoreVirtualTuple(slot[slot_stored_count]);
135 : 285547 : slot_stored_count++;
136 : :
137 : : /* If slots are full, insert a batch of tuples */
138 [ + + ]: 285547 : if (slot_stored_count == max_slots)
139 : : {
140 : : /* fetch index info only when we know we need it */
8433 tgl@sss.pgh.pa.us 141 [ + - ]: 198915 : if (indstate == NULL)
142 : 198915 : indstate = CatalogOpenIndexes(dependDesc);
143 : :
1827 michael@paquier.xyz 144 : 198915 : CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
145 : : indstate);
146 : 198915 : slot_stored_count = 0;
147 : : }
148 : : }
149 : :
150 : : /* Insert any tuples left in the buffer */
151 [ + + ]: 592117 : if (slot_stored_count > 0)
152 : : {
153 : : /* fetch index info only when we know we need it */
154 [ + - ]: 43883 : if (indstate == NULL)
155 : 43883 : indstate = CatalogOpenIndexes(dependDesc);
156 : :
157 : 43883 : CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
158 : : indstate);
159 : : }
160 : :
8433 tgl@sss.pgh.pa.us 161 [ + + ]: 592117 : if (indstate != NULL)
162 : 242798 : CatalogCloseIndexes(indstate);
163 : :
2420 andres@anarazel.de 164 : 592117 : table_close(dependDesc, RowExclusiveLock);
165 : :
166 : : /* Drop only the number of slots used */
1827 michael@paquier.xyz 167 [ + + ]: 877664 : for (i = 0; i < slot_init_count; i++)
168 : 285547 : ExecDropSingleTupleTableSlot(slot[i]);
169 : 592117 : pfree(slot);
170 : : }
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
5159 tgl@sss.pgh.pa.us 193 : 186229 : recordDependencyOnCurrentExtension(const ObjectAddress *object,
194 : : bool isReplace)
195 : : {
196 : : /* Only whole objects can be extension members */
197 [ - + ]: 186229 : Assert(object->objectSubId == 0);
198 : :
5324 199 [ + + ]: 186229 : if (creating_extension)
200 : : {
201 : : ObjectAddress extension;
202 : :
203 : : /* Only need to check for existing membership if isReplace */
5159 204 [ + + ]: 5303 : if (isReplace)
205 : : {
206 : : 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 : 395 : oldext = getExtensionOfObject(object->classId, object->objectId);
215 [ + + ]: 395 : if (OidIsValid(oldext))
216 : : {
217 : : /* If already a member of this extension, nothing to do */
218 [ + - ]: 391 : if (oldext == CurrentExtensionObject)
219 : 391 : return;
220 : : /* Already a member of some other extension, so reject */
5159 tgl@sss.pgh.pa.us 221 [ # # ]:UBC 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 : : }
227 : : /* It's a free-standing object, so reject */
1125 tgl@sss.pgh.pa.us 228 [ + - ]:CBC 4 : 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 : : }
235 : :
236 : : /* OK, record it as a member of CurrentExtensionObject */
5324 237 : 4908 : extension.classId = ExtensionRelationId;
238 : 4908 : extension.objectId = CurrentExtensionObject;
239 : 4908 : extension.objectSubId = 0;
240 : :
241 : 4908 : recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION);
242 : : }
243 : : }
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
1125 258 : 69 : 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 [ - + ]: 69 : Assert(object->objectSubId == 0);
269 : :
270 [ + + ]: 69 : if (creating_extension)
271 : : {
272 : : Oid oldext;
273 : :
274 : 14 : oldext = getExtensionOfObject(object->classId, object->objectId);
275 : : /* If already a member of this extension, OK */
276 [ + + ]: 14 : if (oldext == CurrentExtensionObject)
277 : 7 : return;
278 : : /* Else complain */
279 [ + - ]: 7 : 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 : : }
286 : : }
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
5324 301 : 10558 : deleteDependencyRecordsFor(Oid classId, Oid objectId,
302 : : bool skipExtensionDeps)
303 : : {
8403 bruce@momjian.us 304 : 10558 : long count = 0;
305 : : Relation depRel;
306 : : ScanKeyData key[2];
307 : : SysScanDesc scan;
308 : : HeapTuple tup;
309 : :
2420 andres@anarazel.de 310 : 10558 : depRel = table_open(DependRelationId, RowExclusiveLock);
311 : :
7969 tgl@sss.pgh.pa.us 312 : 10558 : ScanKeyInit(&key[0],
313 : : Anum_pg_depend_classid,
314 : : BTEqualStrategyNumber, F_OIDEQ,
315 : : ObjectIdGetDatum(classId));
316 : 10558 : ScanKeyInit(&key[1],
317 : : Anum_pg_depend_objid,
318 : : BTEqualStrategyNumber, F_OIDEQ,
319 : : ObjectIdGetDatum(objectId));
320 : :
7450 321 : 10558 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
322 : : NULL, 2, key);
323 : :
8453 324 [ + + ]: 17234 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
325 : : {
5324 326 [ + + ]: 6676 : if (skipExtensionDeps &&
2999 327 [ + + ]: 5161 : ((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION)
5324 328 : 627 : continue;
329 : :
3139 330 : 6049 : CatalogTupleDelete(depRel, &tup->t_self);
8427 331 : 6049 : count++;
332 : : }
333 : :
8453 334 : 10558 : systable_endscan(scan);
335 : :
2420 andres@anarazel.de 336 : 10558 : table_close(depRel, RowExclusiveLock);
337 : :
8427 tgl@sss.pgh.pa.us 338 : 10558 : return count;
339 : : }
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
5322 351 : 7093 : deleteDependencyRecordsForClass(Oid classId, Oid objectId,
352 : : Oid refclassId, char deptype)
353 : : {
354 : 7093 : long count = 0;
355 : : Relation depRel;
356 : : ScanKeyData key[2];
357 : : SysScanDesc scan;
358 : : HeapTuple tup;
359 : :
2420 andres@anarazel.de 360 : 7093 : depRel = table_open(DependRelationId, RowExclusiveLock);
361 : :
5322 tgl@sss.pgh.pa.us 362 : 7093 : ScanKeyInit(&key[0],
363 : : Anum_pg_depend_classid,
364 : : BTEqualStrategyNumber, F_OIDEQ,
365 : : ObjectIdGetDatum(classId));
366 : 7093 : ScanKeyInit(&key[1],
367 : : Anum_pg_depend_objid,
368 : : BTEqualStrategyNumber, F_OIDEQ,
369 : : ObjectIdGetDatum(objectId));
370 : :
371 : 7093 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
372 : : NULL, 2, key);
373 : :
374 [ + + ]: 10563 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
375 : : {
376 : 3470 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
377 : :
378 [ + + + + ]: 3470 : if (depform->refclassid == refclassId && depform->deptype == deptype)
379 : : {
3139 380 : 707 : CatalogTupleDelete(depRel, &tup->t_self);
5322 381 : 707 : count++;
382 : : }
383 : : }
384 : :
385 : 7093 : systable_endscan(scan);
386 : :
2420 andres@anarazel.de 387 : 7093 : table_close(depRel, RowExclusiveLock);
388 : :
5322 tgl@sss.pgh.pa.us 389 : 7093 : return count;
390 : : }
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
1965 alvherre@alvh.no-ip. 398 : 43 : deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype,
399 : : Oid refclassId, Oid refobjectId)
400 : : {
401 : 43 : long count = 0;
402 : : Relation depRel;
403 : : ScanKeyData key[2];
404 : : SysScanDesc scan;
405 : : HeapTuple tup;
406 : :
407 : 43 : depRel = table_open(DependRelationId, RowExclusiveLock);
408 : :
409 : 43 : ScanKeyInit(&key[0],
410 : : Anum_pg_depend_classid,
411 : : BTEqualStrategyNumber, F_OIDEQ,
412 : : ObjectIdGetDatum(classId));
413 : 43 : ScanKeyInit(&key[1],
414 : : Anum_pg_depend_objid,
415 : : BTEqualStrategyNumber, F_OIDEQ,
416 : : ObjectIdGetDatum(objectId));
417 : :
418 : 43 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
419 : : NULL, 2, key);
420 : :
421 [ + + ]: 233 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
422 : : {
423 : 190 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
424 : :
425 [ + + ]: 190 : if (depform->refclassid == refclassId &&
426 [ + - ]: 43 : depform->refobjid == refobjectId &&
427 [ + - ]: 43 : depform->deptype == deptype)
428 : : {
429 : 43 : CatalogTupleDelete(depRel, &tup->t_self);
430 : 43 : count++;
431 : : }
432 : : }
433 : :
434 : 43 : systable_endscan(scan);
435 : :
436 : 43 : table_close(depRel, RowExclusiveLock);
437 : :
438 : 43 : return count;
439 : : }
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
7341 tgl@sss.pgh.pa.us 457 : 187 : changeDependencyFor(Oid classId, Oid objectId,
458 : : Oid refClassId, Oid oldRefObjectId,
459 : : Oid newRefObjectId)
460 : : {
461 : 187 : long count = 0;
462 : : Relation depRel;
463 : : ScanKeyData key[2];
464 : : SysScanDesc scan;
465 : : HeapTuple tup;
466 : : ObjectAddress objAddr;
467 : : ObjectAddress depAddr;
468 : : bool oldIsPinned;
469 : : 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 : 187 : objAddr.classId = refClassId;
478 : 187 : objAddr.objectId = oldRefObjectId;
479 : 187 : objAddr.objectSubId = 0;
480 : :
1514 481 : 187 : oldIsPinned = isObjectPinned(&objAddr);
482 : :
7341 483 : 187 : objAddr.objectId = newRefObjectId;
484 : :
1514 485 : 187 : newIsPinned = isObjectPinned(&objAddr);
486 : :
2401 487 [ + + ]: 187 : 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 [ - + ]: 28 : if (newIsPinned)
2401 tgl@sss.pgh.pa.us 494 :UBC 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 : : */
2401 tgl@sss.pgh.pa.us 500 :CBC 28 : depAddr.classId = classId;
501 : 28 : depAddr.objectId = objectId;
502 : 28 : depAddr.objectSubId = 0;
503 : 28 : recordDependencyOn(&depAddr, &objAddr, DEPENDENCY_NORMAL);
504 : :
505 : 28 : return 1;
506 : : }
507 : :
1514 508 : 159 : depRel = table_open(DependRelationId, RowExclusiveLock);
509 : :
510 : : /* There should be existing dependency record(s), so search. */
7341 511 : 159 : ScanKeyInit(&key[0],
512 : : Anum_pg_depend_classid,
513 : : BTEqualStrategyNumber, F_OIDEQ,
514 : : ObjectIdGetDatum(classId));
515 : 159 : ScanKeyInit(&key[1],
516 : : Anum_pg_depend_objid,
517 : : BTEqualStrategyNumber, F_OIDEQ,
518 : : ObjectIdGetDatum(objectId));
519 : :
520 : 159 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
521 : : NULL, 2, key);
522 : :
523 [ + + ]: 425 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
524 : : {
525 : 266 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
526 : :
527 [ + + ]: 266 : if (depform->refclassid == refClassId &&
528 [ + - ]: 159 : depform->refobjid == oldRefObjectId)
529 : : {
530 [ + + ]: 159 : if (newIsPinned)
3139 531 : 33 : CatalogTupleDelete(depRel, &tup->t_self);
532 : : else
533 : : {
534 : : /* make a modifiable copy */
7341 535 : 126 : tup = heap_copytuple(tup);
536 : 126 : depform = (Form_pg_depend) GETSTRUCT(tup);
537 : :
538 : 126 : depform->refobjid = newRefObjectId;
539 : :
3140 alvherre@alvh.no-ip. 540 : 126 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
541 : :
7341 tgl@sss.pgh.pa.us 542 : 126 : heap_freetuple(tup);
543 : : }
544 : :
545 : 159 : count++;
546 : : }
547 : : }
548 : :
549 : 159 : systable_endscan(scan);
550 : :
2420 andres@anarazel.de 551 : 159 : table_close(depRel, RowExclusiveLock);
552 : :
7341 tgl@sss.pgh.pa.us 553 : 159 : return count;
554 : : }
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
2339 peter@eisentraut.org 565 : 500 : changeDependenciesOf(Oid classId, Oid oldObjectId,
566 : : Oid newObjectId)
567 : : {
568 : 500 : long count = 0;
569 : : Relation depRel;
570 : : ScanKeyData key[2];
571 : : SysScanDesc scan;
572 : : HeapTuple tup;
573 : :
574 : 500 : depRel = table_open(DependRelationId, RowExclusiveLock);
575 : :
576 : 500 : ScanKeyInit(&key[0],
577 : : Anum_pg_depend_classid,
578 : : BTEqualStrategyNumber, F_OIDEQ,
579 : : ObjectIdGetDatum(classId));
580 : 500 : ScanKeyInit(&key[1],
581 : : Anum_pg_depend_objid,
582 : : BTEqualStrategyNumber, F_OIDEQ,
583 : : ObjectIdGetDatum(oldObjectId));
584 : :
585 : 500 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
586 : : NULL, 2, key);
587 : :
588 [ + + ]: 1388 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
589 : : {
590 : : Form_pg_depend depform;
591 : :
592 : : /* make a modifiable copy */
593 : 888 : tup = heap_copytuple(tup);
594 : 888 : depform = (Form_pg_depend) GETSTRUCT(tup);
595 : :
596 : 888 : depform->objid = newObjectId;
597 : :
598 : 888 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
599 : :
600 : 888 : heap_freetuple(tup);
601 : :
602 : 888 : count++;
603 : : }
604 : :
605 : 500 : systable_endscan(scan);
606 : :
607 : 500 : table_close(depRel, RowExclusiveLock);
608 : :
609 : 500 : return count;
610 : : }
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
2353 621 : 500 : changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
622 : : Oid newRefObjectId)
623 : : {
624 : 500 : long count = 0;
625 : : Relation depRel;
626 : : ScanKeyData key[2];
627 : : SysScanDesc scan;
628 : : HeapTuple tup;
629 : : ObjectAddress objAddr;
630 : : bool newIsPinned;
631 : :
632 : 500 : 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 : 500 : objAddr.classId = refClassId;
641 : 500 : objAddr.objectId = oldRefObjectId;
642 : 500 : objAddr.objectSubId = 0;
643 : :
1514 tgl@sss.pgh.pa.us 644 [ - + ]: 500 : if (isObjectPinned(&objAddr))
2353 peter@eisentraut.org 645 [ # # ]:UBC 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 : : */
2353 peter@eisentraut.org 654 :CBC 500 : objAddr.objectId = newRefObjectId;
655 : :
1514 tgl@sss.pgh.pa.us 656 : 500 : newIsPinned = isObjectPinned(&objAddr);
657 : :
658 : : /* Now search for dependency records */
2353 peter@eisentraut.org 659 : 500 : ScanKeyInit(&key[0],
660 : : Anum_pg_depend_refclassid,
661 : : BTEqualStrategyNumber, F_OIDEQ,
662 : : ObjectIdGetDatum(refClassId));
663 : 500 : ScanKeyInit(&key[1],
664 : : Anum_pg_depend_refobjid,
665 : : BTEqualStrategyNumber, F_OIDEQ,
666 : : ObjectIdGetDatum(oldRefObjectId));
667 : :
668 : 500 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
669 : : NULL, 2, key);
670 : :
671 [ + + ]: 509 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
672 : : {
673 [ - + ]: 9 : if (newIsPinned)
2353 peter@eisentraut.org 674 :UBC 0 : CatalogTupleDelete(depRel, &tup->t_self);
675 : : else
676 : : {
677 : : Form_pg_depend depform;
678 : :
679 : : /* make a modifiable copy */
2353 peter@eisentraut.org 680 :CBC 9 : tup = heap_copytuple(tup);
681 : 9 : depform = (Form_pg_depend) GETSTRUCT(tup);
682 : :
683 : 9 : depform->refobjid = newRefObjectId;
684 : :
685 : 9 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
686 : :
687 : 9 : heap_freetuple(tup);
688 : : }
689 : :
690 : 9 : count++;
691 : : }
692 : :
693 : 500 : systable_endscan(scan);
694 : :
695 : 500 : table_close(depRel, RowExclusiveLock);
696 : :
697 : 500 : return count;
698 : : }
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
1514 tgl@sss.pgh.pa.us 709 : 1092592 : isObjectPinned(const ObjectAddress *object)
710 : : {
711 : 1092592 : 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
5324 732 : 664 : getExtensionOfObject(Oid classId, Oid objectId)
733 : : {
734 : 664 : Oid result = InvalidOid;
735 : : Relation depRel;
736 : : ScanKeyData key[2];
737 : : SysScanDesc scan;
738 : : HeapTuple tup;
739 : :
2420 andres@anarazel.de 740 : 664 : depRel = table_open(DependRelationId, AccessShareLock);
741 : :
5324 tgl@sss.pgh.pa.us 742 : 664 : ScanKeyInit(&key[0],
743 : : Anum_pg_depend_classid,
744 : : BTEqualStrategyNumber, F_OIDEQ,
745 : : ObjectIdGetDatum(classId));
746 : 664 : ScanKeyInit(&key[1],
747 : : Anum_pg_depend_objid,
748 : : BTEqualStrategyNumber, F_OIDEQ,
749 : : ObjectIdGetDatum(objectId));
750 : :
751 : 664 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
752 : : NULL, 2, key);
753 : :
754 [ + + ]: 1561 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
755 : : {
756 : 1487 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
757 : :
758 [ + + ]: 1487 : if (depform->refclassid == ExtensionRelationId &&
759 [ + - ]: 590 : depform->deptype == DEPENDENCY_EXTENSION)
760 : : {
761 : 590 : result = depform->refobjid;
762 : 590 : break; /* no need to keep scanning */
763 : : }
764 : : }
765 : :
766 : 664 : systable_endscan(scan);
767 : :
2420 andres@anarazel.de 768 : 664 : table_close(depRel, AccessShareLock);
769 : :
5324 tgl@sss.pgh.pa.us 770 : 664 : return result;
771 : : }
772 : :
773 : : /*
774 : : * Return (possibly NIL) list of extensions that the given object depends on
775 : : * in DEPENDENCY_AUTO_EXTENSION mode.
776 : : */
777 : : List *
2005 alvherre@alvh.no-ip. 778 : 19 : getAutoExtensionsOfObject(Oid classId, Oid objectId)
779 : : {
780 : 19 : List *result = NIL;
781 : : Relation depRel;
782 : : ScanKeyData key[2];
783 : : SysScanDesc scan;
784 : : HeapTuple tup;
785 : :
786 : 19 : depRel = table_open(DependRelationId, AccessShareLock);
787 : :
788 : 19 : ScanKeyInit(&key[0],
789 : : Anum_pg_depend_classid,
790 : : BTEqualStrategyNumber, F_OIDEQ,
791 : : ObjectIdGetDatum(classId));
792 : 19 : ScanKeyInit(&key[1],
793 : : Anum_pg_depend_objid,
794 : : BTEqualStrategyNumber, F_OIDEQ,
795 : : ObjectIdGetDatum(objectId));
796 : :
797 : 19 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
798 : : NULL, 2, key);
799 : :
800 [ + + ]: 48 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
801 : : {
802 : 29 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
803 : :
804 [ + + ]: 29 : if (depform->refclassid == ExtensionRelationId &&
805 [ + - ]: 1 : depform->deptype == DEPENDENCY_AUTO_EXTENSION)
806 : 1 : result = lappend_oid(result, depform->refobjid);
807 : : }
808 : :
809 : 19 : systable_endscan(scan);
810 : :
811 : 19 : table_close(depRel, AccessShareLock);
812 : :
813 : 19 : return result;
814 : : }
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
3075 peter_e@gmx.net 828 : 416 : sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
829 : : {
6956 tgl@sss.pgh.pa.us 830 : 416 : bool ret = false;
831 : : Relation depRel;
832 : : ScanKeyData key[2];
833 : : SysScanDesc scan;
834 : : HeapTuple tup;
835 : :
2420 andres@anarazel.de 836 : 416 : depRel = table_open(DependRelationId, AccessShareLock);
837 : :
6956 tgl@sss.pgh.pa.us 838 : 416 : ScanKeyInit(&key[0],
839 : : Anum_pg_depend_classid,
840 : : BTEqualStrategyNumber, F_OIDEQ,
841 : : ObjectIdGetDatum(RelationRelationId));
842 : 416 : ScanKeyInit(&key[1],
843 : : Anum_pg_depend_objid,
844 : : BTEqualStrategyNumber, F_OIDEQ,
845 : : ObjectIdGetDatum(seqId));
846 : :
847 : 416 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
848 : : NULL, 2, key);
849 : :
850 [ + + ]: 834 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
851 : : {
852 : 424 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
853 : :
854 [ + + ]: 424 : if (depform->refclassid == RelationRelationId &&
3075 peter_e@gmx.net 855 [ + - ]: 6 : depform->deptype == deptype)
856 : : {
6956 tgl@sss.pgh.pa.us 857 : 6 : *tableId = depform->refobjid;
858 : 6 : *colId = depform->refobjsubid;
859 : 6 : ret = true;
860 : 6 : break; /* no need to keep scanning */
861 : : }
862 : : }
863 : :
864 : 416 : systable_endscan(scan);
865 : :
2420 andres@anarazel.de 866 : 416 : table_close(depRel, AccessShareLock);
867 : :
6956 tgl@sss.pgh.pa.us 868 : 416 : return ret;
869 : : }
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 *
2238 peter@eisentraut.org 877 : 351 : getOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype)
878 : : {
6322 tgl@sss.pgh.pa.us 879 : 351 : List *result = NIL;
880 : : Relation depRel;
881 : : ScanKeyData key[3];
882 : : SysScanDesc scan;
883 : : HeapTuple tup;
884 : :
2420 andres@anarazel.de 885 : 351 : depRel = table_open(DependRelationId, AccessShareLock);
886 : :
6322 tgl@sss.pgh.pa.us 887 : 351 : ScanKeyInit(&key[0],
888 : : Anum_pg_depend_refclassid,
889 : : BTEqualStrategyNumber, F_OIDEQ,
890 : : ObjectIdGetDatum(RelationRelationId));
891 : 351 : ScanKeyInit(&key[1],
892 : : Anum_pg_depend_refobjid,
893 : : BTEqualStrategyNumber, F_OIDEQ,
894 : : ObjectIdGetDatum(relid));
3075 peter_e@gmx.net 895 [ + + ]: 351 : if (attnum)
896 : 301 : ScanKeyInit(&key[2],
897 : : Anum_pg_depend_refobjsubid,
898 : : BTEqualStrategyNumber, F_INT4EQ,
899 : : Int32GetDatum(attnum));
900 : :
6322 tgl@sss.pgh.pa.us 901 [ + + ]: 351 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
902 : : NULL, attnum ? 3 : 2, key);
903 : :
904 [ + + ]: 1170 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
905 : : {
906 : 819 : 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 [ + + ]: 819 : if (deprec->classid == RelationRelationId &&
914 [ + - ]: 356 : deprec->objsubid == 0 &&
915 [ + + ]: 356 : deprec->refobjsubid != 0 &&
3075 peter_e@gmx.net 916 [ + + + - : 682 : (deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) &&
+ + ]
6322 tgl@sss.pgh.pa.us 917 : 341 : get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
918 : : {
2238 peter@eisentraut.org 919 [ + + + + ]: 339 : if (!deptype || deprec->deptype == deptype)
920 : 336 : result = lappend_oid(result, deprec->objid);
921 : : }
922 : : }
923 : :
6322 tgl@sss.pgh.pa.us 924 : 351 : systable_endscan(scan);
925 : :
2420 andres@anarazel.de 926 : 351 : table_close(depRel, AccessShareLock);
927 : :
6322 tgl@sss.pgh.pa.us 928 : 351 : return result;
929 : : }
930 : :
931 : : /*
932 : : * Collect a list of OIDs of all sequences owned (identity or serial) by the
933 : : * specified relation.
934 : : */
935 : : List *
2238 peter@eisentraut.org 936 : 50 : getOwnedSequences(Oid relid)
937 : : {
938 : 50 : return getOwnedSequences_internal(relid, 0, 0);
939 : : }
940 : :
941 : : /*
942 : : * Get owned identity sequence, error if not exactly one.
943 : : */
944 : : Oid
487 945 : 301 : getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
946 : : {
468 michael@paquier.xyz 947 : 301 : Oid relid = RelationGetRelid(rel);
948 : : 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 : : */
487 peter@eisentraut.org 954 [ + + ]: 301 : if (RelationGetForm(rel)->relispartition)
955 : : {
468 michael@paquier.xyz 956 : 27 : List *ancestors = get_partition_ancestors(relid);
957 : 27 : const char *attname = get_attname(relid, attnum, false);
958 : :
487 peter@eisentraut.org 959 : 27 : relid = llast_oid(ancestors);
468 michael@paquier.xyz 960 : 27 : attnum = get_attnum(relid, attname);
961 [ - + ]: 27 : if (attnum == InvalidAttrNumber)
468 michael@paquier.xyz 962 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
963 : : attname, relid);
487 peter@eisentraut.org 964 :CBC 27 : list_free(ancestors);
965 : : }
966 : :
967 : 301 : seqlist = getOwnedSequences_internal(relid, attnum, DEPENDENCY_INTERNAL);
3075 peter_e@gmx.net 968 [ - + ]: 301 : if (list_length(seqlist) > 1)
3075 peter_e@gmx.net 969 [ # # ]:UBC 0 : elog(ERROR, "more than one owned sequence found");
1116 tgl@sss.pgh.pa.us 970 [ + + ]:CBC 301 : else if (seqlist == NIL)
971 : : {
2238 peter@eisentraut.org 972 [ + - ]: 6 : if (missing_ok)
973 : 6 : return InvalidOid;
974 : : else
2238 peter@eisentraut.org 975 [ # # ]:UBC 0 : elog(ERROR, "no owned sequence found");
976 : : }
977 : :
3074 peter_e@gmx.net 978 :CBC 295 : return linitial_oid(seqlist);
979 : : }
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
6489 tgl@sss.pgh.pa.us 988 : 6846 : get_index_constraint(Oid indexId)
989 : : {
990 : 6846 : Oid constraintId = InvalidOid;
991 : : Relation depRel;
992 : : ScanKeyData key[3];
993 : : SysScanDesc scan;
994 : : HeapTuple tup;
995 : :
996 : : /* Search the dependency table for the index */
2420 andres@anarazel.de 997 : 6846 : depRel = table_open(DependRelationId, AccessShareLock);
998 : :
6489 tgl@sss.pgh.pa.us 999 : 6846 : ScanKeyInit(&key[0],
1000 : : Anum_pg_depend_classid,
1001 : : BTEqualStrategyNumber, F_OIDEQ,
1002 : : ObjectIdGetDatum(RelationRelationId));
1003 : 6846 : ScanKeyInit(&key[1],
1004 : : Anum_pg_depend_objid,
1005 : : BTEqualStrategyNumber, F_OIDEQ,
1006 : : ObjectIdGetDatum(indexId));
1007 : 6846 : ScanKeyInit(&key[2],
1008 : : Anum_pg_depend_objsubid,
1009 : : BTEqualStrategyNumber, F_INT4EQ,
1010 : : Int32GetDatum(0));
1011 : :
1012 : 6846 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
1013 : : NULL, 3, key);
1014 : :
1015 [ + + ]: 8351 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
1016 : : {
1017 : 2269 : 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 [ + + ]: 2269 : if (deprec->refclassid == ConstraintRelationId &&
1024 [ + - ]: 764 : deprec->refobjsubid == 0 &&
1025 [ + - ]: 764 : deprec->deptype == DEPENDENCY_INTERNAL)
1026 : : {
1027 : 764 : constraintId = deprec->refobjid;
1028 : 764 : break;
1029 : : }
1030 : : }
1031 : :
1032 : 6846 : systable_endscan(scan);
2420 andres@anarazel.de 1033 : 6846 : table_close(depRel, AccessShareLock);
1034 : :
6489 tgl@sss.pgh.pa.us 1035 : 6846 : return constraintId;
1036 : : }
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 *
2353 peter@eisentraut.org 1044 : 250 : get_index_ref_constraints(Oid indexId)
1045 : : {
1046 : 250 : List *result = NIL;
1047 : : Relation depRel;
1048 : : ScanKeyData key[3];
1049 : : SysScanDesc scan;
1050 : : HeapTuple tup;
1051 : :
1052 : : /* Search the dependency table for the index */
1053 : 250 : depRel = table_open(DependRelationId, AccessShareLock);
1054 : :
1055 : 250 : ScanKeyInit(&key[0],
1056 : : Anum_pg_depend_refclassid,
1057 : : BTEqualStrategyNumber, F_OIDEQ,
1058 : : ObjectIdGetDatum(RelationRelationId));
1059 : 250 : ScanKeyInit(&key[1],
1060 : : Anum_pg_depend_refobjid,
1061 : : BTEqualStrategyNumber, F_OIDEQ,
1062 : : ObjectIdGetDatum(indexId));
1063 : 250 : ScanKeyInit(&key[2],
1064 : : Anum_pg_depend_refobjsubid,
1065 : : BTEqualStrategyNumber, F_INT4EQ,
1066 : : Int32GetDatum(0));
1067 : :
1068 : 250 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
1069 : : NULL, 3, key);
1070 : :
1071 [ + + ]: 259 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
1072 : : {
1073 : 9 : 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 [ + - ]: 9 : if (deprec->classid == ConstraintRelationId &&
1080 [ + - ]: 9 : deprec->objsubid == 0 &&
1081 [ + - ]: 9 : deprec->deptype == DEPENDENCY_NORMAL)
1082 : : {
1083 : 9 : result = lappend_oid(result, deprec->objid);
1084 : : }
1085 : : }
1086 : :
1087 : 250 : systable_endscan(scan);
1088 : 250 : table_close(depRel, AccessShareLock);
1089 : :
1090 : 250 : return result;
1091 : : }
|