Alexander Lakhin <exclus...@gmail.com> writes: > Sorry if I've chosen the wrong thread, but starting from 0313c5dc6, > the following script: > CREATE TYPE aggtype AS (a int, b text); > CREATE FUNCTION aggfns_trans(aggtype[], integer, text) RETURNS aggtype[] > LANGUAGE sql AS 'SELECT > array_append($1,ROW($2,$3)::aggtype)'; > CREATE AGGREGATE aggfns(integer, text) (SFUNC = public.aggfns_trans, STYPE = > public.aggtype[], INITCOND = '{}');
> CREATE TABLE t(i int, k int); > INSERT INTO t SELECT 1, 2 FROM generate_series(1, 4000); > SET statement_timeout = '10s'; > SELECT aggfns(i, repeat('x', 8192)) OVER (PARTITION BY i) FROM t; > crashes the server for me like this: > corrupted size vs. prev_size while consolidating Hmm. What seems to be going on here is that once the aggfns_trans() result gets large enough that the SQL-function-result tuplestore decides to spill to disk, when we pull the result tuple back out of the tuplestore with tuplestore_gettupleslot we end up with the jf_resultSlot holding a should-free tuple pointer that points into the tuplestore's storage. After tuplestore_clear that is a dangling pointer, and the next use of the jf_resultSlot fails while trying to free the tuple. So the attached fixes it for me, but I'm still mighty confused because I don't understand why it didn't fail in the old code. This logic doesn't seem noticeably different from before, and there's even a very old comment (in the SRF path) alleging that /* NB: this might delete the slot's content, but we don't care */ Now we *do* care, but what changed? (As an aside, seems like tuplestore is leaving money on the table, because there's hardly any point in spilling to disk when it never holds more than one tuple. But that's not something to change now.) regards, tom lane
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index e0bca7cb81c..37f616280c6 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -1728,8 +1728,9 @@ fmgr_sql(PG_FUNCTION_ARGS) elog(ERROR, "failed to fetch lazy-eval tuple"); /* Extract the result as a datum, and copy out from the slot */ result = postquel_get_single_result(slot, fcinfo, fcache); + /* Clear the slot, in case it points into the tuplestore */ + ExecClearTuple(slot); /* Clear the tuplestore, but keep it for next time */ - /* NB: this might delete the slot's content, but we don't care */ tuplestore_clear(fcache->tstore); /* @@ -1810,7 +1811,11 @@ fmgr_sql(PG_FUNCTION_ARGS) /* Re-use the junkfilter's output slot to fetch back the tuple */ slot = fcache->junkFilter->jf_resultSlot; if (tuplestore_gettupleslot(fcache->tstore, true, false, slot)) + { result = postquel_get_single_result(slot, fcinfo, fcache); + /* Clear the slot, in case it points into the tuplestore */ + ExecClearTuple(slot); + } else { fcinfo->isnull = true;