| From: | Yugo Nagata <nagata(at)sraoss(dot)co(dot)jp> |
|---|---|
| To: | Ilia Evdokimov <ilya(dot)evdokimov(at)tantorlabs(dot)com> |
| Cc: | PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org> |
| Subject: | Re: Fold NOT IN / <> ALL expressions containing NULL to FALSE |
| Date: | 2026-07-31 15:36:58 |
| Message-ID: | 20260801003658.44dae4e807043c8ce0445156@sraoss.co.jp |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
On Tue, 16 Jun 2026 23:15:51 +0300
Ilia Evdokimov <ilya(dot)evdokimov(at)tantorlabs(dot)com> wrote:
> Hi everyone,
>
> In commit c95cd299, we added an early-exit in `scalararraysel()` to
> return selectivity 0.0 when a NOT IN / <> ALL list contains a NULL and
> the operator is strict. The commit message noted a possible follow-up:
>
> In the future, it might be better to do something for this case in
> constant folding. We would need to be careful to only do this for
> strict operators on expressions located in places that don't care about
> distinguishing false from NULL returns. i.e. EXPRKIND_QUAL expressions.
> Doing that requires a bit more thought and effort, so here we just fix
> some needlessly slow selectivity estimations for ScalarArrayOpExpr
> containing many array elements and at least one NULL.
>
> This patch implements that follow-up.
>
> When a <> ALL / NOT IN expression appears in a qual context and its
> array contains a NULL element, the expression can never evaluate to
> true; it can only return false or NULL. In a qual, both mean the row is
> excluded. We can therefore fold the entire SAOP to constant false during
> `eval_const_expressions()`, which the planner can then use to eliminate
> the scan entirely.
If I understand the patch correctly, the optimization seems to be applied
not only to <> ALL/NOT IN, but to any op ALL expression whose operator is
strict. Is that right?
> A new `is_qual` flag is added to `eval_const_expressions_context`. A new
> function `eval_const_expressions_qual()` sets this flag and is called
> from sites that process WHERE/qual expressions. To prevent the flag from
> leaking into non-qual contexts (e.g. `func(x NOT IN (NULL, 1))`),
> is_qual is saved into a local variable and immediately reset to false at
> the start of `eval_const_expressions_mutator`. Only the SAOP case reads
> `this_node_is_qual` - after processing its arguments with `is_qual = false`.
>
> Any suggestions?
This could improve performance when the array contains many elements including
NULL, especially if one of the elements takes a long time to evaluate.
For example, after applying the patch, the following query returns immediately
without evaluating pg_sleep(3):
postgres=# explain analyze select * from tbl where i not in (null, (select 1 from pg_sleep(3)));
QUERY PLAN
---------------------------------------------------------------------------------------
Result (cost=0.00..0.00 rows=0 width=0) (actual time=0.004..0.004 rows=0.00 loops=1)
Replaces: Scan on tbl
One-Time Filter: false
Planning Time: 0.088 ms
Execution Time: 0.033 ms
(5 rows)
This seems like a nice optimization. However, I wonder whether skipping the
evaluation of subqueries or function calls in the array could cause compatibility
issues, especially if they have side effects.
Regards,
Yugo Nagata
--
Yugo Nagata <nagata(at)sraoss(dot)co(dot)jp>
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Christoph Berg | 2026-07-31 15:50:41 | Re: WAL compression setting after PostgreSQL LZ4 default change |
| Previous Message | Nathan Bossart | 2026-07-31 15:36:02 | Re: glibc qsort() vulnerability |