Add outer join clause list extraction for subquery pushdown logic

In subquery pushdown, we allow outer joins if the join condition is on the
partition columns. WhereClauseList() used to return all join conditions including
outer joins. However, this has been changed with a commit related to outer join
support on regular queries. With this commit, we refactored ExtractFromExpressionWalker()
to return two lists of qualifiers. The first list is for inner join and filter
clauses and the second list is for outer join clauses. Therefore, we can also
use outer join clauses to check subquery pushdown prerequisites.
pull/755/head
Metin Doslu 2016-08-26 16:00:31 +03:00
parent 6d2567f1a2
commit 7d212b847f
3 changed files with 82 additions and 33 deletions

View File

@ -3400,10 +3400,10 @@ SupportedLateralQuery(Query *parentQuery, Query *lateralQuery)
bool supportedLateralQuery = false; bool supportedLateralQuery = false;
List *outerCompositeFieldList = NIL; List *outerCompositeFieldList = NIL;
List *localCompositeFieldList = NIL; List *localCompositeFieldList = NIL;
List *whereClauseList = WhereClauseList(lateralQuery->jointree); ListCell *qualifierCell = NULL;
ListCell *whereClauseCell = NULL; List *qualifierList = QualifierList(lateralQuery->jointree);
foreach(whereClauseCell, whereClauseList) foreach(qualifierCell, qualifierList)
{ {
OpExpr *operatorExpression = NULL; OpExpr *operatorExpression = NULL;
List *argumentList = NIL; List *argumentList = NIL;
@ -3417,13 +3417,13 @@ SupportedLateralQuery(Query *parentQuery, Query *lateralQuery)
bool outerColumnIsPartitionColumn = false; bool outerColumnIsPartitionColumn = false;
bool localColumnIsPartitionColumn = false; bool localColumnIsPartitionColumn = false;
Node *clause = (Node *) lfirst(whereClauseCell); Node *qualifier = (Node *) lfirst(qualifierCell);
if (!IsA(clause, OpExpr)) if (!IsA(qualifier, OpExpr))
{ {
continue; continue;
} }
operatorExpression = (OpExpr *) clause; operatorExpression = (OpExpr *) qualifier;
argumentList = operatorExpression->args; argumentList = operatorExpression->args;
/* /*
@ -3566,8 +3566,8 @@ JoinOnPartitionColumn(Query *query)
bool joinOnPartitionColumn = false; bool joinOnPartitionColumn = false;
List *leftCompositeFieldList = NIL; List *leftCompositeFieldList = NIL;
List *rightCompositeFieldList = NIL; List *rightCompositeFieldList = NIL;
List *whereClauseList = WhereClauseList(query->jointree); List *qualifierList = QualifierList(query->jointree);
List *joinClauseList = JoinClauseList(whereClauseList); List *joinClauseList = JoinClauseList(qualifierList);
ListCell *joinClauseCell = NULL; ListCell *joinClauseCell = NULL;
foreach(joinClauseCell, joinClauseList) foreach(joinClauseCell, joinClauseList)

View File

@ -38,6 +38,14 @@
bool SubqueryPushdown = false; /* is subquery pushdown enabled */ bool SubqueryPushdown = false; /* is subquery pushdown enabled */
/* Struct to differentiate different qualifier types in an expression tree walker */
typedef struct QualifierWalkerContext
{
List *baseQualifierList;
List *outerJoinQualifierList;
} QualifierWalkerContext;
/* Function pointer type definition for apply join rule functions */ /* Function pointer type definition for apply join rule functions */
typedef MultiNode *(*RuleApplyFunction) (MultiNode *leftNode, MultiNode *rightNode, typedef MultiNode *(*RuleApplyFunction) (MultiNode *leftNode, MultiNode *rightNode,
Var *partitionColumn, JoinType joinType, Var *partitionColumn, JoinType joinType,
@ -56,7 +64,8 @@ static bool HasOuterJoinWalker(Node *node, void *maxJoinLevel);
static bool HasComplexJoinOrder(Query *queryTree); static bool HasComplexJoinOrder(Query *queryTree);
static bool HasComplexRangeTableType(Query *queryTree); static bool HasComplexRangeTableType(Query *queryTree);
static void ValidateClauseList(List *clauseList); static void ValidateClauseList(List *clauseList);
static bool ExtractFromExpressionWalker(Node *node, List **qualifierList); static bool ExtractFromExpressionWalker(Node *node,
QualifierWalkerContext *walkerContext);
static List * MultiTableNodeList(List *tableEntryList, List *rangeTableList); static List * MultiTableNodeList(List *tableEntryList, List *rangeTableList);
static List * AddMultiCollectNodes(List *tableNodeList); static List * AddMultiCollectNodes(List *tableNodeList);
static MultiNode * MultiJoinTree(List *joinOrderList, List *collectTableList, static MultiNode * MultiJoinTree(List *joinOrderList, List *collectTableList,
@ -720,23 +729,48 @@ ExtractRangeTableIndexWalker(Node *node, List **rangeTableIndexList)
/* /*
* WhereClauseList walks over the FROM expression in the query tree, and builds * WhereClauseList walks over the FROM expression in the query tree, and builds
* a list of all clauses from the expression tree. The function checks for both * a list of all clauses from the expression tree. The function checks for both
* implicitly and explicitly defined clauses. Explicit clauses are expressed as * implicitly and explicitly defined clauses, but only selects INNER join
* "SELECT ... FROM R1 INNER JOIN R2 ON R1.A = R2.A". Implicit joins differ in * explicit clauses, and skips any outer-join clauses. Explicit clauses are
* that they live in the WHERE clause, and are expressed as "SELECT ... FROM * expressed as "SELECT ... FROM R1 INNER JOIN R2 ON R1.A = R2.A". Implicit
* ... WHERE R1.a = R2.a". * joins differ in that they live in the WHERE clause, and are expressed as
* "SELECT ... FROM ... WHERE R1.a = R2.a".
*/ */
List * List *
WhereClauseList(FromExpr *fromExpr) WhereClauseList(FromExpr *fromExpr)
{ {
FromExpr *fromExprCopy = copyObject(fromExpr); FromExpr *fromExprCopy = copyObject(fromExpr);
QualifierWalkerContext *walkerContext = palloc0(sizeof(QualifierWalkerContext));
List *whereClauseList = NIL; List *whereClauseList = NIL;
ExtractFromExpressionWalker((Node *) fromExprCopy, &whereClauseList); ExtractFromExpressionWalker((Node *) fromExprCopy, walkerContext);
whereClauseList = walkerContext->baseQualifierList;
return whereClauseList; return whereClauseList;
} }
/*
* QualifierList walks over the FROM expression in the query tree, and builds
* a list of all qualifiers from the expression tree. The function checks for
* both implicitly and explicitly defined qualifiers. Note that this function
* is very similar to WhereClauseList(), but QualifierList() also includes
* outer-join clauses.
*/
List *
QualifierList(FromExpr *fromExpr)
{
FromExpr *fromExprCopy = copyObject(fromExpr);
QualifierWalkerContext *walkerContext = palloc0(sizeof(QualifierWalkerContext));
List *qualifierList = NIL;
ExtractFromExpressionWalker((Node *) fromExprCopy, walkerContext);
qualifierList = list_concat(qualifierList, walkerContext->baseQualifierList);
qualifierList = list_concat(qualifierList, walkerContext->outerJoinQualifierList);
return qualifierList;
}
/* /*
* ValidateClauseList walks over the given list of clauses, and checks that we * ValidateClauseList walks over the given list of clauses, and checks that we
* can recognize all the clauses. This function ensures that we do not drop an * can recognize all the clauses. This function ensures that we do not drop an
@ -790,8 +824,15 @@ JoinClauseList(List *whereClauseList)
/* /*
* ExtractFromExpressionWalker walks over a FROM expression, and finds all * ExtractFromExpressionWalker walks over a FROM expression, and finds all
* explicit qualifiers in the expression. The function looks at join and from * implicit and explicit qualifiers in the expression. The function looks at
* expression nodes to find explicit qualifiers, and returns these qualifiers. * join and from expression nodes to find qualifiers, and returns these
* qualifiers.
*
* Note that we don't want outer join clauses in regular outer join planning,
* but we need outer join clauses in subquery pushdown prerequisite checks.
* Therefore, outer join qualifiers are returned in a different list than other
* qualifiers inside the given walker context. For this reason, we return two
* qualifier lists.
* *
* Note that we check if the qualifier node in join and from expression nodes * Note that we check if the qualifier node in join and from expression nodes
* is a list node. If it is not a list node which is the case for subqueries, * is a list node. If it is not a list node which is the case for subqueries,
@ -803,7 +844,7 @@ JoinClauseList(List *whereClauseList)
* query tree. * query tree.
*/ */
static bool static bool
ExtractFromExpressionWalker(Node *node, List **qualifierList) ExtractFromExpressionWalker(Node *node, QualifierWalkerContext *walkerContext)
{ {
bool walkerResult = false; bool walkerResult = false;
if (node == NULL) if (node == NULL)
@ -824,11 +865,7 @@ ExtractFromExpressionWalker(Node *node, List **qualifierList)
Node *joinQualifiersNode = joinExpression->quals; Node *joinQualifiersNode = joinExpression->quals;
JoinType joinType = joinExpression->jointype; JoinType joinType = joinExpression->jointype;
/* if (joinQualifiersNode != NULL)
* We only extract qualifiers from inner join clauses, which can be
* treated as WHERE clauses.
*/
if (joinQualifiersNode != NULL && joinType == JOIN_INNER)
{ {
if (IsA(joinQualifiersNode, List)) if (IsA(joinQualifiersNode, List))
{ {
@ -841,8 +878,18 @@ ExtractFromExpressionWalker(Node *node, List **qualifierList)
joinClause = (Node *) canonicalize_qual((Expr *) joinClause); joinClause = (Node *) canonicalize_qual((Expr *) joinClause);
joinQualifierList = make_ands_implicit((Expr *) joinClause); joinQualifierList = make_ands_implicit((Expr *) joinClause);
} }
}
(*qualifierList) = list_concat(*qualifierList, joinQualifierList); /* return outer join clauses in a separate list */
if (joinType == JOIN_INNER)
{
walkerContext->baseQualifierList =
list_concat(walkerContext->baseQualifierList, joinQualifierList);
}
else if (IS_OUTER_JOIN(joinType))
{
walkerContext->outerJoinQualifierList =
list_concat(walkerContext->outerJoinQualifierList, joinQualifierList);
} }
} }
else if (IsA(node, FromExpr)) else if (IsA(node, FromExpr))
@ -865,12 +912,13 @@ ExtractFromExpressionWalker(Node *node, List **qualifierList)
fromQualifierList = make_ands_implicit((Expr *) fromClause); fromQualifierList = make_ands_implicit((Expr *) fromClause);
} }
(*qualifierList) = list_concat(*qualifierList, fromQualifierList); walkerContext->baseQualifierList =
list_concat(walkerContext->baseQualifierList, fromQualifierList);
} }
} }
walkerResult = expression_tree_walker(node, ExtractFromExpressionWalker, walkerResult = expression_tree_walker(node, ExtractFromExpressionWalker,
(void *) qualifierList); (void *) walkerContext);
return walkerResult; return walkerResult;
} }
@ -1867,8 +1915,8 @@ static MultiNode *
SubqueryPushdownMultiPlanTree(Query *queryTree, List *subqueryEntryList) SubqueryPushdownMultiPlanTree(Query *queryTree, List *subqueryEntryList)
{ {
List *targetEntryList = queryTree->targetList; List *targetEntryList = queryTree->targetList;
List *whereClauseList = NIL; List *qualifierList = NIL;
List *whereClauseColumnList = NIL; List *qualifierColumnList = NIL;
List *targetListColumnList = NIL; List *targetListColumnList = NIL;
List *columnList = NIL; List *columnList = NIL;
ListCell *columnCell = NULL; ListCell *columnCell = NULL;
@ -1884,9 +1932,9 @@ SubqueryPushdownMultiPlanTree(Query *queryTree, List *subqueryEntryList)
ErrorIfQueryNotSupported(queryTree); ErrorIfQueryNotSupported(queryTree);
ErrorIfSubqueryJoin(queryTree); ErrorIfSubqueryJoin(queryTree);
/* extract where clause qualifiers and verify we can plan for them */ /* extract qualifiers and verify we can plan for them */
whereClauseList = WhereClauseList(queryTree->jointree); qualifierList = QualifierList(queryTree->jointree);
ValidateClauseList(whereClauseList); ValidateClauseList(qualifierList);
/* /*
* We disregard pulled subqueries. This changes order of range table list. * We disregard pulled subqueries. This changes order of range table list.
@ -1897,10 +1945,10 @@ SubqueryPushdownMultiPlanTree(Query *queryTree, List *subqueryEntryList)
*/ */
Assert(list_length(subqueryEntryList) == 1); Assert(list_length(subqueryEntryList) == 1);
whereClauseColumnList = pull_var_clause_default((Node *) whereClauseList); qualifierColumnList = pull_var_clause_default((Node *) qualifierList);
targetListColumnList = pull_var_clause_default((Node *) targetEntryList); targetListColumnList = pull_var_clause_default((Node *) targetEntryList);
columnList = list_concat(whereClauseColumnList, targetListColumnList); columnList = list_concat(qualifierColumnList, targetListColumnList);
foreach(columnCell, columnList) foreach(columnCell, columnList)
{ {
Var *column = (Var *) lfirst(columnCell); Var *column = (Var *) lfirst(columnCell);
@ -1915,7 +1963,7 @@ SubqueryPushdownMultiPlanTree(Query *queryTree, List *subqueryEntryList)
currentTopNode = (MultiNode *) subqueryCollectNode; currentTopNode = (MultiNode *) subqueryCollectNode;
/* build select node if the query has selection criteria */ /* build select node if the query has selection criteria */
selectNode = MultiSelectNode(whereClauseList); selectNode = MultiSelectNode(qualifierList);
if (selectNode != NULL) if (selectNode != NULL)
{ {
SetChild((MultiUnaryNode *) selectNode, currentTopNode); SetChild((MultiUnaryNode *) selectNode, currentTopNode);

View File

@ -198,6 +198,7 @@ extern bool IsJoinClause(Node *clause);
extern List * SubqueryEntryList(Query *queryTree); extern List * SubqueryEntryList(Query *queryTree);
extern bool ExtractRangeTableIndexWalker(Node *node, List **rangeTableIndexList); extern bool ExtractRangeTableIndexWalker(Node *node, List **rangeTableIndexList);
extern List * WhereClauseList(FromExpr *fromExpr); extern List * WhereClauseList(FromExpr *fromExpr);
extern List * QualifierList(FromExpr *fromExpr);
extern List * TableEntryList(List *rangeTableList); extern List * TableEntryList(List *rangeTableList);
extern bool ExtractRangeTableRelationWalker(Node *node, List **rangeTableList); extern bool ExtractRangeTableRelationWalker(Node *node, List **rangeTableList);
extern bool ExtractRangeTableEntryWalker(Node *node, List **rangeTableList); extern bool ExtractRangeTableEntryWalker(Node *node, List **rangeTableList);