/*------------------------------------------------------------------------- * datumCopy * * Make a copy of a non-NULL datum. * * If the datatype is pass-by-reference, memory is obtained with palloc(). * * If the value is a reference to an expanded object, we flatten into memory * obtained with palloc(). We need to copy because one of the main uses of * this function is to copy a datum out of a transient memory context that's * about to be destroyed, and the expanded object is probably in a child * context that will also go away. Moreover, many callers assume that the * result is a single pfree-able chunk. *------------------------------------------------------------------------- */ Datum datumCopy(Datum value, bool typByVal, int typLen) { Datum res; if (typByVal) res = value; else if (typLen == -1) { /* It is a varlena datatype */ struct varlena *vl = (struct varlena *) DatumGetPointer(value); if (!vl) return PointerGetDatum(NULL); if (VARATT_IS_EXTERNAL_EXPANDED(vl)) { /* Flatten into the caller's memory context */ ExpandedObjectHeader *eoh = DatumGetEOHP(value); Size resultsize; char *resultptr; resultsize = EOH_get_flat_size(eoh); resultptr = (char *) palloc(resultsize); EOH_flatten_into(eoh, (void *) resultptr, resultsize); res = PointerGetDatum(resultptr); } else { /* Otherwise, just copy the varlena datum verbatim */ Size realSize; char *resultptr; realSize = (Size) VARSIZE_ANY(vl); resultptr = (char *) palloc(realSize); memcpy(resultptr, vl, realSize); res = PointerGetDatum(resultptr); } } else { /* Pass by reference, but not varlena, so not toasted */ Size realSize; char *resultptr; realSize = datumGetSize(value, typByVal, typLen); resultptr = (char *) palloc(realSize); memcpy(resultptr, DatumGetPointer(value), realSize); res = PointerGetDatum(resultptr); } return res; }
/*------------------------------------------------------------------------- * datumSerialize * * Serialize a possibly-NULL datum into caller-provided storage. * * Note: "expanded" objects are flattened so as to produce a self-contained * representation, but other sorts of toast pointers are transferred as-is. * This is because the intended use of this function is to pass the value * to another process within the same database server. The other process * could not access an "expanded" object within this process's memory, but * we assume it can dereference the same TOAST pointers this one can. * * The format is as follows: first, we write a 4-byte header word, which * is either the length of a pass-by-reference datum, -1 for a * pass-by-value datum, or -2 for a NULL. If the value is NULL, nothing * further is written. If it is pass-by-value, sizeof(Datum) bytes * follow. Otherwise, the number of bytes indicated by the header word * follow. The caller is responsible for ensuring that there is enough * storage to store the number of bytes that will be written; use * datumEstimateSpace() to find out how many will be needed. * *start_address is updated to point to the byte immediately following * those written. *------------------------------------------------------------------------- */ void datumSerialize(Datum value, bool isnull, bool typByVal, int typLen, char **start_address) { ExpandedObjectHeader *eoh = NULL; int header; /* Write header word. */ if (isnull) header = -2; else if (typByVal) header = -1; else if (typLen == -1 && VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(value))) { eoh = DatumGetEOHP(value); header = EOH_get_flat_size(eoh); } else header = datumGetSize(value, typByVal, typLen); memcpy(*start_address, &header, sizeof(int)); *start_address += sizeof(int); /* If not null, write payload bytes. */ if (!isnull) { if (typByVal) { memcpy(*start_address, &value, sizeof(Datum)); *start_address += sizeof(Datum); } else if (eoh) { EOH_flatten_into(eoh, (void *) *start_address, header); *start_address += header; } else { memcpy(*start_address, DatumGetPointer(value), header); *start_address += header; } } }
/* * heap_fill_tuple * Load data portion of a tuple from values/isnull arrays * * We also fill the null bitmap (if any) and set the infomask bits * that reflect the tuple's data contents. * * NOTE: it is now REQUIRED that the caller have pre-zeroed the data area. */ void heap_fill_tuple(TupleDesc tupleDesc, Datum *values, bool *isnull, char *data, Size data_size, uint16 *infomask, bits8 *bit) { bits8 *bitP; int bitmask; int i; int numberOfAttributes = tupleDesc->natts; Form_pg_attribute *att = tupleDesc->attrs; #ifdef USE_ASSERT_CHECKING char *start = data; #endif if (bit != NULL) { bitP = &bit[-1]; bitmask = HIGHBIT; } else { /* just to keep compiler quiet */ bitP = NULL; bitmask = 0; } *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTERNAL); for (i = 0; i < numberOfAttributes; i++) { Size data_length; if (bit != NULL) { if (bitmask != HIGHBIT) bitmask <<= 1; else { bitP += 1; *bitP = 0x0; bitmask = 1; } if (isnull[i]) { *infomask |= HEAP_HASNULL; continue; } *bitP |= bitmask; } /* * XXX we use the att_align macros on the pointer value itself, not on * an offset. This is a bit of a hack. */ if (att[i]->attbyval) { /* pass-by-value */ data = (char *) att_align_nominal(data, att[i]->attalign); store_att_byval(data, values[i], att[i]->attlen); data_length = att[i]->attlen; } else if (att[i]->attlen == -1) { /* varlena */ Pointer val = DatumGetPointer(values[i]); *infomask |= HEAP_HASVARWIDTH; if (VARATT_IS_EXTERNAL(val)) { if (VARATT_IS_EXTERNAL_EXPANDED(val)) { /* * we want to flatten the expanded value so that the * constructed tuple doesn't depend on it */ ExpandedObjectHeader *eoh = DatumGetEOHP(values[i]); data = (char *) att_align_nominal(data, att[i]->attalign); data_length = EOH_get_flat_size(eoh); EOH_flatten_into(eoh, data, data_length); } else { *infomask |= HEAP_HASEXTERNAL; /* no alignment, since it's short by definition */ data_length = VARSIZE_EXTERNAL(val); memcpy(data, val, data_length); } } else if (VARATT_IS_SHORT(val)) { /* no alignment for short varlenas */ data_length = VARSIZE_SHORT(val); memcpy(data, val, data_length); } else if (VARLENA_ATT_IS_PACKABLE(att[i]) && VARATT_CAN_MAKE_SHORT(val)) { /* convert to short varlena -- no alignment */ data_length = VARATT_CONVERTED_SHORT_SIZE(val); SET_VARSIZE_SHORT(data, data_length); memcpy(data + 1, VARDATA(val), data_length - 1); } else { /* full 4-byte header varlena */ data = (char *) att_align_nominal(data, att[i]->attalign); data_length = VARSIZE(val); memcpy(data, val, data_length); } } else if (att[i]->attlen == -2) { /* cstring ... never needs alignment */ *infomask |= HEAP_HASVARWIDTH; Assert(att[i]->attalign == 'c'); data_length = strlen(DatumGetCString(values[i])) + 1; memcpy(data, DatumGetPointer(values[i]), data_length); } else { /* fixed-length pass-by-reference */ data = (char *) att_align_nominal(data, att[i]->attalign); Assert(att[i]->attlen > 0); data_length = att[i]->attlen; memcpy(data, DatumGetPointer(values[i]), data_length); } data += data_length; } Assert((data - start) == data_size); }
/* * Per-attribute helper for heap_fill_tuple and other routines building tuples. * * Fill in either a data value or a bit in the null bitmask */ static inline void fill_val(Form_pg_attribute att, bits8 **bit, int *bitmask, char **dataP, uint16 *infomask, Datum datum, bool isnull) { Size data_length; char *data = *dataP; /* * If we're building a null bitmap, set the appropriate bit for the * current column value here. */ if (bit != NULL) { if (*bitmask != HIGHBIT) *bitmask <<= 1; else { *bit += 1; **bit = 0x0; *bitmask = 1; } if (isnull) { *infomask |= HEAP_HASNULL; return; } **bit |= *bitmask; } /* * XXX we use the att_align macros on the pointer value itself, not on an * offset. This is a bit of a hack. */ if (att->attbyval) { /* pass-by-value */ data = (char *) att_align_nominal(data, att->attalign); store_att_byval(data, datum, att->attlen); data_length = att->attlen; } else if (att->attlen == -1) { /* varlena */ Pointer val = DatumGetPointer(datum); *infomask |= HEAP_HASVARWIDTH; if (VARATT_IS_EXTERNAL(val)) { if (VARATT_IS_EXTERNAL_EXPANDED(val)) { /* * we want to flatten the expanded value so that the * constructed tuple doesn't depend on it */ ExpandedObjectHeader *eoh = DatumGetEOHP(datum); data = (char *) att_align_nominal(data, att->attalign); data_length = EOH_get_flat_size(eoh); EOH_flatten_into(eoh, data, data_length); } else { *infomask |= HEAP_HASEXTERNAL; /* no alignment, since it's short by definition */ data_length = VARSIZE_EXTERNAL(val); memcpy(data, val, data_length); } } else if (VARATT_IS_SHORT(val)) { /* no alignment for short varlenas */ data_length = VARSIZE_SHORT(val); memcpy(data, val, data_length); } else if (VARLENA_ATT_IS_PACKABLE(att) && VARATT_CAN_MAKE_SHORT(val)) { /* convert to short varlena -- no alignment */ data_length = VARATT_CONVERTED_SHORT_SIZE(val); SET_VARSIZE_SHORT(data, data_length); memcpy(data + 1, VARDATA(val), data_length - 1); } else { /* full 4-byte header varlena */ data = (char *) att_align_nominal(data, att->attalign); data_length = VARSIZE(val); memcpy(data, val, data_length); } } else if (att->attlen == -2) { /* cstring ... never needs alignment */ *infomask |= HEAP_HASVARWIDTH; Assert(att->attalign == 'c'); data_length = strlen(DatumGetCString(datum)) + 1; memcpy(data, DatumGetPointer(datum), data_length); } else { /* fixed-length pass-by-reference */ data = (char *) att_align_nominal(data, att->attalign); Assert(att->attlen > 0); data_length = att->attlen; memcpy(data, DatumGetPointer(datum), data_length); } data += data_length; *dataP = data; }