*
* knl_utuple.cpp
* the row format of inplace update engine.
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/gausskernel/storage/access/ustore/knl_utuple.cpp
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/tupmacs.h"
#include "access/htup.h"
#include "access/xact.h"
#include "access/transam.h"
#include "access/tableam.h"
#include "access/tuptoaster.h"
#include "access/sysattr.h"
#include "access/ustore/knl_utuple.h"
#include "access/ustore/knl_uvisibility.h"
#include "access/ustore/knl_whitebox_test.h"
#include "access/tuptoaster.h"
#include "utils/typcache.h"
#define TUPLE_IS_HEAP_TUPLE(tup) (((HeapTuple)tup)->tupTableType == HEAP_TUPLE)
#define TUPLE_IS_UHEAP_TUPLE(tup) (((UHeapTuple)tup)->tupTableType == UHEAP_TUPLE)
static const int ISNULL_ATTRS_PER_BYTE = 8;
static inline bool ATT_IS_DROPPED(Form_pg_attribute attr)
{
return (attr->attisdropped || strstr(attr->attname.data, "........pg.dropped."));
}
* The primary goal of this function is to deform the tuple before
* it makes its way into the data page. This acts as a validation
* of the tuple data, especially in case of varlenas.
*/
void CheckTupleValidity(Relation rel, UHeapTuple utuple)
{
bool isNull[MaxHeapAttributeNumber];
Datum values[MaxHeapAttributeNumber];
UHeapDeformTuple(utuple, RelationGetDescr(rel), values, isNull);
}
* the process of form a tuple is a two-phase operation, firstly calculating the size and then filling the tuple.
* The value of enableReserve should not be change between the two steps, otherwise it is a bug.
* So only read enableReserve from GUC at the begining of forming a tuple, and then pass it to UHeapCalcTupleDataSize
* and UheapFillDiskTuple. In this way we can make sure that they use the same value.
* The same for toast operations.
*/
uint32 UHeapCalcTupleDataSize(TupleDesc tuple_desc, Datum *values, const bool *is_nulls, uint32 hoff,
bool enableReverseBitmap, bool enableReserve)
{
FormData_pg_attribute *attrs = tuple_desc->attrs;
int attrNum = tuple_desc->natts;
int size = hoff;
Assert(NAttrsReserveSpace(attrNum) == enableReverseBitmap);
Assert(!enableReserve || (enableReserve && enableReverseBitmap));
Assert(enableReverseBitmap || (enableReverseBitmap == false && enableReserve == false));
for (int i = 0; i < attrNum; ++i) {
Form_pg_attribute attr = &attrs[i];
Datum val = values[i];
if (is_nulls[i] && (!enableReserve || !attr->attbyval || ATT_IS_DROPPED(attr))) {
continue;
}
if (attr->attbyval) {
size += attr->attlen;
} else if (attr->attlen == -1 && attr->attstorage != 'p' && VARATT_CAN_MAKE_SHORT(DatumGetPointer(val))) {
* we're anticipating converting to a short varlena header, so
* adjust length and don't count any alignment
*/
int attrLen = VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val));
size += attrLen;
} else {
* We'll reach this case when storing a varlena that needs a
* 4-byte header, a variable-width type that requires alignment
* such as a record type, and for fixed-width types that are not
* pass-by-value (e.g. aclitem).
*/
size = att_align_datum(size, attr->attalign, attr->attlen, val);
int attrLen = att_addlength_datum(size, attr->attlen, val) - size;
size += attrLen;
}
}
return size - hoff;
}
* The reason why we pass enableReserve to UHeapCalcTupleDataSize but not read it from GUC(PGC_USERSET level) is:
* the process of form a tuple is a two-phase operation, firstly calculating the size and then filling the tuple.
* The value of enableReserve should not be change between the two steps, otherwise it is a bug.
* So only read enableReserve from GUC at the begining of forming a tuple, and then pass it to UHeapCalcTupleDataSize
* and UheapFillDiskTuple. In this way we can make sure that they use the same value.
* The same for toast operations.
*/
template<bool hasnull>
void UHeapFillDiskTuple(TupleDesc tupleDesc, Datum *values, const bool *isnull, UHeapDiskTupleData *diskTuple,
uint32 dataSize, bool enableReverseBitmap, bool enableReserve)
{
bits8 *bitP = NULL;
uint32 bitmask;
bits8 *bitPReserve = NULL;
uint32 bitmaskReserve = 0;
char *begin = NULL;
int numberOfAttributes = tupleDesc->natts;
Assert(NAttrsReserveSpace(numberOfAttributes) == enableReverseBitmap);
Assert(!enableReserve || enableReverseBitmap);
Assert(enableReverseBitmap || (enableReverseBitmap == false && enableReserve == false));
FormData_pg_attribute *att = tupleDesc->attrs;
char *data = (char *)diskTuple->data;
if (hasnull) {
int nullcount = 0;
int bitmapBytes = 0;
int tupleAttrs = UHeapTupleHeaderGetNatts(diskTuple);
Assert(tupleAttrs == numberOfAttributes);
for (int i = 0; i < numberOfAttributes; i++) {
if (isnull[i]) {
nullcount++;
}
}
bitP = &diskTuple->data[0] - 1;
bitmask = HIGHBIT;
UHeapDiskTupSetHasNulls(diskTuple);
if (enableReverseBitmap) {
bitmapBytes = BITMAPLEN(tupleAttrs + nullcount);
} else {
bitmapBytes = BITMAPLEN(tupleAttrs);
}
errno_t rc = memset_s(data, bitmapBytes, 0, bitmapBytes);
securec_check(rc, "", "");
bitPReserve = (bits8*)(data + (tupleAttrs / ISNULL_ATTRS_PER_BYTE));
bitmaskReserve = (1 << (uint32)(tupleAttrs % ISNULL_ATTRS_PER_BYTE));
data = data + bitmapBytes;
}
begin = data;
for (int i = 0; i < numberOfAttributes; i++) {
errno_t rc = EOK;
Size attrLength;
Size remainingLen = dataSize - (size_t)(data - begin);
if (hasnull) {
if (bitmask != HIGHBIT) {
bitmask <<= 1;
} else {
bitP += 1;
bitmask = 1;
}
if (isnull[i]) {
if (!enableReserve || !att[i].attbyval || ATT_IS_DROPPED(&att[i])) {
;
} else {
Assert(att[i].attlen > 0);
data += att[i].attlen;
*bitPReserve |= bitmaskReserve;
}
if (bitmaskReserve != HIGHBIT) {
bitmaskReserve <<= 1;
} else {
bitPReserve += 1;
bitmaskReserve = 1;
}
continue;
}
*bitP |= bitmask;
}
if (att[i].attbyval) {
* Not aligned
*/
store_att_byval(data, values[i], att[i].attlen);
attrLength = att[i].attlen;
} else if (att[i].attlen == LEN_VARLENA) {
Pointer val = DatumGetPointer(values[i]);
diskTuple->flag |= UHEAP_HASVARWIDTH;
if (VARATT_IS_EXTERNAL(val)) {
diskTuple->flag |= HEAP_HASEXTERNAL;
attrLength = VARSIZE_EXTERNAL(val);
rc = memcpy_s(data, remainingLen, val, attrLength);
securec_check(rc, "\0", "\0");
} else if (VARATT_IS_SHORT(val)) {
attrLength = VARSIZE_SHORT(val);
Assert(attrLength <= MaxPossibleUHeapTupleSize);
rc = memcpy_s(data, remainingLen, val, attrLength);
securec_check(rc, "\0", "\0");
} else if (att[i].attstorage != 'p' && VARATT_CAN_MAKE_SHORT(val)) {
attrLength = VARATT_CONVERTED_SHORT_SIZE(val);
SET_VARSIZE_SHORT(data, attrLength);
Assert(attrLength <= MaxPossibleUHeapTupleSize);
rc = memcpy_s(data + 1, remainingLen, VARDATA(val), attrLength - 1);
securec_check(rc, "\0", "\0");
} else {
data = (char *)att_align_nominal(data, att[i].attalign);
attrLength = VARSIZE(val);
rc = memcpy_s(data, remainingLen, val, attrLength);
securec_check(rc, "\0", "\0");
}
} else if (att[i].attlen == LEN_CSTRING) {
diskTuple->flag |= UHEAP_HASVARWIDTH;
Assert(att[i].attalign == 'c');
attrLength = strlen(DatumGetCString(values[i])) + 1;
Assert(attrLength <= MaxPossibleUHeapTupleSize);
rc = memcpy_s(data, remainingLen, DatumGetPointer(values[i]), attrLength);
securec_check(rc, "\0", "\0");
} else {
data = (char *)att_align_nominal(data, att[i].attalign);
Assert(att[i].attlen > 0);
attrLength = att[i].attlen;
rc = memcpy_s(data, remainingLen, DatumGetPointer(values[i]), attrLength);
securec_check(rc, "\0", "\0");
}
data += attrLength;
}
Assert((size_t)(data - begin) == dataSize);
}
template void UHeapFillDiskTuple<true>(TupleDesc tupleDesc, Datum *values, const bool *isnull,
UHeapDiskTupleData *diskTuple, uint32 dataSize, bool enableReverseBitmap, bool enableReserve);
template void UHeapFillDiskTuple<false>(TupleDesc tupleDesc, Datum *values, const bool *isnull,
UHeapDiskTupleData *diskTuple, uint32 dataSize, bool enableReverseBitmap, bool enableReserve);
* Copy a varlena column from source disk tuple (val) to destination (data)
* Param names match UHeapCopyDiskTupleNoNull and UHeapCopyDiskTupleWithNulls
*/
static Pointer UHeapCopyDtupleVarlena(Pointer val, Pointer data, Size& attrLength, const Size remainingLen,
const Form_pg_attribute att)
{
errno_t rc = EOK;
if (VARATT_IS_EXTERNAL(val)) {
attrLength = VARSIZE_EXTERNAL(val);
rc = memcpy_s(data, remainingLen, val, attrLength);
} else if (VARATT_IS_SHORT(val)) {
attrLength = VARSIZE_SHORT(val);
Assert(attrLength <= MaxPossibleUHeapTupleSize);
rc = memcpy_s(data, remainingLen, val, attrLength);
} else if (att->attstorage != 'p' && VARATT_CAN_MAKE_SHORT(val)) {
attrLength = VARATT_CONVERTED_SHORT_SIZE(val);
SET_VARSIZE_SHORT(data, attrLength);
Assert(attrLength <= MaxPossibleUHeapTupleSize);
rc = memcpy_s(data + 1, remainingLen, VARDATA(val), attrLength - 1);
} else {
data = (char *)att_align_nominal(data, att->attalign);
attrLength = VARSIZE(val);
rc = memcpy_s(data, remainingLen, val, attrLength);
}
securec_check(rc, "\0", "\0");
return data;
}
* Partially copy a src disk tuple (lacking nulls) to a dest disk tuple
* Only columns marked false in destnull are copied
* Dest disk tuple has a null bitmap which marks which cols were copied
*/
void UHeapCopyDiskTupleNoNull(TupleDesc tupleDesc, const bool *destNull, UHeapTuple destTup,
AttrNumber lastVar, const UHeapDiskTupleData *srcDtup)
{
UHeapDiskTupleData *destDtup = destTup->disk_tuple;
long srcOff = srcDtup->t_hoff;
char *srcTupPtr = (char *)srcDtup;
char *data = (char *)destDtup + destDtup->t_hoff;
char *begin = data;
bool enableReverseBitmap = NAttrsReserveSpace(UHeapTupleHeaderGetNatts(destDtup));
bool enableReserve = u_sess->attr.attr_storage.reserve_space_for_nullable_atts;
enableReserve = enableReserve && enableReverseBitmap;
Assert(NAttrsReserveSpace(UHeapTupleHeaderGetNatts(destDtup)) == enableReverseBitmap);
Assert(!enableReserve || (enableReserve && enableReverseBitmap));
Assert(enableReverseBitmap || (enableReverseBitmap == false && enableReserve == false));
uint32 dataSize = destTup->disk_tuple_size;
bits8 *bitP = &destDtup->data[0] - 1;
uint32 bitmask = HIGHBIT;
bits8 *bitPReserve = NULL;
uint32 bitmaskReserve = 0;
FormData_pg_attribute *att = tupleDesc->attrs;
int tupleAttrs = UHeapTupleHeaderGetNatts(srcDtup);
bitPReserve = (bits8*)((char *)destDtup->data + (tupleAttrs / ISNULL_ATTRS_PER_BYTE));
bitmaskReserve = (1 << (tupleAttrs % ISNULL_ATTRS_PER_BYTE));
for (int i = 0; i < lastVar; i++) {
errno_t rc = EOK;
Size attrLength;
Size remainingLen = dataSize - (size_t)(data - begin);
if (bitmask != HIGHBIT) {
bitmask <<= 1;
} else {
bitP += 1;
bitmask = 1;
}
if (!destNull[i]) {
*bitP |= bitmask;
}
if (att[i].attbyval) {
if (destNull[i]) {
srcOff += att[i].attlen;
if (enableReserve) {
Assert(att[i].attlen > 0);
data += att[i].attlen;
*bitPReserve |= bitmaskReserve;
}
if (bitmaskReserve != HIGHBIT) {
bitmaskReserve <<= 1;
} else {
bitPReserve += 1;
bitmaskReserve = 1;
}
continue;
}
attrLength = att[i].attlen;
store_att_byval(data, *((Datum *)((char *)srcTupPtr + srcOff)), att[i].attlen);
} else if (att[i].attlen == LEN_VARLENA) {
srcOff = att_align_pointer(srcOff, att[i].attalign, -1, srcTupPtr + srcOff);
attrLength = VARSIZE_ANY(srcTupPtr + srcOff);
if (!destNull[i]) {
Pointer val = (Pointer) (srcTupPtr + srcOff);
data = UHeapCopyDtupleVarlena(val, data, attrLength, remainingLen, &att[i]);
} else {
if (bitmaskReserve != HIGHBIT) {
bitmaskReserve <<= 1;
} else {
bitPReserve += 1;
bitmaskReserve = 1;
}
}
} else if (att[i].attlen == LEN_CSTRING) {
srcOff = att_align_nominal(srcOff, att[i].attalign);
Assert(att[i].attalign == 'c');
attrLength = (uint32)strlen(srcTupPtr + srcOff) + 1;
Assert(attrLength <= MaxPossibleUHeapTupleSize);
if (!destNull[i]) {
data = (char *)att_align_nominal(data, att[i].attalign);
rc = memcpy_s(data, remainingLen, (srcTupPtr + srcOff), attrLength);
securec_check(rc, "\0", "\0");
} else {
if (bitmaskReserve != HIGHBIT) {
bitmaskReserve <<= 1;
} else {
bitPReserve += 1;
bitmaskReserve = 1;
}
}
} else {
srcOff = att_align_nominal(srcOff, att[i].attalign);
Assert(att[i].attlen > 0);
attrLength = att[i].attlen;
if (!destNull[i]) {
data = (char *)att_align_nominal(data, att[i].attalign);
rc = memcpy_s(data, remainingLen, (srcTupPtr + srcOff), attrLength);
securec_check(rc, "\0", "\0");
} else {
if (bitmaskReserve != HIGHBIT) {
bitmaskReserve <<= 1;
} else {
bitPReserve += 1;
bitmaskReserve = 1;
}
}
}
srcOff += attrLength;
if (!destNull[i])
data += attrLength;
}
}
* Partially copy a src disk tuple with nulls to a dest disk tuple
* Only columns marked false in destNull as false are copied unless they are null in src
*/
void UHeapCopyDiskTupleWithNulls(TupleDesc tupleDesc, const bool *destNull, UHeapTuple destTup,
AttrNumber lastVar, UHeapDiskTupleData *srcDtup)
{
UHeapDiskTupleData *destDtup = destTup->disk_tuple;
long srcOff = srcDtup->t_hoff;
char *srcTupPtr = (char *)srcDtup;
char *data = (char *)destDtup + destDtup->t_hoff;
char *begin = data;
bool enableReverseBitmap = NAttrsReserveSpace(UHeapTupleHeaderGetNatts(destDtup));
bool enableReserve = u_sess->attr.attr_storage.reserve_space_for_nullable_atts;
enableReserve = enableReserve && enableReverseBitmap;
Assert(NAttrsReserveSpace(UHeapTupleHeaderGetNatts(destDtup)) == enableReverseBitmap);
Assert(!enableReserve || (enableReserve && enableReverseBitmap));
Assert(enableReverseBitmap || (enableReverseBitmap == false && enableReserve == false));
uint32 dataSize = destTup->disk_tuple_size;
bits8 *srcBp = srcDtup->data;
bits8 *bitP = &destDtup->data[0] - 1;
uint32 bitmask = HIGHBIT;
bits8 *bitPReserve = NULL;
uint32 bitmaskReserve = 0;
FormData_pg_attribute *att = tupleDesc->attrs;
int srcnullcount = 0;
int tupleAttrs = UHeapTupleHeaderGetNatts(srcDtup);
bitPReserve = (bits8*)((char *)destDtup->data + (tupleAttrs / ISNULL_ATTRS_PER_BYTE));
bitmaskReserve = (1 << (tupleAttrs % ISNULL_ATTRS_PER_BYTE));
for (int i = 0; i < lastVar; i++) {
errno_t rc = EOK;
Size attrLength;
Size remainingLen = dataSize - (size_t)(data - begin);
if (bitmask != HIGHBIT) {
bitmask <<= 1;
} else {
bitP += 1;
bitmask = 1;
}
if (att_isnull(i, srcBp)) {
if (enableReverseBitmap) {
if (!att_isnull(tupleAttrs + srcnullcount, srcBp)) {
Assert(att[i].attlen > 0);
data += att[i].attlen;
srcOff += att[i].attlen;
*bitPReserve |= bitmaskReserve;
}
}
if (bitmaskReserve != HIGHBIT) {
bitmaskReserve <<= 1;
} else {
bitPReserve += 1;
bitmaskReserve = 1;
}
srcnullcount++;
continue;
}
if (!destNull[i])
*bitP |= bitmask;
if (att[i].attbyval) {
if (destNull[i]) {
srcOff += att[i].attlen;
if (enableReserve) {
Assert(att[i].attlen > 0);
data += att[i].attlen;
*bitPReserve |= bitmaskReserve;
}
if (bitmaskReserve != HIGHBIT) {
bitmaskReserve <<= 1;
} else {
bitPReserve += 1;
bitmaskReserve = 1;
}
continue;
}
attrLength = att[i].attlen;
store_att_byval(data, *((Datum *)((char *)srcTupPtr + srcOff)), att[i].attlen);
} else if (att[i].attlen == LEN_VARLENA) {
srcOff = att_align_pointer(srcOff, att[i].attalign, -1, srcTupPtr + srcOff);
attrLength = VARSIZE_ANY(srcTupPtr + srcOff);
if (!destNull[i]) {
Pointer val = (Pointer) (srcTupPtr + srcOff);
data = UHeapCopyDtupleVarlena(val, data, attrLength, remainingLen, &att[i]);
} else {
if (bitmaskReserve != HIGHBIT) {
bitmaskReserve <<= 1;
} else {
bitPReserve += 1;
bitmaskReserve = 1;
}
}
} else if (att[i].attlen == LEN_CSTRING) {
srcOff = att_align_nominal(srcOff, att[i].attalign);
Assert(att[i].attalign == 'c');
attrLength = (uint32)strlen(srcTupPtr + srcOff) + 1;
Assert(attrLength <= MaxPossibleUHeapTupleSize);
if (!destNull[i]) {
data = (char *)att_align_nominal(data, att[i].attalign);
rc = memcpy_s(data, remainingLen, (srcTupPtr + srcOff), attrLength);
securec_check(rc, "\0", "\0");
} else {
if (bitmaskReserve != HIGHBIT) {
bitmaskReserve <<= 1;
} else {
bitPReserve += 1;
bitmaskReserve = 1;
}
}
} else {
srcOff = att_align_nominal(srcOff, att[i].attalign);
Assert(att[i].attlen > 0);
attrLength = att[i].attlen;
if (!destNull[i]) {
data = (char *)att_align_nominal(data, att[i].attalign);
rc = memcpy_s(data, remainingLen, (srcTupPtr + srcOff), attrLength);
securec_check(rc, "\0", "\0");
} else {
if (bitmaskReserve != HIGHBIT) {
bitmaskReserve <<= 1;
} else {
bitPReserve += 1;
bitmaskReserve = 1;
}
}
}
srcOff += attrLength;
if (!destNull[i])
data += attrLength;
}
}
UHeapTuple UHeapFormTuple(TupleDesc tuple_desc, Datum *values, bool *is_nulls)
{
uint8 hoff = 0;
uint32 diskTupleSize = 0;
uint32 i = 0;
uint32 attrNum = tuple_desc->natts;
bool enableReverseBitmap = NAttrsReserveSpace(attrNum);
bool enableReserve = u_sess->attr.attr_storage.reserve_space_for_nullable_atts;
UHeapDiskTupleData *diskTuple = NULL;
FormData_pg_attribute *att = tuple_desc->attrs;
int nullcount = 0;
enableReserve = enableReserve && enableReverseBitmap;
if (attrNum > MaxTupleAttributeNumber) {
ereport(ERROR, (errcode(ERRCODE_TOO_MANY_COLUMNS),
errmsg("number of columns (%d) exceeds limit (%d), AM type (%d), type id (%u)", attrNum,
MaxTupleAttributeNumber, GetTableAmType(tuple_desc->td_tam_ops), tuple_desc->tdtypeid)));
}
WHITEBOX_TEST_STUB(UHEAP_FORM_TUPLE_FAILED, WhiteboxDefaultErrorEmit);
for (i = 0; i < attrNum; i++) {
if (is_nulls[i]) {
nullcount++;
} else if (att[i].attlen == -1 && att[i].attalign == 'd' && att[i].attndims == 0 &&
!VARATT_IS_EXTENDED(DatumGetPointer(values[i]))) {
values[i] = toast_flatten_tuple_attribute(values[i], att[i].atttypid, att[i].atttypmod);
} else if (att[i].attlen == -1 && att[i].attalign == 'i' &&
VARATT_IS_HUGE_TOAST_POINTER(DatumGetPointer(values[i])) &&
!(att[i].atttypid == CLOBOID || att[i].atttypid == BLOBOID)) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("only suport type(clob/blob) for more than 1G toast")));
}
}
diskTupleSize = SizeOfUHeapDiskTupleData;
uint32 dataSize = 0;
if (nullcount > 0) {
if (enableReverseBitmap) {
diskTupleSize += BITMAPLEN(attrNum + nullcount);
} else {
diskTupleSize += BITMAPLEN(attrNum);
}
dataSize = UHeapCalcTupleDataSize(tuple_desc, values, is_nulls, diskTupleSize, enableReverseBitmap,
enableReserve);
} else {
dataSize = UHeapCalcTupleDataSize(tuple_desc, values, is_nulls, diskTupleSize, enableReverseBitmap,
enableReserve);
}
hoff = diskTupleSize;
diskTupleSize += dataSize;
UHeapTuple uheapTuplePtr = (UHeapTuple)uheaptup_alloc(diskTupleSize + sizeof(UHeapTupleData));
uheapTuplePtr->disk_tuple = (UHeapDiskTupleData *)((char *)uheapTuplePtr + sizeof(UHeapTupleData));
diskTuple = uheapTuplePtr->disk_tuple;
UHeapTupleHeaderSetNatts(diskTuple, attrNum);
diskTuple->t_hoff = hoff;
if (nullcount > 0) {
UHeapFillDiskTuple<true>(tuple_desc, values, is_nulls, diskTuple, dataSize, enableReverseBitmap,
enableReserve);
} else {
UHeapFillDiskTuple<false>(tuple_desc, values, is_nulls, diskTuple, dataSize, enableReverseBitmap,
enableReserve);
}
uheapTuplePtr->disk_tuple_size = diskTupleSize;
FastVerifyUTuple(uheapTuplePtr->disk_tuple, InvalidBuffer);
return uheapTuplePtr;
}
UHeapTuple UHeapModifyTuple(UHeapTuple tuple, TupleDesc tupleDesc, Datum *replValues, const bool *replIsnull,
const bool *doReplace)
{
int numberOfAttributes = tupleDesc->natts;
int attoff;
Datum *values = NULL;
bool *isnull = NULL;
UHeapTuple newTuple;
* allocate and fill values and isnull arrays from either the tuple or the
* repl information, as appropriate.
*
* NOTE: it's debatable whether to use heap_deform_tuple() here or just
* heap_getattr() only the non-replaced columns. The latter could win if
* there are many replaced columns and few non-replaced ones. However,
* heap_deform_tuple costs only O(N) while the heap_getattr way would cost
* O(N^2) if there are many non-replaced columns, so it seems better to
* err on the side of linear cost.
*/
values = (Datum *)palloc(numberOfAttributes * sizeof(Datum));
isnull = (bool *)palloc(numberOfAttributes * sizeof(bool));
tableam_tops_deform_tuple(tuple, tupleDesc, values, isnull);
for (attoff = 0; attoff < numberOfAttributes; attoff++) {
if (doReplace[attoff]) {
values[attoff] = replValues[attoff];
isnull[attoff] = replIsnull[attoff];
}
}
* create a new tuple from the values and isnull arrays
*/
newTuple = (UHeapTuple)tableam_tops_form_tuple(tupleDesc, values, isnull, TableAmUstore);
pfree(values);
pfree(isnull);
* copy the identification info of the old tuple: t_ctid, t_self, and OID
* (if any)
*/
newTuple->ctid = tuple->ctid;
newTuple->table_oid = tuple->table_oid;
HeapTupleCopyBase(newTuple, tuple);
#ifdef PGXC
newTuple->xc_node_id = tuple->xc_node_id;
#endif
Assert (tupleDesc->tdhasoid == false);
return newTuple;
}
void SlotDeformUTuple(TupleTableSlot *slot, UHeapTuple tuple, long *offp, int natts)
{
Assert(tuple->tupTableType == UHEAP_TUPLE);
TupleDesc tupleDesc = slot->tts_tupleDescriptor;
Datum *values = slot->tts_values;
bool *isNulls = slot->tts_isnull;
UHeapDiskTuple tup = tuple->disk_tuple;
FormData_pg_attribute *att = tupleDesc->attrs;
bool hasnulls = UHeapDiskTupHasNulls(tup);
char *tp = (char *) tup;
bits8 *bp = tup->data;
int nullcount = 0;
int tupleAttrs = UHeapTupleHeaderGetNatts(tuple->disk_tuple);
bool enableReverseBitmap = NAttrsReserveSpace(tupleAttrs);
int nattsAvailable = Min(tupleAttrs, natts);
Assert(*offp >= 0);
* Check whether the first call for this tuple, and initialize or restore
* loop state.
*/
long off = tup->t_hoff;
int attnum = slot->tts_nvalid;
if (attnum != 0) {
off = *offp;
}
for (; attnum < nattsAvailable; attnum++) {
Form_pg_attribute thisatt = &att[attnum];
if (hasnulls && att_isnull(attnum, bp)) {
space reserved for null attributes */
if (enableReverseBitmap) {
if (!att_isnull(tupleAttrs + nullcount, bp)) {
off += thisatt->attlen;
}
}
nullcount++;
values[attnum] = (Datum)0;
isNulls[attnum] = true;
continue;
}
isNulls[attnum] = false;
if (thisatt->attlen == LEN_VARLENA) {
off = att_align_pointer(off, thisatt->attalign, -1, tp + off);
} else if (!thisatt->attbyval) {
off = att_align_nominal(off, thisatt->attalign);
}
values[attnum] = fetchatt(thisatt, tp + off);
off = att_addlength_pointer(off, thisatt->attlen, tp + off);
}
* If the tuple doesn't have values for any column, populate the
* data from default values of that column.
* This is the original behavior for row table.
*/
for (; attnum < natts; attnum++) {
slot->tts_values[attnum] = heapGetInitDefVal(attnum + 1, slot->tts_tupleDescriptor, &slot->tts_isnull[attnum]);
}
* Save state for next execution
*/
slot->tts_nvalid = attnum;
*offp = off;
}
void UHeapDeformTuple(UHeapTuple utuple, TupleDesc rowDesc, Datum *values, bool *isNulls)
{
UHeapDeformTupleGuts(utuple, rowDesc, values, isNulls, rowDesc->natts);
}
void UHeapDeformTupleGuts(UHeapTuple utuple, TupleDesc rowDesc, Datum *values, bool *isNulls, int unatts)
{
Assert(utuple->tupTableType == UHEAP_TUPLE);
UHeapDiskTuple diskTuple = utuple->disk_tuple;
bool hasnulls = UHeapDiskTupHasNulls(diskTuple);
FormData_pg_attribute *att = rowDesc->attrs;
int natts;
int attnum;
bits8 *bp = diskTuple->data;
long off = diskTuple->t_hoff;
char *tupPtr = (char *)diskTuple;
int nullcount = 0;
int tupleAttrs = UHeapTupleHeaderGetNatts(diskTuple);
bool enableReverseBitmap = NAttrsReserveSpace(tupleAttrs);
WHITEBOX_TEST_STUB(UHEAP_DEFORM_TUPLE_FAILED, WhiteboxDefaultErrorEmit);
natts = Min(tupleAttrs, unatts);
for (attnum = 0; attnum < natts; attnum++) {
Form_pg_attribute thisatt = &att[attnum];
if (hasnulls && att_isnull(attnum, bp)) {
space reserved for null attributes */
if (enableReverseBitmap) {
if (!att_isnull(tupleAttrs + nullcount, bp)) {
off += thisatt->attlen;
}
}
nullcount++;
values[attnum] = (Datum)0;
isNulls[attnum] = true;
continue;
}
isNulls[attnum] = false;
* If this is a varlena, there might be alignment padding, if it has a
* 4-byte header. Otherwise, there will only be padding if it's not
* pass-by-value.
*/
if (thisatt->attlen == -1) {
off = att_align_pointer(off, thisatt->attalign, -1, tupPtr + off);
} else if (!thisatt->attbyval) {
off = att_align_nominal(off, thisatt->attalign);
}
values[attnum] = fetchatt(thisatt, tupPtr + off);
off = att_addlength_pointer(off, thisatt->attlen, tupPtr + off);
}
* If tuple doesn't have all the atts indicated by tupleDesc, read the
* rest as null
*/
for (; attnum < unatts; attnum++) {
* The original Code is:
* example code: values[attnum] = (Datum) 0;
* example code: isNulls[attnum] = true;
*/
values[attnum] = heapGetInitDefVal(attnum + 1, rowDesc, &isNulls[attnum]);
}
}
* This function is used to cast heap type tuple to uheap type tuple
*/
UHeapTuple HeapToUHeap(TupleDesc tuple_desc, HeapTuple heaptuple)
{
Assert(TUPLE_IS_HEAP_TUPLE(heaptuple));
Datum *values = (Datum *)palloc(sizeof(Datum) * tuple_desc->natts);
bool *isnull = (bool *)palloc(sizeof(bool) * tuple_desc->natts);
heap_deform_tuple(heaptuple, tuple_desc, values, isnull);
UHeapTuple uheaptuple = UHeapFormTuple(tuple_desc, values, isnull);
uheaptuple->ctid = heaptuple->t_self;
pfree_ext(values);
pfree_ext(isnull);
return uheaptuple;
}
* This function is used to cast uheap type tuple to heap type tuple
*/
HeapTuple UHeapToHeap(TupleDesc tuple_desc, UHeapTuple uheaptuple)
{
Assert(uheaptuple->tupTableType == UHEAP_TUPLE);
Datum *values = (Datum *)palloc(sizeof(Datum) * tuple_desc->natts);
bool *isnull = (bool *)palloc(sizeof(bool) * tuple_desc->natts);
UHeapDeformTuple(uheaptuple, tuple_desc, values, isnull);
HeapTuple heaptuple = heap_form_tuple(tuple_desc, values, isnull);
heaptuple->t_self = uheaptuple->ctid;
pfree_ext(values);
pfree_ext(isnull);
return heaptuple;
}
* UHeapGetSysAttr
* Fetch the value of a system attribute for a tuple.
*/
Datum UHeapGetSysAttr(UHeapTuple uhtup, Buffer buf, int attnum, TupleDesc tupleDesc, bool *isnull)
{
Datum result = (Datum)0;
Assert(uhtup);
Assert(uhtup->tupTableType == UHEAP_TUPLE);
*isnull = false;
switch (attnum) {
case SelfItemPointerAttributeNumber:
result = PointerGetDatum(&(uhtup->ctid));
break;
case MinTransactionIdAttributeNumber:
result = TransactionIdGetDatum(uhtup->xmin);
break;
case MaxTransactionIdAttributeNumber:
result = TransactionIdGetDatum(uhtup->xmax);
break;
case MinCommandIdAttributeNumber:
case MaxCommandIdAttributeNumber:
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cmin, and cmax are not supported for ustore tuples")));
break;
case TableOidAttributeNumber:
result = ObjectIdGetDatum(uhtup->table_oid);
break;
case XC_NodeIdAttributeNumber:
result = UInt32GetDatum(uhtup->xc_node_id);
break;
default:
elog(ERROR, "invalid attnum: %d", attnum);
result = 0;
break;
}
return result;
}
* UHeapTupleAttrEquals
* Subroutine for UHeapDetermineModifiedColumns which returns the set of
* attributes from the given att_list that are different in tup1 and tup2.
*/
Bitmapset *UHeapTupleAttrEquals(TupleDesc tupdesc, Bitmapset *att_list, UHeapTuple tup1, UHeapTuple tup2)
{
int lAttno = 0, col = -1;
bool oldIsnull[MaxHeapAttributeNumber];
bool newIsnull[MaxHeapAttributeNumber];
Datum oldValues[MaxHeapAttributeNumber];
Datum newValues[MaxHeapAttributeNumber];
Bitmapset *modified = NULL;
while ((col = bms_next_member(att_list, col)) >= 0) {
AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber;
* Donot allow update on any system attribute other than tableOID,
* which is stored in index tuples of Global Partition Indexes
* and might not have been set correctly yet in the new tuple.
*/
if (attno <= InvalidAttrNumber && attno != TableOidAttributeNumber) {
elog(ERROR, "system-column update is not supported");
}
if (lAttno < attno)
lAttno = attno;
}
Assert(lAttno <= tupdesc->natts);
UHeapDeformTupleGuts(tup1, tupdesc, oldValues, oldIsnull, lAttno);
UHeapDeformTupleGuts(tup2, tupdesc, newValues, newIsnull, lAttno);
col = -1;
while ((col = bms_next_member(att_list, col)) >= 0) {
AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber;
if (attno == TableOidAttributeNumber)
continue;
if (oldIsnull[attno - 1] != newIsnull[attno - 1])
modified = bms_add_member(modified, attno - FirstLowInvalidHeapAttributeNumber);
else if (oldIsnull[attno - 1])
continue;
else {
Form_pg_attribute att = (TupleDescAttr(tupdesc, attno - 1));
* We do simple binary comparison of the two datums. This may be
* overly strict because there can be multiple binary
* representations for the same logical value. But we should be
* OK as long as there are no false positives. Using a
* type-specific equality operator is messy because there could be
* multiple notions of equality in different operator classes;
* furthermore, we cannot safely invoke user-defined functions
* while holding exclusive buffer lock.
*/
if (!datumIsEqual(oldValues[attno - 1], newValues[attno - 1], att->attbyval, att->attlen))
modified = bms_add_member(modified, attno - FirstLowInvalidHeapAttributeNumber);
}
}
return modified;
}
* UHeapCopyTuple
* Returns a copy of an entire tuple.
*
* The UHeapTuple struct, tuple header, and tuple data are all allocated
* as a single palloc() block.
*/
UHeapTuple UHeapCopyTuple(UHeapTuple uhtup)
{
UHeapTuple newTuple;
errno_t rc = EOK;
if (!UHeapTupleIsValid(uhtup) || uhtup->disk_tuple == NULL) {
return NULL;
}
Assert(uhtup->tupTableType == UHEAP_TUPLE);
newTuple = (UHeapTuple)uheaptup_alloc(UHeapTupleDataSize + uhtup->disk_tuple_size);
newTuple->ctid = uhtup->ctid;
newTuple->table_oid = uhtup->table_oid;
newTuple->xc_node_id = uhtup->xc_node_id;
newTuple->disk_tuple_size = uhtup->disk_tuple_size;
newTuple->t_xid_base = uhtup->t_xid_base;
newTuple->t_multi_base = uhtup->t_multi_base;
newTuple->disk_tuple = (UHeapDiskTuple)((char *)newTuple + UHeapTupleDataSize);
rc = memcpy_s((char *)newTuple->disk_tuple, newTuple->disk_tuple_size, (char *)uhtup->disk_tuple,
uhtup->disk_tuple_size);
securec_check(rc, "\0", "\0");
return newTuple;
}
void UHeapCopyTupleWithBuffer(UHeapTuple srcTup, UHeapTuple destTup)
{
Assert(UHeapTupleIsValid(srcTup));
Assert(srcTup->disk_tuple != NULL);
Assert(UHeapTupleIsValid(destTup));
Assert(destTup->disk_tuple != NULL);
destTup->ctid = srcTup->ctid;
destTup->table_oid = srcTup->table_oid;
destTup->xc_node_id = srcTup->xc_node_id;
destTup->t_xid_base = srcTup->t_xid_base;
destTup->t_multi_base = srcTup->t_multi_base;
destTup->disk_tuple_size = srcTup->disk_tuple_size;
errno_t rc = memcpy_s((char *)destTup->disk_tuple, destTup->disk_tuple_size, (char *)srcTup->disk_tuple,
srcTup->disk_tuple_size);
securec_check(rc, "\0", "\0");
FastVerifyUTuple(destTup->disk_tuple, InvalidBuffer);
}
* UHeapTupleHeaderAdvanceLatestRemovedXid - Advance the latestRemovedXid, if
* tuple is deleted by a transaction greater than latestRemovedXid. This is
* required to generate conflicts on hot standby.
*
* If we change this function then we need a similar change in
* *_xlog_vacuum_get_latestRemovedXid functions as well.
*
* This is quite similar to HeapTupleHeaderAdvanceLatestRemovedXid.
*/
void UHeapTupleHeaderAdvanceLatestRemovedXid(UHeapDiskTuple tuple, TransactionId xid, TransactionId *latestRemovedXid)
{
* Ignore tuples inserted by an aborted transaction.
*
* XXX we can ignore the tuple if it was non-in-place updated/deleted by
* the inserting transaction, but for that we need to traverse the
* complete undo chain to find the root tuple, is it really worth?
*/
if (UHeapTransactionIdDidCommit(xid)) {
Assert((tuple->flag & UHEAP_DELETED) || (tuple->flag & UHEAP_UPDATED));
if (TransactionIdFollows(xid, *latestRemovedXid))
*latestRemovedXid = xid;
}
}
* UHeapAttIsNull
* Returns TRUE if uheap tuple attribute is not present.
*/
bool UHeapAttIsNull(UHeapTuple tup, int attnum, TupleDesc tupleDesc)
{
Assert(tup->tupTableType == UHEAP_TUPLE);
* We allow a NULL tupledesc for relations not expected to have missing
* values, such as catalog relations and indexes.
*/
Assert(!tupleDesc || attnum <= tupleDesc->natts);
if (attnum > (int)UHeapTupleHeaderGetNatts(tup->disk_tuple)) {
return true;
}
if (attnum > 0) {
if (UHeapDiskTupNoNulls(tup->disk_tuple)) {
return false;
}
return att_isnull(attnum - 1, tup->disk_tuple->data);
}
switch (attnum) {
case TableOidAttributeNumber:
case SelfItemPointerAttributeNumber:
case MinTransactionIdAttributeNumber:
case MinCommandIdAttributeNumber:
case MaxTransactionIdAttributeNumber:
case MaxCommandIdAttributeNumber:
break;
default:
elog(ERROR, "invalid attnum: %d", attnum);
}
return false;
}
* uheap equivalent to nocachegetattr
*/
Datum UHeapNoCacheGetAttr(UHeapTuple tuple, uint32 attnum, TupleDesc tupleDesc)
{
UHeapDiskTuple tup = tuple->disk_tuple;
FormData_pg_attribute *att = tupleDesc->attrs;
char *tp = (char *)tup;
bits8 *bp = tup->data;
bool slow = false;
int hoff = tup->t_hoff;
int off = hoff;
bool hasnulls = UHeapDiskTupHasNulls(tup);
* 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.
* ----------------
*/
* important:
* maybe this function is not safe for accessing some attribute, which is different
* from methods slot_getattr(), slot_getallattrs(), slot_getsomeattrs(). those three
* always make sure that attnum is always valid between 1 and InplaceHeapTupleHeaderGetNatts(),
* because the caller has guarantee that.
* we find that the caller fastgetattr() doesn't guarantee the validition, and top callers
* are almost in system table level, for example pg_class and so on. so that it's NOT
* recommended that users' table functions call fastgetattr() and nocachegetattr();
*/
Assert(attnum <= UHeapTupleHeaderGetNatts(tup));
attnum--;
if (!UHeapDiskTupNoNulls(tup)) {
* there's a null somewhere in the tuple
* null columns have different hoff with non-null columns,
* not reuse the offset when have null columns.
*/
slow = true;
}
if (!slow) {
* If we get here, there are no nulls up to and including the target
* attribute. 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 (UHeapDiskTupHasVarWidth(tup)) {
for (uint32 j = 0; j <= attnum; j++) {
if (att[j].attlen <= 0) {
slow = true;
break;
}
}
}
if (att[0].attlen <= 0) {
slow = true;
}
}
if (!slow) {
uint32 natts = tupleDesc->natts;
uint32 j = 1;
Assert(att[0].attlen > 0);
* 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.
*/
if (att[0].attbyval) {
att[0].attcacheoff = 0;
} else {
off = att_align_nominal((uint32)off, att[0].attalign);
att[0].attcacheoff = off - hoff;
}
while (j < natts && att[j].attcacheoff > 0) {
Assert(att[j].attbyval || (!att[j].attbyval && ((uint32)(att[j].attcacheoff) ==
att_align_nominal((uint32)(att[j].attcacheoff + hoff), att[j].attalign) - hoff)));
j++;
}
off = att[j - 1].attcacheoff + att[j - 1].attlen + hoff;
for (; j < natts; j++) {
if (att[j].attlen <= 0)
break;
if (!att[j].attbyval) {
off = att_align_nominal((uint32)off, att[j].attalign);
}
att[j].attcacheoff = off - hoff;
off += att[j].attlen;
}
Assert(j > attnum);
off = att[attnum].attcacheoff + hoff;
} else {
* 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.
*/
bool usecache = true;
int nullcount = 0;
int tupleAttrs = UHeapTupleHeaderGetNatts(tup);
bool enableReverseBitmap = NAttrsReserveSpace(tupleAttrs);
for (uint32 i = 0; i <= attnum; i++) {
Assert(i < (uint32)tupleDesc->natts);
int attrLen = att[i].attlen;
if (hasnulls && att_isnull(i, bp)) {
if (enableReverseBitmap && !att_isnull(tupleAttrs + nullcount, bp))
off += att[i].attlen;
nullcount++;
usecache = false;
continue;
}
if (att[i].attlen == LEN_VARLENA) {
off = att_align_pointer(off, att[i].attalign, -1, tp + off);
attrLen = VARSIZE_ANY(tp + off);
} else if (!att[i].attbyval) {
off = att_align_nominal(off, att[i].attalign);
} else if (usecache && UHeapDiskTupNoNulls(tup)) {
att[i].attcacheoff = off - hoff;
}
if (i == (uint32)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);
}
* UHeapCopyHeapTuple
* Return a heap tuple constructed from the contents of the slot.
*
* heap_form_tuple will always a build a new tuple, so we don't need an
* explicit copy step.
*/
HeapTuple UHeapCopyHeapTuple(TupleTableSlot *slot)
{
HeapTuple tuple;
Assert(!TTS_EMPTY(slot));
Assert(TTS_TABLEAM_IS_USTORE(slot));
UHeapSlotGetAllAttrs(slot);
tuple = heap_form_tuple(slot->tts_tupleDescriptor, slot->tts_values, slot->tts_isnull);
if (slot->tts_tuple != NULL) {
tuple->t_self = ((UHeapTuple)slot->tts_tuple)->ctid;
tuple->t_tableOid = ((UHeapTuple)slot->tts_tuple)->table_oid;
}
return tuple;
}
* Clears the contents of the table slot that contains uheap table tuple data.
*/
void UHeapSlotClear(TupleTableSlot *slot)
{
* sanity checks
*/
Assert(slot != NULL);
Assert(TTS_TABLEAM_IS_USTORE(slot));
* Free any old physical tuple belonging to the slot.
*/
if (TTS_SHOULDFREE(slot) && (UHeapTuple)slot->tts_tuple != NULL) {
UHeapFreeTuple(slot->tts_tuple);
slot->tts_tuple = NULL;
slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
}
if (TTS_SHOULDFREEMIN(slot)) {
heap_free_minimal_tuple(slot->tts_mintuple);
slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
}
}
* UHeapSlotGetSomeAttrs
* This function forces the entries of the slot's Datum/isnull
* arrays to be valid at least up through the attnum'th entry.
*
* @param slot:input Tuple Table slot from which attributes are extracted.
* @param attnum: index until which slots attributes are extracted.
*/
void UHeapSlotGetSomeAttrs(TupleTableSlot *slot, int attnum)
{
Assert(TTS_TABLEAM_IS_USTORE(slot));
if (slot->tts_nvalid >= attnum) {
return;
}
UHeapDeformTupleGuts((UHeapTuple)slot->tts_tuple, slot->tts_tupleDescriptor, slot->tts_values, slot->tts_isnull,
attnum);
}
void UHeapSlotFormBatch(TupleTableSlot* slot, VectorBatch* batch, int cur_rows, int attnum)
{
Assert(TTS_TABLEAM_IS_USTORE(slot));
if (slot->tts_nvalid >= attnum) {
return;
}
UHeapTuple utuple = (UHeapTuple)slot->tts_tuple;
TupleDesc rowDesc = slot->tts_tupleDescriptor;
bool isNull = slot->tts_isnull;
UHeapDiskTuple diskTuple = utuple->disk_tuple;
bool hasNull = UHeapDiskTupHasNulls(diskTuple);
FormData_pg_attribute* att = rowDesc->attrs;
int attno;
bits8* bp = diskTuple->data;
long off = diskTuple->t_hoff;
char* tupPtr = (char*)diskTuple;
int nullcount = 0;
int tupleAttrs = UHeapTupleHeaderGetNatts(diskTuple);
bool enableReverseBitmap = NAttrsReserveSpace(tupleAttrs);
WHITEBOX_TEST_STUB(UHEAP_DEFORM_TUPLE_FAILED, WhiteboxDefaultErrorEmit);
int natts = Min(tupleAttrs, attnum);
for (attno = 0; attno < natts; attno++) {
Form_pg_attribute thisatt = &att[attno];
ScalarVector* pVector = &batch->m_arr[attno];
if (hasNull && att_isnull(attno, bp)) {
space reserved for null attributes */
if (enableReverseBitmap && !att_isnull(tupleAttrs + nullcount, bp)) {
off += thisatt->attlen;
}
nullcount++;
pVector->m_vals[cur_rows] = (Datum)0;
SET_NULL(pVector->m_flag[cur_rows]);
pVector->m_const = true;
continue;
}
SET_NOTNULL(pVector->m_flag[cur_rows]);
* If this is a varlena, there might be alignment padding, if it has a
* 4-byte header. Otherwise, there will only be padding if it's not
* pass-by-value.
*/
if (thisatt->attlen == -1) {
off = att_align_pointer(off, thisatt->attalign, -1, tupPtr + off);
} else if (!thisatt->attbyval) {
off = att_align_nominal(off, thisatt->attalign);
}
pVector->m_vals[cur_rows] = fetchatt(thisatt, tupPtr + off);
off = att_addlength_pointer(off, thisatt->attlen, tupPtr + off);
}
* If tuple doesn't have all the atts indicated by tupleDesc, read the
* rest as null
*/
for (; attno < attnum; attno++) {
ScalarVector* pVector = &batch->m_arr[attno];
* The original Code is:
* example code: values[attnum] = (Datum) 0;
* example code: isNulls[attnum] = true;
*/
pVector->m_vals[cur_rows] = heapGetInitDefVal(attno + 1, rowDesc, &isNull);
if (isNull) {
SET_NULL(pVector->m_flag[cur_rows]);
pVector->m_const = true;
} else {
SET_NOTNULL(pVector->m_flag[cur_rows]);
}
}
}
* UHeapSlotAttIsNull
* Detect whether an attribute of the slot is null, without
* actually fetching it.
*
* @param slot: Tabletuple slot
* @para attnum: attribute index that should be checked for null value.
*/
bool UHeapSlotAttIsNull(const TupleTableSlot *slot, int attnum)
{
TupleDesc tupleDesc = slot->tts_tupleDescriptor;
UHeapTuple uhtup = (UHeapTuple)slot->tts_tuple;
Assert(TTS_TABLEAM_IS_USTORE(slot));
* system attributes are handled by heap_attisnull
*/
if (attnum <= 0) {
if (uhtup == NULL) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot extract system attribute from virtual tuple")));
}
return UHeapAttIsNull(uhtup, attnum, tupleDesc);
}
* fast path if desired attribute already cached
*/
if (attnum <= slot->tts_nvalid) {
return slot->tts_isnull[attnum - 1];
}
* return NULL if attnum is out of range according to the tupdesc
*/
if (attnum > tupleDesc->natts) {
return true;
}
* otherwise we had better have a physical tuple (tts_nvalid should equal
* natts in all virtual-tuple cases)
*/
if (uhtup == NULL) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot extract attribute from empty tuple slot")));
}
* The original Code is:
* return heap_attisnull(tuple, attnum, tupleDesc);
*/
return UHeapAttIsNull(uhtup, attnum, tupleDesc);
}
* UHeapSlotGetAllAttrs
* This function forces all the entries of the slot's Datum/isnull
* arrays to be valid. The caller may then extract data directly
* from those arrays instead of using heap_slot_getattr.
*
* @param slot: TableTuple slot from this attributes are extracted
*/
void UHeapSlotGetAllAttrs(TupleTableSlot *slot)
{
Assert(TTS_TABLEAM_IS_USTORE(slot));
if (slot->tts_nvalid == slot->tts_tupleDescriptor->natts) {
return;
}
SlotDeformUTuple(slot, (UHeapTuple)slot->tts_tuple, &slot->tts_off, slot->tts_tupleDescriptor->natts);
}
* UHeapSlotGetAttr
* This function fetches an attribute of the slot's current tuple.
* It is functionally equivalent to heap_getattr, but fetches of
* multiple attributes of the same tuple will be optimized better,
* because we avoid O(N^2) behavior from multiple calls of
* nocachegetattr(), even when attcacheoff isn't usable.
*
* A difference from raw heap_getattr is that attnums beyond the
* slot's tupdesc's last attribute will be considered NULL even
* when the physical tuple is longer than the tupdesc.
*
* @param slot: TableTuple slot from this attribute is extracted
* @param attnum: index of the atribute to be extracted.
* @param isnull: set to true, if the attribute is NULL.
*/
Datum UHeapSlotGetAttr(TupleTableSlot *slot, int attnum, bool *isnull)
{
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
TupleDesc tupleDesc = slot->tts_tupleDescriptor;
UHeapTuple utuple = (UHeapTuple)slot->tts_tuple;
UHeapDiskTuple utup = NULL;
* system attributes are handled by heap_getsysattr
*/
if (attnum <= 0) {
if (utuple == NULL) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot extract system attribute from virtual tuple")));
}
return UHeapGetSysAttr(utuple, InvalidBuffer, attnum, tupleDesc, isnull);
}
* fast path if desired attribute already cached
*/
if (attnum <= slot->tts_nvalid) {
*isnull = slot->tts_isnull[attnum - 1];
return slot->tts_values[attnum - 1];
}
* return NULL if attnum is out of range according to the tupdesc
*/
if (attnum > tupleDesc->natts) {
*isnull = true;
return (Datum)0;
}
* If the attribute's column has been dropped, we force a NULL result.
* This case should not happen in normal use, but it could happen if we
* are executing a plan cached before the column was dropped.
*/
if (tupleDesc->attrs[attnum - 1].attisdropped) {
*isnull = true;
return (Datum)0;
}
* otherwise we had better have a physical tuple (tts_nvalid should equal
* natts in all virtual-tuple cases)
*/
if (utuple == NULL) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot extract attribute from empty tuple slot")));
}
* return NULL if attnum is out of range according to the tuple
*
* (We have to check this separately because of various inheritance and
* table-alteration scenarios: the utuple could be either longer or shorter
* than the tupdesc.)
*/
utup = utuple->disk_tuple;
if (attnum > (int)UHeapTupleHeaderGetNatts(utup)) {
* The original Code is:
* example code: *isnull = true;
* example code: return (Datum) 0;
*/
return heapGetInitDefVal(attnum, tupleDesc, isnull);
}
* check if target attribute is null: no point in groveling through tuple
*/
if (UHeapDiskTupHasNulls(utup) && att_isnull(attnum - 1, utup->data)) {
*isnull = true;
return (Datum)0;
}
* Extract the attribute, along with any preceding attributes.
*/
UHeapSlotGetSomeAttrs(slot, attnum);
* The result is acquired from tts_values array.
*/
*isnull = slot->tts_isnull[attnum - 1];
return slot->tts_values[attnum - 1];
}
* Return a copy of uheap table minimal tuple representing the contents of the slot.
* The copy needs to be palloc'd in the current memory context. The slot
* itself is expected to remain unaffected. It is *not* expected to have
* meaningful "system columns" in the copy. The copy is not be "owned" by
* the slot i.e. the caller has to take responsibility to free memory
* consumed by the slot.
*
* @param slot: slot from which minimal tuple to be copied.
* @return slot's tuple minimal tuple copy
*/
MinimalTuple UHeapSlotCopyMinimalTuple(TupleTableSlot *slot)
{
* sanity checks.
*/
Assert(slot != NULL);
Assert(!TTS_EMPTY(slot));
Assert(slot->tts_tupleDescriptor != NULL);
Assert(TTS_TABLEAM_IS_USTORE(slot));
UHeapSlotGetAllAttrs(slot);
return heap_form_minimal_tuple(slot->tts_tupleDescriptor, slot->tts_values, slot->tts_isnull);
}
* Return a minimal tuple "owned" by the slot. It is slot's responsibility
* to free the memory consumed by the minimal tuple. If the slot can not
* "own" a minimal tuple, it should not implement this callback and should
* set it as NULL.
*
* @param slot: slot from minimal tuple to fetch.
* @return slot's minimal tuple.
*
*/
MinimalTuple UHeapSlotGetMinimalTuple(TupleTableSlot *slot)
{
* sanity checks
*/
Assert(slot != NULL);
Assert(!TTS_EMPTY(slot));
* If we have a minimal physical tuple (local or not) then just return it.
*/
if (slot->tts_mintuple != NULL) {
return slot->tts_mintuple;
}
* Otherwise, copy or build a minimal tuple, and store it into the slot.
*
* We may be called in a context that is shorter-lived than the tuple
* slot, but we have to ensure that the materialized tuple will survive
* anyway.
*/
MemoryContext oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
slot->tts_mintuple = UHeapSlotCopyMinimalTuple(slot);
slot->tts_flags |= TTS_FLAG_SHOULDFREEMIN;
MemoryContextSwitchTo(oldContext);
* Note: we may now have a situation where we have a local minimal tuple
* attached to a virtual or non-local physical tuple. There seems no harm
* in that at the moment, but if any materializes, we should change this
* function to force the slot into minimal-tuple-only state.
*/
return slot->tts_mintuple;
}
* Stores uheap minimal tuple in the TupleTableSlot. Release the current slots buffer and Free's any slot's
* minimal and uheap tuple.
*
* @param mtup: minimal tuple to be stored.
* @param slot: slot to store tuple.
* @param: should_free true if clear the slot's tuple contents by pfree_ext() during ExecClearTuple.
*/
void UHeapSlotStoreMinimalTuple(MinimalTuple mtup, TupleTableSlot *slot, bool shouldFree)
{
* sanity checks
*/
Assert(mtup != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
Assert(TTS_TABLEAM_IS_USTORE(slot));
* Free any old physical tuple belonging to the slot.
*/
if (TTS_SHOULDFREE(slot) && (UHeapTuple)slot->tts_tuple != NULL) {
UHeapFreeTuple(slot->tts_tuple);
slot->tts_tuple = NULL;
}
if (TTS_SHOULDFREEMIN(slot)) {
heap_free_minimal_tuple(slot->tts_mintuple);
}
* Drop the pin on the referenced buffer, if there is one.
*/
if (BufferIsValid(slot->tts_buffer)) {
ReleaseBuffer(slot->tts_buffer);
}
slot->tts_buffer = InvalidBuffer;
* Store the new tuple into the specified slot.
*/
slot->tts_flags &= ~TTS_FLAG_EMPTY;
slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
if (shouldFree) {
slot->tts_flags |= TTS_FLAG_SHOULDFREEMIN;
} else {
slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
}
slot->tts_tuple = &slot->tts_minhdr;
slot->tts_mintuple = mtup;
slot->tts_minhdr.tupTableType = HEAP_TUPLE;
slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
slot->tts_minhdr.t_data = (HeapTupleHeader)((char *)mtup - MINIMAL_TUPLE_OFFSET);
slot->tts_tam_ops = TableAmHeap;
slot->tts_nvalid = 0;
}
* Stores UHeapTuple in the slot, slot contents are cleared prior to storing.
*
* @param utuple: uheap to be stored.
* @param slot: slot inwhich tuple needs to be stored.
* @param should_free: whether slot assumes responsibility of freeing up the tuple.
*/
void UHeapSlotStoreUHeapTuple(UHeapTuple utuple, TupleTableSlot *slot, bool shouldFree, bool batchMode)
{
* sanity checks
*/
Assert(utuple != NULL && utuple->tupTableType == UHEAP_TUPLE);
Assert(slot != NULL && TTS_TABLEAM_IS_USTORE(slot));
Assert(slot->tts_tupleDescriptor != NULL);
if (TTS_SHOULDFREEMIN(slot)) {
heap_free_minimal_tuple(slot->tts_mintuple);
slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
}
UHeapSlotClear(slot);
* Store the new tuple into the specified slot.
*/
slot->tts_flags &= ~TTS_FLAG_EMPTY;
if (shouldFree)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
else
slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
slot->tts_tuple = utuple;
slot->tts_mintuple = NULL;
slot->tts_nvalid = 0;
}
* Make the contents of the uheap table's slot contents solely depend on the slot(make them a local copy),
* and not on underlying external resources like another memory context, buffers etc.
*
* @pram slot: slot to be materialized.
*/
Tuple UHeapMaterialize(TupleTableSlot *slot)
{
Assert(!TTS_EMPTY(slot));
Assert(TTS_TABLEAM_IS_USTORE(slot));
Assert(slot->tts_tupleDescriptor != NULL);
* If we have a regular physical tuple, and it's locally palloc'd, we have
* nothing to do.
*/
if (slot->tts_tuple && TTS_SHOULDFREE(slot)) {
return slot->tts_tuple;
}
* Otherwise, copy or build a physical tuple, and store it into the slot.
*
* We may be called in a context that is shorter-lived than the tuple
* slot, but we have to ensure that the materialized tuple will survive
* anyway.
*/
MemoryContext old_context = MemoryContextSwitchTo(slot->tts_mcxt);
if (slot->tts_tuple != NULL) {
slot->tts_tuple = UHeapCopyTuple((UHeapTuple)slot->tts_tuple);
} else {
slot->tts_tuple = UHeapFormTuple(slot->tts_tupleDescriptor, slot->tts_values, slot->tts_isnull);
}
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
MemoryContextSwitchTo(old_context);
* Have to deform from scratch, otherwise tts_values[] entries could point
* into the non-materialized tuple (which might be gone when accessed).
*/
slot->tts_nvalid = 0;
return slot->tts_tuple;
}