1/*-------------------------------------------------------------------------
4 * Functions for manipulating composite expanded objects.
6 * This module supports "expanded objects" (cf. expandeddatum.h) that can
7 * store values of named composite types, domains over named composite types,
8 * and record types (registered or anonymous).
10 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
11 * Portions Copyright (c) 1994, Regents of the University of California
15 * src/backend/utils/adt/expandedrecord.c
17 *-------------------------------------------------------------------------
33/* "Methods" required for an expanded object */
36 void *result,
Size allocated_size);
44/* Other local functions */
50 Datum newValue,
bool isnull);
56 * Build an expanded record of the specified composite type
58 * type_id can be RECORDOID, but only if a positive typmod is given.
60 * The expanded record is initially "empty", having a state logically
61 * equivalent to a NULL composite value (not ROW(NULL, NULL, ...)).
62 * Note that this might not be a valid state for a domain type;
63 * if the caller needs to check that, call
64 * expanded_record_set_tuple(erh, NULL, false, false).
66 * The expanded object will be a child of parentcontext.
79 if (type_id != RECORDOID)
82 * Consult the typcache to see if it's a domain over composite, and in
83 * any case to get the tupdesc and tupdesc identifier.
90 if (typentry->
typtype == TYPTYPE_DOMAIN)
98 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
99 errmsg(
"type %s is not composite",
107 * For RECORD types, get the tupdesc and identifier from typcache.
114 * Allocate private context for expanded object. We use a regular-size
115 * context, not a small one, to improve the odds that we can fit a tupdesc
116 * into it without needing an extra malloc block. (This code path doesn't
117 * ever need to copy a tupdesc into the expanded record, but let's be
118 * consistent with the other ways of making an expanded record.)
125 * Since we already know the number of fields in the tupdesc, we can
126 * allocate the dvalues/dnulls arrays along with the record header. This
127 * is useless if we never need those arrays, but it costs almost nothing,
128 * and it will save a palloc cycle if we do need them.
132 + tupdesc->
natts * (
sizeof(
Datum) +
sizeof(
bool)));
134 /* Ensure all header fields are initialized to 0/null */
140 /* Set up dvalues/dnulls, with no valid contents as yet */
146 /* Fill in composite-type identification info */
155 * If what we got from the typcache is a refcounted tupdesc, we need to
156 * acquire our own refcount on it. We manage the refcount with a memory
157 * context callback rather than assuming that the CurrentResourceOwner is
158 * longer-lived than this expanded object.
162 /* Register callback to release the refcount */
168 /* And save the pointer */
172 /* If we called lookup_rowtype_tupdesc, release the pin it took */
173 if (type_id == RECORDOID)
179 * If it's not refcounted, just assume it will outlive the expanded
180 * object. (This can happen for shared record types, for instance.)
186 * We don't set ER_FLAG_DVALUES_VALID or ER_FLAG_FVALUE_VALID, so the
187 * record remains logically empty.
194 * Build an expanded record of the rowtype defined by the tupdesc
196 * The tupdesc is copied if necessary (i.e., if we can't just bump its
197 * reference count instead).
199 * The expanded record is initially "empty", having a state logically
200 * equivalent to a NULL composite value (not ROW(NULL, NULL, ...)).
202 * The expanded object will be a child of parentcontext.
217 * If it's a named composite type (not RECORD), we prefer to reference
218 * the typcache's copy of the tupdesc, which is guaranteed to be
219 * refcounted (the given tupdesc might not be). In any case, we need
220 * to consult the typcache to get the correct tupdesc identifier.
222 * Note that tdtypeid couldn't be a domain type, so we need not
223 * consider that case here.
230 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
231 errmsg(
"type %s is not composite",
239 * For RECORD types, get the appropriate unique identifier (possibly
247 * Allocate private context for expanded object. We use a regular-size
248 * context, not a small one, to improve the odds that we can fit a tupdesc
249 * into it without needing an extra malloc block.
256 * Since we already know the number of fields in the tupdesc, we can
257 * allocate the dvalues/dnulls arrays along with the record header. This
258 * is useless if we never need those arrays, but it costs almost nothing,
259 * and it will save a palloc cycle if we do need them.
263 + tupdesc->
natts * (
sizeof(
Datum) +
sizeof(
bool)));
265 /* Ensure all header fields are initialized to 0/null */
271 /* Set up dvalues/dnulls, with no valid contents as yet */
277 /* Fill in composite-type identification info */
283 * Copy tupdesc if needed, but we prefer to bump its refcount if possible.
284 * We manage the refcount with a memory context callback rather than
285 * assuming that the CurrentResourceOwner is longer-lived than this
290 /* Register callback to release the refcount */
296 /* And save the pointer */
310 * We don't set ER_FLAG_DVALUES_VALID or ER_FLAG_FVALUE_VALID, so the
311 * record remains logically empty.
318 * Build an expanded record of the same rowtype as the given expanded record
320 * This is faster than either of the above routines because we can bypass
321 * typcache lookup(s).
323 * The expanded record is initially "empty" --- we do not copy whatever
324 * tuple might be in the source expanded record.
326 * The expanded object will be a child of parentcontext.
339 * Allocate private context for expanded object. We use a regular-size
340 * context, not a small one, to improve the odds that we can fit a tupdesc
341 * into it without needing an extra malloc block.
348 * Since we already know the number of fields in the tupdesc, we can
349 * allocate the dvalues/dnulls arrays along with the record header. This
350 * is useless if we never need those arrays, but it costs almost nothing,
351 * and it will save a palloc cycle if we do need them.
355 + tupdesc->
natts * (
sizeof(
Datum) +
sizeof(
bool)));
357 /* Ensure all header fields are initialized to 0/null */
363 /* Set up dvalues/dnulls, with no valid contents as yet */
369 /* Fill in composite-type identification info */
375 /* The only flag bit that transfers over is IS_DOMAIN */
379 * Copy tupdesc if needed, but we prefer to bump its refcount if possible.
380 * We manage the refcount with a memory context callback rather than
381 * assuming that the CurrentResourceOwner is longer-lived than this
386 /* Register callback to release the refcount */
392 /* And save the pointer */
398 /* We need to make our own copy of the tupdesc */
407 * Assume the tupdesc will outlive this expanded object, just like
408 * we're assuming it will outlive the source object.
414 * We don't set ER_FLAG_DVALUES_VALID or ER_FLAG_FVALUE_VALID, so the
415 * record remains logically empty.
422 * Insert given tuple as the value of the expanded record
424 * It is caller's responsibility that the tuple matches the record's
425 * previously-assigned rowtype. (However domain constraints, if any,
426 * will be checked here.)
428 * The tuple is physically copied into the expanded record's local storage
429 * if "copy" is true, otherwise it's caller's responsibility that the tuple
430 * will live as long as the expanded record does.
432 * Out-of-line field values in the tuple are automatically inlined if
433 * "expand_external" is true, otherwise not. (The combination copy = false,
434 * expand_external = true is not sensible and not supported.)
436 * Alternatively, tuple can be NULL, in which case we just set the expanded
437 * record to be empty.
443 bool expand_external)
453 /* Shouldn't ever be trying to assign new data to a dummy header */
457 * Before performing the assignment, see if result will satisfy domain.
463 * If we need to get rid of out-of-line field values, do so, using the
464 * short-term context to avoid leaking whatever cruft the toast fetch
467 if (expand_external && tuple)
469 /* Assert caller didn't ask for unsupported case */
478 expand_external =
false;
/* need not clean up below */
482 * Initialize new flags, keeping only non-data status bits.
484 oldflags = erh->
flags;
488 * Copy tuple into local storage if needed. We must be sure this succeeds
489 * before we start to modify the expanded record's state.
498 /* We can now flush anything that detoasting might have leaked. */
505 /* Make copies of fields we're about to overwrite */
511 * It's now safe to update the expanded record's state.
515 /* Save flat representation */
521 /* Remember if we have any out-of-line field values */
531 erh->
flags = newflags;
533 /* Reset flat-size info; we don't bother to make it valid now */
537 * Now, release any storage belonging to old field values. It's safe to
538 * do this because ER_FLAG_DVALUES_VALID is no longer set in erh->flags;
539 * even if we fail partway through, the record is valid, and at worst
540 * we've failed to reclaim some space.
554 if (oldValue < oldfstartptr || oldValue >= oldfendptr)
560 /* Likewise free the old tuple, if it was locally allocated */
564 /* We won't make a new deconstructed representation until/unless needed */
568 * make_expanded_record_from_datum: build expanded record from composite Datum
570 * This combines the functions of make_expanded_record_from_typeid and
571 * expanded_record_set_tuple. However, we do not force a lookup of the
572 * tupdesc immediately, reasoning that it might never be needed.
574 * The expanded object will be a child of parentcontext.
576 * Note: a composite datum cannot self-identify as being of a domain type,
577 * so we need not consider domain cases here.
590 * Allocate private context for expanded object. We use a regular-size
591 * context, not a small one, to improve the odds that we can fit a tupdesc
592 * into it without needing an extra malloc block.
598 /* Set up expanded record header, initializing fields to 0/null */
606 * Detoast and copy source record into private context, as a HeapTuple.
607 * (If we actually have to detoast the source, we'll leak some memory in
608 * the caller's context, but it doesn't seem worth worrying about.)
622 /* Fill in composite-type identification info */
626 /* remember we have a flat representation */
632 /* Shouldn't need to set ER_FLAG_HAVE_EXTERNAL */
636 * We won't look up the tupdesc till we have to, nor make a deconstructed
637 * representation. We don't have enough info to fill flat_size and
641 /* return a R/W pointer to the expanded record */
646 * get_flat_size method for expanded records
648 * Note: call this in a reasonably short-lived memory context, in case of
649 * memory leaks from activities such as detoasting.
665 * The flat representation has to be a valid composite datum. Make sure
666 * that we have a registered, not anonymous, RECORD type.
677 * If we have a valid flattened value without out-of-line fields, we can
684 /* If we have a cached size value, believe that */
688 /* If we haven't yet deconstructed the tuple, do that */
692 /* Tuple descriptor must be valid by now */
696 * Composite datums mustn't contain any out-of-line values.
709 * expanded_record_set_field_internal can do the actual work
710 * of detoasting. It needn't recheck domain constraints.
720 * We have now removed all external field values, so we can clear the
721 * flag about them. This won't cause ER_flatten_into() to mistakenly
722 * take the fast path, since expanded_record_set_field() will have
723 * cleared ER_FLAG_FVALUE_VALID.
725 erh->
flags &= ~ER_FLAG_HAVE_EXTERNAL;
728 /* Test if we currently have any null values */
739 /* Determine total space needed */
751 /* Cache for next time */
761 * flatten_into method for expanded records
765 void *result,
Size allocated_size)
773 /* Easy if we have a valid flattened value without out-of-line fields */
779 /* The original flattened value might not have datum header fields */
786 /* Else allocation should match previous get_flat_size result */
789 /* We'll need the tuple descriptor */
792 /* We must ensure that any pad space is zero-filled */
793 memset(tuphdr, 0, allocated_size);
795 /* Set up header fields of composite Datum */
799 /* We also make sure that t_ctid is invalid unless explicitly set */
805 /* And fill the data area from dvalues/dnulls */
809 (
char *) tuphdr + erh->
hoff,
816 * Look up the tupdesc for the expanded record's actual type
818 * Note: code internal to this module is allowed to just fetch
819 * erh->er_tupdesc if ER_FLAG_DVALUES_VALID is set; otherwise it should call
820 * expanded_record_get_tupdesc. This function is the out-of-line portion
821 * of expanded_record_get_tupdesc.
828 /* Easy if we already have it (but caller should have checked already) */
832 /* Lookup the composite type's tupdesc using the typcache */
836 * If it's a refcounted tupdesc rather than a statically allocated one, we
837 * want to manage the refcount with a memory context callback rather than
838 * assuming that the CurrentResourceOwner is longer-lived than this
843 /* Register callback if we didn't already */
852 /* Remember our own pointer */
856 /* Release the pin lookup_rowtype_tupdesc acquired */
861 /* Just remember the pointer */
865 /* In either case, fetch the process-global ID for this tupdesc */
873 * Get a HeapTuple representing the current value of the expanded record
875 * If valid, the originally stored tuple is returned, so caller must not
876 * scribble on it. Otherwise, we return a HeapTuple created in the current
877 * memory context. In either case, no attempt has been made to inline
878 * out-of-line toasted values, so the tuple isn't usable as a composite
881 * Returns NULL if expanded record is empty.
886 /* Easy case if we still have original tuple */
890 /* Else just build a tuple from datums */
894 /* Expanded record is empty */
899 * Memory context reset callback for cleaning up external resources
907 /* Release our privately-managed tupdesc refcount, if any */
920 * DatumGetExpandedRecord: get a writable expanded record from an input argument
922 * Caution: if the input is a read/write pointer, this returns the input
923 * argument; so callers must be sure that their changes are "safe", that is
924 * they cannot leave the record in a corrupt state.
929 /* If it's a writable expanded record already, just return it */
938 /* Else expand the hard way */
944 * Create the Datum/isnull representation of an expanded record object
945 * if we didn't do so already. After calling this, it's OK to read the
946 * dvalues/dnulls arrays directly, rather than going through get_field.
948 * Note that if the object is currently empty ("null"), this will change
949 * it to represent a row of nulls.
960 return;
/* already valid, nothing to do */
962 /* We'll need the tuple descriptor */
966 * Allocate arrays in private context, if we don't have them already. We
967 * don't expect to see a change in nfields here, so while we cope if it
968 * happens, we don't bother avoiding a leak of the old arrays (which might
969 * not be separately palloc'd, anyway).
971 nfields = tupdesc->
natts;
977 * To save a palloc cycle, we allocate both the Datum and isnull
978 * arrays in one palloc chunk.
981 nfields * (
sizeof(
Datum) +
sizeof(
bool)));
982 dvalues = (
Datum *) chunk;
983 dnulls = (
bool *) (chunk + nfields *
sizeof(
Datum));
996 /* Deconstruct tuple */
1001 /* If record was empty, instantiate it as a row of nulls */
1002 memset(dvalues, 0, nfields *
sizeof(
Datum));
1003 memset(dnulls,
true, nfields *
sizeof(
bool));
1006 /* Mark the dvalues as valid */
1011 * Look up a record field by name
1013 * If there is a field named "fieldname", fill in the contents of finfo
1014 * and return "true". Else return "false" without changing *finfo.
1027 /* First, check user-defined attributes */
1028 for (fno = 0; fno < tupdesc->
natts; fno++)
1031 if (
namestrcmp(&attr->attname, fieldname) == 0 &&
1032 !attr->attisdropped)
1034 finfo->
fnumber = attr->attnum;
1035 finfo->
ftypeid = attr->atttypid;
1036 finfo->
ftypmod = attr->atttypmod;
1042 /* How about system attributes? */
1044 if (sysattr != NULL)
1046 finfo->
fnumber = sysattr->attnum;
1047 finfo->
ftypeid = sysattr->atttypid;
1048 finfo->
ftypmod = sysattr->atttypmod;
1057 * Fetch value of record field
1059 * expanded_record_get_field is the frontend for this; it handles the
1060 * easy inline-able cases.
1068 /* Empty record has null fields */
1074 /* Make sure we have deconstructed form */
1076 /* Out-of-range field number reads as null */
1082 *isnull = erh->
dnulls[fnumber - 1];
1083 return erh->
dvalues[fnumber - 1];
1087 /* System columns read as null if we haven't got flat tuple */
1093 /* heap_getsysattr doesn't actually use tupdesc, so just pass null */
1099 * Set value of record field
1101 * If the expanded record is of domain type, the assignment will be rejected
1102 * (without changing the record's state) if the domain's constraints would
1105 * If expand_external is true and newValue is an out-of-line value, we'll
1106 * forcibly detoast it so that the record does not depend on external storage.
1108 * Internal callers can pass check_constraints = false to skip application
1109 * of domain constraints. External callers should never do that.
1113 Datum newValue,
bool isnull,
1114 bool expand_external,
1115 bool check_constraints)
1124 * Shouldn't ever be trying to assign new data to a dummy header, except
1125 * in the case of an internal call for field inlining.
1129 /* Before performing the assignment, see if result will satisfy domain */
1133 /* If we haven't yet deconstructed the tuple, do that */
1137 /* Tuple descriptor must be valid by now */
1141 /* Caller error if fnumber is system column or nonexistent column */
1143 elog(
ERROR,
"cannot assign to field %d of expanded record", fnumber);
1146 * Copy new field value into record's context, and deal with detoasting,
1154 /* If requested, detoast any external value */
1155 if (expand_external)
1157 if (attr->
attlen == -1 &&
1160 /* Detoasting should be done in short-lived context. */
1166 expand_external =
false;
/* need not clean up below */
1169 /* Copy value into record's context */
1174 /* We can now flush anything that detoasting might have leaked */
1175 if (expand_external)
1178 /* Remember that we have field(s) that may need to be pfree'd */
1182 * While we're here, note whether it's an external toasted value,
1183 * because that could mean we need to inline it later. (Think not to
1184 * merge this into the previous expand_external logic: datumCopy could
1185 * by itself have made the value non-external.)
1187 if (attr->
attlen == -1 &&
1193 * We're ready to make irreversible changes.
1198 /* Flattened value will no longer represent record accurately */
1199 erh->
flags &= ~ER_FLAG_FVALUE_VALID;
1200 /* And we don't know the flattened size either */
1203 /* Grab old field value for pfree'ing, if needed. */
1204 if (!attr->
attbyval && !dnulls[fnumber - 1])
1209 /* And finally we can insert the new field. */
1210 dvalues[fnumber - 1] = newValue;
1211 dnulls[fnumber - 1] = isnull;
1214 * Free old field if needed; this keeps repeated field replacements from
1215 * bloating the record's storage. If the pfree somehow fails, it won't
1216 * corrupt the record.
1218 * If we're updating a dummy header, we can't risk pfree'ing the old
1219 * value, because most likely the expanded record's main header still has
1220 * a pointer to it. This won't result in any sustained memory leak, since
1221 * whatever we just allocated here is in the short-lived domain check
1226 /* Don't try to pfree a part of the original flat record */
1227 if (oldValue < erh->fstartptr || oldValue >= erh->
fendptr)
1233 * Set all record field(s)
1235 * Caller must ensure that the provided datums are of the right types
1236 * to match the record's previously assigned rowtype.
1238 * If expand_external is true, we'll forcibly detoast out-of-line field values
1239 * so that the record does not depend on external storage.
1241 * Unlike repeated application of expanded_record_set_field(), this does not
1242 * guarantee to leave the expanded record in a non-corrupt state in event
1243 * of an error. Typically it would only be used for initializing a new
1244 * expanded record. Also, because we expect this to be applied at most once
1245 * in the lifespan of an expanded record, we do not worry about any cruft
1246 * that detoasting might leak.
1250 const Datum *newValues,
const bool *isnulls,
1251 bool expand_external)
1259 /* Shouldn't ever be trying to assign new data to a dummy header */
1262 /* If we haven't yet deconstructed the tuple, do that */
1266 /* Tuple descriptor must be valid by now */
1270 /* Flattened value will no longer represent record accurately */
1271 erh->
flags &= ~ER_FLAG_FVALUE_VALID;
1272 /* And we don't know the flattened size either */
1280 for (fnumber = 0; fnumber < erh->
nfields; fnumber++)
1286 /* Ignore dropped columns */
1290 newValue = newValues[fnumber];
1291 isnull = isnulls[fnumber];
1296 * Copy new field value into record's context, and deal with
1297 * detoasting, if needed.
1301 /* Is it an external toasted value? */
1302 if (attr->
attlen == -1 &&
1305 if (expand_external)
1307 /* Detoast as requested while copying the value */
1312 /* Just copy the value */
1313 newValue =
datumCopy(newValue,
false, -1);
1314 /* If it's still external, remember that */
1321 /* Not an external value, just copy it */
1325 /* Remember that we have field(s) that need to be pfree'd */
1330 * Free old field value, if any (not likely, since really we ought
1331 * to be inserting into an empty record).
1338 /* Don't try to pfree a part of the original flat record */
1339 if (oldValue < erh->fstartptr || oldValue >= erh->
fendptr)
1344 /* And finally we can insert the new field. */
1345 dvalues[fnumber] = newValue;
1346 dnulls[fnumber] = isnull;
1350 * Because we don't guarantee atomicity of set_fields(), we can just leave
1351 * checking of domain constraints to occur as the final step; if it throws
1352 * an error, too bad.
1356 /* We run domain_check in a short-lived context to limit cruft */
1369 * Construct (or reset) working memory context for short-term operations.
1371 * This context is used for domain check evaluation and for detoasting.
1373 * If we don't have a short-lived memory context, make one; if we have one,
1374 * reset it to get rid of any leftover cruft. (It is a tad annoying to need a
1375 * whole context for this, since it will often go unused --- but it's hard to
1376 * avoid memory leaks otherwise. We can make the context small, at least.)
1384 "expanded record short-term context",
1392 * Construct "dummy header" for checking domain constraints.
1394 * Since we don't want to modify the state of the expanded record until
1395 * we've validated the constraints, our approach is to set up a dummy
1396 * record header containing the new field value(s) and then pass that to
1397 * domain_check. We retain the dummy header as part of the expanded
1398 * record's state to save palloc cycles, but reinitialize (most of)
1399 * its contents on each use.
1407 /* Ensure we have a short-lived context */
1411 * Allocate dummy header on first time through, or in the unlikely event
1412 * that the number of fields changes (in which case we just leak the old
1413 * one). Include space for its field values in the request.
1423 + tupdesc->
natts * (
sizeof(
Datum) +
sizeof(bool)));
1425 /* Ensure all header fields are initialized to 0/null */
1429 * We set up the dummy header with an indication that its memory
1430 * context is the short-lived context. This is so that, if any
1431 * detoasting of out-of-line values happens due to an attempt to
1432 * extract a composite datum from the dummy header, the detoasted
1433 * stuff will end up in the short-lived context and not cause a leak.
1434 * This is cheating a bit on the expanded-object protocol; but since
1435 * we never pass a R/W pointer to the dummy object to any other code,
1436 * nothing else is authorized to delete or transfer ownership of the
1437 * object's context, so it should be safe enough.
1442 /* Set up dvalues/dnulls, with no valid contents as yet */
1449 * The fields we just set are assumed to remain constant through
1450 * multiple uses of the dummy header to check domain constraints. All
1451 * other dummy header fields should be explicitly reset below, to
1452 * ensure there's not accidental effects of one check on the next one.
1459 * If anything inquires about the dummy header's declared type, it should
1460 * report the composite base type, not the domain type (since the VALUE in
1461 * a domain check constraint is of the base type not the domain). Hence
1462 * we do not transfer over the IS_DOMAIN flag, nor indeed any of the main
1463 * header's flags, since the dummy header is empty of data at this point.
1464 * But don't forget to mark header as dummy.
1468 /* Copy composite-type identification info */
1472 /* Dummy header does not need its own tupdesc refcount */
1477 * It's tempting to copy over whatever we know about the flat size, but
1478 * there's no point since we're surely about to modify the dummy record's
1479 * field(s). Instead just clear anything left over from a previous usage
1484 /* Copy over fvalue if we have it, so that system columns are available */
1491 * Precheck domain constraints for a set_field operation
1495 Datum newValue,
bool isnull)
1500 /* Construct dummy header to contain proposed new field set */
1505 * If record isn't empty, just deconstruct it (if needed) and copy over
1506 * the existing field values. If it is empty, just fill fields with nulls
1507 * manually --- don't call deconstruct_expanded_record prematurely.
1515 dummy_erh->
nfields *
sizeof(
bool));
1516 /* There might be some external values in there... */
1522 memset(dummy_erh->
dnulls,
true, dummy_erh->
nfields *
sizeof(
bool));
1525 /* Either way, we now have valid dvalues */
1528 /* Caller error if fnumber is system column or nonexistent column */
1530 elog(
ERROR,
"cannot assign to field %d of expanded record", fnumber);
1532 /* Insert proposed new value into dummy field array */
1533 dummy_erh->
dvalues[fnumber - 1] = newValue;
1534 dummy_erh->
dnulls[fnumber - 1] = isnull;
1537 * The proposed new value might be external, in which case we'd better set
1538 * the flag for that in dummy_erh. (This matters in case something in the
1539 * domain check expressions tries to extract a flat value from the dummy
1552 * We call domain_check in the short-lived context, so that any cruft
1553 * leaked by expression evaluation can be reclaimed.
1558 * And now we can apply the check. Note we use main header's domain cache
1559 * space, so that caching carries across repeated uses.
1568 /* We might as well clean up cruft immediately. */
1573 * Precheck domain constraints for a set_tuple operation
1581 /* If we're being told to set record to empty, just see if NULL is OK */
1584 /* We run domain_check in a short-lived context to limit cruft */
1594 /* We might as well clean up cruft immediately. */
1600 /* Construct dummy header to contain replacement tuple */
1604 /* Insert tuple, but don't bother to deconstruct its fields for now */
1605 dummy_erh->
fvalue = tuple;
1610 /* Remember if we have any out-of-line field values */
1615 * We call domain_check in the short-lived context, so that any cruft
1616 * leaked by expression evaluation can be reclaimed.
1621 * And now we can apply the check. Note we use main header's domain cache
1622 * space, so that caching carries across repeated uses.
1631 /* We might as well clean up cruft immediately. */
Datum datumCopy(Datum value, bool typByVal, int typLen)
struct varlena * detoast_external_attr(struct varlena *attr)
void domain_check(Datum value, bool isnull, Oid domainType, void **extra, MemoryContext mcxt)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
ExpandedObjectHeader * DatumGetEOHP(Datum d)
void EOH_init_header(ExpandedObjectHeader *eohptr, const ExpandedObjectMethods *methods, MemoryContext obj_context)
static Datum EOHPGetRWDatum(const struct ExpandedObjectHeader *eohptr)
static const ExpandedObjectMethods ER_methods
static pg_noinline void check_domain_for_new_tuple(ExpandedRecordHeader *erh, HeapTuple tuple)
static void build_dummy_expanded_header(ExpandedRecordHeader *main_erh)
static void ER_flatten_into(ExpandedObjectHeader *eohptr, void *result, Size allocated_size)
ExpandedRecordHeader * make_expanded_record_from_exprecord(ExpandedRecordHeader *olderh, MemoryContext parentcontext)
ExpandedRecordHeader * make_expanded_record_from_tupdesc(TupleDesc tupdesc, MemoryContext parentcontext)
void expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, Datum newValue, bool isnull, bool expand_external, bool check_constraints)
bool expanded_record_lookup_field(ExpandedRecordHeader *erh, const char *fieldname, ExpandedRecordFieldInfo *finfo)
static MemoryContext get_short_term_cxt(ExpandedRecordHeader *erh)
Datum expanded_record_fetch_field(ExpandedRecordHeader *erh, int fnumber, bool *isnull)
static Size ER_get_flat_size(ExpandedObjectHeader *eohptr)
static pg_noinline void check_domain_for_new_field(ExpandedRecordHeader *erh, int fnumber, Datum newValue, bool isnull)
static void ER_mc_callback(void *arg)
Datum make_expanded_record_from_datum(Datum recorddatum, MemoryContext parentcontext)
ExpandedRecordHeader * make_expanded_record_from_typeid(Oid type_id, int32 typmod, MemoryContext parentcontext)
ExpandedRecordHeader * DatumGetExpandedRecord(Datum d)
TupleDesc expanded_record_fetch_tupdesc(ExpandedRecordHeader *erh)
void expanded_record_set_tuple(ExpandedRecordHeader *erh, HeapTuple tuple, bool copy, bool expand_external)
HeapTuple expanded_record_get_tuple(ExpandedRecordHeader *erh)
void deconstruct_expanded_record(ExpandedRecordHeader *erh)
void expanded_record_set_fields(ExpandedRecordHeader *erh, const Datum *newValues, const bool *isnulls, bool expand_external)
#define ER_FLAG_HAVE_EXTERNAL
#define ExpandedRecordIsEmpty(erh)
static Datum ExpandedRecordGetRODatum(const ExpandedRecordHeader *erh)
#define ER_FLAG_DVALUES_VALID
#define ER_FLAG_TUPDESC_ALLOCED
#define ER_FLAG_DVALUES_ALLOCED
#define ER_FLAG_IS_DOMAIN
static TupleDesc expanded_record_get_tupdesc(ExpandedRecordHeader *erh)
#define ER_FLAGS_NON_DATA
#define ER_FLAG_FVALUE_VALID
#define ER_FLAG_FVALUE_ALLOCED
#define DatumGetHeapTupleHeader(X)
Assert(PointerIsAligned(start, uint64))
const FormData_pg_attribute * SystemAttributeByName(const char *attname)
HeapTuple toast_flatten_tuple(HeapTuple tup, TupleDesc tupleDesc)
Size heap_compute_data_size(TupleDesc tupleDesc, const Datum *values, const bool *isnull)
HeapTuple heap_copytuple(HeapTuple tuple)
Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
void heap_fill_tuple(TupleDesc tupleDesc, const Datum *values, const bool *isnull, char *data, Size data_size, uint16 *infomask, bits8 *bit)
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
void heap_freetuple(HeapTuple htup)
HeapTupleHeaderData * HeapTupleHeader
static void HeapTupleHeaderSetTypMod(HeapTupleHeaderData *tup, int32 typmod)
static int BITMAPLEN(int NATTS)
static int32 HeapTupleHeaderGetTypMod(const HeapTupleHeaderData *tup)
static bool HeapTupleHasExternal(const HeapTupleData *tuple)
static void HeapTupleHeaderSetTypeId(HeapTupleHeaderData *tup, Oid datum_typeid)
static void HeapTupleHeaderSetDatumLength(HeapTupleHeaderData *tup, uint32 len)
static uint32 HeapTupleHeaderGetDatumLength(const HeapTupleHeaderData *tup)
#define HeapTupleHeaderSetNatts(tup, natts)
#define HeapTupleHeaderHasExternal(tup)
static Oid HeapTupleHeaderGetTypeId(const HeapTupleHeaderData *tup)
if(TABLE==NULL||TABLE_index==NULL)
static void ItemPointerSetInvalid(ItemPointerData *pointer)
void * MemoryContextAlloc(MemoryContext context, Size size)
void MemoryContextReset(MemoryContext context)
void * MemoryContextAllocZero(MemoryContext context, Size size)
void MemoryContextRegisterResetCallback(MemoryContext context, MemoryContextCallback *cb)
void pfree(void *pointer)
MemoryContext CurrentMemoryContext
#define AllocSetContextCreate
#define ALLOCSET_DEFAULT_SIZES
#define ALLOCSET_SMALL_SIZES
int namestrcmp(Name name, const char *str)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
FormData_pg_attribute * Form_pg_attribute
static Datum PointerGetDatum(const void *X)
static Pointer DatumGetPointer(Datum X)
MemoryContext eoh_context
MemoryContext er_short_term_cxt
MemoryContextCallback er_mcb
struct ExpandedRecordHeader * er_dummy_header
MemoryContextCallbackFunction func
uint64 tupDesc_identifier
void FreeTupleDesc(TupleDesc tupdesc)
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
#define ReleaseTupleDesc(tupdesc)
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
uint64 assign_record_type_identifier(Oid type_id, int32 typmod)
void assign_record_type_typmod(TupleDesc tupDesc)
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
#define TYPECACHE_TUPDESC
#define TYPECACHE_DOMAIN_BASE_INFO
static bool VARATT_IS_EXTERNAL_EXPANDED_RW(const void *PTR)
static bool VARATT_IS_EXTERNAL(const void *PTR)