diff options
Diffstat (limited to 'src/include/utils/expandedrecord.h')
-rw-r--r-- | src/include/utils/expandedrecord.h | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/src/include/utils/expandedrecord.h b/src/include/utils/expandedrecord.h new file mode 100644 index 0000000..0f744b1 --- /dev/null +++ b/src/include/utils/expandedrecord.h @@ -0,0 +1,231 @@ +/*------------------------------------------------------------------------- + * + * expandedrecord.h + * Declarations for composite expanded objects. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/utils/expandedrecord.h + * + *------------------------------------------------------------------------- + */ +#ifndef EXPANDEDRECORD_H +#define EXPANDEDRECORD_H + +#include "access/htup.h" +#include "access/tupdesc.h" +#include "fmgr.h" +#include "utils/expandeddatum.h" + + +/* + * An expanded record is contained within a private memory context (as + * all expanded objects must be) and has a control structure as below. + * + * The expanded record might contain a regular "flat" tuple if that was the + * original input and we've not modified it. Otherwise, the contents are + * represented by Datum/isnull arrays plus type information. We could also + * have both forms, if we've deconstructed the original tuple for access + * purposes but not yet changed it. For pass-by-reference field types, the + * Datums would point into the flat tuple in this situation. Once we start + * modifying tuple fields, new pass-by-ref fields are separately palloc'd + * within the memory context. + * + * It's possible to build an expanded record that references a "flat" tuple + * stored externally, if the caller can guarantee that that tuple will not + * change for the lifetime of the expanded record. (This frammish is mainly + * meant to avoid unnecessary data copying in trigger functions.) + */ +#define ER_MAGIC 1384727874 /* ID for debugging crosschecks */ + +typedef struct ExpandedRecordHeader +{ + /* Standard header for expanded objects */ + ExpandedObjectHeader hdr; + + /* Magic value identifying an expanded record (for debugging only) */ + int er_magic; + + /* Assorted flag bits */ + int flags; +#define ER_FLAG_FVALUE_VALID 0x0001 /* fvalue is up to date? */ +#define ER_FLAG_FVALUE_ALLOCED 0x0002 /* fvalue is local storage? */ +#define ER_FLAG_DVALUES_VALID 0x0004 /* dvalues/dnulls are up to date? */ +#define ER_FLAG_DVALUES_ALLOCED 0x0008 /* any field values local storage? */ +#define ER_FLAG_HAVE_EXTERNAL 0x0010 /* any field values are external? */ +#define ER_FLAG_TUPDESC_ALLOCED 0x0020 /* tupdesc is local storage? */ +#define ER_FLAG_IS_DOMAIN 0x0040 /* er_decltypeid is domain? */ +#define ER_FLAG_IS_DUMMY 0x0080 /* this header is dummy (see below) */ +/* flag bits that are not to be cleared when replacing tuple data: */ +#define ER_FLAGS_NON_DATA \ + (ER_FLAG_TUPDESC_ALLOCED | ER_FLAG_IS_DOMAIN | ER_FLAG_IS_DUMMY) + + /* Declared type of the record variable (could be a domain type) */ + Oid er_decltypeid; + + /* + * Actual composite type/typmod; never a domain (if ER_FLAG_IS_DOMAIN, + * these identify the composite base type). These will match + * er_tupdesc->tdtypeid/tdtypmod, as well as the header fields of + * composite datums made from or stored in this expanded record. + */ + Oid er_typeid; /* type OID of the composite type */ + int32 er_typmod; /* typmod of the composite type */ + + /* + * Tuple descriptor, if we have one, else NULL. This may point to a + * reference-counted tupdesc originally belonging to the typcache, in + * which case we use a memory context reset callback to release the + * refcount. It can also be locally allocated in this object's private + * context (in which case ER_FLAG_TUPDESC_ALLOCED is set). + */ + TupleDesc er_tupdesc; + + /* + * Unique-within-process identifier for the tupdesc (see typcache.h). This + * field will never be equal to INVALID_TUPLEDESC_IDENTIFIER. + */ + uint64 er_tupdesc_id; + + /* + * If we have a Datum-array representation of the record, it's kept here; + * else ER_FLAG_DVALUES_VALID is not set, and dvalues/dnulls may be NULL + * if they've not yet been allocated. If allocated, the dvalues and + * dnulls arrays are palloc'd within the object private context, and are + * of length matching er_tupdesc->natts. For pass-by-ref field types, + * dvalues entries might point either into the fstartptr..fendptr area, or + * to separately palloc'd chunks. + */ + Datum *dvalues; /* array of Datums */ + bool *dnulls; /* array of is-null flags for Datums */ + int nfields; /* length of above arrays */ + + /* + * flat_size is the current space requirement for the flat equivalent of + * the expanded record, if known; otherwise it's 0. We store this to make + * consecutive calls of get_flat_size cheap. If flat_size is not 0, the + * component values data_len, hoff, and hasnull must be valid too. + */ + Size flat_size; + + Size data_len; /* data len within flat_size */ + int hoff; /* header offset */ + bool hasnull; /* null bitmap needed? */ + + /* + * fvalue points to the flat representation if we have one, else it is + * NULL. If the flat representation is valid (up to date) then + * ER_FLAG_FVALUE_VALID is set. Even if we've outdated the flat + * representation due to changes of user fields, it can still be used to + * fetch system column values. If we have a flat representation then + * fstartptr/fendptr point to the start and end+1 of its data area; this + * is so that we can tell which Datum pointers point into the flat + * representation rather than being pointers to separately palloc'd data. + */ + HeapTuple fvalue; /* might or might not be private storage */ + char *fstartptr; /* start of its data area */ + char *fendptr; /* end+1 of its data area */ + + /* Some operations on the expanded record need a short-lived context */ + MemoryContext er_short_term_cxt; /* short-term memory context */ + + /* Working state for domain checking, used if ER_FLAG_IS_DOMAIN is set */ + struct ExpandedRecordHeader *er_dummy_header; /* dummy record header */ + void *er_domaininfo; /* cache space for domain_check() */ + + /* Callback info (it's active if er_mcb.arg is not NULL) */ + MemoryContextCallback er_mcb; +} ExpandedRecordHeader; + +/* fmgr macros for expanded record objects */ +#define PG_GETARG_EXPANDED_RECORD(n) DatumGetExpandedRecord(PG_GETARG_DATUM(n)) +#define ExpandedRecordGetDatum(erh) EOHPGetRWDatum(&(erh)->hdr) +#define ExpandedRecordGetRODatum(erh) EOHPGetRODatum(&(erh)->hdr) +#define PG_RETURN_EXPANDED_RECORD(x) PG_RETURN_DATUM(ExpandedRecordGetDatum(x)) + +/* assorted other macros */ +#define ExpandedRecordIsEmpty(erh) \ + (((erh)->flags & (ER_FLAG_DVALUES_VALID | ER_FLAG_FVALUE_VALID)) == 0) +#define ExpandedRecordIsDomain(erh) \ + (((erh)->flags & ER_FLAG_IS_DOMAIN) != 0) + +/* this can substitute for TransferExpandedObject() when we already have erh */ +#define TransferExpandedRecord(erh, cxt) \ + MemoryContextSetParent((erh)->hdr.eoh_context, cxt) + +/* information returned by expanded_record_lookup_field() */ +typedef struct ExpandedRecordFieldInfo +{ + int fnumber; /* field's attr number in record */ + Oid ftypeid; /* field's type/typmod info */ + int32 ftypmod; + Oid fcollation; /* field's collation if any */ +} ExpandedRecordFieldInfo; + +/* + * prototypes for functions defined in expandedrecord.c + */ +extern ExpandedRecordHeader *make_expanded_record_from_typeid(Oid type_id, int32 typmod, + MemoryContext parentcontext); +extern ExpandedRecordHeader *make_expanded_record_from_tupdesc(TupleDesc tupdesc, + MemoryContext parentcontext); +extern ExpandedRecordHeader *make_expanded_record_from_exprecord(ExpandedRecordHeader *olderh, + MemoryContext parentcontext); +extern void expanded_record_set_tuple(ExpandedRecordHeader *erh, + HeapTuple tuple, bool copy, bool expand_external); +extern Datum make_expanded_record_from_datum(Datum recorddatum, + MemoryContext parentcontext); +extern TupleDesc expanded_record_fetch_tupdesc(ExpandedRecordHeader *erh); +extern HeapTuple expanded_record_get_tuple(ExpandedRecordHeader *erh); +extern ExpandedRecordHeader *DatumGetExpandedRecord(Datum d); +extern void deconstruct_expanded_record(ExpandedRecordHeader *erh); +extern bool expanded_record_lookup_field(ExpandedRecordHeader *erh, + const char *fieldname, + ExpandedRecordFieldInfo *finfo); +extern Datum expanded_record_fetch_field(ExpandedRecordHeader *erh, int fnumber, + bool *isnull); +extern void expanded_record_set_field_internal(ExpandedRecordHeader *erh, + int fnumber, + Datum newValue, bool isnull, + bool expand_external, + bool check_constraints); +extern void expanded_record_set_fields(ExpandedRecordHeader *erh, + const Datum *newValues, const bool *isnulls, + bool expand_external); + +/* outside code should never call expanded_record_set_field_internal as such */ +#define expanded_record_set_field(erh, fnumber, newValue, isnull, expand_external) \ + expanded_record_set_field_internal(erh, fnumber, newValue, isnull, expand_external, true) + +/* + * Inline-able fast cases. The expanded_record_fetch_xxx functions above + * handle the general cases. + */ + +/* Get the tupdesc for the expanded record's actual type */ +static inline TupleDesc +expanded_record_get_tupdesc(ExpandedRecordHeader *erh) +{ + if (likely(erh->er_tupdesc != NULL)) + return erh->er_tupdesc; + else + return expanded_record_fetch_tupdesc(erh); +} + +/* Get value of record field */ +static inline Datum +expanded_record_get_field(ExpandedRecordHeader *erh, int fnumber, + bool *isnull) +{ + if ((erh->flags & ER_FLAG_DVALUES_VALID) && + likely(fnumber > 0 && fnumber <= erh->nfields)) + { + *isnull = erh->dnulls[fnumber - 1]; + return erh->dvalues[fnumber - 1]; + } + else + return expanded_record_fetch_field(erh, fnumber, isnull); +} + +#endif /* EXPANDEDRECORD_H */ |