ATTACH PARTITION cost grows linearly with pg_constraint size (seqscan in CloneFkReferenced), much worse since not-null constraints are in pg_constraint (PG 18)
Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.
You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:
docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t253993psql -h localhost -U postgresBuilt from patchset v10 (message #10), October 22, 2026 at 04:45 AM.
Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:
git clone --branch t253993_10 https://github.com/hackorum-dev/postgres.gitIn a checkout you already have, add the fork once:
git remote add hackorum https://github.com/hackorum-dev/postgres.gitthen, for this patchset and every later one:
git fetch hackorum t253993_10 && git checkout t253993_10Patchset v10 (message #10) is on t253993_10
<html><body><div style="font-family: 'verdana'; font-size: 12px; color: #000;">
<div>
<pre>Hi hackers,<br><br>On PostgreSQL 18.6 we see ATTACH PARTITION and CREATE TABLE ... PARTITION<br>OF become steadily slower as the database grows. That happens even when<br>the partitioned table involved is empty, has only a handful of<br>partitions, and no foreign key references it or goes out from it. The<br>cost per attached partition is proportional to the total size of<br>pg_constraint.<br><br><br>1. Where the time goes<br>----------------------<br><br>Both ATExecAttachPartition() and DefineRelation() (for PARTITION OF)<br>call CloneForeignKeyConstraints() unconditionally, which calls<br>CloneFkReferenced(). That function looks for foreign keys referencing<br>the parent with a heap scan of pg_constraint, because there is no<br>index on confrelid (src/backend/commands/tablecmds.c, REL_18_STABLE):<br><br> ScanKeyInit(&key[0],<br> Anum_pg_constraint_confrelid, BTEqualStrategyNumber,<br> F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));<br> ScanKeyInit(&key[1],<br> Anum_pg_constraint_contype, BTEqualStrategyNumber,<br> F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));<br> /* This is a seqscan, as we don't have a usable index ... */<br> scan = systable_beginscan(pg_constraint, InvalidOid, true,<br> NULL, 2, key);<br><br>So every partition added costs one full scan of pg_constraint. I<br>confirmed this with pg_stat_sys_tables.seq_scan on pg_constraint.<br>10 x CREATE TABLE ... (LIKE ...) added 0 seq scans, while 10 x ATTACH<br>PARTITION plus 10 x CREATE TABLE ... PARTITION OF added exactly 20.<br><br>This used to be cheap. Since commit 14e87ffa5 ("Add pg_constraint rows<br>for not-null constraints", PG 18), every NOT NULL column of every table<br>and every partition has a row in pg_constraint. Partitioned schemas in<br>particular now have far more rows there than before. In our database<br>97.5% of pg_constraint consists of rows belonging to partitions:<br><br> contype | count<br> --------+--------<br> n | 639630<br> p | 39054<br> c | 30035<br> f | 1001<br> u | 123<br><br> pg_constraint: 711k rows, 288 MB heap (469 MB total)<br><br> EXPLAIN (ANALYZE, BUFFERS) -- max_parallel_workers_per_gather = 0<br> SELECT * FROM pg_constraint WHERE confrelid = '<parent>'::regclass AND contype = 'f';<br> Seq Scan on pg_constraint (actual time=97.877..97.878 rows=0 loops=1)<br> Filter: ((confrelid = '1151806'::oid) AND (contype = 'f'::"char"))<br> Rows Removed by Filter: 709854<br> Buffers: shared hit=15015 read=21873<br> Execution Time: 97.895 ms<br><br>The same GetParentedForeignKeyRefs() pattern ("XXX This is a seqscan,<br>as we don't have a usable index") exists on the DETACH path.<br><br><br>2. Standalone reproducer<br>------------------------<br><br>Run this in an empty PG 18 database. It attaches 50 fresh, empty<br>partitions to a table without any FK and then grows pg_constraint with<br>unrelated tables that have NOT NULL columns. The nullable filler step is<br>a control: many more tables and columns, but no pg_constraint rows.<br><br> CREATE TABLE parent (k INTEGER NOT NULL, v TEXT) PARTITION BY LIST (k);<br> CREATE SEQUENCE part_seq;<br><br> CREATE FUNCTION time_attach(p_n INTEGER) RETURNS NUMERIC<br> LANGUAGE plpgsql AS $f$<br> DECLARE<br> t0 TIMESTAMPTZ;<br> v_first BIGINT := nextval('part_seq');<br> i BIGINT;<br> BEGIN<br> PERFORM setval('part_seq', v_first + p_n);<br> FOR i IN v_first .. v_first + p_n LOOP<br> EXECUTE format('CREATE TABLE parent_p%s (LIKE parent)', i);<br> END LOOP;<br> -- one warm-up ATTACH, not measured<br> EXECUTE format('ALTER TABLE parent ATTACH PARTITION parent_p%s FOR VALUES IN (%s)', v_first, v_first);<br> t0 := clock_timestamp();<br> FOR i IN v_first + 1 .. v_first + p_n LOOP<br> EXECUTE format('ALTER TABLE parent ATTACH PARTITION parent_p%s FOR VALUES IN (%s)', i, i);<br> END LOOP;<br> RETURN ROUND(EXTRACT(EPOCH FROM clock_timestamp() - t0) * 1000 / p_n, 2);<br> END;<br> $f$;<br><br> CREATE FUNCTION filler(p_prefix TEXT, p_tables INTEGER, p_cols INTEGER, p_not_null BOOLEAN) RETURNS VOID<br> LANGUAGE plpgsql AS $f$<br> DECLARE<br> v_cols TEXT;<br> BEGIN<br> SELECT string_agg(format('c%s INTEGER%s', c, CASE WHEN p_not_null THEN ' NOT NULL' ELSE '' END), ', ')<br> INTO v_cols<br> FROM generate_series(1, p_cols) c;<br> FOR t IN 1 .. p_tables LOOP<br> EXECUTE format('CREATE TABLE %s_%s (%s)', p_prefix, t, v_cols);<br> END LOOP;<br> END;<br> $f$;<br><br> CREATE TABLE results (step TEXT, pg_constraint_rows BIGINT, pg_constraint_mb NUMERIC,<br> ms_per_attach NUMERIC, ts TIMESTAMPTZ DEFAULT clock_timestamp());<br><br> CREATE FUNCTION measure(p_step TEXT) RETURNS VOID<br> LANGUAGE sql AS $f$<br> INSERT INTO results (step, pg_constraint_rows, pg_constraint_mb, ms_per_attach)<br> SELECT p_step, (SELECT COUNT(*) FROM pg_constraint),<br> ROUND(pg_relation_size('pg_constraint') / 1048576.0, 1), time_attach(50);<br> $f$;<br><br> SELECT measure('empty database');<br> BEGIN; SELECT filler('nullable', 500, 500, false); COMMIT;<br> SELECT measure('+500 tables x 500 nullable columns');<br> BEGIN; SELECT filler('nn1', 500, 500, true); COMMIT;<br> SELECT measure('+250k NOT NULL constraints');<br> BEGIN; SELECT filler('nn2', 500, 500, true); COMMIT;<br> SELECT measure('+500k NOT NULL constraints');<br> BEGIN; SELECT filler('nn3', 500, 500, true); COMMIT;<br> SELECT measure('+750k NOT NULL constraints');<br> BEGIN; SELECT filler('nn4', 500, 500, true); COMMIT;<br> SELECT measure('+1M NOT NULL constraints');<br><br> SELECT step, pg_constraint_rows, pg_constraint_mb, ms_per_attach FROM results ORDER BY ts;<br><br>Result on 18.6 (x86_64, Alpine/musl build, shared_buffers = 128MB, a<br>local developer machine):<br><br> step | pg_constraint_rows | pg_constraint_mb | ms_per_attach<br> -----------------------------------+--------------------+------------------+---------------<br> empty database | 195 | 0.0 | 0.78<br> +500 tables x 500 nullable columns | 246 | 0.1 | 1.05<br> +250k NOT NULL constraints | 250297 | 41.6 | 12.99<br> +500k NOT NULL constraints | 500348 | 83.2 | 29.42<br> +750k NOT NULL constraints | 750399 | 124.8 | 44.73<br> +1M NOT NULL constraints | 1000450 | 166.3 | 64.50<br><br>ATTACH of an empty partition into a table with no FKs goes from under<br>1 ms to 64 ms, linear in pg_constraint size. The nullable control shows<br>that the number of tables or columns doesn't matter; only the rows in<br>pg_constraint do. Before PG 18 the same filler tables would have added<br>no pg_constraint rows at all. I haven't measured the same script on<br>PG 17.<br><br><br>3. Real-world impact<br>--------------------<br><br>We use list partitioning for our data: 53 tables, all partitioned<br>by the same "partition number", each currently with 700 partitions<br>(38k leaf partitions in total). A maintenance job creates 50 new<br>partition numbers at a time, i.e. 53 x 50 = 2,650 ATTACH PARTITION per<br>run. Each new partition number adds ~990 pg_constraint rows (≈19 per<br>leaf, nearly all not-null constraints), so each run makes the next one<br>slower. Wall-clock times of consecutive runs:<br><br> 1:03, 1:09, 1:13, 1:21, 1:29, 1:31, 1:55, 2:10, 2:55, 3:46, 4:42, 6:16<br><br>Per-step timing shows ATTACH is ~95% of the runtime (~120 ms per<br>ATTACH at 700 partitions per table). CREATE TABLE ... LIKE, triggers,<br>grants and so on stay in the low milliseconds. Switching to CREATE<br>TABLE ... PARTITION OF doesn't help, since it goes through the same<br>CloneFkReferenced(). The curve bends upward once pg_constraint no longer<br>fits in shared_buffers.<br><br><br>4. Possible directions<br>----------------------<br><br>I'm not proposing a patch yet, but a few ideas:<br><br>a) Avoid the heap scan by finding the referencing FKs through an<br> existing index. An FK constraint records a dependency on the<br> referenced relation's columns, so pg_depend_reference_index<br> (refclassid, refobjid, refobjsubid) with refobjid = parent and<br> classid = pg_constraint yields exactly the candidate constraints.<br> The cost is then proportional to the number of dependents of the<br> parent, not the size of the catalog.<br><br>b) Cheap early exit: a table referenced by an FK carries the internal<br> RI action triggers (RI_FKey_*_del/_upd). If the parent has no such<br> triggers, CloneFkReferenced() could skip the scan entirely. That<br> covers the common case of partitioned tables that are never<br> referenced.<br><br>c) A catalog index on pg_constraint (confrelid). This is the most<br> direct fix, but it adds maintenance cost to every constraint insert,<br> and in PG 18 the vast majority of rows have confrelid = 0.<br><br>The same applies to GetParentedForeignKeyRefs() on the DETACH path.<br><br>Is this known, or worth a patch? I'm happy to test patches against our<br>workload.<br><br>Regards,<br>Bernhard Wonisch<br><br></pre>
</div>
</div></body></html>
Hi Bernhard,
Good report. I reproduced it and measured a few things around it.
The slowdown reproduces on 18.6. With your script, ms per ATTACH goes
from 0.07 (empty) to 20.7 at 1M pg_constraint rows, the nullable control
stays flat, and pg_stat_sys_tables shows exactly one pg_constraint
seqscan per ATTACH. My absolute numbers are lower than yours (faster
machine) but the shape is the same.
I also ran your script on 17.11, which you hadn't. It stays flat, 0.06
to 0.26 ms per ATTACH, and pg_constraint stays at ~110 rows the whole
way. The seqscan happens on 17 too, but with almost nothing in
pg_constraint it costs nothing. So this is 14e87ffa5 feeding a scan
that was always there, as you say.
On your option (c), one thing makes it cheaper than it looks: only
foreign-key rows have confrelid <> 0. I checked every contype, and
not-null, check, primary-key and unique constraints all store
confrelid = 0, so a partial index
CREATE INDEX ... ON pg_constraint (confrelid) WHERE confrelid <> 0;
covers only the FK rows, not the not-null rows that dominate the
catalog. On a database with 1,000,198 constraints of which 1 is an FK,
that index is 16 kB, and the lookup CloneFkReferenced() does drops from
a 58 ms seqscan (1M rows filtered) to a 0.03 ms index scan. The partial
predicate also answers the maintenance worry you raised for (c): the
not-null inserts that make up the bulk match confrelid = 0, so they
never touch the index.
One correction on the DETACH path: I could not reproduce a slowdown
there. Detaching 50 partitions with 1M pg_constraint rows stays at
0.06 ms each and adds zero pg_constraint seqscans, so
GetParentedForeignKeyRefs() does not seem to be reached on a plain
DETACH with no foreign key involved. ATTACH is where the cost is.
I'd be glad to put a patch together once there's a sense of the
preferred direction. The partial index is the smallest change, but
whether a new catalog index is the way to go, versus the pg_depend
lookup or the trigger-based early exit, is more your and the
committers' call. Happy to test any of them against the reproducer.
Regards,
Manu
On 2026-Sep-29, Manu wrote:
I'd be glad to put a patch together once there's a sense of the
preferred direction. The partial index is the smallest change, but
whether a new catalog index is the way to go, versus the pg_depend
lookup or the trigger-based early exit, is more your and the
committers' call. Happy to test any of them against the reproducer.
I vaguely recall looking into adding such an index, and finding out that
we don't support partial indexes on catalogs. Is that doable in some
clean way nowadays? (Or maybe the index worked fine, and what failed
was adding a syscache on top of it? Not sure.)
I first mentioned this in 2019 ...
/messages/by-id/20190318220223.GA15196@alvherre.pgsql
--
Álvaro Herrera Breisgau, Deutschland — https://www.EnterpriseDB.com/
"Drawing in linguistics, Makefiles are more like proto languages, where you might
identify a few fragments but otherwise never be sure if the speaker asked for
a proto sandwich or all your money." (grmnsftphr, https://lwn.net/Articles/1093871/)
I vaguely recall looking into adding such an index, and finding out
that we don't support partial indexes on catalogs. Is that doable in
some clean way nowadays? (Or maybe the index worked fine, and what
failed was adding a syscache on top of it? Not sure.)
Thanks for the pointer -- it sent me to check, and your recollection
holds up. It's the same "no usable index" seqscan from back then; it
only stayed cheap while pg_constraint was small, and not-null
constraints moving into it for 18 is what surfaced it.
On whether a partial index is doable cleanly: still not, and there are
two separate walls, not one.
Declaration: the bootstrap grammar has no place for a predicate.
Boot_DeclareIndexStmt in src/backend/bootstrap/bootparse.y is just
"DECLARE INDEX name oid ON table USING am ( params )", no WHERE. genbki
does pass the predicate string through into postgres.bki, so the build
succeeds, but initdb then fails with a syntax error at that line.
Maintenance: even past that, the catalog insert path assumes
non-partial. CatalogIndexInsert() in src/backend/catalog/indexing.c:
/*
* Expressional and partial indexes on system catalogs are not
* supported, nor exclusion constraints, nor deferred uniqueness
*/
Assert(indexInfo->ii_Predicate == NIL);
It never evaluates a predicate. Forcing a partial index in with
allow_system_table_mods confirms it: after ~1M not-null rows the
"WHERE confrelid <> 0" index holds all of them rather than the one FK
row, silently on a non-assert build. So a clean partial catalog index
would mean teaching both the bootstrap grammar and CatalogIndexInsert to
carry and evaluate a predicate.
The syscache isn't the blocker here. CloneFkReferenced() scans with
systable_beginscan(pg_constraint, InvalidOid, true, ...), not a syscache
lookup, so a plain (non-unique) index on confrelid is picked up just by
passing its OID in place of InvalidOid; no syscache involved.
So I went with a full index on pg_constraint(confrelid), which is
declarable today, and pointed the scan at it (one scankey on confrelid,
contype filtered in the loop). That's the attached v1. With the catalog
grown to ~1M not-null rows, ms per ATTACH goes from about 25 ms (growing
linearly) to 0.27 ms and stays flat as the catalog grows; make check is
clean. The cost is that a full index also covers every not-null/pk/check
row, so it is ~6 MB rather than the ~16 kB a confrelid<>0 partial would
be, and adds ~5% to bulk DDL on pg_constraint. That size gap is exactly
what makes the partial version attractive, and exactly what can't be
declared.
Glad to drop it for the trigger-based early-exit instead if you'd rather
not add a catalog index; that route also has the advantage of being
backpatchable, which a catalog change is not.
--
Manu
On 2026-Sep-29, Manu wrote:
So I went with a full index on pg_constraint(confrelid), which is
declarable today, and pointed the scan at it (one scankey on confrelid,
contype filtered in the loop). That's the attached v1. With the catalog
grown to ~1M not-null rows, ms per ATTACH goes from about 25 ms (growing
linearly) to 0.27 ms and stays flat as the catalog grows; make check is
clean. The cost is that a full index also covers every not-null/pk/check
row, so it is ~6 MB rather than the ~16 kB a confrelid<>0 partial would
be, and adds ~5% to bulk DDL on pg_constraint. That size gap is exactly
what makes the partial version attractive, and exactly what can't be
declared.
If I recall correctly, there are other pg_constraint scans that could
benefit from this index -- GetParentedForeignKeyRefs() at least; maybe
others? I couldn't find anything in a quick grep.
I mentioned the syscache because I think I wanted to add a syscache on
top of such index for some reason. It might well be that I'm
remembering a syscache that I wanted to add on some other column, maybe
even on a different catalog altogether :-)
Glad to drop it for the trigger-based early-exit instead if you'd rather
not add a catalog index; that route also has the advantage of being
backpatchable, which a catalog change is not.
Hmm, I'm not eager to backpatch anything here, I'd rather go with a
master-only solution.
--
Álvaro Herrera Breisgau, Deutschland — https://www.EnterpriseDB.com/
"Ninguna manada de bestias tiene una voz tan horrible como la humana" (Orual)
If I recall correctly, there are other pg_constraint scans that could
benefit from this index -- GetParentedForeignKeyRefs() at least; maybe
others? I couldn't find anything in a quick grep.
Right. v2 attached points three confrelid scans at the index:
- CloneFkReferenced() on ATTACH PARTITION / PARTITION OF
- GetParentedForeignKeyRefs() on DETACH PARTITION
- ATPrepChangePersistence() on ALTER TABLE ... SET UNLOGGED
The third is the "maybe others": grepping Anum_pg_constraint_confrelid
turns up its else branch, which scans confrelid on SET UNLOGGED and
whose own comment already noted it had no usable index -- it does now.
The other two share CloneFkReferenced's shape, so they scan on confrelid
alone and filter contype in the loop.
On a catalog bloated to ~250k rows, each of the three drops from a
pg_constraint seqscan to an index scan (seq_scan delta 1-2 -> 0), and
ATTACH stays at the ~25 ms -> 0.3 ms per partition from before. make
check is clean.
I mentioned the syscache because I think I wanted to add a syscache on
top of such index for some reason.
No need for these three -- they all go through systable_beginscan, so a
plain index is enough; I left the syscache out.
Hmm, I'm not eager to backpatch anything here, I'd rather go with a
master-only solution.
Works for me -- a catalog change is master-only anyway.
--
Manu
On 2026-Sep-29, Manu wrote:
The third is the "maybe others": grepping Anum_pg_constraint_confrelid
turns up its else branch, which scans confrelid on SET UNLOGGED and
whose own comment already noted it had no usable index -- it does now.
The other two share CloneFkReferenced's shape, so they scan on confrelid
alone and filter contype in the loop.
Makes sense. Please create a commitfest entry for this, if there isn't
one already.
I think we should explore the idea of adding support for partial indexes
on catalogs, though. Having an index 90% populated by useless entries
doesn't sound like the best use of resources. Perhaps we could also use
such functionality on ConstraintRelidTypidNameIndexId, splitting that
into two indexes, one for constraints on types and another for indexes
on relations, and avoid having to store InvalidOid on the other column.
This doesn't have to delay this patch, however.
--
Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/
"La rebeldía es la virtud original del hombre" (Arthur Schopenhauer)
On 2026-Sep-29, Álvaro Herrera wrote:
Makes sense. Please create a commitfest entry for this, if there
isn't one already.
Done -- it's CF 7365 in PG20-3.
I think we should explore the idea of adding support for partial
indexes on catalogs, though. Having an index 90% populated by
useless entries doesn't sound like the best use of resources.
Perhaps we could also use such functionality on
ConstraintRelidTypidNameIndexId, splitting that into two indexes,
one for constraints on types and another for indexes on relations,
and avoid having to store InvalidOid on the other column.
This doesn't have to delay this patch, however.
I'd like to take that up. I spent some time mapping what stands in
the way today, and it comes down to two places:
- The bootstrap index grammar (Boot_DeclareIndexStmt in
bootparse.y) has no WHERE production, so a predicate declared in
the catalog headers reaches genbki but initdb cannot parse it.
- CatalogIndexInsert() asserts ii_Predicate == NIL and never
evaluates a predicate, so even a built index would be maintained
for every row.
The engine side (index_create) already carries ii_Predicate, so the
work looks contained to those two spots rather than the access method
layer.
If that reading matches yours, I can start a separate thread with a
first sketch, using confrelid and the ConstraintRelidTypidNameIndexId
split you mention as the two motivating cases. Happy to hear if
you'd shape it differently.
Manu
On 2026-Sep-30, Manu wrote:
I spent some time mapping what stands in
the way today, and it comes down to two places:- The bootstrap index grammar (Boot_DeclareIndexStmt in
bootparse.y) has no WHERE production, so a predicate declared in
the catalog headers reaches genbki but initdb cannot parse it.- CatalogIndexInsert() asserts ii_Predicate == NIL and never
evaluates a predicate, so even a built index would be maintained
for every row.The engine side (index_create) already carries ii_Predicate, so the
work looks contained to those two spots rather than the access method
layer.If that reading matches yours, I can start a separate thread with a
first sketch, using confrelid and the ConstraintRelidTypidNameIndexId
split you mention as the two motivating cases. Happy to hear if
you'd shape it differently.
I think the difficult question here is how are we going to evaluate the
condition on the tuple. We absolutely can't run the full executor, so
there needs to be some way to limit what we're willing to do, and
finding a way to do it without calling anything other than a very
limited set of functions -- something like "is OID different than
0" has got to be enough. I have no idea how to make that work and still
have a representation of the restriction in the system catalogs for the
catalog rows themselves.
But yeah, let's discuss in a new thread if you can figure out an answer
to that.
--
Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/
Hi,
v3 attached, rebased on current master for CF 7365. The only change
from v2 is that it no longer bumps catversion, which was the conflict;
that's left to the committer. make check passes.
Regards,
Manu
Attachments:
t253993_10v3-0001-Index-pg_constraint.confrelid-to-avoid-seqscans-i.patchtext/x-patchDownload+22-15
Hi,
A status note for CF 7365, since the patch ended up split across two
threads.
The current version is v4, posted in the "Partial indexes on system
catalogs" thread [1]/messages/by-id/179115578677.1450953.3979833043111954204@gmail.com, after Tom pointed out [2]/messages/by-id/1298334.1790896057@sss.pgh.pa.us that a plain non-unique
index on confrelid, like the one on contypid, is all this needs. That
is what v4 does. It also covers a lookup that v3 missed,
heap_truncate_find_FKs(), which runs on TRUNCATE and at every commit
that touches an ON COMMIT DELETE ROWS table with a foreign key or
trigger.
v4 still applies cleanly to master, make check passes, CFbot is green,
and amcheck is clean on the new index after DDL churn. On today's
master with about a million constraints, the affected operations drop
from tens of milliseconds to under two: ATTACH PARTITION, DETACH
PARTITION, CREATE TABLE ... PARTITION OF, TRUNCATE of a table with a
trigger, and ALTER TABLE ... SET UNLOGGED are all around 50-90 ms on
master and under 2 ms with the index, roughly an 80x drop. A commit
loop over an ON COMMIT DELETE ROWS table that has a foreign key goes
from about 3.5 s to a few ms. Creating not-null or foreign-key
constraints costs the same as before, and the index is about 6 MB next
to a 340 MB catalog.
The existing regression tests already run all four call sites
(foreign_key, truncate, temp, alter_table), so I don't think it needs
new tests; it needs a reviewer. Álvaro, since this got slow when
not-null constraints moved into pg_constraint, would you have time to
look at it?
[1]: /messages/by-id/179115578677.1450953.3979833043111954204@gmail.com
[2]: /messages/by-id/1298334.1790896057@sss.pgh.pa.us
Regards,
Manu
On 2026-Oct-15, Manu wrote:
The existing regression tests already run all four call sites
(foreign_key, truncate, temp, alter_table), so I don't think it needs
new tests; it needs a reviewer. Álvaro, since this got slow when
not-null constraints moved into pg_constraint, would you have time to
look at it?
Sure. But this is pg20 development work, and I'm still on the hook for
the yet-to-be-released pg19 and I also want to get older bugs repaired.
So I'm afraid this will have to wait.
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
"No hay ausente sin culpa ni presente sin disculpa" (Prov. francés)