Re: Bug in row_number() optimization
Richard Guo <guofenglinux@gmail.com>
From: Richard Guo <guofenglinux@gmail.com>
To: Sergey Shinderuk <s.shinderuk@postgrespro.ru>
Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>,
David Rowley <drowley@postgresql.org>
Date: 2022-11-22T07:44:57Z
Lists: pgsql-hackers
On Wed, Nov 16, 2022 at 7:38 AM Sergey Shinderuk <s.shinderuk@postgrespro.ru>
wrote:
> The failing query is:
> SELECT * FROM
> (SELECT *,
> count(salary) OVER (PARTITION BY depname || '') c1, -- w1
> row_number() OVER (PARTITION BY depname) rn, -- w2
> count(*) OVER (PARTITION BY depname) c2, -- w2
> count(*) OVER (PARTITION BY '' || depname) c3 -- w3
> FROM empsalary
> ) e WHERE rn <= 1 AND c1 <= 3;
> As far as I understand, ExecWindowAgg for the intermediate WindowAgg
> node switches into pass-through mode, stops evaluating row_number(), and
> returns the previous value instead. But if int8 is passed by reference,
> the previous value stored in econtext->ecxt_aggvalues becomes a dangling
> pointer when the per-output-tuple memory context is reset.
Yeah, you're right. In this example the window function row_number()
goes into pass-through mode after the second evaluation because its
run condition does not hold true any more. The remaining run would just
return the result from the second evaluation, which is stored in
econtext->ecxt_aggvalues[wfuncno].
If int8 is configured as pass-by-ref, the precomputed value from the
second evaluation is actually located in a memory area from context
ecxt_per_tuple_memory, with its pointer stored in ecxt_aggvalues. As
this memory context is reset once per tuple, we would be prone to wrong
results.
I tried with memory context ecxt_per_query_memory when evaluating
window function in the case where int8 is configured as pass-by-ref and
I can see the problem vanishes. I'm using the changes as below
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1027,8 +1027,14 @@ eval_windowfunction(WindowAggState *winstate,
WindowStatePerFunc perfuncstate,
{
LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
MemoryContext oldContext;
-
- oldContext =
MemoryContextSwitchTo(winstate->ss.ps.ps_ExprContext->ecxt_per_tuple_memory);
+ MemoryContext evalWfuncContext;
+
+#ifdef USE_FLOAT8_BYVAL
+ evalWfuncContext =
winstate->ss.ps.ps_ExprContext->ecxt_per_tuple_memory;
+#else
+ evalWfuncContext =
winstate->ss.ps.ps_ExprContext->ecxt_per_query_memory;
+#endif
+ oldContext = MemoryContextSwitchTo(evalWfuncContext);
Thanks
Richard
Commits
-
Fix 32-bit build dangling pointer issue in WindowAgg
- 2a535620cec5 15.2 landed
- a8583272218a 16.0 landed
-
Teach planner and executor about monotonic window funcs
- 9d9c02ccd1ae 15.0 cited