public inbox for [email protected]
help / color / mirror / Atom feedFrom: Paul A Jungwirth <[email protected]>
To: Tom Lane <[email protected]>
Cc: Peter Eisentraut <[email protected]>
Cc: Aleksander Alekseev <[email protected]>
Cc: PostgreSQL Development <[email protected]>
Subject: Re: [PATCH] Fix null pointer dereference in PG19
Date: Wed, 8 Jul 2026 09:48:08 -0700
Message-ID: <CA+renyV1WUtDSOHpNVv3fhD6JqhzZY_mV-0Nks23MczXLxpE8A@mail.gmail.com> (raw)
In-Reply-To: <[email protected]>
References: <CAJ7c6TME+ix6VRf-2TPnVTsj8qn_hy6sYAOmMhZEivwsu2wS6g@mail.gmail.com>
<[email protected]>
<CACJufxFgKVL=NnPocAwhRFqpLHckN3N1Ndf7FjGNTdtsAA26mg@mail.gmail.com>
<CA+renyW52FJ=+pnh+N_xqjUev_MNUCk_mgD2UC7UT0jyAHCE8Q@mail.gmail.com>
<[email protected]>
<CAJ7c6TO+kTwk5MBayLARxsPk-YCsDt2QH9P_PeTYoc3zPYCS3Q@mail.gmail.com>
<CA+renyU6UZnpkgAuog0BVhqyD=G+_UVLzN2Ccpt2sV-qkF-_VQ@mail.gmail.com>
<CAJ7c6TN7dekEHU8Vu4jV5+Zq7h2cVZQgwkYFCrU0jrjT26-ANw@mail.gmail.com>
<CA+renyXs5mkPb-Vg=uF2Hj0jP2ufutc1_Eht=eb3+b_WpjiqLg@mail.gmail.com>
<[email protected]>
<CA+renyUkKaWtWZX2UcojKVjM=f1F5kgdvR8Pr5QqyJqqe_dw-g@mail.gmail.com>
<[email protected]>
<CA+renyXPEJYOVov3XtzTp3NusGkObc9UeW-Nm78K8_nsWSnnfg@mail.gmail.com>
<[email protected]>
On Tue, Jul 7, 2026 at 7:07 PM Tom Lane <[email protected]> wrote:
>
> Paul A Jungwirth <[email protected]> writes:
> > On Tue, Apr 21, 2026 at 8:24 AM Tom Lane <[email protected]> wrote:
> >> Checking this at parse time is completely the wrong thing.
> >> The view could have gained (or lost) triggers by the time
> >> it's executed.
>
> > But INSTEAD OF triggers are selected in the rewriter, which uses the
> > same relcache snapshot as parse analysis. And a concurrent change
> > can't sneak in different triggers, because that causes a relcache
> > invalidation, so we redo the parse & rewrite phases.
>
> You have forgotten about views and rewrite rules. Those go to disk in
> post-parser form, and will be rewritten only at execution sometime
> later, *without* a re-parse.
Ah yes, thank you! I should have been able to work that out.
Here is another patch series. No code changes, but I inserted a new
patch with tests showing that parse-time checking crashes, but
rewrite-time and exec-time checking catches the forbidden statement.
So the series is:
v1: parse-time check, tests pass
v2: add tests that crash the server
v3: rewrite-time check: tests pass
v4: exec-time check: tests pass
These are against REL_19_STABLE, not master (but I don't think it
makes a difference).
Yours,
--
Paul ~{:-)
[email protected]
Attachments:
[text/x-patch] v8-0003-Check-FOR-PORTION-OF-against-INSTEAD-OF-triggers-.patch (4.3K, ../CA+renyV1WUtDSOHpNVv3fhD6JqhzZY_mV-0Nks23MczXLxpE8A@mail.gmail.com/2-v8-0003-Check-FOR-PORTION-OF-against-INSTEAD-OF-triggers-.patch)
download | inline diff:
From d0661fdf84e6d16af5cb59b18360cc08b51f8ab0 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <[email protected]>
Date: Tue, 7 Jul 2026 17:21:34 -0700
Subject: [PATCH v8 3/4] Check FOR PORTION OF against INSTEAD OF triggers
during rewriting
Move the INSTEAD OF trigger check to the rewriter. This loses the error
position but gives the check better locality with where we look up the
triggers.
Discussion: https://www.postgresql.org/message-id/CAJ7c6TME%2Bix6VRf-2TPnVTsj8qn_hy6sYAOmMhZEivwsu2wS6g%40mail.gmail.com
---
src/backend/parser/analyze.c | 11 -----------
src/backend/rewrite/rewriteHandler.c | 8 ++++++++
src/test/regress/expected/updatable_views.out | 8 --------
3 files changed, 8 insertions(+), 19 deletions(-)
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 1e6fbc95964..2932d17a107 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -1344,17 +1344,6 @@ transformForPortionOfClause(ParseState *pstate,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("WHERE CURRENT OF with FOR PORTION OF is not implemented"));
- /* We don't support FOR PORTION OF on views with INSTEAD OF triggers. */
- if (targetrel->rd_rel->relkind == RELKIND_VIEW &&
- targetrel->rd_rel->relhastriggers &&
- targetrel->trigdesc != NULL &&
- (isUpdate ? targetrel->trigdesc->trig_update_instead_row
- : targetrel->trigdesc->trig_delete_instead_row))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("views with INSTEAD OF triggers do not support FOR PORTION OF"),
- parser_errposition(pstate, forPortionOf->location)));
-
result = makeNode(ForPortionOfExpr);
/* Look up the FOR PORTION OF name requested. */
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index e7ae9cce65f..38f54b57eec 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -4171,6 +4171,14 @@ RewriteQuery(Query *parsetree, List *rewrite_events, int orig_rt_length,
*/
rt_entry_relation = relation_open(rt_entry->relid, NoLock);
+ /* We don't support FOR PORTION OF on views with INSTEAD OF triggers. */
+ if (parsetree->forPortionOf &&
+ rt_entry_relation->rd_rel->relkind == RELKIND_VIEW &&
+ view_has_instead_trigger(rt_entry_relation, event, NIL))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("views with INSTEAD OF triggers do not support FOR PORTION OF")));
+
/*
* Rewrite the targetlist as needed for the command type.
*/
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index e6232a5f8c0..9c6bb2219f9 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -4179,28 +4179,20 @@ update uv_fpo_instead_view
for portion of valid_at from '2015-01-01' to '2020-01-01'
set b = 99 where id = '[1,1]'; -- error
ERROR: views with INSTEAD OF triggers do not support FOR PORTION OF
-LINE 2: for portion of valid_at from '2015-01-01' to '2020-01-01'
- ^
delete from uv_fpo_instead_view
for portion of valid_at from '2017-01-01' to '2022-01-01'
where id = '[1,1]'; -- error
ERROR: views with INSTEAD OF triggers do not support FOR PORTION OF
-LINE 2: for portion of valid_at from '2017-01-01' to '2022-01-01'
- ^
-- The check does not depend on which rows match, so it errors even when
-- no rows do.
update uv_fpo_instead_view
for portion of valid_at from '2015-01-01' to '2020-01-01'
set b = 99 where id = '[9,9]'; -- error, even with no matching rows
ERROR: views with INSTEAD OF triggers do not support FOR PORTION OF
-LINE 2: for portion of valid_at from '2015-01-01' to '2020-01-01'
- ^
delete from uv_fpo_instead_view
for portion of valid_at from '2017-01-01' to '2022-01-01'
where id = '[9,9]'; -- error, even with no matching rows
ERROR: views with INSTEAD OF triggers do not support FOR PORTION OF
-LINE 2: for portion of valid_at from '2017-01-01' to '2022-01-01'
- ^
drop view uv_fpo_instead_view;
drop function uv_fpo_instead_trig();
-- Forbid INSTEAD OF triggers with FOR PORTION OF even if the FOR PORTION OF
--
2.47.3
[text/x-patch] v8-0001-Forbid-FOR-PORTION-OF-on-views-with-INSTEAD-OF-tr.patch (5.6K, ../CA+renyV1WUtDSOHpNVv3fhD6JqhzZY_mV-0Nks23MczXLxpE8A@mail.gmail.com/3-v8-0001-Forbid-FOR-PORTION-OF-on-views-with-INSTEAD-OF-tr.patch)
download | inline diff:
From b0f83ff70a1e0ee2c544c3cd31a2c40adbc9e058 Mon Sep 17 00:00:00 2001
From: Aleksander Alekseev <[email protected]>
Date: Tue, 7 Jul 2026 17:20:05 -0700
Subject: [PATCH v8 1/4] Forbid FOR PORTION OF on views with INSTEAD OF
triggers
Previously an attempt to use these features together caused a crash.
Oversight of commit 8e72d914c528.
Author: Aleksander Alekseev <[email protected]>
Discussion: https://www.postgresql.org/message-id/CAJ7c6TME%2Bix6VRf-2TPnVTsj8qn_hy6sYAOmMhZEivwsu2wS6g%40mail.gmail.com
---
src/backend/parser/analyze.c | 11 ++++++
src/test/regress/expected/updatable_views.out | 38 +++++++++++++++++++
src/test/regress/sql/updatable_views.sql | 34 +++++++++++++++++
3 files changed, 83 insertions(+)
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 2932d17a107..1e6fbc95964 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -1344,6 +1344,17 @@ transformForPortionOfClause(ParseState *pstate,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("WHERE CURRENT OF with FOR PORTION OF is not implemented"));
+ /* We don't support FOR PORTION OF on views with INSTEAD OF triggers. */
+ if (targetrel->rd_rel->relkind == RELKIND_VIEW &&
+ targetrel->rd_rel->relhastriggers &&
+ targetrel->trigdesc != NULL &&
+ (isUpdate ? targetrel->trigdesc->trig_update_instead_row
+ : targetrel->trigdesc->trig_delete_instead_row))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("views with INSTEAD OF triggers do not support FOR PORTION OF"),
+ parser_errposition(pstate, forPortionOf->location)));
+
result = makeNode(ForPortionOfExpr);
/* Look up the FOR PORTION OF name requested. */
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 7b00c742776..acab165ae4c 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -4165,3 +4165,41 @@ select * from base_tab order by a;
drop view base_tab_view;
drop table base_tab;
+-- FOR PORTION OF is not supported on views with INSTEAD OF triggers
+create view uv_fpo_instead_view as select id, valid_at, b from uv_fpo_tab;
+create function uv_fpo_instead_trig() returns trigger language plpgsql as
+$$ begin return null; end $$;
+create trigger uv_fpo_instead_upd_trig
+ instead of update on uv_fpo_instead_view
+ for each row execute function uv_fpo_instead_trig();
+create trigger uv_fpo_instead_del_trig
+ instead of delete on uv_fpo_instead_view
+ for each row execute function uv_fpo_instead_trig();
+update uv_fpo_instead_view
+ for portion of valid_at from '2015-01-01' to '2020-01-01'
+ set b = 99 where id = '[1,1]'; -- error
+ERROR: views with INSTEAD OF triggers do not support FOR PORTION OF
+LINE 2: for portion of valid_at from '2015-01-01' to '2020-01-01'
+ ^
+delete from uv_fpo_instead_view
+ for portion of valid_at from '2017-01-01' to '2022-01-01'
+ where id = '[1,1]'; -- error
+ERROR: views with INSTEAD OF triggers do not support FOR PORTION OF
+LINE 2: for portion of valid_at from '2017-01-01' to '2022-01-01'
+ ^
+-- The check does not depend on which rows match, so it errors even when
+-- no rows do.
+update uv_fpo_instead_view
+ for portion of valid_at from '2015-01-01' to '2020-01-01'
+ set b = 99 where id = '[9,9]'; -- error, even with no matching rows
+ERROR: views with INSTEAD OF triggers do not support FOR PORTION OF
+LINE 2: for portion of valid_at from '2015-01-01' to '2020-01-01'
+ ^
+delete from uv_fpo_instead_view
+ for portion of valid_at from '2017-01-01' to '2022-01-01'
+ where id = '[9,9]'; -- error, even with no matching rows
+ERROR: views with INSTEAD OF triggers do not support FOR PORTION OF
+LINE 2: for portion of valid_at from '2017-01-01' to '2022-01-01'
+ ^
+drop view uv_fpo_instead_view;
+drop function uv_fpo_instead_trig();
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index 4a60126ec90..f4a7e0327b0 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -2148,3 +2148,37 @@ values (1, 2, default, 5, 4, default, 3), (10, 11, 'C value', 14, 13, 100, 12);
select * from base_tab order by a;
drop view base_tab_view;
drop table base_tab;
+
+-- FOR PORTION OF is not supported on views with INSTEAD OF triggers
+create view uv_fpo_instead_view as select id, valid_at, b from uv_fpo_tab;
+
+create function uv_fpo_instead_trig() returns trigger language plpgsql as
+$$ begin return null; end $$;
+
+create trigger uv_fpo_instead_upd_trig
+ instead of update on uv_fpo_instead_view
+ for each row execute function uv_fpo_instead_trig();
+create trigger uv_fpo_instead_del_trig
+ instead of delete on uv_fpo_instead_view
+ for each row execute function uv_fpo_instead_trig();
+
+update uv_fpo_instead_view
+ for portion of valid_at from '2015-01-01' to '2020-01-01'
+ set b = 99 where id = '[1,1]'; -- error
+
+delete from uv_fpo_instead_view
+ for portion of valid_at from '2017-01-01' to '2022-01-01'
+ where id = '[1,1]'; -- error
+
+-- The check does not depend on which rows match, so it errors even when
+-- no rows do.
+update uv_fpo_instead_view
+ for portion of valid_at from '2015-01-01' to '2020-01-01'
+ set b = 99 where id = '[9,9]'; -- error, even with no matching rows
+
+delete from uv_fpo_instead_view
+ for portion of valid_at from '2017-01-01' to '2022-01-01'
+ where id = '[9,9]'; -- error, even with no matching rows
+
+drop view uv_fpo_instead_view;
+drop function uv_fpo_instead_trig();
--
2.47.3
[text/x-patch] v8-0004-Check-FOR-PORTION-OF-against-INSTEAD-OF-triggers-.patch (5.3K, ../CA+renyV1WUtDSOHpNVv3fhD6JqhzZY_mV-0Nks23MczXLxpE8A@mail.gmail.com/4-v8-0004-Check-FOR-PORTION-OF-against-INSTEAD-OF-triggers-.patch)
download | inline diff:
From 52aa3063d498ac7380068c1537985e9cfcff0be3 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <[email protected]>
Date: Tue, 7 Jul 2026 17:23:01 -0700
Subject: [PATCH v8 4/4] Check FOR PORTION OF against INSTEAD OF triggers at
execution time
Doing the test at execution time means that zero-row updates don't fail.
Discussion: https://www.postgresql.org/message-id/CAJ7c6TME%2Bix6VRf-2TPnVTsj8qn_hy6sYAOmMhZEivwsu2wS6g%40mail.gmail.com
---
src/backend/executor/nodeModifyTable.c | 13 +++++++++++++
src/backend/rewrite/rewriteHandler.c | 8 --------
src/test/regress/expected/updatable_views.out | 10 ++++------
src/test/regress/sql/updatable_views.sql | 8 ++++----
4 files changed, 21 insertions(+), 18 deletions(-)
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index c333d7139fa..440f535dd04 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1879,6 +1879,13 @@ ExecDelete(ModifyTableContext *context,
bool dodelete;
Assert(oldtuple != NULL);
+
+ /* We don't support FOR PORTION OF on views with INSTEAD OF triggers. */
+ if (((ModifyTable *) context->mtstate->ps.plan)->forPortionOf)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("views with INSTEAD OF triggers do not support FOR PORTION OF")));
+
dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple);
if (!dodelete) /* "do nothing" */
@@ -2772,6 +2779,12 @@ ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_update_instead_row)
{
+ /* We don't support FOR PORTION OF on views with INSTEAD OF triggers. */
+ if (((ModifyTable *) context->mtstate->ps.plan)->forPortionOf)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("views with INSTEAD OF triggers do not support FOR PORTION OF")));
+
if (!ExecIRUpdateTriggers(estate, resultRelInfo,
oldtuple, slot))
return NULL; /* "do nothing" */
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 38f54b57eec..e7ae9cce65f 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -4171,14 +4171,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events, int orig_rt_length,
*/
rt_entry_relation = relation_open(rt_entry->relid, NoLock);
- /* We don't support FOR PORTION OF on views with INSTEAD OF triggers. */
- if (parsetree->forPortionOf &&
- rt_entry_relation->rd_rel->relkind == RELKIND_VIEW &&
- view_has_instead_trigger(rt_entry_relation, event, NIL))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("views with INSTEAD OF triggers do not support FOR PORTION OF")));
-
/*
* Rewrite the targetlist as needed for the command type.
*/
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 9c6bb2219f9..68927e78ce3 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -4183,16 +4183,14 @@ delete from uv_fpo_instead_view
for portion of valid_at from '2017-01-01' to '2022-01-01'
where id = '[1,1]'; -- error
ERROR: views with INSTEAD OF triggers do not support FOR PORTION OF
--- The check does not depend on which rows match, so it errors even when
--- no rows do.
+-- Here the check runs per row in the executor, so a statement that matches
+-- no rows never reaches it and is allowed.
update uv_fpo_instead_view
for portion of valid_at from '2015-01-01' to '2020-01-01'
- set b = 99 where id = '[9,9]'; -- error, even with no matching rows
-ERROR: views with INSTEAD OF triggers do not support FOR PORTION OF
+ set b = 99 where id = '[9,9]'; -- ok, no rows affected
delete from uv_fpo_instead_view
for portion of valid_at from '2017-01-01' to '2022-01-01'
- where id = '[9,9]'; -- error, even with no matching rows
-ERROR: views with INSTEAD OF triggers do not support FOR PORTION OF
+ where id = '[9,9]'; -- ok, no rows affected
drop view uv_fpo_instead_view;
drop function uv_fpo_instead_trig();
-- Forbid INSTEAD OF triggers with FOR PORTION OF even if the FOR PORTION OF
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index 2ef9aa32f36..b0b607ec335 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -2170,15 +2170,15 @@ delete from uv_fpo_instead_view
for portion of valid_at from '2017-01-01' to '2022-01-01'
where id = '[1,1]'; -- error
--- The check does not depend on which rows match, so it errors even when
--- no rows do.
+-- Here the check runs per row in the executor, so a statement that matches
+-- no rows never reaches it and is allowed.
update uv_fpo_instead_view
for portion of valid_at from '2015-01-01' to '2020-01-01'
- set b = 99 where id = '[9,9]'; -- error, even with no matching rows
+ set b = 99 where id = '[9,9]'; -- ok, no rows affected
delete from uv_fpo_instead_view
for portion of valid_at from '2017-01-01' to '2022-01-01'
- where id = '[9,9]'; -- error, even with no matching rows
+ where id = '[9,9]'; -- ok, no rows affected
drop view uv_fpo_instead_view;
drop function uv_fpo_instead_trig();
--
2.47.3
[text/x-patch] v8-0002-Add-tests-for-FOR-PORTION-OF-reaching-INSTEAD-OF-.patch (5.5K, ../CA+renyV1WUtDSOHpNVv3fhD6JqhzZY_mV-0Nks23MczXLxpE8A@mail.gmail.com/5-v8-0002-Add-tests-for-FOR-PORTION-OF-reaching-INSTEAD-OF-.patch)
download | inline diff:
From 5d41626a1c2fe5f7cd99e2b973552e9aae109549 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <[email protected]>
Date: Wed, 8 Jul 2026 08:43:10 -0700
Subject: [PATCH v8 2/4] Add tests for FOR PORTION OF reaching INSTEAD OF
triggers via stored queries
These tests crash if we forbid FOR PORTION OF with INSTEAD OF triggers
at parse time. So they show why checking at rewrite or later is
necessary.
Anything that stores the parsed query risks a TOCTOU vulnerability. The
tests show two ways: a rewrite RULE and a BEGIN ATOMIC function.
Discussion: https://www.postgresql.org/message-id/1894792.1783476457%40sss.pgh.pa.us
---
src/test/regress/expected/updatable_views.out | 41 +++++++++++++++++++
src/test/regress/sql/updatable_views.sql | 39 ++++++++++++++++++
2 files changed, 80 insertions(+)
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index acab165ae4c..e6232a5f8c0 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -4203,3 +4203,44 @@ LINE 2: for portion of valid_at from '2017-01-01' to '2022-01-01'
^
drop view uv_fpo_instead_view;
drop function uv_fpo_instead_trig();
+-- Forbid INSTEAD OF triggers with FOR PORTION OF even if the FOR PORTION OF
+-- statement is parsed before the trigger exists.
+-- This can happen in at least a couple ways: a rewrite rule or a BEGIN ATOMIC function.
+create function uv_fpo_instead_trig() returns trigger language plpgsql as
+$$ begin return new; end $$;
+-- via a rewrite rule
+create table uv_fpo_rule_tab (id int4range, valid_at tsrange, b float);
+insert into uv_fpo_rule_tab values ('[1,1]', '[2000-01-01, 2030-01-01)', 0);
+create view uv_fpo_rule_view as select id, valid_at, b from uv_fpo_rule_tab;
+create rule uv_fpo_rule as on insert to uv_fpo_rule_tab do also
+ update uv_fpo_rule_view
+ for portion of valid_at from '2010-01-01' to '2020-01-01'
+ set b = 99 where id = '[1,1]';
+create trigger uv_fpo_rule_instead
+ instead of update on uv_fpo_rule_view
+ for each row execute function uv_fpo_instead_trig();
+-- Would crash if we checked at parse-time:
+insert into uv_fpo_rule_tab values ('[2,2]', '[2000-01-01,2030-01-01)', 0);
+ERROR: views with INSTEAD OF triggers do not support FOR PORTION OF
+drop table uv_fpo_rule_tab cascade;
+NOTICE: drop cascades to view uv_fpo_rule_view
+-- via a BEGIN ATOMIC function body
+create table uv_fpo_atomic_tab (id int4range, valid_at tsrange, b float);
+insert into uv_fpo_atomic_tab values ('[1,1]', '[2000-01-01, 2030-01-01)', 0);
+create view uv_fpo_atomic_view as select id, valid_at, b from uv_fpo_atomic_tab;
+create function uv_fpo_atomic_fn() returns void language sql begin atomic
+ update uv_fpo_atomic_view
+ for portion of valid_at from '2010-01-01' to '2020-01-01'
+ set b = 99 where id = '[1,1]';
+end;
+create trigger uv_fpo_atomic_instead
+ instead of update on uv_fpo_atomic_view
+ for each row execute function uv_fpo_instead_trig();
+-- Would crash if we checked at parse-time:
+select uv_fpo_atomic_fn();
+ERROR: views with INSTEAD OF triggers do not support FOR PORTION OF
+CONTEXT: SQL function "uv_fpo_atomic_fn" statement 1
+drop function uv_fpo_atomic_fn();
+drop table uv_fpo_atomic_tab cascade;
+NOTICE: drop cascades to view uv_fpo_atomic_view
+drop function uv_fpo_instead_trig();
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index f4a7e0327b0..2ef9aa32f36 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -2182,3 +2182,42 @@ delete from uv_fpo_instead_view
drop view uv_fpo_instead_view;
drop function uv_fpo_instead_trig();
+
+-- Forbid INSTEAD OF triggers with FOR PORTION OF even if the FOR PORTION OF
+-- statement is parsed before the trigger exists.
+-- This can happen in at least a couple ways: a rewrite rule or a BEGIN ATOMIC function.
+create function uv_fpo_instead_trig() returns trigger language plpgsql as
+$$ begin return new; end $$;
+
+-- via a rewrite rule
+create table uv_fpo_rule_tab (id int4range, valid_at tsrange, b float);
+insert into uv_fpo_rule_tab values ('[1,1]', '[2000-01-01, 2030-01-01)', 0);
+create view uv_fpo_rule_view as select id, valid_at, b from uv_fpo_rule_tab;
+create rule uv_fpo_rule as on insert to uv_fpo_rule_tab do also
+ update uv_fpo_rule_view
+ for portion of valid_at from '2010-01-01' to '2020-01-01'
+ set b = 99 where id = '[1,1]';
+create trigger uv_fpo_rule_instead
+ instead of update on uv_fpo_rule_view
+ for each row execute function uv_fpo_instead_trig();
+-- Would crash if we checked at parse-time:
+insert into uv_fpo_rule_tab values ('[2,2]', '[2000-01-01,2030-01-01)', 0);
+drop table uv_fpo_rule_tab cascade;
+
+-- via a BEGIN ATOMIC function body
+create table uv_fpo_atomic_tab (id int4range, valid_at tsrange, b float);
+insert into uv_fpo_atomic_tab values ('[1,1]', '[2000-01-01, 2030-01-01)', 0);
+create view uv_fpo_atomic_view as select id, valid_at, b from uv_fpo_atomic_tab;
+create function uv_fpo_atomic_fn() returns void language sql begin atomic
+ update uv_fpo_atomic_view
+ for portion of valid_at from '2010-01-01' to '2020-01-01'
+ set b = 99 where id = '[1,1]';
+end;
+create trigger uv_fpo_atomic_instead
+ instead of update on uv_fpo_atomic_view
+ for each row execute function uv_fpo_instead_trig();
+-- Would crash if we checked at parse-time:
+select uv_fpo_atomic_fn();
+drop function uv_fpo_atomic_fn();
+drop table uv_fpo_atomic_tab cascade;
+drop function uv_fpo_instead_trig();
--
2.47.3
view thread (13+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected]
Subject: Re: [PATCH] Fix null pointer dereference in PG19
In-Reply-To: <CA+renyV1WUtDSOHpNVv3fhD6JqhzZY_mV-0Nks23MczXLxpE8A@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox