Commit 5f677af2 authored by Tom Lane's avatar Tom Lane

Adjust subquery qual pushdown rules so that we can push down a qual

into a UNION that has some type coercions applied to the component
queries, so long as the qual itself does not reference any columns that
have such coercions.  Per example from Jonathan Bartlett 24-Apr-03.
parent 555fe9dd
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.101 2003/03/22 17:11:25 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.102 2003/04/24 23:43:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -49,9 +49,14 @@ static void set_function_pathlist(Query *root, RelOptInfo *rel, ...@@ -49,9 +49,14 @@ static void set_function_pathlist(Query *root, RelOptInfo *rel,
RangeTblEntry *rte); RangeTblEntry *rte);
static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed, static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
List *initial_rels); List *initial_rels);
static bool subquery_is_pushdown_safe(Query *subquery, Query *topquery); static bool subquery_is_pushdown_safe(Query *subquery, Query *topquery,
static bool recurse_pushdown_safe(Node *setOp, Query *topquery); bool *differentTypes);
static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual); static bool recurse_pushdown_safe(Node *setOp, Query *topquery,
bool *differentTypes);
static void compare_tlist_datatypes(List *tlist, List *colTypes,
bool *differentTypes);
static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
bool *differentTypes);
static void subquery_push_qual(Query *subquery, Index rti, Node *qual); static void subquery_push_qual(Query *subquery, Index rti, Node *qual);
static void recurse_push_qual(Node *setOp, Query *topquery, static void recurse_push_qual(Node *setOp, Query *topquery,
Index rti, Node *qual); Index rti, Node *qual);
...@@ -294,8 +299,13 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel, ...@@ -294,8 +299,13 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte) Index rti, RangeTblEntry *rte)
{ {
Query *subquery = rte->subquery; Query *subquery = rte->subquery;
bool *differentTypes;
List *pathkeys; List *pathkeys;
/* We need a workspace for keeping track of set-op type coercions */
differentTypes = (bool *)
palloc0((length(subquery->targetList) + 1) * sizeof(bool));
/* /*
* If there are any restriction clauses that have been attached to the * If there are any restriction clauses that have been attached to the
* subquery relation, consider pushing them down to become HAVING * subquery relation, consider pushing them down to become HAVING
...@@ -318,7 +328,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel, ...@@ -318,7 +328,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
* push down a pushable qual, because it'd result in a worse plan? * push down a pushable qual, because it'd result in a worse plan?
*/ */
if (rel->baserestrictinfo != NIL && if (rel->baserestrictinfo != NIL &&
subquery_is_pushdown_safe(subquery, subquery)) subquery_is_pushdown_safe(subquery, subquery, differentTypes))
{ {
/* OK to consider pushing down individual quals */ /* OK to consider pushing down individual quals */
List *upperrestrictlist = NIL; List *upperrestrictlist = NIL;
...@@ -329,7 +339,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel, ...@@ -329,7 +339,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lst); RestrictInfo *rinfo = (RestrictInfo *) lfirst(lst);
Node *clause = (Node *) rinfo->clause; Node *clause = (Node *) rinfo->clause;
if (qual_is_pushdown_safe(subquery, rti, clause)) if (qual_is_pushdown_safe(subquery, rti, clause, differentTypes))
{ {
/* Push it down */ /* Push it down */
subquery_push_qual(subquery, rti, clause); subquery_push_qual(subquery, rti, clause);
...@@ -343,6 +353,8 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel, ...@@ -343,6 +353,8 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
rel->baserestrictinfo = upperrestrictlist; rel->baserestrictinfo = upperrestrictlist;
} }
pfree(differentTypes);
/* Generate the plan for the subquery */ /* Generate the plan for the subquery */
rel->subplan = subquery_planner(subquery, 0.0 /* default case */ ); rel->subplan = subquery_planner(subquery, 0.0 /* default case */ );
...@@ -532,15 +544,19 @@ make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels) ...@@ -532,15 +544,19 @@ make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels)
* since that could change the set of rows returned. * since that could change the set of rows returned.
* *
* 2. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push * 2. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push
* quals into it, because that would change the results. For subqueries * quals into it, because that would change the results.
* using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can push the quals *
* into each component query, so long as all the component queries share * 3. For subqueries using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can
* identical output types. (That restriction could probably be relaxed, * push quals into each component query, but the quals can only reference
* but it would take much more code to include type coercion code into * subquery columns that suffer no type coercions in the set operation.
* the quals, and I'm also concerned about possible semantic gotchas.) * Otherwise there are possible semantic gotchas. So, we check the
* component queries to see if any of them have different output types;
* differentTypes[k] is set true if column k has different type in any
* component.
*/ */
static bool static bool
subquery_is_pushdown_safe(Query *subquery, Query *topquery) subquery_is_pushdown_safe(Query *subquery, Query *topquery,
bool *differentTypes)
{ {
SetOperationStmt *topop; SetOperationStmt *topop;
...@@ -553,7 +569,8 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery) ...@@ -553,7 +569,8 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery)
{ {
/* Top level, so check any component queries */ /* Top level, so check any component queries */
if (subquery->setOperations != NULL) if (subquery->setOperations != NULL)
if (!recurse_pushdown_safe(subquery->setOperations, topquery)) if (!recurse_pushdown_safe(subquery->setOperations, topquery,
differentTypes))
return false; return false;
} }
else else
...@@ -561,14 +578,12 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery) ...@@ -561,14 +578,12 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery)
/* Setop component must not have more components (too weird) */ /* Setop component must not have more components (too weird) */
if (subquery->setOperations != NULL) if (subquery->setOperations != NULL)
return false; return false;
/* Setop component output types must match top level */ /* Check whether setop component output types match top level */
topop = (SetOperationStmt *) topquery->setOperations; topop = (SetOperationStmt *) topquery->setOperations;
Assert(topop && IsA(topop, SetOperationStmt)); Assert(topop && IsA(topop, SetOperationStmt));
if (!tlist_same_datatypes(subquery->targetList, compare_tlist_datatypes(subquery->targetList,
topop->colTypes, topop->colTypes,
true)) differentTypes);
return false;
} }
return true; return true;
} }
...@@ -577,7 +592,8 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery) ...@@ -577,7 +592,8 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery)
* Helper routine to recurse through setOperations tree * Helper routine to recurse through setOperations tree
*/ */
static bool static bool
recurse_pushdown_safe(Node *setOp, Query *topquery) recurse_pushdown_safe(Node *setOp, Query *topquery,
bool *differentTypes)
{ {
if (IsA(setOp, RangeTblRef)) if (IsA(setOp, RangeTblRef))
{ {
...@@ -586,7 +602,7 @@ recurse_pushdown_safe(Node *setOp, Query *topquery) ...@@ -586,7 +602,7 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
Query *subquery = rte->subquery; Query *subquery = rte->subquery;
Assert(subquery != NULL); Assert(subquery != NULL);
return subquery_is_pushdown_safe(subquery, topquery); return subquery_is_pushdown_safe(subquery, topquery, differentTypes);
} }
else if (IsA(setOp, SetOperationStmt)) else if (IsA(setOp, SetOperationStmt))
{ {
...@@ -596,9 +612,9 @@ recurse_pushdown_safe(Node *setOp, Query *topquery) ...@@ -596,9 +612,9 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
if (op->op == SETOP_EXCEPT) if (op->op == SETOP_EXCEPT)
return false; return false;
/* Else recurse */ /* Else recurse */
if (!recurse_pushdown_safe(op->larg, topquery)) if (!recurse_pushdown_safe(op->larg, topquery, differentTypes))
return false; return false;
if (!recurse_pushdown_safe(op->rarg, topquery)) if (!recurse_pushdown_safe(op->rarg, topquery, differentTypes))
return false; return false;
} }
else else
...@@ -609,6 +625,33 @@ recurse_pushdown_safe(Node *setOp, Query *topquery) ...@@ -609,6 +625,33 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
return true; return true;
} }
/*
* Compare tlist's datatypes against the list of set-operation result types.
* For any items that are different, mark the appropriate element of
* differentTypes[] to show that this column will have type conversions.
*/
static void
compare_tlist_datatypes(List *tlist, List *colTypes,
bool *differentTypes)
{
List *i;
foreach(i, tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(i);
if (tle->resdom->resjunk)
continue; /* ignore resjunk columns */
if (colTypes == NIL)
elog(ERROR, "wrong number of tlist entries");
if (tle->resdom->restype != lfirsto(colTypes))
differentTypes[tle->resdom->resno] = true;
colTypes = lnext(colTypes);
}
if (colTypes != NIL)
elog(ERROR, "wrong number of tlist entries");
}
/* /*
* qual_is_pushdown_safe - is a particular qual safe to push down? * qual_is_pushdown_safe - is a particular qual safe to push down?
* *
...@@ -621,7 +664,11 @@ recurse_pushdown_safe(Node *setOp, Query *topquery) ...@@ -621,7 +664,11 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
* it will work correctly: sublinks will already have been transformed into * it will work correctly: sublinks will already have been transformed into
* subplans in the qual, but not in the subquery). * subplans in the qual, but not in the subquery).
* *
* 2. If the subquery uses DISTINCT ON, we must not push down any quals that * 2. The qual must not refer to any subquery output columns that were
* found to have inconsistent types across a set operation tree by
* subquery_is_pushdown_safe().
*
* 3. If the subquery uses DISTINCT ON, we must not push down any quals that
* refer to non-DISTINCT output columns, because that could change the set * refer to non-DISTINCT output columns, because that could change the set
* of rows returned. This condition is vacuous for DISTINCT, because then * of rows returned. This condition is vacuous for DISTINCT, because then
* there are no non-DISTINCT output columns, but unfortunately it's fairly * there are no non-DISTINCT output columns, but unfortunately it's fairly
...@@ -629,12 +676,13 @@ recurse_pushdown_safe(Node *setOp, Query *topquery) ...@@ -629,12 +676,13 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
* parsetree representation. It's cheaper to just make sure all the Vars * parsetree representation. It's cheaper to just make sure all the Vars
* in the qual refer to DISTINCT columns. * in the qual refer to DISTINCT columns.
* *
* 3. We must not push down any quals that refer to subselect outputs that * 4. We must not push down any quals that refer to subselect outputs that
* return sets, else we'd introduce functions-returning-sets into the * return sets, else we'd introduce functions-returning-sets into the
* subquery's WHERE/HAVING quals. * subquery's WHERE/HAVING quals.
*/ */
static bool static bool
qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual) qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
bool *differentTypes)
{ {
bool safe = true; bool safe = true;
List *vars; List *vars;
...@@ -666,6 +714,14 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual) ...@@ -666,6 +714,14 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual)
continue; continue;
tested = bms_add_member(tested, var->varattno); tested = bms_add_member(tested, var->varattno);
/* Check point 2 */
if (differentTypes[var->varattno])
{
safe = false;
break;
}
/* Must find the tlist element referenced by the Var */
foreach(tl, subquery->targetList) foreach(tl, subquery->targetList)
{ {
tle = (TargetEntry *) lfirst(tl); tle = (TargetEntry *) lfirst(tl);
...@@ -675,7 +731,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual) ...@@ -675,7 +731,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual)
Assert(tl != NIL); Assert(tl != NIL);
Assert(!tle->resdom->resjunk); Assert(!tle->resdom->resjunk);
/* If subquery uses DISTINCT or DISTINCT ON, check point 2 */ /* If subquery uses DISTINCT or DISTINCT ON, check point 3 */
if (subquery->distinctClause != NIL && if (subquery->distinctClause != NIL &&
!targetIsInSortList(tle, subquery->distinctClause)) !targetIsInSortList(tle, subquery->distinctClause))
{ {
...@@ -684,7 +740,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual) ...@@ -684,7 +740,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual)
break; break;
} }
/* Refuse functions returning sets (point 3) */ /* Refuse functions returning sets (point 4) */
if (expression_returns_set((Node *) tle->expr)) if (expression_returns_set((Node *) tle->expr))
{ {
safe = false; safe = false;
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.92 2003/03/10 03:53:50 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.93 2003/04/24 23:43:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -60,6 +60,7 @@ static List *generate_setop_tlist(List *colTypes, int flag, ...@@ -60,6 +60,7 @@ 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 Node *adjust_inherited_attrs_mutator(Node *node, static Node *adjust_inherited_attrs_mutator(Node *node,
adjust_inherited_attrs_context *context); adjust_inherited_attrs_context *context);
static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid); static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);
...@@ -572,7 +573,7 @@ generate_append_tlist(List *colTypes, bool flag, ...@@ -572,7 +573,7 @@ generate_append_tlist(List *colTypes, bool flag,
* Resjunk columns are ignored if junkOK is true; otherwise presence of * Resjunk columns are ignored if junkOK is true; otherwise presence of
* a resjunk column will always cause a 'false' result. * a resjunk column will always cause a 'false' result.
*/ */
bool static bool
tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK) tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
{ {
List *i; List *i;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: prep.h,v 1.38 2003/03/05 20:01:04 tgl Exp $ * $Id: prep.h,v 1.39 2003/04/24 23:43:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -59,6 +59,4 @@ extern Node *adjust_inherited_attrs(Node *node, ...@@ -59,6 +59,4 @@ extern Node *adjust_inherited_attrs(Node *node,
Index old_rt_index, Oid old_relid, Index old_rt_index, Oid old_relid,
Index new_rt_index, Oid new_relid); Index new_rt_index, Oid new_relid);
extern bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
#endif /* PREP_H */ #endif /* PREP_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment