PostgreSQL Source Code git master
Loading...
Searching...
No Matches
typcache.c File Reference
#include "postgres.h"
#include <limits.h>
#include "access/hash.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
#include "access/parallel.h"
#include "access/relation.h"
#include "access/session.h"
#include "access/table.h"
#include "catalog/pg_am.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_enum.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_range.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "common/int.h"
#include "executor/executor.h"
#include "lib/dshash.h"
#include "optimizer/optimizer.h"
#include "port/pg_bitutils.h"
#include "storage/lwlock.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/fmgroids.h"
#include "utils/injection_point.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
Include dependency graph for typcache.c:

Go to the source code of this file.

Data Structures

struct  RelIdToTypeIdCacheEntry
 
struct  DomainConstraintCache
 
struct  EnumItem
 
struct  TypeCacheEnumData
 
struct  RecordCacheEntry
 
struct  SharedRecordTypmodRegistry
 
struct  SharedRecordTableKey
 
struct  SharedRecordTableEntry
 
struct  SharedTypmodTableEntry
 
struct  RecordCacheArrayEntry
 

Macros

#define TCFLAGS_HAVE_PG_TYPE_DATA   0x000001
 
#define TCFLAGS_CHECKED_BTREE_OPCLASS   0x000002
 
#define TCFLAGS_CHECKED_HASH_OPCLASS   0x000004
 
#define TCFLAGS_CHECKED_EQ_OPR   0x000008
 
#define TCFLAGS_CHECKED_LT_OPR   0x000010
 
#define TCFLAGS_CHECKED_GT_OPR   0x000020
 
#define TCFLAGS_CHECKED_CMP_PROC   0x000040
 
#define TCFLAGS_CHECKED_HASH_PROC   0x000080
 
#define TCFLAGS_CHECKED_HASH_EXTENDED_PROC   0x000100
 
#define TCFLAGS_CHECKED_ELEM_PROPERTIES   0x000200
 
#define TCFLAGS_HAVE_ELEM_EQUALITY   0x000400
 
#define TCFLAGS_HAVE_ELEM_COMPARE   0x000800
 
#define TCFLAGS_HAVE_ELEM_HASHING   0x001000
 
#define TCFLAGS_HAVE_ELEM_EXTENDED_HASHING   0x002000
 
#define TCFLAGS_CHECKED_FIELD_PROPERTIES   0x004000
 
#define TCFLAGS_HAVE_FIELD_EQUALITY   0x008000
 
#define TCFLAGS_HAVE_FIELD_COMPARE   0x010000
 
#define TCFLAGS_HAVE_FIELD_HASHING   0x020000
 
#define TCFLAGS_HAVE_FIELD_EXTENDED_HASHING   0x040000
 
#define TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS   0x080000
 
#define TCFLAGS_DOMAIN_BASE_IS_COMPOSITE   0x100000
 
#define TCFLAGS_OPERATOR_FLAGS
 

Typedefs

typedef struct RelIdToTypeIdCacheEntry RelIdToTypeIdCacheEntry
 
typedef struct TypeCacheEnumData TypeCacheEnumData
 
typedef struct RecordCacheEntry RecordCacheEntry
 
typedef struct SharedRecordTableKey SharedRecordTableKey
 
typedef struct SharedRecordTableEntry SharedRecordTableEntry
 
typedef struct SharedTypmodTableEntry SharedTypmodTableEntry
 
typedef struct RecordCacheArrayEntry RecordCacheArrayEntry
 

Functions

static int shared_record_table_compare (const void *a, const void *b, size_t size, void *arg)
 
static uint32 shared_record_table_hash (const void *a, size_t size, void *arg)
 
static void load_typcache_tupdesc (TypeCacheEntry *typentry)
 
static void load_rangetype_info (TypeCacheEntry *typentry)
 
static void load_multirangetype_info (TypeCacheEntry *typentry)
 
static void load_domaintype_info (TypeCacheEntry *typentry)
 
static int dcs_cmp (const void *a, const void *b)
 
static void decr_dcc_refcount (DomainConstraintCache *dcc)
 
static void dccref_deletion_callback (void *arg)
 
static Listprep_domain_constraints (List *constraints, MemoryContext execctx)
 
static bool array_element_has_equality (TypeCacheEntry *typentry)
 
static bool array_element_has_compare (TypeCacheEntry *typentry)
 
static bool array_element_has_hashing (TypeCacheEntry *typentry)
 
static bool array_element_has_extended_hashing (TypeCacheEntry *typentry)
 
static void cache_array_element_properties (TypeCacheEntry *typentry)
 
static bool record_fields_have_equality (TypeCacheEntry *typentry)
 
static bool record_fields_have_compare (TypeCacheEntry *typentry)
 
static bool record_fields_have_hashing (TypeCacheEntry *typentry)
 
static bool record_fields_have_extended_hashing (TypeCacheEntry *typentry)
 
static void cache_record_field_properties (TypeCacheEntry *typentry)
 
static bool range_element_has_hashing (TypeCacheEntry *typentry)
 
static bool range_element_has_extended_hashing (TypeCacheEntry *typentry)
 
static void cache_range_element_properties (TypeCacheEntry *typentry)
 
static bool multirange_element_has_hashing (TypeCacheEntry *typentry)
 
static bool multirange_element_has_extended_hashing (TypeCacheEntry *typentry)
 
static void cache_multirange_element_properties (TypeCacheEntry *typentry)
 
static void TypeCacheRelCallback (Datum arg, Oid relid)
 
static void TypeCacheTypCallback (Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
 
static void TypeCacheOpcCallback (Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
 
static void TypeCacheConstrCallback (Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
 
static void load_enum_cache_data (TypeCacheEntry *tcache)
 
static EnumItemfind_enumitem (TypeCacheEnumData *enumdata, Oid arg)
 
static int enum_oid_cmp (const void *left, const void *right)
 
static void shared_record_typmod_registry_detach (dsm_segment *segment, Datum datum)
 
static TupleDesc find_or_make_matching_shared_tupledesc (TupleDesc tupdesc)
 
static dsa_pointer share_tupledesc (dsa_area *area, TupleDesc tupdesc, uint32 typmod)
 
static void insert_rel_type_cache_if_needed (TypeCacheEntry *typentry)
 
static void delete_rel_type_cache_if_needed (TypeCacheEntry *typentry)
 
static uint32 type_cache_syshash (const void *key, Size keysize)
 
TypeCacheEntrylookup_type_cache (Oid type_id, int flags)
 
void InitDomainConstraintRef (Oid type_id, DomainConstraintRef *ref, MemoryContext refctx, bool need_exprstate)
 
void UpdateDomainConstraintRef (DomainConstraintRef *ref)
 
bool DomainHasConstraints (Oid type_id, bool *has_volatile)
 
static void ensure_record_cache_typmod_slot_exists (int32 typmod)
 
static TupleDesc lookup_rowtype_tupdesc_internal (Oid type_id, int32 typmod, bool noError)
 
TupleDesc lookup_rowtype_tupdesc (Oid type_id, int32 typmod)
 
TupleDesc lookup_rowtype_tupdesc_noerror (Oid type_id, int32 typmod, bool noError)
 
TupleDesc lookup_rowtype_tupdesc_copy (Oid type_id, int32 typmod)
 
TupleDesc lookup_rowtype_tupdesc_domain (Oid type_id, int32 typmod, bool noError)
 
static uint32 record_type_typmod_hash (const void *data, size_t size)
 
static int record_type_typmod_compare (const void *a, const void *b, size_t size)
 
void assign_record_type_typmod (TupleDesc tupDesc)
 
uint64 assign_record_type_identifier (Oid type_id, int32 typmod)
 
size_t SharedRecordTypmodRegistryEstimate (void)
 
void SharedRecordTypmodRegistryInit (SharedRecordTypmodRegistry *registry, dsm_segment *segment, dsa_area *area)
 
void SharedRecordTypmodRegistryAttach (SharedRecordTypmodRegistry *registry)
 
static void InvalidateCompositeTypeCacheEntry (TypeCacheEntry *typentry)
 
static bool enum_known_sorted (TypeCacheEnumData *enumdata, Oid arg)
 
int compare_values_of_enum (TypeCacheEntry *tcache, Oid arg1, Oid arg2)
 
static void finalize_in_progress_typentries (void)
 
void AtEOXact_TypeCache (void)
 
void AtEOSubXact_TypeCache (void)
 

Variables

static HTABTypeCacheHash = NULL
 
static HTABRelIdToTypeIdCacheHash = NULL
 
static TypeCacheEntryfirstDomainTypeEntry = NULL
 
static Oidin_progress_list
 
static int in_progress_list_len
 
static int in_progress_list_maxlen
 
static const dshash_parameters srtr_record_table_params
 
static const dshash_parameters srtr_typmod_table_params
 
static HTABRecordCacheHash = NULL
 
static RecordCacheArrayEntryRecordCacheArray = NULL
 
static int32 RecordCacheArrayLen = 0
 
static int32 NextRecordTypmod = 0
 
static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER
 

Macro Definition Documentation

◆ TCFLAGS_CHECKED_BTREE_OPCLASS

#define TCFLAGS_CHECKED_BTREE_OPCLASS   0x000002

Definition at line 100 of file typcache.c.

◆ TCFLAGS_CHECKED_CMP_PROC

#define TCFLAGS_CHECKED_CMP_PROC   0x000040

Definition at line 105 of file typcache.c.

◆ TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS

#define TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS   0x080000

Definition at line 118 of file typcache.c.

◆ TCFLAGS_CHECKED_ELEM_PROPERTIES

#define TCFLAGS_CHECKED_ELEM_PROPERTIES   0x000200

Definition at line 108 of file typcache.c.

◆ TCFLAGS_CHECKED_EQ_OPR

#define TCFLAGS_CHECKED_EQ_OPR   0x000008

Definition at line 102 of file typcache.c.

◆ TCFLAGS_CHECKED_FIELD_PROPERTIES

#define TCFLAGS_CHECKED_FIELD_PROPERTIES   0x004000

Definition at line 113 of file typcache.c.

◆ TCFLAGS_CHECKED_GT_OPR

#define TCFLAGS_CHECKED_GT_OPR   0x000020

Definition at line 104 of file typcache.c.

◆ TCFLAGS_CHECKED_HASH_EXTENDED_PROC

#define TCFLAGS_CHECKED_HASH_EXTENDED_PROC   0x000100

Definition at line 107 of file typcache.c.

◆ TCFLAGS_CHECKED_HASH_OPCLASS

#define TCFLAGS_CHECKED_HASH_OPCLASS   0x000004

Definition at line 101 of file typcache.c.

◆ TCFLAGS_CHECKED_HASH_PROC

#define TCFLAGS_CHECKED_HASH_PROC   0x000080

Definition at line 106 of file typcache.c.

◆ TCFLAGS_CHECKED_LT_OPR

#define TCFLAGS_CHECKED_LT_OPR   0x000010

Definition at line 103 of file typcache.c.

◆ TCFLAGS_DOMAIN_BASE_IS_COMPOSITE

#define TCFLAGS_DOMAIN_BASE_IS_COMPOSITE   0x100000

Definition at line 119 of file typcache.c.

◆ TCFLAGS_HAVE_ELEM_COMPARE

#define TCFLAGS_HAVE_ELEM_COMPARE   0x000800

Definition at line 110 of file typcache.c.

◆ TCFLAGS_HAVE_ELEM_EQUALITY

#define TCFLAGS_HAVE_ELEM_EQUALITY   0x000400

Definition at line 109 of file typcache.c.

◆ TCFLAGS_HAVE_ELEM_EXTENDED_HASHING

#define TCFLAGS_HAVE_ELEM_EXTENDED_HASHING   0x002000

Definition at line 112 of file typcache.c.

◆ TCFLAGS_HAVE_ELEM_HASHING

#define TCFLAGS_HAVE_ELEM_HASHING   0x001000

Definition at line 111 of file typcache.c.

◆ TCFLAGS_HAVE_FIELD_COMPARE

#define TCFLAGS_HAVE_FIELD_COMPARE   0x010000

Definition at line 115 of file typcache.c.

◆ TCFLAGS_HAVE_FIELD_EQUALITY

#define TCFLAGS_HAVE_FIELD_EQUALITY   0x008000

Definition at line 114 of file typcache.c.

◆ TCFLAGS_HAVE_FIELD_EXTENDED_HASHING

#define TCFLAGS_HAVE_FIELD_EXTENDED_HASHING   0x040000

Definition at line 117 of file typcache.c.

◆ TCFLAGS_HAVE_FIELD_HASHING

#define TCFLAGS_HAVE_FIELD_HASHING   0x020000

Definition at line 116 of file typcache.c.

◆ TCFLAGS_HAVE_PG_TYPE_DATA

#define TCFLAGS_HAVE_PG_TYPE_DATA   0x000001

Definition at line 99 of file typcache.c.

◆ TCFLAGS_OPERATOR_FLAGS

#define TCFLAGS_OPERATOR_FLAGS
Value:
#define TCFLAGS_DOMAIN_BASE_IS_COMPOSITE
Definition typcache.c:119
#define TCFLAGS_HAVE_PG_TYPE_DATA
Definition typcache.c:99
#define TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS
Definition typcache.c:118

Definition at line 122 of file typcache.c.

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

Typedef Documentation

◆ RecordCacheArrayEntry

◆ RecordCacheEntry

◆ RelIdToTypeIdCacheEntry

◆ SharedRecordTableEntry

◆ SharedRecordTableKey

◆ SharedTypmodTableEntry

◆ TypeCacheEnumData

Function Documentation

◆ array_element_has_compare()

static bool array_element_has_compare ( TypeCacheEntry typentry)
static

Definition at line 1542 of file typcache.c.

1543{
1544 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1546 return (typentry->flags & TCFLAGS_HAVE_ELEM_COMPARE) != 0;
1547}

References cache_array_element_properties(), TypeCacheEntry::flags, TCFLAGS_CHECKED_ELEM_PROPERTIES, and TCFLAGS_HAVE_ELEM_COMPARE.

Referenced by lookup_type_cache().

◆ array_element_has_equality()

static bool array_element_has_equality ( TypeCacheEntry typentry)
static

Definition at line 1534 of file typcache.c.

1535{
1536 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1538 return (typentry->flags & TCFLAGS_HAVE_ELEM_EQUALITY) != 0;
1539}

References cache_array_element_properties(), TypeCacheEntry::flags, TCFLAGS_CHECKED_ELEM_PROPERTIES, and TCFLAGS_HAVE_ELEM_EQUALITY.

Referenced by lookup_type_cache().

◆ array_element_has_extended_hashing()

static bool array_element_has_extended_hashing ( TypeCacheEntry typentry)
static

◆ array_element_has_hashing()

static bool array_element_has_hashing ( TypeCacheEntry typentry)
static

Definition at line 1550 of file typcache.c.

1551{
1552 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1554 return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1555}

References cache_array_element_properties(), TypeCacheEntry::flags, TCFLAGS_CHECKED_ELEM_PROPERTIES, and TCFLAGS_HAVE_ELEM_HASHING.

Referenced by lookup_type_cache().

◆ assign_record_type_identifier()

uint64 assign_record_type_identifier ( Oid  type_id,
int32  typmod 
)

Definition at line 2152 of file typcache.c.

2153{
2154 if (type_id != RECORDOID)
2155 {
2156 /*
2157 * It's a named composite type, so use the regular typcache.
2158 */
2159 TypeCacheEntry *typentry;
2160
2161 typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
2162 if (typentry->tupDesc == NULL)
2163 ereport(ERROR,
2165 errmsg("type %s is not composite",
2166 format_type_be(type_id))));
2167 Assert(typentry->tupDesc_identifier != 0);
2168 return typentry->tupDesc_identifier;
2169 }
2170 else
2171 {
2172 /*
2173 * It's a transient record type, so look in our record-type table.
2174 */
2175 if (typmod >= 0 && typmod < RecordCacheArrayLen &&
2176 RecordCacheArray[typmod].tupdesc != NULL)
2177 {
2178 Assert(RecordCacheArray[typmod].id != 0);
2179 return RecordCacheArray[typmod].id;
2180 }
2181
2182 /* For anonymous or unrecognized record type, generate a new ID */
2183 return ++tupledesc_id_counter;
2184 }
2185}

References Assert, ereport, errcode(), errmsg, ERROR, fb(), format_type_be(), RecordCacheArrayEntry::id, lookup_type_cache(), RecordCacheArray, RecordCacheArrayLen, TypeCacheEntry::tupDesc, TypeCacheEntry::tupDesc_identifier, tupledesc_id_counter, and TYPECACHE_TUPDESC.

Referenced by expanded_record_fetch_tupdesc(), make_expanded_record_from_tupdesc(), and make_expanded_record_from_typeid().

◆ assign_record_type_typmod()

void assign_record_type_typmod ( TupleDesc  tupDesc)

Definition at line 2060 of file typcache.c.

2061{
2064 bool found;
2066
2067 Assert(tupDesc->tdtypeid == RECORDOID);
2068
2069 if (RecordCacheHash == NULL)
2070 {
2071 /* First time through: initialize the hash table */
2072 HASHCTL ctl;
2073
2074 ctl.keysize = sizeof(TupleDesc); /* just the pointer */
2075 ctl.entrysize = sizeof(RecordCacheEntry);
2078 RecordCacheHash = hash_create("Record information cache", 64,
2079 &ctl,
2081
2082 /* Also make sure CacheMemoryContext exists */
2083 if (!CacheMemoryContext)
2085 }
2086
2087 /*
2088 * Find a hashtable entry for this tuple descriptor. We don't use
2089 * HASH_ENTER yet, because if it's missing, we need to make sure that all
2090 * the allocations succeed before we create the new entry.
2091 */
2093 &tupDesc,
2094 HASH_FIND, &found);
2095 if (found && recentry->tupdesc != NULL)
2096 {
2097 tupDesc->tdtypmod = recentry->tupdesc->tdtypmod;
2098 return;
2099 }
2100
2101 /* Not present, so need to manufacture an entry */
2103
2104 /* Look in the SharedRecordTypmodRegistry, if attached */
2106 if (entDesc == NULL)
2107 {
2108 /*
2109 * Make sure we have room before we CreateTupleDescCopy() or advance
2110 * NextRecordTypmod.
2111 */
2113
2114 /* Reference-counted local cache only. */
2115 entDesc = CreateTupleDescCopy(tupDesc);
2116 entDesc->tdrefcount = 1;
2117 entDesc->tdtypmod = NextRecordTypmod++;
2118 }
2119 else
2120 {
2122 }
2123
2125
2126 /* Assign a unique tupdesc identifier, too. */
2128
2129 /* Fully initialized; create the hash table entry */
2131 &tupDesc,
2132 HASH_ENTER, NULL);
2133 recentry->tupdesc = entDesc;
2134
2135 /* Update the caller's tuple descriptor. */
2136 tupDesc->tdtypmod = entDesc->tdtypmod;
2137
2139}

References Assert, CacheMemoryContext, CreateCacheMemoryContext(), CreateTupleDescCopy(), ctl, ensure_record_cache_typmod_slot_exists(), fb(), find_or_make_matching_shared_tupledesc(), HASH_COMPARE, hash_create(), HASH_ELEM, HASH_ENTER, HASH_FIND, HASH_FUNCTION, hash_search(), RecordCacheArrayEntry::id, HASHCTL::keysize, MemoryContextSwitchTo(), NextRecordTypmod, record_type_typmod_compare(), record_type_typmod_hash(), RecordCacheArray, RecordCacheHash, TupleDescData::tdtypeid, TupleDescData::tdtypmod, RecordCacheArrayEntry::tupdesc, and tupledesc_id_counter.

Referenced by BlessTupleDesc(), ER_get_flat_size(), internal_get_result_type(), and SPI_returntuple().

◆ AtEOSubXact_TypeCache()

void AtEOSubXact_TypeCache ( void  )

Definition at line 3216 of file typcache.c.

3217{
3219}

References finalize_in_progress_typentries().

Referenced by AbortSubTransaction(), and CommitSubTransaction().

◆ AtEOXact_TypeCache()

void AtEOXact_TypeCache ( void  )

◆ cache_array_element_properties()

◆ cache_multirange_element_properties()

static void cache_multirange_element_properties ( TypeCacheEntry typentry)
static

Definition at line 1789 of file typcache.c.

1790{
1791 /* load up range link if we didn't already */
1792 if (typentry->rngtype == NULL &&
1793 typentry->typtype == TYPTYPE_MULTIRANGE)
1794 load_multirangetype_info(typentry);
1795
1796 if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
1797 {
1799
1800 /* might need to calculate subtype's hash function properties */
1804 if (OidIsValid(elementry->hash_proc))
1805 typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1806 if (OidIsValid(elementry->hash_extended_proc))
1808 }
1810}

References fb(), TypeCacheEntry::flags, load_multirangetype_info(), lookup_type_cache(), OidIsValid, TypeCacheEntry::rngelemtype, TypeCacheEntry::rngtype, TCFLAGS_CHECKED_ELEM_PROPERTIES, TCFLAGS_HAVE_ELEM_EXTENDED_HASHING, TCFLAGS_HAVE_ELEM_HASHING, TypeCacheEntry::type_id, TYPECACHE_HASH_EXTENDED_PROC, TYPECACHE_HASH_PROC, and TypeCacheEntry::typtype.

Referenced by multirange_element_has_extended_hashing(), and multirange_element_has_hashing().

◆ cache_range_element_properties()

static void cache_range_element_properties ( TypeCacheEntry typentry)
static

Definition at line 1749 of file typcache.c.

1750{
1751 /* load up subtype link if we didn't already */
1752 if (typentry->rngelemtype == NULL &&
1753 typentry->typtype == TYPTYPE_RANGE)
1754 load_rangetype_info(typentry);
1755
1756 if (typentry->rngelemtype != NULL)
1757 {
1759
1760 /* might need to calculate subtype's hash function properties */
1764 if (OidIsValid(elementry->hash_proc))
1765 typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1766 if (OidIsValid(elementry->hash_extended_proc))
1768 }
1770}

References fb(), TypeCacheEntry::flags, load_rangetype_info(), lookup_type_cache(), OidIsValid, TypeCacheEntry::rngelemtype, TCFLAGS_CHECKED_ELEM_PROPERTIES, TCFLAGS_HAVE_ELEM_EXTENDED_HASHING, TCFLAGS_HAVE_ELEM_HASHING, TypeCacheEntry::type_id, TYPECACHE_HASH_EXTENDED_PROC, TYPECACHE_HASH_PROC, and TypeCacheEntry::typtype.

Referenced by range_element_has_extended_hashing(), and range_element_has_hashing().

◆ cache_record_field_properties()

static void cache_record_field_properties ( TypeCacheEntry typentry)
static

Definition at line 1628 of file typcache.c.

1629{
1630 /*
1631 * For type RECORD, we can't really tell what will work, since we don't
1632 * have access here to the specific anonymous type. Just assume that
1633 * equality and comparison will (we may get a failure at runtime). We
1634 * could also claim that hashing works, but then if code that has the
1635 * option between a comparison-based (sort-based) and a hash-based plan
1636 * chooses hashing, stuff could fail that would otherwise work if it chose
1637 * a comparison-based plan. In practice more types support comparison
1638 * than hashing.
1639 */
1640 if (typentry->type_id == RECORDOID)
1641 {
1642 typentry->flags |= (TCFLAGS_HAVE_FIELD_EQUALITY |
1644 }
1645 else if (typentry->typtype == TYPTYPE_COMPOSITE)
1646 {
1647 TupleDesc tupdesc;
1648 int newflags;
1649 int i;
1650
1651 /* Fetch composite type's tupdesc if we don't have it already */
1652 if (typentry->tupDesc == NULL)
1653 load_typcache_tupdesc(typentry);
1654 tupdesc = typentry->tupDesc;
1655
1656 /* Must bump the refcount while we do additional catalog lookups */
1657 IncrTupleDescRefCount(tupdesc);
1658
1659 /* Have each property if all non-dropped fields have the property */
1664 for (i = 0; i < tupdesc->natts; i++)
1665 {
1667 Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
1668
1669 if (attr->attisdropped)
1670 continue;
1671
1672 fieldentry = lookup_type_cache(attr->atttypid,
1677 if (!OidIsValid(fieldentry->eq_opr))
1679 if (!OidIsValid(fieldentry->cmp_proc))
1681 if (!OidIsValid(fieldentry->hash_proc))
1683 if (!OidIsValid(fieldentry->hash_extended_proc))
1685
1686 /* We can drop out of the loop once we disprove all bits */
1687 if (newflags == 0)
1688 break;
1689 }
1690 typentry->flags |= newflags;
1691
1692 DecrTupleDescRefCount(tupdesc);
1693 }
1694 else if (typentry->typtype == TYPTYPE_DOMAIN)
1695 {
1696 /* If it's domain over composite, copy base type's properties */
1698
1699 /* load up basetype info if we didn't already */
1700 if (typentry->domainBaseType == InvalidOid)
1701 {
1702 typentry->domainBaseTypmod = -1;
1703 typentry->domainBaseType =
1704 getBaseTypeAndTypmod(typentry->type_id,
1705 &typentry->domainBaseTypmod);
1706 }
1712 if (baseentry->typtype == TYPTYPE_COMPOSITE)
1713 {
1715 typentry->flags |= baseentry->flags & (TCFLAGS_HAVE_FIELD_EQUALITY |
1719 }
1720 }
1722}

References DecrTupleDescRefCount(), TypeCacheEntry::domainBaseType, TypeCacheEntry::domainBaseTypmod, fb(), TypeCacheEntry::flags, getBaseTypeAndTypmod(), i, IncrTupleDescRefCount(), InvalidOid, load_typcache_tupdesc(), lookup_type_cache(), TupleDescData::natts, OidIsValid, TCFLAGS_CHECKED_FIELD_PROPERTIES, TCFLAGS_DOMAIN_BASE_IS_COMPOSITE, TCFLAGS_HAVE_FIELD_COMPARE, TCFLAGS_HAVE_FIELD_EQUALITY, TCFLAGS_HAVE_FIELD_EXTENDED_HASHING, TCFLAGS_HAVE_FIELD_HASHING, TypeCacheEntry::tupDesc, TupleDescAttr(), TypeCacheEntry::type_id, TYPECACHE_CMP_PROC, TYPECACHE_EQ_OPR, TYPECACHE_HASH_EXTENDED_PROC, TYPECACHE_HASH_PROC, and TypeCacheEntry::typtype.

Referenced by record_fields_have_compare(), record_fields_have_equality(), record_fields_have_extended_hashing(), and record_fields_have_hashing().

◆ compare_values_of_enum()

int compare_values_of_enum ( TypeCacheEntry tcache,
Oid  arg1,
Oid  arg2 
)

Definition at line 2682 of file typcache.c.

2683{
2685 EnumItem *item1;
2686 EnumItem *item2;
2687
2688 /*
2689 * Equal OIDs are certainly equal --- this case was probably handled by
2690 * our caller, but we may as well check.
2691 */
2692 if (arg1 == arg2)
2693 return 0;
2694
2695 /* Load up the cache if first time through */
2696 if (tcache->enumData == NULL)
2697 load_enum_cache_data(tcache);
2698 enumdata = tcache->enumData;
2699
2700 /*
2701 * If both OIDs are known-sorted, we can just compare them directly.
2702 */
2705 {
2706 if (arg1 < arg2)
2707 return -1;
2708 else
2709 return 1;
2710 }
2711
2712 /*
2713 * Slow path: we have to identify their actual sort-order positions.
2714 */
2717
2718 if (item1 == NULL || item2 == NULL)
2719 {
2720 /*
2721 * We couldn't find one or both values. That means the enum has
2722 * changed under us, so re-initialize the cache and try again. We
2723 * don't bother retrying the known-sorted case in this path.
2724 */
2725 load_enum_cache_data(tcache);
2726 enumdata = tcache->enumData;
2727
2730
2731 /*
2732 * If we still can't find the values, complain: we must have corrupt
2733 * data.
2734 */
2735 if (item1 == NULL)
2736 elog(ERROR, "enum value %u not found in cache for enum %s",
2737 arg1, format_type_be(tcache->type_id));
2738 if (item2 == NULL)
2739 elog(ERROR, "enum value %u not found in cache for enum %s",
2740 arg2, format_type_be(tcache->type_id));
2741 }
2742
2743 if (item1->sort_order < item2->sort_order)
2744 return -1;
2745 else if (item1->sort_order > item2->sort_order)
2746 return 1;
2747 else
2748 return 0;
2749}

References elog, enum_known_sorted(), TypeCacheEntry::enumData, ERROR, fb(), find_enumitem(), format_type_be(), load_enum_cache_data(), and TypeCacheEntry::type_id.

Referenced by enum_cmp_internal().

◆ dccref_deletion_callback()

static void dccref_deletion_callback ( void arg)
static

Definition at line 1338 of file typcache.c.

1339{
1341 DomainConstraintCache *dcc = ref->dcc;
1342
1343 /* Paranoia --- be sure link is nulled before trying to release */
1344 if (dcc)
1345 {
1346 ref->constraints = NIL;
1347 ref->dcc = NULL;
1348 decr_dcc_refcount(dcc);
1349 }
1350}

References arg, DomainConstraintCache::constraints, decr_dcc_refcount(), fb(), and NIL.

Referenced by InitDomainConstraintRef().

◆ dcs_cmp()

static int dcs_cmp ( const void a,
const void b 
)
static

Definition at line 1314 of file typcache.c.

1315{
1316 const DomainConstraintState *const *ca = (const DomainConstraintState *const *) a;
1317 const DomainConstraintState *const *cb = (const DomainConstraintState *const *) b;
1318
1319 return strcmp((*ca)->name, (*cb)->name);
1320}

References a, b, and fb().

Referenced by load_domaintype_info().

◆ decr_dcc_refcount()

static void decr_dcc_refcount ( DomainConstraintCache dcc)
static

◆ delete_rel_type_cache_if_needed()

static void delete_rel_type_cache_if_needed ( TypeCacheEntry typentry)
static

Definition at line 3127 of file typcache.c.

3128{
3129#ifdef USE_ASSERT_CHECKING
3130 int i;
3131 bool is_in_progress = false;
3132
3133 for (i = 0; i < in_progress_list_len; i++)
3134 {
3135 if (in_progress_list[i] == typentry->type_id)
3136 {
3137 is_in_progress = true;
3138 break;
3139 }
3140 }
3141#endif
3142
3143 /* Immediately quit for non-composite types */
3144 if (typentry->typtype != TYPTYPE_COMPOSITE)
3145 return;
3146
3147 /* typrelid should be given for composite types */
3148 Assert(OidIsValid(typentry->typrelid));
3149
3150 /*
3151 * Delete a RelIdToTypeIdCacheHash entry if the typentry doesn't have any
3152 * information indicating entry should be still there.
3153 */
3154 if (!(typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA) &&
3155 !(typentry->flags & TCFLAGS_OPERATOR_FLAGS) &&
3156 typentry->tupDesc == NULL)
3157 {
3158 bool found;
3159
3161 &typentry->typrelid,
3162 HASH_REMOVE, &found);
3163 Assert(found || is_in_progress);
3164 }
3165 else
3166 {
3167#ifdef USE_ASSERT_CHECKING
3168 /*
3169 * In assert-enabled builds otherwise check for RelIdToTypeIdCacheHash
3170 * entry if it should exist.
3171 */
3172 bool found;
3173
3174 if (!is_in_progress)
3175 {
3177 &typentry->typrelid,
3178 HASH_FIND, &found);
3179 Assert(found);
3180 }
3181#endif
3182 }
3183}

References Assert, fb(), TypeCacheEntry::flags, HASH_FIND, HASH_REMOVE, hash_search(), i, in_progress_list, in_progress_list_len, OidIsValid, RelIdToTypeIdCacheHash, TCFLAGS_HAVE_PG_TYPE_DATA, TCFLAGS_OPERATOR_FLAGS, TypeCacheEntry::tupDesc, TypeCacheEntry::type_id, TypeCacheEntry::typrelid, and TypeCacheEntry::typtype.

Referenced by InvalidateCompositeTypeCacheEntry(), TypeCacheOpcCallback(), and TypeCacheTypCallback().

◆ DomainHasConstraints()

bool DomainHasConstraints ( Oid  type_id,
bool has_volatile 
)

Definition at line 1488 of file typcache.c.

1489{
1490 TypeCacheEntry *typentry;
1491
1492 /*
1493 * Note: a side effect is to cause the typcache's domain data to become
1494 * valid. This is fine since we'll likely need it soon if there is any.
1495 */
1497
1498 if (typentry->domainData == NULL)
1499 return false;
1500
1501 if (has_volatile)
1502 {
1503 *has_volatile = false;
1504
1506 typentry->domainData->constraints)
1507 {
1508 if (constrstate->constrainttype == DOM_CONSTRAINT_CHECK &&
1510 {
1511 *has_volatile = true;
1512 break;
1513 }
1514 }
1515 }
1516
1517 return true;
1518}

References DomainConstraintCache::constraints, contain_volatile_functions(), DOM_CONSTRAINT_CHECK, TypeCacheEntry::domainData, fb(), foreach_node, lookup_type_cache(), and TYPECACHE_DOMAIN_CONSTR_INFO.

Referenced by ATColumnChangeRequiresRewrite(), ATExecAddColumn(), BeginCopyFrom(), eval_const_expressions_mutator(), ExecInitJsonCoercion(), and transformJsonFuncExpr().

◆ ensure_record_cache_typmod_slot_exists()

static void ensure_record_cache_typmod_slot_exists ( int32  typmod)
static

◆ enum_known_sorted()

static bool enum_known_sorted ( TypeCacheEnumData enumdata,
Oid  arg 
)
inlinestatic

Definition at line 2653 of file typcache.c.

2654{
2655 Oid offset;
2656
2657 if (arg < enumdata->bitmap_base)
2658 return false;
2659 offset = arg - enumdata->bitmap_base;
2660 if (offset > (Oid) INT_MAX)
2661 return false;
2662 return bms_is_member((int) offset, enumdata->sorted_values);
2663}

References arg, bms_is_member(), and fb().

Referenced by compare_values_of_enum().

◆ enum_oid_cmp()

static int enum_oid_cmp ( const void left,
const void right 
)
static

Definition at line 2927 of file typcache.c.

2928{
2929 const EnumItem *l = (const EnumItem *) left;
2930 const EnumItem *r = (const EnumItem *) right;
2931
2932 return pg_cmp_u32(l->enum_oid, r->enum_oid);
2933}

References EnumItem::enum_oid, and pg_cmp_u32().

Referenced by find_enumitem(), and load_enum_cache_data().

◆ finalize_in_progress_typentries()

static void finalize_in_progress_typentries ( void  )
static

Definition at line 3191 of file typcache.c.

3192{
3193 int i;
3194
3195 for (i = 0; i < in_progress_list_len; i++)
3196 {
3197 TypeCacheEntry *typentry;
3198
3201 HASH_FIND, NULL);
3202 if (typentry)
3204 }
3205
3207}

References fb(), HASH_FIND, hash_search(), i, in_progress_list, in_progress_list_len, insert_rel_type_cache_if_needed(), and TypeCacheHash.

Referenced by AtEOSubXact_TypeCache(), and AtEOXact_TypeCache().

◆ find_enumitem()

static EnumItem * find_enumitem ( TypeCacheEnumData enumdata,
Oid  arg 
)
static

Definition at line 2910 of file typcache.c.

2911{
2912 EnumItem srch;
2913
2914 /* On some versions of Solaris, bsearch of zero items dumps core */
2915 if (enumdata->num_values <= 0)
2916 return NULL;
2917
2918 srch.enum_oid = arg;
2919 return bsearch(&srch, enumdata->enum_values, enumdata->num_values,
2920 sizeof(EnumItem), enum_oid_cmp);
2921}

References arg, EnumItem::enum_oid, enum_oid_cmp(), and fb().

Referenced by compare_values_of_enum().

◆ find_or_make_matching_shared_tupledesc()

static TupleDesc find_or_make_matching_shared_tupledesc ( TupleDesc  tupdesc)
static

Definition at line 2961 of file typcache.c.

2962{
2968 bool found;
2969 uint32 typmod;
2970
2971 /* If not even attached, nothing to do. */
2973 return NULL;
2974
2975 /* Try to find a matching tuple descriptor in the record table. */
2976 key.shared = false;
2977 key.u.local_tupdesc = tupdesc;
2981 {
2982 Assert(record_table_entry->key.shared);
2985 result = (TupleDesc)
2987 record_table_entry->key.u.shared_tupdesc);
2988 Assert(result->tdrefcount == -1);
2989
2990 return result;
2991 }
2992
2993 /* Allocate a new typmod number. This will be wasted if we error out. */
2994 typmod = (int)
2996 1);
2997
2998 /* Copy the TupleDesc into shared memory. */
2999 shared_dp = share_tupledesc(CurrentSession->area, tupdesc, typmod);
3000
3001 /*
3002 * Create an entry in the typmod table so that others will understand this
3003 * typmod number.
3004 */
3005 PG_TRY();
3006 {
3009 &typmod, &found);
3010 if (found)
3011 elog(ERROR, "cannot create duplicate shared record typmod");
3012 }
3013 PG_CATCH();
3014 {
3016 PG_RE_THROW();
3017 }
3018 PG_END_TRY();
3019 typmod_table_entry->typmod = typmod;
3020 typmod_table_entry->shared_tupdesc = shared_dp;
3023
3024 /*
3025 * Finally create an entry in the record table so others with matching
3026 * tuple descriptors can reuse the typmod.
3027 */
3030 &found);
3031 if (found)
3032 {
3033 /*
3034 * Someone concurrently inserted a matching tuple descriptor since the
3035 * first time we checked. Use that one instead.
3036 */
3039
3040 /* Might as well free up the space used by the one we created. */
3042 &typmod);
3043 Assert(found);
3045
3046 /* Return the one we found. */
3047 Assert(record_table_entry->key.shared);
3048 result = (TupleDesc)
3050 record_table_entry->key.u.shared_tupdesc);
3051 Assert(result->tdrefcount == -1);
3052
3053 return result;
3054 }
3055
3056 /* Store it and return it. */
3057 record_table_entry->key.shared = true;
3058 record_table_entry->key.u.shared_tupdesc = shared_dp;
3061 result = (TupleDesc)
3063 Assert(result->tdrefcount == -1);
3064
3065 return result;
3066}

References Session::area, Assert, CurrentSession, dsa_free(), dsa_get_address(), dshash_delete_key(), dshash_find(), dshash_find_or_insert, dshash_release_lock(), elog, ERROR, fb(), SharedRecordTypmodRegistry::next_typmod, pg_atomic_fetch_add_u32(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, result, share_tupledesc(), Session::shared_record_table, Session::shared_typmod_registry, and Session::shared_typmod_table.

Referenced by assign_record_type_typmod().

◆ InitDomainConstraintRef()

void InitDomainConstraintRef ( Oid  type_id,
DomainConstraintRef ref,
MemoryContext  refctx,
bool  need_exprstate 
)

Definition at line 1397 of file typcache.c.

1399{
1400 /* Look up the typcache entry --- we assume it survives indefinitely */
1402 ref->need_exprstate = need_exprstate;
1403 /* For safety, establish the callback before acquiring a refcount */
1404 ref->refctx = refctx;
1405 ref->dcc = NULL;
1406 ref->callback.func = dccref_deletion_callback;
1407 ref->callback.arg = ref;
1408 MemoryContextRegisterResetCallback(refctx, &ref->callback);
1409 /* Acquire refcount if there are constraints, and set up exported list */
1410 if (ref->tcache->domainData)
1411 {
1412 ref->dcc = ref->tcache->domainData;
1413 ref->dcc->dccRefCount++;
1414 if (ref->need_exprstate)
1415 ref->constraints = prep_domain_constraints(ref->dcc->constraints,
1416 ref->refctx);
1417 else
1418 ref->constraints = ref->dcc->constraints;
1419 }
1420 else
1421 ref->constraints = NIL;
1422}

References dccref_deletion_callback(), fb(), lookup_type_cache(), MemoryContextRegisterResetCallback(), NIL, prep_domain_constraints(), and TYPECACHE_DOMAIN_CONSTR_INFO.

Referenced by domain_state_setup(), and ExecInitCoerceToDomain().

◆ insert_rel_type_cache_if_needed()

static void insert_rel_type_cache_if_needed ( TypeCacheEntry typentry)
static

Definition at line 3093 of file typcache.c.

3094{
3095 /* Immediately quit for non-composite types */
3096 if (typentry->typtype != TYPTYPE_COMPOSITE)
3097 return;
3098
3099 /* typrelid should be given for composite types */
3100 Assert(OidIsValid(typentry->typrelid));
3101
3102 /*
3103 * Insert a RelIdToTypeIdCacheHash entry if the typentry have any
3104 * information indicating it should be here.
3105 */
3106 if ((typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA) ||
3107 (typentry->flags & TCFLAGS_OPERATOR_FLAGS) ||
3108 typentry->tupDesc != NULL)
3109 {
3111 bool found;
3112
3114 &typentry->typrelid,
3115 HASH_ENTER, &found);
3116 relentry->relid = typentry->typrelid;
3117 relentry->composite_typid = typentry->type_id;
3118 }
3119}

References Assert, fb(), TypeCacheEntry::flags, HASH_ENTER, hash_search(), OidIsValid, RelIdToTypeIdCacheEntry::relid, RelIdToTypeIdCacheHash, TCFLAGS_HAVE_PG_TYPE_DATA, TCFLAGS_OPERATOR_FLAGS, TypeCacheEntry::tupDesc, TypeCacheEntry::type_id, TypeCacheEntry::typrelid, and TypeCacheEntry::typtype.

Referenced by finalize_in_progress_typentries(), and lookup_type_cache().

◆ InvalidateCompositeTypeCacheEntry()

static void InvalidateCompositeTypeCacheEntry ( TypeCacheEntry typentry)
static

Definition at line 2383 of file typcache.c.

2384{
2386
2387 Assert(typentry->typtype == TYPTYPE_COMPOSITE &&
2388 OidIsValid(typentry->typrelid));
2389
2390 hadTupDescOrOpclass = (typentry->tupDesc != NULL) ||
2391 (typentry->flags & TCFLAGS_OPERATOR_FLAGS);
2392
2393 /* Delete tupdesc if we have it */
2394 if (typentry->tupDesc != NULL)
2395 {
2396 /*
2397 * Release our refcount and free the tupdesc if none remain. We can't
2398 * use DecrTupleDescRefCount here because this reference is not logged
2399 * by the current resource owner.
2400 */
2401 Assert(typentry->tupDesc->tdrefcount > 0);
2402 if (--typentry->tupDesc->tdrefcount == 0)
2403 FreeTupleDesc(typentry->tupDesc);
2404 typentry->tupDesc = NULL;
2405
2406 /*
2407 * Also clear tupDesc_identifier, so that anyone watching it will
2408 * realize that the tupdesc has changed.
2409 */
2410 typentry->tupDesc_identifier = 0;
2411 }
2412
2413 /* Reset equality/comparison/hashing validity information */
2414 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2415
2416 /*
2417 * Call delete_rel_type_cache_if_needed() if we actually cleared
2418 * something.
2419 */
2422}

References Assert, delete_rel_type_cache_if_needed(), fb(), TypeCacheEntry::flags, FreeTupleDesc(), OidIsValid, TCFLAGS_OPERATOR_FLAGS, TupleDescData::tdrefcount, TypeCacheEntry::tupDesc, TypeCacheEntry::tupDesc_identifier, TypeCacheEntry::typrelid, and TypeCacheEntry::typtype.

Referenced by TypeCacheRelCallback().

◆ load_domaintype_info()

static void load_domaintype_info ( TypeCacheEntry typentry)
static

Definition at line 1079 of file typcache.c.

1080{
1081 Oid typeOid = typentry->type_id;
1083 bool notNull = false;
1085 int cconslen;
1088
1089 /*
1090 * If we're here, any existing constraint info is stale, so release it.
1091 * For safety, be sure to null the link before trying to delete the data.
1092 */
1093 if (typentry->domainData)
1094 {
1095 dcc = typentry->domainData;
1096 typentry->domainData = NULL;
1097 decr_dcc_refcount(dcc);
1098 }
1099
1100 /*
1101 * We try to optimize the common case of no domain constraints, so don't
1102 * create the dcc object and context until we find a constraint. Likewise
1103 * for the temp sorting array.
1104 */
1105 dcc = NULL;
1106 ccons = NULL;
1107 cconslen = 0;
1108
1109 /*
1110 * Scan pg_constraint for relevant constraints. We want to find
1111 * constraints for not just this domain, but any ancestor domains, so the
1112 * outer loop crawls up the domain stack.
1113 */
1115
1116 for (;;)
1117 {
1118 HeapTuple tup;
1121 int nccons = 0;
1122 ScanKeyData key[1];
1123 SysScanDesc scan;
1124
1126 if (!HeapTupleIsValid(tup))
1127 elog(ERROR, "cache lookup failed for type %u", typeOid);
1129
1130 if (typTup->typtype != TYPTYPE_DOMAIN)
1131 {
1132 /* Not a domain, so done */
1134 break;
1135 }
1136
1137 /* Test for NOT NULL Constraint */
1138 if (typTup->typnotnull)
1139 notNull = true;
1140
1141 /* Look for CHECK Constraints on this domain */
1142 ScanKeyInit(&key[0],
1145 ObjectIdGetDatum(typeOid));
1146
1148 NULL, 1, key);
1149
1151 {
1153 Datum val;
1154 bool isNull;
1155 char *constring;
1156 Expr *check_expr;
1158
1159 /* Ignore non-CHECK constraints */
1160 if (c->contype != CONSTRAINT_CHECK)
1161 continue;
1162
1163 /* Not expecting conbin to be NULL, but we'll test for it anyway */
1165 conRel->rd_att, &isNull);
1166 if (isNull)
1167 elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
1168 NameStr(typTup->typname), NameStr(c->conname));
1169
1170 /* Create the DomainConstraintCache object and context if needed */
1171 if (dcc == NULL)
1172 {
1173 MemoryContext cxt;
1174
1176 "Domain constraints",
1178 dcc = (DomainConstraintCache *)
1180 dcc->constraints = NIL;
1181 dcc->dccContext = cxt;
1182 dcc->dccRefCount = 0;
1183 }
1184
1185 /* Convert conbin to a node tree, still in caller's context */
1187 check_expr = (Expr *) stringToNode(constring);
1188
1189 /*
1190 * Plan the expression, since ExecInitExpr will expect that.
1191 *
1192 * Note: caching the result of expression_planner() is not very
1193 * good practice. Ideally we'd use a CachedExpression here so
1194 * that we would react promptly to, eg, changes in inlined
1195 * functions. However, because we don't support mutable domain
1196 * CHECK constraints, it's not really clear that it's worth the
1197 * extra overhead to do that.
1198 */
1199 check_expr = expression_planner(check_expr);
1200
1201 /* Create only the minimally needed stuff in dccContext */
1203
1206 r->name = pstrdup(NameStr(c->conname));
1207 r->check_expr = copyObject(check_expr);
1208 r->check_exprstate = NULL;
1209
1211
1212 /* Accumulate constraints in an array, for sorting below */
1213 if (ccons == NULL)
1214 {
1215 cconslen = 8;
1218 }
1219 else if (nccons >= cconslen)
1220 {
1221 cconslen *= 2;
1224 }
1225 ccons[nccons++] = r;
1226 }
1227
1228 systable_endscan(scan);
1229
1230 if (nccons > 0)
1231 {
1232 /*
1233 * Sort the items for this domain, so that CHECKs are applied in a
1234 * deterministic order.
1235 */
1236 if (nccons > 1)
1238
1239 /*
1240 * Now attach them to the overall list. Use lcons() here because
1241 * constraints of parent domains should be applied earlier.
1242 */
1244 while (nccons > 0)
1245 dcc->constraints = lcons(ccons[--nccons], dcc->constraints);
1247 }
1248
1249 /* loop to next domain in stack */
1250 typeOid = typTup->typbasetype;
1252 }
1253
1255
1256 /*
1257 * Only need to add one NOT NULL check regardless of how many domains in
1258 * the stack request it.
1259 */
1260 if (notNull)
1261 {
1263
1264 /* Create the DomainConstraintCache object and context if needed */
1265 if (dcc == NULL)
1266 {
1267 MemoryContext cxt;
1268
1270 "Domain constraints",
1272 dcc = (DomainConstraintCache *)
1274 dcc->constraints = NIL;
1275 dcc->dccContext = cxt;
1276 dcc->dccRefCount = 0;
1277 }
1278
1279 /* Create node trees in DomainConstraintCache's context */
1281
1283
1285 r->name = pstrdup("NOT NULL");
1286 r->check_expr = NULL;
1287 r->check_exprstate = NULL;
1288
1289 /* lcons to apply the nullness check FIRST */
1290 dcc->constraints = lcons(r, dcc->constraints);
1291
1293 }
1294
1295 /*
1296 * If we made a constraint object, move it into CacheMemoryContext and
1297 * attach it to the typcache entry.
1298 */
1299 if (dcc)
1300 {
1302 typentry->domainData = dcc;
1303 dcc->dccRefCount++; /* count the typcache's reference */
1304 }
1305
1306 /* Either way, the typcache entry's domain data is now valid. */
1308}

References AccessShareLock, ALLOCSET_SMALL_SIZES, AllocSetContextCreate, BTEqualStrategyNumber, CacheMemoryContext, DomainConstraintState::check_expr, DomainConstraintState::check_exprstate, DomainConstraintCache::constraints, DomainConstraintState::constrainttype, copyObject, CurrentMemoryContext, DomainConstraintCache::dccContext, DomainConstraintCache::dccRefCount, dcs_cmp(), decr_dcc_refcount(), DOM_CONSTRAINT_CHECK, DOM_CONSTRAINT_NOTNULL, TypeCacheEntry::domainData, elog, ERROR, expression_planner(), fastgetattr(), fb(), TypeCacheEntry::flags, Form_pg_constraint, Form_pg_type, GETSTRUCT(), HeapTupleIsValid, lcons(), makeNode, MemoryContextAlloc(), MemoryContextSetParent(), MemoryContextSwitchTo(), DomainConstraintState::name, NameStr, NIL, ObjectIdGetDatum(), palloc(), pstrdup(), qsort, ReleaseSysCache(), repalloc(), ScanKeyInit(), SearchSysCache1(), stringToNode(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS, TextDatumGetCString, TypeCacheEntry::type_id, and val.

Referenced by lookup_type_cache(), and UpdateDomainConstraintRef().

◆ load_enum_cache_data()

static void load_enum_cache_data ( TypeCacheEntry tcache)
static

Definition at line 2755 of file typcache.c.

2756{
2762 EnumItem *items;
2763 int numitems;
2764 int maxitems;
2765 Oid bitmap_base;
2766 Bitmapset *bitmap;
2768 int bm_size,
2769 start_pos;
2770
2771 /* Check that this is actually an enum */
2772 if (tcache->typtype != TYPTYPE_ENUM)
2773 ereport(ERROR,
2775 errmsg("%s is not an enum",
2776 format_type_be(tcache->type_id))));
2777
2778 /*
2779 * Read all the information for members of the enum type. We collect the
2780 * info in working memory in the caller's context, and then transfer it to
2781 * permanent memory in CacheMemoryContext. This minimizes the risk of
2782 * leaking memory from CacheMemoryContext in the event of an error partway
2783 * through.
2784 */
2785 maxitems = 64;
2786 items = palloc_array(EnumItem, maxitems);
2787 numitems = 0;
2788
2789 /* Scan pg_enum for the members of the target enum type. */
2793 ObjectIdGetDatum(tcache->type_id));
2794
2798 true, NULL,
2799 1, &skey);
2800
2802 {
2804
2805 if (numitems >= maxitems)
2806 {
2807 maxitems *= 2;
2808 items = (EnumItem *) repalloc(items, sizeof(EnumItem) * maxitems);
2809 }
2810 items[numitems].enum_oid = en->oid;
2811 items[numitems].sort_order = en->enumsortorder;
2812 numitems++;
2813 }
2814
2817
2818 /* Sort the items into OID order */
2819 qsort(items, numitems, sizeof(EnumItem), enum_oid_cmp);
2820
2821 /*
2822 * Here, we create a bitmap listing a subset of the enum's OIDs that are
2823 * known to be in order and can thus be compared with just OID comparison.
2824 *
2825 * The point of this is that the enum's initial OIDs were certainly in
2826 * order, so there is some subset that can be compared via OID comparison;
2827 * and we'd rather not do binary searches unnecessarily.
2828 *
2829 * This is somewhat heuristic, and might identify a subset of OIDs that
2830 * isn't exactly what the type started with. That's okay as long as the
2831 * subset is correctly sorted.
2832 */
2833 bitmap_base = InvalidOid;
2834 bitmap = NULL;
2835 bm_size = 1; /* only save sets of at least 2 OIDs */
2836
2837 for (start_pos = 0; start_pos < numitems - 1; start_pos++)
2838 {
2839 /*
2840 * Identify longest sorted subsequence starting at start_pos
2841 */
2843 int this_bm_size = 1;
2844 Oid start_oid = items[start_pos].enum_oid;
2845 float4 prev_order = items[start_pos].sort_order;
2846 int i;
2847
2848 for (i = start_pos + 1; i < numitems; i++)
2849 {
2850 Oid offset;
2851
2852 offset = items[i].enum_oid - start_oid;
2853 /* quit if bitmap would be too large; cutoff is arbitrary */
2854 if (offset >= 8192)
2855 break;
2856 /* include the item if it's in-order */
2857 if (items[i].sort_order > prev_order)
2858 {
2859 prev_order = items[i].sort_order;
2860 this_bitmap = bms_add_member(this_bitmap, (int) offset);
2861 this_bm_size++;
2862 }
2863 }
2864
2865 /* Remember it if larger than previous best */
2866 if (this_bm_size > bm_size)
2867 {
2868 bms_free(bitmap);
2869 bitmap_base = start_oid;
2870 bitmap = this_bitmap;
2872 }
2873 else
2875
2876 /*
2877 * Done if it's not possible to find a longer sequence in the rest of
2878 * the list. In typical cases this will happen on the first
2879 * iteration, which is why we create the bitmaps on the fly instead of
2880 * doing a second pass over the list.
2881 */
2882 if (bm_size >= (numitems - start_pos - 1))
2883 break;
2884 }
2885
2886 /* OK, copy the data into CacheMemoryContext */
2889 palloc(offsetof(TypeCacheEnumData, enum_values) +
2890 numitems * sizeof(EnumItem));
2891 enumdata->bitmap_base = bitmap_base;
2892 enumdata->sorted_values = bms_copy(bitmap);
2893 enumdata->num_values = numitems;
2894 memcpy(enumdata->enum_values, items, numitems * sizeof(EnumItem));
2896
2897 pfree(items);
2898 bms_free(bitmap);
2899
2900 /* And link the finished cache struct into the typcache */
2901 if (tcache->enumData != NULL)
2902 pfree(tcache->enumData);
2903 tcache->enumData = enumdata;
2904}

References AccessShareLock, bms_add_member(), bms_copy(), bms_free(), bms_make_singleton(), BTEqualStrategyNumber, CacheMemoryContext, enum_oid_cmp(), TypeCacheEntry::enumData, ereport, errcode(), errmsg, ERROR, fb(), Form_pg_enum, format_type_be(), GETSTRUCT(), HeapTupleIsValid, i, InvalidOid, items, memcpy(), MemoryContextSwitchTo(), ObjectIdGetDatum(), palloc(), palloc_array, pfree(), qsort, repalloc(), ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TypeCacheEntry::type_id, and TypeCacheEntry::typtype.

Referenced by compare_values_of_enum().

◆ load_multirangetype_info()

static void load_multirangetype_info ( TypeCacheEntry typentry)
static

Definition at line 1057 of file typcache.c.

1058{
1060
1063 elog(ERROR, "cache lookup failed for multirange type %u",
1064 typentry->type_id);
1065
1067}

References elog, ERROR, fb(), get_multirange_range(), lookup_type_cache(), OidIsValid, TypeCacheEntry::rngtype, TypeCacheEntry::type_id, and TYPECACHE_RANGE_INFO.

Referenced by cache_multirange_element_properties(), and lookup_type_cache().

◆ load_rangetype_info()

static void load_rangetype_info ( TypeCacheEntry typentry)
static

Definition at line 999 of file typcache.c.

1000{
1002 HeapTuple tup;
1008 Oid opcintype;
1009 Oid cmpFnOid;
1010
1011 /* get information from pg_range */
1013 /* should not fail, since we already checked typtype ... */
1014 if (!HeapTupleIsValid(tup))
1015 elog(ERROR, "cache lookup failed for range type %u",
1016 typentry->type_id);
1018
1019 subtypeOid = pg_range->rngsubtype;
1020 typentry->rng_collation = pg_range->rngcollation;
1021 opclassOid = pg_range->rngsubopc;
1022 canonicalOid = pg_range->rngcanonical;
1023 subdiffOid = pg_range->rngsubdiff;
1024
1026
1027 /* get opclass properties and look up the comparison function */
1030 typentry->rng_opfamily = opfamilyOid;
1031
1032 cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype,
1033 BTORDER_PROC);
1035 elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
1036 BTORDER_PROC, opcintype, opcintype, opfamilyOid);
1037
1038 /* set up cached fmgrinfo structs */
1047
1048 /* Lastly, set up link to the element type --- this marks data valid */
1050}

References BTORDER_PROC, CacheMemoryContext, elog, ERROR, fb(), fmgr_info_cxt(), Form_pg_range, get_opclass_family(), get_opclass_input_type(), get_opfamily_proc(), GETSTRUCT(), HeapTupleIsValid, lookup_type_cache(), ObjectIdGetDatum(), OidIsValid, RegProcedureIsValid, ReleaseSysCache(), TypeCacheEntry::rng_canonical_finfo, TypeCacheEntry::rng_cmp_proc_finfo, TypeCacheEntry::rng_collation, TypeCacheEntry::rng_opfamily, TypeCacheEntry::rng_subdiff_finfo, TypeCacheEntry::rngelemtype, SearchSysCache1(), and TypeCacheEntry::type_id.

Referenced by cache_range_element_properties(), and lookup_type_cache().

◆ load_typcache_tupdesc()

static void load_typcache_tupdesc ( TypeCacheEntry typentry)
static

Definition at line 965 of file typcache.c.

966{
967 Relation rel;
968
969 if (!OidIsValid(typentry->typrelid)) /* should not happen */
970 elog(ERROR, "invalid typrelid for composite type %u",
971 typentry->type_id);
972 rel = relation_open(typentry->typrelid, AccessShareLock);
973 Assert(rel->rd_rel->reltype == typentry->type_id);
974
975 /*
976 * Link to the tupdesc and increment its refcount (we assert it's a
977 * refcounted descriptor). We don't use IncrTupleDescRefCount() for this,
978 * because the reference mustn't be entered in the current resource owner;
979 * it can outlive the current query.
980 */
981 typentry->tupDesc = RelationGetDescr(rel);
982
983 Assert(typentry->tupDesc->tdrefcount > 0);
984 typentry->tupDesc->tdrefcount++;
985
986 /*
987 * In future, we could take some pains to not change tupDesc_identifier if
988 * the tupdesc didn't really change; but for now it's not worth it.
989 */
991
993}

References AccessShareLock, Assert, elog, ERROR, OidIsValid, RelationData::rd_rel, relation_close(), relation_open(), RelationGetDescr, TupleDescData::tdrefcount, TypeCacheEntry::tupDesc, TypeCacheEntry::tupDesc_identifier, tupledesc_id_counter, TypeCacheEntry::type_id, and TypeCacheEntry::typrelid.

Referenced by cache_record_field_properties(), and lookup_type_cache().

◆ lookup_rowtype_tupdesc()

◆ lookup_rowtype_tupdesc_copy()

TupleDesc lookup_rowtype_tupdesc_copy ( Oid  type_id,
int32  typmod 
)

◆ lookup_rowtype_tupdesc_domain()

TupleDesc lookup_rowtype_tupdesc_domain ( Oid  type_id,
int32  typmod,
bool  noError 
)

Definition at line 1996 of file typcache.c.

1997{
1998 TupleDesc tupDesc;
1999
2000 if (type_id != RECORDOID)
2001 {
2002 /*
2003 * Check for domain or named composite type. We might as well load
2004 * whichever data is needed.
2005 */
2006 TypeCacheEntry *typentry;
2007
2008 typentry = lookup_type_cache(type_id,
2011 if (typentry->typtype == TYPTYPE_DOMAIN)
2013 typentry->domainBaseTypmod,
2014 noError);
2015 if (typentry->tupDesc == NULL && !noError)
2016 ereport(ERROR,
2018 errmsg("type %s is not composite",
2019 format_type_be(type_id))));
2020 tupDesc = typentry->tupDesc;
2021 }
2022 else
2023 tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
2024 if (tupDesc != NULL)
2025 PinTupleDesc(tupDesc);
2026 return tupDesc;
2027}

References TypeCacheEntry::domainBaseType, TypeCacheEntry::domainBaseTypmod, ereport, errcode(), errmsg, ERROR, fb(), format_type_be(), lookup_rowtype_tupdesc_internal(), lookup_rowtype_tupdesc_noerror(), lookup_type_cache(), PinTupleDesc, TypeCacheEntry::tupDesc, TYPECACHE_DOMAIN_BASE_INFO, TYPECACHE_TUPDESC, and TypeCacheEntry::typtype.

Referenced by ExecEvalWholeRowVar(), hstore_from_record(), hstore_populate_record(), plperl_sv_to_datum(), and rowtype_field_matches().

◆ lookup_rowtype_tupdesc_internal()

static TupleDesc lookup_rowtype_tupdesc_internal ( Oid  type_id,
int32  typmod,
bool  noError 
)
static

Definition at line 1846 of file typcache.c.

1847{
1848 if (type_id != RECORDOID)
1849 {
1850 /*
1851 * It's a named composite type, so use the regular typcache.
1852 */
1853 TypeCacheEntry *typentry;
1854
1855 typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
1856 if (typentry->tupDesc == NULL && !noError)
1857 ereport(ERROR,
1859 errmsg("type %s is not composite",
1860 format_type_be(type_id))));
1861 return typentry->tupDesc;
1862 }
1863 else
1864 {
1865 /*
1866 * It's a transient record type, so look in our record-type table.
1867 */
1868 if (typmod >= 0)
1869 {
1870 /* It is already in our local cache? */
1871 if (typmod < RecordCacheArrayLen &&
1872 RecordCacheArray[typmod].tupdesc != NULL)
1873 return RecordCacheArray[typmod].tupdesc;
1874
1875 /* Are we attached to a shared record typmod registry? */
1877 {
1879
1880 /* Try to find it in the shared typmod index. */
1882 &typmod, false);
1883 if (entry != NULL)
1884 {
1885 TupleDesc tupdesc;
1886
1887 tupdesc = (TupleDesc)
1889 entry->shared_tupdesc);
1890 Assert(typmod == tupdesc->tdtypmod);
1891
1892 /* We may need to extend the local RecordCacheArray. */
1894
1895 /*
1896 * Our local array can now point directly to the TupleDesc
1897 * in shared memory, which is non-reference-counted.
1898 */
1899 RecordCacheArray[typmod].tupdesc = tupdesc;
1900 Assert(tupdesc->tdrefcount == -1);
1901
1902 /*
1903 * We don't share tupdesc identifiers across processes, so
1904 * assign one locally.
1905 */
1907
1909 entry);
1910
1911 return RecordCacheArray[typmod].tupdesc;
1912 }
1913 }
1914 }
1915
1916 if (!noError)
1917 ereport(ERROR,
1919 errmsg("record type has not been registered")));
1920 return NULL;
1921 }
1922}

References Session::area, Assert, CurrentSession, dsa_get_address(), dshash_find(), dshash_release_lock(), ensure_record_cache_typmod_slot_exists(), ereport, errcode(), errmsg, ERROR, fb(), format_type_be(), RecordCacheArrayEntry::id, lookup_type_cache(), RecordCacheArray, RecordCacheArrayLen, SharedTypmodTableEntry::shared_tupdesc, Session::shared_typmod_registry, Session::shared_typmod_table, TupleDescData::tdrefcount, TupleDescData::tdtypmod, RecordCacheArrayEntry::tupdesc, TypeCacheEntry::tupDesc, tupledesc_id_counter, and TYPECACHE_TUPDESC.

Referenced by lookup_rowtype_tupdesc(), lookup_rowtype_tupdesc_copy(), lookup_rowtype_tupdesc_domain(), and lookup_rowtype_tupdesc_noerror().

◆ lookup_rowtype_tupdesc_noerror()

TupleDesc lookup_rowtype_tupdesc_noerror ( Oid  type_id,
int32  typmod,
bool  noError 
)

Definition at line 1957 of file typcache.c.

1958{
1959 TupleDesc tupDesc;
1960
1961 tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
1962 if (tupDesc != NULL)
1963 PinTupleDesc(tupDesc);
1964 return tupDesc;
1965}

References fb(), lookup_rowtype_tupdesc_internal(), and PinTupleDesc.

Referenced by lookup_rowtype_tupdesc_domain().

◆ lookup_type_cache()

TypeCacheEntry * lookup_type_cache ( Oid  type_id,
int  flags 
)

Definition at line 389 of file typcache.c.

390{
391 TypeCacheEntry *typentry;
392 bool found;
394
395 if (TypeCacheHash == NULL)
396 {
397 /* First time through: initialize the hash table */
398 HASHCTL ctl;
399 int allocsize;
400
401 ctl.keysize = sizeof(Oid);
402 ctl.entrysize = sizeof(TypeCacheEntry);
403
404 /*
405 * TypeCacheEntry takes hash value from the system cache. For
406 * TypeCacheHash we use the same hash in order to speedup search by
407 * hash value. This is used by hash_seq_init_with_hash_value().
408 */
409 ctl.hash = type_cache_syshash;
410
411 TypeCacheHash = hash_create("Type information cache", 64,
413
415
416 ctl.keysize = sizeof(Oid);
417 ctl.entrysize = sizeof(RelIdToTypeIdCacheEntry);
418 RelIdToTypeIdCacheHash = hash_create("Map from relid to OID of cached composite type", 64,
420
421 /* Also set up callbacks for SI invalidations */
426
427 /* Also make sure CacheMemoryContext exists */
430
431 /*
432 * reserve enough in_progress_list slots for many cases
433 */
434 allocsize = 4;
437 allocsize * sizeof(*in_progress_list));
438 in_progress_list_maxlen = allocsize;
439 }
440
442
443 /* Register to catch invalidation messages */
445 {
446 int allocsize;
447
448 allocsize = in_progress_list_maxlen * 2;
450 allocsize * sizeof(*in_progress_list));
451 in_progress_list_maxlen = allocsize;
452 }
455
456 /* Try to look up an existing entry */
458 &type_id,
459 HASH_FIND, NULL);
460 if (typentry == NULL)
461 {
462 /*
463 * If we didn't find one, we want to make one. But first look up the
464 * pg_type row, just to make sure we don't make a cache entry for an
465 * invalid type OID. If the type OID is not valid, present a
466 * user-facing error, since some code paths such as domain_in() allow
467 * this function to be reached with a user-supplied OID.
468 */
469 HeapTuple tp;
471
473 if (!HeapTupleIsValid(tp))
476 errmsg("type with OID %u does not exist", type_id)));
478 if (!typtup->typisdefined)
481 errmsg("type \"%s\" is only a shell",
482 NameStr(typtup->typname))));
483
484 /* Now make the typcache entry */
486 &type_id,
487 HASH_ENTER, &found);
488 Assert(!found); /* it wasn't there a moment ago */
489
490 MemSet(typentry, 0, sizeof(TypeCacheEntry));
491
492 /* These fields can never change, by definition */
493 typentry->type_id = type_id;
494 typentry->type_id_hash = get_hash_value(TypeCacheHash, &type_id);
495
496 /* Keep this part in sync with the code below */
497 typentry->typlen = typtup->typlen;
498 typentry->typbyval = typtup->typbyval;
499 typentry->typalign = typtup->typalign;
500 typentry->typstorage = typtup->typstorage;
501 typentry->typtype = typtup->typtype;
502 typentry->typrelid = typtup->typrelid;
503 typentry->typsubscript = typtup->typsubscript;
504 typentry->typelem = typtup->typelem;
505 typentry->typarray = typtup->typarray;
506 typentry->typcollation = typtup->typcollation;
507 typentry->flags |= TCFLAGS_HAVE_PG_TYPE_DATA;
508
509 /* If it's a domain, immediately thread it into the domain cache list */
510 if (typentry->typtype == TYPTYPE_DOMAIN)
511 {
513 firstDomainTypeEntry = typentry;
514 }
515
516 ReleaseSysCache(tp);
517 }
518 else if (!(typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA))
519 {
520 /*
521 * We have an entry, but its pg_type row got changed, so reload the
522 * data obtained directly from pg_type.
523 */
524 HeapTuple tp;
526
528 if (!HeapTupleIsValid(tp))
531 errmsg("type with OID %u does not exist", type_id)));
533 if (!typtup->typisdefined)
536 errmsg("type \"%s\" is only a shell",
537 NameStr(typtup->typname))));
538
539 /*
540 * Keep this part in sync with the code above. Many of these fields
541 * shouldn't ever change, particularly typtype, but copy 'em anyway.
542 */
543 typentry->typlen = typtup->typlen;
544 typentry->typbyval = typtup->typbyval;
545 typentry->typalign = typtup->typalign;
546 typentry->typstorage = typtup->typstorage;
547 typentry->typtype = typtup->typtype;
548 typentry->typrelid = typtup->typrelid;
549 typentry->typsubscript = typtup->typsubscript;
550 typentry->typelem = typtup->typelem;
551 typentry->typarray = typtup->typarray;
552 typentry->typcollation = typtup->typcollation;
553 typentry->flags |= TCFLAGS_HAVE_PG_TYPE_DATA;
554
555 ReleaseSysCache(tp);
556 }
557
558 /*
559 * Look up opclasses if we haven't already and any dependent info is
560 * requested.
561 */
567 {
568 Oid opclass;
569
570 opclass = GetDefaultOpClass(type_id, BTREE_AM_OID);
571 if (OidIsValid(opclass))
572 {
573 typentry->btree_opf = get_opclass_family(opclass);
574 typentry->btree_opintype = get_opclass_input_type(opclass);
575 }
576 else
577 {
578 typentry->btree_opf = typentry->btree_opintype = InvalidOid;
579 }
580
581 /*
582 * Reset information derived from btree opclass. Note in particular
583 * that we'll redetermine the eq_opr even if we previously found one;
584 * this matters in case a btree opclass has been added to a type that
585 * previously had only a hash opclass.
586 */
587 typentry->flags &= ~(TCFLAGS_CHECKED_EQ_OPR |
592 }
593
594 /*
595 * If we need to look up equality operator, and there's no btree opclass,
596 * force lookup of hash opclass.
597 */
598 if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
599 !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR) &&
600 typentry->btree_opf == InvalidOid)
602
607 !(typentry->flags & TCFLAGS_CHECKED_HASH_OPCLASS))
608 {
609 Oid opclass;
610
611 opclass = GetDefaultOpClass(type_id, HASH_AM_OID);
612 if (OidIsValid(opclass))
613 {
614 typentry->hash_opf = get_opclass_family(opclass);
615 typentry->hash_opintype = get_opclass_input_type(opclass);
616 }
617 else
618 {
619 typentry->hash_opf = typentry->hash_opintype = InvalidOid;
620 }
621
622 /*
623 * Reset information derived from hash opclass. We do *not* reset the
624 * eq_opr; if we already found one from the btree opclass, that
625 * decision is still good.
626 */
627 typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC |
630 }
631
632 /*
633 * Look for requested operators and functions, if we haven't already.
634 */
635 if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
636 !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR))
637 {
638 Oid eq_opr = InvalidOid;
639
640 if (typentry->btree_opf != InvalidOid)
641 eq_opr = get_opfamily_member(typentry->btree_opf,
642 typentry->btree_opintype,
643 typentry->btree_opintype,
645 if (eq_opr == InvalidOid &&
646 typentry->hash_opf != InvalidOid)
647 eq_opr = get_opfamily_member(typentry->hash_opf,
648 typentry->hash_opintype,
649 typentry->hash_opintype,
651
652 /*
653 * If the proposed equality operator is array_eq or record_eq, check
654 * to see if the element type or column types support equality. If
655 * not, array_eq or record_eq would fail at runtime, so we don't want
656 * to report that the type has equality. (We can omit similar
657 * checking for ranges and multiranges because ranges can't be created
658 * in the first place unless their subtypes support equality.)
659 */
660 if (eq_opr == ARRAY_EQ_OP &&
662 eq_opr = InvalidOid;
663 else if (eq_opr == RECORD_EQ_OP &&
665 eq_opr = InvalidOid;
666
667 /* Force update of eq_opr_finfo only if we're changing state */
668 if (typentry->eq_opr != eq_opr)
669 typentry->eq_opr_finfo.fn_oid = InvalidOid;
670
671 typentry->eq_opr = eq_opr;
672
673 /*
674 * Reset info about hash functions whenever we pick up new info about
675 * equality operator. This is so we can ensure that the hash
676 * functions match the operator.
677 */
678 typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC |
680 typentry->flags |= TCFLAGS_CHECKED_EQ_OPR;
681 }
682 if ((flags & TYPECACHE_LT_OPR) &&
683 !(typentry->flags & TCFLAGS_CHECKED_LT_OPR))
684 {
685 Oid lt_opr = InvalidOid;
686
687 if (typentry->btree_opf != InvalidOid)
688 lt_opr = get_opfamily_member(typentry->btree_opf,
689 typentry->btree_opintype,
690 typentry->btree_opintype,
692
693 /*
694 * As above, make sure array_cmp or record_cmp will succeed; but again
695 * we need no special check for ranges or multiranges.
696 */
697 if (lt_opr == ARRAY_LT_OP &&
698 !array_element_has_compare(typentry))
699 lt_opr = InvalidOid;
700 else if (lt_opr == RECORD_LT_OP &&
702 lt_opr = InvalidOid;
703
704 typentry->lt_opr = lt_opr;
705 typentry->flags |= TCFLAGS_CHECKED_LT_OPR;
706 }
707 if ((flags & TYPECACHE_GT_OPR) &&
708 !(typentry->flags & TCFLAGS_CHECKED_GT_OPR))
709 {
710 Oid gt_opr = InvalidOid;
711
712 if (typentry->btree_opf != InvalidOid)
713 gt_opr = get_opfamily_member(typentry->btree_opf,
714 typentry->btree_opintype,
715 typentry->btree_opintype,
717
718 /*
719 * As above, make sure array_cmp or record_cmp will succeed; but again
720 * we need no special check for ranges or multiranges.
721 */
722 if (gt_opr == ARRAY_GT_OP &&
723 !array_element_has_compare(typentry))
724 gt_opr = InvalidOid;
725 else if (gt_opr == RECORD_GT_OP &&
727 gt_opr = InvalidOid;
728
729 typentry->gt_opr = gt_opr;
730 typentry->flags |= TCFLAGS_CHECKED_GT_OPR;
731 }
733 !(typentry->flags & TCFLAGS_CHECKED_CMP_PROC))
734 {
735 Oid cmp_proc = InvalidOid;
736
737 if (typentry->btree_opf != InvalidOid)
738 cmp_proc = get_opfamily_proc(typentry->btree_opf,
739 typentry->btree_opintype,
740 typentry->btree_opintype,
742
743 /*
744 * As above, make sure array_cmp or record_cmp will succeed; but again
745 * we need no special check for ranges or multiranges.
746 */
747 if (cmp_proc == F_BTARRAYCMP &&
748 !array_element_has_compare(typentry))
749 cmp_proc = InvalidOid;
750 else if (cmp_proc == F_BTRECORDCMP &&
752 cmp_proc = InvalidOid;
753
754 /* Force update of cmp_proc_finfo only if we're changing state */
755 if (typentry->cmp_proc != cmp_proc)
756 typentry->cmp_proc_finfo.fn_oid = InvalidOid;
757
758 typentry->cmp_proc = cmp_proc;
759 typentry->flags |= TCFLAGS_CHECKED_CMP_PROC;
760 }
762 !(typentry->flags & TCFLAGS_CHECKED_HASH_PROC))
763 {
764 Oid hash_proc = InvalidOid;
765
766 /*
767 * We insist that the eq_opr, if one has been determined, match the
768 * hash opclass; else report there is no hash function.
769 */
770 if (typentry->hash_opf != InvalidOid &&
771 (!OidIsValid(typentry->eq_opr) ||
772 typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
773 typentry->hash_opintype,
774 typentry->hash_opintype,
776 hash_proc = get_opfamily_proc(typentry->hash_opf,
777 typentry->hash_opintype,
778 typentry->hash_opintype,
780
781 /*
782 * As above, make sure hash_array, hash_record, hash_range, or
783 * hash_multirange will succeed. Here we do need to check the range
784 * cases.
785 */
786 if (hash_proc == F_HASH_ARRAY &&
787 !array_element_has_hashing(typentry))
788 hash_proc = InvalidOid;
789 else if (hash_proc == F_HASH_RECORD &&
791 hash_proc = InvalidOid;
792 else if (hash_proc == F_HASH_RANGE &&
793 !range_element_has_hashing(typentry))
794 hash_proc = InvalidOid;
795 else if (hash_proc == F_HASH_MULTIRANGE &&
797 hash_proc = InvalidOid;
798
799 /* Force update of hash_proc_finfo only if we're changing state */
800 if (typentry->hash_proc != hash_proc)
802
803 typentry->hash_proc = hash_proc;
804 typentry->flags |= TCFLAGS_CHECKED_HASH_PROC;
805 }
806 if ((flags & (TYPECACHE_HASH_EXTENDED_PROC |
809 {
810 Oid hash_extended_proc = InvalidOid;
811
812 /*
813 * We insist that the eq_opr, if one has been determined, match the
814 * hash opclass; else report there is no hash function.
815 */
816 if (typentry->hash_opf != InvalidOid &&
817 (!OidIsValid(typentry->eq_opr) ||
818 typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
819 typentry->hash_opintype,
820 typentry->hash_opintype,
822 hash_extended_proc = get_opfamily_proc(typentry->hash_opf,
823 typentry->hash_opintype,
824 typentry->hash_opintype,
826
827 /*
828 * As above, make sure hash_array_extended, hash_record_extended,
829 * hash_range_extended, or hash_multirange_extended will succeed.
830 */
831 if (hash_extended_proc == F_HASH_ARRAY_EXTENDED &&
833 hash_extended_proc = InvalidOid;
834 else if (hash_extended_proc == F_HASH_RECORD_EXTENDED &&
836 hash_extended_proc = InvalidOid;
837 else if (hash_extended_proc == F_HASH_RANGE_EXTENDED &&
839 hash_extended_proc = InvalidOid;
840 else if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
842 hash_extended_proc = InvalidOid;
843
844 /* Force update of proc finfo only if we're changing state */
845 if (typentry->hash_extended_proc != hash_extended_proc)
847
848 typentry->hash_extended_proc = hash_extended_proc;
850 }
851
852 /*
853 * Set up fmgr lookup info as requested
854 *
855 * Note: we tell fmgr the finfo structures live in CacheMemoryContext,
856 * which is not quite right (they're really in the hash table's private
857 * memory context) but this will do for our purposes.
858 *
859 * Note: the code above avoids invalidating the finfo structs unless the
860 * referenced operator/function OID actually changes. This is to prevent
861 * unnecessary leakage of any subsidiary data attached to an finfo, since
862 * that would cause session-lifespan memory leaks.
863 */
864 if ((flags & TYPECACHE_EQ_OPR_FINFO) &&
865 typentry->eq_opr_finfo.fn_oid == InvalidOid &&
866 typentry->eq_opr != InvalidOid)
867 {
869
870 eq_opr_func = get_opcode(typentry->eq_opr);
871 if (eq_opr_func != InvalidOid)
874 }
875 if ((flags & TYPECACHE_CMP_PROC_FINFO) &&
876 typentry->cmp_proc_finfo.fn_oid == InvalidOid &&
877 typentry->cmp_proc != InvalidOid)
878 {
879 fmgr_info_cxt(typentry->cmp_proc, &typentry->cmp_proc_finfo,
881 }
882 if ((flags & TYPECACHE_HASH_PROC_FINFO) &&
883 typentry->hash_proc_finfo.fn_oid == InvalidOid &&
884 typentry->hash_proc != InvalidOid)
885 {
886 fmgr_info_cxt(typentry->hash_proc, &typentry->hash_proc_finfo,
888 }
891 typentry->hash_extended_proc != InvalidOid)
892 {
894 &typentry->hash_extended_proc_finfo,
896 }
897
898 /*
899 * If it's a composite type (row type), get tupdesc if requested
900 */
901 if ((flags & TYPECACHE_TUPDESC) &&
902 typentry->tupDesc == NULL &&
903 typentry->typtype == TYPTYPE_COMPOSITE)
904 {
905 load_typcache_tupdesc(typentry);
906 }
907
908 /*
909 * If requested, get information about a range type
910 *
911 * This includes making sure that the basic info about the range element
912 * type is up-to-date.
913 */
914 if ((flags & TYPECACHE_RANGE_INFO) &&
915 typentry->typtype == TYPTYPE_RANGE)
916 {
917 if (typentry->rngelemtype == NULL)
918 load_rangetype_info(typentry);
919 else if (!(typentry->rngelemtype->flags & TCFLAGS_HAVE_PG_TYPE_DATA))
920 (void) lookup_type_cache(typentry->rngelemtype->type_id, 0);
921 }
922
923 /*
924 * If requested, get information about a multirange type
925 */
926 if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
927 typentry->rngtype == NULL &&
928 typentry->typtype == TYPTYPE_MULTIRANGE)
929 {
930 load_multirangetype_info(typentry);
931 }
932
933 /*
934 * If requested, get information about a domain type
935 */
936 if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
937 typentry->domainBaseType == InvalidOid &&
938 typentry->typtype == TYPTYPE_DOMAIN)
939 {
940 typentry->domainBaseTypmod = -1;
941 typentry->domainBaseType =
942 getBaseTypeAndTypmod(type_id, &typentry->domainBaseTypmod);
943 }
944 if ((flags & TYPECACHE_DOMAIN_CONSTR_INFO) &&
945 (typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
946 typentry->typtype == TYPTYPE_DOMAIN)
947 {
948 load_domaintype_info(typentry);
949 }
950
951 INJECTION_POINT("typecache-before-rel-type-cache-insert", NULL);
952
955
957
958 return typentry;
959}

References array_element_has_compare(), array_element_has_equality(), array_element_has_extended_hashing(), array_element_has_hashing(), Assert, BTEqualStrategyNumber, BTGreaterStrategyNumber, BTLessStrategyNumber, BTORDER_PROC, TypeCacheEntry::btree_opf, TypeCacheEntry::btree_opintype, CacheMemoryContext, CacheRegisterRelcacheCallback(), CacheRegisterSyscacheCallback(), TypeCacheEntry::cmp_proc, TypeCacheEntry::cmp_proc_finfo, CreateCacheMemoryContext(), ctl, TypeCacheEntry::domainBaseType, TypeCacheEntry::domainBaseTypmod, TypeCacheEntry::eq_opr, TypeCacheEntry::eq_opr_finfo, ereport, errcode(), errmsg, ERROR, fb(), firstDomainTypeEntry, TypeCacheEntry::flags, fmgr_info_cxt(), FmgrInfo::fn_oid, Form_pg_type, get_hash_value(), get_opclass_family(), get_opclass_input_type(), get_opcode(), get_opfamily_member(), get_opfamily_proc(), getBaseTypeAndTypmod(), GetDefaultOpClass(), GETSTRUCT(), TypeCacheEntry::gt_opr, HASH_BLOBS, hash_create(), HASH_ELEM, HASH_ENTER, TypeCacheEntry::hash_extended_proc, TypeCacheEntry::hash_extended_proc_finfo, HASH_FIND, HASH_FUNCTION, TypeCacheEntry::hash_opf, TypeCacheEntry::hash_opintype, TypeCacheEntry::hash_proc, TypeCacheEntry::hash_proc_finfo, hash_search(), HASHEXTENDED_PROC, HASHSTANDARD_PROC, HeapTupleIsValid, HTEqualStrategyNumber, in_progress_list, in_progress_list_len, in_progress_list_maxlen, INJECTION_POINT, insert_rel_type_cache_if_needed(), InvalidOid, HASHCTL::keysize, load_domaintype_info(), load_multirangetype_info(), load_rangetype_info(), load_typcache_tupdesc(), lookup_type_cache(), TypeCacheEntry::lt_opr, MemoryContextAlloc(), MemSet, multirange_element_has_extended_hashing(), multirange_element_has_hashing(), NameStr, TypeCacheEntry::nextDomain, ObjectIdGetDatum(), OidIsValid, range_element_has_extended_hashing(), range_element_has_hashing(), record_fields_have_compare(), record_fields_have_equality(), record_fields_have_extended_hashing(), record_fields_have_hashing(), ReleaseSysCache(), RelIdToTypeIdCacheHash, repalloc(), TypeCacheEntry::rngelemtype, TypeCacheEntry::rngtype, SearchSysCache1(), TCFLAGS_CHECKED_BTREE_OPCLASS, TCFLAGS_CHECKED_CMP_PROC, TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS, TCFLAGS_CHECKED_EQ_OPR, TCFLAGS_CHECKED_GT_OPR, TCFLAGS_CHECKED_HASH_EXTENDED_PROC, TCFLAGS_CHECKED_HASH_OPCLASS, TCFLAGS_CHECKED_HASH_PROC, TCFLAGS_CHECKED_LT_OPR, TCFLAGS_HAVE_PG_TYPE_DATA, TypeCacheEntry::tupDesc, TypeCacheEntry::typalign, TypeCacheEntry::typarray, TypeCacheEntry::typbyval, TypeCacheEntry::typcollation, type_cache_syshash(), TypeCacheEntry::type_id, TypeCacheEntry::type_id_hash, TYPECACHE_BTREE_OPFAMILY, TYPECACHE_CMP_PROC, TYPECACHE_CMP_PROC_FINFO, TYPECACHE_DOMAIN_BASE_INFO, TYPECACHE_DOMAIN_CONSTR_INFO, TYPECACHE_EQ_OPR, TYPECACHE_EQ_OPR_FINFO, TYPECACHE_GT_OPR, TYPECACHE_HASH_EXTENDED_PROC, TYPECACHE_HASH_EXTENDED_PROC_FINFO, TYPECACHE_HASH_OPFAMILY, TYPECACHE_HASH_PROC, TYPECACHE_HASH_PROC_FINFO, TYPECACHE_LT_OPR, TYPECACHE_MULTIRANGE_INFO, TYPECACHE_RANGE_INFO, TYPECACHE_TUPDESC, TypeCacheConstrCallback(), TypeCacheHash, TypeCacheOpcCallback(), TypeCacheRelCallback(), TypeCacheTypCallback(), TypeCacheEntry::typelem, TypeCacheEntry::typlen, TypeCacheEntry::typrelid, TypeCacheEntry::typstorage, TypeCacheEntry::typsubscript, and TypeCacheEntry::typtype.

Referenced by analyzeCTE(), appendOrderBySuffix(), array_cmp(), array_contain_compare(), array_eq(), array_position_common(), array_positions(), array_replace_internal(), array_reverse(), array_sample(), array_shuffle(), array_sort_internal(), array_typanalyze(), assign_record_type_identifier(), brin_bloom_opcinfo(), brin_inclusion_opcinfo(), brin_minmax_multi_opcinfo(), brin_minmax_opcinfo(), build_datatype(), build_mss(), cache_array_element_properties(), cache_multirange_element_properties(), cache_range_element_properties(), cache_record_field_properties(), calc_arraycontsel(), check_exclusion_or_unique_constraint(), check_memoizable(), contain_leaked_vars_walker(), create_grouping_expr_infos(), CreateStatistics(), dependency_degree(), domain_state_setup(), DomainHasConstraints(), enum_cmp_internal(), ExecForPortionOfLeftovers(), ExecInitExprRec(), find_simplified_clause(), foreign_expr_walker(), get_cached_rowtype(), get_multirange_io_data(), get_op_hash_functions_ext(), get_range_io_data(), get_rule_orderby(), get_sort_group_operators(), GinBufferInit(), hash_array(), hash_array_extended(), hash_multirange(), hash_multirange_extended(), hash_range(), hash_range_extended(), hash_record(), hash_record_extended(), import_pg_statistic(), init_grouping_targets(), InitDomainConstraintRef(), initGinState(), IsIndexUsableForReplicaIdentityFull(), load_multirangetype_info(), load_rangetype_info(), lookup_rowtype_tupdesc_domain(), lookup_rowtype_tupdesc_internal(), lookup_type_cache(), make_expanded_record_from_tupdesc(), make_expanded_record_from_typeid(), multirange_get_typcache(), multirange_minus_multi(), multirange_unnest(), ndistinct_for_combination(), op_hashjoinable(), op_mergejoinable(), paraminfo_get_equal_hashops(), PLy_input_setup_func(), PLy_output_setup_func(), range_fast_cmp(), range_get_typcache(), range_minus_multi(), record_cmp(), record_eq(), revalidate_rectypeid(), scalararraysel(), scalararraysel_containment(), show_sortorder_options(), statatt_get_elem_type(), statatt_get_type(), statext_mcv_serialize(), tuples_equal(), tuplesort_begin_index_gin(), and width_bucket_array().

◆ multirange_element_has_extended_hashing()

static bool multirange_element_has_extended_hashing ( TypeCacheEntry typentry)
static

◆ multirange_element_has_hashing()

static bool multirange_element_has_hashing ( TypeCacheEntry typentry)
static

◆ prep_domain_constraints()

static List * prep_domain_constraints ( List constraints,
MemoryContext  execctx 
)
static

Definition at line 1359 of file typcache.c.

1360{
1361 List *result = NIL;
1363 ListCell *lc;
1364
1366
1367 foreach(lc, constraints)
1368 {
1371
1373 newr->constrainttype = r->constrainttype;
1374 newr->name = r->name;
1375 newr->check_expr = r->check_expr;
1376 newr->check_exprstate = ExecInitExpr(r->check_expr, NULL);
1377
1379 }
1380
1382
1383 return result;
1384}

References DomainConstraintState::check_expr, DomainConstraintState::constrainttype, ExecInitExpr(), fb(), lappend(), lfirst, makeNode, MemoryContextSwitchTo(), DomainConstraintState::name, NIL, and result.

Referenced by InitDomainConstraintRef(), and UpdateDomainConstraintRef().

◆ range_element_has_extended_hashing()

static bool range_element_has_extended_hashing ( TypeCacheEntry typentry)
static

◆ range_element_has_hashing()

static bool range_element_has_hashing ( TypeCacheEntry typentry)
static

Definition at line 1733 of file typcache.c.

1734{
1735 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1737 return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1738}

References cache_range_element_properties(), TypeCacheEntry::flags, TCFLAGS_CHECKED_ELEM_PROPERTIES, and TCFLAGS_HAVE_ELEM_HASHING.

Referenced by lookup_type_cache().

◆ record_fields_have_compare()

static bool record_fields_have_compare ( TypeCacheEntry typentry)
static

Definition at line 1604 of file typcache.c.

1605{
1606 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1608 return (typentry->flags & TCFLAGS_HAVE_FIELD_COMPARE) != 0;
1609}

References cache_record_field_properties(), TypeCacheEntry::flags, TCFLAGS_CHECKED_FIELD_PROPERTIES, and TCFLAGS_HAVE_FIELD_COMPARE.

Referenced by lookup_type_cache().

◆ record_fields_have_equality()

static bool record_fields_have_equality ( TypeCacheEntry typentry)
static

◆ record_fields_have_extended_hashing()

static bool record_fields_have_extended_hashing ( TypeCacheEntry typentry)
static

◆ record_fields_have_hashing()

static bool record_fields_have_hashing ( TypeCacheEntry typentry)
static

Definition at line 1612 of file typcache.c.

1613{
1614 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1616 return (typentry->flags & TCFLAGS_HAVE_FIELD_HASHING) != 0;
1617}

References cache_record_field_properties(), TypeCacheEntry::flags, TCFLAGS_CHECKED_FIELD_PROPERTIES, and TCFLAGS_HAVE_FIELD_HASHING.

Referenced by lookup_type_cache().

◆ record_type_typmod_compare()

static int record_type_typmod_compare ( const void a,
const void b,
size_t  size 
)
static

Definition at line 2044 of file typcache.c.

2045{
2046 const RecordCacheEntry *left = a;
2047 const RecordCacheEntry *right = b;
2048
2049 return equalRowTypes(left->tupdesc, right->tupdesc) ? 0 : 1;
2050}

References a, b, equalRowTypes(), and RecordCacheEntry::tupdesc.

Referenced by assign_record_type_typmod().

◆ record_type_typmod_hash()

static uint32 record_type_typmod_hash ( const void data,
size_t  size 
)
static

Definition at line 2033 of file typcache.c.

2034{
2035 const RecordCacheEntry *entry = data;
2036
2037 return hashRowType(entry->tupdesc);
2038}

References data, hashRowType(), and RecordCacheEntry::tupdesc.

Referenced by assign_record_type_typmod().

◆ share_tupledesc()

static dsa_pointer share_tupledesc ( dsa_area area,
TupleDesc  tupdesc,
uint32  typmod 
)
static

Definition at line 2940 of file typcache.c.

2941{
2943 TupleDesc shared;
2944
2945 shared_dp = dsa_allocate(area, TupleDescSize(tupdesc));
2946 shared = (TupleDesc) dsa_get_address(area, shared_dp);
2947 TupleDescCopy(shared, tupdesc);
2948 shared->tdtypmod = typmod;
2949
2950 return shared_dp;
2951}

References dsa_allocate, dsa_get_address(), fb(), TupleDescData::tdtypmod, TupleDescCopy(), and TupleDescSize.

Referenced by find_or_make_matching_shared_tupledesc(), and SharedRecordTypmodRegistryInit().

◆ shared_record_table_compare()

static int shared_record_table_compare ( const void a,
const void b,
size_t  size,
void arg 
)
static

Definition at line 234 of file typcache.c.

236{
237 dsa_area *area = (dsa_area *) arg;
238 const SharedRecordTableKey *k1 = a;
239 const SharedRecordTableKey *k2 = b;
242
243 if (k1->shared)
244 t1 = (TupleDesc) dsa_get_address(area, k1->u.shared_tupdesc);
245 else
246 t1 = k1->u.local_tupdesc;
247
248 if (k2->shared)
249 t2 = (TupleDesc) dsa_get_address(area, k2->u.shared_tupdesc);
250 else
251 t2 = k2->u.local_tupdesc;
252
253 return equalRowTypes(t1, t2) ? 0 : 1;
254}

References a, arg, b, dsa_get_address(), equalRowTypes(), and fb().

◆ shared_record_table_hash()

static uint32 shared_record_table_hash ( const void a,
size_t  size,
void arg 
)
static

Definition at line 260 of file typcache.c.

261{
262 dsa_area *area = arg;
263 const SharedRecordTableKey *k = a;
264 TupleDesc t;
265
266 if (k->shared)
268 else
269 t = k->u.local_tupdesc;
270
271 return hashRowType(t);
272}

References a, arg, dsa_get_address(), hashRowType(), SharedRecordTableKey::local_tupdesc, SharedRecordTableKey::shared, SharedRecordTableKey::shared_tupdesc, and SharedRecordTableKey::u.

◆ shared_record_typmod_registry_detach()

static void shared_record_typmod_registry_detach ( dsm_segment segment,
Datum  datum 
)
static

◆ SharedRecordTypmodRegistryAttach()

void SharedRecordTypmodRegistryAttach ( SharedRecordTypmodRegistry registry)

Definition at line 2314 of file typcache.c.

2315{
2319
2321
2322 /* We can't already be attached to a shared registry. */
2329
2330 /*
2331 * We can't already have typmods in our local cache, because they'd clash
2332 * with those imported by SharedRecordTypmodRegistryInit. This should be
2333 * a freshly started parallel worker. If we ever support worker
2334 * recycling, a worker would need to zap its local cache in between
2335 * servicing different queries, in order to be able to call this and
2336 * synchronize typmods with a new leader; but that's problematic because
2337 * we can't be very sure that record-typmod-related state hasn't escaped
2338 * to anywhere else in the process.
2339 */
2341
2343
2344 /* Attach to the two hash tables. */
2347 registry->record_table_handle,
2351 registry->typmod_table_handle,
2352 NULL);
2353
2355
2356 /*
2357 * Set up detach hook to run at worker exit. Currently this is the same
2358 * as the leader's detach hook, but in future they might need to be
2359 * different.
2360 */
2364
2365 /*
2366 * Set up the session state that will tell assign_record_type_typmod and
2367 * lookup_rowtype_tupdesc_internal about the shared registry.
2368 */
2372}

References Session::area, Assert, CurrentSession, dshash_attach(), fb(), IsParallelWorker, MemoryContextSwitchTo(), NextRecordTypmod, on_dsm_detach(), PointerGetDatum, Session::segment, Session::shared_record_table, shared_record_typmod_registry_detach(), Session::shared_typmod_registry, Session::shared_typmod_table, srtr_record_table_params, srtr_typmod_table_params, and TopMemoryContext.

Referenced by AttachSession().

◆ SharedRecordTypmodRegistryEstimate()

size_t SharedRecordTypmodRegistryEstimate ( void  )

Definition at line 2193 of file typcache.c.

2194{
2195 return sizeof(SharedRecordTypmodRegistry);
2196}

Referenced by GetSessionDsmHandle().

◆ SharedRecordTypmodRegistryInit()

void SharedRecordTypmodRegistryInit ( SharedRecordTypmodRegistry registry,
dsm_segment segment,
dsa_area area 
)

Definition at line 2215 of file typcache.c.

2218{
2222 int32 typmod;
2223
2225
2226 /* We can't already be attached to a shared registry. */
2230
2232
2233 /* Create the hash table of tuple descriptors indexed by themselves. */
2235
2236 /* Create the hash table of tuple descriptors indexed by typmod. */
2238
2240
2241 /* Initialize the SharedRecordTypmodRegistry. */
2242 registry->record_table_handle = dshash_get_hash_table_handle(record_table);
2243 registry->typmod_table_handle = dshash_get_hash_table_handle(typmod_table);
2245
2246 /*
2247 * Copy all entries from this backend's private registry into the shared
2248 * registry.
2249 */
2250 for (typmod = 0; typmod < NextRecordTypmod; ++typmod)
2251 {
2256 TupleDesc tupdesc;
2257 bool found;
2258
2259 tupdesc = RecordCacheArray[typmod].tupdesc;
2260 if (tupdesc == NULL)
2261 continue;
2262
2263 /* Copy the TupleDesc into shared memory. */
2264 shared_dp = share_tupledesc(area, tupdesc, typmod);
2265
2266 /* Insert into the typmod table. */
2268 &tupdesc->tdtypmod,
2269 &found);
2270 if (found)
2271 elog(ERROR, "cannot create duplicate shared record typmod");
2272 typmod_table_entry->typmod = tupdesc->tdtypmod;
2273 typmod_table_entry->shared_tupdesc = shared_dp;
2275
2276 /* Insert into the record table. */
2277 record_table_key.shared = false;
2278 record_table_key.u.local_tupdesc = tupdesc;
2281 &found);
2282 if (!found)
2283 {
2284 record_table_entry->key.shared = true;
2285 record_table_entry->key.u.shared_tupdesc = shared_dp;
2286 }
2288 }
2289
2290 /*
2291 * Set up the global state that will tell assign_record_type_typmod and
2292 * lookup_rowtype_tupdesc_internal about the shared registry.
2293 */
2297
2298 /*
2299 * We install a detach hook in the leader, but only to handle cleanup on
2300 * failure during GetSessionDsmHandle(). Once GetSessionDsmHandle() pins
2301 * the memory, the leader process will use a shared registry until it
2302 * exits.
2303 */
2305}

References Assert, CurrentSession, dshash_create(), dshash_find_or_insert, dshash_get_hash_table_handle(), dshash_release_lock(), elog, ERROR, fb(), IsParallelWorker, MemoryContextSwitchTo(), NextRecordTypmod, on_dsm_detach(), pg_atomic_init_u32(), RecordCacheArray, share_tupledesc(), Session::shared_record_table, shared_record_typmod_registry_detach(), Session::shared_typmod_registry, Session::shared_typmod_table, srtr_record_table_params, srtr_typmod_table_params, TupleDescData::tdtypmod, TopMemoryContext, and RecordCacheArrayEntry::tupdesc.

Referenced by GetSessionDsmHandle().

◆ type_cache_syshash()

static uint32 type_cache_syshash ( const void key,
Size  keysize 
)
static

Definition at line 362 of file typcache.c.

363{
364 Assert(keysize == sizeof(Oid));
365 return GetSysCacheHashValue1(TYPEOID, ObjectIdGetDatum(*(const Oid *) key));
366}

References Assert, fb(), GetSysCacheHashValue1, and ObjectIdGetDatum().

Referenced by lookup_type_cache().

◆ TypeCacheConstrCallback()

static void TypeCacheConstrCallback ( Datum  arg,
SysCacheIdentifier  cacheid,
uint32  hashvalue 
)
static

Definition at line 2629 of file typcache.c.

2630{
2631 TypeCacheEntry *typentry;
2632
2633 /*
2634 * Because this is called very frequently, and typically very few of the
2635 * typcache entries are for domains, we don't use hash_seq_search here.
2636 * Instead we thread all the domain-type entries together so that we can
2637 * visit them cheaply.
2638 */
2639 for (typentry = firstDomainTypeEntry;
2640 typentry != NULL;
2641 typentry = typentry->nextDomain)
2642 {
2643 /* Reset domain constraint validity information */
2645 }
2646}

References fb(), firstDomainTypeEntry, TypeCacheEntry::flags, and TypeCacheEntry::nextDomain.

Referenced by lookup_type_cache().

◆ TypeCacheOpcCallback()

static void TypeCacheOpcCallback ( Datum  arg,
SysCacheIdentifier  cacheid,
uint32  hashvalue 
)
static

Definition at line 2591 of file typcache.c.

2592{
2593 HASH_SEQ_STATUS status;
2594 TypeCacheEntry *typentry;
2595
2596 /* TypeCacheHash must exist, else this callback wouldn't be registered */
2597 hash_seq_init(&status, TypeCacheHash);
2598 while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2599 {
2600 bool hadOpclass = (typentry->flags & TCFLAGS_OPERATOR_FLAGS);
2601
2602 /* Reset equality/comparison/hashing validity information */
2603 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2604
2605 /*
2606 * Call delete_rel_type_cache_if_needed() if we actually cleared some
2607 * of TCFLAGS_OPERATOR_FLAGS.
2608 */
2609 if (hadOpclass)
2611 }
2612}

References delete_rel_type_cache_if_needed(), fb(), TypeCacheEntry::flags, hash_seq_init(), hash_seq_search(), TCFLAGS_OPERATOR_FLAGS, and TypeCacheHash.

Referenced by lookup_type_cache().

◆ TypeCacheRelCallback()

static void TypeCacheRelCallback ( Datum  arg,
Oid  relid 
)
static

Definition at line 2438 of file typcache.c.

2439{
2440 TypeCacheEntry *typentry;
2441
2442 /*
2443 * RelIdToTypeIdCacheHash and TypeCacheHash should exist, otherwise this
2444 * callback wouldn't be registered
2445 */
2446 if (OidIsValid(relid))
2447 {
2449
2450 /*
2451 * Find a RelIdToTypeIdCacheHash entry, which should exist as soon as
2452 * corresponding typcache entry has something to clean.
2453 */
2455 &relid,
2456 HASH_FIND, NULL);
2457
2458 if (relentry != NULL)
2459 {
2461 &relentry->composite_typid,
2462 HASH_FIND, NULL);
2463
2464 if (typentry != NULL)
2465 {
2466 Assert(typentry->typtype == TYPTYPE_COMPOSITE);
2467 Assert(relid == typentry->typrelid);
2468
2470 }
2471 }
2472
2473 /*
2474 * Visit all the domain types sequentially. Typically, this shouldn't
2475 * affect performance since domain types are less tended to bloat.
2476 * Domain types are created manually, unlike composite types which are
2477 * automatically created for every temporary table.
2478 */
2479 for (typentry = firstDomainTypeEntry;
2480 typentry != NULL;
2481 typentry = typentry->nextDomain)
2482 {
2483 /*
2484 * If it's domain over composite, reset flags. (We don't bother
2485 * trying to determine whether the specific base type needs a
2486 * reset.) Note that if we haven't determined whether the base
2487 * type is composite, we don't need to reset anything.
2488 */
2490 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2491 }
2492 }
2493 else
2494 {
2495 HASH_SEQ_STATUS status;
2496
2497 /*
2498 * Relid is invalid. By convention, we need to reset all composite
2499 * types in cache. Also, we should reset flags for domain types, and
2500 * we loop over all entries in hash, so, do it in a single scan.
2501 */
2502 hash_seq_init(&status, TypeCacheHash);
2503 while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2504 {
2505 if (typentry->typtype == TYPTYPE_COMPOSITE)
2506 {
2508 }
2509 else if (typentry->typtype == TYPTYPE_DOMAIN)
2510 {
2511 /*
2512 * If it's domain over composite, reset flags. (We don't
2513 * bother trying to determine whether the specific base type
2514 * needs a reset.) Note that if we haven't determined whether
2515 * the base type is composite, we don't need to reset
2516 * anything.
2517 */
2519 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2520 }
2521 }
2522 }
2523}

References Assert, fb(), firstDomainTypeEntry, TypeCacheEntry::flags, HASH_FIND, hash_search(), hash_seq_init(), hash_seq_search(), InvalidateCompositeTypeCacheEntry(), TypeCacheEntry::nextDomain, OidIsValid, RelIdToTypeIdCacheHash, TCFLAGS_DOMAIN_BASE_IS_COMPOSITE, TypeCacheHash, TypeCacheEntry::typrelid, and TypeCacheEntry::typtype.

Referenced by lookup_type_cache().

◆ TypeCacheTypCallback()

static void TypeCacheTypCallback ( Datum  arg,
SysCacheIdentifier  cacheid,
uint32  hashvalue 
)
static

Definition at line 2534 of file typcache.c.

2535{
2536 HASH_SEQ_STATUS status;
2537 TypeCacheEntry *typentry;
2538
2539 /* TypeCacheHash must exist, else this callback wouldn't be registered */
2540
2541 /*
2542 * By convention, zero hash value is passed to the callback as a sign that
2543 * it's time to invalidate the whole cache. See sinval.c, inval.c and
2544 * InvalidateSystemCachesExtended().
2545 */
2546 if (hashvalue == 0)
2547 hash_seq_init(&status, TypeCacheHash);
2548 else
2549 hash_seq_init_with_hash_value(&status, TypeCacheHash, hashvalue);
2550
2551 while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2552 {
2553 bool hadPgTypeData = (typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA);
2554
2555 Assert(hashvalue == 0 || typentry->type_id_hash == hashvalue);
2556
2557 /*
2558 * Mark the data obtained directly from pg_type as invalid. Also, if
2559 * it's a domain, typnotnull might've changed, so we'll need to
2560 * recalculate its constraints.
2561 */
2562 typentry->flags &= ~(TCFLAGS_HAVE_PG_TYPE_DATA |
2564
2565 /*
2566 * Call delete_rel_type_cache_if_needed() if we cleaned
2567 * TCFLAGS_HAVE_PG_TYPE_DATA flag previously.
2568 */
2569 if (hadPgTypeData)
2571 }
2572}

References Assert, delete_rel_type_cache_if_needed(), fb(), TypeCacheEntry::flags, hash_seq_init(), hash_seq_init_with_hash_value(), hash_seq_search(), TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS, TCFLAGS_HAVE_PG_TYPE_DATA, TypeCacheEntry::type_id_hash, and TypeCacheHash.

Referenced by lookup_type_cache().

◆ UpdateDomainConstraintRef()

void UpdateDomainConstraintRef ( DomainConstraintRef ref)

Definition at line 1435 of file typcache.c.

1436{
1437 TypeCacheEntry *typentry = ref->tcache;
1438
1439 /* Make sure typcache entry's data is up to date */
1440 if ((typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
1441 typentry->typtype == TYPTYPE_DOMAIN)
1442 load_domaintype_info(typentry);
1443
1444 /* Transfer to ref object if there's new info, adjusting refcounts */
1445 if (ref->dcc != typentry->domainData)
1446 {
1447 /* Paranoia --- be sure link is nulled before trying to release */
1448 DomainConstraintCache *dcc = ref->dcc;
1449
1450 if (dcc)
1451 {
1452 /*
1453 * Note: we just leak the previous list of executable domain
1454 * constraints. Alternatively, we could keep those in a child
1455 * context of ref->refctx and free that context at this point.
1456 * However, in practice this code path will be taken so seldom
1457 * that the extra bookkeeping for a child context doesn't seem
1458 * worthwhile; we'll just allow a leak for the lifespan of refctx.
1459 */
1460 ref->constraints = NIL;
1461 ref->dcc = NULL;
1462 decr_dcc_refcount(dcc);
1463 }
1464 dcc = typentry->domainData;
1465 if (dcc)
1466 {
1467 ref->dcc = dcc;
1468 dcc->dccRefCount++;
1469 if (ref->need_exprstate)
1470 ref->constraints = prep_domain_constraints(dcc->constraints,
1471 ref->refctx);
1472 else
1473 ref->constraints = dcc->constraints;
1474 }
1475 }
1476}

References DomainConstraintCache::constraints, DomainConstraintCache::dccRefCount, decr_dcc_refcount(), TypeCacheEntry::domainData, fb(), TypeCacheEntry::flags, load_domaintype_info(), NIL, prep_domain_constraints(), TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS, and TypeCacheEntry::typtype.

Referenced by domain_check_input().

Variable Documentation

◆ firstDomainTypeEntry

TypeCacheEntry* firstDomainTypeEntry = NULL
static

Definition at line 96 of file typcache.c.

Referenced by lookup_type_cache(), TypeCacheConstrCallback(), and TypeCacheRelCallback().

◆ in_progress_list

Oid* in_progress_list
static

◆ in_progress_list_len

int in_progress_list_len
static

◆ in_progress_list_maxlen

int in_progress_list_maxlen
static

Definition at line 228 of file typcache.c.

Referenced by lookup_type_cache().

◆ NextRecordTypmod

int32 NextRecordTypmod = 0
static

◆ RecordCacheArray

◆ RecordCacheArrayLen

int32 RecordCacheArrayLen = 0
static

◆ RecordCacheHash

HTAB* RecordCacheHash = NULL
static

Definition at line 295 of file typcache.c.

Referenced by assign_record_type_typmod().

◆ RelIdToTypeIdCacheHash

HTAB* RelIdToTypeIdCacheHash = NULL
static

◆ srtr_record_table_params

◆ srtr_typmod_table_params

◆ tupledesc_id_counter

◆ TypeCacheHash