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 | |
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 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() */ |
158 | typedef 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 | */ |
169 | extern ExpandedRecordHeader *make_expanded_record_from_typeid(Oid type_id, int32 typmod, |
170 | MemoryContext parentcontext); |
171 | extern ExpandedRecordHeader *make_expanded_record_from_tupdesc(TupleDesc tupdesc, |
172 | MemoryContext parentcontext); |
173 | extern ExpandedRecordHeader *make_expanded_record_from_exprecord(ExpandedRecordHeader *olderh, |
174 | MemoryContext parentcontext); |
175 | extern void expanded_record_set_tuple(ExpandedRecordHeader *erh, |
176 | HeapTuple tuple, bool copy, bool expand_external); |
177 | extern Datum make_expanded_record_from_datum(Datum recorddatum, |
178 | MemoryContext parentcontext); |
179 | extern TupleDesc expanded_record_fetch_tupdesc(ExpandedRecordHeader *erh); |
180 | extern HeapTuple expanded_record_get_tuple(ExpandedRecordHeader *erh); |
181 | extern ExpandedRecordHeader *DatumGetExpandedRecord(Datum d); |
182 | extern void deconstruct_expanded_record(ExpandedRecordHeader *erh); |
183 | extern bool expanded_record_lookup_field(ExpandedRecordHeader *erh, |
184 | const char *fieldname, |
185 | ExpandedRecordFieldInfo *finfo); |
186 | extern Datum expanded_record_fetch_field(ExpandedRecordHeader *erh, int fnumber, |
187 | bool *isnull); |
188 | extern void expanded_record_set_field_internal(ExpandedRecordHeader *erh, |
189 | int fnumber, |
190 | Datum newValue, bool isnull, |
191 | bool expand_external, |
192 | bool check_constraints); |
193 | extern 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 */ |
207 | static inline TupleDesc |
208 | expanded_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 */ |
217 | static inline Datum |
218 | expanded_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 | |