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 (in_progress_list == NULL)
395 {
396 /* First time through: initialize the hash table */
397 HASHCTL ctl;
398 int allocsize;
399
400 if (TypeCacheHash == NULL)
401 {
402 ctl.keysize = sizeof(Oid);
403 ctl.entrysize = sizeof(TypeCacheEntry);
404
405 /*
406 * TypeCacheEntry takes hash value from the system cache. For
407 * TypeCacheHash we use the same hash in order to speedup search
408 * by hash value. This is used by hash_seq_init_with_hash_value().
409 */
410 ctl.hash = type_cache_syshash;
411
412 TypeCacheHash = hash_create("Type information cache", 64,
414 }
415
417 {
418 ctl.keysize = sizeof(Oid);
419 ctl.entrysize = sizeof(RelIdToTypeIdCacheEntry);
420 RelIdToTypeIdCacheHash = hash_create("Map from relid to OID of cached composite type", 64,
422 }
423
424 /* Also make sure CacheMemoryContext exists */
427
428 /*
429 * Reserve enough in_progress_list slots for many cases. This is the
430 * last allocation on purpose, done after the two others.
431 */
432 allocsize = 4;
435 allocsize * sizeof(*in_progress_list));
436 in_progress_list_maxlen = allocsize;
437
438 /*
439 * Set up callbacks for SI invalidations. These steps are done last,
440 * once all the other initializations are done, and can fail only with
441 * a FATAL error.
442 */
447 }
448
450
451 /* Register to catch invalidation messages */
453 {
454 int allocsize;
455
456 allocsize = in_progress_list_maxlen * 2;
458 allocsize * sizeof(*in_progress_list));
459 in_progress_list_maxlen = allocsize;
460 }
463
464 /* Try to look up an existing entry */
466 &type_id,
467 HASH_FIND, NULL);
468 if (typentry == NULL)
469 {
470 /*
471 * If we didn't find one, we want to make one. But first look up the
472 * pg_type row, just to make sure we don't make a cache entry for an
473 * invalid type OID. If the type OID is not valid, present a
474 * user-facing error, since some code paths such as domain_in() allow
475 * this function to be reached with a user-supplied OID.
476 */
477 HeapTuple tp;
479
481 if (!HeapTupleIsValid(tp))
484 errmsg("type with OID %u does not exist", type_id)));
486 if (!typtup->typisdefined)
489 errmsg("type \"%s\" is only a shell",
490 NameStr(typtup->typname))));
491
492 /* Now make the typcache entry */
494 &type_id,
495 HASH_ENTER, &found);
496 Assert(!found); /* it wasn't there a moment ago */
497
498 MemSet(typentry, 0, sizeof(TypeCacheEntry));
499
500 /* These fields can never change, by definition */
501 typentry->type_id = type_id;
502 typentry->type_id_hash = get_hash_value(TypeCacheHash, &type_id);
503
504 /* Keep this part in sync with the code below */
505 typentry->typlen = typtup->typlen;
506 typentry->typbyval = typtup->typbyval;
507 typentry->typalign = typtup->typalign;
508 typentry->typstorage = typtup->typstorage;
509 typentry->typtype = typtup->typtype;
510 typentry->typrelid = typtup->typrelid;
511 typentry->typsubscript = typtup->typsubscript;
512 typentry->typelem = typtup->typelem;
513 typentry->typarray = typtup->typarray;
514 typentry->typcollation = typtup->typcollation;
515 typentry->flags |= TCFLAGS_HAVE_PG_TYPE_DATA;
516
517 /* If it's a domain, immediately thread it into the domain cache list */
518 if (typentry->typtype == TYPTYPE_DOMAIN)
519 {
521 firstDomainTypeEntry = typentry;
522 }
523
524 ReleaseSysCache(tp);
525 }
526 else if (!(typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA))
527 {
528 /*
529 * We have an entry, but its pg_type row got changed, so reload the
530 * data obtained directly from pg_type.
531 */
532 HeapTuple tp;
534
536 if (!HeapTupleIsValid(tp))
539 errmsg("type with OID %u does not exist", type_id)));
541 if (!typtup->typisdefined)
544 errmsg("type \"%s\" is only a shell",
545 NameStr(typtup->typname))));
546
547 /*
548 * Keep this part in sync with the code above. Many of these fields
549 * shouldn't ever change, particularly typtype, but copy 'em anyway.
550 */
551 typentry->typlen = typtup->typlen;
552 typentry->typbyval = typtup->typbyval;
553 typentry->typalign = typtup->typalign;
554 typentry->typstorage = typtup->typstorage;
555 typentry->typtype = typtup->typtype;
556 typentry->typrelid = typtup->typrelid;
557 typentry->typsubscript = typtup->typsubscript;
558 typentry->typelem = typtup->typelem;
559 typentry->typarray = typtup->typarray;
560 typentry->typcollation = typtup->typcollation;
561 typentry->flags |= TCFLAGS_HAVE_PG_TYPE_DATA;
562
563 ReleaseSysCache(tp);
564 }
565
566 /*
567 * Look up opclasses if we haven't already and any dependent info is
568 * requested.
569 */
575 {
576 Oid opclass;
577
578 opclass = GetDefaultOpClass(type_id, BTREE_AM_OID);
579 if (OidIsValid(opclass))
580 {
581 typentry->btree_opf = get_opclass_family(opclass);
582 typentry->btree_opintype = get_opclass_input_type(opclass);
583 }
584 else
585 {
586 typentry->btree_opf = typentry->btree_opintype = InvalidOid;
587 }
588
589 /*
590 * Reset information derived from btree opclass. Note in particular
591 * that we'll redetermine the eq_opr even if we previously found one;
592 * this matters in case a btree opclass has been added to a type that
593 * previously had only a hash opclass.
594 */
595 typentry->flags &= ~(TCFLAGS_CHECKED_EQ_OPR |
600 }
601
602 /*
603 * If we need to look up equality operator, and there's no btree opclass,
604 * force lookup of hash opclass.
605 */
606 if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
607 !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR) &&
608 typentry->btree_opf == InvalidOid)
610
615 !(typentry->flags & TCFLAGS_CHECKED_HASH_OPCLASS))
616 {
617 Oid opclass;
618
619 opclass = GetDefaultOpClass(type_id, HASH_AM_OID);
620 if (OidIsValid(opclass))
621 {
622 typentry->hash_opf = get_opclass_family(opclass);
623 typentry->hash_opintype = get_opclass_input_type(opclass);
624 }
625 else
626 {
627 typentry->hash_opf = typentry->hash_opintype = InvalidOid;
628 }
629
630 /*
631 * Reset information derived from hash opclass. We do *not* reset the
632 * eq_opr; if we already found one from the btree opclass, that
633 * decision is still good.
634 */
635 typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC |
638 }
639
640 /*
641 * Look for requested operators and functions, if we haven't already.
642 */
643 if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
644 !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR))
645 {
646 Oid eq_opr = InvalidOid;
647
648 if (typentry->btree_opf != InvalidOid)
649 eq_opr = get_opfamily_member(typentry->btree_opf,
650 typentry->btree_opintype,
651 typentry->btree_opintype,
653 if (eq_opr == InvalidOid &&
654 typentry->hash_opf != InvalidOid)
655 eq_opr = get_opfamily_member(typentry->hash_opf,
656 typentry->hash_opintype,
657 typentry->hash_opintype,
659
660 /*
661 * If the proposed equality operator is array_eq or record_eq, check
662 * to see if the element type or column types support equality. If
663 * not, array_eq or record_eq would fail at runtime, so we don't want
664 * to report that the type has equality. (We can omit similar
665 * checking for ranges and multiranges because ranges can't be created
666 * in the first place unless their subtypes support equality.)
667 */
668 if (eq_opr == ARRAY_EQ_OP &&
670 eq_opr = InvalidOid;
671 else if (eq_opr == RECORD_EQ_OP &&
673 eq_opr = InvalidOid;
674
675 /* Force update of eq_opr_finfo only if we're changing state */
676 if (typentry->eq_opr != eq_opr)
677 typentry->eq_opr_finfo.fn_oid = InvalidOid;
678
679 typentry->eq_opr = eq_opr;
680
681 /*
682 * Reset info about hash functions whenever we pick up new info about
683 * equality operator. This is so we can ensure that the hash
684 * functions match the operator.
685 */
686 typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC |
688 typentry->flags |= TCFLAGS_CHECKED_EQ_OPR;
689 }
690 if ((flags & TYPECACHE_LT_OPR) &&
691 !(typentry->flags & TCFLAGS_CHECKED_LT_OPR))
692 {
693 Oid lt_opr = InvalidOid;
694
695 if (typentry->btree_opf != InvalidOid)
696 lt_opr = get_opfamily_member(typentry->btree_opf,
697 typentry->btree_opintype,
698 typentry->btree_opintype,
700
701 /*
702 * As above, make sure array_cmp or record_cmp will succeed; but again
703 * we need no special check for ranges or multiranges.
704 */
705 if (lt_opr == ARRAY_LT_OP &&
706 !array_element_has_compare(typentry))
707 lt_opr = InvalidOid;
708 else if (lt_opr == RECORD_LT_OP &&
710 lt_opr = InvalidOid;
711
712 typentry->lt_opr = lt_opr;
713 typentry->flags |= TCFLAGS_CHECKED_LT_OPR;
714 }
715 if ((flags & TYPECACHE_GT_OPR) &&
716 !(typentry->flags & TCFLAGS_CHECKED_GT_OPR))
717 {
718 Oid gt_opr = InvalidOid;
719
720 if (typentry->btree_opf != InvalidOid)
721 gt_opr = get_opfamily_member(typentry->btree_opf,
722 typentry->btree_opintype,
723 typentry->btree_opintype,
725
726 /*
727 * As above, make sure array_cmp or record_cmp will succeed; but again
728 * we need no special check for ranges or multiranges.
729 */
730 if (gt_opr == ARRAY_GT_OP &&
731 !array_element_has_compare(typentry))
732 gt_opr = InvalidOid;
733 else if (gt_opr == RECORD_GT_OP &&
735 gt_opr = InvalidOid;
736
737 typentry->gt_opr = gt_opr;
738 typentry->flags |= TCFLAGS_CHECKED_GT_OPR;
739 }
741 !(typentry->flags & TCFLAGS_CHECKED_CMP_PROC))
742 {
743 Oid cmp_proc = InvalidOid;
744
745 if (typentry->btree_opf != InvalidOid)
746 cmp_proc = get_opfamily_proc(typentry->btree_opf,
747 typentry->btree_opintype,
748 typentry->btree_opintype,
750
751 /*
752 * As above, make sure array_cmp or record_cmp will succeed; but again
753 * we need no special check for ranges or multiranges.
754 */
755 if (cmp_proc == F_BTARRAYCMP &&
756 !array_element_has_compare(typentry))
757 cmp_proc = InvalidOid;
758 else if (cmp_proc == F_BTRECORDCMP &&
760 cmp_proc = InvalidOid;
761
762 /* Force update of cmp_proc_finfo only if we're changing state */
763 if (typentry->cmp_proc != cmp_proc)
764 typentry->cmp_proc_finfo.fn_oid = InvalidOid;
765
766 typentry->cmp_proc = cmp_proc;
767 typentry->flags |= TCFLAGS_CHECKED_CMP_PROC;
768 }
770 !(typentry->flags & TCFLAGS_CHECKED_HASH_PROC))
771 {
772 Oid hash_proc = InvalidOid;
773
774 /*
775 * We insist that the eq_opr, if one has been determined, match the
776 * hash opclass; else report there is no hash function.
777 */
778 if (typentry->hash_opf != InvalidOid &&
779 (!OidIsValid(typentry->eq_opr) ||
780 typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
781 typentry->hash_opintype,
782 typentry->hash_opintype,
784 hash_proc = get_opfamily_proc(typentry->hash_opf,
785 typentry->hash_opintype,
786 typentry->hash_opintype,
788
789 /*
790 * As above, make sure hash_array, hash_record, hash_range, or
791 * hash_multirange will succeed. Here we do need to check the range
792 * cases.
793 */
794 if (hash_proc == F_HASH_ARRAY &&
795 !array_element_has_hashing(typentry))
796 hash_proc = InvalidOid;
797 else if (hash_proc == F_HASH_RECORD &&
799 hash_proc = InvalidOid;
800 else if (hash_proc == F_HASH_RANGE &&
801 !range_element_has_hashing(typentry))
802 hash_proc = InvalidOid;
803 else if (hash_proc == F_HASH_MULTIRANGE &&
805 hash_proc = InvalidOid;
806
807 /* Force update of hash_proc_finfo only if we're changing state */
808 if (typentry->hash_proc != hash_proc)
810
811 typentry->hash_proc = hash_proc;
812 typentry->flags |= TCFLAGS_CHECKED_HASH_PROC;
813 }
814 if ((flags & (TYPECACHE_HASH_EXTENDED_PROC |
817 {
818 Oid hash_extended_proc = InvalidOid;
819
820 /*
821 * We insist that the eq_opr, if one has been determined, match the
822 * hash opclass; else report there is no hash function.
823 */
824 if (typentry->hash_opf != InvalidOid &&
825 (!OidIsValid(typentry->eq_opr) ||
826 typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
827 typentry->hash_opintype,
828 typentry->hash_opintype,
830 hash_extended_proc = get_opfamily_proc(typentry->hash_opf,
831 typentry->hash_opintype,
832 typentry->hash_opintype,
834
835 /*
836 * As above, make sure hash_array_extended, hash_record_extended,
837 * hash_range_extended, or hash_multirange_extended will succeed.
838 */
839 if (hash_extended_proc == F_HASH_ARRAY_EXTENDED &&
841 hash_extended_proc = InvalidOid;
842 else if (hash_extended_proc == F_HASH_RECORD_EXTENDED &&
844 hash_extended_proc = InvalidOid;
845 else if (hash_extended_proc == F_HASH_RANGE_EXTENDED &&
847 hash_extended_proc = InvalidOid;
848 else if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
850 hash_extended_proc = InvalidOid;
851
852 /* Force update of proc finfo only if we're changing state */
853 if (typentry->hash_extended_proc != hash_extended_proc)
855
856 typentry->hash_extended_proc = hash_extended_proc;
858 }
859
860 /*
861 * Set up fmgr lookup info as requested
862 *
863 * Note: we tell fmgr the finfo structures live in CacheMemoryContext,
864 * which is not quite right (they're really in the hash table's private
865 * memory context) but this will do for our purposes.
866 *
867 * Note: the code above avoids invalidating the finfo structs unless the
868 * referenced operator/function OID actually changes. This is to prevent
869 * unnecessary leakage of any subsidiary data attached to an finfo, since
870 * that would cause session-lifespan memory leaks.
871 */
872 if ((flags & TYPECACHE_EQ_OPR_FINFO) &&
873 typentry->eq_opr_finfo.fn_oid == InvalidOid &&
874 typentry->eq_opr != InvalidOid)
875 {
877
878 eq_opr_func = get_opcode(typentry->eq_opr);
879 if (eq_opr_func != InvalidOid)
882 }
883 if ((flags & TYPECACHE_CMP_PROC_FINFO) &&
884 typentry->cmp_proc_finfo.fn_oid == InvalidOid &&
885 typentry->cmp_proc != InvalidOid)
886 {
887 fmgr_info_cxt(typentry->cmp_proc, &typentry->cmp_proc_finfo,
889 }
890 if ((flags & TYPECACHE_HASH_PROC_FINFO) &&
891 typentry->hash_proc_finfo.fn_oid == InvalidOid &&
892 typentry->hash_proc != InvalidOid)
893 {
894 fmgr_info_cxt(typentry->hash_proc, &typentry->hash_proc_finfo,
896 }
899 typentry->hash_extended_proc != InvalidOid)
900 {
902 &typentry->hash_extended_proc_finfo,
904 }
905
906 /*
907 * If it's a composite type (row type), get tupdesc if requested
908 */
909 if ((flags & TYPECACHE_TUPDESC) &&
910 typentry->tupDesc == NULL &&
911 typentry->typtype == TYPTYPE_COMPOSITE)
912 {
913 load_typcache_tupdesc(typentry);
914 }
915
916 /*
917 * If requested, get information about a range type
918 *
919 * This includes making sure that the basic info about the range element
920 * type is up-to-date.
921 */
922 if ((flags & TYPECACHE_RANGE_INFO) &&
923 typentry->typtype == TYPTYPE_RANGE)
924 {
925 if (typentry->rngelemtype == NULL)
926 load_rangetype_info(typentry);
927 else if (!(typentry->rngelemtype->flags & TCFLAGS_HAVE_PG_TYPE_DATA))
928 (void) lookup_type_cache(typentry->rngelemtype->type_id, 0);
929 }
930
931 /*
932 * If requested, get information about a multirange type
933 */
934 if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
935 typentry->rngtype == NULL &&
936 typentry->typtype == TYPTYPE_MULTIRANGE)
937 {
938 load_multirangetype_info(typentry);
939 }
940
941 /*
942 * If requested, get information about a domain type
943 */
944 if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
945 typentry->domainBaseType == InvalidOid &&
946 typentry->typtype == TYPTYPE_DOMAIN)
947 {
948 typentry->domainBaseTypmod = -1;
949 typentry->domainBaseType =
950 getBaseTypeAndTypmod(type_id, &typentry->domainBaseTypmod);
951 }
952 if ((flags & TYPECACHE_DOMAIN_CONSTR_INFO) &&
953 (typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
954 typentry->typtype == TYPTYPE_DOMAIN)
955 {
956 load_domaintype_info(typentry);
957 }
958
959 INJECTION_POINT("typecache-before-rel-type-cache-insert", NULL);
960
963
965
966 return typentry;
967}
968
969/*
970 * load_typcache_tupdesc --- helper routine to set up composite type's tupDesc
971 */
972static void
974{
975 Relation rel;
976
977 if (!OidIsValid(typentry->typrelid)) /* should not happen */
978 elog(ERROR, "invalid typrelid for composite type %u",
979 typentry->type_id);
980 rel = relation_open(typentry->typrelid, AccessShareLock);
981 Assert(rel->rd_rel->reltype == typentry->type_id);
982
983 /*
984 * Link to the tupdesc and increment its refcount (we assert it's a
985 * refcounted descriptor). We don't use IncrTupleDescRefCount() for this,
986 * because the reference mustn't be entered in the current resource owner;
987 * it can outlive the current query.
988 */
989 typentry->tupDesc = RelationGetDescr(rel);
990
991 Assert(typentry->tupDesc->tdrefcount > 0);
992 typentry->tupDesc->tdrefcount++;
993
994 /*
995 * In future, we could take some pains to not change tupDesc_identifier if
996 * the tupdesc didn't really change; but for now it's not worth it.
997 */
999
1001}
1002
1003/*
1004 * load_rangetype_info --- helper routine to set up range type information
1005 */
1006static void
1008{
1010 HeapTuple tup;
1016 Oid opcintype;
1017 Oid cmpFnOid;
1018
1019 /* get information from pg_range */
1021 /* should not fail, since we already checked typtype ... */
1022 if (!HeapTupleIsValid(tup))
1023 elog(ERROR, "cache lookup failed for range type %u",
1024 typentry->type_id);
1026
1027 subtypeOid = pg_range->rngsubtype;
1028 typentry->rng_collation = pg_range->rngcollation;
1029 opclassOid = pg_range->rngsubopc;
1030 canonicalOid = pg_range->rngcanonical;
1031 subdiffOid = pg_range->rngsubdiff;
1032
1034
1035 /* get opclass properties and look up the comparison function */
1038 typentry->rng_opfamily = opfamilyOid;
1039
1040 cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype,
1041 BTORDER_PROC);
1043 elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
1044 BTORDER_PROC, opcintype, opcintype, opfamilyOid);
1045
1046 /* set up cached fmgrinfo structs */
1055
1056 /* Lastly, set up link to the element type --- this marks data valid */
1058}
1059
1060/*
1061 * load_multirangetype_info --- helper routine to set up multirange type
1062 * information
1063 */
1064static void
1066{
1068
1071 elog(ERROR, "cache lookup failed for multirange type %u",
1072 typentry->type_id);
1073
1075}
1076
1077/*
1078 * load_domaintype_info --- helper routine to set up domain constraint info
1079 *
1080 * Note: we assume we're called in a relatively short-lived context, so it's
1081 * okay to leak data into the current context while scanning pg_constraint.
1082 * We build the new DomainConstraintCache data in a context underneath
1083 * CurrentMemoryContext, and reparent it under CacheMemoryContext when
1084 * complete.
1085 */
1086static void
1088{
1089 Oid typeOid = typentry->type_id;
1091 bool notNull = false;
1093 int cconslen;
1096
1097 /*
1098 * If we're here, any existing constraint info is stale, so release it.
1099 * For safety, be sure to null the link before trying to delete the data.
1100 */
1101 if (typentry->domainData)
1102 {
1103 dcc = typentry->domainData;
1104 typentry->domainData = NULL;
1105 decr_dcc_refcount(dcc);
1106 }
1107
1108 /*
1109 * We try to optimize the common case of no domain constraints, so don't
1110 * create the dcc object and context until we find a constraint. Likewise
1111 * for the temp sorting array.
1112 */
1113 dcc = NULL;
1114 ccons = NULL;
1115 cconslen = 0;
1116
1117 /*
1118 * Scan pg_constraint for relevant constraints. We want to find
1119 * constraints for not just this domain, but any ancestor domains, so the
1120 * outer loop crawls up the domain stack.
1121 */
1123
1124 for (;;)
1125 {
1126 HeapTuple tup;
1129 int nccons = 0;
1130 ScanKeyData key[1];
1131 SysScanDesc scan;
1132
1134 if (!HeapTupleIsValid(tup))
1135 elog(ERROR, "cache lookup failed for type %u", typeOid);
1137
1138 if (typTup->typtype != TYPTYPE_DOMAIN)
1139 {
1140 /* Not a domain, so done */
1142 break;
1143 }
1144
1145 /* Test for NOT NULL Constraint */
1146 if (typTup->typnotnull)
1147 notNull = true;
1148
1149 /* Look for CHECK Constraints on this domain */
1150 ScanKeyInit(&key[0],
1153 ObjectIdGetDatum(typeOid));
1154
1156 NULL, 1, key);
1157
1159 {
1161 Datum val;
1162 bool isNull;
1163 char *constring;
1164 Expr *check_expr;
1166
1167 /* Ignore non-CHECK constraints */
1168 if (c->contype != CONSTRAINT_CHECK)
1169 continue;
1170
1171 /* Not expecting conbin to be NULL, but we'll test for it anyway */
1173 conRel->rd_att, &isNull);
1174 if (isNull)
1175 elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
1176 NameStr(typTup->typname), NameStr(c->conname));
1177
1178 /* Create the DomainConstraintCache object and context if needed */
1179 if (dcc == NULL)
1180 {
1181 MemoryContext cxt;
1182
1184 "Domain constraints",
1186 dcc = (DomainConstraintCache *)
1188 dcc->constraints = NIL;
1189 dcc->dccContext = cxt;
1190 dcc->dccRefCount = 0;
1191 }
1192
1193 /* Convert conbin to a node tree, still in caller's context */
1195 check_expr = (Expr *) stringToNode(constring);
1196
1197 /*
1198 * Plan the expression, since ExecInitExpr will expect that.
1199 *
1200 * Note: caching the result of expression_planner() is not very
1201 * good practice. Ideally we'd use a CachedExpression here so
1202 * that we would react promptly to, eg, changes in inlined
1203 * functions. However, because we don't support mutable domain
1204 * CHECK constraints, it's not really clear that it's worth the
1205 * extra overhead to do that.
1206 */
1207 check_expr = expression_planner(check_expr);
1208
1209 /* Create only the minimally needed stuff in dccContext */
1211
1214 r->name = pstrdup(NameStr(c->conname));
1215 r->check_expr = copyObject(check_expr);
1216 r->check_exprstate = NULL;
1217
1219
1220 /* Accumulate constraints in an array, for sorting below */
1221 if (ccons == NULL)
1222 {
1223 cconslen = 8;
1226 }
1227 else if (nccons >= cconslen)
1228 {
1229 cconslen *= 2;
1232 }
1233 ccons[nccons++] = r;
1234 }
1235
1236 systable_endscan(scan);
1237
1238 if (nccons > 0)
1239 {
1240 /*
1241 * Sort the items for this domain, so that CHECKs are applied in a
1242 * deterministic order.
1243 */
1244 if (nccons > 1)
1246
1247 /*
1248 * Now attach them to the overall list. Use lcons() here because
1249 * constraints of parent domains should be applied earlier.
1250 */
1252 while (nccons > 0)
1253 dcc->constraints = lcons(ccons[--nccons], dcc->constraints);
1255 }
1256
1257 /* loop to next domain in stack */
1258 typeOid = typTup->typbasetype;
1260 }
1261
1263
1264 /*
1265 * Only need to add one NOT NULL check regardless of how many domains in
1266 * the stack request it.
1267 */
1268 if (notNull)
1269 {
1271
1272 /* Create the DomainConstraintCache object and context if needed */
1273 if (dcc == NULL)
1274 {
1275 MemoryContext cxt;
1276
1278 "Domain constraints",
1280 dcc = (DomainConstraintCache *)
1282 dcc->constraints = NIL;
1283 dcc->dccContext = cxt;
1284 dcc->dccRefCount = 0;
1285 }
1286
1287 /* Create node trees in DomainConstraintCache's context */
1289
1291
1293 r->name = pstrdup("NOT NULL");
1294 r->check_expr = NULL;
1295 r->check_exprstate = NULL;
1296
1297 /* lcons to apply the nullness check FIRST */
1298 dcc->constraints = lcons(r, dcc->constraints);
1299
1301 }
1302
1303 /*
1304 * If we made a constraint object, move it into CacheMemoryContext and
1305 * attach it to the typcache entry.
1306 */
1307 if (dcc)
1308 {
1310 typentry->domainData = dcc;
1311 dcc->dccRefCount++; /* count the typcache's reference */
1312 }
1313
1314 /* Either way, the typcache entry's domain data is now valid. */
1316}
1317
1318/*
1319 * qsort comparator to sort DomainConstraintState pointers by name
1320 */
1321static int
1322dcs_cmp(const void *a, const void *b)
1323{
1324 const DomainConstraintState *const *ca = (const DomainConstraintState *const *) a;
1325 const DomainConstraintState *const *cb = (const DomainConstraintState *const *) b;
1326
1327 return strcmp((*ca)->name, (*cb)->name);
1328}
1329
1330/*
1331 * decr_dcc_refcount --- decrement a DomainConstraintCache's refcount,
1332 * and free it if no references remain
1333 */
1334static void
1336{
1337 Assert(dcc->dccRefCount > 0);
1338 if (--(dcc->dccRefCount) <= 0)
1340}
1341
1342/*
1343 * Context reset/delete callback for a DomainConstraintRef
1344 */
1345static void
1347{
1349 DomainConstraintCache *dcc = ref->dcc;
1350
1351 /* Paranoia --- be sure link is nulled before trying to release */
1352 if (dcc)
1353 {
1354 ref->constraints = NIL;
1355 ref->dcc = NULL;
1356 decr_dcc_refcount(dcc);
1357 }
1358}
1359
1360/*
1361 * prep_domain_constraints --- prepare domain constraints for execution
1362 *
1363 * The expression trees stored in the DomainConstraintCache's list are
1364 * converted to executable expression state trees stored in execctx.
1365 */
1366static List *
1368{
1369 List *result = NIL;
1371 ListCell *lc;
1372
1374
1375 foreach(lc, constraints)
1376 {
1379
1381 newr->constrainttype = r->constrainttype;
1382 newr->name = r->name;
1383 newr->check_expr = r->check_expr;
1384 newr->check_exprstate = ExecInitExpr(r->check_expr, NULL);
1385
1387 }
1388
1390
1391 return result;
1392}
1393
1394/*
1395 * InitDomainConstraintRef --- initialize a DomainConstraintRef struct
1396 *
1397 * Caller must tell us the MemoryContext in which the DomainConstraintRef
1398 * lives. The ref will be cleaned up when that context is reset/deleted.
1399 *
1400 * Caller must also tell us whether it wants check_exprstate fields to be
1401 * computed in the DomainConstraintState nodes attached to this ref.
1402 * If it doesn't, we need not make a copy of the DomainConstraintState list.
1403 */
1404void
1406 MemoryContext refctx, bool need_exprstate)
1407{
1408 /* Look up the typcache entry --- we assume it survives indefinitely */
1410 ref->need_exprstate = need_exprstate;
1411 /* For safety, establish the callback before acquiring a refcount */
1412 ref->refctx = refctx;
1413 ref->dcc = NULL;
1414 ref->callback.func = dccref_deletion_callback;
1415 ref->callback.arg = ref;
1416 MemoryContextRegisterResetCallback(refctx, &ref->callback);
1417 /* Acquire refcount if there are constraints, and set up exported list */
1418 if (ref->tcache->domainData)
1419 {
1420 ref->dcc = ref->tcache->domainData;
1421 ref->dcc->dccRefCount++;
1422 if (ref->need_exprstate)
1423 ref->constraints = prep_domain_constraints(ref->dcc->constraints,
1424 ref->refctx);
1425 else
1426 ref->constraints = ref->dcc->constraints;
1427 }
1428 else
1429 ref->constraints = NIL;
1430}
1431
1432/*
1433 * UpdateDomainConstraintRef --- recheck validity of domain constraint info
1434 *
1435 * If the domain's constraint set changed, ref->constraints is updated to
1436 * point at a new list of cached constraints.
1437 *
1438 * In the normal case where nothing happened to the domain, this is cheap
1439 * enough that it's reasonable (and expected) to check before *each* use
1440 * of the constraint info.
1441 */
1442void
1444{
1445 TypeCacheEntry *typentry = ref->tcache;
1446
1447 /* Make sure typcache entry's data is up to date */
1448 if ((typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
1449 typentry->typtype == TYPTYPE_DOMAIN)
1450 load_domaintype_info(typentry);
1451
1452 /* Transfer to ref object if there's new info, adjusting refcounts */
1453 if (ref->dcc != typentry->domainData)
1454 {
1455 /* Paranoia --- be sure link is nulled before trying to release */
1456 DomainConstraintCache *dcc = ref->dcc;
1457
1458 if (dcc)
1459 {
1460 /*
1461 * Note: we just leak the previous list of executable domain
1462 * constraints. Alternatively, we could keep those in a child
1463 * context of ref->refctx and free that context at this point.
1464 * However, in practice this code path will be taken so seldom
1465 * that the extra bookkeeping for a child context doesn't seem
1466 * worthwhile; we'll just allow a leak for the lifespan of refctx.
1467 */
1468 ref->constraints = NIL;
1469 ref->dcc = NULL;
1470 decr_dcc_refcount(dcc);
1471 }
1472 dcc = typentry->domainData;
1473 if (dcc)
1474 {
1475 ref->dcc = dcc;
1476 dcc->dccRefCount++;
1477 if (ref->need_exprstate)
1478 ref->constraints = prep_domain_constraints(dcc->constraints,
1479 ref->refctx);
1480 else
1481 ref->constraints = dcc->constraints;
1482 }
1483 }
1484}
1485
1486/*
1487 * DomainHasConstraints --- utility routine to check if a domain has constraints
1488 *
1489 * Returns true if the domain has any constraints at all. If has_volatile
1490 * is not NULL, also checks whether any CHECK constraint contains a volatile
1491 * expression and sets *has_volatile accordingly.
1492 *
1493 * This is defined to return false, not fail, if type is not a domain.
1494 */
1495bool
1497{
1498 TypeCacheEntry *typentry;
1499
1500 /*
1501 * Note: a side effect is to cause the typcache's domain data to become
1502 * valid. This is fine since we'll likely need it soon if there is any.
1503 */
1505
1506 if (typentry->domainData == NULL)
1507 return false;
1508
1509 if (has_volatile)
1510 {
1511 *has_volatile = false;
1512
1514 typentry->domainData->constraints)
1515 {
1516 if (constrstate->constrainttype == DOM_CONSTRAINT_CHECK &&
1518 {
1519 *has_volatile = true;
1520 break;
1521 }
1522 }
1523 }
1524
1525 return true;
1526}
1527
1528
1529/*
1530 * array_element_has_equality and friends are helper routines to check
1531 * whether we should believe that array_eq and related functions will work
1532 * on the given array type or composite type.
1533 *
1534 * The logic above may call these repeatedly on the same type entry, so we
1535 * make use of the typentry->flags field to cache the results once known.
1536 * Also, we assume that we'll probably want all these facts about the type
1537 * if we want any, so we cache them all using only one lookup of the
1538 * component datatype(s).
1539 */
1540
1541static bool
1543{
1544 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1546 return (typentry->flags & TCFLAGS_HAVE_ELEM_EQUALITY) != 0;
1547}
1548
1549static bool
1551{
1552 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1554 return (typentry->flags & TCFLAGS_HAVE_ELEM_COMPARE) != 0;
1555}
1556
1557static bool
1559{
1560 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1562 return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1563}
1564
1565static bool
1567{
1568 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1570 return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
1571}
1572
1573static void
1575{
1577
1578 if (OidIsValid(elem_type))
1579 {
1581
1587 if (OidIsValid(elementry->eq_opr))
1588 typentry->flags |= TCFLAGS_HAVE_ELEM_EQUALITY;
1589 if (OidIsValid(elementry->cmp_proc))
1590 typentry->flags |= TCFLAGS_HAVE_ELEM_COMPARE;
1591 if (OidIsValid(elementry->hash_proc))
1592 typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1593 if (OidIsValid(elementry->hash_extended_proc))
1595 }
1597}
1598
1599/*
1600 * Likewise, some helper functions for composite types.
1601 */
1602
1603static bool
1605{
1606 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1608 return (typentry->flags & TCFLAGS_HAVE_FIELD_EQUALITY) != 0;
1609}
1610
1611static bool
1613{
1614 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1616 return (typentry->flags & TCFLAGS_HAVE_FIELD_COMPARE) != 0;
1617}
1618
1619static bool
1621{
1622 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1624 return (typentry->flags & TCFLAGS_HAVE_FIELD_HASHING) != 0;
1625}
1626
1627static bool
1629{
1630 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1632 return (typentry->flags & TCFLAGS_HAVE_FIELD_EXTENDED_HASHING) != 0;
1633}
1634
1635static void
1637{
1638 /*
1639 * For type RECORD, we can't really tell what will work, since we don't
1640 * have access here to the specific anonymous type. Just assume that
1641 * equality and comparison will (we may get a failure at runtime). We
1642 * could also claim that hashing works, but then if code that has the
1643 * option between a comparison-based (sort-based) and a hash-based plan
1644 * chooses hashing, stuff could fail that would otherwise work if it chose
1645 * a comparison-based plan. In practice more types support comparison
1646 * than hashing.
1647 */
1648 if (typentry->type_id == RECORDOID)
1649 {
1650 typentry->flags |= (TCFLAGS_HAVE_FIELD_EQUALITY |
1652 }
1653 else if (typentry->typtype == TYPTYPE_COMPOSITE)
1654 {
1655 TupleDesc tupdesc;
1656 int newflags;
1657 int i;
1658
1659 /* Fetch composite type's tupdesc if we don't have it already */
1660 if (typentry->tupDesc == NULL)
1661 load_typcache_tupdesc(typentry);
1662 tupdesc = typentry->tupDesc;
1663
1664 /* Must bump the refcount while we do additional catalog lookups */
1665 IncrTupleDescRefCount(tupdesc);
1666
1667 /* Have each property if all non-dropped fields have the property */
1672 for (i = 0; i < tupdesc->natts; i++)
1673 {
1675 Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
1676
1677 if (attr->attisdropped)
1678 continue;
1679
1680 fieldentry = lookup_type_cache(attr->atttypid,
1685 if (!OidIsValid(fieldentry->eq_opr))
1687 if (!OidIsValid(fieldentry->cmp_proc))
1689 if (!OidIsValid(fieldentry->hash_proc))
1691 if (!OidIsValid(fieldentry->hash_extended_proc))
1693
1694 /* We can drop out of the loop once we disprove all bits */
1695 if (newflags == 0)
1696 break;
1697 }
1698 typentry->flags |= newflags;
1699
1700 DecrTupleDescRefCount(tupdesc);
1701 }
1702 else if (typentry->typtype == TYPTYPE_DOMAIN)
1703 {
1704 /* If it's domain over composite, copy base type's properties */
1706
1707 /* load up basetype info if we didn't already */
1708 if (typentry->domainBaseType == InvalidOid)
1709 {
1710 typentry->domainBaseTypmod = -1;
1711 typentry->domainBaseType =
1712 getBaseTypeAndTypmod(typentry->type_id,
1713 &typentry->domainBaseTypmod);
1714 }
1720 if (baseentry->typtype == TYPTYPE_COMPOSITE)
1721 {
1723 typentry->flags |= baseentry->flags & (TCFLAGS_HAVE_FIELD_EQUALITY |
1727 }
1728 }
1730}
1731
1732/*
1733 * Likewise, some helper functions for range and multirange types.
1734 *
1735 * We can borrow the flag bits for array element properties to use for range
1736 * element properties, since those flag bits otherwise have no use in a
1737 * range or multirange type's typcache entry.
1738 */
1739
1740static bool
1742{
1743 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1745 return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1746}
1747
1748static bool
1750{
1751 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1753 return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
1754}
1755
1756static void
1758{
1759 /* load up subtype link if we didn't already */
1760 if (typentry->rngelemtype == NULL &&
1761 typentry->typtype == TYPTYPE_RANGE)
1762 load_rangetype_info(typentry);
1763
1764 if (typentry->rngelemtype != NULL)
1765 {
1767
1768 /* might need to calculate subtype's hash function properties */
1772 if (OidIsValid(elementry->hash_proc))
1773 typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1774 if (OidIsValid(elementry->hash_extended_proc))
1776 }
1778}
1779
1780static bool
1782{
1783 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1785 return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1786}
1787
1788static bool
1790{
1791 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1793 return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
1794}
1795
1796static void
1798{
1799 /* load up range link if we didn't already */
1800 if (typentry->rngtype == NULL &&
1801 typentry->typtype == TYPTYPE_MULTIRANGE)
1802 load_multirangetype_info(typentry);
1803
1804 if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
1805 {
1807
1808 /* might need to calculate subtype's hash function properties */
1812 if (OidIsValid(elementry->hash_proc))
1813 typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1814 if (OidIsValid(elementry->hash_extended_proc))
1816 }
1818}
1819
1820/*
1821 * Make sure that RecordCacheArray and RecordIdentifierArray are large enough
1822 * to store 'typmod'.
1823 */
1824static void
1826{
1827 if (RecordCacheArray == NULL)
1828 {
1831 64 * sizeof(RecordCacheArrayEntry));
1833 }
1834
1835 if (typmod >= RecordCacheArrayLen)
1836 {
1837 int32 newlen = pg_nextpower2_32(typmod + 1);
1838
1842 newlen);
1844 }
1845}
1846
1847/*
1848 * lookup_rowtype_tupdesc_internal --- internal routine to lookup a rowtype
1849 *
1850 * Same API as lookup_rowtype_tupdesc_noerror, but the returned tupdesc
1851 * hasn't had its refcount bumped.
1852 */
1853static TupleDesc
1854lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
1855{
1856 if (type_id != RECORDOID)
1857 {
1858 /*
1859 * It's a named composite type, so use the regular typcache.
1860 */
1861 TypeCacheEntry *typentry;
1862
1863 typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
1864 if (typentry->tupDesc == NULL && !noError)
1865 ereport(ERROR,
1867 errmsg("type %s is not composite",
1868 format_type_be(type_id))));
1869 return typentry->tupDesc;
1870 }
1871 else
1872 {
1873 /*
1874 * It's a transient record type, so look in our record-type table.
1875 */
1876 if (typmod >= 0)
1877 {
1878 /* It is already in our local cache? */
1879 if (typmod < RecordCacheArrayLen &&
1880 RecordCacheArray[typmod].tupdesc != NULL)
1881 return RecordCacheArray[typmod].tupdesc;
1882
1883 /* Are we attached to a shared record typmod registry? */
1885 {
1887
1888 /* Try to find it in the shared typmod index. */
1890 &typmod, false);
1891 if (entry != NULL)
1892 {
1893 TupleDesc tupdesc;
1894
1895 tupdesc = (TupleDesc)
1897 entry->shared_tupdesc);
1898 Assert(typmod == tupdesc->tdtypmod);
1899
1900 /* We may need to extend the local RecordCacheArray. */
1902
1903 /*
1904 * Our local array can now point directly to the TupleDesc
1905 * in shared memory, which is non-reference-counted.
1906 */
1907 RecordCacheArray[typmod].tupdesc = tupdesc;
1908 Assert(tupdesc->tdrefcount == -1);
1909
1910 /*
1911 * We don't share tupdesc identifiers across processes, so
1912 * assign one locally.
1913 */
1915
1917 entry);
1918
1919 return RecordCacheArray[typmod].tupdesc;
1920 }
1921 }
1922 }
1923
1924 if (!noError)
1925 ereport(ERROR,
1927 errmsg("record type has not been registered")));
1928 return NULL;
1929 }
1930}
1931
1932/*
1933 * lookup_rowtype_tupdesc
1934 *
1935 * Given a typeid/typmod that should describe a known composite type,
1936 * return the tuple descriptor for the type. Will ereport on failure.
1937 * (Use ereport because this is reachable with user-specified OIDs,
1938 * for example from record_in().)
1939 *
1940 * Note: on success, we increment the refcount of the returned TupleDesc,
1941 * and log the reference in CurrentResourceOwner. Caller must call
1942 * ReleaseTupleDesc when done using the tupdesc. (There are some
1943 * cases in which the returned tupdesc is not refcounted, in which
1944 * case PinTupleDesc/ReleaseTupleDesc are no-ops; but in these cases
1945 * the tupdesc is guaranteed to live till process exit.)
1946 */
1948lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
1949{
1950 TupleDesc tupDesc;
1951
1952 tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
1953 PinTupleDesc(tupDesc);
1954 return tupDesc;
1955}
1956
1957/*
1958 * lookup_rowtype_tupdesc_noerror
1959 *
1960 * As above, but if the type is not a known composite type and noError
1961 * is true, returns NULL instead of ereport'ing. (Note that if a bogus
1962 * type_id is passed, you'll get an ereport anyway.)
1963 */
1965lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
1966{
1967 TupleDesc tupDesc;
1968
1969 tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
1970 if (tupDesc != NULL)
1971 PinTupleDesc(tupDesc);
1972 return tupDesc;
1973}
1974
1975/*
1976 * lookup_rowtype_tupdesc_copy
1977 *
1978 * Like lookup_rowtype_tupdesc(), but the returned TupleDesc has been
1979 * copied into the CurrentMemoryContext and is not reference-counted.
1980 */
1982lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod)
1983{
1984 TupleDesc tmp;
1985
1986 tmp = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
1987 return CreateTupleDescCopyConstr(tmp);
1988}
1989
1990/*
1991 * lookup_rowtype_tupdesc_domain
1992 *
1993 * Same as lookup_rowtype_tupdesc_noerror(), except that the type can also be
1994 * a domain over a named composite type; so this is effectively equivalent to
1995 * lookup_rowtype_tupdesc_noerror(getBaseType(type_id), typmod, noError)
1996 * except for being a tad faster.
1997 *
1998 * Note: the reason we don't fold the look-through-domain behavior into plain
1999 * lookup_rowtype_tupdesc() is that we want callers to know they might be
2000 * dealing with a domain. Otherwise they might construct a tuple that should
2001 * be of the domain type, but not apply domain constraints.
2002 */
2004lookup_rowtype_tupdesc_domain(Oid type_id, int32 typmod, bool noError)
2005{
2006 TupleDesc tupDesc;
2007
2008 if (type_id != RECORDOID)
2009 {
2010 /*
2011 * Check for domain or named composite type. We might as well load
2012 * whichever data is needed.
2013 */
2014 TypeCacheEntry *typentry;
2015
2016 typentry = lookup_type_cache(type_id,
2019 if (typentry->typtype == TYPTYPE_DOMAIN)
2021 typentry->domainBaseTypmod,
2022 noError);
2023 if (typentry->tupDesc == NULL && !noError)
2024 ereport(ERROR,
2026 errmsg("type %s is not composite",
2027 format_type_be(type_id))));
2028 tupDesc = typentry->tupDesc;
2029 }
2030 else
2031 tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
2032 if (tupDesc != NULL)
2033 PinTupleDesc(tupDesc);
2034 return tupDesc;
2035}
2036
2037/*
2038 * Hash function for the hash table of RecordCacheEntry.
2039 */
2040static uint32
2041record_type_typmod_hash(const void *data, size_t size)
2042{
2043 const RecordCacheEntry *entry = data;
2044
2045 return hashRowType(entry->tupdesc);
2046}
2047
2048/*
2049 * Match function for the hash table of RecordCacheEntry.
2050 */
2051static int
2052record_type_typmod_compare(const void *a, const void *b, size_t size)
2053{
2054 const RecordCacheEntry *left = a;
2055 const RecordCacheEntry *right = b;
2056
2057 return equalRowTypes(left->tupdesc, right->tupdesc) ? 0 : 1;
2058}
2059
2060/*
2061 * assign_record_type_typmod
2062 *
2063 * Given a tuple descriptor for a RECORD type, find or create a cache entry
2064 * for the type, and set the tupdesc's tdtypmod field to a value that will
2065 * identify this cache entry to lookup_rowtype_tupdesc.
2066 */
2067void
2069{
2072 bool found;
2074
2075 Assert(tupDesc->tdtypeid == RECORDOID);
2076
2077 if (RecordCacheHash == NULL)
2078 {
2079 /* First time through: initialize the hash table */
2080 HASHCTL ctl;
2081
2082 ctl.keysize = sizeof(TupleDesc); /* just the pointer */
2083 ctl.entrysize = sizeof(RecordCacheEntry);
2086 RecordCacheHash = hash_create("Record information cache", 64,
2087 &ctl,
2089
2090 /* Also make sure CacheMemoryContext exists */
2091 if (!CacheMemoryContext)
2093 }
2094
2095 /*
2096 * Find a hashtable entry for this tuple descriptor. We don't use
2097 * HASH_ENTER yet, because if it's missing, we need to make sure that all
2098 * the allocations succeed before we create the new entry.
2099 */
2101 &tupDesc,
2102 HASH_FIND, &found);
2103 if (found && recentry->tupdesc != NULL)
2104 {
2105 tupDesc->tdtypmod = recentry->tupdesc->tdtypmod;
2106 return;
2107 }
2108
2109 /* Not present, so need to manufacture an entry */
2111
2112 /* Look in the SharedRecordTypmodRegistry, if attached */
2114 if (entDesc == NULL)
2115 {
2116 /*
2117 * Make sure we have room before we CreateTupleDescCopy() or advance
2118 * NextRecordTypmod.
2119 */
2121
2122 /* Reference-counted local cache only. */
2123 entDesc = CreateTupleDescCopy(tupDesc);
2124 entDesc->tdrefcount = 1;
2125 entDesc->tdtypmod = NextRecordTypmod++;
2126 }
2127 else
2128 {
2130 }
2131
2133
2134 /* Assign a unique tupdesc identifier, too. */
2136
2137 /* Fully initialized; create the hash table entry */
2139 &tupDesc,
2140 HASH_ENTER, NULL);
2141 recentry->tupdesc = entDesc;
2142
2143 /* Update the caller's tuple descriptor. */
2144 tupDesc->tdtypmod = entDesc->tdtypmod;
2145
2147}
2148
2149/*
2150 * assign_record_type_identifier
2151 *
2152 * Get an identifier, which will be unique over the lifespan of this backend
2153 * process, for the current tuple descriptor of the specified composite type.
2154 * For named composite types, the value is guaranteed to change if the type's
2155 * definition does. For registered RECORD types, the value will not change
2156 * once assigned, since the registered type won't either. If an anonymous
2157 * RECORD type is specified, we return a new identifier on each call.
2158 */
2159uint64
2161{
2162 if (type_id != RECORDOID)
2163 {
2164 /*
2165 * It's a named composite type, so use the regular typcache.
2166 */
2167 TypeCacheEntry *typentry;
2168
2169 typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
2170 if (typentry->tupDesc == NULL)
2171 ereport(ERROR,
2173 errmsg("type %s is not composite",
2174 format_type_be(type_id))));
2175 Assert(typentry->tupDesc_identifier != 0);
2176 return typentry->tupDesc_identifier;
2177 }
2178 else
2179 {
2180 /*
2181 * It's a transient record type, so look in our record-type table.
2182 */
2183 if (typmod >= 0 && typmod < RecordCacheArrayLen &&
2184 RecordCacheArray[typmod].tupdesc != NULL)
2185 {
2186 Assert(RecordCacheArray[typmod].id != 0);
2187 return RecordCacheArray[typmod].id;
2188 }
2189
2190 /* For anonymous or unrecognized record type, generate a new ID */
2191 return ++tupledesc_id_counter;
2192 }
2193}
2194
2195/*
2196 * Return the amount of shmem required to hold a SharedRecordTypmodRegistry.
2197 * This exists only to avoid exposing private innards of
2198 * SharedRecordTypmodRegistry in a header.
2199 */
2200size_t
2202{
2203 return sizeof(SharedRecordTypmodRegistry);
2204}
2205
2206/*
2207 * Initialize 'registry' in a pre-existing shared memory region, which must be
2208 * maximally aligned and have space for SharedRecordTypmodRegistryEstimate()
2209 * bytes.
2210 *
2211 * 'area' will be used to allocate shared memory space as required for the
2212 * typemod registration. The current process, expected to be a leader process
2213 * in a parallel query, will be attached automatically and its current record
2214 * types will be loaded into *registry. While attached, all calls to
2215 * assign_record_type_typmod will use the shared registry. Worker backends
2216 * will need to attach explicitly.
2217 *
2218 * Note that this function takes 'area' and 'segment' as arguments rather than
2219 * accessing them via CurrentSession, because they aren't installed there
2220 * until after this function runs.
2221 */
2222void
2224 dsm_segment *segment,
2225 dsa_area *area)
2226{
2230 int32 typmod;
2231
2233
2234 /* We can't already be attached to a shared registry. */
2238
2240
2241 /* Create the hash table of tuple descriptors indexed by themselves. */
2243
2244 /* Create the hash table of tuple descriptors indexed by typmod. */
2246
2248
2249 /* Initialize the SharedRecordTypmodRegistry. */
2250 registry->record_table_handle = dshash_get_hash_table_handle(record_table);
2251 registry->typmod_table_handle = dshash_get_hash_table_handle(typmod_table);
2253
2254 /*
2255 * Copy all entries from this backend's private registry into the shared
2256 * registry.
2257 */
2258 for (typmod = 0; typmod < NextRecordTypmod; ++typmod)
2259 {
2264 TupleDesc tupdesc;
2265 bool found;
2266
2267 tupdesc = RecordCacheArray[typmod].tupdesc;
2268 if (tupdesc == NULL)
2269 continue;
2270
2271 /* Copy the TupleDesc into shared memory. */
2272 shared_dp = share_tupledesc(area, tupdesc, typmod);
2273
2274 /* Insert into the typmod table. */
2276 &tupdesc->tdtypmod,
2277 &found);
2278 if (found)
2279 elog(ERROR, "cannot create duplicate shared record typmod");
2280 typmod_table_entry->typmod = tupdesc->tdtypmod;
2281 typmod_table_entry->shared_tupdesc = shared_dp;
2283
2284 /* Insert into the record table. */
2285 record_table_key.shared = false;
2286 record_table_key.u.local_tupdesc = tupdesc;
2289 &found);
2290 if (!found)
2291 {
2292 record_table_entry->key.shared = true;
2293 record_table_entry->key.u.shared_tupdesc = shared_dp;
2294 }
2296 }
2297
2298 /*
2299 * Set up the global state that will tell assign_record_type_typmod and
2300 * lookup_rowtype_tupdesc_internal about the shared registry.
2301 */
2305
2306 /*
2307 * We install a detach hook in the leader, but only to handle cleanup on
2308 * failure during GetSessionDsmHandle(). Once GetSessionDsmHandle() pins
2309 * the memory, the leader process will use a shared registry until it
2310 * exits.
2311 */
2313}
2314
2315/*
2316 * Attach to 'registry', which must have been initialized already by another
2317 * backend. Future calls to assign_record_type_typmod and
2318 * lookup_rowtype_tupdesc_internal will use the shared registry until the
2319 * current session is detached.
2320 */
2321void
2323{
2327
2329
2330 /* We can't already be attached to a shared registry. */
2337
2338 /*
2339 * We can't already have typmods in our local cache, because they'd clash
2340 * with those imported by SharedRecordTypmodRegistryInit. This should be
2341 * a freshly started parallel worker. If we ever support worker
2342 * recycling, a worker would need to zap its local cache in between
2343 * servicing different queries, in order to be able to call this and
2344 * synchronize typmods with a new leader; but that's problematic because
2345 * we can't be very sure that record-typmod-related state hasn't escaped
2346 * to anywhere else in the process.
2347 */
2349
2351
2352 /* Attach to the two hash tables. */
2355 registry->record_table_handle,
2359 registry->typmod_table_handle,
2360 NULL);
2361
2363
2364 /*
2365 * Set up detach hook to run at worker exit. Currently this is the same
2366 * as the leader's detach hook, but in future they might need to be
2367 * different.
2368 */
2372
2373 /*
2374 * Set up the session state that will tell assign_record_type_typmod and
2375 * lookup_rowtype_tupdesc_internal about the shared registry.
2376 */
2380}
2381
2382/*
2383 * InvalidateCompositeTypeCacheEntry
2384 * Invalidate particular TypeCacheEntry on Relcache inval callback
2385 *
2386 * Delete the cached tuple descriptor (if any) for the given composite
2387 * type, and reset whatever info we have cached about the composite type's
2388 * comparability.
2389 */
2390static void
2392{
2394
2395 Assert(typentry->typtype == TYPTYPE_COMPOSITE &&
2396 OidIsValid(typentry->typrelid));
2397
2398 hadTupDescOrOpclass = (typentry->tupDesc != NULL) ||
2399 (typentry->flags & TCFLAGS_OPERATOR_FLAGS);
2400
2401 /* Delete tupdesc if we have it */
2402 if (typentry->tupDesc != NULL)
2403 {
2404 /*
2405 * Release our refcount and free the tupdesc if none remain. We can't
2406 * use DecrTupleDescRefCount here because this reference is not logged
2407 * by the current resource owner.
2408 */
2409 Assert(typentry->tupDesc->tdrefcount > 0);
2410 if (--typentry->tupDesc->tdrefcount == 0)
2411 FreeTupleDesc(typentry->tupDesc);
2412 typentry->tupDesc = NULL;
2413
2414 /*
2415 * Also clear tupDesc_identifier, so that anyone watching it will
2416 * realize that the tupdesc has changed.
2417 */
2418 typentry->tupDesc_identifier = 0;
2419 }
2420
2421 /* Reset equality/comparison/hashing validity information */
2422 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2423
2424 /*
2425 * Call delete_rel_type_cache_if_needed() if we actually cleared
2426 * something.
2427 */
2430}
2431
2432/*
2433 * TypeCacheRelCallback
2434 * Relcache inval callback function
2435 *
2436 * Delete the cached tuple descriptor (if any) for the given rel's composite
2437 * type, or for all composite types if relid == InvalidOid. Also reset
2438 * whatever info we have cached about the composite type's comparability.
2439 *
2440 * This is called when a relcache invalidation event occurs for the given
2441 * relid. We can't use syscache to find a type corresponding to the given
2442 * relation because the code can be called outside of transaction. Thus, we
2443 * use the RelIdToTypeIdCacheHash map to locate appropriate typcache entry.
2444 */
2445static void
2447{
2448 TypeCacheEntry *typentry;
2449
2450 /*
2451 * RelIdToTypeIdCacheHash and TypeCacheHash should exist, otherwise this
2452 * callback wouldn't be registered
2453 */
2454 if (OidIsValid(relid))
2455 {
2457
2458 /*
2459 * Find a RelIdToTypeIdCacheHash entry, which should exist as soon as
2460 * corresponding typcache entry has something to clean.
2461 */
2463 &relid,
2464 HASH_FIND, NULL);
2465
2466 if (relentry != NULL)
2467 {
2469 &relentry->composite_typid,
2470 HASH_FIND, NULL);
2471
2472 if (typentry != NULL)
2473 {
2474 Assert(typentry->typtype == TYPTYPE_COMPOSITE);
2475 Assert(relid == typentry->typrelid);
2476
2478 }
2479 }
2480
2481 /*
2482 * Visit all the domain types sequentially. Typically, this shouldn't
2483 * affect performance since domain types are less tended to bloat.
2484 * Domain types are created manually, unlike composite types which are
2485 * automatically created for every temporary table.
2486 */
2487 for (typentry = firstDomainTypeEntry;
2488 typentry != NULL;
2489 typentry = typentry->nextDomain)
2490 {
2491 /*
2492 * If it's domain over composite, reset flags. (We don't bother
2493 * trying to determine whether the specific base type needs a
2494 * reset.) Note that if we haven't determined whether the base
2495 * type is composite, we don't need to reset anything.
2496 */
2498 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2499 }
2500 }
2501 else
2502 {
2503 HASH_SEQ_STATUS status;
2504
2505 /*
2506 * Relid is invalid. By convention, we need to reset all composite
2507 * types in cache. Also, we should reset flags for domain types, and
2508 * we loop over all entries in hash, so, do it in a single scan.
2509 */
2510 hash_seq_init(&status, TypeCacheHash);
2511 while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2512 {
2513 if (typentry->typtype == TYPTYPE_COMPOSITE)
2514 {
2516 }
2517 else if (typentry->typtype == TYPTYPE_DOMAIN)
2518 {
2519 /*
2520 * If it's domain over composite, reset flags. (We don't
2521 * bother trying to determine whether the specific base type
2522 * needs a reset.) Note that if we haven't determined whether
2523 * the base type is composite, we don't need to reset
2524 * anything.
2525 */
2527 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2528 }
2529 }
2530 }
2531}
2532
2533/*
2534 * TypeCacheTypCallback
2535 * Syscache inval callback function
2536 *
2537 * This is called when a syscache invalidation event occurs for any
2538 * pg_type row. If we have information cached about that type, mark
2539 * it as needing to be reloaded.
2540 */
2541static void
2543{
2544 HASH_SEQ_STATUS status;
2545 TypeCacheEntry *typentry;
2546
2547 /* TypeCacheHash must exist, else this callback wouldn't be registered */
2548
2549 /*
2550 * By convention, zero hash value is passed to the callback as a sign that
2551 * it's time to invalidate the whole cache. See sinval.c, inval.c and
2552 * InvalidateSystemCachesExtended().
2553 */
2554 if (hashvalue == 0)
2555 hash_seq_init(&status, TypeCacheHash);
2556 else
2557 hash_seq_init_with_hash_value(&status, TypeCacheHash, hashvalue);
2558
2559 while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2560 {
2561 bool hadPgTypeData = (typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA);
2562
2563 Assert(hashvalue == 0 || typentry->type_id_hash == hashvalue);
2564
2565 /*
2566 * Mark the data obtained directly from pg_type as invalid. Also, if
2567 * it's a domain, typnotnull might've changed, so we'll need to
2568 * recalculate its constraints.
2569 */
2570 typentry->flags &= ~(TCFLAGS_HAVE_PG_TYPE_DATA |
2572
2573 /*
2574 * Call delete_rel_type_cache_if_needed() if we cleaned
2575 * TCFLAGS_HAVE_PG_TYPE_DATA flag previously.
2576 */
2577 if (hadPgTypeData)
2579 }
2580}
2581
2582/*
2583 * TypeCacheOpcCallback
2584 * Syscache inval callback function
2585 *
2586 * This is called when a syscache invalidation event occurs for any pg_opclass
2587 * row. In principle we could probably just invalidate data dependent on the
2588 * particular opclass, but since updates on pg_opclass are rare in production
2589 * it doesn't seem worth a lot of complication: we just mark all cached data
2590 * invalid.
2591 *
2592 * Note that we don't bother watching for updates on pg_amop or pg_amproc.
2593 * This should be safe because ALTER OPERATOR FAMILY ADD/DROP OPERATOR/FUNCTION
2594 * is not allowed to be used to add/drop the primary operators and functions
2595 * of an opclass, only cross-type members of a family; and the latter sorts
2596 * of members are not going to get cached here.
2597 */
2598static void
2600{
2601 HASH_SEQ_STATUS status;
2602 TypeCacheEntry *typentry;
2603
2604 /* TypeCacheHash must exist, else this callback wouldn't be registered */
2605 hash_seq_init(&status, TypeCacheHash);
2606 while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2607 {
2608 bool hadOpclass = (typentry->flags & TCFLAGS_OPERATOR_FLAGS);
2609
2610 /* Reset equality/comparison/hashing validity information */
2611 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2612
2613 /*
2614 * Call delete_rel_type_cache_if_needed() if we actually cleared some
2615 * of TCFLAGS_OPERATOR_FLAGS.
2616 */
2617 if (hadOpclass)
2619 }
2620}
2621
2622/*
2623 * TypeCacheConstrCallback
2624 * Syscache inval callback function
2625 *
2626 * This is called when a syscache invalidation event occurs for any
2627 * pg_constraint row. We flush information about domain constraints
2628 * when this happens.
2629 *
2630 * It's slightly annoying that we can't tell whether the inval event was for
2631 * a domain constraint record or not; there's usually more update traffic
2632 * for table constraints than domain constraints, so we'll do a lot of
2633 * useless flushes. Still, this is better than the old no-caching-at-all
2634 * approach to domain constraints.
2635 */
2636static void
2638{
2639 TypeCacheEntry *typentry;
2640
2641 /*
2642 * Because this is called very frequently, and typically very few of the
2643 * typcache entries are for domains, we don't use hash_seq_search here.
2644 * Instead we thread all the domain-type entries together so that we can
2645 * visit them cheaply.
2646 */
2647 for (typentry = firstDomainTypeEntry;
2648 typentry != NULL;
2649 typentry = typentry->nextDomain)
2650 {
2651 /* Reset domain constraint validity information */
2653 }
2654}
2655
2656
2657/*
2658 * Check if given OID is part of the subset that's sortable by comparisons
2659 */
2660static inline bool
2662{
2663 Oid offset;
2664
2665 if (arg < enumdata->bitmap_base)
2666 return false;
2667 offset = arg - enumdata->bitmap_base;
2668 if (offset > (Oid) INT_MAX)
2669 return false;
2670 return bms_is_member((int) offset, enumdata->sorted_values);
2671}
2672
2673
2674/*
2675 * compare_values_of_enum
2676 * Compare two members of an enum type.
2677 * Return <0, 0, or >0 according as arg1 <, =, or > arg2.
2678 *
2679 * Note: currently, the enumData cache is refreshed only if we are asked
2680 * to compare an enum value that is not already in the cache. This is okay
2681 * because there is no support for re-ordering existing values, so comparisons
2682 * of previously cached values will return the right answer even if other
2683 * values have been added since we last loaded the cache.
2684 *
2685 * Note: the enum logic has a special-case rule about even-numbered versus
2686 * odd-numbered OIDs, but we take no account of that rule here; this
2687 * routine shouldn't even get called when that rule applies.
2688 */
2689int
2691{
2693 EnumItem *item1;
2694 EnumItem *item2;
2695
2696 /*
2697 * Equal OIDs are certainly equal --- this case was probably handled by
2698 * our caller, but we may as well check.
2699 */
2700 if (arg1 == arg2)
2701 return 0;
2702
2703 /* Load up the cache if first time through */
2704 if (tcache->enumData == NULL)
2705 load_enum_cache_data(tcache);
2706 enumdata = tcache->enumData;
2707
2708 /*
2709 * If both OIDs are known-sorted, we can just compare them directly.
2710 */
2713 {
2714 if (arg1 < arg2)
2715 return -1;
2716 else
2717 return 1;
2718 }
2719
2720 /*
2721 * Slow path: we have to identify their actual sort-order positions.
2722 */
2725
2726 if (item1 == NULL || item2 == NULL)
2727 {
2728 /*
2729 * We couldn't find one or both values. That means the enum has
2730 * changed under us, so re-initialize the cache and try again. We
2731 * don't bother retrying the known-sorted case in this path.
2732 */
2733 load_enum_cache_data(tcache);
2734 enumdata = tcache->enumData;
2735
2738
2739 /*
2740 * If we still can't find the values, complain: we must have corrupt
2741 * data.
2742 */
2743 if (item1 == NULL)
2744 elog(ERROR, "enum value %u not found in cache for enum %s",
2745 arg1, format_type_be(tcache->type_id));
2746 if (item2 == NULL)
2747 elog(ERROR, "enum value %u not found in cache for enum %s",
2748 arg2, format_type_be(tcache->type_id));
2749 }
2750
2751 if (item1->sort_order < item2->sort_order)
2752 return -1;
2753 else if (item1->sort_order > item2->sort_order)
2754 return 1;
2755 else
2756 return 0;
2757}
2758
2759/*
2760 * Load (or re-load) the enumData member of the typcache entry.
2761 */
2762static void
2764{
2770 EnumItem *items;
2771 int numitems;
2772 int maxitems;
2773 Oid bitmap_base;
2774 Bitmapset *bitmap;
2776 int bm_size,
2777 start_pos;
2778
2779 /* Check that this is actually an enum */
2780 if (tcache->typtype != TYPTYPE_ENUM)
2781 ereport(ERROR,
2783 errmsg("%s is not an enum",
2784 format_type_be(tcache->type_id))));
2785
2786 /*
2787 * Read all the information for members of the enum type. We collect the
2788 * info in working memory in the caller's context, and then transfer it to
2789 * permanent memory in CacheMemoryContext. This minimizes the risk of
2790 * leaking memory from CacheMemoryContext in the event of an error partway
2791 * through.
2792 */
2793 maxitems = 64;
2794 items = palloc_array(EnumItem, maxitems);
2795 numitems = 0;
2796
2797 /* Scan pg_enum for the members of the target enum type. */
2801 ObjectIdGetDatum(tcache->type_id));
2802
2806 true, NULL,
2807 1, &skey);
2808
2810 {
2812
2813 if (numitems >= maxitems)
2814 {
2815 maxitems *= 2;
2816 items = (EnumItem *) repalloc(items, sizeof(EnumItem) * maxitems);
2817 }
2818 items[numitems].enum_oid = en->oid;
2819 items[numitems].sort_order = en->enumsortorder;
2820 numitems++;
2821 }
2822
2825
2826 /* Sort the items into OID order */
2827 qsort(items, numitems, sizeof(EnumItem), enum_oid_cmp);
2828
2829 /*
2830 * Here, we create a bitmap listing a subset of the enum's OIDs that are
2831 * known to be in order and can thus be compared with just OID comparison.
2832 *
2833 * The point of this is that the enum's initial OIDs were certainly in
2834 * order, so there is some subset that can be compared via OID comparison;
2835 * and we'd rather not do binary searches unnecessarily.
2836 *
2837 * This is somewhat heuristic, and might identify a subset of OIDs that
2838 * isn't exactly what the type started with. That's okay as long as the
2839 * subset is correctly sorted.
2840 */
2841 bitmap_base = InvalidOid;
2842 bitmap = NULL;
2843 bm_size = 1; /* only save sets of at least 2 OIDs */
2844
2845 for (start_pos = 0; start_pos < numitems - 1; start_pos++)
2846 {
2847 /*
2848 * Identify longest sorted subsequence starting at start_pos
2849 */
2851 int this_bm_size = 1;
2852 Oid start_oid = items[start_pos].enum_oid;
2853 float4 prev_order = items[start_pos].sort_order;
2854 int i;
2855
2856 for (i = start_pos + 1; i < numitems; i++)
2857 {
2858 Oid offset;
2859
2860 offset = items[i].enum_oid - start_oid;
2861 /* quit if bitmap would be too large; cutoff is arbitrary */
2862 if (offset >= 8192)
2863 break;
2864 /* include the item if it's in-order */
2865 if (items[i].sort_order > prev_order)
2866 {
2867 prev_order = items[i].sort_order;
2868 this_bitmap = bms_add_member(this_bitmap, (int) offset);
2869 this_bm_size++;
2870 }
2871 }
2872
2873 /* Remember it if larger than previous best */
2874 if (this_bm_size > bm_size)
2875 {
2876 bms_free(bitmap);
2877 bitmap_base = start_oid;
2878 bitmap = this_bitmap;
2880 }
2881 else
2883
2884 /*
2885 * Done if it's not possible to find a longer sequence in the rest of
2886 * the list. In typical cases this will happen on the first
2887 * iteration, which is why we create the bitmaps on the fly instead of
2888 * doing a second pass over the list.
2889 */
2890 if (bm_size >= (numitems - start_pos - 1))
2891 break;
2892 }
2893
2894 /* OK, copy the data into CacheMemoryContext */
2897 palloc(offsetof(TypeCacheEnumData, enum_values) +
2898 numitems * sizeof(EnumItem));
2899 enumdata->bitmap_base = bitmap_base;
2900 enumdata->sorted_values = bms_copy(bitmap);
2901 enumdata->num_values = numitems;
2902 memcpy(enumdata->enum_values, items, numitems * sizeof(EnumItem));
2904
2905 pfree(items);
2906 bms_free(bitmap);
2907
2908 /* And link the finished cache struct into the typcache */
2909 if (tcache->enumData != NULL)
2910 pfree(tcache->enumData);
2911 tcache->enumData = enumdata;
2912}
2913
2914/*
2915 * Locate the EnumItem with the given OID, if present
2916 */
2917static EnumItem *
2919{
2920 EnumItem srch;
2921
2922 /* On some versions of Solaris, bsearch of zero items dumps core */
2923 if (enumdata->num_values <= 0)
2924 return NULL;
2925
2926 srch.enum_oid = arg;
2927 return bsearch(&srch, enumdata->enum_values, enumdata->num_values,
2928 sizeof(EnumItem), enum_oid_cmp);
2929}
2930
2931/*
2932 * qsort comparison function for OID-ordered EnumItems
2933 */
2934static int
2935enum_oid_cmp(const void *left, const void *right)
2936{
2937 const EnumItem *l = (const EnumItem *) left;
2938 const EnumItem *r = (const EnumItem *) right;
2939
2940 return pg_cmp_u32(l->enum_oid, r->enum_oid);
2941}
2942
2943/*
2944 * Copy 'tupdesc' into newly allocated shared memory in 'area', set its typmod
2945 * to the given value and return a dsa_pointer.
2946 */
2947static dsa_pointer
2948share_tupledesc(dsa_area *area, TupleDesc tupdesc, uint32 typmod)
2949{
2951 TupleDesc shared;
2952
2953 shared_dp = dsa_allocate(area, TupleDescSize(tupdesc));
2954 shared = (TupleDesc) dsa_get_address(area, shared_dp);
2955 TupleDescCopy(shared, tupdesc);
2956 shared->tdtypmod = typmod;
2957
2958 return shared_dp;
2959}
2960
2961/*
2962 * If we are attached to a SharedRecordTypmodRegistry, use it to find or
2963 * create a shared TupleDesc that matches 'tupdesc'. Otherwise return NULL.
2964 * Tuple descriptors returned by this function are not reference counted, and
2965 * will exist at least as long as the current backend remained attached to the
2966 * current session.
2967 */
2968static TupleDesc
2970{
2976 bool found;
2977 uint32 typmod;
2978
2979 /* If not even attached, nothing to do. */
2981 return NULL;
2982
2983 /* Try to find a matching tuple descriptor in the record table. */
2984 key.shared = false;
2985 key.u.local_tupdesc = tupdesc;
2989 {
2990 Assert(record_table_entry->key.shared);
2993 result = (TupleDesc)
2995 record_table_entry->key.u.shared_tupdesc);
2996 Assert(result->tdrefcount == -1);
2997
2998 return result;
2999 }
3000
3001 /* Allocate a new typmod number. This will be wasted if we error out. */
3002 typmod = (int)
3004 1);
3005
3006 /* Copy the TupleDesc into shared memory. */
3007 shared_dp = share_tupledesc(CurrentSession->area, tupdesc, typmod);
3008
3009 /*
3010 * Create an entry in the typmod table so that others will understand this
3011 * typmod number.
3012 */
3013 PG_TRY();
3014 {
3017 &typmod, &found);
3018 if (found)
3019 elog(ERROR, "cannot create duplicate shared record typmod");
3020 }
3021 PG_CATCH();
3022 {
3024 PG_RE_THROW();
3025 }
3026 PG_END_TRY();
3027 typmod_table_entry->typmod = typmod;
3028 typmod_table_entry->shared_tupdesc = shared_dp;
3031
3032 /*
3033 * Finally create an entry in the record table so others with matching
3034 * tuple descriptors can reuse the typmod.
3035 */
3038 &found);
3039 if (found)
3040 {
3041 /*
3042 * Someone concurrently inserted a matching tuple descriptor since the
3043 * first time we checked. Use that one instead.
3044 */
3047
3048 /* Might as well free up the space used by the one we created. */
3050 &typmod);
3051 Assert(found);
3053
3054 /* Return the one we found. */
3055 Assert(record_table_entry->key.shared);
3056 result = (TupleDesc)
3058 record_table_entry->key.u.shared_tupdesc);
3059 Assert(result->tdrefcount == -1);
3060
3061 return result;
3062 }
3063
3064 /* Store it and return it. */
3065 record_table_entry->key.shared = true;
3066 record_table_entry->key.u.shared_tupdesc = shared_dp;
3069 result = (TupleDesc)
3071 Assert(result->tdrefcount == -1);
3072
3073 return result;
3074}
3075
3076/*
3077 * On-DSM-detach hook to forget about the current shared record typmod
3078 * infrastructure. This is currently used by both leader and workers.
3079 */
3080static void
3082{
3083 /* Be cautious here: maybe we didn't finish initializing. */
3085 {
3088 }
3090 {
3093 }
3095}
3096
3097/*
3098 * Insert RelIdToTypeIdCacheHash entry if needed.
3099 */
3100static void
3102{
3103 /* Immediately quit for non-composite types */
3104 if (typentry->typtype != TYPTYPE_COMPOSITE)
3105 return;
3106
3107 /* typrelid should be given for composite types */
3108 Assert(OidIsValid(typentry->typrelid));
3109
3110 /*
3111 * Insert a RelIdToTypeIdCacheHash entry if the typentry have any
3112 * information indicating it should be here.
3113 */
3114 if ((typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA) ||
3115 (typentry->flags & TCFLAGS_OPERATOR_FLAGS) ||
3116 typentry->tupDesc != NULL)
3117 {
3119 bool found;
3120
3122 &typentry->typrelid,
3123 HASH_ENTER, &found);
3124 relentry->relid = typentry->typrelid;
3125 relentry->composite_typid = typentry->type_id;
3126 }
3127}
3128
3129/*
3130 * Delete entry RelIdToTypeIdCacheHash if needed after resetting of the
3131 * TCFLAGS_HAVE_PG_TYPE_DATA flag, or any of TCFLAGS_OPERATOR_FLAGS,
3132 * or tupDesc.
3133 */
3134static void
3136{
3137#ifdef USE_ASSERT_CHECKING
3138 int i;
3139 bool is_in_progress = false;
3140
3141 for (i = 0; i < in_progress_list_len; i++)
3142 {
3143 if (in_progress_list[i] == typentry->type_id)
3144 {
3145 is_in_progress = true;
3146 break;
3147 }
3148 }
3149#endif
3150
3151 /* Immediately quit for non-composite types */
3152 if (typentry->typtype != TYPTYPE_COMPOSITE)
3153 return;
3154
3155 /* typrelid should be given for composite types */
3156 Assert(OidIsValid(typentry->typrelid));
3157
3158 /*
3159 * Delete a RelIdToTypeIdCacheHash entry if the typentry doesn't have any
3160 * information indicating entry should be still there.
3161 */
3162 if (!(typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA) &&
3163 !(typentry->flags & TCFLAGS_OPERATOR_FLAGS) &&
3164 typentry->tupDesc == NULL)
3165 {
3166 bool found;
3167
3169 &typentry->typrelid,
3170 HASH_REMOVE, &found);
3171 Assert(found || is_in_progress);
3172 }
3173 else
3174 {
3175#ifdef USE_ASSERT_CHECKING
3176 /*
3177 * In assert-enabled builds otherwise check for RelIdToTypeIdCacheHash
3178 * entry if it should exist.
3179 */
3180 bool found;
3181
3182 if (!is_in_progress)
3183 {
3185 &typentry->typrelid,
3186 HASH_FIND, &found);
3187 Assert(found);
3188 }
3189#endif
3190 }
3191}
3192
3193/*
3194 * Add possibly missing RelIdToTypeId entries related to TypeCacheHash
3195 * entries, marked as in-progress by lookup_type_cache(). It may happen
3196 * in case of an error or interruption during the lookup_type_cache() call.
3197 */
3198static void
3200{
3201 int i;
3202
3203 for (i = 0; i < in_progress_list_len; i++)
3204 {
3205 TypeCacheEntry *typentry;
3206
3209 HASH_FIND, NULL);
3210 if (typentry)
3212 }
3213
3215}
3216
3217void
3219{
3221}
3222
3223void
3225{
3227}
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:230
#define makeNode(_type_)
Definition nodes.h:159
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
#define repalloc0_array(pointer, type, oldcount, count)
Definition palloc.h:122
FormData_pg_attribute * Form_pg_attribute
static uint32 pg_nextpower2_32(uint32 num)
END_CATALOG_STRUCT typedef FormData_pg_constraint * Form_pg_constraint
const void * data
END_CATALOG_STRUCT typedef FormData_pg_enum * Form_pg_enum
Definition pg_enum.h:48
#define lfirst(lc)
Definition pg_list.h:172
#define NIL
Definition pg_list.h:68
#define foreach_node(type, var, lst)
Definition pg_list.h:528
END_CATALOG_STRUCT typedef FormData_pg_range * Form_pg_range
Definition pg_range.h:71
END_CATALOG_STRUCT typedef FormData_pg_type * Form_pg_type
Definition pg_type.h:265
Expr * expression_planner(Expr *expr)
Definition planner.c: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:133
TupleDesc tupdesc
Definition typcache.c:174
Form_pg_class rd_rel
Definition rel.h:111
dsm_segment * segment
Definition session.h:27
dshash_table * shared_record_table
Definition session.h:32
struct SharedRecordTypmodRegistry * shared_typmod_registry
Definition session.h:31
dsa_area * area
Definition session.h:28
dshash_table * shared_typmod_table
Definition session.h:33
SharedRecordTableKey key
Definition typcache.c:213
TupleDesc local_tupdesc
Definition typcache.c:201
union SharedRecordTableKey::@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:1497
#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:1742
static void insert_rel_type_cache_if_needed(TypeCacheEntry *typentry)
Definition typcache.c:3102
void InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref, MemoryContext refctx, bool need_exprstate)
Definition typcache.c:1406
static TupleDesc lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
Definition typcache.c:1855
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition typcache.c:1949
static void TypeCacheOpcCallback(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
Definition typcache.c:2600
void SharedRecordTypmodRegistryAttach(SharedRecordTypmodRegistry *registry)
Definition typcache.c:2323
#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:1758
#define TCFLAGS_HAVE_FIELD_COMPARE
Definition typcache.c:115
void AtEOXact_TypeCache(void)
Definition typcache.c:3219
static void load_enum_cache_data(TypeCacheEntry *tcache)
Definition typcache.c:2764
static bool record_fields_have_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1621
static HTAB * RelIdToTypeIdCacheHash
Definition typcache.c:87
static EnumItem * find_enumitem(TypeCacheEnumData *enumdata, Oid arg)
Definition typcache.c:2919
static bool record_fields_have_extended_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1629
static TupleDesc find_or_make_matching_shared_tupledesc(TupleDesc tupdesc)
Definition typcache.c:2970
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:2005
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:3136
#define TCFLAGS_CHECKED_GT_OPR
Definition typcache.c:104
static bool multirange_element_has_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1782
static List * prep_domain_constraints(List *constraints, MemoryContext execctx)
Definition typcache.c:1368
TupleDesc lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
Definition typcache.c:1966
static bool record_fields_have_equality(TypeCacheEntry *typentry)
Definition typcache.c:1605
#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:1347
#define TCFLAGS_HAVE_FIELD_EQUALITY
Definition typcache.c:114
static void InvalidateCompositeTypeCacheEntry(TypeCacheEntry *typentry)
Definition typcache.c:2392
void SharedRecordTypmodRegistryInit(SharedRecordTypmodRegistry *registry, dsm_segment *segment, dsa_area *area)
Definition typcache.c:2224
static int dcs_cmp(const void *a, const void *b)
Definition typcache.c:1323
static bool array_element_has_extended_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1567
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:1559
static void load_multirangetype_info(TypeCacheEntry *typentry)
Definition typcache.c:1066
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:1790
static int in_progress_list_len
Definition typcache.c:227
static bool array_element_has_equality(TypeCacheEntry *typentry)
Definition typcache.c:1543
static dsa_pointer share_tupledesc(dsa_area *area, TupleDesc tupdesc, uint32 typmod)
Definition typcache.c:2949
static void load_rangetype_info(TypeCacheEntry *typentry)
Definition typcache.c:1008
uint64 assign_record_type_identifier(Oid type_id, int32 typmod)
Definition typcache.c:2161
static RecordCacheArrayEntry * RecordCacheArray
Definition typcache.c:304
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1750
static HTAB * RecordCacheHash
Definition typcache.c:295
static bool enum_known_sorted(TypeCacheEnumData *enumdata, Oid arg)
Definition typcache.c:2662
static TypeCacheEntry * firstDomainTypeEntry
Definition typcache.c:96
void AtEOSubXact_TypeCache(void)
Definition typcache.c:3225
static void shared_record_typmod_registry_detach(dsm_segment *segment, Datum datum)
Definition typcache.c:3082
#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:1088
#define TCFLAGS_HAVE_ELEM_COMPARE
Definition typcache.c:110
static void TypeCacheRelCallback(Datum arg, Oid relid)
Definition typcache.c:2447
static void cache_array_element_properties(TypeCacheEntry *typentry)
Definition typcache.c:1575
static void TypeCacheTypCallback(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
Definition typcache.c:2543
size_t SharedRecordTypmodRegistryEstimate(void)
Definition typcache.c:2202
static void cache_multirange_element_properties(TypeCacheEntry *typentry)
Definition typcache.c:1798
#define TCFLAGS_CHECKED_ELEM_PROPERTIES
Definition typcache.c:108
static void TypeCacheConstrCallback(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
Definition typcache.c:2638
#define TCFLAGS_HAVE_ELEM_EQUALITY
Definition typcache.c:109
static bool array_element_has_compare(TypeCacheEntry *typentry)
Definition typcache.c:1551
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:2691
#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:2069
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:1613
#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:2053
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:1983
static int enum_oid_cmp(const void *left, const void *right)
Definition typcache.c:2936
static void finalize_in_progress_typentries(void)
Definition typcache.c:3200
static void decr_dcc_refcount(DomainConstraintCache *dcc)
Definition typcache.c:1336
#define TCFLAGS_CHECKED_EQ_OPR
Definition typcache.c:102
void UpdateDomainConstraintRef(DomainConstraintRef *ref)
Definition typcache.c:1444
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:1826
static void cache_record_field_properties(TypeCacheEntry *typentry)
Definition typcache.c:1637
static uint32 record_type_typmod_hash(const void *data, size_t size)
Definition typcache.c:2042
static void load_typcache_tupdesc(TypeCacheEntry *typentry)
Definition typcache.c:974
#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 1551 of file typcache.c.

1552{
1553 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1555 return (typentry->flags & TCFLAGS_HAVE_ELEM_COMPARE) != 0;
1556}

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 1543 of file typcache.c.

1544{
1545 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1547 return (typentry->flags & TCFLAGS_HAVE_ELEM_EQUALITY) != 0;
1548}

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 1559 of file typcache.c.

1560{
1561 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1563 return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1564}

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 2161 of file typcache.c.

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

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 2069 of file typcache.c.

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

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 3225 of file typcache.c.

3226{
3228}

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 1798 of file typcache.c.

1799{
1800 /* load up range link if we didn't already */
1801 if (typentry->rngtype == NULL &&
1802 typentry->typtype == TYPTYPE_MULTIRANGE)
1803 load_multirangetype_info(typentry);
1804
1805 if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
1806 {
1808
1809 /* might need to calculate subtype's hash function properties */
1813 if (OidIsValid(elementry->hash_proc))
1814 typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1815 if (OidIsValid(elementry->hash_extended_proc))
1817 }
1819}

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 1758 of file typcache.c.

1759{
1760 /* load up subtype link if we didn't already */
1761 if (typentry->rngelemtype == NULL &&
1762 typentry->typtype == TYPTYPE_RANGE)
1763 load_rangetype_info(typentry);
1764
1765 if (typentry->rngelemtype != NULL)
1766 {
1768
1769 /* might need to calculate subtype's hash function properties */
1773 if (OidIsValid(elementry->hash_proc))
1774 typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1775 if (OidIsValid(elementry->hash_extended_proc))
1777 }
1779}

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 1637 of file typcache.c.

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

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 2691 of file typcache.c.

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

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 1347 of file typcache.c.

1348{
1350 DomainConstraintCache *dcc = ref->dcc;
1351
1352 /* Paranoia --- be sure link is nulled before trying to release */
1353 if (dcc)
1354 {
1355 ref->constraints = NIL;
1356 ref->dcc = NULL;
1357 decr_dcc_refcount(dcc);
1358 }
1359}

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 1323 of file typcache.c.

1324{
1325 const DomainConstraintState *const *ca = (const DomainConstraintState *const *) a;
1326 const DomainConstraintState *const *cb = (const DomainConstraintState *const *) b;
1327
1328 return strcmp((*ca)->name, (*cb)->name);
1329}

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 3136 of file typcache.c.

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

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 1497 of file typcache.c.

1498{
1499 TypeCacheEntry *typentry;
1500
1501 /*
1502 * Note: a side effect is to cause the typcache's domain data to become
1503 * valid. This is fine since we'll likely need it soon if there is any.
1504 */
1506
1507 if (typentry->domainData == NULL)
1508 return false;
1509
1510 if (has_volatile)
1511 {
1512 *has_volatile = false;
1513
1515 typentry->domainData->constraints)
1516 {
1517 if (constrstate->constrainttype == DOM_CONSTRAINT_CHECK &&
1519 {
1520 *has_volatile = true;
1521 break;
1522 }
1523 }
1524 }
1525
1526 return true;
1527}

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 2662 of file typcache.c.

2663{
2664 Oid offset;
2665
2666 if (arg < enumdata->bitmap_base)
2667 return false;
2668 offset = arg - enumdata->bitmap_base;
2669 if (offset > (Oid) INT_MAX)
2670 return false;
2671 return bms_is_member((int) offset, enumdata->sorted_values);
2672}

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 2936 of file typcache.c.

2937{
2938 const EnumItem *l = (const EnumItem *) left;
2939 const EnumItem *r = (const EnumItem *) right;
2940
2941 return pg_cmp_u32(l->enum_oid, r->enum_oid);
2942}

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 3200 of file typcache.c.

3201{
3202 int i;
3203
3204 for (i = 0; i < in_progress_list_len; i++)
3205 {
3206 TypeCacheEntry *typentry;
3207
3210 HASH_FIND, NULL);
3211 if (typentry)
3213 }
3214
3216}

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 2919 of file typcache.c.

2920{
2921 EnumItem srch;
2922
2923 /* On some versions of Solaris, bsearch of zero items dumps core */
2924 if (enumdata->num_values <= 0)
2925 return NULL;
2926
2927 srch.enum_oid = arg;
2928 return bsearch(&srch, enumdata->enum_values, enumdata->num_values,
2929 sizeof(EnumItem), enum_oid_cmp);
2930}

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 2970 of file typcache.c.

2971{
2977 bool found;
2978 uint32 typmod;
2979
2980 /* If not even attached, nothing to do. */
2982 return NULL;
2983
2984 /* Try to find a matching tuple descriptor in the record table. */
2985 key.shared = false;
2986 key.u.local_tupdesc = tupdesc;
2990 {
2991 Assert(record_table_entry->key.shared);
2994 result = (TupleDesc)
2996 record_table_entry->key.u.shared_tupdesc);
2997 Assert(result->tdrefcount == -1);
2998
2999 return result;
3000 }
3001
3002 /* Allocate a new typmod number. This will be wasted if we error out. */
3003 typmod = (int)
3005 1);
3006
3007 /* Copy the TupleDesc into shared memory. */
3008 shared_dp = share_tupledesc(CurrentSession->area, tupdesc, typmod);
3009
3010 /*
3011 * Create an entry in the typmod table so that others will understand this
3012 * typmod number.
3013 */
3014 PG_TRY();
3015 {
3018 &typmod, &found);
3019 if (found)
3020 elog(ERROR, "cannot create duplicate shared record typmod");
3021 }
3022 PG_CATCH();
3023 {
3025 PG_RE_THROW();
3026 }
3027 PG_END_TRY();
3028 typmod_table_entry->typmod = typmod;
3029 typmod_table_entry->shared_tupdesc = shared_dp;
3032
3033 /*
3034 * Finally create an entry in the record table so others with matching
3035 * tuple descriptors can reuse the typmod.
3036 */
3039 &found);
3040 if (found)
3041 {
3042 /*
3043 * Someone concurrently inserted a matching tuple descriptor since the
3044 * first time we checked. Use that one instead.
3045 */
3048
3049 /* Might as well free up the space used by the one we created. */
3051 &typmod);
3052 Assert(found);
3054
3055 /* Return the one we found. */
3056 Assert(record_table_entry->key.shared);
3057 result = (TupleDesc)
3059 record_table_entry->key.u.shared_tupdesc);
3060 Assert(result->tdrefcount == -1);
3061
3062 return result;
3063 }
3064
3065 /* Store it and return it. */
3066 record_table_entry->key.shared = true;
3067 record_table_entry->key.u.shared_tupdesc = shared_dp;
3070 result = (TupleDesc)
3072 Assert(result->tdrefcount == -1);
3073
3074 return result;
3075}

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 1406 of file typcache.c.

1408{
1409 /* Look up the typcache entry --- we assume it survives indefinitely */
1411 ref->need_exprstate = need_exprstate;
1412 /* For safety, establish the callback before acquiring a refcount */
1413 ref->refctx = refctx;
1414 ref->dcc = NULL;
1415 ref->callback.func = dccref_deletion_callback;
1416 ref->callback.arg = ref;
1417 MemoryContextRegisterResetCallback(refctx, &ref->callback);
1418 /* Acquire refcount if there are constraints, and set up exported list */
1419 if (ref->tcache->domainData)
1420 {
1421 ref->dcc = ref->tcache->domainData;
1422 ref->dcc->dccRefCount++;
1423 if (ref->need_exprstate)
1424 ref->constraints = prep_domain_constraints(ref->dcc->constraints,
1425 ref->refctx);
1426 else
1427 ref->constraints = ref->dcc->constraints;
1428 }
1429 else
1430 ref->constraints = NIL;
1431}

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 3102 of file typcache.c.

3103{
3104 /* Immediately quit for non-composite types */
3105 if (typentry->typtype != TYPTYPE_COMPOSITE)
3106 return;
3107
3108 /* typrelid should be given for composite types */
3109 Assert(OidIsValid(typentry->typrelid));
3110
3111 /*
3112 * Insert a RelIdToTypeIdCacheHash entry if the typentry have any
3113 * information indicating it should be here.
3114 */
3115 if ((typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA) ||
3116 (typentry->flags & TCFLAGS_OPERATOR_FLAGS) ||
3117 typentry->tupDesc != NULL)
3118 {
3120 bool found;
3121
3123 &typentry->typrelid,
3124 HASH_ENTER, &found);
3125 relentry->relid = typentry->typrelid;
3126 relentry->composite_typid = typentry->type_id;
3127 }
3128}

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 2392 of file typcache.c.

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

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 1088 of file typcache.c.

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

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 2764 of file typcache.c.

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

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 1066 of file typcache.c.

1067{
1069
1072 elog(ERROR, "cache lookup failed for multirange type %u",
1073 typentry->type_id);
1074
1076}

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 1008 of file typcache.c.

1009{
1011 HeapTuple tup;
1017 Oid opcintype;
1018 Oid cmpFnOid;
1019
1020 /* get information from pg_range */
1022 /* should not fail, since we already checked typtype ... */
1023 if (!HeapTupleIsValid(tup))
1024 elog(ERROR, "cache lookup failed for range type %u",
1025 typentry->type_id);
1027
1028 subtypeOid = pg_range->rngsubtype;
1029 typentry->rng_collation = pg_range->rngcollation;
1030 opclassOid = pg_range->rngsubopc;
1031 canonicalOid = pg_range->rngcanonical;
1032 subdiffOid = pg_range->rngsubdiff;
1033
1035
1036 /* get opclass properties and look up the comparison function */
1039 typentry->rng_opfamily = opfamilyOid;
1040
1041 cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype,
1042 BTORDER_PROC);
1044 elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
1045 BTORDER_PROC, opcintype, opcintype, opfamilyOid);
1046
1047 /* set up cached fmgrinfo structs */
1056
1057 /* Lastly, set up link to the element type --- this marks data valid */
1059}

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 974 of file typcache.c.

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

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 2005 of file typcache.c.

2006{
2007 TupleDesc tupDesc;
2008
2009 if (type_id != RECORDOID)
2010 {
2011 /*
2012 * Check for domain or named composite type. We might as well load
2013 * whichever data is needed.
2014 */
2015 TypeCacheEntry *typentry;
2016
2017 typentry = lookup_type_cache(type_id,
2020 if (typentry->typtype == TYPTYPE_DOMAIN)
2022 typentry->domainBaseTypmod,
2023 noError);
2024 if (typentry->tupDesc == NULL && !noError)
2025 ereport(ERROR,
2027 errmsg("type %s is not composite",
2028 format_type_be(type_id))));
2029 tupDesc = typentry->tupDesc;
2030 }
2031 else
2032 tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
2033 if (tupDesc != NULL)
2034 PinTupleDesc(tupDesc);
2035 return tupDesc;
2036}

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 1855 of file typcache.c.

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

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 1966 of file typcache.c.

1967{
1968 TupleDesc tupDesc;
1969
1970 tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
1971 if (tupDesc != NULL)
1972 PinTupleDesc(tupDesc);
1973 return tupDesc;
1974}

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

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 1368 of file typcache.c.

1369{
1370 List *result = NIL;
1372 ListCell *lc;
1373
1375
1376 foreach(lc, constraints)
1377 {
1380
1382 newr->constrainttype = r->constrainttype;
1383 newr->name = r->name;
1384 newr->check_expr = r->check_expr;
1385 newr->check_exprstate = ExecInitExpr(r->check_expr, NULL);
1386
1388 }
1389
1391
1392 return result;
1393}

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 1742 of file typcache.c.

1743{
1744 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1746 return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1747}

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 1613 of file typcache.c.

1614{
1615 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1617 return (typentry->flags & TCFLAGS_HAVE_FIELD_COMPARE) != 0;
1618}

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 1621 of file typcache.c.

1622{
1623 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1625 return (typentry->flags & TCFLAGS_HAVE_FIELD_HASHING) != 0;
1626}

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 2053 of file typcache.c.

2054{
2055 const RecordCacheEntry *left = a;
2056 const RecordCacheEntry *right = b;
2057
2058 return equalRowTypes(left->tupdesc, right->tupdesc) ? 0 : 1;
2059}

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 2042 of file typcache.c.

2043{
2044 const RecordCacheEntry *entry = data;
2045
2046 return hashRowType(entry->tupdesc);
2047}

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 2949 of file typcache.c.

2950{
2952 TupleDesc shared;
2953
2954 shared_dp = dsa_allocate(area, TupleDescSize(tupdesc));
2955 shared = (TupleDesc) dsa_get_address(area, shared_dp);
2956 TupleDescCopy(shared, tupdesc);
2957 shared->tdtypmod = typmod;
2958
2959 return shared_dp;
2960}

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 2323 of file typcache.c.

2324{
2328
2330
2331 /* We can't already be attached to a shared registry. */
2338
2339 /*
2340 * We can't already have typmods in our local cache, because they'd clash
2341 * with those imported by SharedRecordTypmodRegistryInit. This should be
2342 * a freshly started parallel worker. If we ever support worker
2343 * recycling, a worker would need to zap its local cache in between
2344 * servicing different queries, in order to be able to call this and
2345 * synchronize typmods with a new leader; but that's problematic because
2346 * we can't be very sure that record-typmod-related state hasn't escaped
2347 * to anywhere else in the process.
2348 */
2350
2352
2353 /* Attach to the two hash tables. */
2356 registry->record_table_handle,
2360 registry->typmod_table_handle,
2361 NULL);
2362
2364
2365 /*
2366 * Set up detach hook to run at worker exit. Currently this is the same
2367 * as the leader's detach hook, but in future they might need to be
2368 * different.
2369 */
2373
2374 /*
2375 * Set up the session state that will tell assign_record_type_typmod and
2376 * lookup_rowtype_tupdesc_internal about the shared registry.
2377 */
2381}

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 2202 of file typcache.c.

2203{
2204 return sizeof(SharedRecordTypmodRegistry);
2205}

Referenced by GetSessionDsmHandle().

◆ SharedRecordTypmodRegistryInit()

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

Definition at line 2224 of file typcache.c.

2227{
2231 int32 typmod;
2232
2234
2235 /* We can't already be attached to a shared registry. */
2239
2241
2242 /* Create the hash table of tuple descriptors indexed by themselves. */
2244
2245 /* Create the hash table of tuple descriptors indexed by typmod. */
2247
2249
2250 /* Initialize the SharedRecordTypmodRegistry. */
2251 registry->record_table_handle = dshash_get_hash_table_handle(record_table);
2252 registry->typmod_table_handle = dshash_get_hash_table_handle(typmod_table);
2254
2255 /*
2256 * Copy all entries from this backend's private registry into the shared
2257 * registry.
2258 */
2259 for (typmod = 0; typmod < NextRecordTypmod; ++typmod)
2260 {
2265 TupleDesc tupdesc;
2266 bool found;
2267
2268 tupdesc = RecordCacheArray[typmod].tupdesc;
2269 if (tupdesc == NULL)
2270 continue;
2271
2272 /* Copy the TupleDesc into shared memory. */
2273 shared_dp = share_tupledesc(area, tupdesc, typmod);
2274
2275 /* Insert into the typmod table. */
2277 &tupdesc->tdtypmod,
2278 &found);
2279 if (found)
2280 elog(ERROR, "cannot create duplicate shared record typmod");
2281 typmod_table_entry->typmod = tupdesc->tdtypmod;
2282 typmod_table_entry->shared_tupdesc = shared_dp;
2284
2285 /* Insert into the record table. */
2286 record_table_key.shared = false;
2287 record_table_key.u.local_tupdesc = tupdesc;
2290 &found);
2291 if (!found)
2292 {
2293 record_table_entry->key.shared = true;
2294 record_table_entry->key.u.shared_tupdesc = shared_dp;
2295 }
2297 }
2298
2299 /*
2300 * Set up the global state that will tell assign_record_type_typmod and
2301 * lookup_rowtype_tupdesc_internal about the shared registry.
2302 */
2306
2307 /*
2308 * We install a detach hook in the leader, but only to handle cleanup on
2309 * failure during GetSessionDsmHandle(). Once GetSessionDsmHandle() pins
2310 * the memory, the leader process will use a shared registry until it
2311 * exits.
2312 */
2314}

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 2638 of file typcache.c.

2639{
2640 TypeCacheEntry *typentry;
2641
2642 /*
2643 * Because this is called very frequently, and typically very few of the
2644 * typcache entries are for domains, we don't use hash_seq_search here.
2645 * Instead we thread all the domain-type entries together so that we can
2646 * visit them cheaply.
2647 */
2648 for (typentry = firstDomainTypeEntry;
2649 typentry != NULL;
2650 typentry = typentry->nextDomain)
2651 {
2652 /* Reset domain constraint validity information */
2654 }
2655}

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 2600 of file typcache.c.

2601{
2602 HASH_SEQ_STATUS status;
2603 TypeCacheEntry *typentry;
2604
2605 /* TypeCacheHash must exist, else this callback wouldn't be registered */
2606 hash_seq_init(&status, TypeCacheHash);
2607 while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2608 {
2609 bool hadOpclass = (typentry->flags & TCFLAGS_OPERATOR_FLAGS);
2610
2611 /* Reset equality/comparison/hashing validity information */
2612 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2613
2614 /*
2615 * Call delete_rel_type_cache_if_needed() if we actually cleared some
2616 * of TCFLAGS_OPERATOR_FLAGS.
2617 */
2618 if (hadOpclass)
2620 }
2621}

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 2447 of file typcache.c.

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

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 2543 of file typcache.c.

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

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 1444 of file typcache.c.

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

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