Add infrastructure for filtering restriction contexts based on the input query

In subquery pushdown, we first ensure that each relation is joined with at least
on another relation on the partition keys. That's fine given that the decision
is binary: pushdown the query at all or not.

With recursive planning, we'd want to check whether any specific part
of the query can be pushded down or not. Thus, we need the ability to
understand which part(s) of the subquery is safe to pushdown. This commit
adds the infrastructure for doing that.
pull/1833/head
Onder Kalaci 2017-11-27 15:35:44 +02:00
parent 26d9b58e9e
commit 05fb0dd020
4 changed files with 222 additions and 3 deletions

View File

@ -85,6 +85,17 @@ static DeferredErrorMessage * DeferErrorIfUnsupportedSubqueryPushdown(Query *
PlannerRestrictionContext PlannerRestrictionContext
* *
plannerRestrictionContext); plannerRestrictionContext);
static RelationRestrictionContext * FilterRelationRestrictionContext(
RelationRestrictionContext *relationRestrictionContext,
Relids
queryRteIdentities);
static JoinRestrictionContext * FilterJoinRestrictionContext(
JoinRestrictionContext *joinRestrictionContext, Relids
queryRteIdentities);
static bool RangeTableArrayContainsAnyRTEIdentities(RangeTblEntry **rangeTableEntries, int
rangeTableArrayLength, Relids
queryRteIdentities);
static Relids QueryRteIdentities(Query *queryTree);
static DeferredErrorMessage * DeferErrorIfUnsupportedSublinkAndReferenceTable( static DeferredErrorMessage * DeferErrorIfUnsupportedSublinkAndReferenceTable(
Query *queryTree); Query *queryTree);
static DeferredErrorMessage * DeferErrorIfUnsupportedFilters(Query *subquery); static DeferredErrorMessage * DeferErrorIfUnsupportedFilters(Query *subquery);
@ -704,6 +715,210 @@ DeferErrorIfUnsupportedSubqueryPushdown(Query *originalQuery,
} }
/*
* FilterPlannerRestrictionForQuery gets a planner restriction context and
* set of rte identities. It returns the restrictions that that appear
* in the queryRteIdentities and returns a newly allocated
* PlannerRestrictionContext. The function also sets all the other fields of
* the PlannerRestrictionContext with respect to the filtered restrictions.
*/
PlannerRestrictionContext *
FilterPlannerRestrictionForQuery(PlannerRestrictionContext *plannerRestrictionContext,
Query *query)
{
PlannerRestrictionContext *filteredPlannerRestrictionContext = NULL;
int referenceRelationCount = 0;
int totalRelationCount = 0;
Relids queryRteIdentities = QueryRteIdentities(query);
RelationRestrictionContext *relationRestrictionContext =
plannerRestrictionContext->relationRestrictionContext;
JoinRestrictionContext *joinRestrictionContext =
plannerRestrictionContext->joinRestrictionContext;
RelationRestrictionContext *filteredRelationRestrictionContext =
FilterRelationRestrictionContext(relationRestrictionContext, queryRteIdentities);
JoinRestrictionContext *filtererdJoinRestrictionContext =
FilterJoinRestrictionContext(joinRestrictionContext, queryRteIdentities);
/* allocate the filtered planner restriction context and set all the fields */
filteredPlannerRestrictionContext = palloc0(sizeof(PlannerRestrictionContext));
filteredPlannerRestrictionContext->memoryContext =
plannerRestrictionContext->memoryContext;
totalRelationCount = list_length(
filteredRelationRestrictionContext->relationRestrictionList);
referenceRelationCount = ReferenceRelationCount(filteredRelationRestrictionContext);
filteredRelationRestrictionContext->allReferenceTables =
(totalRelationCount == referenceRelationCount);
/* we currently don't support local relations and we cannot come up to this point */
filteredRelationRestrictionContext->hasLocalRelation = false;
filteredRelationRestrictionContext->hasDistributedRelation = true;
/* finally set the relation and join restriction contexts */
filteredPlannerRestrictionContext->relationRestrictionContext =
filteredRelationRestrictionContext;
filteredPlannerRestrictionContext->joinRestrictionContext =
filtererdJoinRestrictionContext;
return filteredPlannerRestrictionContext;
}
/*
* FilterRelationRestrictionContext gets a relation restriction context and
* set of rte identities. It returns the relation restrictions that that appear
* in the queryRteIdentities and returns a newly allocated
* RelationRestrictionContext.
*/
static RelationRestrictionContext *
FilterRelationRestrictionContext(RelationRestrictionContext *relationRestrictionContext,
Relids queryRteIdentities)
{
RelationRestrictionContext *filteredRestrictionContext =
palloc0(sizeof(RelationRestrictionContext));
ListCell *relationRestrictionCell = NULL;
foreach(relationRestrictionCell, relationRestrictionContext->relationRestrictionList)
{
RelationRestriction *relationRestriction =
(RelationRestriction *) lfirst(relationRestrictionCell);
int rteIdentity = GetRTEIdentity(relationRestriction->rte);
if (bms_is_member(rteIdentity, queryRteIdentities))
{
filteredRestrictionContext->relationRestrictionList =
lappend(filteredRestrictionContext->relationRestrictionList,
relationRestriction);
}
}
return filteredRestrictionContext;
}
/*
* FilterJoinRestrictionContext gets a join restriction context and
* set of rte identities. It returns the join restrictions that that appear
* in the queryRteIdentities and returns a newly allocated
* JoinRestrictionContext.
*
* Note that the join restriction is added to the return context as soon as
* any range table entry that appear in the join belongs to queryRteIdentities.
*/
static JoinRestrictionContext *
FilterJoinRestrictionContext(JoinRestrictionContext *joinRestrictionContext, Relids
queryRteIdentities)
{
JoinRestrictionContext *filtererdJoinRestrictionContext =
palloc0(sizeof(JoinRestrictionContext));
ListCell *joinRestrictionCell = NULL;
foreach(joinRestrictionCell, joinRestrictionContext->joinRestrictionList)
{
JoinRestriction *joinRestriction =
(JoinRestriction *) lfirst(joinRestrictionCell);
RangeTblEntry **rangeTableEntries =
joinRestriction->plannerInfo->simple_rte_array;
int rangeTableArrayLength = joinRestriction->plannerInfo->simple_rel_array_size;
if (RangeTableArrayContainsAnyRTEIdentities(rangeTableEntries,
rangeTableArrayLength,
queryRteIdentities))
{
filtererdJoinRestrictionContext->joinRestrictionList = lappend(
filtererdJoinRestrictionContext->joinRestrictionList,
joinRestriction);
}
}
return filtererdJoinRestrictionContext;
}
/*
* RangeTableArrayContainsAnyRTEIdentities returns true if any of the range table entries
* int rangeTableEntries array is an range table relation specified in queryRteIdentities.
*/
static bool
RangeTableArrayContainsAnyRTEIdentities(RangeTblEntry **rangeTableEntries, int
rangeTableArrayLength, Relids queryRteIdentities)
{
int rteIndex = 0;
/* simple_rte_array starts from 1, see plannerInfo struct */
for (rteIndex = 1; rteIndex < rangeTableArrayLength; ++rteIndex)
{
RangeTblEntry *rangeTableEntry = rangeTableEntries[rteIndex];
List *rangeTableRelationList = NULL;
ListCell *rteRelationCell = NULL;
/*
* Get list of all RTE_RELATIONs in the given range table entry
* (i.e.,rangeTableEntry could be a subquery where we're interested
* in relations).
*/
ExtractRangeTableRelationWalker((Node *) rangeTableEntry,
&rangeTableRelationList);
foreach(rteRelationCell, rangeTableRelationList)
{
RangeTblEntry *rteRelation = (RangeTblEntry *) lfirst(rteRelationCell);
int rteIdentity = 0;
Assert(rteRelation->rtekind == RTE_RELATION);
rteIdentity = GetRTEIdentity(rteRelation);
if (bms_is_member(rteIdentity, queryRteIdentities))
{
return true;
}
}
}
return false;
}
/*
* QueryRteIdentities gets a queryTree, find get all the rte identities assigned by
* us.
*/
static Relids
QueryRteIdentities(Query *queryTree)
{
List *rangeTableList = NULL;
ListCell *rangeTableCell = NULL;
Relids queryRteIdentities = NULL;
/* extract range table entries for simple relations only */
ExtractRangeTableRelationWalker((Node *) queryTree, &rangeTableList);
foreach(rangeTableCell, rangeTableList)
{
RangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rangeTableCell);
int rteIdentity = 0;
/* we're only interested in relations */
Assert(rangeTableEntry->rtekind == RTE_RELATION);
rteIdentity = GetRTEIdentity(rangeTableEntry);
queryRteIdentities = bms_add_member(queryRteIdentities, rteIdentity);
}
return queryRteIdentities;
}
/* /*
* DeferErrorIfUnsupportedSublinkAndReferenceTable returns a deferred error if the * DeferErrorIfUnsupportedSublinkAndReferenceTable returns a deferred error if the
* given query is not suitable for subquery pushdown. * given query is not suitable for subquery pushdown.
@ -3453,7 +3668,8 @@ ExtractRangeTableEntryWalker(Node *node, List **rangeTableList)
} }
else if (IsA(node, Query)) else if (IsA(node, Query))
{ {
walkIsComplete = query_tree_walker((Query *) node, ExtractRangeTableEntryWalker, walkIsComplete = query_tree_walker((Query *) node,
ExtractRangeTableEntryWalker,
rangeTableList, QTW_EXAMINE_RTES); rangeTableList, QTW_EXAMINE_RTES);
} }
else else

View File

@ -62,7 +62,6 @@ typedef struct AttributeEquivalenceClassMember
} AttributeEquivalenceClassMember; } AttributeEquivalenceClassMember;
static uint32 ReferenceRelationCount(RelationRestrictionContext *restrictionContext);
static Var * FindTranslatedVar(List *appendRelList, Oid relationOid, static Var * FindTranslatedVar(List *appendRelList, Oid relationOid,
Index relationRteIndex, Index *partitionKeyIndex); Index relationRteIndex, Index *partitionKeyIndex);
static bool EquivalenceListContainsRelationsEquality(List *attributeEquivalenceList, static bool EquivalenceListContainsRelationsEquality(List *attributeEquivalenceList,
@ -408,7 +407,7 @@ RestrictionEquivalenceForPartitionKeys(PlannerRestrictionContext *
* ReferenceRelationCount iterates over the relations and returns the reference table * ReferenceRelationCount iterates over the relations and returns the reference table
* relation count. * relation count.
*/ */
static uint32 uint32
ReferenceRelationCount(RelationRestrictionContext *restrictionContext) ReferenceRelationCount(RelationRestrictionContext *restrictionContext)
{ {
ListCell *relationRestrictionCell = NULL; ListCell *relationRestrictionCell = NULL;

View File

@ -187,6 +187,9 @@ extern MultiTreeRoot * MultiLogicalPlanCreate(Query *originalQuery, Query *query
PlannerRestrictionContext * PlannerRestrictionContext *
plannerRestrictionContext, plannerRestrictionContext,
ParamListInfo boundParams); ParamListInfo boundParams);
extern PlannerRestrictionContext * FilterPlannerRestrictionForQuery(
PlannerRestrictionContext *plannerRestrictionContext,
Query *query);
extern bool SafeToPushdownWindowFunction(Query *query, StringInfo *errorDetail); extern bool SafeToPushdownWindowFunction(Query *query, StringInfo *errorDetail);
extern bool TargetListOnPartitionColumn(Query *query, List *targetEntryList); extern bool TargetListOnPartitionColumn(Query *query, List *targetEntryList);
extern bool NeedsDistributedPlanning(Query *queryTree); extern bool NeedsDistributedPlanning(Query *queryTree);

View File

@ -18,6 +18,7 @@
extern bool ContainsUnionSubquery(Query *queryTree); extern bool ContainsUnionSubquery(Query *queryTree);
extern bool RestrictionEquivalenceForPartitionKeys(PlannerRestrictionContext * extern bool RestrictionEquivalenceForPartitionKeys(PlannerRestrictionContext *
plannerRestrictionContext); plannerRestrictionContext);
extern uint32 ReferenceRelationCount(RelationRestrictionContext *restrictionContext);
extern bool SafeToPushdownUnionSubquery(RelationRestrictionContext *restrictionContext); extern bool SafeToPushdownUnionSubquery(RelationRestrictionContext *restrictionContext);
extern List * RelationIdList(Query *query); extern List * RelationIdList(Query *query);