PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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. Information dependent on opclasses is cleared if we
27  * detect updates to pg_opclass. We also support clearing the tuple
28  * descriptor and operator/function parts of a rowtype's cache entry,
29  * since those may need to change as a consequence of ALTER TABLE.
30  * Domain constraint changes are also tracked properly.
31  *
32  *
33  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
34  * Portions Copyright (c) 1994, Regents of the University of California
35  *
36  * IDENTIFICATION
37  * src/backend/utils/cache/typcache.c
38  *
39  *-------------------------------------------------------------------------
40  */
41 #include "postgres.h"
42 
43 #include <limits.h>
44 
45 #include "access/hash.h"
46 #include "access/heapam.h"
47 #include "access/htup_details.h"
48 #include "access/nbtree.h"
49 #include "catalog/indexing.h"
50 #include "catalog/pg_am.h"
51 #include "catalog/pg_constraint.h"
52 #include "catalog/pg_enum.h"
53 #include "catalog/pg_operator.h"
54 #include "catalog/pg_range.h"
55 #include "catalog/pg_type.h"
56 #include "commands/defrem.h"
57 #include "executor/executor.h"
58 #include "optimizer/planner.h"
59 #include "utils/builtins.h"
60 #include "utils/catcache.h"
61 #include "utils/fmgroids.h"
62 #include "utils/inval.h"
63 #include "utils/lsyscache.h"
64 #include "utils/memutils.h"
65 #include "utils/rel.h"
66 #include "utils/snapmgr.h"
67 #include "utils/syscache.h"
68 #include "utils/typcache.h"
69 
70 
71 /* The main type cache hashtable searched by lookup_type_cache */
73 
74 /* List of type cache entries for domain types */
76 
77 /* Private flag bits in the TypeCacheEntry.flags field */
78 #define TCFLAGS_CHECKED_BTREE_OPCLASS 0x0001
79 #define TCFLAGS_CHECKED_HASH_OPCLASS 0x0002
80 #define TCFLAGS_CHECKED_EQ_OPR 0x0004
81 #define TCFLAGS_CHECKED_LT_OPR 0x0008
82 #define TCFLAGS_CHECKED_GT_OPR 0x0010
83 #define TCFLAGS_CHECKED_CMP_PROC 0x0020
84 #define TCFLAGS_CHECKED_HASH_PROC 0x0040
85 #define TCFLAGS_CHECKED_ELEM_PROPERTIES 0x0080
86 #define TCFLAGS_HAVE_ELEM_EQUALITY 0x0100
87 #define TCFLAGS_HAVE_ELEM_COMPARE 0x0200
88 #define TCFLAGS_HAVE_ELEM_HASHING 0x0400
89 #define TCFLAGS_CHECKED_FIELD_PROPERTIES 0x0800
90 #define TCFLAGS_HAVE_FIELD_EQUALITY 0x1000
91 #define TCFLAGS_HAVE_FIELD_COMPARE 0x2000
92 #define TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS 0x4000
93 
94 /*
95  * Data stored about a domain type's constraints. Note that we do not create
96  * this struct for the common case of a constraint-less domain; we just set
97  * domainData to NULL to indicate that.
98  *
99  * Within a DomainConstraintCache, we abuse the DomainConstraintState node
100  * type a bit: check_expr fields point to expression plan trees, not plan
101  * state trees. When needed, expression state trees are built by flat-copying
102  * the DomainConstraintState nodes and applying ExecInitExpr to check_expr.
103  * Such a state tree is not part of the DomainConstraintCache, but is
104  * considered to belong to a DomainConstraintRef.
105  */
107 {
108  List *constraints; /* list of DomainConstraintState nodes */
109  MemoryContext dccContext; /* memory context holding all associated data */
110  long dccRefCount; /* number of references to this struct */
111 };
112 
113 /* Private information to support comparisons of enum values */
114 typedef struct
115 {
116  Oid enum_oid; /* OID of one enum value */
117  float4 sort_order; /* its sort position */
118 } EnumItem;
119 
120 typedef struct TypeCacheEnumData
121 {
122  Oid bitmap_base; /* OID corresponding to bit 0 of bitmapset */
123  Bitmapset *sorted_values; /* Set of OIDs known to be in order */
124  int num_values; /* total number of values in enum */
125  EnumItem enum_values[FLEXIBLE_ARRAY_MEMBER];
127 
128 /*
129  * We use a separate table for storing the definitions of non-anonymous
130  * record types. Once defined, a record type will be remembered for the
131  * life of the backend. Subsequent uses of the "same" record type (where
132  * sameness means equalTupleDescs) will refer to the existing table entry.
133  *
134  * Stored record types are remembered in a linear array of TupleDescs,
135  * which can be indexed quickly with the assigned typmod. There is also
136  * a hash table to speed searches for matching TupleDescs. The hash key
137  * uses just the first N columns' type OIDs, and so we may have multiple
138  * entries with the same hash key.
139  */
140 #define REC_HASH_KEYS 16 /* use this many columns in hash key */
141 
142 typedef struct RecordCacheEntry
143 {
144  /* the hash lookup key MUST BE FIRST */
145  Oid hashkey[REC_HASH_KEYS]; /* column type IDs, zero-filled */
146 
147  /* list of TupleDescs for record types with this hashkey */
150 
152 
154 static int32 RecordCacheArrayLen = 0; /* allocated length of array */
155 static int32 NextRecordTypmod = 0; /* number of entries used */
156 
157 static void load_typcache_tupdesc(TypeCacheEntry *typentry);
158 static void load_rangetype_info(TypeCacheEntry *typentry);
159 static void load_domaintype_info(TypeCacheEntry *typentry);
160 static int dcs_cmp(const void *a, const void *b);
161 static void decr_dcc_refcount(DomainConstraintCache *dcc);
162 static void dccref_deletion_callback(void *arg);
163 static List *prep_domain_constraints(List *constraints, MemoryContext execctx);
164 static bool array_element_has_equality(TypeCacheEntry *typentry);
165 static bool array_element_has_compare(TypeCacheEntry *typentry);
166 static bool array_element_has_hashing(TypeCacheEntry *typentry);
167 static void cache_array_element_properties(TypeCacheEntry *typentry);
168 static bool record_fields_have_equality(TypeCacheEntry *typentry);
169 static bool record_fields_have_compare(TypeCacheEntry *typentry);
170 static void cache_record_field_properties(TypeCacheEntry *typentry);
171 static void TypeCacheRelCallback(Datum arg, Oid relid);
172 static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
173 static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue);
174 static void load_enum_cache_data(TypeCacheEntry *tcache);
175 static EnumItem *find_enumitem(TypeCacheEnumData *enumdata, Oid arg);
176 static int enum_oid_cmp(const void *left, const void *right);
177 
178 
179 /*
180  * lookup_type_cache
181  *
182  * Fetch the type cache entry for the specified datatype, and make sure that
183  * all the fields requested by bits in 'flags' are valid.
184  *
185  * The result is never NULL --- we will ereport() if the passed type OID is
186  * invalid. Note however that we may fail to find one or more of the
187  * values requested by 'flags'; the caller needs to check whether the fields
188  * are InvalidOid or not.
189  */
191 lookup_type_cache(Oid type_id, int flags)
192 {
193  TypeCacheEntry *typentry;
194  bool found;
195 
196  if (TypeCacheHash == NULL)
197  {
198  /* First time through: initialize the hash table */
199  HASHCTL ctl;
200 
201  MemSet(&ctl, 0, sizeof(ctl));
202  ctl.keysize = sizeof(Oid);
203  ctl.entrysize = sizeof(TypeCacheEntry);
204  TypeCacheHash = hash_create("Type information cache", 64,
205  &ctl, HASH_ELEM | HASH_BLOBS);
206 
207  /* Also set up callbacks for SI invalidations */
212 
213  /* Also make sure CacheMemoryContext exists */
214  if (!CacheMemoryContext)
216  }
217 
218  /* Try to look up an existing entry */
219  typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
220  (void *) &type_id,
221  HASH_FIND, NULL);
222  if (typentry == NULL)
223  {
224  /*
225  * If we didn't find one, we want to make one. But first look up the
226  * pg_type row, just to make sure we don't make a cache entry for an
227  * invalid type OID. If the type OID is not valid, present a
228  * user-facing error, since some code paths such as domain_in() allow
229  * this function to be reached with a user-supplied OID.
230  */
231  HeapTuple tp;
232  Form_pg_type typtup;
233 
234  tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id));
235  if (!HeapTupleIsValid(tp))
236  ereport(ERROR,
237  (errcode(ERRCODE_UNDEFINED_OBJECT),
238  errmsg("type with OID %u does not exist", type_id)));
239  typtup = (Form_pg_type) GETSTRUCT(tp);
240  if (!typtup->typisdefined)
241  ereport(ERROR,
242  (errcode(ERRCODE_UNDEFINED_OBJECT),
243  errmsg("type \"%s\" is only a shell",
244  NameStr(typtup->typname))));
245 
246  /* Now make the typcache entry */
247  typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
248  (void *) &type_id,
249  HASH_ENTER, &found);
250  Assert(!found); /* it wasn't there a moment ago */
251 
252  MemSet(typentry, 0, sizeof(TypeCacheEntry));
253  typentry->type_id = type_id;
254  typentry->typlen = typtup->typlen;
255  typentry->typbyval = typtup->typbyval;
256  typentry->typalign = typtup->typalign;
257  typentry->typstorage = typtup->typstorage;
258  typentry->typtype = typtup->typtype;
259  typentry->typrelid = typtup->typrelid;
260 
261  /* If it's a domain, immediately thread it into the domain cache list */
262  if (typentry->typtype == TYPTYPE_DOMAIN)
263  {
264  typentry->nextDomain = firstDomainTypeEntry;
265  firstDomainTypeEntry = typentry;
266  }
267 
268  ReleaseSysCache(tp);
269  }
270 
271  /*
272  * Look up opclasses if we haven't already and any dependent info is
273  * requested.
274  */
279  !(typentry->flags & TCFLAGS_CHECKED_BTREE_OPCLASS))
280  {
281  Oid opclass;
282 
283  opclass = GetDefaultOpClass(type_id, BTREE_AM_OID);
284  if (OidIsValid(opclass))
285  {
286  typentry->btree_opf = get_opclass_family(opclass);
287  typentry->btree_opintype = get_opclass_input_type(opclass);
288  }
289  else
290  {
291  typentry->btree_opf = typentry->btree_opintype = InvalidOid;
292  }
293 
294  /*
295  * Reset information derived from btree opclass. Note in particular
296  * that we'll redetermine the eq_opr even if we previously found one;
297  * this matters in case a btree opclass has been added to a type that
298  * previously had only a hash opclass.
299  */
300  typentry->flags &= ~(TCFLAGS_CHECKED_EQ_OPR |
305  }
306 
307  /*
308  * If we need to look up equality operator, and there's no btree opclass,
309  * force lookup of hash opclass.
310  */
311  if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
312  !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR) &&
313  typentry->btree_opf == InvalidOid)
314  flags |= TYPECACHE_HASH_OPFAMILY;
315 
318  !(typentry->flags & TCFLAGS_CHECKED_HASH_OPCLASS))
319  {
320  Oid opclass;
321 
322  opclass = GetDefaultOpClass(type_id, HASH_AM_OID);
323  if (OidIsValid(opclass))
324  {
325  typentry->hash_opf = get_opclass_family(opclass);
326  typentry->hash_opintype = get_opclass_input_type(opclass);
327  }
328  else
329  {
330  typentry->hash_opf = typentry->hash_opintype = InvalidOid;
331  }
332 
333  /*
334  * Reset information derived from hash opclass. We do *not* reset the
335  * eq_opr; if we already found one from the btree opclass, that
336  * decision is still good.
337  */
338  typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC);
339  typentry->flags |= TCFLAGS_CHECKED_HASH_OPCLASS;
340  }
341 
342  /*
343  * Look for requested operators and functions, if we haven't already.
344  */
345  if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
346  !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR))
347  {
348  Oid eq_opr = InvalidOid;
349 
350  if (typentry->btree_opf != InvalidOid)
351  eq_opr = get_opfamily_member(typentry->btree_opf,
352  typentry->btree_opintype,
353  typentry->btree_opintype,
355  if (eq_opr == InvalidOid &&
356  typentry->hash_opf != InvalidOid)
357  eq_opr = get_opfamily_member(typentry->hash_opf,
358  typentry->hash_opintype,
359  typentry->hash_opintype,
361 
362  /*
363  * If the proposed equality operator is array_eq or record_eq, check
364  * to see if the element type or column types support equality. If
365  * not, array_eq or record_eq would fail at runtime, so we don't want
366  * to report that the type has equality.
367  */
368  if (eq_opr == ARRAY_EQ_OP &&
369  !array_element_has_equality(typentry))
370  eq_opr = InvalidOid;
371  else if (eq_opr == RECORD_EQ_OP &&
372  !record_fields_have_equality(typentry))
373  eq_opr = InvalidOid;
374 
375  /* Force update of eq_opr_finfo only if we're changing state */
376  if (typentry->eq_opr != eq_opr)
377  typentry->eq_opr_finfo.fn_oid = InvalidOid;
378 
379  typentry->eq_opr = eq_opr;
380 
381  /*
382  * Reset info about hash function whenever we pick up new info about
383  * equality operator. This is so we can ensure that the hash function
384  * matches the operator.
385  */
386  typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC);
387  typentry->flags |= TCFLAGS_CHECKED_EQ_OPR;
388  }
389  if ((flags & TYPECACHE_LT_OPR) &&
390  !(typentry->flags & TCFLAGS_CHECKED_LT_OPR))
391  {
392  Oid lt_opr = InvalidOid;
393 
394  if (typentry->btree_opf != InvalidOid)
395  lt_opr = get_opfamily_member(typentry->btree_opf,
396  typentry->btree_opintype,
397  typentry->btree_opintype,
399 
400  /* As above, make sure array_cmp or record_cmp will succeed */
401  if (lt_opr == ARRAY_LT_OP &&
402  !array_element_has_compare(typentry))
403  lt_opr = InvalidOid;
404  else if (lt_opr == RECORD_LT_OP &&
405  !record_fields_have_compare(typentry))
406  lt_opr = InvalidOid;
407 
408  typentry->lt_opr = lt_opr;
409  typentry->flags |= TCFLAGS_CHECKED_LT_OPR;
410  }
411  if ((flags & TYPECACHE_GT_OPR) &&
412  !(typentry->flags & TCFLAGS_CHECKED_GT_OPR))
413  {
414  Oid gt_opr = InvalidOid;
415 
416  if (typentry->btree_opf != InvalidOid)
417  gt_opr = get_opfamily_member(typentry->btree_opf,
418  typentry->btree_opintype,
419  typentry->btree_opintype,
421 
422  /* As above, make sure array_cmp or record_cmp will succeed */
423  if (gt_opr == ARRAY_GT_OP &&
424  !array_element_has_compare(typentry))
425  gt_opr = InvalidOid;
426  else if (gt_opr == RECORD_GT_OP &&
427  !record_fields_have_compare(typentry))
428  gt_opr = InvalidOid;
429 
430  typentry->gt_opr = gt_opr;
431  typentry->flags |= TCFLAGS_CHECKED_GT_OPR;
432  }
433  if ((flags & (TYPECACHE_CMP_PROC | TYPECACHE_CMP_PROC_FINFO)) &&
434  !(typentry->flags & TCFLAGS_CHECKED_CMP_PROC))
435  {
436  Oid cmp_proc = InvalidOid;
437 
438  if (typentry->btree_opf != InvalidOid)
439  cmp_proc = get_opfamily_proc(typentry->btree_opf,
440  typentry->btree_opintype,
441  typentry->btree_opintype,
442  BTORDER_PROC);
443 
444  /* As above, make sure array_cmp or record_cmp will succeed */
445  if (cmp_proc == F_BTARRAYCMP &&
446  !array_element_has_compare(typentry))
447  cmp_proc = InvalidOid;
448  else if (cmp_proc == F_BTRECORDCMP &&
449  !record_fields_have_compare(typentry))
450  cmp_proc = InvalidOid;
451 
452  /* Force update of cmp_proc_finfo only if we're changing state */
453  if (typentry->cmp_proc != cmp_proc)
454  typentry->cmp_proc_finfo.fn_oid = InvalidOid;
455 
456  typentry->cmp_proc = cmp_proc;
457  typentry->flags |= TCFLAGS_CHECKED_CMP_PROC;
458  }
460  !(typentry->flags & TCFLAGS_CHECKED_HASH_PROC))
461  {
462  Oid hash_proc = InvalidOid;
463 
464  /*
465  * We insist that the eq_opr, if one has been determined, match the
466  * hash opclass; else report there is no hash function.
467  */
468  if (typentry->hash_opf != InvalidOid &&
469  (!OidIsValid(typentry->eq_opr) ||
470  typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
471  typentry->hash_opintype,
472  typentry->hash_opintype,
474  hash_proc = get_opfamily_proc(typentry->hash_opf,
475  typentry->hash_opintype,
476  typentry->hash_opintype,
477  HASHPROC);
478 
479  /*
480  * As above, make sure hash_array will succeed. We don't currently
481  * support hashing for composite types, but when we do, we'll need
482  * more logic here to check that case too.
483  */
484  if (hash_proc == F_HASH_ARRAY &&
485  !array_element_has_hashing(typentry))
486  hash_proc = InvalidOid;
487 
488  /* Force update of hash_proc_finfo only if we're changing state */
489  if (typentry->hash_proc != hash_proc)
490  typentry->hash_proc_finfo.fn_oid = InvalidOid;
491 
492  typentry->hash_proc = hash_proc;
493  typentry->flags |= TCFLAGS_CHECKED_HASH_PROC;
494  }
495 
496  /*
497  * Set up fmgr lookup info as requested
498  *
499  * Note: we tell fmgr the finfo structures live in CacheMemoryContext,
500  * which is not quite right (they're really in the hash table's private
501  * memory context) but this will do for our purposes.
502  *
503  * Note: the code above avoids invalidating the finfo structs unless the
504  * referenced operator/function OID actually changes. This is to prevent
505  * unnecessary leakage of any subsidiary data attached to an finfo, since
506  * that would cause session-lifespan memory leaks.
507  */
508  if ((flags & TYPECACHE_EQ_OPR_FINFO) &&
509  typentry->eq_opr_finfo.fn_oid == InvalidOid &&
510  typentry->eq_opr != InvalidOid)
511  {
512  Oid eq_opr_func;
513 
514  eq_opr_func = get_opcode(typentry->eq_opr);
515  if (eq_opr_func != InvalidOid)
516  fmgr_info_cxt(eq_opr_func, &typentry->eq_opr_finfo,
518  }
519  if ((flags & TYPECACHE_CMP_PROC_FINFO) &&
520  typentry->cmp_proc_finfo.fn_oid == InvalidOid &&
521  typentry->cmp_proc != InvalidOid)
522  {
523  fmgr_info_cxt(typentry->cmp_proc, &typentry->cmp_proc_finfo,
525  }
526  if ((flags & TYPECACHE_HASH_PROC_FINFO) &&
527  typentry->hash_proc_finfo.fn_oid == InvalidOid &&
528  typentry->hash_proc != InvalidOid)
529  {
530  fmgr_info_cxt(typentry->hash_proc, &typentry->hash_proc_finfo,
532  }
533 
534  /*
535  * If it's a composite type (row type), get tupdesc if requested
536  */
537  if ((flags & TYPECACHE_TUPDESC) &&
538  typentry->tupDesc == NULL &&
539  typentry->typtype == TYPTYPE_COMPOSITE)
540  {
541  load_typcache_tupdesc(typentry);
542  }
543 
544  /*
545  * If requested, get information about a range type
546  */
547  if ((flags & TYPECACHE_RANGE_INFO) &&
548  typentry->rngelemtype == NULL &&
549  typentry->typtype == TYPTYPE_RANGE)
550  {
551  load_rangetype_info(typentry);
552  }
553 
554  /*
555  * If requested, get information about a domain type
556  */
557  if ((flags & TYPECACHE_DOMAIN_INFO) &&
558  (typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
559  typentry->typtype == TYPTYPE_DOMAIN)
560  {
561  load_domaintype_info(typentry);
562  }
563 
564  return typentry;
565 }
566 
567 /*
568  * load_typcache_tupdesc --- helper routine to set up composite type's tupDesc
569  */
570 static void
572 {
573  Relation rel;
574 
575  if (!OidIsValid(typentry->typrelid)) /* should not happen */
576  elog(ERROR, "invalid typrelid for composite type %u",
577  typentry->type_id);
578  rel = relation_open(typentry->typrelid, AccessShareLock);
579  Assert(rel->rd_rel->reltype == typentry->type_id);
580 
581  /*
582  * Link to the tupdesc and increment its refcount (we assert it's a
583  * refcounted descriptor). We don't use IncrTupleDescRefCount() for this,
584  * because the reference mustn't be entered in the current resource owner;
585  * it can outlive the current query.
586  */
587  typentry->tupDesc = RelationGetDescr(rel);
588 
589  Assert(typentry->tupDesc->tdrefcount > 0);
590  typentry->tupDesc->tdrefcount++;
591 
593 }
594 
595 /*
596  * load_rangetype_info --- helper routine to set up range type information
597  */
598 static void
600 {
601  Form_pg_range pg_range;
602  HeapTuple tup;
603  Oid subtypeOid;
604  Oid opclassOid;
605  Oid canonicalOid;
606  Oid subdiffOid;
607  Oid opfamilyOid;
608  Oid opcintype;
609  Oid cmpFnOid;
610 
611  /* get information from pg_range */
613  /* should not fail, since we already checked typtype ... */
614  if (!HeapTupleIsValid(tup))
615  elog(ERROR, "cache lookup failed for range type %u",
616  typentry->type_id);
617  pg_range = (Form_pg_range) GETSTRUCT(tup);
618 
619  subtypeOid = pg_range->rngsubtype;
620  typentry->rng_collation = pg_range->rngcollation;
621  opclassOid = pg_range->rngsubopc;
622  canonicalOid = pg_range->rngcanonical;
623  subdiffOid = pg_range->rngsubdiff;
624 
625  ReleaseSysCache(tup);
626 
627  /* get opclass properties and look up the comparison function */
628  opfamilyOid = get_opclass_family(opclassOid);
629  opcintype = get_opclass_input_type(opclassOid);
630 
631  cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype,
632  BTORDER_PROC);
633  if (!RegProcedureIsValid(cmpFnOid))
634  elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
635  BTORDER_PROC, opcintype, opcintype, opfamilyOid);
636 
637  /* set up cached fmgrinfo structs */
638  fmgr_info_cxt(cmpFnOid, &typentry->rng_cmp_proc_finfo,
640  if (OidIsValid(canonicalOid))
641  fmgr_info_cxt(canonicalOid, &typentry->rng_canonical_finfo,
643  if (OidIsValid(subdiffOid))
644  fmgr_info_cxt(subdiffOid, &typentry->rng_subdiff_finfo,
646 
647  /* Lastly, set up link to the element type --- this marks data valid */
648  typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
649 }
650 
651 
652 /*
653  * load_domaintype_info --- helper routine to set up domain constraint info
654  *
655  * Note: we assume we're called in a relatively short-lived context, so it's
656  * okay to leak data into the current context while scanning pg_constraint.
657  * We build the new DomainConstraintCache data in a context underneath
658  * CurrentMemoryContext, and reparent it under CacheMemoryContext when
659  * complete.
660  */
661 static void
663 {
664  Oid typeOid = typentry->type_id;
666  bool notNull = false;
667  DomainConstraintState **ccons;
668  int cconslen;
669  Relation conRel;
670  MemoryContext oldcxt;
671 
672  /*
673  * If we're here, any existing constraint info is stale, so release it.
674  * For safety, be sure to null the link before trying to delete the data.
675  */
676  if (typentry->domainData)
677  {
678  dcc = typentry->domainData;
679  typentry->domainData = NULL;
680  decr_dcc_refcount(dcc);
681  }
682 
683  /*
684  * We try to optimize the common case of no domain constraints, so don't
685  * create the dcc object and context until we find a constraint. Likewise
686  * for the temp sorting array.
687  */
688  dcc = NULL;
689  ccons = NULL;
690  cconslen = 0;
691 
692  /*
693  * Scan pg_constraint for relevant constraints. We want to find
694  * constraints for not just this domain, but any ancestor domains, so the
695  * outer loop crawls up the domain stack.
696  */
698 
699  for (;;)
700  {
701  HeapTuple tup;
702  HeapTuple conTup;
703  Form_pg_type typTup;
704  int nccons = 0;
705  ScanKeyData key[1];
706  SysScanDesc scan;
707 
708  tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid));
709  if (!HeapTupleIsValid(tup))
710  elog(ERROR, "cache lookup failed for type %u", typeOid);
711  typTup = (Form_pg_type) GETSTRUCT(tup);
712 
713  if (typTup->typtype != TYPTYPE_DOMAIN)
714  {
715  /* Not a domain, so done */
716  ReleaseSysCache(tup);
717  break;
718  }
719 
720  /* Test for NOT NULL Constraint */
721  if (typTup->typnotnull)
722  notNull = true;
723 
724  /* Look for CHECK Constraints on this domain */
725  ScanKeyInit(&key[0],
727  BTEqualStrategyNumber, F_OIDEQ,
728  ObjectIdGetDatum(typeOid));
729 
730  scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
731  NULL, 1, key);
732 
733  while (HeapTupleIsValid(conTup = systable_getnext(scan)))
734  {
736  Datum val;
737  bool isNull;
738  char *constring;
739  Expr *check_expr;
741 
742  /* Ignore non-CHECK constraints (presently, shouldn't be any) */
743  if (c->contype != CONSTRAINT_CHECK)
744  continue;
745 
746  /* Not expecting conbin to be NULL, but we'll test for it anyway */
748  conRel->rd_att, &isNull);
749  if (isNull)
750  elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
751  NameStr(typTup->typname), NameStr(c->conname));
752 
753  /* Convert conbin to C string in caller context */
754  constring = TextDatumGetCString(val);
755 
756  /* Create the DomainConstraintCache object and context if needed */
757  if (dcc == NULL)
758  {
759  MemoryContext cxt;
760 
762  "Domain constraints",
764  dcc = (DomainConstraintCache *)
766  dcc->constraints = NIL;
767  dcc->dccContext = cxt;
768  dcc->dccRefCount = 0;
769  }
770 
771  /* Create node trees in DomainConstraintCache's context */
772  oldcxt = MemoryContextSwitchTo(dcc->dccContext);
773 
774  check_expr = (Expr *) stringToNode(constring);
775 
776  /* ExecInitExpr will assume we've planned the expression */
777  check_expr = expression_planner(check_expr);
778 
781  r->name = pstrdup(NameStr(c->conname));
782  /* Must cast here because we're not storing an expr state node */
783  r->check_expr = (ExprState *) check_expr;
784 
785  MemoryContextSwitchTo(oldcxt);
786 
787  /* Accumulate constraints in an array, for sorting below */
788  if (ccons == NULL)
789  {
790  cconslen = 8;
791  ccons = (DomainConstraintState **)
792  palloc(cconslen * sizeof(DomainConstraintState *));
793  }
794  else if (nccons >= cconslen)
795  {
796  cconslen *= 2;
797  ccons = (DomainConstraintState **)
798  repalloc(ccons, cconslen * sizeof(DomainConstraintState *));
799  }
800  ccons[nccons++] = r;
801  }
802 
803  systable_endscan(scan);
804 
805  if (nccons > 0)
806  {
807  /*
808  * Sort the items for this domain, so that CHECKs are applied in a
809  * deterministic order.
810  */
811  if (nccons > 1)
812  qsort(ccons, nccons, sizeof(DomainConstraintState *), dcs_cmp);
813 
814  /*
815  * Now attach them to the overall list. Use lcons() here because
816  * constraints of parent domains should be applied earlier.
817  */
818  oldcxt = MemoryContextSwitchTo(dcc->dccContext);
819  while (nccons > 0)
820  dcc->constraints = lcons(ccons[--nccons], dcc->constraints);
821  MemoryContextSwitchTo(oldcxt);
822  }
823 
824  /* loop to next domain in stack */
825  typeOid = typTup->typbasetype;
826  ReleaseSysCache(tup);
827  }
828 
829  heap_close(conRel, AccessShareLock);
830 
831  /*
832  * Only need to add one NOT NULL check regardless of how many domains in
833  * the stack request it.
834  */
835  if (notNull)
836  {
838 
839  /* Create the DomainConstraintCache object and context if needed */
840  if (dcc == NULL)
841  {
842  MemoryContext cxt;
843 
845  "Domain constraints",
847  dcc = (DomainConstraintCache *)
849  dcc->constraints = NIL;
850  dcc->dccContext = cxt;
851  dcc->dccRefCount = 0;
852  }
853 
854  /* Create node trees in DomainConstraintCache's context */
855  oldcxt = MemoryContextSwitchTo(dcc->dccContext);
856 
858 
860  r->name = pstrdup("NOT NULL");
861  r->check_expr = NULL;
862 
863  /* lcons to apply the nullness check FIRST */
864  dcc->constraints = lcons(r, dcc->constraints);
865 
866  MemoryContextSwitchTo(oldcxt);
867  }
868 
869  /*
870  * If we made a constraint object, move it into CacheMemoryContext and
871  * attach it to the typcache entry.
872  */
873  if (dcc)
874  {
876  typentry->domainData = dcc;
877  dcc->dccRefCount++; /* count the typcache's reference */
878  }
879 
880  /* Either way, the typcache entry's domain data is now valid. */
882 }
883 
884 /*
885  * qsort comparator to sort DomainConstraintState pointers by name
886  */
887 static int
888 dcs_cmp(const void *a, const void *b)
889 {
890  const DomainConstraintState *const * ca = (const DomainConstraintState *const *) a;
891  const DomainConstraintState *const * cb = (const DomainConstraintState *const *) b;
892 
893  return strcmp((*ca)->name, (*cb)->name);
894 }
895 
896 /*
897  * decr_dcc_refcount --- decrement a DomainConstraintCache's refcount,
898  * and free it if no references remain
899  */
900 static void
902 {
903  Assert(dcc->dccRefCount > 0);
904  if (--(dcc->dccRefCount) <= 0)
906 }
907 
908 /*
909  * Context reset/delete callback for a DomainConstraintRef
910  */
911 static void
913 {
915  DomainConstraintCache *dcc = ref->dcc;
916 
917  /* Paranoia --- be sure link is nulled before trying to release */
918  if (dcc)
919  {
920  ref->constraints = NIL;
921  ref->dcc = NULL;
922  decr_dcc_refcount(dcc);
923  }
924 }
925 
926 /*
927  * prep_domain_constraints --- prepare domain constraints for execution
928  *
929  * The expression trees stored in the DomainConstraintCache's list are
930  * converted to executable expression state trees stored in execctx.
931  */
932 static List *
934 {
935  List *result = NIL;
936  MemoryContext oldcxt;
937  ListCell *lc;
938 
939  oldcxt = MemoryContextSwitchTo(execctx);
940 
941  foreach(lc, constraints)
942  {
944  DomainConstraintState *newr;
945 
947  newr->constrainttype = r->constrainttype;
948  newr->name = r->name;
949  /* Must cast here because cache items contain expr plan trees */
950  newr->check_expr = ExecInitExpr((Expr *) r->check_expr, NULL);
951 
952  result = lappend(result, newr);
953  }
954 
955  MemoryContextSwitchTo(oldcxt);
956 
957  return result;
958 }
959 
960 /*
961  * InitDomainConstraintRef --- initialize a DomainConstraintRef struct
962  *
963  * Caller must tell us the MemoryContext in which the DomainConstraintRef
964  * lives. The ref will be cleaned up when that context is reset/deleted.
965  */
966 void
968  MemoryContext refctx)
969 {
970  /* Look up the typcache entry --- we assume it survives indefinitely */
972  /* For safety, establish the callback before acquiring a refcount */
973  ref->refctx = refctx;
974  ref->dcc = NULL;
976  ref->callback.arg = (void *) ref;
978  /* Acquire refcount if there are constraints, and set up exported list */
979  if (ref->tcache->domainData)
980  {
981  ref->dcc = ref->tcache->domainData;
982  ref->dcc->dccRefCount++;
984  ref->refctx);
985  }
986  else
987  ref->constraints = NIL;
988 }
989 
990 /*
991  * UpdateDomainConstraintRef --- recheck validity of domain constraint info
992  *
993  * If the domain's constraint set changed, ref->constraints is updated to
994  * point at a new list of cached constraints.
995  *
996  * In the normal case where nothing happened to the domain, this is cheap
997  * enough that it's reasonable (and expected) to check before *each* use
998  * of the constraint info.
999  */
1000 void
1002 {
1003  TypeCacheEntry *typentry = ref->tcache;
1004 
1005  /* Make sure typcache entry's data is up to date */
1006  if ((typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
1007  typentry->typtype == TYPTYPE_DOMAIN)
1008  load_domaintype_info(typentry);
1009 
1010  /* Transfer to ref object if there's new info, adjusting refcounts */
1011  if (ref->dcc != typentry->domainData)
1012  {
1013  /* Paranoia --- be sure link is nulled before trying to release */
1014  DomainConstraintCache *dcc = ref->dcc;
1015 
1016  if (dcc)
1017  {
1018  /*
1019  * Note: we just leak the previous list of executable domain
1020  * constraints. Alternatively, we could keep those in a child
1021  * context of ref->refctx and free that context at this point.
1022  * However, in practice this code path will be taken so seldom
1023  * that the extra bookkeeping for a child context doesn't seem
1024  * worthwhile; we'll just allow a leak for the lifespan of refctx.
1025  */
1026  ref->constraints = NIL;
1027  ref->dcc = NULL;
1028  decr_dcc_refcount(dcc);
1029  }
1030  dcc = typentry->domainData;
1031  if (dcc)
1032  {
1033  ref->dcc = dcc;
1034  dcc->dccRefCount++;
1036  ref->refctx);
1037  }
1038  }
1039 }
1040 
1041 /*
1042  * DomainHasConstraints --- utility routine to check if a domain has constraints
1043  *
1044  * This is defined to return false, not fail, if type is not a domain.
1045  */
1046 bool
1048 {
1049  TypeCacheEntry *typentry;
1050 
1051  /*
1052  * Note: a side effect is to cause the typcache's domain data to become
1053  * valid. This is fine since we'll likely need it soon if there is any.
1054  */
1055  typentry = lookup_type_cache(type_id, TYPECACHE_DOMAIN_INFO);
1056 
1057  return (typentry->domainData != NULL);
1058 }
1059 
1060 
1061 /*
1062  * array_element_has_equality and friends are helper routines to check
1063  * whether we should believe that array_eq and related functions will work
1064  * on the given array type or composite type.
1065  *
1066  * The logic above may call these repeatedly on the same type entry, so we
1067  * make use of the typentry->flags field to cache the results once known.
1068  * Also, we assume that we'll probably want all these facts about the type
1069  * if we want any, so we cache them all using only one lookup of the
1070  * component datatype(s).
1071  */
1072 
1073 static bool
1075 {
1076  if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1078  return (typentry->flags & TCFLAGS_HAVE_ELEM_EQUALITY) != 0;
1079 }
1080 
1081 static bool
1083 {
1084  if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1086  return (typentry->flags & TCFLAGS_HAVE_ELEM_COMPARE) != 0;
1087 }
1088 
1089 static bool
1091 {
1092  if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1094  return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1095 }
1096 
1097 static void
1099 {
1100  Oid elem_type = get_base_element_type(typentry->type_id);
1101 
1102  if (OidIsValid(elem_type))
1103  {
1104  TypeCacheEntry *elementry;
1105 
1106  elementry = lookup_type_cache(elem_type,
1110  if (OidIsValid(elementry->eq_opr))
1111  typentry->flags |= TCFLAGS_HAVE_ELEM_EQUALITY;
1112  if (OidIsValid(elementry->cmp_proc))
1113  typentry->flags |= TCFLAGS_HAVE_ELEM_COMPARE;
1114  if (OidIsValid(elementry->hash_proc))
1115  typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1116  }
1118 }
1119 
1120 static bool
1122 {
1123  if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1125  return (typentry->flags & TCFLAGS_HAVE_FIELD_EQUALITY) != 0;
1126 }
1127 
1128 static bool
1130 {
1131  if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1133  return (typentry->flags & TCFLAGS_HAVE_FIELD_COMPARE) != 0;
1134 }
1135 
1136 static void
1138 {
1139  /*
1140  * For type RECORD, we can't really tell what will work, since we don't
1141  * have access here to the specific anonymous type. Just assume that
1142  * everything will (we may get a failure at runtime ...)
1143  */
1144  if (typentry->type_id == RECORDOID)
1145  typentry->flags |= (TCFLAGS_HAVE_FIELD_EQUALITY |
1147  else if (typentry->typtype == TYPTYPE_COMPOSITE)
1148  {
1149  TupleDesc tupdesc;
1150  int newflags;
1151  int i;
1152 
1153  /* Fetch composite type's tupdesc if we don't have it already */
1154  if (typentry->tupDesc == NULL)
1155  load_typcache_tupdesc(typentry);
1156  tupdesc = typentry->tupDesc;
1157 
1158  /* Must bump the refcount while we do additional catalog lookups */
1159  IncrTupleDescRefCount(tupdesc);
1160 
1161  /* Have each property if all non-dropped fields have the property */
1162  newflags = (TCFLAGS_HAVE_FIELD_EQUALITY |
1164  for (i = 0; i < tupdesc->natts; i++)
1165  {
1166  TypeCacheEntry *fieldentry;
1167 
1168  if (tupdesc->attrs[i]->attisdropped)
1169  continue;
1170 
1171  fieldentry = lookup_type_cache(tupdesc->attrs[i]->atttypid,
1174  if (!OidIsValid(fieldentry->eq_opr))
1175  newflags &= ~TCFLAGS_HAVE_FIELD_EQUALITY;
1176  if (!OidIsValid(fieldentry->cmp_proc))
1177  newflags &= ~TCFLAGS_HAVE_FIELD_COMPARE;
1178 
1179  /* We can drop out of the loop once we disprove all bits */
1180  if (newflags == 0)
1181  break;
1182  }
1183  typentry->flags |= newflags;
1184 
1185  DecrTupleDescRefCount(tupdesc);
1186  }
1188 }
1189 
1190 
1191 /*
1192  * lookup_rowtype_tupdesc_internal --- internal routine to lookup a rowtype
1193  *
1194  * Same API as lookup_rowtype_tupdesc_noerror, but the returned tupdesc
1195  * hasn't had its refcount bumped.
1196  */
1197 static TupleDesc
1198 lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
1199 {
1200  if (type_id != RECORDOID)
1201  {
1202  /*
1203  * It's a named composite type, so use the regular typcache.
1204  */
1205  TypeCacheEntry *typentry;
1206 
1207  typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
1208  if (typentry->tupDesc == NULL && !noError)
1209  ereport(ERROR,
1210  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1211  errmsg("type %s is not composite",
1212  format_type_be(type_id))));
1213  return typentry->tupDesc;
1214  }
1215  else
1216  {
1217  /*
1218  * It's a transient record type, so look in our record-type table.
1219  */
1220  if (typmod < 0 || typmod >= NextRecordTypmod)
1221  {
1222  if (!noError)
1223  ereport(ERROR,
1224  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1225  errmsg("record type has not been registered")));
1226  return NULL;
1227  }
1228  return RecordCacheArray[typmod];
1229  }
1230 }
1231 
1232 /*
1233  * lookup_rowtype_tupdesc
1234  *
1235  * Given a typeid/typmod that should describe a known composite type,
1236  * return the tuple descriptor for the type. Will ereport on failure.
1237  * (Use ereport because this is reachable with user-specified OIDs,
1238  * for example from record_in().)
1239  *
1240  * Note: on success, we increment the refcount of the returned TupleDesc,
1241  * and log the reference in CurrentResourceOwner. Caller should call
1242  * ReleaseTupleDesc or DecrTupleDescRefCount when done using the tupdesc.
1243  */
1244 TupleDesc
1246 {
1247  TupleDesc tupDesc;
1248 
1249  tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
1250  IncrTupleDescRefCount(tupDesc);
1251  return tupDesc;
1252 }
1253 
1254 /*
1255  * lookup_rowtype_tupdesc_noerror
1256  *
1257  * As above, but if the type is not a known composite type and noError
1258  * is true, returns NULL instead of ereport'ing. (Note that if a bogus
1259  * type_id is passed, you'll get an ereport anyway.)
1260  */
1261 TupleDesc
1262 lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
1263 {
1264  TupleDesc tupDesc;
1265 
1266  tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
1267  if (tupDesc != NULL)
1268  IncrTupleDescRefCount(tupDesc);
1269  return tupDesc;
1270 }
1271 
1272 /*
1273  * lookup_rowtype_tupdesc_copy
1274  *
1275  * Like lookup_rowtype_tupdesc(), but the returned TupleDesc has been
1276  * copied into the CurrentMemoryContext and is not reference-counted.
1277  */
1278 TupleDesc
1280 {
1281  TupleDesc tmp;
1282 
1283  tmp = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
1284  return CreateTupleDescCopyConstr(tmp);
1285 }
1286 
1287 
1288 /*
1289  * assign_record_type_typmod
1290  *
1291  * Given a tuple descriptor for a RECORD type, find or create a cache entry
1292  * for the type, and set the tupdesc's tdtypmod field to a value that will
1293  * identify this cache entry to lookup_rowtype_tupdesc.
1294  */
1295 void
1297 {
1298  RecordCacheEntry *recentry;
1299  TupleDesc entDesc;
1300  Oid hashkey[REC_HASH_KEYS];
1301  bool found;
1302  int i;
1303  ListCell *l;
1304  int32 newtypmod;
1305  MemoryContext oldcxt;
1306 
1307  Assert(tupDesc->tdtypeid == RECORDOID);
1308 
1309  if (RecordCacheHash == NULL)
1310  {
1311  /* First time through: initialize the hash table */
1312  HASHCTL ctl;
1313 
1314  MemSet(&ctl, 0, sizeof(ctl));
1315  ctl.keysize = REC_HASH_KEYS * sizeof(Oid);
1316  ctl.entrysize = sizeof(RecordCacheEntry);
1317  RecordCacheHash = hash_create("Record information cache", 64,
1318  &ctl, HASH_ELEM | HASH_BLOBS);
1319 
1320  /* Also make sure CacheMemoryContext exists */
1321  if (!CacheMemoryContext)
1323  }
1324 
1325  /* Find or create a hashtable entry for this hash class */
1326  MemSet(hashkey, 0, sizeof(hashkey));
1327  for (i = 0; i < tupDesc->natts; i++)
1328  {
1329  if (i >= REC_HASH_KEYS)
1330  break;
1331  hashkey[i] = tupDesc->attrs[i]->atttypid;
1332  }
1333  recentry = (RecordCacheEntry *) hash_search(RecordCacheHash,
1334  (void *) hashkey,
1335  HASH_ENTER, &found);
1336  if (!found)
1337  {
1338  /* New entry ... hash_search initialized only the hash key */
1339  recentry->tupdescs = NIL;
1340  }
1341 
1342  /* Look for existing record cache entry */
1343  foreach(l, recentry->tupdescs)
1344  {
1345  entDesc = (TupleDesc) lfirst(l);
1346  if (equalTupleDescs(tupDesc, entDesc))
1347  {
1348  tupDesc->tdtypmod = entDesc->tdtypmod;
1349  return;
1350  }
1351  }
1352 
1353  /* Not present, so need to manufacture an entry */
1355 
1356  if (RecordCacheArray == NULL)
1357  {
1358  RecordCacheArray = (TupleDesc *) palloc(64 * sizeof(TupleDesc));
1359  RecordCacheArrayLen = 64;
1360  }
1362  {
1363  int32 newlen = RecordCacheArrayLen * 2;
1364 
1365  RecordCacheArray = (TupleDesc *) repalloc(RecordCacheArray,
1366  newlen * sizeof(TupleDesc));
1367  RecordCacheArrayLen = newlen;
1368  }
1369 
1370  /* if fail in subrs, no damage except possibly some wasted memory... */
1371  entDesc = CreateTupleDescCopy(tupDesc);
1372  recentry->tupdescs = lcons(entDesc, recentry->tupdescs);
1373  /* mark it as a reference-counted tupdesc */
1374  entDesc->tdrefcount = 1;
1375  /* now it's safe to advance NextRecordTypmod */
1376  newtypmod = NextRecordTypmod++;
1377  entDesc->tdtypmod = newtypmod;
1378  RecordCacheArray[newtypmod] = entDesc;
1379 
1380  /* report to caller as well */
1381  tupDesc->tdtypmod = newtypmod;
1382 
1383  MemoryContextSwitchTo(oldcxt);
1384 }
1385 
1386 /*
1387  * TypeCacheRelCallback
1388  * Relcache inval callback function
1389  *
1390  * Delete the cached tuple descriptor (if any) for the given rel's composite
1391  * type, or for all composite types if relid == InvalidOid. Also reset
1392  * whatever info we have cached about the composite type's comparability.
1393  *
1394  * This is called when a relcache invalidation event occurs for the given
1395  * relid. We must scan the whole typcache hash since we don't know the
1396  * type OID corresponding to the relid. We could do a direct search if this
1397  * were a syscache-flush callback on pg_type, but then we would need all
1398  * ALTER-TABLE-like commands that could modify a rowtype to issue syscache
1399  * invals against the rel's pg_type OID. The extra SI signaling could very
1400  * well cost more than we'd save, since in most usages there are not very
1401  * many entries in a backend's typcache. The risk of bugs-of-omission seems
1402  * high, too.
1403  *
1404  * Another possibility, with only localized impact, is to maintain a second
1405  * hashtable that indexes composite-type typcache entries by their typrelid.
1406  * But it's still not clear it's worth the trouble.
1407  */
1408 static void
1410 {
1412  TypeCacheEntry *typentry;
1413 
1414  /* TypeCacheHash must exist, else this callback wouldn't be registered */
1415  hash_seq_init(&status, TypeCacheHash);
1416  while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
1417  {
1418  if (typentry->typtype != TYPTYPE_COMPOSITE)
1419  continue; /* skip non-composites */
1420 
1421  /* Skip if no match, unless we're zapping all composite types */
1422  if (relid != typentry->typrelid && relid != InvalidOid)
1423  continue;
1424 
1425  /* Delete tupdesc if we have it */
1426  if (typentry->tupDesc != NULL)
1427  {
1428  /*
1429  * Release our refcount, and free the tupdesc if none remain.
1430  * (Can't use DecrTupleDescRefCount because this reference is not
1431  * logged in current resource owner.)
1432  */
1433  Assert(typentry->tupDesc->tdrefcount > 0);
1434  if (--typentry->tupDesc->tdrefcount == 0)
1435  FreeTupleDesc(typentry->tupDesc);
1436  typentry->tupDesc = NULL;
1437  }
1438 
1439  /* Reset equality/comparison/hashing validity information */
1440  typentry->flags = 0;
1441  }
1442 }
1443 
1444 /*
1445  * TypeCacheOpcCallback
1446  * Syscache inval callback function
1447  *
1448  * This is called when a syscache invalidation event occurs for any pg_opclass
1449  * row. In principle we could probably just invalidate data dependent on the
1450  * particular opclass, but since updates on pg_opclass are rare in production
1451  * it doesn't seem worth a lot of complication: we just mark all cached data
1452  * invalid.
1453  *
1454  * Note that we don't bother watching for updates on pg_amop or pg_amproc.
1455  * This should be safe because ALTER OPERATOR FAMILY ADD/DROP OPERATOR/FUNCTION
1456  * is not allowed to be used to add/drop the primary operators and functions
1457  * of an opclass, only cross-type members of a family; and the latter sorts
1458  * of members are not going to get cached here.
1459  */
1460 static void
1461 TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue)
1462 {
1464  TypeCacheEntry *typentry;
1465 
1466  /* TypeCacheHash must exist, else this callback wouldn't be registered */
1467  hash_seq_init(&status, TypeCacheHash);
1468  while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
1469  {
1470  /* Reset equality/comparison/hashing validity information */
1471  typentry->flags = 0;
1472  }
1473 }
1474 
1475 /*
1476  * TypeCacheConstrCallback
1477  * Syscache inval callback function
1478  *
1479  * This is called when a syscache invalidation event occurs for any
1480  * pg_constraint or pg_type row. We flush information about domain
1481  * constraints when this happens.
1482  *
1483  * It's slightly annoying that we can't tell whether the inval event was for a
1484  * domain constraint/type record or not; there's usually more update traffic
1485  * for table constraints/types than domain constraints, so we'll do a lot of
1486  * useless flushes. Still, this is better than the old no-caching-at-all
1487  * approach to domain constraints.
1488  */
1489 static void
1490 TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue)
1491 {
1492  TypeCacheEntry *typentry;
1493 
1494  /*
1495  * Because this is called very frequently, and typically very few of the
1496  * typcache entries are for domains, we don't use hash_seq_search here.
1497  * Instead we thread all the domain-type entries together so that we can
1498  * visit them cheaply.
1499  */
1500  for (typentry = firstDomainTypeEntry;
1501  typentry != NULL;
1502  typentry = typentry->nextDomain)
1503  {
1504  /* Reset domain constraint validity information */
1506  }
1507 }
1508 
1509 
1510 /*
1511  * Check if given OID is part of the subset that's sortable by comparisons
1512  */
1513 static inline bool
1515 {
1516  Oid offset;
1517 
1518  if (arg < enumdata->bitmap_base)
1519  return false;
1520  offset = arg - enumdata->bitmap_base;
1521  if (offset > (Oid) INT_MAX)
1522  return false;
1523  return bms_is_member((int) offset, enumdata->sorted_values);
1524 }
1525 
1526 
1527 /*
1528  * compare_values_of_enum
1529  * Compare two members of an enum type.
1530  * Return <0, 0, or >0 according as arg1 <, =, or > arg2.
1531  *
1532  * Note: currently, the enumData cache is refreshed only if we are asked
1533  * to compare an enum value that is not already in the cache. This is okay
1534  * because there is no support for re-ordering existing values, so comparisons
1535  * of previously cached values will return the right answer even if other
1536  * values have been added since we last loaded the cache.
1537  *
1538  * Note: the enum logic has a special-case rule about even-numbered versus
1539  * odd-numbered OIDs, but we take no account of that rule here; this
1540  * routine shouldn't even get called when that rule applies.
1541  */
1542 int
1544 {
1545  TypeCacheEnumData *enumdata;
1546  EnumItem *item1;
1547  EnumItem *item2;
1548 
1549  /*
1550  * Equal OIDs are certainly equal --- this case was probably handled by
1551  * our caller, but we may as well check.
1552  */
1553  if (arg1 == arg2)
1554  return 0;
1555 
1556  /* Load up the cache if first time through */
1557  if (tcache->enumData == NULL)
1558  load_enum_cache_data(tcache);
1559  enumdata = tcache->enumData;
1560 
1561  /*
1562  * If both OIDs are known-sorted, we can just compare them directly.
1563  */
1564  if (enum_known_sorted(enumdata, arg1) &&
1565  enum_known_sorted(enumdata, arg2))
1566  {
1567  if (arg1 < arg2)
1568  return -1;
1569  else
1570  return 1;
1571  }
1572 
1573  /*
1574  * Slow path: we have to identify their actual sort-order positions.
1575  */
1576  item1 = find_enumitem(enumdata, arg1);
1577  item2 = find_enumitem(enumdata, arg2);
1578 
1579  if (item1 == NULL || item2 == NULL)
1580  {
1581  /*
1582  * We couldn't find one or both values. That means the enum has
1583  * changed under us, so re-initialize the cache and try again. We
1584  * don't bother retrying the known-sorted case in this path.
1585  */
1586  load_enum_cache_data(tcache);
1587  enumdata = tcache->enumData;
1588 
1589  item1 = find_enumitem(enumdata, arg1);
1590  item2 = find_enumitem(enumdata, arg2);
1591 
1592  /*
1593  * If we still can't find the values, complain: we must have corrupt
1594  * data.
1595  */
1596  if (item1 == NULL)
1597  elog(ERROR, "enum value %u not found in cache for enum %s",
1598  arg1, format_type_be(tcache->type_id));
1599  if (item2 == NULL)
1600  elog(ERROR, "enum value %u not found in cache for enum %s",
1601  arg2, format_type_be(tcache->type_id));
1602  }
1603 
1604  if (item1->sort_order < item2->sort_order)
1605  return -1;
1606  else if (item1->sort_order > item2->sort_order)
1607  return 1;
1608  else
1609  return 0;
1610 }
1611 
1612 /*
1613  * Load (or re-load) the enumData member of the typcache entry.
1614  */
1615 static void
1617 {
1618  TypeCacheEnumData *enumdata;
1619  Relation enum_rel;
1620  SysScanDesc enum_scan;
1621  HeapTuple enum_tuple;
1622  ScanKeyData skey;
1623  EnumItem *items;
1624  int numitems;
1625  int maxitems;
1626  Oid bitmap_base;
1627  Bitmapset *bitmap;
1628  MemoryContext oldcxt;
1629  int bm_size,
1630  start_pos;
1631 
1632  /* Check that this is actually an enum */
1633  if (tcache->typtype != TYPTYPE_ENUM)
1634  ereport(ERROR,
1635  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1636  errmsg("%s is not an enum",
1637  format_type_be(tcache->type_id))));
1638 
1639  /*
1640  * Read all the information for members of the enum type. We collect the
1641  * info in working memory in the caller's context, and then transfer it to
1642  * permanent memory in CacheMemoryContext. This minimizes the risk of
1643  * leaking memory from CacheMemoryContext in the event of an error partway
1644  * through.
1645  */
1646  maxitems = 64;
1647  items = (EnumItem *) palloc(sizeof(EnumItem) * maxitems);
1648  numitems = 0;
1649 
1650  /* Scan pg_enum for the members of the target enum type. */
1651  ScanKeyInit(&skey,
1653  BTEqualStrategyNumber, F_OIDEQ,
1654  ObjectIdGetDatum(tcache->type_id));
1655 
1657  enum_scan = systable_beginscan(enum_rel,
1659  true, NULL,
1660  1, &skey);
1661 
1662  while (HeapTupleIsValid(enum_tuple = systable_getnext(enum_scan)))
1663  {
1664  Form_pg_enum en = (Form_pg_enum) GETSTRUCT(enum_tuple);
1665 
1666  if (numitems >= maxitems)
1667  {
1668  maxitems *= 2;
1669  items = (EnumItem *) repalloc(items, sizeof(EnumItem) * maxitems);
1670  }
1671  items[numitems].enum_oid = HeapTupleGetOid(enum_tuple);
1672  items[numitems].sort_order = en->enumsortorder;
1673  numitems++;
1674  }
1675 
1676  systable_endscan(enum_scan);
1677  heap_close(enum_rel, AccessShareLock);
1678 
1679  /* Sort the items into OID order */
1680  qsort(items, numitems, sizeof(EnumItem), enum_oid_cmp);
1681 
1682  /*
1683  * Here, we create a bitmap listing a subset of the enum's OIDs that are
1684  * known to be in order and can thus be compared with just OID comparison.
1685  *
1686  * The point of this is that the enum's initial OIDs were certainly in
1687  * order, so there is some subset that can be compared via OID comparison;
1688  * and we'd rather not do binary searches unnecessarily.
1689  *
1690  * This is somewhat heuristic, and might identify a subset of OIDs that
1691  * isn't exactly what the type started with. That's okay as long as the
1692  * subset is correctly sorted.
1693  */
1694  bitmap_base = InvalidOid;
1695  bitmap = NULL;
1696  bm_size = 1; /* only save sets of at least 2 OIDs */
1697 
1698  for (start_pos = 0; start_pos < numitems - 1; start_pos++)
1699  {
1700  /*
1701  * Identify longest sorted subsequence starting at start_pos
1702  */
1703  Bitmapset *this_bitmap = bms_make_singleton(0);
1704  int this_bm_size = 1;
1705  Oid start_oid = items[start_pos].enum_oid;
1706  float4 prev_order = items[start_pos].sort_order;
1707  int i;
1708 
1709  for (i = start_pos + 1; i < numitems; i++)
1710  {
1711  Oid offset;
1712 
1713  offset = items[i].enum_oid - start_oid;
1714  /* quit if bitmap would be too large; cutoff is arbitrary */
1715  if (offset >= 8192)
1716  break;
1717  /* include the item if it's in-order */
1718  if (items[i].sort_order > prev_order)
1719  {
1720  prev_order = items[i].sort_order;
1721  this_bitmap = bms_add_member(this_bitmap, (int) offset);
1722  this_bm_size++;
1723  }
1724  }
1725 
1726  /* Remember it if larger than previous best */
1727  if (this_bm_size > bm_size)
1728  {
1729  bms_free(bitmap);
1730  bitmap_base = start_oid;
1731  bitmap = this_bitmap;
1732  bm_size = this_bm_size;
1733  }
1734  else
1735  bms_free(this_bitmap);
1736 
1737  /*
1738  * Done if it's not possible to find a longer sequence in the rest of
1739  * the list. In typical cases this will happen on the first
1740  * iteration, which is why we create the bitmaps on the fly instead of
1741  * doing a second pass over the list.
1742  */
1743  if (bm_size >= (numitems - start_pos - 1))
1744  break;
1745  }
1746 
1747  /* OK, copy the data into CacheMemoryContext */
1749  enumdata = (TypeCacheEnumData *)
1750  palloc(offsetof(TypeCacheEnumData, enum_values) +
1751  numitems * sizeof(EnumItem));
1752  enumdata->bitmap_base = bitmap_base;
1753  enumdata->sorted_values = bms_copy(bitmap);
1754  enumdata->num_values = numitems;
1755  memcpy(enumdata->enum_values, items, numitems * sizeof(EnumItem));
1756  MemoryContextSwitchTo(oldcxt);
1757 
1758  pfree(items);
1759  bms_free(bitmap);
1760 
1761  /* And link the finished cache struct into the typcache */
1762  if (tcache->enumData != NULL)
1763  pfree(tcache->enumData);
1764  tcache->enumData = enumdata;
1765 }
1766 
1767 /*
1768  * Locate the EnumItem with the given OID, if present
1769  */
1770 static EnumItem *
1772 {
1773  EnumItem srch;
1774 
1775  /* On some versions of Solaris, bsearch of zero items dumps core */
1776  if (enumdata->num_values <= 0)
1777  return NULL;
1778 
1779  srch.enum_oid = arg;
1780  return bsearch(&srch, enumdata->enum_values, enumdata->num_values,
1781  sizeof(EnumItem), enum_oid_cmp);
1782 }
1783 
1784 /*
1785  * qsort comparison function for OID-ordered EnumItems
1786  */
1787 static int
1788 enum_oid_cmp(const void *left, const void *right)
1789 {
1790  const EnumItem *l = (const EnumItem *) left;
1791  const EnumItem *r = (const EnumItem *) right;
1792 
1793  if (l->enum_oid < r->enum_oid)
1794  return -1;
1795  else if (l->enum_oid > r->enum_oid)
1796  return 1;
1797  else
1798  return 0;
1799 }
MemoryContextCallback callback
Definition: typcache.h:138
int compare_values_of_enum(TypeCacheEntry *tcache, Oid arg1, Oid arg2)
Definition: typcache.c:1543
struct TypeCacheEnumData TypeCacheEnumData
MemoryContextCallbackFunction func
Definition: palloc.h:49
struct TypeCacheEnumData * enumData
Definition: typcache.h:103
#define NIL
Definition: pg_list.h:69
static bool array_element_has_hashing(TypeCacheEntry *typentry)
Definition: typcache.c:1090
#define TYPTYPE_DOMAIN
Definition: pg_type.h:710
static void load_typcache_tupdesc(TypeCacheEntry *typentry)
Definition: typcache.c:571
void IncrTupleDescRefCount(TupleDesc tupdesc)
Definition: tupdesc.c:316
void * stringToNode(char *str)
Definition: read.c:38
FormData_pg_range * Form_pg_range
Definition: pg_range.h:49
FmgrInfo rng_cmp_proc_finfo
Definition: typcache.h:86
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:141
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:200
#define BTORDER_PROC
Definition: nbtree.h:229
Oid tdtypeid
Definition: tupdesc.h:77
DomainConstraintCache * dcc
Definition: typcache.h:137
#define TYPECACHE_RANGE_INFO
Definition: typcache.h:121
#define TCFLAGS_CHECKED_FIELD_PROPERTIES
Definition: typcache.c:89
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:1383
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:493
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:110
#define fastgetattr(tup, attnum, tupleDesc, isnull)
Definition: htup_details.h:719
Oid hash_opintype
Definition: typcache.h:53
#define TCFLAGS_CHECKED_EQ_OPR
Definition: typcache.c:80
void UpdateDomainConstraintRef(DomainConstraintRef *ref)
Definition: typcache.c:1001
#define HASH_ELEM
Definition: hsearch.h:87
static TypeCacheEntry * firstDomainTypeEntry
Definition: typcache.c:75
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1245
#define RelationGetDescr(relation)
Definition: rel.h:425
#define TCFLAGS_HAVE_ELEM_COMPARE
Definition: typcache.c:87
static void dccref_deletion_callback(void *arg)
Definition: typcache.c:912
MemoryContext dccContext
Definition: typcache.c:109
DomainConstraintType constrainttype
Definition: execnodes.h:1022
List * tupdescs
Definition: typcache.c:148
DomainConstraintCache * domainData
Definition: typcache.h:94
#define TYPTYPE_COMPOSITE
Definition: pg_type.h:709
void MemoryContextSetParent(MemoryContext context, MemoryContext new_parent)
Definition: mcxt.c:317
struct RecordCacheEntry RecordCacheEntry
struct TypeCacheEntry TypeCacheEntry
#define HTEqualStrategyNumber
Definition: hash.h:261
void InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref, MemoryContext refctx)
Definition: typcache.c:967
char * pstrdup(const char *in)
Definition: mcxt.c:1165
#define TYPECACHE_EQ_OPR_FINFO
Definition: typcache.h:115
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:155
#define BTREE_AM_OID
Definition: pg_am.h:70
Expr * expression_planner(Expr *expr)
Definition: planner.c:5211
#define TYPECACHE_HASH_PROC_FINFO
Definition: typcache.h:117
Form_pg_attribute * attrs
Definition: tupdesc.h:74
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
ExprState * check_expr
Definition: execnodes.h:1024
#define AccessShareLock
Definition: lockdefs.h:36
Size entrysize
Definition: hsearch.h:73
#define TYPECACHE_EQ_OPR
Definition: typcache.h:110
int errcode(int sqlerrcode)
Definition: elog.c:575
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: heapam.c:1263
#define MemSet(start, val, len)
Definition: c.h:853
char * format_type_be(Oid type_oid)
Definition: format_type.c:94
static int dcs_cmp(const void *a, const void *b)
Definition: typcache.c:888
static HTAB * RecordCacheHash
Definition: typcache.c:151
#define heap_close(r, l)
Definition: heapam.h:97
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:885
FormData_pg_type * Form_pg_type
Definition: pg_type.h:233
Form_pg_class rd_rel
Definition: rel.h:113
unsigned int Oid
Definition: postgres_ext.h:31
#define RECORD_EQ_OP
Definition: pg_operator.h:1699
#define EnumTypIdLabelIndexId
Definition: indexing.h:157
int16 typlen
Definition: typcache.h:35
#define OidIsValid(objectId)
Definition: c.h:534
bool typbyval
Definition: typcache.h:36
#define HASH_AM_OID
Definition: pg_am.h:73
#define Anum_pg_constraint_conbin
#define ConstraintTypidIndexId
Definition: indexing.h:128
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:322
int natts
Definition: tupdesc.h:73
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:149
int32 tdtypmod
Definition: tupdesc.h:78
signed int int32
Definition: c.h:253
void assign_record_type_typmod(TupleDesc tupDesc)
Definition: typcache.c:1296
static TupleDesc * RecordCacheArray
Definition: typcache.c:153
void CacheRegisterRelcacheCallback(RelcacheCallbackFunction func, Datum arg)
Definition: inval.c:1405
bool DomainHasConstraints(Oid type_id)
Definition: typcache.c:1047
FmgrInfo rng_subdiff_finfo
Definition: typcache.h:88
#define CONSTRAINT_CHECK
FmgrInfo cmp_proc_finfo
Definition: typcache.h:68
static void cache_record_field_properties(TypeCacheEntry *typentry)
Definition: typcache.c:1137
struct TypeCacheEntry * nextDomain
Definition: typcache.h:106
Definition: dynahash.c:193
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execQual.c:4266
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:410
#define TCFLAGS_CHECKED_ELEM_PROPERTIES
Definition: typcache.c:85
#define REC_HASH_KEYS
Definition: typcache.c:140
Bitmapset * sorted_values
Definition: typcache.c:123
void pfree(void *pointer)
Definition: mcxt.c:992
#define TCFLAGS_CHECKED_GT_OPR
Definition: typcache.c:82
#define ObjectIdGetDatum(X)
Definition: postgres.h:515
#define ERROR
Definition: elog.h:43
#define EnumRelationId
Definition: pg_enum.h:32
#define TCFLAGS_HAVE_FIELD_COMPARE
Definition: typcache.c:91
static bool enum_known_sorted(TypeCacheEnumData *enumdata, Oid arg)
Definition: typcache.c:1514
#define ARRAY_LT_OP
Definition: pg_operator.h:781
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:178
char * c
char typstorage
Definition: typcache.h:38
static void TypeCacheRelCallback(Datum arg, Oid relid)
Definition: typcache.c:1409
#define RegProcedureIsValid(p)
Definition: c.h:536
static bool array_element_has_compare(TypeCacheEntry *typentry)
Definition: typcache.c:1082
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:163
FormData_pg_enum * Form_pg_enum
Definition: pg_enum.h:46
unsigned int uint32
Definition: c.h:265
FmgrInfo hash_proc_finfo
Definition: typcache.h:69
#define RECORD_LT_OP
Definition: pg_operator.h:1704
#define RECORDOID
Definition: pg_type.h:668
#define TYPECACHE_GT_OPR
Definition: typcache.h:112
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
TupleDesc lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
Definition: typcache.c:1262
struct tupleDesc * TupleDesc
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition: tupdesc.c:167
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:169
#define ereport(elevel, rest)
Definition: elog.h:122
#define TYPECACHE_BTREE_OPFAMILY
Definition: typcache.h:119
static EnumItem * find_enumitem(TypeCacheEnumData *enumdata, Oid arg)
Definition: typcache.c:1771
#define TCFLAGS_HAVE_FIELD_EQUALITY
Definition: typcache.c:90
FmgrInfo rng_canonical_finfo
Definition: typcache.h:87
EnumItem enum_values[FLEXIBLE_ARRAY_MEMBER]
Definition: typcache.c:125
#define Anum_pg_constraint_contypid
MemoryContext refctx
Definition: typcache.h:133
struct TypeCacheEntry * rngelemtype
Definition: typcache.h:84
#define RECORD_GT_OP
Definition: pg_operator.h:1707
List * lappend(List *list, void *datum)
Definition: list.c:128
static void cache_array_element_properties(TypeCacheEntry *typentry)
Definition: typcache.c:1098
#define TYPTYPE_RANGE
Definition: pg_type.h:713
float float4
Definition: c.h:377
#define HASH_BLOBS
Definition: hsearch.h:88
#define TextDatumGetCString(d)
Definition: builtins.h:91
static int32 RecordCacheArrayLen
Definition: typcache.c:154
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:440
void CacheRegisterSyscacheCallback(int cacheid, SyscacheCallbackFunction func, Datum arg)
Definition: inval.c:1381
static int32 NextRecordTypmod
Definition: typcache.c:155
Oid enum_oid
Definition: typcache.c:116
HTAB * hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
Definition: dynahash.c:301
uintptr_t Datum
Definition: postgres.h:374
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1083
Oid btree_opintype
Definition: typcache.h:51
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1287
Size keysize
Definition: hsearch.h:72
TupleDesc rd_att
Definition: rel.h:114
FmgrInfo eq_opr_finfo
Definition: typcache.h:67
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:191
#define InvalidOid
Definition: postgres_ext.h:36
static void load_rangetype_info(TypeCacheEntry *typentry)
Definition: typcache.c:599
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1062
Oid fn_oid
Definition: fmgr.h:56
#define TYPECACHE_CMP_PROC
Definition: typcache.h:113
List * lcons(void *datum, List *list)
Definition: list.c:259
char typtype
Definition: typcache.h:39
void bms_free(Bitmapset *a)
Definition: bitmapset.c:200
#define makeNode(_type_)
Definition: nodes.h:556
FormData_pg_constraint * Form_pg_constraint
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define NULL
Definition: c.h:226
#define Assert(condition)
Definition: c.h:671
#define lfirst(lc)
Definition: pg_list.h:106
static List * prep_domain_constraints(List *constraints, MemoryContext execctx)
Definition: typcache.c:933
void DecrTupleDescRefCount(TupleDesc tupdesc)
Definition: tupdesc.c:334
Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
Definition: lsyscache.c:744
#define TCFLAGS_HAVE_ELEM_EQUALITY
Definition: typcache.c:86
static void load_enum_cache_data(TypeCacheEntry *tcache)
Definition: typcache.c:1616
#define ARRAY_GT_OP
Definition: pg_operator.h:784
TypeCacheEntry * tcache
Definition: typcache.h:134
void CreateCacheMemoryContext(void)
Definition: catcache.c:525
#define TCFLAGS_CHECKED_BTREE_OPCLASS
Definition: typcache.c:78
#define TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS
Definition: typcache.c:92
Oid rng_collation
Definition: typcache.h:85
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1353
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:668
static bool record_fields_have_compare(TypeCacheEntry *typentry)
Definition: typcache.c:1129
#define ARRAY_EQ_OP
Definition: pg_operator.h:776
Oid get_opclass_family(Oid opclass)
Definition: lsyscache.c:1015
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1021
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1343
#define TCFLAGS_HAVE_ELEM_HASHING
Definition: typcache.c:88
static bool array_element_has_equality(TypeCacheEntry *typentry)
Definition: typcache.c:1074
void FreeTupleDesc(TupleDesc tupdesc)
Definition: tupdesc.c:266
static void load_domaintype_info(TypeCacheEntry *typentry)
Definition: typcache.c:662
Oid get_base_element_type(Oid typid)
Definition: lsyscache.c:2525
#define Anum_pg_enum_enumtypid
Definition: pg_enum.h:53
float4 sort_order
Definition: typcache.c:117
#define TCFLAGS_CHECKED_HASH_PROC
Definition: typcache.c:84
char typalign
Definition: typcache.h:37
void * palloc(Size size)
Definition: mcxt.c:891
int errmsg(const char *fmt,...)
Definition: elog.c:797
static int enum_oid_cmp(const void *left, const void *right)
Definition: typcache.c:1788
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:749
int tdrefcount
Definition: tupdesc.h:80
#define HASHPROC
Definition: hash.h:269
int i
#define TYPECACHE_LT_OPR
Definition: typcache.h:111
#define TCFLAGS_CHECKED_LT_OPR
Definition: typcache.c:81
#define NameStr(name)
Definition: c.h:495
#define TYPTYPE_ENUM
Definition: pg_type.h:711
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
bool equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
Definition: tupdesc.c:352
void * arg
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1117
void MemoryContextRegisterResetCallback(MemoryContext context, MemoryContextCallback *cb)
Definition: mcxt.c:265
#define TYPECACHE_CMP_PROC_FINFO
Definition: typcache.h:116
#define ConstraintRelationId
Definition: pg_constraint.h:29
#define TCFLAGS_CHECKED_HASH_OPCLASS
Definition: typcache.c:79
#define elog
Definition: elog.h:219
#define TYPECACHE_HASH_OPFAMILY
Definition: typcache.h:120
#define TYPECACHE_DOMAIN_INFO
Definition: typcache.h:122
#define HeapTupleGetOid(tuple)
Definition: htup_details.h:695
#define qsort(a, b, c, d)
Definition: port.h:440
static bool record_fields_have_equality(TypeCacheEntry *typentry)
Definition: typcache.c:1121
#define TCFLAGS_CHECKED_CMP_PROC
Definition: typcache.c:83
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:222
#define BTLessStrategyNumber
Definition: stratnum.h:29
static void decr_dcc_refcount(DomainConstraintCache *dcc)
Definition: typcache.c:901
Definition: pg_list.h:45
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:419
TupleDesc tupDesc
Definition: typcache.h:76
static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue)
Definition: typcache.c:1490
static TupleDesc lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
Definition: typcache.c:1198
static HTAB * TypeCacheHash
Definition: typcache.c:72
long val
Definition: informix.c:689
#define TYPECACHE_HASH_PROC
Definition: typcache.h:114
#define TYPECACHE_TUPDESC
Definition: typcache.h:118
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define offsetof(type, field)
Definition: c.h:551
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue)
Definition: typcache.c:1461
MemoryContext CacheMemoryContext
Definition: mcxt.c:46
TupleDesc lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod)
Definition: typcache.c:1279
Oid hashkey[REC_HASH_KEYS]
Definition: typcache.c:145
Oid get_opclass_input_type(Oid opclass)
Definition: lsyscache.c:1037