mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-10-06 01:46:58 +02:00
c8b2ef05f4
The previous macro implementations just cast the argument to a target type but did not check whether the input type was appropriate. The function implementation can do better type checking of the input type. For the *GetDatumFast() macros, converting to an inline function doesn't work in the !USE_FLOAT8_BYVAL case, but we can use AssertVariableIsOfTypeMacro() to get a similar level of type checking. Reviewed-by: Aleksander Alekseev <aleksander@timescale.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://www.postgresql.org/message-id/flat/8528fb7e-0aa2-6b54-85fb-0c0886dbd6ed%40enterprisedb.com
242 lines
9.5 KiB
C
242 lines
9.5 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* expandedrecord.h
|
|
* Declarations for composite expanded objects.
|
|
*
|
|
* Portions Copyright (c) 1996-2022, 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 functions and macros for expanded record objects */
|
|
static inline Datum
|
|
ExpandedRecordGetDatum(const ExpandedRecordHeader *erh)
|
|
{
|
|
return EOHPGetRWDatum(&erh->hdr);
|
|
}
|
|
|
|
static inline Datum
|
|
ExpandedRecordGetRODatum(const ExpandedRecordHeader *erh)
|
|
{
|
|
return EOHPGetRODatum(&erh->hdr);
|
|
}
|
|
|
|
#define PG_GETARG_EXPANDED_RECORD(n) DatumGetExpandedRecord(PG_GETARG_DATUM(n))
|
|
#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 */
|