1 /*-------------------------------------------------------------------------
2  *
3  * syscache.c
4  *	  System cache management routines
5  *
6  * Portions Copyright (c) 1996-2018, 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 "access/sysattr.h"
25 #include "catalog/indexing.h"
26 #include "catalog/pg_aggregate.h"
27 #include "catalog/pg_am.h"
28 #include "catalog/pg_amop.h"
29 #include "catalog/pg_amproc.h"
30 #include "catalog/pg_auth_members.h"
31 #include "catalog/pg_authid.h"
32 #include "catalog/pg_cast.h"
33 #include "catalog/pg_collation.h"
34 #include "catalog/pg_constraint.h"
35 #include "catalog/pg_conversion.h"
36 #include "catalog/pg_database.h"
37 #include "catalog/pg_db_role_setting.h"
38 #include "catalog/pg_default_acl.h"
39 #include "catalog/pg_depend.h"
40 #include "catalog/pg_description.h"
41 #include "catalog/pg_enum.h"
42 #include "catalog/pg_event_trigger.h"
43 #include "catalog/pg_foreign_data_wrapper.h"
44 #include "catalog/pg_foreign_server.h"
45 #include "catalog/pg_foreign_table.h"
46 #include "catalog/pg_language.h"
47 #include "catalog/pg_namespace.h"
48 #include "catalog/pg_opclass.h"
49 #include "catalog/pg_operator.h"
50 #include "catalog/pg_opfamily.h"
51 #include "catalog/pg_partitioned_table.h"
52 #include "catalog/pg_proc.h"
53 #include "catalog/pg_publication.h"
54 #include "catalog/pg_publication_rel.h"
55 #include "catalog/pg_range.h"
56 #include "catalog/pg_rewrite.h"
57 #include "catalog/pg_seclabel.h"
58 #include "catalog/pg_sequence.h"
59 #include "catalog/pg_shdepend.h"
60 #include "catalog/pg_shdescription.h"
61 #include "catalog/pg_shseclabel.h"
62 #include "catalog/pg_replication_origin.h"
63 #include "catalog/pg_statistic.h"
64 #include "catalog/pg_statistic_ext.h"
65 #include "catalog/pg_subscription.h"
66 #include "catalog/pg_subscription_rel.h"
67 #include "catalog/pg_tablespace.h"
68 #include "catalog/pg_transform.h"
69 #include "catalog/pg_ts_config.h"
70 #include "catalog/pg_ts_config_map.h"
71 #include "catalog/pg_ts_dict.h"
72 #include "catalog/pg_ts_parser.h"
73 #include "catalog/pg_ts_template.h"
74 #include "catalog/pg_type.h"
75 #include "catalog/pg_user_mapping.h"
76 #include "utils/rel.h"
77 #include "utils/catcache.h"
78 #include "utils/syscache.h"
79 
80 
81 /*---------------------------------------------------------------------------
82 
83 	Adding system caches:
84 
85 	Add your new cache to the list in include/utils/syscache.h.
86 	Keep the list sorted alphabetically.
87 
88 	Add your entry to the cacheinfo[] array below. All cache lists are
89 	alphabetical, so add it in the proper place.  Specify the relation OID,
90 	index OID, number of keys, key attribute numbers, and initial number of
91 	hash buckets.
92 
93 	The number of hash buckets must be a power of 2.  It's reasonable to
94 	set this to the number of entries that might be in the particular cache
95 	in a medium-size database.
96 
97 	There must be a unique index underlying each syscache (ie, an index
98 	whose key is the same as that of the cache).  If there is not one
99 	already, add definitions for it to include/catalog/indexing.h: you need
100 	to add a DECLARE_UNIQUE_INDEX macro and a #define for the index OID.
101 	(Adding an index requires a catversion.h update, while simply
102 	adding/deleting caches only requires a recompile.)
103 
104 	Finally, any place your relation gets heap_insert() or
105 	heap_update() calls, use CatalogTupleInsert() or CatalogTupleUpdate()
106 	instead, which also update indexes.  The heap_* calls do not do that.
107 
108 *---------------------------------------------------------------------------
109 */
110 
111 /*
112  *		struct cachedesc: information defining a single syscache
113  */
114 struct cachedesc
115 {
116 	Oid			reloid;			/* OID of the relation being cached */
117 	Oid			indoid;			/* OID of index relation for this cache */
118 	int			nkeys;			/* # of keys needed for cache lookup */
119 	int			key[4];			/* attribute numbers of key attrs */
120 	int			nbuckets;		/* number of hash buckets for this cache */
121 };
122 
123 static const struct cachedesc cacheinfo[] = {
124 	{AggregateRelationId,		/* AGGFNOID */
125 		AggregateFnoidIndexId,
126 		1,
127 		{
128 			Anum_pg_aggregate_aggfnoid,
129 			0,
130 			0,
131 			0
132 		},
133 		16
134 	},
135 	{AccessMethodRelationId,	/* AMNAME */
136 		AmNameIndexId,
137 		1,
138 		{
139 			Anum_pg_am_amname,
140 			0,
141 			0,
142 			0
143 		},
144 		4
145 	},
146 	{AccessMethodRelationId,	/* AMOID */
147 		AmOidIndexId,
148 		1,
149 		{
150 			ObjectIdAttributeNumber,
151 			0,
152 			0,
153 			0
154 		},
155 		4
156 	},
157 	{AccessMethodOperatorRelationId,	/* AMOPOPID */
158 		AccessMethodOperatorIndexId,
159 		3,
160 		{
161 			Anum_pg_amop_amopopr,
162 			Anum_pg_amop_amoppurpose,
163 			Anum_pg_amop_amopfamily,
164 			0
165 		},
166 		64
167 	},
168 	{AccessMethodOperatorRelationId,	/* AMOPSTRATEGY */
169 		AccessMethodStrategyIndexId,
170 		4,
171 		{
172 			Anum_pg_amop_amopfamily,
173 			Anum_pg_amop_amoplefttype,
174 			Anum_pg_amop_amoprighttype,
175 			Anum_pg_amop_amopstrategy
176 		},
177 		64
178 	},
179 	{AccessMethodProcedureRelationId,	/* AMPROCNUM */
180 		AccessMethodProcedureIndexId,
181 		4,
182 		{
183 			Anum_pg_amproc_amprocfamily,
184 			Anum_pg_amproc_amproclefttype,
185 			Anum_pg_amproc_amprocrighttype,
186 			Anum_pg_amproc_amprocnum
187 		},
188 		16
189 	},
190 	{AttributeRelationId,		/* ATTNAME */
191 		AttributeRelidNameIndexId,
192 		2,
193 		{
194 			Anum_pg_attribute_attrelid,
195 			Anum_pg_attribute_attname,
196 			0,
197 			0
198 		},
199 		32
200 	},
201 	{AttributeRelationId,		/* ATTNUM */
202 		AttributeRelidNumIndexId,
203 		2,
204 		{
205 			Anum_pg_attribute_attrelid,
206 			Anum_pg_attribute_attnum,
207 			0,
208 			0
209 		},
210 		128
211 	},
212 	{AuthMemRelationId,			/* AUTHMEMMEMROLE */
213 		AuthMemMemRoleIndexId,
214 		2,
215 		{
216 			Anum_pg_auth_members_member,
217 			Anum_pg_auth_members_roleid,
218 			0,
219 			0
220 		},
221 		8
222 	},
223 	{AuthMemRelationId,			/* AUTHMEMROLEMEM */
224 		AuthMemRoleMemIndexId,
225 		2,
226 		{
227 			Anum_pg_auth_members_roleid,
228 			Anum_pg_auth_members_member,
229 			0,
230 			0
231 		},
232 		8
233 	},
234 	{AuthIdRelationId,			/* AUTHNAME */
235 		AuthIdRolnameIndexId,
236 		1,
237 		{
238 			Anum_pg_authid_rolname,
239 			0,
240 			0,
241 			0
242 		},
243 		8
244 	},
245 	{AuthIdRelationId,			/* AUTHOID */
246 		AuthIdOidIndexId,
247 		1,
248 		{
249 			ObjectIdAttributeNumber,
250 			0,
251 			0,
252 			0
253 		},
254 		8
255 	},
256 	{
257 		CastRelationId,			/* CASTSOURCETARGET */
258 		CastSourceTargetIndexId,
259 		2,
260 		{
261 			Anum_pg_cast_castsource,
262 			Anum_pg_cast_casttarget,
263 			0,
264 			0
265 		},
266 		256
267 	},
268 	{OperatorClassRelationId,	/* CLAAMNAMENSP */
269 		OpclassAmNameNspIndexId,
270 		3,
271 		{
272 			Anum_pg_opclass_opcmethod,
273 			Anum_pg_opclass_opcname,
274 			Anum_pg_opclass_opcnamespace,
275 			0
276 		},
277 		8
278 	},
279 	{OperatorClassRelationId,	/* CLAOID */
280 		OpclassOidIndexId,
281 		1,
282 		{
283 			ObjectIdAttributeNumber,
284 			0,
285 			0,
286 			0
287 		},
288 		8
289 	},
290 	{CollationRelationId,		/* COLLNAMEENCNSP */
291 		CollationNameEncNspIndexId,
292 		3,
293 		{
294 			Anum_pg_collation_collname,
295 			Anum_pg_collation_collencoding,
296 			Anum_pg_collation_collnamespace,
297 			0
298 		},
299 		8
300 	},
301 	{CollationRelationId,		/* COLLOID */
302 		CollationOidIndexId,
303 		1,
304 		{
305 			ObjectIdAttributeNumber,
306 			0,
307 			0,
308 			0
309 		},
310 		8
311 	},
312 	{ConversionRelationId,		/* CONDEFAULT */
313 		ConversionDefaultIndexId,
314 		4,
315 		{
316 			Anum_pg_conversion_connamespace,
317 			Anum_pg_conversion_conforencoding,
318 			Anum_pg_conversion_contoencoding,
319 			ObjectIdAttributeNumber,
320 		},
321 		8
322 	},
323 	{ConversionRelationId,		/* CONNAMENSP */
324 		ConversionNameNspIndexId,
325 		2,
326 		{
327 			Anum_pg_conversion_conname,
328 			Anum_pg_conversion_connamespace,
329 			0,
330 			0
331 		},
332 		8
333 	},
334 	{ConstraintRelationId,		/* CONSTROID */
335 		ConstraintOidIndexId,
336 		1,
337 		{
338 			ObjectIdAttributeNumber,
339 			0,
340 			0,
341 			0
342 		},
343 		16
344 	},
345 	{ConversionRelationId,		/* CONVOID */
346 		ConversionOidIndexId,
347 		1,
348 		{
349 			ObjectIdAttributeNumber,
350 			0,
351 			0,
352 			0
353 		},
354 		8
355 	},
356 	{DatabaseRelationId,		/* DATABASEOID */
357 		DatabaseOidIndexId,
358 		1,
359 		{
360 			ObjectIdAttributeNumber,
361 			0,
362 			0,
363 			0
364 		},
365 		4
366 	},
367 	{DefaultAclRelationId,		/* DEFACLROLENSPOBJ */
368 		DefaultAclRoleNspObjIndexId,
369 		3,
370 		{
371 			Anum_pg_default_acl_defaclrole,
372 			Anum_pg_default_acl_defaclnamespace,
373 			Anum_pg_default_acl_defaclobjtype,
374 			0
375 		},
376 		8
377 	},
378 	{EnumRelationId,			/* ENUMOID */
379 		EnumOidIndexId,
380 		1,
381 		{
382 			ObjectIdAttributeNumber,
383 			0,
384 			0,
385 			0
386 		},
387 		8
388 	},
389 	{EnumRelationId,			/* ENUMTYPOIDNAME */
390 		EnumTypIdLabelIndexId,
391 		2,
392 		{
393 			Anum_pg_enum_enumtypid,
394 			Anum_pg_enum_enumlabel,
395 			0,
396 			0
397 		},
398 		8
399 	},
400 	{EventTriggerRelationId,	/* EVENTTRIGGERNAME */
401 		EventTriggerNameIndexId,
402 		1,
403 		{
404 			Anum_pg_event_trigger_evtname,
405 			0,
406 			0,
407 			0
408 		},
409 		8
410 	},
411 	{EventTriggerRelationId,	/* EVENTTRIGGEROID */
412 		EventTriggerOidIndexId,
413 		1,
414 		{
415 			ObjectIdAttributeNumber,
416 			0,
417 			0,
418 			0
419 		},
420 		8
421 	},
422 	{ForeignDataWrapperRelationId,	/* FOREIGNDATAWRAPPERNAME */
423 		ForeignDataWrapperNameIndexId,
424 		1,
425 		{
426 			Anum_pg_foreign_data_wrapper_fdwname,
427 			0,
428 			0,
429 			0
430 		},
431 		2
432 	},
433 	{ForeignDataWrapperRelationId,	/* FOREIGNDATAWRAPPEROID */
434 		ForeignDataWrapperOidIndexId,
435 		1,
436 		{
437 			ObjectIdAttributeNumber,
438 			0,
439 			0,
440 			0
441 		},
442 		2
443 	},
444 	{ForeignServerRelationId,	/* FOREIGNSERVERNAME */
445 		ForeignServerNameIndexId,
446 		1,
447 		{
448 			Anum_pg_foreign_server_srvname,
449 			0,
450 			0,
451 			0
452 		},
453 		2
454 	},
455 	{ForeignServerRelationId,	/* FOREIGNSERVEROID */
456 		ForeignServerOidIndexId,
457 		1,
458 		{
459 			ObjectIdAttributeNumber,
460 			0,
461 			0,
462 			0
463 		},
464 		2
465 	},
466 	{ForeignTableRelationId,	/* FOREIGNTABLEREL */
467 		ForeignTableRelidIndexId,
468 		1,
469 		{
470 			Anum_pg_foreign_table_ftrelid,
471 			0,
472 			0,
473 			0
474 		},
475 		4
476 	},
477 	{IndexRelationId,			/* INDEXRELID */
478 		IndexRelidIndexId,
479 		1,
480 		{
481 			Anum_pg_index_indexrelid,
482 			0,
483 			0,
484 			0
485 		},
486 		64
487 	},
488 	{LanguageRelationId,		/* LANGNAME */
489 		LanguageNameIndexId,
490 		1,
491 		{
492 			Anum_pg_language_lanname,
493 			0,
494 			0,
495 			0
496 		},
497 		4
498 	},
499 	{LanguageRelationId,		/* LANGOID */
500 		LanguageOidIndexId,
501 		1,
502 		{
503 			ObjectIdAttributeNumber,
504 			0,
505 			0,
506 			0
507 		},
508 		4
509 	},
510 	{NamespaceRelationId,		/* NAMESPACENAME */
511 		NamespaceNameIndexId,
512 		1,
513 		{
514 			Anum_pg_namespace_nspname,
515 			0,
516 			0,
517 			0
518 		},
519 		4
520 	},
521 	{NamespaceRelationId,		/* NAMESPACEOID */
522 		NamespaceOidIndexId,
523 		1,
524 		{
525 			ObjectIdAttributeNumber,
526 			0,
527 			0,
528 			0
529 		},
530 		16
531 	},
532 	{OperatorRelationId,		/* OPERNAMENSP */
533 		OperatorNameNspIndexId,
534 		4,
535 		{
536 			Anum_pg_operator_oprname,
537 			Anum_pg_operator_oprleft,
538 			Anum_pg_operator_oprright,
539 			Anum_pg_operator_oprnamespace
540 		},
541 		256
542 	},
543 	{OperatorRelationId,		/* OPEROID */
544 		OperatorOidIndexId,
545 		1,
546 		{
547 			ObjectIdAttributeNumber,
548 			0,
549 			0,
550 			0
551 		},
552 		32
553 	},
554 	{OperatorFamilyRelationId,	/* OPFAMILYAMNAMENSP */
555 		OpfamilyAmNameNspIndexId,
556 		3,
557 		{
558 			Anum_pg_opfamily_opfmethod,
559 			Anum_pg_opfamily_opfname,
560 			Anum_pg_opfamily_opfnamespace,
561 			0
562 		},
563 		8
564 	},
565 	{OperatorFamilyRelationId,	/* OPFAMILYOID */
566 		OpfamilyOidIndexId,
567 		1,
568 		{
569 			ObjectIdAttributeNumber,
570 			0,
571 			0,
572 			0
573 		},
574 		8
575 	},
576 	{PartitionedRelationId,		/* PARTRELID */
577 		PartitionedRelidIndexId,
578 		1,
579 		{
580 			Anum_pg_partitioned_table_partrelid,
581 			0,
582 			0,
583 			0
584 		},
585 		32
586 	},
587 	{ProcedureRelationId,		/* PROCNAMEARGSNSP */
588 		ProcedureNameArgsNspIndexId,
589 		3,
590 		{
591 			Anum_pg_proc_proname,
592 			Anum_pg_proc_proargtypes,
593 			Anum_pg_proc_pronamespace,
594 			0
595 		},
596 		128
597 	},
598 	{ProcedureRelationId,		/* PROCOID */
599 		ProcedureOidIndexId,
600 		1,
601 		{
602 			ObjectIdAttributeNumber,
603 			0,
604 			0,
605 			0
606 		},
607 		128
608 	},
609 	{PublicationRelationId,		/* PUBLICATIONNAME */
610 		PublicationNameIndexId,
611 		1,
612 		{
613 			Anum_pg_publication_pubname,
614 			0,
615 			0,
616 			0
617 		},
618 		8
619 	},
620 	{PublicationRelationId,		/* PUBLICATIONOID */
621 		PublicationObjectIndexId,
622 		1,
623 		{
624 			ObjectIdAttributeNumber,
625 			0,
626 			0,
627 			0
628 		},
629 		8
630 	},
631 	{PublicationRelRelationId,	/* PUBLICATIONREL */
632 		PublicationRelObjectIndexId,
633 		1,
634 		{
635 			ObjectIdAttributeNumber,
636 			0,
637 			0,
638 			0
639 		},
640 		64
641 	},
642 	{PublicationRelRelationId,	/* PUBLICATIONRELMAP */
643 		PublicationRelPrrelidPrpubidIndexId,
644 		2,
645 		{
646 			Anum_pg_publication_rel_prrelid,
647 			Anum_pg_publication_rel_prpubid,
648 			0,
649 			0
650 		},
651 		64
652 	},
653 	{RangeRelationId,			/* RANGETYPE */
654 		RangeTypidIndexId,
655 		1,
656 		{
657 			Anum_pg_range_rngtypid,
658 			0,
659 			0,
660 			0
661 		},
662 		4
663 	},
664 	{RelationRelationId,		/* RELNAMENSP */
665 		ClassNameNspIndexId,
666 		2,
667 		{
668 			Anum_pg_class_relname,
669 			Anum_pg_class_relnamespace,
670 			0,
671 			0
672 		},
673 		128
674 	},
675 	{RelationRelationId,		/* RELOID */
676 		ClassOidIndexId,
677 		1,
678 		{
679 			ObjectIdAttributeNumber,
680 			0,
681 			0,
682 			0
683 		},
684 		128
685 	},
686 	{ReplicationOriginRelationId,	/* REPLORIGIDENT */
687 		ReplicationOriginIdentIndex,
688 		1,
689 		{
690 			Anum_pg_replication_origin_roident,
691 			0,
692 			0,
693 			0
694 		},
695 		16
696 	},
697 	{ReplicationOriginRelationId,	/* REPLORIGNAME */
698 		ReplicationOriginNameIndex,
699 		1,
700 		{
701 			Anum_pg_replication_origin_roname,
702 			0,
703 			0,
704 			0
705 		},
706 		16
707 	},
708 	{RewriteRelationId,			/* RULERELNAME */
709 		RewriteRelRulenameIndexId,
710 		2,
711 		{
712 			Anum_pg_rewrite_ev_class,
713 			Anum_pg_rewrite_rulename,
714 			0,
715 			0
716 		},
717 		8
718 	},
719 	{SequenceRelationId,		/* SEQRELID */
720 		SequenceRelidIndexId,
721 		1,
722 		{
723 			Anum_pg_sequence_seqrelid,
724 			0,
725 			0,
726 			0
727 		},
728 		32
729 	},
730 	{StatisticExtRelationId,	/* STATEXTNAMENSP */
731 		StatisticExtNameIndexId,
732 		2,
733 		{
734 			Anum_pg_statistic_ext_stxname,
735 			Anum_pg_statistic_ext_stxnamespace,
736 			0,
737 			0
738 		},
739 		4
740 	},
741 	{StatisticExtRelationId,	/* STATEXTOID */
742 		StatisticExtOidIndexId,
743 		1,
744 		{
745 			ObjectIdAttributeNumber,
746 			0,
747 			0,
748 			0
749 		},
750 		4
751 	},
752 	{StatisticRelationId,		/* STATRELATTINH */
753 		StatisticRelidAttnumInhIndexId,
754 		3,
755 		{
756 			Anum_pg_statistic_starelid,
757 			Anum_pg_statistic_staattnum,
758 			Anum_pg_statistic_stainherit,
759 			0
760 		},
761 		128
762 	},
763 	{SubscriptionRelationId,	/* SUBSCRIPTIONNAME */
764 		SubscriptionNameIndexId,
765 		2,
766 		{
767 			Anum_pg_subscription_subdbid,
768 			Anum_pg_subscription_subname,
769 			0,
770 			0
771 		},
772 		4
773 	},
774 	{SubscriptionRelationId,	/* SUBSCRIPTIONOID */
775 		SubscriptionObjectIndexId,
776 		1,
777 		{
778 			ObjectIdAttributeNumber,
779 			0,
780 			0,
781 			0
782 		},
783 		4
784 	},
785 	{SubscriptionRelRelationId, /* SUBSCRIPTIONRELMAP */
786 		SubscriptionRelSrrelidSrsubidIndexId,
787 		2,
788 		{
789 			Anum_pg_subscription_rel_srrelid,
790 			Anum_pg_subscription_rel_srsubid,
791 			0,
792 			0
793 		},
794 		64
795 	},
796 	{TableSpaceRelationId,		/* TABLESPACEOID */
797 		TablespaceOidIndexId,
798 		1,
799 		{
800 			ObjectIdAttributeNumber,
801 			0,
802 			0,
803 			0,
804 		},
805 		4
806 	},
807 	{TransformRelationId,		/* TRFOID */
808 		TransformOidIndexId,
809 		1,
810 		{
811 			ObjectIdAttributeNumber,
812 			0,
813 			0,
814 			0,
815 		},
816 		16
817 	},
818 	{TransformRelationId,		/* TRFTYPELANG */
819 		TransformTypeLangIndexId,
820 		2,
821 		{
822 			Anum_pg_transform_trftype,
823 			Anum_pg_transform_trflang,
824 			0,
825 			0,
826 		},
827 		16
828 	},
829 	{TSConfigMapRelationId,		/* TSCONFIGMAP */
830 		TSConfigMapIndexId,
831 		3,
832 		{
833 			Anum_pg_ts_config_map_mapcfg,
834 			Anum_pg_ts_config_map_maptokentype,
835 			Anum_pg_ts_config_map_mapseqno,
836 			0
837 		},
838 		2
839 	},
840 	{TSConfigRelationId,		/* TSCONFIGNAMENSP */
841 		TSConfigNameNspIndexId,
842 		2,
843 		{
844 			Anum_pg_ts_config_cfgname,
845 			Anum_pg_ts_config_cfgnamespace,
846 			0,
847 			0
848 		},
849 		2
850 	},
851 	{TSConfigRelationId,		/* TSCONFIGOID */
852 		TSConfigOidIndexId,
853 		1,
854 		{
855 			ObjectIdAttributeNumber,
856 			0,
857 			0,
858 			0
859 		},
860 		2
861 	},
862 	{TSDictionaryRelationId,	/* TSDICTNAMENSP */
863 		TSDictionaryNameNspIndexId,
864 		2,
865 		{
866 			Anum_pg_ts_dict_dictname,
867 			Anum_pg_ts_dict_dictnamespace,
868 			0,
869 			0
870 		},
871 		2
872 	},
873 	{TSDictionaryRelationId,	/* TSDICTOID */
874 		TSDictionaryOidIndexId,
875 		1,
876 		{
877 			ObjectIdAttributeNumber,
878 			0,
879 			0,
880 			0
881 		},
882 		2
883 	},
884 	{TSParserRelationId,		/* TSPARSERNAMENSP */
885 		TSParserNameNspIndexId,
886 		2,
887 		{
888 			Anum_pg_ts_parser_prsname,
889 			Anum_pg_ts_parser_prsnamespace,
890 			0,
891 			0
892 		},
893 		2
894 	},
895 	{TSParserRelationId,		/* TSPARSEROID */
896 		TSParserOidIndexId,
897 		1,
898 		{
899 			ObjectIdAttributeNumber,
900 			0,
901 			0,
902 			0
903 		},
904 		2
905 	},
906 	{TSTemplateRelationId,		/* TSTEMPLATENAMENSP */
907 		TSTemplateNameNspIndexId,
908 		2,
909 		{
910 			Anum_pg_ts_template_tmplname,
911 			Anum_pg_ts_template_tmplnamespace,
912 			0,
913 			0
914 		},
915 		2
916 	},
917 	{TSTemplateRelationId,		/* TSTEMPLATEOID */
918 		TSTemplateOidIndexId,
919 		1,
920 		{
921 			ObjectIdAttributeNumber,
922 			0,
923 			0,
924 			0
925 		},
926 		2
927 	},
928 	{TypeRelationId,			/* TYPENAMENSP */
929 		TypeNameNspIndexId,
930 		2,
931 		{
932 			Anum_pg_type_typname,
933 			Anum_pg_type_typnamespace,
934 			0,
935 			0
936 		},
937 		64
938 	},
939 	{TypeRelationId,			/* TYPEOID */
940 		TypeOidIndexId,
941 		1,
942 		{
943 			ObjectIdAttributeNumber,
944 			0,
945 			0,
946 			0
947 		},
948 		64
949 	},
950 	{UserMappingRelationId,		/* USERMAPPINGOID */
951 		UserMappingOidIndexId,
952 		1,
953 		{
954 			ObjectIdAttributeNumber,
955 			0,
956 			0,
957 			0
958 		},
959 		2
960 	},
961 	{UserMappingRelationId,		/* USERMAPPINGUSERSERVER */
962 		UserMappingUserServerIndexId,
963 		2,
964 		{
965 			Anum_pg_user_mapping_umuser,
966 			Anum_pg_user_mapping_umserver,
967 			0,
968 			0
969 		},
970 		2
971 	}
972 };
973 
974 static CatCache *SysCache[SysCacheSize];
975 
976 static bool CacheInitialized = false;
977 
978 /* Sorted array of OIDs of tables that have caches on them */
979 static Oid	SysCacheRelationOid[SysCacheSize];
980 static int	SysCacheRelationOidSize;
981 
982 /* Sorted array of OIDs of tables and indexes used by caches */
983 static Oid	SysCacheSupportingRelOid[SysCacheSize * 2];
984 static int	SysCacheSupportingRelOidSize;
985 
986 static int	oid_compare(const void *a, const void *b);
987 
988 
989 /*
990  * InitCatalogCache - initialize the caches
991  *
992  * Note that no database access is done here; we only allocate memory
993  * and initialize the cache structure.  Interrogation of the database
994  * to complete initialization of a cache happens upon first use
995  * of that cache.
996  */
997 void
InitCatalogCache(void)998 InitCatalogCache(void)
999 {
1000 	int			cacheId;
1001 	int			i,
1002 				j;
1003 
1004 	StaticAssertStmt(SysCacheSize == (int) lengthof(cacheinfo),
1005 					 "SysCacheSize does not match syscache.c's array");
1006 
1007 	Assert(!CacheInitialized);
1008 
1009 	SysCacheRelationOidSize = SysCacheSupportingRelOidSize = 0;
1010 
1011 	for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
1012 	{
1013 		SysCache[cacheId] = InitCatCache(cacheId,
1014 										 cacheinfo[cacheId].reloid,
1015 										 cacheinfo[cacheId].indoid,
1016 										 cacheinfo[cacheId].nkeys,
1017 										 cacheinfo[cacheId].key,
1018 										 cacheinfo[cacheId].nbuckets);
1019 		if (!PointerIsValid(SysCache[cacheId]))
1020 			elog(ERROR, "could not initialize cache %u (%d)",
1021 				 cacheinfo[cacheId].reloid, cacheId);
1022 		/* Accumulate data for OID lists, too */
1023 		SysCacheRelationOid[SysCacheRelationOidSize++] =
1024 			cacheinfo[cacheId].reloid;
1025 		SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
1026 			cacheinfo[cacheId].reloid;
1027 		SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
1028 			cacheinfo[cacheId].indoid;
1029 		/* see comments for RelationInvalidatesSnapshotsOnly */
1030 		Assert(!RelationInvalidatesSnapshotsOnly(cacheinfo[cacheId].reloid));
1031 	}
1032 
1033 	Assert(SysCacheRelationOidSize <= lengthof(SysCacheRelationOid));
1034 	Assert(SysCacheSupportingRelOidSize <= lengthof(SysCacheSupportingRelOid));
1035 
1036 	/* Sort and de-dup OID arrays, so we can use binary search. */
1037 	pg_qsort(SysCacheRelationOid, SysCacheRelationOidSize,
1038 			 sizeof(Oid), oid_compare);
1039 	for (i = 1, j = 0; i < SysCacheRelationOidSize; i++)
1040 	{
1041 		if (SysCacheRelationOid[i] != SysCacheRelationOid[j])
1042 			SysCacheRelationOid[++j] = SysCacheRelationOid[i];
1043 	}
1044 	SysCacheRelationOidSize = j + 1;
1045 
1046 	pg_qsort(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
1047 			 sizeof(Oid), oid_compare);
1048 	for (i = 1, j = 0; i < SysCacheSupportingRelOidSize; i++)
1049 	{
1050 		if (SysCacheSupportingRelOid[i] != SysCacheSupportingRelOid[j])
1051 			SysCacheSupportingRelOid[++j] = SysCacheSupportingRelOid[i];
1052 	}
1053 	SysCacheSupportingRelOidSize = j + 1;
1054 
1055 	CacheInitialized = true;
1056 }
1057 
1058 /*
1059  * InitCatalogCachePhase2 - finish initializing the caches
1060  *
1061  * Finish initializing all the caches, including necessary database
1062  * access.
1063  *
1064  * This is *not* essential; normally we allow syscaches to be initialized
1065  * on first use.  However, it is useful as a mechanism to preload the
1066  * relcache with entries for the most-commonly-used system catalogs.
1067  * Therefore, we invoke this routine when we need to write a new relcache
1068  * init file.
1069  */
1070 void
InitCatalogCachePhase2(void)1071 InitCatalogCachePhase2(void)
1072 {
1073 	int			cacheId;
1074 
1075 	Assert(CacheInitialized);
1076 
1077 	for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
1078 		InitCatCachePhase2(SysCache[cacheId], true);
1079 }
1080 
1081 
1082 /*
1083  * SearchSysCache
1084  *
1085  *	A layer on top of SearchCatCache that does the initialization and
1086  *	key-setting for you.
1087  *
1088  *	Returns the cache copy of the tuple if one is found, NULL if not.
1089  *	The tuple is the 'cache' copy and must NOT be modified!
1090  *
1091  *	When the caller is done using the tuple, call ReleaseSysCache()
1092  *	to release the reference count grabbed by SearchSysCache().  If this
1093  *	is not done, the tuple will remain locked in cache until end of
1094  *	transaction, which is tolerable but not desirable.
1095  *
1096  *	CAUTION: The tuple that is returned must NOT be freed by the caller!
1097  */
1098 HeapTuple
SearchSysCache(int cacheId,Datum key1,Datum key2,Datum key3,Datum key4)1099 SearchSysCache(int cacheId,
1100 			   Datum key1,
1101 			   Datum key2,
1102 			   Datum key3,
1103 			   Datum key4)
1104 {
1105 	Assert(cacheId >= 0 && cacheId < SysCacheSize &&
1106 		   PointerIsValid(SysCache[cacheId]));
1107 
1108 	return SearchCatCache(SysCache[cacheId], key1, key2, key3, key4);
1109 }
1110 
1111 HeapTuple
SearchSysCache1(int cacheId,Datum key1)1112 SearchSysCache1(int cacheId,
1113 				Datum key1)
1114 {
1115 	Assert(cacheId >= 0 && cacheId < SysCacheSize &&
1116 		   PointerIsValid(SysCache[cacheId]));
1117 	Assert(SysCache[cacheId]->cc_nkeys == 1);
1118 
1119 	return SearchCatCache1(SysCache[cacheId], key1);
1120 }
1121 
1122 HeapTuple
SearchSysCache2(int cacheId,Datum key1,Datum key2)1123 SearchSysCache2(int cacheId,
1124 				Datum key1, Datum key2)
1125 {
1126 	Assert(cacheId >= 0 && cacheId < SysCacheSize &&
1127 		   PointerIsValid(SysCache[cacheId]));
1128 	Assert(SysCache[cacheId]->cc_nkeys == 2);
1129 
1130 	return SearchCatCache2(SysCache[cacheId], key1, key2);
1131 }
1132 
1133 HeapTuple
SearchSysCache3(int cacheId,Datum key1,Datum key2,Datum key3)1134 SearchSysCache3(int cacheId,
1135 				Datum key1, Datum key2, Datum key3)
1136 {
1137 	Assert(cacheId >= 0 && cacheId < SysCacheSize &&
1138 		   PointerIsValid(SysCache[cacheId]));
1139 	Assert(SysCache[cacheId]->cc_nkeys == 3);
1140 
1141 	return SearchCatCache3(SysCache[cacheId], key1, key2, key3);
1142 }
1143 
1144 HeapTuple
SearchSysCache4(int cacheId,Datum key1,Datum key2,Datum key3,Datum key4)1145 SearchSysCache4(int cacheId,
1146 				Datum key1, Datum key2, Datum key3, Datum key4)
1147 {
1148 	Assert(cacheId >= 0 && cacheId < SysCacheSize &&
1149 		   PointerIsValid(SysCache[cacheId]));
1150 	Assert(SysCache[cacheId]->cc_nkeys == 4);
1151 
1152 	return SearchCatCache4(SysCache[cacheId], key1, key2, key3, key4);
1153 }
1154 
1155 /*
1156  * ReleaseSysCache
1157  *		Release previously grabbed reference count on a tuple
1158  */
1159 void
ReleaseSysCache(HeapTuple tuple)1160 ReleaseSysCache(HeapTuple tuple)
1161 {
1162 	ReleaseCatCache(tuple);
1163 }
1164 
1165 /*
1166  * SearchSysCacheCopy
1167  *
1168  * A convenience routine that does SearchSysCache and (if successful)
1169  * returns a modifiable copy of the syscache entry.  The original
1170  * syscache entry is released before returning.  The caller should
1171  * heap_freetuple() the result when done with it.
1172  */
1173 HeapTuple
SearchSysCacheCopy(int cacheId,Datum key1,Datum key2,Datum key3,Datum key4)1174 SearchSysCacheCopy(int cacheId,
1175 				   Datum key1,
1176 				   Datum key2,
1177 				   Datum key3,
1178 				   Datum key4)
1179 {
1180 	HeapTuple	tuple,
1181 				newtuple;
1182 
1183 	tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
1184 	if (!HeapTupleIsValid(tuple))
1185 		return tuple;
1186 	newtuple = heap_copytuple(tuple);
1187 	ReleaseSysCache(tuple);
1188 	return newtuple;
1189 }
1190 
1191 /*
1192  * SearchSysCacheExists
1193  *
1194  * A convenience routine that just probes to see if a tuple can be found.
1195  * No lock is retained on the syscache entry.
1196  */
1197 bool
SearchSysCacheExists(int cacheId,Datum key1,Datum key2,Datum key3,Datum key4)1198 SearchSysCacheExists(int cacheId,
1199 					 Datum key1,
1200 					 Datum key2,
1201 					 Datum key3,
1202 					 Datum key4)
1203 {
1204 	HeapTuple	tuple;
1205 
1206 	tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
1207 	if (!HeapTupleIsValid(tuple))
1208 		return false;
1209 	ReleaseSysCache(tuple);
1210 	return true;
1211 }
1212 
1213 /*
1214  * GetSysCacheOid
1215  *
1216  * A convenience routine that does SearchSysCache and returns the OID
1217  * of the found tuple, or InvalidOid if no tuple could be found.
1218  * No lock is retained on the syscache entry.
1219  */
1220 Oid
GetSysCacheOid(int cacheId,Datum key1,Datum key2,Datum key3,Datum key4)1221 GetSysCacheOid(int cacheId,
1222 			   Datum key1,
1223 			   Datum key2,
1224 			   Datum key3,
1225 			   Datum key4)
1226 {
1227 	HeapTuple	tuple;
1228 	Oid			result;
1229 
1230 	tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
1231 	if (!HeapTupleIsValid(tuple))
1232 		return InvalidOid;
1233 	result = HeapTupleGetOid(tuple);
1234 	ReleaseSysCache(tuple);
1235 	return result;
1236 }
1237 
1238 
1239 /*
1240  * SearchSysCacheAttName
1241  *
1242  * This routine is equivalent to SearchSysCache on the ATTNAME cache,
1243  * except that it will return NULL if the found attribute is marked
1244  * attisdropped.  This is convenient for callers that want to act as
1245  * though dropped attributes don't exist.
1246  */
1247 HeapTuple
SearchSysCacheAttName(Oid relid,const char * attname)1248 SearchSysCacheAttName(Oid relid, const char *attname)
1249 {
1250 	HeapTuple	tuple;
1251 
1252 	tuple = SearchSysCache2(ATTNAME,
1253 							ObjectIdGetDatum(relid),
1254 							CStringGetDatum(attname));
1255 	if (!HeapTupleIsValid(tuple))
1256 		return NULL;
1257 	if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
1258 	{
1259 		ReleaseSysCache(tuple);
1260 		return NULL;
1261 	}
1262 	return tuple;
1263 }
1264 
1265 /*
1266  * SearchSysCacheCopyAttName
1267  *
1268  * As above, an attisdropped-aware version of SearchSysCacheCopy.
1269  */
1270 HeapTuple
SearchSysCacheCopyAttName(Oid relid,const char * attname)1271 SearchSysCacheCopyAttName(Oid relid, const char *attname)
1272 {
1273 	HeapTuple	tuple,
1274 				newtuple;
1275 
1276 	tuple = SearchSysCacheAttName(relid, attname);
1277 	if (!HeapTupleIsValid(tuple))
1278 		return tuple;
1279 	newtuple = heap_copytuple(tuple);
1280 	ReleaseSysCache(tuple);
1281 	return newtuple;
1282 }
1283 
1284 /*
1285  * SearchSysCacheExistsAttName
1286  *
1287  * As above, an attisdropped-aware version of SearchSysCacheExists.
1288  */
1289 bool
SearchSysCacheExistsAttName(Oid relid,const char * attname)1290 SearchSysCacheExistsAttName(Oid relid, const char *attname)
1291 {
1292 	HeapTuple	tuple;
1293 
1294 	tuple = SearchSysCacheAttName(relid, attname);
1295 	if (!HeapTupleIsValid(tuple))
1296 		return false;
1297 	ReleaseSysCache(tuple);
1298 	return true;
1299 }
1300 
1301 
1302 /*
1303  * SearchSysCacheAttNum
1304  *
1305  * This routine is equivalent to SearchSysCache on the ATTNUM cache,
1306  * except that it will return NULL if the found attribute is marked
1307  * attisdropped.  This is convenient for callers that want to act as
1308  * though dropped attributes don't exist.
1309  */
1310 HeapTuple
SearchSysCacheAttNum(Oid relid,int16 attnum)1311 SearchSysCacheAttNum(Oid relid, int16 attnum)
1312 {
1313 	HeapTuple	tuple;
1314 
1315 	tuple = SearchSysCache2(ATTNUM,
1316 							ObjectIdGetDatum(relid),
1317 							Int16GetDatum(attnum));
1318 	if (!HeapTupleIsValid(tuple))
1319 		return NULL;
1320 	if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
1321 	{
1322 		ReleaseSysCache(tuple);
1323 		return NULL;
1324 	}
1325 	return tuple;
1326 }
1327 
1328 /*
1329  * SearchSysCacheCopyAttNum
1330  *
1331  * As above, an attisdropped-aware version of SearchSysCacheCopy.
1332  */
1333 HeapTuple
SearchSysCacheCopyAttNum(Oid relid,int16 attnum)1334 SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
1335 {
1336 	HeapTuple	tuple,
1337 				newtuple;
1338 
1339 	tuple = SearchSysCacheAttNum(relid, attnum);
1340 	if (!HeapTupleIsValid(tuple))
1341 		return NULL;
1342 	newtuple = heap_copytuple(tuple);
1343 	ReleaseSysCache(tuple);
1344 	return newtuple;
1345 }
1346 
1347 
1348 /*
1349  * SysCacheGetAttr
1350  *
1351  *		Given a tuple previously fetched by SearchSysCache(),
1352  *		extract a specific attribute.
1353  *
1354  * This is equivalent to using heap_getattr() on a tuple fetched
1355  * from a non-cached relation.  Usually, this is only used for attributes
1356  * that could be NULL or variable length; the fixed-size attributes in
1357  * a system table are accessed just by mapping the tuple onto the C struct
1358  * declarations from include/catalog/.
1359  *
1360  * As with heap_getattr(), if the attribute is of a pass-by-reference type
1361  * then a pointer into the tuple data area is returned --- the caller must
1362  * not modify or pfree the datum!
1363  *
1364  * Note: it is legal to use SysCacheGetAttr() with a cacheId referencing
1365  * a different cache for the same catalog the tuple was fetched from.
1366  */
1367 Datum
SysCacheGetAttr(int cacheId,HeapTuple tup,AttrNumber attributeNumber,bool * isNull)1368 SysCacheGetAttr(int cacheId, HeapTuple tup,
1369 				AttrNumber attributeNumber,
1370 				bool *isNull)
1371 {
1372 	/*
1373 	 * We just need to get the TupleDesc out of the cache entry, and then we
1374 	 * can apply heap_getattr().  Normally the cache control data is already
1375 	 * valid (because the caller recently fetched the tuple via this same
1376 	 * cache), but there are cases where we have to initialize the cache here.
1377 	 */
1378 	if (cacheId < 0 || cacheId >= SysCacheSize ||
1379 		!PointerIsValid(SysCache[cacheId]))
1380 		elog(ERROR, "invalid cache ID: %d", cacheId);
1381 	if (!PointerIsValid(SysCache[cacheId]->cc_tupdesc))
1382 	{
1383 		InitCatCachePhase2(SysCache[cacheId], false);
1384 		Assert(PointerIsValid(SysCache[cacheId]->cc_tupdesc));
1385 	}
1386 
1387 	return heap_getattr(tup, attributeNumber,
1388 						SysCache[cacheId]->cc_tupdesc,
1389 						isNull);
1390 }
1391 
1392 /*
1393  * GetSysCacheHashValue
1394  *
1395  * Get the hash value that would be used for a tuple in the specified cache
1396  * with the given search keys.
1397  *
1398  * The reason for exposing this as part of the API is that the hash value is
1399  * exposed in cache invalidation operations, so there are places outside the
1400  * catcache code that need to be able to compute the hash values.
1401  */
1402 uint32
GetSysCacheHashValue(int cacheId,Datum key1,Datum key2,Datum key3,Datum key4)1403 GetSysCacheHashValue(int cacheId,
1404 					 Datum key1,
1405 					 Datum key2,
1406 					 Datum key3,
1407 					 Datum key4)
1408 {
1409 	if (cacheId < 0 || cacheId >= SysCacheSize ||
1410 		!PointerIsValid(SysCache[cacheId]))
1411 		elog(ERROR, "invalid cache ID: %d", cacheId);
1412 
1413 	return GetCatCacheHashValue(SysCache[cacheId], key1, key2, key3, key4);
1414 }
1415 
1416 /*
1417  * List-search interface
1418  */
1419 struct catclist *
SearchSysCacheList(int cacheId,int nkeys,Datum key1,Datum key2,Datum key3)1420 SearchSysCacheList(int cacheId, int nkeys,
1421 				   Datum key1, Datum key2, Datum key3)
1422 {
1423 	if (cacheId < 0 || cacheId >= SysCacheSize ||
1424 		!PointerIsValid(SysCache[cacheId]))
1425 		elog(ERROR, "invalid cache ID: %d", cacheId);
1426 
1427 	return SearchCatCacheList(SysCache[cacheId], nkeys,
1428 							  key1, key2, key3);
1429 }
1430 
1431 /*
1432  * SysCacheInvalidate
1433  *
1434  *	Invalidate entries in the specified cache, given a hash value.
1435  *	See CatCacheInvalidate() for more info.
1436  *
1437  *	This routine is only quasi-public: it should only be used by inval.c.
1438  */
1439 void
SysCacheInvalidate(int cacheId,uint32 hashValue)1440 SysCacheInvalidate(int cacheId, uint32 hashValue)
1441 {
1442 	if (cacheId < 0 || cacheId >= SysCacheSize)
1443 		elog(ERROR, "invalid cache ID: %d", cacheId);
1444 
1445 	/* if this cache isn't initialized yet, no need to do anything */
1446 	if (!PointerIsValid(SysCache[cacheId]))
1447 		return;
1448 
1449 	CatCacheInvalidate(SysCache[cacheId], hashValue);
1450 }
1451 
1452 /*
1453  * Certain relations that do not have system caches send snapshot invalidation
1454  * messages in lieu of catcache messages.  This is for the benefit of
1455  * GetCatalogSnapshot(), which can then reuse its existing MVCC snapshot
1456  * for scanning one of those catalogs, rather than taking a new one, if no
1457  * invalidation has been received.
1458  *
1459  * Relations that have syscaches need not (and must not) be listed here.  The
1460  * catcache invalidation messages will also flush the snapshot.  If you add a
1461  * syscache for one of these relations, remove it from this list.
1462  */
1463 bool
RelationInvalidatesSnapshotsOnly(Oid relid)1464 RelationInvalidatesSnapshotsOnly(Oid relid)
1465 {
1466 	switch (relid)
1467 	{
1468 		case DbRoleSettingRelationId:
1469 		case DependRelationId:
1470 		case SharedDependRelationId:
1471 		case DescriptionRelationId:
1472 		case SharedDescriptionRelationId:
1473 		case SecLabelRelationId:
1474 		case SharedSecLabelRelationId:
1475 			return true;
1476 		default:
1477 			break;
1478 	}
1479 
1480 	return false;
1481 }
1482 
1483 /*
1484  * Test whether a relation has a system cache.
1485  */
1486 bool
RelationHasSysCache(Oid relid)1487 RelationHasSysCache(Oid relid)
1488 {
1489 	int			low = 0,
1490 				high = SysCacheRelationOidSize - 1;
1491 
1492 	while (low <= high)
1493 	{
1494 		int			middle = low + (high - low) / 2;
1495 
1496 		if (SysCacheRelationOid[middle] == relid)
1497 			return true;
1498 		if (SysCacheRelationOid[middle] < relid)
1499 			low = middle + 1;
1500 		else
1501 			high = middle - 1;
1502 	}
1503 
1504 	return false;
1505 }
1506 
1507 /*
1508  * Test whether a relation supports a system cache, ie it is either a
1509  * cached table or the index used for a cache.
1510  */
1511 bool
RelationSupportsSysCache(Oid relid)1512 RelationSupportsSysCache(Oid relid)
1513 {
1514 	int			low = 0,
1515 				high = SysCacheSupportingRelOidSize - 1;
1516 
1517 	while (low <= high)
1518 	{
1519 		int			middle = low + (high - low) / 2;
1520 
1521 		if (SysCacheSupportingRelOid[middle] == relid)
1522 			return true;
1523 		if (SysCacheSupportingRelOid[middle] < relid)
1524 			low = middle + 1;
1525 		else
1526 			high = middle - 1;
1527 	}
1528 
1529 	return false;
1530 }
1531 
1532 
1533 /*
1534  * OID comparator for pg_qsort
1535  */
1536 static int
oid_compare(const void * a,const void * b)1537 oid_compare(const void *a, const void *b)
1538 {
1539 	Oid			oa = *((const Oid *) a);
1540 	Oid			ob = *((const Oid *) b);
1541 
1542 	if (oa == ob)
1543 		return 0;
1544 	return (oa > ob) ? 1 : -1;
1545 }
1546