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