PostgreSQL Source Code git master
Loading...
Searching...
No Matches
typcache.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * typcache.c
4 * POSTGRES type cache code
5 *
6 * The type cache exists to speed lookup of certain information about data
7 * types that is not directly available from a type's pg_type row. For
8 * example, we use a type's default btree opclass, or the default hash
9 * opclass if no btree opclass exists, to determine which operators should
10 * be used for grouping and sorting the type (GROUP BY, ORDER BY ASC/DESC).
11 *
12 * Several seemingly-odd choices have been made to support use of the type
13 * cache by generic array and record handling routines, such as array_eq(),
14 * record_cmp(), and hash_array(). Because those routines are used as index
15 * support operations, they cannot leak memory. To allow them to execute
16 * efficiently, all information that they would like to re-use across calls
17 * is kept in the type cache.
18 *
19 * Once created, a type cache entry lives as long as the backend does, so
20 * there is no need for a call to release a cache entry. If the type is
21 * dropped, the cache entry simply becomes wasted storage. This is not
22 * expected to happen often, and assuming that typcache entries are good
23 * permanently allows caching pointers to them in long-lived places.
24 *
25 * We have some provisions for updating cache entries if the stored data
26 * becomes obsolete. Core data extracted from the pg_type row is updated
27 * when we detect updates to pg_type. Information dependent on opclasses is
28 * cleared if we detect updates to pg_opclass. We also support clearing the
29 * tuple descriptor and operator/function parts of a rowtype's cache entry,
30 * since those may need to change as a consequence of ALTER TABLE. Domain
31 * constraint changes are also tracked properly.
32 *
33 *
34 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
35 * Portions Copyright (c) 1994, Regents of the University of California
36 *
37 * IDENTIFICATION
38 * src/backend/utils/cache/typcache.c
39 *
40 *-------------------------------------------------------------------------
41 */
42#include "postgres.h"
43
44#include <limits.h>
45
46#include "access/hash.h"
47#include "access/htup_details.h"
48#include "access/nbtree.h"
49#include "access/parallel.h"
50#include "access/relation.h"
51#include "access/session.h"
52#include "access/table.h"
53#include "catalog/pg_am.h"
55#include "catalog/pg_enum.h"
56#include "catalog/pg_operator.h"
57#include "catalog/pg_range.h"
58#include "catalog/pg_type.h"
59#include "commands/defrem.h"
60#include "common/int.h"
61#include "executor/executor.h"
62#include "lib/dshash.h"
63#include "optimizer/optimizer.h"
64#include "port/pg_bitutils.h"
65#include "storage/lwlock.h"
66#include "utils/builtins.h"
67#include "utils/catcache.h"
68#include "utils/fmgroids.h"
70#include "utils/inval.h"
71#include "utils/lsyscache.h"
72#include "utils/memutils.h"
73#include "utils/rel.h"
74#include "utils/syscache.h"
75#include "utils/typcache.h"
76
77
78/* The main type cache hashtable searched by lookup_type_cache */
80
81/*
82 * The mapping of relation's OID to the corresponding composite type OID.
83 * We're keeping the map entry when the corresponding typentry has something
84 * to clear i.e it has either TCFLAGS_HAVE_PG_TYPE_DATA, or
85 * TCFLAGS_OPERATOR_FLAGS, or tupdesc.
86 */
88
90{
91 Oid relid; /* OID of the relation */
92 Oid composite_typid; /* OID of the relation's composite type */
94
95/* List of type cache entries for domain types */
97
98/* Private flag bits in the TypeCacheEntry.flags field */
99#define TCFLAGS_HAVE_PG_TYPE_DATA 0x000001
100#define TCFLAGS_CHECKED_BTREE_OPCLASS 0x000002
101#define TCFLAGS_CHECKED_HASH_OPCLASS 0x000004
102#define TCFLAGS_CHECKED_EQ_OPR 0x000008
103#define TCFLAGS_CHECKED_LT_OPR 0x000010
104#define TCFLAGS_CHECKED_GT_OPR 0x000020
105#define TCFLAGS_CHECKED_CMP_PROC 0x000040
106#define TCFLAGS_CHECKED_HASH_PROC 0x000080
107#define TCFLAGS_CHECKED_HASH_EXTENDED_PROC 0x000100
108#define TCFLAGS_CHECKED_ELEM_PROPERTIES 0x000200
109#define TCFLAGS_HAVE_ELEM_EQUALITY 0x000400
110#define TCFLAGS_HAVE_ELEM_COMPARE 0x000800
111#define TCFLAGS_HAVE_ELEM_HASHING 0x001000
112#define TCFLAGS_HAVE_ELEM_EXTENDED_HASHING 0x002000
113#define TCFLAGS_CHECKED_FIELD_PROPERTIES 0x004000
114#define TCFLAGS_HAVE_FIELD_EQUALITY 0x008000
115#define TCFLAGS_HAVE_FIELD_COMPARE 0x010000
116#define TCFLAGS_HAVE_FIELD_HASHING 0x020000
117#define TCFLAGS_HAVE_FIELD_EXTENDED_HASHING 0x040000
118#define TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS 0x080000
119#define TCFLAGS_DOMAIN_BASE_IS_COMPOSITE 0x100000
120
121/* The flags associated with equality/comparison/hashing are all but these: */
122#define TCFLAGS_OPERATOR_FLAGS \
123 (~(TCFLAGS_HAVE_PG_TYPE_DATA | \
124 TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS | \
125 TCFLAGS_DOMAIN_BASE_IS_COMPOSITE))
126
127/*
128 * Data stored about a domain type's constraints. Note that we do not create
129 * this struct for the common case of a constraint-less domain; we just set
130 * domainData to NULL to indicate that.
131 *
132 * Within a DomainConstraintCache, we store expression plan trees, but the
133 * check_exprstate fields of the DomainConstraintState nodes are just NULL.
134 * When needed, expression evaluation nodes are built by flat-copying the
135 * DomainConstraintState nodes and applying ExecInitExpr to check_expr.
136 * Such a node tree is not part of the DomainConstraintCache, but is
137 * considered to belong to a DomainConstraintRef.
138 */
140{
141 List *constraints; /* list of DomainConstraintState nodes */
142 MemoryContext dccContext; /* memory context holding all associated data */
143 long dccRefCount; /* number of references to this struct */
144};
145
146/* Private information to support comparisons of enum values */
147typedef struct
148{
149 Oid enum_oid; /* OID of one enum value */
150 float4 sort_order; /* its sort position */
151} EnumItem;
152
153typedef struct TypeCacheEnumData
154{
155 Oid bitmap_base; /* OID corresponding to bit 0 of bitmapset */
156 Bitmapset *sorted_values; /* Set of OIDs known to be in order */
157 int num_values; /* total number of values in enum */
160
161/*
162 * We use a separate table for storing the definitions of non-anonymous
163 * record types. Once defined, a record type will be remembered for the
164 * life of the backend. Subsequent uses of the "same" record type (where
165 * sameness means equalRowTypes) will refer to the existing table entry.
166 *
167 * Stored record types are remembered in a linear array of TupleDescs,
168 * which can be indexed quickly with the assigned typmod. There is also
169 * a hash table to speed searches for matching TupleDescs.
170 */
171
176
177/*
178 * To deal with non-anonymous record types that are exchanged by backends
179 * involved in a parallel query, we also need a shared version of the above.
180 */
182{
183 /* A hash table for finding a matching TupleDesc. */
185 /* A hash table for finding a TupleDesc by typmod. */
187 /* A source of new record typmod numbers. */
189};
190
191/*
192 * When using shared tuple descriptors as hash table keys we need a way to be
193 * able to search for an equal shared TupleDesc using a backend-local
194 * TupleDesc. So we use this type which can hold either, and hash and compare
195 * functions that know how to handle both.
196 */
206
207/*
208 * The shared version of RecordCacheEntry. This lets us look up a typmod
209 * using a TupleDesc which may be in local or shared memory.
210 */
215
216/*
217 * An entry in SharedRecordTypmodRegistry's typmod table. This lets us look
218 * up a TupleDesc in shared memory using a typmod.
219 */
225
229
230/*
231 * A comparator function for SharedRecordTableKey.
232 */
233static int
234shared_record_table_compare(const void *a, const void *b, size_t size,
235 void *arg)
236{
237 dsa_area *area = (dsa_area *) arg;
238 const SharedRecordTableKey *k1 = a;
239 const SharedRecordTableKey *k2 = b;
242
243 if (k1->shared)
244 t1 = (TupleDesc) dsa_get_address(area, k1->u.shared_tupdesc);
245 else
246 t1 = k1->u.local_tupdesc;
247
248 if (k2->shared)
249 t2 = (TupleDesc) dsa_get_address(area, k2->u.shared_tupdesc);
250 else
251 t2 = k2->u.local_tupdesc;
252
253 return equalRowTypes(t1, t2) ? 0 : 1;
254}
255
256/*
257 * A hash function for SharedRecordTableKey.
258 */
259static uint32
260shared_record_table_hash(const void *a, size_t size, void *arg)
261{
262 dsa_area *area = arg;
263 const SharedRecordTableKey *k = a;
264 TupleDesc t;
265
266 if (k->shared)
268 else
269 t = k->u.local_tupdesc;
270
271 return hashRowType(t);
272}
273
274/* Parameters for SharedRecordTypmodRegistry's TupleDesc table. */
283
284/* Parameters for SharedRecordTypmodRegistry's typmod hash table. */
293
294/* hashtable for recognizing registered record types */
296
302
303/* array of info about registered record types, indexed by assigned typmod */
305static int32 RecordCacheArrayLen = 0; /* allocated length of above array */
306static int32 NextRecordTypmod = 0; /* number of entries used */
307
308/*
309 * Process-wide counter for generating unique tupledesc identifiers.
310 * Zero and one (INVALID_TUPLEDESC_IDENTIFIER) aren't allowed to be chosen
311 * as identifiers, so we start the counter at INVALID_TUPLEDESC_IDENTIFIER.
312 */
314
315static void load_typcache_tupdesc(TypeCacheEntry *typentry);
316static void load_rangetype_info(TypeCacheEntry *typentry);
317static void load_multirangetype_info(TypeCacheEntry *typentry);
318static void load_domaintype_info(TypeCacheEntry *typentry);
319static int dcs_cmp(const void *a, const void *b);
321static void dccref_deletion_callback(void *arg);
323static bool array_element_has_equality(TypeCacheEntry *typentry);
324static bool array_element_has_compare(TypeCacheEntry *typentry);
325static bool array_element_has_hashing(TypeCacheEntry *typentry);
328static bool record_fields_have_equality(TypeCacheEntry *typentry);
329static bool record_fields_have_compare(TypeCacheEntry *typentry);
330static bool record_fields_have_hashing(TypeCacheEntry *typentry);
332static void cache_record_field_properties(TypeCacheEntry *typentry);
333static bool range_element_has_hashing(TypeCacheEntry *typentry);
339static void TypeCacheRelCallback(Datum arg, Oid relid);
341 uint32 hashvalue);
343 uint32 hashvalue);
345 uint32 hashvalue);
346static void load_enum_cache_data(TypeCacheEntry *tcache);
348static int enum_oid_cmp(const void *left, const void *right);
350 Datum datum);
352static dsa_pointer share_tupledesc(dsa_area *area, TupleDesc tupdesc,
353 uint32 typmod);
356
357
358/*
359 * Hash function compatible with one-arg system cache hash function.
360 */
361static uint32
362type_cache_syshash(const void *key, Size keysize)
363{
364 Assert(keysize == sizeof(Oid));
365 return GetSysCacheHashValue1(TYPEOID, ObjectIdGetDatum(*(const Oid *) key));
366}
367
368/*
369 * lookup_type_cache
370 *
371 * Fetch the type cache entry for the specified datatype, and make sure that
372 * all the fields requested by bits in 'flags' are valid.
373 *
374 * The result is never NULL --- we will ereport() if the passed type OID is
375 * invalid. Note however that we may fail to find one or more of the
376 * values requested by 'flags'; the caller needs to check whether the fields
377 * are InvalidOid or not.
378 *
379 * Note that while filling TypeCacheEntry we might process concurrent
380 * invalidation messages, causing our not-yet-filled TypeCacheEntry to be
381 * invalidated. In this case, we typically only clear flags while values are
382 * still available for the caller. It's expected that the caller holds
383 * enough locks on type-depending objects that the values are still relevant.
384 * It's also important that the tupdesc is filled after all other
385 * TypeCacheEntry items for TYPTYPE_COMPOSITE. So, tupdesc can't get
386 * invalidated during the lookup_type_cache() call.
387 */
389lookup_type_cache(Oid type_id, int flags)
390{
391 TypeCacheEntry *typentry;
392 bool found;
394
395 if (in_progress_list == NULL)
396 {
397 /* First time through: initialize the hash table */
398 HASHCTL ctl;
399 int allocsize;
400
401 if (TypeCacheHash == NULL)
402 {
403 ctl.keysize = sizeof(Oid);
404 ctl.entrysize = sizeof(TypeCacheEntry);
405
406 /*
407 * TypeCacheEntry takes hash value from the system cache. For
408 * TypeCacheHash we use the same hash in order to speedup search
409 * by hash value. This is used by hash_seq_init_with_hash_value().
410 */
411 ctl.hash = type_cache_syshash;
412
413 TypeCacheHash = hash_create("Type information cache", 64,
415 }
416
418 {
419 ctl.keysize = sizeof(Oid);
420 ctl.entrysize = sizeof(RelIdToTypeIdCacheEntry);
421 RelIdToTypeIdCacheHash = hash_create("Map from relid to OID of cached composite type", 64,
423 }
424
425 /* Also make sure CacheMemoryContext exists */
428
429 /*
430 * Reserve enough in_progress_list slots for many cases. This is the
431 * last allocation on purpose, done after the two others.
432 */
433 allocsize = 4;
436 allocsize * sizeof(*in_progress_list));
437 in_progress_list_maxlen = allocsize;
438
439 /*
440 * Set up callbacks for SI invalidations. These steps are done last,
441 * once all the other initializations are done, and can fail only with
442 * a FATAL error.
443 */
448 }
449
451
452 /* Register to catch invalidation messages */
454 {
455 int allocsize;
456
457 allocsize = in_progress_list_maxlen * 2;
459 allocsize * sizeof(*in_progress_list));
460 in_progress_list_maxlen = allocsize;
461 }
462
463 /* Try to look up an existing entry */
465 &type_id,
466 HASH_FIND, NULL);
467
468 /*
469 * Only mark the new entry as "in progress" after the initial entry
470 * lookup.
471 *
472 * TypeCacheHash uses type_cache_syshash(), potentially triggering the
473 * initialization of the TYPEOID catcache, where an out-of-memory failure
474 * is possible. If an out-of-memory happens, error recovery would call
475 * finalize_in_progress_typentries(), that could attempt a catcache
476 * initialization again outside a transaction context.
477 *
478 * See also ConditionalCatalogCacheInitializeCache().
479 */
482
483 if (typentry == NULL)
484 {
485 /*
486 * If we didn't find one, we want to make one. But first look up the
487 * pg_type row, just to make sure we don't make a cache entry for an
488 * invalid type OID. If the type OID is not valid, present a
489 * user-facing error, since some code paths such as domain_in() allow
490 * this function to be reached with a user-supplied OID.
491 */
492 HeapTuple tp;
494
496 if (!HeapTupleIsValid(tp))
499 errmsg("type with OID %u does not exist", type_id)));
501 if (!typtup->typisdefined)
504 errmsg("type \"%s\" is only a shell",
505 NameStr(typtup->typname))));
506
507 /* Now make the typcache entry */
509 &type_id,
510 HASH_ENTER, &found);
511 Assert(!found); /* it wasn't there a moment ago */
512
513 MemSet(typentry, 0, sizeof(TypeCacheEntry));
514
515 /* These fields can never change, by definition */
516 typentry->type_id = type_id;
517 typentry->type_id_hash = get_hash_value(TypeCacheHash, &type_id);
518
519 /* Keep this part in sync with the code below */
520 typentry->typlen = typtup->typlen;
521 typentry->typbyval = typtup->typbyval;
522 typentry->typalign = typtup->typalign;
523 typentry->typstorage = typtup->typstorage;
524 typentry->typtype = typtup->typtype;
525 typentry->typrelid = typtup->typrelid;
526 typentry->typsubscript = typtup->typsubscript;
527 typentry->typelem = typtup->typelem;
528 typentry->typarray = typtup->typarray;
529 typentry->typcollation = typtup->typcollation;
530 typentry->flags |= TCFLAGS_HAVE_PG_TYPE_DATA;
531
532 /* If it's a domain, immediately thread it into the domain cache list */
533 if (typentry->typtype == TYPTYPE_DOMAIN)
534 {
536 firstDomainTypeEntry = typentry;
537 }
538
539 ReleaseSysCache(tp);
540 }
541 else if (!(typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA))
542 {
543 /*
544 * We have an entry, but its pg_type row got changed, so reload the
545 * data obtained directly from pg_type.
546 */
547 HeapTuple tp;
549
551 if (!HeapTupleIsValid(tp))
554 errmsg("type with OID %u does not exist", type_id)));
556 if (!typtup->typisdefined)
559 errmsg("type \"%s\" is only a shell",
560 NameStr(typtup->typname))));
561
562 /*
563 * Keep this part in sync with the code above. Many of these fields
564 * shouldn't ever change, particularly typtype, but copy 'em anyway.
565 */
566 typentry->typlen = typtup->typlen;
567 typentry->typbyval = typtup->typbyval;
568 typentry->typalign = typtup->typalign;
569 typentry->typstorage = typtup->typstorage;
570 typentry->typtype = typtup->typtype;
571 typentry->typrelid = typtup->typrelid;
572 typentry->typsubscript = typtup->typsubscript;
573 typentry->typelem = typtup->typelem;
574 typentry->typarray = typtup->typarray;
575 typentry->typcollation = typtup->typcollation;
576 typentry->flags |= TCFLAGS_HAVE_PG_TYPE_DATA;
577
578 ReleaseSysCache(tp);
579 }
580
581 /*
582 * Look up opclasses if we haven't already and any dependent info is
583 * requested.
584 */
590 {
591 Oid opclass;
592
593 opclass = GetDefaultOpClass(type_id, BTREE_AM_OID);
594 if (OidIsValid(opclass))
595 {
596 typentry->btree_opf = get_opclass_family(opclass);
597 typentry->btree_opintype = get_opclass_input_type(opclass);
598 }
599 else
600 {
601 typentry->btree_opf = typentry->btree_opintype = InvalidOid;
602 }
603
604 /*
605 * Reset information derived from btree opclass. Note in particular
606 * that we'll redetermine the eq_opr even if we previously found one;
607 * this matters in case a btree opclass has been added to a type that
608 * previously had only a hash opclass.
609 */
610 typentry->flags &= ~(TCFLAGS_CHECKED_EQ_OPR |
615 }
616
617 /*
618 * If we need to look up equality operator, and there's no btree opclass,
619 * force lookup of hash opclass.
620 */
621 if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
622 !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR) &&
623 typentry->btree_opf == InvalidOid)
625
630 !(typentry->flags & TCFLAGS_CHECKED_HASH_OPCLASS))
631 {
632 Oid opclass;
633
634 opclass = GetDefaultOpClass(type_id, HASH_AM_OID);
635 if (OidIsValid(opclass))
636 {
637 typentry->hash_opf = get_opclass_family(opclass);
638 typentry->hash_opintype = get_opclass_input_type(opclass);
639 }
640 else
641 {
642 typentry->hash_opf = typentry->hash_opintype = InvalidOid;
643 }
644
645 /*
646 * Reset information derived from hash opclass. We do *not* reset the
647 * eq_opr; if we already found one from the btree opclass, that
648 * decision is still good.
649 */
650 typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC |
653 }
654
655 /*
656 * Look for requested operators and functions, if we haven't already.
657 */
658 if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
659 !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR))
660 {
661 Oid eq_opr = InvalidOid;
662
663 if (typentry->btree_opf != InvalidOid)
664 eq_opr = get_opfamily_member(typentry->btree_opf,
665 typentry->btree_opintype,
666 typentry->btree_opintype,
668 if (eq_opr == InvalidOid &&
669 typentry->hash_opf != InvalidOid)
670 eq_opr = get_opfamily_member(typentry->hash_opf,
671 typentry->hash_opintype,
672 typentry->hash_opintype,
674
675 /*
676 * If the proposed equality operator is array_eq or record_eq, check
677 * to see if the element type or column types support equality. If
678 * not, array_eq or record_eq would fail at runtime, so we don't want
679 * to report that the type has equality. (We can omit similar
680 * checking for ranges and multiranges because ranges can't be created
681 * in the first place unless their subtypes support equality.)
682 */
683 if (eq_opr == ARRAY_EQ_OP &&
685 eq_opr = InvalidOid;
686 else if (eq_opr == RECORD_EQ_OP &&
688 eq_opr = InvalidOid;
689
690 /* Force update of eq_opr_finfo only if we're changing state */
691 if (typentry->eq_opr != eq_opr)
692 typentry->eq_opr_finfo.fn_oid = InvalidOid;
693
694 typentry->eq_opr = eq_opr;
695
696 /*
697 * Reset info about hash functions whenever we pick up new info about
698 * equality operator. This is so we can ensure that the hash
699 * functions match the operator.
700 */
701 typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC |
703 typentry->flags |= TCFLAGS_CHECKED_EQ_OPR;
704 }
705 if ((flags & TYPECACHE_LT_OPR) &&
706 !(typentry->flags & TCFLAGS_CHECKED_LT_OPR))
707 {
708 Oid lt_opr = InvalidOid;
709
710 if (typentry->btree_opf != InvalidOid)
711 lt_opr = get_opfamily_member(typentry->btree_opf,
712 typentry->btree_opintype,
713 typentry->btree_opintype,
715
716 /*
717 * As above, make sure array_cmp or record_cmp will succeed; but again
718 * we need no special check for ranges or multiranges.
719 */
720 if (lt_opr == ARRAY_LT_OP &&
721 !array_element_has_compare(typentry))
722 lt_opr = InvalidOid;
723 else if (lt_opr == RECORD_LT_OP &&
725 lt_opr = InvalidOid;
726
727 typentry->lt_opr = lt_opr;
728 typentry->flags |= TCFLAGS_CHECKED_LT_OPR;
729 }
730 if ((flags & TYPECACHE_GT_OPR) &&
731 !(typentry->flags & TCFLAGS_CHECKED_GT_OPR))
732 {
733 Oid gt_opr = InvalidOid;
734
735 if (typentry->btree_opf != InvalidOid)
736 gt_opr = get_opfamily_member(typentry->btree_opf,
737 typentry->btree_opintype,
738 typentry->btree_opintype,
740
741 /*
742 * As above, make sure array_cmp or record_cmp will succeed; but again
743 * we need no special check for ranges or multiranges.
744 */
745 if (gt_opr == ARRAY_GT_OP &&
746 !array_element_has_compare(typentry))
747 gt_opr = InvalidOid;
748 else if (gt_opr == RECORD_GT_OP &&
750 gt_opr = InvalidOid;
751
752 typentry->gt_opr = gt_opr;
753 typentry->flags |= TCFLAGS_CHECKED_GT_OPR;
754 }
756 !(typentry->flags & TCFLAGS_CHECKED_CMP_PROC))
757 {
758 Oid cmp_proc = InvalidOid;
759
760 if (typentry->btree_opf != InvalidOid)
761 cmp_proc = get_opfamily_proc(typentry->btree_opf,
762 typentry->btree_opintype,
763 typentry->btree_opintype,
765
766 /*
767 * As above, make sure array_cmp or record_cmp will succeed; but again
768 * we need no special check for ranges or multiranges.
769 */
770 if (cmp_proc == F_BTARRAYCMP &&
771 !array_element_has_compare(typentry))
772 cmp_proc = InvalidOid;
773 else if (cmp_proc == F_BTRECORDCMP &&
775 cmp_proc = InvalidOid;
776
777 /* Force update of cmp_proc_finfo only if we're changing state */
778 if (typentry->cmp_proc != cmp_proc)
779 typentry->cmp_proc_finfo.fn_oid = InvalidOid;
780
781 typentry->cmp_proc = cmp_proc;
782 typentry->flags |= TCFLAGS_CHECKED_CMP_PROC;
783 }
785 !(typentry->flags & TCFLAGS_CHECKED_HASH_PROC))
786 {
787 Oid hash_proc = InvalidOid;
788
789 /*
790 * We insist that the eq_opr, if one has been determined, match the
791 * hash opclass; else report there is no hash function.
792 */
793 if (typentry->hash_opf != InvalidOid &&
794 (!OidIsValid(typentry->eq_opr) ||
795 typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
796 typentry->hash_opintype,
797 typentry->hash_opintype,
799 hash_proc = get_opfamily_proc(typentry->hash_opf,
800 typentry->hash_opintype,
801 typentry->hash_opintype,
803
804 /*
805 * As above, make sure hash_array, hash_record, hash_range, or
806 * hash_multirange will succeed. Here we do need to check the range
807 * cases.
808 */
809 if (hash_proc == F_HASH_ARRAY &&
810 !array_element_has_hashing(typentry))
811 hash_proc = InvalidOid;
812 else if (hash_proc == F_HASH_RECORD &&
814 hash_proc = InvalidOid;
815 else if (hash_proc == F_HASH_RANGE &&
816 !range_element_has_hashing(typentry))
817 hash_proc = InvalidOid;
818 else if (hash_proc == F_HASH_MULTIRANGE &&
820 hash_proc = InvalidOid;
821
822 /* Force update of hash_proc_finfo only if we're changing state */
823 if (typentry->hash_proc != hash_proc)
825
826 typentry->hash_proc = hash_proc;
827 typentry->flags |= TCFLAGS_CHECKED_HASH_PROC;
828 }
829 if ((flags & (TYPECACHE_HASH_EXTENDED_PROC |
832 {
833 Oid hash_extended_proc = InvalidOid;
834
835 /*
836 * We insist that the eq_opr, if one has been determined, match the
837 * hash opclass; else report there is no hash function.
838 */
839 if (typentry->hash_opf != InvalidOid &&
840 (!OidIsValid(typentry->eq_opr) ||
841 typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
842 typentry->hash_opintype,
843 typentry->hash_opintype,
845 hash_extended_proc = get_opfamily_proc(typentry->hash_opf,
846 typentry->hash_opintype,
847 typentry->hash_opintype,
849
850 /*
851 * As above, make sure hash_array_extended, hash_record_extended,
852 * hash_range_extended, or hash_multirange_extended will succeed.
853 */
854 if (hash_extended_proc == F_HASH_ARRAY_EXTENDED &&
856 hash_extended_proc = InvalidOid;
857 else if (hash_extended_proc == F_HASH_RECORD_EXTENDED &&
859 hash_extended_proc = InvalidOid;
860 else if (hash_extended_proc == F_HASH_RANGE_EXTENDED &&
862 hash_extended_proc = InvalidOid;
863 else if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
865 hash_extended_proc = InvalidOid;
866
867 /* Force update of proc finfo only if we're changing state */
868 if (typentry->hash_extended_proc != hash_extended_proc)
870
871 typentry->hash_extended_proc = hash_extended_proc;
873 }
874
875 /*
876 * Set up fmgr lookup info as requested
877 *
878 * Note: we tell fmgr the finfo structures live in CacheMemoryContext,
879 * which is not quite right (they're really in the hash table's private
880 * memory context) but this will do for our purposes.
881 *
882 * Note: the code above avoids invalidating the finfo structs unless the
883 * referenced operator/function OID actually changes. This is to prevent
884 * unnecessary leakage of any subsidiary data attached to an finfo, since
885 * that would cause session-lifespan memory leaks.
886 */
887 if ((flags & TYPECACHE_EQ_OPR_FINFO) &&
888 typentry->eq_opr_finfo.fn_oid == InvalidOid &&
889 typentry->eq_opr != InvalidOid)
890 {
892
893 eq_opr_func = get_opcode(typentry->eq_opr);
894 if (eq_opr_func != InvalidOid)
897 }
898 if ((flags & TYPECACHE_CMP_PROC_FINFO) &&
899 typentry->cmp_proc_finfo.fn_oid == InvalidOid &&
900 typentry->cmp_proc != InvalidOid)
901 {
902 fmgr_info_cxt(typentry->cmp_proc, &typentry->cmp_proc_finfo,
904 }
905 if ((flags & TYPECACHE_HASH_PROC_FINFO) &&
906 typentry->hash_proc_finfo.fn_oid == InvalidOid &&
907 typentry->hash_proc != InvalidOid)
908 {
909 fmgr_info_cxt(typentry->hash_proc, &typentry->hash_proc_finfo,
911 }
914 typentry->hash_extended_proc != InvalidOid)
915 {
917 &typentry->hash_extended_proc_finfo,
919 }
920
921 /*
922 * If it's a composite type (row type), get tupdesc if requested
923 */
924 if ((flags & TYPECACHE_TUPDESC) &&
925 typentry->tupDesc == NULL &&
926 typentry->typtype == TYPTYPE_COMPOSITE)
927 {
928 load_typcache_tupdesc(typentry);
929 }
930
931 /*
932 * If requested, get information about a range type
933 *
934 * This includes making sure that the basic info about the range element
935 * type is up-to-date.
936 */
937 if ((flags & TYPECACHE_RANGE_INFO) &&
938 typentry->typtype == TYPTYPE_RANGE)
939 {
940 if (typentry->rngelemtype == NULL)
941 load_rangetype_info(typentry);
942 else if (!(typentry->rngelemtype->flags & TCFLAGS_HAVE_PG_TYPE_DATA))
943 (void) lookup_type_cache(typentry->rngelemtype->type_id, 0);
944 }
945
946 /*
947 * If requested, get information about a multirange type
948 */
949 if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
950 typentry->rngtype == NULL &&
951 typentry->typtype == TYPTYPE_MULTIRANGE)
952 {
953 load_multirangetype_info(typentry);
954 }
955
956 /*
957 * If requested, get information about a domain type
958 */
959 if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
960 typentry->domainBaseType == InvalidOid &&
961 typentry->typtype == TYPTYPE_DOMAIN)
962 {
963 typentry->domainBaseTypmod = -1;
964 typentry->domainBaseType =
965 getBaseTypeAndTypmod(type_id, &typentry->domainBaseTypmod);
966 }
967 if ((flags & TYPECACHE_DOMAIN_CONSTR_INFO) &&
968 (typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
969 typentry->typtype == TYPTYPE_DOMAIN)
970 {
971 load_domaintype_info(typentry);
972 }
973
974 INJECTION_POINT("typecache-before-rel-type-cache-insert", NULL);
975
978
980
981 return typentry;
982}
983
984/*
985 * load_typcache_tupdesc --- helper routine to set up composite type's tupDesc
986 */
987static void
989{
990 Relation rel;
991
992 if (!OidIsValid(typentry->typrelid)) /* should not happen */
993 elog(ERROR, "invalid typrelid for composite type %u",
994 typentry->type_id);
995 rel = relation_open(typentry->typrelid, AccessShareLock);
996 Assert(rel->rd_rel->reltype == typentry->type_id);
997
998 /*
999 * Link to the tupdesc and increment its refcount (we assert it's a
1000 * refcounted descriptor). We don't use IncrTupleDescRefCount() for this,
1001 * because the reference mustn't be entered in the current resource owner;
1002 * it can outlive the current query.
1003 */
1004 typentry->tupDesc = RelationGetDescr(rel);
1005
1006 Assert(typentry->tupDesc->tdrefcount > 0);
1007 typentry->tupDesc->tdrefcount++;
1008
1009 /*
1010 * In future, we could take some pains to not change tupDesc_identifier if
1011 * the tupdesc didn't really change; but for now it's not worth it.
1012 */
1014
1016}
1017
1018/*
1019 * load_rangetype_info --- helper routine to set up range type information
1020 */
1021static void
1023{
1025 HeapTuple tup;
1031 Oid opcintype;
1032 Oid cmpFnOid;
1033
1034 /* get information from pg_range */
1036 /* should not fail, since we already checked typtype ... */
1037 if (!HeapTupleIsValid(tup))
1038 elog(ERROR, "cache lookup failed for range type %u",
1039 typentry->type_id);
1041
1042 subtypeOid = pg_range->rngsubtype;
1043 typentry->rng_collation = pg_range->rngcollation;
1044 opclassOid = pg_range->rngsubopc;
1045 canonicalOid = pg_range->rngcanonical;
1046 subdiffOid = pg_range->rngsubdiff;
1047
1049
1050 /* get opclass properties and look up the comparison function */
1053 typentry->rng_opfamily = opfamilyOid;
1054
1055 cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype,
1056 BTORDER_PROC);
1058 elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
1059 BTORDER_PROC, opcintype, opcintype, opfamilyOid);
1060
1061 /* set up cached fmgrinfo structs */
1070
1071 /* Lastly, set up link to the element type --- this marks data valid */
1073}
1074
1075/*
1076 * load_multirangetype_info --- helper routine to set up multirange type
1077 * information
1078 */
1079static void
1081{
1083
1086 elog(ERROR, "cache lookup failed for multirange type %u",
1087 typentry->type_id);
1088
1090}
1091
1092/*
1093 * load_domaintype_info --- helper routine to set up domain constraint info
1094 *
1095 * Note: we assume we're called in a relatively short-lived context, so it's
1096 * okay to leak data into the current context while scanning pg_constraint.
1097 * We build the new DomainConstraintCache data in a context underneath
1098 * CurrentMemoryContext, and reparent it under CacheMemoryContext when
1099 * complete.
1100 */
1101static void
1103{
1104 Oid typeOid = typentry->type_id;
1106 bool notNull = false;
1108 int cconslen;
1111
1112 /*
1113 * If we're here, any existing constraint info is stale, so release it.
1114 * For safety, be sure to null the link before trying to delete the data.
1115 */
1116 if (typentry->domainData)
1117 {
1118 dcc = typentry->domainData;
1119 typentry->domainData = NULL;
1120 decr_dcc_refcount(dcc);
1121 }
1122
1123 /*
1124 * We try to optimize the common case of no domain constraints, so don't
1125 * create the dcc object and context until we find a constraint. Likewise
1126 * for the temp sorting array.
1127 */
1128 dcc = NULL;
1129 ccons = NULL;
1130 cconslen = 0;
1131
1132 /*
1133 * Scan pg_constraint for relevant constraints. We want to find
1134 * constraints for not just this domain, but any ancestor domains, so the
1135 * outer loop crawls up the domain stack.
1136 */
1138
1139 for (;;)
1140 {
1141 HeapTuple tup;
1144 int nccons = 0;
1145 ScanKeyData key[1];
1146 SysScanDesc scan;
1147
1149 if (!HeapTupleIsValid(tup))
1150 elog(ERROR, "cache lookup failed for type %u", typeOid);
1152
1153 if (typTup->typtype != TYPTYPE_DOMAIN)
1154 {
1155 /* Not a domain, so done */
1157 break;
1158 }
1159
1160 /* Test for NOT NULL Constraint */
1161 if (typTup->typnotnull)
1162 notNull = true;
1163
1164 /* Look for CHECK Constraints on this domain */
1165 ScanKeyInit(&key[0],
1168 ObjectIdGetDatum(typeOid));
1169
1171 NULL, 1, key);
1172
1174 {
1176 Datum val;
1177 bool isNull;
1178 char *constring;
1179 Expr *check_expr;
1181
1182 /* Ignore non-CHECK constraints */
1183 if (c->contype != CONSTRAINT_CHECK)
1184 continue;
1185
1186 /* Not expecting conbin to be NULL, but we'll test for it anyway */
1188 conRel->rd_att, &isNull);
1189 if (isNull)
1190 elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
1191 NameStr(typTup->typname), NameStr(c->conname));
1192
1193 /* Create the DomainConstraintCache object and context if needed */
1194 if (dcc == NULL)
1195 {
1196 MemoryContext cxt;
1197
1199 "Domain constraints",
1201 dcc = (DomainConstraintCache *)
1203 dcc->constraints = NIL;
1204 dcc->dccContext = cxt;
1205 dcc->dccRefCount = 0;
1206 }
1207
1208 /* Convert conbin to a node tree, still in caller's context */
1210 check_expr = (Expr *) stringToNode(constring);
1211
1212 /*
1213 * Plan the expression, since ExecInitExpr will expect that.
1214 *
1215 * Note: caching the result of expression_planner() is not very
1216 * good practice. Ideally we'd use a CachedExpression here so
1217 * that we would react promptly to, eg, changes in inlined
1218 * functions. However, because we don't support mutable domain
1219 * CHECK constraints, it's not really clear that it's worth the
1220 * extra overhead to do that.
1221 */
1222 check_expr = expression_planner(check_expr);
1223
1224 /* Create only the minimally needed stuff in dccContext */
1226
1229 r->name = pstrdup(NameStr(c->conname));
1230 r->check_expr = copyObject(check_expr);
1231 r->check_exprstate = NULL;
1232
1234
1235 /* Accumulate constraints in an array, for sorting below */
1236 if (ccons == NULL)
1237 {
1238 cconslen = 8;
1241 }
1242 else if (nccons >= cconslen)
1243 {
1244 cconslen *= 2;
1247 }
1248 ccons[nccons++] = r;
1249 }
1250
1251 systable_endscan(scan);
1252
1253 if (nccons > 0)
1254 {
1255 /*
1256 * Sort the items for this domain, so that CHECKs are applied in a
1257 * deterministic order.
1258 */
1259 if (nccons > 1)
1261
1262 /*
1263 * Now attach them to the overall list. Use lcons() here because
1264 * constraints of parent domains should be applied earlier.
1265 */
1267 while (nccons > 0)
1268 dcc->constraints = lcons(ccons[--nccons], dcc->constraints);
1270 }
1271
1272 /* loop to next domain in stack */
1273 typeOid = typTup->typbasetype;
1275 }
1276
1278
1279 /*
1280 * Only need to add one NOT NULL check regardless of how many domains in
1281 * the stack request it.
1282 */
1283 if (notNull)
1284 {
1286
1287 /* Create the DomainConstraintCache object and context if needed */
1288 if (dcc == NULL)
1289 {
1290 MemoryContext cxt;
1291
1293 "Domain constraints",
1295 dcc = (DomainConstraintCache *)
1297 dcc->constraints = NIL;
1298 dcc->dccContext = cxt;
1299 dcc->dccRefCount = 0;
1300 }
1301
1302 /* Create node trees in DomainConstraintCache's context */
1304
1306
1308 r->name = pstrdup("NOT NULL");
1309 r->check_expr = NULL;
1310 r->check_exprstate = NULL;
1311
1312 /* lcons to apply the nullness check FIRST */
1313 dcc->constraints = lcons(r, dcc->constraints);
1314
1316 }
1317
1318 /*
1319 * If we made a constraint object, move it into CacheMemoryContext and
1320 * attach it to the typcache entry.
1321 */
1322 if (dcc)
1323 {
1325 typentry->domainData = dcc;
1326 dcc->dccRefCount++; /* count the typcache's reference */
1327 }
1328
1329 /* Either way, the typcache entry's domain data is now valid. */
1331}
1332
1333/*
1334 * qsort comparator to sort DomainConstraintState pointers by name
1335 */
1336static int
1337dcs_cmp(const void *a, const void *b)
1338{
1339 const DomainConstraintState *const *ca = (const DomainConstraintState *const *) a;
1340 const DomainConstraintState *const *cb = (const DomainConstraintState *const *) b;
1341
1342 return strcmp((*ca)->name, (*cb)->name);
1343}
1344
1345/*
1346 * decr_dcc_refcount --- decrement a DomainConstraintCache's refcount,
1347 * and free it if no references remain
1348 */
1349static void
1351{
1352 Assert(dcc->dccRefCount > 0);
1353 if (--(dcc->dccRefCount) <= 0)
1355}
1356
1357/*
1358 * Context reset/delete callback for a DomainConstraintRef
1359 */
1360static void
1362{
1364 DomainConstraintCache *dcc = ref->dcc;
1365
1366 /* Paranoia --- be sure link is nulled before trying to release */
1367 if (dcc)
1368 {
1369 ref->constraints = NIL;
1370 ref->dcc = NULL;
1371 decr_dcc_refcount(dcc);
1372 }
1373}
1374
1375/*
1376 * prep_domain_constraints --- prepare domain constraints for execution
1377 *
1378 * The expression trees stored in the DomainConstraintCache's list are
1379 * converted to executable expression state trees stored in execctx.
1380 */
1381static List *
1383{
1384 List *result = NIL;
1386 ListCell *lc;
1387
1389
1390 foreach(lc, constraints)
1391 {
1394
1396 newr->constrainttype = r->constrainttype;
1397 newr->name = r->name;
1398 newr->check_expr = r->check_expr;
1399 newr->check_exprstate = ExecInitExpr(r->check_expr, NULL);
1400
1402 }
1403
1405
1406 return result;
1407}
1408
1409/*
1410 * InitDomainConstraintRef --- initialize a DomainConstraintRef struct
1411 *
1412 * Caller must tell us the MemoryContext in which the DomainConstraintRef
1413 * lives. The ref will be cleaned up when that context is reset/deleted.
1414 *
1415 * Caller must also tell us whether it wants check_exprstate fields to be
1416 * computed in the DomainConstraintState nodes attached to this ref.
1417 * If it doesn't, we need not make a copy of the DomainConstraintState list.
1418 */
1419void
1421 MemoryContext refctx, bool need_exprstate)
1422{
1423 /* Look up the typcache entry --- we assume it survives indefinitely */
1425 ref->need_exprstate = need_exprstate;
1426 /* For safety, establish the callback before acquiring a refcount */
1427 ref->refctx = refctx;
1428 ref->dcc = NULL;
1429 ref->callback.func = dccref_deletion_callback;
1430 ref->callback.arg = ref;
1431 MemoryContextRegisterResetCallback(refctx, &ref->callback);
1432 /* Acquire refcount if there are constraints, and set up exported list */
1433 if (ref->tcache->domainData)
1434 {
1435 ref->dcc = ref->tcache->domainData;
1436 ref->dcc->dccRefCount++;
1437 if (ref->need_exprstate)
1438 ref->constraints = prep_domain_constraints(ref->dcc->constraints,
1439 ref->refctx);
1440 else
1441 ref->constraints = ref->dcc->constraints;
1442 }
1443 else
1444 ref->constraints = NIL;
1445}
1446
1447/*
1448 * UpdateDomainConstraintRef --- recheck validity of domain constraint info
1449 *
1450 * If the domain's constraint set changed, ref->constraints is updated to
1451 * point at a new list of cached constraints.
1452 *
1453 * In the normal case where nothing happened to the domain, this is cheap
1454 * enough that it's reasonable (and expected) to check before *each* use
1455 * of the constraint info.
1456 */
1457void
1459{
1460 TypeCacheEntry *typentry = ref->tcache;
1461
1462 /* Make sure typcache entry's data is up to date */
1463 if ((typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
1464 typentry->typtype == TYPTYPE_DOMAIN)
1465 load_domaintype_info(typentry);
1466
1467 /* Transfer to ref object if there's new info, adjusting refcounts */
1468 if (ref->dcc != typentry->domainData)
1469 {
1470 /* Paranoia --- be sure link is nulled before trying to release */
1471 DomainConstraintCache *dcc = ref->dcc;
1472
1473 if (dcc)
1474 {
1475 /*
1476 * Note: we just leak the previous list of executable domain
1477 * constraints. Alternatively, we could keep those in a child
1478 * context of ref->refctx and free that context at this point.
1479 * However, in practice this code path will be taken so seldom
1480 * that the extra bookkeeping for a child context doesn't seem
1481 * worthwhile; we'll just allow a leak for the lifespan of refctx.
1482 */
1483 ref->constraints = NIL;
1484 ref->dcc = NULL;
1485 decr_dcc_refcount(dcc);
1486 }
1487 dcc = typentry->domainData;
1488 if (dcc)
1489 {
1490 ref->dcc = dcc;
1491 dcc->dccRefCount++;
1492 if (ref->need_exprstate)
1493 ref->constraints = prep_domain_constraints(dcc->constraints,
1494 ref->refctx);
1495 else
1496 ref->constraints = dcc->constraints;
1497 }
1498 }
1499}
1500
1501/*
1502 * DomainHasConstraints --- utility routine to check if a domain has constraints
1503 *
1504 * Returns true if the domain has any constraints at all. If has_volatile
1505 * is not NULL, also checks whether any CHECK constraint contains a volatile
1506 * expression and sets *has_volatile accordingly.
1507 *
1508 * This is defined to return false, not fail, if type is not a domain.
1509 */
1510bool
1512{
1513 TypeCacheEntry *typentry;
1514
1515 /*
1516 * Note: a side effect is to cause the typcache's domain data to become
1517 * valid. This is fine since we'll likely need it soon if there is any.
1518 */
1520
1521 if (typentry->domainData == NULL)
1522 return false;
1523
1524 if (has_volatile)
1525 {
1526 *has_volatile = false;
1527
1529 typentry->domainData->constraints)
1530 {
1531 if (constrstate->constrainttype == DOM_CONSTRAINT_CHECK &&
1533 {
1534 *has_volatile = true;
1535 break;
1536 }
1537 }
1538 }
1539
1540 return true;
1541}
1542
1543
1544/*
1545 * array_element_has_equality and friends are helper routines to check
1546 * whether we should believe that array_eq and related functions will work
1547 * on the given array type or composite type.
1548 *
1549 * The logic above may call these repeatedly on the same type entry, so we
1550 * make use of the typentry->flags field to cache the results once known.
1551 * Also, we assume that we'll probably want all these facts about the type
1552 * if we want any, so we cache them all using only one lookup of the
1553 * component datatype(s).
1554 */
1555
1556static bool
1558{
1559 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1561 return (typentry->flags & TCFLAGS_HAVE_ELEM_EQUALITY) != 0;
1562}
1563
1564static bool
1566{
1567 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1569 return (typentry->flags & TCFLAGS_HAVE_ELEM_COMPARE) != 0;
1570}
1571
1572static bool
1574{
1575 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1577 return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1578}
1579
1580static bool
1587
1588static void
1590{
1592
1593 if (OidIsValid(elem_type))
1594 {
1596
1602 if (OidIsValid(elementry->eq_opr))
1603 typentry->flags |= TCFLAGS_HAVE_ELEM_EQUALITY;
1604 if (OidIsValid(elementry->cmp_proc))
1605 typentry->flags |= TCFLAGS_HAVE_ELEM_COMPARE;
1606 if (OidIsValid(elementry->hash_proc))
1607 typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1608 if (OidIsValid(elementry->hash_extended_proc))
1610 }
1612}
1613
1614/*
1615 * Likewise, some helper functions for composite types.
1616 */
1617
1618static bool
1620{
1621 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1623 return (typentry->flags & TCFLAGS_HAVE_FIELD_EQUALITY) != 0;
1624}
1625
1626static bool
1628{
1629 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1631 return (typentry->flags & TCFLAGS_HAVE_FIELD_COMPARE) != 0;
1632}
1633
1634static bool
1636{
1637 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1639 return (typentry->flags & TCFLAGS_HAVE_FIELD_HASHING) != 0;
1640}
1641
1642static bool
1649
1650static void
1652{
1653 /*
1654 * For type RECORD, we can't really tell what will work, since we don't
1655 * have access here to the specific anonymous type. Just assume that
1656 * equality and comparison will (we may get a failure at runtime). We
1657 * could also claim that hashing works, but then if code that has the
1658 * option between a comparison-based (sort-based) and a hash-based plan
1659 * chooses hashing, stuff could fail that would otherwise work if it chose
1660 * a comparison-based plan. In practice more types support comparison
1661 * than hashing.
1662 */
1663 if (typentry->type_id == RECORDOID)
1664 {
1665 typentry->flags |= (TCFLAGS_HAVE_FIELD_EQUALITY |
1667 }
1668 else if (typentry->typtype == TYPTYPE_COMPOSITE)
1669 {
1670 TupleDesc tupdesc;
1671 int newflags;
1672 int i;
1673
1674 /* Fetch composite type's tupdesc if we don't have it already */
1675 if (typentry->tupDesc == NULL)
1676 load_typcache_tupdesc(typentry);
1677 tupdesc = typentry->tupDesc;
1678
1679 /* Must bump the refcount while we do additional catalog lookups */
1680 IncrTupleDescRefCount(tupdesc);
1681
1682 /* Have each property if all non-dropped fields have the property */
1687 for (i = 0; i < tupdesc->natts; i++)
1688 {
1690 Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
1691
1692 if (attr->attisdropped)
1693 continue;
1694
1695 fieldentry = lookup_type_cache(attr->atttypid,
1700 if (!OidIsValid(fieldentry->eq_opr))
1702 if (!OidIsValid(fieldentry->cmp_proc))
1704 if (!OidIsValid(fieldentry->hash_proc))
1706 if (!OidIsValid(fieldentry->hash_extended_proc))
1708
1709 /* We can drop out of the loop once we disprove all bits */
1710 if (newflags == 0)
1711 break;
1712 }
1713 typentry->flags |= newflags;
1714
1715 DecrTupleDescRefCount(tupdesc);
1716 }
1717 else if (typentry->typtype == TYPTYPE_DOMAIN)
1718 {
1719 /* If it's domain over composite, copy base type's properties */
1721
1722 /* load up basetype info if we didn't already */
1723 if (typentry->domainBaseType == InvalidOid)
1724 {
1725 typentry->domainBaseTypmod = -1;
1726 typentry->domainBaseType =
1727 getBaseTypeAndTypmod(typentry->type_id,
1728 &typentry->domainBaseTypmod);
1729 }
1735 if (baseentry->typtype == TYPTYPE_COMPOSITE)
1736 {
1738 typentry->flags |= baseentry->flags & (TCFLAGS_HAVE_FIELD_EQUALITY |
1742 }
1743 }
1745}
1746
1747/*
1748 * Likewise, some helper functions for range and multirange types.
1749 *
1750 * We can borrow the flag bits for array element properties to use for range
1751 * element properties, since those flag bits otherwise have no use in a
1752 * range or multirange type's typcache entry.
1753 */
1754
1755static bool
1757{
1758 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1760 return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1761}
1762
1763static bool
1770
1771static void
1773{
1774 /* load up subtype link if we didn't already */
1775 if (typentry->rngelemtype == NULL &&
1776 typentry->typtype == TYPTYPE_RANGE)
1777 load_rangetype_info(typentry);
1778
1779 if (typentry->rngelemtype != NULL)
1780 {
1782
1783 /* might need to calculate subtype's hash function properties */
1787 if (OidIsValid(elementry->hash_proc))
1788 typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1789 if (OidIsValid(elementry->hash_extended_proc))
1791 }
1793}
1794
1795static bool
1797{
1798 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1800 return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1801}
1802
1803static bool
1810
1811static void
1813{
1814 /* load up range link if we didn't already */
1815 if (typentry->rngtype == NULL &&
1816 typentry->typtype == TYPTYPE_MULTIRANGE)
1817 load_multirangetype_info(typentry);
1818
1819 if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
1820 {
1822
1823 /* might need to calculate subtype's hash function properties */
1827 if (OidIsValid(elementry->hash_proc))
1828 typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1829 if (OidIsValid(elementry->hash_extended_proc))
1831 }
1833}
1834
1835/*
1836 * Make sure that RecordCacheArray and RecordIdentifierArray are large enough
1837 * to store 'typmod'.
1838 */
1839static void
1861
1862/*
1863 * lookup_rowtype_tupdesc_internal --- internal routine to lookup a rowtype
1864 *
1865 * Same API as lookup_rowtype_tupdesc_noerror, but the returned tupdesc
1866 * hasn't had its refcount bumped.
1867 */
1868static TupleDesc
1870{
1871 if (type_id != RECORDOID)
1872 {
1873 /*
1874 * It's a named composite type, so use the regular typcache.
1875 */
1876 TypeCacheEntry *typentry;
1877
1878 typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
1879 if (typentry->tupDesc == NULL && !noError)
1880 ereport(ERROR,
1882 errmsg("type %s is not composite",
1883 format_type_be(type_id))));
1884 return typentry->tupDesc;
1885 }
1886 else
1887 {
1888 /*
1889 * It's a transient record type, so look in our record-type table.
1890 */
1891 if (typmod >= 0)
1892 {
1893 /* It is already in our local cache? */
1894 if (typmod < RecordCacheArrayLen &&
1895 RecordCacheArray[typmod].tupdesc != NULL)
1896 return RecordCacheArray[typmod].tupdesc;
1897
1898 /* Are we attached to a shared record typmod registry? */
1900 {
1902
1903 /* Try to find it in the shared typmod index. */
1905 &typmod, false);
1906 if (entry != NULL)
1907 {
1908 TupleDesc tupdesc;
1909
1910 tupdesc = (TupleDesc)
1912 entry->shared_tupdesc);
1913 Assert(typmod == tupdesc->tdtypmod);
1914
1915 /* We may need to extend the local RecordCacheArray. */
1917
1918 /*
1919 * Our local array can now point directly to the TupleDesc
1920 * in shared memory, which is non-reference-counted.
1921 */
1922 RecordCacheArray[typmod].tupdesc = tupdesc;
1923 Assert(tupdesc->tdrefcount == -1);
1924
1925 /*
1926 * We don't share tupdesc identifiers across processes, so
1927 * assign one locally.
1928 */
1930
1932 entry);
1933
1934 return RecordCacheArray[typmod].tupdesc;
1935 }
1936 }
1937 }
1938
1939 if (!noError)
1940 ereport(ERROR,
1942 errmsg("record type has not been registered")));
1943 return NULL;
1944 }
1945}
1946
1947/*
1948 * lookup_rowtype_tupdesc
1949 *
1950 * Given a typeid/typmod that should describe a known composite type,
1951 * return the tuple descriptor for the type. Will ereport on failure.
1952 * (Use ereport because this is reachable with user-specified OIDs,
1953 * for example from record_in().)
1954 *
1955 * Note: on success, we increment the refcount of the returned TupleDesc,
1956 * and log the reference in CurrentResourceOwner. Caller must call
1957 * ReleaseTupleDesc when done using the tupdesc. (There are some
1958 * cases in which the returned tupdesc is not refcounted, in which
1959 * case PinTupleDesc/ReleaseTupleDesc are no-ops; but in these cases
1960 * the tupdesc is guaranteed to live till process exit.)
1961 */
1964{
1965 TupleDesc tupDesc;
1966
1967 tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
1968 PinTupleDesc(tupDesc);
1969 return tupDesc;
1970}
1971
1972/*
1973 * lookup_rowtype_tupdesc_noerror
1974 *
1975 * As above, but if the type is not a known composite type and noError
1976 * is true, returns NULL instead of ereport'ing. (Note that if a bogus
1977 * type_id is passed, you'll get an ereport anyway.)
1978 */
1981{
1982 TupleDesc tupDesc;
1983
1984 tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
1985 if (tupDesc != NULL)
1986 PinTupleDesc(tupDesc);
1987 return tupDesc;
1988}
1989
1990/*
1991 * lookup_rowtype_tupdesc_copy
1992 *
1993 * Like lookup_rowtype_tupdesc(), but the returned TupleDesc has been
1994 * copied into the CurrentMemoryContext and is not reference-counted.
1995 */
1998{
1999 TupleDesc tmp;
2000
2001 tmp = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
2002 return CreateTupleDescCopyConstr(tmp);
2003}
2004
2005/*
2006 * lookup_rowtype_tupdesc_domain
2007 *
2008 * Same as lookup_rowtype_tupdesc_noerror(), except that the type can also be
2009 * a domain over a named composite type; so this is effectively equivalent to
2010 * lookup_rowtype_tupdesc_noerror(getBaseType(type_id), typmod, noError)
2011 * except for being a tad faster.
2012 *
2013 * Note: the reason we don't fold the look-through-domain behavior into plain
2014 * lookup_rowtype_tupdesc() is that we want callers to know they might be
2015 * dealing with a domain. Otherwise they might construct a tuple that should
2016 * be of the domain type, but not apply domain constraints.
2017 */
2020{
2021 TupleDesc tupDesc;
2022
2023 if (type_id != RECORDOID)
2024 {
2025 /*
2026 * Check for domain or named composite type. We might as well load
2027 * whichever data is needed.
2028 */
2029 TypeCacheEntry *typentry;
2030
2031 typentry = lookup_type_cache(type_id,
2034 if (typentry->typtype == TYPTYPE_DOMAIN)
2036 typentry->domainBaseTypmod,
2037 noError);
2038 if (typentry->tupDesc == NULL && !noError)
2039 ereport(ERROR,
2041 errmsg("type %s is not composite",
2042 format_type_be(type_id))));
2043 tupDesc = typentry->tupDesc;
2044 }
2045 else
2046 tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
2047 if (tupDesc != NULL)
2048 PinTupleDesc(tupDesc);
2049 return tupDesc;
2050}
2051
2052/*
2053 * Hash function for the hash table of RecordCacheEntry.
2054 */
2055static uint32
2056record_type_typmod_hash(const void *data, size_t size)
2057{
2058 const RecordCacheEntry *entry = data;
2059
2060 return hashRowType(entry->tupdesc);
2061}
2062
2063/*
2064 * Match function for the hash table of RecordCacheEntry.
2065 */
2066static int
2067record_type_typmod_compare(const void *a, const void *b, size_t size)
2068{
2069 const RecordCacheEntry *left = a;
2070 const RecordCacheEntry *right = b;
2071
2072 return equalRowTypes(left->tupdesc, right->tupdesc) ? 0 : 1;
2073}
2074
2075/*
2076 * assign_record_type_typmod
2077 *
2078 * Given a tuple descriptor for a RECORD type, find or create a cache entry
2079 * for the type, and set the tupdesc's tdtypmod field to a value that will
2080 * identify this cache entry to lookup_rowtype_tupdesc.
2081 */
2082void
2084{
2087 bool found;
2089
2090 Assert(tupDesc->tdtypeid == RECORDOID);
2091
2092 if (RecordCacheHash == NULL)
2093 {
2094 /* First time through: initialize the hash table */
2095 HASHCTL ctl;
2096
2097 ctl.keysize = sizeof(TupleDesc); /* just the pointer */
2098 ctl.entrysize = sizeof(RecordCacheEntry);
2101 RecordCacheHash = hash_create("Record information cache", 64,
2102 &ctl,
2104
2105 /* Also make sure CacheMemoryContext exists */
2106 if (!CacheMemoryContext)
2108 }
2109
2110 /*
2111 * Find a hashtable entry for this tuple descriptor. We don't use
2112 * HASH_ENTER yet, because if it's missing, we need to make sure that all
2113 * the allocations succeed before we create the new entry.
2114 */
2116 &tupDesc,
2117 HASH_FIND, &found);
2118 if (found && recentry->tupdesc != NULL)
2119 {
2120 tupDesc->tdtypmod = recentry->tupdesc->tdtypmod;
2121 return;
2122 }
2123
2124 /* Not present, so need to manufacture an entry */
2126
2127 /* Look in the SharedRecordTypmodRegistry, if attached */
2129 if (entDesc == NULL)
2130 {
2131 /*
2132 * Make sure we have room before we CreateTupleDescCopy() or advance
2133 * NextRecordTypmod.
2134 */
2136
2137 /* Reference-counted local cache only. */
2138 entDesc = CreateTupleDescCopy(tupDesc);
2139 entDesc->tdrefcount = 1;
2140 entDesc->tdtypmod = NextRecordTypmod++;
2141 }
2142 else
2143 {
2145 }
2146
2148
2149 /* Assign a unique tupdesc identifier, too. */
2151
2152 /* Fully initialized; create the hash table entry */
2154 &tupDesc,
2155 HASH_ENTER, NULL);
2156 recentry->tupdesc = entDesc;
2157
2158 /* Update the caller's tuple descriptor. */
2159 tupDesc->tdtypmod = entDesc->tdtypmod;
2160
2162}
2163
2164/*
2165 * assign_record_type_identifier
2166 *
2167 * Get an identifier, which will be unique over the lifespan of this backend
2168 * process, for the current tuple descriptor of the specified composite type.
2169 * For named composite types, the value is guaranteed to change if the type's
2170 * definition does. For registered RECORD types, the value will not change
2171 * once assigned, since the registered type won't either. If an anonymous
2172 * RECORD type is specified, we return a new identifier on each call.
2173 */
2174uint64
2176{
2177 if (type_id != RECORDOID)
2178 {
2179 /*
2180 * It's a named composite type, so use the regular typcache.
2181 */
2182 TypeCacheEntry *typentry;
2183
2184 typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
2185 if (typentry->tupDesc == NULL)
2186 ereport(ERROR,
2188 errmsg("type %s is not composite",
2189 format_type_be(type_id))));
2190 Assert(typentry->tupDesc_identifier != 0);
2191 return typentry->tupDesc_identifier;
2192 }
2193 else
2194 {
2195 /*
2196 * It's a transient record type, so look in our record-type table.
2197 */
2198 if (typmod >= 0 && typmod < RecordCacheArrayLen &&
2199 RecordCacheArray[typmod].tupdesc != NULL)
2200 {
2201 Assert(RecordCacheArray[typmod].id != 0);
2202 return RecordCacheArray[typmod].id;
2203 }
2204
2205 /* For anonymous or unrecognized record type, generate a new ID */
2206 return ++tupledesc_id_counter;
2207 }
2208}
2209
2210/*
2211 * Return the amount of shmem required to hold a SharedRecordTypmodRegistry.
2212 * This exists only to avoid exposing private innards of
2213 * SharedRecordTypmodRegistry in a header.
2214 */
2215size_t
2220
2221/*
2222 * Initialize 'registry' in a pre-existing shared memory region, which must be
2223 * maximally aligned and have space for SharedRecordTypmodRegistryEstimate()
2224 * bytes.
2225 *
2226 * 'area' will be used to allocate shared memory space as required for the
2227 * typemod registration. The current process, expected to be a leader process
2228 * in a parallel query, will be attached automatically and its current record
2229 * types will be loaded into *registry. While attached, all calls to
2230 * assign_record_type_typmod will use the shared registry. Worker backends
2231 * will need to attach explicitly.
2232 *
2233 * Note that this function takes 'area' and 'segment' as arguments rather than
2234 * accessing them via CurrentSession, because they aren't installed there
2235 * until after this function runs.
2236 */
2237void
2239 dsm_segment *segment,
2240 dsa_area *area)
2241{
2245 int32 typmod;
2246
2248
2249 /* We can't already be attached to a shared registry. */
2253
2255
2256 /* Create the hash table of tuple descriptors indexed by themselves. */
2258
2259 /* Create the hash table of tuple descriptors indexed by typmod. */
2261
2263
2264 /* Initialize the SharedRecordTypmodRegistry. */
2265 registry->record_table_handle = dshash_get_hash_table_handle(record_table);
2266 registry->typmod_table_handle = dshash_get_hash_table_handle(typmod_table);
2268
2269 /*
2270 * Copy all entries from this backend's private registry into the shared
2271 * registry.
2272 */
2273 for (typmod = 0; typmod < NextRecordTypmod; ++typmod)
2274 {
2279 TupleDesc tupdesc;
2280 bool found;
2281
2282 tupdesc = RecordCacheArray[typmod].tupdesc;
2283 if (tupdesc == NULL)
2284 continue;
2285
2286 /* Copy the TupleDesc into shared memory. */
2287 shared_dp = share_tupledesc(area, tupdesc, typmod);
2288
2289 /* Insert into the typmod table. */
2291 &tupdesc->tdtypmod,
2292 &found);
2293 if (found)
2294 elog(ERROR, "cannot create duplicate shared record typmod");
2295 typmod_table_entry->typmod = tupdesc->tdtypmod;
2296 typmod_table_entry->shared_tupdesc = shared_dp;
2298
2299 /* Insert into the record table. */
2300 record_table_key.shared = false;
2301 record_table_key.u.local_tupdesc = tupdesc;
2304 &found);
2305 if (!found)
2306 {
2307 record_table_entry->key.shared = true;
2308 record_table_entry->key.u.shared_tupdesc = shared_dp;
2309 }
2311 }
2312
2313 /*
2314 * Set up the global state that will tell assign_record_type_typmod and
2315 * lookup_rowtype_tupdesc_internal about the shared registry.
2316 */
2320
2321 /*
2322 * We install a detach hook in the leader, but only to handle cleanup on
2323 * failure during GetSessionDsmHandle(). Once GetSessionDsmHandle() pins
2324 * the memory, the leader process will use a shared registry until it
2325 * exits.
2326 */
2328}
2329
2330/*
2331 * Attach to 'registry', which must have been initialized already by another
2332 * backend. Future calls to assign_record_type_typmod and
2333 * lookup_rowtype_tupdesc_internal will use the shared registry until the
2334 * current session is detached.
2335 */
2336void
2338{
2342
2344
2345 /* We can't already be attached to a shared registry. */
2352
2353 /*
2354 * We can't already have typmods in our local cache, because they'd clash
2355 * with those imported by SharedRecordTypmodRegistryInit. This should be
2356 * a freshly started parallel worker. If we ever support worker
2357 * recycling, a worker would need to zap its local cache in between
2358 * servicing different queries, in order to be able to call this and
2359 * synchronize typmods with a new leader; but that's problematic because
2360 * we can't be very sure that record-typmod-related state hasn't escaped
2361 * to anywhere else in the process.
2362 */
2364
2366
2367 /* Attach to the two hash tables. */
2370 registry->record_table_handle,
2374 registry->typmod_table_handle,
2375 NULL);
2376
2378
2379 /*
2380 * Set up detach hook to run at worker exit. Currently this is the same
2381 * as the leader's detach hook, but in future they might need to be
2382 * different.
2383 */
2387
2388 /*
2389 * Set up the session state that will tell assign_record_type_typmod and
2390 * lookup_rowtype_tupdesc_internal about the shared registry.
2391 */
2395}
2396
2397/*
2398 * InvalidateCompositeTypeCacheEntry
2399 * Invalidate particular TypeCacheEntry on Relcache inval callback
2400 *
2401 * Delete the cached tuple descriptor (if any) for the given composite
2402 * type, and reset whatever info we have cached about the composite type's
2403 * comparability.
2404 */
2405static void
2407{
2409
2410 Assert(typentry->typtype == TYPTYPE_COMPOSITE &&
2411 OidIsValid(typentry->typrelid));
2412
2413 hadTupDescOrOpclass = (typentry->tupDesc != NULL) ||
2414 (typentry->flags & TCFLAGS_OPERATOR_FLAGS);
2415
2416 /* Delete tupdesc if we have it */
2417 if (typentry->tupDesc != NULL)
2418 {
2419 /*
2420 * Release our refcount and free the tupdesc if none remain. We can't
2421 * use DecrTupleDescRefCount here because this reference is not logged
2422 * by the current resource owner.
2423 */
2424 Assert(typentry->tupDesc->tdrefcount > 0);
2425 if (--typentry->tupDesc->tdrefcount == 0)
2426 FreeTupleDesc(typentry->tupDesc);
2427 typentry->tupDesc = NULL;
2428
2429 /*
2430 * Also clear tupDesc_identifier, so that anyone watching it will
2431 * realize that the tupdesc has changed.
2432 */
2433 typentry->tupDesc_identifier = 0;
2434 }
2435
2436 /* Reset equality/comparison/hashing validity information */
2437 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2438
2439 /*
2440 * Call delete_rel_type_cache_if_needed() if we actually cleared
2441 * something.
2442 */
2445}
2446
2447/*
2448 * TypeCacheRelCallback
2449 * Relcache inval callback function
2450 *
2451 * Delete the cached tuple descriptor (if any) for the given rel's composite
2452 * type, or for all composite types if relid == InvalidOid. Also reset
2453 * whatever info we have cached about the composite type's comparability.
2454 *
2455 * This is called when a relcache invalidation event occurs for the given
2456 * relid. We can't use syscache to find a type corresponding to the given
2457 * relation because the code can be called outside of transaction. Thus, we
2458 * use the RelIdToTypeIdCacheHash map to locate appropriate typcache entry.
2459 */
2460static void
2462{
2463 TypeCacheEntry *typentry;
2464
2465 /*
2466 * RelIdToTypeIdCacheHash and TypeCacheHash should exist, otherwise this
2467 * callback wouldn't be registered
2468 */
2469 if (OidIsValid(relid))
2470 {
2472
2473 /*
2474 * Find a RelIdToTypeIdCacheHash entry, which should exist as soon as
2475 * corresponding typcache entry has something to clean.
2476 */
2478 &relid,
2479 HASH_FIND, NULL);
2480
2481 if (relentry != NULL)
2482 {
2484 &relentry->composite_typid,
2485 HASH_FIND, NULL);
2486
2487 if (typentry != NULL)
2488 {
2489 Assert(typentry->typtype == TYPTYPE_COMPOSITE);
2490 Assert(relid == typentry->typrelid);
2491
2493 }
2494 }
2495
2496 /*
2497 * Visit all the domain types sequentially. Typically, this shouldn't
2498 * affect performance since domain types are less tended to bloat.
2499 * Domain types are created manually, unlike composite types which are
2500 * automatically created for every temporary table.
2501 */
2502 for (typentry = firstDomainTypeEntry;
2503 typentry != NULL;
2504 typentry = typentry->nextDomain)
2505 {
2506 /*
2507 * If it's domain over composite, reset flags. (We don't bother
2508 * trying to determine whether the specific base type needs a
2509 * reset.) Note that if we haven't determined whether the base
2510 * type is composite, we don't need to reset anything.
2511 */
2513 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2514 }
2515 }
2516 else
2517 {
2518 HASH_SEQ_STATUS status;
2519
2520 /*
2521 * Relid is invalid. By convention, we need to reset all composite
2522 * types in cache. Also, we should reset flags for domain types, and
2523 * we loop over all entries in hash, so, do it in a single scan.
2524 */
2525 hash_seq_init(&status, TypeCacheHash);
2526 while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2527 {
2528 if (typentry->typtype == TYPTYPE_COMPOSITE)
2529 {
2531 }
2532 else if (typentry->typtype == TYPTYPE_DOMAIN)
2533 {
2534 /*
2535 * If it's domain over composite, reset flags. (We don't
2536 * bother trying to determine whether the specific base type
2537 * needs a reset.) Note that if we haven't determined whether
2538 * the base type is composite, we don't need to reset
2539 * anything.
2540 */
2542 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2543 }
2544 }
2545 }
2546}
2547
2548/*
2549 * TypeCacheTypCallback
2550 * Syscache inval callback function
2551 *
2552 * This is called when a syscache invalidation event occurs for any
2553 * pg_type row. If we have information cached about that type, mark
2554 * it as needing to be reloaded.
2555 */
2556static void
2558{
2559 HASH_SEQ_STATUS status;
2560 TypeCacheEntry *typentry;
2561
2562 /* TypeCacheHash must exist, else this callback wouldn't be registered */
2563
2564 /*
2565 * By convention, zero hash value is passed to the callback as a sign that
2566 * it's time to invalidate the whole cache. See sinval.c, inval.c and
2567 * InvalidateSystemCachesExtended().
2568 */
2569 if (hashvalue == 0)
2570 hash_seq_init(&status, TypeCacheHash);
2571 else
2572 hash_seq_init_with_hash_value(&status, TypeCacheHash, hashvalue);
2573
2574 while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2575 {
2576 bool hadPgTypeData = (typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA);
2577
2578 Assert(hashvalue == 0 || typentry->type_id_hash == hashvalue);
2579
2580 /*
2581 * Mark the data obtained directly from pg_type as invalid. Also, if
2582 * it's a domain, typnotnull might've changed, so we'll need to
2583 * recalculate its constraints.
2584 */
2585 typentry->flags &= ~(TCFLAGS_HAVE_PG_TYPE_DATA |
2587
2588 /*
2589 * Call delete_rel_type_cache_if_needed() if we cleaned
2590 * TCFLAGS_HAVE_PG_TYPE_DATA flag previously.
2591 */
2592 if (hadPgTypeData)
2594 }
2595}
2596
2597/*
2598 * TypeCacheOpcCallback
2599 * Syscache inval callback function
2600 *
2601 * This is called when a syscache invalidation event occurs for any pg_opclass
2602 * row. In principle we could probably just invalidate data dependent on the
2603 * particular opclass, but since updates on pg_opclass are rare in production
2604 * it doesn't seem worth a lot of complication: we just mark all cached data
2605 * invalid.
2606 *
2607 * Note that we don't bother watching for updates on pg_amop or pg_amproc.
2608 * This should be safe because ALTER OPERATOR FAMILY ADD/DROP OPERATOR/FUNCTION
2609 * is not allowed to be used to add/drop the primary operators and functions
2610 * of an opclass, only cross-type members of a family; and the latter sorts
2611 * of members are not going to get cached here.
2612 */
2613static void
2615{
2616 HASH_SEQ_STATUS status;
2617 TypeCacheEntry *typentry;
2618
2619 /* TypeCacheHash must exist, else this callback wouldn't be registered */
2620 hash_seq_init(&status, TypeCacheHash);
2621 while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2622 {
2623 bool hadOpclass = (typentry->flags & TCFLAGS_OPERATOR_FLAGS);
2624
2625 /* Reset equality/comparison/hashing validity information */
2626 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2627
2628 /*
2629 * Call delete_rel_type_cache_if_needed() if we actually cleared some
2630 * of TCFLAGS_OPERATOR_FLAGS.
2631 */
2632 if (hadOpclass)
2634 }
2635}
2636
2637/*
2638 * TypeCacheConstrCallback
2639 * Syscache inval callback function
2640 *
2641 * This is called when a syscache invalidation event occurs for any
2642 * pg_constraint row. We flush information about domain constraints
2643 * when this happens.
2644 *
2645 * It's slightly annoying that we can't tell whether the inval event was for
2646 * a domain constraint record or not; there's usually more update traffic
2647 * for table constraints than domain constraints, so we'll do a lot of
2648 * useless flushes. Still, this is better than the old no-caching-at-all
2649 * approach to domain constraints.
2650 */
2651static void
2653{
2654 TypeCacheEntry *typentry;
2655
2656 /*
2657 * Because this is called very frequently, and typically very few of the
2658 * typcache entries are for domains, we don't use hash_seq_search here.
2659 * Instead we thread all the domain-type entries together so that we can
2660 * visit them cheaply.
2661 */
2662 for (typentry = firstDomainTypeEntry;
2663 typentry != NULL;
2664 typentry = typentry->nextDomain)
2665 {
2666 /* Reset domain constraint validity information */
2668 }
2669}
2670
2671
2672/*
2673 * Check if given OID is part of the subset that's sortable by comparisons
2674 */
2675static inline bool
2677{
2678 Oid offset;
2679
2680 if (arg < enumdata->bitmap_base)
2681 return false;
2682 offset = arg - enumdata->bitmap_base;
2683 if (offset > (Oid) INT_MAX)
2684 return false;
2685 return bms_is_member((int) offset, enumdata->sorted_values);
2686}
2687
2688
2689/*
2690 * compare_values_of_enum
2691 * Compare two members of an enum type.
2692 * Return <0, 0, or >0 according as arg1 <, =, or > arg2.
2693 *
2694 * Note: currently, the enumData cache is refreshed only if we are asked
2695 * to compare an enum value that is not already in the cache. This is okay
2696 * because there is no support for re-ordering existing values, so comparisons
2697 * of previously cached values will return the right answer even if other
2698 * values have been added since we last loaded the cache.
2699 *
2700 * Note: the enum logic has a special-case rule about even-numbered versus
2701 * odd-numbered OIDs, but we take no account of that rule here; this
2702 * routine shouldn't even get called when that rule applies.
2703 */
2704int
2706{
2708 EnumItem *item1;
2709 EnumItem *item2;
2710
2711 /*
2712 * Equal OIDs are certainly equal --- this case was probably handled by
2713 * our caller, but we may as well check.
2714 */
2715 if (arg1 == arg2)
2716 return 0;
2717
2718 /* Load up the cache if first time through */
2719 if (tcache->enumData == NULL)
2720 load_enum_cache_data(tcache);
2721 enumdata = tcache->enumData;
2722
2723 /*
2724 * If both OIDs are known-sorted, we can just compare them directly.
2725 */
2728 {
2729 if (arg1 < arg2)
2730 return -1;
2731 else
2732 return 1;
2733 }
2734
2735 /*
2736 * Slow path: we have to identify their actual sort-order positions.
2737 */
2740
2741 if (item1 == NULL || item2 == NULL)
2742 {
2743 /*
2744 * We couldn't find one or both values. That means the enum has
2745 * changed under us, so re-initialize the cache and try again. We
2746 * don't bother retrying the known-sorted case in this path.
2747 */
2748 load_enum_cache_data(tcache);
2749 enumdata = tcache->enumData;
2750
2753
2754 /*
2755 * If we still can't find the values, complain: we must have corrupt
2756 * data.
2757 */
2758 if (item1 == NULL)
2759 elog(ERROR, "enum value %u not found in cache for enum %s",
2760 arg1, format_type_be(tcache->type_id));
2761 if (item2 == NULL)
2762 elog(ERROR, "enum value %u not found in cache for enum %s",
2763 arg2, format_type_be(tcache->type_id));
2764 }
2765
2766 if (item1->sort_order < item2->sort_order)
2767 return -1;
2768 else if (item1->sort_order > item2->sort_order)
2769 return 1;
2770 else
2771 return 0;
2772}
2773
2774/*
2775 * Load (or re-load) the enumData member of the typcache entry.
2776 */
2777static void
2779{
2785 EnumItem *items;
2786 int numitems;
2787 int maxitems;
2788 Oid bitmap_base;
2789 Bitmapset *bitmap;
2791 int bm_size,
2792 start_pos;
2793
2794 /* Check that this is actually an enum */
2795 if (tcache->typtype != TYPTYPE_ENUM)
2796 ereport(ERROR,
2798 errmsg("%s is not an enum",
2799 format_type_be(tcache->type_id))));
2800
2801 /*
2802 * Read all the information for members of the enum type. We collect the
2803 * info in working memory in the caller's context, and then transfer it to
2804 * permanent memory in CacheMemoryContext. This minimizes the risk of
2805 * leaking memory from CacheMemoryContext in the event of an error partway
2806 * through.
2807 */
2808 maxitems = 64;
2809 items = palloc_array(EnumItem, maxitems);
2810 numitems = 0;
2811
2812 /* Scan pg_enum for the members of the target enum type. */
2816 ObjectIdGetDatum(tcache->type_id));
2817
2821 true, NULL,
2822 1, &skey);
2823
2825 {
2827
2828 if (numitems >= maxitems)
2829 {
2830 maxitems *= 2;
2831 items = (EnumItem *) repalloc(items, sizeof(EnumItem) * maxitems);
2832 }
2833 items[numitems].enum_oid = en->oid;
2834 items[numitems].sort_order = en->enumsortorder;
2835 numitems++;
2836 }
2837
2840
2841 /* Sort the items into OID order */
2842 qsort(items, numitems, sizeof(EnumItem), enum_oid_cmp);
2843
2844 /*
2845 * Here, we create a bitmap listing a subset of the enum's OIDs that are
2846 * known to be in order and can thus be compared with just OID comparison.
2847 *
2848 * The point of this is that the enum's initial OIDs were certainly in
2849 * order, so there is some subset that can be compared via OID comparison;
2850 * and we'd rather not do binary searches unnecessarily.
2851 *
2852 * This is somewhat heuristic, and might identify a subset of OIDs that
2853 * isn't exactly what the type started with. That's okay as long as the
2854 * subset is correctly sorted.
2855 */
2856 bitmap_base = InvalidOid;
2857 bitmap = NULL;
2858 bm_size = 1; /* only save sets of at least 2 OIDs */
2859
2860 for (start_pos = 0; start_pos < numitems - 1; start_pos++)
2861 {
2862 /*
2863 * Identify longest sorted subsequence starting at start_pos
2864 */
2866 int this_bm_size = 1;
2867 Oid start_oid = items[start_pos].enum_oid;
2868 float4 prev_order = items[start_pos].sort_order;
2869 int i;
2870
2871 for (i = start_pos + 1; i < numitems; i++)
2872 {
2873 Oid offset;
2874
2875 offset = items[i].enum_oid - start_oid;
2876 /* quit if bitmap would be too large; cutoff is arbitrary */
2877 if (offset >= 8192)
2878 break;
2879 /* include the item if it's in-order */
2880 if (items[i].sort_order > prev_order)
2881 {
2882 prev_order = items[i].sort_order;
2883 this_bitmap = bms_add_member(this_bitmap, (int) offset);
2884 this_bm_size++;
2885 }
2886 }
2887
2888 /* Remember it if larger than previous best */
2889 if (this_bm_size > bm_size)
2890 {
2891 bms_free(bitmap);
2892 bitmap_base = start_oid;
2893 bitmap = this_bitmap;
2895 }
2896 else
2898
2899 /*
2900 * Done if it's not possible to find a longer sequence in the rest of
2901 * the list. In typical cases this will happen on the first
2902 * iteration, which is why we create the bitmaps on the fly instead of
2903 * doing a second pass over the list.
2904 */
2905 if (bm_size >= (numitems - start_pos - 1))
2906 break;
2907 }
2908
2909 /* OK, copy the data into CacheMemoryContext */
2912 palloc(offsetof(TypeCacheEnumData, enum_values) +
2913 numitems * sizeof(EnumItem));
2914 enumdata->bitmap_base = bitmap_base;
2915 enumdata->sorted_values = bms_copy(bitmap);
2916 enumdata->num_values = numitems;
2917 memcpy(enumdata->enum_values, items, numitems * sizeof(EnumItem));
2919
2920 pfree(items);
2921 bms_free(bitmap);
2922
2923 /* And link the finished cache struct into the typcache */
2924 if (tcache->enumData != NULL)
2925 pfree(tcache->enumData);
2926 tcache->enumData = enumdata;
2927}
2928
2929/*
2930 * Locate the EnumItem with the given OID, if present
2931 */
2932static EnumItem *
2934{
2935 EnumItem srch;
2936
2937 /* On some versions of Solaris, bsearch of zero items dumps core */
2938 if (enumdata->num_values <= 0)
2939 return NULL;
2940
2941 srch.enum_oid = arg;
2942 return bsearch(&srch, enumdata->enum_values, enumdata->num_values,
2943 sizeof(EnumItem), enum_oid_cmp);
2944}
2945
2946/*
2947 * qsort comparison function for OID-ordered EnumItems
2948 */
2949static int
2950enum_oid_cmp(const void *left, const void *right)
2951{
2952 const EnumItem *l = (const EnumItem *) left;
2953 const EnumItem *r = (const EnumItem *) right;
2954
2955 return pg_cmp_u32(l->enum_oid, r->enum_oid);
2956}
2957
2958/*
2959 * Copy 'tupdesc' into newly allocated shared memory in 'area', set its typmod
2960 * to the given value and return a dsa_pointer.
2961 */
2962static dsa_pointer
2964{
2966 TupleDesc shared;
2967
2968 shared_dp = dsa_allocate(area, TupleDescSize(tupdesc));
2969 shared = (TupleDesc) dsa_get_address(area, shared_dp);
2970 TupleDescCopy(shared, tupdesc);
2971 shared->tdtypmod = typmod;
2972
2973 return shared_dp;
2974}
2975
2976/*
2977 * If we are attached to a SharedRecordTypmodRegistry, use it to find or
2978 * create a shared TupleDesc that matches 'tupdesc'. Otherwise return NULL.
2979 * Tuple descriptors returned by this function are not reference counted, and
2980 * will exist at least as long as the current backend remained attached to the
2981 * current session.
2982 */
2983static TupleDesc
2985{
2991 bool found;
2992 uint32 typmod;
2993
2994 /* If not even attached, nothing to do. */
2996 return NULL;
2997
2998 /* Try to find a matching tuple descriptor in the record table. */
2999 key.shared = false;
3000 key.u.local_tupdesc = tupdesc;
3004 {
3005 Assert(record_table_entry->key.shared);
3008 result = (TupleDesc)
3010 record_table_entry->key.u.shared_tupdesc);
3011 Assert(result->tdrefcount == -1);
3012
3013 return result;
3014 }
3015
3016 /* Allocate a new typmod number. This will be wasted if we error out. */
3017 typmod = (int)
3019 1);
3020
3021 /* Copy the TupleDesc into shared memory. */
3022 shared_dp = share_tupledesc(CurrentSession->area, tupdesc, typmod);
3023
3024 /*
3025 * Create an entry in the typmod table so that others will understand this
3026 * typmod number.
3027 */
3028 PG_TRY();
3029 {
3032 &typmod, &found);
3033 if (found)
3034 elog(ERROR, "cannot create duplicate shared record typmod");
3035 }
3036 PG_CATCH();
3037 {
3039 PG_RE_THROW();
3040 }
3041 PG_END_TRY();
3042 typmod_table_entry->typmod = typmod;
3043 typmod_table_entry->shared_tupdesc = shared_dp;
3046
3047 /*
3048 * Finally create an entry in the record table so others with matching
3049 * tuple descriptors can reuse the typmod.
3050 */
3053 &found);
3054 if (found)
3055 {
3056 /*
3057 * Someone concurrently inserted a matching tuple descriptor since the
3058 * first time we checked. Use that one instead.
3059 */
3062
3063 /* Might as well free up the space used by the one we created. */
3065 &typmod);
3066 Assert(found);
3068
3069 /* Return the one we found. */
3070 Assert(record_table_entry->key.shared);
3071 result = (TupleDesc)
3073 record_table_entry->key.u.shared_tupdesc);
3074 Assert(result->tdrefcount == -1);
3075
3076 return result;
3077 }
3078
3079 /* Store it and return it. */
3080 record_table_entry->key.shared = true;
3081 record_table_entry->key.u.shared_tupdesc = shared_dp;
3084 result = (TupleDesc)
3086 Assert(result->tdrefcount == -1);
3087
3088 return result;
3089}
3090
3091/*
3092 * On-DSM-detach hook to forget about the current shared record typmod
3093 * infrastructure. This is currently used by both leader and workers.
3094 */
3095static void
3111
3112/*
3113 * Insert RelIdToTypeIdCacheHash entry if needed.
3114 */
3115static void
3117{
3118 /* Immediately quit for non-composite types */
3119 if (typentry->typtype != TYPTYPE_COMPOSITE)
3120 return;
3121
3122 /* typrelid should be given for composite types */
3123 Assert(OidIsValid(typentry->typrelid));
3124
3125 /*
3126 * Insert a RelIdToTypeIdCacheHash entry if the typentry have any
3127 * information indicating it should be here.
3128 */
3129 if ((typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA) ||
3130 (typentry->flags & TCFLAGS_OPERATOR_FLAGS) ||
3131 typentry->tupDesc != NULL)
3132 {
3134 bool found;
3135
3137 &typentry->typrelid,
3138 HASH_ENTER, &found);
3139 relentry->relid = typentry->typrelid;
3140 relentry->composite_typid = typentry->type_id;
3141 }
3142}
3143
3144/*
3145 * Delete entry RelIdToTypeIdCacheHash if needed after resetting of the
3146 * TCFLAGS_HAVE_PG_TYPE_DATA flag, or any of TCFLAGS_OPERATOR_FLAGS,
3147 * or tupDesc.
3148 */
3149static void
3151{
3152#ifdef USE_ASSERT_CHECKING
3153 int i;
3154 bool is_in_progress = false;
3155
3156 for (i = 0; i < in_progress_list_len; i++)
3157 {
3158 if (in_progress_list[i] == typentry->type_id)
3159 {
3160 is_in_progress = true;
3161 break;
3162 }
3163 }
3164#endif
3165
3166 /* Immediately quit for non-composite types */
3167 if (typentry->typtype != TYPTYPE_COMPOSITE)
3168 return;
3169
3170 /* typrelid should be given for composite types */
3171 Assert(OidIsValid(typentry->typrelid));
3172
3173 /*
3174 * Delete a RelIdToTypeIdCacheHash entry if the typentry doesn't have any
3175 * information indicating entry should be still there.
3176 */
3177 if (!(typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA) &&
3178 !(typentry->flags & TCFLAGS_OPERATOR_FLAGS) &&
3179 typentry->tupDesc == NULL)
3180 {
3181 bool found;
3182
3184 &typentry->typrelid,
3185 HASH_REMOVE, &found);
3186 Assert(found || is_in_progress);
3187 }
3188 else
3189 {
3190#ifdef USE_ASSERT_CHECKING
3191 /*
3192 * In assert-enabled builds otherwise check for RelIdToTypeIdCacheHash
3193 * entry if it should exist.
3194 */
3195 bool found;
3196
3197 if (!is_in_progress)
3198 {
3200 &typentry->typrelid,
3201 HASH_FIND, &found);
3202 Assert(found);
3203 }
3204#endif
3205 }
3206}
3207
3208/*
3209 * Add possibly missing RelIdToTypeId entries related to TypeCacheHash
3210 * entries, marked as in-progress by lookup_type_cache(). It may happen
3211 * in case of an error or interruption during the lookup_type_cache() call.
3212 */
3213static void
3215{
3216 int i;
3217
3218 for (i = 0; i < in_progress_list_len; i++)
3219 {
3220 TypeCacheEntry *typentry;
3221
3224 HASH_FIND, NULL);
3225 if (typentry)
3227 }
3228
3230}
3231
3232void
3237
3238void
static void pg_atomic_init_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
Definition atomics.h:214
static uint32 pg_atomic_fetch_add_u32(volatile pg_atomic_uint32 *ptr, int32 add_)
Definition atomics.h:361
Bitmapset * bms_make_singleton(int x)
Definition bitmapset.c:217
void bms_free(Bitmapset *a)
Definition bitmapset.c:240
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:645
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:934
Bitmapset * bms_copy(const Bitmapset *a)
Definition bitmapset.c:123
#define TextDatumGetCString(d)
Definition builtins.h:99
#define NameStr(name)
Definition c.h:894
#define RegProcedureIsValid(p)
Definition c.h:921
#define Assert(condition)
Definition c.h:1002
#define FLEXIBLE_ARRAY_MEMBER
Definition c.h:617
int32_t int32
Definition c.h:679
uint64_t uint64
Definition c.h:684
uint32_t uint32
Definition c.h:683
float float4
Definition c.h:772
#define MemSet(start, val, len)
Definition c.h:1147
#define OidIsValid(objectId)
Definition c.h:917
size_t Size
Definition c.h:748
void CreateCacheMemoryContext(void)
Definition catcache.c:726
uint32 result
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
bool contain_volatile_functions(Node *clause)
Definition clauses.c:567
void * dsa_get_address(dsa_area *area, dsa_pointer dp)
Definition dsa.c:954
void dsa_free(dsa_area *area, dsa_pointer dp)
Definition dsa.c:838
uint64 dsa_pointer
Definition dsa.h:62
#define dsa_allocate(area, size)
Definition dsa.h:109
bool dshash_delete_key(dshash_table *hash_table, const void *key)
Definition dshash.c:524
void dshash_memcpy(void *dest, const void *src, size_t size, void *arg)
Definition dshash.c:611
void dshash_release_lock(dshash_table *hash_table, void *entry)
Definition dshash.c:579
void dshash_detach(dshash_table *hash_table)
Definition dshash.c:311
void * dshash_find(dshash_table *hash_table, const void *key, bool exclusive)
Definition dshash.c:394
dshash_table_handle dshash_get_hash_table_handle(dshash_table *hash_table)
Definition dshash.c:371
dshash_table * dshash_attach(dsa_area *area, const dshash_parameters *params, dshash_table_handle handle, void *arg)
Definition dshash.c:274
dshash_hash dshash_memhash(const void *v, size_t size, void *arg)
Definition dshash.c:602
dshash_table * dshash_create(dsa_area *area, const dshash_parameters *params, void *arg)
Definition dshash.c:210
int dshash_memcmp(const void *a, const void *b, size_t size, void *arg)
Definition dshash.c:593
dsa_pointer dshash_table_handle
Definition dshash.h:24
#define dshash_find_or_insert(hash_table, key, found)
Definition dshash.h:109
void on_dsm_detach(dsm_segment *seg, on_dsm_detach_callback function, Datum arg)
Definition dsm.c:1140
void hash_seq_init_with_hash_value(HASH_SEQ_STATUS *status, HTAB *hashp, uint32 hashvalue)
Definition dynahash.c:1337
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition dynahash.c:889
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
Definition dynahash.c:360
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition dynahash.c:1352
uint32 get_hash_value(HTAB *hashp, const void *keyPtr)
Definition dynahash.c:845
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition dynahash.c:1317
Datum arg
Definition elog.c:1323
int errcode(int sqlerrcode)
Definition elog.c:875
#define PG_RE_THROW()
Definition elog.h:407
#define PG_TRY(...)
Definition elog.h:374
#define PG_END_TRY(...)
Definition elog.h:399
#define ERROR
Definition elog.h:40
#define PG_CATCH(...)
Definition elog.h:384
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition execExpr.c:143
@ DOM_CONSTRAINT_CHECK
Definition execnodes.h:1087
@ DOM_CONSTRAINT_NOTNULL
Definition execnodes.h:1086
#define palloc_array(type, count)
Definition fe_memutils.h:91
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition fmgr.c:139
char * format_type_be(Oid type_oid)
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:604
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:515
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
#define HASHSTANDARD_PROC
Definition hash.h:355
#define HASHEXTENDED_PROC
Definition hash.h:356
@ HASH_FIND
Definition hsearch.h:108
@ HASH_REMOVE
Definition hsearch.h:110
@ HASH_ENTER
Definition hsearch.h:109
#define HASH_ELEM
Definition hsearch.h:90
#define HASH_COMPARE
Definition hsearch.h:94
#define HASH_FUNCTION
Definition hsearch.h:93
#define HASH_BLOBS
Definition hsearch.h:92
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
static Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
#define IsParallelWorker()
Definition parallel.h:62
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition indexcmds.c:2381
long val
Definition informix.c:689
#define INJECTION_POINT(name, arg)
static int pg_cmp_u32(uint32 a, uint32 b)
Definition int.h:719
void CacheRegisterSyscacheCallback(SysCacheIdentifier cacheid, SyscacheCallbackFunction func, Datum arg)
Definition inval.c:1813
void CacheRegisterRelcacheCallback(RelcacheCallbackFunction func, Datum arg)
Definition inval.c:1855
int b
Definition isn.c:74
int a
Definition isn.c:73
int i
Definition isn.c:77
List * lappend(List *list, void *datum)
Definition list.c:339
List * lcons(void *datum, List *list)
Definition list.c:495
#define AccessShareLock
Definition lockdefs.h:36
Oid get_opclass_input_type(Oid opclass)
Definition lsyscache.c:1464
Oid get_opclass_family(Oid opclass)
Definition lsyscache.c:1442
Oid get_multirange_range(Oid multirangeOid)
Definition lsyscache.c:3844
Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
Definition lsyscache.c:1022
RegProcedure get_opcode(Oid opno)
Definition lsyscache.c:1585
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition lsyscache.c:170
Oid get_base_element_type(Oid typid)
Definition lsyscache.c:3148
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition lsyscache.c:2854
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition mcxt.c:1235
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition mcxt.c:1269
char * pstrdup(const char *in)
Definition mcxt.c:1910
void MemoryContextRegisterResetCallback(MemoryContext context, MemoryContextCallback *cb)
Definition mcxt.c:585
void MemoryContextSetParent(MemoryContext context, MemoryContext new_parent)
Definition mcxt.c:689
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1635
void pfree(void *pointer)
Definition mcxt.c:1619
MemoryContext TopMemoryContext
Definition mcxt.c:167
void * palloc(Size size)
Definition mcxt.c:1390
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
MemoryContext CacheMemoryContext
Definition mcxt.c:170
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:475
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_SMALL_SIZES
Definition memutils.h:170
#define BTORDER_PROC
Definition nbtree.h:717
#define copyObject(obj)
Definition nodes.h:230
#define makeNode(_type_)
Definition nodes.h:159
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
#define repalloc0_array(pointer, type, oldcount, count)
Definition palloc.h:122
FormData_pg_attribute * Form_pg_attribute
static uint32 pg_nextpower2_32(uint32 num)
END_CATALOG_STRUCT typedef FormData_pg_constraint * Form_pg_constraint
const void * data
END_CATALOG_STRUCT typedef FormData_pg_enum * Form_pg_enum
Definition pg_enum.h:48
#define lfirst(lc)
Definition pg_list.h:172
#define NIL
Definition pg_list.h:68
#define foreach_node(type, var, lst)
Definition pg_list.h:528
END_CATALOG_STRUCT typedef FormData_pg_range * Form_pg_range
Definition pg_range.h:71
END_CATALOG_STRUCT typedef FormData_pg_type * Form_pg_type
Definition pg_type.h:265
Expr * expression_planner(Expr *expr)
Definition planner.c:7010
#define qsort(a, b, c, d)
Definition port.h:496
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
#define PointerGetDatum(X)
Definition postgres.h:354
#define InvalidOid
unsigned int Oid
char * c
static int fb(int x)
tree ctl
Definition radixtree.h:1838
void * stringToNode(const char *str)
Definition read.c:90
#define RelationGetDescr(relation)
Definition rel.h:542
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition scankey.c:76
Session * CurrentSession
Definition session.c:48
void relation_close(Relation relation, LOCKMODE lockmode)
Definition relation.c:206
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:48
#define BTGreaterStrategyNumber
Definition stratnum.h:33
#define HTEqualStrategyNumber
Definition stratnum.h:41
#define BTLessStrategyNumber
Definition stratnum.h:29
#define BTEqualStrategyNumber
Definition stratnum.h:31
MemoryContext dccContext
Definition typcache.c:142
DomainConstraintType constrainttype
Definition execnodes.h:1093
ExprState * check_exprstate
Definition execnodes.h:1096
float4 sort_order
Definition typcache.c:150
Oid enum_oid
Definition typcache.c:149
Oid fn_oid
Definition fmgr.h:59
Size keysize
Definition hsearch.h:69
Definition pg_list.h:54
Definition nodes.h:133
TupleDesc tupdesc
Definition typcache.c:174
Form_pg_class rd_rel
Definition rel.h:111
dsm_segment * segment
Definition session.h:27
dshash_table * shared_record_table
Definition session.h:32
struct SharedRecordTypmodRegistry * shared_typmod_registry
Definition session.h:31
dsa_area * area
Definition session.h:28
dshash_table * shared_typmod_table
Definition session.h:33
SharedRecordTableKey key
Definition typcache.c:213
TupleDesc local_tupdesc
Definition typcache.c:201
union SharedRecordTableKey::@34 u
dsa_pointer shared_tupdesc
Definition typcache.c:202
dshash_table_handle typmod_table_handle
Definition typcache.c:186
pg_atomic_uint32 next_typmod
Definition typcache.c:188
dshash_table_handle record_table_handle
Definition typcache.c:184
dsa_pointer shared_tupdesc
Definition typcache.c:223
int32 tdtypmod
Definition tupdesc.h:152
uint32 type_id_hash
Definition typcache.h:36
uint64 tupDesc_identifier
Definition typcache.h:91
FmgrInfo hash_proc_finfo
Definition typcache.h:78
int32 domainBaseTypmod
Definition typcache.h:116
Oid hash_extended_proc
Definition typcache.h:67
FmgrInfo rng_cmp_proc_finfo
Definition typcache.h:102
FmgrInfo cmp_proc_finfo
Definition typcache.h:77
struct TypeCacheEntry * rngelemtype
Definition typcache.h:99
TupleDesc tupDesc
Definition typcache.h:90
FmgrInfo hash_extended_proc_finfo
Definition typcache.h:79
DomainConstraintCache * domainData
Definition typcache.h:122
struct TypeCacheEntry * rngtype
Definition typcache.h:109
FmgrInfo rng_subdiff_finfo
Definition typcache.h:104
FmgrInfo eq_opr_finfo
Definition typcache.h:76
Oid btree_opintype
Definition typcache.h:59
struct TypeCacheEnumData * enumData
Definition typcache.h:131
struct TypeCacheEntry * nextDomain
Definition typcache.h:134
FmgrInfo rng_canonical_finfo
Definition typcache.h:103
Oid hash_opintype
Definition typcache.h:61
char typstorage
Definition typcache.h:42
Bitmapset * sorted_values
Definition typcache.c:156
EnumItem enum_values[FLEXIBLE_ARRAY_MEMBER]
Definition typcache.c:158
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
#define GetSysCacheHashValue1(cacheId, key1)
Definition syscache.h:118
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40
static ItemArray items
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition tupdesc.c:336
void TupleDescCopy(TupleDesc dst, TupleDesc src)
Definition tupdesc.c:427
void DecrTupleDescRefCount(TupleDesc tupdesc)
Definition tupdesc.c:644
void FreeTupleDesc(TupleDesc tupdesc)
Definition tupdesc.c:569
void IncrTupleDescRefCount(TupleDesc tupdesc)
Definition tupdesc.c:626
uint32 hashRowType(TupleDesc desc)
Definition tupdesc.c:880
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition tupdesc.c:242
bool equalRowTypes(TupleDesc tupdesc1, TupleDesc tupdesc2)
Definition tupdesc.c:844
#define TupleDescSize(src)
Definition tupdesc.h:218
#define PinTupleDesc(tupdesc)
Definition tupdesc.h:234
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178
struct TupleDescData * TupleDesc
Definition tupdesc.h:163
bool DomainHasConstraints(Oid type_id, bool *has_volatile)
Definition typcache.c:1511
#define TCFLAGS_CHECKED_BTREE_OPCLASS
Definition typcache.c:100
#define TCFLAGS_CHECKED_HASH_OPCLASS
Definition typcache.c:101
static bool range_element_has_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1756
static void insert_rel_type_cache_if_needed(TypeCacheEntry *typentry)
Definition typcache.c:3116
void InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref, MemoryContext refctx, bool need_exprstate)
Definition typcache.c:1420
static TupleDesc lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
Definition typcache.c:1869
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition typcache.c:1963
static void TypeCacheOpcCallback(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
Definition typcache.c:2614
void SharedRecordTypmodRegistryAttach(SharedRecordTypmodRegistry *registry)
Definition typcache.c:2337
#define TCFLAGS_OPERATOR_FLAGS
Definition typcache.c:122
#define TCFLAGS_CHECKED_FIELD_PROPERTIES
Definition typcache.c:113
static void cache_range_element_properties(TypeCacheEntry *typentry)
Definition typcache.c:1772
#define TCFLAGS_HAVE_FIELD_COMPARE
Definition typcache.c:115
void AtEOXact_TypeCache(void)
Definition typcache.c:3233
#define TCFLAGS_DOMAIN_BASE_IS_COMPOSITE
Definition typcache.c:119
static void load_enum_cache_data(TypeCacheEntry *tcache)
Definition typcache.c:2778
static bool record_fields_have_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1635
static HTAB * RelIdToTypeIdCacheHash
Definition typcache.c:87
static EnumItem * find_enumitem(TypeCacheEnumData *enumdata, Oid arg)
Definition typcache.c:2933
static bool record_fields_have_extended_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1643
static TupleDesc find_or_make_matching_shared_tupledesc(TupleDesc tupdesc)
Definition typcache.c:2984
static int in_progress_list_maxlen
Definition typcache.c:228
static int32 NextRecordTypmod
Definition typcache.c:306
TupleDesc lookup_rowtype_tupdesc_domain(Oid type_id, int32 typmod, bool noError)
Definition typcache.c:2019
static Oid * in_progress_list
Definition typcache.c:226
static const dshash_parameters srtr_typmod_table_params
Definition typcache.c:285
static void delete_rel_type_cache_if_needed(TypeCacheEntry *typentry)
Definition typcache.c:3150
#define TCFLAGS_CHECKED_GT_OPR
Definition typcache.c:104
static bool multirange_element_has_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1796
static List * prep_domain_constraints(List *constraints, MemoryContext execctx)
Definition typcache.c:1382
TupleDesc lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
Definition typcache.c:1980
static bool record_fields_have_equality(TypeCacheEntry *typentry)
Definition typcache.c:1619
#define TCFLAGS_CHECKED_LT_OPR
Definition typcache.c:103
#define TCFLAGS_CHECKED_HASH_PROC
Definition typcache.c:106
static void dccref_deletion_callback(void *arg)
Definition typcache.c:1361
#define TCFLAGS_HAVE_FIELD_EQUALITY
Definition typcache.c:114
static void InvalidateCompositeTypeCacheEntry(TypeCacheEntry *typentry)
Definition typcache.c:2406
void SharedRecordTypmodRegistryInit(SharedRecordTypmodRegistry *registry, dsm_segment *segment, dsa_area *area)
Definition typcache.c:2238
static int dcs_cmp(const void *a, const void *b)
Definition typcache.c:1337
static bool array_element_has_extended_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1581
static int shared_record_table_compare(const void *a, const void *b, size_t size, void *arg)
Definition typcache.c:234
static bool array_element_has_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1573
static void load_multirangetype_info(TypeCacheEntry *typentry)
Definition typcache.c:1080
static uint32 type_cache_syshash(const void *key, Size keysize)
Definition typcache.c:362
#define TCFLAGS_CHECKED_CMP_PROC
Definition typcache.c:105
#define TCFLAGS_HAVE_ELEM_EXTENDED_HASHING
Definition typcache.c:112
static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1804
static int in_progress_list_len
Definition typcache.c:227
static bool array_element_has_equality(TypeCacheEntry *typentry)
Definition typcache.c:1557
static dsa_pointer share_tupledesc(dsa_area *area, TupleDesc tupdesc, uint32 typmod)
Definition typcache.c:2963
static void load_rangetype_info(TypeCacheEntry *typentry)
Definition typcache.c:1022
uint64 assign_record_type_identifier(Oid type_id, int32 typmod)
Definition typcache.c:2175
static RecordCacheArrayEntry * RecordCacheArray
Definition typcache.c:304
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1764
static HTAB * RecordCacheHash
Definition typcache.c:295
static bool enum_known_sorted(TypeCacheEnumData *enumdata, Oid arg)
Definition typcache.c:2676
static TypeCacheEntry * firstDomainTypeEntry
Definition typcache.c:96
void AtEOSubXact_TypeCache(void)
Definition typcache.c:3239
static void shared_record_typmod_registry_detach(dsm_segment *segment, Datum datum)
Definition typcache.c:3096
#define TCFLAGS_HAVE_ELEM_HASHING
Definition typcache.c:111
#define TCFLAGS_CHECKED_HASH_EXTENDED_PROC
Definition typcache.c:107
static void load_domaintype_info(TypeCacheEntry *typentry)
Definition typcache.c:1102
#define TCFLAGS_HAVE_ELEM_COMPARE
Definition typcache.c:110
static void TypeCacheRelCallback(Datum arg, Oid relid)
Definition typcache.c:2461
static void cache_array_element_properties(TypeCacheEntry *typentry)
Definition typcache.c:1589
static void TypeCacheTypCallback(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
Definition typcache.c:2557
size_t SharedRecordTypmodRegistryEstimate(void)
Definition typcache.c:2216
static void cache_multirange_element_properties(TypeCacheEntry *typentry)
Definition typcache.c:1812
#define TCFLAGS_CHECKED_ELEM_PROPERTIES
Definition typcache.c:108
static void TypeCacheConstrCallback(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
Definition typcache.c:2652
#define TCFLAGS_HAVE_ELEM_EQUALITY
Definition typcache.c:109
static bool array_element_has_compare(TypeCacheEntry *typentry)
Definition typcache.c:1565
#define TCFLAGS_HAVE_PG_TYPE_DATA
Definition typcache.c:99
static uint32 shared_record_table_hash(const void *a, size_t size, void *arg)
Definition typcache.c:260
int compare_values_of_enum(TypeCacheEntry *tcache, Oid arg1, Oid arg2)
Definition typcache.c:2705
#define TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS
Definition typcache.c:118
#define TCFLAGS_HAVE_FIELD_EXTENDED_HASHING
Definition typcache.c:117
static int32 RecordCacheArrayLen
Definition typcache.c:305
void assign_record_type_typmod(TupleDesc tupDesc)
Definition typcache.c:2083
static HTAB * TypeCacheHash
Definition typcache.c:79
static uint64 tupledesc_id_counter
Definition typcache.c:313
static bool record_fields_have_compare(TypeCacheEntry *typentry)
Definition typcache.c:1627
#define TCFLAGS_HAVE_FIELD_HASHING
Definition typcache.c:116
static int record_type_typmod_compare(const void *a, const void *b, size_t size)
Definition typcache.c:2067
static const dshash_parameters srtr_record_table_params
Definition typcache.c:275
TupleDesc lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod)
Definition typcache.c:1997
static int enum_oid_cmp(const void *left, const void *right)
Definition typcache.c:2950
static void finalize_in_progress_typentries(void)
Definition typcache.c:3214
static void decr_dcc_refcount(DomainConstraintCache *dcc)
Definition typcache.c:1350
#define TCFLAGS_CHECKED_EQ_OPR
Definition typcache.c:102
void UpdateDomainConstraintRef(DomainConstraintRef *ref)
Definition typcache.c:1458
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition typcache.c:389
static void ensure_record_cache_typmod_slot_exists(int32 typmod)
Definition typcache.c:1840
static void cache_record_field_properties(TypeCacheEntry *typentry)
Definition typcache.c:1651
static uint32 record_type_typmod_hash(const void *data, size_t size)
Definition typcache.c:2056
static void load_typcache_tupdesc(TypeCacheEntry *typentry)
Definition typcache.c:988
#define INVALID_TUPLEDESC_IDENTIFIER
Definition typcache.h:157
#define TYPECACHE_HASH_PROC_FINFO
Definition typcache.h:145
#define TYPECACHE_EQ_OPR
Definition typcache.h:138
#define TYPECACHE_HASH_OPFAMILY
Definition typcache.h:148
#define TYPECACHE_TUPDESC
Definition typcache.h:146
#define TYPECACHE_MULTIRANGE_INFO
Definition typcache.h:154
#define TYPECACHE_EQ_OPR_FINFO
Definition typcache.h:143
#define TYPECACHE_HASH_EXTENDED_PROC
Definition typcache.h:152
#define TYPECACHE_BTREE_OPFAMILY
Definition typcache.h:147
#define TYPECACHE_DOMAIN_BASE_INFO
Definition typcache.h:150
#define TYPECACHE_DOMAIN_CONSTR_INFO
Definition typcache.h:151
#define TYPECACHE_RANGE_INFO
Definition typcache.h:149
#define TYPECACHE_GT_OPR
Definition typcache.h:140
#define TYPECACHE_CMP_PROC
Definition typcache.h:141
#define TYPECACHE_LT_OPR
Definition typcache.h:139
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO
Definition typcache.h:153
#define TYPECACHE_CMP_PROC_FINFO
Definition typcache.h:144
#define TYPECACHE_HASH_PROC
Definition typcache.h:142