Attached is a patch that allows an array of Datums + nulls to be
inserted into a tuplestore without first creating a HeapTuple, per
recent suggestion on -hackers. This avoids making an unnecessary copy.
There isn't a really analogous optimization to be applied to tuplesort:
it takes either a TTS, an IndexTuple or a basic Datum, none of which
involve an extra copy.

BTW, I notice that almost all of the callers of the various
tuplestore_put methods switch into the tuplestore's context first. We
could simplify their lives a bit by having the tuplestore remember the
context in which it is allocated and do the switch itself. Perhaps it's
not worth bothering with at this point, though.

-Neil

Index: src/backend/commands/prepare.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/commands/prepare.c,v
retrieving revision 1.80
diff -p -c -r1.80 prepare.c
*** src/backend/commands/prepare.c	1 Jan 2008 19:45:49 -0000	1.80
--- src/backend/commands/prepare.c	27 Feb 2008 21:55:21 -0000
*************** pg_prepared_statement(PG_FUNCTION_ARGS)
*** 764,770 ****
  		hash_seq_init(&hash_seq, prepared_queries);
  		while ((prep_stmt = hash_seq_search(&hash_seq)) != NULL)
  		{
- 			HeapTuple	tuple;
  			Datum		values[5];
  			bool		nulls[5];
  
--- 764,769 ----
*************** pg_prepared_statement(PG_FUNCTION_ARGS)
*** 787,797 ****
  										  prep_stmt->plansource->num_params);
  			values[4] = BoolGetDatum(prep_stmt->from_sql);
  
- 			tuple = heap_form_tuple(tupdesc, values, nulls);
- 
  			/* switch to appropriate context while storing the tuple */
  			MemoryContextSwitchTo(per_query_ctx);
! 			tuplestore_puttuple(tupstore, tuple);
  		}
  	}
  
--- 786,794 ----
  										  prep_stmt->plansource->num_params);
  			values[4] = BoolGetDatum(prep_stmt->from_sql);
  
  			/* switch to appropriate context while storing the tuple */
  			MemoryContextSwitchTo(per_query_ctx);
! 			tuplestore_putvalues(tupstore, tupdesc, values, nulls);
  		}
  	}
  
Index: src/backend/executor/execQual.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/executor/execQual.c,v
retrieving revision 1.226
diff -p -c -r1.226 execQual.c
*** src/backend/executor/execQual.c	1 Jan 2008 19:45:49 -0000	1.226
--- src/backend/executor/execQual.c	27 Feb 2008 22:13:10 -0000
*************** ExecMakeTableFunctionResult(ExprState *f
*** 1547,1553 ****
  	for (;;)
  	{
  		Datum		result;
- 		HeapTuple	tuple;
  
  		CHECK_FOR_INTERRUPTS();
  
--- 1547,1552 ----
*************** ExecMakeTableFunctionResult(ExprState *f
*** 1649,1663 ****
  				 */
  				tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
  				tmptup.t_data = td;
! 				tuple = &tmptup;
  			}
  			else
  			{
! 				tuple = heap_form_tuple(tupdesc, &result, &fcinfo.isnull);
  			}
- 
- 			oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
- 			tuplestore_puttuple(tupstore, tuple);
  			MemoryContextSwitchTo(oldcontext);
  
  			/*
--- 1648,1662 ----
  				 */
  				tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
  				tmptup.t_data = td;
! 
! 				oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
! 				tuplestore_puttuple(tupstore, &tmptup);
  			}
  			else
  			{
! 				oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
! 				tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull);
  			}
  			MemoryContextSwitchTo(oldcontext);
  
  			/*
*************** no_function_result:
*** 1702,1716 ****
  			int			natts = expectedDesc->natts;
  			Datum	   *nulldatums;
  			bool	   *nullflags;
- 			HeapTuple	tuple;
  
  			MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
  			nulldatums = (Datum *) palloc0(natts * sizeof(Datum));
  			nullflags = (bool *) palloc(natts * sizeof(bool));
  			memset(nullflags, true, natts * sizeof(bool));
- 			tuple = heap_form_tuple(expectedDesc, nulldatums, nullflags);
  			MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
! 			tuplestore_puttuple(tupstore, tuple);
  		}
  	}
  
--- 1701,1713 ----
  			int			natts = expectedDesc->natts;
  			Datum	   *nulldatums;
  			bool	   *nullflags;
  
  			MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
  			nulldatums = (Datum *) palloc0(natts * sizeof(Datum));
  			nullflags = (bool *) palloc(natts * sizeof(bool));
  			memset(nullflags, true, natts * sizeof(bool));
  			MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
! 			tuplestore_putvalues(tupstore, expectedDesc, nulldatums, nullflags);
  		}
  	}
  
Index: src/backend/utils/mmgr/portalmem.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/utils/mmgr/portalmem.c,v
retrieving revision 1.106
diff -p -c -r1.106 portalmem.c
*** src/backend/utils/mmgr/portalmem.c	1 Jan 2008 19:45:55 -0000	1.106
--- src/backend/utils/mmgr/portalmem.c	27 Feb 2008 22:03:33 -0000
*************** pg_cursor(PG_FUNCTION_ARGS)
*** 911,917 ****
  	while ((hentry = hash_seq_search(&hash_seq)) != NULL)
  	{
  		Portal		portal = hentry->portal;
- 		HeapTuple	tuple;
  		Datum		values[6];
  		bool		nulls[6];
  
--- 911,916 ----
*************** pg_cursor(PG_FUNCTION_ARGS)
*** 935,945 ****
  		values[4] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_SCROLL);
  		values[5] = TimestampTzGetDatum(portal->creation_time);
  
- 		tuple = heap_form_tuple(tupdesc, values, nulls);
- 
  		/* switch to appropriate context while storing the tuple */
  		MemoryContextSwitchTo(per_query_ctx);
! 		tuplestore_puttuple(tupstore, tuple);
  	}
  
  	/* clean up and return the tuplestore */
--- 934,942 ----
  		values[4] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_SCROLL);
  		values[5] = TimestampTzGetDatum(portal->creation_time);
  
  		/* switch to appropriate context while storing the tuple */
  		MemoryContextSwitchTo(per_query_ctx);
! 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
  	}
  
  	/* clean up and return the tuplestore */
Index: src/backend/utils/sort/tuplestore.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/utils/sort/tuplestore.c,v
retrieving revision 1.36
diff -p -c -r1.36 tuplestore.c
*** src/backend/utils/sort/tuplestore.c	1 Jan 2008 19:45:55 -0000	1.36
--- src/backend/utils/sort/tuplestore.c	28 Feb 2008 20:11:52 -0000
*************** tuplestore_puttupleslot(Tuplestorestate 
*** 366,373 ****
  /*
   * "Standard" case to copy from a HeapTuple.  This is actually now somewhat
   * deprecated, but not worth getting rid of in view of the number of callers.
-  * (Consider adding something that takes a tupdesc+values/nulls arrays so
-  * that we can use heap_form_minimal_tuple() and avoid a copy step.)
   */
  void
  tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
--- 366,371 ----
*************** tuplestore_puttuple(Tuplestorestate *sta
*** 380,385 ****
--- 378,399 ----
  	tuplestore_puttuple_common(state, (void *) tuple);
  }
  
+ /*
+  * Similar to tuplestore_puttuple(), but start from the values + nulls
+  * array. This avoids requiring that the caller construct a HeapTuple,
+  * saving a copy.
+  */
+ void
+ tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc,
+ 					 Datum *values, bool *isnull)
+ {
+ 	MinimalTuple tuple;
+ 
+ 	tuple = heap_form_minimal_tuple(tdesc, values, isnull);
+ 
+ 	tuplestore_puttuple_common(state, (void *) tuple);
+ }
+ 
  static void
  tuplestore_puttuple_common(Tuplestorestate *state, void *tuple)
  {
Index: src/include/utils/tuplestore.h
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/utils/tuplestore.h,v
retrieving revision 1.22
diff -p -c -r1.22 tuplestore.h
*** src/include/utils/tuplestore.h	1 Jan 2008 19:45:59 -0000	1.22
--- src/include/utils/tuplestore.h	27 Feb 2008 21:55:14 -0000
*************** extern void tuplestore_set_eflags(Tuples
*** 51,56 ****
--- 51,58 ----
  extern void tuplestore_puttupleslot(Tuplestorestate *state,
  						TupleTableSlot *slot);
  extern void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple);
+ extern void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc,
+ 								 Datum *values, bool *isnull);
  
  /* tuplestore_donestoring() used to be required, but is no longer used */
  #define tuplestore_donestoring(state)	((void) 0)
Index: src/pl/plperl/plperl.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/pl/plperl/plperl.c,v
retrieving revision 1.136
diff -p -c -r1.136 plperl.c
*** src/pl/plperl/plperl.c	23 Jan 2008 00:55:47 -0000	1.136
--- src/pl/plperl/plperl.c	29 Feb 2008 00:02:11 -0000
*************** plperl_return_next(SV *sv)
*** 1869,1875 ****
  	FunctionCallInfo fcinfo;
  	ReturnSetInfo *rsi;
  	MemoryContext old_cxt;
- 	HeapTuple	tuple;
  
  	if (!sv)
  		return;
--- 1869,1874 ----
*************** plperl_return_next(SV *sv)
*** 1944,1951 ****
--- 1943,1957 ----
  
  	if (prodesc->fn_retistuple)
  	{
+ 		HeapTuple tuple;
+ 
  		tuple = plperl_build_tuple_result((HV *) SvRV(sv),
  										  current_call_data->attinmeta);
+ 
+ 		/* Make sure to store the tuple in a long-lived memory context */
+ 		MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+ 		tuplestore_puttuple(current_call_data->tuple_store, tuple);
+ 		MemoryContextSwitchTo(old_cxt);
  	}
  	else
  	{
*************** plperl_return_next(SV *sv)
*** 1967,1980 ****
  			isNull = true;
  		}
  
! 		tuple = heap_form_tuple(current_call_data->ret_tdesc, &ret, &isNull);
  	}
  
- 	/* Make sure to store the tuple in a long-lived memory context */
- 	MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
- 	tuplestore_puttuple(current_call_data->tuple_store, tuple);
- 	MemoryContextSwitchTo(old_cxt);
- 
  	MemoryContextReset(current_call_data->tmp_cxt);
  }
  
--- 1973,1986 ----
  			isNull = true;
  		}
  
! 		/* Make sure to store the tuple in a long-lived memory context */
! 		MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
! 		tuplestore_putvalues(current_call_data->tuple_store,
! 							 current_call_data->ret_tdesc,
! 							 &ret, &isNull);
! 		MemoryContextSwitchTo(old_cxt);
  	}
  
  	MemoryContextReset(current_call_data->tmp_cxt);
  }
  
Index: src/pl/plpgsql/src/pl_exec.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/pl/plpgsql/src/pl_exec.c,v
retrieving revision 1.202
diff -p -c -r1.202 pl_exec.c
*** src/pl/plpgsql/src/pl_exec.c	1 Jan 2008 19:46:00 -0000	1.202
--- src/pl/plpgsql/src/pl_exec.c	29 Feb 2008 00:30:29 -0000
*************** static int
*** 2007,2016 ****
  exec_stmt_return_next(PLpgSQL_execstate *estate,
  					  PLpgSQL_stmt_return_next *stmt)
  {
! 	TupleDesc	tupdesc;
! 	int			natts;
! 	HeapTuple	tuple;
! 	bool		free_tuple = false;
  
  	if (!estate->retisset)
  		ereport(ERROR,
--- 2007,2017 ----
  exec_stmt_return_next(PLpgSQL_execstate *estate,
  					  PLpgSQL_stmt_return_next *stmt)
  {
! 	TupleDesc		tupdesc;
! 	int				natts;
! 	HeapTuple		tuple = NULL;
! 	MemoryContext	oldcxt;
! 	bool			free_tuple = false;
  
  	if (!estate->retisset)
  		ereport(ERROR,
*************** exec_stmt_return_next(PLpgSQL_execstate 
*** 2048,2056 ****
  												tupdesc->attrs[0]->atttypmod,
  													isNull);
  
! 					tuple = heap_form_tuple(tupdesc, &retval, &isNull);
! 
! 					free_tuple = true;
  				}
  				break;
  
--- 2049,2058 ----
  												tupdesc->attrs[0]->atttypmod,
  													isNull);
  
! 					oldcxt = MemoryContextSwitchTo(estate->tuple_store_cxt);
! 					tuplestore_putvalues(estate->tuple_store, tupdesc,
! 										 &retval, &isNull);
! 					MemoryContextSwitchTo(oldcxt);
  				}
  				break;
  
*************** exec_stmt_return_next(PLpgSQL_execstate 
*** 2087,2093 ****
  
  			default:
  				elog(ERROR, "unrecognized dtype: %d", retvar->dtype);
- 				tuple = NULL;	/* keep compiler quiet */
  				break;
  		}
  	}
--- 2089,2094 ----
*************** exec_stmt_return_next(PLpgSQL_execstate 
*** 2114,2122 ****
  										tupdesc->attrs[0]->atttypmod,
  										isNull);
  
! 		tuple = heap_form_tuple(tupdesc, &retval, &isNull);
! 
! 		free_tuple = true;
  
  		exec_eval_cleanup(estate);
  	}
--- 2115,2124 ----
  										tupdesc->attrs[0]->atttypmod,
  										isNull);
  
! 		oldcxt = MemoryContextSwitchTo(estate->tuple_store_cxt);
! 		tuplestore_putvalues(estate->tuple_store, tupdesc,
! 							 &retval, &isNull);
! 		MemoryContextSwitchTo(oldcxt);
  
  		exec_eval_cleanup(estate);
  	}
*************** exec_stmt_return_next(PLpgSQL_execstate 
*** 2125,2137 ****
  		ereport(ERROR,
  				(errcode(ERRCODE_SYNTAX_ERROR),
  				 errmsg("RETURN NEXT must have a parameter")));
- 		tuple = NULL;			/* keep compiler quiet */
  	}
  
  	if (HeapTupleIsValid(tuple))
  	{
- 		MemoryContext oldcxt;
- 
  		oldcxt = MemoryContextSwitchTo(estate->tuple_store_cxt);
  		tuplestore_puttuple(estate->tuple_store, tuple);
  		MemoryContextSwitchTo(oldcxt);
--- 2127,2136 ----
---------------------------(end of broadcast)---------------------------
TIP 4: Have you searched our list archives?

               http://archives.postgresql.org

Reply via email to