Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * expandedrecord.h
4 : * Declarations for composite expanded objects.
5 : *
6 : * Portions Copyright (c) 1996-2024, 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 :
42 : typedef 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 functions and macros for expanded record objects */
142 : static inline Datum
143 17722 : ExpandedRecordGetDatum(const ExpandedRecordHeader *erh)
144 : {
145 17722 : return EOHPGetRWDatum(&erh->hdr);
146 : }
147 :
148 : static inline Datum
149 74 : ExpandedRecordGetRODatum(const ExpandedRecordHeader *erh)
150 : {
151 74 : return EOHPGetRODatum(&erh->hdr);
152 : }
153 :
154 : #define PG_GETARG_EXPANDED_RECORD(n) DatumGetExpandedRecord(PG_GETARG_DATUM(n))
155 : #define PG_RETURN_EXPANDED_RECORD(x) PG_RETURN_DATUM(ExpandedRecordGetDatum(x))
156 :
157 : /* assorted other macros */
158 : #define ExpandedRecordIsEmpty(erh) \
159 : (((erh)->flags & (ER_FLAG_DVALUES_VALID | ER_FLAG_FVALUE_VALID)) == 0)
160 : #define ExpandedRecordIsDomain(erh) \
161 : (((erh)->flags & ER_FLAG_IS_DOMAIN) != 0)
162 :
163 : /* this can substitute for TransferExpandedObject() when we already have erh */
164 : #define TransferExpandedRecord(erh, cxt) \
165 : MemoryContextSetParent((erh)->hdr.eoh_context, cxt)
166 :
167 : /* information returned by expanded_record_lookup_field() */
168 : typedef struct ExpandedRecordFieldInfo
169 : {
170 : int fnumber; /* field's attr number in record */
171 : Oid ftypeid; /* field's type/typmod info */
172 : int32 ftypmod;
173 : Oid fcollation; /* field's collation if any */
174 : } ExpandedRecordFieldInfo;
175 :
176 : /*
177 : * prototypes for functions defined in expandedrecord.c
178 : */
179 : extern ExpandedRecordHeader *make_expanded_record_from_typeid(Oid type_id, int32 typmod,
180 : MemoryContext parentcontext);
181 : extern ExpandedRecordHeader *make_expanded_record_from_tupdesc(TupleDesc tupdesc,
182 : MemoryContext parentcontext);
183 : extern ExpandedRecordHeader *make_expanded_record_from_exprecord(ExpandedRecordHeader *olderh,
184 : MemoryContext parentcontext);
185 : extern void expanded_record_set_tuple(ExpandedRecordHeader *erh,
186 : HeapTuple tuple, bool copy, bool expand_external);
187 : extern Datum make_expanded_record_from_datum(Datum recorddatum,
188 : MemoryContext parentcontext);
189 : extern TupleDesc expanded_record_fetch_tupdesc(ExpandedRecordHeader *erh);
190 : extern HeapTuple expanded_record_get_tuple(ExpandedRecordHeader *erh);
191 : extern ExpandedRecordHeader *DatumGetExpandedRecord(Datum d);
192 : extern void deconstruct_expanded_record(ExpandedRecordHeader *erh);
193 : extern bool expanded_record_lookup_field(ExpandedRecordHeader *erh,
194 : const char *fieldname,
195 : ExpandedRecordFieldInfo *finfo);
196 : extern Datum expanded_record_fetch_field(ExpandedRecordHeader *erh, int fnumber,
197 : bool *isnull);
198 : extern void expanded_record_set_field_internal(ExpandedRecordHeader *erh,
199 : int fnumber,
200 : Datum newValue, bool isnull,
201 : bool expand_external,
202 : bool check_constraints);
203 : extern void expanded_record_set_fields(ExpandedRecordHeader *erh,
204 : const Datum *newValues, const bool *isnulls,
205 : bool expand_external);
206 :
207 : /* outside code should never call expanded_record_set_field_internal as such */
208 : #define expanded_record_set_field(erh, fnumber, newValue, isnull, expand_external) \
209 : expanded_record_set_field_internal(erh, fnumber, newValue, isnull, expand_external, true)
210 :
211 : /*
212 : * Inline-able fast cases. The expanded_record_fetch_xxx functions above
213 : * handle the general cases.
214 : */
215 :
216 : /* Get the tupdesc for the expanded record's actual type */
217 : static inline TupleDesc
218 59134 : expanded_record_get_tupdesc(ExpandedRecordHeader *erh)
219 : {
220 59134 : if (likely(erh->er_tupdesc != NULL))
221 59134 : return erh->er_tupdesc;
222 : else
223 0 : return expanded_record_fetch_tupdesc(erh);
224 : }
225 :
226 : /* Get value of record field */
227 : static inline Datum
228 62766 : expanded_record_get_field(ExpandedRecordHeader *erh, int fnumber,
229 : bool *isnull)
230 : {
231 62766 : if ((erh->flags & ER_FLAG_DVALUES_VALID) &&
232 40964 : likely(fnumber > 0 && fnumber <= erh->nfields))
233 : {
234 40964 : *isnull = erh->dnulls[fnumber - 1];
235 40964 : return erh->dvalues[fnumber - 1];
236 : }
237 : else
238 21802 : return expanded_record_fetch_field(erh, fnumber, isnull);
239 : }
240 :
241 : #endif /* EXPANDEDRECORD_H */
|