1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-10 17:42:29 +03:00

Avoid O(N^2) overhead in repeated nocachegetattr calls when columns of

a tuple are being accessed via ExecEvalVar and the attcacheoff shortcut
isn't usable (due to nulls and/or varlena columns).  To do this, cache
Datums extracted from a tuple in the associated TupleTableSlot.
Also some code cleanup in and around the TupleTable handling.
Atsushi Ogawa with some kibitzing by Tom Lane.
This commit is contained in:
Tom Lane
2005-03-14 04:41:13 +00:00
parent d1022ce3a1
commit a9b05bdc83
8 changed files with 378 additions and 300 deletions

View File

@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.96 2005/01/27 23:23:49 neilc Exp $
* $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.97 2005/03/14 04:41:12 tgl Exp $
*
* NOTES
* The old interface functions have been converted to macros
@@ -23,6 +23,7 @@
#include "access/heapam.h"
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "executor/tuptable.h"
/* ----------------------------------------------------------------
@@ -751,6 +752,7 @@ heap_deformtuple(HeapTuple tuple,
char *nulls)
{
HeapTupleHeader tup = tuple->t_data;
bool hasnulls = HeapTupleHasNulls(tuple);
Form_pg_attribute *att = tupleDesc->attrs;
int tdesc_natts = tupleDesc->natts;
int natts; /* number of atts to extract */
@@ -775,7 +777,9 @@ heap_deformtuple(HeapTuple tuple,
for (attnum = 0; attnum < natts; attnum++)
{
if (HeapTupleHasNulls(tuple) && att_isnull(attnum, bp))
Form_pg_attribute thisatt = att[attnum];
if (hasnulls && att_isnull(attnum, bp))
{
values[attnum] = (Datum) 0;
nulls[attnum] = 'n';
@@ -785,21 +789,21 @@ heap_deformtuple(HeapTuple tuple,
nulls[attnum] = ' ';
if (!slow && att[attnum]->attcacheoff >= 0)
off = att[attnum]->attcacheoff;
if (!slow && thisatt->attcacheoff >= 0)
off = thisatt->attcacheoff;
else
{
off = att_align(off, att[attnum]->attalign);
off = att_align(off, thisatt->attalign);
if (!slow)
att[attnum]->attcacheoff = off;
thisatt->attcacheoff = off;
}
values[attnum] = fetchatt(att[attnum], tp + off);
values[attnum] = fetchatt(thisatt, tp + off);
off = att_addlength(off, att[attnum]->attlen, tp + off);
off = att_addlength(off, thisatt->attlen, tp + off);
if (att[attnum]->attlen <= 0)
if (thisatt->attlen <= 0)
slow = true; /* can't use attcacheoff anymore */
}
@@ -814,6 +818,177 @@ heap_deformtuple(HeapTuple tuple,
}
}
/* ----------------
* slot_deformtuple
*
* Given a TupleTableSlot, extract data into cache_values array
* from the slot's tuple.
*
* This is essentially an incremental version of heap_deformtuple:
* on each call we extract attributes up to the one needed, without
* re-computing information about previously extracted attributes.
* slot->cache_natts is the number of attributes already extracted.
*
* This only gets called from slot_getattr. Note that slot_getattr
* must check for a null attribute since we don't create an array
* of null indicators.
* ----------------
*/
static void
slot_deformtuple(TupleTableSlot *slot, int natts)
{
HeapTuple tuple = slot->val;
TupleDesc tupleDesc = slot->ttc_tupleDescriptor;
Datum *values = slot->cache_values;
HeapTupleHeader tup = tuple->t_data;
bool hasnulls = HeapTupleHasNulls(tuple);
Form_pg_attribute *att = tupleDesc->attrs;
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; /* can we use/set attcacheoff? */
/*
* Check whether the first call for this tuple, and initialize or
* restore loop state.
*/
attnum = slot->cache_natts;
if (attnum == 0)
{
/* Start from the first attribute */
off = 0;
slow = false;
}
else
{
/* Restore state from previous execution */
off = slot->cache_off;
slow = slot->cache_slow;
}
tp = (char *) tup + tup->t_hoff;
for (; attnum < natts; attnum++)
{
Form_pg_attribute thisatt = att[attnum];
if (hasnulls && att_isnull(attnum, bp))
{
values[attnum] = (Datum) 0;
slow = true; /* can't use attcacheoff anymore */
continue;
}
if (!slow && thisatt->attcacheoff >= 0)
off = thisatt->attcacheoff;
else
{
off = att_align(off, thisatt->attalign);
if (!slow)
thisatt->attcacheoff = off;
}
values[attnum] = fetchatt(thisatt, tp + off);
off = att_addlength(off, thisatt->attlen, tp + off);
if (thisatt->attlen <= 0)
slow = true; /* can't use attcacheoff anymore */
}
/*
* Save state for next execution
*/
slot->cache_natts = attnum;
slot->cache_off = off;
slot->cache_slow = slow;
}
/* --------------------------------
* slot_getattr
*
* 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.
* --------------------------------
*/
Datum
slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
{
HeapTuple tuple = slot->val;
TupleDesc tupleDesc = slot->ttc_tupleDescriptor;
HeapTupleHeader tup;
/*
* system attributes are handled by heap_getsysattr
*/
if (attnum <= 0)
return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
/*
* check if attnum is out of range according to either the tupdesc
* or the tuple itself; if so return NULL
*/
tup = tuple->t_data;
if (attnum > tup->t_natts || attnum > tupleDesc->natts)
{
*isnull = true;
return (Datum) 0;
}
/*
* check if target attribute is null
*/
if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
{
*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;
}
/*
* If attribute wasn't already extracted, extract it and preceding
* attributes.
*/
if (attnum > slot->cache_natts)
{
/*
* If first time for this TupleTableSlot, allocate the cache
* workspace. It must have the same lifetime as the slot, so allocate
* it in the slot's own context. We size the array according to what
* the tupdesc says, NOT the tuple.
*/
if (slot->cache_values == NULL)
slot->cache_values = (Datum *)
MemoryContextAlloc(slot->ttc_mcxt,
tupleDesc->natts * sizeof(Datum));
slot_deformtuple(slot, attnum);
}
/*
* The result is acquired from cache_values array.
*/
*isnull = false;
return slot->cache_values[attnum - 1];
}
/* ----------------
* heap_freetuple
* ----------------

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.47 2005/01/01 05:43:06 momjian Exp $
* $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.48 2005/03/14 04:41:12 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -1197,9 +1197,9 @@ toast_fetch_datum(varattrib *attr)
/*
* Have a chunk, extract the sequence number and the data
*/
residx = DatumGetInt32(heap_getattr(ttup, 2, toasttupDesc, &isnull));
residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
Assert(!isnull);
chunk = DatumGetPointer(heap_getattr(ttup, 3, toasttupDesc, &isnull));
chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
Assert(!isnull);
chunksize = VARATT_SIZE(chunk) - VARHDRSZ;
@@ -1372,9 +1372,9 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
/*
* Have a chunk, extract the sequence number and the data
*/
residx = DatumGetInt32(heap_getattr(ttup, 2, toasttupDesc, &isnull));
residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
Assert(!isnull);
chunk = DatumGetPointer(heap_getattr(ttup, 3, toasttupDesc, &isnull));
chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
Assert(!isnull);
chunksize = VARATT_SIZE(chunk) - VARHDRSZ;