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