*** ./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;
