/* * SerializeSingleDatum serializes the given datum value and appends it to the * provided string info buffer. */ static void SerializeSingleDatum(StringInfo datumBuffer, Datum datum, bool datumTypeByValue, int datumTypeLength, char datumTypeAlign) { uint32 datumLength = att_addlength_datum(0, datumTypeLength, datum); uint32 datumLengthAligned = att_align_nominal(datumLength, datumTypeAlign); char *currentDatumDataPointer = NULL; enlargeStringInfo(datumBuffer, datumLengthAligned); currentDatumDataPointer = datumBuffer->data + datumBuffer->len; memset(currentDatumDataPointer, 0, datumLengthAligned); if (datumTypeLength > 0) { if (datumTypeByValue) { store_att_byval(currentDatumDataPointer, datum, datumTypeLength); } else { memcpy(currentDatumDataPointer, DatumGetPointer(datum), datumTypeLength); } } else { Assert(!datumTypeByValue); memcpy(currentDatumDataPointer, DatumGetPointer(datum), datumLength); } datumBuffer->len += datumLengthAligned; }
static jvalue _Array_coerceDatum(Type self, Datum arg) { jvalue result; jsize idx; Type elemType = Type_getElementType(self); int16 elemLength = Type_getLength(elemType); char elemAlign = Type_getAlign(elemType); bool elemByValue = Type_isByValue(elemType); ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jobjectArray objArray = JNI_newObjectArray(nElems, Type_getJavaClass(elemType), 0); const char* values = ARR_DATA_PTR(v); bits8* nullBitMap = ARR_NULLBITMAP(v); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) JNI_setObjectArrayElement(objArray, idx, 0); else { Datum value = fetch_att(values, elemByValue, elemLength); jvalue obj = Type_coerceDatum(elemType, value); JNI_setObjectArrayElement(objArray, idx, obj.l); JNI_deleteLocalRef(obj.l); values = att_addlength_datum(values, elemLength, PointerGetDatum(values)); values = (char*)att_align_nominal(values, elemAlign); } } result.l = (jobject)objArray; return result; }
void dump_bvf1_inputs(ArrayType *broker_list_p, text *sector_name_p) { int ndim, nitems; int *dim; int16 typlen; bool typbyval; char typalign; int i; char *broker_list; ndim = ARR_NDIM(broker_list_p); dim = ARR_DIMS(broker_list_p); nitems = ArrayGetNItems(ndim, dim); get_typlenbyvalalign(ARR_ELEMTYPE(broker_list_p), &typlen, &typbyval, &typalign); broker_list = ARR_DATA_PTR(broker_list_p); elog(NOTICE, "BVF1: INPUTS START"); for (i = 0; i < nitems; i++) { elog(NOTICE, "BVF1: broker_list[%d] %s", i, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(broker_list)))); broker_list = att_addlength_pointer(broker_list, typlen, broker_list); broker_list = (char *) att_align_nominal(broker_list, typalign); } elog(NOTICE, "BVF1: sector_name %s", DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(sector_name_p)))); elog(NOTICE, "BVF1: INPUTS END"); }
/* * get_flat_size method for expanded arrays */ static Size EA_get_flat_size(ExpandedObjectHeader *eohptr) { ExpandedArrayHeader *eah = (ExpandedArrayHeader *) eohptr; int nelems; int ndims; Datum *dvalues; bool *dnulls; Size nbytes; int i; Assert(eah->ea_magic == EA_MAGIC); /* Easy if we have a valid flattened value */ if (eah->fvalue) return ARR_SIZE(eah->fvalue); /* If we have a cached size value, believe that */ if (eah->flat_size) return eah->flat_size; /* * Compute space needed by examining dvalues/dnulls. Note that the result * array will have a nulls bitmap if dnulls isn't NULL, even if the array * doesn't actually contain any nulls now. */ nelems = eah->nelems; ndims = eah->ndims; Assert(nelems == ArrayGetNItems(ndims, eah->dims)); dvalues = eah->dvalues; dnulls = eah->dnulls; nbytes = 0; for (i = 0; i < nelems; i++) { if (dnulls && dnulls[i]) continue; nbytes = att_addlength_datum(nbytes, eah->typlen, dvalues[i]); nbytes = att_align_nominal(nbytes, eah->typalign); /* check for overflow of total request */ if (!AllocSizeIsValid(nbytes)) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("array size exceeds the maximum allowed (%d)", (int) MaxAllocSize))); } if (dnulls) nbytes += ARR_OVERHEAD_WITHNULLS(ndims, nelems); else nbytes += ARR_OVERHEAD_NONULLS(ndims); /* cache for next time */ eah->flat_size = nbytes; return nbytes; }
/* * Sets the binding length according to the following binding's alignment. * Adds the aligned length into the array holding the space saved from null attributes. * Returns true if the binding length is aligned to the following binding's alignment. */ static bool add_null_save_aligned(MemTupleAttrBinding *bind, short *null_save_aligned, int i, char next_attr_align) { Assert(bind); Assert(bind->len > 0); Assert(null_save_aligned); Assert(i >= 0); bind->len_aligned = att_align_nominal(bind->len, next_attr_align); add_null_save(null_save_aligned, i, bind->len_aligned); return (bind->len == bind->len_aligned); }
/* * heap_compute_data_size * Determine size of the data area of a tuple to be constructed */ Size heap_compute_data_size(TupleDesc tupleDesc, Datum *values, bool *isnull) { Size data_length = 0; int i; int numberOfAttributes = tupleDesc->natts; Form_pg_attribute *att = tupleDesc->attrs; for (i = 0; i < numberOfAttributes; i++) { Datum val; Form_pg_attribute atti; if (isnull[i]) continue; val = values[i]; atti = att[i]; if (ATT_IS_PACKABLE(atti) && VARATT_CAN_MAKE_SHORT(DatumGetPointer(val))) { /* * we're anticipating converting to a short varlena header, so * adjust length and don't count any alignment */ data_length += VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val)); } else if (atti->attlen == -1 && VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(val))) { /* * we want to flatten the expanded value so that the constructed * tuple doesn't depend on it */ data_length = att_align_nominal(data_length, atti->attalign); data_length += EOH_get_flat_size(DatumGetEOHP(val)); } else { data_length = att_align_datum(data_length, atti->attalign, atti->attlen, val); data_length = att_addlength_datum(data_length, atti->attlen, val); } } return data_length; }
/* * Check to see whether the table needs a TOAST table. It does only if * (1) there are any toastable attributes, and (2) the maximum length * of a tuple could exceed TOAST_TUPLE_THRESHOLD. (We don't want to * create a toast table for something like "f1 varchar(20)".) * No need to create a TOAST table for partitioned tables. */ static bool needs_toast_table(Relation rel) { int32 data_length = 0; bool maxlength_unknown = false; bool has_toastable_attrs = false; TupleDesc tupdesc; int32 tuple_length; int i; if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) return false; tupdesc = rel->rd_att; for (i = 0; i < tupdesc->natts; i++) { Form_pg_attribute att = TupleDescAttr(tupdesc, i); if (att->attisdropped) continue; data_length = att_align_nominal(data_length, att->attalign); if (att->attlen > 0) { /* Fixed-length types are never toastable */ data_length += att->attlen; } else { int32 maxlen = type_maximum_size(att->atttypid, att->atttypmod); if (maxlen < 0) maxlength_unknown = true; else data_length += maxlen; if (att->attstorage != 'p') has_toastable_attrs = true; } } if (!has_toastable_attrs) return false; /* nothing to toast? */ if (maxlength_unknown) return true; /* any unlimited-length attrs? */ tuple_length = MAXALIGN(SizeofHeapTupleHeader + BITMAPLEN(tupdesc->natts)) + MAXALIGN(data_length); return (tuple_length > TOAST_TUPLE_THRESHOLD); }
/* * Check to see whether the table needs a TOAST table. It does only if * (1) there are any toastable attributes, and (2) the maximum length * of a tuple could exceed TOAST_TUPLE_THRESHOLD. (We don't want to * create a toast table for something like "f1 varchar(20)".) */ bool RelationNeedsToastTable(Relation rel) { int32 data_length = 0; bool maxlength_unknown = false; bool has_toastable_attrs = false; TupleDesc tupdesc; Form_pg_attribute *att; int32 tuple_length; int i; if(RelationIsExternal(rel)) return false; tupdesc = rel->rd_att; att = tupdesc->attrs; for (i = 0; i < tupdesc->natts; i++) { if (att[i]->attisdropped) continue; data_length = att_align_nominal(data_length, att[i]->attalign); if (att[i]->attlen > 0) { /* Fixed-length types are never toastable */ data_length += att[i]->attlen; } else { int32 maxlen = type_maximum_size(att[i]->atttypid, att[i]->atttypmod); if (maxlen < 0) maxlength_unknown = true; else data_length += maxlen; if (att[i]->attstorage != 'p') has_toastable_attrs = true; } } if (!has_toastable_attrs) return false; /* nothing to toast? */ if (maxlength_unknown) return true; /* any unlimited-length attrs? */ tuple_length = MAXALIGN(offsetof(HeapTupleHeaderData, t_bits) + BITMAPLEN(tupdesc->natts)) + MAXALIGN(data_length); return (tuple_length > TOAST_TUPLE_THRESHOLD); }
/* * SerializeDatumArray serializes the non-null elements of the given datum array * into a string info buffer, and then returns this buffer. */ static StringInfo SerializeDatumArray(Datum *datumArray, bool *existsArray, uint32 datumCount, bool datumTypeByValue, int datumTypeLength, char datumTypeAlign) { StringInfo datumBuffer = makeStringInfo(); uint32 datumIndex = 0; for (datumIndex = 0; datumIndex < datumCount; datumIndex++) { Datum datum = datumArray[datumIndex]; uint32 datumLength = 0; uint32 datumLengthAligned = 0; char *currentDatumDataPointer = NULL; if (!existsArray[datumIndex]) { continue; } datumLength = att_addlength_datum(0, datumTypeLength, datum); datumLengthAligned = att_align_nominal(datumLength, datumTypeAlign); enlargeStringInfo(datumBuffer, datumBuffer->len + datumLengthAligned); currentDatumDataPointer = datumBuffer->data + datumBuffer->len; memset(currentDatumDataPointer, 0, datumLengthAligned); if (datumTypeLength > 0) { if (datumTypeByValue) { store_att_byval(currentDatumDataPointer, datum, datumTypeLength); } else { memcpy(currentDatumDataPointer, DatumGetPointer(datum), datumTypeLength); } } else { Assert(!datumTypeByValue); memcpy(currentDatumDataPointer, DatumGetPointer(datum), datumLength); } datumBuffer->len += datumLengthAligned; } return datumBuffer; }
/* * brin_deconstruct_tuple * Guts of attribute extraction from an on-disk BRIN tuple. * * Its arguments are: * brdesc BRIN descriptor for the stored tuple * tp pointer to the tuple data area * nullbits pointer to the tuple nulls bitmask * nulls "has nulls" bit in tuple infomask * values output values, array of size brdesc->bd_totalstored * allnulls output "allnulls", size brdesc->bd_tupdesc->natts * hasnulls output "hasnulls", size brdesc->bd_tupdesc->natts * * Output arrays must have been allocated by caller. */ static inline void brin_deconstruct_tuple(BrinDesc *brdesc, char *tp, bits8 *nullbits, bool nulls, Datum *values, bool *allnulls, bool *hasnulls) { int attnum; int stored; TupleDesc diskdsc; long off; /* * First iterate to natts to obtain both null flags for each attribute. * Note that we reverse the sense of the att_isnull test, because we store * 1 for a null value (rather than a 1 for a not null value as is the * att_isnull convention used elsewhere.) See brin_form_tuple. */ for (attnum = 0; attnum < brdesc->bd_tupdesc->natts; attnum++) { /* * the "all nulls" bit means that all values in the page range for * this column are nulls. Therefore there are no values in the tuple * data area. */ allnulls[attnum] = nulls && !att_isnull(attnum, nullbits); /* * the "has nulls" bit means that some tuples have nulls, but others * have not-null values. Therefore we know the tuple contains data * for this column. * * The hasnulls bits follow the allnulls bits in the same bitmask. */ hasnulls[attnum] = nulls && !att_isnull(brdesc->bd_tupdesc->natts + attnum, nullbits); } /* * Iterate to obtain each attribute's stored values. Note that since we * may reuse attribute entries for more than one column, we cannot cache * offsets here. */ diskdsc = brtuple_disk_tupdesc(brdesc); stored = 0; off = 0; for (attnum = 0; attnum < brdesc->bd_tupdesc->natts; attnum++) { int datumno; if (allnulls[attnum]) { stored += brdesc->bd_info[attnum]->oi_nstored; continue; } for (datumno = 0; datumno < brdesc->bd_info[attnum]->oi_nstored; datumno++) { Form_pg_attribute thisatt = TupleDescAttr(diskdsc, stored); if (thisatt->attlen == -1) { off = att_align_pointer(off, thisatt->attalign, -1, tp + off); } else { /* not varlena, so safe to use att_align_nominal */ off = att_align_nominal(off, thisatt->attalign); } values[stored++] = fetchatt(thisatt, tp + off); off = att_addlength_pointer(off, thisatt->attlen, tp + off); } } }
/* ---------------- * nocache_index_getattr * * This gets called from index_getattr() macro, and only in cases * where we can't use cacheoffset and the value is not null. * * This caches attribute offsets in the attribute descriptor. * * An alternative way to speed things up would be to cache offsets * with the tuple, but that seems more difficult unless you take * the storage hit of actually putting those offsets into the * tuple you send to disk. Yuck. * * This scheme will be slightly slower than that, but should * perform well for queries which hit large #'s of tuples. After * you cache the offsets once, examining all the other tuples using * the same attribute descriptor will go much quicker. -cim 5/4/91 * ---------------- */ Datum nocache_index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc) { Form_pg_attribute *att = tupleDesc->attrs; char *tp; /* ptr to data part of tuple */ bits8 *bp = NULL; /* ptr to null bitmap in tuple */ bool slow = false; /* do we have to walk attrs? */ int data_off; /* tuple data offset */ int off; /* current offset within data */ /* ---------------- * Three cases: * * 1: No nulls and no variable-width attributes. * 2: Has a null or a var-width AFTER att. * 3: Has nulls or var-widths BEFORE att. * ---------------- */ data_off = IndexInfoFindDataOffset(tup->t_info); attnum--; if (IndexTupleHasNulls(tup)) { /* * there's a null somewhere in the tuple * * check to see if desired att is null */ /* XXX "knows" t_bits are just after fixed tuple header! */ bp = (bits8 *) ((char *) tup + sizeof(IndexTupleData)); /* * Now check to see if any preceding bits are null... */ { int byte = attnum >> 3; int finalbit = attnum & 0x07; /* check for nulls "before" final bit of last byte */ if ((~bp[byte]) & ((1 << finalbit) - 1)) slow = true; else { /* check for nulls in any "earlier" bytes */ int i; for (i = 0; i < byte; i++) { if (bp[i] != 0xFF) { slow = true; break; } } } } } tp = (char *) tup + data_off; if (!slow) { /* * If we get here, there are no nulls up to and including the target * attribute. If we have a cached offset, we can use it. */ if (att[attnum]->attcacheoff >= 0) { return fetchatt(att[attnum], tp + att[attnum]->attcacheoff); } /* * Otherwise, check for non-fixed-length attrs up to and including * target. If there aren't any, it's safe to cheaply initialize the * cached offsets for these attrs. */ if (IndexTupleHasVarwidths(tup)) { int j; for (j = 0; j <= attnum; j++) { if (att[j]->attlen <= 0) { slow = true; break; } } } } if (!slow) { int natts = tupleDesc->natts; int j = 1; /* * If we get here, we have a tuple with no nulls or var-widths up to * and including the target attribute, so we can use the cached offset * ... only we don't have it yet, or we'd not have got here. Since * it's cheap to compute offsets for fixed-width columns, we take the * opportunity to initialize the cached offsets for *all* the leading * fixed-width columns, in hope of avoiding future visits to this * routine. */ att[0]->attcacheoff = 0; /* we might have set some offsets in the slow path previously */ while (j < natts && att[j]->attcacheoff > 0) j++; off = att[j - 1]->attcacheoff + att[j - 1]->attlen; for (; j < natts; j++) { if (att[j]->attlen <= 0) break; off = att_align_nominal(off, att[j]->attalign); att[j]->attcacheoff = off; off += att[j]->attlen; } Assert(j > attnum); off = att[attnum]->attcacheoff; } else { bool usecache = true; int i; /* * Now we know that we have to walk the tuple CAREFULLY. But we still * might be able to cache some offsets for next time. * * Note - This loop is a little tricky. For each non-null attribute, * we have to first account for alignment padding before the attr, * then advance over the attr based on its length. Nulls have no * storage and no alignment padding either. We can use/set * attcacheoff until we reach either a null or a var-width attribute. */ off = 0; for (i = 0;; i++) /* loop exit is at "break" */ { if (IndexTupleHasNulls(tup) && att_isnull(i, bp)) { usecache = false; continue; /* this cannot be the target att */ } /* If we know the next offset, we can skip the rest */ if (usecache && att[i]->attcacheoff >= 0) off = att[i]->attcacheoff; else if (att[i]->attlen == -1) { /* * We can only cache the offset for a varlena attribute if the * offset is already suitably aligned, so that there would be * no pad bytes in any case: then the offset will be valid for * either an aligned or unaligned value. */ if (usecache && off == att_align_nominal(off, att[i]->attalign)) att[i]->attcacheoff = off; else { off = att_align_pointer(off, att[i]->attalign, -1, tp + off); usecache = false; } } else { /* not varlena, so safe to use att_align_nominal */ off = att_align_nominal(off, att[i]->attalign); if (usecache) att[i]->attcacheoff = off; } if (i == attnum) break; off = att_addlength_pointer(off, att[i]->attlen, tp + off); if (usecache && att[i]->attlen <= 0) usecache = false; } } return fetchatt(att[attnum], tp + off); }
/* * Convert an index tuple into Datum/isnull arrays. * * The caller must allocate sufficient storage for the output arrays. * (INDEX_MAX_KEYS entries should be enough.) * * This is nearly the same as heap_deform_tuple(), but for IndexTuples. * One difference is that the tuple should never have any missing columns. */ void index_deform_tuple(IndexTuple tup, TupleDesc tupleDescriptor, Datum *values, bool *isnull) { int hasnulls = IndexTupleHasNulls(tup); int natts = tupleDescriptor->natts; /* number of atts to extract */ int attnum; char *tp; /* ptr to tuple data */ int off; /* offset in tuple data */ bits8 *bp; /* ptr to null bitmap in tuple */ bool slow = false; /* can we use/set attcacheoff? */ /* Assert to protect callers who allocate fixed-size arrays */ Assert(natts <= INDEX_MAX_KEYS); /* XXX "knows" t_bits are just after fixed tuple header! */ bp = (bits8 *) ((char *) tup + sizeof(IndexTupleData)); tp = (char *) tup + IndexInfoFindDataOffset(tup->t_info); off = 0; for (attnum = 0; attnum < natts; attnum++) { Form_pg_attribute thisatt = TupleDescAttr(tupleDescriptor, attnum); if (hasnulls && att_isnull(attnum, bp)) { values[attnum] = (Datum) 0; isnull[attnum] = true; slow = true; /* can't use attcacheoff anymore */ continue; } isnull[attnum] = false; if (!slow && thisatt->attcacheoff >= 0) off = thisatt->attcacheoff; else if (thisatt->attlen == -1) { /* * We can only cache the offset for a varlena attribute if the * offset is already suitably aligned, so that there would be no * pad bytes in any case: then the offset will be valid for either * an aligned or unaligned value. */ if (!slow && off == att_align_nominal(off, thisatt->attalign)) thisatt->attcacheoff = off; else { off = att_align_pointer(off, thisatt->attalign, -1, tp + off); slow = true; } } else { /* not varlena, so safe to use att_align_nominal */ off = att_align_nominal(off, thisatt->attalign); if (!slow) thisatt->attcacheoff = off; } values[attnum] = fetchatt(thisatt, tp + off); off = att_addlength_pointer(off, thisatt->attlen, tp + off); if (thisatt->attlen <= 0) slow = true; /* can't use attcacheoff anymore */ } }
/* * tuple_data_split_internal * * Split raw tuple data taken directly from a page into an array of bytea * elements. This routine does a lookup on NULL values and creates array * elements accordingly. This is a reimplementation of nocachegetattr() * in heaptuple.c simplified for educational purposes. */ static Datum tuple_data_split_internal(Oid relid, char *tupdata, uint16 tupdata_len, uint16 t_infomask, uint16 t_infomask2, bits8 *t_bits, bool do_detoast) { ArrayBuildState *raw_attrs; int nattrs; int i; int off = 0; Relation rel; TupleDesc tupdesc; /* Get tuple descriptor from relation OID */ rel = relation_open(relid, AccessShareLock); tupdesc = RelationGetDescr(rel); raw_attrs = initArrayResult(BYTEAOID, CurrentMemoryContext, false); nattrs = tupdesc->natts; if (nattrs < (t_infomask2 & HEAP_NATTS_MASK)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("number of attributes in tuple header is greater than number of attributes in tuple descriptor"))); for (i = 0; i < nattrs; i++) { Form_pg_attribute attr; bool is_null; bytea *attr_data = NULL; attr = TupleDescAttr(tupdesc, i); /* * Tuple header can specify less attributes than tuple descriptor as * ALTER TABLE ADD COLUMN without DEFAULT keyword does not actually * change tuples in pages, so attributes with numbers greater than * (t_infomask2 & HEAP_NATTS_MASK) should be treated as NULL. */ if (i >= (t_infomask2 & HEAP_NATTS_MASK)) is_null = true; else is_null = (t_infomask & HEAP_HASNULL) && att_isnull(i, t_bits); if (!is_null) { int len; if (attr->attlen == -1) { off = att_align_pointer(off, attr->attalign, -1, tupdata + off); /* * As VARSIZE_ANY throws an exception if it can't properly * detect the type of external storage in macros VARTAG_SIZE, * this check is repeated to have a nicer error handling. */ if (VARATT_IS_EXTERNAL(tupdata + off) && !VARATT_IS_EXTERNAL_ONDISK(tupdata + off) && !VARATT_IS_EXTERNAL_INDIRECT(tupdata + off)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("first byte of varlena attribute is incorrect for attribute %d", i))); len = VARSIZE_ANY(tupdata + off); } else { off = att_align_nominal(off, attr->attalign); len = attr->attlen; } if (tupdata_len < off + len) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("unexpected end of tuple data"))); if (attr->attlen == -1 && do_detoast) attr_data = DatumGetByteaPCopy(tupdata + off); else { attr_data = (bytea *) palloc(len + VARHDRSZ); SET_VARSIZE(attr_data, len + VARHDRSZ); memcpy(VARDATA(attr_data), tupdata + off, len); } off = att_addlength_pointer(off, attr->attlen, tupdata + off); } raw_attrs = accumArrayResult(raw_attrs, PointerGetDatum(attr_data), is_null, BYTEAOID, CurrentMemoryContext); if (attr_data) pfree(attr_data); } if (tupdata_len != off) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("end of tuple reached without looking at all its data"))); relation_close(rel, AccessShareLock); return makeArrayResult(raw_attrs, CurrentMemoryContext); }
static void convert_heaptuple(HeapTupleHeader htup) { /* HEAP_HASEXTENDED and HEAP_HASCOMPRESSED flags were removed. Clear them out */ htup->t_infomask &= ~(VERSION4_HEAP_HASEXTENDED | VERSION4_HEAP_HASCOMPRESSED); /* HEAP_HASOID flag was moved */ if (htup->t_infomask & VERSION4_HEAP_HASOID) { htup->t_infomask &= ~(VERSION4_HEAP_HASOID); htup->t_infomask |= HEAP_HASOID; } /* Any numeric columns? */ if (curr_hasnumerics) { /* This is like heap_deform_tuple() */ bool hasnulls = (htup->t_infomask & HEAP_HASNULL) != 0; int attnum; char *tp; long off; bits8 *bp = htup->t_bits; /* ptr to null bitmap in tuple */ tp = (char *) htup + htup->t_hoff; off = 0; for (attnum = 0; attnum < curr_natts; attnum++) { AttInfo *thisatt = &curr_atts[attnum]; if (hasnulls && att_isnull(attnum, bp)) continue; if (thisatt->attlen == -1) { off = att_align_pointer(off, thisatt->attalign, -1, tp + off); } else { /* not varlena, so safe to use att_align_nominal */ off = att_align_nominal(off, thisatt->attalign); } if (thisatt->is_numeric) { /* * Before PostgreSQL 8.3, the n_weight and n_sign_dscale fields * were the other way 'round. Swap them. * * FIXME: need to handle toasted datums here. */ Datum datum = PointerGetDatum(tp + off); if (VARATT_IS_COMPRESSED(datum)) pg_log(PG_FATAL, "converting compressed numeric datums is not implemented\n"); else if (VARATT_IS_EXTERNAL(datum)) pg_log(PG_FATAL, "converting toasted numeric datums is not implemented\n"); else { char *numericdata = VARDATA_ANY(DatumGetPointer(datum)); int sz = VARSIZE_ANY_EXHDR(DatumGetPointer(datum)); uint16 tmp; if (sz < 4) pg_log(PG_FATAL, "unexpected size for numeric datum: %d\n", sz); memcpy(&tmp, &numericdata[0], 2); memcpy(&numericdata[0], &numericdata[2], 2); memcpy(&numericdata[2], &tmp, 2); } } off = att_addlength_pointer(off, thisatt->attlen, tp + off); } } }
void dump_mff1_inputs(ArrayType *price_quote_p, char *status_submitted_p, ArrayType *symbol_p, ArrayType *trade_qty, char *type_limit_buy_p, char *type_limit_sell_p, char *type_stop_loss_p) { int i; int nitems_pq; Datum *transdatums_pq; char *p_s; int16 typlen_s; bool typbyval_s; char typalign_s; int16 typlen_tq; bool typbyval_tq; char typalign_tq; int *p_tq; deconstruct_array(price_quote_p, NUMERICOID, -1, false, 'i', &transdatums_pq, NULL, &nitems_pq); get_typlenbyvalalign(ARR_ELEMTYPE(trade_qty), &typlen_tq, &typbyval_tq, &typalign_tq); p_tq = (int *) ARR_DATA_PTR(trade_qty); get_typlenbyvalalign(ARR_ELEMTYPE(symbol_p), &typlen_s, &typbyval_s, &typalign_s); p_s = ARR_DATA_PTR(symbol_p); elog(NOTICE, "MFF1: INPUTS START"); for (i = 0; i < nitems_pq; i++) { elog(NOTICE, "MFF1: price_quote[%d] %s", i, DatumGetCString(DirectFunctionCall1(numeric_out, transdatums_pq[i]))); } elog(NOTICE, "MFF1: status_submitted '%s'", DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(status_submitted_p)))); for (i = 0; i < nitems_pq; i++) { elog(NOTICE, "MFF1: symbol[%d] '%s'", i, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(p_s)))); } for (i = 0; i < nitems_pq; i++) { elog(NOTICE, "MFF1: trade_qty[%d] %d", i, p_tq[i]); p_s = att_addlength_pointer(p_s, typlen_s, p_s); p_s = (char *) att_align_nominal(p_s, typalign_s); } elog(NOTICE, "MFF1: type_limit_buy '%s'", DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(type_limit_buy_p)))); elog(NOTICE, "MFF1: type_limit_sell '%s'", DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(type_limit_sell_p)))); elog(NOTICE, "MFF1: type_stop_loss '%s'", DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(type_stop_loss_p)))); elog(NOTICE, "MFF1: INPUTS END"); }
/* Clause 3.3.1.3 */ Datum MarketFeedFrame1(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; AttInMetadata *attinmeta; int call_cntr; int max_calls; int i, j, n; int num_updated = 0; int rows_sent; int send_len = 0; int count = 0; int nitems_pq; int *p_tq; char *p_s; char **values = NULL; /* Stuff done only on the first call of the function. */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; ArrayType *price_quote_p = PG_GETARG_ARRAYTYPE_P(0); char *status_submitted_p = (char *) PG_GETARG_TEXT_P(1); ArrayType *symbol_p = PG_GETARG_ARRAYTYPE_P(2); ArrayType *trade_qty = PG_GETARG_ARRAYTYPE_P(3); char *type_limit_buy_p = (char *) PG_GETARG_TEXT_P(4); char *type_limit_sell_p = (char *) PG_GETARG_TEXT_P(5); char *type_stop_loss_p = (char *) PG_GETARG_TEXT_P(6); enum mff1 { i_num_updated=0, i_send_len, i_symbol, i_trade_id, i_price_quote, i_trade_qty, i_trade_type }; Datum *transdatums_pq; int16 typlen_s; bool typbyval_s; char typalign_s; int16 typlen_tq; bool typbyval_tq; char typalign_tq; int ret; TupleDesc tupdesc; SPITupleTable *tuptable = NULL; HeapTuple tuple = NULL; #ifdef DEBUG char sql[2048]; #endif Datum args[7]; char nulls[] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ' }; char price_quote[S_PRICE_T_LEN + 1]; char status_submitted[ST_ID_LEN + 1]; char symbol[S_SYMB_LEN + 1]; char type_limit_buy[TT_ID_LEN + 1]; char type_limit_sell[TT_ID_LEN + 1]; char type_stop_loss[TT_ID_LEN + 1]; char *trade_id; char *req_price_quote; char *req_trade_type; char *req_trade_qty; /* * Prepare a values array for building the returned tuple. * This should be an array of C strings, which will * be processed later by the type input functions. */ values = (char **) palloc(sizeof(char *) * 7); values[i_num_updated] = (char *) palloc((INTEGER_LEN + 1) * sizeof(char)); values[i_send_len] = (char *) palloc((INTEGER_LEN + 1) * sizeof(char)); /* * FIXME: We don't know how many rows could be returned. The average * is supposed to be 4. Let's be prepared for 100, just to be safe. */ values[i_symbol] = (char *) palloc(((S_SYMB_LEN + 3) * 100 + 3) * sizeof(char)); values[i_trade_id] = (char *) palloc(((IDENT_T_LEN + 1) * 100 + 2) * sizeof(char)); values[i_price_quote] = (char *) palloc(((S_PRICE_T_LEN + 1) * 100 + 2) * sizeof(char)); values[i_trade_qty] = (char *) palloc(((INTEGER_LEN + 1) * 100 + 2) * sizeof(char)); values[i_trade_type] = (char *) palloc(((TT_ID_LEN + 3) * 100 + 3) * sizeof(char)); /* * This might be overkill since we always expect single dimensions * arrays. * Should probably check the count of all the arrays to make sure * they are the same... */ get_typlenbyvalalign(ARR_ELEMTYPE(symbol_p), &typlen_s, &typbyval_s, &typalign_s); p_s = ARR_DATA_PTR(symbol_p); get_typlenbyvalalign(ARR_ELEMTYPE(trade_qty), &typlen_tq, &typbyval_tq, &typalign_tq); p_tq = (int *) ARR_DATA_PTR(trade_qty); deconstruct_array(price_quote_p, NUMERICOID, -1, false, 'i', &transdatums_pq, NULL, &nitems_pq); strcpy(status_submitted, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(status_submitted_p)))); strcpy(type_limit_buy, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(type_limit_buy_p)))); strcpy(type_limit_sell, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(type_limit_sell_p)))); strcpy(type_stop_loss, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(type_stop_loss_p)))); #ifdef DEBUG dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); #endif /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); /* switch to memory context appropriate for multiple function calls */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); SPI_connect(); plan_queries(MFF1_statements); strcpy(values[i_symbol], "{"); strcpy(values[i_trade_id], "{"); strcpy(values[i_price_quote], "{"); strcpy(values[i_trade_type], "{"); strcpy(values[i_trade_qty], "{"); for (i = 0; i < nitems_pq; i++) { rows_sent = 0; strcpy(price_quote, DatumGetCString(DirectFunctionCall1(numeric_out, transdatums_pq[i]))); strcpy(symbol, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(p_s)))); /* FIXME: BEGIN/COMMIT statements not supported with SPI. */ /* ret = SPI_exec("BEGIN;", 0); if (ret == SPI_OK_SELECT) { } else { elog(NOTICE, "ERROR: BEGIN not ok = %d", ret); } */ #ifdef DEBUG sprintf(sql, SQLMFF1_1, DatumGetCString(DirectFunctionCall1(numeric_out, transdatums_pq[i])), p_tq[i], symbol); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = Float8GetDatum(atof(price_quote)); args[1] = Int64GetDatum(p_tq[i]); args[2] = CStringGetTextDatum(symbol); ret = SPI_execute_plan(MFF1_1, args, nulls, false, 0); if (ret != SPI_OK_UPDATE) { dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[0].sql); } num_updated += SPI_processed; #ifdef DEBUG elog(NOTICE, "%d row(s) updated", num_updated); sprintf(sql, SQLMFF1_2, symbol, type_stop_loss, price_quote, type_limit_sell, price_quote, type_limit_buy, price_quote); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = CStringGetTextDatum(symbol); args[1] = CStringGetTextDatum(type_stop_loss); args[2] = Float8GetDatum(atof(price_quote)); args[3] = CStringGetTextDatum(type_limit_sell); args[4] = args[2]; args[5] = CStringGetTextDatum(type_limit_buy); args[6] = args[2]; ret = SPI_execute_plan(MFF1_2, args, nulls, true, 0); if (ret != SPI_OK_SELECT) { dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[1].sql); continue; } #ifdef DEBUG elog(NOTICE, "%d row(s) returned", SPI_processed); #endif /* DEBUG */ tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; n = SPI_processed; for (j = 0; j < n; j++) { tuple = tuptable->vals[j]; trade_id = SPI_getvalue(tuple, tupdesc, 1); req_price_quote = SPI_getvalue(tuple, tupdesc, 2); req_trade_type = SPI_getvalue(tuple, tupdesc, 3); req_trade_qty = SPI_getvalue(tuple, tupdesc, 4); #ifdef DEBUG elog(NOTICE, "trade_id = %s", trade_id); sprintf(sql, SQLMFF1_3, status_submitted, trade_id); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = CStringGetTextDatum(status_submitted); args[1] = Int64GetDatum(atoll(trade_id)); ret = SPI_execute_plan(MFF1_3, args, nulls, false, 0); if (ret != SPI_OK_UPDATE) { dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[2].sql); } #ifdef DEBUG sprintf(sql, SQLMFF1_4, trade_id); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = Int64GetDatum(atoll(trade_id)); ret = SPI_execute_plan(MFF1_4, args, nulls, false, 0); if (ret != SPI_OK_DELETE) { dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[3].sql); } #ifdef DEBUG sprintf(sql, SQLMFF1_5, trade_id, status_submitted); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[1] = CStringGetTextDatum(status_submitted); ret = SPI_execute_plan(MFF1_5, args, nulls, false, 0); if (ret != SPI_OK_INSERT) { dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[4].sql); } ++rows_sent; #ifdef DEBUG elog(NOTICE, "%d row(s) sent", rows_sent); #endif /* DEBUG */ if (count > 0) { strcat(values[i_symbol], ","); strcat(values[i_trade_id], ","); strcat(values[i_price_quote], ","); strcat(values[i_trade_type], ","); strcat(values[i_trade_qty], ","); } strcat(values[i_symbol], symbol); strcat(values[i_trade_id], trade_id); strcat(values[i_price_quote], req_price_quote); strcat(values[i_trade_type], req_trade_type); strcat(values[i_trade_qty], req_trade_qty); ++count; } /* FIXME: BEGIN/COMMIT statements not supported with SPI. */ /* ret = SPI_exec("COMMIT;", 0); if (ret == SPI_OK_SELECT) { } else { elog(NOTICE, "ERROR: COMMIT not ok = %d", ret); } */ send_len += rows_sent; p_s = att_addlength_pointer(p_s, typlen_s, p_s); p_s = (char *) att_align_nominal(p_s, typalign_s); } strcat(values[i_symbol], "}"); strcat(values[i_trade_id], "}"); strcat(values[i_price_quote], "}"); strcat(values[i_trade_qty], "}"); strcat(values[i_trade_type], "}"); sprintf(values[i_num_updated], "%d", num_updated); sprintf(values[i_send_len], "%d", send_len); funcctx->max_calls = 1; /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); } /* * generate attribute metadata needed later to produce tuples from raw * C strings */ attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); call_cntr = funcctx->call_cntr; max_calls = funcctx->max_calls; attinmeta = funcctx->attinmeta; if (call_cntr < max_calls) { /* do when there is more left to send */ HeapTuple tuple; Datum result; #ifdef DEBUG for (i = 0; i < 7; i++) { elog(NOTICE, "MFF1 OUT: %d %s", i, values[i]); } #endif /* DEBUG */ /* Build a tuple. */ tuple = BuildTupleFromCStrings(attinmeta, values); /* Make the tuple into a datum. */ result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } else { /* Do when there is no more left. */ SPI_finish(); SRF_RETURN_DONE(funcctx); } }
/* * 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)) { *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); }
/* Create columns binding, depends on islarge, using 2 or 4 bytes for offset_len */ static void create_col_bind(MemTupleBindingCols *colbind, bool islarge, TupleDesc tupdesc, int col_align) { int i = 0; int physical_col = 0; int pass = 0; uint32 cur_offset = (tupdesc->tdhasoid || col_align == 8) ? 8 : 4; uint32 null_save_entries = compute_null_save_entries(tupdesc->natts); /* alloc null save entries. Zero it */ colbind->null_saves = (short *) palloc0(sizeof(short) * null_save_entries); colbind->null_saves_aligned = (short *) palloc0(sizeof(short) * null_save_entries); colbind->has_null_saves_alignment_mismatch = false; colbind->has_dropped_attr_alignment_mismatch = false; /* alloc bindings, no need to zero because we will fill them out */ colbind->bindings = (MemTupleAttrBinding *) palloc(sizeof(MemTupleAttrBinding) * tupdesc->natts); /* * The length of each binding is determined according to the alignment * of the physically following binding. Use this pointer to keep track * of the previously processed binding. */ MemTupleAttrBinding *previous_bind = NULL; /* * First pass, do 8 bytes aligned, native type. * Sencond pass, do 4 bytes aligned, native type. * Third pass, do 2 bytes aligned, native type. * Finall, do 1 bytes aligned native type. * * depends on islarge, varlena types are either handled in the * second pass (is large, varoffset using 4 bytes), or in the * third pass (not large, varoffset using 2 bytes). */ for(pass =0; pass < 4; ++pass) { for(i=0; i<tupdesc->natts; ++i) { Form_pg_attribute attr = tupdesc->attrs[i]; MemTupleAttrBinding *bind = &colbind->bindings[i]; if(pass == 0 && attr->attlen > 0 && attr->attalign == 'd') { bind->offset = att_align_nominal(cur_offset, attr->attalign); bind->len = attr->attlen; add_null_save(colbind->null_saves, physical_col, attr->attlen); if (physical_col) { /* Set the aligned length of the previous binding according to current alignment. */ if (add_null_save_aligned(previous_bind, colbind->null_saves_aligned, physical_col - 1, 'd')) { colbind->has_null_saves_alignment_mismatch = true; if (attr->attisdropped) { colbind->has_dropped_attr_alignment_mismatch = true; } } } bind->flag = attr->attbyval ? MTB_ByVal_Native : MTB_ByVal_Ptr; bind->null_byte = physical_col >> 3; bind->null_mask = 1 << (physical_col-(bind->null_byte << 3)); physical_col += 1; cur_offset = bind->offset + bind->len; previous_bind = bind; } else if (pass == 1 &&( (attr->attlen > 0 && attr->attalign == 'i') || ( islarge && att_bind_as_varoffset(attr)) ) ) { bind->offset = att_align_nominal(cur_offset, 'i'); bind->len = attr->attlen > 0 ? attr->attlen : 4; add_null_save(colbind->null_saves, physical_col, bind->len); if (physical_col) { /* Set the aligned length of the previous binding according to current alignment. */ if (add_null_save_aligned(previous_bind, colbind->null_saves_aligned, physical_col - 1, 'i')) { colbind->has_null_saves_alignment_mismatch = true; if (attr->attisdropped) { colbind->has_dropped_attr_alignment_mismatch = true; } } } if(attr->attlen > 0) bind->flag = attr->attbyval ? MTB_ByVal_Native : MTB_ByVal_Ptr; else if(attr->attlen == -1) bind->flag = MTB_ByRef; else { Assert(attr->attlen == -2); bind->flag = MTB_ByRef_CStr; } bind->null_byte = physical_col >> 3; bind->null_mask = 1 << (physical_col-(bind->null_byte << 3)); physical_col += 1; cur_offset = bind->offset + bind->len; previous_bind = bind; }
/* * make_variant_int: Converts our external (Variant) representation to a VariantInt. */ static VariantInt make_variant_int(Variant v, FunctionCallInfo fcinfo, IOFuncSelector func) { VariantCache *cache; VariantInt vi; long data_length; /* long instead of size_t because we're subtracting */ Pointer ptr; uint flags; /* Ensure v is fully detoasted */ Assert(!VARATT_IS_EXTENDED(v)); /* May need to be careful about what context this stuff is palloc'd in */ vi = palloc0(sizeof(VariantDataInt)); vi->typid = get_oid(v, &flags); #ifdef VARIANT_TEST_OID vi->typid -= OID_MASK; #endif vi->typmod = v->typmod; vi->isnull = (flags & VAR_ISNULL ? true : false); cache = get_cache(fcinfo, vi, func); /* * by-value type. We do special things with all pass-by-reference when we * store, so we only use this for typbyval even though fetch_att supports * pass-by-reference. * * Note that fetch_att sanity-checks typlen for us (because we're only passing typbyval). */ if(cache->typbyval) { if(!vi->isnull) { Pointer p = VDATAPTR_ALIGN(v, cache->typalign); vi->data = fetch_att(p, cache->typbyval, cache->typlen); } return vi; } /* we don't store a varlena header for varlena data; instead we compute * it's size based on ours: * * Our size - our header size - overflow byte (if present) * * For cstring, we don't store the trailing NUL */ data_length = VARSIZE(v) - VHDRSZ - (flags & VAR_OVERFLOW ? 1 : 0); if( data_length < 0 ) elog(ERROR, "Negative data_length %li", data_length); if (cache->typlen == -1) /* varlena */ { ptr = palloc0(data_length + VARHDRSZ); SET_VARSIZE(ptr, data_length + VARHDRSZ); memcpy(VARDATA(ptr), VDATAPTR(v), data_length); } else if(cache->typlen == -2) /* cstring */ { ptr = palloc(data_length + 1); /* Need space for NUL terminator */ memcpy(ptr, VDATAPTR(v), data_length); *(ptr + data_length + 1) = '\0'; } else /* Fixed size, pass by reference */ { if(vi->isnull) { vi->data = (Datum) 0; return vi; } Assert(data_length == cache->typlen); ptr = palloc0(data_length); Assert(ptr == (char *) att_align_nominal(ptr, cache->typalign)); memcpy(ptr, VDATAPTR(v), data_length); } vi->data = PointerGetDatum(ptr); return vi; }