Age Owner Branch data TLA 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
565 peter@eisentraut.org 143 :CBC 8860 : ExpandedRecordGetDatum(const ExpandedRecordHeader *erh)
144 : : {
145 : 8860 : return EOHPGetRWDatum(&erh->hdr);
146 : : }
147 : :
148 : : static inline Datum
149 : 37 : ExpandedRecordGetRODatum(const ExpandedRecordHeader *erh)
150 : : {
151 : 37 : 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
2252 tgl@sss.pgh.pa.us 218 : 29580 : expanded_record_get_tupdesc(ExpandedRecordHeader *erh)
219 : : {
220 [ + - ]: 29580 : if (likely(erh->er_tupdesc != NULL))
221 : 29580 : return erh->er_tupdesc;
222 : : else
2252 tgl@sss.pgh.pa.us 223 :UBC 0 : return expanded_record_fetch_tupdesc(erh);
224 : : }
225 : :
226 : : /* Get value of record field */
227 : : static inline Datum
2252 tgl@sss.pgh.pa.us 228 :CBC 31396 : expanded_record_get_field(ExpandedRecordHeader *erh, int fnumber,
229 : : bool *isnull)
230 : : {
231 [ + + ]: 31396 : if ((erh->flags & ER_FLAG_DVALUES_VALID) &&
232 [ + - + - : 20516 : likely(fnumber > 0 && fnumber <= erh->nfields))
+ - ]
233 : : {
234 : 20516 : *isnull = erh->dnulls[fnumber - 1];
235 : 20516 : return erh->dvalues[fnumber - 1];
236 : : }
237 : : else
238 : 10880 : return expanded_record_fetch_field(erh, fnumber, isnull);
239 : : }
240 : :
241 : : #endif /* EXPANDEDRECORD_H */
|