Skip to content

Commit a9b05bd

Browse files
committed
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.
1 parent d1022ce commit a9b05bd

File tree

8 files changed

+361
-283
lines changed

8 files changed

+361
-283
lines changed

src/backend/access/common/heaptuple.c

Lines changed: 184 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.96 2005/01/27 23:23:49 neilc Exp $
12+
* $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.97 2005/03/14 04:41:12 tgl Exp $
1313
*
1414
* NOTES
1515
* The old interface functions have been converted to macros
@@ -23,6 +23,7 @@
2323
#include "access/heapam.h"
2424
#include "access/tuptoaster.h"
2525
#include "catalog/pg_type.h"
26+
#include "executor/tuptable.h"
2627

2728

2829
/* ----------------------------------------------------------------
@@ -751,6 +752,7 @@ heap_deformtuple(HeapTuple tuple,
751752
char *nulls)
752753
{
753754
HeapTupleHeader tup = tuple->t_data;
755+
bool hasnulls = HeapTupleHasNulls(tuple);
754756
Form_pg_attribute *att = tupleDesc->attrs;
755757
int tdesc_natts = tupleDesc->natts;
756758
int natts; /* number of atts to extract */
@@ -775,7 +777,9 @@ heap_deformtuple(HeapTuple tuple,
775777

776778
for (attnum = 0; attnum < natts; attnum++)
777779
{
778-
if (HeapTupleHasNulls(tuple) && att_isnull(attnum, bp))
780+
Form_pg_attribute thisatt = att[attnum];
781+
782+
if (hasnulls && att_isnull(attnum, bp))
779783
{
780784
values[attnum] = (Datum) 0;
781785
nulls[attnum] = 'n';
@@ -785,21 +789,21 @@ heap_deformtuple(HeapTuple tuple,
785789

786790
nulls[attnum] = ' ';
787791

788-
if (!slow && att[attnum]->attcacheoff >= 0)
789-
off = att[attnum]->attcacheoff;
792+
if (!slow && thisatt->attcacheoff >= 0)
793+
off = thisatt->attcacheoff;
790794
else
791795
{
792-
off = att_align(off, att[attnum]->attalign);
796+
off = att_align(off, thisatt->attalign);
793797

794798
if (!slow)
795-
att[attnum]->attcacheoff = off;
799+
thisatt->attcacheoff = off;
796800
}
797801

798-
values[attnum] = fetchatt(att[attnum], tp + off);
802+
values[attnum] = fetchatt(thisatt, tp + off);
799803

800-
off = att_addlength(off, att[attnum]->attlen, tp + off);
804+
off = att_addlength(off, thisatt->attlen, tp + off);
801805

802-
if (att[attnum]->attlen <= 0)
806+
if (thisatt->attlen <= 0)
803807
slow = true; /* can't use attcacheoff anymore */
804808
}
805809

@@ -814,6 +818,177 @@ heap_deformtuple(HeapTuple tuple,
814818
}
815819
}
816820

821+
/* ----------------
822+
* slot_deformtuple
823+
*
824+
* Given a TupleTableSlot, extract data into cache_values array
825+
* from the slot's tuple.
826+
*
827+
* This is essentially an incremental version of heap_deformtuple:
828+
* on each call we extract attributes up to the one needed, without
829+
* re-computing information about previously extracted attributes.
830+
* slot->cache_natts is the number of attributes already extracted.
831+
*
832+
* This only gets called from slot_getattr. Note that slot_getattr
833+
* must check for a null attribute since we don't create an array
834+
* of null indicators.
835+
* ----------------
836+
*/
837+
static void
838+
slot_deformtuple(TupleTableSlot *slot, int natts)
839+
{
840+
HeapTuple tuple = slot->val;
841+
TupleDesc tupleDesc = slot->ttc_tupleDescriptor;
842+
Datum *values = slot->cache_values;
843+
HeapTupleHeader tup = tuple->t_data;
844+
bool hasnulls = HeapTupleHasNulls(tuple);
845+
Form_pg_attribute *att = tupleDesc->attrs;
846+
int attnum;
847+
char *tp; /* ptr to tuple data */
848+
long off; /* offset in tuple data */
849+
bits8 *bp = tup->t_bits; /* ptr to null bitmask in tuple */
850+
bool slow; /* can we use/set attcacheoff? */
851+
852+
/*
853+
* Check whether the first call for this tuple, and initialize or
854+
* restore loop state.
855+
*/
856+
attnum = slot->cache_natts;
857+
if (attnum == 0)
858+
{
859+
/* Start from the first attribute */
860+
off = 0;
861+
slow = false;
862+
}
863+
else
864+
{
865+
/* Restore state from previous execution */
866+
off = slot->cache_off;
867+
slow = slot->cache_slow;
868+
}
869+
870+
tp = (char *) tup + tup->t_hoff;
871+
872+
for (; attnum < natts; attnum++)
873+
{
874+
Form_pg_attribute thisatt = att[attnum];
875+
876+
if (hasnulls && att_isnull(attnum, bp))
877+
{
878+
values[attnum] = (Datum) 0;
879+
slow = true; /* can't use attcacheoff anymore */
880+
continue;
881+
}
882+
883+
if (!slow && thisatt->attcacheoff >= 0)
884+
off = thisatt->attcacheoff;
885+
else
886+
{
887+
off = att_align(off, thisatt->attalign);
888+
889+
if (!slow)
890+
thisatt->attcacheoff = off;
891+
}
892+
893+
values[attnum] = fetchatt(thisatt, tp + off);
894+
895+
off = att_addlength(off, thisatt->attlen, tp + off);
896+
897+
if (thisatt->attlen <= 0)
898+
slow = true; /* can't use attcacheoff anymore */
899+
}
900+
901+
/*
902+
* Save state for next execution
903+
*/
904+
slot->cache_natts = attnum;
905+
slot->cache_off = off;
906+
slot->cache_slow = slow;
907+
}
908+
909+
/* --------------------------------
910+
* slot_getattr
911+
*
912+
* This function fetches an attribute of the slot's current tuple.
913+
* It is functionally equivalent to heap_getattr, but fetches of
914+
* multiple attributes of the same tuple will be optimized better,
915+
* because we avoid O(N^2) behavior from multiple calls of
916+
* nocachegetattr(), even when attcacheoff isn't usable.
917+
* --------------------------------
918+
*/
919+
Datum
920+
slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
921+
{
922+
HeapTuple tuple = slot->val;
923+
TupleDesc tupleDesc = slot->ttc_tupleDescriptor;
924+
HeapTupleHeader tup;
925+
926+
/*
927+
* system attributes are handled by heap_getsysattr
928+
*/
929+
if (attnum <= 0)
930+
return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
931+
932+
/*
933+
* check if attnum is out of range according to either the tupdesc
934+
* or the tuple itself; if so return NULL
935+
*/
936+
tup = tuple->t_data;
937+
938+
if (attnum > tup->t_natts || attnum > tupleDesc->natts)
939+
{
940+
*isnull = true;
941+
return (Datum) 0;
942+
}
943+
944+
/*
945+
* check if target attribute is null
946+
*/
947+
if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
948+
{
949+
*isnull = true;
950+
return (Datum) 0;
951+
}
952+
953+
/*
954+
* If the attribute's column has been dropped, we force a NULL
955+
* result. This case should not happen in normal use, but it could
956+
* happen if we are executing a plan cached before the column was
957+
* dropped.
958+
*/
959+
if (tupleDesc->attrs[attnum - 1]->attisdropped)
960+
{
961+
*isnull = true;
962+
return (Datum) 0;
963+
}
964+
965+
/*
966+
* If attribute wasn't already extracted, extract it and preceding
967+
* attributes.
968+
*/
969+
if (attnum > slot->cache_natts)
970+
{
971+
/*
972+
* If first time for this TupleTableSlot, allocate the cache
973+
* workspace. It must have the same lifetime as the slot, so allocate
974+
* it in the slot's own context. We size the array according to what
975+
* the tupdesc says, NOT the tuple.
976+
*/
977+
if (slot->cache_values == NULL)
978+
slot->cache_values = (Datum *)
979+
MemoryContextAlloc(slot->ttc_mcxt,
980+
tupleDesc->natts * sizeof(Datum));
981+
982+
slot_deformtuple(slot, attnum);
983+
}
984+
985+
/*
986+
* The result is acquired from cache_values array.
987+
*/
988+
*isnull = false;
989+
return slot->cache_values[attnum - 1];
990+
}
991+
817992
/* ----------------
818993
* heap_freetuple
819994
* ----------------

src/backend/access/heap/tuptoaster.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.47 2005/01/01 05:43:06 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.48 2005/03/14 04:41:12 tgl Exp $
1212
*
1313
*
1414
* INTERFACE ROUTINES
@@ -1197,9 +1197,9 @@ toast_fetch_datum(varattrib *attr)
11971197
/*
11981198
* Have a chunk, extract the sequence number and the data
11991199
*/
1200-
residx = DatumGetInt32(heap_getattr(ttup, 2, toasttupDesc, &isnull));
1200+
residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
12011201
Assert(!isnull);
1202-
chunk = DatumGetPointer(heap_getattr(ttup, 3, toasttupDesc, &isnull));
1202+
chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
12031203
Assert(!isnull);
12041204
chunksize = VARATT_SIZE(chunk) - VARHDRSZ;
12051205

@@ -1372,9 +1372,9 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
13721372
/*
13731373
* Have a chunk, extract the sequence number and the data
13741374
*/
1375-
residx = DatumGetInt32(heap_getattr(ttup, 2, toasttupDesc, &isnull));
1375+
residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
13761376
Assert(!isnull);
1377-
chunk = DatumGetPointer(heap_getattr(ttup, 3, toasttupDesc, &isnull));
1377+
chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
13781378
Assert(!isnull);
13791379
chunksize = VARATT_SIZE(chunk) - VARHDRSZ;
13801380

src/backend/executor/execJunk.c

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.46 2004/12/31 21:59:45 pgsql Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.47 2005/03/14 04:41:12 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -209,20 +209,13 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
209209
Datum *value,
210210
bool *isNull)
211211
{
212-
List *targetList;
213212
ListCell *t;
214-
AttrNumber resno;
215-
TupleDesc tupType;
216-
HeapTuple tuple;
217213

218214
/*
219-
* first look in the junkfilter's target list for an attribute with
215+
* Look in the junkfilter's target list for an attribute with
220216
* the given name
221217
*/
222-
resno = InvalidAttrNumber;
223-
targetList = junkfilter->jf_targetList;
224-
225-
foreach(t, targetList)
218+
foreach(t, junkfilter->jf_targetList)
226219
{
227220
TargetEntry *tle = lfirst(t);
228221
Resdom *resdom = tle->resdom;
@@ -231,26 +224,13 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
231224
(strcmp(resdom->resname, attrName) == 0))
232225
{
233226
/* We found it ! */
234-
resno = resdom->resno;
235-
break;
227+
*value = slot_getattr(slot, resdom->resno, isNull);
228+
return true;
236229
}
237230
}
238231

239-
if (resno == InvalidAttrNumber)
240-
{
241-
/* Ooops! We couldn't find this attribute... */
242-
return false;
243-
}
244-
245-
/*
246-
* Now extract the attribute value from the tuple.
247-
*/
248-
tuple = slot->val;
249-
tupType = slot->ttc_tupleDescriptor;
250-
251-
*value = heap_getattr(tuple, resno, tupType, isNull);
252-
253-
return true;
232+
/* Ooops! We couldn't find this attribute... */
233+
return false;
254234
}
255235

256236
/*

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy