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:
@ -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
|
||||
|
Reference in New Issue
Block a user