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