*** ./src/backend/executor/execQual.c.orig Mon Nov 1 11:55:40 2004 --- ./src/backend/executor/execQual.c Mon Nov 1 19:44:28 2004 *************** *** 424,429 **** --- 424,591 ---- } /* ---------------------------------------------------------------- + * slot_deformtuple + * + * Given a TupleTableSlot, extract data into cache_values array + * from tuple inside that. + * This only gets called from slot_getattr(). + * + * This function can be resumed from the previous execution + * using the information encapsulated in the TupleTableSlot. + * This function fills the cache_values start from + * slot->cache_natts and up to natts. + * + * Example: The tupple has 100 columns. + * - first call to get col5 will fill first 5 positions in the array. + * - next call to get col75 will fill starts from 5 and up to 75. + * - next call to get col60 will do nothing, because already extracted. + * + * This function just like heap_deformtuple(). + * Different from heap_deformtuple(), nulls array is not created, + * because null check has finished at slot_getattr(). + * ---------------------------------------------------------------- */ + static void + slot_deformtuple(TupleTableSlot *slot, int natts) + { + HeapTuple tuple = slot->val; + HeapTupleHeader tup = tuple->t_data; + TupleDesc tupDesc = slot->ttc_tupleDescriptor; + Form_pg_attribute *att = tupDesc->attrs; + int attnum; + Datum *values; + 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? */ + + attnum = slot->cache_natts; + values = slot->cache_values; + + /* + * Check whether the first call of this tuple. + */ + if (attnum == 0) { + /* + * Start from the first attribute. + */ + off = 0; + slow = false; + } else { + /* + * Restore state of previous execution. + */ + off = slot->cache_off; + slow = slot->cache_slow; + } + + tp = (char *) tup + tup->t_hoff; + + for (; attnum < natts; attnum++) + { + if (HeapTupleHasNulls(tuple) && att_isnull(attnum, bp)) + { + values[attnum] = (Datum) 0; + slow = true; /* can't use attcacheoff anymore */ + continue; + } + + 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 */ + } + + /* + * Save state for next execution + */ + slot->cache_natts = attnum; + slot->cache_off = off; + slot->cache_slow = slow; + } + + /* ---------------------------------------------------------------- + * slot_getattr + * + * Given a TupleTableSlot, extract an attribute of a + * tuple inside that, and return it as a Datum. + * This works for only user attributes. The given attnum + * is properly range-checked. + * + * If the field in question has a NULL value, we return + * a zero Datum and set *isnull == true. Otherwise, we set + * *isnull == false. + * + * This only gets called from ExecEvalVar(). + * This is just like heap_getattr and fast_getattr macro, + * except it is given a TupleTableSlot, and this function + * uses slot_deformtuple() insetead of nocachegetattr(). + * + * If tuple has many columns, and it has varlen column or + * null data, time spent in nocachegetattr() is O(N^2) in + * the number of fields. When slot_deformtuple() is used + * in place of nocachegetattr(), would reduce the time + * from O(N^2) to O(N). + * ---------------------------------------------------------------- */ + static Datum + slot_getattr(TupleTableSlot *slot, int attnum, bool *isNull) + { + HeapTuple tuple = slot->val; + HeapTupleHeader tup = tuple->t_data; + TupleDesc tupDesc = slot->ttc_tupleDescriptor; + + if (attnum > tup->t_natts) { + *isNull = true; + return (Datum)NULL; + } + + if (HeapTupleNoNulls(tuple)) { + /* + * Check whether we can use attcacheoff. + */ + if (tupDesc->attrs[attnum - 1]->attcacheoff >= 0) { + *isNull = false; + return fetchatt(tupDesc->attrs[attnum - 1], + (char *)tup + tup->t_hoff + + tupDesc->attrs[attnum - 1]->attcacheoff); + } + } else { + if (att_isnull(attnum - 1, tup->t_bits)) { + *isNull = true; + return (Datum)NULL; + } + } + + /* + * Check whether the attribute was extracted already. + */ + if (slot->cache_natts < attnum) { + /* + * It is not extracted yet. + * Fills the cache_values arrays up to attnum. + */ + slot_deformtuple(slot, attnum); + } + + /* + * The result is acquired from cache_values array. + */ + *isNull = false; + return slot->cache_values[attnum - 1]; + } + + /* ---------------------------------------------------------------- * ExecEvalVar * * Returns a Datum whose value is the value of a range *************** *** 434,440 **** bool *isNull, ExprDoneCond *isDone) { Var *variable = (Var *) exprstate->expr; - Datum result; TupleTableSlot *slot; AttrNumber attnum; HeapTuple heapTuple; --- 596,601 ---- *************** *** 512,526 **** * been zeroed. */ Assert(variable->vartype == tuple_type->attrs[attnum - 1]->atttypid); } ! result = heap_getattr(heapTuple, /* tuple containing attribute */ attnum, /* attribute number of desired * attribute */ tuple_type, /* tuple descriptor of tuple */ isNull); /* return: is attribute null? */ - - return result; } /* ---------------------------------------------------------------- --- 673,694 ---- * been zeroed. */ Assert(variable->vartype == tuple_type->attrs[attnum - 1]->atttypid); + + /* + * Get a user attribute. + */ + return slot_getattr(slot, attnum, isNull); } ! /* ! * heap_getsysattr is called directly, because it comes here ! * only at attnum <= 0. ! */ ! return heap_getsysattr(heapTuple, /* tuple containing attribute */ attnum, /* attribute number of desired * attribute */ tuple_type, /* tuple descriptor of tuple */ isNull); /* return: is attribute null? */ } /* ---------------------------------------------------------------- *** ./src/backend/executor/execTuples.c.orig Mon Nov 1 11:55:07 2004 --- ./src/backend/executor/execTuples.c Mon Nov 1 18:04:36 2004 *************** *** 121,126 **** --- 121,182 ---- /* ---------------------------------------------------------------- + * cache_values array create/delete functions + * ---------------------------------------------------------------- + */ + /* -------------------------------- + * ExecCreateCacheValues + * + * This allocates cache_values array of TupleTableSlot + * -------------------------------- + */ + static void + ExecCreateCacheValuesArray(TupleTableSlot *slot) + { + MemoryContext oldcxt; + int natts; + + if (slot->cache_mcxt == NULL) return; + + oldcxt = MemoryContextSwitchTo(slot->cache_mcxt); + + natts = slot->ttc_tupleDescriptor->natts; + slot->cache_values = palloc(natts * sizeof(Datum)); + + MemoryContextSwitchTo(oldcxt); + } + + /* -------------------------------- + * ExecDeleteCacheValues + * + * This frees cache_values array of TupleTableSlot. + * -------------------------------- + */ + static void + ExecDeleteCacheValuesArray(TupleTableSlot *slot) + { + if (slot->cache_values) { + pfree(slot->cache_values); + slot->cache_values = NULL; + } + } + + /* -------------------------------- + * ExecResetCacheValuesArray + * + * Resets the attribute offsets cache. + * -------------------------------- + */ + static void + ExecResetCacheValuesArray(TupleTableSlot *slot) + { + /* + * number of the valid data in the cache_values is set to 0. + */ + slot->cache_natts = 0; + } + + /* ---------------------------------------------------------------- * tuple table create/delete functions * ---------------------------------------------------------------- */ *************** *** 161,166 **** --- 217,223 ---- newtable->size = initialSize; newtable->next = 0; newtable->array = array; + newtable->mcxt = CurrentMemoryContext; return newtable; } *************** *** 204,209 **** --- 261,267 ---- for (i = 0; i < next; i++) { ExecClearTuple(&array[i]); + ExecDeleteCacheValuesArray(&array[i]); if (array[i].ttc_shouldFreeDesc && array[i].ttc_tupleDescriptor != NULL) FreeTupleDesc(array[i].ttc_tupleDescriptor); *************** *** 222,227 **** --- 280,309 ---- * tuple table slot reservation functions * ---------------------------------------------------------------- */ + + /* -------------------------------- + * InitTupleTableSlot + * + * Initializes fields of TupleTableSlot. + * -------------------------------- + */ + static void + InitTupleTableSlot(TupleTableSlot *slot) + { + slot->val = NULL; + slot->ttc_shouldFree = true; + slot->ttc_descIsNew = true; + slot->ttc_shouldFreeDesc = true; + slot->ttc_tupleDescriptor = NULL; + slot->ttc_buffer = InvalidBuffer; + + slot->cache_natts = 0; + slot->cache_values = NULL; + slot->cache_off = 0; + slot->cache_slow = false; + slot->cache_mcxt = NULL; + } + /* -------------------------------- * ExecAllocTableSlot * *************** *** 271,282 **** /* Make sure the allocated slot is valid (and empty) */ slot->type = T_TupleTableSlot; ! slot->val = NULL; ! slot->ttc_shouldFree = true; ! slot->ttc_descIsNew = true; ! slot->ttc_shouldFreeDesc = true; ! slot->ttc_tupleDescriptor = NULL; ! slot->ttc_buffer = InvalidBuffer; return slot; } --- 353,362 ---- /* Make sure the allocated slot is valid (and empty) */ slot->type = T_TupleTableSlot; ! InitTupleTableSlot(slot); ! ! /* Set MemoryContext for allocates cache_values array. */ ! slot->cache_mcxt = table->mcxt; return slot; } *************** *** 295,307 **** { TupleTableSlot *slot = makeNode(TupleTableSlot); ! /* This should match ExecAllocTableSlot() */ ! slot->val = NULL; ! slot->ttc_shouldFree = true; ! slot->ttc_descIsNew = true; ! slot->ttc_shouldFreeDesc = true; ! slot->ttc_tupleDescriptor = NULL; ! slot->ttc_buffer = InvalidBuffer; return slot; } --- 375,383 ---- { TupleTableSlot *slot = makeNode(TupleTableSlot); ! /* Make sure the allocated slot is valid (and empty) */ ! InitTupleTableSlot(slot); ! slot->cache_mcxt = CurrentMemoryContext; return slot; } *************** *** 418,423 **** --- 494,505 ---- slot->ttc_buffer = InvalidBuffer; + /* + * Resets the attribute offsets cache, because disassembly of + * another tuple will start. + */ + ExecResetCacheValuesArray(slot); + return slot; } *************** *** 439,444 **** --- 521,532 ---- slot->ttc_tupleDescriptor = tupdesc; slot->ttc_shouldFreeDesc = shouldFree; + + /* Deletes the old cache_values */ + ExecDeleteCacheValuesArray(slot); + + /* Creates the cache_values */ + ExecCreateCacheValuesArray(slot); } /* -------------------------------- *** ./src/include/executor/tuptable.h.orig Mon Nov 1 11:54:31 2004 --- ./src/include/executor/tuptable.h Mon Nov 1 14:58:36 2004 *************** *** 32,37 **** --- 32,43 ---- * tupleDescriptor type information for the tuple data * shouldFreeDesc boolean - should we free tupleDescriptor * buffer the buffer for tuples pointing to disk pages + * cache_natts number of the valid data in the cache_values + * cache_values keeps the known attribute offsets of a tuple + * cache_off keeps the last known offset in tuple data + * cache_slow keeps the previous execution state of the + * slot_deformtuple() + * cache_mcxt for allocate cache_values array * * The executor stores pointers to tuples in a ``tuple table'' * which is composed of TupleTableSlots. Sometimes the tuples *************** *** 55,60 **** --- 61,69 ---- * See executor.h for decls of functions defined in execTuples.c * -jolly * + * cache_xxx fields are used by ExecEvalVar, in order to optimize + * acquisition of the attributes from tuple. + * * ---------------- */ typedef struct TupleTableSlot *************** *** 66,71 **** --- 75,85 ---- bool ttc_shouldFreeDesc; TupleDesc ttc_tupleDescriptor; Buffer ttc_buffer; + int cache_natts; + Datum *cache_values; + long cache_off; + bool cache_slow; + MemoryContext cache_mcxt; } TupleTableSlot; /* ---------------- *************** *** 77,82 **** --- 91,97 ---- int size; /* size of the table */ int next; /* next available slot number */ TupleTableSlot *array; /* array of TupleTableSlot's */ + MemoryContext mcxt; /* for cache_mcxt of TupleTableSlot. */ } TupleTableData; typedef TupleTableData *TupleTable;