| 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 | |