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/807/head
Metin Doslu 2016-08-26 16:00:31 +03:00 committed by Jason Petersen
parent ce0a0ab813
commit fa4f3b4d9a
No known key found for this signature in database
GPG Key ID: 9F1D3510D110ABA9
3 changed files with 82 additions and 33 deletions

View File

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

View File

@ -38,6 +38,14 @@
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 */
typedef MultiNode *(*RuleApplyFunction) (MultiNode *leftNode, MultiNode *rightNode,
Var *partitionColumn, JoinType joinType,
@ -56,7 +64,8 @@ static bool HasOuterJoinWalker(Node *node, void *maxJoinLevel);
static bool HasComplexJoinOrder(Query *queryTree);
static bool HasComplexRangeTableType(Query *queryTree);
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 * AddMultiCollectNodes(List *tableNodeList);
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
* a list of all clauses from the expression tree. The function checks for both
* implicitly and explicitly defined clauses. Explicit clauses are expressed as
* "SELECT ... FROM R1 INNER JOIN R2 ON R1.A = R2.A". Implicit joins differ in
* that they live in the WHERE clause, and are expressed as "SELECT ... FROM
* ... WHERE R1.a = R2.a".
* implicitly and explicitly defined clauses, but only selects INNER join
* explicit clauses, and skips any outer-join clauses. Explicit clauses are
* expressed as "SELECT ... FROM R1 INNER JOIN R2 ON R1.A = R2.A". Implicit
* joins differ in that they live in the WHERE clause, and are expressed as
* "SELECT ... FROM ... WHERE R1.a = R2.a".
*/
List *
WhereClauseList(FromExpr *fromExpr)
{
FromExpr *fromExprCopy = copyObject(fromExpr);
QualifierWalkerContext *walkerContext = palloc0(sizeof(QualifierWalkerContext));
List *whereClauseList = NIL;
ExtractFromExpressionWalker((Node *) fromExprCopy, &whereClauseList);
ExtractFromExpressionWalker((Node *) fromExprCopy, walkerContext);
whereClauseList = walkerContext->baseQualifierList;
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
* 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
* explicit qualifiers in the expression. The function looks at join and from
* expression nodes to find explicit qualifiers, and returns these qualifiers.
* implicit and explicit qualifiers in the expression. The function looks at
* 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
* 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.
*/
static bool
ExtractFromExpressionWalker(Node *node, List **qualifierList)
ExtractFromExpressionWalker(Node *node, QualifierWalkerContext *walkerContext)
{
bool walkerResult = false;
if (node == NULL)
@ -824,11 +865,7 @@ ExtractFromExpressionWalker(Node *node, List **qualifierList)
Node *joinQualifiersNode = joinExpression->quals;
JoinType joinType = joinExpression->jointype;
/*
* We only extract qualifiers from inner join clauses, which can be
* treated as WHERE clauses.
*/
if (joinQualifiersNode != NULL && joinType == JOIN_INNER)
if (joinQualifiersNode != NULL)
{
if (IsA(joinQualifiersNode, List))
{
@ -841,8 +878,18 @@ ExtractFromExpressionWalker(Node *node, List **qualifierList)
joinClause = (Node *) canonicalize_qual((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))
@ -865,12 +912,13 @@ ExtractFromExpressionWalker(Node *node, List **qualifierList)
fromQualifierList = make_ands_implicit((Expr *) fromClause);
}
(*qualifierList) = list_concat(*qualifierList, fromQualifierList);
walkerContext->baseQualifierList =
list_concat(walkerContext->baseQualifierList, fromQualifierList);
}
}
walkerResult = expression_tree_walker(node, ExtractFromExpressionWalker,
(void *) qualifierList);
(void *) walkerContext);
return walkerResult;
}
@ -1867,8 +1915,8 @@ static MultiNode *
SubqueryPushdownMultiPlanTree(Query *queryTree, List *subqueryEntryList)
{
List *targetEntryList = queryTree->targetList;
List *whereClauseList = NIL;
List *whereClauseColumnList = NIL;
List *qualifierList = NIL;
List *qualifierColumnList = NIL;
List *targetListColumnList = NIL;
List *columnList = NIL;
ListCell *columnCell = NULL;
@ -1884,9 +1932,9 @@ SubqueryPushdownMultiPlanTree(Query *queryTree, List *subqueryEntryList)
ErrorIfQueryNotSupported(queryTree);
ErrorIfSubqueryJoin(queryTree);
/* extract where clause qualifiers and verify we can plan for them */
whereClauseList = WhereClauseList(queryTree->jointree);
ValidateClauseList(whereClauseList);
/* extract qualifiers and verify we can plan for them */
qualifierList = QualifierList(queryTree->jointree);
ValidateClauseList(qualifierList);
/*
* 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);
whereClauseColumnList = pull_var_clause_default((Node *) whereClauseList);
qualifierColumnList = pull_var_clause_default((Node *) qualifierList);
targetListColumnList = pull_var_clause_default((Node *) targetEntryList);
columnList = list_concat(whereClauseColumnList, targetListColumnList);
columnList = list_concat(qualifierColumnList, targetListColumnList);
foreach(columnCell, columnList)
{
Var *column = (Var *) lfirst(columnCell);
@ -1915,7 +1963,7 @@ SubqueryPushdownMultiPlanTree(Query *queryTree, List *subqueryEntryList)
currentTopNode = (MultiNode *) subqueryCollectNode;
/* build select node if the query has selection criteria */
selectNode = MultiSelectNode(whereClauseList);
selectNode = MultiSelectNode(qualifierList);
if (selectNode != NULL)
{
SetChild((MultiUnaryNode *) selectNode, currentTopNode);

View File

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