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