Обсуждение: BUG #19358: Short circuit optimization exists in generic plan but missed in custom plan

Поиск
Список
Период
Сортировка

BUG #19358: Short circuit optimization exists in generic plan but missed in custom plan

От
PG Bug reporting form
Дата:
The following bug has been logged on the website:

Bug reference:      19358
Logged by:          Chi Zhang
Email address:      798604270@qq.com
PostgreSQL version: 17.6
Operating system:   ubuntu 24.04 with docker
Description:

Hi,

In the following test case, there are two equivalent updates; the first one
is in prepared form, and the second one is a normal update. There is a
configuration that sets plan_cache_mode = force_generic_plan, which means
the prepared updates will use a generic plan. When I run this test case, I
found the prepared update can execute, but the normal update triggers an
error `result of range difference would not be contiguous`, I can understand
this because this error is skipped in the prepared update by short circuit
optimization. As shown in the document at
https://www.postgresql.org/docs/current/runtime-config-query.html that, the
generic plan maybe inefficient than the custom plan. So why the short
circuit optimization applied in the prepared update (with a generic plan)
but not in the normal update (with a custom plan).

```
SET plan_cache_mode = force_generic_plan;
CREATE TABLE t3(c0 TEXT);
INSERT INTO t3(c0) VALUES('');
PREPARE prepare_query (text, int4range, int4range) AS UPDATE t3 SET c0=$1
WHERE (((t3.c0) IN (t3.c0))OR(((t3.c0)) BETWEEN (((t3.c0)||((($2)-($3)))))
AND (t3.c0)));
EXECUTE prepare_query('', '[-993693027,1525305818]'::int4range,
'[-168306621,-163656947)'::int4range);
DEALLOCATE prepare_query;
UPDATE t3 SET c0=''::text WHERE (((t3.c0) IN (t3.c0))OR(((t3.c0 )) BETWEEN
(((t3.c0)||((('[-993693027,1525305818]'::int4range)-('[-168306621,-163656947)'::int4range)))))
AND (t3.c0))); -- ERROR:  result of range difference would not be contiguous
```


PG Bug reporting form <noreply@postgresql.org> writes:
> ... So why the short
> circuit optimization applied in the prepared update (with a generic plan)
> but not in the normal update (with a custom plan).

Const-folding doesn't stop just because the current part of the
expression tree might not be reached at runtime.  EXPLAIN shows
that your generic plan is

regression=# explain verbose EXECUTE prepare_query('', '[-993693027,1525305818]'::int4range,
'[-168306621,-163656947)'::int4range);
                                             QUERY PLAN
-----------------------------------------------------------------------------------------------------
 Update on public.t3  (cost=0.00..47.40 rows=0 width=0)
   ->  Seq Scan on public.t3  (cost=0.00..47.40 rows=157 width=38)
         Output: $1, ctid
         Filter: ((t3.c0 = t3.c0) OR ((t3.c0 >= (t3.c0 || (($2 - $3))::text)) AND (t3.c0 <= t3.c0)))
(4 rows)

So the "t3.c0 = t3.c0" condition will be found to be true and we don't
reach the right-hand side of the OR.  However, if $2 and $3 are
replaced by constants, the planner will attempt to reduce the range
difference to a constant at plan time.

There have been previous discussions about how this sometimes leads to
unintuitive failures, but I don't see how we could skip const-folding
without huge performance penalties in some cases.  In any case, we
clearly document that you can't rely on specific execution order of
boolean expressions.  You will not find anything in our docs promising
left-to-right evaluation of ORs.

            regards, tom lane