1/*-------------------------------------------------------------------------
2 *
3 * expandedrecord.h
4 * Declarations for composite expanded objects.
5 *
6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 * src/include/utils/expandedrecord.h
10 *
11 *-------------------------------------------------------------------------
12 */
13#ifndef EXPANDEDRECORD_H
14#define EXPANDEDRECORD_H
15
16#include "access/htup.h"
17#include "access/tupdesc.h"
18#include "fmgr.h"
19#include "utils/expandeddatum.h"
20
21
22/*
23 * An expanded record is contained within a private memory context (as
24 * all expanded objects must be) and has a control structure as below.
25 *
26 * The expanded record might contain a regular "flat" tuple if that was the
27 * original input and we've not modified it. Otherwise, the contents are
28 * represented by Datum/isnull arrays plus type information. We could also
29 * have both forms, if we've deconstructed the original tuple for access
30 * purposes but not yet changed it. For pass-by-reference field types, the
31 * Datums would point into the flat tuple in this situation. Once we start
32 * modifying tuple fields, new pass-by-ref fields are separately palloc'd
33 * within the memory context.
34 *
35 * It's possible to build an expanded record that references a "flat" tuple
36 * stored externally, if the caller can guarantee that that tuple will not
37 * change for the lifetime of the expanded record. (This frammish is mainly
38 * meant to avoid unnecessary data copying in trigger functions.)
39 */
40#define ER_MAGIC 1384727874 /* ID for debugging crosschecks */
41
42typedef struct ExpandedRecordHeader
43{
44 /* Standard header for expanded objects */
45 ExpandedObjectHeader hdr;
46
47 /* Magic value identifying an expanded record (for debugging only) */
48 int er_magic;
49
50 /* Assorted flag bits */
51 int flags;
52#define ER_FLAG_FVALUE_VALID 0x0001 /* fvalue is up to date? */
53#define ER_FLAG_FVALUE_ALLOCED 0x0002 /* fvalue is local storage? */
54#define ER_FLAG_DVALUES_VALID 0x0004 /* dvalues/dnulls are up to date? */
55#define ER_FLAG_DVALUES_ALLOCED 0x0008 /* any field values local storage? */
56#define ER_FLAG_HAVE_EXTERNAL 0x0010 /* any field values are external? */
57#define ER_FLAG_TUPDESC_ALLOCED 0x0020 /* tupdesc is local storage? */
58#define ER_FLAG_IS_DOMAIN 0x0040 /* er_decltypeid is domain? */
59#define ER_FLAG_IS_DUMMY 0x0080 /* this header is dummy (see below) */
60/* flag bits that are not to be cleared when replacing tuple data: */
61#define ER_FLAGS_NON_DATA \
62 (ER_FLAG_TUPDESC_ALLOCED | ER_FLAG_IS_DOMAIN | ER_FLAG_IS_DUMMY)
63
64 /* Declared type of the record variable (could be a domain type) */
65 Oid er_decltypeid;
66
67 /*
68 * Actual composite type/typmod; never a domain (if ER_FLAG_IS_DOMAIN,
69 * these identify the composite base type). These will match
70 * er_tupdesc->tdtypeid/tdtypmod, as well as the header fields of
71 * composite datums made from or stored in this expanded record.
72 */
73 Oid er_typeid; /* type OID of the composite type */
74 int32 er_typmod; /* typmod of the composite type */
75
76 /*
77 * Tuple descriptor, if we have one, else NULL. This may point to a
78 * reference-counted tupdesc originally belonging to the typcache, in
79 * which case we use a memory context reset callback to release the
80 * refcount. It can also be locally allocated in this object's private
81 * context (in which case ER_FLAG_TUPDESC_ALLOCED is set).
82 */
83 TupleDesc er_tupdesc;
84
85 /*
86 * Unique-within-process identifier for the tupdesc (see typcache.h). This
87 * field will never be equal to INVALID_TUPLEDESC_IDENTIFIER.
88 */
89 uint64 er_tupdesc_id;
90
91 /*
92 * If we have a Datum-array representation of the record, it's kept here;
93 * else ER_FLAG_DVALUES_VALID is not set, and dvalues/dnulls may be NULL
94 * if they've not yet been allocated. If allocated, the dvalues and
95 * dnulls arrays are palloc'd within the object private context, and are
96 * of length matching er_tupdesc->natts. For pass-by-ref field types,
97 * dvalues entries might point either into the fstartptr..fendptr area, or
98 * to separately palloc'd chunks.
99 */
100 Datum *dvalues; /* array of Datums */
101 bool *dnulls; /* array of is-null flags for Datums */
102 int nfields; /* length of above arrays */
103
104 /*
105 * flat_size is the current space requirement for the flat equivalent of
106 * the expanded record, if known; otherwise it's 0. We store this to make
107 * consecutive calls of get_flat_size cheap. If flat_size is not 0, the
108 * component values data_len, hoff, and hasnull must be valid too.
109 */
110 Size flat_size;
111
112 Size data_len; /* data len within flat_size */
113 int hoff; /* header offset */
114 bool hasnull; /* null bitmap needed? */
115
116 /*
117 * fvalue points to the flat representation if we have one, else it is
118 * NULL. If the flat representation is valid (up to date) then
119 * ER_FLAG_FVALUE_VALID is set. Even if we've outdated the flat
120 * representation due to changes of user fields, it can still be used to
121 * fetch system column values. If we have a flat representation then
122 * fstartptr/fendptr point to the start and end+1 of its data area; this
123 * is so that we can tell which Datum pointers point into the flat
124 * representation rather than being pointers to separately palloc'd data.
125 */
126 HeapTuple fvalue; /* might or might not be private storage */
127 char *fstartptr; /* start of its data area */
128 char *fendptr; /* end+1 of its data area */
129
130 /* Some operations on the expanded record need a short-lived context */
131 MemoryContext er_short_term_cxt; /* short-term memory context */
132
133 /* Working state for domain checking, used if ER_FLAG_IS_DOMAIN is set */
134 struct ExpandedRecordHeader *er_dummy_header; /* dummy record header */
135 void *er_domaininfo; /* cache space for domain_check() */
136
137 /* Callback info (it's active if er_mcb.arg is not NULL) */
138 MemoryContextCallback er_mcb;
139} ExpandedRecordHeader;
140
141/* fmgr macros for expanded record objects */
142#define PG_GETARG_EXPANDED_RECORD(n) DatumGetExpandedRecord(PG_GETARG_DATUM(n))
143#define ExpandedRecordGetDatum(erh) EOHPGetRWDatum(&(erh)->hdr)
144#define ExpandedRecordGetRODatum(erh) EOHPGetRODatum(&(erh)->hdr)
145#define PG_RETURN_EXPANDED_RECORD(x) PG_RETURN_DATUM(ExpandedRecordGetDatum(x))
146
147/* assorted other macros */
148#define ExpandedRecordIsEmpty(erh) \
149 (((erh)->flags & (ER_FLAG_DVALUES_VALID | ER_FLAG_FVALUE_VALID)) == 0)
150#define ExpandedRecordIsDomain(erh) \
151 (((erh)->flags & ER_FLAG_IS_DOMAIN) != 0)
152
153/* this can substitute for TransferExpandedObject() when we already have erh */
154#define TransferExpandedRecord(erh, cxt) \
155 MemoryContextSetParent((erh)->hdr.eoh_context, cxt)
156
157/* information returned by expanded_record_lookup_field() */
158typedef struct ExpandedRecordFieldInfo
159{
160 int fnumber; /* field's attr number in record */
161 Oid ftypeid; /* field's type/typmod info */
162 int32 ftypmod;
163 Oid fcollation; /* field's collation if any */
164} ExpandedRecordFieldInfo;
165
166/*
167 * prototypes for functions defined in expandedrecord.c
168 */
169extern ExpandedRecordHeader *make_expanded_record_from_typeid(Oid type_id, int32 typmod,
170 MemoryContext parentcontext);
171extern ExpandedRecordHeader *make_expanded_record_from_tupdesc(TupleDesc tupdesc,
172 MemoryContext parentcontext);
173extern ExpandedRecordHeader *make_expanded_record_from_exprecord(ExpandedRecordHeader *olderh,
174 MemoryContext parentcontext);
175extern void expanded_record_set_tuple(ExpandedRecordHeader *erh,
176 HeapTuple tuple, bool copy, bool expand_external);
177extern Datum make_expanded_record_from_datum(Datum recorddatum,
178 MemoryContext parentcontext);
179extern TupleDesc expanded_record_fetch_tupdesc(ExpandedRecordHeader *erh);
180extern HeapTuple expanded_record_get_tuple(ExpandedRecordHeader *erh);
181extern ExpandedRecordHeader *DatumGetExpandedRecord(Datum d);
182extern void deconstruct_expanded_record(ExpandedRecordHeader *erh);
183extern bool expanded_record_lookup_field(ExpandedRecordHeader *erh,
184 const char *fieldname,
185 ExpandedRecordFieldInfo *finfo);
186extern Datum expanded_record_fetch_field(ExpandedRecordHeader *erh, int fnumber,
187 bool *isnull);
188extern void expanded_record_set_field_internal(ExpandedRecordHeader *erh,
189 int fnumber,
190 Datum newValue, bool isnull,
191 bool expand_external,
192 bool check_constraints);
193extern void expanded_record_set_fields(ExpandedRecordHeader *erh,
194 const Datum *newValues, const bool *isnulls,
195 bool expand_external);
196
197/* outside code should never call expanded_record_set_field_internal as such */
198#define expanded_record_set_field(erh, fnumber, newValue, isnull, expand_external) \
199 expanded_record_set_field_internal(erh, fnumber, newValue, isnull, expand_external, true)
200
201/*
202 * Inline-able fast cases. The expanded_record_fetch_xxx functions above
203 * handle the general cases.
204 */
205
206/* Get the tupdesc for the expanded record's actual type */
207static inline TupleDesc
208expanded_record_get_tupdesc(ExpandedRecordHeader *erh)
209{
210 if (likely(erh->er_tupdesc != NULL))
211 return erh->er_tupdesc;
212 else
213 return expanded_record_fetch_tupdesc(erh);
214}
215
216/* Get value of record field */
217static inline Datum
218expanded_record_get_field(ExpandedRecordHeader *erh, int fnumber,
219 bool *isnull)
220{
221 if ((erh->flags & ER_FLAG_DVALUES_VALID) &&
222 likely(fnumber > 0 && fnumber <= erh->nfields))
223 {
224 *isnull = erh->dnulls[fnumber - 1];
225 return erh->dvalues[fnumber - 1];
226 }
227 else
228 return expanded_record_fetch_field(erh, fnumber, isnull);
229}
230
231#endif /* EXPANDEDRECORD_H */
232