Re: could not devise a query plan - Mailing list pgsql-sql

From Tom Lane
Subject Re: could not devise a query plan
Date
Msg-id 334.1081278333@sss.pgh.pa.us
Whole thread Raw
In response to could not devise a query plan  ("SZŰCS Gábor" <surrano@mailbox.hu>)
List pgsql-sql
"SZŰCS Gábor" <surrano@mailbox.hu> writes:
> I couldn't find the string of my email's subject on the web, except for one
> place: the PostgreSQL source :)

Seems that you've managed to tickle a strange corner case, which can be
reduced to simplest form like this:

regression=# select * from a full join b on true;
ERROR:  Unable to devise a query plan for the given query

In your first example, the empty join clause falls out because you don't
actually have any columns of the same names on both sides, and so the
NATURAL join doesn't find any columns to join.  AFAICS this is allowed
by the SQL spec, but still I can't help suspecting that it is
programming error on your part.  You're going to get a cross-product
join ... is that really what you intended?

The second example is slightly more interesting: it boils down to a case
like this:

select * from (select unique1 from tenk1 where unique1 = 42) afull join (select unique1 from tenk1 where unique1 = 42)
bona.unique1 = b.unique1;
 

7.4 is perhaps too smart for its own good here: it is able to figure out
that the join clause is redundant because every row coming up from the
subselects must have the same value in the join columns (here, 42).  So
it discards the join clause ... leaving it in the same situation where
it can't generate a plan :-(

Although I think the first case is really user error, the second case
looks like it could arise unexpectedly in program-generated queries
given the right combination of inputs, so we probably ought to do
something about it.  I have applied the attached patch to 7.4.
(It would probably work in 7.3 too, but no guarantees.)
        regards, tom lane

Index: costsize.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/optimizer/path/costsize.c,v
retrieving revision 1.115.2.1
diff -c -r1.115.2.1 costsize.c
*** costsize.c    3 Dec 2003 17:45:36 -0000    1.115.2.1
--- costsize.c    6 Apr 2004 18:41:17 -0000
***************
*** 928,950 ****      * all mergejoin paths associated with the merge clause, we cache the      * results in the
RestrictInfonode.      */
 
!     firstclause = (RestrictInfo *) lfirst(mergeclauses);
!     if (firstclause->left_mergescansel < 0)        /* not computed yet? */
!         mergejoinscansel(root, (Node *) firstclause->clause,
!                          &firstclause->left_mergescansel,
!                          &firstclause->right_mergescansel);
! 
!     if (bms_is_subset(firstclause->left_relids, outer_path->parent->relids))     {
!         /* left side of clause is outer */
!         outerscansel = firstclause->left_mergescansel;
!         innerscansel = firstclause->right_mergescansel;     }     else     {
!         /* left side of clause is inner */
!         outerscansel = firstclause->right_mergescansel;
!         innerscansel = firstclause->left_mergescansel;     }      /* convert selectivity to row count; must scan at
leastone row */
 
--- 928,958 ----      * all mergejoin paths associated with the merge clause, we cache the      * results in the
RestrictInfonode.      */
 
!     if (mergeclauses)     {
!         firstclause = (RestrictInfo *) lfirst(mergeclauses);
!         if (firstclause->left_mergescansel < 0)    /* not computed yet? */
!             mergejoinscansel(root, (Node *) firstclause->clause,
!                              &firstclause->left_mergescansel,
!                              &firstclause->right_mergescansel);
! 
!         if (bms_is_subset(firstclause->left_relids, outer_path->parent->relids))
!         {
!             /* left side of clause is outer */
!             outerscansel = firstclause->left_mergescansel;
!             innerscansel = firstclause->right_mergescansel;
!         }
!         else
!         {
!             /* left side of clause is inner */
!             outerscansel = firstclause->right_mergescansel;
!             innerscansel = firstclause->left_mergescansel;
!         }     }     else     {
!         /* cope with clauseless mergejoin */
!         outerscansel = innerscansel = 1.0;     }      /* convert selectivity to row count; must scan at least one row
*/
Index: joinpath.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/optimizer/path/joinpath.c,v
retrieving revision 1.82
diff -c -r1.82 joinpath.c
*** joinpath.c    25 Sep 2003 06:58:00 -0000    1.82
--- joinpath.c    6 Apr 2004 18:41:18 -0000
***************
*** 489,497 ****                                                       outerpath->pathkeys,
                         mergeclause_list); 
 
!         /* Done with this outer path if no chance for a mergejoin */         if (mergeclauses == NIL)
!             continue;         if (useallclauses && length(mergeclauses) != length(mergeclause_list))
continue;
 
--- 489,515 ----                                                       outerpath->pathkeys,
                         mergeclause_list); 
 
!         /*
!          * Done with this outer path if no chance for a mergejoin.
!          *
!          * Special corner case: for "x FULL JOIN y ON true", there will be
!          * no join clauses at all.  Ordinarily we'd generate a clauseless
!          * nestloop path, but since mergejoin is our only join type that
!          * supports FULL JOIN, it's necessary to generate a clauseless
!          * mergejoin path instead.
!          *
!          * Unfortunately this can't easily be extended to handle the case
!          * where there are joinclauses but none of them use mergejoinable
!          * operators; nodeMergejoin.c can only do a full join correctly if
!          * all the joinclauses are mergeclauses.
!          */         if (mergeclauses == NIL)
!         {
!             if (jointype == JOIN_FULL && restrictlist == NIL)
!                 /* okay to try for mergejoin */ ;
!             else
!                 continue;
!         }         if (useallclauses && length(mergeclauses) != length(mergeclause_list))             continue; 


pgsql-sql by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: SQL Standatd
Next
From: Robert Treat
Date:
Subject: Re: partial unique constraint