mirror of https://github.com/citusdata/citus.git
Move physical planner checks to logical planner
parent
0359d03530
commit
ceba81ce35
|
@ -85,6 +85,8 @@ static FieldSelect * CompositeFieldRecursive(Expr *expression, Query *query);
|
||||||
static bool FullCompositeFieldList(List *compositeFieldList);
|
static bool FullCompositeFieldList(List *compositeFieldList);
|
||||||
static MultiNode * MultiPlanTree(Query *queryTree);
|
static MultiNode * MultiPlanTree(Query *queryTree);
|
||||||
static void ErrorIfQueryNotSupported(Query *queryTree);
|
static void ErrorIfQueryNotSupported(Query *queryTree);
|
||||||
|
static bool HasUnsupportedReferenceTableJoin(
|
||||||
|
PlannerRestrictionContext *plannerRestrictionContext);
|
||||||
static bool HasUnsupportedJoinWalker(Node *node, void *context);
|
static bool HasUnsupportedJoinWalker(Node *node, void *context);
|
||||||
static bool ErrorHintRequired(const char *errorHint, Query *queryTree);
|
static bool ErrorHintRequired(const char *errorHint, Query *queryTree);
|
||||||
static DeferredErrorMessage * DeferErrorIfUnsupportedSubqueryRepartition(Query *
|
static DeferredErrorMessage * DeferErrorIfUnsupportedSubqueryRepartition(Query *
|
||||||
|
@ -94,6 +96,8 @@ static bool HasOuterJoin(Query *queryTree);
|
||||||
static bool HasOuterJoinWalker(Node *node, void *maxJoinLevel);
|
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 bool RelationInfoHasReferenceTable(PlannerInfo *plannerInfo,
|
||||||
|
RelOptInfo *relationInfo);
|
||||||
static void ValidateClauseList(List *clauseList);
|
static void ValidateClauseList(List *clauseList);
|
||||||
static void ValidateSubqueryPushdownClauseList(List *clauseList);
|
static void ValidateSubqueryPushdownClauseList(List *clauseList);
|
||||||
static bool ExtractFromExpressionWalker(Node *node,
|
static bool ExtractFromExpressionWalker(Node *node,
|
||||||
|
@ -188,7 +192,6 @@ MultiLogicalPlanCreate(Query *originalQuery, Query *queryTree,
|
||||||
{
|
{
|
||||||
originalQuery = (Query *) ResolveExternalParams((Node *) originalQuery,
|
originalQuery = (Query *) ResolveExternalParams((Node *) originalQuery,
|
||||||
boundParams);
|
boundParams);
|
||||||
|
|
||||||
multiQueryNode = MultiSubqueryPlanTree(originalQuery, queryTree,
|
multiQueryNode = MultiSubqueryPlanTree(originalQuery, queryTree,
|
||||||
plannerRestrictionContext);
|
plannerRestrictionContext);
|
||||||
}
|
}
|
||||||
|
@ -539,6 +542,13 @@ DeferErrorIfUnsupportedSubqueryPushdown(Query *originalQuery,
|
||||||
"one another relation using distribution keys and "
|
"one another relation using distribution keys and "
|
||||||
"equality operator.", NULL);
|
"equality operator.", NULL);
|
||||||
}
|
}
|
||||||
|
else if (HasUnsupportedReferenceTableJoin(plannerRestrictionContext))
|
||||||
|
{
|
||||||
|
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
||||||
|
"cannot pushdown the subquery",
|
||||||
|
"There exist a reference table in the outer part of the outer join",
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We first extract all the queries that appear in the original query. Later,
|
* We first extract all the queries that appear in the original query. Later,
|
||||||
|
@ -871,6 +881,10 @@ DeferErrorIfUnsupportedUnionQuery(Query *subqueryTree,
|
||||||
{
|
{
|
||||||
SetOperationStmt *setOperation =
|
SetOperationStmt *setOperation =
|
||||||
(SetOperationStmt *) lfirst(setOperationStatmentCell);
|
(SetOperationStmt *) lfirst(setOperationStatmentCell);
|
||||||
|
Node *leftArg = setOperation->larg;
|
||||||
|
Node *rightArg = setOperation->rarg;
|
||||||
|
int leftArgRTI = 0;
|
||||||
|
int rightArgRTI = 0;
|
||||||
|
|
||||||
if (setOperation->op != SETOP_UNION)
|
if (setOperation->op != SETOP_UNION)
|
||||||
{
|
{
|
||||||
|
@ -878,6 +892,36 @@ DeferErrorIfUnsupportedUnionQuery(Query *subqueryTree,
|
||||||
"cannot push down this subquery",
|
"cannot push down this subquery",
|
||||||
"Intersect and Except are currently unsupported", NULL);
|
"Intersect and Except are currently unsupported", NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsA(leftArg, RangeTblRef))
|
||||||
|
{
|
||||||
|
Node *leftArgSubquery = NULL;
|
||||||
|
leftArgRTI = ((RangeTblRef *) leftArg)->rtindex;
|
||||||
|
leftArgSubquery = (Node *) rt_fetch(leftArgRTI,
|
||||||
|
subqueryTree->rtable)->subquery;
|
||||||
|
if (HasReferenceTable(leftArgSubquery))
|
||||||
|
{
|
||||||
|
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
||||||
|
"cannot push down this subquery ",
|
||||||
|
"Reference tables are not supported with union"
|
||||||
|
" operator", NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsA(rightArg, RangeTblRef))
|
||||||
|
{
|
||||||
|
Node *rightArgSubquery = NULL;
|
||||||
|
rightArgRTI = ((RangeTblRef *) rightArg)->rtindex;
|
||||||
|
rightArgSubquery = (Node *) rt_fetch(rightArgRTI,
|
||||||
|
subqueryTree->rtable)->subquery;
|
||||||
|
if (HasReferenceTable(rightArgSubquery))
|
||||||
|
{
|
||||||
|
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
||||||
|
"cannot push down this subquery",
|
||||||
|
"Reference tables are not supported with union"
|
||||||
|
" operator", NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -982,7 +1026,7 @@ DeferErrorIfUnsupportedTableCombination(Query *queryTree)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TargetListOnPartitionColumn checks if at least one target list entry is on
|
* TargetListOnPartitionColumn checks if at least one target list entry is on
|
||||||
* partition column.
|
* partition column or the table is a reference table.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
TargetListOnPartitionColumn(Query *query, List *targetEntryList)
|
TargetListOnPartitionColumn(Query *query, List *targetEntryList)
|
||||||
|
@ -1003,9 +1047,9 @@ TargetListOnPartitionColumn(Query *query, List *targetEntryList)
|
||||||
FindReferencedTableColumn(targetExpression, NIL, query, &relationId, &column);
|
FindReferencedTableColumn(targetExpression, NIL, query, &relationId, &column);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the expression belongs to reference table directly returns true,
|
* If the expression belongs to reference table directly returns true.
|
||||||
* since logic of caller function checks whether it can find the necessaary
|
* We can assume that target list entry always on partition column of
|
||||||
* data from each node.
|
* reference tables.
|
||||||
*/
|
*/
|
||||||
if (IsDistributedTable(relationId) && PartitionMethod(relationId) ==
|
if (IsDistributedTable(relationId) && PartitionMethod(relationId) ==
|
||||||
DISTRIBUTE_BY_NONE)
|
DISTRIBUTE_BY_NONE)
|
||||||
|
@ -1380,6 +1424,149 @@ MultiPlanTree(Query *queryTree)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HasUnsupportedReferenceTableJoin returns true if there exists a outer join
|
||||||
|
* exist between reference table and distributed tables which does not obey the
|
||||||
|
* rules :
|
||||||
|
* - Reference tables can not be located in the outer part of the semi join (or
|
||||||
|
* the inner part of the anti join). Otherwise, we may have duplicate results.
|
||||||
|
* Although getting duplicate results is not possible by checking the equality
|
||||||
|
* on the column of the reference table and partition column of distributed table,
|
||||||
|
* we still keep these checks. Because, using the reference table in the outer
|
||||||
|
* part of the semi join is not very common.
|
||||||
|
* - Reference tables can not be located in the outer part of the left join and
|
||||||
|
* inner part of the right join. Otherwise we will definitely have duplicate rows.
|
||||||
|
* Beside, reference tables can not be used with full outer joins because of the
|
||||||
|
* same reason.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
HasUnsupportedReferenceTableJoin(PlannerRestrictionContext *plannerRestrictionContext)
|
||||||
|
{
|
||||||
|
List *joinRestrictionList =
|
||||||
|
plannerRestrictionContext->joinRestrictionContext->joinRestrictionList;
|
||||||
|
ListCell *joinRestrictionCell = NULL;
|
||||||
|
|
||||||
|
foreach(joinRestrictionCell, joinRestrictionList)
|
||||||
|
{
|
||||||
|
JoinRestriction *joinRestriction = (JoinRestriction *) lfirst(
|
||||||
|
joinRestrictionCell);
|
||||||
|
JoinType joinType = joinRestriction->joinType;
|
||||||
|
PlannerInfo *plannerInfo = joinRestriction->plannerInfo;
|
||||||
|
RelOptInfo *innerrel = joinRestriction->innerrel;
|
||||||
|
RelOptInfo *outerrel = joinRestriction->outerrel;
|
||||||
|
|
||||||
|
switch (joinType)
|
||||||
|
{
|
||||||
|
case JOIN_SEMI:
|
||||||
|
{
|
||||||
|
if (RelationInfoHasReferenceTable(plannerInfo, outerrel))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JOIN_ANTI:
|
||||||
|
{
|
||||||
|
if (RelationInfoHasReferenceTable(plannerInfo, innerrel))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JOIN_LEFT:
|
||||||
|
{
|
||||||
|
if (RelationInfoHasReferenceTable(plannerInfo, outerrel))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JOIN_RIGHT:
|
||||||
|
{
|
||||||
|
if (RelationInfoHasReferenceTable(plannerInfo, innerrel))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JOIN_FULL:
|
||||||
|
{
|
||||||
|
if (RelationInfoHasReferenceTable(plannerInfo, innerrel) ||
|
||||||
|
RelationInfoHasReferenceTable(
|
||||||
|
plannerInfo, outerrel))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{ }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReferenceTableExist check whether the relationInfo has reference table.
|
||||||
|
* Since relation ids of relationInfo indexes to the range table entry list of
|
||||||
|
* planner info, planner info is also passed.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
RelationInfoHasReferenceTable(PlannerInfo *plannerInfo, RelOptInfo *relationInfo)
|
||||||
|
{
|
||||||
|
Relids relids = bms_copy(relationInfo->relids);
|
||||||
|
int relationId = -1;
|
||||||
|
|
||||||
|
while ((relationId = bms_first_member(relids)) >= 0)
|
||||||
|
{
|
||||||
|
RangeTblEntry *rangeTableEntry = plannerInfo->simple_rte_array[relationId];
|
||||||
|
|
||||||
|
/* relationInfo has this range table entry */
|
||||||
|
if (HasReferenceTable((Node *) rangeTableEntry))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HasReferenceTable checks whether there exist a reference table in the
|
||||||
|
* given node.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
HasReferenceTable(Node *node)
|
||||||
|
{
|
||||||
|
List *relationList = NIL;
|
||||||
|
ListCell *relationCell = NULL;
|
||||||
|
ExtractRangeTableRelationWalkerInRTE(node, &relationList);
|
||||||
|
|
||||||
|
foreach(relationCell, relationList)
|
||||||
|
{
|
||||||
|
RangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(relationCell);
|
||||||
|
Oid relationId = rangeTableEntry->relid;
|
||||||
|
|
||||||
|
if (IsDistributedTable(relationId) && PartitionMethod(relationId) ==
|
||||||
|
DISTRIBUTE_BY_NONE)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ErrorIfQueryNotSupported checks that we can perform distributed planning for
|
* ErrorIfQueryNotSupported checks that we can perform distributed planning for
|
||||||
* the given query. The checks in this function will be removed as we support
|
* the given query. The checks in this function will be removed as we support
|
||||||
|
@ -2745,44 +2932,41 @@ ExtractRangeTableRelationWalker(Node *node, List **rangeTableRelationList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Get the list of relations from the given node. Note that the difference between
|
/*
|
||||||
* this function and ExtractRangeTableRelationWalker is that this one recursively
|
* ExtractRangeTableRelationWalkerInRTE obtains the list of relations from the
|
||||||
|
* given node. Note that the difference between this function and
|
||||||
|
* ExtractRangeTableRelationWalker is that this one recursively
|
||||||
* walk into range table entries if it can.
|
* walk into range table entries if it can.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
ExtractRTRelationFromNode(Node *node, List **rangeTableList)
|
ExtractRangeTableRelationWalkerInRTE(Node *node, List **rangeTableRelationList)
|
||||||
{
|
{
|
||||||
bool walkIsComplete = false;
|
bool walkIsComplete = false;
|
||||||
|
|
||||||
if (node == NULL)
|
if (node == NULL)
|
||||||
{
|
{
|
||||||
return false;
|
return walkIsComplete;
|
||||||
}
|
}
|
||||||
|
else if (IsA(node, RangeTblEntry))
|
||||||
if (IsA(node, RangeTblEntry))
|
|
||||||
{
|
{
|
||||||
RangeTblEntry *rangeTableEntry = (RangeTblEntry *) node;
|
RangeTblEntry *rangeTableEntry = (RangeTblEntry *) node;
|
||||||
|
List *rangeTableList = NIL;
|
||||||
|
rangeTableList = lappend(rangeTableList, rangeTableEntry);
|
||||||
|
|
||||||
if (rangeTableEntry->rtekind == RTE_RELATION &&
|
if (rangeTableEntry->rtekind == RTE_RELATION)
|
||||||
rangeTableEntry->relkind != RELKIND_VIEW)
|
|
||||||
{
|
{
|
||||||
(*rangeTableList) = lappend(*rangeTableList, rangeTableEntry);
|
(*rangeTableRelationList) = lappend(*rangeTableRelationList, rangeTableEntry);
|
||||||
}
|
}
|
||||||
else if (rangeTableEntry->rtekind == RTE_SUBQUERY)
|
else
|
||||||
{
|
{
|
||||||
walkIsComplete = query_tree_walker(rangeTableEntry->subquery,
|
walkIsComplete = range_table_walker(rangeTableList,
|
||||||
ExtractRTRelationFromNode,
|
ExtractRangeTableRelationWalkerInRTE,
|
||||||
rangeTableList, QTW_EXAMINE_RTES);
|
rangeTableRelationList, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (IsA(node, Query))
|
|
||||||
{
|
|
||||||
walkIsComplete = query_tree_walker((Query *) node, ExtractRTRelationFromNode,
|
|
||||||
rangeTableList, QTW_EXAMINE_RTES);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
walkIsComplete = expression_tree_walker(node, ExtractRTRelationFromNode,
|
walkIsComplete = ExtractRangeTableRelationWalker(node, rangeTableRelationList);
|
||||||
rangeTableList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return walkIsComplete;
|
return walkIsComplete;
|
||||||
|
|
|
@ -127,8 +127,6 @@ static Job * BuildJobTreeTaskList(Job *jobTree,
|
||||||
static List * SubquerySqlTaskList(Job *job,
|
static List * SubquerySqlTaskList(Job *job,
|
||||||
PlannerRestrictionContext *plannerRestrictionContext);
|
PlannerRestrictionContext *plannerRestrictionContext);
|
||||||
static void ErrorIfUnsupportedShardDistribution(Query *query);
|
static void ErrorIfUnsupportedShardDistribution(Query *query);
|
||||||
static void ErrorIfUnsupportedJoinReferenceTable(
|
|
||||||
PlannerRestrictionContext *plannerRestrictionContext);
|
|
||||||
static bool CoPartitionedTables(Oid firstRelationId, Oid secondRelationId);
|
static bool CoPartitionedTables(Oid firstRelationId, Oid secondRelationId);
|
||||||
static bool ShardIntervalsEqual(FmgrInfo *comparisonFunction,
|
static bool ShardIntervalsEqual(FmgrInfo *comparisonFunction,
|
||||||
ShardInterval *firstInterval,
|
ShardInterval *firstInterval,
|
||||||
|
@ -197,8 +195,6 @@ static StringInfo MergeTableQueryString(uint32 taskIdIndex, List *targetEntryLis
|
||||||
static StringInfo IntermediateTableQueryString(uint64 jobId, uint32 taskIdIndex,
|
static StringInfo IntermediateTableQueryString(uint64 jobId, uint32 taskIdIndex,
|
||||||
Query *reduceQuery);
|
Query *reduceQuery);
|
||||||
static uint32 FinalTargetEntryCount(List *targetEntryList);
|
static uint32 FinalTargetEntryCount(List *targetEntryList);
|
||||||
static bool ReferenceTableExist(PlannerInfo *plannerInfo, RelOptInfo *relationInfo);
|
|
||||||
static void ErrorIfSetOpWithReferenceTable(Query *queryTree);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2042,12 +2038,6 @@ SubquerySqlTaskList(Job *job, PlannerRestrictionContext *plannerRestrictionConte
|
||||||
/* error if shards are not co-partitioned */
|
/* error if shards are not co-partitioned */
|
||||||
ErrorIfUnsupportedShardDistribution(subquery);
|
ErrorIfUnsupportedShardDistribution(subquery);
|
||||||
|
|
||||||
/* error if unsupported join on reference tables */
|
|
||||||
ErrorIfUnsupportedJoinReferenceTable(plannerRestrictionContext);
|
|
||||||
|
|
||||||
/* error if reference table exists as a part of any set operation */
|
|
||||||
ErrorIfSetOpWithReferenceTable(subquery);
|
|
||||||
|
|
||||||
/* get list of all range tables in subquery tree */
|
/* get list of all range tables in subquery tree */
|
||||||
ExtractRangeTableRelationWalker((Node *) subquery, &rangeTableList);
|
ExtractRangeTableRelationWalker((Node *) subquery, &rangeTableList);
|
||||||
|
|
||||||
|
@ -2072,8 +2062,8 @@ SubquerySqlTaskList(Job *job, PlannerRestrictionContext *plannerRestrictionConte
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* That means all table are reference table and we can assign any reference
|
* That means all tables are reference tables and we can pick any any of them
|
||||||
* table as an anchor one .
|
* as an anchor table.
|
||||||
*/
|
*/
|
||||||
if (targetCacheEntry == NULL)
|
if (targetCacheEntry == NULL)
|
||||||
{
|
{
|
||||||
|
@ -2107,196 +2097,6 @@ SubquerySqlTaskList(Job *job, PlannerRestrictionContext *plannerRestrictionConte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ErrorIfUnsupportedJoinReferenceTable errors out if there exists a outer join
|
|
||||||
* exist between reference table and distributed tables.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
ErrorIfUnsupportedJoinReferenceTable(PlannerRestrictionContext *plannerRestrictionContext)
|
|
||||||
{
|
|
||||||
List *joinRestrictionList =
|
|
||||||
plannerRestrictionContext->joinRestrictionContext->joinRestrictionList;
|
|
||||||
ListCell *joinRestrictionCell = NULL;
|
|
||||||
|
|
||||||
foreach(joinRestrictionCell, joinRestrictionList)
|
|
||||||
{
|
|
||||||
JoinRestriction *joinRestriction = (JoinRestriction *) lfirst(
|
|
||||||
joinRestrictionCell);
|
|
||||||
JoinType joinType = joinRestriction->joinType;
|
|
||||||
PlannerInfo *plannerInfo = joinRestriction->plannerInfo;
|
|
||||||
RelOptInfo *innerrel = joinRestriction->innerrel;
|
|
||||||
RelOptInfo *outerrel = joinRestriction->outerrel;
|
|
||||||
|
|
||||||
switch (joinType)
|
|
||||||
{
|
|
||||||
case JOIN_SEMI:
|
|
||||||
{
|
|
||||||
if (ReferenceTableExist(plannerInfo, outerrel))
|
|
||||||
{
|
|
||||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("can not plan query having reference table on"
|
|
||||||
" the left part of semi join")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JOIN_ANTI:
|
|
||||||
{
|
|
||||||
if (ReferenceTableExist(plannerInfo, innerrel))
|
|
||||||
{
|
|
||||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("can not plan query having reference table on"
|
|
||||||
" the left part of anti join")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JOIN_LEFT:
|
|
||||||
{
|
|
||||||
if (ReferenceTableExist(plannerInfo, outerrel))
|
|
||||||
{
|
|
||||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("can not plan query having reference table on"
|
|
||||||
" the left part of left join")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JOIN_RIGHT:
|
|
||||||
{
|
|
||||||
if (ReferenceTableExist(plannerInfo, innerrel))
|
|
||||||
{
|
|
||||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("can not plan query having reference table on"
|
|
||||||
" the right part of right join")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JOIN_FULL:
|
|
||||||
{
|
|
||||||
if (ReferenceTableExist(plannerInfo, innerrel) || ReferenceTableExist(
|
|
||||||
plannerInfo, outerrel))
|
|
||||||
{
|
|
||||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg(
|
|
||||||
"can not plan query having reference table as a"
|
|
||||||
" part of full join")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
{ }
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ReferenceTableExist check whether the relationInfo has reference table.
|
|
||||||
* Since relation ids of relationInfo indexes to the range table entry list of
|
|
||||||
* query, query is also passed.
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
ReferenceTableExist(PlannerInfo *plannerInfo, RelOptInfo *relationInfo)
|
|
||||||
{
|
|
||||||
Relids relids = bms_copy(relationInfo->relids);
|
|
||||||
int relationId = -1;
|
|
||||||
|
|
||||||
while ((relationId = bms_first_member(relids)) >= 0)
|
|
||||||
{
|
|
||||||
RangeTblEntry *rangeTableEntry = plannerInfo->simple_rte_array[relationId];
|
|
||||||
|
|
||||||
/* relationInfo has this range table entry */
|
|
||||||
if (RTEContainsReferenceTable(rangeTableEntry))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* RTEContainsReferenceTable checks whether there exist a reference table in the
|
|
||||||
* given range table entry.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
RTEContainsReferenceTable(RangeTblEntry *rangeTableEntry)
|
|
||||||
{
|
|
||||||
List *relationList = NIL;
|
|
||||||
ListCell *relationCell = NULL;
|
|
||||||
ExtractRTRelationFromNode((Node *) rangeTableEntry, &relationList);
|
|
||||||
|
|
||||||
foreach(relationCell, relationList)
|
|
||||||
{
|
|
||||||
RangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(relationCell);
|
|
||||||
Oid relationId = rangeTableEntry->relid;
|
|
||||||
|
|
||||||
if (IsDistributedTable(relationId) && PartitionMethod(relationId) ==
|
|
||||||
DISTRIBUTE_BY_NONE)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ErrorIfSetOpWithReferenceTable checks whether there exist a reference table
|
|
||||||
* as a part of any set operation.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
ErrorIfSetOpWithReferenceTable(Query *queryTree)
|
|
||||||
{
|
|
||||||
List *joinTreeTableIndexList = NIL;
|
|
||||||
Index subqueryRteIndex = 0;
|
|
||||||
RangeTblEntry *rangeTableEntry = NULL;
|
|
||||||
Query *subqueryTree = NULL;
|
|
||||||
List *rangeTableList = queryTree->rtable;
|
|
||||||
Node *setOperations = queryTree->setOperations;
|
|
||||||
ExtractRangeTableIndexWalker((Node *) queryTree->jointree, &joinTreeTableIndexList);
|
|
||||||
|
|
||||||
if (setOperations != NULL)
|
|
||||||
{
|
|
||||||
List *rangeTableList = NIL;
|
|
||||||
ListCell *rangeTableCell = NULL;
|
|
||||||
ExtractRangeTableRelationWalker((Node *) queryTree, &rangeTableList);
|
|
||||||
|
|
||||||
foreach(rangeTableCell, rangeTableList)
|
|
||||||
{
|
|
||||||
RangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rangeTableCell);
|
|
||||||
Oid relationId = rangeTableEntry->relid;
|
|
||||||
if (PartitionMethod(relationId) == DISTRIBUTE_BY_NONE)
|
|
||||||
{
|
|
||||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg(
|
|
||||||
"can not plan query having reference table with union")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (list_length(joinTreeTableIndexList) < 1)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
subqueryRteIndex = linitial_int(joinTreeTableIndexList);
|
|
||||||
rangeTableEntry = rt_fetch(subqueryRteIndex, rangeTableList);
|
|
||||||
subqueryTree = rangeTableEntry->subquery;
|
|
||||||
|
|
||||||
if (subqueryTree != NULL)
|
|
||||||
{
|
|
||||||
return ErrorIfSetOpWithReferenceTable(subqueryTree);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ErrorIfUnsupportedShardDistribution gets list of relations in the given query
|
* ErrorIfUnsupportedShardDistribution gets list of relations in the given query
|
||||||
* and checks if two conditions below hold for them, otherwise it errors out.
|
* and checks if two conditions below hold for them, otherwise it errors out.
|
||||||
|
@ -2311,6 +2111,7 @@ ErrorIfUnsupportedShardDistribution(Query *query)
|
||||||
{
|
{
|
||||||
Oid firstTableRelationId = InvalidOid;
|
Oid firstTableRelationId = InvalidOid;
|
||||||
List *relationIdList = RelationIdList(query);
|
List *relationIdList = RelationIdList(query);
|
||||||
|
List *distributedRelationIdList = NIL;
|
||||||
ListCell *relationIdCell = NULL;
|
ListCell *relationIdCell = NULL;
|
||||||
uint32 relationIndex = 0;
|
uint32 relationIndex = 0;
|
||||||
uint32 rangeDistributedRelationCount = 0;
|
uint32 rangeDistributedRelationCount = 0;
|
||||||
|
@ -2320,19 +2121,31 @@ ErrorIfUnsupportedShardDistribution(Query *query)
|
||||||
{
|
{
|
||||||
Oid relationId = lfirst_oid(relationIdCell);
|
Oid relationId = lfirst_oid(relationIdCell);
|
||||||
char partitionMethod = PartitionMethod(relationId);
|
char partitionMethod = PartitionMethod(relationId);
|
||||||
|
|
||||||
if (partitionMethod == DISTRIBUTE_BY_RANGE)
|
if (partitionMethod == DISTRIBUTE_BY_RANGE)
|
||||||
{
|
{
|
||||||
rangeDistributedRelationCount++;
|
rangeDistributedRelationCount++;
|
||||||
|
distributedRelationIdList = lappend_oid(distributedRelationIdList,
|
||||||
|
relationId);
|
||||||
}
|
}
|
||||||
else if (partitionMethod == DISTRIBUTE_BY_HASH)
|
else if (partitionMethod == DISTRIBUTE_BY_HASH)
|
||||||
{
|
{
|
||||||
hashDistributedRelationCount++;
|
hashDistributedRelationCount++;
|
||||||
|
distributedRelationIdList = lappend_oid(distributedRelationIdList,
|
||||||
|
relationId);
|
||||||
}
|
}
|
||||||
else
|
else if (partitionMethod == DISTRIBUTE_BY_NONE)
|
||||||
{
|
{
|
||||||
/* do not need to handle reference tables */
|
/* do not need to handle reference tables */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot pushdown this subquery"),
|
||||||
|
errdetail("Currently append partitioned relations "
|
||||||
|
"are not supported")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((rangeDistributedRelationCount > 0) && (hashDistributedRelationCount > 0))
|
if ((rangeDistributedRelationCount > 0) && (hashDistributedRelationCount > 0))
|
||||||
|
@ -2343,18 +2156,11 @@ ErrorIfUnsupportedShardDistribution(Query *query)
|
||||||
"partitioned relations are unsupported")));
|
"partitioned relations are unsupported")));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(relationIdCell, relationIdList)
|
foreach(relationIdCell, distributedRelationIdList)
|
||||||
{
|
{
|
||||||
Oid relationId = lfirst_oid(relationIdCell);
|
Oid relationId = lfirst_oid(relationIdCell);
|
||||||
bool coPartitionedTables = false;
|
bool coPartitionedTables = false;
|
||||||
Oid currentRelationId = relationId;
|
Oid currentRelationId = relationId;
|
||||||
char partitionMethod = PartitionMethod(relationId);
|
|
||||||
|
|
||||||
/* do not need to check reference tables */
|
|
||||||
if (partitionMethod == DISTRIBUTE_BY_NONE)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get shard list of first relation and continue for the next relation */
|
/* get shard list of first relation and continue for the next relation */
|
||||||
if (relationIndex == 0)
|
if (relationIndex == 0)
|
||||||
|
|
|
@ -275,7 +275,7 @@ CreateSingleTaskRouterPlan(Query *originalQuery, Query *query,
|
||||||
* The function returns hashed columns generated by MakeInt4Column() for the hash
|
* The function returns hashed columns generated by MakeInt4Column() for the hash
|
||||||
* partitioned tables in place of partition columns.
|
* partitioned tables in place of partition columns.
|
||||||
*
|
*
|
||||||
* The function errors out if the given shard interval does not belong to a hash,
|
* The function returns NIL if shard interval does not belong to a hash,
|
||||||
* range and append distributed tables.
|
* range and append distributed tables.
|
||||||
*
|
*
|
||||||
* NB: If you update this, also look at PrunableExpressionsWalker().
|
* NB: If you update this, also look at PrunableExpressionsWalker().
|
||||||
|
|
|
@ -153,6 +153,7 @@ SafeToPushdownUnionSubquery(RelationRestrictionContext *restrictionContext)
|
||||||
foreach(relationRestrictionCell, restrictionContext->relationRestrictionList)
|
foreach(relationRestrictionCell, restrictionContext->relationRestrictionList)
|
||||||
{
|
{
|
||||||
RelationRestriction *relationRestriction = lfirst(relationRestrictionCell);
|
RelationRestriction *relationRestriction = lfirst(relationRestrictionCell);
|
||||||
|
Oid relationId = relationRestriction->relationId;
|
||||||
Index partitionKeyIndex = InvalidAttrNumber;
|
Index partitionKeyIndex = InvalidAttrNumber;
|
||||||
PlannerInfo *relationPlannerRoot = relationRestriction->plannerInfo;
|
PlannerInfo *relationPlannerRoot = relationRestriction->plannerInfo;
|
||||||
List *targetList = relationPlannerRoot->parse->targetList;
|
List *targetList = relationPlannerRoot->parse->targetList;
|
||||||
|
@ -160,6 +161,20 @@ SafeToPushdownUnionSubquery(RelationRestrictionContext *restrictionContext)
|
||||||
Var *varToBeAdded = NULL;
|
Var *varToBeAdded = NULL;
|
||||||
TargetEntry *targetEntryToAdd = NULL;
|
TargetEntry *targetEntryToAdd = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Although it is not the best place to error out when facing with reference
|
||||||
|
* tables, we decide to error out here. Otherwise, we need to add equality
|
||||||
|
* for each reference table and it is more complex to implement. In the
|
||||||
|
* future implementation all checks will be gathered to single point.
|
||||||
|
*/
|
||||||
|
if (PartitionMethod(relationId) == DISTRIBUTE_BY_NONE)
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot pushdown this query"),
|
||||||
|
errdetail(
|
||||||
|
"Reference tables are not allowed with set operations")));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We first check whether UNION ALLs are pulled up or not. Note that Postgres
|
* We first check whether UNION ALLs are pulled up or not. Note that Postgres
|
||||||
* planner creates AppendRelInfos per each UNION ALL query that is pulled up.
|
* planner creates AppendRelInfos per each UNION ALL query that is pulled up.
|
||||||
|
|
|
@ -206,7 +206,7 @@ extern List * TableEntryList(List *rangeTableList);
|
||||||
extern List * UsedTableEntryList(Query *query);
|
extern List * UsedTableEntryList(Query *query);
|
||||||
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);
|
||||||
extern bool ExtractRTRelationFromNode(Node *node, List **rangeTableList);
|
extern bool ExtractRangeTableRelationWalkerInRTE(Node *node, List **rangeTableList);
|
||||||
extern List * pull_var_clause_default(Node *node);
|
extern List * pull_var_clause_default(Node *node);
|
||||||
extern bool OperatorImplementsEquality(Oid opno);
|
extern bool OperatorImplementsEquality(Oid opno);
|
||||||
|
|
||||||
|
|
|
@ -286,7 +286,7 @@ extern Const * MakeInt4Constant(Datum constantValue);
|
||||||
extern int CompareShardPlacements(const void *leftElement, const void *rightElement);
|
extern int CompareShardPlacements(const void *leftElement, const void *rightElement);
|
||||||
extern bool ShardIntervalsOverlap(ShardInterval *firstInterval,
|
extern bool ShardIntervalsOverlap(ShardInterval *firstInterval,
|
||||||
ShardInterval *secondInterval);
|
ShardInterval *secondInterval);
|
||||||
extern bool RTEContainsReferenceTable(RangeTblEntry *rangeTableEntry);
|
extern bool HasReferenceTable(Node *node);
|
||||||
|
|
||||||
/* function declarations for Task and Task list operations */
|
/* function declarations for Task and Task list operations */
|
||||||
extern bool TasksEqual(const Task *a, const Task *b);
|
extern bool TasksEqual(const Task *a, const Task *b);
|
||||||
|
|
|
@ -303,7 +303,8 @@ count(*) AS cnt, "generated_group_field"
|
||||||
ORDER BY
|
ORDER BY
|
||||||
cnt DESC, generated_group_field ASC
|
cnt DESC, generated_group_field ASC
|
||||||
LIMIT 10;
|
LIMIT 10;
|
||||||
ERROR: can not plan query having reference table on the left part of left join
|
ERROR: cannot pushdown the subquery
|
||||||
|
DETAIL: There exist a reference table in the outer part of the outer join
|
||||||
-- RIGHT JOINs used with INNER JOINs should error out since reference table exist in the
|
-- RIGHT JOINs used with INNER JOINs should error out since reference table exist in the
|
||||||
-- right side of the RIGHT JOIN.
|
-- right side of the RIGHT JOIN.
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -342,7 +343,8 @@ count(*) AS cnt, "generated_group_field"
|
||||||
ORDER BY
|
ORDER BY
|
||||||
cnt DESC, generated_group_field ASC
|
cnt DESC, generated_group_field ASC
|
||||||
LIMIT 10;
|
LIMIT 10;
|
||||||
ERROR: can not plan query having reference table on the left part of left join
|
ERROR: cannot pushdown the subquery
|
||||||
|
DETAIL: There exist a reference table in the outer part of the outer join
|
||||||
-- Outer subquery with reference table
|
-- Outer subquery with reference table
|
||||||
SELECT "some_users_data".user_id, lastseen
|
SELECT "some_users_data".user_id, lastseen
|
||||||
FROM
|
FROM
|
||||||
|
@ -373,7 +375,8 @@ FROM
|
||||||
ORDER BY
|
ORDER BY
|
||||||
user_id
|
user_id
|
||||||
limit 50;
|
limit 50;
|
||||||
ERROR: can not plan query having reference table as a part of full join
|
ERROR: cannot pushdown the subquery
|
||||||
|
DETAIL: There exist a reference table in the outer part of the outer join
|
||||||
--
|
--
|
||||||
-- UNIONs and JOINs with reference tables, shoukld error out
|
-- UNIONs and JOINs with reference tables, shoukld error out
|
||||||
--
|
--
|
||||||
|
@ -437,7 +440,8 @@ GROUP BY
|
||||||
types
|
types
|
||||||
ORDER BY
|
ORDER BY
|
||||||
types;
|
types;
|
||||||
ERROR: can not plan query having reference table with union
|
ERROR: cannot push down this subquery
|
||||||
|
DETAIL: Reference tables are not supported with union operator
|
||||||
-- reference table exist in the subquery of union, should error out
|
-- reference table exist in the subquery of union, should error out
|
||||||
SELECT ("final_query"."event_types") as types, count(*) AS sumOfEventType
|
SELECT ("final_query"."event_types") as types, count(*) AS sumOfEventType
|
||||||
FROM
|
FROM
|
||||||
|
@ -509,7 +513,8 @@ GROUP BY
|
||||||
types
|
types
|
||||||
ORDER BY
|
ORDER BY
|
||||||
types;
|
types;
|
||||||
ERROR: can not plan query having reference table with union
|
ERROR: cannot push down this subquery
|
||||||
|
DETAIL: Reference tables are not supported with union operator
|
||||||
--
|
--
|
||||||
-- Should error out with UNION ALL Queries on reference tables
|
-- Should error out with UNION ALL Queries on reference tables
|
||||||
--
|
--
|
||||||
|
@ -563,4 +568,5 @@ INNER JOIN
|
||||||
WHERE value_1 > 50 and value_1 < 70) AS t ON (t.user_id = q.user_id)) as final_query
|
WHERE value_1 > 50 and value_1 < 70) AS t ON (t.user_id = q.user_id)) as final_query
|
||||||
GROUP BY types
|
GROUP BY types
|
||||||
ORDER BY types;
|
ORDER BY types;
|
||||||
ERROR: can not plan query having reference table with union
|
ERROR: cannot push down this subquery
|
||||||
|
DETAIL: Reference tables are not supported with union operator
|
||||||
|
|
|
@ -139,6 +139,7 @@ SELECT user_id, value_2 FROM users_table WHERE
|
||||||
HAVING sum(submit_card_info) > 0
|
HAVING sum(submit_card_info) > 0
|
||||||
)
|
)
|
||||||
ORDER BY 1, 2;
|
ORDER BY 1, 2;
|
||||||
ERROR: can not plan query having reference table on the left part of left join
|
ERROR: cannot pushdown the subquery
|
||||||
|
DETAIL: There exist a reference table in the outer part of the outer join
|
||||||
DROP TABLE events_reference_table;
|
DROP TABLE events_reference_table;
|
||||||
DROP TABLE users_reference_table;
|
DROP TABLE users_reference_table;
|
||||||
|
|
Loading…
Reference in New Issue