1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-28 23:42:10 +03:00

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.
This commit is contained in:
Tom Lane
2004-06-04 20:35:21 +00:00
parent af44cac6ef
commit 8f2ea8b7b5
8 changed files with 313 additions and 207 deletions

View File

@ -9,7 +9,7 @@
*
*
* 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
* The old interface functions have been converted to macros
@ -37,10 +37,10 @@
*/
Size
ComputeDataSize(TupleDesc tupleDesc,
Datum *value,
Datum *values,
char *nulls)
{
uint32 data_length = 0;
Size data_length = 0;
int i;
int numberOfAttributes = tupleDesc->natts;
Form_pg_attribute *att = tupleDesc->attrs;
@ -51,7 +51,7 @@ ComputeDataSize(TupleDesc tupleDesc,
continue;
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;
@ -59,19 +59,20 @@ ComputeDataSize(TupleDesc tupleDesc,
/* ----------------
* DataFill
*
* Load data portion of a tuple from values/nulls arrays
* ----------------
*/
void
DataFill(char *data,
TupleDesc tupleDesc,
Datum *value,
Datum *values,
char *nulls,
uint16 *infomask,
bits8 *bit)
{
bits8 *bitP = 0;
int bitmask = 0;
Size data_length;
bits8 *bitP;
int bitmask;
int i;
int numberOfAttributes = tupleDesc->natts;
Form_pg_attribute *att = tupleDesc->attrs;
@ -81,11 +82,19 @@ DataFill(char *data,
bitP = &bit[-1];
bitmask = CSIGNBIT;
}
else
{
/* just to keep compiler quiet */
bitP = NULL;
bitmask = 0;
}
*infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTENDED);
for (i = 0; i < numberOfAttributes; i++)
{
Size data_length;
if (bit != NULL)
{
if (bitmask != CSIGNBIT)
@ -112,33 +121,33 @@ DataFill(char *data,
if (att[i]->attbyval)
{
/* 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;
}
else if (att[i]->attlen == -1)
{
/* varlena */
*infomask |= HEAP_HASVARWIDTH;
if (VARATT_IS_EXTERNAL(value[i]))
if (VARATT_IS_EXTERNAL(values[i]))
*infomask |= HEAP_HASEXTERNAL;
if (VARATT_IS_COMPRESSED(value[i]))
if (VARATT_IS_COMPRESSED(values[i]))
*infomask |= HEAP_HASCOMPRESSED;
data_length = VARATT_SIZE(DatumGetPointer(value[i]));
memcpy(data, DatumGetPointer(value[i]), data_length);
data_length = VARATT_SIZE(DatumGetPointer(values[i]));
memcpy(data, DatumGetPointer(values[i]), data_length);
}
else if (att[i]->attlen == -2)
{
/* cstring */
*infomask |= HEAP_HASVARWIDTH;
data_length = strlen(DatumGetCString(value[i])) + 1;
memcpy(data, DatumGetPointer(value[i]), data_length);
data_length = strlen(DatumGetCString(values[i])) + 1;
memcpy(data, DatumGetPointer(values[i]), data_length);
}
else
{
/* fixed-length pass-by-reference */
Assert(att[i]->attlen > 0);
data_length = att[i]->attlen;
memcpy(data, DatumGetPointer(value[i]), data_length);
memcpy(data, DatumGetPointer(values[i]), data_length);
}
data += data_length;
@ -160,27 +169,28 @@ heap_attisnull(HeapTuple tup, int attnum)
if (attnum > (int) tup->t_data->t_natts)
return 1;
if (HeapTupleNoNulls(tup))
return 0;
if (attnum > 0)
{
if (HeapTupleNoNulls(tup))
return 0;
return att_isnull(attnum - 1, tup->t_data->t_bits);
else
switch (attnum)
{
case TableOidAttributeNumber:
case SelfItemPointerAttributeNumber:
case ObjectIdAttributeNumber:
case MinTransactionIdAttributeNumber:
case MinCommandIdAttributeNumber:
case MaxTransactionIdAttributeNumber:
case MaxCommandIdAttributeNumber:
/* these are never null */
break;
}
default:
elog(ERROR, "invalid attnum: %d", attnum);
}
switch (attnum)
{
case TableOidAttributeNumber:
case SelfItemPointerAttributeNumber:
case ObjectIdAttributeNumber:
case MinTransactionIdAttributeNumber:
case MinCommandIdAttributeNumber:
case MaxTransactionIdAttributeNumber:
case MaxCommandIdAttributeNumber:
/* these are never null */
break;
default:
elog(ERROR, "invalid attnum: %d", attnum);
}
return 0;
}
@ -202,6 +212,8 @@ heap_attisnull(HeapTuple tup, int attnum)
* perform well for queries which hit large #'s of tuples. After
* you cache the offsets once, examining all the other tuples using
* the same attribute descriptor will go much quicker. -cim 5/4/91
*
* NOTE: if you need to change this code, see also heap_deformtuple.
* ----------------
*/
Datum
@ -536,53 +548,18 @@ heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest)
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
*
* 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
* of *nulls. Non-null attributes are indicated by a ' ' (space).
* of nulls[]. Non-null attributes are indicated by a ' ' (space).
* ----------------
*/
HeapTuple
heap_formtuple(TupleDesc tupleDescriptor,
Datum *value,
Datum *values,
char *nulls)
{
HeapTuple tuple; /* return tuple */
@ -621,7 +598,7 @@ heap_formtuple(TupleDesc tupleDescriptor,
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
@ -651,7 +628,7 @@ heap_formtuple(TupleDesc tupleDescriptor,
DataFill((char *) td + hoff,
tupleDescriptor,
value,
values,
nulls,
&td->t_infomask,
(hasnull ? td->t_bits : NULL));
@ -664,68 +641,59 @@ heap_formtuple(TupleDesc tupleDescriptor,
*
* forms a new tuple from an old tuple and a set of replacement values.
* 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
heap_modifytuple(HeapTuple tuple,
Relation relation,
Datum *replValue,
char *replNull,
char *repl)
Datum *replValues,
char *replNulls,
char *replActions)
{
TupleDesc tupleDesc = RelationGetDescr(relation);
int numberOfAttributes = tupleDesc->natts;
int attoff;
int numberOfAttributes;
Datum *value;
Datum *values;
char *nulls;
bool isNull;
HeapTuple newTuple;
/*
* sanity checks
*/
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
* allocate and fill values and nulls arrays from either the tuple or
* 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));
heap_deformtuple(tuple, tupleDesc, values, nulls);
for (attoff = 0; attoff < numberOfAttributes; attoff++)
{
if (repl[attoff] == ' ')
if (replActions[attoff] == 'r')
{
value[attoff] = heap_getattr(tuple,
AttrOffsetGetAttrNumber(attoff),
RelationGetDescr(relation),
&isNull);
nulls[attoff] = (isNull) ? 'n' : ' ';
values[attoff] = replValues[attoff];
nulls[attoff] = replNulls[attoff];
}
else if (repl[attoff] == 'r')
{
value[attoff] = replValue[attoff];
nulls[attoff] = replNull[attoff];
}
else
elog(ERROR, "unrecognized replace flag: %d", (int) repl[attoff]);
else if (replActions[attoff] != ' ')
elog(ERROR, "unrecognized replace flag: %d",
(int) replActions[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),
value,
nulls);
newTuple = heap_formtuple(tupleDesc, values, nulls);
pfree(value);
pfree(values);
pfree(nulls);
/*
@ -735,12 +703,96 @@ heap_modifytuple(HeapTuple tuple,
newTuple->t_data->t_ctid = tuple->t_data->t_ctid;
newTuple->t_self = tuple->t_self;
newTuple->t_tableOid = tuple->t_tableOid;
if (relation->rd_rel->relhasoids)
if (tupleDesc->tdhasoid)
HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple));
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