1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * pg_depend.c |
4 | * routines to support manipulation of the pg_depend relation |
5 | * |
6 | * Portions Copyright (c) 1996-2019, 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/dependency.h" |
21 | #include "catalog/indexing.h" |
22 | #include "catalog/pg_constraint.h" |
23 | #include "catalog/pg_depend.h" |
24 | #include "catalog/pg_extension.h" |
25 | #include "commands/extension.h" |
26 | #include "miscadmin.h" |
27 | #include "utils/fmgroids.h" |
28 | #include "utils/lsyscache.h" |
29 | #include "utils/rel.h" |
30 | |
31 | |
32 | static bool isObjectPinned(const ObjectAddress *object, Relation rel); |
33 | |
34 | |
35 | /* |
36 | * Record a dependency between 2 objects via their respective objectAddress. |
37 | * The first argument is the dependent object, the second the one it |
38 | * references. |
39 | * |
40 | * This simply creates an entry in pg_depend, without any other processing. |
41 | */ |
42 | void |
43 | recordDependencyOn(const ObjectAddress *depender, |
44 | const ObjectAddress *referenced, |
45 | DependencyType behavior) |
46 | { |
47 | recordMultipleDependencies(depender, referenced, 1, behavior); |
48 | } |
49 | |
50 | /* |
51 | * Record multiple dependencies (of the same kind) for a single dependent |
52 | * object. This has a little less overhead than recording each separately. |
53 | */ |
54 | void |
55 | recordMultipleDependencies(const ObjectAddress *depender, |
56 | const ObjectAddress *referenced, |
57 | int nreferenced, |
58 | DependencyType behavior) |
59 | { |
60 | Relation dependDesc; |
61 | CatalogIndexState indstate; |
62 | HeapTuple tup; |
63 | int i; |
64 | bool nulls[Natts_pg_depend]; |
65 | Datum values[Natts_pg_depend]; |
66 | |
67 | if (nreferenced <= 0) |
68 | return; /* nothing to do */ |
69 | |
70 | /* |
71 | * During bootstrap, do nothing since pg_depend may not exist yet. initdb |
72 | * will fill in appropriate pg_depend entries after bootstrap. |
73 | */ |
74 | if (IsBootstrapProcessingMode()) |
75 | return; |
76 | |
77 | dependDesc = table_open(DependRelationId, RowExclusiveLock); |
78 | |
79 | /* Don't open indexes unless we need to make an update */ |
80 | indstate = NULL; |
81 | |
82 | memset(nulls, false, sizeof(nulls)); |
83 | |
84 | for (i = 0; i < nreferenced; i++, referenced++) |
85 | { |
86 | /* |
87 | * If the referenced object is pinned by the system, there's no real |
88 | * need to record dependencies on it. This saves lots of space in |
89 | * pg_depend, so it's worth the time taken to check. |
90 | */ |
91 | if (!isObjectPinned(referenced, dependDesc)) |
92 | { |
93 | /* |
94 | * Record the Dependency. Note we don't bother to check for |
95 | * duplicate dependencies; there's no harm in them. |
96 | */ |
97 | values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId); |
98 | values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId); |
99 | values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId); |
100 | |
101 | values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId); |
102 | values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId); |
103 | values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId); |
104 | |
105 | values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior); |
106 | |
107 | tup = heap_form_tuple(dependDesc->rd_att, values, nulls); |
108 | |
109 | /* fetch index info only when we know we need it */ |
110 | if (indstate == NULL) |
111 | indstate = CatalogOpenIndexes(dependDesc); |
112 | |
113 | CatalogTupleInsertWithInfo(dependDesc, tup, indstate); |
114 | |
115 | heap_freetuple(tup); |
116 | } |
117 | } |
118 | |
119 | if (indstate != NULL) |
120 | CatalogCloseIndexes(indstate); |
121 | |
122 | table_close(dependDesc, RowExclusiveLock); |
123 | } |
124 | |
125 | /* |
126 | * If we are executing a CREATE EXTENSION operation, mark the given object |
127 | * as being a member of the extension. Otherwise, do nothing. |
128 | * |
129 | * This must be called during creation of any user-definable object type |
130 | * that could be a member of an extension. |
131 | * |
132 | * If isReplace is true, the object already existed (or might have already |
133 | * existed), so we must check for a pre-existing extension membership entry. |
134 | * Passing false is a guarantee that the object is newly created, and so |
135 | * could not already be a member of any extension. |
136 | */ |
137 | void |
138 | recordDependencyOnCurrentExtension(const ObjectAddress *object, |
139 | bool isReplace) |
140 | { |
141 | /* Only whole objects can be extension members */ |
142 | Assert(object->objectSubId == 0); |
143 | |
144 | if (creating_extension) |
145 | { |
146 | ObjectAddress extension; |
147 | |
148 | /* Only need to check for existing membership if isReplace */ |
149 | if (isReplace) |
150 | { |
151 | Oid oldext; |
152 | |
153 | oldext = getExtensionOfObject(object->classId, object->objectId); |
154 | if (OidIsValid(oldext)) |
155 | { |
156 | /* If already a member of this extension, nothing to do */ |
157 | if (oldext == CurrentExtensionObject) |
158 | return; |
159 | /* Already a member of some other extension, so reject */ |
160 | ereport(ERROR, |
161 | (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), |
162 | errmsg("%s is already a member of extension \"%s\"" , |
163 | getObjectDescription(object), |
164 | get_extension_name(oldext)))); |
165 | } |
166 | } |
167 | |
168 | /* OK, record it as a member of CurrentExtensionObject */ |
169 | extension.classId = ExtensionRelationId; |
170 | extension.objectId = CurrentExtensionObject; |
171 | extension.objectSubId = 0; |
172 | |
173 | recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION); |
174 | } |
175 | } |
176 | |
177 | /* |
178 | * deleteDependencyRecordsFor -- delete all records with given depender |
179 | * classId/objectId. Returns the number of records deleted. |
180 | * |
181 | * This is used when redefining an existing object. Links leading to the |
182 | * object do not change, and links leading from it will be recreated |
183 | * (possibly with some differences from before). |
184 | * |
185 | * If skipExtensionDeps is true, we do not delete any dependencies that |
186 | * show that the given object is a member of an extension. This avoids |
187 | * needing a lot of extra logic to fetch and recreate that dependency. |
188 | */ |
189 | long |
190 | deleteDependencyRecordsFor(Oid classId, Oid objectId, |
191 | bool skipExtensionDeps) |
192 | { |
193 | long count = 0; |
194 | Relation depRel; |
195 | ScanKeyData key[2]; |
196 | SysScanDesc scan; |
197 | HeapTuple tup; |
198 | |
199 | depRel = table_open(DependRelationId, RowExclusiveLock); |
200 | |
201 | ScanKeyInit(&key[0], |
202 | Anum_pg_depend_classid, |
203 | BTEqualStrategyNumber, F_OIDEQ, |
204 | ObjectIdGetDatum(classId)); |
205 | ScanKeyInit(&key[1], |
206 | Anum_pg_depend_objid, |
207 | BTEqualStrategyNumber, F_OIDEQ, |
208 | ObjectIdGetDatum(objectId)); |
209 | |
210 | scan = systable_beginscan(depRel, DependDependerIndexId, true, |
211 | NULL, 2, key); |
212 | |
213 | while (HeapTupleIsValid(tup = systable_getnext(scan))) |
214 | { |
215 | if (skipExtensionDeps && |
216 | ((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION) |
217 | continue; |
218 | |
219 | CatalogTupleDelete(depRel, &tup->t_self); |
220 | count++; |
221 | } |
222 | |
223 | systable_endscan(scan); |
224 | |
225 | table_close(depRel, RowExclusiveLock); |
226 | |
227 | return count; |
228 | } |
229 | |
230 | /* |
231 | * deleteDependencyRecordsForClass -- delete all records with given depender |
232 | * classId/objectId, dependee classId, and deptype. |
233 | * Returns the number of records deleted. |
234 | * |
235 | * This is a variant of deleteDependencyRecordsFor, useful when revoking |
236 | * an object property that is expressed by a dependency record (such as |
237 | * extension membership). |
238 | */ |
239 | long |
240 | deleteDependencyRecordsForClass(Oid classId, Oid objectId, |
241 | Oid refclassId, char deptype) |
242 | { |
243 | long count = 0; |
244 | Relation depRel; |
245 | ScanKeyData key[2]; |
246 | SysScanDesc scan; |
247 | HeapTuple tup; |
248 | |
249 | depRel = table_open(DependRelationId, RowExclusiveLock); |
250 | |
251 | ScanKeyInit(&key[0], |
252 | Anum_pg_depend_classid, |
253 | BTEqualStrategyNumber, F_OIDEQ, |
254 | ObjectIdGetDatum(classId)); |
255 | ScanKeyInit(&key[1], |
256 | Anum_pg_depend_objid, |
257 | BTEqualStrategyNumber, F_OIDEQ, |
258 | ObjectIdGetDatum(objectId)); |
259 | |
260 | scan = systable_beginscan(depRel, DependDependerIndexId, true, |
261 | NULL, 2, key); |
262 | |
263 | while (HeapTupleIsValid(tup = systable_getnext(scan))) |
264 | { |
265 | Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup); |
266 | |
267 | if (depform->refclassid == refclassId && depform->deptype == deptype) |
268 | { |
269 | CatalogTupleDelete(depRel, &tup->t_self); |
270 | count++; |
271 | } |
272 | } |
273 | |
274 | systable_endscan(scan); |
275 | |
276 | table_close(depRel, RowExclusiveLock); |
277 | |
278 | return count; |
279 | } |
280 | |
281 | /* |
282 | * Adjust dependency record(s) to point to a different object of the same type |
283 | * |
284 | * classId/objectId specify the referencing object. |
285 | * refClassId/oldRefObjectId specify the old referenced object. |
286 | * newRefObjectId is the new referenced object (must be of class refClassId). |
287 | * |
288 | * Note the lack of objsubid parameters. If there are subobject references |
289 | * they will all be readjusted. Also, there is an expectation that we are |
290 | * dealing with NORMAL dependencies: if we have to replace an (implicit) |
291 | * dependency on a pinned object with an explicit dependency on an unpinned |
292 | * one, the new one will be NORMAL. |
293 | * |
294 | * Returns the number of records updated -- zero indicates a problem. |
295 | */ |
296 | long |
297 | changeDependencyFor(Oid classId, Oid objectId, |
298 | Oid refClassId, Oid oldRefObjectId, |
299 | Oid newRefObjectId) |
300 | { |
301 | long count = 0; |
302 | Relation depRel; |
303 | ScanKeyData key[2]; |
304 | SysScanDesc scan; |
305 | HeapTuple tup; |
306 | ObjectAddress objAddr; |
307 | ObjectAddress depAddr; |
308 | bool oldIsPinned; |
309 | bool newIsPinned; |
310 | |
311 | depRel = table_open(DependRelationId, RowExclusiveLock); |
312 | |
313 | /* |
314 | * Check to see if either oldRefObjectId or newRefObjectId is pinned. |
315 | * Pinned objects should not have any dependency entries pointing to them, |
316 | * so in these cases we should add or remove a pg_depend entry, or do |
317 | * nothing at all, rather than update an entry as in the normal case. |
318 | */ |
319 | objAddr.classId = refClassId; |
320 | objAddr.objectId = oldRefObjectId; |
321 | objAddr.objectSubId = 0; |
322 | |
323 | oldIsPinned = isObjectPinned(&objAddr, depRel); |
324 | |
325 | objAddr.objectId = newRefObjectId; |
326 | |
327 | newIsPinned = isObjectPinned(&objAddr, depRel); |
328 | |
329 | if (oldIsPinned) |
330 | { |
331 | table_close(depRel, RowExclusiveLock); |
332 | |
333 | /* |
334 | * If both are pinned, we need do nothing. However, return 1 not 0, |
335 | * else callers will think this is an error case. |
336 | */ |
337 | if (newIsPinned) |
338 | return 1; |
339 | |
340 | /* |
341 | * There is no old dependency record, but we should insert a new one. |
342 | * Assume a normal dependency is wanted. |
343 | */ |
344 | depAddr.classId = classId; |
345 | depAddr.objectId = objectId; |
346 | depAddr.objectSubId = 0; |
347 | recordDependencyOn(&depAddr, &objAddr, DEPENDENCY_NORMAL); |
348 | |
349 | return 1; |
350 | } |
351 | |
352 | /* There should be existing dependency record(s), so search. */ |
353 | ScanKeyInit(&key[0], |
354 | Anum_pg_depend_classid, |
355 | BTEqualStrategyNumber, F_OIDEQ, |
356 | ObjectIdGetDatum(classId)); |
357 | ScanKeyInit(&key[1], |
358 | Anum_pg_depend_objid, |
359 | BTEqualStrategyNumber, F_OIDEQ, |
360 | ObjectIdGetDatum(objectId)); |
361 | |
362 | scan = systable_beginscan(depRel, DependDependerIndexId, true, |
363 | NULL, 2, key); |
364 | |
365 | while (HeapTupleIsValid((tup = systable_getnext(scan)))) |
366 | { |
367 | Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup); |
368 | |
369 | if (depform->refclassid == refClassId && |
370 | depform->refobjid == oldRefObjectId) |
371 | { |
372 | if (newIsPinned) |
373 | CatalogTupleDelete(depRel, &tup->t_self); |
374 | else |
375 | { |
376 | /* make a modifiable copy */ |
377 | tup = heap_copytuple(tup); |
378 | depform = (Form_pg_depend) GETSTRUCT(tup); |
379 | |
380 | depform->refobjid = newRefObjectId; |
381 | |
382 | CatalogTupleUpdate(depRel, &tup->t_self, tup); |
383 | |
384 | heap_freetuple(tup); |
385 | } |
386 | |
387 | count++; |
388 | } |
389 | } |
390 | |
391 | systable_endscan(scan); |
392 | |
393 | table_close(depRel, RowExclusiveLock); |
394 | |
395 | return count; |
396 | } |
397 | |
398 | /* |
399 | * Adjust all dependency records to come from a different object of the same type |
400 | * |
401 | * classId/oldObjectId specify the old referencing object. |
402 | * newObjectId is the new referencing object (must be of class classId). |
403 | * |
404 | * Returns the number of records updated. |
405 | */ |
406 | long |
407 | changeDependenciesOf(Oid classId, Oid oldObjectId, |
408 | Oid newObjectId) |
409 | { |
410 | long count = 0; |
411 | Relation depRel; |
412 | ScanKeyData key[2]; |
413 | SysScanDesc scan; |
414 | HeapTuple tup; |
415 | |
416 | depRel = table_open(DependRelationId, RowExclusiveLock); |
417 | |
418 | ScanKeyInit(&key[0], |
419 | Anum_pg_depend_classid, |
420 | BTEqualStrategyNumber, F_OIDEQ, |
421 | ObjectIdGetDatum(classId)); |
422 | ScanKeyInit(&key[1], |
423 | Anum_pg_depend_objid, |
424 | BTEqualStrategyNumber, F_OIDEQ, |
425 | ObjectIdGetDatum(oldObjectId)); |
426 | |
427 | scan = systable_beginscan(depRel, DependDependerIndexId, true, |
428 | NULL, 2, key); |
429 | |
430 | while (HeapTupleIsValid((tup = systable_getnext(scan)))) |
431 | { |
432 | Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup); |
433 | |
434 | /* make a modifiable copy */ |
435 | tup = heap_copytuple(tup); |
436 | depform = (Form_pg_depend) GETSTRUCT(tup); |
437 | |
438 | depform->objid = newObjectId; |
439 | |
440 | CatalogTupleUpdate(depRel, &tup->t_self, tup); |
441 | |
442 | heap_freetuple(tup); |
443 | |
444 | count++; |
445 | } |
446 | |
447 | systable_endscan(scan); |
448 | |
449 | table_close(depRel, RowExclusiveLock); |
450 | |
451 | return count; |
452 | } |
453 | |
454 | /* |
455 | * Adjust all dependency records to point to a different object of the same type |
456 | * |
457 | * refClassId/oldRefObjectId specify the old referenced object. |
458 | * newRefObjectId is the new referenced object (must be of class refClassId). |
459 | * |
460 | * Returns the number of records updated. |
461 | */ |
462 | long |
463 | changeDependenciesOn(Oid refClassId, Oid oldRefObjectId, |
464 | Oid newRefObjectId) |
465 | { |
466 | long count = 0; |
467 | Relation depRel; |
468 | ScanKeyData key[2]; |
469 | SysScanDesc scan; |
470 | HeapTuple tup; |
471 | ObjectAddress objAddr; |
472 | bool newIsPinned; |
473 | |
474 | depRel = table_open(DependRelationId, RowExclusiveLock); |
475 | |
476 | /* |
477 | * If oldRefObjectId is pinned, there won't be any dependency entries on |
478 | * it --- we can't cope in that case. (This isn't really worth expending |
479 | * code to fix, in current usage; it just means you can't rename stuff out |
480 | * of pg_catalog, which would likely be a bad move anyway.) |
481 | */ |
482 | objAddr.classId = refClassId; |
483 | objAddr.objectId = oldRefObjectId; |
484 | objAddr.objectSubId = 0; |
485 | |
486 | if (isObjectPinned(&objAddr, depRel)) |
487 | ereport(ERROR, |
488 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
489 | errmsg("cannot remove dependency on %s because it is a system object" , |
490 | getObjectDescription(&objAddr)))); |
491 | |
492 | /* |
493 | * We can handle adding a dependency on something pinned, though, since |
494 | * that just means deleting the dependency entry. |
495 | */ |
496 | objAddr.objectId = newRefObjectId; |
497 | |
498 | newIsPinned = isObjectPinned(&objAddr, depRel); |
499 | |
500 | /* Now search for dependency records */ |
501 | ScanKeyInit(&key[0], |
502 | Anum_pg_depend_refclassid, |
503 | BTEqualStrategyNumber, F_OIDEQ, |
504 | ObjectIdGetDatum(refClassId)); |
505 | ScanKeyInit(&key[1], |
506 | Anum_pg_depend_refobjid, |
507 | BTEqualStrategyNumber, F_OIDEQ, |
508 | ObjectIdGetDatum(oldRefObjectId)); |
509 | |
510 | scan = systable_beginscan(depRel, DependReferenceIndexId, true, |
511 | NULL, 2, key); |
512 | |
513 | while (HeapTupleIsValid((tup = systable_getnext(scan)))) |
514 | { |
515 | Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup); |
516 | |
517 | if (newIsPinned) |
518 | CatalogTupleDelete(depRel, &tup->t_self); |
519 | else |
520 | { |
521 | /* make a modifiable copy */ |
522 | tup = heap_copytuple(tup); |
523 | depform = (Form_pg_depend) GETSTRUCT(tup); |
524 | |
525 | depform->refobjid = newRefObjectId; |
526 | |
527 | CatalogTupleUpdate(depRel, &tup->t_self, tup); |
528 | |
529 | heap_freetuple(tup); |
530 | } |
531 | |
532 | count++; |
533 | } |
534 | |
535 | systable_endscan(scan); |
536 | |
537 | table_close(depRel, RowExclusiveLock); |
538 | |
539 | return count; |
540 | } |
541 | |
542 | /* |
543 | * isObjectPinned() |
544 | * |
545 | * Test if an object is required for basic database functionality. |
546 | * Caller must already have opened pg_depend. |
547 | * |
548 | * The passed subId, if any, is ignored; we assume that only whole objects |
549 | * are pinned (and that this implies pinning their components). |
550 | */ |
551 | static bool |
552 | isObjectPinned(const ObjectAddress *object, Relation rel) |
553 | { |
554 | bool ret = false; |
555 | SysScanDesc scan; |
556 | HeapTuple tup; |
557 | ScanKeyData key[2]; |
558 | |
559 | ScanKeyInit(&key[0], |
560 | Anum_pg_depend_refclassid, |
561 | BTEqualStrategyNumber, F_OIDEQ, |
562 | ObjectIdGetDatum(object->classId)); |
563 | |
564 | ScanKeyInit(&key[1], |
565 | Anum_pg_depend_refobjid, |
566 | BTEqualStrategyNumber, F_OIDEQ, |
567 | ObjectIdGetDatum(object->objectId)); |
568 | |
569 | scan = systable_beginscan(rel, DependReferenceIndexId, true, |
570 | NULL, 2, key); |
571 | |
572 | /* |
573 | * Since we won't generate additional pg_depend entries for pinned |
574 | * objects, there can be at most one entry referencing a pinned object. |
575 | * Hence, it's sufficient to look at the first returned tuple; we don't |
576 | * need to loop. |
577 | */ |
578 | tup = systable_getnext(scan); |
579 | if (HeapTupleIsValid(tup)) |
580 | { |
581 | Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup); |
582 | |
583 | if (foundDep->deptype == DEPENDENCY_PIN) |
584 | ret = true; |
585 | } |
586 | |
587 | systable_endscan(scan); |
588 | |
589 | return ret; |
590 | } |
591 | |
592 | |
593 | /* |
594 | * Various special-purpose lookups and manipulations of pg_depend. |
595 | */ |
596 | |
597 | |
598 | /* |
599 | * Find the extension containing the specified object, if any |
600 | * |
601 | * Returns the OID of the extension, or InvalidOid if the object does not |
602 | * belong to any extension. |
603 | * |
604 | * Extension membership is marked by an EXTENSION dependency from the object |
605 | * to the extension. Note that the result will be indeterminate if pg_depend |
606 | * contains links from this object to more than one extension ... but that |
607 | * should never happen. |
608 | */ |
609 | Oid |
610 | getExtensionOfObject(Oid classId, Oid objectId) |
611 | { |
612 | Oid result = InvalidOid; |
613 | Relation depRel; |
614 | ScanKeyData key[2]; |
615 | SysScanDesc scan; |
616 | HeapTuple tup; |
617 | |
618 | depRel = table_open(DependRelationId, AccessShareLock); |
619 | |
620 | ScanKeyInit(&key[0], |
621 | Anum_pg_depend_classid, |
622 | BTEqualStrategyNumber, F_OIDEQ, |
623 | ObjectIdGetDatum(classId)); |
624 | ScanKeyInit(&key[1], |
625 | Anum_pg_depend_objid, |
626 | BTEqualStrategyNumber, F_OIDEQ, |
627 | ObjectIdGetDatum(objectId)); |
628 | |
629 | scan = systable_beginscan(depRel, DependDependerIndexId, true, |
630 | NULL, 2, key); |
631 | |
632 | while (HeapTupleIsValid((tup = systable_getnext(scan)))) |
633 | { |
634 | Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup); |
635 | |
636 | if (depform->refclassid == ExtensionRelationId && |
637 | depform->deptype == DEPENDENCY_EXTENSION) |
638 | { |
639 | result = depform->refobjid; |
640 | break; /* no need to keep scanning */ |
641 | } |
642 | } |
643 | |
644 | systable_endscan(scan); |
645 | |
646 | table_close(depRel, AccessShareLock); |
647 | |
648 | return result; |
649 | } |
650 | |
651 | /* |
652 | * Detect whether a sequence is marked as "owned" by a column |
653 | * |
654 | * An ownership marker is an AUTO or INTERNAL dependency from the sequence to the |
655 | * column. If we find one, store the identity of the owning column |
656 | * into *tableId and *colId and return true; else return false. |
657 | * |
658 | * Note: if there's more than one such pg_depend entry then you get |
659 | * a random one of them returned into the out parameters. This should |
660 | * not happen, though. |
661 | */ |
662 | bool |
663 | sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId) |
664 | { |
665 | bool ret = false; |
666 | Relation depRel; |
667 | ScanKeyData key[2]; |
668 | SysScanDesc scan; |
669 | HeapTuple tup; |
670 | |
671 | depRel = table_open(DependRelationId, AccessShareLock); |
672 | |
673 | ScanKeyInit(&key[0], |
674 | Anum_pg_depend_classid, |
675 | BTEqualStrategyNumber, F_OIDEQ, |
676 | ObjectIdGetDatum(RelationRelationId)); |
677 | ScanKeyInit(&key[1], |
678 | Anum_pg_depend_objid, |
679 | BTEqualStrategyNumber, F_OIDEQ, |
680 | ObjectIdGetDatum(seqId)); |
681 | |
682 | scan = systable_beginscan(depRel, DependDependerIndexId, true, |
683 | NULL, 2, key); |
684 | |
685 | while (HeapTupleIsValid((tup = systable_getnext(scan)))) |
686 | { |
687 | Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup); |
688 | |
689 | if (depform->refclassid == RelationRelationId && |
690 | depform->deptype == deptype) |
691 | { |
692 | *tableId = depform->refobjid; |
693 | *colId = depform->refobjsubid; |
694 | ret = true; |
695 | break; /* no need to keep scanning */ |
696 | } |
697 | } |
698 | |
699 | systable_endscan(scan); |
700 | |
701 | table_close(depRel, AccessShareLock); |
702 | |
703 | return ret; |
704 | } |
705 | |
706 | /* |
707 | * Collect a list of OIDs of all sequences owned by the specified relation, |
708 | * and column if specified. |
709 | */ |
710 | List * |
711 | getOwnedSequences(Oid relid, AttrNumber attnum) |
712 | { |
713 | List *result = NIL; |
714 | Relation depRel; |
715 | ScanKeyData key[3]; |
716 | SysScanDesc scan; |
717 | HeapTuple tup; |
718 | |
719 | depRel = table_open(DependRelationId, AccessShareLock); |
720 | |
721 | ScanKeyInit(&key[0], |
722 | Anum_pg_depend_refclassid, |
723 | BTEqualStrategyNumber, F_OIDEQ, |
724 | ObjectIdGetDatum(RelationRelationId)); |
725 | ScanKeyInit(&key[1], |
726 | Anum_pg_depend_refobjid, |
727 | BTEqualStrategyNumber, F_OIDEQ, |
728 | ObjectIdGetDatum(relid)); |
729 | if (attnum) |
730 | ScanKeyInit(&key[2], |
731 | Anum_pg_depend_refobjsubid, |
732 | BTEqualStrategyNumber, F_INT4EQ, |
733 | Int32GetDatum(attnum)); |
734 | |
735 | scan = systable_beginscan(depRel, DependReferenceIndexId, true, |
736 | NULL, attnum ? 3 : 2, key); |
737 | |
738 | while (HeapTupleIsValid(tup = systable_getnext(scan))) |
739 | { |
740 | Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup); |
741 | |
742 | /* |
743 | * We assume any auto or internal dependency of a sequence on a column |
744 | * must be what we are looking for. (We need the relkind test because |
745 | * indexes can also have auto dependencies on columns.) |
746 | */ |
747 | if (deprec->classid == RelationRelationId && |
748 | deprec->objsubid == 0 && |
749 | deprec->refobjsubid != 0 && |
750 | (deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) && |
751 | get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE) |
752 | { |
753 | result = lappend_oid(result, deprec->objid); |
754 | } |
755 | } |
756 | |
757 | systable_endscan(scan); |
758 | |
759 | table_close(depRel, AccessShareLock); |
760 | |
761 | return result; |
762 | } |
763 | |
764 | /* |
765 | * Get owned sequence, error if not exactly one. |
766 | */ |
767 | Oid |
768 | getOwnedSequence(Oid relid, AttrNumber attnum) |
769 | { |
770 | List *seqlist = getOwnedSequences(relid, attnum); |
771 | |
772 | if (list_length(seqlist) > 1) |
773 | elog(ERROR, "more than one owned sequence found" ); |
774 | else if (list_length(seqlist) < 1) |
775 | elog(ERROR, "no owned sequence found" ); |
776 | |
777 | return linitial_oid(seqlist); |
778 | } |
779 | |
780 | /* |
781 | * get_constraint_index |
782 | * Given the OID of a unique, primary-key, or exclusion constraint, |
783 | * return the OID of the underlying index. |
784 | * |
785 | * Return InvalidOid if the index couldn't be found; this suggests the |
786 | * given OID is bogus, but we leave it to caller to decide what to do. |
787 | */ |
788 | Oid |
789 | get_constraint_index(Oid constraintId) |
790 | { |
791 | Oid indexId = InvalidOid; |
792 | Relation depRel; |
793 | ScanKeyData key[3]; |
794 | SysScanDesc scan; |
795 | HeapTuple tup; |
796 | |
797 | /* Search the dependency table for the dependent index */ |
798 | depRel = table_open(DependRelationId, AccessShareLock); |
799 | |
800 | ScanKeyInit(&key[0], |
801 | Anum_pg_depend_refclassid, |
802 | BTEqualStrategyNumber, F_OIDEQ, |
803 | ObjectIdGetDatum(ConstraintRelationId)); |
804 | ScanKeyInit(&key[1], |
805 | Anum_pg_depend_refobjid, |
806 | BTEqualStrategyNumber, F_OIDEQ, |
807 | ObjectIdGetDatum(constraintId)); |
808 | ScanKeyInit(&key[2], |
809 | Anum_pg_depend_refobjsubid, |
810 | BTEqualStrategyNumber, F_INT4EQ, |
811 | Int32GetDatum(0)); |
812 | |
813 | scan = systable_beginscan(depRel, DependReferenceIndexId, true, |
814 | NULL, 3, key); |
815 | |
816 | while (HeapTupleIsValid(tup = systable_getnext(scan))) |
817 | { |
818 | Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup); |
819 | |
820 | /* |
821 | * We assume any internal dependency of an index on the constraint |
822 | * must be what we are looking for. |
823 | */ |
824 | if (deprec->classid == RelationRelationId && |
825 | deprec->objsubid == 0 && |
826 | deprec->deptype == DEPENDENCY_INTERNAL) |
827 | { |
828 | char relkind = get_rel_relkind(deprec->objid); |
829 | |
830 | /* |
831 | * This is pure paranoia; there shouldn't be any other relkinds |
832 | * dependent on a constraint. |
833 | */ |
834 | if (relkind != RELKIND_INDEX && |
835 | relkind != RELKIND_PARTITIONED_INDEX) |
836 | continue; |
837 | |
838 | indexId = deprec->objid; |
839 | break; |
840 | } |
841 | } |
842 | |
843 | systable_endscan(scan); |
844 | table_close(depRel, AccessShareLock); |
845 | |
846 | return indexId; |
847 | } |
848 | |
849 | /* |
850 | * get_index_constraint |
851 | * Given the OID of an index, return the OID of the owning unique, |
852 | * primary-key, or exclusion constraint, or InvalidOid if there |
853 | * is no owning constraint. |
854 | */ |
855 | Oid |
856 | get_index_constraint(Oid indexId) |
857 | { |
858 | Oid constraintId = InvalidOid; |
859 | Relation depRel; |
860 | ScanKeyData key[3]; |
861 | SysScanDesc scan; |
862 | HeapTuple tup; |
863 | |
864 | /* Search the dependency table for the index */ |
865 | depRel = table_open(DependRelationId, AccessShareLock); |
866 | |
867 | ScanKeyInit(&key[0], |
868 | Anum_pg_depend_classid, |
869 | BTEqualStrategyNumber, F_OIDEQ, |
870 | ObjectIdGetDatum(RelationRelationId)); |
871 | ScanKeyInit(&key[1], |
872 | Anum_pg_depend_objid, |
873 | BTEqualStrategyNumber, F_OIDEQ, |
874 | ObjectIdGetDatum(indexId)); |
875 | ScanKeyInit(&key[2], |
876 | Anum_pg_depend_objsubid, |
877 | BTEqualStrategyNumber, F_INT4EQ, |
878 | Int32GetDatum(0)); |
879 | |
880 | scan = systable_beginscan(depRel, DependDependerIndexId, true, |
881 | NULL, 3, key); |
882 | |
883 | while (HeapTupleIsValid(tup = systable_getnext(scan))) |
884 | { |
885 | Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup); |
886 | |
887 | /* |
888 | * We assume any internal dependency on a constraint must be what we |
889 | * are looking for. |
890 | */ |
891 | if (deprec->refclassid == ConstraintRelationId && |
892 | deprec->refobjsubid == 0 && |
893 | deprec->deptype == DEPENDENCY_INTERNAL) |
894 | { |
895 | constraintId = deprec->refobjid; |
896 | break; |
897 | } |
898 | } |
899 | |
900 | systable_endscan(scan); |
901 | table_close(depRel, AccessShareLock); |
902 | |
903 | return constraintId; |
904 | } |
905 | |
906 | /* |
907 | * get_index_ref_constraints |
908 | * Given the OID of an index, return the OID of all foreign key |
909 | * constraints which reference the index. |
910 | */ |
911 | List * |
912 | get_index_ref_constraints(Oid indexId) |
913 | { |
914 | List *result = NIL; |
915 | Relation depRel; |
916 | ScanKeyData key[3]; |
917 | SysScanDesc scan; |
918 | HeapTuple tup; |
919 | |
920 | /* Search the dependency table for the index */ |
921 | depRel = table_open(DependRelationId, AccessShareLock); |
922 | |
923 | ScanKeyInit(&key[0], |
924 | Anum_pg_depend_refclassid, |
925 | BTEqualStrategyNumber, F_OIDEQ, |
926 | ObjectIdGetDatum(RelationRelationId)); |
927 | ScanKeyInit(&key[1], |
928 | Anum_pg_depend_refobjid, |
929 | BTEqualStrategyNumber, F_OIDEQ, |
930 | ObjectIdGetDatum(indexId)); |
931 | ScanKeyInit(&key[2], |
932 | Anum_pg_depend_refobjsubid, |
933 | BTEqualStrategyNumber, F_INT4EQ, |
934 | Int32GetDatum(0)); |
935 | |
936 | scan = systable_beginscan(depRel, DependReferenceIndexId, true, |
937 | NULL, 3, key); |
938 | |
939 | while (HeapTupleIsValid(tup = systable_getnext(scan))) |
940 | { |
941 | Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup); |
942 | |
943 | /* |
944 | * We assume any normal dependency from a constraint must be what we |
945 | * are looking for. |
946 | */ |
947 | if (deprec->classid == ConstraintRelationId && |
948 | deprec->objsubid == 0 && |
949 | deprec->deptype == DEPENDENCY_NORMAL) |
950 | { |
951 | result = lappend_oid(result, deprec->objid); |
952 | } |
953 | } |
954 | |
955 | systable_endscan(scan); |
956 | table_close(depRel, AccessShareLock); |
957 | |
958 | return result; |
959 | } |
960 | |