Thread: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
npage@dynamicsignal.com
Date:
The following bug has been logged on the website:

Bug reference:      11457
Logged by:          Nelson Page
Email address:      npage@dynamicsignal.com
PostgreSQL version: 9.3.5
Operating system:   WIndows 8.1
Description:

SELECT

    "GroupBy1"."A1" AS "C1"

    FROM ( SELECT Count(1) AS "A1"

        FROM   (SELECT

            "UnionAll4"."C1"

            FROM  (SELECT

                "UnionAll3"."C1"

                FROM  (SELECT

                    "UnionAll2"."C1"

                    FROM  (SELECT

                        "UnionAll1"."activityId" AS "C1"

                        FROM  (SELECT

                            "Extent1"."activityId"

                            FROM "ActivityBlogCampaign" AS "Extent1"

                        UNION ALL

                            SELECT

                            "Extent2"."activityId"

                            FROM "ActivitySubmitArticle" AS "Extent2") AS "UnionAll1"

                    UNION ALL

                        SELECT

                        "Extent3"."activityId"

                        FROM "ActivityCuratedPost" AS "Extent3") AS "UnionAll2"

                UNION ALL

                    SELECT

                    "Extent4"."activityId"

                    FROM "ActivitySurvey" AS "Extent4") AS "UnionAll3"

            UNION ALL

                SELECT

                "Extent5"."activityId"

                FROM "ActivityHashTag" AS "Extent5") AS "UnionAll4"

        UNION ALL

            SELECT

            "Extent6"."activityId"

            FROM "ActivityShareArticle" AS "Extent6") AS "UnionAll5"

        INNER JOIN "Activity" AS "Extent7" ON "UnionAll5"."C1" =
"Extent7"."activityId"
        GROUP BY "Extent7"."activityId"

    ) As "GroupBy1"

Re: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
Michael Paquier
Date:
On Fri, Sep 19, 2014 at 7:23 PM,  <npage@dynamicsignal.com> wrote:
> SELECT
> [stuff]
>         ) As "GroupBy1"
I tried to come up with a minimum schema close to what is used in your
example, aka 7 relations, 6 unioning on an ID column with an extra
inner join, and then tried the same query but I could not trigger the
crash on master, latest REL9_3_STABLE (9474c9d) or even 9.3.5.

Having only a query (on 7 relations btw!) and not a self-contained
test-case makes it harder to reproduce a crash. Could you provide at
least a minimum schema that triggers the crash so as we could
reproduce it more easily? I am attaching as well the self-contained
test case with your query and the minimal schema I could come up with
for the moment just for reference.
Regards,
--
Michael

Attachment

Re: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
Nelson Page
Date:
Hi Michael,

I've attached the create scripts for those tables.  I'm relatively new to postgresql, so if that's not as helpful as I
thinkit is, let me know what else I can provide. 

Thanks,
Nelson

-----Original Message-----
From: Michael Paquier [mailto:michael.paquier@gmail.com]
Sent: Saturday, September 20, 2014 10:44 AM
To: Nelson Page
Cc: pgsql-bugs@postgresql.org
Subject: Re: [BUGS] BUG #11457: The below query crashes 9.3.5, but not 9.3.4

On Fri, Sep 19, 2014 at 7:23 PM,  <npage@dynamicsignal.com> wrote:
> SELECT
> [stuff]
>         ) As "GroupBy1"
I tried to come up with a minimum schema close to what is used in your example, aka 7 relations, 6 unioning on an ID
columnwith an extra inner join, and then tried the same query but I could not trigger the crash on master, latest
REL9_3_STABLE(9474c9d) or even 9.3.5. 

Having only a query (on 7 relations btw!) and not a self-contained test-case makes it harder to reproduce a crash.
Couldyou provide at least a minimum schema that triggers the crash so as we could reproduce it more easily? I am
attachingas well the self-contained test case with your query and the minimal schema I could come up with for the
momentjust for reference. 
Regards,
--
Michael

Attachment

Re: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
Alvaro Herrera
Date:
Nelson Page wrote:
> Hi Michael,
>
> I've attached the create scripts for those tables.  I'm relatively new
> to postgresql, so if that's not as helpful as I think it is, let me
> know what else I can provide.

FWIW I can reproduce the crash in 9.3 HEAD with these scripts, so that's
probably helpful enough.  Table "Article" (not sent) is referenced in an
FK, but I just commented out that clause.  Backtrace is:

#0  0x00007fd34d9ca1a5 in *__GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1  0x00007fd34d9cd420 in *__GI_abort () at abort.c:92
#2  0x0000000000768c27 in ExceptionalCondition (conditionName=conditionName@entry=0x8c76b0
"!(!bms_overlap(appendrel->relids,required_outer))",  
    errorType=errorType@entry=0x7a080c "FailedAssertion",
    fileName=fileName@entry=0x8c7470
"../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/util/relnode.c", 
    lineNumber=lineNumber@entry=953) at
../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/utils/error/assert.c:54
#3  0x000000000063c3a5 in get_appendrel_parampathinfo (appendrel=appendrel@entry=0x7fd3444db9c0,
required_outer=required_outer@entry=0x7fd3444e40e8)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/util/relnode.c:953
#4  0x0000000000636293 in create_append_path (rel=rel@entry=0x7fd3444db9c0, subpaths=subpaths@entry=0x7fd3444f6bd8,
    required_outer=required_outer@entry=0x7fd3444e40e8)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/util/pathnode.c:898
#5  0x0000000000605df0 in set_append_rel_pathlist (rti=1146046832, rel=0x7fd3444db9c0, root=0x7fd344506c80,
rte=<optimizedout>) 
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/path/allpaths.c:852
#6  set_rel_pathlist (root=root@entry=0x7fd344506c80, rel=0x7fd3444db9c0, rti=1146046832, rti@entry=1, rte=<optimized
out>)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/path/allpaths.c:306
#7  0x0000000000606407 in set_base_rel_pathlists (root=<optimized out>)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/path/allpaths.c:208
#8  make_one_rel (root=root@entry=0x7fd344506c80, joinlist=joinlist@entry=0x7fd3444df710)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/path/allpaths.c:138
#9  0x0000000000620d7d in query_planner (root=root@entry=0x7fd344506c80, tlist=tlist@entry=0x7fd3444db700,
tuple_fraction=0, 
    tuple_fraction@entry=<error reading variable: Could not find type for DW_OP_GNU_const_type>, limit_tuples=-1,
    qp_callback=qp_callback@entry=0x6218d0 <standard_qp_callback>, qp_extra=qp_extra@entry=0x7fff4c709470,
    cheapest_path=cheapest_path@entry=0x7fff4c709460, sorted_path=sorted_path@entry=0x7fff4c709468,
num_groups=num_groups@entry=0x7fff4c709450)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/plan/planmain.c:261
#10 0x0000000000622157 in grouping_planner (root=root@entry=0x7fd344506c80, tuple_fraction=0,
    tuple_fraction@entry=<error reading variable: Could not find type for DW_OP_GNU_const_type>)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/plan/planner.c:1214
#11 0x0000000000624acb in subquery_planner (glob=0x27e0bc0, parse=parse@entry=0x27efdf0,
parent_root=parent_root@entry=0x27f1ab0, 
    hasRecursion=hasRecursion@entry=0 '\000', tuple_fraction=0, subroot=subroot@entry=0x7fff4c709638)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/plan/planner.c:558
#12 0x00000000006050ef in set_subquery_pathlist (rte=<optimized out>, rti=1, rel=<optimized out>, root=<optimized out>)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/path/allpaths.c:1231
#13 set_rel_size (root=root@entry=0x27f1ab0, rel=<optimized out>, rti=rti@entry=1, rte=<optimized out>)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/path/allpaths.c:264
#14 0x00000000006063a6 in set_base_rel_sizes (root=<optimized out>)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/path/allpaths.c:179
#15 make_one_rel (root=root@entry=0x27f1ab0, joinlist=joinlist@entry=0x27f25b0)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/path/allpaths.c:137
#16 0x0000000000620d7d in query_planner (root=root@entry=0x27f1ab0, tlist=tlist@entry=0x27f2498, tuple_fraction=0,
    tuple_fraction@entry=<error reading variable: Could not find type for DW_OP_GNU_const_type>, limit_tuples=-1,
    qp_callback=qp_callback@entry=0x6218d0 <standard_qp_callback>, qp_extra=qp_extra@entry=0x7fff4c709850,
    cheapest_path=cheapest_path@entry=0x7fff4c709840, sorted_path=sorted_path@entry=0x7fff4c709848,
num_groups=num_groups@entry=0x7fff4c709830)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/plan/planmain.c:261
#17 0x0000000000622157 in grouping_planner (root=root@entry=0x27f1ab0, tuple_fraction=0,
    tuple_fraction@entry=<error reading variable: Could not find type for DW_OP_GNU_const_type>)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/plan/planner.c:1214
#18 0x0000000000624acb in subquery_planner (glob=glob@entry=0x27e0bc0, parse=parse@entry=0x271c428,
parent_root=parent_root@entry=0x0, 
    hasRecursion=hasRecursion@entry=0 '\000', tuple_fraction=0, subroot=subroot@entry=0x7fff4c7099e8)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/plan/planner.c:558
#19 0x0000000000624df7 in standard_planner (parse=0x271c428, cursorOptions=0, boundParams=0x0)
    at ../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/plan/planner.c:209
#20 0x0000000000696549 in pg_plan_query (querytree=<optimized out>, cursorOptions=cursorOptions@entry=0,
boundParams=boundParams@entry=0x0)
    at ../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/tcop/postgres.c:753



--
Álvaro Herrera                http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Re: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
Tom Lane
Date:
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> FWIW I can reproduce the crash in 9.3 HEAD with these scripts, so that's
> probably helpful enough.  Table "Article" (not sent) is referenced in an
> FK, but I just commented out that clause.  Backtrace is:

> #0  0x00007fd34d9ca1a5 in *__GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
> #1  0x00007fd34d9cd420 in *__GI_abort () at abort.c:92
> #2  0x0000000000768c27 in ExceptionalCondition (conditionName=conditionName@entry=0x8c76b0
"!(!bms_overlap(appendrel->relids,required_outer))",  
>     errorType=errorType@entry=0x7a080c "FailedAssertion",
>     fileName=fileName@entry=0x8c7470
"../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/optimizer/util/relnode.c", 
>     lineNumber=lineNumber@entry=953) at
../../../../../../../../../../pgsql/source/REL9_3_STABLE/src/backend/utils/error/assert.c:54

Hm, probably the same thing then as
http://www.postgresql.org/message-id/2326379.AOuSqtNClj@klinga.prans.org

I was poking at that one earlier this evening.  The immediate fix is clear
enough (generate_implied_equalities_for_column should be considering
"grandparent" appendrels), but I'm not sure yet whether we need to change
the logic in generate_join_implied_equalities.

In the meantime, the OP could probably dodge the problem by not nesting
the UNION ALLs like that ...

            regards, tom lane

Re: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
Michael Paquier
Date:
On Tue, Sep 23, 2014 at 1:04 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Hm, probably the same thing then as
> http://www.postgresql.org/message-id/2326379.AOuSqtNClj@klinga.prans.org
Yes, both cases are similar, with the use of two levels of relations
introducing.

> I was poking at that one earlier this evening.  The immediate fix is clear
> enough (generate_implied_equalities_for_column should be considering
> "grandparent" appendrels), but I'm not sure yet whether we need to change
> the logic in generate_join_implied_equalities.

I could reproduce it as well with the scripts previously sent, and
this regression has been introduced by a2db7b7 on REL9_3_STABLE,
backpatched from a87c729 on master. Hence I imagine that this is
reproducible down to 9.1. Also, looking at the code the additions in
add_child_rel_equivalences to bypass the case where parent is not
mentioned in equivclass seems to be the root cause of the regression.

I have not come up yet with a patch fixing this issue though without
introducing regressions in other code paths... But I have been able at
least to come up with the attached minimalistic example to reproduce
the problem. Something similar to that could be part of a regression
test to add in the final patch.
Regards,
--
Michael

Attachment

Re: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
Alvaro Herrera
Date:
Nelson Page wrote:
> As mentioned in the original title, we cannot reproduce this crash in 9.3.4, but can in 9.3.5.  So our particular
crashdoes not go back to 9.1 (unless the 9.1 you mention is some other context maybe?) 

Bug fixes are backpatched to supported branches; 9.3.4 is older than
9.1.14.  9.1.14 does contain the backpatched bug fix which seems to have
introduced the new bug:

commit 555d0b2000e33fd1ad2721015996a66c43bbb3cd
Author: Tom Lane <tgl@sss.pgh.pa.us>
Date:   Thu Jun 26 10:41:10 2014 -0700

    Back-patch "Fix EquivalenceClass processing for nested append relations".

    When we committed a87c729153e372f3731689a7be007bc2b53f1410, we somehow
    failed to notice that it didn't merely improve plan quality for expression
    indexes; there were very closely related cases that failed outright with
    "could not find pathkey item to sort".  The failing cases seem to be those
    where the planner was already capable of selecting a MergeAppend plan,
    and there was inheritance involved: the lack of appropriate eclass child
    members would prevent prepare_sort_from_pathkeys() from succeeding on the
    MergeAppend's child plan nodes for inheritance child tables.

    Accordingly, back-patch into 9.1 through 9.3, along with an extra
    regression test case covering the problem.

    Per trouble report from Michael Glaesemann.



--
Álvaro Herrera                http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Re: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
Nelson Page
Date:
As mentioned in the original title, we cannot reproduce this crash in 9.3.4=
, but can in 9.3.5.  So our particular crash does not go back to 9.1 (unles=
s the 9.1 you mention is some other context maybe?)

Regards,
Nelson

-----Original Message-----
From: Michael Paquier [mailto:michael.paquier@gmail.com]=20
Sent: Friday, September 26, 2014 12:29 AM
To: Tom Lane
Cc: Alvaro Herrera; Nelson Page; pgsql-bugs@postgresql.org
Subject: Re: [BUGS] BUG #11457: The below query crashes 9.3.5, but not 9.3.=
4

On Tue, Sep 23, 2014 at 1:04 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Hm, probably the same thing then as
> http://www.postgresql.org/message-id/2326379.AOuSqtNClj@klinga.prans.o
> rg
Yes, both cases are similar, with the use of two levels of relations introd=
ucing.

> I was poking at that one earlier this evening.  The immediate fix is=20
> clear enough (generate_implied_equalities_for_column should be=20
> considering "grandparent" appendrels), but I'm not sure yet whether we=20
> need to change the logic in generate_join_implied_equalities.

I could reproduce it as well with the scripts previously sent, and this reg=
ression has been introduced by a2db7b7 on REL9_3_STABLE, backpatched from a=
87c729 on master. Hence I imagine that this is reproducible down to 9.1. Al=
so, looking at the code the additions in add_child_rel_equivalences to bypa=
ss the case where parent is not mentioned in equivclass seems to be the roo=
t cause of the regression.

I have not come up yet with a patch fixing this issue though without introd=
ucing regressions in other code paths... But I have been able at least to c=
ome up with the attached minimalistic example to reproduce the problem. Som=
ething similar to that could be part of a regression test to add in the fin=
al patch.
Regards,
--
Michael

Re: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
Tom Lane
Date:
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> Nelson Page wrote:
>> As mentioned in the original title, we cannot reproduce this crash in 9.3.4, but can in 9.3.5.  So our particular
crashdoes not go back to 9.1 (unless the 9.1 you mention is some other context maybe?) 

> Bug fixes are backpatched to supported branches; 9.3.4 is older than
> 9.1.14.  9.1.14 does contain the backpatched bug fix which seems to have
> introduced the new bug:

The Assert failure mentioned in the other thread reproduces for me back to
9.2; that one is contingent on trying to build a parameterized path, so
of course it wouldn't occur before 9.2.  But I imagine other variants of
this issue can be reproduced further back.  The fundamental problem is
that we still have work to do to make nested appendrels behave properly.

I think basically the issue here is that in some situations we really want
to find the top-level ancestor appendrel, not just the immediate parent,
as it is the top-level ancestor that will be mentioned in join clauses,
ORDER BY, etc.

I came across a closely related issue too: in
generate_join_implied_equalities_broken we try to translate original join
clauses to apply to a particular child rel by applying the Var translation
from the AppendRelInfo for that child.  I think this is wrong for the same
reasons: the original join clause will mention top-level ancestor Vars,
meaning we need to successively apply all the translations working down to
the descendant if we want a correct result.

We could probably hack solutions to these problems without changing the
AppendRelInfo data structures, but I'm wondering if it wouldn't be a good
idea to redefine them or at least add more fields to make it easier to
work with multi-level appendrel ancestry.

            regards, tom lane

Re: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
Michael Paquier
Date:
On Sat, Sep 27, 2014 at 1:57 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

> We could probably hack solutions to these problems without changing the
> AppendRelInfo data structures, but I'm wondering if it wouldn't be a good
> idea to redefine them or at least add more fields to make it easier to
> work with multi-level appendrel ancestry.
>
Hm. Something like oldest_parent_reltype and oldest_parent_relid when
defining it? IMHO it would be nice to avoid simple hacks if possible but is
changing AppendRelInfo really something back-patchable at this point?
--
Michael

Re: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
Tom Lane
Date:
Michael Paquier <michael.paquier@gmail.com> writes:
> On Sat, Sep 27, 2014 at 1:57 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> We could probably hack solutions to these problems without changing the
>> AppendRelInfo data structures, but I'm wondering if it wouldn't be a good
>> idea to redefine them or at least add more fields to make it easier to
>> work with multi-level appendrel ancestry.

> Hm. Something like oldest_parent_reltype and oldest_parent_relid when
> defining it? IMHO it would be nice to avoid simple hacks if possible but is
> changing AppendRelInfo really something back-patchable at this point?

It seems unlikely that any extensions are creating these structs for
themselves.  So I think we could safely add fields at the end in back
branches.  We've done that before in other planner structs.

            regards, tom lane

Re: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
Michael Paquier
Date:
On Mon, Sep 29, 2014 at 11:06 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
>
> Michael Paquier <michael.paquier@gmail.com> writes:
> > On Sat, Sep 27, 2014 at 1:57 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> >> We could probably hack solutions to these problems without changing the
> >> AppendRelInfo data structures, but I'm wondering if it wouldn't be a good
> >> idea to redefine them or at least add more fields to make it easier to
> >> work with multi-level appendrel ancestry.
>
> > Hm. Something like oldest_parent_reltype and oldest_parent_relid when
> > defining it? IMHO it would be nice to avoid simple hacks if possible but is
> > changing AppendRelInfo really something back-patchable at this point?
>
> It seems unlikely that any extensions are creating these structs for
> themselves.  So I think we could safely add fields at the end in back
> branches.  We've done that before in other planner structs.


OK. So I have been able to put my head into that and using the
multiple tips given out by Tom I have finished with the patch
attached. The fix done is rather simple: when creating a AppendRelInfo
entry for a join (pull_up_union_leaf_queries) or a union
(expand_inherited_rtentry), I added a new field in AppendRelInfo to
track the top-level parent, value that is afterwards used in
generate_implied_equalities_for_column when a child relation is
checked.

Patch contains as well a regression test with UNION ALL and a join.
Also, I imagine that the new functions I introduced would make more
sense in a different file, and that perhaps the new variables are not
correctly named, if you have any thoughts just let me know and I'll
update the fix. Regression tests are passing, but it may be possible
that some other code paths are broken, in this case I guess that we
could improve the fix by covering those other code paths as well with
additional regression tests. Also, I think that it would make sense to
track as well the composite type Oid of the top-level parent, I have
just not added it yet because it did not seem necessary for this
particular patch, just let me know your thoughts on the matter.

Note that this patch fixes as well the test case reported here:
http://www.postgresql.org/message-id/2326379.AOuSqtNClj@klinga.prans.org
Regards,
--
Michael

Attachment

Re: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
Michael Paquier
Date:
On Tue, Sep 30, 2014 at 5:04 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:
> OK. So I have been able to put my head into that and using the
> multiple tips given out by Tom I have finished with the patch
> attached.
Attached is an improved version of this patch with better names for
the new functions and variables. I added more comments at the same
time.
Regards,
--
Michael

Attachment

Re: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
Tom Lane
Date:
Michael Paquier <michael.paquier@gmail.com> writes:
> Attached is an improved version of this patch with better names for
> the new functions and variables. I added more comments at the same
> time.

After studying this a bit more, I've gotten disillusioned with the
idea of precalculating the top_parent_relid for an AppendRelInfo.

1. It's too complicated to maintain.  Your patch misses out doing
something when expand_inherited_rtentry() creates a child rel of
a rel that's already somebody else's child, and much harder to fix,
it misses out doing something when pull_up_simple_subquery() merges
a child query's append_rel_list with the parent query's.  That
action could well result in some of the pulled-up child AppendRelInfos
now being children of pre-existing parent AppendRelInfos.
These things could probably be fixed, but:

2. There's no evidence that we actually have a performance problem we
need to fix in this area.  Multilevel parentage is pretty rare, else
we'd have noticed problems here before.  By the time we fix #1, we could
easily waste more cycles maintaining the data structure than we save.

3. Keeping the topmost parent relid only helps in some places anyhow.
In particular, in generate_join_implied_equalities_broken, we really
have to apply all the translation steps down from the top rel.  (It
took me several hours of fooling around to generate a test case for
this ... but the attached patch includes a new regression test file
that exercises that code, and it shows a failure both with HEAD and
with your patch.)

4. Also, in generate_implied_equalities_for_column and
check_partial_indexes, it seems prudent to me to exclude all appendrel
parents of the child we're considering, not only the topmost.  This is
probably moot at the moment but it might not be so forever, in particular
if we ever get around to fixing the problem that sub-SELECTs containing
WHERE clauses can't be pulled up as appendrels.  (That'd result in WHERE
clauses associated with intermediate-level appendrels, and I think that
might lead us to generate bogus join paths in the same way as the current
problem does.)

Accordingly, I propose a patch more like the attached.  This doesn't
try to change the data structures, but it does take the viewpoint that
all current callers of find_childrel_appendrelinfo() need to be fixed
to explicitly consider multiple levels of parent appendrels.

            regards, tom lane

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index b7aff37..e5dd58e 100644
*** a/src/backend/optimizer/path/equivclass.c
--- b/src/backend/optimizer/path/equivclass.c
*************** static List *generate_join_implied_equal
*** 48,54 ****
                                          Relids nominal_join_relids,
                                          Relids outer_relids,
                                          Relids nominal_inner_relids,
!                                         AppendRelInfo *inner_appinfo);
  static Oid select_equality_operator(EquivalenceClass *ec,
                           Oid lefttype, Oid righttype);
  static RestrictInfo *create_join_clause(PlannerInfo *root,
--- 48,54 ----
                                          Relids nominal_join_relids,
                                          Relids outer_relids,
                                          Relids nominal_inner_relids,
!                                         RelOptInfo *inner_rel);
  static Oid select_equality_operator(EquivalenceClass *ec,
                           Oid lefttype, Oid righttype);
  static RestrictInfo *create_join_clause(PlannerInfo *root,
*************** generate_join_implied_equalities(Planner
*** 1000,1021 ****
      Relids        inner_relids = inner_rel->relids;
      Relids        nominal_inner_relids;
      Relids        nominal_join_relids;
-     AppendRelInfo *inner_appinfo;
      ListCell   *lc;

      /* If inner rel is a child, extra setup work is needed */
      if (inner_rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
      {
!         /* Lookup parent->child translation data */
!         inner_appinfo = find_childrel_appendrelinfo(root, inner_rel);
!         /* Construct relids for the parent rel */
!         nominal_inner_relids = bms_make_singleton(inner_appinfo->parent_relid);
          /* ECs will be marked with the parent's relid, not the child's */
          nominal_join_relids = bms_union(outer_relids, nominal_inner_relids);
      }
      else
      {
-         inner_appinfo = NULL;
          nominal_inner_relids = inner_relids;
          nominal_join_relids = join_relids;
      }
--- 1000,1017 ----
      Relids        inner_relids = inner_rel->relids;
      Relids        nominal_inner_relids;
      Relids        nominal_join_relids;
      ListCell   *lc;

      /* If inner rel is a child, extra setup work is needed */
      if (inner_rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
      {
!         /* Fetch relid set for the topmost parent rel */
!         nominal_inner_relids = find_childrel_top_parent(root, inner_rel)->relids;
          /* ECs will be marked with the parent's relid, not the child's */
          nominal_join_relids = bms_union(outer_relids, nominal_inner_relids);
      }
      else
      {
          nominal_inner_relids = inner_relids;
          nominal_join_relids = join_relids;
      }
*************** generate_join_implied_equalities(Planner
*** 1051,1057 ****
                                                           nominal_join_relids,
                                                                outer_relids,
                                                          nominal_inner_relids,
!                                                               inner_appinfo);

          result = list_concat(result, sublist);
      }
--- 1047,1053 ----
                                                           nominal_join_relids,
                                                                outer_relids,
                                                          nominal_inner_relids,
!                                                               inner_rel);

          result = list_concat(result, sublist);
      }
*************** generate_join_implied_equalities_broken(
*** 1244,1250 ****
                                          Relids nominal_join_relids,
                                          Relids outer_relids,
                                          Relids nominal_inner_relids,
!                                         AppendRelInfo *inner_appinfo)
  {
      List       *result = NIL;
      ListCell   *lc;
--- 1240,1246 ----
                                          Relids nominal_join_relids,
                                          Relids outer_relids,
                                          Relids nominal_inner_relids,
!                                         RelOptInfo *inner_rel)
  {
      List       *result = NIL;
      ListCell   *lc;
*************** generate_join_implied_equalities_broken(
*** 1266,1275 ****
       * RestrictInfos that are not listed in ec_derives, but there shouldn't be
       * any duplication, and it's a sufficiently narrow corner case that we
       * shouldn't sweat too much over it anyway.
       */
!     if (inner_appinfo)
!         result = (List *) adjust_appendrel_attrs(root, (Node *) result,
!                                                  inner_appinfo);

      return result;
  }
--- 1262,1277 ----
       * RestrictInfos that are not listed in ec_derives, but there shouldn't be
       * any duplication, and it's a sufficiently narrow corner case that we
       * shouldn't sweat too much over it anyway.
+      *
+      * Since inner_rel might be an indirect descendant of the baserel
+      * mentioned in the ec_sources clauses, we have to be prepared to apply
+      * multiple levels of Var translation.
       */
!     if (inner_rel->reloptkind == RELOPT_OTHER_MEMBER_REL &&
!         result != NIL)
!         result = (List *) adjust_appendrel_attrs_multilevel(root,
!                                                             (Node *) result,
!                                                             inner_rel);

      return result;
  }
*************** generate_implied_equalities_for_column(P
*** 2071,2084 ****
  {
      List       *result = NIL;
      bool        is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
!     Index        parent_relid;
      ListCell   *lc1;

!     /* If it's a child rel, we'll need to know what its parent is */
      if (is_child_rel)
!         parent_relid = find_childrel_appendrelinfo(root, rel)->parent_relid;
      else
!         parent_relid = 0;        /* not used, but keep compiler quiet */

      foreach(lc1, root->eq_classes)
      {
--- 2073,2086 ----
  {
      List       *result = NIL;
      bool        is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
!     Relids        parent_relids;
      ListCell   *lc1;

!     /* If it's a child rel, we'll need to know what its parent(s) are */
      if (is_child_rel)
!         parent_relids = find_childrel_parents(root, rel);
      else
!         parent_relids = NULL;    /* not used, but keep compiler quiet */

      foreach(lc1, root->eq_classes)
      {
*************** generate_implied_equalities_for_column(P
*** 2148,2157 ****

              /*
               * Also, if this is a child rel, avoid generating a useless join
!              * to its parent rel.
               */
              if (is_child_rel &&
!                 bms_is_member(parent_relid, other_em->em_relids))
                  continue;

              eq_op = select_equality_operator(cur_ec,
--- 2150,2159 ----

              /*
               * Also, if this is a child rel, avoid generating a useless join
!              * to its parent rel(s).
               */
              if (is_child_rel &&
!                 bms_overlap(parent_relids, other_em->em_relids))
                  continue;

              eq_op = select_equality_operator(cur_ec,
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 42dcb11..9c22d31 100644
*** a/src/backend/optimizer/path/indxpath.c
--- b/src/backend/optimizer/path/indxpath.c
*************** check_partial_indexes(PlannerInfo *root,
*** 2586,2601 ****
       * Add on any equivalence-derivable join clauses.  Computing the correct
       * relid sets for generate_join_implied_equalities is slightly tricky
       * because the rel could be a child rel rather than a true baserel, and in
!      * that case we must remove its parent's relid from all_baserels.
       */
      if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
-     {
-         /* Lookup parent->child translation data */
-         AppendRelInfo *appinfo = find_childrel_appendrelinfo(root, rel);
-
          otherrels = bms_difference(root->all_baserels,
!                                    bms_make_singleton(appinfo->parent_relid));
!     }
      else
          otherrels = bms_difference(root->all_baserels, rel->relids);

--- 2586,2596 ----
       * Add on any equivalence-derivable join clauses.  Computing the correct
       * relid sets for generate_join_implied_equalities is slightly tricky
       * because the rel could be a child rel rather than a true baserel, and in
!      * that case we must remove its parents' relid(s) from all_baserels.
       */
      if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
          otherrels = bms_difference(root->all_baserels,
!                                    find_childrel_parents(root, rel));
      else
          otherrels = bms_difference(root->all_baserels, rel->relids);

diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 0410fdd..58d5333 100644
*** a/src/backend/optimizer/prep/prepunion.c
--- b/src/backend/optimizer/prep/prepunion.c
*************** adjust_inherited_tlist(List *tlist, Appe
*** 1979,1981 ****
--- 1979,2002 ----

      return new_tlist;
  }
+
+ /*
+  * adjust_appendrel_attrs_multilevel
+  *      Apply Var translations from a toplevel appendrel parent down to a child.
+  *
+  * In some cases we need to translate expressions referencing a baserel
+  * to reference an appendrel child that's multiple levels removed from it.
+  */
+ Node *
+ adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
+                                   RelOptInfo *child_rel)
+ {
+     AppendRelInfo *appinfo = find_childrel_appendrelinfo(root, child_rel);
+     RelOptInfo *parent_rel = find_base_rel(root, appinfo->parent_relid);
+
+     /* If parent is also a child, first recurse to apply its translations */
+     if (parent_rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
+         node = adjust_appendrel_attrs_multilevel(root, node, parent_rel);
+     /* Now translate for this child */
+     return adjust_appendrel_attrs(root, node, appinfo);
+ }
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index c938c27..2594827 100644
*** a/src/backend/optimizer/util/relnode.c
--- b/src/backend/optimizer/util/relnode.c
*************** build_empty_join_rel(PlannerInfo *root)
*** 713,719 ****
   *        Get the AppendRelInfo associated with an appendrel child rel.
   *
   * This search could be eliminated by storing a link in child RelOptInfos,
!  * but for now it doesn't seem performance-critical.
   */
  AppendRelInfo *
  find_childrel_appendrelinfo(PlannerInfo *root, RelOptInfo *rel)
--- 713,720 ----
   *        Get the AppendRelInfo associated with an appendrel child rel.
   *
   * This search could be eliminated by storing a link in child RelOptInfos,
!  * but for now it doesn't seem performance-critical.  (Also, it might be
!  * difficult to maintain such a link during mutation of the append_rel_list.)
   */
  AppendRelInfo *
  find_childrel_appendrelinfo(PlannerInfo *root, RelOptInfo *rel)
*************** find_childrel_appendrelinfo(PlannerInfo
*** 738,743 ****
--- 739,796 ----


  /*
+  * find_childrel_top_parent
+  *        Fetch the topmost appendrel parent rel of an appendrel child rel.
+  *
+  * Since appendrels can be nested, a child could have multiple levels of
+  * appendrel ancestors.  This function locates the topmost ancestor,
+  * which will be a regular baserel not an otherrel.
+  */
+ RelOptInfo *
+ find_childrel_top_parent(PlannerInfo *root, RelOptInfo *rel)
+ {
+     do
+     {
+         AppendRelInfo *appinfo = find_childrel_appendrelinfo(root, rel);
+         Index        prelid = appinfo->parent_relid;
+
+         /* traverse up to the parent rel, loop if it's also a child rel */
+         rel = find_base_rel(root, prelid);
+     } while (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
+
+     return rel;
+ }
+
+
+ /*
+  * find_childrel_parents
+  *        Compute the set of parent relids of an appendrel child rel.
+  *
+  * Since appendrels can be nested, a child could have multiple levels of
+  * appendrel ancestors.  This function computes a Relids set of all the
+  * parent relation IDs.
+  */
+ Relids
+ find_childrel_parents(PlannerInfo *root, RelOptInfo *rel)
+ {
+     Relids        result = NULL;
+
+     do
+     {
+         AppendRelInfo *appinfo = find_childrel_appendrelinfo(root, rel);
+         Index        prelid = appinfo->parent_relid;
+
+         result = bms_add_member(result, prelid);
+
+         /* traverse up to the parent rel, loop if it's also a child rel */
+         rel = find_base_rel(root, prelid);
+     } while (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
+
+     return result;
+ }
+
+
+ /*
   * get_baserel_parampathinfo
   *        Get the ParamPathInfo for a parameterized path for a base relation,
   *        constructing one if we don't have one already.
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index a0bcc82..26b17f5 100644
*** a/src/include/optimizer/pathnode.h
--- b/src/include/optimizer/pathnode.h
*************** extern RelOptInfo *build_join_rel(Planne
*** 145,150 ****
--- 145,152 ----
  extern RelOptInfo *build_empty_join_rel(PlannerInfo *root);
  extern AppendRelInfo *find_childrel_appendrelinfo(PlannerInfo *root,
                              RelOptInfo *rel);
+ extern RelOptInfo *find_childrel_top_parent(PlannerInfo *root, RelOptInfo *rel);
+ extern Relids find_childrel_parents(PlannerInfo *root, RelOptInfo *rel);
  extern ParamPathInfo *get_baserel_parampathinfo(PlannerInfo *root,
                            RelOptInfo *baserel,
                            Relids required_outer);
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
index f5fc7e8..1891f4d 100644
*** a/src/include/optimizer/prep.h
--- b/src/include/optimizer/prep.h
*************** extern void expand_inherited_tables(Plan
*** 58,61 ****
--- 58,64 ----
  extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,
                         AppendRelInfo *appinfo);

+ extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
+                                   RelOptInfo *child_rel);
+
  #endif   /* PREP_H */
diff --git a/src/test/regress/expected/equivclass.out b/src/test/regress/expected/equivclass.out
index ...b882242 .
*** a/src/test/regress/expected/equivclass.out
--- b/src/test/regress/expected/equivclass.out
***************
*** 0 ****
--- 1,296 ----
+ --
+ -- Tests for the planner's "equivalence class" mechanism
+ --
+ -- One thing that's not tested well during normal querying is the logic
+ -- for handling "broken" ECs.  This is because an EC can only become broken
+ -- if its underlying btree operator family doesn't include a complete set
+ -- of cross-type equality operators.  There are not (and should not be)
+ -- any such families built into Postgres; so we have to hack things up
+ -- to create one.  We do this by making two alias types that are really
+ -- int8 (so we need no new C code) and adding only some operators for them
+ -- into the standard integer_ops opfamily.
+ create type int8alias1;
+ create function int8alias1in(cstring) returns int8alias1
+   strict immutable language internal as 'int8in';
+ NOTICE:  return type int8alias1 is only a shell
+ create function int8alias1out(int8alias1) returns cstring
+   strict immutable language internal as 'int8out';
+ NOTICE:  argument type int8alias1 is only a shell
+ create type int8alias1 (
+     input = int8alias1in,
+     output = int8alias1out,
+     like = int8
+ );
+ create type int8alias2;
+ create function int8alias2in(cstring) returns int8alias2
+   strict immutable language internal as 'int8in';
+ NOTICE:  return type int8alias2 is only a shell
+ create function int8alias2out(int8alias2) returns cstring
+   strict immutable language internal as 'int8out';
+ NOTICE:  argument type int8alias2 is only a shell
+ create type int8alias2 (
+     input = int8alias2in,
+     output = int8alias2out,
+     like = int8
+ );
+ create cast (int8 as int8alias1) without function;
+ create cast (int8 as int8alias2) without function;
+ create cast (int8alias1 as int8) without function;
+ create cast (int8alias2 as int8) without function;
+ create function int8alias1eq(int8alias1, int8alias1) returns bool
+   strict immutable language internal as 'int8eq';
+ create operator = (
+     procedure = int8alias1eq,
+     leftarg = int8alias1, rightarg = int8alias1,
+     commutator = =,
+     restrict = eqsel, join = eqjoinsel,
+     merges
+ );
+ alter operator family integer_ops using btree add
+   operator 3 = (int8alias1, int8alias1);
+ create function int8alias2eq(int8alias2, int8alias2) returns bool
+   strict immutable language internal as 'int8eq';
+ create operator = (
+     procedure = int8alias2eq,
+     leftarg = int8alias2, rightarg = int8alias2,
+     commutator = =,
+     restrict = eqsel, join = eqjoinsel,
+     merges
+ );
+ alter operator family integer_ops using btree add
+   operator 3 = (int8alias2, int8alias2);
+ create function int8alias1eq(int8, int8alias1) returns bool
+   strict immutable language internal as 'int8eq';
+ create operator = (
+     procedure = int8alias1eq,
+     leftarg = int8, rightarg = int8alias1,
+     restrict = eqsel, join = eqjoinsel,
+     merges
+ );
+ alter operator family integer_ops using btree add
+   operator 3 = (int8, int8alias1);
+ create function int8alias1eq(int8alias1, int8alias2) returns bool
+   strict immutable language internal as 'int8eq';
+ create operator = (
+     procedure = int8alias1eq,
+     leftarg = int8alias1, rightarg = int8alias2,
+     restrict = eqsel, join = eqjoinsel,
+     merges
+ );
+ alter operator family integer_ops using btree add
+   operator 3 = (int8alias1, int8alias2);
+ create table ec0 (ff int8 primary key, f1 int8, f2 int8);
+ create table ec1 (ff int8 primary key, f1 int8alias1, f2 int8alias2);
+ create table ec2 (xf int8 primary key, x1 int8alias1, x2 int8alias2);
+ -- we didn't provide enough infrastructure for hashjoin and mergejoin plans
+ set enable_hashjoin = off;
+ set enable_mergejoin = off;
+ --
+ -- Note that for cases where there's a missing operator, we don't care so
+ -- much whether the plan is ideal as that we don't fail or generate an
+ -- outright incorrect plan.
+ --
+ explain (costs off)
+   select * from ec0 where ff = f1 and f1 = '42'::int8;
+             QUERY PLAN
+ ----------------------------------
+  Index Scan using ec0_pkey on ec0
+    Index Cond: (ff = 42::bigint)
+    Filter: (f1 = 42::bigint)
+ (3 rows)
+
+ explain (costs off)
+   select * from ec0 where ff = f1 and f1 = '42'::int8alias1;
+               QUERY PLAN
+ ---------------------------------------
+  Index Scan using ec0_pkey on ec0
+    Index Cond: (ff = '42'::int8alias1)
+    Filter: (f1 = '42'::int8alias1)
+ (3 rows)
+
+ explain (costs off)
+   select * from ec1 where ff = f1 and f1 = '42'::int8alias1;
+               QUERY PLAN
+ ---------------------------------------
+  Index Scan using ec1_pkey on ec1
+    Index Cond: (ff = '42'::int8alias1)
+    Filter: (f1 = '42'::int8alias1)
+ (3 rows)
+
+ explain (costs off)
+   select * from ec1 where ff = f1 and f1 = '42'::int8alias2;
+                     QUERY PLAN
+ ---------------------------------------------------
+  Seq Scan on ec1
+    Filter: ((ff = f1) AND (f1 = '42'::int8alias2))
+ (2 rows)
+
+ explain (costs off)
+   select * from ec1, ec2 where ff = x1 and ff = '42'::int8;
+                           QUERY PLAN
+ ---------------------------------------------------------------
+  Nested Loop
+    Join Filter: (ec1.ff = ec2.x1)
+    ->  Index Scan using ec1_pkey on ec1
+          Index Cond: ((ff = 42::bigint) AND (ff = 42::bigint))
+    ->  Seq Scan on ec2
+ (5 rows)
+
+ explain (costs off)
+   select * from ec1, ec2 where ff = x1 and ff = '42'::int8alias1;
+                  QUERY PLAN
+ ---------------------------------------------
+  Nested Loop
+    ->  Index Scan using ec1_pkey on ec1
+          Index Cond: (ff = '42'::int8alias1)
+    ->  Seq Scan on ec2
+          Filter: (x1 = '42'::int8alias1)
+ (5 rows)
+
+ explain (costs off)
+   select * from ec1, ec2 where ff = x1 and '42'::int8 = x1;
+                QUERY PLAN
+ ----------------------------------------
+  Nested Loop
+    Join Filter: (ec1.ff = ec2.x1)
+    ->  Index Scan using ec1_pkey on ec1
+          Index Cond: (ff = 42::bigint)
+    ->  Seq Scan on ec2
+          Filter: (42::bigint = x1)
+ (6 rows)
+
+ explain (costs off)
+   select * from ec1, ec2 where ff = x1 and x1 = '42'::int8alias1;
+                  QUERY PLAN
+ ---------------------------------------------
+  Nested Loop
+    ->  Index Scan using ec1_pkey on ec1
+          Index Cond: (ff = '42'::int8alias1)
+    ->  Seq Scan on ec2
+          Filter: (x1 = '42'::int8alias1)
+ (5 rows)
+
+ explain (costs off)
+   select * from ec1, ec2 where ff = x1 and x1 = '42'::int8alias2;
+                QUERY PLAN
+ -----------------------------------------
+  Nested Loop
+    ->  Seq Scan on ec2
+          Filter: (x1 = '42'::int8alias2)
+    ->  Index Scan using ec1_pkey on ec1
+          Index Cond: (ff = ec2.x1)
+ (5 rows)
+
+ create unique index ec1_expr1 on ec1((ff + 1));
+ create unique index ec1_expr2 on ec1((ff + 2 + 1));
+ create unique index ec1_expr3 on ec1((ff + 3 + 1));
+ create unique index ec1_expr4 on ec1((ff + 4));
+ explain (costs off)
+   select * from ec1,
+     (select ff + 1 as x from
+        (select ff + 2 as ff from ec1
+         union all
+         select ff + 3 as ff from ec1) ss0
+      union all
+      select ff + 4 as x from ec1) as ss1
+   where ss1.x = ec1.f1 and ec1.ff = 42::int8;
+                      QUERY PLAN
+ -----------------------------------------------------
+  Nested Loop
+    ->  Index Scan using ec1_pkey on ec1
+          Index Cond: (ff = 42::bigint)
+    ->  Append
+          ->  Index Scan using ec1_expr2 on ec1 ec1_1
+                Index Cond: (((ff + 2) + 1) = ec1.f1)
+          ->  Index Scan using ec1_expr3 on ec1 ec1_2
+                Index Cond: (((ff + 3) + 1) = ec1.f1)
+          ->  Index Scan using ec1_expr4 on ec1 ec1_3
+                Index Cond: ((ff + 4) = ec1.f1)
+ (10 rows)
+
+ explain (costs off)
+   select * from ec1,
+     (select ff + 1 as x from
+        (select ff + 2 as ff from ec1
+         union all
+         select ff + 3 as ff from ec1) ss0
+      union all
+      select ff + 4 as x from ec1) as ss1
+   where ss1.x = ec1.f1 and ec1.ff = 42::int8 and ec1.ff = ec1.f1;
+                           QUERY PLAN
+ ---------------------------------------------------------------
+  Nested Loop
+    Join Filter: ((((ec1_1.ff + 2) + 1)) = ec1.f1)
+    ->  Index Scan using ec1_pkey on ec1
+          Index Cond: ((ff = 42::bigint) AND (ff = 42::bigint))
+          Filter: (ff = f1)
+    ->  Append
+          ->  Index Scan using ec1_expr2 on ec1 ec1_1
+                Index Cond: (((ff + 2) + 1) = 42::bigint)
+          ->  Index Scan using ec1_expr3 on ec1 ec1_2
+                Index Cond: (((ff + 3) + 1) = 42::bigint)
+          ->  Index Scan using ec1_expr4 on ec1 ec1_3
+                Index Cond: ((ff + 4) = 42::bigint)
+ (12 rows)
+
+ explain (costs off)
+   select * from ec1,
+     (select ff + 1 as x from
+        (select ff + 2 as ff from ec1
+         union all
+         select ff + 3 as ff from ec1) ss0
+      union all
+      select ff + 4 as x from ec1) as ss1,
+     (select ff + 1 as x from
+        (select ff + 2 as ff from ec1
+         union all
+         select ff + 3 as ff from ec1) ss0
+      union all
+      select ff + 4 as x from ec1) as ss2
+   where ss1.x = ec1.f1 and ss1.x = ss2.x and ec1.ff = 42::int8;
+                              QUERY PLAN
+ ---------------------------------------------------------------------
+  Nested Loop
+    ->  Nested Loop
+          ->  Index Scan using ec1_pkey on ec1
+                Index Cond: (ff = 42::bigint)
+          ->  Append
+                ->  Index Scan using ec1_expr2 on ec1 ec1_1
+                      Index Cond: (((ff + 2) + 1) = ec1.f1)
+                ->  Index Scan using ec1_expr3 on ec1 ec1_2
+                      Index Cond: (((ff + 3) + 1) = ec1.f1)
+                ->  Index Scan using ec1_expr4 on ec1 ec1_3
+                      Index Cond: ((ff + 4) = ec1.f1)
+    ->  Append
+          ->  Index Scan using ec1_expr2 on ec1 ec1_4
+                Index Cond: (((ff + 2) + 1) = (((ec1_1.ff + 2) + 1)))
+          ->  Index Scan using ec1_expr3 on ec1 ec1_5
+                Index Cond: (((ff + 3) + 1) = (((ec1_1.ff + 2) + 1)))
+          ->  Index Scan using ec1_expr4 on ec1 ec1_6
+                Index Cond: ((ff + 4) = (((ec1_1.ff + 2) + 1)))
+ (18 rows)
+
+ drop index ec1_expr3;
+ explain (costs off)
+   select * from ec1,
+     (select ff + 1 as x from
+        (select ff + 2 as ff from ec1
+         union all
+         select ff + 3 as ff from ec1) ss0
+      union all
+      select ff + 4 as x from ec1) as ss1
+   where ss1.x = ec1.f1 and ec1.ff = 42::int8;
+                      QUERY PLAN
+ -----------------------------------------------------
+  Nested Loop
+    ->  Index Scan using ec1_pkey on ec1
+          Index Cond: (ff = 42::bigint)
+    ->  Append
+          ->  Index Scan using ec1_expr2 on ec1 ec1_1
+                Index Cond: (((ff + 2) + 1) = ec1.f1)
+          ->  Seq Scan on ec1 ec1_2
+                Filter: (((ff + 3) + 1) = ec1.f1)
+          ->  Index Scan using ec1_expr4 on ec1 ec1_3
+                Index Cond: ((ff + 4) = ec1.f1)
+ (10 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index ab6c4e2..9902dbe 100644
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
*************** test: event_trigger
*** 98,104 ****
  # ----------
  # Another group of parallel tests
  # ----------
! test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data
windowxmlmap functional_deps advisory_lock json jsonb indirect_toast 
  # ----------
  # Another group of parallel tests
  # NB: temp.sql does a reconnect which transiently uses 2 connections,
--- 98,104 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data
windowxmlmap functional_deps advisory_lock json jsonb indirect_toast equivclass 
  # ----------
  # Another group of parallel tests
  # NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 5ed2bf0..2902a05 100644
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
*************** test: advisory_lock
*** 126,131 ****
--- 126,132 ----
  test: json
  test: jsonb
  test: indirect_toast
+ test: equivclass
  test: plancache
  test: limit
  test: plpgsql
diff --git a/src/test/regress/sql/equivclass.sql b/src/test/regress/sql/equivclass.sql
index ...fc775a5 .
*** a/src/test/regress/sql/equivclass.sql
--- b/src/test/regress/sql/equivclass.sql
***************
*** 0 ****
--- 1,172 ----
+ --
+ -- Tests for the planner's "equivalence class" mechanism
+ --
+
+ -- One thing that's not tested well during normal querying is the logic
+ -- for handling "broken" ECs.  This is because an EC can only become broken
+ -- if its underlying btree operator family doesn't include a complete set
+ -- of cross-type equality operators.  There are not (and should not be)
+ -- any such families built into Postgres; so we have to hack things up
+ -- to create one.  We do this by making two alias types that are really
+ -- int8 (so we need no new C code) and adding only some operators for them
+ -- into the standard integer_ops opfamily.
+
+ create type int8alias1;
+ create function int8alias1in(cstring) returns int8alias1
+   strict immutable language internal as 'int8in';
+ create function int8alias1out(int8alias1) returns cstring
+   strict immutable language internal as 'int8out';
+ create type int8alias1 (
+     input = int8alias1in,
+     output = int8alias1out,
+     like = int8
+ );
+
+ create type int8alias2;
+ create function int8alias2in(cstring) returns int8alias2
+   strict immutable language internal as 'int8in';
+ create function int8alias2out(int8alias2) returns cstring
+   strict immutable language internal as 'int8out';
+ create type int8alias2 (
+     input = int8alias2in,
+     output = int8alias2out,
+     like = int8
+ );
+
+ create cast (int8 as int8alias1) without function;
+ create cast (int8 as int8alias2) without function;
+ create cast (int8alias1 as int8) without function;
+ create cast (int8alias2 as int8) without function;
+
+ create function int8alias1eq(int8alias1, int8alias1) returns bool
+   strict immutable language internal as 'int8eq';
+ create operator = (
+     procedure = int8alias1eq,
+     leftarg = int8alias1, rightarg = int8alias1,
+     commutator = =,
+     restrict = eqsel, join = eqjoinsel,
+     merges
+ );
+ alter operator family integer_ops using btree add
+   operator 3 = (int8alias1, int8alias1);
+
+ create function int8alias2eq(int8alias2, int8alias2) returns bool
+   strict immutable language internal as 'int8eq';
+ create operator = (
+     procedure = int8alias2eq,
+     leftarg = int8alias2, rightarg = int8alias2,
+     commutator = =,
+     restrict = eqsel, join = eqjoinsel,
+     merges
+ );
+ alter operator family integer_ops using btree add
+   operator 3 = (int8alias2, int8alias2);
+
+ create function int8alias1eq(int8, int8alias1) returns bool
+   strict immutable language internal as 'int8eq';
+ create operator = (
+     procedure = int8alias1eq,
+     leftarg = int8, rightarg = int8alias1,
+     restrict = eqsel, join = eqjoinsel,
+     merges
+ );
+ alter operator family integer_ops using btree add
+   operator 3 = (int8, int8alias1);
+
+ create function int8alias1eq(int8alias1, int8alias2) returns bool
+   strict immutable language internal as 'int8eq';
+ create operator = (
+     procedure = int8alias1eq,
+     leftarg = int8alias1, rightarg = int8alias2,
+     restrict = eqsel, join = eqjoinsel,
+     merges
+ );
+ alter operator family integer_ops using btree add
+   operator 3 = (int8alias1, int8alias2);
+
+ create table ec0 (ff int8 primary key, f1 int8, f2 int8);
+ create table ec1 (ff int8 primary key, f1 int8alias1, f2 int8alias2);
+ create table ec2 (xf int8 primary key, x1 int8alias1, x2 int8alias2);
+
+ -- we didn't provide enough infrastructure for hashjoin and mergejoin plans
+ set enable_hashjoin = off;
+ set enable_mergejoin = off;
+
+ --
+ -- Note that for cases where there's a missing operator, we don't care so
+ -- much whether the plan is ideal as that we don't fail or generate an
+ -- outright incorrect plan.
+ --
+
+ explain (costs off)
+   select * from ec0 where ff = f1 and f1 = '42'::int8;
+ explain (costs off)
+   select * from ec0 where ff = f1 and f1 = '42'::int8alias1;
+ explain (costs off)
+   select * from ec1 where ff = f1 and f1 = '42'::int8alias1;
+ explain (costs off)
+   select * from ec1 where ff = f1 and f1 = '42'::int8alias2;
+
+ explain (costs off)
+   select * from ec1, ec2 where ff = x1 and ff = '42'::int8;
+ explain (costs off)
+   select * from ec1, ec2 where ff = x1 and ff = '42'::int8alias1;
+ explain (costs off)
+   select * from ec1, ec2 where ff = x1 and '42'::int8 = x1;
+ explain (costs off)
+   select * from ec1, ec2 where ff = x1 and x1 = '42'::int8alias1;
+ explain (costs off)
+   select * from ec1, ec2 where ff = x1 and x1 = '42'::int8alias2;
+
+ create unique index ec1_expr1 on ec1((ff + 1));
+ create unique index ec1_expr2 on ec1((ff + 2 + 1));
+ create unique index ec1_expr3 on ec1((ff + 3 + 1));
+ create unique index ec1_expr4 on ec1((ff + 4));
+
+ explain (costs off)
+   select * from ec1,
+     (select ff + 1 as x from
+        (select ff + 2 as ff from ec1
+         union all
+         select ff + 3 as ff from ec1) ss0
+      union all
+      select ff + 4 as x from ec1) as ss1
+   where ss1.x = ec1.f1 and ec1.ff = 42::int8;
+
+ explain (costs off)
+   select * from ec1,
+     (select ff + 1 as x from
+        (select ff + 2 as ff from ec1
+         union all
+         select ff + 3 as ff from ec1) ss0
+      union all
+      select ff + 4 as x from ec1) as ss1
+   where ss1.x = ec1.f1 and ec1.ff = 42::int8 and ec1.ff = ec1.f1;
+
+ explain (costs off)
+   select * from ec1,
+     (select ff + 1 as x from
+        (select ff + 2 as ff from ec1
+         union all
+         select ff + 3 as ff from ec1) ss0
+      union all
+      select ff + 4 as x from ec1) as ss1,
+     (select ff + 1 as x from
+        (select ff + 2 as ff from ec1
+         union all
+         select ff + 3 as ff from ec1) ss0
+      union all
+      select ff + 4 as x from ec1) as ss2
+   where ss1.x = ec1.f1 and ss1.x = ss2.x and ec1.ff = 42::int8;
+
+ drop index ec1_expr3;
+
+ explain (costs off)
+   select * from ec1,
+     (select ff + 1 as x from
+        (select ff + 2 as ff from ec1
+         union all
+         select ff + 3 as ff from ec1) ss0
+      union all
+      select ff + 4 as x from ec1) as ss1
+   where ss1.x = ec1.f1 and ec1.ff = 42::int8;

Re: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
Michael Paquier
Date:
On Wed, Oct 1, 2014 at 2:43 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

> Accordingly, I propose a patch more like the attached.  This doesn't
> try to change the data structures, but it does take the viewpoint that
> all current callers of find_childrel_appendrelinfo() need to be fixed
> to explicitly consider multiple levels of parent appendrels.
>
This approach is far more solid than my previous stuff and makes the
approach I took really brittle... Looking at your code, I am wondering if
it would not be safer to add some assertions in find_childrel_top_parent
and find_childrel_parents, something like a check on RELOPT_BASEREL for
rel->reloptkind before returning a result. Er, those new functions should
always finishing by scanning a base rel, right?

Btw, this code will need minor adjustments for REL9_2_STABLE and
REL9_3_STABLE. Is a backpatch down to 9.1 to be considered?
--
Michael

Re: BUG #11457: The below query crashes 9.3.5, but not 9.3.4

From
Tom Lane
Date:
Michael Paquier <michael.paquier@gmail.com> writes:
> This approach is far more solid than my previous stuff and makes the
> approach I took really brittle...

Hey, the idea to add a field was mine, so don't beat yourself up about
it :-).  Sometimes it takes working out an idea to realize it's bad.

> Looking at your code, I am wondering if
> it would not be safer to add some assertions in find_childrel_top_parent
> and find_childrel_parents, something like a check on RELOPT_BASEREL for
> rel->reloptkind before returning a result. Er, those new functions should
> always finishing by scanning a base rel, right?

Yeah, not a bad idea.

> Btw, this code will need minor adjustments for REL9_2_STABLE and
> REL9_3_STABLE. Is a backpatch down to 9.1 to be considered?

The bugs we're working on here are definitely demonstrable back to 9.2.
I'm not sure about 9.1 yet; but considering that a87c72915 had to get
back-patched as far as 9.1, there may well be some manifestations of
these problems visible in 9.1.  (The new regression test that I wrote
doesn't seem to show any big problems there, but maybe I'm just short
a case or two.)

            regards, tom lane