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