Teach planner to convert simple UNION ALL subqueries into append relations,
thereby sharing code with the inheritance case. This puts the UNION-ALL-view approach to partitioned tables on par with inheritance, so far as constraint exclusion is concerned: it works either way. (Still need to update the docs to say so.) The definition of "simple UNION ALL" is a little simpler than I would like --- basically the union arms can only be SELECT * FROM foo --- but it's good enough for partitioned-table cases.
This commit is contained in:
parent
a25b1decef
commit
8b109ebf14
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.140 2006/01/31 21:39:23 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.141 2006/02/03 21:08:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -42,6 +42,8 @@ int geqo_threshold;
|
||||||
|
|
||||||
|
|
||||||
static void set_base_rel_pathlists(PlannerInfo *root);
|
static void set_base_rel_pathlists(PlannerInfo *root);
|
||||||
|
static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||||
|
Index rti, RangeTblEntry *rte);
|
||||||
static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||||
RangeTblEntry *rte);
|
RangeTblEntry *rte);
|
||||||
static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||||
|
@ -133,7 +135,6 @@ set_base_rel_pathlists(PlannerInfo *root)
|
||||||
for (rti = 1; rti < root->simple_rel_array_size; rti++)
|
for (rti = 1; rti < root->simple_rel_array_size; rti++)
|
||||||
{
|
{
|
||||||
RelOptInfo *rel = root->simple_rel_array[rti];
|
RelOptInfo *rel = root->simple_rel_array[rti];
|
||||||
RangeTblEntry *rte;
|
|
||||||
|
|
||||||
/* there may be empty slots corresponding to non-baserel RTEs */
|
/* there may be empty slots corresponding to non-baserel RTEs */
|
||||||
if (rel == NULL)
|
if (rel == NULL)
|
||||||
|
@ -145,8 +146,19 @@ set_base_rel_pathlists(PlannerInfo *root)
|
||||||
if (rel->reloptkind != RELOPT_BASEREL)
|
if (rel->reloptkind != RELOPT_BASEREL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
rte = rt_fetch(rti, root->parse->rtable);
|
set_rel_pathlist(root, rel, rti,
|
||||||
|
rt_fetch(rti, root->parse->rtable));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set_rel_pathlist
|
||||||
|
* Build access paths for a base relation
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||||
|
Index rti, RangeTblEntry *rte)
|
||||||
|
{
|
||||||
if (rte->inh)
|
if (rte->inh)
|
||||||
{
|
{
|
||||||
/* It's an "append relation", process accordingly */
|
/* It's an "append relation", process accordingly */
|
||||||
|
@ -165,13 +177,13 @@ set_base_rel_pathlists(PlannerInfo *root)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Plain relation */
|
/* Plain relation */
|
||||||
|
Assert(rel->rtekind == RTE_RELATION);
|
||||||
set_plain_rel_pathlist(root, rel, rte);
|
set_plain_rel_pathlist(root, rel, rte);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef OPTIMIZER_DEBUG
|
#ifdef OPTIMIZER_DEBUG
|
||||||
debug_print_rel(root, rel);
|
debug_print_rel(root, rel);
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -181,9 +193,6 @@ set_base_rel_pathlists(PlannerInfo *root)
|
||||||
static void
|
static void
|
||||||
set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
|
set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
|
||||||
{
|
{
|
||||||
Assert(rel->rtekind == RTE_RELATION);
|
|
||||||
Assert(!rte->inh);
|
|
||||||
|
|
||||||
/* Mark rel with estimated output rows, width, etc */
|
/* Mark rel with estimated output rows, width, etc */
|
||||||
set_baserel_size_estimates(root, rel);
|
set_baserel_size_estimates(root, rel);
|
||||||
|
|
||||||
|
@ -265,6 +274,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||||
int childRTindex;
|
int childRTindex;
|
||||||
RelOptInfo *childrel;
|
RelOptInfo *childrel;
|
||||||
RangeTblEntry *childrte;
|
RangeTblEntry *childrte;
|
||||||
|
Path *childpath;
|
||||||
ListCell *parentvars;
|
ListCell *parentvars;
|
||||||
ListCell *childvars;
|
ListCell *childvars;
|
||||||
|
|
||||||
|
@ -346,10 +356,20 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compute the child's access paths, and save the cheapest.
|
* Compute the child's access paths, and save the cheapest.
|
||||||
|
*
|
||||||
|
* It's possible that the child is itself an appendrel, in which
|
||||||
|
* case we can "cut out the middleman" and just add its child
|
||||||
|
* paths to our own list. (We don't try to do this earlier because
|
||||||
|
* we need to apply both levels of transformation to the quals.)
|
||||||
*/
|
*/
|
||||||
set_plain_rel_pathlist(root, childrel, childrte);
|
set_rel_pathlist(root, childrel, childRTindex, childrte);
|
||||||
|
|
||||||
subpaths = lappend(subpaths, childrel->cheapest_total_path);
|
childpath = childrel->cheapest_total_path;
|
||||||
|
if (IsA(childpath, AppendPath))
|
||||||
|
subpaths = list_concat(subpaths,
|
||||||
|
((AppendPath *) childpath)->subpaths);
|
||||||
|
else
|
||||||
|
subpaths = lappend(subpaths, childpath);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Propagate size information from the child back to the parent. For
|
* Propagate size information from the child back to the parent. For
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.99 2006/01/31 21:39:23 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.100 2006/02/03 21:08:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -20,7 +20,6 @@
|
||||||
#include "optimizer/cost.h"
|
#include "optimizer/cost.h"
|
||||||
#include "optimizer/pathnode.h"
|
#include "optimizer/pathnode.h"
|
||||||
#include "optimizer/paths.h"
|
#include "optimizer/paths.h"
|
||||||
#include "optimizer/prep.h"
|
|
||||||
#include "parser/parsetree.h"
|
#include "parser/parsetree.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
|
||||||
|
@ -856,8 +855,6 @@ join_before_append(PlannerInfo *root,
|
||||||
int childRTindex;
|
int childRTindex;
|
||||||
RelOptInfo *childrel;
|
RelOptInfo *childrel;
|
||||||
Path *bestinnerjoin;
|
Path *bestinnerjoin;
|
||||||
Relids joinrelids;
|
|
||||||
Relids *save_attr_needed;
|
|
||||||
RelOptInfo *this_joinrel;
|
RelOptInfo *this_joinrel;
|
||||||
List *this_restrictlist;
|
List *this_restrictlist;
|
||||||
|
|
||||||
|
@ -899,27 +896,9 @@ join_before_append(PlannerInfo *root,
|
||||||
* in joinrels.c, it provides necessary context for the Path,
|
* in joinrels.c, it provides necessary context for the Path,
|
||||||
* such as properly-translated target and quals lists.
|
* such as properly-translated target and quals lists.
|
||||||
*/
|
*/
|
||||||
joinrelids = bms_copy(joinrel->relids);
|
this_joinrel = translate_join_rel(root, joinrel, appinfo,
|
||||||
joinrelids = bms_del_member(joinrelids, parentRTindex);
|
outerrel, childrel, jointype,
|
||||||
joinrelids = bms_add_member(joinrelids, childRTindex);
|
&this_restrictlist);
|
||||||
|
|
||||||
/*
|
|
||||||
* Kluge: temporarily adjust the outer rel's attr_needed info so
|
|
||||||
* that it references the member rel instead of the appendrel.
|
|
||||||
* This is needed to build the correct targetlist for the joinrel.
|
|
||||||
*/
|
|
||||||
save_attr_needed = outerrel->attr_needed;
|
|
||||||
outerrel->attr_needed =
|
|
||||||
adjust_other_rel_attr_needed(outerrel, appinfo,
|
|
||||||
outerrel->min_attr,
|
|
||||||
outerrel->max_attr);
|
|
||||||
|
|
||||||
this_joinrel = build_join_rel(root, joinrelids, outerrel, childrel,
|
|
||||||
jointype, &this_restrictlist);
|
|
||||||
|
|
||||||
/* Now we can undo the hack on attr_needed */
|
|
||||||
pfree(outerrel->attr_needed);
|
|
||||||
outerrel->attr_needed = save_attr_needed;
|
|
||||||
|
|
||||||
/* Build Path for join and add to result list */
|
/* Build Path for join and add to result list */
|
||||||
append_paths = lappend(append_paths,
|
append_paths = lappend(append_paths,
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.197 2006/01/31 21:39:24 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.198 2006/02/03 21:08:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -222,7 +222,7 @@ subquery_planner(Query *parse, double tuple_fraction,
|
||||||
* this query.
|
* this query.
|
||||||
*/
|
*/
|
||||||
parse->jointree = (FromExpr *)
|
parse->jointree = (FromExpr *)
|
||||||
pull_up_subqueries(root, (Node *) parse->jointree, false);
|
pull_up_subqueries(root, (Node *) parse->jointree, false, false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Detect whether any rangetable entries are RTE_JOIN kind; if not, we can
|
* Detect whether any rangetable entries are RTE_JOIN kind; if not, we can
|
||||||
|
|
|
@ -15,16 +15,19 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.34 2006/01/31 21:39:24 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.35 2006/02/03 21:08:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "nodes/makefuncs.h"
|
||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
#include "optimizer/prep.h"
|
#include "optimizer/prep.h"
|
||||||
#include "optimizer/subselect.h"
|
#include "optimizer/subselect.h"
|
||||||
|
#include "optimizer/tlist.h"
|
||||||
#include "optimizer/var.h"
|
#include "optimizer/var.h"
|
||||||
|
#include "parser/parse_expr.h"
|
||||||
#include "parser/parsetree.h"
|
#include "parser/parsetree.h"
|
||||||
#include "rewrite/rewriteManip.h"
|
#include "rewrite/rewriteManip.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
@ -37,8 +40,23 @@ typedef struct reduce_outer_joins_state
|
||||||
List *sub_states; /* List of states for subtree components */
|
List *sub_states; /* List of states for subtree components */
|
||||||
} reduce_outer_joins_state;
|
} reduce_outer_joins_state;
|
||||||
|
|
||||||
|
static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode,
|
||||||
|
RangeTblEntry *rte,
|
||||||
|
bool below_outer_join,
|
||||||
|
bool append_rel_member);
|
||||||
|
static Node *pull_up_simple_union_all(PlannerInfo *root, Node *jtnode,
|
||||||
|
RangeTblEntry *rte);
|
||||||
|
static void pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root,
|
||||||
|
int parentRTindex, Query *setOpQuery);
|
||||||
|
static void make_setop_translation_lists(Query *query,
|
||||||
|
Index newvarno,
|
||||||
|
List **col_mappings, List **translated_vars);
|
||||||
static bool is_simple_subquery(Query *subquery);
|
static bool is_simple_subquery(Query *subquery);
|
||||||
|
static bool is_simple_union_all(Query *subquery);
|
||||||
|
static bool is_simple_union_all_recurse(Node *setOp, Query *setOpQuery,
|
||||||
|
List *colTypes);
|
||||||
static bool has_nullable_targetlist(Query *subquery);
|
static bool has_nullable_targetlist(Query *subquery);
|
||||||
|
static bool is_safe_append_member(Query *subquery);
|
||||||
static void resolvenew_in_jointree(Node *jtnode, int varno,
|
static void resolvenew_in_jointree(Node *jtnode, int varno,
|
||||||
RangeTblEntry *rte, List *subtlist);
|
RangeTblEntry *rte, List *subtlist);
|
||||||
static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode);
|
static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode);
|
||||||
|
@ -48,6 +66,8 @@ static void reduce_outer_joins_pass2(Node *jtnode,
|
||||||
Relids nonnullable_rels);
|
Relids nonnullable_rels);
|
||||||
static void fix_in_clause_relids(List *in_info_list, int varno,
|
static void fix_in_clause_relids(List *in_info_list, int varno,
|
||||||
Relids subrelids);
|
Relids subrelids);
|
||||||
|
static void fix_append_rel_relids(List *append_rel_list, int varno,
|
||||||
|
Relids subrelids);
|
||||||
static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
|
static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
|
||||||
|
|
||||||
|
|
||||||
|
@ -110,10 +130,16 @@ pull_up_IN_clauses(PlannerInfo *root, Node *node)
|
||||||
* Look for subqueries in the rangetable that can be pulled up into
|
* Look for subqueries in the rangetable that can be pulled up into
|
||||||
* the parent query. If the subquery has no special features like
|
* the parent query. If the subquery has no special features like
|
||||||
* grouping/aggregation then we can merge it into the parent's jointree.
|
* grouping/aggregation then we can merge it into the parent's jointree.
|
||||||
|
* Also, subqueries that are simple UNION ALL structures can be
|
||||||
|
* converted into "append relations".
|
||||||
*
|
*
|
||||||
* below_outer_join is true if this jointree node is within the nullable
|
* below_outer_join is true if this jointree node is within the nullable
|
||||||
* side of an outer join. This restricts what we can do.
|
* side of an outer join. This restricts what we can do.
|
||||||
*
|
*
|
||||||
|
* append_rel_member is true if we are looking at a member subquery of
|
||||||
|
* an append relation. This puts some different restrictions on what
|
||||||
|
* we can do.
|
||||||
|
*
|
||||||
* A tricky aspect of this code is that if we pull up a subquery we have
|
* A tricky aspect of this code is that if we pull up a subquery we have
|
||||||
* to replace Vars that reference the subquery's outputs throughout the
|
* to replace Vars that reference the subquery's outputs throughout the
|
||||||
* parent query, including quals attached to jointree nodes above the one
|
* parent query, including quals attached to jointree nodes above the one
|
||||||
|
@ -124,16 +150,15 @@ pull_up_IN_clauses(PlannerInfo *root, Node *node)
|
||||||
* copy of the tree; we have to invoke it just on the quals, instead.
|
* copy of the tree; we have to invoke it just on the quals, instead.
|
||||||
*/
|
*/
|
||||||
Node *
|
Node *
|
||||||
pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
|
pull_up_subqueries(PlannerInfo *root, Node *jtnode,
|
||||||
|
bool below_outer_join, bool append_rel_member)
|
||||||
{
|
{
|
||||||
if (jtnode == NULL)
|
if (jtnode == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (IsA(jtnode, RangeTblRef))
|
if (IsA(jtnode, RangeTblRef))
|
||||||
{
|
{
|
||||||
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
||||||
Query *parse = root->parse;
|
RangeTblEntry *rte = rt_fetch(varno, root->parse->rtable);
|
||||||
RangeTblEntry *rte = rt_fetch(varno, parse->rtable);
|
|
||||||
Query *subquery = rte->subquery;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Is this a subquery RTE, and if so, is the subquery simple enough to
|
* Is this a subquery RTE, and if so, is the subquery simple enough to
|
||||||
|
@ -148,11 +173,113 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
|
||||||
* expressions; we'd have to figure out how to get the pseudo-
|
* expressions; we'd have to figure out how to get the pseudo-
|
||||||
* variables evaluated at the right place in the modified plan tree.
|
* variables evaluated at the right place in the modified plan tree.
|
||||||
* Fix it someday.
|
* Fix it someday.
|
||||||
|
*
|
||||||
|
* If we are looking at an append-relation member, we can't pull
|
||||||
|
* it up unless is_safe_append_member says so.
|
||||||
*/
|
*/
|
||||||
if (rte->rtekind == RTE_SUBQUERY &&
|
if (rte->rtekind == RTE_SUBQUERY &&
|
||||||
is_simple_subquery(subquery) &&
|
is_simple_subquery(rte->subquery) &&
|
||||||
(!below_outer_join || has_nullable_targetlist(subquery)))
|
(!below_outer_join || has_nullable_targetlist(rte->subquery)) &&
|
||||||
|
(!append_rel_member || is_safe_append_member(rte->subquery)))
|
||||||
|
return pull_up_simple_subquery(root, jtnode, rte,
|
||||||
|
below_outer_join,
|
||||||
|
append_rel_member);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Alternatively, is it a simple UNION ALL subquery? If so, flatten
|
||||||
|
* into an "append relation". We can do this regardless of nullability
|
||||||
|
* considerations since this transformation does not result in
|
||||||
|
* propagating non-Var expressions into upper levels of the query.
|
||||||
|
*
|
||||||
|
* It's also safe to do this regardless of whether this query is
|
||||||
|
* itself an appendrel member. (If you're thinking we should try
|
||||||
|
* to flatten the two levels of appendrel together, you're right;
|
||||||
|
* but we handle that in set_append_rel_pathlist, not here.)
|
||||||
|
*/
|
||||||
|
if (rte->rtekind == RTE_SUBQUERY &&
|
||||||
|
is_simple_union_all(rte->subquery))
|
||||||
|
return pull_up_simple_union_all(root, jtnode, rte);
|
||||||
|
}
|
||||||
|
else if (IsA(jtnode, FromExpr))
|
||||||
{
|
{
|
||||||
|
FromExpr *f = (FromExpr *) jtnode;
|
||||||
|
ListCell *l;
|
||||||
|
|
||||||
|
Assert(!append_rel_member);
|
||||||
|
foreach(l, f->fromlist)
|
||||||
|
lfirst(l) = pull_up_subqueries(root, lfirst(l),
|
||||||
|
below_outer_join, false);
|
||||||
|
}
|
||||||
|
else if (IsA(jtnode, JoinExpr))
|
||||||
|
{
|
||||||
|
JoinExpr *j = (JoinExpr *) jtnode;
|
||||||
|
|
||||||
|
Assert(!append_rel_member);
|
||||||
|
/* Recurse, being careful to tell myself when inside outer join */
|
||||||
|
switch (j->jointype)
|
||||||
|
{
|
||||||
|
case JOIN_INNER:
|
||||||
|
j->larg = pull_up_subqueries(root, j->larg,
|
||||||
|
below_outer_join, false);
|
||||||
|
j->rarg = pull_up_subqueries(root, j->rarg,
|
||||||
|
below_outer_join, false);
|
||||||
|
break;
|
||||||
|
case JOIN_LEFT:
|
||||||
|
j->larg = pull_up_subqueries(root, j->larg,
|
||||||
|
below_outer_join, false);
|
||||||
|
j->rarg = pull_up_subqueries(root, j->rarg,
|
||||||
|
true, false);
|
||||||
|
break;
|
||||||
|
case JOIN_FULL:
|
||||||
|
j->larg = pull_up_subqueries(root, j->larg,
|
||||||
|
true, false);
|
||||||
|
j->rarg = pull_up_subqueries(root, j->rarg,
|
||||||
|
true, false);
|
||||||
|
break;
|
||||||
|
case JOIN_RIGHT:
|
||||||
|
j->larg = pull_up_subqueries(root, j->larg,
|
||||||
|
true, false);
|
||||||
|
j->rarg = pull_up_subqueries(root, j->rarg,
|
||||||
|
below_outer_join, false);
|
||||||
|
break;
|
||||||
|
case JOIN_UNION:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is where we fail if upper levels of planner haven't
|
||||||
|
* rewritten UNION JOIN as an Append ...
|
||||||
|
*/
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("UNION JOIN is not implemented")));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unrecognized join type: %d",
|
||||||
|
(int) j->jointype);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
elog(ERROR, "unrecognized node type: %d",
|
||||||
|
(int) nodeTag(jtnode));
|
||||||
|
return jtnode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pull_up_simple_subquery
|
||||||
|
* Attempt to pull up a single simple subquery.
|
||||||
|
*
|
||||||
|
* jtnode is a RangeTblRef that has been tentatively identified as a simple
|
||||||
|
* subquery by pull_up_subqueries. We return the replacement jointree node,
|
||||||
|
* or jtnode itself if we determine that the subquery can't be pulled up after
|
||||||
|
* all.
|
||||||
|
*/
|
||||||
|
static Node *
|
||||||
|
pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
|
||||||
|
bool below_outer_join, bool append_rel_member)
|
||||||
|
{
|
||||||
|
Query *parse = root->parse;
|
||||||
|
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
||||||
|
Query *subquery;
|
||||||
PlannerInfo *subroot;
|
PlannerInfo *subroot;
|
||||||
int rtoffset;
|
int rtoffset;
|
||||||
List *subtlist;
|
List *subtlist;
|
||||||
|
@ -165,7 +292,7 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
|
||||||
* multiple jointree items (which can't happen normally, but might
|
* multiple jointree items (which can't happen normally, but might
|
||||||
* after rule rewriting).
|
* after rule rewriting).
|
||||||
*/
|
*/
|
||||||
subquery = copyObject(subquery);
|
subquery = copyObject(rte->subquery);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a PlannerInfo data structure for this subquery.
|
* Create a PlannerInfo data structure for this subquery.
|
||||||
|
@ -188,17 +315,17 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
|
||||||
subquery->jointree->quals);
|
subquery->jointree->quals);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Recursively pull up the subquery's subqueries, so that this
|
* Recursively pull up the subquery's subqueries, so that
|
||||||
* routine's processing is complete for its jointree and
|
* pull_up_subqueries' processing is complete for its jointree and
|
||||||
* rangetable.
|
* rangetable.
|
||||||
*
|
*
|
||||||
* Note: 'false' is correct here even if we are within an outer
|
* Note: below_outer_join = false is correct here even if we are within an
|
||||||
* join in the upper query; the lower query starts with a clean
|
* outer join in the upper query; the lower query starts with a clean
|
||||||
* slate for outer-join semantics.
|
* slate for outer-join semantics. Likewise, we say we aren't handling
|
||||||
|
* an appendrel member.
|
||||||
*/
|
*/
|
||||||
subquery->jointree = (FromExpr *)
|
subquery->jointree = (FromExpr *)
|
||||||
pull_up_subqueries(subroot, (Node *) subquery->jointree,
|
pull_up_subqueries(subroot, (Node *) subquery->jointree, false, false);
|
||||||
false);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now we must recheck whether the subquery is still simple enough
|
* Now we must recheck whether the subquery is still simple enough
|
||||||
|
@ -206,10 +333,11 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
|
||||||
*
|
*
|
||||||
* We don't really need to recheck all the conditions involved,
|
* We don't really need to recheck all the conditions involved,
|
||||||
* but it's easier just to keep this "if" looking the same as the
|
* but it's easier just to keep this "if" looking the same as the
|
||||||
* one above.
|
* one in pull_up_subqueries.
|
||||||
*/
|
*/
|
||||||
if (is_simple_subquery(subquery) &&
|
if (is_simple_subquery(subquery) &&
|
||||||
(!below_outer_join || has_nullable_targetlist(subquery)))
|
(!below_outer_join || has_nullable_targetlist(subquery)) &&
|
||||||
|
(!append_rel_member || is_safe_append_member(subquery)))
|
||||||
{
|
{
|
||||||
/* good to go */
|
/* good to go */
|
||||||
}
|
}
|
||||||
|
@ -319,27 +447,26 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
|
||||||
* We also have to fix the relid sets of any parent InClauseInfo
|
* We also have to fix the relid sets of any parent InClauseInfo
|
||||||
* nodes. (This could perhaps be done by ResolveNew, but it would
|
* nodes. (This could perhaps be done by ResolveNew, but it would
|
||||||
* clutter that routine's API unreasonably.)
|
* clutter that routine's API unreasonably.)
|
||||||
|
*
|
||||||
|
* Likewise, relids appearing in AppendRelInfo nodes have to be fixed
|
||||||
|
* (but we took care of their translated_vars lists above). We already
|
||||||
|
* checked that this won't require introducing multiple subrelids into
|
||||||
|
* the single-slot AppendRelInfo structs.
|
||||||
*/
|
*/
|
||||||
if (root->in_info_list)
|
if (root->in_info_list || root->append_rel_list)
|
||||||
{
|
{
|
||||||
Relids subrelids;
|
Relids subrelids;
|
||||||
|
|
||||||
subrelids = get_relids_in_jointree((Node *) subquery->jointree);
|
subrelids = get_relids_in_jointree((Node *) subquery->jointree);
|
||||||
fix_in_clause_relids(root->in_info_list, varno, subrelids);
|
fix_in_clause_relids(root->in_info_list, varno, subrelids);
|
||||||
|
fix_append_rel_relids(root->append_rel_list, varno, subrelids);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* And now append any subquery InClauseInfos to our list.
|
* And now add any subquery InClauseInfos and AppendRelInfos to our lists.
|
||||||
*/
|
*/
|
||||||
root->in_info_list = list_concat(root->in_info_list,
|
root->in_info_list = list_concat(root->in_info_list,
|
||||||
subroot->in_info_list);
|
subroot->in_info_list);
|
||||||
|
|
||||||
/*
|
|
||||||
* XXX need to do something about adjusting AppendRelInfos too
|
|
||||||
*/
|
|
||||||
Assert(root->append_rel_list == NIL);
|
|
||||||
|
|
||||||
/* Also pull up any subquery AppendRelInfos */
|
|
||||||
root->append_rel_list = list_concat(root->append_rel_list,
|
root->append_rel_list = list_concat(root->append_rel_list,
|
||||||
subroot->append_rel_list);
|
subroot->append_rel_list);
|
||||||
|
|
||||||
|
@ -358,71 +485,156 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the adjusted subquery jointree to replace the
|
* Return the adjusted subquery jointree to replace the
|
||||||
* RangeTblRef entry in my jointree.
|
* RangeTblRef entry in parent's jointree.
|
||||||
*/
|
*/
|
||||||
return (Node *) subquery->jointree;
|
return (Node *) subquery->jointree;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (IsA(jtnode, FromExpr))
|
|
||||||
{
|
|
||||||
FromExpr *f = (FromExpr *) jtnode;
|
|
||||||
ListCell *l;
|
|
||||||
|
|
||||||
foreach(l, f->fromlist)
|
/*
|
||||||
lfirst(l) = pull_up_subqueries(root, lfirst(l),
|
* pull_up_simple_union_all
|
||||||
below_outer_join);
|
* Pull up a single simple UNION ALL subquery.
|
||||||
}
|
*
|
||||||
else if (IsA(jtnode, JoinExpr))
|
* jtnode is a RangeTblRef that has been identified as a simple UNION ALL
|
||||||
{
|
* subquery by pull_up_subqueries. We pull up the leaf subqueries and
|
||||||
JoinExpr *j = (JoinExpr *) jtnode;
|
* build an "append relation" for the union set. The result value is just
|
||||||
|
* jtnode, since we don't actually need to change the query jointree.
|
||||||
/* Recurse, being careful to tell myself when inside outer join */
|
*/
|
||||||
switch (j->jointype)
|
static Node *
|
||||||
{
|
pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
|
||||||
case JOIN_INNER:
|
{
|
||||||
j->larg = pull_up_subqueries(root, j->larg,
|
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
||||||
below_outer_join);
|
Query *subquery = rte->subquery;
|
||||||
j->rarg = pull_up_subqueries(root, j->rarg,
|
|
||||||
below_outer_join);
|
|
||||||
break;
|
|
||||||
case JOIN_LEFT:
|
|
||||||
j->larg = pull_up_subqueries(root, j->larg,
|
|
||||||
below_outer_join);
|
|
||||||
j->rarg = pull_up_subqueries(root, j->rarg,
|
|
||||||
true);
|
|
||||||
break;
|
|
||||||
case JOIN_FULL:
|
|
||||||
j->larg = pull_up_subqueries(root, j->larg,
|
|
||||||
true);
|
|
||||||
j->rarg = pull_up_subqueries(root, j->rarg,
|
|
||||||
true);
|
|
||||||
break;
|
|
||||||
case JOIN_RIGHT:
|
|
||||||
j->larg = pull_up_subqueries(root, j->larg,
|
|
||||||
true);
|
|
||||||
j->rarg = pull_up_subqueries(root, j->rarg,
|
|
||||||
below_outer_join);
|
|
||||||
break;
|
|
||||||
case JOIN_UNION:
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is where we fail if upper levels of planner haven't
|
* Recursively scan the subquery's setOperations tree and copy the leaf
|
||||||
* rewritten UNION JOIN as an Append ...
|
* subqueries into the parent rangetable. Add AppendRelInfo nodes for
|
||||||
|
* them to the parent's append_rel_list, too.
|
||||||
*/
|
*/
|
||||||
ereport(ERROR,
|
Assert(subquery->setOperations);
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
pull_up_union_leaf_queries(subquery->setOperations, root, varno, subquery);
|
||||||
errmsg("UNION JOIN is not implemented")));
|
|
||||||
break;
|
/*
|
||||||
default:
|
* Mark the parent as an append relation.
|
||||||
elog(ERROR, "unrecognized join type: %d",
|
*/
|
||||||
(int) j->jointype);
|
rte->inh = true;
|
||||||
break;
|
|
||||||
|
return jtnode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pull_up_union_leaf_queries -- recursive guts of pull_up_simple_union_all
|
||||||
|
*
|
||||||
|
* Note that setOpQuery is the Query containing the setOp node, whose rtable
|
||||||
|
* is where to look up the RTE if setOp is a RangeTblRef. This is *not* the
|
||||||
|
* same as root->parse, which is the top-level Query we are pulling up into.
|
||||||
|
* parentRTindex is the appendrel parent's index in root->parse->rtable.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex,
|
||||||
|
Query *setOpQuery)
|
||||||
|
{
|
||||||
|
if (IsA(setOp, RangeTblRef))
|
||||||
|
{
|
||||||
|
RangeTblRef *rtr = (RangeTblRef *) setOp;
|
||||||
|
RangeTblEntry *rte = rt_fetch(rtr->rtindex, setOpQuery->rtable);
|
||||||
|
Query *subquery;
|
||||||
|
int childRTindex;
|
||||||
|
AppendRelInfo *appinfo;
|
||||||
|
Query *parse = root->parse;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make a modifiable copy of the child RTE and contained query.
|
||||||
|
*/
|
||||||
|
rte = copyObject(rte);
|
||||||
|
subquery = rte->subquery;
|
||||||
|
Assert(subquery != NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Upper-level vars in subquery are now one level closer to their
|
||||||
|
* parent than before. We don't have to worry about offsetting
|
||||||
|
* varnos, though, because any such vars must refer to stuff above
|
||||||
|
* the level of the query we are pulling into.
|
||||||
|
*/
|
||||||
|
IncrementVarSublevelsUp((Node *) subquery, -1, 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attach child RTE to parent rtable.
|
||||||
|
*/
|
||||||
|
parse->rtable = lappend(parse->rtable, rte);
|
||||||
|
childRTindex = list_length(parse->rtable);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build a suitable AppendRelInfo, and attach to parent's list.
|
||||||
|
*/
|
||||||
|
appinfo = makeNode(AppendRelInfo);
|
||||||
|
appinfo->parent_relid = parentRTindex;
|
||||||
|
appinfo->child_relid = childRTindex;
|
||||||
|
appinfo->parent_reltype = InvalidOid;
|
||||||
|
appinfo->child_reltype = InvalidOid;
|
||||||
|
make_setop_translation_lists(setOpQuery, childRTindex,
|
||||||
|
&appinfo->col_mappings,
|
||||||
|
&appinfo->translated_vars);
|
||||||
|
appinfo->parent_reloid = InvalidOid;
|
||||||
|
root->append_rel_list = lappend(root->append_rel_list, appinfo);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Recursively apply pull_up_subqueries to the new child RTE. (We
|
||||||
|
* must build the AppendRelInfo first, because this will modify it.)
|
||||||
|
* Note that we can pass below_outer_join = false even if we're
|
||||||
|
* actually under an outer join, because the child's expressions
|
||||||
|
* aren't going to propagate up above the join.
|
||||||
|
*/
|
||||||
|
rtr = makeNode(RangeTblRef);
|
||||||
|
rtr->rtindex = childRTindex;
|
||||||
|
(void) pull_up_subqueries(root, (Node *) rtr, false, true);
|
||||||
}
|
}
|
||||||
|
else if (IsA(setOp, SetOperationStmt))
|
||||||
|
{
|
||||||
|
SetOperationStmt *op = (SetOperationStmt *) setOp;
|
||||||
|
|
||||||
|
/* Recurse to reach leaf queries */
|
||||||
|
pull_up_union_leaf_queries(op->larg, root, parentRTindex, setOpQuery);
|
||||||
|
pull_up_union_leaf_queries(op->rarg, root, parentRTindex, setOpQuery);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
elog(ERROR, "unrecognized node type: %d",
|
elog(ERROR, "unrecognized node type: %d",
|
||||||
(int) nodeTag(jtnode));
|
(int) nodeTag(setOp));
|
||||||
return jtnode;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* make_setop_translation_lists
|
||||||
|
* Build the lists of translations from parent Vars to child Vars for
|
||||||
|
* a UNION ALL member. We need both a column number mapping list
|
||||||
|
* and a list of Vars representing the child columns.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
make_setop_translation_lists(Query *query,
|
||||||
|
Index newvarno,
|
||||||
|
List **col_mappings, List **translated_vars)
|
||||||
|
{
|
||||||
|
List *numbers = NIL;
|
||||||
|
List *vars = NIL;
|
||||||
|
ListCell *l;
|
||||||
|
|
||||||
|
foreach(l, query->targetList)
|
||||||
|
{
|
||||||
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
||||||
|
|
||||||
|
if (tle->resjunk)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
numbers = lappend_int(numbers, tle->resno);
|
||||||
|
vars = lappend(vars, makeVar(newvarno,
|
||||||
|
tle->resno,
|
||||||
|
exprType((Node *) tle->expr),
|
||||||
|
exprTypmod((Node *) tle->expr),
|
||||||
|
0));
|
||||||
|
}
|
||||||
|
|
||||||
|
*col_mappings = numbers;
|
||||||
|
*translated_vars = vars;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -443,7 +655,8 @@ is_simple_subquery(Query *subquery)
|
||||||
elog(ERROR, "subquery is bogus");
|
elog(ERROR, "subquery is bogus");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Can't currently pull up a query with setops. Maybe after querytree
|
* Can't currently pull up a query with setops (unless it's simple UNION
|
||||||
|
* ALL, which is handled by a different code path). Maybe after querytree
|
||||||
* redesign...
|
* redesign...
|
||||||
*/
|
*/
|
||||||
if (subquery->setOperations)
|
if (subquery->setOperations)
|
||||||
|
@ -484,6 +697,78 @@ is_simple_subquery(Query *subquery)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* is_simple_union_all
|
||||||
|
* Check a subquery to see if it's a simple UNION ALL.
|
||||||
|
*
|
||||||
|
* We require all the setops to be UNION ALL (no mixing) and there can't be
|
||||||
|
* any datatype coercions involved, ie, all the leaf queries must emit the
|
||||||
|
* same datatypes.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
is_simple_union_all(Query *subquery)
|
||||||
|
{
|
||||||
|
SetOperationStmt *topop;
|
||||||
|
|
||||||
|
/* Let's just make sure it's a valid subselect ... */
|
||||||
|
if (!IsA(subquery, Query) ||
|
||||||
|
subquery->commandType != CMD_SELECT ||
|
||||||
|
subquery->resultRelation != 0 ||
|
||||||
|
subquery->into != NULL)
|
||||||
|
elog(ERROR, "subquery is bogus");
|
||||||
|
|
||||||
|
/* Is it a set-operation query at all? */
|
||||||
|
topop = (SetOperationStmt *) subquery->setOperations;
|
||||||
|
if (!topop)
|
||||||
|
return false;
|
||||||
|
Assert(IsA(topop, SetOperationStmt));
|
||||||
|
|
||||||
|
/* Can't handle ORDER BY, LIMIT/OFFSET, or locking */
|
||||||
|
if (subquery->sortClause ||
|
||||||
|
subquery->limitOffset ||
|
||||||
|
subquery->limitCount ||
|
||||||
|
subquery->rowMarks)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Recursively check the tree of set operations */
|
||||||
|
return is_simple_union_all_recurse((Node *) topop, subquery,
|
||||||
|
topop->colTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
is_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes)
|
||||||
|
{
|
||||||
|
if (IsA(setOp, RangeTblRef))
|
||||||
|
{
|
||||||
|
RangeTblRef *rtr = (RangeTblRef *) setOp;
|
||||||
|
RangeTblEntry *rte = rt_fetch(rtr->rtindex, setOpQuery->rtable);
|
||||||
|
Query *subquery = rte->subquery;
|
||||||
|
|
||||||
|
Assert(subquery != NULL);
|
||||||
|
|
||||||
|
/* Leaf nodes are OK if they match the toplevel column types */
|
||||||
|
return tlist_same_datatypes(subquery->targetList, colTypes, true);
|
||||||
|
}
|
||||||
|
else if (IsA(setOp, SetOperationStmt))
|
||||||
|
{
|
||||||
|
SetOperationStmt *op = (SetOperationStmt *) setOp;
|
||||||
|
|
||||||
|
/* Must be UNION ALL */
|
||||||
|
if (op->op != SETOP_UNION || !op->all)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Recurse to check inputs */
|
||||||
|
return is_simple_union_all_recurse(op->larg, setOpQuery, colTypes) &&
|
||||||
|
is_simple_union_all_recurse(op->rarg, setOpQuery, colTypes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
elog(ERROR, "unrecognized node type: %d",
|
||||||
|
(int) nodeTag(setOp));
|
||||||
|
return false; /* keep compiler quiet */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* has_nullable_targetlist
|
* has_nullable_targetlist
|
||||||
* Check a subquery in the range table to see if all the non-junk
|
* Check a subquery in the range table to see if all the non-junk
|
||||||
|
@ -521,6 +806,62 @@ has_nullable_targetlist(Query *subquery)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* is_safe_append_member
|
||||||
|
* Check a subquery that is a leaf of a UNION ALL appendrel to see if it's
|
||||||
|
* safe to pull up.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
is_safe_append_member(Query *subquery)
|
||||||
|
{
|
||||||
|
FromExpr *jtnode;
|
||||||
|
ListCell *l;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It's only safe to pull up the child if its jointree contains
|
||||||
|
* exactly one RTE, else the AppendRelInfo data structure breaks.
|
||||||
|
* The one base RTE could be buried in several levels of FromExpr,
|
||||||
|
* however.
|
||||||
|
*
|
||||||
|
* Also, the child can't have any WHERE quals because there's no
|
||||||
|
* place to put them in an appendrel. (This is a bit annoying...)
|
||||||
|
* If we didn't need to check this, we'd just test whether
|
||||||
|
* get_relids_in_jointree() yields a singleton set, to be more
|
||||||
|
* consistent with the coding of fix_append_rel_relids().
|
||||||
|
*/
|
||||||
|
jtnode = subquery->jointree;
|
||||||
|
while (IsA(jtnode, FromExpr))
|
||||||
|
{
|
||||||
|
if (jtnode->quals != NULL)
|
||||||
|
return false;
|
||||||
|
if (list_length(jtnode->fromlist) != 1)
|
||||||
|
return false;
|
||||||
|
jtnode = linitial(jtnode->fromlist);
|
||||||
|
}
|
||||||
|
if (!IsA(jtnode, RangeTblRef))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX For the moment we also have to insist that the subquery's tlist
|
||||||
|
* includes only simple Vars. This is pretty annoying, but fixing it
|
||||||
|
* seems to require nontrivial changes --- mainly because joinrel
|
||||||
|
* tlists are presently assumed to contain only Vars. Perhaps a
|
||||||
|
* pseudo-variable mechanism similar to the one speculated about
|
||||||
|
* in pull_up_subqueries' comments would help? FIXME someday.
|
||||||
|
*/
|
||||||
|
foreach(l, subquery->targetList)
|
||||||
|
{
|
||||||
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
||||||
|
|
||||||
|
if (tle->resjunk)
|
||||||
|
continue;
|
||||||
|
if (!(tle->expr && IsA(tle->expr, Var)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper routine for pull_up_subqueries: do ResolveNew on every expression
|
* Helper routine for pull_up_subqueries: do ResolveNew on every expression
|
||||||
* in the jointree, without changing the jointree structure itself. Ugly,
|
* in the jointree, without changing the jointree structure itself. Ugly,
|
||||||
|
@ -851,6 +1192,43 @@ fix_in_clause_relids(List *in_info_list, int varno, Relids subrelids)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fix_append_rel_relids: update RT-index fields of AppendRelInfo nodes
|
||||||
|
*
|
||||||
|
* When we pull up a subquery, any AppendRelInfo references to the subquery's
|
||||||
|
* RT index have to be replaced by the substituted relid (and there had better
|
||||||
|
* be only one).
|
||||||
|
*
|
||||||
|
* We assume we may modify the AppendRelInfo nodes in-place.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
fix_append_rel_relids(List *append_rel_list, int varno, Relids subrelids)
|
||||||
|
{
|
||||||
|
ListCell *l;
|
||||||
|
int subvarno = -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We only want to extract the member relid once, but we mustn't fail
|
||||||
|
* immediately if there are multiple members; it could be that none of
|
||||||
|
* the AppendRelInfo nodes refer to it. So compute it on first use.
|
||||||
|
* Note that bms_singleton_member will complain if set is not singleton.
|
||||||
|
*/
|
||||||
|
foreach(l, append_rel_list)
|
||||||
|
{
|
||||||
|
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
|
||||||
|
|
||||||
|
/* The parent_relid shouldn't ever be a pullup target */
|
||||||
|
Assert(appinfo->parent_relid != varno);
|
||||||
|
|
||||||
|
if (appinfo->child_relid == varno)
|
||||||
|
{
|
||||||
|
if (subvarno < 0)
|
||||||
|
subvarno = bms_singleton_member(subrelids);
|
||||||
|
appinfo->child_relid = subvarno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_relids_in_jointree: get set of base RT indexes present in a jointree
|
* get_relids_in_jointree: get set of base RT indexes present in a jointree
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -4,13 +4,17 @@
|
||||||
* Routines to plan set-operation queries. The filename is a leftover
|
* Routines to plan set-operation queries. The filename is a leftover
|
||||||
* from a time when only UNIONs were implemented.
|
* from a time when only UNIONs were implemented.
|
||||||
*
|
*
|
||||||
|
* There are two code paths in the planner for set-operation queries.
|
||||||
|
* If a subquery consists entirely of simple UNION ALL operations, it
|
||||||
|
* is converted into an "append relation". Otherwise, it is handled
|
||||||
|
* by the general code in this module (plan_set_operations and its
|
||||||
|
* subroutines). There is some support code here for the append-relation
|
||||||
|
* case, but most of the heavy lifting for that is done elsewhere,
|
||||||
|
* notably in prepjointree.c and allpaths.c.
|
||||||
|
*
|
||||||
* There is also some code here to support planning of queries that use
|
* There is also some code here to support planning of queries that use
|
||||||
* inheritance (SELECT FROM foo*). Although inheritance is radically
|
* inheritance (SELECT FROM foo*). Inheritance trees are converted into
|
||||||
* different from set operations as far as the parser representation of
|
* append relations, and thenceforth share code with the UNION ALL case.
|
||||||
* a query is concerned, we try to handle it identically to the UNION ALL
|
|
||||||
* case during planning: both are converted to "append rels". (Note that
|
|
||||||
* UNION ALL is special-cased: other kinds of set operations go through
|
|
||||||
* a completely different code path.)
|
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
|
@ -18,7 +22,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.129 2006/01/31 21:39:24 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.130 2006/02/03 21:08:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -64,12 +68,13 @@ static List *generate_setop_tlist(List *colTypes, int flag,
|
||||||
static List *generate_append_tlist(List *colTypes, bool flag,
|
static List *generate_append_tlist(List *colTypes, bool flag,
|
||||||
List *input_plans,
|
List *input_plans,
|
||||||
List *refnames_tlist);
|
List *refnames_tlist);
|
||||||
static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
|
|
||||||
static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,
|
static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,
|
||||||
Index rti);
|
Index rti);
|
||||||
static void make_translation_lists(Relation oldrelation, Relation newrelation,
|
static void make_inh_translation_lists(Relation oldrelation,
|
||||||
|
Relation newrelation,
|
||||||
Index newvarno,
|
Index newvarno,
|
||||||
List **col_mappings, List **translated_vars);
|
List **col_mappings,
|
||||||
|
List **translated_vars);
|
||||||
static Node *adjust_appendrel_attrs_mutator(Node *node,
|
static Node *adjust_appendrel_attrs_mutator(Node *node,
|
||||||
AppendRelInfo *context);
|
AppendRelInfo *context);
|
||||||
static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);
|
static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);
|
||||||
|
@ -659,41 +664,6 @@ generate_append_tlist(List *colTypes, bool flag,
|
||||||
return tlist;
|
return tlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Does tlist have same datatypes as requested colTypes?
|
|
||||||
*
|
|
||||||
* Resjunk columns are ignored if junkOK is true; otherwise presence of
|
|
||||||
* a resjunk column will always cause a 'false' result.
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
|
|
||||||
{
|
|
||||||
ListCell *l;
|
|
||||||
ListCell *curColType = list_head(colTypes);
|
|
||||||
|
|
||||||
foreach(l, tlist)
|
|
||||||
{
|
|
||||||
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
|
||||||
|
|
||||||
if (tle->resjunk)
|
|
||||||
{
|
|
||||||
if (!junkOK)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (curColType == NULL)
|
|
||||||
return false;
|
|
||||||
if (exprType((Node *) tle->expr) != lfirst_oid(curColType))
|
|
||||||
return false;
|
|
||||||
curColType = lnext(curColType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (curColType != NULL)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* find_all_inheritors -
|
* find_all_inheritors -
|
||||||
|
@ -896,7 +866,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
|
||||||
appinfo->child_relid = childRTindex;
|
appinfo->child_relid = childRTindex;
|
||||||
appinfo->parent_reltype = oldrelation->rd_rel->reltype;
|
appinfo->parent_reltype = oldrelation->rd_rel->reltype;
|
||||||
appinfo->child_reltype = newrelation->rd_rel->reltype;
|
appinfo->child_reltype = newrelation->rd_rel->reltype;
|
||||||
make_translation_lists(oldrelation, newrelation, childRTindex,
|
make_inh_translation_lists(oldrelation, newrelation, childRTindex,
|
||||||
&appinfo->col_mappings,
|
&appinfo->col_mappings,
|
||||||
&appinfo->translated_vars);
|
&appinfo->translated_vars);
|
||||||
appinfo->parent_reloid = parentOID;
|
appinfo->parent_reloid = parentOID;
|
||||||
|
@ -933,7 +903,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* make_translation_lists
|
* make_inh_translation_lists
|
||||||
* Build the lists of translations from parent Vars to child Vars for
|
* Build the lists of translations from parent Vars to child Vars for
|
||||||
* an inheritance child. We need both a column number mapping list
|
* an inheritance child. We need both a column number mapping list
|
||||||
* and a list of Vars representing the child columns.
|
* and a list of Vars representing the child columns.
|
||||||
|
@ -941,7 +911,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
|
||||||
* For paranoia's sake, we match type as well as attribute name.
|
* For paranoia's sake, we match type as well as attribute name.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
make_translation_lists(Relation oldrelation, Relation newrelation,
|
make_inh_translation_lists(Relation oldrelation, Relation newrelation,
|
||||||
Index newvarno,
|
Index newvarno,
|
||||||
List **col_mappings, List **translated_vars)
|
List **col_mappings, List **translated_vars)
|
||||||
{
|
{
|
||||||
|
@ -1123,8 +1093,18 @@ adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* XXX copy some code from ResolveNew */
|
/*
|
||||||
Assert(false);/* not done yet */
|
* Build a RowExpr containing the translated variables.
|
||||||
|
*/
|
||||||
|
RowExpr *rowexpr;
|
||||||
|
List *fields;
|
||||||
|
|
||||||
|
fields = (List *) copyObject(context->translated_vars);
|
||||||
|
rowexpr = makeNode(RowExpr);
|
||||||
|
rowexpr->args = fields;
|
||||||
|
rowexpr->row_typeid = var->vartype;
|
||||||
|
rowexpr->row_format = COERCE_IMPLICIT_CAST;
|
||||||
|
return (Node *) rowexpr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* system attributes don't need any other translation */
|
/* system attributes don't need any other translation */
|
||||||
|
@ -1337,45 +1317,6 @@ adjust_appendrel_attr_needed(RelOptInfo *oldrel, AppendRelInfo *appinfo,
|
||||||
return new_attr_needed;
|
return new_attr_needed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* adjust_other_rel_attr_needed
|
|
||||||
* Adjust an attr_needed[] array to reference a member rel instead of
|
|
||||||
* the original appendrel
|
|
||||||
*
|
|
||||||
* This is exactly like adjust_appendrel_attr_needed except that we disregard
|
|
||||||
* appinfo->col_mappings and instead assume that the mapping of user
|
|
||||||
* attributes is one-to-one. This is appropriate for generating an attr_needed
|
|
||||||
* array that describes another relation to be joined with a member rel.
|
|
||||||
*/
|
|
||||||
Relids *
|
|
||||||
adjust_other_rel_attr_needed(RelOptInfo *oldrel, AppendRelInfo *appinfo,
|
|
||||||
AttrNumber new_min_attr, AttrNumber new_max_attr)
|
|
||||||
{
|
|
||||||
Relids *new_attr_needed;
|
|
||||||
Index parent_relid = appinfo->parent_relid;
|
|
||||||
Index child_relid = appinfo->child_relid;
|
|
||||||
int parent_attr;
|
|
||||||
|
|
||||||
/* Create empty result array */
|
|
||||||
Assert(new_min_attr <= oldrel->min_attr);
|
|
||||||
Assert(new_max_attr >= oldrel->max_attr);
|
|
||||||
new_attr_needed = (Relids *)
|
|
||||||
palloc0((new_max_attr - new_min_attr + 1) * sizeof(Relids));
|
|
||||||
/* Process user attributes and system attributes */
|
|
||||||
for (parent_attr = oldrel->min_attr; parent_attr <= oldrel->max_attr;
|
|
||||||
parent_attr++)
|
|
||||||
{
|
|
||||||
Relids attrneeded;
|
|
||||||
|
|
||||||
attrneeded = oldrel->attr_needed[parent_attr - oldrel->min_attr];
|
|
||||||
attrneeded = adjust_relid_set(attrneeded,
|
|
||||||
parent_relid, child_relid);
|
|
||||||
new_attr_needed[parent_attr - new_min_attr] = attrneeded;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new_attr_needed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Adjust the targetlist entries of an inherited UPDATE operation
|
* Adjust the targetlist entries of an inherited UPDATE operation
|
||||||
*
|
*
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.75 2006/01/31 21:39:24 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.76 2006/02/03 21:08:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -18,6 +18,7 @@
|
||||||
#include "optimizer/joininfo.h"
|
#include "optimizer/joininfo.h"
|
||||||
#include "optimizer/pathnode.h"
|
#include "optimizer/pathnode.h"
|
||||||
#include "optimizer/plancat.h"
|
#include "optimizer/plancat.h"
|
||||||
|
#include "optimizer/prep.h"
|
||||||
#include "optimizer/restrictinfo.h"
|
#include "optimizer/restrictinfo.h"
|
||||||
#include "optimizer/tlist.h"
|
#include "optimizer/tlist.h"
|
||||||
#include "parser/parsetree.h"
|
#include "parser/parsetree.h"
|
||||||
|
@ -570,3 +571,144 @@ subbuild_joinrel_joinlist(RelOptInfo *joinrel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* translate_join_rel
|
||||||
|
* Returns relation entry corresponding to the union of two given rels,
|
||||||
|
* creating a new relation entry if none already exists. This is used
|
||||||
|
* when one of the inputs is an append child relation. In addition to
|
||||||
|
* data about the input rels themselves, the corresponding joinrel for
|
||||||
|
* the append parent relation must be provided, plus the AppendRelInfo
|
||||||
|
* showing the parent-to-child translation.
|
||||||
|
*
|
||||||
|
* The reason for having this code, instead of just applying build_join_rel,
|
||||||
|
* is that we must have corresponding tlist orderings for all joinrels that
|
||||||
|
* are involved in an Append plan. So we generate the tlist for joinrels
|
||||||
|
* involving append child relations by translating the parent joinrel's tlist,
|
||||||
|
* rather than examining the input relations directly. (Another reason for
|
||||||
|
* doing it this way is that the base relation attr_needed info in relations
|
||||||
|
* being joined to the appendrel doesn't refer to the append child rel, but
|
||||||
|
* the append parent, and so couldn't be used directly anyway.) Otherwise
|
||||||
|
* this is exactly like build_join_rel.
|
||||||
|
*/
|
||||||
|
RelOptInfo *
|
||||||
|
translate_join_rel(PlannerInfo *root,
|
||||||
|
RelOptInfo *oldjoinrel,
|
||||||
|
AppendRelInfo *appinfo,
|
||||||
|
RelOptInfo *outer_rel,
|
||||||
|
RelOptInfo *inner_rel,
|
||||||
|
JoinType jointype,
|
||||||
|
List **restrictlist_ptr)
|
||||||
|
{
|
||||||
|
RelOptInfo *joinrel;
|
||||||
|
Relids joinrelids;
|
||||||
|
List *restrictlist;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Construct the Relids set for the translated joinrel, and see if
|
||||||
|
* we've already built it.
|
||||||
|
*/
|
||||||
|
joinrelids = bms_copy(oldjoinrel->relids);
|
||||||
|
joinrelids = bms_del_member(joinrelids, appinfo->parent_relid);
|
||||||
|
joinrelids = bms_add_member(joinrelids, appinfo->child_relid);
|
||||||
|
joinrel = find_join_rel(root, joinrelids);
|
||||||
|
if (joinrel)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Yes, so we only need to figure the restrictlist for this particular
|
||||||
|
* pair of component relations.
|
||||||
|
*/
|
||||||
|
bms_free(joinrelids);
|
||||||
|
if (restrictlist_ptr)
|
||||||
|
*restrictlist_ptr = build_joinrel_restrictlist(root,
|
||||||
|
joinrel,
|
||||||
|
outer_rel,
|
||||||
|
inner_rel,
|
||||||
|
jointype);
|
||||||
|
return joinrel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nope, so make one.
|
||||||
|
*/
|
||||||
|
joinrel = makeNode(RelOptInfo);
|
||||||
|
joinrel->reloptkind = RELOPT_JOINREL;
|
||||||
|
joinrel->relids = joinrelids;
|
||||||
|
joinrel->rows = 0;
|
||||||
|
joinrel->width = 0;
|
||||||
|
joinrel->reltargetlist = NIL;
|
||||||
|
joinrel->pathlist = NIL;
|
||||||
|
joinrel->cheapest_startup_path = NULL;
|
||||||
|
joinrel->cheapest_total_path = NULL;
|
||||||
|
joinrel->cheapest_unique_path = NULL;
|
||||||
|
joinrel->relid = 0; /* indicates not a baserel */
|
||||||
|
joinrel->rtekind = RTE_JOIN;
|
||||||
|
joinrel->min_attr = 0;
|
||||||
|
joinrel->max_attr = 0;
|
||||||
|
joinrel->attr_needed = NULL;
|
||||||
|
joinrel->attr_widths = NULL;
|
||||||
|
joinrel->indexlist = NIL;
|
||||||
|
joinrel->pages = 0;
|
||||||
|
joinrel->tuples = 0;
|
||||||
|
joinrel->subplan = NULL;
|
||||||
|
joinrel->baserestrictinfo = NIL;
|
||||||
|
joinrel->baserestrictcost.startup = 0;
|
||||||
|
joinrel->baserestrictcost.per_tuple = 0;
|
||||||
|
joinrel->joininfo = NIL;
|
||||||
|
joinrel->index_outer_relids = NULL;
|
||||||
|
joinrel->index_inner_paths = NIL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make the tlist by translating oldjoinrel's tlist, to ensure they
|
||||||
|
* are in compatible orders. Since we don't call build_joinrel_tlist,
|
||||||
|
* we need another way to set the rel width; for the moment, just
|
||||||
|
* assume it is the same as oldjoinrel. (The correct value may well be
|
||||||
|
* less, but it's not clear it's worth the trouble to get it right.)
|
||||||
|
*/
|
||||||
|
joinrel->reltargetlist = (List *)
|
||||||
|
adjust_appendrel_attrs((Node *) oldjoinrel->reltargetlist,
|
||||||
|
appinfo);
|
||||||
|
joinrel->width = oldjoinrel->width;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Construct restrict and join clause lists for the new joinrel. (The
|
||||||
|
* caller might or might not need the restrictlist, but I need it anyway
|
||||||
|
* for set_joinrel_size_estimates().)
|
||||||
|
*/
|
||||||
|
restrictlist = build_joinrel_restrictlist(root,
|
||||||
|
joinrel,
|
||||||
|
outer_rel,
|
||||||
|
inner_rel,
|
||||||
|
jointype);
|
||||||
|
if (restrictlist_ptr)
|
||||||
|
*restrictlist_ptr = restrictlist;
|
||||||
|
build_joinrel_joinlist(joinrel, outer_rel, inner_rel);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set estimates of the joinrel's size.
|
||||||
|
*/
|
||||||
|
set_joinrel_size_estimates(root, joinrel, outer_rel, inner_rel,
|
||||||
|
jointype, restrictlist);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the joinrel to the query's joinrel list, and store it into the
|
||||||
|
* auxiliary hashtable if there is one. NB: GEQO requires us to append
|
||||||
|
* the new joinrel to the end of the list!
|
||||||
|
*/
|
||||||
|
root->join_rel_list = lappend(root->join_rel_list, joinrel);
|
||||||
|
|
||||||
|
if (root->join_rel_hash)
|
||||||
|
{
|
||||||
|
JoinHashEntry *hentry;
|
||||||
|
bool found;
|
||||||
|
|
||||||
|
hentry = (JoinHashEntry *) hash_search(root->join_rel_hash,
|
||||||
|
&(joinrel->relids),
|
||||||
|
HASH_ENTER,
|
||||||
|
&found);
|
||||||
|
Assert(!found);
|
||||||
|
hentry->join_rel = joinrel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return joinrel;
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.70 2005/10/15 02:49:21 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.71 2006/02/03 21:08:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -167,3 +167,39 @@ get_sortgrouplist_exprs(List *sortClauses, List *targetList)
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Does tlist have same output datatypes as listed in colTypes?
|
||||||
|
*
|
||||||
|
* Resjunk columns are ignored if junkOK is true; otherwise presence of
|
||||||
|
* a resjunk column will always cause a 'false' result.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
|
||||||
|
{
|
||||||
|
ListCell *l;
|
||||||
|
ListCell *curColType = list_head(colTypes);
|
||||||
|
|
||||||
|
foreach(l, tlist)
|
||||||
|
{
|
||||||
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
||||||
|
|
||||||
|
if (tle->resjunk)
|
||||||
|
{
|
||||||
|
if (!junkOK)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (curColType == NULL)
|
||||||
|
return false; /* tlist longer than colTypes */
|
||||||
|
if (exprType((Node *) tle->expr) != lfirst_oid(curColType))
|
||||||
|
return false;
|
||||||
|
curColType = lnext(curColType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (curColType != NULL)
|
||||||
|
return false; /* tlist shorter than colTypes */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.64 2006/01/31 21:39:25 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.65 2006/02/03 21:08:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -95,5 +95,12 @@ extern RelOptInfo *build_join_rel(PlannerInfo *root,
|
||||||
RelOptInfo *inner_rel,
|
RelOptInfo *inner_rel,
|
||||||
JoinType jointype,
|
JoinType jointype,
|
||||||
List **restrictlist_ptr);
|
List **restrictlist_ptr);
|
||||||
|
extern RelOptInfo *translate_join_rel(PlannerInfo *root,
|
||||||
|
RelOptInfo *oldjoinrel,
|
||||||
|
AppendRelInfo *appinfo,
|
||||||
|
RelOptInfo *outer_rel,
|
||||||
|
RelOptInfo *inner_rel,
|
||||||
|
JoinType jointype,
|
||||||
|
List **restrictlist_ptr);
|
||||||
|
|
||||||
#endif /* PATHNODE_H */
|
#endif /* PATHNODE_H */
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.54 2006/01/31 21:39:25 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.55 2006/02/03 21:08:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
*/
|
*/
|
||||||
extern Node *pull_up_IN_clauses(PlannerInfo *root, Node *node);
|
extern Node *pull_up_IN_clauses(PlannerInfo *root, Node *node);
|
||||||
extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode,
|
extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode,
|
||||||
bool below_outer_join);
|
bool below_outer_join, bool append_rel_member);
|
||||||
extern void reduce_outer_joins(PlannerInfo *root);
|
extern void reduce_outer_joins(PlannerInfo *root);
|
||||||
extern Relids get_relids_in_jointree(Node *jtnode);
|
extern Relids get_relids_in_jointree(Node *jtnode);
|
||||||
extern Relids get_relids_for_join(PlannerInfo *root, int joinrelid);
|
extern Relids get_relids_for_join(PlannerInfo *root, int joinrelid);
|
||||||
|
@ -55,9 +55,4 @@ extern Relids *adjust_appendrel_attr_needed(RelOptInfo *oldrel,
|
||||||
AttrNumber new_min_attr,
|
AttrNumber new_min_attr,
|
||||||
AttrNumber new_max_attr);
|
AttrNumber new_max_attr);
|
||||||
|
|
||||||
extern Relids *adjust_other_rel_attr_needed(RelOptInfo *oldrel,
|
|
||||||
AppendRelInfo *appinfo,
|
|
||||||
AttrNumber new_min_attr,
|
|
||||||
AttrNumber new_max_attr);
|
|
||||||
|
|
||||||
#endif /* PREP_H */
|
#endif /* PREP_H */
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/optimizer/tlist.h,v 1.42 2005/04/06 16:34:07 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/optimizer/tlist.h,v 1.43 2006/02/03 21:08:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -29,4 +29,6 @@ extern Node *get_sortgroupclause_expr(SortClause *sortClause,
|
||||||
extern List *get_sortgrouplist_exprs(List *sortClauses,
|
extern List *get_sortgrouplist_exprs(List *sortClauses,
|
||||||
List *targetList);
|
List *targetList);
|
||||||
|
|
||||||
|
extern bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
|
||||||
|
|
||||||
#endif /* TLIST_H */
|
#endif /* TLIST_H */
|
||||||
|
|
Loading…
Reference in New Issue