/* Set up dvalues/dnulls, with no valid contents as yet */
chunk = (char *) erh + MAXALIGN(sizeof(ExpandedRecordHeader));
erh->dvalues = (Datum *) chunk;
erh->dnulls = (bool *) (chunk + tupdesc->natts * sizeof(Datum));
erh->nfields = tupdesc->natts;
/* Fill in composite-type identification info */
erh->er_decltypeid = type_id;
erh->er_typeid = tupdesc->tdtypeid;
erh->er_typmod = tupdesc->tdtypmod;
erh->er_tupdesc_id = tupdesc_id;
erh->flags = flags;
/* *Ifwhatwegotfromthetypcacheisarefcountedtupdesc,weneedto *acquireourownrefcountonit.Wemanagetherefcountwithamemory *contextcallbackratherthanassumingthattheCurrentResourceOwneris *longer-livedthanthisexpandedobject.
*/ if (tupdesc->tdrefcount >= 0)
{ /* Register callback to release the refcount */
erh->er_mcb.func = ER_mc_callback;
erh->er_mcb.arg = erh;
MemoryContextRegisterResetCallback(erh->hdr.eoh_context,
&erh->er_mcb);
/* And save the pointer */
erh->er_tupdesc = tupdesc;
tupdesc->tdrefcount++;
/* If we called lookup_rowtype_tupdesc, release the pin it took */ if (type_id == RECORDOID)
ReleaseTupleDesc(tupdesc);
} else
{ /* *Ifit'snotrefcounted,justassumeitwilloutlivetheexpanded *object.(Thiscanhappenforsharedrecordtypes,forinstance.)
*/
erh->er_tupdesc = tupdesc;
}
/* Set up dvalues/dnulls, with no valid contents as yet */
chunk = (char *) erh + MAXALIGN(sizeof(ExpandedRecordHeader));
erh->dvalues = (Datum *) chunk;
erh->dnulls = (bool *) (chunk + tupdesc->natts * sizeof(Datum));
erh->nfields = tupdesc->natts;
/* Fill in composite-type identification info */
erh->er_decltypeid = olderh->er_decltypeid;
erh->er_typeid = olderh->er_typeid;
erh->er_typmod = olderh->er_typmod;
erh->er_tupdesc_id = olderh->er_tupdesc_id;
/* The only flag bit that transfers over is IS_DOMAIN */
erh->flags = olderh->flags & ER_FLAG_IS_DOMAIN;
/* *Copytupdescifneeded,butweprefertobumpitsrefcountifpossible. *Wemanagetherefcountwithamemorycontextcallbackratherthan *assumingthattheCurrentResourceOwnerislonger-livedthanthis *expandedobject.
*/ if (tupdesc->tdrefcount >= 0)
{ /* Register callback to release the refcount */
erh->er_mcb.func = ER_mc_callback;
erh->er_mcb.arg = erh;
MemoryContextRegisterResetCallback(erh->hdr.eoh_context,
&erh->er_mcb);
/* And save the pointer */
erh->er_tupdesc = tupdesc;
tupdesc->tdrefcount++;
} elseif (olderh->flags & ER_FLAG_TUPDESC_ALLOCED)
{ /* We need to make our own copy of the tupdesc */
oldcxt = MemoryContextSwitchTo(objcxt);
erh->er_tupdesc = CreateTupleDescCopy(tupdesc);
erh->flags |= ER_FLAG_TUPDESC_ALLOCED;
MemoryContextSwitchTo(oldcxt);
} else
{ /* *Assumethetupdescwilloutlivethisexpandedobject,justlike *we'reassumingitwilloutlivethesourceobject.
*/
erh->er_tupdesc = tupdesc;
}
/* We can now flush anything that detoasting might have leaked. */ if (expand_external)
MemoryContextReset(erh->er_short_term_cxt);
} else
newtuple = tuple;
/* Make copies of fields we're about to overwrite */
oldtuple = erh->fvalue;
oldfstartptr = erh->fstartptr;
oldfendptr = erh->fendptr;
/* Remember if we have any out-of-line field values */ if (HeapTupleHasExternal(newtuple))
newflags |= ER_FLAG_HAVE_EXTERNAL;
} else
{
erh->fvalue = NULL;
erh->fstartptr = erh->fendptr = NULL;
}
erh->flags = newflags;
/* Reset flat-size info; we don't bother to make it valid now */
erh->flat_size = 0;
/* *Now,releaseanystoragebelongingtooldfieldvalues.It'ssafeto *dothisbecauseER_FLAG_DVALUES_VALIDisnolongersetinerh->flags; *evenifwefailpartwaythrough,therecordisvalid,andatworst *we'vefailedtoreclaimsomespace.
*/ if (oldflags & ER_FLAG_DVALUES_ALLOCED)
{
TupleDesc tupdesc = erh->er_tupdesc; int i;
for (i = 0; i < erh->nfields; i++)
{ if (!erh->dnulls[i] &&
!(TupleDescAttr(tupdesc, i)->attbyval))
{ char *oldValue = (char *) DatumGetPointer(erh->dvalues[i]);
/* Set up expanded record header, initializing fields to 0/null */
erh = (ExpandedRecordHeader *)
MemoryContextAllocZero(objcxt, sizeof(ExpandedRecordHeader));
/* Fill in composite-type identification info */
erh->er_decltypeid = erh->er_typeid = HeapTupleHeaderGetTypeId(tuphdr);
erh->er_typmod = HeapTupleHeaderGetTypMod(tuphdr);
/* remember we have a flat representation */
erh->fvalue = newtuple;
erh->fstartptr = (char *) newtuple->t_data;
erh->fendptr = ((char *) newtuple->t_data) + newtuple->t_len;
erh->flags |= ER_FLAG_FVALUE_VALID;
/* Shouldn't need to set ER_FLAG_HAVE_EXTERNAL */
Assert(!HeapTupleHeaderHasExternal(tuphdr));
/* Easy if we have a valid flattened value without out-of-line fields */ if (erh->flags & ER_FLAG_FVALUE_VALID &&
!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
{
Assert(allocated_size == erh->fvalue->t_len);
memcpy(tuphdr, erh->fvalue->t_data, allocated_size); /* The original flattened value might not have datum header fields */
HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); return;
}
/* Else allocation should match previous get_flat_size result */
Assert(allocated_size == erh->flat_size);
/* We'll need the tuple descriptor */
tupdesc = expanded_record_get_tupdesc(erh);
/* We must ensure that any pad space is zero-filled */
memset(tuphdr, 0, allocated_size);
/* Set up header fields of composite Datum */
HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); /* We also make sure that t_ctid is invalid unless explicitly set */
ItemPointerSetInvalid(&(tuphdr->t_ctid));
/* And fill the data area from dvalues/dnulls */
heap_fill_tuple(tupdesc,
erh->dvalues,
erh->dnulls,
(char *) tuphdr + erh->hoff,
erh->data_len,
&tuphdr->t_infomask,
(erh->hasnull ? tuphdr->t_bits : NULL));
}
/* Easy if we already have it (but caller should have checked already) */ if (erh->er_tupdesc) return erh->er_tupdesc;
/* Lookup the composite type's tupdesc using the typcache */
tupdesc = lookup_rowtype_tupdesc(erh->er_typeid, erh->er_typmod);
/* *Ifit'sarefcountedtupdescratherthanastaticallyallocatedone,we *wanttomanagetherefcountwithamemorycontextcallbackratherthan *assumingthattheCurrentResourceOwnerislonger-livedthanthis *expandedobject.
*/ if (tupdesc->tdrefcount >= 0)
{ /* Register callback if we didn't already */ if (erh->er_mcb.arg == NULL)
{
erh->er_mcb.func = ER_mc_callback;
erh->er_mcb.arg = erh;
MemoryContextRegisterResetCallback(erh->hdr.eoh_context,
&erh->er_mcb);
}
/* Remember our own pointer */
erh->er_tupdesc = tupdesc;
tupdesc->tdrefcount++;
/* Release the pin lookup_rowtype_tupdesc acquired */
ReleaseTupleDesc(tupdesc);
} else
{ /* Just remember the pointer */
erh->er_tupdesc = tupdesc;
}
/* In either case, fetch the process-global ID for this tupdesc */
erh->er_tupdesc_id = assign_record_type_identifier(tupdesc->tdtypeid,
tupdesc->tdtypmod);
return tupdesc;
}
/* *GetaHeapTuplerepresentingthecurrentvalueoftheexpandedrecord * *Ifvalid,theoriginallystoredtupleisreturned,socallermustnot *scribbleonit.Otherwise,wereturnaHeapTuplecreatedinthecurrent *memorycontext.Ineithercase,noattempthasbeenmadetoinline *out-of-linetoastedvalues,sothetupleisn'tusableasacomposite *datum. * *ReturnsNULLifexpandedrecordisempty.
*/
HeapTuple
expanded_record_get_tuple(ExpandedRecordHeader *erh)
{ /* Easy case if we still have original tuple */ if (erh->flags & ER_FLAG_FVALUE_VALID) return erh->fvalue;
/* Else just build a tuple from datums */ if (erh->flags & ER_FLAG_DVALUES_VALID) return heap_form_tuple(erh->er_tupdesc, erh->dvalues, erh->dnulls);
/* Release our privately-managed tupdesc refcount, if any */ if (tupdesc)
{
erh->er_tupdesc = NULL; /* just for luck */ if (tupdesc->tdrefcount > 0)
{ if (--tupdesc->tdrefcount == 0)
FreeTupleDesc(tupdesc);
}
}
}
/* *DatumGetExpandedRecord:getawritableexpandedrecordfromaninputargument * *Caution:iftheinputisaread/writepointer,thisreturnstheinput *argument;socallersmustbesurethattheirchangesare"safe",thatis *theycannotleavetherecordinacorruptstate.
*/
ExpandedRecordHeader *
DatumGetExpandedRecord(Datum d)
{ /* If it's a writable expanded record already, just return it */ if (VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(d)))
{
ExpandedRecordHeader *erh = (ExpandedRecordHeader *) DatumGetEOHP(d);
Assert(erh->er_magic == ER_MAGIC); return erh;
}
/* Else expand the hard way */
d = make_expanded_record_from_datum(d, CurrentMemoryContext); return (ExpandedRecordHeader *) DatumGetEOHP(d);
}
/* *CreatetheDatum/isnullrepresentationofanexpandedrecordobject *ifwedidn'tdosoalready.Aftercallingthis,it'sOKtoreadthe *dvalues/dnullsarraysdirectly,ratherthangoingthroughget_field. * *Notethatiftheobjectiscurrentlyempty("null"),thiswillchange *ittorepresentarowofnulls.
*/ void
deconstruct_expanded_record(ExpandedRecordHeader *erh)
{
TupleDesc tupdesc;
Datum *dvalues; bool *dnulls; int nfields;
if (erh->flags & ER_FLAG_DVALUES_VALID) return; /* already valid, nothing to do */
/* We'll need the tuple descriptor */
tupdesc = expanded_record_get_tupdesc(erh);
if (erh->flags & ER_FLAG_FVALUE_VALID)
{ /* Deconstruct tuple */
heap_deform_tuple(erh->fvalue, tupdesc, dvalues, dnulls);
} else
{ /* If record was empty, instantiate it as a row of nulls */
memset(dvalues, 0, nfields * sizeof(Datum));
memset(dnulls, true, nfields * sizeof(bool));
}
/* Mark the dvalues as valid */
erh->flags |= ER_FLAG_DVALUES_VALID;
}
/* Before performing the assignment, see if result will satisfy domain */ if ((erh->flags & ER_FLAG_IS_DOMAIN) && check_constraints)
check_domain_for_new_field(erh, fnumber, newValue, isnull);
/* If we haven't yet deconstructed the tuple, do that */ if (!(erh->flags & ER_FLAG_DVALUES_VALID))
deconstruct_expanded_record(erh);
/* Tuple descriptor must be valid by now */
tupdesc = erh->er_tupdesc;
Assert(erh->nfields == tupdesc->natts);
/* Caller error if fnumber is system column or nonexistent column */ if (unlikely(fnumber <= 0 || fnumber > erh->nfields))
elog(ERROR, "cannot assign to field %d of expanded record", fnumber);
/* If requested, detoast any external value */ if (expand_external)
{ if (attr->attlen == -1 &&
VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
{ /* Detoasting should be done in short-lived context. */
oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
MemoryContextSwitchTo(oldcxt);
} else
expand_external = false; /* need not clean up below */
}
/* Copy value into record's context */
oldcxt = MemoryContextSwitchTo(erh->hdr.eoh_context);
newValue = datumCopy(newValue, false, attr->attlen);
MemoryContextSwitchTo(oldcxt);
/* We can now flush anything that detoasting might have leaked */ if (expand_external)
MemoryContextReset(erh->er_short_term_cxt);
/* Remember that we have field(s) that may need to be pfree'd */
erh->flags |= ER_FLAG_DVALUES_ALLOCED;
/* Flattened value will no longer represent record accurately */
erh->flags &= ~ER_FLAG_FVALUE_VALID; /* And we don't know the flattened size either */
erh->flat_size = 0;
/* Grab old field value for pfree'ing, if needed. */ if (!attr->attbyval && !dnulls[fnumber - 1])
oldValue = (char *) DatumGetPointer(dvalues[fnumber - 1]); else
oldValue = NULL;
/* And finally we can insert the new field. */
dvalues[fnumber - 1] = newValue;
dnulls[fnumber - 1] = isnull;
/* *Freeoldfieldifneeded;thiskeepsrepeatedfieldreplacementsfrom *bloatingtherecord'sstorage.Ifthepfreesomehowfails,itwon't *corrupttherecord. * *Ifwe'reupdatingadummyheader,wecan'triskpfree'ingtheold *value,becausemostlikelytheexpandedrecord'smainheaderstillhas *apointertoit.Thiswon'tresultinanysustainedmemoryleak,since *whateverwejustallocatedhereisintheshort-liveddomaincheck *context.
*/ if (oldValue && !(erh->flags & ER_FLAG_IS_DUMMY))
{ /* Don't try to pfree a part of the original flat record */ if (oldValue < erh->fstartptr || oldValue >= erh->fendptr)
pfree(oldValue);
}
}
/* Shouldn't ever be trying to assign new data to a dummy header */
Assert(!(erh->flags & ER_FLAG_IS_DUMMY));
/* If we haven't yet deconstructed the tuple, do that */ if (!(erh->flags & ER_FLAG_DVALUES_VALID))
deconstruct_expanded_record(erh);
/* Tuple descriptor must be valid by now */
tupdesc = erh->er_tupdesc;
Assert(erh->nfields == tupdesc->natts);
/* Flattened value will no longer represent record accurately */
erh->flags &= ~ER_FLAG_FVALUE_VALID; /* And we don't know the flattened size either */
erh->flat_size = 0;
if (!attr->attbyval)
{ /* *Copynewfieldvalueintorecord'scontext,anddealwith *detoasting,ifneeded.
*/ if (!isnull)
{ /* Is it an external toasted value? */ if (attr->attlen == -1 &&
VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
{ if (expand_external)
{ /* Detoast as requested while copying the value */
newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
} else
{ /* Just copy the value */
newValue = datumCopy(newValue, false, -1); /* If it's still external, remember that */ if (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
erh->flags |= ER_FLAG_HAVE_EXTERNAL;
}
} else
{ /* Not an external value, just copy it */
newValue = datumCopy(newValue, false, attr->attlen);
}
/* Remember that we have field(s) that need to be pfree'd */
erh->flags |= ER_FLAG_DVALUES_ALLOCED;
}
/* *Freeoldfieldvalue,ifany(notlikely,sincereallyweought *tobeinsertingintoanemptyrecord).
*/ if (unlikely(!dnulls[fnumber]))
{ char *oldValue;
oldValue = (char *) DatumGetPointer(dvalues[fnumber]); /* Don't try to pfree a part of the original flat record */ if (oldValue < erh->fstartptr || oldValue >= erh->fendptr)
pfree(oldValue);
}
}
/* And finally we can insert the new field. */
dvalues[fnumber] = newValue;
dnulls[fnumber] = isnull;
}
/* *Becausewedon'tguaranteeatomicityofset_fields(),wecanjustleave *checkingofdomainconstraintstooccurasthefinalstep;ifitthrows *anerror,toobad.
*/ if (erh->flags & ER_FLAG_IS_DOMAIN)
{ /* We run domain_check in a short-lived context to limit cruft */
MemoryContextSwitchTo(get_short_term_cxt(erh));
/* Copy over fvalue if we have it, so that system columns are available */
erh->fvalue = main_erh->fvalue;
erh->fstartptr = main_erh->fstartptr;
erh->fendptr = main_erh->fendptr;
}
/* *Precheckdomainconstraintsforaset_fieldoperation
*/ static pg_noinline void
check_domain_for_new_field(ExpandedRecordHeader *erh, int fnumber,
Datum newValue, bool isnull)
{
ExpandedRecordHeader *dummy_erh;
MemoryContext oldcxt;
/* Construct dummy header to contain proposed new field set */
build_dummy_expanded_header(erh);
dummy_erh = erh->er_dummy_header;
/* *Ifrecordisn'tempty,justdeconstructit(ifneeded)andcopyover *theexistingfieldvalues.Ifitisempty,justfillfieldswithnulls *manually---don'tcalldeconstruct_expanded_recordprematurely.
*/ if (!ExpandedRecordIsEmpty(erh))
{
deconstruct_expanded_record(erh);
memcpy(dummy_erh->dvalues, erh->dvalues,
dummy_erh->nfields * sizeof(Datum));
memcpy(dummy_erh->dnulls, erh->dnulls,
dummy_erh->nfields * sizeof(bool)); /* There might be some external values in there... */
dummy_erh->flags |= erh->flags & ER_FLAG_HAVE_EXTERNAL;
} else
{
memset(dummy_erh->dvalues, 0, dummy_erh->nfields * sizeof(Datum));
memset(dummy_erh->dnulls, true, dummy_erh->nfields * sizeof(bool));
}
/* Either way, we now have valid dvalues */
dummy_erh->flags |= ER_FLAG_DVALUES_VALID;
/* Caller error if fnumber is system column or nonexistent column */ if (unlikely(fnumber <= 0 || fnumber > dummy_erh->nfields))
elog(ERROR, "cannot assign to field %d of expanded record", fnumber);
/* Insert proposed new value into dummy field array */
dummy_erh->dvalues[fnumber - 1] = newValue;
dummy_erh->dnulls[fnumber - 1] = isnull;
/* If we're being told to set record to empty, just see if NULL is OK */ if (tuple == NULL)
{ /* We run domain_check in a short-lived context to limit cruft */
oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.