Thread: BUG #18147: ERROR: invalid perminfoindex 0 in RTE with relid xxxxx
The following bug has been logged on the website: Bug reference: 18147 Logged by: Hans Buschmann Email address: buschmann@nidsa.net PostgreSQL version: 16.0 Operating system: Fedora 38 x86-64 64bit, also on Win64 Description: We have recently moved our production cluster from pg15.4 to pg16.0 In a long lasting correct case (since about pg 9.6) an update statement now fails with $subject. I have simplified the case and the error remains (here shown on windows) ------------------ the query: -- explain analyze -- explain analyze verbose -- explain -- select * from ( -- select count(*) from ( -- select length(sel) from ( with qp_netto as ( select 77812::int as id_of , 1.000000::numeric(8,6) as fac_to_us , 6.9318647425014148::numeric(8,3) as prfac_netto_1, 0.0::numeric(8,3) as prfac_netto_2, 1.000000::numeric(8,6) as our_to_us , 6.88795000000000000000::numeric(8,3) as prour_netto_1, 0.0::numeric(8,3) as prour_netto_2 ) -- select * from qp_netto; update or_followup set of_pr1_fac_netto=coalesce(prfac_netto_1,0.0) ,of_pr1_fac_netusd=coalesce(prfac_netto_1*fac_to_us,0.0) ,of_pr2_fac_netto=coalesce(prfac_netto_2,0.0) ,of_pr2_fac_netusd=coalesce(prfac_netto_2*fac_to_us,0.0) ,of_pr1_our_netto=coalesce(prour_netto_1,0.0) ,of_pr1_our_netusd=coalesce(prour_netto_1*our_to_us,0.0) ,of_pr2_our_netto=coalesce(prour_netto_2,0.0) ,of_pr2_our_netusd=coalesce(prour_netto_2*our_to_us,0.0) from qp_netto where or_followup.id_of=qp_netto.id_of and or_followup.of_season=35 ; ------------------------ result: xxxdb=# select version (); -[ RECORD 1 ]------------------------------------------------------- version | PostgreSQL 16.0, compiled by Visual C++ build 1935, 64-bit xxxdb=# explain analyze -- explain analyze verbose -- explain -- select * from ( -- select count(*) from ( -- select length(sel) from ( xxxdb-# with qp_netto as ( xxxdb(# select xxxdb(# 77812::int as id_of , xxxdb(# 1.000000::numeric(8,6) as fac_to_us , xxxdb(# 6.9318647425014148::numeric(8,3) as prfac_netto_1, xxxdb(# 0.0::numeric(8,3) as prfac_netto_2, xxxdb(# 1.000000::numeric(8,6) as our_to_us , xxxdb(# 6.88795000000000000000::numeric(8,3) as prour_netto_1, xxxdb(# 0.0::numeric(8,3) as prour_netto_2 xxxdb(# ) xxxdb-# -- select * from qp_netto; xxxdb-# update or_followup set xxxdb-# of_pr1_fac_netto=coalesce(prfac_netto_1,0.0) xxxdb-# ,of_pr1_fac_netusd=coalesce(prfac_netto_1*fac_to_us,0.0) xxxdb-# ,of_pr2_fac_netto=coalesce(prfac_netto_2,0.0) xxxdb-# ,of_pr2_fac_netusd=coalesce(prfac_netto_2*fac_to_us,0.0) xxxdb-# ,of_pr1_our_netto=coalesce(prour_netto_1,0.0) xxxdb-# ,of_pr1_our_netusd=coalesce(prour_netto_1*our_to_us,0.0) xxxdb-# ,of_pr2_our_netto=coalesce(prour_netto_2,0.0) xxxdb-# ,of_pr2_our_netusd=coalesce(prour_netto_2*our_to_us,0.0) xxxdb-# from qp_netto xxxdb-# where xxxdb-# or_followup.id_of=qp_netto.id_of xxxdb-# and or_followup.of_season=35 xxxdb-# ; FEHLER: invalid perminfoindex 0 in RTE with relid 17034 I have found a relating discussion under https://www.postgresql.org/message-id/flat/CANQ0oxfxBKKTReQgSh_KbL99DqdjfBZTastC0XT2ZZMBkAhTQw%40mail.gmail.com but could not resolve the problem. The query is quite simplified.. Perhaps it is good to now, that the table or_followup has an inherited table or_followup_archiv (= relid 17034) which is chosen by of_season and has not the same index definitions as or_followup. Thank you for looking! Hans Buschmann
Re: BUG #18147: ERROR: invalid perminfoindex 0 in RTE with relid xxxxx
From
"David G. Johnston"
Date:
On Thu, Oct 5, 2023 at 8:05 AM PG Bug reporting form <noreply@postgresql.org> wrote:
The following bug has been logged on the website:
Bug reference: 18147
Logged by: Hans Buschmann
Email address: buschmann@nidsa.net
PostgreSQL version: 16.0
Operating system: Fedora 38 x86-64 64bit, also on Win64
Description:
The query is quite simplified.. Perhaps it is good to now, that the table
or_followup has an inherited table or_followup_archiv (= relid 17034) which
is chosen by of_season and has not the same index definitions as
or_followup.
The simplified query is preferred so long as it produces the error - but you really should be providing the create table commands to make the entire script self-contained.
David J.
"David G. Johnston" <david.g.johnston@gmail.com> writes: > The simplified query is preferred so long as it produces the error - but > you really should be providing the create table commands to make the entire > script self-contained. Yeah, it's impossible to reproduce or investigate this without table definitions ... and I for one don't feel like guessing at the table details. regards, tom lane
Hello,
For your reference I include a simple dump of a test case database, which executes the queries but does NOT reproduce the error.
This case seems much more complicated then I thought on first view.
The problem arose on the production database after it has been dumped/restore from pg15.4 to pg16.0 and was observed on failing queries from the application.
Many tables in production have an inherited table called xxxtable_archiv, which contains elder data and are not often updated by the application. So the error is seldom.
The normal access is only through the main table like:
explain analyze -- explain analyze verbose -- explain -- select * from ( -- select count(*) from ( -- select length(sel) from (
with qp_netto as (
select
77812::int as id_of ,
1.000000::numeric(8,6) as fac_to_us ,
6.9318647425014148::numeric(8,3) as prfac_netto_1,
0.0::numeric(8,3) as prfac_netto_2,
1.000000::numeric(8,6) as our_to_us ,
6.88795000000000000000::numeric(8,3) as prour_netto_1,
0.0::numeric(8,3) as prour_netto_2
)
-- select * from qp_netto;
update or_followup set
of_pr1_fac_netto=coalesce(prfac_netto_1,0.0)
,of_pr1_fac_netusd=coalesce(prfac_netto_1*fac_to_us,0.0)
,of_pr2_fac_netto=coalesce(prfac_netto_2,0.0)
,of_pr2_fac_netusd=coalesce(prfac_netto_2*fac_to_us,0.0)
,of_pr1_our_netto=coalesce(prour_netto_1,0.0)
,of_pr1_our_netusd=coalesce(prour_netto_1*our_to_us,0.0)
,of_pr2_our_netto=coalesce(prour_netto_2,0.0)
,of_pr2_our_netusd=coalesce(prour_netto_2*our_to_us,0.0)
from qp_netto
where
or_followup.id_of=qp_netto.id_of
and or_followup.of_season=35
;
xxxdb-# ;
FEHLER: invalid perminfoindex 0 in RTE with relid 17034
(relid 17034=or_followup_archiv)
which failed repeatedly on production and a local copy (pg_dump/restore).
When you try to access the xxx_archiv table directly like :
explain analyze -- explain analyze verbose -- explain -- select * from ( -- select count(*) from ( -- select length(sel) from (
with qp_netto as (
select
77812::int as id_of ,
1.000000::numeric(8,6) as fac_to_us ,
6.9318647425014148::numeric(8,3) as prfac_netto_1,
0.0::numeric(8,3) as prfac_netto_2,
1.000000::numeric(8,6) as our_to_us ,
6.88795000000000000000::numeric(8,3) as prour_netto_1,
0.0::numeric(8,3) as prour_netto_2
)
-- select * from qp_netto;
update or_followup_archiv set
of_pr1_fac_netto=coalesce(prfac_netto_1,0.0)
,of_pr1_fac_netusd=coalesce(prfac_netto_1*fac_to_us,0.0)
,of_pr2_fac_netto=coalesce(prfac_netto_2,0.0)
,of_pr2_fac_netusd=coalesce(prfac_netto_2*fac_to_us,0.0)
,of_pr1_our_netto=coalesce(prour_netto_1,0.0)
,of_pr1_our_netusd=coalesce(prour_netto_1*our_to_us,0.0)
,of_pr2_our_netto=coalesce(prour_netto_2,0.0)
,of_pr2_our_netusd=coalesce(prour_netto_2*our_to_us,0.0)
from qp_netto
where
or_followup_archiv.id_of=qp_netto.id_of
and or_followup_archiv.of_season=35
;
BUT:
Once this query of the archiv table is run (and updated 1 record) the original query through the main table (without archiv) also succeeds!
So when one update is run successfully, the error is not reproducable any more!
This behavior is preserved through pg_dump/pg_restore of the whole databsase for both succes and failure case.
I have no clue what difference in the dump file would trigger this: On comparison of an sql dump only the updated row is moved at the end of the copy, nothing else changed.
Unfortunatly the provided errdb_noerr does not show the error (due to manually creation steps perhaps).
Hans Buschmann
Attachment
Hans Buschmann <buschmann@nidsa.net> writes: > For your reference I include a simple dump of a test case database, which executes the queries but does NOT reproduce theerror. > This case seems much more complicated then I thought on first view. Hmm. The most likely explanation for the error sometimes appearing and sometimes not is that it depends on the shape of the selected query plan, which in turn would depend on current pg_statistics contents. Perhaps you could devise some dummy data that would repro the issue and you could share with us? regards, tom lane
AW: AW: BUG #18147: ERROR: invalid perminfoindex 0 in RTE with relid xxxxx
From
Hans Buschmann
Date:
Hello all,
I finally managed to reproduce the bug on a demo database as shown below.
The file err_demo.sql shows all the steps to reproduce. The table creation is done with errdb_noerr_231008.sql (unchanged from last mail).
The clue is:
- you have an inherited table
- on the archiv part, you have only (one or more) brin indexes
- on the archiv part, you have only (one or more) brin indexes
- to reorder the tuples before moving to a new major version (with pg_dumpo/restore) the archiv part is clustered.
this requires to create a btree index, which is removed immediately after the cluster statement.
when all this has been done, a write access to the archiv-part of the table hierarchy causes the error.
A direct update access to or_followup_archiv clears the error (as shown in the final comment part of err_demo.sql).
To make the error reappear, you have to redo the 3 recluster steps.
PS: please apply the steps of err_demo.sql manually because it was not tested for full automation.
Hope this enables you to investigate the root cause of the error.
Hans Buschmann
Attachment
Hans Buschmann <buschmann@nidsa.net> writes: > I finally managed to reproduce the bug on a demo database as shown below. Thanks! Reproduced it as described on HEAD (although for me, repeating the query repeats the error, there's no need to do anything fancy to re-arm it). Backtrace looks like #0 errfinish (filename=0xaad704 "parse_relation.c", lineno=3907, funcname=0xaae690 <__func__.24632> "getRTEPermissionInfo") at elog.c:480 #1 0x00000000004b07fa in getRTEPermissionInfo (rteperminfos=<optimized out>, rte=0x1725ba8) at parse_relation.c:3906 #2 0x00000000006d6345 in GetResultRTEPermissionInfo ( estate=estate@entry=0x1739120, relinfo=<optimized out>, relinfo=<optimized out>) at execUtils.c:1386 #3 0x00000000006d755c in ExecGetUpdatedCols (relinfo=relinfo@entry=0x17395e0, estate=estate@entry=0x1739120) at execUtils.c:1295 #4 0x00000000006c8292 in index_unchanged_by_update (indexRelation=0x1772770, indexInfo=0x17ea4a0, estate=0x1739120, resultRelInfo=0x17395e0) at execIndexing.c:985 #5 ExecInsertIndexTuples (resultRelInfo=resultRelInfo@entry=0x17395e0, slot=slot@entry=0x17ea140, estate=0x1739120, update=update@entry=true, noDupErr=noDupErr@entry=false, specConflict=specConflict@entry=0x0, arbiterIndexes=0x0, onlySummarizing=false) at execIndexing.c:427 #6 0x00000000006f560a in ExecUpdateEpilogue ( resultRelInfo=resultRelInfo@entry=0x17395e0, tupleid=tupleid@entry=0x7ffd1fa2b10e, oldtuple=oldtuple@entry=0x0, slot=0x17ea140, updateCxt=<optimized out>, context=<optimized out>, context=<optimized out>) at nodeModifyTable.c:2130 #7 0x00000000006f6b24 in ExecUpdate (context=0x7ffd1fa2b140, resultRelInfo=0x17395e0, tupleid=0x7ffd1fa2b10e, oldtuple=0x0, slot=0x17ea140, canSetTag=<optimized out>) at nodeModifyTable.c:2478 #8 0x00000000006f834b in ExecModifyTable (pstate=<optimized out>) at nodeModifyTable.c:3824 In ExecGetUpdatedCols, (gdb) p relinfo->ri_RootResultRelInfo $2 = (struct ResultRelInfo *) 0x0 (gdb) p relinfo->ri_RangeTableIndex $3 = 5 RTE 5 is the added-on RTE for the child table or_followup_archiv. It looks like {RANGETBLENTRY :alias {ALIAS :aliasname or_followup :colnames ("id_of" "of_season" "of_pr1_fac_netto" "of_pr1_fac_netusd" "of_pr2_fac_netto" "of_pr2_fac_netusd" "of_pr1_our_netto" "of_pr1_ou r_netusd" "of_pr2_our_netto" "of_pr2_our_netusd") } :eref {ALIAS :aliasname or_followup :colnames ("id_of" "of_season" "of_pr1_fac_netto" "of_pr1_fac_netusd" "of_pr2_fac_netto" "of_pr2_fac_netusd" "of_pr1_our_netto" "of_pr1_ou r_netusd" "of_pr2_our_netto" "of_pr2_our_netusd") } :rtekind 0 :relid 41352 or_followup_archiv :relkind r :rellockmode 3 :tablesample <> :perminfoindex 0 :lateral false :inh false :inFromCl false :securityQuals <> } So the proximate problem is that we're trying to fetch an updatedCols bitmapset for a child table that has no permissions checks to make and thus doesn't need an RTEPermissionInfo, so its perminfoindex is zero. It might be that this relinfo should have ri_RootResultRelInfo set, but it doesn't. It's also possible that we erred by taking those bitmapsets out of RTEs, although that'd be an unpleasant conclusion with v16 already released. However ... the need to fetch that data is ultimately coming from index_unchanged_by_update, and I don't see how that is not buggy as can be, independently of this. How can it be okay to ignore the effects of BEFORE triggers when deciding if an index is unchanged? If this is because it's "only a hint" and doesn't have to be reliable, okay, but the documentation around indexUnchanged utterly fails to make that clear. I fear some poor index AM writer is going to get screwed big time when they assume this flag is good for more than heuristic decisions about when to do noncritical maintenance. The reason I'm on about that is that if it's okay for index_unchanged_by_update to lie, another approach we could consider is to return a default result if we're looking at a child table for which we lack updatedCols data. We might have to do that in v16, since we don't have much wiggle room to adjust the RTEPermissionInfo data in a released branch. (I haven't looked into exactly why it's so hard to reach this error. Seems like any inherited UPDATE is at risk, so there must be additional gating factors to explain why we've not noticed this before.) regards, tom lane
Re: AW: AW: BUG #18147: ERROR: invalid perminfoindex 0 in RTE with relid xxxxx
From
Peter Geoghegan
Date:
On Mon, Oct 23, 2023 at 11:30 AM Tom Lane <tgl@sss.pgh.pa.us> wrote: > However ... the need to fetch that data is ultimately coming from > index_unchanged_by_update, and I don't see how that is not buggy > as can be, independently of this. How can it be okay to ignore > the effects of BEFORE triggers when deciding if an index is unchanged? > If this is because it's "only a hint" and doesn't have to be reliable, > okay, but the documentation around indexUnchanged utterly fails to > make that clear. I fear some poor index AM writer is going to get > screwed big time when they assume this flag is good for more than > heuristic decisions about when to do noncritical maintenance. That's fair, though note that index_unchanged_by_update does at least own the fact that it ignores the effects of BEFORE triggers in code comments. It also doesn't care about predicates in partial indexes, for reasons that are fairly specific to the way that the hint is actually used on the nbtree side. > The reason I'm on about that is that if it's okay for > index_unchanged_by_update to lie, another approach we could consider > is to return a default result if we're looking at a child table > for which we lack updatedCols data. We might have to do that in > v16, since we don't have much wiggle room to adjust the > RTEPermissionInfo data in a released branch. Note that index_unchanged_by_update already lies on v14: it unconditionally indicates that any non-HOT update has unchanged indexes for all indexes, regardless of their individual status. This was due to a pragmatic trade-off made when looking for a backpatchable fix, but I don't think that there's actually much downside to it. In general I suspect that index_unchanged_by_update is a little overengineered. -- Peter Geoghegan
Peter Geoghegan <pg@bowt.ie> writes: > On Mon, Oct 23, 2023 at 11:30 AM Tom Lane <tgl@sss.pgh.pa.us> wrote: >> If this is because it's "only a hint" and doesn't have to be reliable, >> okay, but the documentation around indexUnchanged utterly fails to >> make that clear. I fear some poor index AM writer is going to get >> screwed big time when they assume this flag is good for more than >> heuristic decisions about when to do noncritical maintenance. > That's fair, though note that index_unchanged_by_update does at least > own the fact that it ignores the effects of BEFORE triggers in code > comments. It also doesn't care about predicates in partial indexes, > for reasons that are fairly specific to the way that the hint is > actually used on the nbtree side. Yeah, there are comments within index_unchanged_by_update about those things. What I'm unhappy about is that indexam.sgml's discussion of the indexUnchanged flag makes it sound far more trustworthy than it actually is. Somebody who just read that doco and didn't scour the underlying code would be badly misled. regards, tom lane
Re: AW: AW: BUG #18147: ERROR: invalid perminfoindex 0 in RTE with relid xxxxx
From
Peter Geoghegan
Date:
On Mon, Oct 23, 2023 at 11:55 AM Tom Lane <tgl@sss.pgh.pa.us> wrote: > Yeah, there are comments within index_unchanged_by_update about those > things. What I'm unhappy about is that indexam.sgml's discussion of > the indexUnchanged flag makes it sound far more trustworthy than it > actually is. Somebody who just read that doco and didn't scour the > underlying code would be badly misled. I understand. I'll come up with a doc patch for that later on today. -- Peter Geoghegan
I wrote: > So the proximate problem is that we're trying to fetch an updatedCols > bitmapset for a child table that has no permissions checks to make > and thus doesn't need an RTEPermissionInfo, so its perminfoindex is > zero. It might be that this relinfo should have ri_RootResultRelInfo > set, but it doesn't. That seems to be a fairly good guess. I was able to reduce the test case to this: drop table if exists t1 cascade; create table t1 (f1 int, f2 int, f3 int , check (f1 < 10) no inherit ); create table t2 () inherits(t1); insert into t2 select i, i+1, 0 from generate_series(1,1000) i; create index on t2(f1,f2); update t1 set f3 = 11 where f1 = 12 and f2 = 13; You don't see the failure without the check constraint on t1. What is happening is that with that, we remove t1 from the plan entirely, causing us to think that t2 is the root target relation, and then things blow up because the root should have an RTEPermissionInfo and doesn't. The place where things start to go off the rails is in ExecInitModifyTable: * If it's a partitioned table, the root partition doesn't appear * elsewhere in the plan and its RT index is given explicitly in * node->rootRelation. Otherwise (i.e. table inheritance) the target * relation is the first relation in the node->resultRelations list. node->resultRelations contains only t2, so boom. Maybe we could fix this by setting ModifyTable.rootRelation to the parent relation in the plain-inheritance case not only the partitioned case; but I've not investigated what else might be expecting it to be set only for partitions. According to this understanding, the root cause is quite old and the RTEPermissionInfo failure just exposes it in an obvious way. I wonder whether we can find related cases that misbehave even before v16. > (I haven't looked into exactly why it's so hard to reach this > error. Seems like any inherited UPDATE is at risk, so there > must be additional gating factors to explain why we've not > noticed this before.) I haven't tracked that down yet, but I did find that without the CHECK constraint we don't reach index_unchanged_by_update at all in repeat executions of the problem query. This is because ExecUpdateEpilogue sees updateCxt->updateIndexes == TU_None and thinks it doesn't have to do anything. This seems a bit suspicious in its own right. Maybe it's okay, because the updates would be HOT in this test case, but the first one should be HOT too. It smells like something is being cached across queries that probably should not be. regards, tom lane
Re: AW: AW: BUG #18147: ERROR: invalid perminfoindex 0 in RTE with relid xxxxx
From
Peter Geoghegan
Date:
On Mon, Oct 23, 2023 at 12:01 PM Peter Geoghegan <pg@bowt.ie> wrote: > On Mon, Oct 23, 2023 at 11:55 AM Tom Lane <tgl@sss.pgh.pa.us> wrote: > > Yeah, there are comments within index_unchanged_by_update about those > > things. What I'm unhappy about is that indexam.sgml's discussion of > > the indexUnchanged flag makes it sound far more trustworthy than it > > actually is. Somebody who just read that doco and didn't scour the > > underlying code would be badly misled. > > I understand. I'll come up with a doc patch for that later on today. What do you think of the attached? If there are no objections I'll commit this soon, backpatching to v14. Separately, I wonder if index_unchanged_by_update should actually just always give the hint with a non-HOT update, regardless of the specifics for each index/its columns -- just like on the v14 branch. In general I'm more concerned about the danger of not giving the hint when we should, rather than giving the hint too often. Most individual btinsert() calls do nothing with the hint already (because there is still enough free space for the incoming item on the page). The hint is inherently nothing more than a signal that bottom-up index deletion might be a good idea, iff we're just about out of better options for affected leaf pages. -- Peter Geoghegan
Attachment
Peter Geoghegan <pg@bowt.ie> writes: > On Mon, Oct 23, 2023 at 12:01 PM Peter Geoghegan <pg@bowt.ie> wrote: >> I understand. I'll come up with a doc patch for that later on today. > What do you think of the attached? WFM. > Separately, I wonder if index_unchanged_by_update should actually just > always give the hint with a non-HOT update, regardless of the > specifics for each index/its columns -- just like on the v14 branch. I'm confused. Wouldn't that be the exact opposite of "unchanged"? Maybe the real problem here is that the meaning of the hint is not what you'd expect from its name? regards, tom lane
Re: AW: AW: BUG #18147: ERROR: invalid perminfoindex 0 in RTE with relid xxxxx
From
Peter Geoghegan
Date:
On Mon, Oct 23, 2023 at 5:28 PM Tom Lane <tgl@sss.pgh.pa.us> wrote: > > Separately, I wonder if index_unchanged_by_update should actually just > > always give the hint with a non-HOT update, regardless of the > > specifics for each index/its columns -- just like on the v14 branch. > > I'm confused. Wouldn't that be the exact opposite of "unchanged"? Well, in practice "indexUnchanged = true" means "do bottom-up deletion if it's the only way to avoid a page split". The justification is that the incoming tuple is "logically unchanged" (actually it's more complicated than that, but that's our starting point). Maybe that naming convention makes things more confusing than necessary. Naming things is hard. > Maybe the real problem here is that the meaning of the hint is not > what you'd expect from its name? Maybe. Perhaps I should have chosen a name that made it clearer that there really is only one way to apply "indexUnchanged = true". Though the docs are pretty clear about this already. I can't say I feel too strongly about the name myself. -- Peter Geoghegan
Peter Geoghegan <pg@bowt.ie> writes: > On Mon, Oct 23, 2023 at 5:28 PM Tom Lane <tgl@sss.pgh.pa.us> wrote: >>> Separately, I wonder if index_unchanged_by_update should actually just >>> always give the hint with a non-HOT update, regardless of the >>> specifics for each index/its columns -- just like on the v14 branch. >> I'm confused. Wouldn't that be the exact opposite of "unchanged"? > Well, in practice "indexUnchanged = true" means "do bottom-up deletion > if it's the only way to avoid a page split". The justification is that > the incoming tuple is "logically unchanged" (actually it's more > complicated than that, but that's our starting point). But doesn't the need for a non-HOT update show that the tuple *was* changed --- in index-relevant columns, even? Maybe I'm still not understanding exactly what condition we're detecting. regards, tom lane
Re: AW: AW: BUG #18147: ERROR: invalid perminfoindex 0 in RTE with relid xxxxx
From
Peter Geoghegan
Date:
On Mon, Oct 23, 2023 at 6:06 PM Tom Lane <tgl@sss.pgh.pa.us> wrote: > > Well, in practice "indexUnchanged = true" means "do bottom-up deletion > > if it's the only way to avoid a page split". The justification is that > > the incoming tuple is "logically unchanged" (actually it's more > > complicated than that, but that's our starting point). > > But doesn't the need for a non-HOT update show that the tuple *was* > changed --- in index-relevant columns, even? Maybe I'm still not > understanding exactly what condition we're detecting. Not necessarily. For one thing you might need to do a non-HOT update purely because there isn't enough free space to fit the successor version on the original heap page. In general, even a 100% HOT-safe UPDATE statement might not be able to perform HOT updates. As I said earlier, the way that we deal with partial indexes doesn't really make too much sense if you try to shoehorn it into an abstract definition. It makes a lot more sense when seen in the context of a workload with a partial index, and shown by a test case from Marko Tiikkaja: https://www.postgresql.org/message-id/flat/CAL9smLC%3DSxYiN7yZ4HDyk0RnZyXoP2vaHD-Vg1JskOEHyhMXug%40mail.gmail.com#e79eca5922789de828314e296fdcb82d I freely admit that this general approach is non-modular, even ugly. -- Peter Geoghegan
AW: AW: AW: BUG #18147: ERROR: invalid perminfoindex 0 in RTE with relid xxxxx
From
Hans Buschmann
Date:
>Thanks! Reproduced it as described on HEAD (although for me, repeating
>the query repeats the error, there's no need to do anything fancy to
>re-arm it). Backtrace looks like
To clear the error (as it is now on production system) you have to execute the update against or_followup_archiv, directly (bypassing the parent table) as shown in
>the query repeats the error, there's no need to do anything fancy to
>re-arm it). Backtrace looks like
To clear the error (as it is now on production system) you have to execute the update against or_followup_archiv, directly (bypassing the parent table) as shown in
err_demo=#
err_demo=# explain analyze -- explain analyze verbose -- explain -- select * from ( -- select count(*) from ( -- select length(sel) from (
err_demo-# with qp_netto as (
err_demo(# select
err_demo(# 72812::int as id_of ,
err_demo(# 1.000000::numeric(8,6) as fac_to_us ,
err_demo(# 6.9318647425014148::numeric(8,3) as prfac_netto_1,
err_demo(# 0.0::numeric(8,3) as prfac_netto_2,
err_demo(# 1.000000::numeric(8,6) as our_to_us ,
err_demo(# 6.88795000000000000000::numeric(8,3) as prour_netto_1,
err_demo(# 0.0::numeric(8,3) as prour_netto_2
err_demo(# )
err_demo-# -- select * from qp_netto;
err_demo-# update or_followup set
err_demo-# of_pr1_fac_netto=coalesce(prfac_netto_1,0.0)
err_demo-# ,of_pr1_fac_netusd=coalesce(prfac_netto_1*fac_to_us,0.0)
err_demo-# ,of_pr2_fac_netto=coalesce(prfac_netto_2,0.0)
err_demo-# ,of_pr2_fac_netusd=coalesce(prfac_netto_2*fac_to_us,0.0)
err_demo-# ,of_pr1_our_netto=coalesce(prour_netto_1,0.0)
err_demo-# ,of_pr1_our_netusd=coalesce(prour_netto_1*our_to_us,0.0)
err_demo-# ,of_pr2_our_netto=coalesce(prour_netto_2,0.0)
err_demo-# ,of_pr2_our_netusd=coalesce(prour_netto_2*our_to_us,0.0)
err_demo-# from qp_netto
err_demo-# where
err_demo-# or_followup.id_of=qp_netto.id_of
err_demo-# and or_followup.of_season=35
err_demo-# ;
FEHLER: invalid perminfoindex 0 in RTE with relid 30512
err_demo=# explain analyze -- explain analyze verbose -- explain -- select * from ( -- select count(*) from ( -- select length(sel) from (
err_demo-# with qp_netto as (
err_demo(# select
err_demo(# 72812::int as id_of ,
err_demo(# 1.000000::numeric(8,6) as fac_to_us ,
err_demo(# 6.9318647425014148::numeric(8,3) as prfac_netto_1,
err_demo(# 0.0::numeric(8,3) as prfac_netto_2,
err_demo(# 1.000000::numeric(8,6) as our_to_us ,
err_demo(# 6.88795000000000000000::numeric(8,3) as prour_netto_1,
err_demo(# 0.0::numeric(8,3) as prour_netto_2
err_demo(# )
err_demo-# -- select * from qp_netto;
err_demo-# update or_followup_archiv set
err_demo-# of_pr1_fac_netto=coalesce(prfac_netto_1,0.0)
err_demo-# ,of_pr1_fac_netusd=coalesce(prfac_netto_1*fac_to_us,0.0)
err_demo-# ,of_pr2_fac_netto=coalesce(prfac_netto_2,0.0)
err_demo-# ,of_pr2_fac_netusd=coalesce(prfac_netto_2*fac_to_us,0.0)
err_demo-# ,of_pr1_our_netto=coalesce(prour_netto_1,0.0)
err_demo-# ,of_pr1_our_netusd=coalesce(prour_netto_1*our_to_us,0.0)
err_demo-# ,of_pr2_our_netto=coalesce(prour_netto_2,0.0)
err_demo-# ,of_pr2_our_netusd=coalesce(prour_netto_2*our_to_us,0.0)
err_demo-# from qp_netto
err_demo-# where
err_demo-# or_followup_archiv.id_of=qp_netto.id_of
err_demo-# and or_followup_archiv.of_season=35
err_demo-# ;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------
Update on or_followup_archiv (cost=3.00..373.13 rows=0 width=0) (actual time=1.680..1.680 rows=0 loops=1)
-> Bitmap Heap Scan on or_followup_archiv (cost=3.00..373.13 rows=1 width=118) (actual time=0.152..0.161 rows=1 loops=1)
Recheck Cond: ((of_season = 35) AND (id_of = 72812))
Rows Removed by Index Recheck: 543
Heap Blocks: lossy=4
-> Bitmap Index Scan on brin_or_followup_archiv_season_id_of (cost=0.00..3.00 rows=542 width=0) (actual time=0.083..0.083 rows=40 loops=1)
Index Cond: ((of_season = 35) AND (id_of = 72812))
Planning Time: 1.456 ms
Execution Time: 1.733 ms
(9 Zeilen)
err_demo=#
err_demo=# explain analyze -- explain analyze verbose -- explain -- select * from ( -- select count(*) from ( -- select length(sel) from (
err_demo-# with qp_netto as (
err_demo(# select
err_demo(# 72812::int as id_of ,
err_demo(# 1.000000::numeric(8,6) as fac_to_us ,
err_demo(# 6.9318647425014148::numeric(8,3) as prfac_netto_1,
err_demo(# 0.0::numeric(8,3) as prfac_netto_2,
err_demo(# 1.000000::numeric(8,6) as our_to_us ,
err_demo(# 6.88795000000000000000::numeric(8,3) as prour_netto_1,
err_demo(# 0.0::numeric(8,3) as prour_netto_2
err_demo(# )
err_demo-# -- select * from qp_netto;
err_demo-# update or_followup set
err_demo-# of_pr1_fac_netto=coalesce(prfac_netto_1,0.0)
err_demo-# ,of_pr1_fac_netusd=coalesce(prfac_netto_1*fac_to_us,0.0)
err_demo-# ,of_pr2_fac_netto=coalesce(prfac_netto_2,0.0)
err_demo-# ,of_pr2_fac_netusd=coalesce(prfac_netto_2*fac_to_us,0.0)
err_demo-# ,of_pr1_our_netto=coalesce(prour_netto_1,0.0)
err_demo-# ,of_pr1_our_netusd=coalesce(prour_netto_1*our_to_us,0.0)
err_demo-# ,of_pr2_our_netto=coalesce(prour_netto_2,0.0)
err_demo-# ,of_pr2_our_netusd=coalesce(prour_netto_2*our_to_us,0.0)
err_demo-# from qp_netto
err_demo-# where
err_demo-# or_followup.id_of=qp_netto.id_of
err_demo-# and or_followup.of_season=35
err_demo-# ;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------
Update on or_followup (cost=3.00..373.14 rows=0 width=0) (actual time=0.169..0.169 rows=0 loops=1)
Update on or_followup_archiv or_followup_1
-> Result (cost=3.00..373.14 rows=1 width=122) (actual time=0.147..0.148 rows=1 loops=1)
-> Bitmap Heap Scan on or_followup_archiv or_followup_1 (cost=3.00..373.13 rows=1 width=10) (actual time=0.147..0.147 rows=1 loops=1)
Recheck Cond: ((of_season = 35) AND (id_of = 72812))
Rows Removed by Index Recheck: 831
Heap Blocks: lossy=7
-> Bitmap Index Scan on brin_or_followup_archiv_season_id_of (cost=0.00..3.00 rows=542 width=0) (actual time=0.042..0.042 rows=70 loops=1)
Index Cond: ((of_season = 35) AND (id_of = 72812))
Planning Time: 0.671 ms
Execution Time: 0.201 ms
(11 Zeilen)
err_demo=#
1st query: ERROR
2nd query to or_followup_archiv: Succeeds
then 1st query repeated: SUCCEEDS!!
With this direct update to or_followup_archiv (or an unclustered table) the error disappears.
Hans Buschmann
Re: AW: AW: BUG #18147: ERROR: invalid perminfoindex 0 in RTE with relid xxxxx
From
Amit Langote
Date:
On Tue, Oct 24, 2023 at 5:59 AM Tom Lane <tgl@sss.pgh.pa.us> wrote: > I wrote: > > So the proximate problem is that we're trying to fetch an updatedCols > > bitmapset for a child table that has no permissions checks to make > > and thus doesn't need an RTEPermissionInfo, so its perminfoindex is > > zero. It might be that this relinfo should have ri_RootResultRelInfo > > set, but it doesn't. > > That seems to be a fairly good guess. I was able to reduce the > test case to this: > > drop table if exists t1 cascade; > > create table t1 (f1 int, f2 int, f3 int > , check (f1 < 10) no inherit > ); > > create table t2 () inherits(t1); > > insert into t2 select i, i+1, 0 from generate_series(1,1000) i; > > create index on t2(f1,f2); > > update t1 set f3 = 11 where f1 = 12 and f2 = 13; > > > You don't see the failure without the check constraint on t1. > What is happening is that with that, we remove t1 from the plan > entirely, causing us to think that t2 is the root target relation, > and then things blow up because the root should have an > RTEPermissionInfo and doesn't. The place where things start > to go off the rails is in ExecInitModifyTable: > > * If it's a partitioned table, the root partition doesn't appear > * elsewhere in the plan and its RT index is given explicitly in > * node->rootRelation. Otherwise (i.e. table inheritance) the target > * relation is the first relation in the node->resultRelations list. > > node->resultRelations contains only t2, so boom. > > Maybe we could fix this by setting ModifyTable.rootRelation to > the parent relation in the plain-inheritance case not only the > partitioned case; but I've not investigated what else might be > expecting it to be set only for partitions. That appears to be the easiest fix to me (attached PoC makes the error go away for me and passes check-world). By setting rootRelation for plan-inheritance roots, there would be two ResultRelInfos for the root relation (that is, if it's also present in resultRelations, unlike in this case) -- one in ModifyTableState.rootResultRelInfo and another in the 1st element of ModifyTableState.resultRelInfo[]. That might be fine, because, AFAICS, the partitioning-related code sites that look at the former have checks to ensure that they don't accidentally try to access a plain-inheritance relation as a partitioned one. I might need to take a closer look. > According to this understanding, the root cause is quite old > and the RTEPermissionInfo failure just exposes it in an obvious > way. I wonder whether we can find related cases that misbehave > even before v16. I decided to look at fire{BS|AS}Triggers() first, because we changed them in f49b85d783f to look at ModifyTableState.rootResultRelInfo to get the table whose triggers to fire. They seem broken for this case: create or replace function t1_stmt_trig_func() returns trigger as $$ begin raise notice 'updating t1'; return NULL; end; $$ language plpgsql; create trigger t1_stmt_trig before update on t1 execute function t1_stmt_trig_func(); -- trigger should've been fired update t1 set f3 = 11 where f1 = 12 and f2 = 13; ERROR: invalid perminfoindex 0 in RTE with relid 32799 -- partitioned roots seem fine create table p (a int check (a = 0)) partition by list (a); create table p1 partition of p default; create trigger p_stmt_trig before update on p execute function t1_stmt_trig_func(); update p set a = 1 where a = 1; NOTICE: updating t1 UPDATE 0 I recalled we fixed a similar case in ab5fcf2b04f. I suspect we would not have seen this report if we had also considered the excluded-root-parent case in that commit, though I might be wrong. -- Thanks, Amit Langote EDB: http://www.enterprisedb.com
Attachment
Amit Langote <amitlangote09@gmail.com> writes: > On Tue, Oct 24, 2023 at 5:59 AM Tom Lane <tgl@sss.pgh.pa.us> wrote: >> Maybe we could fix this by setting ModifyTable.rootRelation to >> the parent relation in the plain-inheritance case not only the >> partitioned case; but I've not investigated what else might be >> expecting it to be set only for partitions. > That appears to be the easiest fix to me (attached PoC makes the error > go away for me and passes check-world). > By setting rootRelation for plan-inheritance roots, there would be two > ResultRelInfos for the root relation (that is, if it's also present in > resultRelations, unlike in this case) -- one in > ModifyTableState.rootResultRelInfo and another in the 1st element of > ModifyTableState.resultRelInfo[]. That might be fine, because, > AFAICS, the partitioning-related code sites that look at the former > have checks to ensure that they don't accidentally try to access a > plain-inheritance relation as a partitioned one. I might need to take > a closer look. I suggest that it might be cleaner if we make rootRelation point to the original appendrel (that is, the RTE entry with inh = true). That would be exactly parallel to the partitioning situation, in that that RTE is not scanned in the plan. But it's for the same table as what's normally the first result table, so it should have the same permissions info. BTW, I noted while looking at this example that there are two RTEPermissionInfo structs with identical contents, one for the appendrel RTE and one for the first child. That seems kind of unnecessary. > I decided to look at fire{BS|AS}Triggers() first, because we changed > them in f49b85d783f to look at ModifyTableState.rootResultRelInfo to > get the table whose triggers to fire. They seem broken for this case: Yuck. But either proposal above would fix that, right? regards, tom lane
Re: AW: AW: AW: BUG #18147: ERROR: invalid perminfoindex 0 in RTE with relid xxxxx
From
Tom Lane
Date:
Hans Buschmann <buschmann@nidsa.net> writes: > 1st query: ERROR > 2nd query to or_followup_archiv: Succeeds > then 1st query repeated: SUCCEEDS!! > With this direct update to or_followup_archiv (or an unclustered table) the error disappears. Yeah. After more study I think there's less there than meets the eye. If the row update manages to be HOT then we don't need to make any new index entries so we never reach the troublesome code. In your original reproducer, the CLUSTER step was important because it left the target tuple in a fully-packed page with no room for a HOT update on the same page. When playing around with variants or even re-executing the same query, it matters how much free space there is on the page containing the target tuple, and that is affected by all sorts of seemingly-irrelevant actions. Anyway, we do now have a simple and reliable reproducer, so we can work on fixing this. Thanks again for the report! regards, tom lane
I wrote: > I suggest that it might be cleaner if we make rootRelation point > to the original appendrel (that is, the RTE entry with inh = true). > That would be exactly parallel to the partitioning situation, in > that that RTE is not scanned in the plan. But it's for the same > table as what's normally the first result table, so it should have > the same permissions info. After looking closer I see that that's exactly what's happening in your patch, so it should all be good. We can make the code in grouping_planner be simpler rather than more complicated, though. I went ahead and pushed that to v14 and up, with adjustments of relevant comments and a test case. There are still loose ends here, though: * The wrong-table-for-triggers bug occurs pre-v14, but the patch doesn't come close to applying because both the planner and executor look quite a bit different in this area. We could devise a separate patch maybe, but given the lack of field complaints I'm not sure this is worth doing. I'd be afraid to put such a patch into v11 at this point anyway, given that there will be no opportunity to fix any new bugs after November. * I'm still seeing the extra RTEPermissionsInfo. It looks like that's a consequence of this kluge in expand_single_inheritance_child: /* * No permission checking for the child RTE unless it's the parent * relation in its child role, which only applies to traditional * inheritance. */ if (childOID != parentOID) childrte->perminfoindex = 0; I suspect that now this should just unconditionally clear childrte->perminfoindex, but it's minor cleanup not a bug fix so I didn't pursue that in the initial patch. * It seems like ModifyTable.nominalRelation and ModifyTable.rootRelation are pretty darn redundant. Maybe we should make an effort to get rid of one of them. Or maybe it's not worth the trouble. regards, tom lane
Re: AW: AW: BUG #18147: ERROR: invalid perminfoindex 0 in RTE with relid xxxxx
From
Amit Langote
Date:
On Wed, Oct 25, 2023 at 4:07 AM Tom Lane <tgl@sss.pgh.pa.us> wrote: > I wrote: > > I suggest that it might be cleaner if we make rootRelation point > > to the original appendrel (that is, the RTE entry with inh = true). > > That would be exactly parallel to the partitioning situation, in > > that that RTE is not scanned in the plan. But it's for the same > > table as what's normally the first result table, so it should have > > the same permissions info. > > After looking closer I see that that's exactly what's happening > in your patch, so it should all be good. We can make the code in > grouping_planner be simpler rather than more complicated, though. > > I went ahead and pushed that to v14 and up, with adjustments of > relevant comments and a test case. Thanks. > There are still loose ends > here, though: > > * The wrong-table-for-triggers bug occurs pre-v14, but the patch > doesn't come close to applying because both the planner and > executor look quite a bit different in this area. We could > devise a separate patch maybe, but given the lack of field > complaints I'm not sure this is worth doing. I'd be afraid > to put such a patch into v11 at this point anyway, given that > there will be no opportunity to fix any new bugs after November. Yeah, it might not be worthwhile to try to back-patch this to 12 and 13. > * I'm still seeing the extra RTEPermissionsInfo. It looks like that's > a consequence of this kluge in expand_single_inheritance_child: > > /* > * No permission checking for the child RTE unless it's the parent > * relation in its child role, which only applies to traditional > * inheritance. > */ > if (childOID != parentOID) > childrte->perminfoindex = 0; > > I suspect that now this should just unconditionally clear > childrte->perminfoindex, but it's minor cleanup not a bug fix > so I didn't pursue that in the initial patch. Would you like me to apply something like the attached? > * It seems like ModifyTable.nominalRelation and > ModifyTable.rootRelation are pretty darn redundant. Maybe we > should make an effort to get rid of one of them. Or maybe > it's not worth the trouble. We had a discussion on unifying the two before: https://www.postgresql.org/message-id/12148.1538938507%40sss.pgh.pa.us I'm fine with leaving that as-is. -- Thanks, Amit Langote EDB: http://www.enterprisedb.com
Attachment
Amit Langote <amitlangote09@gmail.com> writes: > On Wed, Oct 25, 2023 at 4:07 AM Tom Lane <tgl@sss.pgh.pa.us> wrote: >> I suspect that now this should just unconditionally clear >> childrte->perminfoindex, but it's minor cleanup not a bug fix >> so I didn't pursue that in the initial patch. > Would you like me to apply something like the attached? Diff looks fine, but I'm not sure that it's appropriate to characterize the existing code as an oversight. It was a necessary hack while the executor was behaving as it did (ie, using the first child as root). >> * It seems like ModifyTable.nominalRelation and >> ModifyTable.rootRelation are pretty darn redundant. Maybe we >> should make an effort to get rid of one of them. Or maybe >> it's not worth the trouble. > We had a discussion on unifying the two before: > https://www.postgresql.org/message-id/12148.1538938507%40sss.pgh.pa.us Ah, so we did. The "serve different masters" argument did re-occur to me while I was looking at this yesterday, but I'm not sure how strong it is really. Anyway, I'm also content to leave it be. regards, tom lane
Re: AW: AW: BUG #18147: ERROR: invalid perminfoindex 0 in RTE with relid xxxxx
From
Amit Langote
Date:
On Wed, Oct 25, 2023 at 10:51 PM Tom Lane <tgl@sss.pgh.pa.us> wrote: > Amit Langote <amitlangote09@gmail.com> writes: > > On Wed, Oct 25, 2023 at 4:07 AM Tom Lane <tgl@sss.pgh.pa.us> wrote: > >> I suspect that now this should just unconditionally clear > >> childrte->perminfoindex, but it's minor cleanup not a bug fix > >> so I didn't pursue that in the initial patch. > > > Would you like me to apply something like the attached? > > Diff looks fine, but I'm not sure that it's appropriate to characterize > the existing code as an oversight. It was a necessary hack while the > executor was behaving as it did (ie, using the first child as root). Ah, you're right. I've updated the commit message. Will push later today. -- Thanks, Amit Langote EDB: http://www.enterprisedb.com
Attachment
Amit Langote <amitlangote09@gmail.com> writes: > On Wed, Oct 25, 2023 at 10:51 PM Tom Lane <tgl@sss.pgh.pa.us> wrote: >> Diff looks fine, but I'm not sure that it's appropriate to characterize >> the existing code as an oversight. It was a necessary hack while the >> executor was behaving as it did (ie, using the first child as root). > Ah, you're right. I've updated the commit message. Will push later today. I'd suggest specifying *first* child RTE in the commit message. LGTM otherwise. regards, tom lane
Re: AW: AW: BUG #18147: ERROR: invalid perminfoindex 0 in RTE with relid xxxxx
From
Amit Langote
Date:
On Thu, Oct 26, 2023 at 11:31 AM Tom Lane <tgl@sss.pgh.pa.us> wrote: > Amit Langote <amitlangote09@gmail.com> writes: > > On Wed, Oct 25, 2023 at 10:51 PM Tom Lane <tgl@sss.pgh.pa.us> wrote: > >> Diff looks fine, but I'm not sure that it's appropriate to characterize > >> the existing code as an oversight. It was a necessary hack while the > >> executor was behaving as it did (ie, using the first child as root). > > > Ah, you're right. I've updated the commit message. Will push later today. > > I'd suggest specifying *first* child RTE in the commit message. > LGTM otherwise. Thanks for checking. Pushed after changing the commit message like that. -- Thanks, Amit Langote EDB: http://www.enterprisedb.com