PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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{
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:782
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)
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1181
uintptr_t Datum
Definition: postgres.h:69
MemoryContext eoh_context
MemoryContext er_short_term_cxt
ExpandedObjectHeader hdr
struct ExpandedRecordHeader * er_dummy_header

References 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 CompactAttribute *attr = TupleDescCompactAttr(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:347
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
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
int16 attlen
Definition: tupdesc.h:71
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:169
#define VARATT_IS_EXTERNAL(PTR)
Definition: varatt.h:289

References CompactAttribute::attbyval, CompactAttribute::attlen, 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, TupleDescCompactAttr(), 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
static bool HeapTupleHasExternal(const HeapTupleData *tuple)
Definition: htup_details.h:762
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
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 */
941}
ExpandedObjectHeader * DatumGetEOHP(Datum d)
Definition: expandeddatum.c:29
Datum make_expanded_record_from_datum(Datum recorddatum, MemoryContext parentcontext)
Assert(PointerIsAligned(start, uint64))
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 */
1008}
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1346

References 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{
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 &&
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);
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);
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:401
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
static void HeapTupleHeaderSetTypMod(HeapTupleHeaderData *tup, int32 typmod)
Definition: htup_details.h:522
static void HeapTupleHeaderSetTypeId(HeapTupleHeaderData *tup, Oid datum_typeid)
Definition: htup_details.h:510
static void HeapTupleHeaderSetDatumLength(HeapTupleHeaderData *tup, uint32 len)
Definition: htup_details.h:498
#define HeapTupleHeaderSetNatts(tup, natts)
Definition: htup_details.h:580
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{
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);
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 &&
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 CompactAttribute *attr = TupleDescCompactAttr(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:576
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:219
static int BITMAPLEN(int NATTS)
Definition: htup_details.h:594
int i
Definition: isn.c:77
const void size_t len
int32 tdtypmod
Definition: tupdesc.h:133
void assign_record_type_typmod(TupleDesc tupDesc)
Definition: typcache.c:2040

References Assert(), assign_record_type_typmod(), CompactAttribute::attbyval, CompactAttribute::attlen, 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, TupleDescCompactAttr(), 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:134
void FreeTupleDesc(TupleDesc tupdesc)
Definition: tupdesc.c:479

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

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 {
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:132
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:213
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:1117

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:248
int namestrcmp(Name name, const char *str)
Definition: name.c:247
FormData_pg_attribute
Definition: pg_attribute.h:184
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:154

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 CompactAttribute *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 = TupleDescCompactAttr(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 &&
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:1524
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
Definition: c.h:658

References Assert(), CompactAttribute::attbyval, CompactAttribute::attlen, 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_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(), TupleDescCompactAttr(), 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 CompactAttribute *attr = TupleDescCompactAttr(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)))
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}
bool attisdropped
Definition: tupdesc.h:77

References Assert(), CompactAttribute::attbyval, CompactAttribute::attisdropped, CompactAttribute::attlen, 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_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(), TupleDescCompactAttr(), 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 {
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:778
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
bool attbyval
Definition: pg_attribute.h:94

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

◆ 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 */
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;
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
static int32 HeapTupleHeaderGetTypMod(const HeapTupleHeaderData *tup)
Definition: htup_details.h:516
static uint32 HeapTupleHeaderGetDatumLength(const HeapTupleHeaderData *tup)
Definition: htup_details.h:492
#define HeapTupleHeaderHasExternal(tup)
Definition: htup_details.h:585
static Oid HeapTupleHeaderGetTypeId(const HeapTupleHeaderData *tup)
Definition: htup_details.h:504
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1215
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
#define InvalidOid
Definition: postgres_ext.h:35
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 */
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:234

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, MemoryContextCallback::arg, 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)
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 */
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:503
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(), 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)
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 */
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(), 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().