Thread
Commits
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Small terminology fixes in comments
- f0aedc7cb0ed 19 (unreleased) landed
-
Require UPDATE permission on FOR PORTION OF column
- 7ac030d5b152 19 (unreleased) landed
-
FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
SATYANARAYANA NARLAPURAM <satyanarlapuram@gmail.com> — 2026-04-07T08:42:42Z
Hi hackers, It appears that this is a bug where UPDATE FOR ... PORTION OF fails to recompute GENERATED ALWAYS AS ... STORED columns whose expressions reference the range column being narrowed. Please find the repro below. postgres=# CREATE TABLE t (id int, valid_at int4range NOT NULL, val int, range_len int GENERATED ALWAYS AS (upper(valid_at) - lower(valid_at)) STORED); INSERT INTO t VALUES (1, '[1,100)', 10); UPDATE t FOR PORTION OF valid_at FROM 30 TO 70 SET val = 99; SELECT *, upper(valid_at) - lower(valid_at) AS expected FROM t ORDER BY valid_at; CREATE TABLE INSERT 0 1 UPDATE 1 id | valid_at | val | range_len | expected ----+----------+-----+-----------+---------- 1 | [1,30) | 10 | 29 | 29 1 | [30,70) | 99 | 99 | 40 1 | [70,100) | 10 | 30 | 30 (3 rows) The updated row [30,70) retains the stale range_len = 99 from the original [1,100) range. The leftover inserts are correct because CMD_INSERT unconditionally recomputes all generated columns. Virtual generated columns are not affected and are computed correctly because they're evaluated at read time from the actual stored valid_at value. Further looking at the code it appears, In transformForPortionOfClause(), the range column is intentionally not added to perminfo->updatedCols. Since the range column is absent from updatedCols, any generated stored column whose expression depends solely on the range column (e.g., upper(valid_at) - lower(valid_at)) is skipped. Therefore, its expression is never prepared and never recomputed during the FPO update. Attached a draft patch that has the test scenario and a fix to address this issue. In ExecInitGenerated, after retrieving updatedCols, the patch additionally checks whether the owning ModifyTableState contains an FPO clause. If it does, the attribute number (attno) of the range column is added to updatedCols. Thanks, Satya -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
jian he <jian.universality@gmail.com> — 2026-04-10T09:10:30Z
Hi. ExecUpdate->ExecUpdateEpilogue->ExecForPortionOfLeftovers In ExecForPortionOfLeftovers, we have """ if (!resultRelInfo->ri_forPortionOf) { /* * If we don't have a ForPortionOfState yet, we must be a partition * child being hit for the first time. Make a copy from the root, with * our own tupleTableSlot. We do this lazily so that we don't pay the * price of unused partitions. */ ForPortionOfState *leafState = makeNode(ForPortionOfState); } """ We reached the end of ExecUpdate, and then suddenly initialized resultRelInfo->ri_forPortionOf. That seems wrong; we should initialize resultRelInfo->ri_forPortionOf earlier so other places can use that information, such as ForPortionOfState->fp_rangeAttno. We can initialize ForPortionOfState right after ExecModifyTable: """ /* If it's not the same as last time, we need to locate the rel */ if (resultoid != node->mt_lastResultOid) resultRelInfo = ExecLookupResultRelByOid(node, resultoid, false, true); """ In ExecForPortionOfLeftovers, we should use ForPortionOfState more and ForPortionOfExpr less. (ForPortionOfExpr and ForPortionOfState share some overlapping information; maybe we can eliminate some common fields or put ForPortionOfExpr into ForPortionOfState). As noted in [1], the FOR PORTION OF column is physically modified, even though we didn't require explicit UPDATE privileges, we failed to track this column in ExecGetUpdatedCols and ExecGetExtraUpdatedCols. This omission directly impacts the ExecInsertIndexTuples -> index_unchanged_by_update -> ExecGetExtraUpdatedCols execution path. We should ensure ExecGetExtraUpdatedCols also accounts for this column. Otherwise, we need a clearer explanation for why index_unchanged_by_update can safely ignore a column that is being physically modified. I have added regression test cases for CREATE TRIGGER UPDATE OF column_name. The attached patch also addressed the table inheritance issue in https://postgr.es/m/CAHg+QDcsXsUVaZ+JwM02yDRQEi=cL_rTH_ROLDYgOx004sQu7A@mail.gmail.com I've combined all these changes into a single patch for now, as they seem closely related. [1]: https://postgr.es/m/CACJufxHALFKca5SMn5DNnbrX2trPamVL6napn_nm35p15yw+rg@mail.gmail.com -- jian https://www.enterprisedb.com/ -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
SATYANARAYANA NARLAPURAM <satyanarlapuram@gmail.com> — 2026-04-10T22:00:57Z
Hi Jian, On Fri, Apr 10, 2026 at 2:11 AM jian he <jian.universality@gmail.com> wrote: > Hi. > > ExecUpdate->ExecUpdateEpilogue->ExecForPortionOfLeftovers > In ExecForPortionOfLeftovers, we have > """ > if (!resultRelInfo->ri_forPortionOf) > { > /* > * If we don't have a ForPortionOfState yet, we must be a partition > * child being hit for the first time. Make a copy from the root, > with > * our own tupleTableSlot. We do this lazily so that we don't pay > the > * price of unused partitions. > */ > ForPortionOfState *leafState = makeNode(ForPortionOfState); > } > """ > We reached the end of ExecUpdate, and then suddenly initialized > resultRelInfo->ri_forPortionOf. > That seems wrong; we should initialize resultRelInfo->ri_forPortionOf > earlier so other places can use that information, such as > ForPortionOfState->fp_rangeAttno. > > We can initialize ForPortionOfState right after ExecModifyTable: > """ > /* If it's not the same as last time, we need to locate the rel */ > if (resultoid != node->mt_lastResultOid) > resultRelInfo = ExecLookupResultRelByOid(node, resultoid, > false, true); > """ > > In ExecForPortionOfLeftovers, we should use ForPortionOfState more and > ForPortionOfExpr less. > (ForPortionOfExpr and ForPortionOfState share some overlapping information; > maybe we can eliminate some common fields or put ForPortionOfExpr into > ForPortionOfState). > > > As noted in [1], the FOR PORTION OF column is physically modified, > even though we didn't require explicit UPDATE privileges, > we failed to track this column in ExecGetUpdatedCols and > ExecGetExtraUpdatedCols. > This omission directly impacts the ExecInsertIndexTuples -> > index_unchanged_by_update -> ExecGetExtraUpdatedCols execution path. > We should ensure ExecGetExtraUpdatedCols also accounts for this column. > Otherwise, we need a clearer explanation for why > index_unchanged_by_update can safely ignore a column that is being > physically modified. > > I have added regression test cases for CREATE TRIGGER UPDATE OF > column_name. > > The attached patch also addressed the table inheritance issue in > > https://postgr.es/m/CAHg+QDcsXsUVaZ+JwM02yDRQEi=cL_rTH_ROLDYgOx004sQu7A@mail.gmail.com > > I've combined all these changes into a single patch for now, as they > seem closely related. > > [1]: > https://postgr.es/m/CACJufxHALFKca5SMn5DNnbrX2trPamVL6napn_nm35p15yw+rg@mail.gmail.com I applied your patch and tested. The following scenarios are now passing: (1) table inheritance issue I reported in [1], (2) issue reported in this thread. Following are still failing: (1) instead of triggers + views, mentioned in the thread [2], it has both the test case and the fix. (2) For Portion Of DELETE loses rows when a BEFORE INSERT trigger returns NULL DROP TABLE IF EXISTS subscriptions CASCADE; CREATE TABLE subscriptions ( sub_id int, period int4range NOT NULL, plan text ); CREATE OR REPLACE FUNCTION reject_new_subscriptions() RETURNS trigger AS $$ BEGIN -- Business rule: no new subscription rows allowed via INSERT. RETURN NULL; END; $$ LANGUAGE plpgsql; CREATE TRIGGER no_new_subs BEFORE INSERT ON subscriptions FOR EACH ROW EXECUTE FUNCTION reject_new_subscriptions(); -- Pre-existing row (bypass trigger to seed it). ALTER TABLE subscriptions DISABLE TRIGGER no_new_subs; INSERT INTO subscriptions VALUES (1, '[1,100)', 'premium'); ALTER TABLE subscriptions ENABLE TRIGGER no_new_subs; SELECT * FROM subscriptions; -- 1 row: (1, [1,100), premium) -- Delete just the [40,60) slice. DELETE FROM subscriptions FOR PORTION OF period FROM 40 TO 60; SELECT * FROM subscriptions ORDER BY period; -- Should be two rows: [1,40) and [60,100) -- Actually: 0 rows. The whole subscription vanished. SELECT count(*) AS remaining FROM subscriptions; -- Expected 2, got 0. (3) FPO UPDATE loses leftovers the same way DROP TABLE IF EXISTS room_bookings CASCADE; CREATE TABLE room_bookings ( booking_id int, slot int4range NOT NULL, note text ); CREATE OR REPLACE FUNCTION block_booking_inserts() RETURNS trigger AS $$ BEGIN -- Business rule: bookings created only through an API layer. RETURN NULL; END; $$ LANGUAGE plpgsql; CREATE TRIGGER booking_guard BEFORE INSERT ON room_bookings FOR EACH ROW EXECUTE FUNCTION block_booking_inserts(); ALTER TABLE room_bookings DISABLE TRIGGER booking_guard; INSERT INTO room_bookings VALUES (1, '[1,100)', 'team meeting'); ALTER TABLE room_bookings ENABLE TRIGGER booking_guard; SELECT * FROM room_bookings; -- 1 row: (1, [1,100), team meeting) -- Shorten the meeting to only [40,60). UPDATE room_bookings FOR PORTION OF slot FROM 40 TO 60 SET note = 'shortened'; SELECT * FROM room_bookings ORDER BY slot; -- Should be three rows: -- [1,40) team meeting -- [40,60) shortened -- [60,100) team meeting -- Actually: only the [40,60) row survives. SELECT count(*) AS remaining FROM room_bookings; -- Expected 3, got 1. [1]: https://postgr.es/m/CAHg+QDcsXsUVaZ+JwM02yDRQEi=cL_rTH_ROLDYgOx004sQu7A@mail.gmail.com [2]: https://www.postgresql.org/message-id/flat/CACJufxHALFKca5SMn5DNnbrX2trPamVL6napn_nm35p15yw%2Brg%40mail.gmail.com#ab4216dc6828fb0d7ada48aab9e330d0 Thanks, Satya -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
jian he <jian.universality@gmail.com> — 2026-04-11T08:36:51Z
On Sat, Apr 11, 2026 at 6:01 AM SATYANARAYANA NARLAPURAM <satyanarlapuram@gmail.com> wrote: > > Following are still failing: > > (1) instead of triggers + views, mentioned in the thread [2], it has both the test case and the fix. > I will check and reply in that thread. > > (2) For Portion Of DELETE loses rows when a BEFORE INSERT trigger returns NULL > > DROP TABLE IF EXISTS subscriptions CASCADE; > CREATE TABLE subscriptions ( > sub_id int, > period int4range NOT NULL, > plan text > ); > > CREATE OR REPLACE FUNCTION reject_new_subscriptions() RETURNS trigger AS $$ > BEGIN > -- Business rule: no new subscription rows allowed via INSERT. > RETURN NULL; > END; > $$ LANGUAGE plpgsql; > > CREATE TRIGGER no_new_subs > BEFORE INSERT ON subscriptions > FOR EACH ROW EXECUTE FUNCTION reject_new_subscriptions(); > > -- Pre-existing row (bypass trigger to seed it). > ALTER TABLE subscriptions DISABLE TRIGGER no_new_subs; > INSERT INTO subscriptions VALUES (1, '[1,100)', 'premium'); > ALTER TABLE subscriptions ENABLE TRIGGER no_new_subs; > > SELECT * FROM subscriptions; > -- 1 row: (1, [1,100), premium) > > -- Delete just the [40,60) slice. > DELETE FROM subscriptions FOR PORTION OF period FROM 40 TO 60; > > SELECT * FROM subscriptions ORDER BY period; > -- Should be two rows: [1,40) and [60,100) > -- Actually: 0 rows. The whole subscription vanished. > > SELECT count(*) AS remaining FROM subscriptions; > -- Expected 2, got 0. > I think this is expected. https://www.postgresql.org/docs/devel/sql-delete.html says <<>> When FOR PORTION OF is used, this can result in users who don't have INSERT privileges firing INSERT triggers. This should be considered when using SECURITY DEFINER trigger functions. <<>> We first tried inserting [1,40) and [60,100), but they were rejected and not inserted because the trigger function reject_new_subscriptions returned NULL. See ExecInsert: `````` if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_insert_before_row) { /* Flush any pending inserts, so rows are visible to the triggers */ if (estate->es_insert_pending_result_relations != NIL) ExecPendingInserts(estate); if (!ExecBRInsertTriggers(estate, resultRelInfo, slot)) return NULL; /* "do nothing" */ } `````` > (3) FPO UPDATE loses leftovers the same way > > -- Shorten the meeting to only [40,60). > UPDATE room_bookings FOR PORTION OF slot FROM 40 TO 60 SET note = 'shortened'; > > SELECT * FROM room_bookings ORDER BY slot; > -- Should be three rows: > -- [1,40) team meeting > -- [40,60) shortened > -- [60,100) team meeting > -- Actually: only the [40,60) row survives. > For the same reason as above, I think the current behavior is correct. -- jian https://www.enterprisedb.com/ -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Paul A Jungwirth <pj@illuminatedcomputing.com> — 2026-04-15T20:59:21Z
On Fri, Apr 10, 2026 at 3:01 PM SATYANARAYANA NARLAPURAM <satyanarlapuram@gmail.com> wrote: > >> I've combined all these changes into a single patch for now, as they >> seem closely related. >> >> [1]: https://postgr.es/m/CACJufxHALFKca5SMn5DNnbrX2trPamVL6napn_nm35p15yw+rg@mail.gmail.com > > I applied your patch and tested. The following scenarios are now passing: (1) table inheritance issue I reported in [1], (2) issue reported in this thread. They look good to me too. I read through the patch and made some stylistic changes, as well as some grammar/typo fixes. In ExecInitForPortionOf I tried to group things for each case more closely, instead of separate disconnected branches. I also added/moved some comments. Please see the attached. There's something I think we could still improve: we omit the valid-time in updatedCols, since that bitmapset is for permissions checking (at least originally), but now other features are using it as well. Our fix adds special logic to consider the valid-at column for GENERATED column dependencies and more special logic for UPDATE OF triggers. Perhaps we should add the attno to updatedCols after all, and put the special logic in the permissions check instead? That seems simpler and more robust. Or maybe it's time to have separate bitmapsets, one for permissions and another for everything else? What do you think? I'm not sure we should be consolidating these fixes into one patch. But I tried to call out all the issues in my commit message. > Following are still failing: > > (1) instead of triggers + views, mentioned in the thread [2], it has both the test case and the fix. I'll follow up there. > (2) For Portion Of DELETE loses rows when a BEFORE INSERT trigger returns NULL > > ... > > (3) FPO UPDATE loses leftovers the same way I agree with jian that this is the correct behavior. The inserts are supposed to fire triggers, and if a trigger signals to cancel the insert, that's what we should do. Yours, -- Paul ~{:-) pj@illuminatedcomputing.com -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
jian he <jian.universality@gmail.com> — 2026-04-17T08:13:28Z
On Thu, Apr 16, 2026 at 4:59 AM Paul A Jungwirth <pj@illuminatedcomputing.com> wrote: > > There's something I think we could still improve: we omit the > valid-time in updatedCols, since that bitmapset is for permissions > checking (at least originally), but now other features are using it as > well. Our fix adds special logic to consider the valid-at column for > GENERATED column dependencies and more special logic for UPDATE OF > triggers. Perhaps we should add the attno to updatedCols after all, > and put the special logic in the permissions check instead? That seems > simpler and more robust. Or maybe it's time to have separate > bitmapsets, one for permissions and another for everything else? What > do you think? > + /* + * For UPDATE ... FOR PORTION OF, the range column is also being modified + * (narrowed via intersection), but it is not included in updatedCols + * because the user does not need UPDATE permission on it. We must + * account for it here so that generated columns referencing the range + * column are recomputed. + */ + if (resultRelInfo->ri_forPortionOf) + { + AttrNumber rangeAttno = resultRelInfo->ri_forPortionOf->fp_rangeAttno; + + updatedCols = bms_add_member(bms_copy(updatedCols), + rangeAttno - FirstLowInvalidHeapAttributeNumber); + } + Putting the above into ExecGetUpdatedCols would be more neat. InitPlan->ExecCheckPermissions happened earlier than ExecGetUpdatedCols, So I think it will work. Another reason to do it this way is that some places only call ExecGetUpdatedCols, not ExecGetExtraUpdatedCols. Put the above into ExecGetUpdatedCols, then we don't need to worry about whether ExecGetExtraUpdatedCols is called. ExecGetExtraUpdatedCols saves the ri_extraUpdatedCols to estate->es_query_cxt. For ExecGetUpdatedCols, we can do the same: save the FOR PORTION OF column to estate->es_query_cxt. Please find the attached diff, which is based on your V3 patch. ExecForPortionOfLeftovers /* * Get the range's type cache entry. This is worth caching for the whole * UPDATE/DELETE as range functions do. */ typcache = fpoState->fp_leftoverstypcache; if (typcache == NULL) { typcache = lookup_type_cache(forPortionOf->rangeType, 0); fpoState->fp_leftoverstypcache = typcache; } It seems fp_leftoverstypcache is never being used? place it to ExecInitModifyTable would be better, i think. /* * Get the ranges to the left/right of the targeted range. We call a SETOF * support function and insert as many temporal leftovers as it gives us. * Although rangetypes have 0/1/2 leftovers, multiranges have 0/1, and * other types may have more. */ Currently, we only support anymultirange and anyrange. so here "and other types may have more." is kind of wrong? -- jian https://www.enterprisedb.com/ -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Paul A Jungwirth <pj@illuminatedcomputing.com> — 2026-04-19T20:10:06Z
On Fri, Apr 17, 2026 at 1:14 AM jian he <jian.universality@gmail.com> wrote: > > On Thu, Apr 16, 2026 at 4:59 AM Paul A Jungwirth > <pj@illuminatedcomputing.com> wrote: > > > > There's something I think we could still improve: we omit the > > valid-time in updatedCols, since that bitmapset is for permissions > > checking (at least originally), but now other features are using it as > > well. Our fix adds special logic to consider the valid-at column for > > GENERATED column dependencies and more special logic for UPDATE OF > > triggers. Perhaps we should add the attno to updatedCols after all, > > and put the special logic in the permissions check instead? That seems > > simpler and more robust. Or maybe it's time to have separate > > bitmapsets, one for permissions and another for everything else? What > > do you think? > > > > + /* > + * For UPDATE ... FOR PORTION OF, the range column is also being modified > + * (narrowed via intersection), but it is not included in updatedCols > + * because the user does not need UPDATE permission on it. We must > + * account for it here so that generated columns referencing the range > + * column are recomputed. > + */ > + if (resultRelInfo->ri_forPortionOf) > + { > + AttrNumber rangeAttno = resultRelInfo->ri_forPortionOf->fp_rangeAttno; > + > + updatedCols = bms_add_member(bms_copy(updatedCols), > + rangeAttno - FirstLowInvalidHeapAttributeNumber); > + } > + > Putting the above into ExecGetUpdatedCols would be more neat. > InitPlan->ExecCheckPermissions happened earlier than ExecGetUpdatedCols, > So I think it will work. Okay, I like this approach. Thank you for the patch! I'm not sure about mutating perminfo though. It saves repeated bms allocations for multi-row updates, but it seems a bit surprising. > Please find the attached diff, which is based on your V3 patch. This fix doesn't work for partitioned tables, because in ExecGetUpdatedCols ri_forPortionOf->fp_rangeAttno has already been mapped to the child table, but then we add it to the unmapped bitmapset and map it again. I updated an existing FOR PORTION OF test on partitioned tables to show this, by adding a GENERATED STORED column. So instead you need to add to the bitmapset *after* mapping. That's the approach I've taken here. And then mutating perminfo is definitely wrong, because it has the parent attnos. > ExecForPortionOfLeftovers > /* > * Get the range's type cache entry. This is worth caching for the whole > * UPDATE/DELETE as range functions do. > */ > typcache = fpoState->fp_leftoverstypcache; > if (typcache == NULL) > { > typcache = lookup_type_cache(forPortionOf->rangeType, 0); > fpoState->fp_leftoverstypcache = typcache; > } > It seems fp_leftoverstypcache is never being used? > place it to ExecInitModifyTable would be better, i think. You're right; that must have been left over from an earlier patch. But actually the fix about preventing BEFORE triggers from changing valid_at winds up using it. So I left it in for now. > /* > * Get the ranges to the left/right of the targeted range. We call a SETOF > * support function and insert as many temporal leftovers as it gives us. > * Although rangetypes have 0/1/2 leftovers, multiranges have 0/1, and > * other types may have more. > */ > Currently, we only support anymultirange and anyrange. so here > "and other types may have more." is kind of wrong? That's all we support for now, but my goal is to allow user-defined functions to support other types. So this comment is looking forward to that future. v5 attached. Yours, -- Paul ~{:-) pj@illuminatedcomputing.com -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
jian he <jian.universality@gmail.com> — 2026-04-21T03:57:59Z
On Mon, Apr 20, 2026 at 4:10 AM Paul A Jungwirth <pj@illuminatedcomputing.com> wrote: > > v5 attached. > v5 will cause a segfault. + /* + * For UPDATE ... FOR PORTION OF, the range column is being modified + * (narrowed via intersection), but it is not included in updatedCols + * because the user does not need UPDATE permission on it. Now manualy + * add it to updatedCols. Since ri_forPortionOf->fp_rangeAttno is already + * mapped for the child partition, we have to add it after the mapping just + * above. Also that makes it unsafe to mutate perminfo. XXX: Always add the + * unmapped attno instead (before mapping), and mutate perminfo, to avoid + * repeated allocations? + */ + if (relinfo->ri_forPortionOf) + { + AttrNumber rangeAttno = relinfo->ri_forPortionOf->fp_rangeAttno; + + if (!bms_is_member(rangeAttno - FirstLowInvalidHeapAttributeNumber, + updatedCols)) + { + MemoryContext oldContext; + + oldContext = MemoryContextSwitchTo(estate->es_query_cxt); + + updatedCols = + bms_add_member(updatedCols, + rangeAttno - FirstLowInvalidHeapAttributeNumber); + + MemoryContextSwitchTo(oldContext); + } + } + + return updatedCols; + updatedCols = + bms_add_member(updatedCols, + rangeAttno - FirstLowInvalidHeapAttributeNumber); Here, use "perminfo->updatedCols" not "updatedCols", otherwise segfault. The attached diff based on v5, fixes this issue. +ALTER TABLE temporal_partitioned_3 ADD COLUMN range_len int GENERATED ALWAYS AS (upper(valid_at) - lower(valid_at)) STORED; Slightly refactoring the tests will allow for easier comparison of range_len values. -- jian https://www.enterprisedb.com/ -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Paul A Jungwirth <pj@illuminatedcomputing.com> — 2026-04-21T14:59:56Z
On Mon, Apr 20, 2026 at 8:58 PM jian he <jian.universality@gmail.com> wrote: > > + updatedCols = > + bms_add_member(updatedCols, > + rangeAttno - FirstLowInvalidHeapAttributeNumber); > > Here, use "perminfo->updatedCols" not "updatedCols", otherwise segfault. > The attached diff based on v5, fixes this issue. > > +ALTER TABLE temporal_partitioned_3 ADD COLUMN range_len int GENERATED > ALWAYS AS (upper(valid_at) - lower(valid_at)) STORED; > Slightly refactoring the tests will allow for easier comparison of > range_len values. I can't reproduce a segfault. I don't see how that code would lead to one. Can you share what you're doing to cause it? Simply running the regression tests doesn't do it for me. The v5 patch has quite a lot of code shared between both branches. I was trying to avoid that in the v5 patch. Is there any way to clean it up? I like the how the test change makes it easy to verify the range_len column. Yours, -- Paul ~{:-) pj@illuminatedcomputing.com -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
jian he <jian.universality@gmail.com> — 2026-04-22T01:11:18Z
On Tue, Apr 21, 2026 at 11:00 PM Paul A Jungwirth <pj@illuminatedcomputing.com> wrote: > > On Mon, Apr 20, 2026 at 8:58 PM jian he <jian.universality@gmail.com> wrote: > > > > + updatedCols = > > + bms_add_member(updatedCols, > > + rangeAttno - FirstLowInvalidHeapAttributeNumber); > > > > Here, use "perminfo->updatedCols" not "updatedCols", otherwise segfault. > > The attached diff based on v5, fixes this issue. > > > > +ALTER TABLE temporal_partitioned_3 ADD COLUMN range_len int GENERATED > > ALWAYS AS (upper(valid_at) - lower(valid_at)) STORED; > > Slightly refactoring the tests will allow for easier comparison of > > range_len values. > > I can't reproduce a segfault. I don't see how that code would lead to > one. Can you share what you're doing to cause it? Simply running the > regression tests doesn't do it for me. > Sorry for the noise. After cleaning the cached build directory and rebuilding, there is now no issue. +#include "nodes/print.h" This should be removed from v5.
-
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Paul A Jungwirth <pj@illuminatedcomputing.com> — 2026-04-22T18:03:18Z
On Tue, Apr 21, 2026 at 6:11 PM jian he <jian.universality@gmail.com> wrote: > > > I can't reproduce a segfault. I don't see how that code would lead to > > one. Can you share what you're doing to cause it? Simply running the > > regression tests doesn't do it for me. > > > > Sorry for the noise. After cleaning the cached build directory and > rebuilding, there is now no issue. > > +#include "nodes/print.h" > This should be removed from v5. Good catch! I removed that line in v7 (attached). I also included your test change to compute the range len by hand. Also a rebase was necessary after d3bba04154. Yours, -- Paul ~{:-) pj@illuminatedcomputing.com -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Paul A Jungwirth <pj@illuminatedcomputing.com> — 2026-05-05T21:50:05Z
On Wed, Apr 22, 2026 at 11:03 AM Paul A Jungwirth <pj@illuminatedcomputing.com> wrote: > > Good catch! I removed that line in v7 (attached). I also included your > test change to compute the range len by hand. Also a rebase was > necessary after d3bba04154. This needed a rebase. v8 attached. Yours, -- Paul ~{:-) pj@illuminatedcomputing.com -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Peter Eisentraut <peter@eisentraut.org> — 2026-05-06T11:39:18Z
On 05.05.26 23:50, Paul A Jungwirth wrote: > On Wed, Apr 22, 2026 at 11:03 AM Paul A Jungwirth > <pj@illuminatedcomputing.com> wrote: >> >> Good catch! I removed that line in v7 (attached). I also included your >> test change to compute the range len by hand. Also a rebase was >> necessary after d3bba04154. > > This needed a rebase. v8 attached. This patch fails the injection_points/isolation test for me. It looks like it causes a server crash. Check please.
-
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Paul A Jungwirth <pj@illuminatedcomputing.com> — 2026-05-06T17:13:39Z
On Wed, May 6, 2026 at 4:39 AM Peter Eisentraut <peter@eisentraut.org> wrote: > > On 05.05.26 23:50, Paul A Jungwirth wrote: > > On Wed, Apr 22, 2026 at 11:03 AM Paul A Jungwirth > > <pj@illuminatedcomputing.com> wrote: > >> > >> Good catch! I removed that line in v7 (attached). I also included your > >> test change to compute the range len by hand. Also a rebase was > >> necessary after d3bba04154. > > > > This needed a rebase. v8 attached. > > This patch fails the injection_points/isolation test for me. It looks > like it causes a server crash. Check please. Sorry, I didn't have injection_points enabled, but now I see it too. The attached v9 fixes it. Yours, -- Paul ~{:-) pj@illuminatedcomputing.com -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
jian he <jian.universality@gmail.com> — 2026-05-07T01:14:47Z
On Thu, May 7, 2026 at 1:13 AM Paul A Jungwirth <pj@illuminatedcomputing.com> wrote: > > Sorry, I didn't have injection_points enabled, but now I see it too. > The attached v9 fixes it. > diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 1eb6b9f1f40..363830f0158 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -1408,6 +1408,7 @@ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); + Bitmapset *updatedCols = perminfo->updatedCols; if (perminfo == NULL) ---------------------------------------------------- v8 crashes because in some cases, `perminfo` is NULL and we are ``perminfo->updatedCols;`` /* * If we don't have a ForPortionOfState yet, we must be a partition * child being hit for the first time. Make a copy from the root, with * our own TupleTableSlot. We do this lazily so that we don't pay the * price of unused partitions. */ if ((((ModifyTable *) context.mtstate->ps.plan)->forPortionOf) && !resultRelInfo->ri_forPortionOf) { ExecInitForPortionOf(context.mtstate, estate, resultRelInfo); } the comment "partition child" seems not 100% accurate. Since we also need to consider table inheritance. Maybe replace it with "child table". -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Chao Li <li.evan.chao@gmail.com> — 2026-05-07T04:34:43Z
> On May 7, 2026, at 01:13, Paul A Jungwirth <pj@illuminatedcomputing.com> wrote: > > On Wed, May 6, 2026 at 4:39 AM Peter Eisentraut <peter@eisentraut.org> wrote: >> >> On 05.05.26 23:50, Paul A Jungwirth wrote: >>> On Wed, Apr 22, 2026 at 11:03 AM Paul A Jungwirth >>> <pj@illuminatedcomputing.com> wrote: >>>> >>>> Good catch! I removed that line in v7 (attached). I also included your >>>> test change to compute the range len by hand. Also a rebase was >>>> necessary after d3bba04154. >>> >>> This needed a rebase. v8 attached. >> >> This patch fails the injection_points/isolation test for me. It looks >> like it causes a server crash. Check please. > > Sorry, I didn't have injection_points enabled, but now I see it too. > The attached v9 fixes it. > > Yours, > > -- > Paul ~{:-) > pj@illuminatedcomputing.com > <v9-0001-Fix-some-problems-with-UPDATE-FOR-PORTION-OF.patch> Hi Paul, I didn’t review this patch earlier because, from the subject, I thought it was only about recomputing generated stored columns. I just noticed that the patch also changes the inheritance-table path, and I posted another patch for the inheritance-table bug. Please see [1]. I tried applying the new tests from my patch on top of this patch, and it looks like this patch still does not fix the multi-inheritance case. So I’d like to check with you how we should proceed. I think there are two options: 1. Keep this patch focused on the generated-column issue described in the subject, and use my patch to fix the inheritance-table bug. 2. I can continue from this patch and extend it to fix the multi-inheritance case as well. Please let me know what you prefer. [1] https://www.postgresql.org/message-id/4245F94D-84F1-4E05-BF81-C458A6CF9901%40gmail.com Best regards, -- Chao Li (Evan) HighGo Software Co., Ltd. https://www.highgo.com/ -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Chao Li <li.evan.chao@gmail.com> — 2026-05-07T05:54:52Z
> On May 7, 2026, at 12:34, Chao Li <li.evan.chao@gmail.com> wrote: > > > >> On May 7, 2026, at 01:13, Paul A Jungwirth <pj@illuminatedcomputing.com> wrote: >> >> On Wed, May 6, 2026 at 4:39 AM Peter Eisentraut <peter@eisentraut.org> wrote: >>> >>> On 05.05.26 23:50, Paul A Jungwirth wrote: >>>> On Wed, Apr 22, 2026 at 11:03 AM Paul A Jungwirth >>>> <pj@illuminatedcomputing.com> wrote: >>>>> >>>>> Good catch! I removed that line in v7 (attached). I also included your >>>>> test change to compute the range len by hand. Also a rebase was >>>>> necessary after d3bba04154. >>>> >>>> This needed a rebase. v8 attached. >>> >>> This patch fails the injection_points/isolation test for me. It looks >>> like it causes a server crash. Check please. >> >> Sorry, I didn't have injection_points enabled, but now I see it too. >> The attached v9 fixes it. >> >> Yours, >> >> -- >> Paul ~{:-) >> pj@illuminatedcomputing.com >> <v9-0001-Fix-some-problems-with-UPDATE-FOR-PORTION-OF.patch> > > Hi Paul, > > I didn’t review this patch earlier because, from the subject, I thought it was only about recomputing generated stored columns. I just noticed that the patch also changes the inheritance-table path, and I posted another patch for the inheritance-table bug. Please see [1]. > > I tried applying the new tests from my patch on top of this patch, and it looks like this patch still does not fix the multi-inheritance case. > > So I’d like to check with you how we should proceed. I think there are two options: > > 1. Keep this patch focused on the generated-column issue described in the subject, and use my patch to fix the inheritance-table bug. > 2. I can continue from this patch and extend it to fix the multi-inheritance case as well. > > Please let me know what you prefer. > > [1] https://www.postgresql.org/message-id/4245F94D-84F1-4E05-BF81-C458A6CF9901%40gmail.com > I just looked into v9 and made a fix in ExecInitForPortionOf() that resolves the bug with multi-inheritance tables. I also added a test case for that. The inheritance-table bug affects not only UPDATE, but also DELETE, so I added test cases for DELETE as well. Please see 0002 for my changes. To make each commit self-contained, would you mind moving the code for the inheritance-table fix to 0002? Then you can keep focusing on 0001, and I can continue working on 0002. PFA v10 - 0001 the same as v9. 0002 fixed a bug with multi-inheritance tables. (Note, in 0002, there is a comment format change around line 1496, that was done by pgindent.) Best regards, -- Chao Li (Evan) HighGo Software Co., Ltd. https://www.highgo.com/ -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Chao Li <li.evan.chao@gmail.com> — 2026-05-07T07:05:34Z
> On May 7, 2026, at 13:54, Chao Li <li.evan.chao@gmail.com> wrote: > > > >> On May 7, 2026, at 12:34, Chao Li <li.evan.chao@gmail.com> wrote: >> >> >> >>> On May 7, 2026, at 01:13, Paul A Jungwirth <pj@illuminatedcomputing.com> wrote: >>> >>> On Wed, May 6, 2026 at 4:39 AM Peter Eisentraut <peter@eisentraut.org> wrote: >>>> >>>> On 05.05.26 23:50, Paul A Jungwirth wrote: >>>>> On Wed, Apr 22, 2026 at 11:03 AM Paul A Jungwirth >>>>> <pj@illuminatedcomputing.com> wrote: >>>>>> >>>>>> Good catch! I removed that line in v7 (attached). I also included your >>>>>> test change to compute the range len by hand. Also a rebase was >>>>>> necessary after d3bba04154. >>>>> >>>>> This needed a rebase. v8 attached. >>>> >>>> This patch fails the injection_points/isolation test for me. It looks >>>> like it causes a server crash. Check please. >>> >>> Sorry, I didn't have injection_points enabled, but now I see it too. >>> The attached v9 fixes it. >>> >>> Yours, >>> >>> -- >>> Paul ~{:-) >>> pj@illuminatedcomputing.com >>> <v9-0001-Fix-some-problems-with-UPDATE-FOR-PORTION-OF.patch> >> >> Hi Paul, >> >> I didn’t review this patch earlier because, from the subject, I thought it was only about recomputing generated stored columns. I just noticed that the patch also changes the inheritance-table path, and I posted another patch for the inheritance-table bug. Please see [1]. >> >> I tried applying the new tests from my patch on top of this patch, and it looks like this patch still does not fix the multi-inheritance case. >> >> So I’d like to check with you how we should proceed. I think there are two options: >> >> 1. Keep this patch focused on the generated-column issue described in the subject, and use my patch to fix the inheritance-table bug. >> 2. I can continue from this patch and extend it to fix the multi-inheritance case as well. >> >> Please let me know what you prefer. >> >> [1] https://www.postgresql.org/message-id/4245F94D-84F1-4E05-BF81-C458A6CF9901%40gmail.com >> > > I just looked into v9 and made a fix in ExecInitForPortionOf() that resolves the bug with multi-inheritance tables. I also added a test case for that. > > The inheritance-table bug affects not only UPDATE, but also DELETE, so I added test cases for DELETE as well. Please see 0002 for my changes. > > To make each commit self-contained, would you mind moving the code for the inheritance-table fix to 0002? Then you can keep focusing on 0001, and I can continue working on 0002. > > PFA v10 - 0001 the same as v9. 0002 fixed a bug with multi-inheritance tables. > > (Note, in 0002, there is a comment format change around line 1496, that was done by pgindent.) > > Best regards, > -- > Chao Li (Evan) > HighGo Software Co., Ltd. > https://www.highgo.com/ > > > > > <v10-0001-Fix-some-problems-with-UPDATE-FOR-PORTION-OF.patch><v10-0002-Fix-FOR-PORTION-OF-on-inherited-children-with-di.patch> A few comments for 0001: 1 - execUtils.c ``` + updatedCols = perminfo->updatedCols; + /* Map the columns to child's attribute numbers if needed. */ if (relinfo->ri_RootResultRelInfo) { TupleConversionMap *map = ExecGetRootToChildMap(relinfo, estate); if (map) - return execute_attr_map_cols(map->attrMap, perminfo->updatedCols); + updatedCols = execute_attr_map_cols(map->attrMap, updatedCols); + } + + /* + * For UPDATE ... FOR PORTION OF, the range column is being modified + * (narrowed via intersection), but it is not included in updatedCols + * because the user does not need UPDATE permission on it. Now manualy + * add it to updatedCols. Since ri_forPortionOf->fp_rangeAttno is already + * mapped for the child partition, we have to add it after the mapping just + * above. Also that makes it unsafe to mutate perminfo. XXX: Always add the + * unmapped attno instead (before mapping), and mutate perminfo, to avoid + * repeated allocations? + */ + if (relinfo->ri_forPortionOf) + { + AttrNumber rangeAttno = relinfo->ri_forPortionOf->fp_rangeAttno; + + if (!bms_is_member(rangeAttno - FirstLowInvalidHeapAttributeNumber, + updatedCols)) + { + MemoryContext oldContext; + + oldContext = MemoryContextSwitchTo(estate->es_query_cxt); + + updatedCols = + bms_add_member(updatedCols, + rangeAttno - FirstLowInvalidHeapAttributeNumber); ``` The comment explicitly says that it is unsafe to mutate perminfo, but bms_add_member() does not always allocate a new bitmapset. So if updatedCols still points to perminfo->updatedCols, then bms_add_member() may mutate perminfo->updatedCols. To verify that, I added Assert(updatedCols != perminfo->updatedCols); right after the bms_add_member(), then ran "make check". A lot of tests failed, which seems to confirm that perminfo->updatedCols was being mutated. So, I think we should make a copy the bitmapset before bms_add_member when needed, to make sure perminfo is not mutated, something like: ``` if (updatedCols == perminfo->updatedCols) updatedCols = bms_copy(updatedCols); updatedCols = bms_add_member(updatedCols, rangeAttno - FirstLowInvalidHeapAttributeNumber); ``` 2 - execUtils.c ``` + * because the user does not need UPDATE permission on it. Now manualy ``` Typo: manualy -> manually 3 - nodeModifyTable.c ``` + /* + * If we don't have a ForPortionOfState yet, we must be a partition + * child being hit for the first time. Make a copy from the root, with + * our own TupleTableSlot. We do this lazily so that we don't pay the + * price of unused partitions. + */ + if ((((ModifyTable *) context.mtstate->ps.plan)->forPortionOf) && + !resultRelInfo->ri_forPortionOf) + { + ExecInitForPortionOf(context.mtstate, estate, resultRelInfo); + } ``` I think this comment is stale. It could be a partition child or an inheritance child. 4 - nodeModifyTable.c ``` + /* Each partition needs a slot matching its tuple descriptor */ + leafState->fp_Existing = + table_slot_create(resultRelInfo->ri_RelationDesc, + &mtstate->ps.state->es_tupleTable); ``` I think the comment should say "each child relation" rather than "each partition". Best regards, -- Chao Li (Evan) HighGo Software Co., Ltd. https://www.highgo.com/ -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Paul A Jungwirth <pj@illuminatedcomputing.com> — 2026-05-07T23:26:08Z
On Wed, May 6, 2026 at 10:55 PM Chao Li <li.evan.chao@gmail.com> wrote: > > > I didn’t review this patch earlier because, from the subject, I thought it was only about recomputing generated stored columns. I just noticed that the patch also changes the inheritance-table path, and I posted another patch for the inheritance-table bug. Please see [1]. > > > > I tried applying the new tests from my patch on top of this patch, and it looks like this patch still does not fix the multi-inheritance case. > > > > So I’d like to check with you how we should proceed. I think there are two options: > > > > 1. Keep this patch focused on the generated-column issue described in the subject, and use my patch to fix the inheritance-table bug. > > 2. I can continue from this patch and extend it to fix the multi-inheritance case as well. > > > > Please let me know what you prefer. Thanks for your help on this! I agree that separating the patches would be better. > I just looked into v9 and made a fix in ExecInitForPortionOf() that resolves the bug with multi-inheritance tables. I also added a test case for that. > > The inheritance-table bug affects not only UPDATE, but also DELETE, so I added test cases for DELETE as well. Please see 0002 for my changes. > > To make each commit self-contained, would you mind moving the code for the inheritance-table fix to 0002? Then you can keep focusing on 0001, and I can continue working on 0002. > > PFA v10 - 0001 the same as v9. 0002 fixed a bug with multi-inheritance tables. I'll post a v11 addressing the feedback in your other email and moving the fixes for partitions/inheritance. Yours, -- Paul ~{:-) pj@illuminatedcomputing.com -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Paul A Jungwirth <pj@illuminatedcomputing.com> — 2026-05-07T23:47:46Z
On Thu, May 7, 2026 at 12:06 AM Chao Li <li.evan.chao@gmail.com> wrote: > > <v10-0001-Fix-some-problems-with-UPDATE-FOR-PORTION-OF.patch><v10-0002-Fix-FOR-PORTION-OF-on-inherited-children-with-di.patch> > > A few comments for 0001: > > 1 - execUtils.c > The comment explicitly says that it is unsafe to mutate perminfo, but bms_add_member() does not always allocate a new bitmapset. So if updatedCols still points to perminfo->updatedCols, then bms_add_member() may mutate perminfo->updatedCols. > > To verify that, I added Assert(updatedCols != perminfo->updatedCols); right after the bms_add_member(), then ran "make check". A lot of tests failed, which seems to confirm that perminfo->updatedCols was being mutated. > > So, I think we should make a copy the bitmapset before bms_add_member when needed, to make sure perminfo is not mutated, something like: > ``` > if (updatedCols == perminfo->updatedCols) > updatedCols = bms_copy(updatedCols); > > updatedCols = > bms_add_member(updatedCols, > rangeAttno - FirstLowInvalidHeapAttributeNumber); > ``` Ah, thanks for catching this! Fixed. > 2 - execUtils.c > ``` > + * because the user does not need UPDATE permission on it. Now manualy > ``` > > Typo: manualy -> manually Fixed. > 3 - nodeModifyTable.c > ``` > + /* > + * If we don't have a ForPortionOfState yet, we must be a partition > + * child being hit for the first time. Make a copy from the root, with > + * our own TupleTableSlot. We do this lazily so that we don't pay the > + * price of unused partitions. > + */ > + if ((((ModifyTable *) context.mtstate->ps.plan)->forPortionOf) && > + !resultRelInfo->ri_forPortionOf) > + { > + ExecInitForPortionOf(context.mtstate, estate, resultRelInfo); > + } > ``` > > I think this comment is stale. It could be a partition child or an inheritance child. Okay. > 4 - nodeModifyTable.c > ``` > + /* Each partition needs a slot matching its tuple descriptor */ > + leafState->fp_Existing = > + table_slot_create(resultRelInfo->ri_RelationDesc, > + &mtstate->ps.state->es_tupleTable); > ``` > > I think the comment should say "each child relation" rather than "each partition". Okay. In these v11 patches I've tried to separate (1) the fix for GENERATED STORED columns and UPDATE OF triggers (2) fixing inheritance and (and partitions too, for the bugs in #1). I understand why jian he combined these into one patch: there is some overlap. If you don't like my separation, let me know. Yours, -- Paul ~{:-) pj@illuminatedcomputing.com -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Chao Li <li.evan.chao@gmail.com> — 2026-05-08T07:09:46Z
> On May 8, 2026, at 07:47, Paul A Jungwirth <pj@illuminatedcomputing.com> wrote: > > On Thu, May 7, 2026 at 12:06 AM Chao Li <li.evan.chao@gmail.com> wrote: >>> <v10-0001-Fix-some-problems-with-UPDATE-FOR-PORTION-OF.patch><v10-0002-Fix-FOR-PORTION-OF-on-inherited-children-with-di.patch> >> >> A few comments for 0001: >> >> 1 - execUtils.c >> The comment explicitly says that it is unsafe to mutate perminfo, but bms_add_member() does not always allocate a new bitmapset. So if updatedCols still points to perminfo->updatedCols, then bms_add_member() may mutate perminfo->updatedCols. >> >> To verify that, I added Assert(updatedCols != perminfo->updatedCols); right after the bms_add_member(), then ran "make check". A lot of tests failed, which seems to confirm that perminfo->updatedCols was being mutated. >> >> So, I think we should make a copy the bitmapset before bms_add_member when needed, to make sure perminfo is not mutated, something like: >> ``` >> if (updatedCols == perminfo->updatedCols) >> updatedCols = bms_copy(updatedCols); >> >> updatedCols = >> bms_add_member(updatedCols, >> rangeAttno - FirstLowInvalidHeapAttributeNumber); >> ``` > > Ah, thanks for catching this! Fixed. > >> 2 - execUtils.c >> ``` >> + * because the user does not need UPDATE permission on it. Now manualy >> ``` >> >> Typo: manualy -> manually > > Fixed. > >> 3 - nodeModifyTable.c >> ``` >> + /* >> + * If we don't have a ForPortionOfState yet, we must be a partition >> + * child being hit for the first time. Make a copy from the root, with >> + * our own TupleTableSlot. We do this lazily so that we don't pay the >> + * price of unused partitions. >> + */ >> + if ((((ModifyTable *) context.mtstate->ps.plan)->forPortionOf) && >> + !resultRelInfo->ri_forPortionOf) >> + { >> + ExecInitForPortionOf(context.mtstate, estate, resultRelInfo); >> + } >> ``` >> >> I think this comment is stale. It could be a partition child or an inheritance child. > > Okay. > >> 4 - nodeModifyTable.c >> ``` >> + /* Each partition needs a slot matching its tuple descriptor */ >> + leafState->fp_Existing = >> + table_slot_create(resultRelInfo->ri_RelationDesc, >> + &mtstate->ps.state->es_tupleTable); >> ``` >> >> I think the comment should say "each child relation" rather than "each partition". > > Okay. > > In these v11 patches I've tried to separate (1) the fix for GENERATED > STORED columns and UPDATE OF triggers (2) fixing inheritance and (and > partitions too, for the bugs in #1). I understand why jian he combined > these into one patch: there is some overlap. If you don't like my > separation, let me know. > > Yours, > > -- > Paul ~{:-) > pj@illuminatedcomputing.com > <v11-0001-Fix-FOR-PORTION-OF-column-dependency-tracking.patch><v11-0002-Fix-FOR-PORTION-OF-with-partitions-and-inheritan.patch> Thanks for updating the patch and making the separation. After reading v11, I still have a few comments for 0001. ``` + if (relinfo->ri_forPortionOf) + { + AttrNumber rangeAttno = relinfo->ri_forPortionOf->fp_rangeAttno; + + if (!bms_is_member(rangeAttno - FirstLowInvalidHeapAttributeNumber, + updatedCols)) + { + MemoryContext oldContext; + + oldContext = MemoryContextSwitchTo(estate->es_query_cxt); + + updatedCols = bms_copy(updatedCols); + updatedCols = + bms_add_member(updatedCols, + rangeAttno - FirstLowInvalidHeapAttributeNumber); + + MemoryContextSwitchTo(oldContext); + } } ``` 1. I don’t think we should unconditionally do bms_copy, only if (updatedCols == perminfo->updatedCols), we need to make the copy. 2. I doubt if we need to switch to estate->es_query_cxt. Because ExecGetUpdatedCols() is called by ExecGetAllUpdatedCols(), and its header comment says the function runs in per-tuple memory context: ``` /* * Return columns being updated, including generated columns * * The bitmap is allocated in per-tuple memory context. It's up to the caller to * copy it into a different context with the appropriate lifespan, if needed. */ Bitmapset * ExecGetAllUpdatedCols(ResultRelInfo *relinfo, EState *estate) ``` So I think bms_copy and bms_add_member should be just done in the current memory context. 3. "rangeAttno - FirstLowInvalidHeapAttributeNumber” appears twice, maybe add a local variable to avoid the duplication. Best regards, -- Chao Li (Evan) HighGo Software Co., Ltd. https://www.highgo.com/ -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Paul A Jungwirth <pj@illuminatedcomputing.com> — 2026-05-08T15:25:22Z
On Fri, May 8, 2026 at 12:10 AM Chao Li <li.evan.chao@gmail.com> wrote: > > <v11-0001-Fix-FOR-PORTION-OF-column-dependency-tracking.patch><v11-0002-Fix-FOR-PORTION-OF-with-partitions-and-inheritan.patch> > > Thanks for updating the patch and making the separation. After reading v11, I still have a few comments for 0001. > > ``` > + if (relinfo->ri_forPortionOf) > + { > + AttrNumber rangeAttno = relinfo->ri_forPortionOf->fp_rangeAttno; > + > + if (!bms_is_member(rangeAttno - FirstLowInvalidHeapAttributeNumber, > + updatedCols)) > + { > + MemoryContext oldContext; > + > + oldContext = MemoryContextSwitchTo(estate->es_query_cxt); > + > + updatedCols = bms_copy(updatedCols); > + updatedCols = > + bms_add_member(updatedCols, > + rangeAttno - FirstLowInvalidHeapAttributeNumber); > + > + MemoryContextSwitchTo(oldContext); > + } > } > ``` > > 1. I don’t think we should unconditionally do bms_copy, only if (updatedCols == perminfo->updatedCols), we need to make the copy. You're saying we can skip the copy if execute_attr_map_cols already made a new bms above. That's true. Since we're going to just use the current memory context (see below), that seems safe. > 2. I doubt if we need to switch to estate->es_query_cxt. Because ExecGetUpdatedCols() is called by ExecGetAllUpdatedCols(), and its header comment says the function runs in per-tuple memory context: > ``` > /* > * Return columns being updated, including generated columns > * > * The bitmap is allocated in per-tuple memory context. It's up to the caller to > * copy it into a different context with the appropriate lifespan, if needed. > */ > Bitmapset * > ExecGetAllUpdatedCols(ResultRelInfo *relinfo, EState *estate) > ``` > > So I think bms_copy and bms_add_member should be just done in the current memory context. Okay. I think using the current memory context is more correct anyway. There are other callers, and using the query memory context isn't necessarily what they want. Also the bms (potentially) allocated by execute_attr_map_cols is in the current memory context, so doing something different feels surprising. And it's safer not to change the behavior. Maybe there is a memory leak because of a long-lived context, but then it exists already. I added a comment to ExecGetUpdatedCols to call out that we use the current memory context. > 3. "rangeAttno - FirstLowInvalidHeapAttributeNumber” appears twice, maybe add a local variable to avoid the duplication. Okay. v12 attached. Yours, -- Paul ~{:-) pj@illuminatedcomputing.com -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Chao Li <li.evan.chao@gmail.com> — 2026-05-09T06:38:03Z
> On May 8, 2026, at 23:25, Paul A Jungwirth <pj@illuminatedcomputing.com> wrote: > > On Fri, May 8, 2026 at 12:10 AM Chao Li <li.evan.chao@gmail.com> wrote: >>> <v11-0001-Fix-FOR-PORTION-OF-column-dependency-tracking.patch><v11-0002-Fix-FOR-PORTION-OF-with-partitions-and-inheritan.patch> >> >> Thanks for updating the patch and making the separation. After reading v11, I still have a few comments for 0001. >> >> ``` >> + if (relinfo->ri_forPortionOf) >> + { >> + AttrNumber rangeAttno = relinfo->ri_forPortionOf->fp_rangeAttno; >> + >> + if (!bms_is_member(rangeAttno - FirstLowInvalidHeapAttributeNumber, >> + updatedCols)) >> + { >> + MemoryContext oldContext; >> + >> + oldContext = MemoryContextSwitchTo(estate->es_query_cxt); >> + >> + updatedCols = bms_copy(updatedCols); >> + updatedCols = >> + bms_add_member(updatedCols, >> + rangeAttno - FirstLowInvalidHeapAttributeNumber); >> + >> + MemoryContextSwitchTo(oldContext); >> + } >> } >> ``` >> >> 1. I don’t think we should unconditionally do bms_copy, only if (updatedCols == perminfo->updatedCols), we need to make the copy. > > You're saying we can skip the copy if execute_attr_map_cols already > made a new bms above. That's true. Since we're going to just use the > current memory context (see below), that seems safe. > >> 2. I doubt if we need to switch to estate->es_query_cxt. Because ExecGetUpdatedCols() is called by ExecGetAllUpdatedCols(), and its header comment says the function runs in per-tuple memory context: >> ``` >> /* >> * Return columns being updated, including generated columns >> * >> * The bitmap is allocated in per-tuple memory context. It's up to the caller to >> * copy it into a different context with the appropriate lifespan, if needed. >> */ >> Bitmapset * >> ExecGetAllUpdatedCols(ResultRelInfo *relinfo, EState *estate) >> ``` >> >> So I think bms_copy and bms_add_member should be just done in the current memory context. > > Okay. I think using the current memory context is more correct anyway. > There are other callers, and using the query memory context isn't > necessarily what they want. Also the bms (potentially) allocated by > execute_attr_map_cols is in the current memory context, so doing > something different feels surprising. And it's safer not to change the > behavior. Maybe there is a memory leak because of a long-lived > context, but then it exists already. I added a comment to > ExecGetUpdatedCols to call out that we use the current memory context. > >> 3. "rangeAttno - FirstLowInvalidHeapAttributeNumber” appears twice, maybe add a local variable to avoid the duplication. > > Okay. > > v12 attached. > > Yours, > > -- > Paul ~{:-) > pj@illuminatedcomputing.com > <v12-0001-Fix-FOR-PORTION-OF-column-dependency-tracking.patch><v12-0002-Fix-FOR-PORTION-OF-with-partitions-and-inheritan.patch> Thanks for updating the patch. I have no more comment. V12 LGTM. Best regards, -- Chao Li (Evan) HighGo Software Co., Ltd. https://www.highgo.com/ -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
jian he <jian.universality@gmail.com> — 2026-05-11T12:03:17Z
On Fri, May 8, 2026 at 11:25 PM Paul A Jungwirth <pj@illuminatedcomputing.com> wrote: > > On Fri, May 8, 2026 at 12:10 AM Chao Li <li.evan.chao@gmail.com> wrote: > > > <v11-0001-Fix-FOR-PORTION-OF-column-dependency-tracking.patch><v11-0002-Fix-FOR-PORTION-OF-with-partitions-and-inheritan.patch> > > > > Thanks for updating the patch and making the separation. After reading v11, I still have a few comments for 0001. > > > > ``` > > + if (relinfo->ri_forPortionOf) > > + { > > + AttrNumber rangeAttno = relinfo->ri_forPortionOf->fp_rangeAttno; > > + > > + if (!bms_is_member(rangeAttno - FirstLowInvalidHeapAttributeNumber, > > + updatedCols)) > > + { > > + MemoryContext oldContext; > > + > > + oldContext = MemoryContextSwitchTo(estate->es_query_cxt); > > + > > + updatedCols = bms_copy(updatedCols); > > + updatedCols = > > + bms_add_member(updatedCols, > > + rangeAttno - FirstLowInvalidHeapAttributeNumber); > > + > > + MemoryContextSwitchTo(oldContext); > > + } > > } > > ``` > > > > 1. I don’t think we should unconditionally do bms_copy, only if (updatedCols == perminfo->updatedCols), we need to make the copy. > > You're saying we can skip the copy if execute_attr_map_cols already > made a new bms above. That's true. Since we're going to just use the > current memory context (see below), that seems safe. > > > 2. I doubt if we need to switch to estate->es_query_cxt. Because ExecGetUpdatedCols() is called by ExecGetAllUpdatedCols(), and its header comment says the function runs in per-tuple memory context: > > ``` Switching to estate->es_query_cxt can actually save some cycles. See ExecGetExtraUpdatedCols->ExecInitGenerated /* * Make sure these data structures are built in the per-query memory * context so they'll survive throughout the query. */ oldContext = MemoryContextSwitchTo(estate->es_query_cxt); In ExecGetUpdatedCols, we can change it to the following to save some unnecessary bms_add_member cycle. `````` if (relinfo->ri_forPortionOf) { AttrNumber rangeAttno = relinfo->ri_forPortionOf->fp_rangeAttno; if (!bms_is_member(rangeAttno - FirstLowInvalidHeapAttributeNumber, updatedCols)) { MemoryContext oldContext; if (updatedCols != perminfo->updatedCols) updatedCols = bms_add_member(updatedCols, rangeAttno - FirstLowInvalidHeapAttributeNumber); else { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); updatedCols = bms_add_member(updatedCols, rangeAttno - FirstLowInvalidHeapAttributeNumber); MemoryContextSwitchTo(oldContext); } } } `````` -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Paul A Jungwirth <pj@illuminatedcomputing.com> — 2026-05-12T19:26:01Z
On Mon, May 11, 2026 at 5:03 AM jian he <jian.universality@gmail.com> wrote: > > > > 2. I doubt if we need to switch to estate->es_query_cxt. Because ExecGetUpdatedCols() is called by ExecGetAllUpdatedCols(), and its header comment says the function runs in per-tuple memory context: > > > ``` > > Switching to estate->es_query_cxt can actually save some cycles. > > See ExecGetExtraUpdatedCols->ExecInitGenerated > /* > * Make sure these data structures are built in the per-query memory > * context so they'll survive throughout the query. > */ > oldContext = MemoryContextSwitchTo(estate->es_query_cxt); I agree that seems nice, but it doesn't seem correct if we sometimes change the context and sometimes not (from execute_attr_map_cols). Yours, -- Paul ~{:-) pj@illuminatedcomputing.com -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Nathan Bossart <nathandbossart@gmail.com> — 2026-05-12T20:34:18Z
FOR PORTION OF doesn't seem to work well with virtual generated columns, either. The following example seg-faults on my machine: create table t (a int, b int4range generated always as (int4range(a, a + 1)) virtual); insert into t values (1); delete from t for portion of b from 1 to 2; -- nathan -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Paul A Jungwirth <pj@illuminatedcomputing.com> — 2026-05-13T00:05:03Z
On Tue, May 12, 2026 at 1:34 PM Nathan Bossart <nathandbossart@gmail.com> wrote: > > FOR PORTION OF doesn't seem to work well with virtual generated columns, > either. The following example seg-faults on my machine: > > create table t (a int, b int4range generated always as (int4range(a, a + 1)) virtual); > insert into t values (1); > delete from t for portion of b from 1 to 2; Thanks for catching this! Here is a patch forbidding both STORED and VIRTUAL columns here. There is a follow-up patch (not for v19) to add SQL:2011 PERIODs, which will be based on STORED columns, so we will eventually allow those (if they belong to a PERIOD), but it seems right to forbid them for now. I put the check in the analysis phase to match what we have already, but based on [1] that is apparently premature. I think I'd like to move all those things together in a single commit though. I did experiment with putting just this check in ExecInitModifyTable. But (1) the planner will already reject the UPDATE case with a different error message, and (2) it doesn't really improve anything, since rangeVar gets looked up during analysis anyway (until we address the rest of [1]). [1] https://www.postgresql.org/message-id/626986.1776785090%40sss.pgh.pa.us Yours, -- Paul ~{:-) pj@illuminatedcomputing.com -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Paul A Jungwirth <pj@illuminatedcomputing.com> — 2026-05-13T15:42:53Z
On Tue, May 12, 2026 at 5:05 PM Paul A Jungwirth <pj@illuminatedcomputing.com> wrote: > > On Tue, May 12, 2026 at 1:34 PM Nathan Bossart <nathandbossart@gmail.com> wrote: > > > > FOR PORTION OF doesn't seem to work well with virtual generated columns, > > either. The following example seg-faults on my machine: > > > > create table t (a int, b int4range generated always as (int4range(a, a + 1)) virtual); > > insert into t values (1); > > delete from t for portion of b from 1 to 2; > > Thanks for catching this! > > Here is a patch forbidding both STORED and VIRTUAL columns here. There > is a follow-up patch (not for v19) to add SQL:2011 PERIODs, which will > be based on STORED columns, so we will eventually allow those (if they > belong to a PERIOD), but it seems right to forbid them for now. > > I put the check in the analysis phase to match what we have already, > but based on [1] that is apparently premature. I think I'd like to > move all those things together in a single commit though. > > I did experiment with putting just this check in ExecInitModifyTable. > But (1) the planner will already reject the UPDATE case with a > different error message, and (2) it doesn't really improve anything, > since rangeVar gets looked up during analysis anyway (until we address > the rest of [1]). > > [1] https://www.postgresql.org/message-id/626986.1776785090%40sss.pgh.pa.us I started a new thread for this issue and made a CF entry. Please see: - https://www.postgresql.org/message-id/CA%2BrenyVRPyP5TNgEBe%3DhRT1ZR3%3DzWCZLXkAwp5xbWeS_TaMxOA%40mail.gmail.com - https://commitfest.postgresql.org/patch/6764/ Yours, -- Paul ~{:-) pj@illuminatedcomputing.com -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Paul A Jungwirth <pj@illuminatedcomputing.com> — 2026-05-25T22:23:20Z
On Fri, May 8, 2026 at 8:25 AM Paul A Jungwirth <pj@illuminatedcomputing.com> wrote: > > On Fri, May 8, 2026 at 12:10 AM Chao Li <li.evan.chao@gmail.com> wrote: > > > <v11-0001-Fix-FOR-PORTION-OF-column-dependency-tracking.patch><v11-0002-Fix-FOR-PORTION-OF-with-partitions-and-inheritan.patch> > > > > Thanks for updating the patch and making the separation. After reading v11, I still have a few comments for 0001. > . . . > > v12 attached. After discussing this as PGConf.dev, Peter and I agreed that we *should* be checking for UPDATE permission on the application-time column. So we need to add the attno to updatedCols on the RTEPermissionInfo. That is great, because it fixes GENERATED column dependency tracking and also UPDATE OF triggers, without the complexity of changing ExecGetUpdatedCols. Note we still do not require INSERT permission for the temporal leftovers, which is what the SQL Standard says and also makes sense semantically, since those leftovers represent already-existing history. No RLS changes are needed because RLS policies aren't checked based on *column* changes. We still need a fix for non-partitioning inheritance, but the patch becomes a lot simpler. Here is a patch adding the application-time column to updatedCols, along with the tests we've written for GENERATED columns and UPDATE OF triggers. I'll submit a patch for inheritance on that thread. -- Paul ~{:-) pj@illuminatedcomputing.com -
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
jian he <jian.universality@gmail.com> — 2026-05-28T03:23:45Z
On Tue, May 26, 2026 at 6:23 AM Paul A Jungwirth <pj@illuminatedcomputing.com> wrote: > > Here is a patch adding the application-time column to updatedCols, > along with the tests we've written for GENERATED columns and UPDATE OF > triggers. > V13 looks good to me. one minor issue: V13 uses the wording "generated stored column(s)", but all other places use "stored generated column(s)".
-
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Peter Eisentraut <peter@eisentraut.org> — 2026-06-04T09:58:48Z
On 26.05.26 00:23, Paul A Jungwirth wrote: > On Fri, May 8, 2026 at 8:25 AM Paul A Jungwirth > <pj@illuminatedcomputing.com> wrote: >> >> On Fri, May 8, 2026 at 12:10 AM Chao Li <li.evan.chao@gmail.com> wrote: >>>> <v11-0001-Fix-FOR-PORTION-OF-column-dependency-tracking.patch><v11-0002-Fix-FOR-PORTION-OF-with-partitions-and-inheritan.patch> >>> >>> Thanks for updating the patch and making the separation. After reading v11, I still have a few comments for 0001. >> . . . >> >> v12 attached. > > After discussing this as PGConf.dev, Peter and I agreed that we > *should* be checking for UPDATE permission on the application-time > column. So we need to add the attno to updatedCols on the > RTEPermissionInfo. That is great, because it fixes GENERATED column > dependency tracking and also UPDATE OF triggers, without the > complexity of changing ExecGetUpdatedCols. > > Note we still do not require INSERT permission for the temporal > leftovers, which is what the SQL Standard says and also makes sense > semantically, since those leftovers represent already-existing > history. > > No RLS changes are needed because RLS policies aren't checked based on > *column* changes. > > We still need a fix for non-partitioning inheritance, but the patch > becomes a lot simpler. > > Here is a patch adding the application-time column to updatedCols, > along with the tests we've written for GENERATED columns and UPDATE OF > triggers. committed
-
Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column
Peter Eisentraut <peter@eisentraut.org> — 2026-06-04T10:04:25Z
On 28.05.26 05:23, jian he wrote: > On Tue, May 26, 2026 at 6:23 AM Paul A Jungwirth > <pj@illuminatedcomputing.com> wrote: >> >> Here is a patch adding the application-time column to updatedCols, >> along with the tests we've written for GENERATED columns and UPDATE OF >> triggers. >> > > V13 looks good to me. > > one minor issue: > V13 uses the wording "generated stored column(s)", but all other > places use "stored generated column(s)". I fixed that. In one case the test actually included both kinds of generated columns, so I just removed the "stored".