Move physical planner checks to logical planner

pull/1532/head
velioglu 2017-08-08 09:16:27 +03:00
parent 0359d03530
commit ceba81ce35
8 changed files with 258 additions and 246 deletions

View File

@ -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;

View File

@ -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)

View File

@ -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().

View File

@ -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.

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;