Thread
-
UPDATE run check constraints for affected columns only
jian he <jian.universality@gmail.com> — 2025-12-01T06:20:32Z
hi. while casually looking at https://wiki.postgresql.org/wiki/Todo then I found out this thread: https://postgr.es/m/1326055327.15293.13.camel%40vanquo.pezone.net Seems easier to do nowadays. The attached patch implements the $subject. regress tests seems not enough to test it. Following the approach in 001_constraint_validation.pl, we use ereport(DEBUG1, errmsg_internal), then grep the logs to check whether the enforced constraint verification was skipped or not. we can not add check constraint to VIEW, tests covered partitioned table scarenio. DEMO: CREATE TABLE upd_check_skip (a int, b int, c int, d int generated always as (b+c) STORED); ALTER TABLE upd_check_skip ADD CONSTRAINT cc2 CHECK(a+c < 100); ALTER TABLE upd_check_skip ADD CONSTRAINT cc3 CHECK(b < 1); ALTER TABLE upd_check_skip ADD CONSTRAINT cc4 CHECK(d < 2); INSERT INTO upd_check_skip DEFAULT VALUES; SET client_min_messages to DEBUG1; --constraint verification will be skipped for cc3, cc4 UPDATE upd_check_skip SET a = 1; --constraint verification will be skipped for cc2 UPDATE upd_check_skip SET b = -1; --constraint verification will be skipped for cc3 UPDATE upd_check_skip SET c = -1; -- jian https://www.enterprisedb.com
-
Re: UPDATE run check constraints for affected columns only
Tom Lane <tgl@sss.pgh.pa.us> — 2025-12-01T06:33:19Z
jian he <jian.universality@gmail.com> writes: > The attached patch implements the $subject. Does this cover the case where a BEFORE UPDATE trigger has modified columns that were not mentioned in UPDATE...SET? regards, tom lane
-
回复: UPDATE run check constraints for affected columns only
li carol <carol.li2025@outlook.com> — 2025-12-01T09:24:11Z
Hi, +1 on Tom's point about BEFORE UPDATE triggers. I also noticed that in execReplication.c, ExecSimpleRelationUpdate() passes CMD_INSERT to ExecConstraints(): ExecConstraints(CMD_INSERT, resultRelInfo, slot, estate); I think this should be CMD_UPDATE? Regards, Yuan Li(carol) -----邮件原件----- 发件人: Tom Lane <tgl@sss.pgh.pa.us> 发送时间: 2025年12月1日 14:33 收件人: jian he <jian.universality@gmail.com> 抄送: PostgreSQL-development <pgsql-hackers@postgresql.org> 主题: Re: UPDATE run check constraints for affected columns only jian he <jian.universality@gmail.com> writes: > The attached patch implements the $subject. Does this cover the case where a BEFORE UPDATE trigger has modified columns that were not mentioned in UPDATE...SET? regards, tom lane
-
Re: UPDATE run check constraints for affected columns only
jian he <jian.universality@gmail.com> — 2025-12-07T15:50:37Z
On Mon, Dec 1, 2025 at 2:33 PM Tom Lane <tgl@sss.pgh.pa.us> wrote: > > jian he <jian.universality@gmail.com> writes: > > The attached patch implements the $subject. > > Does this cover the case where a BEFORE UPDATE trigger has modified > columns that were not mentioned in UPDATE...SET? > > regards, tom lane hi. in ExecInitGenerated, we have: /* * In an UPDATE, we can skip computing any generated columns that do not * depend on any UPDATE target column. But if there is a BEFORE ROW * UPDATE trigger, we cannot skip because the trigger might change more * columns. */ if (cmdtype == CMD_UPDATE && !(rel->trigdesc && rel->trigdesc->trig_update_before_row)) updatedCols = ExecGetUpdatedCols(resultRelInfo, estate); else updatedCols = NULL; So I applied the equivalent approach. This should works fine, because if we are able to skip computing certain generated columns, then we sure sure be able to skip evaluating some check constraints. -- jian https://www.enterprisedb.com -
Re: UPDATE run check constraints for affected columns only
jian he <jian.universality@gmail.com> — 2026-01-23T02:09:27Z
hi. code has been further simplified and is now more neat. The test is kind of verbose now. -- jian https://www.enterprisedb.com/
-
Re: UPDATE run check constraints for affected columns only
jian he <jian.universality@gmail.com> — 2026-03-04T01:04:49Z
On Fri, Jan 23, 2026 at 10:09 AM jian he <jian.universality@gmail.com> wrote: > code has been further simplified and is now more neat. > The test is kind of verbose now. > if (check_attrs && !bms_is_member(-FirstLowInvalidHeapAttributeNumber, check_attrs) && !bms_overlap(check_attrs, updatedCols)) { ereport(DEBUG1, errmsg_internal("skipping verification for constraint \"%s\" on table \"%s\"", check[i].ccname, RelationGetRelationName(rel))); } We need tests to reach the above ereport(DEBUG1, branch. We can use +SET log_statement to NONE; +SET client_min_messages TO 'debug1'; to make sure the regress output has the DEBUG1 message. -- jian https://www.enterprisedb.com/ -
Re: UPDATE run check constraints for affected columns only
jian he <jian.universality@gmail.com> — 2026-03-05T03:12:04Z
Thank to Jacob Champion for the off-list review. Now ExecRelCheck looks much neater. -- jian https://www.enterprisedb.com/
-
Re: UPDATE run check constraints for affected columns only
jian he <jian.universality@gmail.com> — 2026-03-06T02:17:35Z
hi. We cache ri_CheckConstraintExprs in ExecConstraints and must initialize them all if different actions like INSERT and UPDATE, happen together in a query. Both INSERT and UPDATE need to use these ri_CheckConstraintExprs. Invoke INSERT AND UPDATE together can happen within MERGE command. We confirm it's a MERGE operation by checking that resultRelInfo->ri_MergeActions is not NIL. See ExecMergeNotMatched and ExecMergeMatched. For cross-partition updates (ExecCrossPartitionUpdate), the operation splits into an INSERT and DELETE, we can treat it as a single action. -- jian https://www.enterprisedb.com/
-
Re: UPDATE run check constraints for affected columns only
Florin Irion <irionr@gmail.com> — 2026-03-06T16:10:33Z
Hi Jian, Haritabh and I have been reviewing this patch. The core optimization is sound — the BEFORE ROW UPDATE trigger guard, the generated column handling via expand_generated_columns_in_expr + ExecGetAllUpdatedCols, and the whole-row reference check are all correct. We independently found the same MERGE issue you fixed in v6, where ri_CheckConstraintExprs is shared between INSERT and UPDATE actions on the same ResultRelInfo. Nice catch on the fix. That said, I think v6's approach of disabling the optimization entirely for MERGE is more conservative than necessary. A MERGE with only WHEN MATCHED THEN UPDATE (no INSERT action) would still benefit from skipping unaffected constraints, but v6 disables it for all MERGE operations. An alternative that preserves the optimization for MERGE UPDATE actions would be to follow the existing ri_GeneratedExprsI/ri_GeneratedExprsU pattern — split into two separate cached arrays: ``` /* array of expr states for checking check constraints */ ExprState **ri_CheckConstraintExprsI; /* for INSERT */ ExprState **ri_CheckConstraintExprsU; /* for UPDATE */ ``` Then in ExecRelCheck, select the appropriate array based on cmdtype: ``` ExprState **checkExprs; checkExprs = (cmdtype == CMD_UPDATE) ? resultRelInfo->ri_CheckConstraintExprsU : resultRelInfo->ri_CheckConstraintExprsI; if (checkExprs == NULL) { Bitmapset *updatedCols = NULL; if (cmdtype == CMD_UPDATE && !(rel->trigdesc && rel->trigdesc->trig_update_before_row)) updatedCols = ExecGetAllUpdatedCols(resultRelInfo, estate); /* ... alloc and populate checkExprs ... */ if (cmdtype == CMD_UPDATE) resultRelInfo->ri_CheckConstraintExprsU = checkExprs; else resultRelInfo->ri_CheckConstraintExprsI = checkExprs; } ``` This way INSERT always compiles and checks all constraints, UPDATE gets the skip optimization even during MERGE, and both can safely coexist on the same ResultRelInfo. The lazy-init cost for the second array only applies when both code paths are actually taken, which matches the generated-columns precedent. It also makes INSERT ON CONFLICT DO UPDATE structurally safe rather than relying on INSERT always running first. A few other items on v6: 1. The MERGE test case only tests MERGE with a single UPDATE action and verifies the optimization is disabled. It doesn't test the actual dangerous scenario — MERGE with both INSERT and UPDATE actions where the INSERT row violates a constraint. Without that, a future refactor could reintroduce the original bug without any test failing. Something like: ``` CREATE TABLE merge_cc (id int PRIMARY KEY, a int, b int, CONSTRAINT cc CHECK (a > 0)); INSERT INTO merge_cc VALUES (1, 10, 10); MERGE INTO merge_cc t USING (VALUES (1, 99, 20), (2, -5, 30)) AS s(id, a, b) ON t.id = s.id WHEN MATCHED THEN UPDATE SET b = s.b WHEN NOT MATCHED THEN INSERT VALUES (s.id, s.a, s.b); -- must ERROR on cc (a = -5), not silently succeed ``` 2. The cross-partition update test comment says "cannot be skipped", but cross-partition UPDATE goes through ExecCrossPartitionUpdate which does DELETE + INSERT. The constraint check happens via ExecInsert with CMD_INSERT on the destination partition, so the optimization was never applicable. The test doesn't exercise anything specific to this patch. Cheers, Florin -- * Florin Irion * * https://www.enterprisedb.com <https://www.enterprisedb.com/>* -
Re: UPDATE run check constraints for affected columns only
jian he <jian.universality@gmail.com> — 2026-03-07T09:46:23Z
On Sat, Mar 7, 2026 at 12:10 AM Florin Irion <irionr@gmail.com> wrote: > > Hi Jian, > > An alternative that preserves the optimization for MERGE UPDATE actions > would be to follow the existing ri_GeneratedExprsI/ri_GeneratedExprsU > pattern — split into two separate cached arrays: > > ``` > /* array of expr states for checking check constraints */ > ExprState **ri_CheckConstraintExprsI; /* for INSERT */ > ExprState **ri_CheckConstraintExprsU; /* for UPDATE */ > ``` I have given it a try, please see attached v7. I don’t have a strong preference for either v6 or v7. > 2. The cross-partition update test comment says "cannot be skipped", but > cross-partition UPDATE goes through ExecCrossPartitionUpdate which > does DELETE + INSERT. The constraint check happens via ExecInsert with > CMD_INSERT on the destination partition, so the optimization was never > applicable. The test doesn't exercise anything specific to this patch. > OK. I kept these tests. I think covering this scenario is useful. Perhaps it has already been tested elsewhere, but including it here makes the tests more complete -- jian https://www.enterprisedb.com/