On Wed, Jul 8, 2009 at 4:57 PM, Tom Lane<[email protected]> wrote:
> Well, the reason I'm not voting for #3 is that it looks like a lot of
> work to implement something that would basically be a planner hint,
> which I'm generally against; furthermore, it's a hint that there's been
> no demand for. (We're not even certain that anyone is using the ability
> to *fully* specify the join order, much less wanting some undetermined
> compromise between manual and automatic control.) And anyway I didn't
> hear anyone volunteering to do it. So the realistic alternatives are
> #1, #2, or "do nothing"; and out of those I like #2.
I took a look at this and it seems that #3 can be implemented with
essentially no additional code (the handful of lines I added where
more than balanced out by some simplifications in ruleutils.c). Of
course you still don't have to like it. :-)
Patch attached.
...Robert
*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
***************
*** 2251,2313 **** SELECT * FROM parent WHERE key = 2400;
</listitem>
</varlistentry>
- <varlistentry id="guc-from-collapse-limit" xreflabel="from_collapse_limit">
- <term><varname>from_collapse_limit</varname> (<type>integer</type>)</term>
- <indexterm>
- <primary><varname>from_collapse_limit</> configuration parameter</primary>
- </indexterm>
- <listitem>
- <para>
- The planner will merge sub-queries into upper queries if the
- resulting <literal>FROM</literal> list would have no more than
- this many items. Smaller values reduce planning time but might
- yield inferior query plans. The default is eight.
- For more information see <xref linkend="explicit-joins">.
- </para>
-
- <para>
- Setting this value to <xref linkend="guc-geqo-threshold"> or more
- may trigger use of the GEQO planner, resulting in nondeterministic
- plans. See <xref linkend="runtime-config-query-geqo">.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry id="guc-join-collapse-limit" xreflabel="join_collapse_limit">
- <term><varname>join_collapse_limit</varname> (<type>integer</type>)</term>
- <indexterm>
- <primary><varname>join_collapse_limit</> configuration parameter</primary>
- </indexterm>
- <listitem>
- <para>
- The planner will rewrite explicit <literal>JOIN</>
- constructs (except <literal>FULL JOIN</>s) into lists of
- <literal>FROM</> items whenever a list of no more than this many items
- would result. Smaller values reduce planning time but might
- yield inferior query plans.
- </para>
-
- <para>
- By default, this variable is set the same as
- <varname>from_collapse_limit</varname>, which is appropriate
- for most uses. Setting it to 1 prevents any reordering of
- explicit <literal>JOIN</>s. Thus, the explicit join order
- specified in the query will be the actual order in which the
- relations are joined. The query planner does not always choose
- the optimal join order; advanced users can elect to
- temporarily set this variable to 1, and then specify the join
- order they desire explicitly.
- For more information see <xref linkend="explicit-joins">.
- </para>
-
- <para>
- Setting this value to <xref linkend="guc-geqo-threshold"> or more
- may trigger use of the GEQO planner, resulting in nondeterministic
- plans. See <xref linkend="runtime-config-query-geqo">.
- </para>
- </listitem>
- </varlistentry>
-
</variablelist>
</sect2>
</sect1>
--- 2251,2256 ----
*** a/doc/src/sgml/perform.sgml
--- b/doc/src/sgml/perform.sgml
***************
*** 599,606 **** WHERE tablename = 'road';
<para>
It is possible
! to control the query planner to some extent by using the explicit <literal>JOIN</>
! syntax. To see why this matters, we first need some background.
</para>
<para>
--- 599,607 ----
<para>
It is possible
! to control the query planner to some extent by using <literal>JOIN</>
! with the <literal>FORCE</> keyword. To see why this matters, we first need
! some background.
</para>
<para>
***************
*** 675,681 **** SELECT * FROM a LEFT JOIN b ON (a.bid = b.id) LEFT JOIN c ON (a.cid = c.id);
<para>
Even though most kinds of <literal>JOIN</> don't completely constrain
the join order, it is possible to instruct the
! <productname>PostgreSQL</productname> query planner to treat all
<literal>JOIN</> clauses as constraining the join order anyway.
For example, these three queries are logically equivalent:
<programlisting>
--- 676,682 ----
<para>
Even though most kinds of <literal>JOIN</> don't completely constrain
the join order, it is possible to instruct the
! <productname>PostgreSQL</productname> query planner to treat certain
<literal>JOIN</> clauses as constraining the join order anyway.
For example, these three queries are logically equivalent:
<programlisting>
***************
*** 683,710 **** SELECT * FROM a, b, c WHERE a.id = b.id AND b.ref = c.id;
SELECT * FROM a CROSS JOIN b CROSS JOIN c WHERE a.id = b.id AND b.ref = c.id;
SELECT * FROM a JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id);
</programlisting>
- But if we tell the planner to honor the <literal>JOIN</> order,
- the second and third take less time to plan than the first. This effect
- is not worth worrying about for only three tables, but it can be a
- lifesaver with many tables.
</para>
<para>
To force the planner to follow the join order laid out by explicit
! <literal>JOIN</>s,
! set the <xref linkend="guc-join-collapse-limit"> run-time parameter to 1.
! (Other possible values are discussed below.)
</para>
<para>
You do not need to constrain the join order completely in order to
! cut search time, because it's OK to use <literal>JOIN</> operators
! within items of a plain <literal>FROM</> list. For example, consider:
<programlisting>
! SELECT * FROM a CROSS JOIN b, c, d, e WHERE ...;
</programlisting>
! With <varname>join_collapse_limit</> = 1, this
! forces the planner to join A to B before joining them to other tables,
but doesn't constrain its choices otherwise. In this example, the
number of possible join orders is reduced by a factor of 5.
</para>
--- 684,710 ----
SELECT * FROM a CROSS JOIN b CROSS JOIN c WHERE a.id = b.id AND b.ref = c.id;
SELECT * FROM a JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id);
</programlisting>
</para>
<para>
To force the planner to follow the join order laid out by explicit
! <literal>JOIN</>s, use the <literal>FORCE</> keyword, like this:
! <programlisting>
! SELECT * FROM a INNER FORCE JOIN (b INNER FORCE JOIN c ON (b.ref = c.id)) ON (a.id = b.id);
! </programlisting>
! Because there is only one possible join order, this query will take less
! time to plan. This effect is not worth worrying about for only three
! tables, but it can be a lifesaver with many tables.
</para>
<para>
You do not need to constrain the join order completely in order to
! cut search time; <literal>FORCE</> can be specified for just some of
! the joins in a given query. For example, consider:
<programlisting>
! SELECT * FROM a INNER FORCE JOIN b ON ..., c, d, e WHERE ...;
</programlisting>
! This forces the planner to join A to B before joining them to other tables,
but doesn't constrain its choices otherwise. In this example, the
number of possible join orders is reduced by a factor of 5.
</para>
***************
*** 713,719 **** SELECT * FROM a CROSS JOIN b, c, d, e WHERE ...;
Constraining the planner's search in this way is a useful technique
both for reducing planning time and for directing the planner to a
good query plan. If the planner chooses a bad join order by default,
! you can force it to choose a better order via <literal>JOIN</> syntax
— assuming that you know of a better order, that is. Experimentation
is recommended.
</para>
--- 713,719 ----
Constraining the planner's search in this way is a useful technique
both for reducing planning time and for directing the planner to a
good query plan. If the planner chooses a bad join order by default,
! you can force it to choose a better order using <literal>FORCE</>
— assuming that you know of a better order, that is. Experimentation
is recommended.
</para>
***************
*** 729,736 **** WHERE somethingelse;
</programlisting>
This situation might arise from use of a view that contains a join;
the view's <literal>SELECT</> rule will be inserted in place of the view
! reference, yielding a query much like the above. Normally, the planner
! will try to collapse the subquery into the parent, yielding:
<programlisting>
SELECT * FROM x, y, a, b, c WHERE something AND somethingelse;
</programlisting>
--- 729,736 ----
</programlisting>
This situation might arise from use of a view that contains a join;
the view's <literal>SELECT</> rule will be inserted in place of the view
! reference, yielding a query much like the above. The planner
! will always try to collapse the subquery into the parent, yielding:
<programlisting>
SELECT * FROM x, y, a, b, c WHERE something AND somethingelse;
</programlisting>
***************
*** 741,765 **** SELECT * FROM x, y, a, b, c WHERE something AND somethingelse;
we have increased the planning time; here, we have a five-way join
problem replacing two separate three-way join problems. Because of the
exponential growth of the number of possibilities, this makes a big
! difference. The planner tries to avoid getting stuck in huge join search
! problems by not collapsing a subquery if more than <varname>from_collapse_limit</>
! <literal>FROM</> items would result in the parent
! query. You can trade off planning time against quality of plan by
! adjusting this run-time parameter up or down.
! </para>
!
! <para>
! <xref linkend="guc-from-collapse-limit"> and <xref
! linkend="guc-join-collapse-limit">
! are similarly named because they do almost the same thing: one controls
! when the planner will <quote>flatten out</> subqueries, and the
! other controls when it will flatten out explicit joins. Typically
! you would either set <varname>join_collapse_limit</> equal to
! <varname>from_collapse_limit</> (so that explicit joins and subqueries
! act similarly) or set <varname>join_collapse_limit</> to 1 (if you want
! to control join order with explicit joins). But you might set them
! differently if you are trying to fine-tune the trade-off between planning
! time and run time.
</para>
</sect1>
--- 741,748 ----
we have increased the planning time; here, we have a five-way join
problem replacing two separate three-way join problems. Because of the
exponential growth of the number of possibilities, this makes a big
! difference. For large join problems, you may need to constrain the join
! order or use <xref linkend="guc-geqo-threshold">.
</para>
</sect1>
*** a/doc/src/sgml/ref/select.sgml
--- b/doc/src/sgml/ref/select.sgml
***************
*** 357,372 **** TABLE { [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] |
One of
<itemizedlist>
<listitem>
! <para><literal>[ INNER ] JOIN</literal></para>
</listitem>
<listitem>
! <para><literal>LEFT [ OUTER ] JOIN</literal></para>
</listitem>
<listitem>
! <para><literal>RIGHT [ OUTER ] JOIN</literal></para>
</listitem>
<listitem>
! <para><literal>FULL [ OUTER ] JOIN</literal></para>
</listitem>
<listitem>
<para><literal>CROSS JOIN</literal></para>
--- 357,372 ----
One of
<itemizedlist>
<listitem>
! <para><literal>[ INNER [ FORCE ] ] JOIN</literal></para>
</listitem>
<listitem>
! <para><literal>LEFT [ OUTER ] [ FORCE ] JOIN</literal></para>
</listitem>
<listitem>
! <para><literal>RIGHT [ OUTER ] [ FORCE ] JOIN</literal></para>
</listitem>
<listitem>
! <para><literal>FULL [ OUTER ] [ FORCE ] JOIN</literal></para>
</listitem>
<listitem>
<para><literal>CROSS JOIN</literal></para>
***************
*** 389,395 **** TABLE { [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] |
determine the order of nesting. In the absence of parentheses,
<literal>JOIN</literal>s nest left-to-right. In any case
<literal>JOIN</literal> binds more tightly than the commas
! separating <literal>FROM</> items.
</para>
<para>
--- 389,397 ----
determine the order of nesting. In the absence of parentheses,
<literal>JOIN</literal>s nest left-to-right. In any case
<literal>JOIN</literal> binds more tightly than the commas
! separating <literal>FROM</> items. The keyword
! <literal>FORCE</literal> constrains the join order
! (see <xref linkend="explicit-joins">).
</para>
<para>
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 1542,1547 **** _copyJoinExpr(JoinExpr *from)
--- 1542,1548 ----
COPY_SCALAR_FIELD(jointype);
COPY_SCALAR_FIELD(isNatural);
+ COPY_SCALAR_FIELD(isForce);
COPY_NODE_FIELD(larg);
COPY_NODE_FIELD(rarg);
COPY_NODE_FIELD(using);
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 702,707 **** _equalJoinExpr(JoinExpr *a, JoinExpr *b)
--- 702,708 ----
{
COMPARE_SCALAR_FIELD(jointype);
COMPARE_SCALAR_FIELD(isNatural);
+ COMPARE_SCALAR_FIELD(isForce);
COMPARE_NODE_FIELD(larg);
COMPARE_NODE_FIELD(rarg);
COMPARE_NODE_FIELD(using);
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 1257,1262 **** _outJoinExpr(StringInfo str, JoinExpr *node)
--- 1257,1263 ----
WRITE_ENUM_FIELD(jointype, JoinType);
WRITE_BOOL_FIELD(isNatural);
+ WRITE_BOOL_FIELD(isForce);
WRITE_NODE_FIELD(larg);
WRITE_NODE_FIELD(rarg);
WRITE_NODE_FIELD(using);
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
***************
*** 1068,1073 **** _readJoinExpr(void)
--- 1068,1074 ----
READ_ENUM_FIELD(jointype, JoinType);
READ_BOOL_FIELD(isNatural);
+ READ_BOOL_FIELD(isForce);
READ_NODE_FIELD(larg);
READ_NODE_FIELD(rarg);
READ_NODE_FIELD(using);
*** a/src/backend/optimizer/README
--- b/src/backend/optimizer/README
***************
*** 90,99 **** single join relation.
2) Normally, any explicit JOIN clauses are "flattened" so that we just
have a list of relations to join. However, FULL OUTER JOIN clauses are
never flattened, and other kinds of JOIN might not be either, if the
! flattening process is stopped by join_collapse_limit or from_collapse_limit
! restrictions. Therefore, we end up with a planning problem that contains
! lists of relations to be joined in any order, where any individual item
! might be a sub-list that has to be joined together before we can consider
joining it to its siblings. We process these sub-problems recursively,
bottom up. Note that the join list structure constrains the possible join
orders, but it doesn't constrain the join implementation method at each
--- 90,98 ----
2) Normally, any explicit JOIN clauses are "flattened" so that we just
have a list of relations to join. However, FULL OUTER JOIN clauses are
never flattened, and other kinds of JOIN might not be either, if the
! FORCE keyword is used. Therefore, we end up with a planning problem that
! contains lists of relations to be joined in any order, where any individual
! item might be a sub-list that has to be joined together before we can consider
joining it to its siblings. We process these sub-problems recursively,
bottom up. Note that the join list structure constrains the possible join
orders, but it doesn't constrain the join implementation method at each
*** a/src/backend/optimizer/plan/initsplan.c
--- b/src/backend/optimizer/plan/initsplan.c
***************
*** 32,43 ****
#include "utils/lsyscache.h"
#include "utils/syscache.h"
-
- /* These parameters are set by GUC */
- int from_collapse_limit;
- int join_collapse_limit;
-
-
static List *deconstruct_recurse(PlannerInfo *root, Node *jtnode,
bool below_outer_join,
Relids *qualscope, Relids *inner_join_rels);
--- 32,37 ----
***************
*** 215,221 **** add_vars_to_targetlist(PlannerInfo *root, List *vars, Relids where_needed)
* (note that legal orders may be constrained by SpecialJoinInfo nodes).
* A sub-joinlist represents a subproblem to be planned separately. Currently
* sub-joinlists arise only from FULL OUTER JOIN or when collapsing of
! * subproblems is stopped by join_collapse_limit or from_collapse_limit.
*
* NOTE: when dealing with inner joins, it is appropriate to let a qual clause
* be evaluated at the lowest level where all the variables it mentions are
--- 209,215 ----
* (note that legal orders may be constrained by SpecialJoinInfo nodes).
* A sub-joinlist represents a subproblem to be planned separately. Currently
* sub-joinlists arise only from FULL OUTER JOIN or when collapsing of
! * subproblems is stopped by the FORCE keyword.
*
* NOTE: when dealing with inner joins, it is appropriate to let a qual clause
* be evaluated at the lowest level where all the variables it mentions are
***************
*** 284,320 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
else if (IsA(jtnode, FromExpr))
{
FromExpr *f = (FromExpr *) jtnode;
- int remaining;
ListCell *l;
/*
* First, recurse to handle child joins. We collapse subproblems into
! * a single joinlist whenever the resulting joinlist wouldn't exceed
! * from_collapse_limit members. Also, always collapse one-element
! * subproblems, since that won't lengthen the joinlist anyway.
*/
*qualscope = NULL;
*inner_join_rels = NULL;
joinlist = NIL;
- remaining = list_length(f->fromlist);
foreach(l, f->fromlist)
{
Relids sub_qualscope;
List *sub_joinlist;
- int sub_members;
sub_joinlist = deconstruct_recurse(root, lfirst(l),
below_outer_join,
&sub_qualscope,
inner_join_rels);
*qualscope = bms_add_members(*qualscope, sub_qualscope);
! sub_members = list_length(sub_joinlist);
! remaining--;
! if (sub_members <= 1 ||
! list_length(joinlist) + sub_members + remaining <= from_collapse_limit)
! joinlist = list_concat(joinlist, sub_joinlist);
! else
! joinlist = lappend(joinlist, sub_joinlist);
}
/*
--- 278,303 ----
else if (IsA(jtnode, FromExpr))
{
FromExpr *f = (FromExpr *) jtnode;
ListCell *l;
/*
* First, recurse to handle child joins. We collapse subproblems into
! * a single joinlist.
*/
*qualscope = NULL;
*inner_join_rels = NULL;
joinlist = NIL;
foreach(l, f->fromlist)
{
Relids sub_qualscope;
List *sub_joinlist;
sub_joinlist = deconstruct_recurse(root, lfirst(l),
below_outer_join,
&sub_qualscope,
inner_join_rels);
*qualscope = bms_add_members(*qualscope, sub_qualscope);
! joinlist = list_concat(joinlist, sub_joinlist);
}
/*
***************
*** 469,505 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
/*
* Finally, compute the output joinlist. We fold subproblems together
! * except at a FULL JOIN or where join_collapse_limit would be
! * exceeded.
*/
! if (j->jointype == JOIN_FULL)
{
/* force the join order exactly at this node */
joinlist = list_make1(list_make2(leftjoinlist, rightjoinlist));
}
! else if (list_length(leftjoinlist) + list_length(rightjoinlist) <=
! join_collapse_limit)
{
/* OK to combine subproblems */
joinlist = list_concat(leftjoinlist, rightjoinlist);
}
- else
- {
- /* can't combine, but needn't force join order above here */
- Node *leftpart,
- *rightpart;
-
- /* avoid creating useless 1-element sublists */
- if (list_length(leftjoinlist) == 1)
- leftpart = (Node *) linitial(leftjoinlist);
- else
- leftpart = (Node *) leftjoinlist;
- if (list_length(rightjoinlist) == 1)
- rightpart = (Node *) linitial(rightjoinlist);
- else
- rightpart = (Node *) rightjoinlist;
- joinlist = list_make2(leftpart, rightpart);
- }
}
else
{
--- 452,469 ----
/*
* Finally, compute the output joinlist. We fold subproblems together
! * except at a FULL JOIN or where FORCE has been specified.
*/
! if (j->jointype == JOIN_FULL || j->isForce)
{
/* force the join order exactly at this node */
joinlist = list_make1(list_make2(leftjoinlist, rightjoinlist));
}
! else
{
/* OK to combine subproblems */
joinlist = list_concat(leftjoinlist, rightjoinlist);
}
}
else
{
*** a/src/backend/optimizer/plan/subselect.c
--- b/src/backend/optimizer/plan/subselect.c
***************
*** 1087,1092 **** convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
--- 1087,1093 ----
result = makeNode(JoinExpr);
result->jointype = JOIN_SEMI;
result->isNatural = false;
+ result->isForce = false;
result->larg = NULL; /* caller must fill this in */
result->rarg = (Node *) rtr;
result->using = NIL;
***************
*** 1227,1232 **** convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
--- 1228,1234 ----
result = makeNode(JoinExpr);
result->jointype = under_not ? JOIN_ANTI : JOIN_SEMI;
result->isNatural = false;
+ result->isForce = false;
result->larg = NULL; /* caller must fill this in */
/* flatten out the FromExpr node if it's useless */
if (list_length(subselect->jointree->fromlist) == 1)
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 7437,7459 **** joined_table:
JoinExpr *n = makeNode(JoinExpr);
n->jointype = JOIN_INNER;
n->isNatural = FALSE;
n->larg = $1;
n->rarg = $4;
n->using = NIL;
n->quals = NULL;
$$ = n;
}
! | table_ref join_type JOIN table_ref join_qual
{
JoinExpr *n = makeNode(JoinExpr);
n->jointype = $2;
n->isNatural = FALSE;
n->larg = $1;
! n->rarg = $4;
! if ($5 != NULL && IsA($5, List))
! n->using = (List *) $5; /* USING clause */
else
! n->quals = $5; /* ON clause */
$$ = n;
}
| table_ref JOIN table_ref join_qual
--- 7437,7461 ----
JoinExpr *n = makeNode(JoinExpr);
n->jointype = JOIN_INNER;
n->isNatural = FALSE;
+ n->isForce = FALSE;
n->larg = $1;
n->rarg = $4;
n->using = NIL;
n->quals = NULL;
$$ = n;
}
! | table_ref join_type opt_force JOIN table_ref join_qual
{
JoinExpr *n = makeNode(JoinExpr);
n->jointype = $2;
n->isNatural = FALSE;
+ n->isForce = $3;
n->larg = $1;
! n->rarg = $5;
! if ($6 != NULL && IsA($6, List))
! n->using = (List *) $6; /* USING clause */
else
! n->quals = $6; /* ON clause */
$$ = n;
}
| table_ref JOIN table_ref join_qual
***************
*** 7462,7467 **** joined_table:
--- 7464,7470 ----
JoinExpr *n = makeNode(JoinExpr);
n->jointype = JOIN_INNER;
n->isNatural = FALSE;
+ n->isForce = FALSE;
n->larg = $1;
n->rarg = $3;
if ($4 != NULL && IsA($4, List))
***************
*** 7475,7480 **** joined_table:
--- 7478,7484 ----
JoinExpr *n = makeNode(JoinExpr);
n->jointype = $3;
n->isNatural = TRUE;
+ n->isForce = FALSE;
n->larg = $1;
n->rarg = $5;
n->using = NIL; /* figure out which columns later... */
***************
*** 7487,7492 **** joined_table:
--- 7491,7497 ----
JoinExpr *n = makeNode(JoinExpr);
n->jointype = JOIN_INNER;
n->isNatural = TRUE;
+ n->isForce = FALSE;
n->larg = $1;
n->rarg = $4;
n->using = NIL; /* figure out which columns later... */
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 5824,5829 **** get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
--- 5824,5831 ----
{
JoinExpr *j = (JoinExpr *) jtnode;
bool need_paren_on_right;
+ char buffer[64];
+ int indentPlus;
need_paren_on_right = PRETTY_PAREN(context) &&
!IsA(j->rarg, RangeTblRef) &&
***************
*** 5834,5905 **** get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
get_from_clause_item(j->larg, query, context);
if (j->isNatural)
{
if (!PRETTY_INDENT(context))
appendStringInfoChar(buf, ' ');
! switch (j->jointype)
! {
! case JOIN_INNER:
! appendContextKeyword(context, "NATURAL JOIN ",
! -PRETTYINDENT_JOIN,
! PRETTYINDENT_JOIN, 0);
! break;
! case JOIN_LEFT:
! appendContextKeyword(context, "NATURAL LEFT JOIN ",
! -PRETTYINDENT_JOIN,
! PRETTYINDENT_JOIN, 0);
! break;
! case JOIN_FULL:
! appendContextKeyword(context, "NATURAL FULL JOIN ",
! -PRETTYINDENT_JOIN,
! PRETTYINDENT_JOIN, 0);
! break;
! case JOIN_RIGHT:
! appendContextKeyword(context, "NATURAL RIGHT JOIN ",
! -PRETTYINDENT_JOIN,
! PRETTYINDENT_JOIN, 0);
! break;
! default:
! elog(ERROR, "unrecognized join type: %d",
! (int) j->jointype);
! }
}
else
{
! switch (j->jointype)
! {
! case JOIN_INNER:
! if (j->quals)
! appendContextKeyword(context, " JOIN ",
! -PRETTYINDENT_JOIN,
! PRETTYINDENT_JOIN, 2);
! else
! appendContextKeyword(context, " CROSS JOIN ",
! -PRETTYINDENT_JOIN,
! PRETTYINDENT_JOIN, 1);
! break;
! case JOIN_LEFT:
! appendContextKeyword(context, " LEFT JOIN ",
! -PRETTYINDENT_JOIN,
! PRETTYINDENT_JOIN, 2);
! break;
! case JOIN_FULL:
! appendContextKeyword(context, " FULL JOIN ",
! -PRETTYINDENT_JOIN,
! PRETTYINDENT_JOIN, 2);
! break;
! case JOIN_RIGHT:
! appendContextKeyword(context, " RIGHT JOIN ",
! -PRETTYINDENT_JOIN,
! PRETTYINDENT_JOIN, 2);
! break;
! default:
! elog(ERROR, "unrecognized join type: %d",
! (int) j->jointype);
! }
}
if (need_paren_on_right)
appendStringInfoChar(buf, '(');
get_from_clause_item(j->rarg, query, context);
--- 5836,5891 ----
get_from_clause_item(j->larg, query, context);
+ buffer[0] = '\0';
if (j->isNatural)
{
if (!PRETTY_INDENT(context))
appendStringInfoChar(buf, ' ');
! strcat(buffer, "NATURAL ");
! indentPlus = 0;
}
else
{
! strcat(buffer, " ");
! indentPlus = 2;
! }
!
! switch (j->jointype)
! {
! case JOIN_INNER:
! if (!j->quals && !j->isNatural)
! {
! strcat(buffer, "CROSS ");
! indentPlus = 1;
! }
! /*
! * INNER is normally just decoration, but it's required when
! * we also need to emit FORCE.
! */
! if (j->isForce)
! strcat(buffer, "INNER ");
! break;
! case JOIN_LEFT:
! strcat(buffer, "LEFT ");
! break;
! case JOIN_FULL:
! strcat(buffer, "FULL ");
! break;
! case JOIN_RIGHT:
! strcat(buffer, "RIGHT ");
! break;
! default:
! elog(ERROR, "unrecognized join type: %d", (int) j->jointype);
}
+ if (j->isForce)
+ strcat(buffer, "FORCE JOIN ");
+ else
+ strcat(buffer, "JOIN ");
+
+ appendContextKeyword(context, buffer, -PRETTYINDENT_JOIN,
+ PRETTYINDENT_JOIN, indentPlus);
+
if (need_paren_on_right)
appendStringInfoChar(buf, '(');
get_from_clause_item(j->rarg, query, context);
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 1259,1286 **** static struct config_int ConfigureNamesInt[] =
100, 1, 10000, NULL, NULL
},
{
- {"from_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
- gettext_noop("Sets the FROM-list size beyond which subqueries "
- "are not collapsed."),
- gettext_noop("The planner will merge subqueries into upper "
- "queries if the resulting FROM list would have no more than "
- "this many items.")
- },
- &from_collapse_limit,
- 8, 1, INT_MAX, NULL, NULL
- },
- {
- {"join_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
- gettext_noop("Sets the FROM-list size beyond which JOIN "
- "constructs are not flattened."),
- gettext_noop("The planner will flatten explicit JOIN "
- "constructs into lists of FROM items whenever a "
- "list of no more than this many items would result.")
- },
- &join_collapse_limit,
- 8, 1, INT_MAX, NULL, NULL
- },
- {
{"geqo_threshold", PGC_USERSET, QUERY_TUNING_GEQO,
gettext_noop("Sets the threshold of FROM items beyond which GEQO is used."),
NULL
--- 1259,1264 ----
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 219,227 ****
#default_statistics_target = 100 # range 1-10000
#constraint_exclusion = partition # on, off, or partition
#cursor_tuple_fraction = 0.1 # range 0.0-1.0
- #from_collapse_limit = 8
- #join_collapse_limit = 8 # 1 disables collapsing of explicit
- # JOIN clauses
#------------------------------------------------------------------------------
--- 219,224 ----
*** a/src/include/nodes/primnodes.h
--- b/src/include/nodes/primnodes.h
***************
*** 1150,1155 **** typedef struct JoinExpr
--- 1150,1156 ----
NodeTag type;
JoinType jointype; /* type of join */
bool isNatural; /* Natural join? Will need to shape table */
+ bool isForce; /* Join order forced? */
Node *larg; /* left subtree */
Node *rarg; /* right subtree */
List *using; /* USING clause, if any (list of String) */
*** a/src/include/optimizer/planmain.h
--- b/src/include/optimizer/planmain.h
***************
*** 79,87 **** extern bool is_projection_capable_plan(Plan *plan);
/*
* prototypes for plan/initsplan.c
*/
- extern int from_collapse_limit;
- extern int join_collapse_limit;
-
extern void add_base_rels_to_query(PlannerInfo *root, Node *jtnode);
extern void build_base_rel_tlists(PlannerInfo *root, List *final_tlist);
extern void add_vars_to_targetlist(PlannerInfo *root, List *vars,
--- 79,84 ----
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers