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 }
461
462 /* Try to look up an existing entry */
464 &type_id,
465 HASH_FIND, NULL);
466
467 /*
468 * Only mark the new entry as "in progress" after the initial entry
469 * lookup.
470 *
471 * TypeCacheHash uses type_cache_syshash(), potentially triggering the
472 * initialization of the TYPEOID catcache, where an out-of-memory failure
473 * is possible. If an out-of-memory happens, error recovery would call
474 * finalize_in_progress_typentries(), that could attempt a catcache
475 * initialization again outside a transaction context.
476 *
477 * See also ConditionalCatalogCacheInitializeCache().
478 */
481
482 if (typentry == NULL)
483 {
484 /*
485 * If we didn't find one, we want to make one. But first look up the
486 * pg_type row, just to make sure we don't make a cache entry for an
487 * invalid type OID. If the type OID is not valid, present a
488 * user-facing error, since some code paths such as domain_in() allow
489 * this function to be reached with a user-supplied OID.
490 */
491 HeapTuple tp;
493
495 if (!HeapTupleIsValid(tp))
498 errmsg("type with OID %u does not exist", type_id)));
500 if (!typtup->typisdefined)
503 errmsg("type \"%s\" is only a shell",
504 NameStr(typtup->typname))));
505
506 /* Now make the typcache entry */
508 &type_id,
509 HASH_ENTER, &found);
510 Assert(!found); /* it wasn't there a moment ago */
511
512 MemSet(typentry, 0, sizeof(TypeCacheEntry));
513
514 /* These fields can never change, by definition */
515 typentry->type_id = type_id;
516 typentry->type_id_hash = get_hash_value(TypeCacheHash, &type_id);
517
518 /* Keep this part in sync with the code below */
519 typentry->typlen = typtup->typlen;
520 typentry->typbyval = typtup->typbyval;
521 typentry->typalign = typtup->typalign;
522 typentry->typstorage = typtup->typstorage;
523 typentry->typtype = typtup->typtype;
524 typentry->typrelid = typtup->typrelid;
525 typentry->typsubscript = typtup->typsubscript;
526 typentry->typelem = typtup->typelem;
527 typentry->typarray = typtup->typarray;
528 typentry->typcollation = typtup->typcollation;
529 typentry->flags |= TCFLAGS_HAVE_PG_TYPE_DATA;
530
531 /* If it's a domain, immediately thread it into the domain cache list */
532 if (typentry->typtype == TYPTYPE_DOMAIN)
533 {
535 firstDomainTypeEntry = typentry;
536 }
537
538 ReleaseSysCache(tp);
539 }
540 else if (!(typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA))
541 {
542 /*
543 * We have an entry, but its pg_type row got changed, so reload the
544 * data obtained directly from pg_type.
545 */
546 HeapTuple tp;
548
550 if (!HeapTupleIsValid(tp))
553 errmsg("type with OID %u does not exist", type_id)));
555 if (!typtup->typisdefined)
558 errmsg("type \"%s\" is only a shell",
559 NameStr(typtup->typname))));
560
561 /*
562 * Keep this part in sync with the code above. Many of these fields
563 * shouldn't ever change, particularly typtype, but copy 'em anyway.
564 */
565 typentry->typlen = typtup->typlen;
566 typentry->typbyval = typtup->typbyval;
567 typentry->typalign = typtup->typalign;
568 typentry->typstorage = typtup->typstorage;
569 typentry->typtype = typtup->typtype;
570 typentry->typrelid = typtup->typrelid;
571 typentry->typsubscript = typtup->typsubscript;
572 typentry->typelem = typtup->typelem;
573 typentry->typarray = typtup->typarray;
574 typentry->typcollation = typtup->typcollation;
575 typentry->flags |= TCFLAGS_HAVE_PG_TYPE_DATA;
576
577 ReleaseSysCache(tp);
578 }
579
580 /*
581 * Look up opclasses if we haven't already and any dependent info is
582 * requested.
583 */
589 {
590 Oid opclass;
591
592 opclass = GetDefaultOpClass(type_id, BTREE_AM_OID);
593 if (OidIsValid(opclass))
594 {
595 typentry->btree_opf = get_opclass_family(opclass);
596 typentry->btree_opintype = get_opclass_input_type(opclass);
597 }
598 else
599 {
600 typentry->btree_opf = typentry->btree_opintype = InvalidOid;
601 }
602
603 /*
604 * Reset information derived from btree opclass. Note in particular
605 * that we'll redetermine the eq_opr even if we previously found one;
606 * this matters in case a btree opclass has been added to a type that
607 * previously had only a hash opclass.
608 */
609 typentry->flags &= ~(TCFLAGS_CHECKED_EQ_OPR |
614 }
615
616 /*
617 * If we need to look up equality operator, and there's no btree opclass,
618 * force lookup of hash opclass.
619 */
620 if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
621 !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR) &&
622 typentry->btree_opf == InvalidOid)
624
629 !(typentry->flags & TCFLAGS_CHECKED_HASH_OPCLASS))
630 {
631 Oid opclass;
632
633 opclass = GetDefaultOpClass(type_id, HASH_AM_OID);
634 if (OidIsValid(opclass))
635 {
636 typentry->hash_opf = get_opclass_family(opclass);
637 typentry->hash_opintype = get_opclass_input_type(opclass);
638 }
639 else
640 {
641 typentry->hash_opf = typentry->hash_opintype = InvalidOid;
642 }
643
644 /*
645 * Reset information derived from hash opclass. We do *not* reset the
646 * eq_opr; if we already found one from the btree opclass, that
647 * decision is still good.
648 */
649 typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC |
652 }
653
654 /*
655 * Look for requested operators and functions, if we haven't already.
656 */
657 if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
658 !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR))
659 {
660 Oid eq_opr = InvalidOid;
661
662 if (typentry->btree_opf != InvalidOid)
663 eq_opr = get_opfamily_member(typentry->btree_opf,
664 typentry->btree_opintype,
665 typentry->btree_opintype,
667 if (eq_opr == InvalidOid &&
668 typentry->hash_opf != InvalidOid)
669 eq_opr = get_opfamily_member(typentry->hash_opf,
670 typentry->hash_opintype,
671 typentry->hash_opintype,
673
674 /*
675 * If the proposed equality operator is array_eq or record_eq, check
676 * to see if the element type or column types support equality. If
677 * not, array_eq or record_eq would fail at runtime, so we don't want
678 * to report that the type has equality. (We can omit similar
679 * checking for ranges and multiranges because ranges can't be created
680 * in the first place unless their subtypes support equality.)
681 */
682 if (eq_opr == ARRAY_EQ_OP &&
684 eq_opr = InvalidOid;
685 else if (eq_opr == RECORD_EQ_OP &&
687 eq_opr = InvalidOid;
688
689 /* Force update of eq_opr_finfo only if we're changing state */
690 if (typentry->eq_opr != eq_opr)
691 typentry->eq_opr_finfo.fn_oid = InvalidOid;
692
693 typentry->eq_opr = eq_opr;
694
695 /*
696 * Reset info about hash functions whenever we pick up new info about
697 * equality operator. This is so we can ensure that the hash
698 * functions match the operator.
699 */
700 typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC |
702 typentry->flags |= TCFLAGS_CHECKED_EQ_OPR;
703 }
704 if ((flags & TYPECACHE_LT_OPR) &&
705 !(typentry->flags & TCFLAGS_CHECKED_LT_OPR))
706 {
707 Oid lt_opr = InvalidOid;
708
709 if (typentry->btree_opf != InvalidOid)
710 lt_opr = get_opfamily_member(typentry->btree_opf,
711 typentry->btree_opintype,
712 typentry->btree_opintype,
714
715 /*
716 * As above, make sure array_cmp or record_cmp will succeed; but again
717 * we need no special check for ranges or multiranges.
718 */
719 if (lt_opr == ARRAY_LT_OP &&
720 !array_element_has_compare(typentry))
721 lt_opr = InvalidOid;
722 else if (lt_opr == RECORD_LT_OP &&
724 lt_opr = InvalidOid;
725
726 typentry->lt_opr = lt_opr;
727 typentry->flags |= TCFLAGS_CHECKED_LT_OPR;
728 }
729 if ((flags & TYPECACHE_GT_OPR) &&
730 !(typentry->flags & TCFLAGS_CHECKED_GT_OPR))
731 {
732 Oid gt_opr = InvalidOid;
733
734 if (typentry->btree_opf != InvalidOid)
735 gt_opr = get_opfamily_member(typentry->btree_opf,
736 typentry->btree_opintype,
737 typentry->btree_opintype,
739
740 /*
741 * As above, make sure array_cmp or record_cmp will succeed; but again
742 * we need no special check for ranges or multiranges.
743 */
744 if (gt_opr == ARRAY_GT_OP &&
745 !array_element_has_compare(typentry))
746 gt_opr = InvalidOid;
747 else if (gt_opr == RECORD_GT_OP &&
749 gt_opr = InvalidOid;
750
751 typentry->gt_opr = gt_opr;
752 typentry->flags |= TCFLAGS_CHECKED_GT_OPR;
753 }
755 !(typentry->flags & TCFLAGS_CHECKED_CMP_PROC))
756 {
757 Oid cmp_proc = InvalidOid;
758
759 if (typentry->btree_opf != InvalidOid)
760 cmp_proc = get_opfamily_proc(typentry->btree_opf,
761 typentry->btree_opintype,
762 typentry->btree_opintype,
764
765 /*
766 * As above, make sure array_cmp or record_cmp will succeed; but again
767 * we need no special check for ranges or multiranges.
768 */
769 if (cmp_proc == F_BTARRAYCMP &&
770 !array_element_has_compare(typentry))
771 cmp_proc = InvalidOid;
772 else if (cmp_proc == F_BTRECORDCMP &&
774 cmp_proc = InvalidOid;
775
776 /* Force update of cmp_proc_finfo only if we're changing state */
777 if (typentry->cmp_proc != cmp_proc)
778 typentry->cmp_proc_finfo.fn_oid = InvalidOid;
779
780 typentry->cmp_proc = cmp_proc;
781 typentry->flags |= TCFLAGS_CHECKED_CMP_PROC;
782 }
784 !(typentry->flags & TCFLAGS_CHECKED_HASH_PROC))
785 {
786 Oid hash_proc = InvalidOid;
787
788 /*
789 * We insist that the eq_opr, if one has been determined, match the
790 * hash opclass; else report there is no hash function.
791 */
792 if (typentry->hash_opf != InvalidOid &&
793 (!OidIsValid(typentry->eq_opr) ||
794 typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
795 typentry->hash_opintype,
796 typentry->hash_opintype,
798 hash_proc = get_opfamily_proc(typentry->hash_opf,
799 typentry->hash_opintype,
800 typentry->hash_opintype,
802
803 /*
804 * As above, make sure hash_array, hash_record, hash_range, or
805 * hash_multirange will succeed. Here we do need to check the range
806 * cases.
807 */
808 if (hash_proc == F_HASH_ARRAY &&
809 !array_element_has_hashing(typentry))
810 hash_proc = InvalidOid;
811 else if (hash_proc == F_HASH_RECORD &&
813 hash_proc = InvalidOid;
814 else if (hash_proc == F_HASH_RANGE &&
815 !range_element_has_hashing(typentry))
816 hash_proc = InvalidOid;
817 else if (hash_proc == F_HASH_MULTIRANGE &&
819 hash_proc = InvalidOid;
820
821 /* Force update of hash_proc_finfo only if we're changing state */
822 if (typentry->hash_proc != hash_proc)
824
825 typentry->hash_proc = hash_proc;
826 typentry->flags |= TCFLAGS_CHECKED_HASH_PROC;
827 }
828 if ((flags & (TYPECACHE_HASH_EXTENDED_PROC |
831 {
832 Oid hash_extended_proc = InvalidOid;
833
834 /*
835 * We insist that the eq_opr, if one has been determined, match the
836 * hash opclass; else report there is no hash function.
837 */
838 if (typentry->hash_opf != InvalidOid &&
839 (!OidIsValid(typentry->eq_opr) ||
840 typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
841 typentry->hash_opintype,
842 typentry->hash_opintype,
844 hash_extended_proc = get_opfamily_proc(typentry->hash_opf,
845 typentry->hash_opintype,
846 typentry->hash_opintype,
848
849 /*
850 * As above, make sure hash_array_extended, hash_record_extended,
851 * hash_range_extended, or hash_multirange_extended will succeed.
852 */
853 if (hash_extended_proc == F_HASH_ARRAY_EXTENDED &&
855 hash_extended_proc = InvalidOid;
856 else if (hash_extended_proc == F_HASH_RECORD_EXTENDED &&
858 hash_extended_proc = InvalidOid;
859 else if (hash_extended_proc == F_HASH_RANGE_EXTENDED &&
861 hash_extended_proc = InvalidOid;
862 else if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
864 hash_extended_proc = InvalidOid;
865
866 /* Force update of proc finfo only if we're changing state */
867 if (typentry->hash_extended_proc != hash_extended_proc)
869
870 typentry->hash_extended_proc = hash_extended_proc;
872 }
873
874 /*
875 * Set up fmgr lookup info as requested
876 *
877 * Note: we tell fmgr the finfo structures live in CacheMemoryContext,
878 * which is not quite right (they're really in the hash table's private
879 * memory context) but this will do for our purposes.
880 *
881 * Note: the code above avoids invalidating the finfo structs unless the
882 * referenced operator/function OID actually changes. This is to prevent
883 * unnecessary leakage of any subsidiary data attached to an finfo, since
884 * that would cause session-lifespan memory leaks.
885 */
886 if ((flags & TYPECACHE_EQ_OPR_FINFO) &&
887 typentry->eq_opr_finfo.fn_oid == InvalidOid &&
888 typentry->eq_opr != InvalidOid)
889 {
891
892 eq_opr_func = get_opcode(typentry->eq_opr);
893 if (eq_opr_func != InvalidOid)
896 }
897 if ((flags & TYPECACHE_CMP_PROC_FINFO) &&
898 typentry->cmp_proc_finfo.fn_oid == InvalidOid &&
899 typentry->cmp_proc != InvalidOid)
900 {
901 fmgr_info_cxt(typentry->cmp_proc, &typentry->cmp_proc_finfo,
903 }
904 if ((flags & TYPECACHE_HASH_PROC_FINFO) &&
905 typentry->hash_proc_finfo.fn_oid == InvalidOid &&
906 typentry->hash_proc != InvalidOid)
907 {
908 fmgr_info_cxt(typentry->hash_proc, &typentry->hash_proc_finfo,
910 }
913 typentry->hash_extended_proc != InvalidOid)
914 {
916 &typentry->hash_extended_proc_finfo,
918 }
919
920 /*
921 * If it's a composite type (row type), get tupdesc if requested
922 */
923 if ((flags & TYPECACHE_TUPDESC) &&
924 typentry->tupDesc == NULL &&
925 typentry->typtype == TYPTYPE_COMPOSITE)
926 {
927 load_typcache_tupdesc(typentry);
928 }
929
930 /*
931 * If requested, get information about a range type
932 *
933 * This includes making sure that the basic info about the range element
934 * type is up-to-date.
935 */
936 if ((flags & TYPECACHE_RANGE_INFO) &&
937 typentry->typtype == TYPTYPE_RANGE)
938 {
939 if (typentry->rngelemtype == NULL)
940 load_rangetype_info(typentry);
941 else if (!(typentry->rngelemtype->flags & TCFLAGS_HAVE_PG_TYPE_DATA))
942 (void) lookup_type_cache(typentry->rngelemtype->type_id, 0);
943 }
944
945 /*
946 * If requested, get information about a multirange type
947 */
948 if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
949 typentry->rngtype == NULL &&
950 typentry->typtype == TYPTYPE_MULTIRANGE)
951 {
952 load_multirangetype_info(typentry);
953 }
954
955 /*
956 * If requested, get information about a domain type
957 */
958 if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
959 typentry->domainBaseType == InvalidOid &&
960 typentry->typtype == TYPTYPE_DOMAIN)
961 {
962 typentry->domainBaseTypmod = -1;
963 typentry->domainBaseType =
964 getBaseTypeAndTypmod(type_id, &typentry->domainBaseTypmod);
965 }
966 if ((flags & TYPECACHE_DOMAIN_CONSTR_INFO) &&
967 (typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
968 typentry->typtype == TYPTYPE_DOMAIN)
969 {
970 load_domaintype_info(typentry);
971 }
972
973 INJECTION_POINT("typecache-before-rel-type-cache-insert", NULL);
974
977
979
980 return typentry;
981}
982
983/*
984 * load_typcache_tupdesc --- helper routine to set up composite type's tupDesc
985 */
986static void
988{
989 Relation rel;
990
991 if (!OidIsValid(typentry->typrelid)) /* should not happen */
992 elog(ERROR, "invalid typrelid for composite type %u",
993 typentry->type_id);
994 rel = relation_open(typentry->typrelid, AccessShareLock);
995 Assert(rel->rd_rel->reltype == typentry->type_id);
996
997 /*
998 * Link to the tupdesc and increment its refcount (we assert it's a
999 * refcounted descriptor). We don't use IncrTupleDescRefCount() for this,
1000 * because the reference mustn't be entered in the current resource owner;
1001 * it can outlive the current query.
1002 */
1003 typentry->tupDesc = RelationGetDescr(rel);
1004
1005 Assert(typentry->tupDesc->tdrefcount > 0);
1006 typentry->tupDesc->tdrefcount++;
1007
1008 /*
1009 * In future, we could take some pains to not change tupDesc_identifier if
1010 * the tupdesc didn't really change; but for now it's not worth it.
1011 */
1013
1015}
1016
1017/*
1018 * load_rangetype_info --- helper routine to set up range type information
1019 */
1020static void
1022{
1024 HeapTuple tup;
1030 Oid opcintype;
1031 Oid cmpFnOid;
1032
1033 /* get information from pg_range */
1035 /* should not fail, since we already checked typtype ... */
1036 if (!HeapTupleIsValid(tup))
1037 elog(ERROR, "cache lookup failed for range type %u",
1038 typentry->type_id);
1040
1041 subtypeOid = pg_range->rngsubtype;
1042 typentry->rng_collation = pg_range->rngcollation;
1043 opclassOid = pg_range->rngsubopc;
1044 canonicalOid = pg_range->rngcanonical;
1045 subdiffOid = pg_range->rngsubdiff;
1046
1048
1049 /* get opclass properties and look up the comparison function */
1052 typentry->rng_opfamily = opfamilyOid;
1053
1054 cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype,
1055 BTORDER_PROC);
1057 elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
1058 BTORDER_PROC, opcintype, opcintype, opfamilyOid);
1059
1060 /* set up cached fmgrinfo structs */
1069
1070 /* Lastly, set up link to the element type --- this marks data valid */
1072}
1073
1074/*
1075 * load_multirangetype_info --- helper routine to set up multirange type
1076 * information
1077 */
1078static void
1080{
1082
1085 elog(ERROR, "cache lookup failed for multirange type %u",
1086 typentry->type_id);
1087
1089}
1090
1091/*
1092 * load_domaintype_info --- helper routine to set up domain constraint info
1093 *
1094 * Note: we assume we're called in a relatively short-lived context, so it's
1095 * okay to leak data into the current context while scanning pg_constraint.
1096 * We build the new DomainConstraintCache data in a context underneath
1097 * CurrentMemoryContext, and reparent it under CacheMemoryContext when
1098 * complete.
1099 */
1100static void
1102{
1103 Oid typeOid = typentry->type_id;
1105 bool notNull = false;
1107 int cconslen;
1110
1111 /*
1112 * If we're here, any existing constraint info is stale, so release it.
1113 * For safety, be sure to null the link before trying to delete the data.
1114 */
1115 if (typentry->domainData)
1116 {
1117 dcc = typentry->domainData;
1118 typentry->domainData = NULL;
1119 decr_dcc_refcount(dcc);
1120 }
1121
1122 /*
1123 * We try to optimize the common case of no domain constraints, so don't
1124 * create the dcc object and context until we find a constraint. Likewise
1125 * for the temp sorting array.
1126 */
1127 dcc = NULL;
1128 ccons = NULL;
1129 cconslen = 0;
1130
1131 /*
1132 * Scan pg_constraint for relevant constraints. We want to find
1133 * constraints for not just this domain, but any ancestor domains, so the
1134 * outer loop crawls up the domain stack.
1135 */
1137
1138 for (;;)
1139 {
1140 HeapTuple tup;
1143 int nccons = 0;
1144 ScanKeyData key[1];
1145 SysScanDesc scan;
1146
1148 if (!HeapTupleIsValid(tup))
1149 elog(ERROR, "cache lookup failed for type %u", typeOid);
1151
1152 if (typTup->typtype != TYPTYPE_DOMAIN)
1153 {
1154 /* Not a domain, so done */
1156 break;
1157 }
1158
1159 /* Test for NOT NULL Constraint */
1160 if (typTup->typnotnull)
1161 notNull = true;
1162
1163 /* Look for CHECK Constraints on this domain */
1164 ScanKeyInit(&key[0],
1167 ObjectIdGetDatum(typeOid));
1168
1170 NULL, 1, key);
1171
1173 {
1175 Datum val;
1176 bool isNull;
1177 char *constring;
1178 Expr *check_expr;
1180
1181 /* Ignore non-CHECK constraints */
1182 if (c->contype != CONSTRAINT_CHECK)
1183 continue;
1184
1185 /* Not expecting conbin to be NULL, but we'll test for it anyway */
1187 conRel->rd_att, &isNull);
1188 if (isNull)
1189 elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
1190 NameStr(typTup->typname), NameStr(c->conname));
1191
1192 /* Create the DomainConstraintCache object and context if needed */
1193 if (dcc == NULL)
1194 {
1195 MemoryContext cxt;
1196
1198 "Domain constraints",
1200 dcc = (DomainConstraintCache *)
1202 dcc->constraints = NIL;
1203 dcc->dccContext = cxt;
1204 dcc->dccRefCount = 0;
1205 }
1206
1207 /* Convert conbin to a node tree, still in caller's context */
1209 check_expr = (Expr *) stringToNode(constring);
1210
1211 /*
1212 * Plan the expression, since ExecInitExpr will expect that.
1213 *
1214 * Note: caching the result of expression_planner() is not very
1215 * good practice. Ideally we'd use a CachedExpression here so
1216 * that we would react promptly to, eg, changes in inlined
1217 * functions. However, because we don't support mutable domain
1218 * CHECK constraints, it's not really clear that it's worth the
1219 * extra overhead to do that.
1220 */
1221 check_expr = expression_planner(check_expr);
1222
1223 /* Create only the minimally needed stuff in dccContext */
1225
1228 r->name = pstrdup(NameStr(c->conname));
1229 r->check_expr = copyObject(check_expr);
1230 r->check_exprstate = NULL;
1231
1233
1234 /* Accumulate constraints in an array, for sorting below */
1235 if (ccons == NULL)
1236 {
1237 cconslen = 8;
1240 }
1241 else if (nccons >= cconslen)
1242 {
1243 cconslen *= 2;
1246 }
1247 ccons[nccons++] = r;
1248 }
1249
1250 systable_endscan(scan);
1251
1252 if (nccons > 0)
1253 {
1254 /*
1255 * Sort the items for this domain, so that CHECKs are applied in a
1256 * deterministic order.
1257 */
1258 if (nccons > 1)
1260
1261 /*
1262 * Now attach them to the overall list. Use lcons() here because
1263 * constraints of parent domains should be applied earlier.
1264 */
1266 while (nccons > 0)
1267 dcc->constraints = lcons(ccons[--nccons], dcc->constraints);
1269 }
1270
1271 /* loop to next domain in stack */
1272 typeOid = typTup->typbasetype;
1274 }
1275
1277
1278 /*
1279 * Only need to add one NOT NULL check regardless of how many domains in
1280 * the stack request it.
1281 */
1282 if (notNull)
1283 {
1285
1286 /* Create the DomainConstraintCache object and context if needed */
1287 if (dcc == NULL)
1288 {
1289 MemoryContext cxt;
1290
1292 "Domain constraints",
1294 dcc = (DomainConstraintCache *)
1296 dcc->constraints = NIL;
1297 dcc->dccContext = cxt;
1298 dcc->dccRefCount = 0;
1299 }
1300
1301 /* Create node trees in DomainConstraintCache's context */
1303
1305
1307 r->name = pstrdup("NOT NULL");
1308 r->check_expr = NULL;
1309 r->check_exprstate = NULL;
1310
1311 /* lcons to apply the nullness check FIRST */
1312 dcc->constraints = lcons(r, dcc->constraints);
1313
1315 }
1316
1317 /*
1318 * If we made a constraint object, move it into CacheMemoryContext and
1319 * attach it to the typcache entry.
1320 */
1321 if (dcc)
1322 {
1324 typentry->domainData = dcc;
1325 dcc->dccRefCount++; /* count the typcache's reference */
1326 }
1327
1328 /* Either way, the typcache entry's domain data is now valid. */
1330}
1331
1332/*
1333 * qsort comparator to sort DomainConstraintState pointers by name
1334 */
1335static int
1336dcs_cmp(const void *a, const void *b)
1337{
1338 const DomainConstraintState *const *ca = (const DomainConstraintState *const *) a;
1339 const DomainConstraintState *const *cb = (const DomainConstraintState *const *) b;
1340
1341 return strcmp((*ca)->name, (*cb)->name);
1342}
1343
1344/*
1345 * decr_dcc_refcount --- decrement a DomainConstraintCache's refcount,
1346 * and free it if no references remain
1347 */
1348static void
1350{
1351 Assert(dcc->dccRefCount > 0);
1352 if (--(dcc->dccRefCount) <= 0)
1354}
1355
1356/*
1357 * Context reset/delete callback for a DomainConstraintRef
1358 */
1359static void
1361{
1363 DomainConstraintCache *dcc = ref->dcc;
1364
1365 /* Paranoia --- be sure link is nulled before trying to release */
1366 if (dcc)
1367 {
1368 ref->constraints = NIL;
1369 ref->dcc = NULL;
1370 decr_dcc_refcount(dcc);
1371 }
1372}
1373
1374/*
1375 * prep_domain_constraints --- prepare domain constraints for execution
1376 *
1377 * The expression trees stored in the DomainConstraintCache's list are
1378 * converted to executable expression state trees stored in execctx.
1379 */
1380static List *
1382{
1383 List *result = NIL;
1385 ListCell *lc;
1386
1388
1389 foreach(lc, constraints)
1390 {
1393
1395 newr->constrainttype = r->constrainttype;
1396 newr->name = r->name;
1397 newr->check_expr = r->check_expr;
1398 newr->check_exprstate = ExecInitExpr(r->check_expr, NULL);
1399
1401 }
1402
1404
1405 return result;
1406}
1407
1408/*
1409 * InitDomainConstraintRef --- initialize a DomainConstraintRef struct
1410 *
1411 * Caller must tell us the MemoryContext in which the DomainConstraintRef
1412 * lives. The ref will be cleaned up when that context is reset/deleted.
1413 *
1414 * Caller must also tell us whether it wants check_exprstate fields to be
1415 * computed in the DomainConstraintState nodes attached to this ref.
1416 * If it doesn't, we need not make a copy of the DomainConstraintState list.
1417 */
1418void
1420 MemoryContext refctx, bool need_exprstate)
1421{
1422 /* Look up the typcache entry --- we assume it survives indefinitely */
1424 ref->need_exprstate = need_exprstate;
1425 /* For safety, establish the callback before acquiring a refcount */
1426 ref->refctx = refctx;
1427 ref->dcc = NULL;
1428 ref->callback.func = dccref_deletion_callback;
1429 ref->callback.arg = ref;
1430 MemoryContextRegisterResetCallback(refctx, &ref->callback);
1431 /* Acquire refcount if there are constraints, and set up exported list */
1432 if (ref->tcache->domainData)
1433 {
1434 ref->dcc = ref->tcache->domainData;
1435 ref->dcc->dccRefCount++;
1436 if (ref->need_exprstate)
1437 ref->constraints = prep_domain_constraints(ref->dcc->constraints,
1438 ref->refctx);
1439 else
1440 ref->constraints = ref->dcc->constraints;
1441 }
1442 else
1443 ref->constraints = NIL;
1444}
1445
1446/*
1447 * UpdateDomainConstraintRef --- recheck validity of domain constraint info
1448 *
1449 * If the domain's constraint set changed, ref->constraints is updated to
1450 * point at a new list of cached constraints.
1451 *
1452 * In the normal case where nothing happened to the domain, this is cheap
1453 * enough that it's reasonable (and expected) to check before *each* use
1454 * of the constraint info.
1455 */
1456void
1458{
1459 TypeCacheEntry *typentry = ref->tcache;
1460
1461 /* Make sure typcache entry's data is up to date */
1462 if ((typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
1463 typentry->typtype == TYPTYPE_DOMAIN)
1464 load_domaintype_info(typentry);
1465
1466 /* Transfer to ref object if there's new info, adjusting refcounts */
1467 if (ref->dcc != typentry->domainData)
1468 {
1469 /* Paranoia --- be sure link is nulled before trying to release */
1470 DomainConstraintCache *dcc = ref->dcc;
1471
1472 if (dcc)
1473 {
1474 /*
1475 * Note: we just leak the previous list of executable domain
1476 * constraints. Alternatively, we could keep those in a child
1477 * context of ref->refctx and free that context at this point.
1478 * However, in practice this code path will be taken so seldom
1479 * that the extra bookkeeping for a child context doesn't seem
1480 * worthwhile; we'll just allow a leak for the lifespan of refctx.
1481 */
1482 ref->constraints = NIL;
1483 ref->dcc = NULL;
1484 decr_dcc_refcount(dcc);
1485 }
1486 dcc = typentry->domainData;
1487 if (dcc)
1488 {
1489 ref->dcc = dcc;
1490 dcc->dccRefCount++;
1491 if (ref->need_exprstate)
1492 ref->constraints = prep_domain_constraints(dcc->constraints,
1493 ref->refctx);
1494 else
1495 ref->constraints = dcc->constraints;
1496 }
1497 }
1498}
1499
1500/*
1501 * DomainHasConstraints --- utility routine to check if a domain has constraints
1502 *
1503 * Returns true if the domain has any constraints at all. If has_volatile
1504 * is not NULL, also checks whether any CHECK constraint contains a volatile
1505 * expression and sets *has_volatile accordingly.
1506 *
1507 * This is defined to return false, not fail, if type is not a domain.
1508 */
1509bool
1511{
1512 TypeCacheEntry *typentry;
1513
1514 /*
1515 * Note: a side effect is to cause the typcache's domain data to become
1516 * valid. This is fine since we'll likely need it soon if there is any.
1517 */
1519
1520 if (typentry->domainData == NULL)
1521 return false;
1522
1523 if (has_volatile)
1524 {
1525 *has_volatile = false;
1526
1528 typentry->domainData->constraints)
1529 {
1530 if (constrstate->constrainttype == DOM_CONSTRAINT_CHECK &&
1532 {
1533 *has_volatile = true;
1534 break;
1535 }
1536 }
1537 }
1538
1539 return true;
1540}
1541
1542
1543/*
1544 * array_element_has_equality and friends are helper routines to check
1545 * whether we should believe that array_eq and related functions will work
1546 * on the given array type or composite type.
1547 *
1548 * The logic above may call these repeatedly on the same type entry, so we
1549 * make use of the typentry->flags field to cache the results once known.
1550 * Also, we assume that we'll probably want all these facts about the type
1551 * if we want any, so we cache them all using only one lookup of the
1552 * component datatype(s).
1553 */
1554
1555static bool
1557{
1558 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1560 return (typentry->flags & TCFLAGS_HAVE_ELEM_EQUALITY) != 0;
1561}
1562
1563static bool
1565{
1566 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1568 return (typentry->flags & TCFLAGS_HAVE_ELEM_COMPARE) != 0;
1569}
1570
1571static bool
1573{
1574 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1576 return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1577}
1578
1579static bool
1581{
1582 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1584 return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
1585}
1586
1587static void
1589{
1591
1592 if (OidIsValid(elem_type))
1593 {
1595
1601 if (OidIsValid(elementry->eq_opr))
1602 typentry->flags |= TCFLAGS_HAVE_ELEM_EQUALITY;
1603 if (OidIsValid(elementry->cmp_proc))
1604 typentry->flags |= TCFLAGS_HAVE_ELEM_COMPARE;
1605 if (OidIsValid(elementry->hash_proc))
1606 typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1607 if (OidIsValid(elementry->hash_extended_proc))
1609 }
1611}
1612
1613/*
1614 * Likewise, some helper functions for composite types.
1615 */
1616
1617static bool
1619{
1620 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1622 return (typentry->flags & TCFLAGS_HAVE_FIELD_EQUALITY) != 0;
1623}
1624
1625static bool
1627{
1628 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1630 return (typentry->flags & TCFLAGS_HAVE_FIELD_COMPARE) != 0;
1631}
1632
1633static bool
1635{
1636 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1638 return (typentry->flags & TCFLAGS_HAVE_FIELD_HASHING) != 0;
1639}
1640
1641static bool
1643{
1644 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1646 return (typentry->flags & TCFLAGS_HAVE_FIELD_EXTENDED_HASHING) != 0;
1647}
1648
1649static void
1651{
1652 /*
1653 * For type RECORD, we can't really tell what will work, since we don't
1654 * have access here to the specific anonymous type. Just assume that
1655 * equality and comparison will (we may get a failure at runtime). We
1656 * could also claim that hashing works, but then if code that has the
1657 * option between a comparison-based (sort-based) and a hash-based plan
1658 * chooses hashing, stuff could fail that would otherwise work if it chose
1659 * a comparison-based plan. In practice more types support comparison
1660 * than hashing.
1661 */
1662 if (typentry->type_id == RECORDOID)
1663 {
1664 typentry->flags |= (TCFLAGS_HAVE_FIELD_EQUALITY |
1666 }
1667 else if (typentry->typtype == TYPTYPE_COMPOSITE)
1668 {
1669 TupleDesc tupdesc;
1670 int newflags;
1671 int i;
1672
1673 /* Fetch composite type's tupdesc if we don't have it already */
1674 if (typentry->tupDesc == NULL)
1675 load_typcache_tupdesc(typentry);
1676 tupdesc = typentry->tupDesc;
1677
1678 /* Must bump the refcount while we do additional catalog lookups */
1679 IncrTupleDescRefCount(tupdesc);
1680
1681 /* Have each property if all non-dropped fields have the property */
1686 for (i = 0; i < tupdesc->natts; i++)
1687 {
1689 Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
1690
1691 if (attr->attisdropped)
1692 continue;
1693
1694 fieldentry = lookup_type_cache(attr->atttypid,
1699 if (!OidIsValid(fieldentry->eq_opr))
1701 if (!OidIsValid(fieldentry->cmp_proc))
1703 if (!OidIsValid(fieldentry->hash_proc))
1705 if (!OidIsValid(fieldentry->hash_extended_proc))
1707
1708 /* We can drop out of the loop once we disprove all bits */
1709 if (newflags == 0)
1710 break;
1711 }
1712 typentry->flags |= newflags;
1713
1714 DecrTupleDescRefCount(tupdesc);
1715 }
1716 else if (typentry->typtype == TYPTYPE_DOMAIN)
1717 {
1718 /* If it's domain over composite, copy base type's properties */
1720
1721 /* load up basetype info if we didn't already */
1722 if (typentry->domainBaseType == InvalidOid)
1723 {
1724 typentry->domainBaseTypmod = -1;
1725 typentry->domainBaseType =
1726 getBaseTypeAndTypmod(typentry->type_id,
1727 &typentry->domainBaseTypmod);
1728 }
1734 if (baseentry->typtype == TYPTYPE_COMPOSITE)
1735 {
1737 typentry->flags |= baseentry->flags & (TCFLAGS_HAVE_FIELD_EQUALITY |
1741 }
1742 }
1744}
1745
1746/*
1747 * Likewise, some helper functions for range and multirange types.
1748 *
1749 * We can borrow the flag bits for array element properties to use for range
1750 * element properties, since those flag bits otherwise have no use in a
1751 * range or multirange type's typcache entry.
1752 */
1753
1754static bool
1756{
1757 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1759 return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1760}
1761
1762static bool
1764{
1765 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1767 return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
1768}
1769
1770static void
1772{
1773 /* load up subtype link if we didn't already */
1774 if (typentry->rngelemtype == NULL &&
1775 typentry->typtype == TYPTYPE_RANGE)
1776 load_rangetype_info(typentry);
1777
1778 if (typentry->rngelemtype != NULL)
1779 {
1781
1782 /* might need to calculate subtype's hash function properties */
1786 if (OidIsValid(elementry->hash_proc))
1787 typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1788 if (OidIsValid(elementry->hash_extended_proc))
1790 }
1792}
1793
1794static bool
1796{
1797 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1799 return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1800}
1801
1802static bool
1804{
1805 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1807 return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
1808}
1809
1810static void
1812{
1813 /* load up range link if we didn't already */
1814 if (typentry->rngtype == NULL &&
1815 typentry->typtype == TYPTYPE_MULTIRANGE)
1816 load_multirangetype_info(typentry);
1817
1818 if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
1819 {
1821
1822 /* might need to calculate subtype's hash function properties */
1826 if (OidIsValid(elementry->hash_proc))
1827 typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1828 if (OidIsValid(elementry->hash_extended_proc))
1830 }
1832}
1833
1834/*
1835 * Make sure that RecordCacheArray and RecordIdentifierArray are large enough
1836 * to store 'typmod'.
1837 */
1838static void
1840{
1841 if (RecordCacheArray == NULL)
1842 {
1845 64 * sizeof(RecordCacheArrayEntry));
1847 }
1848
1849 if (typmod >= RecordCacheArrayLen)
1850 {
1851 int32 newlen = pg_nextpower2_32(typmod + 1);
1852
1856 newlen);
1858 }
1859}
1860
1861/*
1862 * lookup_rowtype_tupdesc_internal --- internal routine to lookup a rowtype
1863 *
1864 * Same API as lookup_rowtype_tupdesc_noerror, but the returned tupdesc
1865 * hasn't had its refcount bumped.
1866 */
1867static TupleDesc
1868lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
1869{
1870 if (type_id != RECORDOID)
1871 {
1872 /*
1873 * It's a named composite type, so use the regular typcache.
1874 */
1875 TypeCacheEntry *typentry;
1876
1877 typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
1878 if (typentry->tupDesc == NULL && !noError)
1879 ereport(ERROR,
1881 errmsg("type %s is not composite",
1882 format_type_be(type_id))));
1883 return typentry->tupDesc;
1884 }
1885 else
1886 {
1887 /*
1888 * It's a transient record type, so look in our record-type table.
1889 */
1890 if (typmod >= 0)
1891 {
1892 /* It is already in our local cache? */
1893 if (typmod < RecordCacheArrayLen &&
1894 RecordCacheArray[typmod].tupdesc != NULL)
1895 return RecordCacheArray[typmod].tupdesc;
1896
1897 /* Are we attached to a shared record typmod registry? */
1899 {
1901
1902 /* Try to find it in the shared typmod index. */
1904 &typmod, false);
1905 if (entry != NULL)
1906 {
1907 TupleDesc tupdesc;
1908
1909 tupdesc = (TupleDesc)
1911 entry->shared_tupdesc);
1912 Assert(typmod == tupdesc->tdtypmod);
1913
1914 /* We may need to extend the local RecordCacheArray. */
1916
1917 /*
1918 * Our local array can now point directly to the TupleDesc
1919 * in shared memory, which is non-reference-counted.
1920 */
1921 RecordCacheArray[typmod].tupdesc = tupdesc;
1922 Assert(tupdesc->tdrefcount == -1);
1923
1924 /*
1925 * We don't share tupdesc identifiers across processes, so
1926 * assign one locally.
1927 */
1929
1931 entry);
1932
1933 return RecordCacheArray[typmod].tupdesc;
1934 }
1935 }
1936 }
1937
1938 if (!noError)
1939 ereport(ERROR,
1941 errmsg("record type has not been registered")));
1942 return NULL;
1943 }
1944}
1945
1946/*
1947 * lookup_rowtype_tupdesc
1948 *
1949 * Given a typeid/typmod that should describe a known composite type,
1950 * return the tuple descriptor for the type. Will ereport on failure.
1951 * (Use ereport because this is reachable with user-specified OIDs,
1952 * for example from record_in().)
1953 *
1954 * Note: on success, we increment the refcount of the returned TupleDesc,
1955 * and log the reference in CurrentResourceOwner. Caller must call
1956 * ReleaseTupleDesc when done using the tupdesc. (There are some
1957 * cases in which the returned tupdesc is not refcounted, in which
1958 * case PinTupleDesc/ReleaseTupleDesc are no-ops; but in these cases
1959 * the tupdesc is guaranteed to live till process exit.)
1960 */
1962lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
1963{
1964 TupleDesc tupDesc;
1965
1966 tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
1967 PinTupleDesc(tupDesc);
1968 return tupDesc;
1969}
1970
1971/*
1972 * lookup_rowtype_tupdesc_noerror
1973 *
1974 * As above, but if the type is not a known composite type and noError
1975 * is true, returns NULL instead of ereport'ing. (Note that if a bogus
1976 * type_id is passed, you'll get an ereport anyway.)
1977 */
1979lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
1980{
1981 TupleDesc tupDesc;
1982
1983 tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
1984 if (tupDesc != NULL)
1985 PinTupleDesc(tupDesc);
1986 return tupDesc;
1987}
1988
1989/*
1990 * lookup_rowtype_tupdesc_copy
1991 *
1992 * Like lookup_rowtype_tupdesc(), but the returned TupleDesc has been
1993 * copied into the CurrentMemoryContext and is not reference-counted.
1994 */
1996lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod)
1997{
1998 TupleDesc tmp;
1999
2000 tmp = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
2001 return CreateTupleDescCopyConstr(tmp);
2002}
2003
2004/*
2005 * lookup_rowtype_tupdesc_domain
2006 *
2007 * Same as lookup_rowtype_tupdesc_noerror(), except that the type can also be
2008 * a domain over a named composite type; so this is effectively equivalent to
2009 * lookup_rowtype_tupdesc_noerror(getBaseType(type_id), typmod, noError)
2010 * except for being a tad faster.
2011 *
2012 * Note: the reason we don't fold the look-through-domain behavior into plain
2013 * lookup_rowtype_tupdesc() is that we want callers to know they might be
2014 * dealing with a domain. Otherwise they might construct a tuple that should
2015 * be of the domain type, but not apply domain constraints.
2016 */
2018lookup_rowtype_tupdesc_domain(Oid type_id, int32 typmod, bool noError)
2019{
2020 TupleDesc tupDesc;
2021
2022 if (type_id != RECORDOID)
2023 {
2024 /*
2025 * Check for domain or named composite type. We might as well load
2026 * whichever data is needed.
2027 */
2028 TypeCacheEntry *typentry;
2029
2030 typentry = lookup_type_cache(type_id,
2033 if (typentry->typtype == TYPTYPE_DOMAIN)
2035 typentry->domainBaseTypmod,
2036 noError);
2037 if (typentry->tupDesc == NULL && !noError)
2038 ereport(ERROR,
2040 errmsg("type %s is not composite",
2041 format_type_be(type_id))));
2042 tupDesc = typentry->tupDesc;
2043 }
2044 else
2045 tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
2046 if (tupDesc != NULL)
2047 PinTupleDesc(tupDesc);
2048 return tupDesc;
2049}
2050
2051/*
2052 * Hash function for the hash table of RecordCacheEntry.
2053 */
2054static uint32
2055record_type_typmod_hash(const void *data, size_t size)
2056{
2057 const RecordCacheEntry *entry = data;
2058
2059 return hashRowType(entry->tupdesc);
2060}
2061
2062/*
2063 * Match function for the hash table of RecordCacheEntry.
2064 */
2065static int
2066record_type_typmod_compare(const void *a, const void *b, size_t size)
2067{
2068 const RecordCacheEntry *left = a;
2069 const RecordCacheEntry *right = b;
2070
2071 return equalRowTypes(left->tupdesc, right->tupdesc) ? 0 : 1;
2072}
2073
2074/*
2075 * assign_record_type_typmod
2076 *
2077 * Given a tuple descriptor for a RECORD type, find or create a cache entry
2078 * for the type, and set the tupdesc's tdtypmod field to a value that will
2079 * identify this cache entry to lookup_rowtype_tupdesc.
2080 */
2081void
2083{
2086 bool found;
2088
2089 Assert(tupDesc->tdtypeid == RECORDOID);
2090
2091 if (RecordCacheHash == NULL)
2092 {
2093 /* First time through: initialize the hash table */
2094 HASHCTL ctl;
2095
2096 ctl.keysize = sizeof(TupleDesc); /* just the pointer */
2097 ctl.entrysize = sizeof(RecordCacheEntry);
2100 RecordCacheHash = hash_create("Record information cache", 64,
2101 &ctl,
2103
2104 /* Also make sure CacheMemoryContext exists */
2105 if (!CacheMemoryContext)
2107 }
2108
2109 /*
2110 * Find a hashtable entry for this tuple descriptor. We don't use
2111 * HASH_ENTER yet, because if it's missing, we need to make sure that all
2112 * the allocations succeed before we create the new entry.
2113 */
2115 &tupDesc,
2116 HASH_FIND, &found);
2117 if (found && recentry->tupdesc != NULL)
2118 {
2119 tupDesc->tdtypmod = recentry->tupdesc->tdtypmod;
2120 return;
2121 }
2122
2123 /* Not present, so need to manufacture an entry */
2125
2126 /* Look in the SharedRecordTypmodRegistry, if attached */
2128 if (entDesc == NULL)
2129 {
2130 /*
2131 * Make sure we have room before we CreateTupleDescCopy() or advance
2132 * NextRecordTypmod.
2133 */
2135
2136 /* Reference-counted local cache only. */
2137 entDesc = CreateTupleDescCopy(tupDesc);
2138 entDesc->tdrefcount = 1;
2139 entDesc->tdtypmod = NextRecordTypmod++;
2140 }
2141 else
2142 {
2144 }
2145
2147
2148 /* Assign a unique tupdesc identifier, too. */
2150
2151 /* Fully initialized; create the hash table entry */
2153 &tupDesc,
2154 HASH_ENTER, NULL);
2155 recentry->tupdesc = entDesc;
2156
2157 /* Update the caller's tuple descriptor. */
2158 tupDesc->tdtypmod = entDesc->tdtypmod;
2159
2161}
2162
2163/*
2164 * assign_record_type_identifier
2165 *
2166 * Get an identifier, which will be unique over the lifespan of this backend
2167 * process, for the current tuple descriptor of the specified composite type.
2168 * For named composite types, the value is guaranteed to change if the type's
2169 * definition does. For registered RECORD types, the value will not change
2170 * once assigned, since the registered type won't either. If an anonymous
2171 * RECORD type is specified, we return a new identifier on each call.
2172 */
2173uint64
2175{
2176 if (type_id != RECORDOID)
2177 {
2178 /*
2179 * It's a named composite type, so use the regular typcache.
2180 */
2181 TypeCacheEntry *typentry;
2182
2183 typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
2184 if (typentry->tupDesc == NULL)
2185 ereport(ERROR,
2187 errmsg("type %s is not composite",
2188 format_type_be(type_id))));
2189 Assert(typentry->tupDesc_identifier != 0);
2190 return typentry->tupDesc_identifier;
2191 }
2192 else
2193 {
2194 /*
2195 * It's a transient record type, so look in our record-type table.
2196 */
2197 if (typmod >= 0 && typmod < RecordCacheArrayLen &&
2198 RecordCacheArray[typmod].tupdesc != NULL)
2199 {
2200 Assert(RecordCacheArray[typmod].id != 0);
2201 return RecordCacheArray[typmod].id;
2202 }
2203
2204 /* For anonymous or unrecognized record type, generate a new ID */
2205 return ++tupledesc_id_counter;
2206 }
2207}
2208
2209/*
2210 * Return the amount of shmem required to hold a SharedRecordTypmodRegistry.
2211 * This exists only to avoid exposing private innards of
2212 * SharedRecordTypmodRegistry in a header.
2213 */
2214size_t
2216{
2217 return sizeof(SharedRecordTypmodRegistry);
2218}
2219
2220/*
2221 * Initialize 'registry' in a pre-existing shared memory region, which must be
2222 * maximally aligned and have space for SharedRecordTypmodRegistryEstimate()
2223 * bytes.
2224 *
2225 * 'area' will be used to allocate shared memory space as required for the
2226 * typemod registration. The current process, expected to be a leader process
2227 * in a parallel query, will be attached automatically and its current record
2228 * types will be loaded into *registry. While attached, all calls to
2229 * assign_record_type_typmod will use the shared registry. Worker backends
2230 * will need to attach explicitly.
2231 *
2232 * Note that this function takes 'area' and 'segment' as arguments rather than
2233 * accessing them via CurrentSession, because they aren't installed there
2234 * until after this function runs.
2235 */
2236void
2238 dsm_segment *segment,
2239 dsa_area *area)
2240{
2244 int32 typmod;
2245
2247
2248 /* We can't already be attached to a shared registry. */
2252
2254
2255 /* Create the hash table of tuple descriptors indexed by themselves. */
2257
2258 /* Create the hash table of tuple descriptors indexed by typmod. */
2260
2262
2263 /* Initialize the SharedRecordTypmodRegistry. */
2264 registry->record_table_handle = dshash_get_hash_table_handle(record_table);
2265 registry->typmod_table_handle = dshash_get_hash_table_handle(typmod_table);
2267
2268 /*
2269 * Copy all entries from this backend's private registry into the shared
2270 * registry.
2271 */
2272 for (typmod = 0; typmod < NextRecordTypmod; ++typmod)
2273 {
2278 TupleDesc tupdesc;
2279 bool found;
2280
2281 tupdesc = RecordCacheArray[typmod].tupdesc;
2282 if (tupdesc == NULL)
2283 continue;
2284
2285 /* Copy the TupleDesc into shared memory. */
2286 shared_dp = share_tupledesc(area, tupdesc, typmod);
2287
2288 /* Insert into the typmod table. */
2290 &tupdesc->tdtypmod,
2291 &found);
2292 if (found)
2293 elog(ERROR, "cannot create duplicate shared record typmod");
2294 typmod_table_entry->typmod = tupdesc->tdtypmod;
2295 typmod_table_entry->shared_tupdesc = shared_dp;
2297
2298 /* Insert into the record table. */
2299 record_table_key.shared = false;
2300 record_table_key.u.local_tupdesc = tupdesc;
2303 &found);
2304 if (!found)
2305 {
2306 record_table_entry->key.shared = true;
2307 record_table_entry->key.u.shared_tupdesc = shared_dp;
2308 }
2310 }
2311
2312 /*
2313 * Set up the global state that will tell assign_record_type_typmod and
2314 * lookup_rowtype_tupdesc_internal about the shared registry.
2315 */
2319
2320 /*
2321 * We install a detach hook in the leader, but only to handle cleanup on
2322 * failure during GetSessionDsmHandle(). Once GetSessionDsmHandle() pins
2323 * the memory, the leader process will use a shared registry until it
2324 * exits.
2325 */
2327}
2328
2329/*
2330 * Attach to 'registry', which must have been initialized already by another
2331 * backend. Future calls to assign_record_type_typmod and
2332 * lookup_rowtype_tupdesc_internal will use the shared registry until the
2333 * current session is detached.
2334 */
2335void
2337{
2341
2343
2344 /* We can't already be attached to a shared registry. */
2351
2352 /*
2353 * We can't already have typmods in our local cache, because they'd clash
2354 * with those imported by SharedRecordTypmodRegistryInit. This should be
2355 * a freshly started parallel worker. If we ever support worker
2356 * recycling, a worker would need to zap its local cache in between
2357 * servicing different queries, in order to be able to call this and
2358 * synchronize typmods with a new leader; but that's problematic because
2359 * we can't be very sure that record-typmod-related state hasn't escaped
2360 * to anywhere else in the process.
2361 */
2363
2365
2366 /* Attach to the two hash tables. */
2369 registry->record_table_handle,
2373 registry->typmod_table_handle,
2374 NULL);
2375
2377
2378 /*
2379 * Set up detach hook to run at worker exit. Currently this is the same
2380 * as the leader's detach hook, but in future they might need to be
2381 * different.
2382 */
2386
2387 /*
2388 * Set up the session state that will tell assign_record_type_typmod and
2389 * lookup_rowtype_tupdesc_internal about the shared registry.
2390 */
2394}
2395
2396/*
2397 * InvalidateCompositeTypeCacheEntry
2398 * Invalidate particular TypeCacheEntry on Relcache inval callback
2399 *
2400 * Delete the cached tuple descriptor (if any) for the given composite
2401 * type, and reset whatever info we have cached about the composite type's
2402 * comparability.
2403 */
2404static void
2406{
2408
2409 Assert(typentry->typtype == TYPTYPE_COMPOSITE &&
2410 OidIsValid(typentry->typrelid));
2411
2412 hadTupDescOrOpclass = (typentry->tupDesc != NULL) ||
2413 (typentry->flags & TCFLAGS_OPERATOR_FLAGS);
2414
2415 /* Delete tupdesc if we have it */
2416 if (typentry->tupDesc != NULL)
2417 {
2418 /*
2419 * Release our refcount and free the tupdesc if none remain. We can't
2420 * use DecrTupleDescRefCount here because this reference is not logged
2421 * by the current resource owner.
2422 */
2423 Assert(typentry->tupDesc->tdrefcount > 0);
2424 if (--typentry->tupDesc->tdrefcount == 0)
2425 FreeTupleDesc(typentry->tupDesc);
2426 typentry->tupDesc = NULL;
2427
2428 /*
2429 * Also clear tupDesc_identifier, so that anyone watching it will
2430 * realize that the tupdesc has changed.
2431 */
2432 typentry->tupDesc_identifier = 0;
2433 }
2434
2435 /* Reset equality/comparison/hashing validity information */
2436 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2437
2438 /*
2439 * Call delete_rel_type_cache_if_needed() if we actually cleared
2440 * something.
2441 */
2444}
2445
2446/*
2447 * TypeCacheRelCallback
2448 * Relcache inval callback function
2449 *
2450 * Delete the cached tuple descriptor (if any) for the given rel's composite
2451 * type, or for all composite types if relid == InvalidOid. Also reset
2452 * whatever info we have cached about the composite type's comparability.
2453 *
2454 * This is called when a relcache invalidation event occurs for the given
2455 * relid. We can't use syscache to find a type corresponding to the given
2456 * relation because the code can be called outside of transaction. Thus, we
2457 * use the RelIdToTypeIdCacheHash map to locate appropriate typcache entry.
2458 */
2459static void
2461{
2462 TypeCacheEntry *typentry;
2463
2464 /*
2465 * RelIdToTypeIdCacheHash and TypeCacheHash should exist, otherwise this
2466 * callback wouldn't be registered
2467 */
2468 if (OidIsValid(relid))
2469 {
2471
2472 /*
2473 * Find a RelIdToTypeIdCacheHash entry, which should exist as soon as
2474 * corresponding typcache entry has something to clean.
2475 */
2477 &relid,
2478 HASH_FIND, NULL);
2479
2480 if (relentry != NULL)
2481 {
2483 &relentry->composite_typid,
2484 HASH_FIND, NULL);
2485
2486 if (typentry != NULL)
2487 {
2488 Assert(typentry->typtype == TYPTYPE_COMPOSITE);
2489 Assert(relid == typentry->typrelid);
2490
2492 }
2493 }
2494
2495 /*
2496 * Visit all the domain types sequentially. Typically, this shouldn't
2497 * affect performance since domain types are less tended to bloat.
2498 * Domain types are created manually, unlike composite types which are
2499 * automatically created for every temporary table.
2500 */
2501 for (typentry = firstDomainTypeEntry;
2502 typentry != NULL;
2503 typentry = typentry->nextDomain)
2504 {
2505 /*
2506 * If it's domain over composite, reset flags. (We don't bother
2507 * trying to determine whether the specific base type needs a
2508 * reset.) Note that if we haven't determined whether the base
2509 * type is composite, we don't need to reset anything.
2510 */
2512 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2513 }
2514 }
2515 else
2516 {
2517 HASH_SEQ_STATUS status;
2518
2519 /*
2520 * Relid is invalid. By convention, we need to reset all composite
2521 * types in cache. Also, we should reset flags for domain types, and
2522 * we loop over all entries in hash, so, do it in a single scan.
2523 */
2524 hash_seq_init(&status, TypeCacheHash);
2525 while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2526 {
2527 if (typentry->typtype == TYPTYPE_COMPOSITE)
2528 {
2530 }
2531 else if (typentry->typtype == TYPTYPE_DOMAIN)
2532 {
2533 /*
2534 * If it's domain over composite, reset flags. (We don't
2535 * bother trying to determine whether the specific base type
2536 * needs a reset.) Note that if we haven't determined whether
2537 * the base type is composite, we don't need to reset
2538 * anything.
2539 */
2541 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2542 }
2543 }
2544 }
2545}
2546
2547/*
2548 * TypeCacheTypCallback
2549 * Syscache inval callback function
2550 *
2551 * This is called when a syscache invalidation event occurs for any
2552 * pg_type row. If we have information cached about that type, mark
2553 * it as needing to be reloaded.
2554 */
2555static void
2557{
2558 HASH_SEQ_STATUS status;
2559 TypeCacheEntry *typentry;
2560
2561 /* TypeCacheHash must exist, else this callback wouldn't be registered */
2562
2563 /*
2564 * By convention, zero hash value is passed to the callback as a sign that
2565 * it's time to invalidate the whole cache. See sinval.c, inval.c and
2566 * InvalidateSystemCachesExtended().
2567 */
2568 if (hashvalue == 0)
2569 hash_seq_init(&status, TypeCacheHash);
2570 else
2571 hash_seq_init_with_hash_value(&status, TypeCacheHash, hashvalue);
2572
2573 while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2574 {
2575 bool hadPgTypeData = (typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA);
2576
2577 Assert(hashvalue == 0 || typentry->type_id_hash == hashvalue);
2578
2579 /*
2580 * Mark the data obtained directly from pg_type as invalid. Also, if
2581 * it's a domain, typnotnull might've changed, so we'll need to
2582 * recalculate its constraints.
2583 */
2584 typentry->flags &= ~(TCFLAGS_HAVE_PG_TYPE_DATA |
2586
2587 /*
2588 * Call delete_rel_type_cache_if_needed() if we cleaned
2589 * TCFLAGS_HAVE_PG_TYPE_DATA flag previously.
2590 */
2591 if (hadPgTypeData)
2593 }
2594}
2595
2596/*
2597 * TypeCacheOpcCallback
2598 * Syscache inval callback function
2599 *
2600 * This is called when a syscache invalidation event occurs for any pg_opclass
2601 * row. In principle we could probably just invalidate data dependent on the
2602 * particular opclass, but since updates on pg_opclass are rare in production
2603 * it doesn't seem worth a lot of complication: we just mark all cached data
2604 * invalid.
2605 *
2606 * Note that we don't bother watching for updates on pg_amop or pg_amproc.
2607 * This should be safe because ALTER OPERATOR FAMILY ADD/DROP OPERATOR/FUNCTION
2608 * is not allowed to be used to add/drop the primary operators and functions
2609 * of an opclass, only cross-type members of a family; and the latter sorts
2610 * of members are not going to get cached here.
2611 */
2612static void
2614{
2615 HASH_SEQ_STATUS status;
2616 TypeCacheEntry *typentry;
2617
2618 /* TypeCacheHash must exist, else this callback wouldn't be registered */
2619 hash_seq_init(&status, TypeCacheHash);
2620 while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2621 {
2622 bool hadOpclass = (typentry->flags & TCFLAGS_OPERATOR_FLAGS);
2623
2624 /* Reset equality/comparison/hashing validity information */
2625 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2626
2627 /*
2628 * Call delete_rel_type_cache_if_needed() if we actually cleared some
2629 * of TCFLAGS_OPERATOR_FLAGS.
2630 */
2631 if (hadOpclass)
2633 }
2634}
2635
2636/*
2637 * TypeCacheConstrCallback
2638 * Syscache inval callback function
2639 *
2640 * This is called when a syscache invalidation event occurs for any
2641 * pg_constraint row. We flush information about domain constraints
2642 * when this happens.
2643 *
2644 * It's slightly annoying that we can't tell whether the inval event was for
2645 * a domain constraint record or not; there's usually more update traffic
2646 * for table constraints than domain constraints, so we'll do a lot of
2647 * useless flushes. Still, this is better than the old no-caching-at-all
2648 * approach to domain constraints.
2649 */
2650static void
2652{
2653 TypeCacheEntry *typentry;
2654
2655 /*
2656 * Because this is called very frequently, and typically very few of the
2657 * typcache entries are for domains, we don't use hash_seq_search here.
2658 * Instead we thread all the domain-type entries together so that we can
2659 * visit them cheaply.
2660 */
2661 for (typentry = firstDomainTypeEntry;
2662 typentry != NULL;
2663 typentry = typentry->nextDomain)
2664 {
2665 /* Reset domain constraint validity information */
2667 }
2668}
2669
2670
2671/*
2672 * Check if given OID is part of the subset that's sortable by comparisons
2673 */
2674static inline bool
2676{
2677 Oid offset;
2678
2679 if (arg < enumdata->bitmap_base)
2680 return false;
2681 offset = arg - enumdata->bitmap_base;
2682 if (offset > (Oid) INT_MAX)
2683 return false;
2684 return bms_is_member((int) offset, enumdata->sorted_values);
2685}
2686
2687
2688/*
2689 * compare_values_of_enum
2690 * Compare two members of an enum type.
2691 * Return <0, 0, or >0 according as arg1 <, =, or > arg2.
2692 *
2693 * Note: currently, the enumData cache is refreshed only if we are asked
2694 * to compare an enum value that is not already in the cache. This is okay
2695 * because there is no support for re-ordering existing values, so comparisons
2696 * of previously cached values will return the right answer even if other
2697 * values have been added since we last loaded the cache.
2698 *
2699 * Note: the enum logic has a special-case rule about even-numbered versus
2700 * odd-numbered OIDs, but we take no account of that rule here; this
2701 * routine shouldn't even get called when that rule applies.
2702 */
2703int
2705{
2707 EnumItem *item1;
2708 EnumItem *item2;
2709
2710 /*
2711 * Equal OIDs are certainly equal --- this case was probably handled by
2712 * our caller, but we may as well check.
2713 */
2714 if (arg1 == arg2)
2715 return 0;
2716
2717 /* Load up the cache if first time through */
2718 if (tcache->enumData == NULL)
2719 load_enum_cache_data(tcache);
2720 enumdata = tcache->enumData;
2721
2722 /*
2723 * If both OIDs are known-sorted, we can just compare them directly.
2724 */
2727 {
2728 if (arg1 < arg2)
2729 return -1;
2730 else
2731 return 1;
2732 }
2733
2734 /*
2735 * Slow path: we have to identify their actual sort-order positions.
2736 */
2739
2740 if (item1 == NULL || item2 == NULL)
2741 {
2742 /*
2743 * We couldn't find one or both values. That means the enum has
2744 * changed under us, so re-initialize the cache and try again. We
2745 * don't bother retrying the known-sorted case in this path.
2746 */
2747 load_enum_cache_data(tcache);
2748 enumdata = tcache->enumData;
2749
2752
2753 /*
2754 * If we still can't find the values, complain: we must have corrupt
2755 * data.
2756 */
2757 if (item1 == NULL)
2758 elog(ERROR, "enum value %u not found in cache for enum %s",
2759 arg1, format_type_be(tcache->type_id));
2760 if (item2 == NULL)
2761 elog(ERROR, "enum value %u not found in cache for enum %s",
2762 arg2, format_type_be(tcache->type_id));
2763 }
2764
2765 if (item1->sort_order < item2->sort_order)
2766 return -1;
2767 else if (item1->sort_order > item2->sort_order)
2768 return 1;
2769 else
2770 return 0;
2771}
2772
2773/*
2774 * Load (or re-load) the enumData member of the typcache entry.
2775 */
2776static void
2778{
2784 EnumItem *items;
2785 int numitems;
2786 int maxitems;
2787 Oid bitmap_base;
2788 Bitmapset *bitmap;
2790 int bm_size,
2791 start_pos;
2792
2793 /* Check that this is actually an enum */
2794 if (tcache->typtype != TYPTYPE_ENUM)
2795 ereport(ERROR,
2797 errmsg("%s is not an enum",
2798 format_type_be(tcache->type_id))));
2799
2800 /*
2801 * Read all the information for members of the enum type. We collect the
2802 * info in working memory in the caller's context, and then transfer it to
2803 * permanent memory in CacheMemoryContext. This minimizes the risk of
2804 * leaking memory from CacheMemoryContext in the event of an error partway
2805 * through.
2806 */
2807 maxitems = 64;
2808 items = palloc_array(EnumItem, maxitems);
2809 numitems = 0;
2810
2811 /* Scan pg_enum for the members of the target enum type. */
2815 ObjectIdGetDatum(tcache->type_id));
2816
2820 true, NULL,
2821 1, &skey);
2822
2824 {
2826
2827 if (numitems >= maxitems)
2828 {
2829 maxitems *= 2;
2830 items = (EnumItem *) repalloc(items, sizeof(EnumItem) * maxitems);
2831 }
2832 items[numitems].enum_oid = en->oid;
2833 items[numitems].sort_order = en->enumsortorder;
2834 numitems++;
2835 }
2836
2839
2840 /* Sort the items into OID order */
2841 qsort(items, numitems, sizeof(EnumItem), enum_oid_cmp);
2842
2843 /*
2844 * Here, we create a bitmap listing a subset of the enum's OIDs that are
2845 * known to be in order and can thus be compared with just OID comparison.
2846 *
2847 * The point of this is that the enum's initial OIDs were certainly in
2848 * order, so there is some subset that can be compared via OID comparison;
2849 * and we'd rather not do binary searches unnecessarily.
2850 *
2851 * This is somewhat heuristic, and might identify a subset of OIDs that
2852 * isn't exactly what the type started with. That's okay as long as the
2853 * subset is correctly sorted.
2854 */
2855 bitmap_base = InvalidOid;
2856 bitmap = NULL;
2857 bm_size = 1; /* only save sets of at least 2 OIDs */
2858
2859 for (start_pos = 0; start_pos < numitems - 1; start_pos++)
2860 {
2861 /*
2862 * Identify longest sorted subsequence starting at start_pos
2863 */
2865 int this_bm_size = 1;
2866 Oid start_oid = items[start_pos].enum_oid;
2867 float4 prev_order = items[start_pos].sort_order;
2868 int i;
2869
2870 for (i = start_pos + 1; i < numitems; i++)
2871 {
2872 Oid offset;
2873
2874 offset = items[i].enum_oid - start_oid;
2875 /* quit if bitmap would be too large; cutoff is arbitrary */
2876 if (offset >= 8192)
2877 break;
2878 /* include the item if it's in-order */
2879 if (items[i].sort_order > prev_order)
2880 {
2881 prev_order = items[i].sort_order;
2882 this_bitmap = bms_add_member(this_bitmap, (int) offset);
2883 this_bm_size++;
2884 }
2885 }
2886
2887 /* Remember it if larger than previous best */
2888 if (this_bm_size > bm_size)
2889 {
2890 bms_free(bitmap);
2891 bitmap_base = start_oid;
2892 bitmap = this_bitmap;
2894 }
2895 else
2897
2898 /*
2899 * Done if it's not possible to find a longer sequence in the rest of
2900 * the list. In typical cases this will happen on the first
2901 * iteration, which is why we create the bitmaps on the fly instead of
2902 * doing a second pass over the list.
2903 */
2904 if (bm_size >= (numitems - start_pos - 1))
2905 break;
2906 }
2907
2908 /* OK, copy the data into CacheMemoryContext */
2911 palloc(offsetof(TypeCacheEnumData, enum_values) +
2912 numitems * sizeof(EnumItem));
2913 enumdata->bitmap_base = bitmap_base;
2914 enumdata->sorted_values = bms_copy(bitmap);
2915 enumdata->num_values = numitems;
2916 memcpy(enumdata->enum_values, items, numitems * sizeof(EnumItem));
2918
2919 pfree(items);
2920 bms_free(bitmap);
2921
2922 /* And link the finished cache struct into the typcache */
2923 if (tcache->enumData != NULL)
2924 pfree(tcache->enumData);
2925 tcache->enumData = enumdata;
2926}
2927
2928/*
2929 * Locate the EnumItem with the given OID, if present
2930 */
2931static EnumItem *
2933{
2934 EnumItem srch;
2935
2936 /* On some versions of Solaris, bsearch of zero items dumps core */
2937 if (enumdata->num_values <= 0)
2938 return NULL;
2939
2940 srch.enum_oid = arg;
2941 return bsearch(&srch, enumdata->enum_values, enumdata->num_values,
2942 sizeof(EnumItem), enum_oid_cmp);
2943}
2944
2945/*
2946 * qsort comparison function for OID-ordered EnumItems
2947 */
2948static int
2949enum_oid_cmp(const void *left, const void *right)
2950{
2951 const EnumItem *l = (const EnumItem *) left;
2952 const EnumItem *r = (const EnumItem *) right;
2953
2954 return pg_cmp_u32(l->enum_oid, r->enum_oid);
2955}
2956
2957/*
2958 * Copy 'tupdesc' into newly allocated shared memory in 'area', set its typmod
2959 * to the given value and return a dsa_pointer.
2960 */
2961static dsa_pointer
2962share_tupledesc(dsa_area *area, TupleDesc tupdesc, uint32 typmod)
2963{
2965 TupleDesc shared;
2966
2967 shared_dp = dsa_allocate(area, TupleDescSize(tupdesc));
2968 shared = (TupleDesc) dsa_get_address(area, shared_dp);
2969 TupleDescCopy(shared, tupdesc);
2970 shared->tdtypmod = typmod;
2971
2972 return shared_dp;
2973}
2974
2975/*
2976 * If we are attached to a SharedRecordTypmodRegistry, use it to find or
2977 * create a shared TupleDesc that matches 'tupdesc'. Otherwise return NULL.
2978 * Tuple descriptors returned by this function are not reference counted, and
2979 * will exist at least as long as the current backend remained attached to the
2980 * current session.
2981 */
2982static TupleDesc
2984{
2990 bool found;
2991 uint32 typmod;
2992
2993 /* If not even attached, nothing to do. */
2995 return NULL;
2996
2997 /* Try to find a matching tuple descriptor in the record table. */
2998 key.shared = false;
2999 key.u.local_tupdesc = tupdesc;
3003 {
3004 Assert(record_table_entry->key.shared);
3007 result = (TupleDesc)
3009 record_table_entry->key.u.shared_tupdesc);
3010 Assert(result->tdrefcount == -1);
3011
3012 return result;
3013 }
3014
3015 /* Allocate a new typmod number. This will be wasted if we error out. */
3016 typmod = (int)
3018 1);
3019
3020 /* Copy the TupleDesc into shared memory. */
3021 shared_dp = share_tupledesc(CurrentSession->area, tupdesc, typmod);
3022
3023 /*
3024 * Create an entry in the typmod table so that others will understand this
3025 * typmod number.
3026 */
3027 PG_TRY();
3028 {
3031 &typmod, &found);
3032 if (found)
3033 elog(ERROR, "cannot create duplicate shared record typmod");
3034 }
3035 PG_CATCH();
3036 {
3038 PG_RE_THROW();
3039 }
3040 PG_END_TRY();
3041 typmod_table_entry->typmod = typmod;
3042 typmod_table_entry->shared_tupdesc = shared_dp;
3045
3046 /*
3047 * Finally create an entry in the record table so others with matching
3048 * tuple descriptors can reuse the typmod.
3049 */
3052 &found);
3053 if (found)
3054 {
3055 /*
3056 * Someone concurrently inserted a matching tuple descriptor since the
3057 * first time we checked. Use that one instead.
3058 */
3061
3062 /* Might as well free up the space used by the one we created. */
3064 &typmod);
3065 Assert(found);
3067
3068 /* Return the one we found. */
3069 Assert(record_table_entry->key.shared);
3070 result = (TupleDesc)
3072 record_table_entry->key.u.shared_tupdesc);
3073 Assert(result->tdrefcount == -1);
3074
3075 return result;
3076 }
3077
3078 /* Store it and return it. */
3079 record_table_entry->key.shared = true;
3080 record_table_entry->key.u.shared_tupdesc = shared_dp;
3083 result = (TupleDesc)
3085 Assert(result->tdrefcount == -1);
3086
3087 return result;
3088}
3089
3090/*
3091 * On-DSM-detach hook to forget about the current shared record typmod
3092 * infrastructure. This is currently used by both leader and workers.
3093 */
3094static void
3096{
3097 /* Be cautious here: maybe we didn't finish initializing. */
3099 {
3102 }
3104 {
3107 }
3109}
3110
3111/*
3112 * Insert RelIdToTypeIdCacheHash entry if needed.
3113 */
3114static void
3116{
3117 /* Immediately quit for non-composite types */
3118 if (typentry->typtype != TYPTYPE_COMPOSITE)
3119 return;
3120
3121 /* typrelid should be given for composite types */
3122 Assert(OidIsValid(typentry->typrelid));
3123
3124 /*
3125 * Insert a RelIdToTypeIdCacheHash entry if the typentry have any
3126 * information indicating it should be here.
3127 */
3128 if ((typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA) ||
3129 (typentry->flags & TCFLAGS_OPERATOR_FLAGS) ||
3130 typentry->tupDesc != NULL)
3131 {
3133 bool found;
3134
3136 &typentry->typrelid,
3137 HASH_ENTER, &found);
3138 relentry->relid = typentry->typrelid;
3139 relentry->composite_typid = typentry->type_id;
3140 }
3141}
3142
3143/*
3144 * Delete entry RelIdToTypeIdCacheHash if needed after resetting of the
3145 * TCFLAGS_HAVE_PG_TYPE_DATA flag, or any of TCFLAGS_OPERATOR_FLAGS,
3146 * or tupDesc.
3147 */
3148static void
3150{
3151#ifdef USE_ASSERT_CHECKING
3152 int i;
3153 bool is_in_progress = false;
3154
3155 for (i = 0; i < in_progress_list_len; i++)
3156 {
3157 if (in_progress_list[i] == typentry->type_id)
3158 {
3159 is_in_progress = true;
3160 break;
3161 }
3162 }
3163#endif
3164
3165 /* Immediately quit for non-composite types */
3166 if (typentry->typtype != TYPTYPE_COMPOSITE)
3167 return;
3168
3169 /* typrelid should be given for composite types */
3170 Assert(OidIsValid(typentry->typrelid));
3171
3172 /*
3173 * Delete a RelIdToTypeIdCacheHash entry if the typentry doesn't have any
3174 * information indicating entry should be still there.
3175 */
3176 if (!(typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA) &&
3177 !(typentry->flags & TCFLAGS_OPERATOR_FLAGS) &&
3178 typentry->tupDesc == NULL)
3179 {
3180 bool found;
3181
3183 &typentry->typrelid,
3184 HASH_REMOVE, &found);
3185 Assert(found || is_in_progress);
3186 }
3187 else
3188 {
3189#ifdef USE_ASSERT_CHECKING
3190 /*
3191 * In assert-enabled builds otherwise check for RelIdToTypeIdCacheHash
3192 * entry if it should exist.
3193 */
3194 bool found;
3195
3196 if (!is_in_progress)
3197 {
3199 &typentry->typrelid,
3200 HASH_FIND, &found);
3201 Assert(found);
3202 }
3203#endif
3204 }
3205}
3206
3207/*
3208 * Add possibly missing RelIdToTypeId entries related to TypeCacheHash
3209 * entries, marked as in-progress by lookup_type_cache(). It may happen
3210 * in case of an error or interruption during the lookup_type_cache() call.
3211 */
3212static void
3214{
3215 int i;
3216
3217 for (i = 0; i < in_progress_list_len; i++)
3218 {
3219 TypeCacheEntry *typentry;
3220
3223 HASH_FIND, NULL);
3224 if (typentry)
3226 }
3227
3229}
3230
3231void
3233{
3235}
3236
3237void
3239{
3241}
static void pg_atomic_init_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
Definition atomics.h:214
static uint32 pg_atomic_fetch_add_u32(volatile pg_atomic_uint32 *ptr, int32 add_)
Definition atomics.h:361
Bitmapset * bms_make_singleton(int x)
Definition bitmapset.c:217
void bms_free(Bitmapset *a)
Definition bitmapset.c:240
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:645
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:934
Bitmapset * bms_copy(const Bitmapset *a)
Definition bitmapset.c:123
#define TextDatumGetCString(d)
Definition builtins.h:99
#define NameStr(name)
Definition c.h:894
#define RegProcedureIsValid(p)
Definition c.h:921
#define Assert(condition)
Definition c.h:1002
#define FLEXIBLE_ARRAY_MEMBER
Definition c.h:617
int32_t int32
Definition c.h:679
uint64_t uint64
Definition c.h:684
uint32_t uint32
Definition c.h:683
float float4
Definition c.h:772
#define MemSet(start, val, len)
Definition c.h:1147
#define OidIsValid(objectId)
Definition c.h:917
size_t Size
Definition c.h:748
void CreateCacheMemoryContext(void)
Definition catcache.c:726
uint32 result
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
bool contain_volatile_functions(Node *clause)
Definition clauses.c:567
void * dsa_get_address(dsa_area *area, dsa_pointer dp)
Definition dsa.c:954
void dsa_free(dsa_area *area, dsa_pointer dp)
Definition dsa.c:838
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:2381
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:1464
Oid get_opclass_family(Oid opclass)
Definition lsyscache.c:1442
Oid get_multirange_range(Oid multirangeOid)
Definition lsyscache.c:3844
Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
Definition lsyscache.c:1022
RegProcedure get_opcode(Oid opno)
Definition lsyscache.c:1585
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:3148
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition lsyscache.c:2854
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:7010
#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::@34 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:1511
#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:1756
static void insert_rel_type_cache_if_needed(TypeCacheEntry *typentry)
Definition typcache.c:3116
void InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref, MemoryContext refctx, bool need_exprstate)
Definition typcache.c:1420
static TupleDesc lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
Definition typcache.c:1869
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition typcache.c:1963
static void TypeCacheOpcCallback(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
Definition typcache.c:2614
void SharedRecordTypmodRegistryAttach(SharedRecordTypmodRegistry *registry)
Definition typcache.c:2337
#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:1772
#define TCFLAGS_HAVE_FIELD_COMPARE
Definition typcache.c:115
void AtEOXact_TypeCache(void)
Definition typcache.c:3233
static void load_enum_cache_data(TypeCacheEntry *tcache)
Definition typcache.c:2778
static bool record_fields_have_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1635
static HTAB * RelIdToTypeIdCacheHash
Definition typcache.c:87
static EnumItem * find_enumitem(TypeCacheEnumData *enumdata, Oid arg)
Definition typcache.c:2933
static bool record_fields_have_extended_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1643
static TupleDesc find_or_make_matching_shared_tupledesc(TupleDesc tupdesc)
Definition typcache.c:2984
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:2019
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:3150
#define TCFLAGS_CHECKED_GT_OPR
Definition typcache.c:104
static bool multirange_element_has_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1796
static List * prep_domain_constraints(List *constraints, MemoryContext execctx)
Definition typcache.c:1382
TupleDesc lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
Definition typcache.c:1980
static bool record_fields_have_equality(TypeCacheEntry *typentry)
Definition typcache.c:1619
#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:1361
#define TCFLAGS_HAVE_FIELD_EQUALITY
Definition typcache.c:114
static void InvalidateCompositeTypeCacheEntry(TypeCacheEntry *typentry)
Definition typcache.c:2406
void SharedRecordTypmodRegistryInit(SharedRecordTypmodRegistry *registry, dsm_segment *segment, dsa_area *area)
Definition typcache.c:2238
static int dcs_cmp(const void *a, const void *b)
Definition typcache.c:1337
static bool array_element_has_extended_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1581
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:1573
static void load_multirangetype_info(TypeCacheEntry *typentry)
Definition typcache.c:1080
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:1804
static int in_progress_list_len
Definition typcache.c:227
static bool array_element_has_equality(TypeCacheEntry *typentry)
Definition typcache.c:1557
static dsa_pointer share_tupledesc(dsa_area *area, TupleDesc tupdesc, uint32 typmod)
Definition typcache.c:2963
static void load_rangetype_info(TypeCacheEntry *typentry)
Definition typcache.c:1022
uint64 assign_record_type_identifier(Oid type_id, int32 typmod)
Definition typcache.c:2175
static RecordCacheArrayEntry * RecordCacheArray
Definition typcache.c:304
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry)
Definition typcache.c:1764
static HTAB * RecordCacheHash
Definition typcache.c:295
static bool enum_known_sorted(TypeCacheEnumData *enumdata, Oid arg)
Definition typcache.c:2676
static TypeCacheEntry * firstDomainTypeEntry
Definition typcache.c:96
void AtEOSubXact_TypeCache(void)
Definition typcache.c:3239
static void shared_record_typmod_registry_detach(dsm_segment *segment, Datum datum)
Definition typcache.c:3096
#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:1102
#define TCFLAGS_HAVE_ELEM_COMPARE
Definition typcache.c:110
static void TypeCacheRelCallback(Datum arg, Oid relid)
Definition typcache.c:2461
static void cache_array_element_properties(TypeCacheEntry *typentry)
Definition typcache.c:1589
static void TypeCacheTypCallback(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
Definition typcache.c:2557
size_t SharedRecordTypmodRegistryEstimate(void)
Definition typcache.c:2216
static void cache_multirange_element_properties(TypeCacheEntry *typentry)
Definition typcache.c:1812
#define TCFLAGS_CHECKED_ELEM_PROPERTIES
Definition typcache.c:108
static void TypeCacheConstrCallback(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
Definition typcache.c:2652
#define TCFLAGS_HAVE_ELEM_EQUALITY
Definition typcache.c:109
static bool array_element_has_compare(TypeCacheEntry *typentry)
Definition typcache.c:1565
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:2705
#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:2083
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:1627
#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:2067
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:1997
static int enum_oid_cmp(const void *left, const void *right)
Definition typcache.c:2950
static void finalize_in_progress_typentries(void)
Definition typcache.c:3214
static void decr_dcc_refcount(DomainConstraintCache *dcc)
Definition typcache.c:1350
#define TCFLAGS_CHECKED_EQ_OPR
Definition typcache.c:102
void UpdateDomainConstraintRef(DomainConstraintRef *ref)
Definition typcache.c:1458
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:1840
static void cache_record_field_properties(TypeCacheEntry *typentry)
Definition typcache.c:1651
static uint32 record_type_typmod_hash(const void *data, size_t size)
Definition typcache.c:2056
static void load_typcache_tupdesc(TypeCacheEntry *typentry)
Definition typcache.c:988
#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 1565 of file typcache.c.

1566{
1567 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1569 return (typentry->flags & TCFLAGS_HAVE_ELEM_COMPARE) != 0;
1570}

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

1558{
1559 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1561 return (typentry->flags & TCFLAGS_HAVE_ELEM_EQUALITY) != 0;
1562}

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

1574{
1575 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1577 return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1578}

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

2176{
2177 if (type_id != RECORDOID)
2178 {
2179 /*
2180 * It's a named composite type, so use the regular typcache.
2181 */
2182 TypeCacheEntry *typentry;
2183
2184 typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
2185 if (typentry->tupDesc == NULL)
2186 ereport(ERROR,
2188 errmsg("type %s is not composite",
2189 format_type_be(type_id))));
2190 Assert(typentry->tupDesc_identifier != 0);
2191 return typentry->tupDesc_identifier;
2192 }
2193 else
2194 {
2195 /*
2196 * It's a transient record type, so look in our record-type table.
2197 */
2198 if (typmod >= 0 && typmod < RecordCacheArrayLen &&
2199 RecordCacheArray[typmod].tupdesc != NULL)
2200 {
2201 Assert(RecordCacheArray[typmod].id != 0);
2202 return RecordCacheArray[typmod].id;
2203 }
2204
2205 /* For anonymous or unrecognized record type, generate a new ID */
2206 return ++tupledesc_id_counter;
2207 }
2208}

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

2084{
2087 bool found;
2089
2090 Assert(tupDesc->tdtypeid == RECORDOID);
2091
2092 if (RecordCacheHash == NULL)
2093 {
2094 /* First time through: initialize the hash table */
2095 HASHCTL ctl;
2096
2097 ctl.keysize = sizeof(TupleDesc); /* just the pointer */
2098 ctl.entrysize = sizeof(RecordCacheEntry);
2101 RecordCacheHash = hash_create("Record information cache", 64,
2102 &ctl,
2104
2105 /* Also make sure CacheMemoryContext exists */
2106 if (!CacheMemoryContext)
2108 }
2109
2110 /*
2111 * Find a hashtable entry for this tuple descriptor. We don't use
2112 * HASH_ENTER yet, because if it's missing, we need to make sure that all
2113 * the allocations succeed before we create the new entry.
2114 */
2116 &tupDesc,
2117 HASH_FIND, &found);
2118 if (found && recentry->tupdesc != NULL)
2119 {
2120 tupDesc->tdtypmod = recentry->tupdesc->tdtypmod;
2121 return;
2122 }
2123
2124 /* Not present, so need to manufacture an entry */
2126
2127 /* Look in the SharedRecordTypmodRegistry, if attached */
2129 if (entDesc == NULL)
2130 {
2131 /*
2132 * Make sure we have room before we CreateTupleDescCopy() or advance
2133 * NextRecordTypmod.
2134 */
2136
2137 /* Reference-counted local cache only. */
2138 entDesc = CreateTupleDescCopy(tupDesc);
2139 entDesc->tdrefcount = 1;
2140 entDesc->tdtypmod = NextRecordTypmod++;
2141 }
2142 else
2143 {
2145 }
2146
2148
2149 /* Assign a unique tupdesc identifier, too. */
2151
2152 /* Fully initialized; create the hash table entry */
2154 &tupDesc,
2155 HASH_ENTER, NULL);
2156 recentry->tupdesc = entDesc;
2157
2158 /* Update the caller's tuple descriptor. */
2159 tupDesc->tdtypmod = entDesc->tdtypmod;
2160
2162}

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

3240{
3242}

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

1813{
1814 /* load up range link if we didn't already */
1815 if (typentry->rngtype == NULL &&
1816 typentry->typtype == TYPTYPE_MULTIRANGE)
1817 load_multirangetype_info(typentry);
1818
1819 if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
1820 {
1822
1823 /* might need to calculate subtype's hash function properties */
1827 if (OidIsValid(elementry->hash_proc))
1828 typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1829 if (OidIsValid(elementry->hash_extended_proc))
1831 }
1833}

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

1773{
1774 /* load up subtype link if we didn't already */
1775 if (typentry->rngelemtype == NULL &&
1776 typentry->typtype == TYPTYPE_RANGE)
1777 load_rangetype_info(typentry);
1778
1779 if (typentry->rngelemtype != NULL)
1780 {
1782
1783 /* might need to calculate subtype's hash function properties */
1787 if (OidIsValid(elementry->hash_proc))
1788 typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1789 if (OidIsValid(elementry->hash_extended_proc))
1791 }
1793}

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

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

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

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

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

1362{
1364 DomainConstraintCache *dcc = ref->dcc;
1365
1366 /* Paranoia --- be sure link is nulled before trying to release */
1367 if (dcc)
1368 {
1369 ref->constraints = NIL;
1370 ref->dcc = NULL;
1371 decr_dcc_refcount(dcc);
1372 }
1373}

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

1338{
1339 const DomainConstraintState *const *ca = (const DomainConstraintState *const *) a;
1340 const DomainConstraintState *const *cb = (const DomainConstraintState *const *) b;
1341
1342 return strcmp((*ca)->name, (*cb)->name);
1343}

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

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

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

1512{
1513 TypeCacheEntry *typentry;
1514
1515 /*
1516 * Note: a side effect is to cause the typcache's domain data to become
1517 * valid. This is fine since we'll likely need it soon if there is any.
1518 */
1520
1521 if (typentry->domainData == NULL)
1522 return false;
1523
1524 if (has_volatile)
1525 {
1526 *has_volatile = false;
1527
1529 typentry->domainData->constraints)
1530 {
1531 if (constrstate->constrainttype == DOM_CONSTRAINT_CHECK &&
1533 {
1534 *has_volatile = true;
1535 break;
1536 }
1537 }
1538 }
1539
1540 return true;
1541}

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

2677{
2678 Oid offset;
2679
2680 if (arg < enumdata->bitmap_base)
2681 return false;
2682 offset = arg - enumdata->bitmap_base;
2683 if (offset > (Oid) INT_MAX)
2684 return false;
2685 return bms_is_member((int) offset, enumdata->sorted_values);
2686}

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

2951{
2952 const EnumItem *l = (const EnumItem *) left;
2953 const EnumItem *r = (const EnumItem *) right;
2954
2955 return pg_cmp_u32(l->enum_oid, r->enum_oid);
2956}

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

3215{
3216 int i;
3217
3218 for (i = 0; i < in_progress_list_len; i++)
3219 {
3220 TypeCacheEntry *typentry;
3221
3224 HASH_FIND, NULL);
3225 if (typentry)
3227 }
3228
3230}

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

2934{
2935 EnumItem srch;
2936
2937 /* On some versions of Solaris, bsearch of zero items dumps core */
2938 if (enumdata->num_values <= 0)
2939 return NULL;
2940
2941 srch.enum_oid = arg;
2942 return bsearch(&srch, enumdata->enum_values, enumdata->num_values,
2943 sizeof(EnumItem), enum_oid_cmp);
2944}

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

2985{
2991 bool found;
2992 uint32 typmod;
2993
2994 /* If not even attached, nothing to do. */
2996 return NULL;
2997
2998 /* Try to find a matching tuple descriptor in the record table. */
2999 key.shared = false;
3000 key.u.local_tupdesc = tupdesc;
3004 {
3005 Assert(record_table_entry->key.shared);
3008 result = (TupleDesc)
3010 record_table_entry->key.u.shared_tupdesc);
3011 Assert(result->tdrefcount == -1);
3012
3013 return result;
3014 }
3015
3016 /* Allocate a new typmod number. This will be wasted if we error out. */
3017 typmod = (int)
3019 1);
3020
3021 /* Copy the TupleDesc into shared memory. */
3022 shared_dp = share_tupledesc(CurrentSession->area, tupdesc, typmod);
3023
3024 /*
3025 * Create an entry in the typmod table so that others will understand this
3026 * typmod number.
3027 */
3028 PG_TRY();
3029 {
3032 &typmod, &found);
3033 if (found)
3034 elog(ERROR, "cannot create duplicate shared record typmod");
3035 }
3036 PG_CATCH();
3037 {
3039 PG_RE_THROW();
3040 }
3041 PG_END_TRY();
3042 typmod_table_entry->typmod = typmod;
3043 typmod_table_entry->shared_tupdesc = shared_dp;
3046
3047 /*
3048 * Finally create an entry in the record table so others with matching
3049 * tuple descriptors can reuse the typmod.
3050 */
3053 &found);
3054 if (found)
3055 {
3056 /*
3057 * Someone concurrently inserted a matching tuple descriptor since the
3058 * first time we checked. Use that one instead.
3059 */
3062
3063 /* Might as well free up the space used by the one we created. */
3065 &typmod);
3066 Assert(found);
3068
3069 /* Return the one we found. */
3070 Assert(record_table_entry->key.shared);
3071 result = (TupleDesc)
3073 record_table_entry->key.u.shared_tupdesc);
3074 Assert(result->tdrefcount == -1);
3075
3076 return result;
3077 }
3078
3079 /* Store it and return it. */
3080 record_table_entry->key.shared = true;
3081 record_table_entry->key.u.shared_tupdesc = shared_dp;
3084 result = (TupleDesc)
3086 Assert(result->tdrefcount == -1);
3087
3088 return result;
3089}

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

1422{
1423 /* Look up the typcache entry --- we assume it survives indefinitely */
1425 ref->need_exprstate = need_exprstate;
1426 /* For safety, establish the callback before acquiring a refcount */
1427 ref->refctx = refctx;
1428 ref->dcc = NULL;
1429 ref->callback.func = dccref_deletion_callback;
1430 ref->callback.arg = ref;
1431 MemoryContextRegisterResetCallback(refctx, &ref->callback);
1432 /* Acquire refcount if there are constraints, and set up exported list */
1433 if (ref->tcache->domainData)
1434 {
1435 ref->dcc = ref->tcache->domainData;
1436 ref->dcc->dccRefCount++;
1437 if (ref->need_exprstate)
1438 ref->constraints = prep_domain_constraints(ref->dcc->constraints,
1439 ref->refctx);
1440 else
1441 ref->constraints = ref->dcc->constraints;
1442 }
1443 else
1444 ref->constraints = NIL;
1445}

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

3117{
3118 /* Immediately quit for non-composite types */
3119 if (typentry->typtype != TYPTYPE_COMPOSITE)
3120 return;
3121
3122 /* typrelid should be given for composite types */
3123 Assert(OidIsValid(typentry->typrelid));
3124
3125 /*
3126 * Insert a RelIdToTypeIdCacheHash entry if the typentry have any
3127 * information indicating it should be here.
3128 */
3129 if ((typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA) ||
3130 (typentry->flags & TCFLAGS_OPERATOR_FLAGS) ||
3131 typentry->tupDesc != NULL)
3132 {
3134 bool found;
3135
3137 &typentry->typrelid,
3138 HASH_ENTER, &found);
3139 relentry->relid = typentry->typrelid;
3140 relentry->composite_typid = typentry->type_id;
3141 }
3142}

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

2407{
2409
2410 Assert(typentry->typtype == TYPTYPE_COMPOSITE &&
2411 OidIsValid(typentry->typrelid));
2412
2413 hadTupDescOrOpclass = (typentry->tupDesc != NULL) ||
2414 (typentry->flags & TCFLAGS_OPERATOR_FLAGS);
2415
2416 /* Delete tupdesc if we have it */
2417 if (typentry->tupDesc != NULL)
2418 {
2419 /*
2420 * Release our refcount and free the tupdesc if none remain. We can't
2421 * use DecrTupleDescRefCount here because this reference is not logged
2422 * by the current resource owner.
2423 */
2424 Assert(typentry->tupDesc->tdrefcount > 0);
2425 if (--typentry->tupDesc->tdrefcount == 0)
2426 FreeTupleDesc(typentry->tupDesc);
2427 typentry->tupDesc = NULL;
2428
2429 /*
2430 * Also clear tupDesc_identifier, so that anyone watching it will
2431 * realize that the tupdesc has changed.
2432 */
2433 typentry->tupDesc_identifier = 0;
2434 }
2435
2436 /* Reset equality/comparison/hashing validity information */
2437 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2438
2439 /*
2440 * Call delete_rel_type_cache_if_needed() if we actually cleared
2441 * something.
2442 */
2445}

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

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

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

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

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

1081{
1083
1086 elog(ERROR, "cache lookup failed for multirange type %u",
1087 typentry->type_id);
1088
1090}

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

1023{
1025 HeapTuple tup;
1031 Oid opcintype;
1032 Oid cmpFnOid;
1033
1034 /* get information from pg_range */
1036 /* should not fail, since we already checked typtype ... */
1037 if (!HeapTupleIsValid(tup))
1038 elog(ERROR, "cache lookup failed for range type %u",
1039 typentry->type_id);
1041
1042 subtypeOid = pg_range->rngsubtype;
1043 typentry->rng_collation = pg_range->rngcollation;
1044 opclassOid = pg_range->rngsubopc;
1045 canonicalOid = pg_range->rngcanonical;
1046 subdiffOid = pg_range->rngsubdiff;
1047
1049
1050 /* get opclass properties and look up the comparison function */
1053 typentry->rng_opfamily = opfamilyOid;
1054
1055 cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype,
1056 BTORDER_PROC);
1058 elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
1059 BTORDER_PROC, opcintype, opcintype, opfamilyOid);
1060
1061 /* set up cached fmgrinfo structs */
1070
1071 /* Lastly, set up link to the element type --- this marks data valid */
1073}

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

989{
990 Relation rel;
991
992 if (!OidIsValid(typentry->typrelid)) /* should not happen */
993 elog(ERROR, "invalid typrelid for composite type %u",
994 typentry->type_id);
995 rel = relation_open(typentry->typrelid, AccessShareLock);
996 Assert(rel->rd_rel->reltype == typentry->type_id);
997
998 /*
999 * Link to the tupdesc and increment its refcount (we assert it's a
1000 * refcounted descriptor). We don't use IncrTupleDescRefCount() for this,
1001 * because the reference mustn't be entered in the current resource owner;
1002 * it can outlive the current query.
1003 */
1004 typentry->tupDesc = RelationGetDescr(rel);
1005
1006 Assert(typentry->tupDesc->tdrefcount > 0);
1007 typentry->tupDesc->tdrefcount++;
1008
1009 /*
1010 * In future, we could take some pains to not change tupDesc_identifier if
1011 * the tupdesc didn't really change; but for now it's not worth it.
1012 */
1014
1016}

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

2020{
2021 TupleDesc tupDesc;
2022
2023 if (type_id != RECORDOID)
2024 {
2025 /*
2026 * Check for domain or named composite type. We might as well load
2027 * whichever data is needed.
2028 */
2029 TypeCacheEntry *typentry;
2030
2031 typentry = lookup_type_cache(type_id,
2034 if (typentry->typtype == TYPTYPE_DOMAIN)
2036 typentry->domainBaseTypmod,
2037 noError);
2038 if (typentry->tupDesc == NULL && !noError)
2039 ereport(ERROR,
2041 errmsg("type %s is not composite",
2042 format_type_be(type_id))));
2043 tupDesc = typentry->tupDesc;
2044 }
2045 else
2046 tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
2047 if (tupDesc != NULL)
2048 PinTupleDesc(tupDesc);
2049 return tupDesc;
2050}

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

1870{
1871 if (type_id != RECORDOID)
1872 {
1873 /*
1874 * It's a named composite type, so use the regular typcache.
1875 */
1876 TypeCacheEntry *typentry;
1877
1878 typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
1879 if (typentry->tupDesc == NULL && !noError)
1880 ereport(ERROR,
1882 errmsg("type %s is not composite",
1883 format_type_be(type_id))));
1884 return typentry->tupDesc;
1885 }
1886 else
1887 {
1888 /*
1889 * It's a transient record type, so look in our record-type table.
1890 */
1891 if (typmod >= 0)
1892 {
1893 /* It is already in our local cache? */
1894 if (typmod < RecordCacheArrayLen &&
1895 RecordCacheArray[typmod].tupdesc != NULL)
1896 return RecordCacheArray[typmod].tupdesc;
1897
1898 /* Are we attached to a shared record typmod registry? */
1900 {
1902
1903 /* Try to find it in the shared typmod index. */
1905 &typmod, false);
1906 if (entry != NULL)
1907 {
1908 TupleDesc tupdesc;
1909
1910 tupdesc = (TupleDesc)
1912 entry->shared_tupdesc);
1913 Assert(typmod == tupdesc->tdtypmod);
1914
1915 /* We may need to extend the local RecordCacheArray. */
1917
1918 /*
1919 * Our local array can now point directly to the TupleDesc
1920 * in shared memory, which is non-reference-counted.
1921 */
1922 RecordCacheArray[typmod].tupdesc = tupdesc;
1923 Assert(tupdesc->tdrefcount == -1);
1924
1925 /*
1926 * We don't share tupdesc identifiers across processes, so
1927 * assign one locally.
1928 */
1930
1932 entry);
1933
1934 return RecordCacheArray[typmod].tupdesc;
1935 }
1936 }
1937 }
1938
1939 if (!noError)
1940 ereport(ERROR,
1942 errmsg("record type has not been registered")));
1943 return NULL;
1944 }
1945}

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

1981{
1982 TupleDesc tupDesc;
1983
1984 tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
1985 if (tupDesc != NULL)
1986 PinTupleDesc(tupDesc);
1987 return tupDesc;
1988}

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 }
462
463 /* Try to look up an existing entry */
465 &type_id,
466 HASH_FIND, NULL);
467
468 /*
469 * Only mark the new entry as "in progress" after the initial entry
470 * lookup.
471 *
472 * TypeCacheHash uses type_cache_syshash(), potentially triggering the
473 * initialization of the TYPEOID catcache, where an out-of-memory failure
474 * is possible. If an out-of-memory happens, error recovery would call
475 * finalize_in_progress_typentries(), that could attempt a catcache
476 * initialization again outside a transaction context.
477 *
478 * See also ConditionalCatalogCacheInitializeCache().
479 */
482
483 if (typentry == NULL)
484 {
485 /*
486 * If we didn't find one, we want to make one. But first look up the
487 * pg_type row, just to make sure we don't make a cache entry for an
488 * invalid type OID. If the type OID is not valid, present a
489 * user-facing error, since some code paths such as domain_in() allow
490 * this function to be reached with a user-supplied OID.
491 */
492 HeapTuple tp;
494
496 if (!HeapTupleIsValid(tp))
499 errmsg("type with OID %u does not exist", type_id)));
501 if (!typtup->typisdefined)
504 errmsg("type \"%s\" is only a shell",
505 NameStr(typtup->typname))));
506
507 /* Now make the typcache entry */
509 &type_id,
510 HASH_ENTER, &found);
511 Assert(!found); /* it wasn't there a moment ago */
512
513 MemSet(typentry, 0, sizeof(TypeCacheEntry));
514
515 /* These fields can never change, by definition */
516 typentry->type_id = type_id;
517 typentry->type_id_hash = get_hash_value(TypeCacheHash, &type_id);
518
519 /* Keep this part in sync with the code below */
520 typentry->typlen = typtup->typlen;
521 typentry->typbyval = typtup->typbyval;
522 typentry->typalign = typtup->typalign;
523 typentry->typstorage = typtup->typstorage;
524 typentry->typtype = typtup->typtype;
525 typentry->typrelid = typtup->typrelid;
526 typentry->typsubscript = typtup->typsubscript;
527 typentry->typelem = typtup->typelem;
528 typentry->typarray = typtup->typarray;
529 typentry->typcollation = typtup->typcollation;
530 typentry->flags |= TCFLAGS_HAVE_PG_TYPE_DATA;
531
532 /* If it's a domain, immediately thread it into the domain cache list */
533 if (typentry->typtype == TYPTYPE_DOMAIN)
534 {
536 firstDomainTypeEntry = typentry;
537 }
538
539 ReleaseSysCache(tp);
540 }
541 else if (!(typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA))
542 {
543 /*
544 * We have an entry, but its pg_type row got changed, so reload the
545 * data obtained directly from pg_type.
546 */
547 HeapTuple tp;
549
551 if (!HeapTupleIsValid(tp))
554 errmsg("type with OID %u does not exist", type_id)));
556 if (!typtup->typisdefined)
559 errmsg("type \"%s\" is only a shell",
560 NameStr(typtup->typname))));
561
562 /*
563 * Keep this part in sync with the code above. Many of these fields
564 * shouldn't ever change, particularly typtype, but copy 'em anyway.
565 */
566 typentry->typlen = typtup->typlen;
567 typentry->typbyval = typtup->typbyval;
568 typentry->typalign = typtup->typalign;
569 typentry->typstorage = typtup->typstorage;
570 typentry->typtype = typtup->typtype;
571 typentry->typrelid = typtup->typrelid;
572 typentry->typsubscript = typtup->typsubscript;
573 typentry->typelem = typtup->typelem;
574 typentry->typarray = typtup->typarray;
575 typentry->typcollation = typtup->typcollation;
576 typentry->flags |= TCFLAGS_HAVE_PG_TYPE_DATA;
577
578 ReleaseSysCache(tp);
579 }
580
581 /*
582 * Look up opclasses if we haven't already and any dependent info is
583 * requested.
584 */
590 {
591 Oid opclass;
592
593 opclass = GetDefaultOpClass(type_id, BTREE_AM_OID);
594 if (OidIsValid(opclass))
595 {
596 typentry->btree_opf = get_opclass_family(opclass);
597 typentry->btree_opintype = get_opclass_input_type(opclass);
598 }
599 else
600 {
601 typentry->btree_opf = typentry->btree_opintype = InvalidOid;
602 }
603
604 /*
605 * Reset information derived from btree opclass. Note in particular
606 * that we'll redetermine the eq_opr even if we previously found one;
607 * this matters in case a btree opclass has been added to a type that
608 * previously had only a hash opclass.
609 */
610 typentry->flags &= ~(TCFLAGS_CHECKED_EQ_OPR |
615 }
616
617 /*
618 * If we need to look up equality operator, and there's no btree opclass,
619 * force lookup of hash opclass.
620 */
621 if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
622 !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR) &&
623 typentry->btree_opf == InvalidOid)
625
630 !(typentry->flags & TCFLAGS_CHECKED_HASH_OPCLASS))
631 {
632 Oid opclass;
633
634 opclass = GetDefaultOpClass(type_id, HASH_AM_OID);
635 if (OidIsValid(opclass))
636 {
637 typentry->hash_opf = get_opclass_family(opclass);
638 typentry->hash_opintype = get_opclass_input_type(opclass);
639 }
640 else
641 {
642 typentry->hash_opf = typentry->hash_opintype = InvalidOid;
643 }
644
645 /*
646 * Reset information derived from hash opclass. We do *not* reset the
647 * eq_opr; if we already found one from the btree opclass, that
648 * decision is still good.
649 */
650 typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC |
653 }
654
655 /*
656 * Look for requested operators and functions, if we haven't already.
657 */
658 if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
659 !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR))
660 {
661 Oid eq_opr = InvalidOid;
662
663 if (typentry->btree_opf != InvalidOid)
664 eq_opr = get_opfamily_member(typentry->btree_opf,
665 typentry->btree_opintype,
666 typentry->btree_opintype,
668 if (eq_opr == InvalidOid &&
669 typentry->hash_opf != InvalidOid)
670 eq_opr = get_opfamily_member(typentry->hash_opf,
671 typentry->hash_opintype,
672 typentry->hash_opintype,
674
675 /*
676 * If the proposed equality operator is array_eq or record_eq, check
677 * to see if the element type or column types support equality. If
678 * not, array_eq or record_eq would fail at runtime, so we don't want
679 * to report that the type has equality. (We can omit similar
680 * checking for ranges and multiranges because ranges can't be created
681 * in the first place unless their subtypes support equality.)
682 */
683 if (eq_opr == ARRAY_EQ_OP &&
685 eq_opr = InvalidOid;
686 else if (eq_opr == RECORD_EQ_OP &&
688 eq_opr = InvalidOid;
689
690 /* Force update of eq_opr_finfo only if we're changing state */
691 if (typentry->eq_opr != eq_opr)
692 typentry->eq_opr_finfo.fn_oid = InvalidOid;
693
694 typentry->eq_opr = eq_opr;
695
696 /*
697 * Reset info about hash functions whenever we pick up new info about
698 * equality operator. This is so we can ensure that the hash
699 * functions match the operator.
700 */
701 typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC |
703 typentry->flags |= TCFLAGS_CHECKED_EQ_OPR;
704 }
705 if ((flags & TYPECACHE_LT_OPR) &&
706 !(typentry->flags & TCFLAGS_CHECKED_LT_OPR))
707 {
708 Oid lt_opr = InvalidOid;
709
710 if (typentry->btree_opf != InvalidOid)
711 lt_opr = get_opfamily_member(typentry->btree_opf,
712 typentry->btree_opintype,
713 typentry->btree_opintype,
715
716 /*
717 * As above, make sure array_cmp or record_cmp will succeed; but again
718 * we need no special check for ranges or multiranges.
719 */
720 if (lt_opr == ARRAY_LT_OP &&
721 !array_element_has_compare(typentry))
722 lt_opr = InvalidOid;
723 else if (lt_opr == RECORD_LT_OP &&
725 lt_opr = InvalidOid;
726
727 typentry->lt_opr = lt_opr;
728 typentry->flags |= TCFLAGS_CHECKED_LT_OPR;
729 }
730 if ((flags & TYPECACHE_GT_OPR) &&
731 !(typentry->flags & TCFLAGS_CHECKED_GT_OPR))
732 {
733 Oid gt_opr = InvalidOid;
734
735 if (typentry->btree_opf != InvalidOid)
736 gt_opr = get_opfamily_member(typentry->btree_opf,
737 typentry->btree_opintype,
738 typentry->btree_opintype,
740
741 /*
742 * As above, make sure array_cmp or record_cmp will succeed; but again
743 * we need no special check for ranges or multiranges.
744 */
745 if (gt_opr == ARRAY_GT_OP &&
746 !array_element_has_compare(typentry))
747 gt_opr = InvalidOid;
748 else if (gt_opr == RECORD_GT_OP &&
750 gt_opr = InvalidOid;
751
752 typentry->gt_opr = gt_opr;
753 typentry->flags |= TCFLAGS_CHECKED_GT_OPR;
754 }
756 !(typentry->flags & TCFLAGS_CHECKED_CMP_PROC))
757 {
758 Oid cmp_proc = InvalidOid;
759
760 if (typentry->btree_opf != InvalidOid)
761 cmp_proc = get_opfamily_proc(typentry->btree_opf,
762 typentry->btree_opintype,
763 typentry->btree_opintype,
765
766 /*
767 * As above, make sure array_cmp or record_cmp will succeed; but again
768 * we need no special check for ranges or multiranges.
769 */
770 if (cmp_proc == F_BTARRAYCMP &&
771 !array_element_has_compare(typentry))
772 cmp_proc = InvalidOid;
773 else if (cmp_proc == F_BTRECORDCMP &&
775 cmp_proc = InvalidOid;
776
777 /* Force update of cmp_proc_finfo only if we're changing state */
778 if (typentry->cmp_proc != cmp_proc)
779 typentry->cmp_proc_finfo.fn_oid = InvalidOid;
780
781 typentry->cmp_proc = cmp_proc;
782 typentry->flags |= TCFLAGS_CHECKED_CMP_PROC;
783 }
785 !(typentry->flags & TCFLAGS_CHECKED_HASH_PROC))
786 {
787 Oid hash_proc = InvalidOid;
788
789 /*
790 * We insist that the eq_opr, if one has been determined, match the
791 * hash opclass; else report there is no hash function.
792 */
793 if (typentry->hash_opf != InvalidOid &&
794 (!OidIsValid(typentry->eq_opr) ||
795 typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
796 typentry->hash_opintype,
797 typentry->hash_opintype,
799 hash_proc = get_opfamily_proc(typentry->hash_opf,
800 typentry->hash_opintype,
801 typentry->hash_opintype,
803
804 /*
805 * As above, make sure hash_array, hash_record, hash_range, or
806 * hash_multirange will succeed. Here we do need to check the range
807 * cases.
808 */
809 if (hash_proc == F_HASH_ARRAY &&
810 !array_element_has_hashing(typentry))
811 hash_proc = InvalidOid;
812 else if (hash_proc == F_HASH_RECORD &&
814 hash_proc = InvalidOid;
815 else if (hash_proc == F_HASH_RANGE &&
816 !range_element_has_hashing(typentry))
817 hash_proc = InvalidOid;
818 else if (hash_proc == F_HASH_MULTIRANGE &&
820 hash_proc = InvalidOid;
821
822 /* Force update of hash_proc_finfo only if we're changing state */
823 if (typentry->hash_proc != hash_proc)
825
826 typentry->hash_proc = hash_proc;
827 typentry->flags |= TCFLAGS_CHECKED_HASH_PROC;
828 }
829 if ((flags & (TYPECACHE_HASH_EXTENDED_PROC |
832 {
833 Oid hash_extended_proc = InvalidOid;
834
835 /*
836 * We insist that the eq_opr, if one has been determined, match the
837 * hash opclass; else report there is no hash function.
838 */
839 if (typentry->hash_opf != InvalidOid &&
840 (!OidIsValid(typentry->eq_opr) ||
841 typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
842 typentry->hash_opintype,
843 typentry->hash_opintype,
845 hash_extended_proc = get_opfamily_proc(typentry->hash_opf,
846 typentry->hash_opintype,
847 typentry->hash_opintype,
849
850 /*
851 * As above, make sure hash_array_extended, hash_record_extended,
852 * hash_range_extended, or hash_multirange_extended will succeed.
853 */
854 if (hash_extended_proc == F_HASH_ARRAY_EXTENDED &&
856 hash_extended_proc = InvalidOid;
857 else if (hash_extended_proc == F_HASH_RECORD_EXTENDED &&
859 hash_extended_proc = InvalidOid;
860 else if (hash_extended_proc == F_HASH_RANGE_EXTENDED &&
862 hash_extended_proc = InvalidOid;
863 else if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
865 hash_extended_proc = InvalidOid;
866
867 /* Force update of proc finfo only if we're changing state */
868 if (typentry->hash_extended_proc != hash_extended_proc)
870
871 typentry->hash_extended_proc = hash_extended_proc;
873 }
874
875 /*
876 * Set up fmgr lookup info as requested
877 *
878 * Note: we tell fmgr the finfo structures live in CacheMemoryContext,
879 * which is not quite right (they're really in the hash table's private
880 * memory context) but this will do for our purposes.
881 *
882 * Note: the code above avoids invalidating the finfo structs unless the
883 * referenced operator/function OID actually changes. This is to prevent
884 * unnecessary leakage of any subsidiary data attached to an finfo, since
885 * that would cause session-lifespan memory leaks.
886 */
887 if ((flags & TYPECACHE_EQ_OPR_FINFO) &&
888 typentry->eq_opr_finfo.fn_oid == InvalidOid &&
889 typentry->eq_opr != InvalidOid)
890 {
892
893 eq_opr_func = get_opcode(typentry->eq_opr);
894 if (eq_opr_func != InvalidOid)
897 }
898 if ((flags & TYPECACHE_CMP_PROC_FINFO) &&
899 typentry->cmp_proc_finfo.fn_oid == InvalidOid &&
900 typentry->cmp_proc != InvalidOid)
901 {
902 fmgr_info_cxt(typentry->cmp_proc, &typentry->cmp_proc_finfo,
904 }
905 if ((flags & TYPECACHE_HASH_PROC_FINFO) &&
906 typentry->hash_proc_finfo.fn_oid == InvalidOid &&
907 typentry->hash_proc != InvalidOid)
908 {
909 fmgr_info_cxt(typentry->hash_proc, &typentry->hash_proc_finfo,
911 }
914 typentry->hash_extended_proc != InvalidOid)
915 {
917 &typentry->hash_extended_proc_finfo,
919 }
920
921 /*
922 * If it's a composite type (row type), get tupdesc if requested
923 */
924 if ((flags & TYPECACHE_TUPDESC) &&
925 typentry->tupDesc == NULL &&
926 typentry->typtype == TYPTYPE_COMPOSITE)
927 {
928 load_typcache_tupdesc(typentry);
929 }
930
931 /*
932 * If requested, get information about a range type
933 *
934 * This includes making sure that the basic info about the range element
935 * type is up-to-date.
936 */
937 if ((flags & TYPECACHE_RANGE_INFO) &&
938 typentry->typtype == TYPTYPE_RANGE)
939 {
940 if (typentry->rngelemtype == NULL)
941 load_rangetype_info(typentry);
942 else if (!(typentry->rngelemtype->flags & TCFLAGS_HAVE_PG_TYPE_DATA))
943 (void) lookup_type_cache(typentry->rngelemtype->type_id, 0);
944 }
945
946 /*
947 * If requested, get information about a multirange type
948 */
949 if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
950 typentry->rngtype == NULL &&
951 typentry->typtype == TYPTYPE_MULTIRANGE)
952 {
953 load_multirangetype_info(typentry);
954 }
955
956 /*
957 * If requested, get information about a domain type
958 */
959 if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
960 typentry->domainBaseType == InvalidOid &&
961 typentry->typtype == TYPTYPE_DOMAIN)
962 {
963 typentry->domainBaseTypmod = -1;
964 typentry->domainBaseType =
965 getBaseTypeAndTypmod(type_id, &typentry->domainBaseTypmod);
966 }
967 if ((flags & TYPECACHE_DOMAIN_CONSTR_INFO) &&
968 (typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
969 typentry->typtype == TYPTYPE_DOMAIN)
970 {
971 load_domaintype_info(typentry);
972 }
973
974 INJECTION_POINT("typecache-before-rel-type-cache-insert", NULL);
975
978
980
981 return typentry;
982}

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_check_bounds_histogram(), 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 1382 of file typcache.c.

1383{
1384 List *result = NIL;
1386 ListCell *lc;
1387
1389
1390 foreach(lc, constraints)
1391 {
1394
1396 newr->constrainttype = r->constrainttype;
1397 newr->name = r->name;
1398 newr->check_expr = r->check_expr;
1399 newr->check_exprstate = ExecInitExpr(r->check_expr, NULL);
1400
1402 }
1403
1405
1406 return result;
1407}

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

1757{
1758 if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1760 return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1761}

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

1628{
1629 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1631 return (typentry->flags & TCFLAGS_HAVE_FIELD_COMPARE) != 0;
1632}

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

1636{
1637 if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
1639 return (typentry->flags & TCFLAGS_HAVE_FIELD_HASHING) != 0;
1640}

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

2068{
2069 const RecordCacheEntry *left = a;
2070 const RecordCacheEntry *right = b;
2071
2072 return equalRowTypes(left->tupdesc, right->tupdesc) ? 0 : 1;
2073}

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

2057{
2058 const RecordCacheEntry *entry = data;
2059
2060 return hashRowType(entry->tupdesc);
2061}

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

2964{
2966 TupleDesc shared;
2967
2968 shared_dp = dsa_allocate(area, TupleDescSize(tupdesc));
2969 shared = (TupleDesc) dsa_get_address(area, shared_dp);
2970 TupleDescCopy(shared, tupdesc);
2971 shared->tdtypmod = typmod;
2972
2973 return shared_dp;
2974}

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

2338{
2342
2344
2345 /* We can't already be attached to a shared registry. */
2352
2353 /*
2354 * We can't already have typmods in our local cache, because they'd clash
2355 * with those imported by SharedRecordTypmodRegistryInit. This should be
2356 * a freshly started parallel worker. If we ever support worker
2357 * recycling, a worker would need to zap its local cache in between
2358 * servicing different queries, in order to be able to call this and
2359 * synchronize typmods with a new leader; but that's problematic because
2360 * we can't be very sure that record-typmod-related state hasn't escaped
2361 * to anywhere else in the process.
2362 */
2364
2366
2367 /* Attach to the two hash tables. */
2370 registry->record_table_handle,
2374 registry->typmod_table_handle,
2375 NULL);
2376
2378
2379 /*
2380 * Set up detach hook to run at worker exit. Currently this is the same
2381 * as the leader's detach hook, but in future they might need to be
2382 * different.
2383 */
2387
2388 /*
2389 * Set up the session state that will tell assign_record_type_typmod and
2390 * lookup_rowtype_tupdesc_internal about the shared registry.
2391 */
2395}

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

2217{
2218 return sizeof(SharedRecordTypmodRegistry);
2219}

Referenced by GetSessionDsmHandle().

◆ SharedRecordTypmodRegistryInit()

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

Definition at line 2238 of file typcache.c.

2241{
2245 int32 typmod;
2246
2248
2249 /* We can't already be attached to a shared registry. */
2253
2255
2256 /* Create the hash table of tuple descriptors indexed by themselves. */
2258
2259 /* Create the hash table of tuple descriptors indexed by typmod. */
2261
2263
2264 /* Initialize the SharedRecordTypmodRegistry. */
2265 registry->record_table_handle = dshash_get_hash_table_handle(record_table);
2266 registry->typmod_table_handle = dshash_get_hash_table_handle(typmod_table);
2268
2269 /*
2270 * Copy all entries from this backend's private registry into the shared
2271 * registry.
2272 */
2273 for (typmod = 0; typmod < NextRecordTypmod; ++typmod)
2274 {
2279 TupleDesc tupdesc;
2280 bool found;
2281
2282 tupdesc = RecordCacheArray[typmod].tupdesc;
2283 if (tupdesc == NULL)
2284 continue;
2285
2286 /* Copy the TupleDesc into shared memory. */
2287 shared_dp = share_tupledesc(area, tupdesc, typmod);
2288
2289 /* Insert into the typmod table. */
2291 &tupdesc->tdtypmod,
2292 &found);
2293 if (found)
2294 elog(ERROR, "cannot create duplicate shared record typmod");
2295 typmod_table_entry->typmod = tupdesc->tdtypmod;
2296 typmod_table_entry->shared_tupdesc = shared_dp;
2298
2299 /* Insert into the record table. */
2300 record_table_key.shared = false;
2301 record_table_key.u.local_tupdesc = tupdesc;
2304 &found);
2305 if (!found)
2306 {
2307 record_table_entry->key.shared = true;
2308 record_table_entry->key.u.shared_tupdesc = shared_dp;
2309 }
2311 }
2312
2313 /*
2314 * Set up the global state that will tell assign_record_type_typmod and
2315 * lookup_rowtype_tupdesc_internal about the shared registry.
2316 */
2320
2321 /*
2322 * We install a detach hook in the leader, but only to handle cleanup on
2323 * failure during GetSessionDsmHandle(). Once GetSessionDsmHandle() pins
2324 * the memory, the leader process will use a shared registry until it
2325 * exits.
2326 */
2328}

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

2653{
2654 TypeCacheEntry *typentry;
2655
2656 /*
2657 * Because this is called very frequently, and typically very few of the
2658 * typcache entries are for domains, we don't use hash_seq_search here.
2659 * Instead we thread all the domain-type entries together so that we can
2660 * visit them cheaply.
2661 */
2662 for (typentry = firstDomainTypeEntry;
2663 typentry != NULL;
2664 typentry = typentry->nextDomain)
2665 {
2666 /* Reset domain constraint validity information */
2668 }
2669}

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

2615{
2616 HASH_SEQ_STATUS status;
2617 TypeCacheEntry *typentry;
2618
2619 /* TypeCacheHash must exist, else this callback wouldn't be registered */
2620 hash_seq_init(&status, TypeCacheHash);
2621 while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
2622 {
2623 bool hadOpclass = (typentry->flags & TCFLAGS_OPERATOR_FLAGS);
2624
2625 /* Reset equality/comparison/hashing validity information */
2626 typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS;
2627
2628 /*
2629 * Call delete_rel_type_cache_if_needed() if we actually cleared some
2630 * of TCFLAGS_OPERATOR_FLAGS.
2631 */
2632 if (hadOpclass)
2634 }
2635}

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

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

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

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

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

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

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