summaryrefslogtreecommitdiffstats
path: root/src/include/utils/expandedrecord.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/include/utils/expandedrecord.h')
-rw-r--r--src/include/utils/expandedrecord.h231
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 */