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