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
8647 tgl@sss.pgh.pa.us 47 :CBC 462546 : recordDependencyOn(const ObjectAddress *depender,
48 : : const ObjectAddress *referenced,
49 : : DependencyType behavior)
50 : : {
1773 tmunro@postgresql.or 51 : 462546 : recordMultipleDependencies(depender, referenced, 1, behavior);
8643 tgl@sss.pgh.pa.us 52 : 462546 : }
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 : 690332 : 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 [ + + ]: 690332 : if (nreferenced <= 0)
73 : 26431 : 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 : : */
8647 82 [ + + ]: 663901 : if (IsBootstrapProcessingMode())
83 : 34782 : return;
84 : :
2610 andres@anarazel.de 85 : 629119 : 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 : : */
2017 michael@paquier.xyz 91 [ + - ]: 629119 : max_slots = Min(nreferenced,
92 : : MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_depend));
95 michael@paquier.xyz 93 :GNC 629119 : slot = palloc_array(TupleTableSlot *, max_slots);
94 : :
95 : : /* Don't open indexes unless we need to make an update */
8623 tgl@sss.pgh.pa.us 96 :CBC 629119 : indstate = NULL;
97 : :
98 : : /* number of slots currently storing tuples */
2017 michael@paquier.xyz 99 : 629119 : slot_stored_count = 0;
100 : : /* number of slots currently initialized */
101 : 629119 : slot_init_count = 0;
8643 tgl@sss.pgh.pa.us 102 [ + + ]: 1794967 : 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 : : */
1704 109 [ + + ]: 1165848 : if (isObjectPinned(referenced))
2017 michael@paquier.xyz 110 : 855535 : continue;
111 : :
112 [ + - ]: 310313 : if (slot_init_count < max_slots)
113 : : {
114 : 310313 : slot[slot_stored_count] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc),
115 : : &TTSOpsHeapTuple);
116 : 310313 : slot_init_count++;
117 : : }
118 : :
119 : 310313 : 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 : 310313 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
126 : 310313 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
127 : 310313 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
128 : 310313 : slot[slot_stored_count]->tts_values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
129 : 310313 : slot[slot_stored_count]->tts_values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
130 : 310313 : slot[slot_stored_count]->tts_values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
131 : 310313 : slot[slot_stored_count]->tts_values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
132 : :
1773 tmunro@postgresql.or 133 : 310313 : memset(slot[slot_stored_count]->tts_isnull, false,
134 : 310313 : slot[slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));
135 : :
2017 michael@paquier.xyz 136 : 310313 : ExecStoreVirtualTuple(slot[slot_stored_count]);
137 : 310313 : slot_stored_count++;
138 : :
139 : : /* If slots are full, insert a batch of tuples */
140 [ + + ]: 310313 : if (slot_stored_count == max_slots)
141 : : {
142 : : /* fetch index info only when we know we need it */
8623 tgl@sss.pgh.pa.us 143 [ + - ]: 215979 : if (indstate == NULL)
144 : 215979 : indstate = CatalogOpenIndexes(dependDesc);
145 : :
2017 michael@paquier.xyz 146 : 215979 : CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
147 : : indstate);
148 : 215979 : slot_stored_count = 0;
149 : : }
150 : : }
151 : :
152 : : /* Insert any tuples left in the buffer */
153 [ + + ]: 629119 : if (slot_stored_count > 0)
154 : : {
155 : : /* fetch index info only when we know we need it */
156 [ + - ]: 48967 : if (indstate == NULL)
157 : 48967 : indstate = CatalogOpenIndexes(dependDesc);
158 : :
159 : 48967 : CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
160 : : indstate);
161 : : }
162 : :
8623 tgl@sss.pgh.pa.us 163 [ + + ]: 629119 : if (indstate != NULL)
164 : 264946 : CatalogCloseIndexes(indstate);
165 : :
2610 andres@anarazel.de 166 : 629119 : table_close(dependDesc, RowExclusiveLock);
167 : :
168 : : /* Drop only the number of slots used */
2017 michael@paquier.xyz 169 [ + + ]: 939432 : for (i = 0; i < slot_init_count; i++)
170 : 310313 : ExecDropSingleTupleTableSlot(slot[i]);
171 : 629119 : 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
5349 tgl@sss.pgh.pa.us 195 : 198053 : recordDependencyOnCurrentExtension(const ObjectAddress *object,
196 : : bool isReplace)
197 : : {
198 : : /* Only whole objects can be extension members */
199 [ - + ]: 198053 : Assert(object->objectSubId == 0);
200 : :
5514 201 [ + + ]: 198053 : if (creating_extension)
202 : : {
203 : : ObjectAddress extension;
204 : :
205 : : /* Only need to check for existing membership if isReplace */
5349 206 [ + + ]: 5854 : 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 */
5349 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 */
1315 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 */
5514 239 : 5453 : extension.classId = ExtensionRelationId;
240 : 5453 : extension.objectId = CurrentExtensionObject;
241 : 5453 : extension.objectSubId = 0;
242 : :
243 : 5453 : 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
1315 260 : 69 : 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 [ - + ]: 69 : Assert(object->objectSubId == 0);
271 : :
272 [ + + ]: 69 : 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
5514 303 : 9409 : deleteDependencyRecordsFor(Oid classId, Oid objectId,
304 : : bool skipExtensionDeps)
305 : : {
8593 bruce@momjian.us 306 : 9409 : long count = 0;
307 : : Relation depRel;
308 : : ScanKeyData key[2];
309 : : SysScanDesc scan;
310 : : HeapTuple tup;
311 : :
2610 andres@anarazel.de 312 : 9409 : depRel = table_open(DependRelationId, RowExclusiveLock);
313 : :
8159 tgl@sss.pgh.pa.us 314 : 9409 : ScanKeyInit(&key[0],
315 : : Anum_pg_depend_classid,
316 : : BTEqualStrategyNumber, F_OIDEQ,
317 : : ObjectIdGetDatum(classId));
318 : 9409 : ScanKeyInit(&key[1],
319 : : Anum_pg_depend_objid,
320 : : BTEqualStrategyNumber, F_OIDEQ,
321 : : ObjectIdGetDatum(objectId));
322 : :
7640 323 : 9409 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
324 : : NULL, 2, key);
325 : :
8643 326 [ + + ]: 16317 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
327 : : {
5514 328 [ + + ]: 6908 : if (skipExtensionDeps &&
3189 329 [ + + ]: 5307 : ((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION)
5514 330 : 635 : continue;
331 : :
3329 332 : 6273 : CatalogTupleDelete(depRel, &tup->t_self);
8617 333 : 6273 : count++;
334 : : }
335 : :
8643 336 : 9409 : systable_endscan(scan);
337 : :
2610 andres@anarazel.de 338 : 9409 : table_close(depRel, RowExclusiveLock);
339 : :
8617 tgl@sss.pgh.pa.us 340 : 9409 : 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
5512 353 : 7691 : deleteDependencyRecordsForClass(Oid classId, Oid objectId,
354 : : Oid refclassId, char deptype)
355 : : {
356 : 7691 : long count = 0;
357 : : Relation depRel;
358 : : ScanKeyData key[2];
359 : : SysScanDesc scan;
360 : : HeapTuple tup;
361 : :
2610 andres@anarazel.de 362 : 7691 : depRel = table_open(DependRelationId, RowExclusiveLock);
363 : :
5512 tgl@sss.pgh.pa.us 364 : 7691 : ScanKeyInit(&key[0],
365 : : Anum_pg_depend_classid,
366 : : BTEqualStrategyNumber, F_OIDEQ,
367 : : ObjectIdGetDatum(classId));
368 : 7691 : ScanKeyInit(&key[1],
369 : : Anum_pg_depend_objid,
370 : : BTEqualStrategyNumber, F_OIDEQ,
371 : : ObjectIdGetDatum(objectId));
372 : :
373 : 7691 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
374 : : NULL, 2, key);
375 : :
376 [ + + ]: 12647 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
377 : : {
378 : 4956 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
379 : :
380 [ + + + + ]: 4956 : if (depform->refclassid == refclassId && depform->deptype == deptype)
381 : : {
3329 382 : 1111 : CatalogTupleDelete(depRel, &tup->t_self);
5512 383 : 1111 : count++;
384 : : }
385 : : }
386 : :
387 : 7691 : systable_endscan(scan);
388 : :
2610 andres@anarazel.de 389 : 7691 : table_close(depRel, RowExclusiveLock);
390 : :
5512 tgl@sss.pgh.pa.us 391 : 7691 : 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
2155 alvherre@alvh.no-ip. 400 : 43 : deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype,
401 : : Oid refclassId, Oid refobjectId)
402 : : {
403 : 43 : long count = 0;
404 : : Relation depRel;
405 : : ScanKeyData key[2];
406 : : SysScanDesc scan;
407 : : HeapTuple tup;
408 : :
409 : 43 : depRel = table_open(DependRelationId, RowExclusiveLock);
410 : :
411 : 43 : ScanKeyInit(&key[0],
412 : : Anum_pg_depend_classid,
413 : : BTEqualStrategyNumber, F_OIDEQ,
414 : : ObjectIdGetDatum(classId));
415 : 43 : ScanKeyInit(&key[1],
416 : : Anum_pg_depend_objid,
417 : : BTEqualStrategyNumber, F_OIDEQ,
418 : : ObjectIdGetDatum(objectId));
419 : :
420 : 43 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
421 : : NULL, 2, key);
422 : :
423 [ + + ]: 233 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
424 : : {
425 : 190 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
426 : :
427 [ + + ]: 190 : if (depform->refclassid == refclassId &&
428 [ + - ]: 43 : depform->refobjid == refobjectId &&
429 [ + - ]: 43 : depform->deptype == deptype)
430 : : {
431 : 43 : CatalogTupleDelete(depRel, &tup->t_self);
432 : 43 : count++;
433 : : }
434 : : }
435 : :
436 : 43 : systable_endscan(scan);
437 : :
438 : 43 : table_close(depRel, RowExclusiveLock);
439 : :
440 : 43 : 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
7531 tgl@sss.pgh.pa.us 459 : 187 : changeDependencyFor(Oid classId, Oid objectId,
460 : : Oid refClassId, Oid oldRefObjectId,
461 : : Oid newRefObjectId)
462 : : {
463 : 187 : 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 : 187 : objAddr.classId = refClassId;
480 : 187 : objAddr.objectId = oldRefObjectId;
481 : 187 : objAddr.objectSubId = 0;
482 : :
1704 483 : 187 : oldIsPinned = isObjectPinned(&objAddr);
484 : :
7531 485 : 187 : objAddr.objectId = newRefObjectId;
486 : :
1704 487 : 187 : newIsPinned = isObjectPinned(&objAddr);
488 : :
2591 489 [ + + ]: 187 : 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 [ - + ]: 28 : if (newIsPinned)
2591 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 : : */
2591 tgl@sss.pgh.pa.us 502 :CBC 28 : depAddr.classId = classId;
503 : 28 : depAddr.objectId = objectId;
504 : 28 : depAddr.objectSubId = 0;
505 : 28 : recordDependencyOn(&depAddr, &objAddr, DEPENDENCY_NORMAL);
506 : :
507 : 28 : return 1;
508 : : }
509 : :
1704 510 : 159 : depRel = table_open(DependRelationId, RowExclusiveLock);
511 : :
512 : : /* There should be existing dependency record(s), so search. */
7531 513 : 159 : ScanKeyInit(&key[0],
514 : : Anum_pg_depend_classid,
515 : : BTEqualStrategyNumber, F_OIDEQ,
516 : : ObjectIdGetDatum(classId));
517 : 159 : ScanKeyInit(&key[1],
518 : : Anum_pg_depend_objid,
519 : : BTEqualStrategyNumber, F_OIDEQ,
520 : : ObjectIdGetDatum(objectId));
521 : :
522 : 159 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
523 : : NULL, 2, key);
524 : :
525 [ + + ]: 425 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
526 : : {
527 : 266 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
528 : :
529 [ + + ]: 266 : if (depform->refclassid == refClassId &&
530 [ + - ]: 159 : depform->refobjid == oldRefObjectId)
531 : : {
532 [ + + ]: 159 : if (newIsPinned)
3329 533 : 33 : CatalogTupleDelete(depRel, &tup->t_self);
534 : : else
535 : : {
536 : : /* make a modifiable copy */
7531 537 : 126 : tup = heap_copytuple(tup);
538 : 126 : depform = (Form_pg_depend) GETSTRUCT(tup);
539 : :
540 : 126 : depform->refobjid = newRefObjectId;
541 : :
3330 alvherre@alvh.no-ip. 542 : 126 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
543 : :
7531 tgl@sss.pgh.pa.us 544 : 126 : heap_freetuple(tup);
545 : : }
546 : :
547 : 159 : count++;
548 : : }
549 : : }
550 : :
551 : 159 : systable_endscan(scan);
552 : :
2610 andres@anarazel.de 553 : 159 : table_close(depRel, RowExclusiveLock);
554 : :
7531 tgl@sss.pgh.pa.us 555 : 159 : 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
2529 peter@eisentraut.org 567 : 556 : changeDependenciesOf(Oid classId, Oid oldObjectId,
568 : : Oid newObjectId)
569 : : {
570 : 556 : long count = 0;
571 : : Relation depRel;
572 : : ScanKeyData key[2];
573 : : SysScanDesc scan;
574 : : HeapTuple tup;
575 : :
576 : 556 : depRel = table_open(DependRelationId, RowExclusiveLock);
577 : :
578 : 556 : ScanKeyInit(&key[0],
579 : : Anum_pg_depend_classid,
580 : : BTEqualStrategyNumber, F_OIDEQ,
581 : : ObjectIdGetDatum(classId));
582 : 556 : ScanKeyInit(&key[1],
583 : : Anum_pg_depend_objid,
584 : : BTEqualStrategyNumber, F_OIDEQ,
585 : : ObjectIdGetDatum(oldObjectId));
586 : :
587 : 556 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
588 : : NULL, 2, key);
589 : :
590 [ + + ]: 1516 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
591 : : {
592 : : Form_pg_depend depform;
593 : :
594 : : /* make a modifiable copy */
595 : 960 : tup = heap_copytuple(tup);
596 : 960 : depform = (Form_pg_depend) GETSTRUCT(tup);
597 : :
598 : 960 : depform->objid = newObjectId;
599 : :
600 : 960 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
601 : :
602 : 960 : heap_freetuple(tup);
603 : :
604 : 960 : count++;
605 : : }
606 : :
607 : 556 : systable_endscan(scan);
608 : :
609 : 556 : table_close(depRel, RowExclusiveLock);
610 : :
611 : 556 : 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
2543 623 : 556 : changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
624 : : Oid newRefObjectId)
625 : : {
626 : 556 : long count = 0;
627 : : Relation depRel;
628 : : ScanKeyData key[2];
629 : : SysScanDesc scan;
630 : : HeapTuple tup;
631 : : ObjectAddress objAddr;
632 : : bool newIsPinned;
633 : :
634 : 556 : 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 : 556 : objAddr.classId = refClassId;
643 : 556 : objAddr.objectId = oldRefObjectId;
644 : 556 : objAddr.objectSubId = 0;
645 : :
1704 tgl@sss.pgh.pa.us 646 [ - + ]: 556 : if (isObjectPinned(&objAddr))
2543 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 : : */
2543 peter@eisentraut.org 656 :CBC 556 : objAddr.objectId = newRefObjectId;
657 : :
1704 tgl@sss.pgh.pa.us 658 : 556 : newIsPinned = isObjectPinned(&objAddr);
659 : :
660 : : /* Now search for dependency records */
2543 peter@eisentraut.org 661 : 556 : ScanKeyInit(&key[0],
662 : : Anum_pg_depend_refclassid,
663 : : BTEqualStrategyNumber, F_OIDEQ,
664 : : ObjectIdGetDatum(refClassId));
665 : 556 : ScanKeyInit(&key[1],
666 : : Anum_pg_depend_refobjid,
667 : : BTEqualStrategyNumber, F_OIDEQ,
668 : : ObjectIdGetDatum(oldRefObjectId));
669 : :
670 : 556 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
671 : : NULL, 2, key);
672 : :
673 [ + + ]: 565 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
674 : : {
675 [ - + ]: 9 : if (newIsPinned)
2543 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 */
2543 peter@eisentraut.org 682 :CBC 9 : tup = heap_copytuple(tup);
683 : 9 : depform = (Form_pg_depend) GETSTRUCT(tup);
684 : :
685 : 9 : depform->refobjid = newRefObjectId;
686 : :
687 : 9 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
688 : :
689 : 9 : heap_freetuple(tup);
690 : : }
691 : :
692 : 9 : count++;
693 : : }
694 : :
695 : 556 : systable_endscan(scan);
696 : :
697 : 556 : table_close(depRel, RowExclusiveLock);
698 : :
699 : 556 : 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
1704 tgl@sss.pgh.pa.us 711 : 1167334 : isObjectPinned(const ObjectAddress *object)
712 : : {
713 : 1167334 : 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
5514 734 : 656 : getExtensionOfObject(Oid classId, Oid objectId)
735 : : {
736 : 656 : Oid result = InvalidOid;
737 : : Relation depRel;
738 : : ScanKeyData key[2];
739 : : SysScanDesc scan;
740 : : HeapTuple tup;
741 : :
2610 andres@anarazel.de 742 : 656 : depRel = table_open(DependRelationId, AccessShareLock);
743 : :
5514 tgl@sss.pgh.pa.us 744 : 656 : ScanKeyInit(&key[0],
745 : : Anum_pg_depend_classid,
746 : : BTEqualStrategyNumber, F_OIDEQ,
747 : : ObjectIdGetDatum(classId));
748 : 656 : ScanKeyInit(&key[1],
749 : : Anum_pg_depend_objid,
750 : : BTEqualStrategyNumber, F_OIDEQ,
751 : : ObjectIdGetDatum(objectId));
752 : :
753 : 656 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
754 : : NULL, 2, key);
755 : :
756 [ + + ]: 1562 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
757 : : {
758 : 1484 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
759 : :
760 [ + + ]: 1484 : if (depform->refclassid == ExtensionRelationId &&
761 [ + - ]: 578 : depform->deptype == DEPENDENCY_EXTENSION)
762 : : {
763 : 578 : result = depform->refobjid;
764 : 578 : break; /* no need to keep scanning */
765 : : }
766 : : }
767 : :
768 : 656 : systable_endscan(scan);
769 : :
2610 andres@anarazel.de 770 : 656 : table_close(depRel, AccessShareLock);
771 : :
5514 tgl@sss.pgh.pa.us 772 : 656 : 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 *
2195 alvherre@alvh.no-ip. 780 : 19 : getAutoExtensionsOfObject(Oid classId, Oid objectId)
781 : : {
782 : 19 : List *result = NIL;
783 : : Relation depRel;
784 : : ScanKeyData key[2];
785 : : SysScanDesc scan;
786 : : HeapTuple tup;
787 : :
788 : 19 : depRel = table_open(DependRelationId, AccessShareLock);
789 : :
790 : 19 : ScanKeyInit(&key[0],
791 : : Anum_pg_depend_classid,
792 : : BTEqualStrategyNumber, F_OIDEQ,
793 : : ObjectIdGetDatum(classId));
794 : 19 : ScanKeyInit(&key[1],
795 : : Anum_pg_depend_objid,
796 : : BTEqualStrategyNumber, F_OIDEQ,
797 : : ObjectIdGetDatum(objectId));
798 : :
799 : 19 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
800 : : NULL, 2, key);
801 : :
802 [ + + ]: 48 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
803 : : {
804 : 29 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
805 : :
806 [ + + ]: 29 : if (depform->refclassid == ExtensionRelationId &&
807 [ + - ]: 1 : depform->deptype == DEPENDENCY_AUTO_EXTENSION)
808 : 1 : result = lappend_oid(result, depform->refobjid);
809 : : }
810 : :
811 : 19 : systable_endscan(scan);
812 : :
813 : 19 : table_close(depRel, AccessShareLock);
814 : :
34 tgl@sss.pgh.pa.us 815 : 19 : 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))
34 tgl@sss.pgh.pa.us 870 :UBC 0 : continue; /* should we throw an error? */
34 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 : : }
34 tgl@sss.pgh.pa.us 878 :UBC 0 : ReleaseSysCache(typtup);
879 : : }
880 : : }
881 : :
34 tgl@sss.pgh.pa.us 882 :CBC 1 : systable_endscan(scan);
883 : :
884 : 1 : table_close(depRel, AccessShareLock);
885 : :
2195 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
3265 peter_e@gmx.net 901 : 433 : sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
902 : : {
7146 tgl@sss.pgh.pa.us 903 : 433 : bool ret = false;
904 : : Relation depRel;
905 : : ScanKeyData key[2];
906 : : SysScanDesc scan;
907 : : HeapTuple tup;
908 : :
2610 andres@anarazel.de 909 : 433 : depRel = table_open(DependRelationId, AccessShareLock);
910 : :
7146 tgl@sss.pgh.pa.us 911 : 433 : ScanKeyInit(&key[0],
912 : : Anum_pg_depend_classid,
913 : : BTEqualStrategyNumber, F_OIDEQ,
914 : : ObjectIdGetDatum(RelationRelationId));
915 : 433 : ScanKeyInit(&key[1],
916 : : Anum_pg_depend_objid,
917 : : BTEqualStrategyNumber, F_OIDEQ,
918 : : ObjectIdGetDatum(seqId));
919 : :
920 : 433 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
921 : : NULL, 2, key);
922 : :
923 [ + + ]: 868 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
924 : : {
925 : 441 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
926 : :
927 [ + + ]: 441 : if (depform->refclassid == RelationRelationId &&
3265 peter_e@gmx.net 928 [ + - ]: 6 : depform->deptype == deptype)
929 : : {
7146 tgl@sss.pgh.pa.us 930 : 6 : *tableId = depform->refobjid;
931 : 6 : *colId = depform->refobjsubid;
932 : 6 : ret = true;
933 : 6 : break; /* no need to keep scanning */
934 : : }
935 : : }
936 : :
937 : 433 : systable_endscan(scan);
938 : :
2610 andres@anarazel.de 939 : 433 : table_close(depRel, AccessShareLock);
940 : :
7146 tgl@sss.pgh.pa.us 941 : 433 : 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 *
2428 peter@eisentraut.org 950 : 372 : getOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype)
951 : : {
6512 tgl@sss.pgh.pa.us 952 : 372 : List *result = NIL;
953 : : Relation depRel;
954 : : ScanKeyData key[3];
955 : : SysScanDesc scan;
956 : : HeapTuple tup;
957 : :
2610 andres@anarazel.de 958 : 372 : depRel = table_open(DependRelationId, AccessShareLock);
959 : :
6512 tgl@sss.pgh.pa.us 960 : 372 : ScanKeyInit(&key[0],
961 : : Anum_pg_depend_refclassid,
962 : : BTEqualStrategyNumber, F_OIDEQ,
963 : : ObjectIdGetDatum(RelationRelationId));
964 : 372 : ScanKeyInit(&key[1],
965 : : Anum_pg_depend_refobjid,
966 : : BTEqualStrategyNumber, F_OIDEQ,
967 : : ObjectIdGetDatum(relid));
3265 peter_e@gmx.net 968 [ + + ]: 372 : if (attnum)
969 : 322 : ScanKeyInit(&key[2],
970 : : Anum_pg_depend_refobjsubid,
971 : : BTEqualStrategyNumber, F_INT4EQ,
972 : : Int32GetDatum(attnum));
973 : :
6512 tgl@sss.pgh.pa.us 974 [ + + ]: 372 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
975 : : NULL, attnum ? 3 : 2, key);
976 : :
977 [ + + ]: 1248 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
978 : : {
979 : 876 : 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 [ + + ]: 876 : if (deprec->classid == RelationRelationId &&
987 [ + - ]: 377 : deprec->objsubid == 0 &&
988 [ + + ]: 377 : deprec->refobjsubid != 0 &&
3265 peter_e@gmx.net 989 [ + + + - : 724 : (deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) &&
+ + ]
6512 tgl@sss.pgh.pa.us 990 : 362 : get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
991 : : {
2428 peter@eisentraut.org 992 [ + + + + ]: 360 : if (!deptype || deprec->deptype == deptype)
993 : 357 : result = lappend_oid(result, deprec->objid);
994 : : }
995 : : }
996 : :
6512 tgl@sss.pgh.pa.us 997 : 372 : systable_endscan(scan);
998 : :
2610 andres@anarazel.de 999 : 372 : table_close(depRel, AccessShareLock);
1000 : :
6512 tgl@sss.pgh.pa.us 1001 : 372 : 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 *
2428 peter@eisentraut.org 1009 : 50 : getOwnedSequences(Oid relid)
1010 : : {
1011 : 50 : return getOwnedSequences_internal(relid, 0, 0);
1012 : : }
1013 : :
1014 : : /*
1015 : : * Get owned identity sequence, error if not exactly one.
1016 : : */
1017 : : Oid
677 1018 : 322 : getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
1019 : : {
658 michael@paquier.xyz 1020 : 322 : 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 : : */
677 peter@eisentraut.org 1027 [ + + ]: 322 : if (RelationGetForm(rel)->relispartition)
1028 : : {
658 michael@paquier.xyz 1029 : 27 : List *ancestors = get_partition_ancestors(relid);
1030 : 27 : const char *attname = get_attname(relid, attnum, false);
1031 : :
677 peter@eisentraut.org 1032 : 27 : relid = llast_oid(ancestors);
658 michael@paquier.xyz 1033 : 27 : attnum = get_attnum(relid, attname);
1034 [ - + ]: 27 : if (attnum == InvalidAttrNumber)
658 michael@paquier.xyz 1035 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
1036 : : attname, relid);
677 peter@eisentraut.org 1037 :CBC 27 : list_free(ancestors);
1038 : : }
1039 : :
1040 : 322 : seqlist = getOwnedSequences_internal(relid, attnum, DEPENDENCY_INTERNAL);
3265 peter_e@gmx.net 1041 [ - + ]: 322 : if (list_length(seqlist) > 1)
3265 peter_e@gmx.net 1042 [ # # ]:UBC 0 : elog(ERROR, "more than one owned sequence found");
1306 tgl@sss.pgh.pa.us 1043 [ + + ]:CBC 322 : else if (seqlist == NIL)
1044 : : {
2428 peter@eisentraut.org 1045 [ + - ]: 6 : if (missing_ok)
1046 : 6 : return InvalidOid;
1047 : : else
2428 peter@eisentraut.org 1048 [ # # ]:UBC 0 : elog(ERROR, "no owned sequence found");
1049 : : }
1050 : :
3264 peter_e@gmx.net 1051 :CBC 316 : 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
6679 tgl@sss.pgh.pa.us 1061 : 7142 : get_index_constraint(Oid indexId)
1062 : : {
1063 : 7142 : 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 */
2610 andres@anarazel.de 1070 : 7142 : depRel = table_open(DependRelationId, AccessShareLock);
1071 : :
6679 tgl@sss.pgh.pa.us 1072 : 7142 : ScanKeyInit(&key[0],
1073 : : Anum_pg_depend_classid,
1074 : : BTEqualStrategyNumber, F_OIDEQ,
1075 : : ObjectIdGetDatum(RelationRelationId));
1076 : 7142 : ScanKeyInit(&key[1],
1077 : : Anum_pg_depend_objid,
1078 : : BTEqualStrategyNumber, F_OIDEQ,
1079 : : ObjectIdGetDatum(indexId));
1080 : 7142 : ScanKeyInit(&key[2],
1081 : : Anum_pg_depend_objsubid,
1082 : : BTEqualStrategyNumber, F_INT4EQ,
1083 : : Int32GetDatum(0));
1084 : :
1085 : 7142 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
1086 : : NULL, 3, key);
1087 : :
1088 [ + + ]: 8678 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
1089 : : {
1090 : 2464 : 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 [ + + ]: 2464 : if (deprec->refclassid == ConstraintRelationId &&
1097 [ + - ]: 928 : deprec->refobjsubid == 0 &&
1098 [ + - ]: 928 : deprec->deptype == DEPENDENCY_INTERNAL)
1099 : : {
1100 : 928 : constraintId = deprec->refobjid;
1101 : 928 : break;
1102 : : }
1103 : : }
1104 : :
1105 : 7142 : systable_endscan(scan);
2610 andres@anarazel.de 1106 : 7142 : table_close(depRel, AccessShareLock);
1107 : :
6679 tgl@sss.pgh.pa.us 1108 : 7142 : 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 *
2543 peter@eisentraut.org 1117 : 278 : get_index_ref_constraints(Oid indexId)
1118 : : {
1119 : 278 : 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 : 278 : depRel = table_open(DependRelationId, AccessShareLock);
1127 : :
1128 : 278 : ScanKeyInit(&key[0],
1129 : : Anum_pg_depend_refclassid,
1130 : : BTEqualStrategyNumber, F_OIDEQ,
1131 : : ObjectIdGetDatum(RelationRelationId));
1132 : 278 : ScanKeyInit(&key[1],
1133 : : Anum_pg_depend_refobjid,
1134 : : BTEqualStrategyNumber, F_OIDEQ,
1135 : : ObjectIdGetDatum(indexId));
1136 : 278 : ScanKeyInit(&key[2],
1137 : : Anum_pg_depend_refobjsubid,
1138 : : BTEqualStrategyNumber, F_INT4EQ,
1139 : : Int32GetDatum(0));
1140 : :
1141 : 278 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
1142 : : NULL, 3, key);
1143 : :
1144 [ + + ]: 287 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
1145 : : {
1146 : 9 : 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 [ + - ]: 9 : if (deprec->classid == ConstraintRelationId &&
1153 [ + - ]: 9 : deprec->objsubid == 0 &&
1154 [ + - ]: 9 : deprec->deptype == DEPENDENCY_NORMAL)
1155 : : {
1156 : 9 : result = lappend_oid(result, deprec->objid);
1157 : : }
1158 : : }
1159 : :
1160 : 278 : systable_endscan(scan);
1161 : 278 : table_close(depRel, AccessShareLock);
1162 : :
1163 : 278 : return result;
1164 : : }
|