mirror of https://github.com/citusdata/citus.git
Subqueries containing functions go through subquery pushdown
parent
8877a68a1f
commit
feffe86440
|
@ -159,6 +159,9 @@ static MultiNode * ApplyCartesianProduct(MultiNode *leftNode, MultiNode *rightNo
|
||||||
* Local functions forward declarations for subquery pushdown. Note that these
|
* Local functions forward declarations for subquery pushdown. Note that these
|
||||||
* functions will be removed with upcoming subqery changes.
|
* 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 Node * ResolveExternalParams(Node *inputNode, ParamListInfo boundParams);
|
||||||
static MultiNode * SubqueryMultiNodeTree(Query *originalQuery,
|
static MultiNode * SubqueryMultiNodeTree(Query *originalQuery,
|
||||||
Query *queryTree,
|
Query *queryTree,
|
||||||
|
@ -199,17 +202,7 @@ MultiLogicalPlanCreate(Query *originalQuery, Query *queryTree,
|
||||||
MultiNode *multiQueryNode = NULL;
|
MultiNode *multiQueryNode = NULL;
|
||||||
MultiTreeRoot *rootNode = NULL;
|
MultiTreeRoot *rootNode = NULL;
|
||||||
|
|
||||||
/*
|
if (ShouldUseSubqueryPushDown(originalQuery, queryTree))
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
originalQuery = (Query *) ResolveExternalParams((Node *) originalQuery,
|
originalQuery = (Query *) ResolveExternalParams((Node *) originalQuery,
|
||||||
boundParams);
|
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
|
* ResolveExternalParams replaces the external parameters that appears
|
||||||
* in the query with the corresponding entries in the boundParams.
|
* in the query with the corresponding entries in the boundParams.
|
||||||
|
|
|
@ -251,12 +251,23 @@ SELECT count(*) FROM
|
||||||
10
|
10
|
||||||
(1 row)
|
(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
|
-- table function cannot be the outer relationship in an outer join
|
||||||
SELECT count(*) FROM
|
SELECT count(*) FROM
|
||||||
(SELECT random() FROM user_buy_test_table RIGHT JOIN generate_series(1,10) AS users_ref_test_table(id)
|
(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;
|
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
|
||||||
ERROR: cannot pushdown the subquery
|
ERROR: cannot pushdown the subquery
|
||||||
DETAIL: There exist a table function in the outer part of the outer join
|
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
|
-- volatile functions cannot be used as table expressions
|
||||||
SELECT count(*) FROM
|
SELECT count(*) FROM
|
||||||
(SELECT random() FROM user_buy_test_table JOIN random() AS users_ref_test_table(id)
|
(SELECT random() FROM user_buy_test_table JOIN random() AS users_ref_test_table(id)
|
||||||
|
|
|
@ -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)
|
(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;
|
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
|
-- table function cannot be the outer relationship in an outer join
|
||||||
SELECT count(*) FROM
|
SELECT count(*) FROM
|
||||||
(SELECT random() FROM user_buy_test_table RIGHT JOIN generate_series(1,10) AS users_ref_test_table(id)
|
(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;
|
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
|
-- volatile functions cannot be used as table expressions
|
||||||
SELECT count(*) FROM
|
SELECT count(*) FROM
|
||||||
(SELECT random() FROM user_buy_test_table JOIN random() AS users_ref_test_table(id)
|
(SELECT random() FROM user_buy_test_table JOIN random() AS users_ref_test_table(id)
|
||||||
|
|
Loading…
Reference in New Issue