RE: [PATCH] Restrict partial unique index estimates to base quals

From: Zhang Ziming <toren(dot)zhang(at)outlook(dot)com>
To: Chengpeng Yan <chengpeng_yan(at)outlook(dot)com>, "pgsql-hackers(at)lists(dot)postgresql(dot)org" <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Cc: David Rowley <dgrowleyml(at)gmail(dot)com>
Subject: RE: [PATCH] Restrict partial unique index estimates to base quals
Date: 2026-10-31 06:56:46
Message-ID: TY3P301MB06581CE533F14947FBFB8A6BEF6C2@TY3P301MB0658.JPNP301.PROD.OUTLOOK.COM
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi, Chengpeng

The predOKBase distinction makes sense to me, but I'm wondering whether
using it unconditionally in examine_variable() changes more than
intended:

```diff
if (index->unique &&
index->nkeycolumns == 1 &&
pos == 0 &&
- (index->indpred == NIL || index->predOK))
+ (index->indpred == NIL || index->predOKBase))
vardata->isunique = true;
```

Since eqjoinsel() also uses examine_variable(), this change affects join
estimates even when the join conditions imply the index predicate.

For example:

```sql
CREATE TABLE p (id int, code int);
CREATE TABLE c (parent_id int, code int);

CREATE UNIQUE INDEX c_expr ON c ((code + 0))
WHERE parent_id IS NOT NULL;

INSERT INTO p VALUES (1, 42);
INSERT INTO c SELECT 1, g FROM generate_series(1, 10000) g;
INSERT INTO c SELECT NULL, 42 FROM generate_series(1, 10000);
ANALYZE p, c;

SET enable_nestloop = off;
SET enable_mergejoin = off;
EXPLAIN (ANALYZE, TIMING OFF, SUMMARY OFF, BUFFERS OFF)
SELECT * FROM p JOIN c
ON c.parent_id = p.id AND c.code + 0 = p.code;
```

I tested on PostgreSQL 20devel, with and without v1. The EXPLAIN difference
is:

```diff
-Hash Join (cost=1.02..465.03 rows=1 width=16) (actual rows=1.00 loops=1)
+Hash Join (cost=1.02..465.52 rows=50 width=16) (actual rows=1.00 loops=1)
Hash Cond: ((c.parent_id = p.id) AND ((c.code + 0) = p.code))
-> Seq Scan on c (cost=0.00..289.00 rows=20000 width=8) (actual rows=20000.00 loops=1)
-> Hash (cost=1.01..1.01 rows=1 width=8) (actual rows=1.00 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
-> Seq Scan on p (cost=0.00..1.01 rows=1 width=8) (actual rows=1.00 loops=1)
```

Here the join conditions imply parent_id IS NOT NULL, so each p row can
match at most one c row. With v1, predOKBase is false and
examine_variable() no longer supplies that uniqueness information to
eqjoinsel().

Losing this information may also affect the join order and the cost
estimates of subsequent joins, so the impact could extend beyond the row
estimate shown here.

Could we distinguish base-relation restriction estimates from join
estimates here, so that the former use the base-only proof while the
latter can still benefit from uniqueness when the relevant join
conditions imply the index predicate?

Best regards,
Ziming Zhang

In response to

Browse pgsql-hackers by date

  From Date Subject
Previous Message Roman Khapov 2026-10-31 06:54:32 Print latency percentiles in pgbench