|
| 1 | +/*------------------------------------------------------------------------- |
| 2 | + * |
| 3 | + * pg_inherits.c |
| 4 | + * routines to support manipulation of the pg_inherits relation |
| 5 | + * |
| 6 | + * Note: currently, this module only contains inquiry functions; the actual |
| 7 | + * creation and deletion of pg_inherits entries is done in tablecmds.c. |
| 8 | + * Perhaps someday that code should be moved here, but it'd have to be |
| 9 | + * disentangled from other stuff such as pg_depend updates. |
| 10 | + * |
| 11 | + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group |
| 12 | + * Portions Copyright (c) 1994, Regents of the University of California |
| 13 | + * |
| 14 | + * |
| 15 | + * IDENTIFICATION |
| 16 | + * $PostgreSQL: pgsql/src/backend/catalog/pg_inherits.c,v 1.1 2009/05/12 00:56:05 tgl Exp $ |
| 17 | + * |
| 18 | + *------------------------------------------------------------------------- |
| 19 | + */ |
| 20 | +#include"postgres.h" |
| 21 | + |
| 22 | +#include"access/heapam.h" |
| 23 | +#include"catalog/pg_class.h" |
| 24 | +#include"catalog/pg_inherits.h" |
| 25 | +#include"parser/parse_type.h" |
| 26 | +#include"utils/fmgroids.h" |
| 27 | +#include"utils/syscache.h" |
| 28 | +#include"utils/tqual.h" |
| 29 | + |
| 30 | + |
| 31 | +/* |
| 32 | + * find_inheritance_children |
| 33 | + * |
| 34 | + * Returns a list containing the OIDs of all relations which |
| 35 | + * inherit *directly* from the relation with OID 'parentrelId'. |
| 36 | + */ |
| 37 | +List* |
| 38 | +find_inheritance_children(OidparentrelId) |
| 39 | +{ |
| 40 | +List*list=NIL; |
| 41 | +Relationrelation; |
| 42 | +HeapScanDescscan; |
| 43 | +ScanKeyDatakey[1]; |
| 44 | +HeapTupleinheritsTuple; |
| 45 | +Oidinhrelid; |
| 46 | + |
| 47 | +/* |
| 48 | + * Can skip the scan if pg_class shows the relation has never had a |
| 49 | + * subclass. |
| 50 | + */ |
| 51 | +if (!has_subclass(parentrelId)) |
| 52 | +returnNIL; |
| 53 | + |
| 54 | +/* |
| 55 | + * XXX might be a good idea to create an index on pg_inherits' inhparent |
| 56 | + * field, so that we can use an indexscan instead of sequential scan here. |
| 57 | + * However, in typical databases pg_inherits won't have enough entries to |
| 58 | + * justify an indexscan... |
| 59 | + */ |
| 60 | +relation=heap_open(InheritsRelationId,AccessShareLock); |
| 61 | +ScanKeyInit(&key[0], |
| 62 | +Anum_pg_inherits_inhparent, |
| 63 | +BTEqualStrategyNumber,F_OIDEQ, |
| 64 | +ObjectIdGetDatum(parentrelId)); |
| 65 | +scan=heap_beginscan(relation,SnapshotNow,1,key); |
| 66 | +while ((inheritsTuple=heap_getnext(scan,ForwardScanDirection))!=NULL) |
| 67 | +{ |
| 68 | +inhrelid= ((Form_pg_inherits)GETSTRUCT(inheritsTuple))->inhrelid; |
| 69 | +list=lappend_oid(list,inhrelid); |
| 70 | +} |
| 71 | +heap_endscan(scan); |
| 72 | +heap_close(relation,AccessShareLock); |
| 73 | +returnlist; |
| 74 | +} |
| 75 | + |
| 76 | + |
| 77 | +/* |
| 78 | + * find_all_inheritors - |
| 79 | + *Returns a list of relation OIDs including the given rel plus |
| 80 | + *all relations that inherit from it, directly or indirectly. |
| 81 | + */ |
| 82 | +List* |
| 83 | +find_all_inheritors(OidparentrelId) |
| 84 | +{ |
| 85 | +List*rels_list; |
| 86 | +ListCell*l; |
| 87 | + |
| 88 | +/* |
| 89 | + * We build a list starting with the given rel and adding all direct and |
| 90 | + * indirect children. We can use a single list as both the record of |
| 91 | + * already-found rels and the agenda of rels yet to be scanned for more |
| 92 | + * children. This is a bit tricky but works because the foreach() macro |
| 93 | + * doesn't fetch the next list element until the bottom of the loop. |
| 94 | + */ |
| 95 | +rels_list=list_make1_oid(parentrelId); |
| 96 | + |
| 97 | +foreach(l,rels_list) |
| 98 | +{ |
| 99 | +Oidcurrentrel=lfirst_oid(l); |
| 100 | +List*currentchildren; |
| 101 | + |
| 102 | +/* Get the direct children of this rel */ |
| 103 | +currentchildren=find_inheritance_children(currentrel); |
| 104 | + |
| 105 | +/* |
| 106 | + * Add to the queue only those children not already seen. This avoids |
| 107 | + * making duplicate entries in case of multiple inheritance paths from |
| 108 | + * the same parent. (It'll also keep us from getting into an infinite |
| 109 | + * loop, though theoretically there can't be any cycles in the |
| 110 | + * inheritance graph anyway.) |
| 111 | + */ |
| 112 | +rels_list=list_concat_unique_oid(rels_list,currentchildren); |
| 113 | +} |
| 114 | + |
| 115 | +returnrels_list; |
| 116 | +} |
| 117 | + |
| 118 | + |
| 119 | +/* |
| 120 | + * has_subclass - does this relation have any children? |
| 121 | + * |
| 122 | + * In the current implementation, has_subclass returns whether a |
| 123 | + * particular class *might* have a subclass. It will not return the |
| 124 | + * correct result if a class had a subclass which was later dropped. |
| 125 | + * This is because relhassubclass in pg_class is not updated when a |
| 126 | + * subclass is dropped, primarily because of concurrency concerns. |
| 127 | + * |
| 128 | + * Currently has_subclass is only used as an efficiency hack to skip |
| 129 | + * unnecessary inheritance searches, so this is OK. |
| 130 | + * |
| 131 | + * Although this doesn't actually touch pg_inherits, it seems reasonable |
| 132 | + * to keep it here since it's normally used with the other routines here. |
| 133 | + */ |
| 134 | +bool |
| 135 | +has_subclass(OidrelationId) |
| 136 | +{ |
| 137 | +HeapTupletuple; |
| 138 | +boolresult; |
| 139 | + |
| 140 | +tuple=SearchSysCache(RELOID, |
| 141 | +ObjectIdGetDatum(relationId), |
| 142 | +0,0,0); |
| 143 | +if (!HeapTupleIsValid(tuple)) |
| 144 | +elog(ERROR,"cache lookup failed for relation %u",relationId); |
| 145 | + |
| 146 | +result= ((Form_pg_class)GETSTRUCT(tuple))->relhassubclass; |
| 147 | +ReleaseSysCache(tuple); |
| 148 | +returnresult; |
| 149 | +} |
| 150 | + |
| 151 | + |
| 152 | +/* |
| 153 | + * Given two type OIDs, determine whether the first is a complex type |
| 154 | + * (class type) that inherits from the second. |
| 155 | + */ |
| 156 | +bool |
| 157 | +typeInheritsFrom(OidsubclassTypeId,OidsuperclassTypeId) |
| 158 | +{ |
| 159 | +boolresult= false; |
| 160 | +OidsubclassRelid; |
| 161 | +OidsuperclassRelid; |
| 162 | +Relationinhrel; |
| 163 | +List*visited, |
| 164 | +*queue; |
| 165 | +ListCell*queue_item; |
| 166 | + |
| 167 | +/* We need to work with the associated relation OIDs */ |
| 168 | +subclassRelid=typeidTypeRelid(subclassTypeId); |
| 169 | +if (subclassRelid==InvalidOid) |
| 170 | +return false;/* not a complex type */ |
| 171 | +superclassRelid=typeidTypeRelid(superclassTypeId); |
| 172 | +if (superclassRelid==InvalidOid) |
| 173 | +return false;/* not a complex type */ |
| 174 | + |
| 175 | +/* No point in searching if the superclass has no subclasses */ |
| 176 | +if (!has_subclass(superclassRelid)) |
| 177 | +return false; |
| 178 | + |
| 179 | +/* |
| 180 | + * Begin the search at the relation itself, so add its relid to the queue. |
| 181 | + */ |
| 182 | +queue=list_make1_oid(subclassRelid); |
| 183 | +visited=NIL; |
| 184 | + |
| 185 | +inhrel=heap_open(InheritsRelationId,AccessShareLock); |
| 186 | + |
| 187 | +/* |
| 188 | + * Use queue to do a breadth-first traversal of the inheritance graph from |
| 189 | + * the relid supplied up to the root. Notice that we append to the queue |
| 190 | + * inside the loop --- this is okay because the foreach() macro doesn't |
| 191 | + * advance queue_item until the next loop iteration begins. |
| 192 | + */ |
| 193 | +foreach(queue_item,queue) |
| 194 | +{ |
| 195 | +Oidthis_relid=lfirst_oid(queue_item); |
| 196 | +ScanKeyDataskey; |
| 197 | +HeapScanDescinhscan; |
| 198 | +HeapTupleinhtup; |
| 199 | + |
| 200 | +/* |
| 201 | + * If we've seen this relid already, skip it. This avoids extra |
| 202 | + * work in multiple-inheritance scenarios, and also protects us |
| 203 | + * from an infinite loop in case there is a cycle in pg_inherits |
| 204 | + * (though theoretically that shouldn't happen). |
| 205 | + */ |
| 206 | +if (list_member_oid(visited,this_relid)) |
| 207 | +continue; |
| 208 | + |
| 209 | +/* |
| 210 | + * Okay, this is a not-yet-seen relid. Add it to the list of |
| 211 | + * already-visited OIDs, then find all the types this relid inherits |
| 212 | + * from and add them to the queue. |
| 213 | + */ |
| 214 | +visited=lappend_oid(visited,this_relid); |
| 215 | + |
| 216 | +ScanKeyInit(&skey, |
| 217 | +Anum_pg_inherits_inhrelid, |
| 218 | +BTEqualStrategyNumber,F_OIDEQ, |
| 219 | +ObjectIdGetDatum(this_relid)); |
| 220 | + |
| 221 | +inhscan=heap_beginscan(inhrel,SnapshotNow,1,&skey); |
| 222 | + |
| 223 | +while ((inhtup=heap_getnext(inhscan,ForwardScanDirection))!=NULL) |
| 224 | +{ |
| 225 | +Form_pg_inheritsinh= (Form_pg_inherits)GETSTRUCT(inhtup); |
| 226 | +Oidinhparent=inh->inhparent; |
| 227 | + |
| 228 | +/* If this is the target superclass, we're done */ |
| 229 | +if (inhparent==superclassRelid) |
| 230 | +{ |
| 231 | +result= true; |
| 232 | +break; |
| 233 | +} |
| 234 | + |
| 235 | +/* Else add to queue */ |
| 236 | +queue=lappend_oid(queue,inhparent); |
| 237 | +} |
| 238 | + |
| 239 | +heap_endscan(inhscan); |
| 240 | + |
| 241 | +if (result) |
| 242 | +break; |
| 243 | +} |
| 244 | + |
| 245 | +/* clean up ... */ |
| 246 | +heap_close(inhrel,AccessShareLock); |
| 247 | + |
| 248 | +list_free(visited); |
| 249 | +list_free(queue); |
| 250 | + |
| 251 | +returnresult; |
| 252 | +} |