syscache.c 16.4 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * syscache.c
4
 *	  System cache management routines
5
 *
6
 * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
Bruce Momjian's avatar
Add:  
Bruce Momjian committed
7
 * Portions Copyright (c) 1994, Regents of the University of California
8 9 10
 *
 *
 * IDENTIFICATION
11
 *	  $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.105 2006/07/11 18:26:11 momjian Exp $
12 13
 *
 * NOTES
14 15
 *	  These routines allow the parser/planner/executor to perform
 *	  rapid lookups on the contents of the system catalogs.
16
 *
17
 *	  see catalog/syscache.h for a list of the cache id's
18 19 20
 *
 *-------------------------------------------------------------------------
 */
21
#include "postgres.h"
22

23
#include "access/heapam.h"
24 25
#include "access/transam.h"
#include "catalog/indexing.h"
Bruce Momjian's avatar
Bruce Momjian committed
26
#include "catalog/pg_aggregate.h"
27
#include "catalog/pg_amop.h"
28
#include "catalog/pg_amproc.h"
29
#include "catalog/pg_auth_members.h"
30
#include "catalog/pg_authid.h"
31
#include "catalog/pg_cast.h"
32
#include "catalog/pg_conversion.h"
33
#include "catalog/pg_database.h"
34 35 36
#include "catalog/pg_index.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_language.h"
37
#include "catalog/pg_namespace.h"
38 39 40 41
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_rewrite.h"
42
#include "catalog/pg_statistic.h"
Bruce Momjian's avatar
Bruce Momjian committed
43
#include "catalog/pg_type.h"
44
#include "utils/builtins.h"
Bruce Momjian's avatar
Bruce Momjian committed
45
#include "utils/catcache.h"
46
#include "utils/syscache.h"
Hiroshi Inoue's avatar
Hiroshi Inoue committed
47
#include "miscadmin.h"
48

49 50 51 52 53 54 55 56

/*---------------------------------------------------------------------------

	Adding system caches:

	Add your new cache to the list in include/utils/syscache.h.  Keep
	the list sorted alphabetically and adjust the cache numbers
	accordingly.
57

58 59 60 61 62 63 64 65 66 67 68 69
	Add your entry to the cacheinfo[] array below. All cache lists are
	alphabetical, so add it in the proper place.  Specify the relation OID,
	index OID, number of keys, key attribute numbers, and number of hash
	buckets.  If the relation contains tuples that are associated with a
	particular relation (for example, its attributes, rules, triggers, etc)
	then specify the attribute number that contains the OID of the associated
	relation.  This is used by CatalogCacheFlushRelation() to remove the
	correct tuples during a table drop or relcache invalidation event.

	The number of hash buckets must be a power of 2.  It's reasonable to
	set this to the number of entries that might be in the particular cache
	in a medium-size database.
70

71 72
	There must be a unique index underlying each syscache (ie, an index
	whose key is the same as that of the cache).  If there is not one
73 74 75 76
	already, add definitions for it to include/catalog/indexing.h: you need
	to add a DECLARE_UNIQUE_INDEX macro and a #define for the index OID.
	(Adding an index requires a catversion.h update, while simply
	adding/deleting caches only requires a recompile.)
77

78
	Finally, any place your relation gets heap_insert() or
79
	heap_update() calls, make sure there is a CatalogUpdateIndexes() or
80
	similar call.  The heap_* calls do not update indexes.
81

82
	bjm 1999/11/22
83

84
*---------------------------------------------------------------------------
85 86
*/

87
/*
88 89 90 91
 *		struct cachedesc: information defining a single syscache
 */
struct cachedesc
{
92 93
	Oid			reloid;			/* OID of the relation being cached */
	Oid			indoid;			/* OID of index relation for this cache */
94
	int			reloidattr;		/* attr number of rel OID reference, or 0 */
95 96
	int			nkeys;			/* # of keys needed for cache lookup */
	int			key[4];			/* attribute numbers of key attrs */
97
	int			nbuckets;		/* number of hash buckets for this cache */
98 99
};

100
static const struct cachedesc cacheinfo[] = {
101
	{AggregateRelationId,		/* AGGFNOID */
102
		AggregateFnoidIndexId,
103 104 105
		0,
		1,
		{
106
			Anum_pg_aggregate_aggfnoid,
107 108 109
			0,
			0,
			0
110 111 112
		},
		32
	},
113
	{AccessMethodRelationId,	/* AMNAME */
114
		AmNameIndexId,
115
		0,
116 117 118 119 120 121
		1,
		{
			Anum_pg_am_amname,
			0,
			0,
			0
122 123 124
		},
		4
	},
125
	{AccessMethodRelationId,	/* AMOID */
126
		AmOidIndexId,
127 128 129 130 131 132 133
		0,
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
134 135 136
		},
		4
	},
137 138
	{AccessMethodOperatorRelationId,	/* AMOPOPID */
		AccessMethodOperatorIndexId,
139
		0,
140
		2,
141
		{
142
			Anum_pg_amop_amopopr,
143
			Anum_pg_amop_amopclaid,
144
			0,
145
			0
146 147 148
		},
		64
	},
149 150
	{AccessMethodOperatorRelationId,	/* AMOPSTRATEGY */
		AccessMethodStrategyIndexId,
151
		0,
152
		3,
153
		{
154
			Anum_pg_amop_amopclaid,
155
			Anum_pg_amop_amopsubtype,
156
			Anum_pg_amop_amopstrategy,
157
			0
158 159 160
		},
		64
	},
161 162
	{AccessMethodProcedureRelationId,	/* AMPROCNUM */
		AccessMethodProcedureIndexId,
163
		0,
164
		3,
165 166
		{
			Anum_pg_amproc_amopclaid,
167
			Anum_pg_amproc_amprocsubtype,
168
			Anum_pg_amproc_amprocnum,
169
			0
170 171 172
		},
		64
	},
173
	{AttributeRelationId,		/* ATTNAME */
174
		AttributeRelidNameIndexId,
175
		Anum_pg_attribute_attrelid,
176
		2,
177 178
		{
			Anum_pg_attribute_attrelid,
179 180
			Anum_pg_attribute_attname,
			0,
181
			0
182 183 184
		},
		2048
	},
185
	{AttributeRelationId,		/* ATTNUM */
186
		AttributeRelidNumIndexId,
187
		Anum_pg_attribute_attrelid,
188
		2,
189 190
		{
			Anum_pg_attribute_attrelid,
191 192
			Anum_pg_attribute_attnum,
			0,
193
			0
194 195 196
		},
		2048
	},
197
	{AuthMemRelationId,			/* AUTHMEMMEMROLE */
198 199 200 201 202 203 204 205
		AuthMemMemRoleIndexId,
		0,
		2,
		{
			Anum_pg_auth_members_member,
			Anum_pg_auth_members_roleid,
			0,
			0
206 207 208
		},
		128
	},
209
	{AuthMemRelationId,			/* AUTHMEMROLEMEM */
210 211 212 213 214 215 216 217
		AuthMemRoleMemIndexId,
		0,
		2,
		{
			Anum_pg_auth_members_roleid,
			Anum_pg_auth_members_member,
			0,
			0
218 219 220
		},
		128
	},
221
	{AuthIdRelationId,			/* AUTHNAME */
222 223 224 225 226 227 228 229
		AuthIdRolnameIndexId,
		0,
		1,
		{
			Anum_pg_authid_rolname,
			0,
			0,
			0
230 231 232
		},
		128
	},
233
	{AuthIdRelationId,			/* AUTHOID */
234 235 236 237 238 239 240 241
		AuthIdOidIndexId,
		0,
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
242 243 244
		},
		128
	},
245
	{
246
		CastRelationId,			/* CASTSOURCETARGET */
247
		CastSourceTargetIndexId,
248 249 250 251 252 253 254
		0,
		2,
		{
			Anum_pg_cast_castsource,
			Anum_pg_cast_casttarget,
			0,
			0
255 256 257
		},
		256
	},
258
	{OperatorClassRelationId,	/* CLAAMNAMENSP */
259
		OpclassAmNameNspIndexId,
260
		0,
261
		3,
262
		{
263 264
			Anum_pg_opclass_opcamid,
			Anum_pg_opclass_opcname,
265
			Anum_pg_opclass_opcnamespace,
266
			0
267 268 269
		},
		64
	},
270
	{OperatorClassRelationId,	/* CLAOID */
271
		OpclassOidIndexId,
272
		0,
273 274
		1,
		{
275
			ObjectIdAttributeNumber,
276 277 278
			0,
			0,
			0
279 280 281
		},
		64
	},
282
	{ConversionRelationId,		/* CONDEFAULT */
283
		ConversionDefaultIndexId,
Tatsuo Ishii's avatar
Tatsuo Ishii committed
284 285 286 287 288 289 290
		0,
		4,
		{
			Anum_pg_conversion_connamespace,
			Anum_pg_conversion_conforencoding,
			Anum_pg_conversion_contoencoding,
			ObjectIdAttributeNumber,
291 292 293
		},
		128
	},
294
	{ConversionRelationId,		/* CONNAMENSP */
295
		ConversionNameNspIndexId,
296 297 298 299 300 301 302
		0,
		2,
		{
			Anum_pg_conversion_conname,
			Anum_pg_conversion_connamespace,
			0,
			0
303 304 305
		},
		128
	},
306
	{ConversionRelationId,		/* CONOID */
307
		ConversionOidIndexId,
Tatsuo Ishii's avatar
Tatsuo Ishii committed
308 309 310 311 312 313 314
		0,
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
315 316 317
		},
		128
	},
318 319 320 321 322 323 324 325 326
	{DatabaseRelationId,		/* DATABASEOID */
		DatabaseOidIndexId,
		0,
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
327 328 329
		},
		4
	},
330
	{IndexRelationId,			/* INDEXRELID */
331
		IndexRelidIndexId,
332
		Anum_pg_index_indrelid,
333
		1,
334 335
		{
			Anum_pg_index_indexrelid,
336 337
			0,
			0,
338
			0
339 340 341
		},
		1024
	},
342
	{InheritsRelationId,		/* INHRELID */
343
		InheritsRelidSeqnoIndexId,
344
		Anum_pg_inherits_inhrelid,
345 346 347 348 349 350
		2,
		{
			Anum_pg_inherits_inhrelid,
			Anum_pg_inherits_inhseqno,
			0,
			0
351 352 353
		},
		256
	},
354
	{LanguageRelationId,		/* LANGNAME */
355
		LanguageNameIndexId,
356
		0,
357
		1,
358 359
		{
			Anum_pg_language_lanname,
360 361
			0,
			0,
362
			0
363 364 365
		},
		4
	},
366
	{LanguageRelationId,		/* LANGOID */
367
		LanguageOidIndexId,
368
		0,
369 370 371 372 373 374
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
375 376 377
		},
		4
	},
378
	{NamespaceRelationId,		/* NAMESPACENAME */
379
		NamespaceNameIndexId,
380 381 382 383 384 385 386
		0,
		1,
		{
			Anum_pg_namespace_nspname,
			0,
			0,
			0
387 388 389
		},
		256
	},
390
	{NamespaceRelationId,		/* NAMESPACEOID */
391
		NamespaceOidIndexId,
392 393 394 395 396 397 398
		0,
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
399 400 401
		},
		256
	},
402
	{OperatorRelationId,		/* OPERNAMENSP */
403
		OperatorNameNspIndexId,
404
		0,
405
		4,
406 407
		{
			Anum_pg_operator_oprname,
408 409
			Anum_pg_operator_oprleft,
			Anum_pg_operator_oprright,
410
			Anum_pg_operator_oprnamespace
411 412 413
		},
		1024
	},
414
	{OperatorRelationId,		/* OPEROID */
415
		OperatorOidIndexId,
416
		0,
417
		1,
418 419
		{
			ObjectIdAttributeNumber,
420 421
			0,
			0,
422
			0
423 424 425
		},
		1024
	},
426
	{ProcedureRelationId,		/* PROCNAMEARGSNSP */
427
		ProcedureNameArgsNspIndexId,
428
		0,
429
		3,
430 431
		{
			Anum_pg_proc_proname,
432
			Anum_pg_proc_proargtypes,
433 434
			Anum_pg_proc_pronamespace,
			0
435 436 437
		},
		2048
	},
438
	{ProcedureRelationId,		/* PROCOID */
439
		ProcedureOidIndexId,
440
		0,
441
		1,
442 443
		{
			ObjectIdAttributeNumber,
444 445
			0,
			0,
446
			0
447 448 449
		},
		2048
	},
450
	{RelationRelationId,		/* RELNAMENSP */
451
		ClassNameNspIndexId,
452
		ObjectIdAttributeNumber,
453
		2,
454 455
		{
			Anum_pg_class_relname,
456
			Anum_pg_class_relnamespace,
457
			0,
458
			0
459 460 461
		},
		1024
	},
462
	{RelationRelationId,		/* RELOID */
463
		ClassOidIndexId,
464
		ObjectIdAttributeNumber,
465
		1,
466 467
		{
			ObjectIdAttributeNumber,
468 469
			0,
			0,
470
			0
471 472 473
		},
		1024
	},
474
	{RewriteRelationId,			/* RULERELNAME */
475
		RewriteRelRulenameIndexId,
476
		Anum_pg_rewrite_ev_class,
477
		2,
478
		{
479
			Anum_pg_rewrite_ev_class,
480
			Anum_pg_rewrite_rulename,
481
			0,
482
			0
483 484 485
		},
		1024
	},
486
	{StatisticRelationId,		/* STATRELATT */
487
		StatisticRelidAttnumIndexId,
488
		Anum_pg_statistic_starelid,
489
		2,
490 491 492
		{
			Anum_pg_statistic_starelid,
			Anum_pg_statistic_staattnum,
493
			0,
494
			0
495 496 497
		},
		1024
	},
498
	{TypeRelationId,			/* TYPENAMENSP */
499
		TypeNameNspIndexId,
500
		Anum_pg_type_typrelid,
501
		2,
502
		{
503
			Anum_pg_type_typname,
504
			Anum_pg_type_typnamespace,
505
			0,
506
			0
507 508 509
		},
		1024
	},
510
	{TypeRelationId,			/* TYPEOID */
511
		TypeOidIndexId,
512
		Anum_pg_type_typrelid,
513
		1,
514
		{
515
			ObjectIdAttributeNumber,
516 517
			0,
			0,
518
			0
519 520 521
		},
		1024
	}
522
};
523

524
static CatCache *SysCache[lengthof(cacheinfo)];
525
static int	SysCacheSize = lengthof(cacheinfo);
526
static bool CacheInitialized = false;
527 528


529
/*
530
 * InitCatalogCache - initialize the caches
531
 *
532
 * Note that no database access is done here; we only allocate memory
533
 * and initialize the cache structure.	Interrogation of the database
534
 * to complete initialization of a cache happens upon first use
535
 * of that cache.
536 537
 */
void
538
InitCatalogCache(void)
539
{
540
	int			cacheId;
541

542
	Assert(!CacheInitialized);
543

544
	MemSet(SysCache, 0, sizeof(SysCache));
545

546
	for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
547
	{
548
		SysCache[cacheId] = InitCatCache(cacheId,
549 550
										 cacheinfo[cacheId].reloid,
										 cacheinfo[cacheId].indoid,
551
										 cacheinfo[cacheId].reloidattr,
552
										 cacheinfo[cacheId].nkeys,
553 554
										 cacheinfo[cacheId].key,
										 cacheinfo[cacheId].nbuckets);
555
		if (!PointerIsValid(SysCache[cacheId]))
556 557
			elog(ERROR, "could not initialize cache %u (%d)",
				 cacheinfo[cacheId].reloid, cacheId);
558
	}
Hiroshi Inoue's avatar
Hiroshi Inoue committed
559
	CacheInitialized = true;
560
}
561

562

563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
/*
 * InitCatalogCachePhase2 - finish initializing the caches
 *
 * Finish initializing all the caches, including necessary database
 * access.
 *
 * This is *not* essential; normally we allow syscaches to be initialized
 * on first use.  However, it is useful as a mechanism to preload the
 * relcache with entries for the most-commonly-used system catalogs.
 * Therefore, we invoke this routine when we need to write a new relcache
 * init file.
 */
void
InitCatalogCachePhase2(void)
{
	int			cacheId;

	Assert(CacheInitialized);

	for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
		InitCatCachePhase2(SysCache[cacheId]);
}


587
/*
588
 * SearchSysCache
589
 *
590
 *	A layer on top of SearchCatCache that does the initialization and
591
 *	key-setting for you.
592
 *
593
 *	Returns the cache copy of the tuple if one is found, NULL if not.
594
 *	The tuple is the 'cache' copy and must NOT be modified!
595
 *
596 597 598 599
 *	When the caller is done using the tuple, call ReleaseSysCache()
 *	to release the reference count grabbed by SearchSysCache().  If this
 *	is not done, the tuple will remain locked in cache until end of
 *	transaction, which is tolerable but not desirable.
600
 *
601
 *	CAUTION: The tuple that is returned must NOT be freed by the caller!
602 603
 */
HeapTuple
604 605 606 607 608
SearchSysCache(int cacheId,
			   Datum key1,
			   Datum key2,
			   Datum key3,
			   Datum key4)
609
{
610
	if (cacheId < 0 || cacheId >= SysCacheSize ||
Bruce Momjian's avatar
Bruce Momjian committed
611
		!PointerIsValid(SysCache[cacheId]))
612
		elog(ERROR, "invalid cache id: %d", cacheId);
613

614
	return SearchCatCache(SysCache[cacheId], key1, key2, key3, key4);
615 616
}

617 618 619 620 621 622 623 624 625
/*
 * ReleaseSysCache
 *		Release previously grabbed reference count on a tuple
 */
void
ReleaseSysCache(HeapTuple tuple)
{
	ReleaseCatCache(tuple);
}
626

627
/*
628
 * SearchSysCacheCopy
629
 *
630 631 632 633
 * A convenience routine that does SearchSysCache and (if successful)
 * returns a modifiable copy of the syscache entry.  The original
 * syscache entry is released before returning.  The caller should
 * heap_freetuple() the result when done with it.
634 635
 */
HeapTuple
636 637 638 639 640
SearchSysCacheCopy(int cacheId,
				   Datum key1,
				   Datum key2,
				   Datum key3,
				   Datum key4)
641
{
642 643 644 645 646 647 648 649 650
	HeapTuple	tuple,
				newtuple;

	tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
	if (!HeapTupleIsValid(tuple))
		return tuple;
	newtuple = heap_copytuple(tuple);
	ReleaseSysCache(tuple);
	return newtuple;
651 652
}

653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
/*
 * SearchSysCacheExists
 *
 * A convenience routine that just probes to see if a tuple can be found.
 * No lock is retained on the syscache entry.
 */
bool
SearchSysCacheExists(int cacheId,
					 Datum key1,
					 Datum key2,
					 Datum key3,
					 Datum key4)
{
	HeapTuple	tuple;

	tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
	if (!HeapTupleIsValid(tuple))
		return false;
	ReleaseSysCache(tuple);
	return true;
}

675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
/*
 * GetSysCacheOid
 *
 * A convenience routine that does SearchSysCache and returns the OID
 * of the found tuple, or InvalidOid if no tuple could be found.
 * No lock is retained on the syscache entry.
 */
Oid
GetSysCacheOid(int cacheId,
			   Datum key1,
			   Datum key2,
			   Datum key3,
			   Datum key4)
{
	HeapTuple	tuple;
	Oid			result;

	tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
	if (!HeapTupleIsValid(tuple))
		return InvalidOid;
695
	result = HeapTupleGetOid(tuple);
696 697 698
	ReleaseSysCache(tuple);
	return result;
}
699

700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764

/*
 * SearchSysCacheAttName
 *
 * This routine is equivalent to SearchSysCache on the ATTNAME cache,
 * except that it will return NULL if the found attribute is marked
 * attisdropped.  This is convenient for callers that want to act as
 * though dropped attributes don't exist.
 */
HeapTuple
SearchSysCacheAttName(Oid relid, const char *attname)
{
	HeapTuple	tuple;

	tuple = SearchSysCache(ATTNAME,
						   ObjectIdGetDatum(relid),
						   CStringGetDatum(attname),
						   0, 0);
	if (!HeapTupleIsValid(tuple))
		return NULL;
	if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
	{
		ReleaseSysCache(tuple);
		return NULL;
	}
	return tuple;
}

/*
 * SearchSysCacheCopyAttName
 *
 * As above, an attisdropped-aware version of SearchSysCacheCopy.
 */
HeapTuple
SearchSysCacheCopyAttName(Oid relid, const char *attname)
{
	HeapTuple	tuple,
				newtuple;

	tuple = SearchSysCacheAttName(relid, attname);
	if (!HeapTupleIsValid(tuple))
		return tuple;
	newtuple = heap_copytuple(tuple);
	ReleaseSysCache(tuple);
	return newtuple;
}

/*
 * SearchSysCacheExistsAttName
 *
 * As above, an attisdropped-aware version of SearchSysCacheExists.
 */
bool
SearchSysCacheExistsAttName(Oid relid, const char *attname)
{
	HeapTuple	tuple;

	tuple = SearchSysCacheAttName(relid, attname);
	if (!HeapTupleIsValid(tuple))
		return false;
	ReleaseSysCache(tuple);
	return true;
}


765
/*
766 767
 * SysCacheGetAttr
 *
768 769
 *		Given a tuple previously fetched by SearchSysCache(),
 *		extract a specific attribute.
770
 *
771
 * This is equivalent to using heap_getattr() on a tuple fetched
772
 * from a non-cached relation.	Usually, this is only used for attributes
773 774 775
 * that could be NULL or variable length; the fixed-size attributes in
 * a system table are accessed just by mapping the tuple onto the C struct
 * declarations from include/catalog/.
776
 *
777 778 779
 * As with heap_getattr(), if the attribute is of a pass-by-reference type
 * then a pointer into the tuple data area is returned --- the caller must
 * not modify or pfree the datum!
780
 */
781 782 783
Datum
SysCacheGetAttr(int cacheId, HeapTuple tup,
				AttrNumber attributeNumber,
784
				bool *isNull)
785
{
786
	/*
787 788 789 790
	 * We just need to get the TupleDesc out of the cache entry, and then we
	 * can apply heap_getattr().  We expect that the cache control data is
	 * currently valid --- if the caller recently fetched the tuple, then it
	 * should be.
791
	 */
792
	if (cacheId < 0 || cacheId >= SysCacheSize)
793
		elog(ERROR, "invalid cache id: %d", cacheId);
794 795
	if (!PointerIsValid(SysCache[cacheId]) ||
		!PointerIsValid(SysCache[cacheId]->cc_tupdesc))
796
		elog(ERROR, "missing cache data for cache id %d", cacheId);
797 798 799

	return heap_getattr(tup, attributeNumber,
						SysCache[cacheId]->cc_tupdesc,
800
						isNull);
801
}
802 803 804 805 806 807 808 809 810

/*
 * List-search interface
 */
struct catclist *
SearchSysCacheList(int cacheId, int nkeys,
				   Datum key1, Datum key2, Datum key3, Datum key4)
{
	if (cacheId < 0 || cacheId >= SysCacheSize ||
Bruce Momjian's avatar
Bruce Momjian committed
811
		!PointerIsValid(SysCache[cacheId]))
812
		elog(ERROR, "invalid cache id: %d", cacheId);
813 814 815 816

	return SearchCatCacheList(SysCache[cacheId], nkeys,
							  key1, key2, key3, key4);
}