PostgreSQL Source Code  git master
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-2024, 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"
54 #include "catalog/pg_constraint.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"
69 #include "utils/inval.h"
70 #include "utils/lsyscache.h"
71 #include "utils/memutils.h"
72 #include "utils/rel.h"
73 #include "utils/syscache.h"
74 #include "utils/typcache.h"
75 
76 
77 /* The main type cache hashtable searched by lookup_type_cache */
78 static HTAB *TypeCacheHash = NULL;
79 
80 /* List of type cache entries for domain types */
82 
83 /* Private flag bits in the TypeCacheEntry.flags field */
84 #define TCFLAGS_HAVE_PG_TYPE_DATA 0x000001
85 #define TCFLAGS_CHECKED_BTREE_OPCLASS 0x000002
86 #define TCFLAGS_CHECKED_HASH_OPCLASS 0x000004
87 #define TCFLAGS_CHECKED_EQ_OPR 0x000008
88 #define TCFLAGS_CHECKED_LT_OPR 0x000010
89 #define TCFLAGS_CHECKED_GT_OPR 0x000020
90 #define TCFLAGS_CHECKED_CMP_PROC 0x000040
91 #define TCFLAGS_CHECKED_HASH_PROC 0x000080
92 #define TCFLAGS_CHECKED_HASH_EXTENDED_PROC 0x000100
93 #define TCFLAGS_CHECKED_ELEM_PROPERTIES 0x000200
94 #define TCFLAGS_HAVE_ELEM_EQUALITY 0x000400
95 #define TCFLAGS_HAVE_ELEM_COMPARE 0x000800
96 #define TCFLAGS_HAVE_ELEM_HASHING 0x001000
97 #define TCFLAGS_HAVE_ELEM_EXTENDED_HASHING 0x002000
98 #define TCFLAGS_CHECKED_FIELD_PROPERTIES 0x004000
99 #define TCFLAGS_HAVE_FIELD_EQUALITY 0x008000
100 #define TCFLAGS_HAVE_FIELD_COMPARE 0x010000
101 #define TCFLAGS_HAVE_FIELD_HASHING 0x020000
102 #define TCFLAGS_HAVE_FIELD_EXTENDED_HASHING 0x040000
103 #define TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS 0x080000
104 #define TCFLAGS_DOMAIN_BASE_IS_COMPOSITE 0x100000
105 
106 /* The flags associated with equality/comparison/hashing are all but these: */
107 #define TCFLAGS_OPERATOR_FLAGS \
108  (~(TCFLAGS_HAVE_PG_TYPE_DATA | \
109  TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS | \
110  TCFLAGS_DOMAIN_BASE_IS_COMPOSITE))
111 
112 /*
113  * Data stored about a domain type's constraints. Note that we do not create
114  * this struct for the common case of a constraint-less domain; we just set
115  * domainData to NULL to indicate that.
116  *
117  * Within a DomainConstraintCache, we store expression plan trees, but the
118  * check_exprstate fields of the DomainConstraintState nodes are just NULL.
119  * When needed, expression evaluation nodes are built by flat-copying the
120  * DomainConstraintState nodes and applying ExecInitExpr to check_expr.
121  * Such a node tree is not part of the DomainConstraintCache, but is
122  * considered to belong to a DomainConstraintRef.
123  */
125 {
126  List *constraints; /* list of DomainConstraintState nodes */
127  MemoryContext dccContext; /* memory context holding all associated data */
128  long dccRefCount; /* number of references to this struct */
129 };
130 
131 /* Private information to support comparisons of enum values */
132 typedef struct
133 {
134  Oid enum_oid; /* OID of one enum value */
135  float4 sort_order; /* its sort position */
136 } EnumItem;
137 
138 typedef struct TypeCacheEnumData
139 {
140  Oid bitmap_base; /* OID corresponding to bit 0 of bitmapset */
141  Bitmapset *sorted_values; /* Set of OIDs known to be in order */
142  int num_values; /* total number of values in enum */
145 
146 /*
147  * We use a separate table for storing the definitions of non-anonymous
148  * record types. Once defined, a record type will be remembered for the
149  * life of the backend. Subsequent uses of the "same" record type (where
150  * sameness means equalTupleDescs) will refer to the existing table entry.
151  *
152  * Stored record types are remembered in a linear array of TupleDescs,
153  * which can be indexed quickly with the assigned typmod. There is also
154  * a hash table to speed searches for matching TupleDescs.
155  */
156 
157 typedef struct RecordCacheEntry
158 {
161 
162 /*
163  * To deal with non-anonymous record types that are exchanged by backends
164  * involved in a parallel query, we also need a shared version of the above.
165  */
167 {
168  /* A hash table for finding a matching TupleDesc. */
170  /* A hash table for finding a TupleDesc by typmod. */
172  /* A source of new record typmod numbers. */
174 };
175 
176 /*
177  * When using shared tuple descriptors as hash table keys we need a way to be
178  * able to search for an equal shared TupleDesc using a backend-local
179  * TupleDesc. So we use this type which can hold either, and hash and compare
180  * functions that know how to handle both.
181  */
182 typedef struct SharedRecordTableKey
183 {
184  union
185  {
188  } u;
189  bool shared;
191 
192 /*
193  * The shared version of RecordCacheEntry. This lets us look up a typmod
194  * using a TupleDesc which may be in local or shared memory.
195  */
197 {
200 
201 /*
202  * An entry in SharedRecordTypmodRegistry's typmod table. This lets us look
203  * up a TupleDesc in shared memory using a typmod.
204  */
206 {
210 
211 /*
212  * A comparator function for SharedRecordTableKey.
213  */
214 static int
215 shared_record_table_compare(const void *a, const void *b, size_t size,
216  void *arg)
217 {
218  dsa_area *area = (dsa_area *) arg;
221  TupleDesc t1;
222  TupleDesc t2;
223 
224  if (k1->shared)
225  t1 = (TupleDesc) dsa_get_address(area, k1->u.shared_tupdesc);
226  else
227  t1 = k1->u.local_tupdesc;
228 
229  if (k2->shared)
230  t2 = (TupleDesc) dsa_get_address(area, k2->u.shared_tupdesc);
231  else
232  t2 = k2->u.local_tupdesc;
233 
234  return equalTupleDescs(t1, t2) ? 0 : 1;
235 }
236 
237 /*
238  * A hash function for SharedRecordTableKey.
239  */
240 static uint32
241 shared_record_table_hash(const void *a, size_t size, void *arg)
242 {
243  dsa_area *area = (dsa_area *) arg;
245  TupleDesc t;
246 
247  if (k->shared)
248  t = (TupleDesc) dsa_get_address(area, k->u.shared_tupdesc);
249  else
250  t = k->u.local_tupdesc;
251 
252  return hashTupleDesc(t);
253 }
254 
255 /* Parameters for SharedRecordTypmodRegistry's TupleDesc table. */
257  sizeof(SharedRecordTableKey), /* unused */
258  sizeof(SharedRecordTableEntry),
263 };
264 
265 /* Parameters for SharedRecordTypmodRegistry's typmod hash table. */
267  sizeof(uint32),
268  sizeof(SharedTypmodTableEntry),
273 };
274 
275 /* hashtable for recognizing registered record types */
276 static HTAB *RecordCacheHash = NULL;
277 
278 typedef struct RecordCacheArrayEntry
279 {
280  uint64 id;
283 
284 /* array of info about registered record types, indexed by assigned typmod */
286 static int32 RecordCacheArrayLen = 0; /* allocated length of above array */
287 static int32 NextRecordTypmod = 0; /* number of entries used */
288 
289 /*
290  * Process-wide counter for generating unique tupledesc identifiers.
291  * Zero and one (INVALID_TUPLEDESC_IDENTIFIER) aren't allowed to be chosen
292  * as identifiers, so we start the counter at INVALID_TUPLEDESC_IDENTIFIER.
293  */
295 
296 static void load_typcache_tupdesc(TypeCacheEntry *typentry);
297 static void load_rangetype_info(TypeCacheEntry *typentry);
298 static void load_multirangetype_info(TypeCacheEntry *typentry);
299 static void load_domaintype_info(TypeCacheEntry *typentry);
300 static int dcs_cmp(const void *a, const void *b);
301 static void decr_dcc_refcount(DomainConstraintCache *dcc);
302 static void dccref_deletion_callback(void *arg);
303 static List *prep_domain_constraints(List *constraints, MemoryContext execctx);
304 static bool array_element_has_equality(TypeCacheEntry *typentry);
305 static bool array_element_has_compare(TypeCacheEntry *typentry);
306 static bool array_element_has_hashing(TypeCacheEntry *typentry);
308 static void cache_array_element_properties(TypeCacheEntry *typentry);
309 static bool record_fields_have_equality(TypeCacheEntry *typentry);
310 static bool record_fields_have_compare(TypeCacheEntry *typentry);
311 static bool record_fields_have_hashing(TypeCacheEntry *typentry);
313 static void cache_record_field_properties(TypeCacheEntry *typentry);
314 static bool range_element_has_hashing(TypeCacheEntry *typentry);
316 static void cache_range_element_properties(TypeCacheEntry *typentry);
317 static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
320 static void TypeCacheRelCallback(Datum arg, Oid relid);
321 static void TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue);
322 static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
323 static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue);
324 static void load_enum_cache_data(TypeCacheEntry *tcache);
325 static EnumItem *find_enumitem(TypeCacheEnumData *enumdata, Oid arg);
326 static int enum_oid_cmp(const void *left, const void *right);
328  Datum datum);
330 static dsa_pointer share_tupledesc(dsa_area *area, TupleDesc tupdesc,
331  uint32 typmod);
332 
333 
334 /*
335  * lookup_type_cache
336  *
337  * Fetch the type cache entry for the specified datatype, and make sure that
338  * all the fields requested by bits in 'flags' are valid.
339  *
340  * The result is never NULL --- we will ereport() if the passed type OID is
341  * invalid. Note however that we may fail to find one or more of the
342  * values requested by 'flags'; the caller needs to check whether the fields
343  * are InvalidOid or not.
344  */
346 lookup_type_cache(Oid type_id, int flags)
347 {
348  TypeCacheEntry *typentry;
349  bool found;
350 
351  if (TypeCacheHash == NULL)
352  {
353  /* First time through: initialize the hash table */
354  HASHCTL ctl;
355 
356  ctl.keysize = sizeof(Oid);
357  ctl.entrysize = sizeof(TypeCacheEntry);
358  TypeCacheHash = hash_create("Type information cache", 64,
359  &ctl, HASH_ELEM | HASH_BLOBS);
360 
361  /* Also set up callbacks for SI invalidations */
366 
367  /* Also make sure CacheMemoryContext exists */
368  if (!CacheMemoryContext)
370  }
371 
372  /* Try to look up an existing entry */
374  &type_id,
375  HASH_FIND, NULL);
376  if (typentry == NULL)
377  {
378  /*
379  * If we didn't find one, we want to make one. But first look up the
380  * pg_type row, just to make sure we don't make a cache entry for an
381  * invalid type OID. If the type OID is not valid, present a
382  * user-facing error, since some code paths such as domain_in() allow
383  * this function to be reached with a user-supplied OID.
384  */
385  HeapTuple tp;
386  Form_pg_type typtup;
387 
388  tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id));
389  if (!HeapTupleIsValid(tp))
390  ereport(ERROR,
391  (errcode(ERRCODE_UNDEFINED_OBJECT),
392  errmsg("type with OID %u does not exist", type_id)));
393  typtup = (Form_pg_type) GETSTRUCT(tp);
394  if (!typtup->typisdefined)
395  ereport(ERROR,
396  (errcode(ERRCODE_UNDEFINED_OBJECT),
397  errmsg("type \"%s\" is only a shell",
398  NameStr(typtup->typname))));
399 
400  /* Now make the typcache entry */
402  &type_id,
403  HASH_ENTER, &found);
404  Assert(!found); /* it wasn't there a moment ago */
405 
406  MemSet(typentry, 0, sizeof(TypeCacheEntry));
407 
408  /* These fields can never change, by definition */
409  typentry->type_id = type_id;
410  typentry->type_id_hash = GetSysCacheHashValue1(TYPEOID,
411  ObjectIdGetDatum(type_id));
412 
413  /* Keep this part in sync with the code below */
414  typentry->typlen = typtup->typlen;
415  typentry->typbyval = typtup->typbyval;
416  typentry->typalign = typtup->typalign;
417  typentry->typstorage = typtup->typstorage;
418  typentry->typtype = typtup->typtype;
419  typentry->typrelid = typtup->typrelid;
420  typentry->typsubscript = typtup->typsubscript;
421  typentry->typelem = typtup->typelem;
422  typentry->typcollation = typtup->typcollation;
423  typentry->flags |= TCFLAGS_HAVE_PG_TYPE_DATA;
424 
425  /* If it's a domain, immediately thread it into the domain cache list */
426  if (typentry->typtype == TYPTYPE_DOMAIN)
427  {
428  typentry->nextDomain = firstDomainTypeEntry;
429  firstDomainTypeEntry = typentry;
430  }
431 
432  ReleaseSysCache(tp);
433  }
434  else if (!(typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA))
435  {
436  /*
437  * We have an entry, but its pg_type row got changed, so reload the
438  * data obtained directly from pg_type.
439  */
440  HeapTuple tp;
441  Form_pg_type typtup;
442 
443  tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id));
444  if (!HeapTupleIsValid(tp))
445  ereport(ERROR,
446  (errcode(ERRCODE_UNDEFINED_OBJECT),
447  errmsg("type with OID %u does not exist", type_id)));
448  typtup = (Form_pg_type) GETSTRUCT(tp);
449  if (!typtup->typisdefined)
450  ereport(ERROR,
451  (errcode(ERRCODE_UNDEFINED_OBJECT),
452  errmsg("type \"%s\" is only a shell",
453  NameStr(typtup->typname))));
454 
455  /*
456  * Keep this part in sync with the code above. Many of these fields
457  * shouldn't ever change, particularly typtype, but copy 'em anyway.
458  */
459  typentry->typlen = typtup->typlen;
460  typentry->typbyval = typtup->typbyval;
461  typentry->typalign = typtup->typalign;
462  typentry->typstorage = typtup->typstorage;
463  typentry->typtype = typtup->typtype;
464  typentry->typrelid = typtup->typrelid;
465  typentry->typsubscript = typtup->typsubscript;
466  typentry->typelem = typtup->typelem;
467  typentry->typcollation = typtup->typcollation;
468  typentry->flags |= TCFLAGS_HAVE_PG_TYPE_DATA;
469 
470  ReleaseSysCache(tp);
471  }
472 
473  /*
474  * Look up opclasses if we haven't already and any dependent info is
475  * requested.
476  */
481  !(typentry->flags & TCFLAGS_CHECKED_BTREE_OPCLASS))
482  {
483  Oid opclass;
484 
485  opclass = GetDefaultOpClass(type_id, BTREE_AM_OID);
486  if (OidIsValid(opclass))
487  {
488  typentry->btree_opf = get_opclass_family(opclass);
489  typentry->btree_opintype = get_opclass_input_type(opclass);
490  }
491  else
492  {
493  typentry->btree_opf = typentry->btree_opintype = InvalidOid;
494  }
495 
496  /*
497  * Reset information derived from btree opclass. Note in particular
498  * that we'll redetermine the eq_opr even if we previously found one;
499  * this matters in case a btree opclass has been added to a type that
500  * previously had only a hash opclass.
501  */
502  typentry->flags &= ~(TCFLAGS_CHECKED_EQ_OPR |
507  }
508 
509  /*
510  * If we need to look up equality operator, and there's no btree opclass,
511  * force lookup of hash opclass.
512  */
513  if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
514  !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR) &&
515  typentry->btree_opf == InvalidOid)
516  flags |= TYPECACHE_HASH_OPFAMILY;
517 
522  !(typentry->flags & TCFLAGS_CHECKED_HASH_OPCLASS))
523  {
524  Oid opclass;
525 
526  opclass = GetDefaultOpClass(type_id, HASH_AM_OID);
527  if (OidIsValid(opclass))
528  {
529  typentry->hash_opf = get_opclass_family(opclass);
530  typentry->hash_opintype = get_opclass_input_type(opclass);
531  }
532  else
533  {
534  typentry->hash_opf = typentry->hash_opintype = InvalidOid;
535  }
536 
537  /*
538  * Reset information derived from hash opclass. We do *not* reset the
539  * eq_opr; if we already found one from the btree opclass, that
540  * decision is still good.
541  */
542  typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC |
544  typentry->flags |= TCFLAGS_CHECKED_HASH_OPCLASS;
545  }
546 
547  /*
548  * Look for requested operators and functions, if we haven't already.
549  */
550  if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
551  !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR))
552  {
553  Oid eq_opr = InvalidOid;
554 
555  if (typentry->btree_opf != InvalidOid)
556  eq_opr = get_opfamily_member(typentry->btree_opf,
557  typentry->btree_opintype,
558  typentry->btree_opintype,
560  if (eq_opr == InvalidOid &&
561  typentry->hash_opf != InvalidOid)
562  eq_opr = get_opfamily_member(typentry->hash_opf,
563  typentry->hash_opintype,
564  typentry->hash_opintype,
566 
567  /*
568  * If the proposed equality operator is array_eq or record_eq, check
569  * to see if the element type or column types support equality. If
570  * not, array_eq or record_eq would fail at runtime, so we don't want
571  * to report that the type has equality. (We can omit similar
572  * checking for ranges and multiranges because ranges can't be created
573  * in the first place unless their subtypes support equality.)
574  */
575  if (eq_opr == ARRAY_EQ_OP &&
576  !array_element_has_equality(typentry))
577  eq_opr = InvalidOid;
578  else if (eq_opr == RECORD_EQ_OP &&
579  !record_fields_have_equality(typentry))
580  eq_opr = InvalidOid;
581 
582  /* Force update of eq_opr_finfo only if we're changing state */
583  if (typentry->eq_opr != eq_opr)
584  typentry->eq_opr_finfo.fn_oid = InvalidOid;
585 
586  typentry->eq_opr = eq_opr;
587 
588  /*
589  * Reset info about hash functions whenever we pick up new info about
590  * equality operator. This is so we can ensure that the hash
591  * functions match the operator.
592  */
593  typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC |
595  typentry->flags |= TCFLAGS_CHECKED_EQ_OPR;
596  }
597  if ((flags & TYPECACHE_LT_OPR) &&
598  !(typentry->flags & TCFLAGS_CHECKED_LT_OPR))
599  {
600  Oid lt_opr = InvalidOid;
601 
602  if (typentry->btree_opf != InvalidOid)
603  lt_opr = get_opfamily_member(typentry->btree_opf,
604  typentry->btree_opintype,
605  typentry->btree_opintype,
607 
608  /*
609  * As above, make sure array_cmp or record_cmp will succeed; but again
610  * we need no special check for ranges or multiranges.
611  */
612  if (lt_opr == ARRAY_LT_OP &&
613  !array_element_has_compare(typentry))
614  lt_opr = InvalidOid;
615  else if (lt_opr == RECORD_LT_OP &&
616  !record_fields_have_compare(typentry))
617  lt_opr = InvalidOid;
618 
619  typentry->lt_opr = lt_opr;
620  typentry->flags |= TCFLAGS_CHECKED_LT_OPR;
621  }
622  if ((flags & TYPECACHE_GT_OPR) &&
623  !(typentry->flags & TCFLAGS_CHECKED_GT_OPR))
624  {
625  Oid gt_opr = InvalidOid;
626 
627  if (typentry->btree_opf != InvalidOid)
628  gt_opr = get_opfamily_member(typentry->btree_opf,
629  typentry->btree_opintype,
630  typentry->btree_opintype,
632 
633  /*
634  * As above, make sure array_cmp or record_cmp will succeed; but again
635  * we need no special check for ranges or multiranges.
636  */
637  if (gt_opr == ARRAY_GT_OP &&
638  !array_element_has_compare(typentry))
639  gt_opr = InvalidOid;
640  else if (gt_opr == RECORD_GT_OP &&
641  !record_fields_have_compare(typentry))
642  gt_opr = InvalidOid;
643 
644  typentry->gt_opr = gt_opr;
645  typentry->flags |= TCFLAGS_CHECKED_GT_OPR;
646  }
647  if ((flags & (TYPECACHE_CMP_PROC | TYPECACHE_CMP_PROC_FINFO)) &&
648  !(typentry->flags & TCFLAGS_CHECKED_CMP_PROC))
649  {
650  Oid cmp_proc = InvalidOid;
651 
652  if (typentry->btree_opf != InvalidOid)
653  cmp_proc = get_opfamily_proc(typentry->btree_opf,
654  typentry->btree_opintype,
655  typentry->btree_opintype,
656  BTORDER_PROC);
657 
658  /*
659  * As above, make sure array_cmp or record_cmp will succeed; but again
660  * we need no special check for ranges or multiranges.
661  */
662  if (cmp_proc == F_BTARRAYCMP &&
663  !array_element_has_compare(typentry))
664  cmp_proc = InvalidOid;
665  else if (cmp_proc == F_BTRECORDCMP &&
666  !record_fields_have_compare(typentry))
667  cmp_proc = InvalidOid;
668 
669  /* Force update of cmp_proc_finfo only if we're changing state */
670  if (typentry->cmp_proc != cmp_proc)
671  typentry->cmp_proc_finfo.fn_oid = InvalidOid;
672 
673  typentry->cmp_proc = cmp_proc;
674  typentry->flags |= TCFLAGS_CHECKED_CMP_PROC;
675  }
677  !(typentry->flags & TCFLAGS_CHECKED_HASH_PROC))
678  {
679  Oid hash_proc = InvalidOid;
680 
681  /*
682  * We insist that the eq_opr, if one has been determined, match the
683  * hash opclass; else report there is no hash function.
684  */
685  if (typentry->hash_opf != InvalidOid &&
686  (!OidIsValid(typentry->eq_opr) ||
687  typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
688  typentry->hash_opintype,
689  typentry->hash_opintype,
691  hash_proc = get_opfamily_proc(typentry->hash_opf,
692  typentry->hash_opintype,
693  typentry->hash_opintype,
695 
696  /*
697  * As above, make sure hash_array, hash_record, or hash_range will
698  * succeed.
699  */
700  if (hash_proc == F_HASH_ARRAY &&
701  !array_element_has_hashing(typentry))
702  hash_proc = InvalidOid;
703  else if (hash_proc == F_HASH_RECORD &&
704  !record_fields_have_hashing(typentry))
705  hash_proc = InvalidOid;
706  else if (hash_proc == F_HASH_RANGE &&
707  !range_element_has_hashing(typentry))
708  hash_proc = InvalidOid;
709 
710  /*
711  * Likewise for hash_multirange.
712  */
713  if (hash_proc == F_HASH_MULTIRANGE &&
715  hash_proc = InvalidOid;
716 
717  /* Force update of hash_proc_finfo only if we're changing state */
718  if (typentry->hash_proc != hash_proc)
719  typentry->hash_proc_finfo.fn_oid = InvalidOid;
720 
721  typentry->hash_proc = hash_proc;
722  typentry->flags |= TCFLAGS_CHECKED_HASH_PROC;
723  }
724  if ((flags & (TYPECACHE_HASH_EXTENDED_PROC |
727  {
728  Oid hash_extended_proc = InvalidOid;
729 
730  /*
731  * We insist that the eq_opr, if one has been determined, match the
732  * hash opclass; else report there is no hash function.
733  */
734  if (typentry->hash_opf != InvalidOid &&
735  (!OidIsValid(typentry->eq_opr) ||
736  typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
737  typentry->hash_opintype,
738  typentry->hash_opintype,
740  hash_extended_proc = get_opfamily_proc(typentry->hash_opf,
741  typentry->hash_opintype,
742  typentry->hash_opintype,
744 
745  /*
746  * As above, make sure hash_array_extended, hash_record_extended, or
747  * hash_range_extended will succeed.
748  */
749  if (hash_extended_proc == F_HASH_ARRAY_EXTENDED &&
751  hash_extended_proc = InvalidOid;
752  else if (hash_extended_proc == F_HASH_RECORD_EXTENDED &&
754  hash_extended_proc = InvalidOid;
755  else if (hash_extended_proc == F_HASH_RANGE_EXTENDED &&
757  hash_extended_proc = InvalidOid;
758 
759  /*
760  * Likewise for hash_multirange_extended.
761  */
762  if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
764  hash_extended_proc = InvalidOid;
765 
766  /* Force update of proc finfo only if we're changing state */
767  if (typentry->hash_extended_proc != hash_extended_proc)
769 
770  typentry->hash_extended_proc = hash_extended_proc;
772  }
773 
774  /*
775  * Set up fmgr lookup info as requested
776  *
777  * Note: we tell fmgr the finfo structures live in CacheMemoryContext,
778  * which is not quite right (they're really in the hash table's private
779  * memory context) but this will do for our purposes.
780  *
781  * Note: the code above avoids invalidating the finfo structs unless the
782  * referenced operator/function OID actually changes. This is to prevent
783  * unnecessary leakage of any subsidiary data attached to an finfo, since
784  * that would cause session-lifespan memory leaks.
785  */
786  if ((flags & TYPECACHE_EQ_OPR_FINFO) &&
787  typentry->eq_opr_finfo.fn_oid == InvalidOid &&
788  typentry->eq_opr != InvalidOid)
789  {
790  Oid eq_opr_func;
791 
792  eq_opr_func = get_opcode(typentry->eq_opr);
793  if (eq_opr_func != InvalidOid)
794  fmgr_info_cxt(eq_opr_func, &typentry->eq_opr_finfo,
796  }
797  if ((flags & TYPECACHE_CMP_PROC_FINFO) &&
798  typentry->cmp_proc_finfo.fn_oid == InvalidOid &&
799  typentry->cmp_proc != InvalidOid)
800  {
801  fmgr_info_cxt(typentry->cmp_proc, &typentry->cmp_proc_finfo,
803  }
804  if ((flags & TYPECACHE_HASH_PROC_FINFO) &&
805  typentry->hash_proc_finfo.fn_oid == InvalidOid &&
806  typentry->hash_proc != InvalidOid)
807  {
808  fmgr_info_cxt(typentry->hash_proc, &typentry->hash_proc_finfo,
810  }
811  if ((flags & TYPECACHE_HASH_EXTENDED_PROC_FINFO) &&
813  typentry->hash_extended_proc != InvalidOid)
814  {
816  &typentry->hash_extended_proc_finfo,
818  }
819 
820  /*
821  * If it's a composite type (row type), get tupdesc if requested
822  */
823  if ((flags & TYPECACHE_TUPDESC) &&
824  typentry->tupDesc == NULL &&
825  typentry->typtype == TYPTYPE_COMPOSITE)
826  {
827  load_typcache_tupdesc(typentry);
828  }
829 
830  /*
831  * If requested, get information about a range type
832  *
833  * This includes making sure that the basic info about the range element
834  * type is up-to-date.
835  */
836  if ((flags & TYPECACHE_RANGE_INFO) &&
837  typentry->typtype == TYPTYPE_RANGE)
838  {
839  if (typentry->rngelemtype == NULL)
840  load_rangetype_info(typentry);
841  else if (!(typentry->rngelemtype->flags & TCFLAGS_HAVE_PG_TYPE_DATA))
842  (void) lookup_type_cache(typentry->rngelemtype->type_id, 0);
843  }
844 
845  /*
846  * If requested, get information about a multirange type
847  */
848  if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
849  typentry->rngtype == NULL &&
850  typentry->typtype == TYPTYPE_MULTIRANGE)
851  {
852  load_multirangetype_info(typentry);
853  }
854 
855  /*
856  * If requested, get information about a domain type
857  */
858  if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
859  typentry->domainBaseType == InvalidOid &&
860  typentry->typtype == TYPTYPE_DOMAIN)
861  {
862  typentry->domainBaseTypmod = -1;
863  typentry->domainBaseType =
864  getBaseTypeAndTypmod(type_id, &typentry->domainBaseTypmod);
865  }
866  if ((flags & TYPECACHE_DOMAIN_CONSTR_INFO) &&
867  (typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
868  typentry->typtype == TYPTYPE_DOMAIN)
869  {
870  load_domaintype_info(typentry);
871  }
872 
873  return typentry;
874 }
875 
876 /*
877  * load_typcache_tupdesc --- helper routine to set up composite type's tupDesc
878  */
879 static void
881 {
882  Relation rel;
883 
884  if (!OidIsValid(typentry->typrelid)) /* should not happen */
885  elog(ERROR, "invalid typrelid for composite type %u",
886  typentry->type_id);
887  rel = relation_open(typentry->typrelid, AccessShareLock);
888  Assert(rel->rd_rel->reltype == typentry->type_id);
889 
890  /*
891  * Link to the tupdesc and increment its refcount (we assert it's a
892  * refcounted descriptor). We don't use IncrTupleDescRefCount() for this,
893  * because the reference mustn't be entered in the current resource owner;
894  * it can outlive the current query.
895  */
896  typentry->tupDesc = RelationGetDescr(rel);
897 
898  Assert(typentry->tupDesc->tdrefcount > 0);
899  typentry->tupDesc->tdrefcount++;
900 
901  /*
902  * In future, we could take some pains to not change tupDesc_identifier if
903  * the tupdesc didn't really change; but for now it's not worth it.
904  */
906 
908 }
909 
910 /*
911  * load_rangetype_info --- helper routine to set up range type information
912  */
913 static void
915 {
916  Form_pg_range pg_range;
917  HeapTuple tup;
918  Oid subtypeOid;
919  Oid opclassOid;
920  Oid canonicalOid;
921  Oid subdiffOid;
922  Oid opfamilyOid;
923  Oid opcintype;
924  Oid cmpFnOid;
925 
926  /* get information from pg_range */
927  tup = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(typentry->type_id));
928  /* should not fail, since we already checked typtype ... */
929  if (!HeapTupleIsValid(tup))
930  elog(ERROR, "cache lookup failed for range type %u",
931  typentry->type_id);
932  pg_range = (Form_pg_range) GETSTRUCT(tup);
933 
934  subtypeOid = pg_range->rngsubtype;
935  typentry->rng_collation = pg_range->rngcollation;
936  opclassOid = pg_range->rngsubopc;
937  canonicalOid = pg_range->rngcanonical;
938  subdiffOid = pg_range->rngsubdiff;
939 
940  ReleaseSysCache(tup);
941 
942  /* get opclass properties and look up the comparison function */
943  opfamilyOid = get_opclass_family(opclassOid);
944  opcintype = get_opclass_input_type(opclassOid);
945  typentry->rng_opfamily = opfamilyOid;
946 
947  cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype,
948  BTORDER_PROC);
949  if (!RegProcedureIsValid(cmpFnOid))
950  elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
951  BTORDER_PROC, opcintype, opcintype, opfamilyOid);
952 
953  /* set up cached fmgrinfo structs */
954  fmgr_info_cxt(cmpFnOid, &typentry->rng_cmp_proc_finfo,
956  if (OidIsValid(canonicalOid))
957  fmgr_info_cxt(canonicalOid, &typentry->rng_canonical_finfo,
959  if (OidIsValid(subdiffOid))
960  fmgr_info_cxt(subdiffOid, &typentry->rng_subdiff_finfo,
962 
963  /* Lastly, set up link to the element type --- this marks data valid */
964  typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
965 }
966 
967 /*
968  * load_multirangetype_info --- helper routine to set up multirange type
969  * information
970  */
971 static void
973 {
974  Oid rangetypeOid;
975 
976  rangetypeOid = get_multirange_range(typentry->type_id);
977  if (!OidIsValid(rangetypeOid))
978  elog(ERROR, "cache lookup failed for multirange type %u",
979  typentry->type_id);
980 
981  typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
982 }
983 
984 /*
985  * load_domaintype_info --- helper routine to set up domain constraint info
986  *
987  * Note: we assume we're called in a relatively short-lived context, so it's
988  * okay to leak data into the current context while scanning pg_constraint.
989  * We build the new DomainConstraintCache data in a context underneath
990  * CurrentMemoryContext, and reparent it under CacheMemoryContext when
991  * complete.
992  */
993 static void
995 {
996  Oid typeOid = typentry->type_id;
998  bool notNull = false;
999  DomainConstraintState **ccons;
1000  int cconslen;
1001  Relation conRel;
1002  MemoryContext oldcxt;
1003 
1004  /*
1005  * If we're here, any existing constraint info is stale, so release it.
1006  * For safety, be sure to null the link before trying to delete the data.
1007  */
1008  if (typentry->domainData)
1009  {
1010  dcc = typentry->domainData;
1011  typentry->domainData = NULL;
1012  decr_dcc_refcount(dcc);
1013  }
1014 
1015  /*
1016  * We try to optimize the common case of no domain constraints, so don't
1017  * create the dcc object and context until we find a constraint. Likewise
1018  * for the temp sorting array.
1019  */
1020  dcc = NULL;
1021  ccons = NULL;
1022  cconslen = 0;
1023 
1024  /*
1025  * Scan pg_constraint for relevant constraints. We want to find
1026  * constraints for not just this domain, but any ancestor domains, so the
1027  * outer loop crawls up the domain stack.
1028  */
1029  conRel = table_open(ConstraintRelationId, AccessShareLock);
1030 
1031  for (;;)
1032  {
1033  HeapTuple tup;
1034  HeapTuple conTup;
1035  Form_pg_type typTup;
1036  int nccons = 0;
1037  ScanKeyData key[1];
1038  SysScanDesc scan;
1039 
1040  tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid));
1041  if (!HeapTupleIsValid(tup))
1042  elog(ERROR, "cache lookup failed for type %u", typeOid);
1043  typTup = (Form_pg_type) GETSTRUCT(tup);
1044 
1045  if (typTup->typtype != TYPTYPE_DOMAIN)
1046  {
1047  /* Not a domain, so done */
1048  ReleaseSysCache(tup);
1049  break;
1050  }
1051 
1052  /* Test for NOT NULL Constraint */
1053  if (typTup->typnotnull)
1054  notNull = true;
1055 
1056  /* Look for CHECK Constraints on this domain */
1057  ScanKeyInit(&key[0],
1058  Anum_pg_constraint_contypid,
1059  BTEqualStrategyNumber, F_OIDEQ,
1060  ObjectIdGetDatum(typeOid));
1061 
1062  scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
1063  NULL, 1, key);
1064 
1065  while (HeapTupleIsValid(conTup = systable_getnext(scan)))
1066  {
1068  Datum val;
1069  bool isNull;
1070  char *constring;
1071  Expr *check_expr;
1073 
1074  /* Ignore non-CHECK constraints (presently, shouldn't be any) */
1075  if (c->contype != CONSTRAINT_CHECK)
1076  continue;
1077 
1078  /* Not expecting conbin to be NULL, but we'll test for it anyway */
1079  val = fastgetattr(conTup, Anum_pg_constraint_conbin,
1080  conRel->rd_att, &isNull);
1081  if (isNull)
1082  elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
1083  NameStr(typTup->typname), NameStr(c->conname));
1084 
1085  /* Convert conbin to C string in caller context */
1086  constring = TextDatumGetCString(val);
1087 
1088  /* Create the DomainConstraintCache object and context if needed */
1089  if (dcc == NULL)
1090  {
1091  MemoryContext cxt;
1092 
1094  "Domain constraints",
1096  dcc = (DomainConstraintCache *)
1098  dcc->constraints = NIL;
1099  dcc->dccContext = cxt;
1100  dcc->dccRefCount = 0;
1101  }
1102 
1103  /* Create node trees in DomainConstraintCache's context */
1104  oldcxt = MemoryContextSwitchTo(dcc->dccContext);
1105 
1106  check_expr = (Expr *) stringToNode(constring);
1107 
1108  /*
1109  * Plan the expression, since ExecInitExpr will expect that.
1110  *
1111  * Note: caching the result of expression_planner() is not very
1112  * good practice. Ideally we'd use a CachedExpression here so
1113  * that we would react promptly to, eg, changes in inlined
1114  * functions. However, because we don't support mutable domain
1115  * CHECK constraints, it's not really clear that it's worth the
1116  * extra overhead to do that.
1117  */
1118  check_expr = expression_planner(check_expr);
1119 
1122  r->name = pstrdup(NameStr(c->conname));
1123  r->check_expr = check_expr;
1124  r->check_exprstate = NULL;
1125 
1126  MemoryContextSwitchTo(oldcxt);
1127 
1128  /* Accumulate constraints in an array, for sorting below */
1129  if (ccons == NULL)
1130  {
1131  cconslen = 8;
1132  ccons = (DomainConstraintState **)
1133  palloc(cconslen * sizeof(DomainConstraintState *));
1134  }
1135  else if (nccons >= cconslen)
1136  {
1137  cconslen *= 2;
1138  ccons = (DomainConstraintState **)
1139  repalloc(ccons, cconslen * sizeof(DomainConstraintState *));
1140  }
1141  ccons[nccons++] = r;
1142  }
1143 
1144  systable_endscan(scan);
1145 
1146  if (nccons > 0)
1147  {
1148  /*
1149  * Sort the items for this domain, so that CHECKs are applied in a
1150  * deterministic order.
1151  */
1152  if (nccons > 1)
1153  qsort(ccons, nccons, sizeof(DomainConstraintState *), dcs_cmp);
1154 
1155  /*
1156  * Now attach them to the overall list. Use lcons() here because
1157  * constraints of parent domains should be applied earlier.
1158  */
1159  oldcxt = MemoryContextSwitchTo(dcc->dccContext);
1160  while (nccons > 0)
1161  dcc->constraints = lcons(ccons[--nccons], dcc->constraints);
1162  MemoryContextSwitchTo(oldcxt);
1163  }
1164 
1165  /* loop to next domain in stack */
1166  typeOid = typTup->typbasetype;
1167  ReleaseSysCache(tup);
1168  }
1169 
1170  table_close(conRel, AccessShareLock);
1171 
1172  /*
1173  * Only need to add one NOT NULL check regardless of how many domains in
1174  * the stack request it.
1175  */
1176  if (notNull)
1177  {
1179 
1180  /* Create the DomainConstraintCache object and context if needed */
1181  if (dcc == NULL)
1182  {
1183  MemoryContext cxt;
1184 
1186  "Domain constraints",
1188  dcc = (DomainConstraintCache *)
1190  dcc->constraints = NIL;
1191  dcc->dccContext = cxt;
1192  dcc->dccRefCount = 0;
1193  }
1194 
1195  /* Create node trees in DomainConstraintCache's context */
1196  oldcxt = MemoryContextSwitchTo(dcc->dccContext);
1197 
1199 
1201  r->name = pstrdup("NOT NULL");
1202  r->check_expr = NULL;
1203  r->check_exprstate = NULL;
1204 
1205  /* lcons to apply the nullness check FIRST */
1206  dcc->constraints = lcons(r, dcc->constraints);
1207 
1208  MemoryContextSwitchTo(oldcxt);
1209  }
1210 
1211  /*
1212  * If we made a constraint object, move it into CacheMemoryContext and
1213  * attach it to the typcache entry.
1214  */
1215  if (dcc)
1216  {
1218  typentry->domainData = dcc;
1219  dcc->dccRefCount++; /* count the typcache's reference */
1220  }
1221 
1222  /* Either way, the typcache entry's domain data is now valid. */
1224 }
1225 
1226 /*
1227  * qsort comparator to sort DomainConstraintState pointers by name
1228  */
1229 static int
1230 dcs_cmp(const void *a, const void *b)
1231 {
1232  const DomainConstraintState *const *ca = (const DomainConstraintState *const *) a;
1233  const DomainConstraintState *const *cb = (const DomainConstraintState *const *) b;
1234 
1235  return strcmp((*ca)->name, (*cb)->name);
1236 }
1237 
1238 /*
1239  * decr_dcc_refcount --- decrement a DomainConstraintCache's refcount,
1240  * and free it if no references remain
1241  */
1242 static void
1244 {
1245  Assert(dcc->dccRefCount > 0);
1246  if (--(dcc->dccRefCount) <= 0)
1248 }
1249 
1250 /*
1251  * Context reset/delete callback for a DomainConstraintRef
1252  */
1253 static void
1255 {
1257  DomainConstraintCache *dcc = ref->dcc;
1258 
1259  /* Paranoia --- be sure link is nulled before trying to release */
1260  if (dcc)
1261  {
1262  ref->constraints = NIL;
1263  ref->dcc = NULL;
1264  decr_dcc_refcount(dcc);
1265  }
1266 }
1267 
1268 /*
1269  * prep_domain_constraints --- prepare domain constraints for execution
1270  *
1271  * The expression trees stored in the DomainConstraintCache's list are
1272  * converted to executable expression state trees stored in execctx.
1273  */
1274 static List *
1276 {
1277  List *result = NIL;
1278  MemoryContext oldcxt;
1279  ListCell *lc;
1280 
1281  oldcxt = MemoryContextSwitchTo(execctx);
1282 
1283  foreach(lc, constraints)
1284  {
1286  DomainConstraintState *newr;
1287 
1289  newr->constrainttype = r->constrainttype;
1290  newr->name = r->name;
1291  newr->check_expr = r->check_expr;
1292  newr->check_exprstate = ExecInitExpr(r->check_expr, NULL);
1293 
1294  result = lappend(result, newr);
1295  }
1296 
1297  MemoryContextSwitchTo(oldcxt);
1298 
1299  return result;
1300 }
1301 
1302 /*
1303  * InitDomainConstraintRef --- initialize a DomainConstraintRef struct
1304  *
1305  * Caller must tell us the MemoryContext in which the DomainConstraintRef
1306  * lives. The ref will be cleaned up when that context is reset/deleted.
1307  *
1308  * Caller must also tell us whether it wants check_exprstate fields to be
1309  * computed in the DomainConstraintState nodes attached to this ref.
1310  * If it doesn't, we need not make a copy of the DomainConstraintState list.
1311  */
1312 void
1314  MemoryContext refctx, bool need_exprstate)
1315 {
1316  /* Look up the typcache entry --- we assume it survives indefinitely */
1318  ref->need_exprstate = need_exprstate;
1319  /* For safety, establish the callback before acquiring a refcount */
1320  ref->refctx = refctx;
1321  ref->dcc = NULL;
1323  ref->callback.arg = (void *) ref;
1325  /* Acquire refcount if there are constraints, and set up exported list */
1326  if (ref->tcache->domainData)
1327  {
1328  ref->dcc = ref->tcache->domainData;
1329  ref->dcc->dccRefCount++;
1330  if (ref->need_exprstate)
1332  ref->refctx);
1333  else
1334  ref->constraints = ref->dcc->constraints;
1335  }
1336  else
1337  ref->constraints = NIL;
1338 }
1339 
1340 /*
1341  * UpdateDomainConstraintRef --- recheck validity of domain constraint info
1342  *
1343  * If the domain's constraint set changed, ref->constraints is updated to
1344  * point at a new list of cached constraints.
1345  *
1346  * In the normal case where nothing happened to the domain, this is cheap
1347  * enough that it's reasonable (and expected) to check before *each* use
1348  * of the constraint info.
1349  */
1350 void
1352 {
1353  TypeCacheEntry *typentry = ref->tcache;
1354 
1355  /* Make sure typcache entry's data is up to date */
1356  if ((typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
1357  typentry->typtype == TYPTYPE_DOMAIN)
1358  load_domaintype_info(typentry);
1359 
1360  /* Transfer to ref object if there's new info, adjusting refcounts */
1361  if (ref->dcc != typentry->domainData)
1362  {
1363  /* Paranoia --- be sure link is nulled before trying to release */
1364  DomainConstraintCache *dcc = ref->dcc;
1365 
1366  if (dcc)
1367  {
1368  /*
1369  * Note: we just leak the previous list of executable domain
1370  * constraints. Alternatively, we could keep those in a child
1371  * context of ref->refctx and free that context at this point.
1372  * However, in practice this code path will be taken so seldom
1373  * that the extra bookkeeping for a child context doesn't seem
1374  * worthwhile; we'll just allow a leak for the lifespan of refctx.
1375  */
1376  ref->constraints = NIL;
1377  ref->dcc = NULL;
1378  decr_dcc_refcount(dcc);
1379  }
1380  dcc = typentry->domainData;
1381  if (dcc)
1382  {
1383  ref->dcc = dcc;
1384  dcc->dccRefCount++;
1385  if (ref->need_exprstate)
1387  ref->refctx);
1388  else
1389  ref->constraints = dcc->constraints;
1390  }
1391  }
1392 }
1393 
1394 /*
1395  * DomainHasConstraints --- utility routine to check if a domain has constraints
1396  *
1397  * This is defined to return false, not fail, if type is not a domain.
1398  */
1399 bool
1401 {
1402  TypeCacheEntry *typentry;
1403 
1404  /*
1405  * Note: a side effect is to cause the typcache's domain data to become
1406  * valid. This is fine since we'll likely need it soon if there is any.
1407  */
1408  typentry = lookup_type_cache(type_id, TYPECACHE_DOMAIN_CONSTR_INFO);
1409 
1410  return (typentry->domainData != NULL);
1411 }
1412 
1413 
1414 /*
1415  * array_element_has_equality and friends are helper routines to check
1416  * whether we should believe that array_eq and related functions will work
1417  * on the given array type or composite type.
1418  *
1419  * The logic above may call these repeatedly on the same type entry, so we
1420  * make use of the typentry->flags field to cache the results once known.
1421  * Also, we assume that we'll probably want all these facts about the type
1422  * if we want any, so we cache them all using only one lookup of the
1423  * component datatype(s).
1424  */
1425 
1426 static bool
1428 {
1429  if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1431  return (typentry->flags & TCFLAGS_HAVE_ELEM_EQUALITY) != 0;
1432 }
1433 
1434 static bool
1436 {
1437  if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1439  return (typentry->flags & TCFLAGS_HAVE_ELEM_COMPARE) != 0;
1440 }
1441 
1442 static bool
1444 {
1445  if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1447  return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1448 }
1449 
1450 static bool
1452 {
1453  if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1455  return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
1456 }
1457 
1458 static void
1460 {
1461  Oid elem_type = get_base_element_type(typentry->type_id);
1462 
1463  if (OidIsValid(elem_type))
1464  {
1465  TypeCacheEntry *elementry;
1466 
1467  elementry = lookup_type_cache(elem_type,
1472  if (OidIsValid(elementry->eq_opr))
1473  typentry->flags |= TCFLAGS_HAVE_ELEM_EQUALITY;
1474  if (OidIsValid(elementry->cmp_proc))
1475  typentry->flags |= TCFLAGS_HAVE_ELEM_COMPARE;
1476  if (OidIsValid(elementry->hash_proc))
1477  typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1478  if (OidIsValid(elementry->hash_extended_proc))
1480  }
1482 }
1483 
1484 /*
1485  * Likewise, some helper functions for composite types.
1486  */
1487 
1488 static bool
1490 {
1491  if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1493  return (typentry->flags & TCFLAGS_HAVE_FIELD_EQUALITY) != 0;
1494 }
1495 
1496 static bool
1498 {
1499  if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1501  return (typentry->flags & TCFLAGS_HAVE_FIELD_COMPARE) != 0;
1502 }
1503 
1504 static bool
1506 {
1507  if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1509  return (typentry->flags & TCFLAGS_HAVE_FIELD_HASHING) != 0;
1510 }
1511 
1512 static bool
1514 {
1515  if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1517  return (typentry->flags & TCFLAGS_HAVE_FIELD_EXTENDED_HASHING) != 0;
1518 }
1519 
1520 static void
1522 {
1523  /*
1524  * For type RECORD, we can't really tell what will work, since we don't
1525  * have access here to the specific anonymous type. Just assume that
1526  * equality and comparison will (we may get a failure at runtime). We
1527  * could also claim that hashing works, but then if code that has the
1528  * option between a comparison-based (sort-based) and a hash-based plan
1529  * chooses hashing, stuff could fail that would otherwise work if it chose
1530  * a comparison-based plan. In practice more types support comparison
1531  * than hashing.
1532  */
1533  if (typentry->type_id == RECORDOID)
1534  {
1535  typentry->flags |= (TCFLAGS_HAVE_FIELD_EQUALITY |
1537  }
1538  else if (typentry->typtype == TYPTYPE_COMPOSITE)
1539  {
1540  TupleDesc tupdesc;
1541  int newflags;
1542  int i;
1543 
1544  /* Fetch composite type's tupdesc if we don't have it already */
1545  if (typentry->tupDesc == NULL)
1546  load_typcache_tupdesc(typentry);
1547  tupdesc = typentry->tupDesc;
1548 
1549  /* Must bump the refcount while we do additional catalog lookups */
1550  IncrTupleDescRefCount(tupdesc);
1551 
1552  /* Have each property if all non-dropped fields have the property */
1553  newflags = (TCFLAGS_HAVE_FIELD_EQUALITY |
1557  for (i = 0; i < tupdesc->natts; i++)
1558  {
1559  TypeCacheEntry *fieldentry;
1560  Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
1561 
1562  if (attr->attisdropped)
1563  continue;
1564 
1565  fieldentry = lookup_type_cache(attr->atttypid,
1570  if (!OidIsValid(fieldentry->eq_opr))
1571  newflags &= ~TCFLAGS_HAVE_FIELD_EQUALITY;
1572  if (!OidIsValid(fieldentry->cmp_proc))
1573  newflags &= ~TCFLAGS_HAVE_FIELD_COMPARE;
1574  if (!OidIsValid(fieldentry->hash_proc))
1575  newflags &= ~TCFLAGS_HAVE_FIELD_HASHING;
1576  if (!OidIsValid(fieldentry->hash_extended_proc))
1578 
1579  /* We can drop out of the loop once we disprove all bits */
1580  if (newflags == 0)
1581  break;
1582  }
1583  typentry->flags |= newflags;
1584 
1585  DecrTupleDescRefCount(tupdesc);
1586  }
1587  else if (typentry->typtype == TYPTYPE_DOMAIN)
1588  {
1589  /* If it's domain over composite, copy base type's properties */
1590  TypeCacheEntry *baseentry;
1591 
1592  /* load up basetype info if we didn't already */
1593  if (typentry->domainBaseType == InvalidOid)
1594  {
1595  typentry->domainBaseTypmod = -1;
1596  typentry->domainBaseType =
1597  getBaseTypeAndTypmod(typentry->type_id,
1598  &typentry->domainBaseTypmod);
1599  }
1600  baseentry = lookup_type_cache(typentry->domainBaseType,
1605  if (baseentry->typtype == TYPTYPE_COMPOSITE)
1606  {
1608  typentry->flags |= baseentry->flags & (TCFLAGS_HAVE_FIELD_EQUALITY |
1612  }
1613  }
1615 }
1616 
1617 /*
1618  * Likewise, some helper functions for range and multirange types.
1619  *
1620  * We can borrow the flag bits for array element properties to use for range
1621  * element properties, since those flag bits otherwise have no use in a
1622  * range or multirange type's typcache entry.
1623  */
1624 
1625 static bool
1627 {
1628  if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1630  return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1631 }
1632 
1633 static bool
1635 {
1636  if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1638  return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
1639 }
1640 
1641 static void
1643 {
1644  /* load up subtype link if we didn't already */
1645  if (typentry->rngelemtype == NULL &&
1646  typentry->typtype == TYPTYPE_RANGE)
1647  load_rangetype_info(typentry);
1648 
1649  if (typentry->rngelemtype != NULL)
1650  {
1651  TypeCacheEntry *elementry;
1652 
1653  /* might need to calculate subtype's hash function properties */
1654  elementry = lookup_type_cache(typentry->rngelemtype->type_id,
1657  if (OidIsValid(elementry->hash_proc))
1658  typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1659  if (OidIsValid(elementry->hash_extended_proc))
1661  }
1663 }
1664 
1665 static bool
1667 {
1668  if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1670  return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1671 }
1672 
1673 static bool
1675 {
1676  if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1678  return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
1679 }
1680 
1681 static void
1683 {
1684  /* load up range link if we didn't already */
1685  if (typentry->rngtype == NULL &&
1686  typentry->typtype == TYPTYPE_MULTIRANGE)
1687  load_multirangetype_info(typentry);
1688 
1689  if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
1690  {
1691  TypeCacheEntry *elementry;
1692 
1693  /* might need to calculate subtype's hash function properties */
1694  elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
1697  if (OidIsValid(elementry->hash_proc))
1698  typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1699  if (OidIsValid(elementry->hash_extended_proc))
1701  }
1703 }
1704 
1705 /*
1706  * Make sure that RecordCacheArray and RecordIdentifierArray are large enough
1707  * to store 'typmod'.
1708  */
1709 static void
1711 {
1712  if (RecordCacheArray == NULL)
1713  {
1716  64 * sizeof(RecordCacheArrayEntry));
1717  RecordCacheArrayLen = 64;
1718  }
1719 
1720  if (typmod >= RecordCacheArrayLen)
1721  {
1722  int32 newlen = pg_nextpower2_32(typmod + 1);
1723 
1727  newlen);
1728  RecordCacheArrayLen = newlen;
1729  }
1730 }
1731 
1732 /*
1733  * lookup_rowtype_tupdesc_internal --- internal routine to lookup a rowtype
1734  *
1735  * Same API as lookup_rowtype_tupdesc_noerror, but the returned tupdesc
1736  * hasn't had its refcount bumped.
1737  */
1738 static TupleDesc
1739 lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
1740 {
1741  if (type_id != RECORDOID)
1742  {
1743  /*
1744  * It's a named composite type, so use the regular typcache.
1745  */
1746  TypeCacheEntry *typentry;
1747 
1748  typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
1749  if (typentry->tupDesc == NULL && !noError)
1750  ereport(ERROR,
1751  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1752  errmsg("type %s is not composite",
1753  format_type_be(type_id))));
1754  return typentry->tupDesc;
1755  }
1756  else
1757  {
1758  /*
1759  * It's a transient record type, so look in our record-type table.
1760  */
1761  if (typmod >= 0)
1762  {
1763  /* It is already in our local cache? */
1764  if (typmod < RecordCacheArrayLen &&
1765  RecordCacheArray[typmod].tupdesc != NULL)
1766  return RecordCacheArray[typmod].tupdesc;
1767 
1768  /* Are we attached to a shared record typmod registry? */
1770  {
1771  SharedTypmodTableEntry *entry;
1772 
1773  /* Try to find it in the shared typmod index. */
1775  &typmod, false);
1776  if (entry != NULL)
1777  {
1778  TupleDesc tupdesc;
1779 
1780  tupdesc = (TupleDesc)
1782  entry->shared_tupdesc);
1783  Assert(typmod == tupdesc->tdtypmod);
1784 
1785  /* We may need to extend the local RecordCacheArray. */
1787 
1788  /*
1789  * Our local array can now point directly to the TupleDesc
1790  * in shared memory, which is non-reference-counted.
1791  */
1792  RecordCacheArray[typmod].tupdesc = tupdesc;
1793  Assert(tupdesc->tdrefcount == -1);
1794 
1795  /*
1796  * We don't share tupdesc identifiers across processes, so
1797  * assign one locally.
1798  */
1800 
1802  entry);
1803 
1804  return RecordCacheArray[typmod].tupdesc;
1805  }
1806  }
1807  }
1808 
1809  if (!noError)
1810  ereport(ERROR,
1811  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1812  errmsg("record type has not been registered")));
1813  return NULL;
1814  }
1815 }
1816 
1817 /*
1818  * lookup_rowtype_tupdesc
1819  *
1820  * Given a typeid/typmod that should describe a known composite type,
1821  * return the tuple descriptor for the type. Will ereport on failure.
1822  * (Use ereport because this is reachable with user-specified OIDs,
1823  * for example from record_in().)
1824  *
1825  * Note: on success, we increment the refcount of the returned TupleDesc,
1826  * and log the reference in CurrentResourceOwner. Caller must call
1827  * ReleaseTupleDesc when done using the tupdesc. (There are some
1828  * cases in which the returned tupdesc is not refcounted, in which
1829  * case PinTupleDesc/ReleaseTupleDesc are no-ops; but in these cases
1830  * the tupdesc is guaranteed to live till process exit.)
1831  */
1832 TupleDesc
1834 {
1835  TupleDesc tupDesc;
1836 
1837  tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
1838  PinTupleDesc(tupDesc);
1839  return tupDesc;
1840 }
1841 
1842 /*
1843  * lookup_rowtype_tupdesc_noerror
1844  *
1845  * As above, but if the type is not a known composite type and noError
1846  * is true, returns NULL instead of ereport'ing. (Note that if a bogus
1847  * type_id is passed, you'll get an ereport anyway.)
1848  */
1849 TupleDesc
1850 lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
1851 {
1852  TupleDesc tupDesc;
1853 
1854  tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
1855  if (tupDesc != NULL)
1856  PinTupleDesc(tupDesc);
1857  return tupDesc;
1858 }
1859 
1860 /*
1861  * lookup_rowtype_tupdesc_copy
1862  *
1863  * Like lookup_rowtype_tupdesc(), but the returned TupleDesc has been
1864  * copied into the CurrentMemoryContext and is not reference-counted.
1865  */
1866 TupleDesc
1868 {
1869  TupleDesc tmp;
1870 
1871  tmp = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
1872  return CreateTupleDescCopyConstr(tmp);
1873 }
1874 
1875 /*
1876  * lookup_rowtype_tupdesc_domain
1877  *
1878  * Same as lookup_rowtype_tupdesc_noerror(), except that the type can also be
1879  * a domain over a named composite type; so this is effectively equivalent to
1880  * lookup_rowtype_tupdesc_noerror(getBaseType(type_id), typmod, noError)
1881  * except for being a tad faster.
1882  *
1883  * Note: the reason we don't fold the look-through-domain behavior into plain
1884  * lookup_rowtype_tupdesc() is that we want callers to know they might be
1885  * dealing with a domain. Otherwise they might construct a tuple that should
1886  * be of the domain type, but not apply domain constraints.
1887  */
1888 TupleDesc
1889 lookup_rowtype_tupdesc_domain(Oid type_id, int32 typmod, bool noError)
1890 {
1891  TupleDesc tupDesc;
1892 
1893  if (type_id != RECORDOID)
1894  {
1895  /*
1896  * Check for domain or named composite type. We might as well load
1897  * whichever data is needed.
1898  */
1899  TypeCacheEntry *typentry;
1900 
1901  typentry = lookup_type_cache(type_id,
1904  if (typentry->typtype == TYPTYPE_DOMAIN)
1906  typentry->domainBaseTypmod,
1907  noError);
1908  if (typentry->tupDesc == NULL && !noError)
1909  ereport(ERROR,
1910  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1911  errmsg("type %s is not composite",
1912  format_type_be(type_id))));
1913  tupDesc = typentry->tupDesc;
1914  }
1915  else
1916  tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
1917  if (tupDesc != NULL)
1918  PinTupleDesc(tupDesc);
1919  return tupDesc;
1920 }
1921 
1922 /*
1923  * Hash function for the hash table of RecordCacheEntry.
1924  */
1925 static uint32
1926 record_type_typmod_hash(const void *data, size_t size)
1927 {
1928  RecordCacheEntry *entry = (RecordCacheEntry *) data;
1929 
1930  return hashTupleDesc(entry->tupdesc);
1931 }
1932 
1933 /*
1934  * Match function for the hash table of RecordCacheEntry.
1935  */
1936 static int
1937 record_type_typmod_compare(const void *a, const void *b, size_t size)
1938 {
1939  RecordCacheEntry *left = (RecordCacheEntry *) a;
1940  RecordCacheEntry *right = (RecordCacheEntry *) b;
1941 
1942  return equalTupleDescs(left->tupdesc, right->tupdesc) ? 0 : 1;
1943 }
1944 
1945 /*
1946  * assign_record_type_typmod
1947  *
1948  * Given a tuple descriptor for a RECORD type, find or create a cache entry
1949  * for the type, and set the tupdesc's tdtypmod field to a value that will
1950  * identify this cache entry to lookup_rowtype_tupdesc.
1951  */
1952 void
1954 {
1955  RecordCacheEntry *recentry;
1956  TupleDesc entDesc;
1957  bool found;
1958  MemoryContext oldcxt;
1959 
1960  Assert(tupDesc->tdtypeid == RECORDOID);
1961 
1962  if (RecordCacheHash == NULL)
1963  {
1964  /* First time through: initialize the hash table */
1965  HASHCTL ctl;
1966 
1967  ctl.keysize = sizeof(TupleDesc); /* just the pointer */
1968  ctl.entrysize = sizeof(RecordCacheEntry);
1971  RecordCacheHash = hash_create("Record information cache", 64,
1972  &ctl,
1974 
1975  /* Also make sure CacheMemoryContext exists */
1976  if (!CacheMemoryContext)
1978  }
1979 
1980  /*
1981  * Find a hashtable entry for this tuple descriptor. We don't use
1982  * HASH_ENTER yet, because if it's missing, we need to make sure that all
1983  * the allocations succeed before we create the new entry.
1984  */
1986  &tupDesc,
1987  HASH_FIND, &found);
1988  if (found && recentry->tupdesc != NULL)
1989  {
1990  tupDesc->tdtypmod = recentry->tupdesc->tdtypmod;
1991  return;
1992  }
1993 
1994  /* Not present, so need to manufacture an entry */
1996 
1997  /* Look in the SharedRecordTypmodRegistry, if attached */
1998  entDesc = find_or_make_matching_shared_tupledesc(tupDesc);
1999  if (entDesc == NULL)
2000  {
2001  /*
2002  * Make sure we have room before we CreateTupleDescCopy() or advance
2003  * NextRecordTypmod.
2004  */
2006 
2007  /* Reference-counted local cache only. */
2008  entDesc = CreateTupleDescCopy(tupDesc);
2009  entDesc->tdrefcount = 1;
2010  entDesc->tdtypmod = NextRecordTypmod++;
2011  }
2012  else
2013  {
2015  }
2016 
2017  RecordCacheArray[entDesc->tdtypmod].tupdesc = entDesc;
2018 
2019  /* Assign a unique tupdesc identifier, too. */
2021 
2022  /* Fully initialized; create the hash table entry */
2024  &tupDesc,
2025  HASH_ENTER, NULL);
2026  recentry->tupdesc = entDesc;
2027 
2028  /* Update the caller's tuple descriptor. */
2029  tupDesc->tdtypmod = entDesc->tdtypmod;
2030 
2031  MemoryContextSwitchTo(oldcxt);
2032 }
2033 
2034 /*
2035  * assign_record_type_identifier
2036  *
2037  * Get an identifier, which will be unique over the lifespan of this backend
2038  * process, for the current tuple descriptor of the specified composite type.
2039  * For named composite types, the value is guaranteed to change if the type's
2040  * definition does. For registered RECORD types, the value will not change
2041  * once assigned, since the registered type won't either. If an anonymous
2042  * RECORD type is specified, we return a new identifier on each call.
2043  */
2044 uint64
2046 {
2047  if (type_id != RECORDOID)
2048  {
2049  /*
2050  * It's a named composite type, so use the regular typcache.
2051  */
2052  TypeCacheEntry *typentry;
2053 
2054  typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
2055  if (typentry->tupDesc == NULL)
2056  ereport(ERROR,
2057  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2058  errmsg("type %s is not composite",
2059  format_type_be(type_id))));
2060  Assert(typentry->tupDesc_identifier != 0);
2061  return typentry->tupDesc_identifier;
2062  }
2063  else
2064  {
2065  /*
2066  * It's a transient record type, so look in our record-type table.
2067  */
2068  if (typmod >= 0 && typmod < RecordCacheArrayLen &&
2069  RecordCacheArray[typmod].tupdesc != NULL)
2070  {
2071  Assert(RecordCacheArray[typmod].id != 0);
2072  return RecordCacheArray[typmod].id;
2073  }
2074 
2075  /* For anonymous or unrecognized record type, generate a new ID */
2076  return ++tupledesc_id_counter;
2077  }
2078 }
2079 
2080 /*
2081  * Return the amount of shmem required to hold a SharedRecordTypmodRegistry.
2082  * This exists only to avoid exposing private innards of
2083  * SharedRecordTypmodRegistry in a header.
2084  */
2085 size_t
2087 {
2088  return sizeof(SharedRecordTypmodRegistry);
2089 }
2090 
2091 /*
2092  * Initialize 'registry' in a pre-existing shared memory region, which must be
2093  * maximally aligned and have space for SharedRecordTypmodRegistryEstimate()
2094  * bytes.
2095  *
2096  * 'area' will be used to allocate shared memory space as required for the
2097  * typemod registration. The current process, expected to be a leader process
2098  * in a parallel query, will be attached automatically and its current record
2099  * types will be loaded into *registry. While attached, all calls to
2100  * assign_record_type_typmod will use the shared registry. Worker backends
2101  * will need to attach explicitly.
2102  *
2103  * Note that this function takes 'area' and 'segment' as arguments rather than
2104  * accessing them via CurrentSession, because they aren't installed there
2105  * until after this function runs.
2106  */
2107 void
2109  dsm_segment *segment,
2110  dsa_area *area)
2111 {
2112  MemoryContext old_context;
2113  dshash_table *record_table;
2114  dshash_table *typmod_table;
2115  int32 typmod;
2116 
2118 
2119  /* We can't already be attached to a shared registry. */
2123 
2124  old_context = MemoryContextSwitchTo(TopMemoryContext);
2125 
2126  /* Create the hash table of tuple descriptors indexed by themselves. */
2127  record_table = dshash_create(area, &srtr_record_table_params, area);
2128 
2129  /* Create the hash table of tuple descriptors indexed by typmod. */
2130  typmod_table = dshash_create(area, &srtr_typmod_table_params, NULL);
2131 
2132  MemoryContextSwitchTo(old_context);
2133 
2134  /* Initialize the SharedRecordTypmodRegistry. */
2135  registry->record_table_handle = dshash_get_hash_table_handle(record_table);
2136  registry->typmod_table_handle = dshash_get_hash_table_handle(typmod_table);
2138 
2139  /*
2140  * Copy all entries from this backend's private registry into the shared
2141  * registry.
2142  */
2143  for (typmod = 0; typmod < NextRecordTypmod; ++typmod)
2144  {
2145  SharedTypmodTableEntry *typmod_table_entry;
2146  SharedRecordTableEntry *record_table_entry;
2147  SharedRecordTableKey record_table_key;
2148  dsa_pointer shared_dp;
2149  TupleDesc tupdesc;
2150  bool found;
2151 
2152  tupdesc = RecordCacheArray[typmod].tupdesc;
2153  if (tupdesc == NULL)
2154  continue;
2155 
2156  /* Copy the TupleDesc into shared memory. */
2157  shared_dp = share_tupledesc(area, tupdesc, typmod);
2158 
2159  /* Insert into the typmod table. */
2160  typmod_table_entry = dshash_find_or_insert(typmod_table,
2161  &tupdesc->tdtypmod,
2162  &found);
2163  if (found)
2164  elog(ERROR, "cannot create duplicate shared record typmod");
2165  typmod_table_entry->typmod = tupdesc->tdtypmod;
2166  typmod_table_entry->shared_tupdesc = shared_dp;
2167  dshash_release_lock(typmod_table, typmod_table_entry);
2168 
2169  /* Insert into the record table. */
2170  record_table_key.shared = false;
2171  record_table_key.u.local_tupdesc = tupdesc;
2172  record_table_entry = dshash_find_or_insert(record_table,
2173  &record_table_key,
2174  &found);
2175  if (!found)
2176  {
2177  record_table_entry->key.shared = true;
2178  record_table_entry->key.u.shared_tupdesc = shared_dp;
2179  }
2180  dshash_release_lock(record_table, record_table_entry);
2181  }
2182 
2183  /*
2184  * Set up the global state that will tell assign_record_type_typmod and
2185  * lookup_rowtype_tupdesc_internal about the shared registry.
2186  */
2187  CurrentSession->shared_record_table = record_table;
2188  CurrentSession->shared_typmod_table = typmod_table;
2190 
2191  /*
2192  * We install a detach hook in the leader, but only to handle cleanup on
2193  * failure during GetSessionDsmHandle(). Once GetSessionDsmHandle() pins
2194  * the memory, the leader process will use a shared registry until it
2195  * exits.
2196  */
2198 }
2199 
2200 /*
2201  * Attach to 'registry', which must have been initialized already by another
2202  * backend. Future calls to assign_record_type_typmod and
2203  * lookup_rowtype_tupdesc_internal will use the shared registry until the
2204  * current session is detached.
2205  */
2206 void
2208 {
2209  MemoryContext old_context;
2210  dshash_table *record_table;
2211  dshash_table *typmod_table;
2212 
2214 
2215  /* We can't already be attached to a shared registry. */
2216  Assert(CurrentSession != NULL);
2217  Assert(CurrentSession->segment != NULL);
2218  Assert(CurrentSession->area != NULL);
2222 
2223  /*
2224  * We can't already have typmods in our local cache, because they'd clash
2225  * with those imported by SharedRecordTypmodRegistryInit. This should be
2226  * a freshly started parallel worker. If we ever support worker
2227  * recycling, a worker would need to zap its local cache in between
2228  * servicing different queries, in order to be able to call this and
2229  * synchronize typmods with a new leader; but that's problematic because
2230  * we can't be very sure that record-typmod-related state hasn't escaped
2231  * to anywhere else in the process.
2232  */
2233  Assert(NextRecordTypmod == 0);
2234 
2235  old_context = MemoryContextSwitchTo(TopMemoryContext);
2236 
2237  /* Attach to the two hash tables. */
2238  record_table = dshash_attach(CurrentSession->area,
2240  registry->record_table_handle,
2241  CurrentSession->area);
2242  typmod_table = dshash_attach(CurrentSession->area,
2244  registry->typmod_table_handle,
2245  NULL);
2246 
2247  MemoryContextSwitchTo(old_context);
2248 
2249  /*
2250  * Set up detach hook to run at worker exit. Currently this is the same
2251  * as the leader's detach hook, but in future they might need to be
2252  * different.
2253  */
2256  PointerGetDatum(registry));
2257 
2258  /*
2259  * Set up the session state that will tell assign_record_type_typmod and
2260  * lookup_rowtype_tupdesc_internal about the shared registry.
2261  */
2263  CurrentSession->shared_record_table = record_table;
2264  CurrentSession->shared_typmod_table = typmod_table;
2265 }
2266 
2267 /*
2268  * TypeCacheRelCallback
2269  * Relcache inval callback function
2270  *
2271  * Delete the cached tuple descriptor (if any) for the given rel's composite
2272  * type, or for all composite types if relid == InvalidOid. Also reset
2273  * whatever info we have cached about the composite type's comparability.
2274  *
2275  * This is called when a relcache invalidation event occurs for the given
2276  * relid. We must scan the whole typcache hash since we don't know the
2277  * type OID corresponding to the relid. We could do a direct search if this
2278  * were a syscache-flush callback on pg_type, but then we would need all
2279  * ALTER-TABLE-like commands that could modify a rowtype to issue syscache
2280  * invals against the rel's pg_type OID. The extra SI signaling could very
2281  * well cost more than we'd save, since in most usages there are not very
2282  * many entries in a backend's typcache. The risk of bugs-of-omission seems
2283  * high, too.
2284  *
2285  * Another possibility, with only localized impact, is to maintain a second
2286  * hashtable that indexes composite-type typcache entries by their typrelid.
2287  * But it's still not clear it's worth the trouble.
2288  */
2289 static void
2291 {
2292  HASH_SEQ_STATUS status;
2293  TypeCacheEntry *typentry;
2294 
2295  /* TypeCacheHash must exist, else this callback wouldn't be registered */
2296  hash_seq_init(&status, TypeCacheHash);
2297  while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2298  {
2299  if (typentry->typtype == TYPTYPE_COMPOSITE)
2300  {
2301  /* Skip if no match, unless we're zapping all composite types */
2302  if (relid != typentry->typrelid && relid != InvalidOid)
2303  continue;
2304 
2305  /* Delete tupdesc if we have it */
2306  if (typentry->tupDesc != NULL)
2307  {
2308  /*
2309  * Release our refcount, and free the tupdesc if none remain.
2310  * (Can't use DecrTupleDescRefCount because this reference is
2311  * not logged in current resource owner.)
2312  */
2313  Assert(typentry->tupDesc->tdrefcount > 0);
2314  if (--typentry->tupDesc->tdrefcount == 0)
2315  FreeTupleDesc(typentry->tupDesc);
2316  typentry->tupDesc = NULL;
2317 
2318  /*
2319  * Also clear tupDesc_identifier, so that anything watching
2320  * that will realize that the tupdesc has possibly changed.
2321  * (Alternatively, we could specify that to detect possible
2322  * tupdesc change, one must check for tupDesc != NULL as well
2323  * as tupDesc_identifier being the same as what was previously
2324  * seen. That seems error-prone.)
2325  */
2326  typentry->tupDesc_identifier = 0;
2327  }
2328 
2329  /* Reset equality/comparison/hashing validity information */
2330  typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2331  }
2332  else if (typentry->typtype == TYPTYPE_DOMAIN)
2333  {
2334  /*
2335  * If it's domain over composite, reset flags. (We don't bother
2336  * trying to determine whether the specific base type needs a
2337  * reset.) Note that if we haven't determined whether the base
2338  * type is composite, we don't need to reset anything.
2339  */
2340  if (typentry->flags & TCFLAGS_DOMAIN_BASE_IS_COMPOSITE)
2341  typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2342  }
2343  }
2344 }
2345 
2346 /*
2347  * TypeCacheTypCallback
2348  * Syscache inval callback function
2349  *
2350  * This is called when a syscache invalidation event occurs for any
2351  * pg_type row. If we have information cached about that type, mark
2352  * it as needing to be reloaded.
2353  */
2354 static void
2355 TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue)
2356 {
2357  HASH_SEQ_STATUS status;
2358  TypeCacheEntry *typentry;
2359 
2360  /* TypeCacheHash must exist, else this callback wouldn't be registered */
2361  hash_seq_init(&status, TypeCacheHash);
2362  while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2363  {
2364  /* Is this the targeted type row (or it's a total cache flush)? */
2365  if (hashvalue == 0 || typentry->type_id_hash == hashvalue)
2366  {
2367  /*
2368  * Mark the data obtained directly from pg_type as invalid. Also,
2369  * if it's a domain, typnotnull might've changed, so we'll need to
2370  * recalculate its constraints.
2371  */
2372  typentry->flags &= ~(TCFLAGS_HAVE_PG_TYPE_DATA |
2374  }
2375  }
2376 }
2377 
2378 /*
2379  * TypeCacheOpcCallback
2380  * Syscache inval callback function
2381  *
2382  * This is called when a syscache invalidation event occurs for any pg_opclass
2383  * row. In principle we could probably just invalidate data dependent on the
2384  * particular opclass, but since updates on pg_opclass are rare in production
2385  * it doesn't seem worth a lot of complication: we just mark all cached data
2386  * invalid.
2387  *
2388  * Note that we don't bother watching for updates on pg_amop or pg_amproc.
2389  * This should be safe because ALTER OPERATOR FAMILY ADD/DROP OPERATOR/FUNCTION
2390  * is not allowed to be used to add/drop the primary operators and functions
2391  * of an opclass, only cross-type members of a family; and the latter sorts
2392  * of members are not going to get cached here.
2393  */
2394 static void
2395 TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue)
2396 {
2397  HASH_SEQ_STATUS status;
2398  TypeCacheEntry *typentry;
2399 
2400  /* TypeCacheHash must exist, else this callback wouldn't be registered */
2401  hash_seq_init(&status, TypeCacheHash);
2402  while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2403  {
2404  /* Reset equality/comparison/hashing validity information */
2405  typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2406  }
2407 }
2408 
2409 /*
2410  * TypeCacheConstrCallback
2411  * Syscache inval callback function
2412  *
2413  * This is called when a syscache invalidation event occurs for any
2414  * pg_constraint row. We flush information about domain constraints
2415  * when this happens.
2416  *
2417  * It's slightly annoying that we can't tell whether the inval event was for
2418  * a domain constraint record or not; there's usually more update traffic
2419  * for table constraints than domain constraints, so we'll do a lot of
2420  * useless flushes. Still, this is better than the old no-caching-at-all
2421  * approach to domain constraints.
2422  */
2423 static void
2424 TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue)
2425 {
2426  TypeCacheEntry *typentry;
2427 
2428  /*
2429  * Because this is called very frequently, and typically very few of the
2430  * typcache entries are for domains, we don't use hash_seq_search here.
2431  * Instead we thread all the domain-type entries together so that we can
2432  * visit them cheaply.
2433  */
2434  for (typentry = firstDomainTypeEntry;
2435  typentry != NULL;
2436  typentry = typentry->nextDomain)
2437  {
2438  /* Reset domain constraint validity information */
2440  }
2441 }
2442 
2443 
2444 /*
2445  * Check if given OID is part of the subset that's sortable by comparisons
2446  */
2447 static inline bool
2449 {
2450  Oid offset;
2451 
2452  if (arg < enumdata->bitmap_base)
2453  return false;
2454  offset = arg - enumdata->bitmap_base;
2455  if (offset > (Oid) INT_MAX)
2456  return false;
2457  return bms_is_member((int) offset, enumdata->sorted_values);
2458 }
2459 
2460 
2461 /*
2462  * compare_values_of_enum
2463  * Compare two members of an enum type.
2464  * Return <0, 0, or >0 according as arg1 <, =, or > arg2.
2465  *
2466  * Note: currently, the enumData cache is refreshed only if we are asked
2467  * to compare an enum value that is not already in the cache. This is okay
2468  * because there is no support for re-ordering existing values, so comparisons
2469  * of previously cached values will return the right answer even if other
2470  * values have been added since we last loaded the cache.
2471  *
2472  * Note: the enum logic has a special-case rule about even-numbered versus
2473  * odd-numbered OIDs, but we take no account of that rule here; this
2474  * routine shouldn't even get called when that rule applies.
2475  */
2476 int
2478 {
2479  TypeCacheEnumData *enumdata;
2480  EnumItem *item1;
2481  EnumItem *item2;
2482 
2483  /*
2484  * Equal OIDs are certainly equal --- this case was probably handled by
2485  * our caller, but we may as well check.
2486  */
2487  if (arg1 == arg2)
2488  return 0;
2489 
2490  /* Load up the cache if first time through */
2491  if (tcache->enumData == NULL)
2492  load_enum_cache_data(tcache);
2493  enumdata = tcache->enumData;
2494 
2495  /*
2496  * If both OIDs are known-sorted, we can just compare them directly.
2497  */
2498  if (enum_known_sorted(enumdata, arg1) &&
2499  enum_known_sorted(enumdata, arg2))
2500  {
2501  if (arg1 < arg2)
2502  return -1;
2503  else
2504  return 1;
2505  }
2506 
2507  /*
2508  * Slow path: we have to identify their actual sort-order positions.
2509  */
2510  item1 = find_enumitem(enumdata, arg1);
2511  item2 = find_enumitem(enumdata, arg2);
2512 
2513  if (item1 == NULL || item2 == NULL)
2514  {
2515  /*
2516  * We couldn't find one or both values. That means the enum has
2517  * changed under us, so re-initialize the cache and try again. We
2518  * don't bother retrying the known-sorted case in this path.
2519  */
2520  load_enum_cache_data(tcache);
2521  enumdata = tcache->enumData;
2522 
2523  item1 = find_enumitem(enumdata, arg1);
2524  item2 = find_enumitem(enumdata, arg2);
2525 
2526  /*
2527  * If we still can't find the values, complain: we must have corrupt
2528  * data.
2529  */
2530  if (item1 == NULL)
2531  elog(ERROR, "enum value %u not found in cache for enum %s",
2532  arg1, format_type_be(tcache->type_id));
2533  if (item2 == NULL)
2534  elog(ERROR, "enum value %u not found in cache for enum %s",
2535  arg2, format_type_be(tcache->type_id));
2536  }
2537 
2538  if (item1->sort_order < item2->sort_order)
2539  return -1;
2540  else if (item1->sort_order > item2->sort_order)
2541  return 1;
2542  else
2543  return 0;
2544 }
2545 
2546 /*
2547  * Load (or re-load) the enumData member of the typcache entry.
2548  */
2549 static void
2551 {
2552  TypeCacheEnumData *enumdata;
2553  Relation enum_rel;
2554  SysScanDesc enum_scan;
2555  HeapTuple enum_tuple;
2556  ScanKeyData skey;
2557  EnumItem *items;
2558  int numitems;
2559  int maxitems;
2560  Oid bitmap_base;
2561  Bitmapset *bitmap;
2562  MemoryContext oldcxt;
2563  int bm_size,
2564  start_pos;
2565 
2566  /* Check that this is actually an enum */
2567  if (tcache->typtype != TYPTYPE_ENUM)
2568  ereport(ERROR,
2569  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2570  errmsg("%s is not an enum",
2571  format_type_be(tcache->type_id))));
2572 
2573  /*
2574  * Read all the information for members of the enum type. We collect the
2575  * info in working memory in the caller's context, and then transfer it to
2576  * permanent memory in CacheMemoryContext. This minimizes the risk of
2577  * leaking memory from CacheMemoryContext in the event of an error partway
2578  * through.
2579  */
2580  maxitems = 64;
2581  items = (EnumItem *) palloc(sizeof(EnumItem) * maxitems);
2582  numitems = 0;
2583 
2584  /* Scan pg_enum for the members of the target enum type. */
2585  ScanKeyInit(&skey,
2586  Anum_pg_enum_enumtypid,
2587  BTEqualStrategyNumber, F_OIDEQ,
2588  ObjectIdGetDatum(tcache->type_id));
2589 
2590  enum_rel = table_open(EnumRelationId, AccessShareLock);
2591  enum_scan = systable_beginscan(enum_rel,
2592  EnumTypIdLabelIndexId,
2593  true, NULL,
2594  1, &skey);
2595 
2596  while (HeapTupleIsValid(enum_tuple = systable_getnext(enum_scan)))
2597  {
2598  Form_pg_enum en = (Form_pg_enum) GETSTRUCT(enum_tuple);
2599 
2600  if (numitems >= maxitems)
2601  {
2602  maxitems *= 2;
2603  items = (EnumItem *) repalloc(items, sizeof(EnumItem) * maxitems);
2604  }
2605  items[numitems].enum_oid = en->oid;
2606  items[numitems].sort_order = en->enumsortorder;
2607  numitems++;
2608  }
2609 
2610  systable_endscan(enum_scan);
2611  table_close(enum_rel, AccessShareLock);
2612 
2613  /* Sort the items into OID order */
2614  qsort(items, numitems, sizeof(EnumItem), enum_oid_cmp);
2615 
2616  /*
2617  * Here, we create a bitmap listing a subset of the enum's OIDs that are
2618  * known to be in order and can thus be compared with just OID comparison.
2619  *
2620  * The point of this is that the enum's initial OIDs were certainly in
2621  * order, so there is some subset that can be compared via OID comparison;
2622  * and we'd rather not do binary searches unnecessarily.
2623  *
2624  * This is somewhat heuristic, and might identify a subset of OIDs that
2625  * isn't exactly what the type started with. That's okay as long as the
2626  * subset is correctly sorted.
2627  */
2628  bitmap_base = InvalidOid;
2629  bitmap = NULL;
2630  bm_size = 1; /* only save sets of at least 2 OIDs */
2631 
2632  for (start_pos = 0; start_pos < numitems - 1; start_pos++)
2633  {
2634  /*
2635  * Identify longest sorted subsequence starting at start_pos
2636  */
2637  Bitmapset *this_bitmap = bms_make_singleton(0);
2638  int this_bm_size = 1;
2639  Oid start_oid = items[start_pos].enum_oid;
2640  float4 prev_order = items[start_pos].sort_order;
2641  int i;
2642 
2643  for (i = start_pos + 1; i < numitems; i++)
2644  {
2645  Oid offset;
2646 
2647  offset = items[i].enum_oid - start_oid;
2648  /* quit if bitmap would be too large; cutoff is arbitrary */
2649  if (offset >= 8192)
2650  break;
2651  /* include the item if it's in-order */
2652  if (items[i].sort_order > prev_order)
2653  {
2654  prev_order = items[i].sort_order;
2655  this_bitmap = bms_add_member(this_bitmap, (int) offset);
2656  this_bm_size++;
2657  }
2658  }
2659 
2660  /* Remember it if larger than previous best */
2661  if (this_bm_size > bm_size)
2662  {
2663  bms_free(bitmap);
2664  bitmap_base = start_oid;
2665  bitmap = this_bitmap;
2666  bm_size = this_bm_size;
2667  }
2668  else
2669  bms_free(this_bitmap);
2670 
2671  /*
2672  * Done if it's not possible to find a longer sequence in the rest of
2673  * the list. In typical cases this will happen on the first
2674  * iteration, which is why we create the bitmaps on the fly instead of
2675  * doing a second pass over the list.
2676  */
2677  if (bm_size >= (numitems - start_pos - 1))
2678  break;
2679  }
2680 
2681  /* OK, copy the data into CacheMemoryContext */
2683  enumdata = (TypeCacheEnumData *)
2684  palloc(offsetof(TypeCacheEnumData, enum_values) +
2685  numitems * sizeof(EnumItem));
2686  enumdata->bitmap_base = bitmap_base;
2687  enumdata->sorted_values = bms_copy(bitmap);
2688  enumdata->num_values = numitems;
2689  memcpy(enumdata->enum_values, items, numitems * sizeof(EnumItem));
2690  MemoryContextSwitchTo(oldcxt);
2691 
2692  pfree(items);
2693  bms_free(bitmap);
2694 
2695  /* And link the finished cache struct into the typcache */
2696  if (tcache->enumData != NULL)
2697  pfree(tcache->enumData);
2698  tcache->enumData = enumdata;
2699 }
2700 
2701 /*
2702  * Locate the EnumItem with the given OID, if present
2703  */
2704 static EnumItem *
2706 {
2707  EnumItem srch;
2708 
2709  /* On some versions of Solaris, bsearch of zero items dumps core */
2710  if (enumdata->num_values <= 0)
2711  return NULL;
2712 
2713  srch.enum_oid = arg;
2714  return bsearch(&srch, enumdata->enum_values, enumdata->num_values,
2715  sizeof(EnumItem), enum_oid_cmp);
2716 }
2717 
2718 /*
2719  * qsort comparison function for OID-ordered EnumItems
2720  */
2721 static int
2722 enum_oid_cmp(const void *left, const void *right)
2723 {
2724  const EnumItem *l = (const EnumItem *) left;
2725  const EnumItem *r = (const EnumItem *) right;
2726 
2727  return pg_cmp_u32(l->enum_oid, r->enum_oid);
2728 }
2729 
2730 /*
2731  * Copy 'tupdesc' into newly allocated shared memory in 'area', set its typmod
2732  * to the given value and return a dsa_pointer.
2733  */
2734 static dsa_pointer
2735 share_tupledesc(dsa_area *area, TupleDesc tupdesc, uint32 typmod)
2736 {
2737  dsa_pointer shared_dp;
2738  TupleDesc shared;
2739 
2740  shared_dp = dsa_allocate(area, TupleDescSize(tupdesc));
2741  shared = (TupleDesc) dsa_get_address(area, shared_dp);
2742  TupleDescCopy(shared, tupdesc);
2743  shared->tdtypmod = typmod;
2744 
2745  return shared_dp;
2746 }
2747 
2748 /*
2749  * If we are attached to a SharedRecordTypmodRegistry, use it to find or
2750  * create a shared TupleDesc that matches 'tupdesc'. Otherwise return NULL.
2751  * Tuple descriptors returned by this function are not reference counted, and
2752  * will exist at least as long as the current backend remained attached to the
2753  * current session.
2754  */
2755 static TupleDesc
2757 {
2758  TupleDesc result;
2760  SharedRecordTableEntry *record_table_entry;
2761  SharedTypmodTableEntry *typmod_table_entry;
2762  dsa_pointer shared_dp;
2763  bool found;
2764  uint32 typmod;
2765 
2766  /* If not even attached, nothing to do. */
2768  return NULL;
2769 
2770  /* Try to find a matching tuple descriptor in the record table. */
2771  key.shared = false;
2772  key.u.local_tupdesc = tupdesc;
2773  record_table_entry = (SharedRecordTableEntry *)
2775  if (record_table_entry)
2776  {
2777  Assert(record_table_entry->key.shared);
2779  record_table_entry);
2780  result = (TupleDesc)
2782  record_table_entry->key.u.shared_tupdesc);
2783  Assert(result->tdrefcount == -1);
2784 
2785  return result;
2786  }
2787 
2788  /* Allocate a new typmod number. This will be wasted if we error out. */
2789  typmod = (int)
2791  1);
2792 
2793  /* Copy the TupleDesc into shared memory. */
2794  shared_dp = share_tupledesc(CurrentSession->area, tupdesc, typmod);
2795 
2796  /*
2797  * Create an entry in the typmod table so that others will understand this
2798  * typmod number.
2799  */
2800  PG_TRY();
2801  {
2802  typmod_table_entry = (SharedTypmodTableEntry *)
2804  &typmod, &found);
2805  if (found)
2806  elog(ERROR, "cannot create duplicate shared record typmod");
2807  }
2808  PG_CATCH();
2809  {
2810  dsa_free(CurrentSession->area, shared_dp);
2811  PG_RE_THROW();
2812  }
2813  PG_END_TRY();
2814  typmod_table_entry->typmod = typmod;
2815  typmod_table_entry->shared_tupdesc = shared_dp;
2817  typmod_table_entry);
2818 
2819  /*
2820  * Finally create an entry in the record table so others with matching
2821  * tuple descriptors can reuse the typmod.
2822  */
2823  record_table_entry = (SharedRecordTableEntry *)
2825  &found);
2826  if (found)
2827  {
2828  /*
2829  * Someone concurrently inserted a matching tuple descriptor since the
2830  * first time we checked. Use that one instead.
2831  */
2833  record_table_entry);
2834 
2835  /* Might as well free up the space used by the one we created. */
2837  &typmod);
2838  Assert(found);
2839  dsa_free(CurrentSession->area, shared_dp);
2840 
2841  /* Return the one we found. */
2842  Assert(record_table_entry->key.shared);
2843  result = (TupleDesc)
2845  record_table_entry->key.u.shared_tupdesc);
2846  Assert(result->tdrefcount == -1);
2847 
2848  return result;
2849  }
2850 
2851  /* Store it and return it. */
2852  record_table_entry->key.shared = true;
2853  record_table_entry->key.u.shared_tupdesc = shared_dp;
2855  record_table_entry);
2856  result = (TupleDesc)
2857  dsa_get_address(CurrentSession->area, shared_dp);
2858  Assert(result->tdrefcount == -1);
2859 
2860  return result;
2861 }
2862 
2863 /*
2864  * On-DSM-detach hook to forget about the current shared record typmod
2865  * infrastructure. This is currently used by both leader and workers.
2866  */
2867 static void
2869 {
2870  /* Be cautious here: maybe we didn't finish initializing. */
2871  if (CurrentSession->shared_record_table != NULL)
2872  {
2875  }
2876  if (CurrentSession->shared_typmod_table != NULL)
2877  {
2880  }
2882 }
static void pg_atomic_init_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
Definition: atomics.h:216
static uint32 pg_atomic_fetch_add_u32(volatile pg_atomic_uint32 *ptr, int32 add_)
Definition: atomics.h:361
void bms_free(Bitmapset *a)
Definition: bitmapset.c:252
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:523
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:229
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:828
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:135
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define NameStr(name)
Definition: c.h:733
unsigned int uint32
Definition: c.h:493
#define RegProcedureIsValid(p)
Definition: c.h:764
signed int int32
Definition: c.h:481
#define FLEXIBLE_ARRAY_MEMBER
Definition: c.h:385
float float4
Definition: c.h:616
#define MemSet(start, val, len)
Definition: c.h:1007
#define OidIsValid(objectId)
Definition: c.h:762
void CreateCacheMemoryContext(void)
Definition: catcache.c:666
void * dsa_get_address(dsa_area *area, dsa_pointer dp)
Definition: dsa.c:955
void dsa_free(dsa_area *area, dsa_pointer dp)
Definition: dsa.c:839
uint64 dsa_pointer
Definition: dsa.h:62
#define dsa_allocate(area, size)
Definition: dsa.h:84
bool dshash_delete_key(dshash_table *hash_table, const void *key)
Definition: dshash.c:503
void dshash_memcpy(void *dest, const void *src, size_t size, void *arg)
Definition: dshash.c:590
void dshash_release_lock(dshash_table *hash_table, void *entry)
Definition: dshash.c:558
void dshash_detach(dshash_table *hash_table)
Definition: dshash.c:307
void * dshash_find(dshash_table *hash_table, const void *key, bool exclusive)
Definition: dshash.c:390
dshash_table_handle dshash_get_hash_table_handle(dshash_table *hash_table)
Definition: dshash.c:367
dshash_hash dshash_memhash(const void *v, size_t size, void *arg)
Definition: dshash.c:581
void * dshash_find_or_insert(dshash_table *hash_table, const void *key, bool *found)
Definition: dshash.c:433
dshash_table * dshash_attach(dsa_area *area, const dshash_parameters *params, dshash_table_handle handle, void *arg)
Definition: dshash.c:270
int dshash_memcmp(const void *a, const void *b, size_t size, void *arg)
Definition: dshash.c:572
dshash_table * dshash_create(dsa_area *area, const dshash_parameters *params, void *arg)
Definition: dshash.c:206
dsa_pointer dshash_table_handle
Definition: dshash.h:24
void on_dsm_detach(dsm_segment *seg, on_dsm_detach_callback function, Datum arg)
Definition: dsm.c:1132
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:953
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:350
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1431
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1421
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define PG_RE_THROW()
Definition: elog.h:411
#define PG_TRY(...)
Definition: elog.h:370
#define PG_END_TRY(...)
Definition: elog.h:395
#define ERROR
Definition: elog.h:39
#define PG_CATCH(...)
Definition: elog.h:380
#define elog(elevel,...)
Definition: elog.h:224
#define ereport(elevel,...)
Definition: elog.h:149
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:127
@ DOM_CONSTRAINT_CHECK
Definition: execnodes.h:999
@ DOM_CONSTRAINT_NOTNULL
Definition: execnodes.h:998
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:137
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:596
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:503
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:384
#define HASHSTANDARD_PROC
Definition: hash.h:355
#define HASHEXTENDED_PROC
Definition: hash.h:356
@ HASH_FIND
Definition: hsearch.h:113
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_COMPARE
Definition: hsearch.h:99
#define HASH_FUNCTION
Definition: hsearch.h:98
#define HASH_BLOBS
Definition: hsearch.h:97
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
static Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:749
#define IsParallelWorker()
Definition: parallel.h:60
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:2334
long val
Definition: informix.c:664
static int pg_cmp_u32(uint32 a, uint32 b)
Definition: int.h:489
void CacheRegisterRelcacheCallback(RelcacheCallbackFunction func, Datum arg)
Definition: inval.c:1558
void CacheRegisterSyscacheCallback(int cacheid, SyscacheCallbackFunction func, Datum arg)
Definition: inval.c:1516
int b
Definition: isn.c:70
int a
Definition: isn.c:69
int i
Definition: isn.c:73
Assert(fmt[strlen(fmt) - 1] !='\n')
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:1190
Oid get_opclass_family(Oid opclass)
Definition: lsyscache.c:1168
Oid get_multirange_range(Oid multirangeOid)
Definition: lsyscache.c:3439
Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
Definition: lsyscache.c:796
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1263
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:166
Oid get_base_element_type(Oid typid)
Definition: lsyscache.c:2788
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition: lsyscache.c:2494
@ LWTRANCHE_PER_SESSION_RECORD_TYPMOD
Definition: lwlock.h:200
@ LWTRANCHE_PER_SESSION_RECORD_TYPE
Definition: lwlock.h:199
void MemoryContextRegisterResetCallback(MemoryContext context, MemoryContextCallback *cb)
Definition: mcxt.c:473
void MemoryContextSetParent(MemoryContext context, MemoryContext new_parent)
Definition: mcxt.c:542
char * pstrdup(const char *in)
Definition: mcxt.c:1576
void pfree(void *pointer)
Definition: mcxt.c:1401
MemoryContext TopMemoryContext
Definition: mcxt.c:137
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1096
MemoryContext CurrentMemoryContext
Definition: mcxt.c:131
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1421
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1062
MemoryContext CacheMemoryContext
Definition: mcxt.c:140
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:399
void * palloc(Size size)
Definition: mcxt.c:1197
#define AllocSetContextCreate
Definition: memutils.h:128
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:162
#define BTORDER_PROC
Definition: nbtree.h:707
#define makeNode(_type_)
Definition: nodes.h:155
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
#define repalloc0_array(pointer, type, oldcount, count)
Definition: palloc.h:109
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
void * arg
static uint32 pg_nextpower2_32(uint32 num)
Definition: pg_bitutils.h:189
FormData_pg_constraint * Form_pg_constraint
const void * data
FormData_pg_enum * Form_pg_enum
Definition: pg_enum.h:44
#define lfirst(lc)
Definition: pg_list.h:172
#define NIL
Definition: pg_list.h:68
FormData_pg_range * Form_pg_range
Definition: pg_range.h:58
FormData_pg_type * Form_pg_type
Definition: pg_type.h:261
Expr * expression_planner(Expr *expr)
Definition: planner.c:6400
#define qsort(a, b, c, d)
Definition: port.h:449
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
char * c
void * stringToNode(const char *str)
Definition: read.c:90
#define RelationGetDescr(relation)
Definition: rel.h:530
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
Session * CurrentSession
Definition: session.c:48
static pg_noinline void Size size
Definition: slab.c:607
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:47
#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:127
DomainConstraintCache * dcc
Definition: typcache.h:172
MemoryContext refctx
Definition: typcache.h:167
MemoryContextCallback callback
Definition: typcache.h:173
TypeCacheEntry * tcache
Definition: typcache.h:168
DomainConstraintType constrainttype
Definition: execnodes.h:1005
ExprState * check_exprstate
Definition: execnodes.h:1008
float4 sort_order
Definition: typcache.c:135
Oid enum_oid
Definition: typcache.c:134
Oid fn_oid
Definition: fmgr.h:59
Size keysize
Definition: hsearch.h:75
HashValueFunc hash
Definition: hsearch.h:78
Size entrysize
Definition: hsearch.h:76
HashCompareFunc match
Definition: hsearch.h:80
Definition: dynahash.c:220
Definition: pg_list.h:54
MemoryContextCallbackFunction func
Definition: palloc.h:49
TupleDesc tupdesc
Definition: typcache.c:159
TupleDesc rd_att
Definition: rel.h:112
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:198
TupleDesc local_tupdesc
Definition: typcache.c:186
union SharedRecordTableKey::@30 u
dsa_pointer shared_tupdesc
Definition: typcache.c:187
dshash_table_handle typmod_table_handle
Definition: typcache.c:171
pg_atomic_uint32 next_typmod
Definition: typcache.c:173
dshash_table_handle record_table_handle
Definition: typcache.c:169
dsa_pointer shared_tupdesc
Definition: typcache.c:208
int tdrefcount
Definition: tupdesc.h:84
int32 tdtypmod
Definition: tupdesc.h:83
Oid tdtypeid
Definition: tupdesc.h:82
uint32 type_id_hash
Definition: typcache.h:36
uint64 tupDesc_identifier
Definition: typcache.h:90
FmgrInfo hash_proc_finfo
Definition: typcache.h:77
int32 domainBaseTypmod
Definition: typcache.h:115
Oid hash_extended_proc
Definition: typcache.h:66
Oid typsubscript
Definition: typcache.h:45
FmgrInfo rng_cmp_proc_finfo
Definition: typcache.h:101
FmgrInfo cmp_proc_finfo
Definition: typcache.h:76
Oid rng_collation
Definition: typcache.h:100
char typalign
Definition: typcache.h:41
struct TypeCacheEntry * rngelemtype
Definition: typcache.h:98
char typtype
Definition: typcache.h:43
TupleDesc tupDesc
Definition: typcache.h:89
FmgrInfo hash_extended_proc_finfo
Definition: typcache.h:78
DomainConstraintCache * domainData
Definition: typcache.h:121
struct TypeCacheEntry * rngtype
Definition: typcache.h:108
FmgrInfo rng_subdiff_finfo
Definition: typcache.h:103
FmgrInfo eq_opr_finfo
Definition: typcache.h:75
Oid btree_opintype
Definition: typcache.h:58
struct TypeCacheEnumData * enumData
Definition: typcache.h:130
struct TypeCacheEntry * nextDomain
Definition: typcache.h:133
bool typbyval
Definition: typcache.h:40
FmgrInfo rng_canonical_finfo
Definition: typcache.h:102
int16 typlen
Definition: typcache.h:39
Oid hash_opintype
Definition: typcache.h:60
Oid typcollation
Definition: typcache.h:47
Oid domainBaseType
Definition: typcache.h:114
char typstorage
Definition: typcache.h:42
Oid rng_opfamily
Definition: typcache.h:99
Bitmapset * sorted_values
Definition: typcache.c:141
EnumItem enum_values[FLEXIBLE_ARRAY_MEMBER]
Definition: typcache.c:143
Definition: dsa.c:366
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:266
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:218
#define GetSysCacheHashValue1(cacheId, key1)
Definition: syscache.h:113
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition: tupdesc.c:173
void TupleDescCopy(TupleDesc dst, TupleDesc src)
Definition: tupdesc.c:251
void DecrTupleDescRefCount(TupleDesc tupdesc)
Definition: tupdesc.c:406
void FreeTupleDesc(TupleDesc tupdesc)
Definition: tupdesc.c:331
void IncrTupleDescRefCount(TupleDesc tupdesc)
Definition: tupdesc.c:388
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:133
uint32 hashTupleDesc(TupleDesc desc)
Definition: tupdesc.c:574
bool equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
Definition: tupdesc.c:424
#define TupleDescSize(src)
Definition: tupdesc.h:102
#define PinTupleDesc(tupdesc)
Definition: tupdesc.h:116
struct TupleDescData * TupleDesc
Definition: tupdesc.h:89
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define TCFLAGS_CHECKED_BTREE_OPCLASS
Definition: typcache.c:85
#define TCFLAGS_CHECKED_HASH_OPCLASS
Definition: typcache.c:86
static bool range_element_has_hashing(TypeCacheEntry *typentry)
Definition: typcache.c:1626
void InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref, MemoryContext refctx, bool need_exprstate)
Definition: typcache.c:1313
static TupleDesc lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
Definition: typcache.c:1739
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1833
void SharedRecordTypmodRegistryAttach(SharedRecordTypmodRegistry *registry)
Definition: typcache.c:2207
#define TCFLAGS_OPERATOR_FLAGS
Definition: typcache.c:107
#define TCFLAGS_CHECKED_FIELD_PROPERTIES
Definition: typcache.c:98
static void cache_range_element_properties(TypeCacheEntry *typentry)
Definition: typcache.c:1642
#define TCFLAGS_HAVE_FIELD_COMPARE
Definition: typcache.c:100
#define TCFLAGS_DOMAIN_BASE_IS_COMPOSITE
Definition: typcache.c:104
static void load_enum_cache_data(TypeCacheEntry *tcache)
Definition: typcache.c:2550
static bool record_fields_have_hashing(TypeCacheEntry *typentry)
Definition: typcache.c:1505
static EnumItem * find_enumitem(TypeCacheEnumData *enumdata, Oid arg)
Definition: typcache.c:2705
static bool record_fields_have_extended_hashing(TypeCacheEntry *typentry)
Definition: typcache.c:1513
static TupleDesc find_or_make_matching_shared_tupledesc(TupleDesc tupdesc)
Definition: typcache.c:2756
static int32 NextRecordTypmod
Definition: typcache.c:287
TupleDesc lookup_rowtype_tupdesc_domain(Oid type_id, int32 typmod, bool noError)
Definition: typcache.c:1889
static const dshash_parameters srtr_typmod_table_params
Definition: typcache.c:266
#define TCFLAGS_CHECKED_GT_OPR
Definition: typcache.c:89
static bool multirange_element_has_hashing(TypeCacheEntry *typentry)
Definition: typcache.c:1666
static List * prep_domain_constraints(List *constraints, MemoryContext execctx)
Definition: typcache.c:1275
TupleDesc lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
Definition: typcache.c:1850
static bool record_fields_have_equality(TypeCacheEntry *typentry)
Definition: typcache.c:1489
#define TCFLAGS_CHECKED_LT_OPR
Definition: typcache.c:88
#define TCFLAGS_CHECKED_HASH_PROC
Definition: typcache.c:91
static void dccref_deletion_callback(void *arg)
Definition: typcache.c:1254
#define TCFLAGS_HAVE_FIELD_EQUALITY
Definition: typcache.c:99
struct SharedRecordTableEntry SharedRecordTableEntry
void SharedRecordTypmodRegistryInit(SharedRecordTypmodRegistry *registry, dsm_segment *segment, dsa_area *area)
Definition: typcache.c:2108
static int dcs_cmp(const void *a, const void *b)
Definition: typcache.c:1230
static bool array_element_has_extended_hashing(TypeCacheEntry *typentry)
Definition: typcache.c:1451
static int shared_record_table_compare(const void *a, const void *b, size_t size, void *arg)
Definition: typcache.c:215
static bool array_element_has_hashing(TypeCacheEntry *typentry)
Definition: typcache.c:1443
static void load_multirangetype_info(TypeCacheEntry *typentry)
Definition: typcache.c:972
#define TCFLAGS_CHECKED_CMP_PROC
Definition: typcache.c:90
struct SharedTypmodTableEntry SharedTypmodTableEntry
#define TCFLAGS_HAVE_ELEM_EXTENDED_HASHING
Definition: typcache.c:97
static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
Definition: typcache.c:1674
static bool array_element_has_equality(TypeCacheEntry *typentry)
Definition: typcache.c:1427
static dsa_pointer share_tupledesc(dsa_area *area, TupleDesc tupdesc, uint32 typmod)
Definition: typcache.c:2735
static void load_rangetype_info(TypeCacheEntry *typentry)
Definition: typcache.c:914
uint64 assign_record_type_identifier(Oid type_id, int32 typmod)
Definition: typcache.c:2045
static RecordCacheArrayEntry * RecordCacheArray
Definition: typcache.c:285
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry)
Definition: typcache.c:1634
static HTAB * RecordCacheHash
Definition: typcache.c:276
static bool enum_known_sorted(TypeCacheEnumData *enumdata, Oid arg)
Definition: typcache.c:2448
static TypeCacheEntry * firstDomainTypeEntry
Definition: typcache.c:81
struct RecordCacheEntry RecordCacheEntry
static void shared_record_typmod_registry_detach(dsm_segment *segment, Datum datum)
Definition: typcache.c:2868
#define TCFLAGS_HAVE_ELEM_HASHING
Definition: typcache.c:96
struct RecordCacheArrayEntry RecordCacheArrayEntry
#define TCFLAGS_CHECKED_HASH_EXTENDED_PROC
Definition: typcache.c:92
static void TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue)
Definition: typcache.c:2355
struct TypeCacheEnumData TypeCacheEnumData
static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue)
Definition: typcache.c:2424
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue)
Definition: typcache.c:2395
static void load_domaintype_info(TypeCacheEntry *typentry)
Definition: typcache.c:994
bool DomainHasConstraints(Oid type_id)
Definition: typcache.c:1400
#define TCFLAGS_HAVE_ELEM_COMPARE
Definition: typcache.c:95
static void TypeCacheRelCallback(Datum arg, Oid relid)
Definition: typcache.c:2290
static void cache_array_element_properties(TypeCacheEntry *typentry)
Definition: typcache.c:1459
size_t SharedRecordTypmodRegistryEstimate(void)
Definition: typcache.c:2086
static void cache_multirange_element_properties(TypeCacheEntry *typentry)
Definition: typcache.c:1682
#define TCFLAGS_CHECKED_ELEM_PROPERTIES
Definition: typcache.c:93
#define TCFLAGS_HAVE_ELEM_EQUALITY
Definition: typcache.c:94
static bool array_element_has_compare(TypeCacheEntry *typentry)
Definition: typcache.c:1435
#define TCFLAGS_HAVE_PG_TYPE_DATA
Definition: typcache.c:84
static uint32 shared_record_table_hash(const void *a, size_t size, void *arg)
Definition: typcache.c:241
int compare_values_of_enum(TypeCacheEntry *tcache, Oid arg1, Oid arg2)
Definition: typcache.c:2477
#define TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS
Definition: typcache.c:103
#define TCFLAGS_HAVE_FIELD_EXTENDED_HASHING
Definition: typcache.c:102
struct SharedRecordTableKey SharedRecordTableKey
static int32 RecordCacheArrayLen
Definition: typcache.c:286
void assign_record_type_typmod(TupleDesc tupDesc)
Definition: typcache.c:1953
static HTAB * TypeCacheHash
Definition: typcache.c:78
static uint64 tupledesc_id_counter
Definition: typcache.c:294
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:346
static bool record_fields_have_compare(TypeCacheEntry *typentry)
Definition: typcache.c:1497
#define TCFLAGS_HAVE_FIELD_HASHING
Definition: typcache.c:101
static int record_type_typmod_compare(const void *a, const void *b, size_t size)
Definition: typcache.c:1937
static const dshash_parameters srtr_record_table_params
Definition: typcache.c:256
TupleDesc lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod)
Definition: typcache.c:1867
static int enum_oid_cmp(const void *left, const void *right)
Definition: typcache.c:2722
static void decr_dcc_refcount(DomainConstraintCache *dcc)
Definition: typcache.c:1243
#define TCFLAGS_CHECKED_EQ_OPR
Definition: typcache.c:87
void UpdateDomainConstraintRef(DomainConstraintRef *ref)
Definition: typcache.c:1351
static void ensure_record_cache_typmod_slot_exists(int32 typmod)
Definition: typcache.c:1710
static void cache_record_field_properties(TypeCacheEntry *typentry)
Definition: typcache.c:1521
static uint32 record_type_typmod_hash(const void *data, size_t size)
Definition: typcache.c:1926
static void load_typcache_tupdesc(TypeCacheEntry *typentry)
Definition: typcache.c:880
#define INVALID_TUPLEDESC_IDENTIFIER
Definition: typcache.h:156
#define TYPECACHE_HASH_PROC_FINFO
Definition: typcache.h:144
#define TYPECACHE_EQ_OPR
Definition: typcache.h:137
#define TYPECACHE_HASH_OPFAMILY
Definition: typcache.h:147
#define TYPECACHE_TUPDESC
Definition: typcache.h:145
#define TYPECACHE_MULTIRANGE_INFO
Definition: typcache.h:153
struct SharedRecordTypmodRegistry SharedRecordTypmodRegistry
Definition: typcache.h:176
#define TYPECACHE_EQ_OPR_FINFO
Definition: typcache.h:142
#define TYPECACHE_HASH_EXTENDED_PROC
Definition: typcache.h:151
#define TYPECACHE_BTREE_OPFAMILY
Definition: typcache.h:146
#define TYPECACHE_DOMAIN_BASE_INFO
Definition: typcache.h:149
#define TYPECACHE_DOMAIN_CONSTR_INFO
Definition: typcache.h:150
#define TYPECACHE_RANGE_INFO
Definition: typcache.h:148
#define TYPECACHE_GT_OPR
Definition: typcache.h:139
#define TYPECACHE_CMP_PROC
Definition: typcache.h:140
struct TypeCacheEntry TypeCacheEntry
#define TYPECACHE_LT_OPR
Definition: typcache.h:138
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO
Definition: typcache.h:152
#define TYPECACHE_CMP_PROC_FINFO
Definition: typcache.h:143
#define TYPECACHE_HASH_PROC
Definition: typcache.h:141