PostgreSQL Source Code  git master
expandedrecord.c File Reference
#include "postgres.h"
#include "access/detoast.h"
#include "access/heaptoast.h"
#include "access/htup_details.h"
#include "catalog/heap.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/expandedrecord.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
Include dependency graph for expandedrecord.c:

Go to the source code of this file.

Functions

static Size ER_get_flat_size (ExpandedObjectHeader *eohptr)
 
static void ER_flatten_into (ExpandedObjectHeader *eohptr, void *result, Size allocated_size)
 
static void ER_mc_callback (void *arg)
 
static MemoryContext get_short_term_cxt (ExpandedRecordHeader *erh)
 
static void build_dummy_expanded_header (ExpandedRecordHeader *main_erh)
 
static pg_noinline void check_domain_for_new_field (ExpandedRecordHeader *erh, int fnumber, Datum newValue, bool isnull)
 
static pg_noinline void check_domain_for_new_tuple (ExpandedRecordHeader *erh, HeapTuple tuple)
 
ExpandedRecordHeadermake_expanded_record_from_typeid (Oid type_id, int32 typmod, MemoryContext parentcontext)
 
ExpandedRecordHeadermake_expanded_record_from_tupdesc (TupleDesc tupdesc, MemoryContext parentcontext)
 
ExpandedRecordHeadermake_expanded_record_from_exprecord (ExpandedRecordHeader *olderh, MemoryContext parentcontext)
 
void expanded_record_set_tuple (ExpandedRecordHeader *erh, HeapTuple tuple, bool copy, bool expand_external)
 
Datum make_expanded_record_from_datum (Datum recorddatum, MemoryContext parentcontext)
 
TupleDesc expanded_record_fetch_tupdesc (ExpandedRecordHeader *erh)
 
HeapTuple expanded_record_get_tuple (ExpandedRecordHeader *erh)
 
ExpandedRecordHeaderDatumGetExpandedRecord (Datum d)
 
void deconstruct_expanded_record (ExpandedRecordHeader *erh)
 
bool expanded_record_lookup_field (ExpandedRecordHeader *erh, const char *fieldname, ExpandedRecordFieldInfo *finfo)
 
Datum expanded_record_fetch_field (ExpandedRecordHeader *erh, int fnumber, bool *isnull)
 
void expanded_record_set_field_internal (ExpandedRecordHeader *erh, int fnumber, Datum newValue, bool isnull, bool expand_external, bool check_constraints)
 
void expanded_record_set_fields (ExpandedRecordHeader *erh, const Datum *newValues, const bool *isnulls, bool expand_external)
 

Variables

static const ExpandedObjectMethods ER_methods
 

Function Documentation

◆ build_dummy_expanded_header()

static void build_dummy_expanded_header ( ExpandedRecordHeader main_erh)
static

Definition at line 1402 of file expandedrecord.c.

1403 {
1404  ExpandedRecordHeader *erh;
1405  TupleDesc tupdesc = expanded_record_get_tupdesc(main_erh);
1406 
1407  /* Ensure we have a short-lived context */
1408  (void) get_short_term_cxt(main_erh);
1409 
1410  /*
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.
1414  */
1415  erh = main_erh->er_dummy_header;
1416  if (erh == NULL || erh->nfields != tupdesc->natts)
1417  {
1418  char *chunk;
1419 
1420  erh = (ExpandedRecordHeader *)
1423  + tupdesc->natts * (sizeof(Datum) + sizeof(bool)));
1424 
1425  /* Ensure all header fields are initialized to 0/null */
1426  memset(erh, 0, sizeof(ExpandedRecordHeader));
1427 
1428  /*
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.
1438  */
1439  EOH_init_header(&erh->hdr, &ER_methods, main_erh->er_short_term_cxt);
1440  erh->er_magic = ER_MAGIC;
1441 
1442  /* Set up dvalues/dnulls, with no valid contents as yet */
1443  chunk = (char *) erh + MAXALIGN(sizeof(ExpandedRecordHeader));
1444  erh->dvalues = (Datum *) chunk;
1445  erh->dnulls = (bool *) (chunk + tupdesc->natts * sizeof(Datum));
1446  erh->nfields = tupdesc->natts;
1447 
1448  /*
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.
1453  */
1454 
1455  main_erh->er_dummy_header = erh;
1456  }
1457 
1458  /*
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.
1465  */
1466  erh->flags = ER_FLAG_IS_DUMMY;
1467 
1468  /* Copy composite-type identification info */
1469  erh->er_decltypeid = erh->er_typeid = main_erh->er_typeid;
1470  erh->er_typmod = main_erh->er_typmod;
1471 
1472  /* Dummy header does not need its own tupdesc refcount */
1473  erh->er_tupdesc = tupdesc;
1474  erh->er_tupdesc_id = main_erh->er_tupdesc_id;
1475 
1476  /*
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
1480  * cycle.
1481  */
1482  erh->flat_size = 0;
1483 
1484  /* Copy over fvalue if we have it, so that system columns are available */
1485  erh->fvalue = main_erh->fvalue;
1486  erh->fstartptr = main_erh->fstartptr;
1487  erh->fendptr = main_erh->fendptr;
1488 }
#define MAXALIGN(LEN)
Definition: c.h:765
void EOH_init_header(ExpandedObjectHeader *eohptr, const ExpandedObjectMethods *methods, MemoryContext obj_context)
Definition: expandeddatum.c:48
static const ExpandedObjectMethods ER_methods
static MemoryContext get_short_term_cxt(ExpandedRecordHeader *erh)
#define ER_FLAG_IS_DUMMY
#define ER_MAGIC
static TupleDesc expanded_record_get_tupdesc(ExpandedRecordHeader *erh)
uint64 chunk
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1181
uintptr_t Datum
Definition: postgres.h:64
MemoryContext eoh_context
MemoryContext er_short_term_cxt
ExpandedObjectHeader hdr
struct ExpandedRecordHeader * er_dummy_header

References chunk, ExpandedRecordHeader::dnulls, ExpandedRecordHeader::dvalues, ExpandedObjectHeader::eoh_context, EOH_init_header(), ExpandedRecordHeader::er_decltypeid, ExpandedRecordHeader::er_dummy_header, ER_FLAG_IS_DUMMY, ER_MAGIC, ExpandedRecordHeader::er_magic, ER_methods, ExpandedRecordHeader::er_short_term_cxt, ExpandedRecordHeader::er_tupdesc, ExpandedRecordHeader::er_tupdesc_id, ExpandedRecordHeader::er_typeid, ExpandedRecordHeader::er_typmod, expanded_record_get_tupdesc(), ExpandedRecordHeader::fendptr, ExpandedRecordHeader::flags, ExpandedRecordHeader::flat_size, ExpandedRecordHeader::fstartptr, ExpandedRecordHeader::fvalue, get_short_term_cxt(), ExpandedRecordHeader::hdr, MAXALIGN, MemoryContextAlloc(), TupleDescData::natts, and ExpandedRecordHeader::nfields.

Referenced by check_domain_for_new_field(), and check_domain_for_new_tuple().

◆ check_domain_for_new_field()

static pg_noinline void check_domain_for_new_field ( ExpandedRecordHeader erh,
int  fnumber,
Datum  newValue,
bool  isnull 
)
static

Definition at line 1494 of file expandedrecord.c.

1496 {
1497  ExpandedRecordHeader *dummy_erh;
1498  MemoryContext oldcxt;
1499 
1500  /* Construct dummy header to contain proposed new field set */
1502  dummy_erh = erh->er_dummy_header;
1503 
1504  /*
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.
1508  */
1509  if (!ExpandedRecordIsEmpty(erh))
1510  {
1512  memcpy(dummy_erh->dvalues, erh->dvalues,
1513  dummy_erh->nfields * sizeof(Datum));
1514  memcpy(dummy_erh->dnulls, erh->dnulls,
1515  dummy_erh->nfields * sizeof(bool));
1516  /* There might be some external values in there... */
1517  dummy_erh->flags |= erh->flags & ER_FLAG_HAVE_EXTERNAL;
1518  }
1519  else
1520  {
1521  memset(dummy_erh->dvalues, 0, dummy_erh->nfields * sizeof(Datum));
1522  memset(dummy_erh->dnulls, true, dummy_erh->nfields * sizeof(bool));
1523  }
1524 
1525  /* Either way, we now have valid dvalues */
1526  dummy_erh->flags |= ER_FLAG_DVALUES_VALID;
1527 
1528  /* Caller error if fnumber is system column or nonexistent column */
1529  if (unlikely(fnumber <= 0 || fnumber > dummy_erh->nfields))
1530  elog(ERROR, "cannot assign to field %d of expanded record", fnumber);
1531 
1532  /* Insert proposed new value into dummy field array */
1533  dummy_erh->dvalues[fnumber - 1] = newValue;
1534  dummy_erh->dnulls[fnumber - 1] = isnull;
1535 
1536  /*
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
1540  * header.)
1541  */
1542  if (!isnull)
1543  {
1544  Form_pg_attribute attr = TupleDescAttr(erh->er_tupdesc, fnumber - 1);
1545 
1546  if (!attr->attbyval && attr->attlen == -1 &&
1548  dummy_erh->flags |= ER_FLAG_HAVE_EXTERNAL;
1549  }
1550 
1551  /*
1552  * We call domain_check in the short-lived context, so that any cruft
1553  * leaked by expression evaluation can be reclaimed.
1554  */
1556 
1557  /*
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.
1560  */
1561  domain_check(ExpandedRecordGetRODatum(dummy_erh), false,
1562  erh->er_decltypeid,
1563  &erh->er_domaininfo,
1564  erh->hdr.eoh_context);
1565 
1566  MemoryContextSwitchTo(oldcxt);
1567 
1568  /* We might as well clean up cruft immediately. */
1570 }
#define unlikely(x)
Definition: c.h:330
void domain_check(Datum value, bool isnull, Oid domainType, void **extra, MemoryContext mcxt)
Definition: domains.c:346
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
static void build_dummy_expanded_header(ExpandedRecordHeader *main_erh)
void deconstruct_expanded_record(ExpandedRecordHeader *erh)
#define ER_FLAG_HAVE_EXTERNAL
#define ExpandedRecordIsEmpty(erh)
static Datum ExpandedRecordGetRODatum(const ExpandedRecordHeader *erh)
#define ER_FLAG_DVALUES_VALID
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:383
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
MemoryContextSwitchTo(old_ctx)
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define VARATT_IS_EXTERNAL(PTR)
Definition: varatt.h:289

References build_dummy_expanded_header(), DatumGetPointer(), deconstruct_expanded_record(), ExpandedRecordHeader::dnulls, domain_check(), ExpandedRecordHeader::dvalues, elog, ExpandedObjectHeader::eoh_context, ExpandedRecordHeader::er_decltypeid, ExpandedRecordHeader::er_domaininfo, ExpandedRecordHeader::er_dummy_header, ER_FLAG_DVALUES_VALID, ER_FLAG_HAVE_EXTERNAL, ExpandedRecordHeader::er_short_term_cxt, ExpandedRecordHeader::er_tupdesc, ERROR, ExpandedRecordGetRODatum(), ExpandedRecordIsEmpty, ExpandedRecordHeader::flags, ExpandedRecordHeader::hdr, MemoryContextReset(), MemoryContextSwitchTo(), ExpandedRecordHeader::nfields, TupleDescAttr, unlikely, and VARATT_IS_EXTERNAL.

Referenced by expanded_record_set_field_internal().

◆ check_domain_for_new_tuple()

static pg_noinline void check_domain_for_new_tuple ( ExpandedRecordHeader erh,
HeapTuple  tuple 
)
static

Definition at line 1576 of file expandedrecord.c.

1577 {
1578  ExpandedRecordHeader *dummy_erh;
1579  MemoryContext oldcxt;
1580 
1581  /* If we're being told to set record to empty, just see if NULL is OK */
1582  if (tuple == NULL)
1583  {
1584  /* We run domain_check in a short-lived context to limit cruft */
1586 
1587  domain_check((Datum) 0, true,
1588  erh->er_decltypeid,
1589  &erh->er_domaininfo,
1590  erh->hdr.eoh_context);
1591 
1592  MemoryContextSwitchTo(oldcxt);
1593 
1594  /* We might as well clean up cruft immediately. */
1596 
1597  return;
1598  }
1599 
1600  /* Construct dummy header to contain replacement tuple */
1602  dummy_erh = erh->er_dummy_header;
1603 
1604  /* Insert tuple, but don't bother to deconstruct its fields for now */
1605  dummy_erh->fvalue = tuple;
1606  dummy_erh->fstartptr = (char *) tuple->t_data;
1607  dummy_erh->fendptr = ((char *) tuple->t_data) + tuple->t_len;
1608  dummy_erh->flags |= ER_FLAG_FVALUE_VALID;
1609 
1610  /* Remember if we have any out-of-line field values */
1611  if (HeapTupleHasExternal(tuple))
1612  dummy_erh->flags |= ER_FLAG_HAVE_EXTERNAL;
1613 
1614  /*
1615  * We call domain_check in the short-lived context, so that any cruft
1616  * leaked by expression evaluation can be reclaimed.
1617  */
1619 
1620  /*
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.
1623  */
1624  domain_check(ExpandedRecordGetRODatum(dummy_erh), false,
1625  erh->er_decltypeid,
1626  &erh->er_domaininfo,
1627  erh->hdr.eoh_context);
1628 
1629  MemoryContextSwitchTo(oldcxt);
1630 
1631  /* We might as well clean up cruft immediately. */
1633 }
#define ER_FLAG_FVALUE_VALID
#define HeapTupleHasExternal(tuple)
Definition: htup_details.h:671
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
uint32 t_len
Definition: htup.h:64
HeapTupleHeader t_data
Definition: htup.h:68

References build_dummy_expanded_header(), domain_check(), ExpandedObjectHeader::eoh_context, ExpandedRecordHeader::er_decltypeid, ExpandedRecordHeader::er_domaininfo, ExpandedRecordHeader::er_dummy_header, ER_FLAG_FVALUE_VALID, ER_FLAG_HAVE_EXTERNAL, ExpandedRecordHeader::er_short_term_cxt, ExpandedRecordGetRODatum(), ExpandedRecordHeader::fendptr, ExpandedRecordHeader::flags, ExpandedRecordHeader::fstartptr, ExpandedRecordHeader::fvalue, get_short_term_cxt(), ExpandedRecordHeader::hdr, HeapTupleHasExternal, if(), MemoryContextReset(), MemoryContextSwitchTo(), HeapTupleData::t_data, and HeapTupleData::t_len.

Referenced by expanded_record_set_tuple().

◆ DatumGetExpandedRecord()

ExpandedRecordHeader* DatumGetExpandedRecord ( Datum  d)

Definition at line 927 of file expandedrecord.c.

928 {
929  /* If it's a writable expanded record already, just return it */
931  {
933 
934  Assert(erh->er_magic == ER_MAGIC);
935  return erh;
936  }
937 
938  /* Else expand the hard way */
940  return (ExpandedRecordHeader *) DatumGetEOHP(d);
941 }
#define Assert(condition)
Definition: c.h:812
ExpandedObjectHeader * DatumGetEOHP(Datum d)
Definition: expandeddatum.c:29
Datum make_expanded_record_from_datum(Datum recorddatum, MemoryContext parentcontext)
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
#define VARATT_IS_EXTERNAL_EXPANDED_RW(PTR)
Definition: varatt.h:296

References Assert, CurrentMemoryContext, DatumGetEOHP(), DatumGetPointer(), ER_MAGIC, ExpandedRecordHeader::er_magic, make_expanded_record_from_datum(), and VARATT_IS_EXTERNAL_EXPANDED_RW.

◆ deconstruct_expanded_record()

void deconstruct_expanded_record ( ExpandedRecordHeader erh)

Definition at line 952 of file expandedrecord.c.

953 {
954  TupleDesc tupdesc;
955  Datum *dvalues;
956  bool *dnulls;
957  int nfields;
958 
959  if (erh->flags & ER_FLAG_DVALUES_VALID)
960  return; /* already valid, nothing to do */
961 
962  /* We'll need the tuple descriptor */
963  tupdesc = expanded_record_get_tupdesc(erh);
964 
965  /*
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).
970  */
971  nfields = tupdesc->natts;
972  if (erh->dvalues == NULL || erh->nfields != nfields)
973  {
974  char *chunk;
975 
976  /*
977  * To save a palloc cycle, we allocate both the Datum and isnull
978  * arrays in one palloc chunk.
979  */
981  nfields * (sizeof(Datum) + sizeof(bool)));
982  dvalues = (Datum *) chunk;
983  dnulls = (bool *) (chunk + nfields * sizeof(Datum));
984  erh->dvalues = dvalues;
985  erh->dnulls = dnulls;
986  erh->nfields = nfields;
987  }
988  else
989  {
990  dvalues = erh->dvalues;
991  dnulls = erh->dnulls;
992  }
993 
994  if (erh->flags & ER_FLAG_FVALUE_VALID)
995  {
996  /* Deconstruct tuple */
997  heap_deform_tuple(erh->fvalue, tupdesc, dvalues, dnulls);
998  }
999  else
1000  {
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));
1004  }
1005 
1006  /* Mark the dvalues as valid */
1007  erh->flags |= ER_FLAG_DVALUES_VALID;
1008 }
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1345

References chunk, ExpandedRecordHeader::dnulls, ExpandedRecordHeader::dvalues, ExpandedObjectHeader::eoh_context, ER_FLAG_DVALUES_VALID, ER_FLAG_FVALUE_VALID, expanded_record_get_tupdesc(), ExpandedRecordHeader::flags, ExpandedRecordHeader::fvalue, ExpandedRecordHeader::hdr, heap_deform_tuple(), MemoryContextAlloc(), TupleDescData::natts, and ExpandedRecordHeader::nfields.

Referenced by check_domain_for_new_field(), ER_get_flat_size(), exec_move_row(), exec_move_row_from_datum(), exec_stmt_return_next(), expanded_record_fetch_field(), expanded_record_set_field_internal(), and expanded_record_set_fields().

◆ ER_flatten_into()

static void ER_flatten_into ( ExpandedObjectHeader eohptr,
void *  result,
Size  allocated_size 
)
static

Definition at line 764 of file expandedrecord.c.

766 {
767  ExpandedRecordHeader *erh = (ExpandedRecordHeader *) eohptr;
768  HeapTupleHeader tuphdr = (HeapTupleHeader) result;
769  TupleDesc tupdesc;
770 
771  Assert(erh->er_magic == ER_MAGIC);
772 
773  /* Easy if we have a valid flattened value without out-of-line fields */
774  if (erh->flags & ER_FLAG_FVALUE_VALID &&
775  !(erh->flags & ER_FLAG_HAVE_EXTERNAL))
776  {
777  Assert(allocated_size == erh->fvalue->t_len);
778  memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
779  /* The original flattened value might not have datum header fields */
780  HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
781  HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
782  HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
783  return;
784  }
785 
786  /* Else allocation should match previous get_flat_size result */
787  Assert(allocated_size == erh->flat_size);
788 
789  /* We'll need the tuple descriptor */
790  tupdesc = expanded_record_get_tupdesc(erh);
791 
792  /* We must ensure that any pad space is zero-filled */
793  memset(tuphdr, 0, allocated_size);
794 
795  /* Set up header fields of composite Datum */
796  HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
797  HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
798  HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
799  /* We also make sure that t_ctid is invalid unless explicitly set */
800  ItemPointerSetInvalid(&(tuphdr->t_ctid));
801 
802  HeapTupleHeaderSetNatts(tuphdr, tupdesc->natts);
803  tuphdr->t_hoff = erh->hoff;
804 
805  /* And fill the data area from dvalues/dnulls */
806  heap_fill_tuple(tupdesc,
807  erh->dvalues,
808  erh->dnulls,
809  (char *) tuphdr + erh->hoff,
810  erh->data_len,
811  &tuphdr->t_infomask,
812  (erh->hasnull ? tuphdr->t_bits : NULL));
813 }
void heap_fill_tuple(TupleDesc tupleDesc, const Datum *values, const bool *isnull, char *data, Size data_size, uint16 *infomask, bits8 *bit)
Definition: heaptuple.c:400
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define HeapTupleHeaderSetTypMod(tup, typmod)
Definition: htup_details.h:471
#define HeapTupleHeaderSetNatts(tup, natts)
Definition: htup_details.h:532
#define HeapTupleHeaderSetDatumLength(tup, len)
Definition: htup_details.h:453
#define HeapTupleHeaderSetTypeId(tup, typeid)
Definition: htup_details.h:461
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition: itemptr.h:184
ItemPointerData t_ctid
Definition: htup_details.h:161
bits8 t_bits[FLEXIBLE_ARRAY_MEMBER]
Definition: htup_details.h:178

References Assert, ExpandedRecordHeader::data_len, ExpandedRecordHeader::dnulls, ExpandedRecordHeader::dvalues, ER_FLAG_FVALUE_VALID, ER_FLAG_HAVE_EXTERNAL, ER_MAGIC, ExpandedRecordHeader::er_magic, ExpandedRecordHeader::er_typeid, ExpandedRecordHeader::er_typmod, expanded_record_get_tupdesc(), ExpandedRecordHeader::flags, ExpandedRecordHeader::flat_size, ExpandedRecordHeader::fvalue, ExpandedRecordHeader::hasnull, heap_fill_tuple(), HeapTupleHeaderSetDatumLength, HeapTupleHeaderSetNatts, HeapTupleHeaderSetTypeId, HeapTupleHeaderSetTypMod, ExpandedRecordHeader::hoff, ItemPointerSetInvalid(), TupleDescData::natts, HeapTupleHeaderData::t_bits, HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, HeapTupleHeaderData::t_hoff, HeapTupleHeaderData::t_infomask, and HeapTupleData::t_len.

◆ ER_get_flat_size()

static Size ER_get_flat_size ( ExpandedObjectHeader eohptr)
static

Definition at line 652 of file expandedrecord.c.

653 {
654  ExpandedRecordHeader *erh = (ExpandedRecordHeader *) eohptr;
655  TupleDesc tupdesc;
656  Size len;
657  Size data_len;
658  int hoff;
659  bool hasnull;
660  int i;
661 
662  Assert(erh->er_magic == ER_MAGIC);
663 
664  /*
665  * The flat representation has to be a valid composite datum. Make sure
666  * that we have a registered, not anonymous, RECORD type.
667  */
668  if (erh->er_typeid == RECORDOID &&
669  erh->er_typmod < 0)
670  {
671  tupdesc = expanded_record_get_tupdesc(erh);
672  assign_record_type_typmod(tupdesc);
673  erh->er_typmod = tupdesc->tdtypmod;
674  }
675 
676  /*
677  * If we have a valid flattened value without out-of-line fields, we can
678  * just use it as-is.
679  */
680  if (erh->flags & ER_FLAG_FVALUE_VALID &&
681  !(erh->flags & ER_FLAG_HAVE_EXTERNAL))
682  return erh->fvalue->t_len;
683 
684  /* If we have a cached size value, believe that */
685  if (erh->flat_size)
686  return erh->flat_size;
687 
688  /* If we haven't yet deconstructed the tuple, do that */
689  if (!(erh->flags & ER_FLAG_DVALUES_VALID))
691 
692  /* Tuple descriptor must be valid by now */
693  tupdesc = erh->er_tupdesc;
694 
695  /*
696  * Composite datums mustn't contain any out-of-line values.
697  */
698  if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
699  {
700  for (i = 0; i < erh->nfields; i++)
701  {
702  Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
703 
704  if (!erh->dnulls[i] &&
705  !attr->attbyval && attr->attlen == -1 &&
707  {
708  /*
709  * expanded_record_set_field_internal can do the actual work
710  * of detoasting. It needn't recheck domain constraints.
711  */
713  erh->dvalues[i], false,
714  true,
715  false);
716  }
717  }
718 
719  /*
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.
724  */
725  erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
726  }
727 
728  /* Test if we currently have any null values */
729  hasnull = false;
730  for (i = 0; i < erh->nfields; i++)
731  {
732  if (erh->dnulls[i])
733  {
734  hasnull = true;
735  break;
736  }
737  }
738 
739  /* Determine total space needed */
740  len = offsetof(HeapTupleHeaderData, t_bits);
741 
742  if (hasnull)
743  len += BITMAPLEN(tupdesc->natts);
744 
745  hoff = len = MAXALIGN(len); /* align user data safely */
746 
747  data_len = heap_compute_data_size(tupdesc, erh->dvalues, erh->dnulls);
748 
749  len += data_len;
750 
751  /* Cache for next time */
752  erh->flat_size = len;
753  erh->data_len = data_len;
754  erh->hoff = hoff;
755  erh->hasnull = hasnull;
756 
757  return len;
758 }
size_t Size
Definition: c.h:559
void expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, Datum newValue, bool isnull, bool expand_external, bool check_constraints)
Size heap_compute_data_size(TupleDesc tupleDesc, const Datum *values, const bool *isnull)
Definition: heaptuple.c:215
#define BITMAPLEN(NATTS)
Definition: htup_details.h:545
int i
Definition: isn.c:72
const void size_t len
int32 tdtypmod
Definition: tupdesc.h:83
void assign_record_type_typmod(TupleDesc tupDesc)
Definition: typcache.c:2040

References Assert, assign_record_type_typmod(), BITMAPLEN, ExpandedRecordHeader::data_len, DatumGetPointer(), deconstruct_expanded_record(), ExpandedRecordHeader::dnulls, ExpandedRecordHeader::dvalues, ER_FLAG_DVALUES_VALID, ER_FLAG_FVALUE_VALID, ER_FLAG_HAVE_EXTERNAL, ER_MAGIC, ExpandedRecordHeader::er_magic, ExpandedRecordHeader::er_tupdesc, ExpandedRecordHeader::er_typeid, ExpandedRecordHeader::er_typmod, expanded_record_get_tupdesc(), expanded_record_set_field_internal(), ExpandedRecordHeader::flags, ExpandedRecordHeader::flat_size, ExpandedRecordHeader::fvalue, ExpandedRecordHeader::hasnull, heap_compute_data_size(), ExpandedRecordHeader::hoff, i, len, MAXALIGN, TupleDescData::natts, ExpandedRecordHeader::nfields, HeapTupleData::t_len, TupleDescData::tdtypmod, TupleDescAttr, and VARATT_IS_EXTERNAL.

◆ ER_mc_callback()

static void ER_mc_callback ( void *  arg)
static

Definition at line 902 of file expandedrecord.c.

903 {
905  TupleDesc tupdesc = erh->er_tupdesc;
906 
907  /* Release our privately-managed tupdesc refcount, if any */
908  if (tupdesc)
909  {
910  erh->er_tupdesc = NULL; /* just for luck */
911  if (tupdesc->tdrefcount > 0)
912  {
913  if (--tupdesc->tdrefcount == 0)
914  FreeTupleDesc(tupdesc);
915  }
916  }
917 }
void * arg
int tdrefcount
Definition: tupdesc.h:84
void FreeTupleDesc(TupleDesc tupdesc)
Definition: tupdesc.c:331

References arg, ExpandedRecordHeader::er_tupdesc, FreeTupleDesc(), and TupleDescData::tdrefcount.

Referenced by expanded_record_fetch_tupdesc(), make_expanded_record_from_exprecord(), make_expanded_record_from_tupdesc(), and make_expanded_record_from_typeid().

◆ expanded_record_fetch_field()

Datum expanded_record_fetch_field ( ExpandedRecordHeader erh,
int  fnumber,
bool *  isnull 
)

Definition at line 1063 of file expandedrecord.c.

1065 {
1066  if (fnumber > 0)
1067  {
1068  /* Empty record has null fields */
1069  if (ExpandedRecordIsEmpty(erh))
1070  {
1071  *isnull = true;
1072  return (Datum) 0;
1073  }
1074  /* Make sure we have deconstructed form */
1076  /* Out-of-range field number reads as null */
1077  if (unlikely(fnumber > erh->nfields))
1078  {
1079  *isnull = true;
1080  return (Datum) 0;
1081  }
1082  *isnull = erh->dnulls[fnumber - 1];
1083  return erh->dvalues[fnumber - 1];
1084  }
1085  else
1086  {
1087  /* System columns read as null if we haven't got flat tuple */
1088  if (erh->fvalue == NULL)
1089  {
1090  *isnull = true;
1091  return (Datum) 0;
1092  }
1093  /* heap_getsysattr doesn't actually use tupdesc, so just pass null */
1094  return heap_getsysattr(erh->fvalue, fnumber, NULL, isnull);
1095  }
1096 }
Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: heaptuple.c:723

References deconstruct_expanded_record(), ExpandedRecordHeader::dnulls, ExpandedRecordHeader::dvalues, ExpandedRecordIsEmpty, ExpandedRecordHeader::fvalue, heap_getsysattr(), ExpandedRecordHeader::nfields, and unlikely.

Referenced by expanded_record_get_field().

◆ expanded_record_fetch_tupdesc()

TupleDesc expanded_record_fetch_tupdesc ( ExpandedRecordHeader erh)

Definition at line 824 of file expandedrecord.c.

825 {
826  TupleDesc tupdesc;
827 
828  /* Easy if we already have it (but caller should have checked already) */
829  if (erh->er_tupdesc)
830  return erh->er_tupdesc;
831 
832  /* Lookup the composite type's tupdesc using the typcache */
833  tupdesc = lookup_rowtype_tupdesc(erh->er_typeid, erh->er_typmod);
834 
835  /*
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
839  * expanded object.
840  */
841  if (tupdesc->tdrefcount >= 0)
842  {
843  /* Register callback if we didn't already */
844  if (erh->er_mcb.arg == NULL)
845  {
846  erh->er_mcb.func = ER_mc_callback;
847  erh->er_mcb.arg = erh;
849  &erh->er_mcb);
850  }
851 
852  /* Remember our own pointer */
853  erh->er_tupdesc = tupdesc;
854  tupdesc->tdrefcount++;
855 
856  /* Release the pin lookup_rowtype_tupdesc acquired */
857  ReleaseTupleDesc(tupdesc);
858  }
859  else
860  {
861  /* Just remember the pointer */
862  erh->er_tupdesc = tupdesc;
863  }
864 
865  /* In either case, fetch the process-global ID for this tupdesc */
867  tupdesc->tdtypmod);
868 
869  return tupdesc;
870 }
static void ER_mc_callback(void *arg)
void MemoryContextRegisterResetCallback(MemoryContext context, MemoryContextCallback *cb)
Definition: mcxt.c:568
MemoryContextCallback er_mcb
MemoryContextCallbackFunction func
Definition: palloc.h:49
Oid tdtypeid
Definition: tupdesc.h:82
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1920
uint64 assign_record_type_identifier(Oid type_id, int32 typmod)
Definition: typcache.c:2132

References MemoryContextCallback::arg, assign_record_type_identifier(), ExpandedObjectHeader::eoh_context, ER_mc_callback(), ExpandedRecordHeader::er_mcb, ExpandedRecordHeader::er_tupdesc, ExpandedRecordHeader::er_tupdesc_id, ExpandedRecordHeader::er_typeid, ExpandedRecordHeader::er_typmod, MemoryContextCallback::func, ExpandedRecordHeader::hdr, lookup_rowtype_tupdesc(), MemoryContextRegisterResetCallback(), ReleaseTupleDesc, TupleDescData::tdrefcount, TupleDescData::tdtypeid, and TupleDescData::tdtypmod.

Referenced by expanded_record_get_tupdesc().

◆ expanded_record_get_tuple()

HeapTuple expanded_record_get_tuple ( ExpandedRecordHeader erh)

Definition at line 884 of file expandedrecord.c.

885 {
886  /* Easy case if we still have original tuple */
887  if (erh->flags & ER_FLAG_FVALUE_VALID)
888  return erh->fvalue;
889 
890  /* Else just build a tuple from datums */
891  if (erh->flags & ER_FLAG_DVALUES_VALID)
892  return heap_form_tuple(erh->er_tupdesc, erh->dvalues, erh->dnulls);
893 
894  /* Expanded record is empty */
895  return NULL;
896 }
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116

References ExpandedRecordHeader::dnulls, ExpandedRecordHeader::dvalues, ER_FLAG_DVALUES_VALID, ER_FLAG_FVALUE_VALID, ExpandedRecordHeader::er_tupdesc, ExpandedRecordHeader::flags, ExpandedRecordHeader::fvalue, and heap_form_tuple().

Referenced by coerce_function_result_tuple(), exec_stmt_return_next(), and plpgsql_exec_trigger().

◆ expanded_record_lookup_field()

bool expanded_record_lookup_field ( ExpandedRecordHeader erh,
const char *  fieldname,
ExpandedRecordFieldInfo finfo 
)

Definition at line 1017 of file expandedrecord.c.

1019 {
1020  TupleDesc tupdesc;
1021  int fno;
1022  Form_pg_attribute attr;
1023  const FormData_pg_attribute *sysattr;
1024 
1025  tupdesc = expanded_record_get_tupdesc(erh);
1026 
1027  /* First, check user-defined attributes */
1028  for (fno = 0; fno < tupdesc->natts; fno++)
1029  {
1030  attr = TupleDescAttr(tupdesc, fno);
1031  if (namestrcmp(&attr->attname, fieldname) == 0 &&
1032  !attr->attisdropped)
1033  {
1034  finfo->fnumber = attr->attnum;
1035  finfo->ftypeid = attr->atttypid;
1036  finfo->ftypmod = attr->atttypmod;
1037  finfo->fcollation = attr->attcollation;
1038  return true;
1039  }
1040  }
1041 
1042  /* How about system attributes? */
1043  sysattr = SystemAttributeByName(fieldname);
1044  if (sysattr != NULL)
1045  {
1046  finfo->fnumber = sysattr->attnum;
1047  finfo->ftypeid = sysattr->atttypid;
1048  finfo->ftypmod = sysattr->atttypmod;
1049  finfo->fcollation = sysattr->attcollation;
1050  return true;
1051  }
1052 
1053  return false;
1054 }
const FormData_pg_attribute * SystemAttributeByName(const char *attname)
Definition: heap.c:252
int namestrcmp(Name name, const char *str)
Definition: name.c:247
FormData_pg_attribute
Definition: pg_attribute.h:193

References expanded_record_get_tupdesc(), ExpandedRecordFieldInfo::fcollation, ExpandedRecordFieldInfo::fnumber, FormData_pg_attribute, ExpandedRecordFieldInfo::ftypeid, ExpandedRecordFieldInfo::ftypmod, namestrcmp(), TupleDescData::natts, SystemAttributeByName(), and TupleDescAttr.

Referenced by exec_assign_value(), exec_eval_datum(), plpgsql_exec_get_datum_type(), plpgsql_exec_get_datum_type_info(), plpgsql_param_eval_recfield(), and plpgsql_param_fetch().

◆ expanded_record_set_field_internal()

void expanded_record_set_field_internal ( ExpandedRecordHeader erh,
int  fnumber,
Datum  newValue,
bool  isnull,
bool  expand_external,
bool  check_constraints 
)

Definition at line 1112 of file expandedrecord.c.

1116 {
1117  TupleDesc tupdesc;
1118  Form_pg_attribute attr;
1119  Datum *dvalues;
1120  bool *dnulls;
1121  char *oldValue;
1122 
1123  /*
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.
1126  */
1127  Assert(!(erh->flags & ER_FLAG_IS_DUMMY) || !check_constraints);
1128 
1129  /* Before performing the assignment, see if result will satisfy domain */
1130  if ((erh->flags & ER_FLAG_IS_DOMAIN) && check_constraints)
1131  check_domain_for_new_field(erh, fnumber, newValue, isnull);
1132 
1133  /* If we haven't yet deconstructed the tuple, do that */
1134  if (!(erh->flags & ER_FLAG_DVALUES_VALID))
1136 
1137  /* Tuple descriptor must be valid by now */
1138  tupdesc = erh->er_tupdesc;
1139  Assert(erh->nfields == tupdesc->natts);
1140 
1141  /* Caller error if fnumber is system column or nonexistent column */
1142  if (unlikely(fnumber <= 0 || fnumber > erh->nfields))
1143  elog(ERROR, "cannot assign to field %d of expanded record", fnumber);
1144 
1145  /*
1146  * Copy new field value into record's context, and deal with detoasting,
1147  * if needed.
1148  */
1149  attr = TupleDescAttr(tupdesc, fnumber - 1);
1150  if (!isnull && !attr->attbyval)
1151  {
1152  MemoryContext oldcxt;
1153 
1154  /* If requested, detoast any external value */
1155  if (expand_external)
1156  {
1157  if (attr->attlen == -1 &&
1159  {
1160  /* Detoasting should be done in short-lived context. */
1162  newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
1163  MemoryContextSwitchTo(oldcxt);
1164  }
1165  else
1166  expand_external = false; /* need not clean up below */
1167  }
1168 
1169  /* Copy value into record's context */
1170  oldcxt = MemoryContextSwitchTo(erh->hdr.eoh_context);
1171  newValue = datumCopy(newValue, false, attr->attlen);
1172  MemoryContextSwitchTo(oldcxt);
1173 
1174  /* We can now flush anything that detoasting might have leaked */
1175  if (expand_external)
1177 
1178  /* Remember that we have field(s) that may need to be pfree'd */
1180 
1181  /*
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.)
1186  */
1187  if (attr->attlen == -1 &&
1189  erh->flags |= ER_FLAG_HAVE_EXTERNAL;
1190  }
1191 
1192  /*
1193  * We're ready to make irreversible changes.
1194  */
1195  dvalues = erh->dvalues;
1196  dnulls = erh->dnulls;
1197 
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 */
1201  erh->flat_size = 0;
1202 
1203  /* Grab old field value for pfree'ing, if needed. */
1204  if (!attr->attbyval && !dnulls[fnumber - 1])
1205  oldValue = (char *) DatumGetPointer(dvalues[fnumber - 1]);
1206  else
1207  oldValue = NULL;
1208 
1209  /* And finally we can insert the new field. */
1210  dvalues[fnumber - 1] = newValue;
1211  dnulls[fnumber - 1] = isnull;
1212 
1213  /*
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.
1217  *
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
1222  * context.
1223  */
1224  if (oldValue && !(erh->flags & ER_FLAG_IS_DUMMY))
1225  {
1226  /* Don't try to pfree a part of the original flat record */
1227  if (oldValue < erh->fstartptr || oldValue >= erh->fendptr)
1228  pfree(oldValue);
1229  }
1230 }
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
struct varlena * detoast_external_attr(struct varlena *attr)
Definition: detoast.c:45
static pg_noinline void check_domain_for_new_field(ExpandedRecordHeader *erh, int fnumber, Datum newValue, bool isnull)
#define ER_FLAG_DVALUES_ALLOCED
#define ER_FLAG_IS_DOMAIN
void pfree(void *pointer)
Definition: mcxt.c:1521
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
Definition: c.h:641

References Assert, check_domain_for_new_field(), datumCopy(), DatumGetPointer(), deconstruct_expanded_record(), detoast_external_attr(), ExpandedRecordHeader::dnulls, ExpandedRecordHeader::dvalues, elog, ExpandedObjectHeader::eoh_context, ER_FLAG_DVALUES_ALLOCED, ER_FLAG_DVALUES_VALID, ER_FLAG_FVALUE_VALID, ER_FLAG_HAVE_EXTERNAL, ER_FLAG_IS_DOMAIN, ER_FLAG_IS_DUMMY, ExpandedRecordHeader::er_short_term_cxt, ExpandedRecordHeader::er_tupdesc, ERROR, ExpandedRecordHeader::fendptr, ExpandedRecordHeader::flags, ExpandedRecordHeader::flat_size, get_short_term_cxt(), ExpandedRecordHeader::hdr, MemoryContextReset(), MemoryContextSwitchTo(), TupleDescData::natts, ExpandedRecordHeader::nfields, pfree(), PointerGetDatum(), TupleDescAttr, unlikely, and VARATT_IS_EXTERNAL.

Referenced by ER_get_flat_size(), and plpgsql_exec_trigger().

◆ expanded_record_set_fields()

void expanded_record_set_fields ( ExpandedRecordHeader erh,
const Datum newValues,
const bool *  isnulls,
bool  expand_external 
)

Definition at line 1249 of file expandedrecord.c.

1252 {
1253  TupleDesc tupdesc;
1254  Datum *dvalues;
1255  bool *dnulls;
1256  int fnumber;
1257  MemoryContext oldcxt;
1258 
1259  /* Shouldn't ever be trying to assign new data to a dummy header */
1260  Assert(!(erh->flags & ER_FLAG_IS_DUMMY));
1261 
1262  /* If we haven't yet deconstructed the tuple, do that */
1263  if (!(erh->flags & ER_FLAG_DVALUES_VALID))
1265 
1266  /* Tuple descriptor must be valid by now */
1267  tupdesc = erh->er_tupdesc;
1268  Assert(erh->nfields == tupdesc->natts);
1269 
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 */
1273  erh->flat_size = 0;
1274 
1275  oldcxt = MemoryContextSwitchTo(erh->hdr.eoh_context);
1276 
1277  dvalues = erh->dvalues;
1278  dnulls = erh->dnulls;
1279 
1280  for (fnumber = 0; fnumber < erh->nfields; fnumber++)
1281  {
1282  Form_pg_attribute attr = TupleDescAttr(tupdesc, fnumber);
1283  Datum newValue;
1284  bool isnull;
1285 
1286  /* Ignore dropped columns */
1287  if (attr->attisdropped)
1288  continue;
1289 
1290  newValue = newValues[fnumber];
1291  isnull = isnulls[fnumber];
1292 
1293  if (!attr->attbyval)
1294  {
1295  /*
1296  * Copy new field value into record's context, and deal with
1297  * detoasting, if needed.
1298  */
1299  if (!isnull)
1300  {
1301  /* Is it an external toasted value? */
1302  if (attr->attlen == -1 &&
1304  {
1305  if (expand_external)
1306  {
1307  /* Detoast as requested while copying the value */
1308  newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
1309  }
1310  else
1311  {
1312  /* Just copy the value */
1313  newValue = datumCopy(newValue, false, -1);
1314  /* If it's still external, remember that */
1315  if (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
1316  erh->flags |= ER_FLAG_HAVE_EXTERNAL;
1317  }
1318  }
1319  else
1320  {
1321  /* Not an external value, just copy it */
1322  newValue = datumCopy(newValue, false, attr->attlen);
1323  }
1324 
1325  /* Remember that we have field(s) that need to be pfree'd */
1327  }
1328 
1329  /*
1330  * Free old field value, if any (not likely, since really we ought
1331  * to be inserting into an empty record).
1332  */
1333  if (unlikely(!dnulls[fnumber]))
1334  {
1335  char *oldValue;
1336 
1337  oldValue = (char *) DatumGetPointer(dvalues[fnumber]);
1338  /* Don't try to pfree a part of the original flat record */
1339  if (oldValue < erh->fstartptr || oldValue >= erh->fendptr)
1340  pfree(oldValue);
1341  }
1342  }
1343 
1344  /* And finally we can insert the new field. */
1345  dvalues[fnumber] = newValue;
1346  dnulls[fnumber] = isnull;
1347  }
1348 
1349  /*
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.
1353  */
1354  if (erh->flags & ER_FLAG_IS_DOMAIN)
1355  {
1356  /* We run domain_check in a short-lived context to limit cruft */
1358 
1360  erh->er_decltypeid,
1361  &erh->er_domaininfo,
1362  erh->hdr.eoh_context);
1363  }
1364 
1365  MemoryContextSwitchTo(oldcxt);
1366 }

References Assert, datumCopy(), DatumGetPointer(), deconstruct_expanded_record(), detoast_external_attr(), ExpandedRecordHeader::dnulls, domain_check(), ExpandedRecordHeader::dvalues, ExpandedObjectHeader::eoh_context, ExpandedRecordHeader::er_decltypeid, ExpandedRecordHeader::er_domaininfo, ER_FLAG_DVALUES_ALLOCED, ER_FLAG_DVALUES_VALID, ER_FLAG_FVALUE_VALID, ER_FLAG_HAVE_EXTERNAL, ER_FLAG_IS_DOMAIN, ER_FLAG_IS_DUMMY, ExpandedRecordHeader::er_tupdesc, ExpandedRecordGetRODatum(), ExpandedRecordHeader::fendptr, ExpandedRecordHeader::flags, ExpandedRecordHeader::flat_size, get_short_term_cxt(), ExpandedRecordHeader::hdr, MemoryContextSwitchTo(), TupleDescData::natts, ExpandedRecordHeader::nfields, pfree(), PointerGetDatum(), TupleDescAttr, unlikely, and VARATT_IS_EXTERNAL.

Referenced by exec_move_row_from_fields().

◆ expanded_record_set_tuple()

void expanded_record_set_tuple ( ExpandedRecordHeader erh,
HeapTuple  tuple,
bool  copy,
bool  expand_external 
)

Definition at line 440 of file expandedrecord.c.

444 {
445  int oldflags;
446  HeapTuple oldtuple;
447  char *oldfstartptr;
448  char *oldfendptr;
449  int newflags;
450  HeapTuple newtuple;
451  MemoryContext oldcxt;
452 
453  /* Shouldn't ever be trying to assign new data to a dummy header */
454  Assert(!(erh->flags & ER_FLAG_IS_DUMMY));
455 
456  /*
457  * Before performing the assignment, see if result will satisfy domain.
458  */
459  if (erh->flags & ER_FLAG_IS_DOMAIN)
460  check_domain_for_new_tuple(erh, tuple);
461 
462  /*
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
465  * might generate.
466  */
467  if (expand_external && tuple)
468  {
469  /* Assert caller didn't ask for unsupported case */
470  Assert(copy);
471  if (HeapTupleHasExternal(tuple))
472  {
474  tuple = toast_flatten_tuple(tuple, erh->er_tupdesc);
475  MemoryContextSwitchTo(oldcxt);
476  }
477  else
478  expand_external = false; /* need not clean up below */
479  }
480 
481  /*
482  * Initialize new flags, keeping only non-data status bits.
483  */
484  oldflags = erh->flags;
485  newflags = oldflags & ER_FLAGS_NON_DATA;
486 
487  /*
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.
490  */
491  if (copy && tuple)
492  {
493  oldcxt = MemoryContextSwitchTo(erh->hdr.eoh_context);
494  newtuple = heap_copytuple(tuple);
495  newflags |= ER_FLAG_FVALUE_ALLOCED;
496  MemoryContextSwitchTo(oldcxt);
497 
498  /* We can now flush anything that detoasting might have leaked. */
499  if (expand_external)
501  }
502  else
503  newtuple = tuple;
504 
505  /* Make copies of fields we're about to overwrite */
506  oldtuple = erh->fvalue;
507  oldfstartptr = erh->fstartptr;
508  oldfendptr = erh->fendptr;
509 
510  /*
511  * It's now safe to update the expanded record's state.
512  */
513  if (newtuple)
514  {
515  /* Save flat representation */
516  erh->fvalue = newtuple;
517  erh->fstartptr = (char *) newtuple->t_data;
518  erh->fendptr = ((char *) newtuple->t_data) + newtuple->t_len;
519  newflags |= ER_FLAG_FVALUE_VALID;
520 
521  /* Remember if we have any out-of-line field values */
522  if (HeapTupleHasExternal(newtuple))
523  newflags |= ER_FLAG_HAVE_EXTERNAL;
524  }
525  else
526  {
527  erh->fvalue = NULL;
528  erh->fstartptr = erh->fendptr = NULL;
529  }
530 
531  erh->flags = newflags;
532 
533  /* Reset flat-size info; we don't bother to make it valid now */
534  erh->flat_size = 0;
535 
536  /*
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.
541  */
542  if (oldflags & ER_FLAG_DVALUES_ALLOCED)
543  {
544  TupleDesc tupdesc = erh->er_tupdesc;
545  int i;
546 
547  for (i = 0; i < erh->nfields; i++)
548  {
549  if (!erh->dnulls[i] &&
550  !(TupleDescAttr(tupdesc, i)->attbyval))
551  {
552  char *oldValue = (char *) DatumGetPointer(erh->dvalues[i]);
553 
554  if (oldValue < oldfstartptr || oldValue >= oldfendptr)
555  pfree(oldValue);
556  }
557  }
558  }
559 
560  /* Likewise free the old tuple, if it was locally allocated */
561  if (oldflags & ER_FLAG_FVALUE_ALLOCED)
562  heap_freetuple(oldtuple);
563 
564  /* We won't make a new deconstructed representation until/unless needed */
565 }
static pg_noinline void check_domain_for_new_tuple(ExpandedRecordHeader *erh, HeapTuple tuple)
#define ER_FLAGS_NON_DATA
#define ER_FLAG_FVALUE_ALLOCED
HeapTuple toast_flatten_tuple(HeapTuple tup, TupleDesc tupleDesc)
Definition: heaptoast.c:350
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:776
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
bool attbyval
Definition: pg_attribute.h:103

References Assert, attbyval, check_domain_for_new_tuple(), DatumGetPointer(), ExpandedRecordHeader::dnulls, ExpandedRecordHeader::dvalues, ExpandedObjectHeader::eoh_context, ER_FLAG_DVALUES_ALLOCED, ER_FLAG_FVALUE_ALLOCED, ER_FLAG_FVALUE_VALID, ER_FLAG_HAVE_EXTERNAL, ER_FLAG_IS_DOMAIN, ER_FLAG_IS_DUMMY, ER_FLAGS_NON_DATA, ExpandedRecordHeader::er_short_term_cxt, ExpandedRecordHeader::er_tupdesc, ExpandedRecordHeader::fendptr, ExpandedRecordHeader::flags, ExpandedRecordHeader::flat_size, ExpandedRecordHeader::fstartptr, ExpandedRecordHeader::fvalue, get_short_term_cxt(), ExpandedRecordHeader::hdr, heap_copytuple(), heap_freetuple(), HeapTupleHasExternal, i, if(), MemoryContextReset(), MemoryContextSwitchTo(), ExpandedRecordHeader::nfields, pfree(), HeapTupleData::t_data, HeapTupleData::t_len, toast_flatten_tuple(), and TupleDescAttr.

Referenced by exec_for_query(), exec_move_row(), exec_move_row_from_datum(), and plpgsql_exec_trigger().

◆ get_short_term_cxt()

static MemoryContext get_short_term_cxt ( ExpandedRecordHeader erh)
static

◆ make_expanded_record_from_datum()

Datum make_expanded_record_from_datum ( Datum  recorddatum,
MemoryContext  parentcontext 
)

Definition at line 580 of file expandedrecord.c.

581 {
583  HeapTupleHeader tuphdr;
584  HeapTupleData tmptup;
585  HeapTuple newtuple;
586  MemoryContext objcxt;
587  MemoryContext oldcxt;
588 
589  /*
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.
593  */
594  objcxt = AllocSetContextCreate(parentcontext,
595  "expanded record",
597 
598  /* Set up expanded record header, initializing fields to 0/null */
599  erh = (ExpandedRecordHeader *)
601 
602  EOH_init_header(&erh->hdr, &ER_methods, objcxt);
603  erh->er_magic = ER_MAGIC;
604 
605  /*
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.)
609  */
610  tuphdr = DatumGetHeapTupleHeader(recorddatum);
611 
612  tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr);
613  ItemPointerSetInvalid(&(tmptup.t_self));
614  tmptup.t_tableOid = InvalidOid;
615  tmptup.t_data = tuphdr;
616 
617  oldcxt = MemoryContextSwitchTo(objcxt);
618  newtuple = heap_copytuple(&tmptup);
620  MemoryContextSwitchTo(oldcxt);
621 
622  /* Fill in composite-type identification info */
623  erh->er_decltypeid = erh->er_typeid = HeapTupleHeaderGetTypeId(tuphdr);
624  erh->er_typmod = HeapTupleHeaderGetTypMod(tuphdr);
625 
626  /* remember we have a flat representation */
627  erh->fvalue = newtuple;
628  erh->fstartptr = (char *) newtuple->t_data;
629  erh->fendptr = ((char *) newtuple->t_data) + newtuple->t_len;
630  erh->flags |= ER_FLAG_FVALUE_VALID;
631 
632  /* Shouldn't need to set ER_FLAG_HAVE_EXTERNAL */
634 
635  /*
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
638  * friends, either.
639  */
640 
641  /* return a R/W pointer to the expanded record */
642  return EOHPGetRWDatum(&erh->hdr);
643 }
static Datum EOHPGetRWDatum(const struct ExpandedObjectHeader *eohptr)
#define DatumGetHeapTupleHeader(X)
Definition: fmgr.h:295
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:466
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:456
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:450
#define HeapTupleHeaderHasExternal(tup)
Definition: htup_details.h:537
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1215
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
#define InvalidOid
Definition: postgres_ext.h:36
ItemPointerData t_self
Definition: htup.h:65
Oid t_tableOid
Definition: htup.h:66

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert, DatumGetHeapTupleHeader, EOH_init_header(), EOHPGetRWDatum(), ExpandedRecordHeader::er_decltypeid, ER_FLAG_FVALUE_ALLOCED, ER_FLAG_FVALUE_VALID, ER_MAGIC, ExpandedRecordHeader::er_magic, ER_methods, ExpandedRecordHeader::er_typeid, ExpandedRecordHeader::er_typmod, ExpandedRecordHeader::fendptr, ExpandedRecordHeader::flags, ExpandedRecordHeader::fstartptr, ExpandedRecordHeader::fvalue, ExpandedRecordHeader::hdr, heap_copytuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, HeapTupleHeaderHasExternal, InvalidOid, ItemPointerSetInvalid(), MemoryContextAllocZero(), MemoryContextSwitchTo(), HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, and HeapTupleData::t_tableOid.

Referenced by DatumGetExpandedRecord().

◆ make_expanded_record_from_exprecord()

ExpandedRecordHeader* make_expanded_record_from_exprecord ( ExpandedRecordHeader olderh,
MemoryContext  parentcontext 
)

Definition at line 329 of file expandedrecord.c.

331 {
333  TupleDesc tupdesc = expanded_record_get_tupdesc(olderh);
334  MemoryContext objcxt;
335  MemoryContext oldcxt;
336  char *chunk;
337 
338  /*
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.
342  */
343  objcxt = AllocSetContextCreate(parentcontext,
344  "expanded record",
346 
347  /*
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.
352  */
353  erh = (ExpandedRecordHeader *)
355  + tupdesc->natts * (sizeof(Datum) + sizeof(bool)));
356 
357  /* Ensure all header fields are initialized to 0/null */
358  memset(erh, 0, sizeof(ExpandedRecordHeader));
359 
360  EOH_init_header(&erh->hdr, &ER_methods, objcxt);
361  erh->er_magic = ER_MAGIC;
362 
363  /* Set up dvalues/dnulls, with no valid contents as yet */
364  chunk = (char *) erh + MAXALIGN(sizeof(ExpandedRecordHeader));
365  erh->dvalues = (Datum *) chunk;
366  erh->dnulls = (bool *) (chunk + tupdesc->natts * sizeof(Datum));
367  erh->nfields = tupdesc->natts;
368 
369  /* Fill in composite-type identification info */
370  erh->er_decltypeid = olderh->er_decltypeid;
371  erh->er_typeid = olderh->er_typeid;
372  erh->er_typmod = olderh->er_typmod;
373  erh->er_tupdesc_id = olderh->er_tupdesc_id;
374 
375  /* The only flag bit that transfers over is IS_DOMAIN */
376  erh->flags = olderh->flags & ER_FLAG_IS_DOMAIN;
377 
378  /*
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
382  * expanded object.
383  */
384  if (tupdesc->tdrefcount >= 0)
385  {
386  /* Register callback to release the refcount */
387  erh->er_mcb.func = ER_mc_callback;
388  erh->er_mcb.arg = erh;
390  &erh->er_mcb);
391 
392  /* And save the pointer */
393  erh->er_tupdesc = tupdesc;
394  tupdesc->tdrefcount++;
395  }
396  else if (olderh->flags & ER_FLAG_TUPDESC_ALLOCED)
397  {
398  /* We need to make our own copy of the tupdesc */
399  oldcxt = MemoryContextSwitchTo(objcxt);
400  erh->er_tupdesc = CreateTupleDescCopy(tupdesc);
402  MemoryContextSwitchTo(oldcxt);
403  }
404  else
405  {
406  /*
407  * Assume the tupdesc will outlive this expanded object, just like
408  * we're assuming it will outlive the source object.
409  */
410  erh->er_tupdesc = tupdesc;
411  }
412 
413  /*
414  * We don't set ER_FLAG_DVALUES_VALID or ER_FLAG_FVALUE_VALID, so the
415  * record remains logically empty.
416  */
417 
418  return erh;
419 }
#define ER_FLAG_TUPDESC_ALLOCED
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:133

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, MemoryContextCallback::arg, chunk, CreateTupleDescCopy(), ExpandedRecordHeader::dnulls, ExpandedRecordHeader::dvalues, ExpandedObjectHeader::eoh_context, EOH_init_header(), ExpandedRecordHeader::er_decltypeid, ER_FLAG_IS_DOMAIN, ER_FLAG_TUPDESC_ALLOCED, ER_MAGIC, ExpandedRecordHeader::er_magic, ER_mc_callback(), ExpandedRecordHeader::er_mcb, ER_methods, ExpandedRecordHeader::er_tupdesc, ExpandedRecordHeader::er_tupdesc_id, ExpandedRecordHeader::er_typeid, ExpandedRecordHeader::er_typmod, expanded_record_get_tupdesc(), ExpandedRecordHeader::flags, MemoryContextCallback::func, ExpandedRecordHeader::hdr, MAXALIGN, MemoryContextAlloc(), MemoryContextRegisterResetCallback(), MemoryContextSwitchTo(), TupleDescData::natts, ExpandedRecordHeader::nfields, and TupleDescData::tdrefcount.

Referenced by make_expanded_record_for_rec(), and plpgsql_exec_trigger().

◆ make_expanded_record_from_tupdesc()

ExpandedRecordHeader* make_expanded_record_from_tupdesc ( TupleDesc  tupdesc,
MemoryContext  parentcontext 
)

Definition at line 205 of file expandedrecord.c.

207 {
209  uint64 tupdesc_id;
210  MemoryContext objcxt;
211  MemoryContext oldcxt;
212  char *chunk;
213 
214  if (tupdesc->tdtypeid != RECORDOID)
215  {
216  /*
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.
221  *
222  * Note that tdtypeid couldn't be a domain type, so we need not
223  * consider that case here.
224  */
225  TypeCacheEntry *typentry;
226 
227  typentry = lookup_type_cache(tupdesc->tdtypeid, TYPECACHE_TUPDESC);
228  if (typentry->tupDesc == NULL)
229  ereport(ERROR,
230  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
231  errmsg("type %s is not composite",
232  format_type_be(tupdesc->tdtypeid))));
233  tupdesc = typentry->tupDesc;
234  tupdesc_id = typentry->tupDesc_identifier;
235  }
236  else
237  {
238  /*
239  * For RECORD types, get the appropriate unique identifier (possibly
240  * freshly assigned).
241  */
242  tupdesc_id = assign_record_type_identifier(tupdesc->tdtypeid,
243  tupdesc->tdtypmod);
244  }
245 
246  /*
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.
250  */
251  objcxt = AllocSetContextCreate(parentcontext,
252  "expanded record",
254 
255  /*
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.
260  */
261  erh = (ExpandedRecordHeader *)
263  + tupdesc->natts * (sizeof(Datum) + sizeof(bool)));
264 
265  /* Ensure all header fields are initialized to 0/null */
266  memset(erh, 0, sizeof(ExpandedRecordHeader));
267 
268  EOH_init_header(&erh->hdr, &ER_methods, objcxt);
269  erh->er_magic = ER_MAGIC;
270 
271  /* Set up dvalues/dnulls, with no valid contents as yet */
272  chunk = (char *) erh + MAXALIGN(sizeof(ExpandedRecordHeader));
273  erh->dvalues = (Datum *) chunk;
274  erh->dnulls = (bool *) (chunk + tupdesc->natts * sizeof(Datum));
275  erh->nfields = tupdesc->natts;
276 
277  /* Fill in composite-type identification info */
278  erh->er_decltypeid = erh->er_typeid = tupdesc->tdtypeid;
279  erh->er_typmod = tupdesc->tdtypmod;
280  erh->er_tupdesc_id = tupdesc_id;
281 
282  /*
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
286  * expanded object.
287  */
288  if (tupdesc->tdrefcount >= 0)
289  {
290  /* Register callback to release the refcount */
291  erh->er_mcb.func = ER_mc_callback;
292  erh->er_mcb.arg = erh;
294  &erh->er_mcb);
295 
296  /* And save the pointer */
297  erh->er_tupdesc = tupdesc;
298  tupdesc->tdrefcount++;
299  }
300  else
301  {
302  /* Just copy it */
303  oldcxt = MemoryContextSwitchTo(objcxt);
304  erh->er_tupdesc = CreateTupleDescCopy(tupdesc);
306  MemoryContextSwitchTo(oldcxt);
307  }
308 
309  /*
310  * We don't set ER_FLAG_DVALUES_VALID or ER_FLAG_FVALUE_VALID, so the
311  * record remains logically empty.
312  */
313 
314  return erh;
315 }
uint64_t uint64
Definition: c.h:486
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ereport(elevel,...)
Definition: elog.h:149
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
uint64 tupDesc_identifier
Definition: typcache.h:90
TupleDesc tupDesc
Definition: typcache.h:89
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:386
#define TYPECACHE_TUPDESC
Definition: typcache.h:145

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, MemoryContextCallback::arg, assign_record_type_identifier(), chunk, CreateTupleDescCopy(), ExpandedRecordHeader::dnulls, ExpandedRecordHeader::dvalues, ExpandedObjectHeader::eoh_context, EOH_init_header(), ExpandedRecordHeader::er_decltypeid, ER_FLAG_TUPDESC_ALLOCED, ER_MAGIC, ExpandedRecordHeader::er_magic, ER_mc_callback(), ExpandedRecordHeader::er_mcb, ER_methods, ExpandedRecordHeader::er_tupdesc, ExpandedRecordHeader::er_tupdesc_id, ExpandedRecordHeader::er_typeid, ExpandedRecordHeader::er_typmod, ereport, errcode(), errmsg(), ERROR, ExpandedRecordHeader::flags, format_type_be(), MemoryContextCallback::func, ExpandedRecordHeader::hdr, lookup_type_cache(), MAXALIGN, MemoryContextAlloc(), MemoryContextRegisterResetCallback(), MemoryContextSwitchTo(), TupleDescData::natts, ExpandedRecordHeader::nfields, TupleDescData::tdrefcount, TupleDescData::tdtypeid, TupleDescData::tdtypmod, TypeCacheEntry::tupDesc, TypeCacheEntry::tupDesc_identifier, and TYPECACHE_TUPDESC.

Referenced by make_expanded_record_for_rec(), and plpgsql_exec_trigger().

◆ make_expanded_record_from_typeid()

ExpandedRecordHeader* make_expanded_record_from_typeid ( Oid  type_id,
int32  typmod,
MemoryContext  parentcontext 
)

Definition at line 69 of file expandedrecord.c.

71 {
73  int flags = 0;
74  TupleDesc tupdesc;
75  uint64 tupdesc_id;
76  MemoryContext objcxt;
77  char *chunk;
78 
79  if (type_id != RECORDOID)
80  {
81  /*
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.
84  */
85  TypeCacheEntry *typentry;
86 
87  typentry = lookup_type_cache(type_id,
90  if (typentry->typtype == TYPTYPE_DOMAIN)
91  {
92  flags |= ER_FLAG_IS_DOMAIN;
93  typentry = lookup_type_cache(typentry->domainBaseType,
95  }
96  if (typentry->tupDesc == NULL)
97  ereport(ERROR,
98  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
99  errmsg("type %s is not composite",
100  format_type_be(type_id))));
101  tupdesc = typentry->tupDesc;
102  tupdesc_id = typentry->tupDesc_identifier;
103  }
104  else
105  {
106  /*
107  * For RECORD types, get the tupdesc and identifier from typcache.
108  */
109  tupdesc = lookup_rowtype_tupdesc(type_id, typmod);
110  tupdesc_id = assign_record_type_identifier(type_id, typmod);
111  }
112 
113  /*
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.)
119  */
120  objcxt = AllocSetContextCreate(parentcontext,
121  "expanded record",
123 
124  /*
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.
129  */
130  erh = (ExpandedRecordHeader *)
132  + tupdesc->natts * (sizeof(Datum) + sizeof(bool)));
133 
134  /* Ensure all header fields are initialized to 0/null */
135  memset(erh, 0, sizeof(ExpandedRecordHeader));
136 
137  EOH_init_header(&erh->hdr, &ER_methods, objcxt);
138  erh->er_magic = ER_MAGIC;
139 
140  /* Set up dvalues/dnulls, with no valid contents as yet */
141  chunk = (char *) erh + MAXALIGN(sizeof(ExpandedRecordHeader));
142  erh->dvalues = (Datum *) chunk;
143  erh->dnulls = (bool *) (chunk + tupdesc->natts * sizeof(Datum));
144  erh->nfields = tupdesc->natts;
145 
146  /* Fill in composite-type identification info */
147  erh->er_decltypeid = type_id;
148  erh->er_typeid = tupdesc->tdtypeid;
149  erh->er_typmod = tupdesc->tdtypmod;
150  erh->er_tupdesc_id = tupdesc_id;
151 
152  erh->flags = flags;
153 
154  /*
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.
159  */
160  if (tupdesc->tdrefcount >= 0)
161  {
162  /* Register callback to release the refcount */
163  erh->er_mcb.func = ER_mc_callback;
164  erh->er_mcb.arg = erh;
166  &erh->er_mcb);
167 
168  /* And save the pointer */
169  erh->er_tupdesc = tupdesc;
170  tupdesc->tdrefcount++;
171 
172  /* If we called lookup_rowtype_tupdesc, release the pin it took */
173  if (type_id == RECORDOID)
174  ReleaseTupleDesc(tupdesc);
175  }
176  else
177  {
178  /*
179  * If it's not refcounted, just assume it will outlive the expanded
180  * object. (This can happen for shared record types, for instance.)
181  */
182  erh->er_tupdesc = tupdesc;
183  }
184 
185  /*
186  * We don't set ER_FLAG_DVALUES_VALID or ER_FLAG_FVALUE_VALID, so the
187  * record remains logically empty.
188  */
189 
190  return erh;
191 }
char typtype
Definition: typcache.h:43
Oid domainBaseType
Definition: typcache.h:114
#define TYPECACHE_DOMAIN_BASE_INFO
Definition: typcache.h:149

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, MemoryContextCallback::arg, assign_record_type_identifier(), chunk, ExpandedRecordHeader::dnulls, TypeCacheEntry::domainBaseType, ExpandedRecordHeader::dvalues, ExpandedObjectHeader::eoh_context, EOH_init_header(), ExpandedRecordHeader::er_decltypeid, ER_FLAG_IS_DOMAIN, ER_MAGIC, ExpandedRecordHeader::er_magic, ER_mc_callback(), ExpandedRecordHeader::er_mcb, ER_methods, ExpandedRecordHeader::er_tupdesc, ExpandedRecordHeader::er_tupdesc_id, ExpandedRecordHeader::er_typeid, ExpandedRecordHeader::er_typmod, ereport, errcode(), errmsg(), ERROR, ExpandedRecordHeader::flags, format_type_be(), MemoryContextCallback::func, ExpandedRecordHeader::hdr, lookup_rowtype_tupdesc(), lookup_type_cache(), MAXALIGN, MemoryContextAlloc(), MemoryContextRegisterResetCallback(), TupleDescData::natts, ExpandedRecordHeader::nfields, ReleaseTupleDesc, TupleDescData::tdrefcount, TupleDescData::tdtypeid, TupleDescData::tdtypmod, TypeCacheEntry::tupDesc, TypeCacheEntry::tupDesc_identifier, TYPECACHE_DOMAIN_BASE_INFO, TYPECACHE_TUPDESC, and TypeCacheEntry::typtype.

Referenced by exec_move_row_from_datum(), instantiate_empty_record_variable(), and make_expanded_record_for_rec().

Variable Documentation

◆ ER_methods

const ExpandedObjectMethods ER_methods
static
Initial value:
=
{
}
static void ER_flatten_into(ExpandedObjectHeader *eohptr, void *result, Size allocated_size)
static Size ER_get_flat_size(ExpandedObjectHeader *eohptr)

Definition at line 38 of file expandedrecord.c.

Referenced by build_dummy_expanded_header(), make_expanded_record_from_datum(), make_expanded_record_from_exprecord(), make_expanded_record_from_tupdesc(), and make_expanded_record_from_typeid().