Subqueries containing functions go through subquery pushdown

pull/1834/head
Marco Slot 2017-11-21 18:41:53 +01:00
parent 8877a68a1f
commit feffe86440
3 changed files with 116 additions and 11 deletions

View File

@ -159,6 +159,9 @@ static MultiNode * ApplyCartesianProduct(MultiNode *leftNode, MultiNode *rightNo
* Local functions forward declarations for subquery pushdown. Note that these
* functions will be removed with upcoming subqery changes.
*/
static bool ShouldUseSubqueryPushDown(Query *originalQuery, Query *rewrittenQuery);
static bool IsFunctionRTE(Node *node);
static bool FindNodeCheck(Node *node, bool (*check)(Node *));
static Node * ResolveExternalParams(Node *inputNode, ParamListInfo boundParams);
static MultiNode * SubqueryMultiNodeTree(Query *originalQuery,
Query *queryTree,
@ -199,17 +202,7 @@ MultiLogicalPlanCreate(Query *originalQuery, Query *queryTree,
MultiNode *multiQueryNode = NULL;
MultiTreeRoot *rootNode = NULL;
/*
* We check the existence of subqueries in FROM clause on the modified query
* given that if postgres already flattened the subqueries, MultiNodeTree()
* can plan corresponding distributed plan.
*
* We also check the existence of subqueries in WHERE clause. Note that
* this check needs to be done on the original query given that
* standard_planner() may replace the sublinks with anti/semi joins and
* MultiNodeTree() cannot plan such queries.
*/
if (SubqueryEntryList(queryTree) != NIL || SublinkList(originalQuery) != NIL)
if (ShouldUseSubqueryPushDown(originalQuery, queryTree))
{
originalQuery = (Query *) ResolveExternalParams((Node *) originalQuery,
boundParams);
@ -229,6 +222,101 @@ MultiLogicalPlanCreate(Query *originalQuery, Query *queryTree,
}
/*
* ShouldUseSubqueryPushDown determines whether it's desirable to use
* subquery pushdown to plan the query based on the original and
* rewritten query.
*/
static bool
ShouldUseSubqueryPushDown(Query *originalQuery, Query *rewrittenQuery)
{
/*
* We check the existence of subqueries in FROM clause on the modified query
* given that if postgres already flattened the subqueries, MultiPlanTree()
* can plan corresponding distributed plan.
*/
if (SubqueryEntryList(rewrittenQuery) != NIL)
{
return true;
}
/*
* We also check the existence of subqueries in WHERE clause. Note that
* this check needs to be done on the original query given that
* standard_planner() may replace the sublinks with anti/semi joins and
* MultiPlanTree() cannot plan such queries.
*/
if (SublinkList(originalQuery) != NIL)
{
return true;
}
/*
* We process function RTEs as subqueries, since the join order planner
* does not know how to handle them.
*/
if (FindNodeCheck((Node *) originalQuery, IsFunctionRTE))
{
return true;
}
return false;
}
/*
* IsFunctionRTE determines whether the given node is a function RTE.
*/
static bool
IsFunctionRTE(Node *node)
{
if (IsA(node, RangeTblEntry))
{
RangeTblEntry *rangeTblEntry = (RangeTblEntry *) node;
if (rangeTblEntry->rtekind == RTE_FUNCTION)
{
return true;
}
}
return false;
}
/*
* FindNodeCheck finds a node for which the check function returns true.
*
* To call this function directly with an RTE, use:
* range_table_walker(rte, FindNodeCheck, check, QTW_EXAMINE_RTES)
*/
static bool
FindNodeCheck(Node *node, bool (*check)(Node *))
{
if (node == NULL)
{
return false;
}
if (check(node))
{
return true;
}
if (IsA(node, RangeTblEntry))
{
/* query_tree_walker descends into RTEs */
return false;
}
else if (IsA(node, Query))
{
return query_tree_walker((Query *) node, FindNodeCheck, check, QTW_EXAMINE_RTES);
}
return expression_tree_walker(node, FindNodeCheck, check);
}
/*
* ResolveExternalParams replaces the external parameters that appears
* in the query with the corresponding entries in the boundParams.

View File

@ -251,12 +251,23 @@ SELECT count(*) FROM
10
(1 row)
SELECT count(*) FROM user_buy_test_table LEFT JOIN (SELECT * FROM generate_series(1,10) id) users_ref_test_table
ON user_buy_test_table.item_id = users_ref_test_table.id;
count
-------
4
(1 row)
-- table function cannot be the outer relationship in an outer join
SELECT count(*) FROM
(SELECT random() FROM user_buy_test_table RIGHT JOIN generate_series(1,10) AS users_ref_test_table(id)
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
ERROR: cannot pushdown the subquery
DETAIL: There exist a table function in the outer part of the outer join
SELECT count(*) FROM user_buy_test_table RIGHT JOIN (SELECT * FROM generate_series(1,10) id) users_ref_test_table
ON user_buy_test_table.item_id = users_ref_test_table.id;
ERROR: cannot pushdown the subquery
DETAIL: There exist a table function in the outer part of the outer join
-- volatile functions cannot be used as table expressions
SELECT count(*) FROM
(SELECT random() FROM user_buy_test_table JOIN random() AS users_ref_test_table(id)

View File

@ -150,11 +150,17 @@ SELECT count(*) FROM
(SELECT random() FROM user_buy_test_table LEFT JOIN generate_series(1,10) AS users_ref_test_table(id)
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
SELECT count(*) FROM user_buy_test_table LEFT JOIN (SELECT * FROM generate_series(1,10) id) users_ref_test_table
ON user_buy_test_table.item_id = users_ref_test_table.id;
-- table function cannot be the outer relationship in an outer join
SELECT count(*) FROM
(SELECT random() FROM user_buy_test_table RIGHT JOIN generate_series(1,10) AS users_ref_test_table(id)
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
SELECT count(*) FROM user_buy_test_table RIGHT JOIN (SELECT * FROM generate_series(1,10) id) users_ref_test_table
ON user_buy_test_table.item_id = users_ref_test_table.id;
-- volatile functions cannot be used as table expressions
SELECT count(*) FROM
(SELECT random() FROM user_buy_test_table JOIN random() AS users_ref_test_table(id)