Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * syscache.c
4 : * System cache management routines
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/utils/cache/syscache.c
12 : *
13 : * NOTES
14 : * These routines allow the parser/planner/executor to perform
15 : * rapid lookups on the contents of the system catalogs.
16 : *
17 : * see utils/syscache.h for a list of the cache IDs
18 : *
19 : *-------------------------------------------------------------------------
20 : */
21 : #include "postgres.h"
22 :
23 : #include "access/htup_details.h"
24 : #include "catalog/pg_db_role_setting_d.h"
25 : #include "catalog/pg_depend_d.h"
26 : #include "catalog/pg_description_d.h"
27 : #include "catalog/pg_seclabel_d.h"
28 : #include "catalog/pg_shdepend_d.h"
29 : #include "catalog/pg_shdescription_d.h"
30 : #include "catalog/pg_shseclabel_d.h"
31 : #include "common/int.h"
32 : #include "lib/qunique.h"
33 : #include "utils/catcache.h"
34 : #include "utils/lsyscache.h"
35 : #include "utils/rel.h"
36 : #include "utils/syscache.h"
37 :
38 : /*---------------------------------------------------------------------------
39 :
40 : Adding system caches:
41 :
42 : There must be a unique index underlying each syscache (ie, an index
43 : whose key is the same as that of the cache). If there is not one
44 : already, add the definition for it to include/catalog/pg_*.h using
45 : DECLARE_UNIQUE_INDEX.
46 : (Adding an index requires a catversion.h update, while simply
47 : adding/deleting caches only requires a recompile.)
48 :
49 : Add a MAKE_SYSCACHE call to the same pg_*.h file specifying the name of
50 : your cache, the underlying index, and the initial number of hash buckets.
51 :
52 : The number of hash buckets must be a power of 2. It's reasonable to
53 : set this to the number of entries that might be in the particular cache
54 : in a medium-size database.
55 :
56 : Finally, any place your relation gets heap_insert() or
57 : heap_update() calls, use CatalogTupleInsert() or CatalogTupleUpdate()
58 : instead, which also update indexes. The heap_* calls do not do that.
59 :
60 : *---------------------------------------------------------------------------
61 : */
62 :
63 : /*
64 : * struct cachedesc: information defining a single syscache
65 : */
66 : struct cachedesc
67 : {
68 : Oid reloid; /* OID of the relation being cached */
69 : Oid indoid; /* OID of index relation for this cache */
70 : int nkeys; /* # of keys needed for cache lookup */
71 : int key[4]; /* attribute numbers of key attrs */
72 : int nbuckets; /* number of hash buckets for this cache */
73 : };
74 :
75 : /* Macro to provide nkeys and key array with convenient syntax. */
76 : #define KEY(...) VA_ARGS_NARGS(__VA_ARGS__), { __VA_ARGS__ }
77 :
78 : #include "catalog/syscache_info.h"
79 :
80 : StaticAssertDecl(lengthof(cacheinfo) == SysCacheSize,
81 : "SysCacheSize does not match syscache.c's array");
82 :
83 : static CatCache *SysCache[SysCacheSize];
84 :
85 : static bool CacheInitialized = false;
86 :
87 : /* Sorted array of OIDs of tables that have caches on them */
88 : static Oid SysCacheRelationOid[SysCacheSize];
89 : static int SysCacheRelationOidSize;
90 :
91 : /* Sorted array of OIDs of tables and indexes used by caches */
92 : static Oid SysCacheSupportingRelOid[SysCacheSize * 2];
93 : static int SysCacheSupportingRelOidSize;
94 :
95 : static int oid_compare(const void *a, const void *b);
96 :
97 :
98 : /*
99 : * InitCatalogCache - initialize the caches
100 : *
101 : * Note that no database access is done here; we only allocate memory
102 : * and initialize the cache structure. Interrogation of the database
103 : * to complete initialization of a cache happens upon first use
104 : * of that cache.
105 : */
106 : void
107 25896 : InitCatalogCache(void)
108 : {
109 : int cacheId;
110 :
111 : Assert(!CacheInitialized);
112 :
113 25896 : SysCacheRelationOidSize = SysCacheSupportingRelOidSize = 0;
114 :
115 2175264 : for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
116 : {
117 : /*
118 : * Assert that every enumeration value defined in syscache.h has been
119 : * populated in the cacheinfo array.
120 : */
121 : Assert(OidIsValid(cacheinfo[cacheId].reloid));
122 : Assert(OidIsValid(cacheinfo[cacheId].indoid));
123 : /* .nbuckets and .key[] are checked by InitCatCache() */
124 :
125 4298736 : SysCache[cacheId] = InitCatCache(cacheId,
126 : cacheinfo[cacheId].reloid,
127 : cacheinfo[cacheId].indoid,
128 : cacheinfo[cacheId].nkeys,
129 2149368 : cacheinfo[cacheId].key,
130 : cacheinfo[cacheId].nbuckets);
131 2149368 : if (!PointerIsValid(SysCache[cacheId]))
132 0 : elog(ERROR, "could not initialize cache %u (%d)",
133 : cacheinfo[cacheId].reloid, cacheId);
134 : /* Accumulate data for OID lists, too */
135 2149368 : SysCacheRelationOid[SysCacheRelationOidSize++] =
136 2149368 : cacheinfo[cacheId].reloid;
137 2149368 : SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
138 2149368 : cacheinfo[cacheId].reloid;
139 2149368 : SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
140 2149368 : cacheinfo[cacheId].indoid;
141 : /* see comments for RelationInvalidatesSnapshotsOnly */
142 : Assert(!RelationInvalidatesSnapshotsOnly(cacheinfo[cacheId].reloid));
143 : }
144 :
145 : Assert(SysCacheRelationOidSize <= lengthof(SysCacheRelationOid));
146 : Assert(SysCacheSupportingRelOidSize <= lengthof(SysCacheSupportingRelOid));
147 :
148 : /* Sort and de-dup OID arrays, so we can use binary search. */
149 25896 : qsort(SysCacheRelationOid, SysCacheRelationOidSize,
150 : sizeof(Oid), oid_compare);
151 25896 : SysCacheRelationOidSize =
152 25896 : qunique(SysCacheRelationOid, SysCacheRelationOidSize, sizeof(Oid),
153 : oid_compare);
154 :
155 25896 : qsort(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
156 : sizeof(Oid), oid_compare);
157 25896 : SysCacheSupportingRelOidSize =
158 25896 : qunique(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
159 : sizeof(Oid), oid_compare);
160 :
161 25896 : CacheInitialized = true;
162 25896 : }
163 :
164 : /*
165 : * InitCatalogCachePhase2 - finish initializing the caches
166 : *
167 : * Finish initializing all the caches, including necessary database
168 : * access.
169 : *
170 : * This is *not* essential; normally we allow syscaches to be initialized
171 : * on first use. However, it is useful as a mechanism to preload the
172 : * relcache with entries for the most-commonly-used system catalogs.
173 : * Therefore, we invoke this routine when we need to write a new relcache
174 : * init file.
175 : */
176 : void
177 2164 : InitCatalogCachePhase2(void)
178 : {
179 : int cacheId;
180 :
181 : Assert(CacheInitialized);
182 :
183 181738 : for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
184 179576 : InitCatCachePhase2(SysCache[cacheId], true);
185 2162 : }
186 :
187 :
188 : /*
189 : * SearchSysCache
190 : *
191 : * A layer on top of SearchCatCache that does the initialization and
192 : * key-setting for you.
193 : *
194 : * Returns the cache copy of the tuple if one is found, NULL if not.
195 : * The tuple is the 'cache' copy and must NOT be modified!
196 : *
197 : * When the caller is done using the tuple, call ReleaseSysCache()
198 : * to release the reference count grabbed by SearchSysCache(). If this
199 : * is not done, the tuple will remain locked in cache until end of
200 : * transaction, which is tolerable but not desirable.
201 : *
202 : * CAUTION: The tuple that is returned must NOT be freed by the caller!
203 : */
204 : HeapTuple
205 4728696 : SearchSysCache(int cacheId,
206 : Datum key1,
207 : Datum key2,
208 : Datum key3,
209 : Datum key4)
210 : {
211 : Assert(cacheId >= 0 && cacheId < SysCacheSize &&
212 : PointerIsValid(SysCache[cacheId]));
213 :
214 4728696 : return SearchCatCache(SysCache[cacheId], key1, key2, key3, key4);
215 : }
216 :
217 : HeapTuple
218 57099338 : SearchSysCache1(int cacheId,
219 : Datum key1)
220 : {
221 : Assert(cacheId >= 0 && cacheId < SysCacheSize &&
222 : PointerIsValid(SysCache[cacheId]));
223 : Assert(SysCache[cacheId]->cc_nkeys == 1);
224 :
225 57099338 : return SearchCatCache1(SysCache[cacheId], key1);
226 : }
227 :
228 : HeapTuple
229 4540694 : SearchSysCache2(int cacheId,
230 : Datum key1, Datum key2)
231 : {
232 : Assert(cacheId >= 0 && cacheId < SysCacheSize &&
233 : PointerIsValid(SysCache[cacheId]));
234 : Assert(SysCache[cacheId]->cc_nkeys == 2);
235 :
236 4540694 : return SearchCatCache2(SysCache[cacheId], key1, key2);
237 : }
238 :
239 : HeapTuple
240 4485800 : SearchSysCache3(int cacheId,
241 : Datum key1, Datum key2, Datum key3)
242 : {
243 : Assert(cacheId >= 0 && cacheId < SysCacheSize &&
244 : PointerIsValid(SysCache[cacheId]));
245 : Assert(SysCache[cacheId]->cc_nkeys == 3);
246 :
247 4485800 : return SearchCatCache3(SysCache[cacheId], key1, key2, key3);
248 : }
249 :
250 : HeapTuple
251 3507590 : SearchSysCache4(int cacheId,
252 : Datum key1, Datum key2, Datum key3, Datum key4)
253 : {
254 : Assert(cacheId >= 0 && cacheId < SysCacheSize &&
255 : PointerIsValid(SysCache[cacheId]));
256 : Assert(SysCache[cacheId]->cc_nkeys == 4);
257 :
258 3507590 : return SearchCatCache4(SysCache[cacheId], key1, key2, key3, key4);
259 : }
260 :
261 : /*
262 : * ReleaseSysCache
263 : * Release previously grabbed reference count on a tuple
264 : */
265 : void
266 69319398 : ReleaseSysCache(HeapTuple tuple)
267 : {
268 69319398 : ReleaseCatCache(tuple);
269 69319398 : }
270 :
271 : /*
272 : * SearchSysCacheCopy
273 : *
274 : * A convenience routine that does SearchSysCache and (if successful)
275 : * returns a modifiable copy of the syscache entry. The original
276 : * syscache entry is released before returning. The caller should
277 : * heap_freetuple() the result when done with it.
278 : */
279 : HeapTuple
280 480692 : SearchSysCacheCopy(int cacheId,
281 : Datum key1,
282 : Datum key2,
283 : Datum key3,
284 : Datum key4)
285 : {
286 : HeapTuple tuple,
287 : newtuple;
288 :
289 480692 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
290 480692 : if (!HeapTupleIsValid(tuple))
291 119796 : return tuple;
292 360896 : newtuple = heap_copytuple(tuple);
293 360896 : ReleaseSysCache(tuple);
294 360896 : return newtuple;
295 : }
296 :
297 : /*
298 : * SearchSysCacheExists
299 : *
300 : * A convenience routine that just probes to see if a tuple can be found.
301 : * No lock is retained on the syscache entry.
302 : */
303 : bool
304 1082326 : SearchSysCacheExists(int cacheId,
305 : Datum key1,
306 : Datum key2,
307 : Datum key3,
308 : Datum key4)
309 : {
310 : HeapTuple tuple;
311 :
312 1082326 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
313 1082326 : if (!HeapTupleIsValid(tuple))
314 206636 : return false;
315 875690 : ReleaseSysCache(tuple);
316 875690 : return true;
317 : }
318 :
319 : /*
320 : * GetSysCacheOid
321 : *
322 : * A convenience routine that does SearchSysCache and returns the OID in the
323 : * oidcol column of the found tuple, or InvalidOid if no tuple could be found.
324 : * No lock is retained on the syscache entry.
325 : */
326 : Oid
327 3165678 : GetSysCacheOid(int cacheId,
328 : AttrNumber oidcol,
329 : Datum key1,
330 : Datum key2,
331 : Datum key3,
332 : Datum key4)
333 : {
334 : HeapTuple tuple;
335 : bool isNull;
336 : Oid result;
337 :
338 3165678 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
339 3165678 : if (!HeapTupleIsValid(tuple))
340 1271530 : return InvalidOid;
341 3788296 : result = heap_getattr(tuple, oidcol,
342 1894148 : SysCache[cacheId]->cc_tupdesc,
343 : &isNull);
344 : Assert(!isNull); /* columns used as oids should never be NULL */
345 1894148 : ReleaseSysCache(tuple);
346 1894148 : return result;
347 : }
348 :
349 :
350 : /*
351 : * SearchSysCacheAttName
352 : *
353 : * This routine is equivalent to SearchSysCache on the ATTNAME cache,
354 : * except that it will return NULL if the found attribute is marked
355 : * attisdropped. This is convenient for callers that want to act as
356 : * though dropped attributes don't exist.
357 : */
358 : HeapTuple
359 101640 : SearchSysCacheAttName(Oid relid, const char *attname)
360 : {
361 : HeapTuple tuple;
362 :
363 101640 : tuple = SearchSysCache2(ATTNAME,
364 : ObjectIdGetDatum(relid),
365 : CStringGetDatum(attname));
366 101640 : if (!HeapTupleIsValid(tuple))
367 858 : return NULL;
368 100782 : if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
369 : {
370 78 : ReleaseSysCache(tuple);
371 78 : return NULL;
372 : }
373 100704 : return tuple;
374 : }
375 :
376 : /*
377 : * SearchSysCacheCopyAttName
378 : *
379 : * As above, an attisdropped-aware version of SearchSysCacheCopy.
380 : */
381 : HeapTuple
382 9952 : SearchSysCacheCopyAttName(Oid relid, const char *attname)
383 : {
384 : HeapTuple tuple,
385 : newtuple;
386 :
387 9952 : tuple = SearchSysCacheAttName(relid, attname);
388 9952 : if (!HeapTupleIsValid(tuple))
389 654 : return tuple;
390 9298 : newtuple = heap_copytuple(tuple);
391 9298 : ReleaseSysCache(tuple);
392 9298 : return newtuple;
393 : }
394 :
395 : /*
396 : * SearchSysCacheExistsAttName
397 : *
398 : * As above, an attisdropped-aware version of SearchSysCacheExists.
399 : */
400 : bool
401 1818 : SearchSysCacheExistsAttName(Oid relid, const char *attname)
402 : {
403 : HeapTuple tuple;
404 :
405 1818 : tuple = SearchSysCacheAttName(relid, attname);
406 1818 : if (!HeapTupleIsValid(tuple))
407 12 : return false;
408 1806 : ReleaseSysCache(tuple);
409 1806 : return true;
410 : }
411 :
412 :
413 : /*
414 : * SearchSysCacheAttNum
415 : *
416 : * This routine is equivalent to SearchSysCache on the ATTNUM cache,
417 : * except that it will return NULL if the found attribute is marked
418 : * attisdropped. This is convenient for callers that want to act as
419 : * though dropped attributes don't exist.
420 : */
421 : HeapTuple
422 24196 : SearchSysCacheAttNum(Oid relid, int16 attnum)
423 : {
424 : HeapTuple tuple;
425 :
426 24196 : tuple = SearchSysCache2(ATTNUM,
427 : ObjectIdGetDatum(relid),
428 : Int16GetDatum(attnum));
429 24196 : if (!HeapTupleIsValid(tuple))
430 12 : return NULL;
431 24184 : if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
432 : {
433 0 : ReleaseSysCache(tuple);
434 0 : return NULL;
435 : }
436 24184 : return tuple;
437 : }
438 :
439 : /*
440 : * SearchSysCacheCopyAttNum
441 : *
442 : * As above, an attisdropped-aware version of SearchSysCacheCopy.
443 : */
444 : HeapTuple
445 24132 : SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
446 : {
447 : HeapTuple tuple,
448 : newtuple;
449 :
450 24132 : tuple = SearchSysCacheAttNum(relid, attnum);
451 24132 : if (!HeapTupleIsValid(tuple))
452 0 : return NULL;
453 24132 : newtuple = heap_copytuple(tuple);
454 24132 : ReleaseSysCache(tuple);
455 24132 : return newtuple;
456 : }
457 :
458 :
459 : /*
460 : * SysCacheGetAttr
461 : *
462 : * Given a tuple previously fetched by SearchSysCache(),
463 : * extract a specific attribute.
464 : *
465 : * This is equivalent to using heap_getattr() on a tuple fetched
466 : * from a non-cached relation. Usually, this is only used for attributes
467 : * that could be NULL or variable length; the fixed-size attributes in
468 : * a system table are accessed just by mapping the tuple onto the C struct
469 : * declarations from include/catalog/.
470 : *
471 : * As with heap_getattr(), if the attribute is of a pass-by-reference type
472 : * then a pointer into the tuple data area is returned --- the caller must
473 : * not modify or pfree the datum!
474 : *
475 : * Note: it is legal to use SysCacheGetAttr() with a cacheId referencing
476 : * a different cache for the same catalog the tuple was fetched from.
477 : */
478 : Datum
479 4543654 : SysCacheGetAttr(int cacheId, HeapTuple tup,
480 : AttrNumber attributeNumber,
481 : bool *isNull)
482 : {
483 : /*
484 : * We just need to get the TupleDesc out of the cache entry, and then we
485 : * can apply heap_getattr(). Normally the cache control data is already
486 : * valid (because the caller recently fetched the tuple via this same
487 : * cache), but there are cases where we have to initialize the cache here.
488 : */
489 4543654 : if (cacheId < 0 || cacheId >= SysCacheSize ||
490 4543654 : !PointerIsValid(SysCache[cacheId]))
491 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
492 4543654 : if (!PointerIsValid(SysCache[cacheId]->cc_tupdesc))
493 : {
494 24518 : InitCatCachePhase2(SysCache[cacheId], false);
495 : Assert(PointerIsValid(SysCache[cacheId]->cc_tupdesc));
496 : }
497 :
498 9087308 : return heap_getattr(tup, attributeNumber,
499 4543654 : SysCache[cacheId]->cc_tupdesc,
500 : isNull);
501 : }
502 :
503 : /*
504 : * SysCacheGetAttrNotNull
505 : *
506 : * As above, a version of SysCacheGetAttr which knows that the attr cannot
507 : * be NULL.
508 : */
509 : Datum
510 2656574 : SysCacheGetAttrNotNull(int cacheId, HeapTuple tup,
511 : AttrNumber attributeNumber)
512 : {
513 : bool isnull;
514 : Datum attr;
515 :
516 2656574 : attr = SysCacheGetAttr(cacheId, tup, attributeNumber, &isnull);
517 :
518 2656574 : if (isnull)
519 : {
520 0 : elog(ERROR,
521 : "unexpected null value in cached tuple for catalog %s column %s",
522 : get_rel_name(cacheinfo[cacheId].reloid),
523 : NameStr(TupleDescAttr(SysCache[cacheId]->cc_tupdesc, attributeNumber - 1)->attname));
524 : }
525 :
526 2656574 : return attr;
527 : }
528 :
529 : /*
530 : * GetSysCacheHashValue
531 : *
532 : * Get the hash value that would be used for a tuple in the specified cache
533 : * with the given search keys.
534 : *
535 : * The reason for exposing this as part of the API is that the hash value is
536 : * exposed in cache invalidation operations, so there are places outside the
537 : * catcache code that need to be able to compute the hash values.
538 : */
539 : uint32
540 141006 : GetSysCacheHashValue(int cacheId,
541 : Datum key1,
542 : Datum key2,
543 : Datum key3,
544 : Datum key4)
545 : {
546 141006 : if (cacheId < 0 || cacheId >= SysCacheSize ||
547 141006 : !PointerIsValid(SysCache[cacheId]))
548 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
549 :
550 141006 : return GetCatCacheHashValue(SysCache[cacheId], key1, key2, key3, key4);
551 : }
552 :
553 : /*
554 : * List-search interface
555 : */
556 : struct catclist *
557 3048946 : SearchSysCacheList(int cacheId, int nkeys,
558 : Datum key1, Datum key2, Datum key3)
559 : {
560 3048946 : if (cacheId < 0 || cacheId >= SysCacheSize ||
561 3048946 : !PointerIsValid(SysCache[cacheId]))
562 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
563 :
564 3048946 : return SearchCatCacheList(SysCache[cacheId], nkeys,
565 : key1, key2, key3);
566 : }
567 :
568 : /*
569 : * SysCacheInvalidate
570 : *
571 : * Invalidate entries in the specified cache, given a hash value.
572 : * See CatCacheInvalidate() for more info.
573 : *
574 : * This routine is only quasi-public: it should only be used by inval.c.
575 : */
576 : void
577 18303686 : SysCacheInvalidate(int cacheId, uint32 hashValue)
578 : {
579 18303686 : if (cacheId < 0 || cacheId >= SysCacheSize)
580 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
581 :
582 : /* if this cache isn't initialized yet, no need to do anything */
583 18303686 : if (!PointerIsValid(SysCache[cacheId]))
584 0 : return;
585 :
586 18303686 : CatCacheInvalidate(SysCache[cacheId], hashValue);
587 : }
588 :
589 : /*
590 : * Certain relations that do not have system caches send snapshot invalidation
591 : * messages in lieu of catcache messages. This is for the benefit of
592 : * GetCatalogSnapshot(), which can then reuse its existing MVCC snapshot
593 : * for scanning one of those catalogs, rather than taking a new one, if no
594 : * invalidation has been received.
595 : *
596 : * Relations that have syscaches need not (and must not) be listed here. The
597 : * catcache invalidation messages will also flush the snapshot. If you add a
598 : * syscache for one of these relations, remove it from this list.
599 : */
600 : bool
601 12443020 : RelationInvalidatesSnapshotsOnly(Oid relid)
602 : {
603 12443020 : switch (relid)
604 : {
605 2236080 : case DbRoleSettingRelationId:
606 : case DependRelationId:
607 : case SharedDependRelationId:
608 : case DescriptionRelationId:
609 : case SharedDescriptionRelationId:
610 : case SecLabelRelationId:
611 : case SharedSecLabelRelationId:
612 2236080 : return true;
613 10206940 : default:
614 10206940 : break;
615 : }
616 :
617 10206940 : return false;
618 : }
619 :
620 : /*
621 : * Test whether a relation has a system cache.
622 : */
623 : bool
624 7650972 : RelationHasSysCache(Oid relid)
625 : {
626 7650972 : int low = 0,
627 7650972 : high = SysCacheRelationOidSize - 1;
628 :
629 33807182 : while (low <= high)
630 : {
631 33383908 : int middle = low + (high - low) / 2;
632 :
633 33383908 : if (SysCacheRelationOid[middle] == relid)
634 7227698 : return true;
635 26156210 : if (SysCacheRelationOid[middle] < relid)
636 9097596 : low = middle + 1;
637 : else
638 17058614 : high = middle - 1;
639 : }
640 :
641 423274 : return false;
642 : }
643 :
644 : /*
645 : * Test whether a relation supports a system cache, ie it is either a
646 : * cached table or the index used for a cache.
647 : */
648 : bool
649 1872440 : RelationSupportsSysCache(Oid relid)
650 : {
651 1872440 : int low = 0,
652 1872440 : high = SysCacheSupportingRelOidSize - 1;
653 :
654 16022568 : while (low <= high)
655 : {
656 14432562 : int middle = low + (high - low) / 2;
657 :
658 14432562 : if (SysCacheSupportingRelOid[middle] == relid)
659 282434 : return true;
660 14150128 : if (SysCacheSupportingRelOid[middle] < relid)
661 13340430 : low = middle + 1;
662 : else
663 809698 : high = middle - 1;
664 : }
665 :
666 1590006 : return false;
667 : }
668 :
669 :
670 : /*
671 : * OID comparator for qsort
672 : */
673 : static int
674 49150608 : oid_compare(const void *a, const void *b)
675 : {
676 49150608 : Oid oa = *((const Oid *) a);
677 49150608 : Oid ob = *((const Oid *) b);
678 :
679 49150608 : return pg_cmp_u32(oa, ob);
680 : }
|