Commit 8f2ea8b7 authored by Tom Lane's avatar Tom Lane

Resurrect heap_deformtuple(), this time implemented as a singly nested

loop over the fields instead of a loop around heap_getattr.  This is
considerably faster (O(N) instead of O(N^2)) when there are nulls or
varlena fields, since those prevent use of attcacheoff.  Replace loops
over heap_getattr with heap_deformtuple in situations where all or most
of the fields have to be fetched, such as printtup and tuptoaster.
Profiling done more than a year ago shows that this should be a nice
win for situations involving many-column tables.
parent af44cac6
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.90 2004/04/01 21:28:43 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.91 2004/06/04 20:35:21 tgl Exp $
* *
* NOTES * NOTES
* The old interface functions have been converted to macros * The old interface functions have been converted to macros
...@@ -37,10 +37,10 @@ ...@@ -37,10 +37,10 @@
*/ */
Size Size
ComputeDataSize(TupleDesc tupleDesc, ComputeDataSize(TupleDesc tupleDesc,
Datum *value, Datum *values,
char *nulls) char *nulls)
{ {
uint32 data_length = 0; Size data_length = 0;
int i; int i;
int numberOfAttributes = tupleDesc->natts; int numberOfAttributes = tupleDesc->natts;
Form_pg_attribute *att = tupleDesc->attrs; Form_pg_attribute *att = tupleDesc->attrs;
...@@ -51,7 +51,7 @@ ComputeDataSize(TupleDesc tupleDesc, ...@@ -51,7 +51,7 @@ ComputeDataSize(TupleDesc tupleDesc,
continue; continue;
data_length = att_align(data_length, att[i]->attalign); data_length = att_align(data_length, att[i]->attalign);
data_length = att_addlength(data_length, att[i]->attlen, value[i]); data_length = att_addlength(data_length, att[i]->attlen, values[i]);
} }
return data_length; return data_length;
...@@ -59,19 +59,20 @@ ComputeDataSize(TupleDesc tupleDesc, ...@@ -59,19 +59,20 @@ ComputeDataSize(TupleDesc tupleDesc,
/* ---------------- /* ----------------
* DataFill * DataFill
*
* Load data portion of a tuple from values/nulls arrays
* ---------------- * ----------------
*/ */
void void
DataFill(char *data, DataFill(char *data,
TupleDesc tupleDesc, TupleDesc tupleDesc,
Datum *value, Datum *values,
char *nulls, char *nulls,
uint16 *infomask, uint16 *infomask,
bits8 *bit) bits8 *bit)
{ {
bits8 *bitP = 0; bits8 *bitP;
int bitmask = 0; int bitmask;
Size data_length;
int i; int i;
int numberOfAttributes = tupleDesc->natts; int numberOfAttributes = tupleDesc->natts;
Form_pg_attribute *att = tupleDesc->attrs; Form_pg_attribute *att = tupleDesc->attrs;
...@@ -81,11 +82,19 @@ DataFill(char *data, ...@@ -81,11 +82,19 @@ DataFill(char *data,
bitP = &bit[-1]; bitP = &bit[-1];
bitmask = CSIGNBIT; bitmask = CSIGNBIT;
} }
else
{
/* just to keep compiler quiet */
bitP = NULL;
bitmask = 0;
}
*infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTENDED); *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTENDED);
for (i = 0; i < numberOfAttributes; i++) for (i = 0; i < numberOfAttributes; i++)
{ {
Size data_length;
if (bit != NULL) if (bit != NULL)
{ {
if (bitmask != CSIGNBIT) if (bitmask != CSIGNBIT)
...@@ -112,33 +121,33 @@ DataFill(char *data, ...@@ -112,33 +121,33 @@ DataFill(char *data,
if (att[i]->attbyval) if (att[i]->attbyval)
{ {
/* pass-by-value */ /* pass-by-value */
store_att_byval(data, value[i], att[i]->attlen); store_att_byval(data, values[i], att[i]->attlen);
data_length = att[i]->attlen; data_length = att[i]->attlen;
} }
else if (att[i]->attlen == -1) else if (att[i]->attlen == -1)
{ {
/* varlena */ /* varlena */
*infomask |= HEAP_HASVARWIDTH; *infomask |= HEAP_HASVARWIDTH;
if (VARATT_IS_EXTERNAL(value[i])) if (VARATT_IS_EXTERNAL(values[i]))
*infomask |= HEAP_HASEXTERNAL; *infomask |= HEAP_HASEXTERNAL;
if (VARATT_IS_COMPRESSED(value[i])) if (VARATT_IS_COMPRESSED(values[i]))
*infomask |= HEAP_HASCOMPRESSED; *infomask |= HEAP_HASCOMPRESSED;
data_length = VARATT_SIZE(DatumGetPointer(value[i])); data_length = VARATT_SIZE(DatumGetPointer(values[i]));
memcpy(data, DatumGetPointer(value[i]), data_length); memcpy(data, DatumGetPointer(values[i]), data_length);
} }
else if (att[i]->attlen == -2) else if (att[i]->attlen == -2)
{ {
/* cstring */ /* cstring */
*infomask |= HEAP_HASVARWIDTH; *infomask |= HEAP_HASVARWIDTH;
data_length = strlen(DatumGetCString(value[i])) + 1; data_length = strlen(DatumGetCString(values[i])) + 1;
memcpy(data, DatumGetPointer(value[i]), data_length); memcpy(data, DatumGetPointer(values[i]), data_length);
} }
else else
{ {
/* fixed-length pass-by-reference */ /* fixed-length pass-by-reference */
Assert(att[i]->attlen > 0); Assert(att[i]->attlen > 0);
data_length = att[i]->attlen; data_length = att[i]->attlen;
memcpy(data, DatumGetPointer(value[i]), data_length); memcpy(data, DatumGetPointer(values[i]), data_length);
} }
data += data_length; data += data_length;
...@@ -160,27 +169,28 @@ heap_attisnull(HeapTuple tup, int attnum) ...@@ -160,27 +169,28 @@ heap_attisnull(HeapTuple tup, int attnum)
if (attnum > (int) tup->t_data->t_natts) if (attnum > (int) tup->t_data->t_natts)
return 1; return 1;
if (HeapTupleNoNulls(tup))
return 0;
if (attnum > 0) if (attnum > 0)
{
if (HeapTupleNoNulls(tup))
return 0;
return att_isnull(attnum - 1, tup->t_data->t_bits); return att_isnull(attnum - 1, tup->t_data->t_bits);
else }
switch (attnum)
{ switch (attnum)
case TableOidAttributeNumber: {
case SelfItemPointerAttributeNumber: case TableOidAttributeNumber:
case ObjectIdAttributeNumber: case SelfItemPointerAttributeNumber:
case MinTransactionIdAttributeNumber: case ObjectIdAttributeNumber:
case MinCommandIdAttributeNumber: case MinTransactionIdAttributeNumber:
case MaxTransactionIdAttributeNumber: case MinCommandIdAttributeNumber:
case MaxCommandIdAttributeNumber: case MaxTransactionIdAttributeNumber:
/* these are never null */ case MaxCommandIdAttributeNumber:
break; /* these are never null */
break;
default:
elog(ERROR, "invalid attnum: %d", attnum); default:
} elog(ERROR, "invalid attnum: %d", attnum);
}
return 0; return 0;
} }
...@@ -202,6 +212,8 @@ heap_attisnull(HeapTuple tup, int attnum) ...@@ -202,6 +212,8 @@ heap_attisnull(HeapTuple tup, int attnum)
* perform well for queries which hit large #'s of tuples. After * perform well for queries which hit large #'s of tuples. After
* you cache the offsets once, examining all the other tuples using * you cache the offsets once, examining all the other tuples using
* the same attribute descriptor will go much quicker. -cim 5/4/91 * the same attribute descriptor will go much quicker. -cim 5/4/91
*
* NOTE: if you need to change this code, see also heap_deformtuple.
* ---------------- * ----------------
*/ */
Datum Datum
...@@ -536,53 +548,18 @@ heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest) ...@@ -536,53 +548,18 @@ heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest)
memcpy((char *) dest->t_data, (char *) src->t_data, src->t_len); memcpy((char *) dest->t_data, (char *) src->t_data, src->t_len);
} }
#ifdef NOT_USED
/* ----------------
* heap_deformtuple
*
* the inverse of heap_formtuple (see below)
* ----------------
*/
void
heap_deformtuple(HeapTuple tuple,
TupleDesc tdesc,
Datum *values,
char *nulls)
{
int i;
int natts;
Assert(HeapTupleIsValid(tuple));
natts = tuple->t_natts;
for (i = 0; i < natts; i++)
{
bool isnull;
values[i] = heap_getattr(tuple,
i + 1,
tdesc,
&isnull);
if (isnull)
nulls[i] = 'n';
else
nulls[i] = ' ';
}
}
#endif
/* ---------------- /* ----------------
* heap_formtuple * heap_formtuple
* *
* constructs a tuple from the given *value and *nulls arrays * construct a tuple from the given values[] and nulls[] arrays
* *
* Null attributes are indicated by a 'n' in the appropriate byte * Null attributes are indicated by a 'n' in the appropriate byte
* of *nulls. Non-null attributes are indicated by a ' ' (space). * of nulls[]. Non-null attributes are indicated by a ' ' (space).
* ---------------- * ----------------
*/ */
HeapTuple HeapTuple
heap_formtuple(TupleDesc tupleDescriptor, heap_formtuple(TupleDesc tupleDescriptor,
Datum *value, Datum *values,
char *nulls) char *nulls)
{ {
HeapTuple tuple; /* return tuple */ HeapTuple tuple; /* return tuple */
...@@ -621,7 +598,7 @@ heap_formtuple(TupleDesc tupleDescriptor, ...@@ -621,7 +598,7 @@ heap_formtuple(TupleDesc tupleDescriptor,
hoff = len = MAXALIGN(len); /* align user data safely */ hoff = len = MAXALIGN(len); /* align user data safely */
len += ComputeDataSize(tupleDescriptor, value, nulls); len += ComputeDataSize(tupleDescriptor, values, nulls);
/* /*
* Allocate and zero the space needed. Note that the tuple body and * Allocate and zero the space needed. Note that the tuple body and
...@@ -651,7 +628,7 @@ heap_formtuple(TupleDesc tupleDescriptor, ...@@ -651,7 +628,7 @@ heap_formtuple(TupleDesc tupleDescriptor,
DataFill((char *) td + hoff, DataFill((char *) td + hoff,
tupleDescriptor, tupleDescriptor,
value, values,
nulls, nulls,
&td->t_infomask, &td->t_infomask,
(hasnull ? td->t_bits : NULL)); (hasnull ? td->t_bits : NULL));
...@@ -664,68 +641,59 @@ heap_formtuple(TupleDesc tupleDescriptor, ...@@ -664,68 +641,59 @@ heap_formtuple(TupleDesc tupleDescriptor,
* *
* forms a new tuple from an old tuple and a set of replacement values. * forms a new tuple from an old tuple and a set of replacement values.
* returns a new palloc'ed tuple. * returns a new palloc'ed tuple.
*
* XXX it is misdesign that this is passed a Relation and not just a
* TupleDesc to describe the tuple structure.
* ---------------- * ----------------
*/ */
HeapTuple HeapTuple
heap_modifytuple(HeapTuple tuple, heap_modifytuple(HeapTuple tuple,
Relation relation, Relation relation,
Datum *replValue, Datum *replValues,
char *replNull, char *replNulls,
char *repl) char *replActions)
{ {
TupleDesc tupleDesc = RelationGetDescr(relation);
int numberOfAttributes = tupleDesc->natts;
int attoff; int attoff;
int numberOfAttributes; Datum *values;
Datum *value;
char *nulls; char *nulls;
bool isNull;
HeapTuple newTuple; HeapTuple newTuple;
/* /*
* sanity checks * allocate and fill values and nulls arrays from either the tuple or
*/
Assert(HeapTupleIsValid(tuple));
Assert(RelationIsValid(relation));
Assert(PointerIsValid(replValue));
Assert(PointerIsValid(replNull));
Assert(PointerIsValid(repl));
numberOfAttributes = RelationGetForm(relation)->relnatts;
/*
* allocate and fill *value and *nulls arrays from either the tuple or
* the repl information, as appropriate. * the repl information, as appropriate.
*
* NOTE: it's debatable whether to use heap_deformtuple() here or
* just heap_getattr() only the non-replaced colums. The latter could
* win if there are many replaced columns and few non-replaced ones.
* However, heap_deformtuple 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.
*/ */
value = (Datum *) palloc(numberOfAttributes * sizeof(Datum)); values = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
nulls = (char *) palloc(numberOfAttributes * sizeof(char)); nulls = (char *) palloc(numberOfAttributes * sizeof(char));
heap_deformtuple(tuple, tupleDesc, values, nulls);
for (attoff = 0; attoff < numberOfAttributes; attoff++) for (attoff = 0; attoff < numberOfAttributes; attoff++)
{ {
if (repl[attoff] == ' ') if (replActions[attoff] == 'r')
{ {
value[attoff] = heap_getattr(tuple, values[attoff] = replValues[attoff];
AttrOffsetGetAttrNumber(attoff), nulls[attoff] = replNulls[attoff];
RelationGetDescr(relation),
&isNull);
nulls[attoff] = (isNull) ? 'n' : ' ';
} }
else if (repl[attoff] == 'r') else if (replActions[attoff] != ' ')
{ elog(ERROR, "unrecognized replace flag: %d",
value[attoff] = replValue[attoff]; (int) replActions[attoff]);
nulls[attoff] = replNull[attoff];
}
else
elog(ERROR, "unrecognized replace flag: %d", (int) repl[attoff]);
} }
/* /*
* create a new tuple from the *values and *nulls arrays * create a new tuple from the values and nulls arrays
*/ */
newTuple = heap_formtuple(RelationGetDescr(relation), newTuple = heap_formtuple(tupleDesc, values, nulls);
value,
nulls);
pfree(value); pfree(values);
pfree(nulls); pfree(nulls);
/* /*
...@@ -735,12 +703,96 @@ heap_modifytuple(HeapTuple tuple, ...@@ -735,12 +703,96 @@ heap_modifytuple(HeapTuple tuple,
newTuple->t_data->t_ctid = tuple->t_data->t_ctid; newTuple->t_data->t_ctid = tuple->t_data->t_ctid;
newTuple->t_self = tuple->t_self; newTuple->t_self = tuple->t_self;
newTuple->t_tableOid = tuple->t_tableOid; newTuple->t_tableOid = tuple->t_tableOid;
if (relation->rd_rel->relhasoids) if (tupleDesc->tdhasoid)
HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple));
return newTuple; return newTuple;
} }
/* ----------------
* heap_deformtuple
*
* Given a tuple, extract data into values/nulls arrays; this is
* the inverse of heap_formtuple.
*
* Storage for the values/nulls arrays is provided by the caller;
* it should be sized according to tupleDesc->natts not tuple->t_natts.
*
* Note that for pass-by-reference datatypes, the pointer placed
* in the Datum will point into the given tuple.
*
* When all or most of a tuple's fields need to be extracted,
* this routine will be significantly quicker than a loop around
* heap_getattr; the loop will become O(N^2) as soon as any
* noncacheable attribute offsets are involved.
* ----------------
*/
void
heap_deformtuple(HeapTuple tuple,
TupleDesc tupleDesc,
Datum *values,
char *nulls)
{
HeapTupleHeader tup = tuple->t_data;
Form_pg_attribute *att = tupleDesc->attrs;
int tdesc_natts = tupleDesc->natts;
int natts; /* number of atts to extract */
int attnum;
char *tp; /* ptr to tuple data */
long off; /* offset in tuple data */
bits8 *bp = tup->t_bits; /* ptr to null bitmask in tuple */
bool slow = false; /* can we use/set attcacheoff? */
natts = tup->t_natts;
/* This min() operation is pure paranoia */
natts = Min(natts, tdesc_natts);
tp = (char *) tup + tup->t_hoff;
off = 0;
for (attnum = 0; attnum < natts; attnum++)
{
if (HeapTupleHasNulls(tuple) && att_isnull(attnum, bp))
{
values[attnum] = (Datum) 0;
nulls[attnum] = 'n';
slow = true; /* can't use attcacheoff anymore */
continue;
}
nulls[attnum] = ' ';
if (!slow && att[attnum]->attcacheoff >= 0)
{
off = att[attnum]->attcacheoff;
}
else
{
off = att_align(off, att[attnum]->attalign);
if (!slow)
att[attnum]->attcacheoff = off;
}
values[attnum] = fetchatt(att[attnum], tp + off);
off = att_addlength(off, att[attnum]->attlen, tp + off);
if (att[attnum]->attlen <= 0)
slow = true; /* can't use attcacheoff anymore */
}
/*
* If tuple doesn't have all the atts indicated by tupleDesc, read
* the rest as null
*/
for (; attnum < tdesc_natts; attnum++)
{
values[attnum] = (Datum) 0;
nulls[attnum] = 'n';
}
}
/* ---------------- /* ----------------
* heap_freetuple * heap_freetuple
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.81 2004/05/26 04:41:03 neilc Exp $ * $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.82 2004/06/04 20:35:21 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -65,6 +65,8 @@ typedef struct ...@@ -65,6 +65,8 @@ typedef struct
TupleDesc attrinfo; /* The attr info we are set up for */ TupleDesc attrinfo; /* The attr info we are set up for */
int nattrs; int nattrs;
PrinttupAttrInfo *myinfo; /* Cached info about each attr */ PrinttupAttrInfo *myinfo; /* Cached info about each attr */
Datum *values; /* preallocated space for deformtuple */
char *nulls;
} DR_printtup; } DR_printtup;
/* ---------------- /* ----------------
...@@ -103,6 +105,8 @@ printtup_create_DR(CommandDest dest, Portal portal) ...@@ -103,6 +105,8 @@ printtup_create_DR(CommandDest dest, Portal portal)
self->attrinfo = NULL; self->attrinfo = NULL;
self->nattrs = 0; self->nattrs = 0;
self->myinfo = NULL; self->myinfo = NULL;
self->values = NULL;
self->nulls = NULL;
return (DestReceiver *) self; return (DestReceiver *) self;
} }
...@@ -243,15 +247,27 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs) ...@@ -243,15 +247,27 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
int16 *formats = myState->portal->formats; int16 *formats = myState->portal->formats;
int i; int i;
/* get rid of any old data */
if (myState->myinfo) if (myState->myinfo)
pfree(myState->myinfo); /* get rid of any old data */ pfree(myState->myinfo);
myState->myinfo = NULL; myState->myinfo = NULL;
if (myState->values)
pfree(myState->values);
myState->values = NULL;
if (myState->nulls)
pfree(myState->nulls);
myState->nulls = NULL;
myState->attrinfo = typeinfo; myState->attrinfo = typeinfo;
myState->nattrs = numAttrs; myState->nattrs = numAttrs;
if (numAttrs <= 0) if (numAttrs <= 0)
return; return;
myState->myinfo = (PrinttupAttrInfo *) myState->myinfo = (PrinttupAttrInfo *)
palloc0(numAttrs * sizeof(PrinttupAttrInfo)); palloc0(numAttrs * sizeof(PrinttupAttrInfo));
myState->values = (Datum *) palloc(numAttrs * sizeof(Datum));
myState->nulls = (char *) palloc(numAttrs * sizeof(char));
for (i = 0; i < numAttrs; i++) for (i = 0; i < numAttrs; i++)
{ {
PrinttupAttrInfo *thisState = myState->myinfo + i; PrinttupAttrInfo *thisState = myState->myinfo + i;
...@@ -297,6 +313,11 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -297,6 +313,11 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
if (myState->attrinfo != typeinfo || myState->nattrs != natts) if (myState->attrinfo != typeinfo || myState->nattrs != natts)
printtup_prepare_info(myState, typeinfo, natts); printtup_prepare_info(myState, typeinfo, natts);
/*
* deconstruct the tuple (faster than a heap_getattr loop)
*/
heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
/* /*
* Prepare a DataRow message * Prepare a DataRow message
*/ */
...@@ -310,12 +331,10 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -310,12 +331,10 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
for (i = 0; i < natts; ++i) for (i = 0; i < natts; ++i)
{ {
PrinttupAttrInfo *thisState = myState->myinfo + i; PrinttupAttrInfo *thisState = myState->myinfo + i;
Datum origattr, Datum origattr = myState->values[i],
attr; attr;
bool isnull;
origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull); if (myState->nulls[i] == 'n')
if (isnull)
{ {
pq_sendint(&buf, -1, 4); pq_sendint(&buf, -1, 4);
continue; continue;
...@@ -383,6 +402,11 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -383,6 +402,11 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
if (myState->attrinfo != typeinfo || myState->nattrs != natts) if (myState->attrinfo != typeinfo || myState->nattrs != natts)
printtup_prepare_info(myState, typeinfo, natts); printtup_prepare_info(myState, typeinfo, natts);
/*
* deconstruct the tuple (faster than a heap_getattr loop)
*/
heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
/* /*
* tell the frontend to expect new tuple data (in ASCII style) * tell the frontend to expect new tuple data (in ASCII style)
*/ */
...@@ -395,7 +419,7 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -395,7 +419,7 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
k = 1 << 7; k = 1 << 7;
for (i = 0; i < natts; ++i) for (i = 0; i < natts; ++i)
{ {
if (!heap_attisnull(tuple, i + 1)) if (myState->nulls[i] != 'n')
j |= k; /* set bit if not null */ j |= k; /* set bit if not null */
k >>= 1; k >>= 1;
if (k == 0) /* end of byte? */ if (k == 0) /* end of byte? */
...@@ -414,13 +438,11 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -414,13 +438,11 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
for (i = 0; i < natts; ++i) for (i = 0; i < natts; ++i)
{ {
PrinttupAttrInfo *thisState = myState->myinfo + i; PrinttupAttrInfo *thisState = myState->myinfo + i;
Datum origattr, Datum origattr = myState->values[i],
attr; attr;
bool isnull;
char *outputstr; char *outputstr;
origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull); if (myState->nulls[i] == 'n')
if (isnull)
continue; continue;
Assert(thisState->format == 0); Assert(thisState->format == 0);
...@@ -461,6 +483,13 @@ printtup_shutdown(DestReceiver *self) ...@@ -461,6 +483,13 @@ printtup_shutdown(DestReceiver *self)
if (myState->myinfo) if (myState->myinfo)
pfree(myState->myinfo); pfree(myState->myinfo);
myState->myinfo = NULL; myState->myinfo = NULL;
if (myState->values)
pfree(myState->values);
myState->values = NULL;
if (myState->nulls)
pfree(myState->nulls);
myState->nulls = NULL;
myState->attrinfo = NULL; myState->attrinfo = NULL;
} }
...@@ -587,6 +616,11 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -587,6 +616,11 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
if (myState->attrinfo != typeinfo || myState->nattrs != natts) if (myState->attrinfo != typeinfo || myState->nattrs != natts)
printtup_prepare_info(myState, typeinfo, natts); printtup_prepare_info(myState, typeinfo, natts);
/*
* deconstruct the tuple (faster than a heap_getattr loop)
*/
heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
/* /*
* tell the frontend to expect new tuple data (in binary style) * tell the frontend to expect new tuple data (in binary style)
*/ */
...@@ -599,7 +633,7 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -599,7 +633,7 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
k = 1 << 7; k = 1 << 7;
for (i = 0; i < natts; ++i) for (i = 0; i < natts; ++i)
{ {
if (!heap_attisnull(tuple, i + 1)) if (myState->nulls[i] != 'n')
j |= k; /* set bit if not null */ j |= k; /* set bit if not null */
k >>= 1; k >>= 1;
if (k == 0) /* end of byte? */ if (k == 0) /* end of byte? */
...@@ -618,13 +652,11 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -618,13 +652,11 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
for (i = 0; i < natts; ++i) for (i = 0; i < natts; ++i)
{ {
PrinttupAttrInfo *thisState = myState->myinfo + i; PrinttupAttrInfo *thisState = myState->myinfo + i;
Datum origattr, Datum origattr = myState->values[i],
attr; attr;
bool isnull;
bytea *outputbytes; bytea *outputbytes;
origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull); if (myState->nulls[i] == 'n')
if (isnull)
continue; continue;
Assert(thisState->format == 1); Assert(thisState->format == 1);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.41 2003/11/29 19:51:40 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.42 2004/06/04 20:35:21 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -281,15 +281,26 @@ toast_delete(Relation rel, HeapTuple oldtup) ...@@ -281,15 +281,26 @@ toast_delete(Relation rel, HeapTuple oldtup)
Form_pg_attribute *att; Form_pg_attribute *att;
int numAttrs; int numAttrs;
int i; int i;
Datum value; Datum toast_values[MaxHeapAttributeNumber];
bool isnull; char toast_nulls[MaxHeapAttributeNumber];
/* /*
* Get the tuple descriptor, the number of and attribute descriptors. * Get the tuple descriptor and break down the tuple into fields.
*
* NOTE: it's debatable whether to use heap_deformtuple() here or
* just heap_getattr() only the varlena columns. The latter could
* win if there are few varlena columns and many non-varlena ones.
* However, heap_deformtuple costs only O(N) while the heap_getattr
* way would cost O(N^2) if there are many varlena columns, so it
* seems better to err on the side of linear cost. (We won't even
* be here unless there's at least one varlena column, by the way.)
*/ */
tupleDesc = rel->rd_att; tupleDesc = rel->rd_att;
numAttrs = tupleDesc->natts;
att = tupleDesc->attrs; att = tupleDesc->attrs;
numAttrs = tupleDesc->natts;
Assert(numAttrs <= MaxHeapAttributeNumber);
heap_deformtuple(oldtup, tupleDesc, toast_values, toast_nulls);
/* /*
* Check for external stored attributes and delete them from the * Check for external stored attributes and delete them from the
...@@ -299,8 +310,9 @@ toast_delete(Relation rel, HeapTuple oldtup) ...@@ -299,8 +310,9 @@ toast_delete(Relation rel, HeapTuple oldtup)
{ {
if (att[i]->attlen == -1) if (att[i]->attlen == -1)
{ {
value = heap_getattr(oldtup, i + 1, tupleDesc, &isnull); Datum value = toast_values[i];
if (!isnull && VARATT_IS_EXTERNAL(value))
if (toast_nulls[i] != 'n' && VARATT_IS_EXTERNAL(value))
toast_delete_datum(rel, value); toast_delete_datum(rel, value);
} }
} }
...@@ -321,8 +333,6 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup) ...@@ -321,8 +333,6 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
Form_pg_attribute *att; Form_pg_attribute *att;
int numAttrs; int numAttrs;
int i; int i;
bool old_isnull;
bool new_isnull;
bool need_change = false; bool need_change = false;
bool need_free = false; bool need_free = false;
...@@ -333,18 +343,24 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup) ...@@ -333,18 +343,24 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
char toast_action[MaxHeapAttributeNumber]; char toast_action[MaxHeapAttributeNumber];
char toast_nulls[MaxHeapAttributeNumber]; char toast_nulls[MaxHeapAttributeNumber];
char toast_oldnulls[MaxHeapAttributeNumber];
Datum toast_values[MaxHeapAttributeNumber]; Datum toast_values[MaxHeapAttributeNumber];
Datum toast_oldvalues[MaxHeapAttributeNumber];
int32 toast_sizes[MaxHeapAttributeNumber]; int32 toast_sizes[MaxHeapAttributeNumber];
bool toast_free[MaxHeapAttributeNumber]; bool toast_free[MaxHeapAttributeNumber];
bool toast_delold[MaxHeapAttributeNumber]; bool toast_delold[MaxHeapAttributeNumber];
/* /*
* Get the tuple descriptor, the number of and attribute descriptors * Get the tuple descriptor and break down the tuple(s) into fields.
* and the location of the tuple values.
*/ */
tupleDesc = rel->rd_att; tupleDesc = rel->rd_att;
numAttrs = tupleDesc->natts;
att = tupleDesc->attrs; att = tupleDesc->attrs;
numAttrs = tupleDesc->natts;
Assert(numAttrs <= MaxHeapAttributeNumber);
heap_deformtuple(newtup, tupleDesc, toast_values, toast_nulls);
if (oldtup != NULL)
heap_deformtuple(oldtup, tupleDesc, toast_oldvalues, toast_oldnulls);
/* ---------- /* ----------
* Then collect information about the values given * Then collect information about the values given
...@@ -353,12 +369,15 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup) ...@@ -353,12 +369,15 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
* ' ' default handling * ' ' default handling
* 'p' already processed --- don't touch it * 'p' already processed --- don't touch it
* 'x' incompressible, but OK to move off * 'x' incompressible, but OK to move off
*
* NOTE: toast_sizes[i] is only made valid for varlena attributes with
* toast_action[i] different from 'p'.
* ---------- * ----------
*/ */
memset(toast_action, ' ', numAttrs * sizeof(char)); memset(toast_action, ' ', numAttrs * sizeof(char));
memset(toast_nulls, ' ', numAttrs * sizeof(char));
memset(toast_free, 0, numAttrs * sizeof(bool)); memset(toast_free, 0, numAttrs * sizeof(bool));
memset(toast_delold, 0, numAttrs * sizeof(bool)); memset(toast_delold, 0, numAttrs * sizeof(bool));
for (i = 0; i < numAttrs; i++) for (i = 0; i < numAttrs; i++)
{ {
varattrib *old_value; varattrib *old_value;
...@@ -369,27 +388,24 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup) ...@@ -369,27 +388,24 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
/* /*
* For UPDATE get the old and new values of this attribute * For UPDATE get the old and new values of this attribute
*/ */
old_value = (varattrib *) DatumGetPointer( old_value = (varattrib *) DatumGetPointer(toast_oldvalues[i]);
heap_getattr(oldtup, i + 1, tupleDesc, &old_isnull));
toast_values[i] =
heap_getattr(newtup, i + 1, tupleDesc, &new_isnull);
new_value = (varattrib *) DatumGetPointer(toast_values[i]); new_value = (varattrib *) DatumGetPointer(toast_values[i]);
/* /*
* If the old value is an external stored one, check if it has * If the old value is an external stored one, check if it has
* changed so we have to delete it later. * changed so we have to delete it later.
*/ */
if (!old_isnull && att[i]->attlen == -1 && if (att[i]->attlen == -1 && toast_oldnulls[i] != 'n' &&
VARATT_IS_EXTERNAL(old_value)) VARATT_IS_EXTERNAL(old_value))
{ {
if (new_isnull || !VARATT_IS_EXTERNAL(new_value) || if (toast_nulls[i] == 'n' || !VARATT_IS_EXTERNAL(new_value) ||
old_value->va_content.va_external.va_valueid != old_value->va_content.va_external.va_valueid !=
new_value->va_content.va_external.va_valueid || new_value->va_content.va_external.va_valueid ||
old_value->va_content.va_external.va_toastrelid != old_value->va_content.va_external.va_toastrelid !=
new_value->va_content.va_external.va_toastrelid) new_value->va_content.va_external.va_toastrelid)
{ {
/* /*
* The old external store value isn't needed any more * The old external stored value isn't needed any more
* after the update * after the update
*/ */
toast_delold[i] = true; toast_delold[i] = true;
...@@ -413,23 +429,21 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup) ...@@ -413,23 +429,21 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
/* /*
* For INSERT simply get the new value * For INSERT simply get the new value
*/ */
toast_values[i] = new_value = (varattrib *) DatumGetPointer(toast_values[i]);
heap_getattr(newtup, i + 1, tupleDesc, &new_isnull);
} }
/* /*
* Handle NULL attributes * Handle NULL attributes
*/ */
if (new_isnull) if (toast_nulls[i] == 'n')
{ {
toast_action[i] = 'p'; toast_action[i] = 'p';
toast_nulls[i] = 'n';
has_nulls = true; has_nulls = true;
continue; continue;
} }
/* /*
* Now look at varsize attributes * Now look at varlena attributes
*/ */
if (att[i]->attlen == -1) if (att[i]->attlen == -1)
{ {
...@@ -461,10 +475,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup) ...@@ -461,10 +475,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
else else
{ {
/* /*
* Not a variable size attribute, plain storage always * Not a varlena attribute, plain storage always
*/ */
toast_action[i] = 'p'; toast_action[i] = 'p';
toast_sizes[i] = att[i]->attlen;
} }
} }
...@@ -768,8 +781,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup) ...@@ -768,8 +781,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
if (need_delold) if (need_delold)
for (i = 0; i < numAttrs; i++) for (i = 0; i < numAttrs; i++)
if (toast_delold[i]) if (toast_delold[i])
toast_delete_datum(rel, toast_delete_datum(rel, toast_oldvalues[i]);
heap_getattr(oldtup, i + 1, tupleDesc, &old_isnull));
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.109 2004/06/02 21:01:08 momjian Exp $ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.110 2004/06/04 20:35:21 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2342,10 +2342,10 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) ...@@ -2342,10 +2342,10 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
newslot = MakeTupleTableSlot(); newslot = MakeTupleTableSlot();
ExecSetSlotDescriptor(newslot, newTupDesc, false); ExecSetSlotDescriptor(newslot, newTupDesc, false);
/* Preallocate values/nulls arrays (+1 in case natts==0) */ /* Preallocate values/nulls arrays */
i = Max(newTupDesc->natts, oldTupDesc->natts); i = Max(newTupDesc->natts, oldTupDesc->natts);
values = (Datum *) palloc(i * sizeof(Datum) + 1); values = (Datum *) palloc(i * sizeof(Datum));
nulls = (char *) palloc(i * sizeof(char) + 1); nulls = (char *) palloc(i * sizeof(char));
memset(values, 0, i * sizeof(Datum)); memset(values, 0, i * sizeof(Datum));
memset(nulls, 'n', i * sizeof(char)); memset(nulls, 'n', i * sizeof(char));
...@@ -2363,24 +2363,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) ...@@ -2363,24 +2363,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
* Extract data from old tuple. We can force to null any * Extract data from old tuple. We can force to null any
* columns that are deleted according to the new tuple. * columns that are deleted according to the new tuple.
*/ */
int natts = oldTupDesc->natts; int natts = newTupDesc->natts;
bool isNull;
heap_deformtuple(tuple, oldTupDesc, values, nulls);
for (i = 0; i < natts; i++) for (i = 0; i < natts; i++)
{ {
if (newTupDesc->attrs[i]->attisdropped) if (newTupDesc->attrs[i]->attisdropped)
nulls[i] = 'n'; nulls[i] = 'n';
else
{
values[i] = heap_getattr(tuple,
i + 1,
oldTupDesc,
&isNull);
if (isNull)
nulls[i] = 'n';
else
nulls[i] = ' ';
}
} }
/* /*
...@@ -2393,6 +2383,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) ...@@ -2393,6 +2383,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
foreach(l, tab->newvals) foreach(l, tab->newvals)
{ {
NewColumnValue *ex = lfirst(l); NewColumnValue *ex = lfirst(l);
bool isNull;
values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate, values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate,
econtext, econtext,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.57 2004/05/26 04:41:12 neilc Exp $ * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.58 2004/06/04 20:35:21 tgl Exp $
* *
* DESCRIPTION * DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the * The "DefineFoo" routines take the parse tree and pick out the
...@@ -1329,12 +1329,8 @@ AlterDomainNotNull(List *names, bool notNull) ...@@ -1329,12 +1329,8 @@ AlterDomainNotNull(List *names, bool notNull)
for (i = 0; i < rtc->natts; i++) for (i = 0; i < rtc->natts; i++)
{ {
int attnum = rtc->atts[i]; int attnum = rtc->atts[i];
Datum d;
bool isNull;
d = heap_getattr(tuple, attnum, tupdesc, &isNull); if (heap_attisnull(tuple, attnum))
if (isNull)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NOT_NULL_VIOLATION), (errcode(ERRCODE_NOT_NULL_VIOLATION),
errmsg("column \"%s\" of table \"%s\" contains null values", errmsg("column \"%s\" of table \"%s\" contains null values",
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.40 2004/05/26 04:41:14 neilc Exp $ * $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.41 2004/06/04 20:35:21 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -246,12 +246,15 @@ ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot) ...@@ -246,12 +246,15 @@ ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
TupleDesc cleanTupType; TupleDesc cleanTupType;
TupleDesc tupType; TupleDesc tupType;
int cleanLength; int cleanLength;
bool isNull;
int i; int i;
Datum *values; Datum *values;
char *nulls; char *nulls;
Datum *old_values;
char *old_nulls;
Datum values_array[64]; Datum values_array[64];
Datum old_values_array[64];
char nulls_array[64]; char nulls_array[64];
char old_nulls_array[64];
/* /*
* get info from the slot and the junk filter * get info from the slot and the junk filter
...@@ -265,11 +268,15 @@ ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot) ...@@ -265,11 +268,15 @@ ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
/* /*
* Create the arrays that will hold the attribute values and the null * Create the arrays that will hold the attribute values and the null
* information for the new "clean" tuple. * information for the old tuple and new "clean" tuple.
* *
* Note: we use memory on the stack to optimize things when we are * Note: we use memory on the stack to optimize things when we are
* dealing with a small number of attributes. for large tuples we just * dealing with a small number of attributes. for large tuples we just
* use palloc. * use palloc.
*
* Note: we could use just one set of arrays if we were willing to
* assume that the resno mapping is monotonic... I think it is, but
* won't take the risk of breaking things right now.
*/ */
if (cleanLength > 64) if (cleanLength > 64)
{ {
...@@ -281,36 +288,52 @@ ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot) ...@@ -281,36 +288,52 @@ ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
values = values_array; values = values_array;
nulls = nulls_array; nulls = nulls_array;
} }
if (tupType->natts > 64)
{
old_values = (Datum *) palloc(tupType->natts * sizeof(Datum));
old_nulls = (char *) palloc(tupType->natts * sizeof(char));
}
else
{
old_values = old_values_array;
old_nulls = old_nulls_array;
}
/* /*
* Exctract one by one all the values of the "clean" tuple. * Extract all the values of the old tuple.
*/
heap_deformtuple(tuple, tupType, old_values, old_nulls);
/*
* Transpose into proper fields of the new tuple.
*/ */
for (i = 0; i < cleanLength; i++) for (i = 0; i < cleanLength; i++)
{ {
values[i] = heap_getattr(tuple, cleanMap[i], tupType, &isNull); int j = cleanMap[i] - 1;
if (isNull) values[i] = old_values[j];
nulls[i] = 'n'; nulls[i] = old_nulls[j];
else
nulls[i] = ' ';
} }
/* /*
* Now form the new tuple. * Now form the new tuple.
*/ */
cleanTuple = heap_formtuple(cleanTupType, cleanTuple = heap_formtuple(cleanTupType, values, nulls);
values,
nulls);
/* /*
* We are done. Free any space allocated for 'values' and 'nulls' and * We are done. Free any space allocated for 'values' and 'nulls' and
* return the new tuple. * return the new tuple.
*/ */
if (cleanLength > 64) if (values != values_array)
{ {
pfree(values); pfree(values);
pfree(nulls); pfree(nulls);
} }
if (old_values != old_values_array)
{
pfree(old_values);
pfree(old_nulls);
}
return cleanTuple; return cleanTuple;
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.115 2004/05/30 23:40:26 neilc Exp $ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.116 2004/06/04 20:35:21 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -427,7 +427,6 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, ...@@ -427,7 +427,6 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
int numberOfAttributes; int numberOfAttributes;
Datum *v; Datum *v;
char *n; char *n;
bool isnull;
int i; int i;
if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL) if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL)
...@@ -448,11 +447,7 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, ...@@ -448,11 +447,7 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
n = (char *) palloc(numberOfAttributes * sizeof(char)); n = (char *) palloc(numberOfAttributes * sizeof(char));
/* fetch old values and nulls */ /* fetch old values and nulls */
for (i = 0; i < numberOfAttributes; i++) heap_deformtuple(tuple, rel->rd_att, v, n);
{
v[i] = heap_getattr(tuple, i + 1, rel->rd_att, &isnull);
n[i] = (isnull) ? 'n' : ' ';
}
/* replace values and nulls */ /* replace values and nulls */
for (i = 0; i < natts; i++) for (i = 0; i < natts; i++)
...@@ -474,7 +469,7 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, ...@@ -474,7 +469,7 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
mtuple->t_data->t_ctid = tuple->t_data->t_ctid; mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
mtuple->t_self = tuple->t_self; mtuple->t_self = tuple->t_self;
mtuple->t_tableOid = tuple->t_tableOid; mtuple->t_tableOid = tuple->t_tableOid;
if (rel->rd_rel->relhasoids) if (rel->rd_att->tdhasoid)
HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple)); HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
} }
else else
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.89 2004/04/21 18:24:26 tgl Exp $ * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.90 2004/06/04 20:35:21 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -184,9 +184,9 @@ extern XLogRecPtr log_heap_move(Relation reln, Buffer oldbuf, ...@@ -184,9 +184,9 @@ extern XLogRecPtr log_heap_move(Relation reln, Buffer oldbuf,
Buffer newbuf, HeapTuple newtup); Buffer newbuf, HeapTuple newtup);
/* in common/heaptuple.c */ /* in common/heaptuple.c */
extern Size ComputeDataSize(TupleDesc tupleDesc, Datum *value, char *nulls); extern Size ComputeDataSize(TupleDesc tupleDesc, Datum *values, char *nulls);
extern void DataFill(char *data, TupleDesc tupleDesc, extern void DataFill(char *data, TupleDesc tupleDesc,
Datum *value, char *nulls, uint16 *infomask, Datum *values, char *nulls, uint16 *infomask,
bits8 *bit); bits8 *bit);
extern int heap_attisnull(HeapTuple tup, int attnum); extern int heap_attisnull(HeapTuple tup, int attnum);
extern Datum nocachegetattr(HeapTuple tup, int attnum, extern Datum nocachegetattr(HeapTuple tup, int attnum,
...@@ -194,9 +194,14 @@ extern Datum nocachegetattr(HeapTuple tup, int attnum, ...@@ -194,9 +194,14 @@ extern Datum nocachegetattr(HeapTuple tup, int attnum,
extern HeapTuple heap_copytuple(HeapTuple tuple); extern HeapTuple heap_copytuple(HeapTuple tuple);
extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest); extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest);
extern HeapTuple heap_formtuple(TupleDesc tupleDescriptor, extern HeapTuple heap_formtuple(TupleDesc tupleDescriptor,
Datum *value, char *nulls); Datum *values, char *nulls);
extern HeapTuple heap_modifytuple(HeapTuple tuple, extern HeapTuple heap_modifytuple(HeapTuple tuple,
Relation relation, Datum *replValue, char *replNull, char *repl); Relation relation,
Datum *replValues,
char *replNulls,
char *replActions);
extern void heap_deformtuple(HeapTuple tuple, TupleDesc tupleDesc,
Datum *values, char *nulls);
extern void heap_freetuple(HeapTuple tuple); extern void heap_freetuple(HeapTuple tuple);
extern HeapTuple heap_addheader(int natts, bool withoid, Size structlen, void *structure); extern HeapTuple heap_addheader(int natts, bool withoid, Size structlen, void *structure);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment