Re: Redundant qualifier elimination

From: Denis Smirnov <darthunix(at)gmail(dot)com>
To: Matheus Alcantara <matheusssilv97(at)gmail(dot)com>
Cc: pgsql-hackers(at)postgresql(dot)org
Subject: Re: Redundant qualifier elimination
Date: 2026-08-14 05:19:58
Message-ID: 8142CF44-D61F-4F5C-8958-A15491202980@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

I tested the patch on current master and found two separate issues.

First, equal clauses cannot always be removed. The patch changes results when
a clause contains a VOLATILE function:

CREATE TEMP TABLE t(x int);
INSERT INTO t VALUES (1);
CREATE TEMP SEQUENCE s;
SELECT setval('s', 1, false);

SELECT count(*)
FROM t
WHERE nextval('s') = 1
AND nextval('s') = 1;

Master returns 0 because nextval() is called twice. With the patch, the second
clause is removed and the query returns 1. Volatile clauses therefore must not
be deduplicated.

Complex clauses may also increase planning time. Each new restriction scans
all previous restrictions and equal() may walk the whole expression tree.
The worst-case cost is quadratic in the number of clauses.

For the original non-volatile predicate, the patch does correct the estimate.
Without it, selectivity P is counted as P * P. Removing the duplicate increases
the estimated row count and plan cost.

The query becomes slower only because the corrected cost enables optimized
JIT. Thus the patch fixes the estimate, but moves both equivalent queries to
the slower side of the JIT threshold.

The original 137-function example compares basic JIT with O3 plus inlining.
The smaller published example compares optimized JIT with no JIT at all.

I profiled the smaller query on current master with LLVM 22.1.6. The first
optimized execution spent:

inlining: 10.95 ms
LLVM IR optimization: 4.02 ms
machine-code generation: 5.38 ms
total JIT time: 20.47 ms

The large jump in machine-code generation happens between O0 and O1.

On AArch64, O0 uses a short instruction-selection path and a simple register
allocator. Starting with O1, LLVM enables the full backend: more expensive
instruction selection, instruction scheduling, live-range analysis and global
register allocation.

For the same final IR, machine-code generation took 1.5 ms at O0 and 6.2 ms
at O1.

Experimental PostgreSQL providers gave these total JIT times:

O1: 7.1 ms
O2: 8.0 ms
O3: 8.1 ms

Therefore most of the cost appears when moving from O0 to O1. The difference
between O1 and O3 is small. Replacing O3 with O1 would not remove this
performance cliff. O3 still seems reasonable once PostgreSQL has decided that
optimized JIT is worth using.

So I think:

1. The patch needs a VOLATILE check and planning-time tests.
2. It fixes the selectivity estimate but does not fix the JIT regression.
3. The main problem is the JIT cost decision: it uses total plan cost but does
not account for the number or size of generated JIT functions.

Best regards,
Denis Smirnov

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Hayato Kuroda (Fujitsu) 2026-08-14 05:29:22 RE: [PATCH] Preserve replication origin OIDs in pg_upgrade
Previous Message Jeff Davis 2026-08-14 05:17:40 Re: Crash issue in PG18.5 regression