mirror of https://github.com/citusdata/citus.git
use planner restriction context
* to fetch join and base restrictions properly, * to push down restrictions properly.outer-join-noncolocated-dist-tables
parent
454857f3a7
commit
222f11e23e
|
@ -59,6 +59,7 @@
|
||||||
#include "optimizer/optimizer.h"
|
#include "optimizer/optimizer.h"
|
||||||
#include "optimizer/plancat.h"
|
#include "optimizer/plancat.h"
|
||||||
#include "optimizer/pathnode.h"
|
#include "optimizer/pathnode.h"
|
||||||
|
#include "optimizer/paths.h"
|
||||||
#include "optimizer/planner.h"
|
#include "optimizer/planner.h"
|
||||||
#include "optimizer/planmain.h"
|
#include "optimizer/planmain.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
@ -126,7 +127,8 @@ static void PopPlannerRestrictionContext(void);
|
||||||
static void ResetPlannerRestrictionContext(
|
static void ResetPlannerRestrictionContext(
|
||||||
PlannerRestrictionContext *plannerRestrictionContext);
|
PlannerRestrictionContext *plannerRestrictionContext);
|
||||||
static PlannedStmt * PlanFastPathDistributedStmt(DistributedPlanningContext *planContext,
|
static PlannedStmt * PlanFastPathDistributedStmt(DistributedPlanningContext *planContext,
|
||||||
Node *distributionKeyValue);
|
Node *distributionKeyValue, int
|
||||||
|
fastPathRelId);
|
||||||
static PlannedStmt * PlanDistributedStmt(DistributedPlanningContext *planContext,
|
static PlannedStmt * PlanDistributedStmt(DistributedPlanningContext *planContext,
|
||||||
int rteIdCounter);
|
int rteIdCounter);
|
||||||
static RTEListProperties * GetRTEListProperties(List *rangeTableList);
|
static RTEListProperties * GetRTEListProperties(List *rangeTableList);
|
||||||
|
@ -144,6 +146,7 @@ distributed_planner(Query *parse,
|
||||||
bool needsDistributedPlanning = false;
|
bool needsDistributedPlanning = false;
|
||||||
bool fastPathRouterQuery = false;
|
bool fastPathRouterQuery = false;
|
||||||
Node *distributionKeyValue = NULL;
|
Node *distributionKeyValue = NULL;
|
||||||
|
int fastPathRelId = InvalidOid;
|
||||||
|
|
||||||
List *rangeTableList = ExtractRangeTableEntryList(parse);
|
List *rangeTableList = ExtractRangeTableEntryList(parse);
|
||||||
|
|
||||||
|
@ -247,7 +250,11 @@ distributed_planner(Query *parse,
|
||||||
{
|
{
|
||||||
if (fastPathRouterQuery)
|
if (fastPathRouterQuery)
|
||||||
{
|
{
|
||||||
result = PlanFastPathDistributedStmt(&planContext, distributionKeyValue);
|
RangeTblEntry *rangeTableEntry = (RangeTblEntry *) linitial(parse->rtable);
|
||||||
|
fastPathRelId = rangeTableEntry->relid;
|
||||||
|
|
||||||
|
result = PlanFastPathDistributedStmt(&planContext, distributionKeyValue,
|
||||||
|
fastPathRelId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -686,7 +693,7 @@ IsUpdateOrDelete(Query *query)
|
||||||
*/
|
*/
|
||||||
static PlannedStmt *
|
static PlannedStmt *
|
||||||
PlanFastPathDistributedStmt(DistributedPlanningContext *planContext,
|
PlanFastPathDistributedStmt(DistributedPlanningContext *planContext,
|
||||||
Node *distributionKeyValue)
|
Node *distributionKeyValue, int fastPathRelId)
|
||||||
{
|
{
|
||||||
FastPathRestrictionContext *fastPathContext =
|
FastPathRestrictionContext *fastPathContext =
|
||||||
planContext->plannerRestrictionContext->fastPathRestrictionContext;
|
planContext->plannerRestrictionContext->fastPathRestrictionContext;
|
||||||
|
@ -694,6 +701,9 @@ PlanFastPathDistributedStmt(DistributedPlanningContext *planContext,
|
||||||
planContext->plannerRestrictionContext->fastPathRestrictionContext->
|
planContext->plannerRestrictionContext->fastPathRestrictionContext->
|
||||||
fastPathRouterQuery = true;
|
fastPathRouterQuery = true;
|
||||||
|
|
||||||
|
planContext->plannerRestrictionContext->fastPathRestrictionContext->
|
||||||
|
distRelId = fastPathRelId;
|
||||||
|
|
||||||
if (distributionKeyValue == NULL)
|
if (distributionKeyValue == NULL)
|
||||||
{
|
{
|
||||||
/* nothing to record */
|
/* nothing to record */
|
||||||
|
@ -710,6 +720,8 @@ PlanFastPathDistributedStmt(DistributedPlanningContext *planContext,
|
||||||
planContext->plan = FastPathPlanner(planContext->originalQuery, planContext->query,
|
planContext->plan = FastPathPlanner(planContext->originalQuery, planContext->query,
|
||||||
planContext->boundParams);
|
planContext->boundParams);
|
||||||
|
|
||||||
|
RelabelPlannerRestrictionContext(planContext->plannerRestrictionContext);
|
||||||
|
|
||||||
return CreateDistributedPlannedStmt(planContext);
|
return CreateDistributedPlannedStmt(planContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -955,6 +967,42 @@ TryCreateDistributedPlannedStmt(PlannedStmt *localPlan,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReplanAfterQueryModification replans modified originalquery to update plannercontext
|
||||||
|
* properly. Returns modified query.
|
||||||
|
*/
|
||||||
|
Query *
|
||||||
|
ReplanAfterQueryModification(Query *originalQuery, ParamListInfo boundParams)
|
||||||
|
{
|
||||||
|
Query *newQuery = copyObject(originalQuery);
|
||||||
|
bool setPartitionedTablesInherited = false;
|
||||||
|
PlannerRestrictionContext *currentPlannerRestrictionContext =
|
||||||
|
CurrentPlannerRestrictionContext();
|
||||||
|
|
||||||
|
/* reset the current planner restrictions context */
|
||||||
|
ResetPlannerRestrictionContext(currentPlannerRestrictionContext);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We force standard_planner to treat partitioned tables as regular tables
|
||||||
|
* by clearing the inh flag on RTEs. We already did this at the start of
|
||||||
|
* distributed_planner, but on a copy of the original query, so we need
|
||||||
|
* to do it again here.
|
||||||
|
*/
|
||||||
|
AdjustPartitioningForDistributedPlanning(ExtractRangeTableEntryList(newQuery),
|
||||||
|
setPartitionedTablesInherited);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some relations may have been removed from the query, but we can skip
|
||||||
|
* AssignRTEIdentities since we currently do not rely on RTE identities
|
||||||
|
* being contiguous.
|
||||||
|
*/
|
||||||
|
|
||||||
|
standard_planner(newQuery, NULL, 0, boundParams);
|
||||||
|
|
||||||
|
return newQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CreateDistributedPlan generates a distributed plan for a query.
|
* CreateDistributedPlan generates a distributed plan for a query.
|
||||||
* It goes through 3 steps:
|
* It goes through 3 steps:
|
||||||
|
@ -1119,30 +1167,7 @@ CreateDistributedPlan(uint64 planId, bool allowRecursivePlanning, Query *origina
|
||||||
"joined on their distribution columns")));
|
"joined on their distribution columns")));
|
||||||
}
|
}
|
||||||
|
|
||||||
Query *newQuery = copyObject(originalQuery);
|
Query *newQuery = ReplanAfterQueryModification(originalQuery, boundParams);
|
||||||
bool setPartitionedTablesInherited = false;
|
|
||||||
PlannerRestrictionContext *currentPlannerRestrictionContext =
|
|
||||||
CurrentPlannerRestrictionContext();
|
|
||||||
|
|
||||||
/* reset the current planner restrictions context */
|
|
||||||
ResetPlannerRestrictionContext(currentPlannerRestrictionContext);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We force standard_planner to treat partitioned tables as regular tables
|
|
||||||
* by clearing the inh flag on RTEs. We already did this at the start of
|
|
||||||
* distributed_planner, but on a copy of the original query, so we need
|
|
||||||
* to do it again here.
|
|
||||||
*/
|
|
||||||
AdjustPartitioningForDistributedPlanning(ExtractRangeTableEntryList(newQuery),
|
|
||||||
setPartitionedTablesInherited);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Some relations may have been removed from the query, but we can skip
|
|
||||||
* AssignRTEIdentities since we currently do not rely on RTE identities
|
|
||||||
* being contiguous.
|
|
||||||
*/
|
|
||||||
|
|
||||||
standard_planner(newQuery, NULL, 0, boundParams);
|
|
||||||
|
|
||||||
/* overwrite the old transformed query with the new transformed query */
|
/* overwrite the old transformed query with the new transformed query */
|
||||||
*query = *newQuery;
|
*query = *newQuery;
|
||||||
|
@ -1874,6 +1899,27 @@ multi_join_restriction_hook(PlannerInfo *root,
|
||||||
joinRestriction->innerrelRelids = bms_copy(innerrel->relids);
|
joinRestriction->innerrelRelids = bms_copy(innerrel->relids);
|
||||||
joinRestriction->outerrelRelids = bms_copy(outerrel->relids);
|
joinRestriction->outerrelRelids = bms_copy(outerrel->relids);
|
||||||
|
|
||||||
|
Relids joinrelids = bms_union(innerrel->relids, outerrel->relids);
|
||||||
|
List *prevVals = NIL;
|
||||||
|
EquivalenceClass *eqclass = NULL;
|
||||||
|
foreach_ptr(eqclass, root->eq_classes)
|
||||||
|
{
|
||||||
|
prevVals = lappend_int(prevVals, eqclass->ec_has_const);
|
||||||
|
eqclass->ec_has_const = false;
|
||||||
|
}
|
||||||
|
joinRestrictionContext->generatedEcJoinRestrictInfoList =
|
||||||
|
generate_join_implied_equalities(
|
||||||
|
root,
|
||||||
|
joinrelids,
|
||||||
|
outerrel->relids,
|
||||||
|
innerrel);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < list_length(prevVals); i++)
|
||||||
|
{
|
||||||
|
EquivalenceClass *eqClass = list_nth(root->eq_classes, i);
|
||||||
|
eqClass->ec_has_const = list_nth_int(prevVals, i);
|
||||||
|
}
|
||||||
|
|
||||||
joinRestrictionContext->joinRestrictionList =
|
joinRestrictionContext->joinRestrictionList =
|
||||||
lappend(joinRestrictionContext->joinRestrictionList, joinRestriction);
|
lappend(joinRestrictionContext->joinRestrictionList, joinRestriction);
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,9 @@ static DeferredErrorMessage * InsertPartitionColumnMatchesSelect(Query *query,
|
||||||
Oid *
|
Oid *
|
||||||
selectPartitionColumnTableId);
|
selectPartitionColumnTableId);
|
||||||
static DistributedPlan * CreateNonPushableInsertSelectPlan(uint64 planId, Query *parse,
|
static DistributedPlan * CreateNonPushableInsertSelectPlan(uint64 planId, Query *parse,
|
||||||
ParamListInfo boundParams);
|
ParamListInfo boundParams,
|
||||||
|
PlannerRestrictionContext *
|
||||||
|
plannerRestrictionContext);
|
||||||
static DeferredErrorMessage * NonPushableInsertSelectSupported(Query *insertSelectQuery);
|
static DeferredErrorMessage * NonPushableInsertSelectSupported(Query *insertSelectQuery);
|
||||||
static Query * WrapSubquery(Query *subquery);
|
static Query * WrapSubquery(Query *subquery);
|
||||||
static void RelabelTargetEntryList(List *selectTargetList, List *insertTargetList);
|
static void RelabelTargetEntryList(List *selectTargetList, List *insertTargetList);
|
||||||
|
@ -253,7 +255,8 @@ CreateInsertSelectPlanInternal(uint64 planId, Query *originalQuery,
|
||||||
* repartitioning.
|
* repartitioning.
|
||||||
*/
|
*/
|
||||||
distributedPlan = CreateNonPushableInsertSelectPlan(planId, originalQuery,
|
distributedPlan = CreateNonPushableInsertSelectPlan(planId, originalQuery,
|
||||||
boundParams);
|
boundParams,
|
||||||
|
plannerRestrictionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
return distributedPlan;
|
return distributedPlan;
|
||||||
|
@ -360,6 +363,62 @@ CreateDistributedInsertSelectPlan(Query *originalQuery,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RelabelPlannerRestrictionContext relabels var nos inside restriction infos to 1.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
RelabelPlannerRestrictionContext(PlannerRestrictionContext *plannerRestrictionContext)
|
||||||
|
{
|
||||||
|
List *relationRestrictionList =
|
||||||
|
plannerRestrictionContext->relationRestrictionContext->relationRestrictionList;
|
||||||
|
|
||||||
|
if (plannerRestrictionContext->fastPathRestrictionContext &&
|
||||||
|
plannerRestrictionContext->fastPathRestrictionContext->distributionKeyValue)
|
||||||
|
{
|
||||||
|
Const *distKeyVal =
|
||||||
|
plannerRestrictionContext->fastPathRestrictionContext->distributionKeyValue;
|
||||||
|
Var *partitionColumn = PartitionColumn(
|
||||||
|
plannerRestrictionContext->fastPathRestrictionContext->distRelId, 1);
|
||||||
|
OpExpr *partitionExpression = MakeOpExpression(partitionColumn,
|
||||||
|
BTEqualStrategyNumber);
|
||||||
|
Node *rightOp = get_rightop((Expr *) partitionExpression);
|
||||||
|
Const *rightConst = (Const *) rightOp;
|
||||||
|
*rightConst = *distKeyVal;
|
||||||
|
|
||||||
|
RestrictInfo *fastpathRestrictInfo = makeNode(RestrictInfo);
|
||||||
|
fastpathRestrictInfo->can_join = false;
|
||||||
|
fastpathRestrictInfo->is_pushed_down = true;
|
||||||
|
fastpathRestrictInfo->clause = (Expr *) partitionExpression;
|
||||||
|
|
||||||
|
RelationRestriction *relationRestriction = palloc0(sizeof(RelationRestriction));
|
||||||
|
relationRestriction->relOptInfo = palloc0(sizeof(RelOptInfo));
|
||||||
|
relationRestriction->relOptInfo->baserestrictinfo = list_make1(
|
||||||
|
fastpathRestrictInfo);
|
||||||
|
plannerRestrictionContext->relationRestrictionContext->relationRestrictionList =
|
||||||
|
list_make1(relationRestriction);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RelationRestriction *relationRestriction = NULL;
|
||||||
|
foreach_ptr(relationRestriction, relationRestrictionList)
|
||||||
|
{
|
||||||
|
RelOptInfo *relOptInfo = relationRestriction->relOptInfo;
|
||||||
|
List *baseRestrictInfoList = relOptInfo->baserestrictinfo;
|
||||||
|
RestrictInfo *baseRestrictInfo = NULL;
|
||||||
|
foreach_ptr(baseRestrictInfo, baseRestrictInfoList)
|
||||||
|
{
|
||||||
|
List *varList = pull_var_clause_default((Node *) baseRestrictInfo->clause);
|
||||||
|
Var *var = NULL;
|
||||||
|
foreach_ptr(var, varList)
|
||||||
|
{
|
||||||
|
var->varno = 1;
|
||||||
|
var->varnosyn = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CreateInsertSelectIntoLocalTablePlan creates the plan for INSERT .. SELECT queries
|
* CreateInsertSelectIntoLocalTablePlan creates the plan for INSERT .. SELECT queries
|
||||||
* where the selected table is distributed and the inserted table is not.
|
* where the selected table is distributed and the inserted table is not.
|
||||||
|
@ -384,6 +443,8 @@ CreateInsertSelectIntoLocalTablePlan(uint64 planId, Query *insertSelectQuery,
|
||||||
/* get the SELECT query (may have changed after PrepareInsertSelectForCitusPlanner) */
|
/* get the SELECT query (may have changed after PrepareInsertSelectForCitusPlanner) */
|
||||||
Query *selectQuery = selectRte->subquery;
|
Query *selectQuery = selectRte->subquery;
|
||||||
|
|
||||||
|
RelabelPlannerRestrictionContext(plannerRestrictionContext);
|
||||||
|
|
||||||
bool allowRecursivePlanning = true;
|
bool allowRecursivePlanning = true;
|
||||||
DistributedPlan *distPlan = CreateDistributedPlan(planId, allowRecursivePlanning,
|
DistributedPlan *distPlan = CreateDistributedPlan(planId, allowRecursivePlanning,
|
||||||
selectQuery,
|
selectQuery,
|
||||||
|
@ -1476,7 +1537,8 @@ InsertPartitionColumnMatchesSelect(Query *query, RangeTblEntry *insertRte,
|
||||||
* distributed table. The query plan can also be executed on a worker in MX.
|
* distributed table. The query plan can also be executed on a worker in MX.
|
||||||
*/
|
*/
|
||||||
static DistributedPlan *
|
static DistributedPlan *
|
||||||
CreateNonPushableInsertSelectPlan(uint64 planId, Query *parse, ParamListInfo boundParams)
|
CreateNonPushableInsertSelectPlan(uint64 planId, Query *parse, ParamListInfo boundParams,
|
||||||
|
PlannerRestrictionContext *plannerRestrictionContext)
|
||||||
{
|
{
|
||||||
Query *insertSelectQuery = copyObject(parse);
|
Query *insertSelectQuery = copyObject(parse);
|
||||||
|
|
||||||
|
|
|
@ -25,19 +25,22 @@
|
||||||
#include "distributed/metadata_cache.h"
|
#include "distributed/metadata_cache.h"
|
||||||
#include "distributed/multi_join_order.h"
|
#include "distributed/multi_join_order.h"
|
||||||
#include "distributed/multi_physical_planner.h"
|
#include "distributed/multi_physical_planner.h"
|
||||||
|
#include "distributed/multi_router_planner.h"
|
||||||
#include "distributed/pg_dist_partition.h"
|
#include "distributed/pg_dist_partition.h"
|
||||||
|
#include "distributed/shard_pruning.h"
|
||||||
#include "distributed/worker_protocol.h"
|
#include "distributed/worker_protocol.h"
|
||||||
#include "lib/stringinfo.h"
|
#include "lib/stringinfo.h"
|
||||||
#include "optimizer/optimizer.h"
|
#include "optimizer/optimizer.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
|
#include "nodes/nodes.h"
|
||||||
|
#include "nodes/pathnodes.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/datum.h"
|
#include "utils/datum.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
|
||||||
/* Config variables managed via guc.c */
|
/* Config variables managed via guc.c */
|
||||||
bool LogMultiJoinOrder = false; /* print join order as a debugging aid */
|
bool LogMultiJoinOrder = false; /* print join order as a debugging aid */
|
||||||
bool EnableSingleHashRepartitioning = false;
|
bool EnableSingleHashRepartitioning = false;
|
||||||
|
@ -55,7 +58,8 @@ static RuleEvalFunction RuleEvalFunctionArray[JOIN_RULE_LAST] = { 0 }; /* join r
|
||||||
/* Local functions forward declarations */
|
/* Local functions forward declarations */
|
||||||
static bool JoinExprListWalker(Node *node, List **joinList);
|
static bool JoinExprListWalker(Node *node, List **joinList);
|
||||||
static List * JoinOrderForTable(TableEntry *firstTable, List *tableEntryList,
|
static List * JoinOrderForTable(TableEntry *firstTable, List *tableEntryList,
|
||||||
List *joinClauseList);
|
List *joinRestrictInfoListList,
|
||||||
|
List *generatedEcJoinClauseList);
|
||||||
static List * BestJoinOrder(List *candidateJoinOrders);
|
static List * BestJoinOrder(List *candidateJoinOrders);
|
||||||
static List * FewestOfJoinRuleType(List *candidateJoinOrders, JoinRuleType ruleType);
|
static List * FewestOfJoinRuleType(List *candidateJoinOrders, JoinRuleType ruleType);
|
||||||
static uint32 JoinRuleTypeCount(List *joinOrder, JoinRuleType ruleTypeToCount);
|
static uint32 JoinRuleTypeCount(List *joinOrder, JoinRuleType ruleTypeToCount);
|
||||||
|
@ -65,27 +69,39 @@ static uint32 LargeDataTransferLocation(List *joinOrder);
|
||||||
static List * TableEntryListDifference(List *lhsTableList, List *rhsTableList);
|
static List * TableEntryListDifference(List *lhsTableList, List *rhsTableList);
|
||||||
static bool ConvertSemiToInnerInJoinInfoContext(JoinInfoContext *joinOrderContext);
|
static bool ConvertSemiToInnerInJoinInfoContext(JoinInfoContext *joinOrderContext);
|
||||||
static bool JoinInfoContextHasAntiJoin(JoinInfoContext *joinOrderContext);
|
static bool JoinInfoContextHasAntiJoin(JoinInfoContext *joinOrderContext);
|
||||||
|
static List * FindJoinClauseForTables(List *joinRestrictInfoListList,
|
||||||
|
List *generatedEcJoinClauseList,
|
||||||
|
List *lhsTableIdList,
|
||||||
|
uint32 rhsTableId,
|
||||||
|
JoinType joinType);
|
||||||
static const char * JoinTypeName(JoinType jointype);
|
static const char * JoinTypeName(JoinType jointype);
|
||||||
|
static List * ExtractPushdownJoinRestrictInfos(List *joinRestrictInfoList,
|
||||||
|
RestrictInfo *joinRestrictInfo, JoinType
|
||||||
|
joinType);
|
||||||
|
|
||||||
/* Local functions forward declarations for join evaluations */
|
/* Local functions forward declarations for join evaluations */
|
||||||
static JoinOrderNode * EvaluateJoinRules(List *joinedTableList,
|
static JoinOrderNode * EvaluateJoinRules(List *joinedTableList,
|
||||||
JoinOrderNode *currentJoinNode,
|
JoinOrderNode *currentJoinNode,
|
||||||
TableEntry *candidateTable,
|
TableEntry *candidateTable,
|
||||||
List *joinClauseList,
|
List *joinRestrictInfoListList,
|
||||||
JoinType joinType,
|
List *generatedEcJoinClauseList,
|
||||||
bool passJoinClauseDirectly);
|
JoinType joinType);
|
||||||
static List * RangeTableIdList(List *tableList);
|
static List * RangeTableIdList(List *tableList);
|
||||||
static TableEntry * TableEntryByRangeTableId(List *tableEntryList, uint32 rangeTableIdx);
|
static TableEntry * TableEntryByRangeTableId(List *tableEntryList, uint32 rangeTableIdx);
|
||||||
static RuleEvalFunction JoinRuleEvalFunction(JoinRuleType ruleType);
|
static RuleEvalFunction JoinRuleEvalFunction(JoinRuleType ruleType);
|
||||||
static char * JoinRuleName(JoinRuleType ruleType);
|
static char * JoinRuleName(JoinRuleType ruleType);
|
||||||
static JoinOrderNode * ReferenceJoin(JoinOrderNode *joinNode, TableEntry *candidateTable,
|
static JoinOrderNode * ReferenceJoin(JoinOrderNode *joinNode,
|
||||||
List *applicableJoinClauses, JoinType joinType);
|
TableEntry *candidateTable,
|
||||||
|
List *applicableJoinClauses,
|
||||||
|
JoinType joinType);
|
||||||
static JoinOrderNode * CartesianProductReferenceJoin(JoinOrderNode *joinNode,
|
static JoinOrderNode * CartesianProductReferenceJoin(JoinOrderNode *joinNode,
|
||||||
TableEntry *candidateTable,
|
TableEntry *candidateTable,
|
||||||
List *applicableJoinClauses,
|
List *applicableJoinClauses,
|
||||||
JoinType joinType);
|
JoinType joinType);
|
||||||
static JoinOrderNode * LocalJoin(JoinOrderNode *joinNode, TableEntry *candidateTable,
|
static JoinOrderNode * LocalJoin(JoinOrderNode *joinNode,
|
||||||
List *applicableJoinClauses, JoinType joinType);
|
TableEntry *candidateTable,
|
||||||
|
List *applicableJoinClauses,
|
||||||
|
JoinType joinType);
|
||||||
static bool JoinOnColumns(List *currentPartitionColumnList, Var *candidatePartitionColumn,
|
static bool JoinOnColumns(List *currentPartitionColumnList, Var *candidatePartitionColumn,
|
||||||
List *joinClauseList);
|
List *joinClauseList);
|
||||||
static JoinOrderNode * SinglePartitionJoin(JoinOrderNode *joinNode,
|
static JoinOrderNode * SinglePartitionJoin(JoinOrderNode *joinNode,
|
||||||
|
@ -290,7 +306,8 @@ NodeIsEqualsOpExpr(Node *node)
|
||||||
* least amount of data across the network, and returns this join order.
|
* least amount of data across the network, and returns this join order.
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
JoinOrderList(List *tableEntryList, List *joinClauseList)
|
JoinOrderList(List *tableEntryList, List *joinRestrictInfoListList,
|
||||||
|
List *generatedEcJoinClauseList, List *pseudoClauseList)
|
||||||
{
|
{
|
||||||
List *candidateJoinOrderList = NIL;
|
List *candidateJoinOrderList = NIL;
|
||||||
ListCell *tableEntryCell = NULL;
|
ListCell *tableEntryCell = NULL;
|
||||||
|
@ -301,7 +318,8 @@ JoinOrderList(List *tableEntryList, List *joinClauseList)
|
||||||
|
|
||||||
/* each candidate join order starts with a different table */
|
/* each candidate join order starts with a different table */
|
||||||
List *candidateJoinOrder = JoinOrderForTable(startingTable, tableEntryList,
|
List *candidateJoinOrder = JoinOrderForTable(startingTable, tableEntryList,
|
||||||
joinClauseList);
|
joinRestrictInfoListList,
|
||||||
|
generatedEcJoinClauseList);
|
||||||
|
|
||||||
if (candidateJoinOrder != NULL)
|
if (candidateJoinOrder != NULL)
|
||||||
{
|
{
|
||||||
|
@ -349,12 +367,90 @@ TableEntryByRangeTableId(List *tableEntryList, uint32 rangeTableIdx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ExtractPushdownJoinRestrictInfos extracts clauses, which are pushed down and treated like a normal filter,
|
||||||
|
* inside given join restriction infos.
|
||||||
|
*/
|
||||||
|
static List *
|
||||||
|
ExtractPushdownJoinRestrictInfos(List *restrictInfoListOfJoin,
|
||||||
|
RestrictInfo *joinRestrictInfo, JoinType joinType)
|
||||||
|
{
|
||||||
|
List *joinFilterRestrictInfoList = NIL;
|
||||||
|
|
||||||
|
/* left and right relids of the join restriction */
|
||||||
|
Bitmapset *joinRelids = bms_union(joinRestrictInfo->left_relids,
|
||||||
|
joinRestrictInfo->right_relids);
|
||||||
|
|
||||||
|
RestrictInfo *restrictInfo = NULL;
|
||||||
|
foreach_ptr(restrictInfo, restrictInfoListOfJoin)
|
||||||
|
{
|
||||||
|
if (!restrictInfo->can_join &&
|
||||||
|
(!IS_OUTER_JOIN(joinType) || RINFO_IS_PUSHED_DOWN(restrictInfo, joinRelids)))
|
||||||
|
{
|
||||||
|
joinFilterRestrictInfoList = lappend(joinFilterRestrictInfoList,
|
||||||
|
restrictInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return joinFilterRestrictInfoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FindJoinClauseForTables finds join clause for given left hand side tables and
|
||||||
|
* right hand side table.
|
||||||
|
*/
|
||||||
|
static List *
|
||||||
|
FindJoinClauseForTables(List *joinRestrictInfoListList, List *generatedEcJoinClauseList,
|
||||||
|
List *lhsTableIdList, uint32 rhsTableId, JoinType joinType)
|
||||||
|
{
|
||||||
|
List *joinRestrictInfoList = NIL;
|
||||||
|
foreach_ptr(joinRestrictInfoList, joinRestrictInfoListList)
|
||||||
|
{
|
||||||
|
RestrictInfo *joinRestrictInfo = NULL;
|
||||||
|
foreach_ptr(joinRestrictInfo, joinRestrictInfoList)
|
||||||
|
{
|
||||||
|
Node *restrictClause = (Node *) joinRestrictInfo->clause;
|
||||||
|
if (joinRestrictInfo->can_join && IsApplicableJoinClause(lhsTableIdList,
|
||||||
|
rhsTableId,
|
||||||
|
restrictClause))
|
||||||
|
{
|
||||||
|
List *pushdownFakeRestrictInfoList = ExtractPushdownJoinRestrictInfos(
|
||||||
|
joinRestrictInfoList, joinRestrictInfo, joinType);
|
||||||
|
List *nonPushdownRestrictInfoList = list_difference(joinRestrictInfoList,
|
||||||
|
pushdownFakeRestrictInfoList);
|
||||||
|
List *nonPushdownRestrictClauseList =
|
||||||
|
ExtractRestrictClausesFromRestrictionInfoList(
|
||||||
|
nonPushdownRestrictInfoList);
|
||||||
|
return nonPushdownRestrictClauseList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (joinType == JOIN_INNER)
|
||||||
|
{
|
||||||
|
Node *ecClause = NULL;
|
||||||
|
foreach_ptr(ecClause, generatedEcJoinClauseList)
|
||||||
|
{
|
||||||
|
if (IsApplicableJoinClause(lhsTableIdList, rhsTableId, ecClause))
|
||||||
|
{
|
||||||
|
return list_make1(ecClause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FixedJoinOrderList returns the best fixed join order according to
|
* FixedJoinOrderList returns the best fixed join order according to
|
||||||
* applicable join rules for the nodes in the list.
|
* applicable join rules for the nodes in the list.
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
FixedJoinOrderList(List *tableEntryList, JoinInfoContext *joinInfoContext)
|
FixedJoinOrderList(List *tableEntryList, JoinInfoContext *joinInfoContext,
|
||||||
|
List *joinRestrictInfoListList, List *generatedEcJoinClauseList,
|
||||||
|
List *pseudoClauseList)
|
||||||
{
|
{
|
||||||
/* we donot support anti joins as ruleutils files cannot deparse JOIN_ANTI */
|
/* we donot support anti joins as ruleutils files cannot deparse JOIN_ANTI */
|
||||||
if (JoinInfoContextHasAntiJoin(joinInfoContext))
|
if (JoinInfoContextHasAntiJoin(joinInfoContext))
|
||||||
|
@ -404,13 +500,12 @@ FixedJoinOrderList(List *tableEntryList, JoinInfoContext *joinInfoContext)
|
||||||
TableEntry *nextTable = TableEntryByRangeTableId(tableEntryList,
|
TableEntry *nextTable = TableEntryByRangeTableId(tableEntryList,
|
||||||
joinInfo->rightTableIdx);
|
joinInfo->rightTableIdx);
|
||||||
|
|
||||||
bool passJoinClauseDirectly = true;
|
|
||||||
nextJoinNode = EvaluateJoinRules(joinedTableList,
|
nextJoinNode = EvaluateJoinRules(joinedTableList,
|
||||||
currentJoinNode,
|
currentJoinNode,
|
||||||
nextTable,
|
nextTable,
|
||||||
joinInfo->joinQualifierList,
|
joinRestrictInfoListList,
|
||||||
joinInfo->joinType,
|
generatedEcJoinClauseList,
|
||||||
passJoinClauseDirectly);
|
joinInfo->joinType);
|
||||||
|
|
||||||
if (nextJoinNode == NULL)
|
if (nextJoinNode == NULL)
|
||||||
{
|
{
|
||||||
|
@ -490,7 +585,8 @@ ConvertSemiToInnerInJoinInfoContext(JoinInfoContext *joinOrderContext)
|
||||||
* returns this list.
|
* returns this list.
|
||||||
*/
|
*/
|
||||||
static List *
|
static List *
|
||||||
JoinOrderForTable(TableEntry *firstTable, List *tableEntryList, List *joinClauseList)
|
JoinOrderForTable(TableEntry *firstTable, List *tableEntryList,
|
||||||
|
List *joinRestrictInfoListList, List *generatedEcJoinClauseList)
|
||||||
{
|
{
|
||||||
JoinRuleType firstJoinRule = JOIN_RULE_INVALID_FIRST;
|
JoinRuleType firstJoinRule = JOIN_RULE_INVALID_FIRST;
|
||||||
int joinedTableCount = 1;
|
int joinedTableCount = 1;
|
||||||
|
@ -533,13 +629,12 @@ JoinOrderForTable(TableEntry *firstTable, List *tableEntryList, List *joinClause
|
||||||
JoinType joinType = JOIN_INNER;
|
JoinType joinType = JOIN_INNER;
|
||||||
|
|
||||||
/* evaluate all join rules for this pending table */
|
/* evaluate all join rules for this pending table */
|
||||||
bool passJoinClauseDirectly = false;
|
|
||||||
JoinOrderNode *pendingJoinNode = EvaluateJoinRules(joinedTableList,
|
JoinOrderNode *pendingJoinNode = EvaluateJoinRules(joinedTableList,
|
||||||
currentJoinNode,
|
currentJoinNode,
|
||||||
pendingTable,
|
pendingTable,
|
||||||
joinClauseList,
|
joinRestrictInfoListList,
|
||||||
joinType,
|
generatedEcJoinClauseList,
|
||||||
passJoinClauseDirectly);
|
joinType);
|
||||||
|
|
||||||
if (pendingJoinNode == NULL)
|
if (pendingJoinNode == NULL)
|
||||||
{
|
{
|
||||||
|
@ -872,40 +967,27 @@ TableEntryListDifference(List *lhsTableList, List *rhsTableList)
|
||||||
* next table, evaluates different join rules between the two tables, and finds
|
* next table, evaluates different join rules between the two tables, and finds
|
||||||
* the best join rule that applies. The function returns the applicable join
|
* the best join rule that applies. The function returns the applicable join
|
||||||
* order node which includes the join rule and the partition information.
|
* order node which includes the join rule and the partition information.
|
||||||
*
|
|
||||||
* When we have only inner joins, we can commute the joins as we wish and it also
|
|
||||||
* does not matter if we merge or move join and where clauses. For query trees with
|
|
||||||
* only inner joins, `joinClauseList` contains join and where clauses combined so that
|
|
||||||
* we can push down some where clauses which are applicable as join clause, which is
|
|
||||||
* determined by `ApplicableJoinClauses`.
|
|
||||||
* When we have at least 1 outer join in a query tree, we cannot commute joins(that is
|
|
||||||
* why we have `FixedJoinOrderList`) or move join and where clauses as we wish because
|
|
||||||
* we would have incorrect results. We should pass join and where clauses separately while
|
|
||||||
* creating tasks. `joinClauseList` contains only join clauses when `passJoinClauseDirectly`
|
|
||||||
* is set true.
|
|
||||||
*/
|
*/
|
||||||
static JoinOrderNode *
|
static JoinOrderNode *
|
||||||
EvaluateJoinRules(List *joinedTableList, JoinOrderNode *currentJoinNode,
|
EvaluateJoinRules(List *joinedTableList, JoinOrderNode *currentJoinNode,
|
||||||
TableEntry *candidateTable, List *joinClauseList,
|
TableEntry *candidateTable, List *joinRestrictInfoListList,
|
||||||
JoinType joinType, bool passJoinClauseDirectly)
|
List *generatedEcJoinClauseList, JoinType joinType)
|
||||||
{
|
{
|
||||||
JoinOrderNode *nextJoinNode = NULL;
|
JoinOrderNode *nextJoinNode = NULL;
|
||||||
uint32 lowestValidIndex = JOIN_RULE_INVALID_FIRST + 1;
|
uint32 lowestValidIndex = JOIN_RULE_INVALID_FIRST + 1;
|
||||||
uint32 highestValidIndex = JOIN_RULE_LAST - 1;
|
uint32 highestValidIndex = JOIN_RULE_LAST - 1;
|
||||||
|
|
||||||
List *joinClauses = joinClauseList;
|
/*
|
||||||
if (!passJoinClauseDirectly)
|
* We first find all applicable join clauses between already joined tables
|
||||||
{
|
* and the candidate table.
|
||||||
/*
|
*/
|
||||||
* We first find all applicable join clauses between already joined tables
|
List *joinedTableIdList = RangeTableIdList(joinedTableList);
|
||||||
* and the candidate table.
|
uint32 candidateTableId = candidateTable->rangeTableId;
|
||||||
*/
|
List *applicableJoinClauseList = FindJoinClauseForTables(joinRestrictInfoListList,
|
||||||
List *joinedTableIdList = RangeTableIdList(joinedTableList);
|
generatedEcJoinClauseList,
|
||||||
uint32 candidateTableId = candidateTable->rangeTableId;
|
joinedTableIdList,
|
||||||
joinClauses = ApplicableJoinClauses(joinedTableIdList,
|
candidateTableId,
|
||||||
candidateTableId,
|
joinType);
|
||||||
joinClauseList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we then evaluate all join rules in order */
|
/* we then evaluate all join rules in order */
|
||||||
for (uint32 ruleIndex = lowestValidIndex; ruleIndex <= highestValidIndex; ruleIndex++)
|
for (uint32 ruleIndex = lowestValidIndex; ruleIndex <= highestValidIndex; ruleIndex++)
|
||||||
|
@ -915,7 +997,7 @@ EvaluateJoinRules(List *joinedTableList, JoinOrderNode *currentJoinNode,
|
||||||
|
|
||||||
nextJoinNode = (*ruleEvalFunction)(currentJoinNode,
|
nextJoinNode = (*ruleEvalFunction)(currentJoinNode,
|
||||||
candidateTable,
|
candidateTable,
|
||||||
joinClauses,
|
applicableJoinClauseList,
|
||||||
joinType);
|
joinType);
|
||||||
|
|
||||||
/* break after finding the first join rule that applies */
|
/* break after finding the first join rule that applies */
|
||||||
|
@ -932,7 +1014,7 @@ EvaluateJoinRules(List *joinedTableList, JoinOrderNode *currentJoinNode,
|
||||||
|
|
||||||
Assert(nextJoinNode != NULL);
|
Assert(nextJoinNode != NULL);
|
||||||
nextJoinNode->joinType = joinType;
|
nextJoinNode->joinType = joinType;
|
||||||
nextJoinNode->joinClauseList = joinClauses;
|
nextJoinNode->joinClauseList = applicableJoinClauseList;
|
||||||
return nextJoinNode;
|
return nextJoinNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1500,27 +1582,30 @@ IsApplicableJoinClause(List *leftTableIdList, uint32 rightTableId, Node *joinCla
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ApplicableJoinClauses finds all join clauses that apply between the given
|
* IsApplicableFalseConstantJoinClause returns true if it can find a constant false filter
|
||||||
* left table list and the right table, and returns these found join clauses.
|
* which is applied to right table and also at least one of the table in left tables.
|
||||||
*/
|
*/
|
||||||
List *
|
bool
|
||||||
ApplicableJoinClauses(List *leftTableIdList, uint32 rightTableId, List *joinClauseList)
|
IsApplicableFalseConstantJoinClause(List *leftTableIdList, uint32 rightTableId,
|
||||||
|
RestrictInfo *restrictInfo)
|
||||||
{
|
{
|
||||||
List *applicableJoinClauses = NIL;
|
/* find whether restrictinfo relids contain right table relid */
|
||||||
|
Relids restrictionRelIds = restrictInfo->required_relids;
|
||||||
|
bool hasRightTable = bms_is_member(rightTableId, restrictionRelIds);
|
||||||
|
|
||||||
/* make sure joinClauseList contains only join clauses */
|
/* convert left table id list to bitmapset */
|
||||||
joinClauseList = JoinClauseList(joinClauseList);
|
Relids leftTableRelIds = NULL;
|
||||||
|
int leftTableId = -1;
|
||||||
Node *joinClause = NULL;
|
foreach_int(leftTableId, leftTableIdList)
|
||||||
foreach_ptr(joinClause, joinClauseList)
|
|
||||||
{
|
{
|
||||||
if (IsApplicableJoinClause(leftTableIdList, rightTableId, joinClause))
|
leftTableRelIds = bms_add_member(leftTableRelIds, leftTableId);
|
||||||
{
|
|
||||||
applicableJoinClauses = lappend(applicableJoinClauses, joinClause);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return applicableJoinClauses;
|
/* find whether restrictinfo relids contain any of the left table relids */
|
||||||
|
Relids intersectLeftRelids = bms_intersect(restrictionRelIds, leftTableRelIds);
|
||||||
|
bool hasOneOfLeftTables = bms_num_members(intersectLeftRelids) > 0;
|
||||||
|
|
||||||
|
return hasRightTable && hasOneOfLeftTables;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -169,9 +169,8 @@ typedef struct OrderByLimitReference
|
||||||
|
|
||||||
|
|
||||||
/* Local functions forward declarations */
|
/* Local functions forward declarations */
|
||||||
static MultiSelect * AndSelectNode(MultiSelect *selectNode);
|
static MultiSelect * PushdownableSelectNode(MultiSelect *selectNode);
|
||||||
static MultiSelect * OrSelectNode(MultiSelect *selectNode);
|
static MultiSelect * NonPushdownableSelectNode(MultiSelect *selectNode);
|
||||||
static List * OrSelectClauseList(List *selectClauseList);
|
|
||||||
static void PushDownNodeLoop(MultiUnaryNode *currentNode);
|
static void PushDownNodeLoop(MultiUnaryNode *currentNode);
|
||||||
static void PullUpCollectLoop(MultiCollect *collectNode);
|
static void PullUpCollectLoop(MultiCollect *collectNode);
|
||||||
static void AddressProjectSpecialConditions(MultiProject *projectNode);
|
static void AddressProjectSpecialConditions(MultiProject *projectNode);
|
||||||
|
@ -381,27 +380,29 @@ MultiLogicalPlanOptimize(MultiTreeRoot *multiLogicalPlan)
|
||||||
if (selectNodeList != NIL)
|
if (selectNodeList != NIL)
|
||||||
{
|
{
|
||||||
MultiSelect *selectNode = (MultiSelect *) linitial(selectNodeList);
|
MultiSelect *selectNode = (MultiSelect *) linitial(selectNodeList);
|
||||||
MultiSelect *andSelectNode = AndSelectNode(selectNode);
|
MultiSelect *pushdownableSelectNode = PushdownableSelectNode(selectNode);
|
||||||
MultiSelect *orSelectNode = OrSelectNode(selectNode);
|
MultiSelect *nonPushdownableSelectNode = NonPushdownableSelectNode(selectNode);
|
||||||
|
|
||||||
if (andSelectNode != NULL && orSelectNode != NULL)
|
if (pushdownableSelectNode != NULL && nonPushdownableSelectNode != NULL)
|
||||||
{
|
{
|
||||||
MultiNode *parentNode = ParentNode((MultiNode *) selectNode);
|
MultiNode *parentNode = ParentNode((MultiNode *) selectNode);
|
||||||
MultiNode *childNode = ChildNode((MultiUnaryNode *) selectNode);
|
MultiNode *childNode = ChildNode((MultiUnaryNode *) selectNode);
|
||||||
Assert(UnaryOperator(parentNode));
|
Assert(UnaryOperator(parentNode));
|
||||||
|
|
||||||
SetChild((MultiUnaryNode *) parentNode, (MultiNode *) orSelectNode);
|
SetChild((MultiUnaryNode *) parentNode,
|
||||||
SetChild((MultiUnaryNode *) orSelectNode, (MultiNode *) andSelectNode);
|
(MultiNode *) nonPushdownableSelectNode);
|
||||||
SetChild((MultiUnaryNode *) andSelectNode, (MultiNode *) childNode);
|
SetChild((MultiUnaryNode *) nonPushdownableSelectNode,
|
||||||
|
(MultiNode *) pushdownableSelectNode);
|
||||||
|
SetChild((MultiUnaryNode *) pushdownableSelectNode, (MultiNode *) childNode);
|
||||||
}
|
}
|
||||||
else if (andSelectNode != NULL && orSelectNode == NULL)
|
else if (pushdownableSelectNode != NULL && nonPushdownableSelectNode == NULL)
|
||||||
{
|
{
|
||||||
andSelectNode = selectNode; /* no need to modify the tree */
|
pushdownableSelectNode = selectNode; /* no need to modify the tree */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (andSelectNode != NULL)
|
if (pushdownableSelectNode != NULL)
|
||||||
{
|
{
|
||||||
PushDownNodeLoop((MultiUnaryNode *) andSelectNode);
|
PushDownNodeLoop((MultiUnaryNode *) pushdownableSelectNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -486,71 +487,44 @@ MultiLogicalPlanOptimize(MultiTreeRoot *multiLogicalPlan)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AndSelectNode looks for AND clauses in the given select node. If they exist,
|
* PushdownableSelectNode looks for pushdownable clauses in the given select node.
|
||||||
* the function returns these clauses in a new node. Otherwise, the function
|
* If they exist, the function returns these clauses in a new node. Otherwise, the function
|
||||||
* returns null.
|
* returns null.
|
||||||
*/
|
*/
|
||||||
static MultiSelect *
|
static MultiSelect *
|
||||||
AndSelectNode(MultiSelect *selectNode)
|
PushdownableSelectNode(MultiSelect *selectNode)
|
||||||
{
|
{
|
||||||
MultiSelect *andSelectNode = NULL;
|
MultiSelect *pushdownableSelectNode = NULL;
|
||||||
List *selectClauseList = selectNode->selectClauseList;
|
|
||||||
List *orSelectClauseList = OrSelectClauseList(selectClauseList);
|
|
||||||
|
|
||||||
/* AND clauses are select clauses that are not OR clauses */
|
if (selectNode->pushdownableSelectClauseList != NIL)
|
||||||
List *andSelectClauseList = list_difference(selectClauseList, orSelectClauseList);
|
|
||||||
if (andSelectClauseList != NIL)
|
|
||||||
{
|
{
|
||||||
andSelectNode = CitusMakeNode(MultiSelect);
|
pushdownableSelectNode = CitusMakeNode(MultiSelect);
|
||||||
andSelectNode->selectClauseList = andSelectClauseList;
|
pushdownableSelectNode->selectClauseList =
|
||||||
|
selectNode->pushdownableSelectClauseList;
|
||||||
}
|
}
|
||||||
|
|
||||||
return andSelectNode;
|
return pushdownableSelectNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* OrSelectNode looks for OR clauses in the given select node. If they exist,
|
* PushdownableSelectNode looks for nonpushdownable clauses in the given select node.
|
||||||
* the function returns these clauses in a new node. Otherwise, the function
|
* If they exist, the function returns these clauses in a new node. Otherwise, the function
|
||||||
* returns null.
|
* returns null.
|
||||||
*/
|
*/
|
||||||
static MultiSelect *
|
static MultiSelect *
|
||||||
OrSelectNode(MultiSelect *selectNode)
|
NonPushdownableSelectNode(MultiSelect *selectNode)
|
||||||
{
|
{
|
||||||
MultiSelect *orSelectNode = NULL;
|
MultiSelect *nonPushdownableSelectNode = NULL;
|
||||||
List *selectClauseList = selectNode->selectClauseList;
|
|
||||||
List *orSelectClauseList = OrSelectClauseList(selectClauseList);
|
|
||||||
|
|
||||||
if (orSelectClauseList != NIL)
|
if (selectNode->nonPushdownableSelectClauseList != NIL)
|
||||||
{
|
{
|
||||||
orSelectNode = CitusMakeNode(MultiSelect);
|
nonPushdownableSelectNode = CitusMakeNode(MultiSelect);
|
||||||
orSelectNode->selectClauseList = orSelectClauseList;
|
nonPushdownableSelectNode->selectClauseList =
|
||||||
|
selectNode->nonPushdownableSelectClauseList;
|
||||||
}
|
}
|
||||||
|
|
||||||
return orSelectNode;
|
return nonPushdownableSelectNode;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* OrSelectClauseList walks over the select clause list, and returns all clauses
|
|
||||||
* that have OR expressions in them.
|
|
||||||
*/
|
|
||||||
static List *
|
|
||||||
OrSelectClauseList(List *selectClauseList)
|
|
||||||
{
|
|
||||||
List *orSelectClauseList = NIL;
|
|
||||||
|
|
||||||
Node *selectClause = NULL;
|
|
||||||
foreach_ptr(selectClause, selectClauseList)
|
|
||||||
{
|
|
||||||
bool orClause = is_orclause(selectClause);
|
|
||||||
if (orClause)
|
|
||||||
{
|
|
||||||
orSelectClauseList = lappend(orSelectClauseList, selectClause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return orSelectClauseList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
#include "distributed/relation_restriction_equivalence.h"
|
#include "distributed/relation_restriction_equivalence.h"
|
||||||
#include "distributed/query_pushdown_planning.h"
|
#include "distributed/query_pushdown_planning.h"
|
||||||
#include "distributed/query_utils.h"
|
#include "distributed/query_utils.h"
|
||||||
#include "distributed/multi_router_planner.h"
|
|
||||||
#include "distributed/worker_protocol.h"
|
#include "distributed/worker_protocol.h"
|
||||||
#include "distributed/version_compat.h"
|
#include "distributed/version_compat.h"
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
|
@ -41,6 +40,7 @@
|
||||||
#include "nodes/pathnodes.h"
|
#include "nodes/pathnodes.h"
|
||||||
#include "optimizer/optimizer.h"
|
#include "optimizer/optimizer.h"
|
||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
|
#include "optimizer/paths.h"
|
||||||
#include "optimizer/prep.h"
|
#include "optimizer/prep.h"
|
||||||
#include "optimizer/tlist.h"
|
#include "optimizer/tlist.h"
|
||||||
#include "parser/parsetree.h"
|
#include "parser/parsetree.h"
|
||||||
|
@ -85,20 +85,20 @@ static bool ExtractFromExpressionWalker(Node *node,
|
||||||
QualifierWalkerContext *walkerContext);
|
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);
|
||||||
bool passJoinClauseDirectly);
|
|
||||||
static MultiCollect * CollectNodeForTable(List *collectTableList, uint32 rangeTableId);
|
static MultiCollect * CollectNodeForTable(List *collectTableList, uint32 rangeTableId);
|
||||||
static MultiSelect * MultiSelectNode(List *whereClauseList, bool passWhereClauseDirectly);
|
static MultiSelect * MultiSelectNode(List *pushdownableClauseList,
|
||||||
|
List *nonPushdownableClauseList);
|
||||||
static bool IsSelectClause(Node *clause);
|
static bool IsSelectClause(Node *clause);
|
||||||
|
|
||||||
static JoinInfoContext * FetchJoinOrderContext(FromExpr *fromExpr);
|
static JoinInfoContext * FetchJoinOrderContext(FromExpr *fromExpr);
|
||||||
static bool JoinInfoWalker(Node *node, JoinInfoContext *joinInfoContext);
|
static bool JoinInfoWalker(Node *node, JoinInfoContext *joinInfoContext);
|
||||||
|
static List * ExtractNonPushdownableJoinClauses(List *joinOrderList);
|
||||||
|
|
||||||
/* Local functions forward declarations for applying joins */
|
/* Local functions forward declarations for applying joins */
|
||||||
static MultiNode * ApplyJoinRule(MultiNode *leftNode, MultiNode *rightNode,
|
static MultiNode * ApplyJoinRule(MultiNode *leftNode, MultiNode *rightNode,
|
||||||
JoinRuleType ruleType, List *partitionColumnList,
|
JoinRuleType ruleType, List *partitionColumnList,
|
||||||
JoinType joinType, List *joinClauseList,
|
JoinType joinType, List *joinClauseList);
|
||||||
bool passJoinClauseDirectly);
|
|
||||||
static RuleApplyFunction JoinRuleApplyFunction(JoinRuleType ruleType);
|
static RuleApplyFunction JoinRuleApplyFunction(JoinRuleType ruleType);
|
||||||
static MultiNode * ApplyReferenceJoin(MultiNode *leftNode, MultiNode *rightNode,
|
static MultiNode * ApplyReferenceJoin(MultiNode *leftNode, MultiNode *rightNode,
|
||||||
List *partitionColumnList, JoinType joinType,
|
List *partitionColumnList, JoinType joinType,
|
||||||
|
@ -157,7 +157,7 @@ MultiLogicalPlanCreate(Query *originalQuery, Query *queryTree,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
multiQueryNode = MultiNodeTree(queryTree);
|
multiQueryNode = MultiNodeTree(queryTree, plannerRestrictionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add a root node to serve as the permanent handle to the tree */
|
/* add a root node to serve as the permanent handle to the tree */
|
||||||
|
@ -566,18 +566,16 @@ SubqueryEntryList(Query *queryTree)
|
||||||
* group, and limit nodes if they appear in the original query tree.
|
* group, and limit nodes if they appear in the original query tree.
|
||||||
*/
|
*/
|
||||||
MultiNode *
|
MultiNode *
|
||||||
MultiNodeTree(Query *queryTree)
|
MultiNodeTree(Query *queryTree, PlannerRestrictionContext *plannerRestrictionContext)
|
||||||
{
|
{
|
||||||
List *rangeTableList = queryTree->rtable;
|
List *rangeTableList = queryTree->rtable;
|
||||||
List *targetEntryList = queryTree->targetList;
|
List *targetEntryList = queryTree->targetList;
|
||||||
List *joinClauseList = NIL;
|
|
||||||
List *joinOrderList = NIL;
|
List *joinOrderList = NIL;
|
||||||
List *tableEntryList = NIL;
|
List *tableEntryList = NIL;
|
||||||
List *tableNodeList = NIL;
|
List *tableNodeList = NIL;
|
||||||
List *collectTableList = NIL;
|
List *collectTableList = NIL;
|
||||||
MultiNode *joinTreeNode = NULL;
|
MultiNode *joinTreeNode = NULL;
|
||||||
MultiNode *currentTopNode = NULL;
|
MultiNode *currentTopNode = NULL;
|
||||||
bool passQualClauseDirectly = false;
|
|
||||||
|
|
||||||
/* verify we can perform distributed planning on this query */
|
/* verify we can perform distributed planning on this query */
|
||||||
DeferredErrorMessage *unsupportedQueryError = DeferErrorIfQueryNotSupported(
|
DeferredErrorMessage *unsupportedQueryError = DeferErrorIfQueryNotSupported(
|
||||||
|
@ -587,17 +585,35 @@ MultiNodeTree(Query *queryTree)
|
||||||
RaiseDeferredError(unsupportedQueryError, ERROR);
|
RaiseDeferredError(unsupportedQueryError, ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* extract where and join clause qualifiers(including outer join quals) and verify we can plan for them. */
|
/* extract join and nonjoin clauses from plannerRestrictionContext */
|
||||||
List *qualClauseList = QualifierList(queryTree->jointree);
|
RestrictInfoContext *restrictInfoContext = ExtractRestrictionInfosFromPlannerContext(
|
||||||
|
plannerRestrictionContext);
|
||||||
|
List *joinRestrictInfoList = restrictInfoContext->joinRestrictInfoList;
|
||||||
|
List *nonJoinRestrictionInfoList = restrictInfoContext->baseRestrictInfoList;
|
||||||
|
List *joinRestrictInfoListList = restrictInfoContext->joinRestrictInfoListList;
|
||||||
|
List *pseudoRestrictInfoList = restrictInfoContext->pseudoRestrictInfoList;
|
||||||
|
List *generatedEcJoinRestrictInfoList =
|
||||||
|
plannerRestrictionContext->joinRestrictionContext->generatedEcJoinRestrictInfoList;
|
||||||
|
|
||||||
|
/* verify we can plan for restriction clauses */
|
||||||
|
List *baseClauseList = ExtractRestrictClausesFromRestrictionInfoList(
|
||||||
|
nonJoinRestrictionInfoList);
|
||||||
|
List *allJoinClauseList = ExtractRestrictClausesFromRestrictionInfoList(
|
||||||
|
joinRestrictInfoList);
|
||||||
|
List *pseudoClauseList = ExtractRestrictClausesFromRestrictionInfoList(
|
||||||
|
pseudoRestrictInfoList);
|
||||||
|
List *generatedEcJoinClauseList = ExtractRestrictClausesFromRestrictionInfoList(
|
||||||
|
generatedEcJoinRestrictInfoList);
|
||||||
|
allJoinClauseList = list_concat_unique(allJoinClauseList, generatedEcJoinClauseList);
|
||||||
|
|
||||||
|
List *qualClauseList = list_concat_copy(baseClauseList, allJoinClauseList);
|
||||||
|
|
||||||
unsupportedQueryError = DeferErrorIfUnsupportedClause(qualClauseList);
|
unsupportedQueryError = DeferErrorIfUnsupportedClause(qualClauseList);
|
||||||
if (unsupportedQueryError)
|
if (unsupportedQueryError)
|
||||||
{
|
{
|
||||||
RaiseDeferredErrorInternal(unsupportedQueryError, ERROR);
|
RaiseDeferredErrorInternal(unsupportedQueryError, ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* WhereClauseList() merges join qualifiers and base qualifiers into result list */
|
|
||||||
List *whereClauseList = WhereClauseList(queryTree->jointree);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we have a subquery, build a multi table node for the subquery and
|
* If we have a subquery, build a multi table node for the subquery and
|
||||||
* add a collect node on top of the multi table node.
|
* add a collect node on top of the multi table node.
|
||||||
|
@ -634,7 +650,7 @@ MultiNodeTree(Query *queryTree)
|
||||||
*/
|
*/
|
||||||
Assert(list_length(subqueryEntryList) == 1);
|
Assert(list_length(subqueryEntryList) == 1);
|
||||||
|
|
||||||
List *whereClauseColumnList = pull_var_clause_default((Node *) whereClauseList);
|
List *whereClauseColumnList = pull_var_clause_default((Node *) baseClauseList);
|
||||||
List *targetListColumnList = pull_var_clause_default((Node *) targetEntryList);
|
List *targetListColumnList = pull_var_clause_default((Node *) targetEntryList);
|
||||||
|
|
||||||
List *columnList = list_concat(whereClauseColumnList, targetListColumnList);
|
List *columnList = list_concat(whereClauseColumnList, targetListColumnList);
|
||||||
|
@ -645,7 +661,8 @@ MultiNodeTree(Query *queryTree)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* recursively create child nested multitree */
|
/* recursively create child nested multitree */
|
||||||
MultiNode *subqueryExtendedNode = MultiNodeTree(subqueryTree);
|
MultiNode *subqueryExtendedNode = MultiNodeTree(subqueryTree,
|
||||||
|
plannerRestrictionContext);
|
||||||
|
|
||||||
SetChild((MultiUnaryNode *) subqueryCollectNode, (MultiNode *) subqueryNode);
|
SetChild((MultiUnaryNode *) subqueryCollectNode, (MultiNode *) subqueryNode);
|
||||||
SetChild((MultiUnaryNode *) subqueryNode, subqueryExtendedNode);
|
SetChild((MultiUnaryNode *) subqueryNode, subqueryExtendedNode);
|
||||||
|
@ -686,35 +703,41 @@ MultiNodeTree(Query *queryTree)
|
||||||
/* extract join infos for left recursive join tree */
|
/* extract join infos for left recursive join tree */
|
||||||
JoinInfoContext *joinInfoContext = FetchJoinOrderContext(queryTree->jointree);
|
JoinInfoContext *joinInfoContext = FetchJoinOrderContext(queryTree->jointree);
|
||||||
|
|
||||||
/* where clause should only contain base qualifiers */
|
|
||||||
whereClauseList = joinInfoContext->baseQualifierList;
|
|
||||||
|
|
||||||
/* we simply donot commute joins as we have at least 1 outer join */
|
/* we simply donot commute joins as we have at least 1 outer join */
|
||||||
joinOrderList = FixedJoinOrderList(tableEntryList, joinInfoContext);
|
joinOrderList = FixedJoinOrderList(tableEntryList, joinInfoContext,
|
||||||
|
joinRestrictInfoListList,
|
||||||
/* pass join clauses directly as they are while creating tasks */
|
generatedEcJoinClauseList,
|
||||||
passQualClauseDirectly = true;
|
pseudoClauseList);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* consider also base qualifications */
|
|
||||||
joinClauseList = JoinClauseList(whereClauseList);
|
|
||||||
|
|
||||||
/* find best join order for commutative inner joins */
|
/* find best join order for commutative inner joins */
|
||||||
joinOrderList = JoinOrderList(tableEntryList, joinClauseList);
|
joinOrderList = JoinOrderList(tableEntryList, joinRestrictInfoListList,
|
||||||
|
generatedEcJoinClauseList, pseudoClauseList);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* build join tree using the join order and collected tables */
|
/* build join tree using the join order and collected tables */
|
||||||
joinTreeNode = MultiJoinTree(joinOrderList, collectTableList,
|
joinTreeNode = MultiJoinTree(joinOrderList, collectTableList);
|
||||||
passQualClauseDirectly);
|
|
||||||
|
|
||||||
currentTopNode = joinTreeNode;
|
currentTopNode = joinTreeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert(currentTopNode != NULL);
|
Assert(currentTopNode != NULL);
|
||||||
|
|
||||||
/* build select node if the query has selection criteria */
|
|
||||||
MultiSelect *selectNode = MultiSelectNode(whereClauseList, passQualClauseDirectly);
|
/*
|
||||||
|
* build select node if the query has selection criteria
|
||||||
|
* select node will have pushdownable and non-pushdownable parts.
|
||||||
|
* - all base clauses can be pushdownable
|
||||||
|
* - some of join clauses cannot be pushed down e.g. they can be only applied after join
|
||||||
|
*/
|
||||||
|
List *pushdownableSelectClauseList = baseClauseList;
|
||||||
|
List *nonpushdownableJoinClauseList = ExtractNonPushdownableJoinClauses(
|
||||||
|
joinOrderList);
|
||||||
|
List *nonPushdownableSelectClauseList = list_difference(allJoinClauseList,
|
||||||
|
nonpushdownableJoinClauseList);
|
||||||
|
MultiSelect *selectNode = MultiSelectNode(pushdownableSelectClauseList,
|
||||||
|
nonPushdownableSelectClauseList);
|
||||||
if (selectNode != NULL)
|
if (selectNode != NULL)
|
||||||
{
|
{
|
||||||
SetChild((MultiUnaryNode *) selectNode, currentTopNode);
|
SetChild((MultiUnaryNode *) selectNode, currentTopNode);
|
||||||
|
@ -740,6 +763,128 @@ MultiNodeTree(Query *queryTree)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ExtractNonPushdownableJoinClauses returns pushdownable clauses from given join
|
||||||
|
* restrict infos.
|
||||||
|
*/
|
||||||
|
static List *
|
||||||
|
ExtractNonPushdownableJoinClauses(List *joinOrderList)
|
||||||
|
{
|
||||||
|
List *nonPushdownJoinClauseList = NIL;
|
||||||
|
|
||||||
|
JoinOrderNode *joinOrderNode = NULL;
|
||||||
|
foreach_ptr(joinOrderNode, joinOrderList)
|
||||||
|
{
|
||||||
|
List *joinClauselist = joinOrderNode->joinClauseList;
|
||||||
|
nonPushdownJoinClauseList = list_concat(nonPushdownJoinClauseList,
|
||||||
|
joinClauselist);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nonPushdownJoinClauseList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RestrictInfoContext extracts all RestrictionInfo from PlannerRestrictionContext.
|
||||||
|
*/
|
||||||
|
RestrictInfoContext *
|
||||||
|
ExtractRestrictionInfosFromPlannerContext(
|
||||||
|
PlannerRestrictionContext *plannerRestrictionContext)
|
||||||
|
{
|
||||||
|
RelationRestrictionContext *relationRestrictionContext =
|
||||||
|
plannerRestrictionContext->relationRestrictionContext;
|
||||||
|
JoinRestrictionContext *joinRestrictionContext =
|
||||||
|
plannerRestrictionContext->joinRestrictionContext;
|
||||||
|
List *baseRestrictInfoList = NIL;
|
||||||
|
List *joinRestrictInfoList = NIL;
|
||||||
|
List *joinRestrictInfoListList = NIL;
|
||||||
|
List *pseudoRestrictInfoList = NIL;
|
||||||
|
|
||||||
|
/* collect all restrictInfos from relationRestrictionContext */
|
||||||
|
RelationRestriction *relationRestriction = NULL;
|
||||||
|
foreach_ptr(relationRestriction, relationRestrictionContext->relationRestrictionList)
|
||||||
|
{
|
||||||
|
RelOptInfo *relOptInfo = relationRestriction->relOptInfo;
|
||||||
|
RestrictInfo *baseRestrictInfo = NULL;
|
||||||
|
foreach_ptr(baseRestrictInfo, relOptInfo->baserestrictinfo)
|
||||||
|
{
|
||||||
|
if (baseRestrictInfo->pseudoconstant)
|
||||||
|
{
|
||||||
|
pseudoRestrictInfoList = list_append_unique(pseudoRestrictInfoList,
|
||||||
|
baseRestrictInfo);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
baseRestrictInfoList = list_append_unique(baseRestrictInfoList,
|
||||||
|
baseRestrictInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
RestrictInfo *joinRestrictInfo = NULL;
|
||||||
|
foreach_ptr(joinRestrictInfo, relOptInfo->joininfo)
|
||||||
|
{
|
||||||
|
if (joinRestrictInfo->pseudoconstant)
|
||||||
|
{
|
||||||
|
pseudoRestrictInfoList = list_append_unique(pseudoRestrictInfoList,
|
||||||
|
joinRestrictInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* collect all restrictInfos from joinRestrictionContext */
|
||||||
|
JoinRestriction *joinRestriction = NULL;
|
||||||
|
foreach_ptr(joinRestriction, joinRestrictionContext->joinRestrictionList)
|
||||||
|
{
|
||||||
|
List *currentJoinRestrictInfoList = NIL;
|
||||||
|
RestrictInfo *joinRestrictInfo = NULL;
|
||||||
|
foreach_ptr(joinRestrictInfo, joinRestriction->joinRestrictInfoList)
|
||||||
|
{
|
||||||
|
if (joinRestrictInfo->pseudoconstant)
|
||||||
|
{
|
||||||
|
pseudoRestrictInfoList = list_append_unique(pseudoRestrictInfoList,
|
||||||
|
joinRestrictInfo);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentJoinRestrictInfoList = list_append_unique(currentJoinRestrictInfoList,
|
||||||
|
joinRestrictInfo);
|
||||||
|
|
||||||
|
joinRestrictInfoList = list_append_unique(joinRestrictInfoList,
|
||||||
|
joinRestrictInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
joinRestrictInfoListList = lappend(joinRestrictInfoListList,
|
||||||
|
currentJoinRestrictInfoList);
|
||||||
|
}
|
||||||
|
|
||||||
|
RestrictInfoContext *restrictInfoContext = palloc0(sizeof(RestrictInfoContext));
|
||||||
|
restrictInfoContext->baseRestrictInfoList = baseRestrictInfoList;
|
||||||
|
restrictInfoContext->joinRestrictInfoList = joinRestrictInfoList;
|
||||||
|
restrictInfoContext->joinRestrictInfoListList = joinRestrictInfoListList;
|
||||||
|
restrictInfoContext->pseudoRestrictInfoList = pseudoRestrictInfoList;
|
||||||
|
|
||||||
|
return restrictInfoContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ExtractRestrictClausesFromRestrictionInfoList extracts RestrictInfo clauses from
|
||||||
|
* given restrictInfoList.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
ExtractRestrictClausesFromRestrictionInfoList(List *restrictInfoList)
|
||||||
|
{
|
||||||
|
List *restrictClauseList = NIL;
|
||||||
|
|
||||||
|
RestrictInfo *restrictInfo = NULL;
|
||||||
|
foreach_ptr(restrictInfo, restrictInfoList)
|
||||||
|
{
|
||||||
|
restrictClauseList = lappend(restrictClauseList, restrictInfo->clause);
|
||||||
|
}
|
||||||
|
|
||||||
|
return restrictClauseList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FetchJoinOrderContext returns all join info for given node.
|
* FetchJoinOrderContext returns all join info for given node.
|
||||||
*/
|
*/
|
||||||
|
@ -1357,28 +1502,6 @@ WhereClauseList(FromExpr *fromExpr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DeferErrorIfUnsupportedClause walks over the given list of clauses, and
|
* DeferErrorIfUnsupportedClause walks over the given list of clauses, and
|
||||||
* checks that we can recognize all the clauses. This function ensures that
|
* checks that we can recognize all the clauses. This function ensures that
|
||||||
|
@ -1405,31 +1528,6 @@ DeferErrorIfUnsupportedClause(List *clauseList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JoinClauseList finds the join clauses from the given where clause expression
|
|
||||||
* list, and returns them. The function does not iterate into nested OR clauses
|
|
||||||
* and relies on find_duplicate_ors() in the optimizer to pull up factorizable
|
|
||||||
* OR clauses.
|
|
||||||
*/
|
|
||||||
List *
|
|
||||||
JoinClauseList(List *whereClauseList)
|
|
||||||
{
|
|
||||||
List *joinClauseList = NIL;
|
|
||||||
ListCell *whereClauseCell = NULL;
|
|
||||||
|
|
||||||
foreach(whereClauseCell, whereClauseList)
|
|
||||||
{
|
|
||||||
Node *whereClause = (Node *) lfirst(whereClauseCell);
|
|
||||||
if (IsJoinClause(whereClause))
|
|
||||||
{
|
|
||||||
joinClauseList = lappend(joinClauseList, whereClause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return joinClauseList;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ExtractFromExpressionWalker walks over a FROM expression, and finds all
|
* ExtractFromExpressionWalker walks over a FROM expression, and finds all
|
||||||
* implicit and explicit qualifiers in the expression. The function looks at
|
* implicit and explicit qualifiers in the expression. The function looks at
|
||||||
|
@ -1712,7 +1810,7 @@ AddMultiCollectNodes(List *tableNodeList)
|
||||||
* this tree after every table in the list has been joined.
|
* this tree after every table in the list has been joined.
|
||||||
*/
|
*/
|
||||||
static MultiNode *
|
static MultiNode *
|
||||||
MultiJoinTree(List *joinOrderList, List *collectTableList, bool passJoinClauseDirectly)
|
MultiJoinTree(List *joinOrderList, List *collectTableList)
|
||||||
{
|
{
|
||||||
MultiNode *currentTopNode = NULL;
|
MultiNode *currentTopNode = NULL;
|
||||||
ListCell *joinOrderCell = NULL;
|
ListCell *joinOrderCell = NULL;
|
||||||
|
@ -1744,8 +1842,7 @@ MultiJoinTree(List *joinOrderList, List *collectTableList, bool passJoinClauseDi
|
||||||
(MultiNode *) collectNode,
|
(MultiNode *) collectNode,
|
||||||
joinRuleType, partitionColumnList,
|
joinRuleType, partitionColumnList,
|
||||||
joinType,
|
joinType,
|
||||||
joinClauseList,
|
joinClauseList);
|
||||||
passJoinClauseDirectly);
|
|
||||||
|
|
||||||
/* the new join node becomes the top of our join tree */
|
/* the new join node becomes the top of our join tree */
|
||||||
currentTopNode = newJoinNode;
|
currentTopNode = newJoinNode;
|
||||||
|
@ -1792,33 +1889,20 @@ CollectNodeForTable(List *collectTableList, uint32 rangeTableId)
|
||||||
* MultiSelectNode extracts the select clauses from the given where clause list,
|
* MultiSelectNode extracts the select clauses from the given where clause list,
|
||||||
* and builds a MultiSelect node from these clauses. If the expression tree does
|
* and builds a MultiSelect node from these clauses. If the expression tree does
|
||||||
* not have any select clauses, the function return null.
|
* not have any select clauses, the function return null.
|
||||||
*
|
|
||||||
* When we have at least 1 outer join in a query tree, we cannot commute joins(that is
|
|
||||||
* why we have `FixedJoinOrderList`) or move join and where clauses as we wish because
|
|
||||||
* we would have incorrect results. We should pass join and where clauses separately as
|
|
||||||
* they are while creating tasks. `whereClauseList` should be passed as it is when
|
|
||||||
* `passWhereClauseDirectly` is set true.
|
|
||||||
*/
|
*/
|
||||||
static MultiSelect *
|
static MultiSelect *
|
||||||
MultiSelectNode(List *whereClauseList, bool passWhereClauseDirectly)
|
MultiSelectNode(List *pushdownableClauseList, List *nonPushdownableClauseList)
|
||||||
{
|
{
|
||||||
List *selectClauseList = NIL;
|
|
||||||
MultiSelect *selectNode = NULL;
|
MultiSelect *selectNode = NULL;
|
||||||
|
|
||||||
ListCell *whereClauseCell = NULL;
|
if (list_length(pushdownableClauseList) > 0 ||
|
||||||
foreach(whereClauseCell, whereClauseList)
|
list_length(nonPushdownableClauseList) > 0)
|
||||||
{
|
|
||||||
Node *whereClause = (Node *) lfirst(whereClauseCell);
|
|
||||||
if (passWhereClauseDirectly || IsSelectClause(whereClause))
|
|
||||||
{
|
|
||||||
selectClauseList = lappend(selectClauseList, whereClause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (list_length(selectClauseList) > 0)
|
|
||||||
{
|
{
|
||||||
selectNode = CitusMakeNode(MultiSelect);
|
selectNode = CitusMakeNode(MultiSelect);
|
||||||
selectNode->selectClauseList = selectClauseList;
|
selectNode->selectClauseList = list_concat_copy(pushdownableClauseList,
|
||||||
|
nonPushdownableClauseList);
|
||||||
|
selectNode->pushdownableSelectClauseList = pushdownableClauseList;
|
||||||
|
selectNode->nonPushdownableSelectClauseList = nonPushdownableClauseList;
|
||||||
}
|
}
|
||||||
|
|
||||||
return selectNode;
|
return selectNode;
|
||||||
|
@ -2100,37 +2184,12 @@ pull_var_clause_default(Node *node)
|
||||||
*/
|
*/
|
||||||
static MultiNode *
|
static MultiNode *
|
||||||
ApplyJoinRule(MultiNode *leftNode, MultiNode *rightNode, JoinRuleType ruleType,
|
ApplyJoinRule(MultiNode *leftNode, MultiNode *rightNode, JoinRuleType ruleType,
|
||||||
List *partitionColumnList, JoinType joinType, List *joinClauseList,
|
List *partitionColumnList, JoinType joinType, List *joinClauseList)
|
||||||
bool passJoinClauseDirectly)
|
|
||||||
{
|
{
|
||||||
List *leftTableIdList = OutputTableIdList(leftNode);
|
|
||||||
List *rightTableIdList = OutputTableIdList(rightNode);
|
|
||||||
int rightTableIdCount PG_USED_FOR_ASSERTS_ONLY = 0;
|
|
||||||
|
|
||||||
rightTableIdCount = list_length(rightTableIdList);
|
|
||||||
Assert(rightTableIdCount == 1);
|
|
||||||
|
|
||||||
List *joinClauses = joinClauseList;
|
|
||||||
if (!passJoinClauseDirectly)
|
|
||||||
{
|
|
||||||
/* find applicable join clauses between the left and right data sources */
|
|
||||||
uint32 rightTableId = (uint32) linitial_int(rightTableIdList);
|
|
||||||
joinClauses = ApplicableJoinClauses(leftTableIdList, rightTableId,
|
|
||||||
joinClauseList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* call the join rule application function to create the new join node */
|
/* call the join rule application function to create the new join node */
|
||||||
RuleApplyFunction ruleApplyFunction = JoinRuleApplyFunction(ruleType);
|
RuleApplyFunction ruleApplyFunction = JoinRuleApplyFunction(ruleType);
|
||||||
MultiNode *multiNode = (*ruleApplyFunction)(leftNode, rightNode, partitionColumnList,
|
MultiNode *multiNode = (*ruleApplyFunction)(leftNode, rightNode, partitionColumnList,
|
||||||
joinType, joinClauses);
|
joinType, joinClauseList);
|
||||||
|
|
||||||
if (joinType != JOIN_INNER && CitusIsA(multiNode, MultiJoin))
|
|
||||||
{
|
|
||||||
MultiJoin *joinNode = (MultiJoin *) multiNode;
|
|
||||||
|
|
||||||
/* preserve non-join clauses for OUTER joins */
|
|
||||||
joinNode->joinClauseList = list_copy(joinClauseList);
|
|
||||||
}
|
|
||||||
|
|
||||||
return multiNode;
|
return multiNode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,8 +224,18 @@ ShouldUseSubqueryPushDown(Query *originalQuery, Query *rewrittenQuery,
|
||||||
* Some unsupported join clauses in logical planner
|
* Some unsupported join clauses in logical planner
|
||||||
* may be supported by subquery pushdown planner.
|
* may be supported by subquery pushdown planner.
|
||||||
*/
|
*/
|
||||||
List *qualifierList = QualifierList(rewrittenQuery->jointree);
|
RestrictInfoContext *restrictInfoContext = ExtractRestrictionInfosFromPlannerContext(
|
||||||
if (DeferErrorIfUnsupportedClause(qualifierList) != NULL)
|
plannerRestrictionContext);
|
||||||
|
List *joinRestrictionInfoList = restrictInfoContext->joinRestrictInfoList;
|
||||||
|
List *nonJoinRestrictionInfoList = restrictInfoContext->baseRestrictInfoList;
|
||||||
|
|
||||||
|
/* verify we can plan for restriction clauses */
|
||||||
|
List *whereClauseList = ExtractRestrictClausesFromRestrictionInfoList(
|
||||||
|
nonJoinRestrictionInfoList);
|
||||||
|
List *joinClauseList = ExtractRestrictClausesFromRestrictionInfoList(
|
||||||
|
joinRestrictionInfoList);
|
||||||
|
List *qualClauseList = list_concat_copy(whereClauseList, joinClauseList);
|
||||||
|
if (DeferErrorIfUnsupportedClause(qualClauseList) != NULL)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,7 @@ typedef struct JoinRestrictionContext
|
||||||
List *joinRestrictionList;
|
List *joinRestrictionList;
|
||||||
bool hasSemiJoin;
|
bool hasSemiJoin;
|
||||||
bool hasOuterJoin;
|
bool hasOuterJoin;
|
||||||
|
List *generatedEcJoinRestrictInfoList;
|
||||||
} JoinRestrictionContext;
|
} JoinRestrictionContext;
|
||||||
|
|
||||||
typedef struct JoinRestriction
|
typedef struct JoinRestriction
|
||||||
|
@ -105,6 +106,8 @@ typedef struct FastPathRestrictionContext
|
||||||
* Set to true when distKey = Param; in the queryTree
|
* Set to true when distKey = Param; in the queryTree
|
||||||
*/
|
*/
|
||||||
bool distributionKeyHasParam;
|
bool distributionKeyHasParam;
|
||||||
|
|
||||||
|
int distRelId;
|
||||||
}FastPathRestrictionContext;
|
}FastPathRestrictionContext;
|
||||||
|
|
||||||
typedef struct PlannerRestrictionContext
|
typedef struct PlannerRestrictionContext
|
||||||
|
@ -241,6 +244,8 @@ extern bool GetOriginalInh(RangeTblEntry *rte);
|
||||||
extern LOCKMODE GetQueryLockMode(Query *query);
|
extern LOCKMODE GetQueryLockMode(Query *query);
|
||||||
extern int32 BlessRecordExpression(Expr *expr);
|
extern int32 BlessRecordExpression(Expr *expr);
|
||||||
extern void DissuadePlannerFromUsingPlan(PlannedStmt *plan);
|
extern void DissuadePlannerFromUsingPlan(PlannedStmt *plan);
|
||||||
|
extern Query * ReplanAfterQueryModification(Query *originalQuery, ParamListInfo
|
||||||
|
boundParams);
|
||||||
extern PlannedStmt * FinalizePlan(PlannedStmt *localPlan,
|
extern PlannedStmt * FinalizePlan(PlannedStmt *localPlan,
|
||||||
struct DistributedPlan *distributedPlan);
|
struct DistributedPlan *distributedPlan);
|
||||||
extern RTEListProperties * GetRTEListPropertiesForQuery(Query *query);
|
extern RTEListProperties * GetRTEListPropertiesForQuery(Query *query);
|
||||||
|
|
|
@ -44,6 +44,8 @@ extern DistributedPlan * CreateInsertSelectIntoLocalTablePlan(uint64 planId,
|
||||||
plannerRestrictionContext);
|
plannerRestrictionContext);
|
||||||
extern char * InsertSelectResultIdPrefix(uint64 planId);
|
extern char * InsertSelectResultIdPrefix(uint64 planId);
|
||||||
extern bool PlanningInsertSelect(void);
|
extern bool PlanningInsertSelect(void);
|
||||||
|
extern void RelabelPlannerRestrictionContext(
|
||||||
|
PlannerRestrictionContext *plannerRestrictionContext);
|
||||||
|
|
||||||
|
|
||||||
#endif /* INSERT_SELECT_PLANNER_H */
|
#endif /* INSERT_SELECT_PLANNER_H */
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "nodes/pathnodes.h"
|
||||||
#include "nodes/pg_list.h"
|
#include "nodes/pg_list.h"
|
||||||
#include "nodes/primnodes.h"
|
#include "nodes/primnodes.h"
|
||||||
|
|
||||||
|
@ -30,6 +31,7 @@
|
||||||
typedef enum JoinRuleType
|
typedef enum JoinRuleType
|
||||||
{
|
{
|
||||||
JOIN_RULE_INVALID_FIRST = 0,
|
JOIN_RULE_INVALID_FIRST = 0,
|
||||||
|
|
||||||
REFERENCE_JOIN = 1,
|
REFERENCE_JOIN = 1,
|
||||||
LOCAL_PARTITION_JOIN = 2,
|
LOCAL_PARTITION_JOIN = 2,
|
||||||
SINGLE_HASH_PARTITION_JOIN = 3,
|
SINGLE_HASH_PARTITION_JOIN = 3,
|
||||||
|
@ -114,13 +116,18 @@ extern bool EnableSingleHashRepartitioning;
|
||||||
|
|
||||||
/* Function declaration for determining table join orders */
|
/* Function declaration for determining table join orders */
|
||||||
extern List * JoinExprList(FromExpr *fromExpr);
|
extern List * JoinExprList(FromExpr *fromExpr);
|
||||||
extern List * JoinOrderList(List *rangeTableEntryList, List *joinClauseList);
|
extern List * JoinOrderList(List *rangeTableEntryList, List *joinRestrictInfoListList,
|
||||||
|
List *generatedEcJoinClauseList, List *pseudoClauseList);
|
||||||
extern List * FixedJoinOrderList(List *rangeTableEntryList,
|
extern List * FixedJoinOrderList(List *rangeTableEntryList,
|
||||||
JoinInfoContext *joinInfoContext);
|
JoinInfoContext *joinInfoContext,
|
||||||
|
List *joinRestrictInfoListList,
|
||||||
|
List *generatedEcJoinClauseList,
|
||||||
|
List *pseudoClauseList);
|
||||||
extern bool IsApplicableJoinClause(List *leftTableIdList, uint32 rightTableId,
|
extern bool IsApplicableJoinClause(List *leftTableIdList, uint32 rightTableId,
|
||||||
Node *joinClause);
|
Node *joinClause);
|
||||||
extern List * ApplicableJoinClauses(List *leftTableIdList, uint32 rightTableId,
|
extern bool IsApplicableFalseConstantJoinClause(List *leftTableIdList, uint32
|
||||||
List *joinClauseList);
|
rightTableId,
|
||||||
|
RestrictInfo *restrictInfo);
|
||||||
extern bool NodeIsEqualsOpExpr(Node *node);
|
extern bool NodeIsEqualsOpExpr(Node *node);
|
||||||
extern bool IsSupportedReferenceJoin(JoinType joinType, bool leftIsReferenceTable,
|
extern bool IsSupportedReferenceJoin(JoinType joinType, bool leftIsReferenceTable,
|
||||||
bool rightIsReferenceTable);
|
bool rightIsReferenceTable);
|
||||||
|
|
|
@ -124,6 +124,8 @@ typedef struct MultiSelect
|
||||||
{
|
{
|
||||||
MultiUnaryNode unaryNode;
|
MultiUnaryNode unaryNode;
|
||||||
List *selectClauseList;
|
List *selectClauseList;
|
||||||
|
List *pushdownableSelectClauseList;
|
||||||
|
List *nonPushdownableSelectClauseList;
|
||||||
} MultiSelect;
|
} MultiSelect;
|
||||||
|
|
||||||
|
|
||||||
|
@ -188,6 +190,17 @@ typedef struct MultiExtendedOp
|
||||||
} MultiExtendedOp;
|
} MultiExtendedOp;
|
||||||
|
|
||||||
|
|
||||||
|
/* RestrictInfoContext stores join and base restriction infos extracted from planner context*/
|
||||||
|
typedef struct RestrictInfoContext
|
||||||
|
{
|
||||||
|
List *baseRestrictInfoList;
|
||||||
|
List *joinRestrictInfoList;
|
||||||
|
List *joinRestrictInfoListList;
|
||||||
|
List *generatedEcJoinClauseList;
|
||||||
|
List *pseudoRestrictInfoList;
|
||||||
|
} RestrictInfoContext;
|
||||||
|
|
||||||
|
|
||||||
/* Function declarations for building logical plans */
|
/* Function declarations for building logical plans */
|
||||||
extern MultiTreeRoot * MultiLogicalPlanCreate(Query *originalQuery, Query *queryTree,
|
extern MultiTreeRoot * MultiLogicalPlanCreate(Query *originalQuery, Query *queryTree,
|
||||||
PlannerRestrictionContext *
|
PlannerRestrictionContext *
|
||||||
|
@ -214,12 +227,13 @@ extern bool UnaryOperator(MultiNode *node);
|
||||||
extern bool BinaryOperator(MultiNode *node);
|
extern bool BinaryOperator(MultiNode *node);
|
||||||
extern List * OutputTableIdList(MultiNode *multiNode);
|
extern List * OutputTableIdList(MultiNode *multiNode);
|
||||||
extern List * FindNodesOfType(MultiNode *node, int type);
|
extern List * FindNodesOfType(MultiNode *node, int type);
|
||||||
extern List * JoinClauseList(List *whereClauseList);
|
|
||||||
extern bool IsJoinClause(Node *clause);
|
extern bool IsJoinClause(Node *clause);
|
||||||
extern List * SubqueryEntryList(Query *queryTree);
|
extern List * SubqueryEntryList(Query *queryTree);
|
||||||
extern DeferredErrorMessage * DeferErrorIfQueryNotSupported(Query *queryTree);
|
extern DeferredErrorMessage * DeferErrorIfQueryNotSupported(Query *queryTree);
|
||||||
extern List * WhereClauseList(FromExpr *fromExpr);
|
extern List * WhereClauseList(FromExpr *fromExpr);
|
||||||
extern List * QualifierList(FromExpr *fromExpr);
|
extern RestrictInfoContext * ExtractRestrictionInfosFromPlannerContext(
|
||||||
|
PlannerRestrictionContext *plannerRestrictionContext);
|
||||||
|
extern List * ExtractRestrictClausesFromRestrictionInfoList(List *restrictInfoList);
|
||||||
extern List * TableEntryList(List *rangeTableList);
|
extern List * TableEntryList(List *rangeTableList);
|
||||||
extern List * UsedTableEntryList(Query *query);
|
extern List * UsedTableEntryList(Query *query);
|
||||||
extern List * pull_var_clause_default(Node *node);
|
extern List * pull_var_clause_default(Node *node);
|
||||||
|
@ -229,7 +243,8 @@ extern MultiProject * MultiProjectNode(List *targetEntryList);
|
||||||
extern MultiExtendedOp * MultiExtendedOpNode(Query *queryTree, Query *originalQuery);
|
extern MultiExtendedOp * MultiExtendedOpNode(Query *queryTree, Query *originalQuery);
|
||||||
extern DeferredErrorMessage * DeferErrorIfUnsupportedSubqueryRepartition(Query *
|
extern DeferredErrorMessage * DeferErrorIfUnsupportedSubqueryRepartition(Query *
|
||||||
subqueryTree);
|
subqueryTree);
|
||||||
extern MultiNode * MultiNodeTree(Query *queryTree);
|
extern MultiNode * MultiNodeTree(Query *queryTree,
|
||||||
|
PlannerRestrictionContext *plannerRestrictionContext);
|
||||||
|
|
||||||
|
|
||||||
#endif /* MULTI_LOGICAL_PLANNER_H */
|
#endif /* MULTI_LOGICAL_PLANNER_H */
|
||||||
|
|
|
@ -47,6 +47,7 @@ order by s_i_id;
|
||||||
Node: host=localhost port=xxxxx dbname=regression
|
Node: host=localhost port=xxxxx dbname=regression
|
||||||
-> Aggregate
|
-> Aggregate
|
||||||
-> Seq Scan on stock_1640000 stock
|
-> Seq Scan on stock_1640000 stock
|
||||||
|
Filter: (s_order_cnt IS NOT NULL)
|
||||||
Task Count: 4
|
Task Count: 4
|
||||||
Tasks Shown: One of 4
|
Tasks Shown: One of 4
|
||||||
-> Task
|
-> Task
|
||||||
|
@ -57,7 +58,7 @@ order by s_i_id;
|
||||||
-> Function Scan on read_intermediate_result intermediate_result
|
-> Function Scan on read_intermediate_result intermediate_result
|
||||||
-> Seq Scan on stock_1640000 stock
|
-> Seq Scan on stock_1640000 stock
|
||||||
Filter: ((s_order_cnt)::numeric > $0)
|
Filter: ((s_order_cnt)::numeric > $0)
|
||||||
(36 rows)
|
(37 rows)
|
||||||
|
|
||||||
explain (costs false, summary false, timing false)
|
explain (costs false, summary false, timing false)
|
||||||
select s_i_id, sum(s_order_cnt) as ordercount
|
select s_i_id, sum(s_order_cnt) as ordercount
|
||||||
|
@ -84,6 +85,7 @@ order by s_i_id;
|
||||||
Node: host=localhost port=xxxxx dbname=regression
|
Node: host=localhost port=xxxxx dbname=regression
|
||||||
-> Aggregate
|
-> Aggregate
|
||||||
-> Seq Scan on stock_1640000 stock
|
-> Seq Scan on stock_1640000 stock
|
||||||
|
Filter: (s_order_cnt IS NOT NULL)
|
||||||
Task Count: 4
|
Task Count: 4
|
||||||
Tasks Shown: One of 4
|
Tasks Shown: One of 4
|
||||||
-> Task
|
-> Task
|
||||||
|
@ -91,7 +93,7 @@ order by s_i_id;
|
||||||
-> HashAggregate
|
-> HashAggregate
|
||||||
Group Key: stock.s_i_id
|
Group Key: stock.s_i_id
|
||||||
-> Seq Scan on stock_1640000 stock
|
-> Seq Scan on stock_1640000 stock
|
||||||
(24 rows)
|
(25 rows)
|
||||||
|
|
||||||
explain (costs false, summary false, timing false)
|
explain (costs false, summary false, timing false)
|
||||||
select s_i_id, sum(s_order_cnt) as ordercount
|
select s_i_id, sum(s_order_cnt) as ordercount
|
||||||
|
@ -115,6 +117,7 @@ having sum(s_order_cnt) > (select max(s_order_cnt) - 3 as having_query from st
|
||||||
Node: host=localhost port=xxxxx dbname=regression
|
Node: host=localhost port=xxxxx dbname=regression
|
||||||
-> Aggregate
|
-> Aggregate
|
||||||
-> Seq Scan on stock_1640000 stock
|
-> Seq Scan on stock_1640000 stock
|
||||||
|
Filter: (s_order_cnt IS NOT NULL)
|
||||||
Task Count: 4
|
Task Count: 4
|
||||||
Tasks Shown: One of 4
|
Tasks Shown: One of 4
|
||||||
-> Task
|
-> Task
|
||||||
|
@ -122,7 +125,7 @@ having sum(s_order_cnt) > (select max(s_order_cnt) - 3 as having_query from st
|
||||||
-> HashAggregate
|
-> HashAggregate
|
||||||
Group Key: stock.s_i_id
|
Group Key: stock.s_i_id
|
||||||
-> Seq Scan on stock_1640000 stock
|
-> Seq Scan on stock_1640000 stock
|
||||||
(22 rows)
|
(23 rows)
|
||||||
|
|
||||||
explain (costs false)
|
explain (costs false)
|
||||||
select s_i_id, sum(s_order_cnt) as ordercount
|
select s_i_id, sum(s_order_cnt) as ordercount
|
||||||
|
|
|
@ -345,7 +345,7 @@ GROUP BY
|
||||||
ORDER BY
|
ORDER BY
|
||||||
revenue DESC,
|
revenue DESC,
|
||||||
o_entry_d;
|
o_entry_d;
|
||||||
LOG: join order: [ "customer" ][ local partition join(INNER) "oorder" ][ local partition join(INNER) "new_order" ][ local partition join(INNER) "order_line" ]
|
LOG: join order: [ "customer" ][ local partition join(INNER) "new_order" ][ local partition join(INNER) "oorder" ][ local partition join(INNER) "order_line" ]
|
||||||
ol_o_id | ol_w_id | ol_d_id | revenue | o_entry_d
|
ol_o_id | ol_w_id | ol_d_id | revenue | o_entry_d
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
10 | 10 | 10 | 10.00 | Fri Oct 17 00:00:00 2008
|
10 | 10 | 10 | 10.00 | Fri Oct 17 00:00:00 2008
|
||||||
|
@ -472,7 +472,7 @@ ORDER BY
|
||||||
su_nationkey,
|
su_nationkey,
|
||||||
cust_nation,
|
cust_nation,
|
||||||
l_year;
|
l_year;
|
||||||
LOG: join order: [ "order_line" ][ local partition join(INNER) "oorder" ][ local partition join(INNER) "customer" ][ reference join(INNER) "nation" ][ reference join(INNER) "nation" ][ reference join(INNER) "supplier" ][ dual partition join(INNER) "stock" ]
|
LOG: join order: [ "order_line" ][ local partition join(INNER) "oorder" ][ local partition join(INNER) "customer" ][ reference join(INNER) "nation" ][ dual partition join(INNER) "stock" ][ reference join(INNER) "supplier" ][ reference join(INNER) "nation" ]
|
||||||
supp_nation | cust_nation | l_year | revenue
|
supp_nation | cust_nation | l_year | revenue
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
9 | C | 2008 | 3.00
|
9 | C | 2008 | 3.00
|
||||||
|
@ -963,7 +963,7 @@ ORDER BY
|
||||||
su_nationkey,
|
su_nationkey,
|
||||||
cust_nation,
|
cust_nation,
|
||||||
l_year;
|
l_year;
|
||||||
LOG: join order: [ "order_line" ][ local partition join(INNER) "oorder" ][ local partition join(INNER) "customer" ][ reference join(INNER) "nation" ][ reference join(INNER) "nation" ][ reference join(INNER) "supplier" ][ single hash partition join(INNER) "stock" ]
|
LOG: join order: [ "order_line" ][ local partition join(INNER) "oorder" ][ local partition join(INNER) "customer" ][ reference join(INNER) "nation" ][ single hash partition join(INNER) "stock" ][ reference join(INNER) "supplier" ][ reference join(INNER) "nation" ]
|
||||||
supp_nation | cust_nation | l_year | revenue
|
supp_nation | cust_nation | l_year | revenue
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
9 | C | 2008 | 3.00
|
9 | C | 2008 | 3.00
|
||||||
|
@ -1003,7 +1003,7 @@ WHERE i_id = s_i_id
|
||||||
AND i_id = ol_i_id
|
AND i_id = ol_i_id
|
||||||
GROUP BY extract(YEAR FROM o_entry_d)
|
GROUP BY extract(YEAR FROM o_entry_d)
|
||||||
ORDER BY l_year;
|
ORDER BY l_year;
|
||||||
LOG: join order: [ "order_line" ][ reference join(INNER) "item" ][ local partition join(INNER) "oorder" ][ local partition join(INNER) "customer" ][ reference join(INNER) "nation" ][ reference join(INNER) "region" ][ single hash partition join(INNER) "stock" ][ reference join(INNER) "supplier" ][ reference join(INNER) "nation" ]
|
LOG: join order: [ "order_line" ][ reference join(INNER) "item" ][ local partition join(INNER) "oorder" ][ local partition join(INNER) "customer" ][ reference join(INNER) "nation" ][ reference join(INNER) "region" ][ dual partition join(INNER) "stock" ][ reference join(INNER) "supplier" ][ reference join(INNER) "nation" ]
|
||||||
l_year | mkt_share
|
l_year | mkt_share
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
2008 | 0.50000000000000000000
|
2008 | 0.50000000000000000000
|
||||||
|
@ -1036,7 +1036,7 @@ GROUP BY
|
||||||
ORDER BY
|
ORDER BY
|
||||||
n_name,
|
n_name,
|
||||||
l_year DESC;
|
l_year DESC;
|
||||||
LOG: join order: [ "order_line" ][ reference join(INNER) "item" ][ local partition join(INNER) "oorder" ][ single hash partition join(INNER) "stock" ][ reference join(INNER) "supplier" ][ reference join(INNER) "nation" ]
|
LOG: join order: [ "order_line" ][ reference join(INNER) "item" ][ local partition join(INNER) "oorder" ][ dual partition join(INNER) "stock" ][ reference join(INNER) "supplier" ][ reference join(INNER) "nation" ]
|
||||||
n_name | l_year | sum_profit
|
n_name | l_year | sum_profit
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
Germany | 2008 | 3.00
|
Germany | 2008 | 3.00
|
||||||
|
|
|
@ -189,12 +189,14 @@ SELECT count(*) FROM users_table u1 CROSS JOIN users_ref_test_table ref2 JOIN us
|
||||||
reset citus.enable_repartition_joins;
|
reset citus.enable_repartition_joins;
|
||||||
-- although the following has the "ref LEFT JOIN dist" type of query, the LEFT JOIN is eliminated by Postgres
|
-- although the following has the "ref LEFT JOIN dist" type of query, the LEFT JOIN is eliminated by Postgres
|
||||||
-- because the INNER JOIN eliminates the LEFT JOIN
|
-- because the INNER JOIN eliminates the LEFT JOIN
|
||||||
|
SET citus.enable_repartition_joins TO on;
|
||||||
SELECT count(*) FROM users_ref_test_table ref1 CROSS JOIN users_ref_test_table ref2 LEFT JOIN users_table ON (ref1.id = users_table.user_id) JOIN users_table u2 ON (u2.user_id = users_table.user_id);
|
SELECT count(*) FROM users_ref_test_table ref1 CROSS JOIN users_ref_test_table ref2 LEFT JOIN users_table ON (ref1.id = users_table.user_id) JOIN users_table u2 ON (u2.user_id = users_table.user_id);
|
||||||
count
|
count
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
11802
|
11802
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
reset citus.enable_repartition_joins;
|
||||||
-- this is the same query as the above, but this time the outer query is also LEFT JOIN, meaning that Postgres
|
-- this is the same query as the above, but this time the outer query is also LEFT JOIN, meaning that Postgres
|
||||||
-- cannot eliminate the outer join
|
-- cannot eliminate the outer join
|
||||||
SELECT count(*) FROM users_ref_test_table ref1 CROSS JOIN users_ref_test_table ref2 LEFT JOIN users_table ON (ref1.id = users_table.user_id) LEFT JOIN users_table u2 ON (u2.user_id = users_table.user_id);
|
SELECT count(*) FROM users_ref_test_table ref1 CROSS JOIN users_ref_test_table ref2 LEFT JOIN users_table ON (ref1.id = users_table.user_id) LEFT JOIN users_table u2 ON (u2.user_id = users_table.user_id);
|
||||||
|
|
|
@ -102,7 +102,9 @@ LOG: join order: [ "lineitem" ][ local partition join(INNER) "orders" ]
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT l_quantity FROM lineitem, orders
|
SELECT l_quantity FROM lineitem, orders
|
||||||
WHERE (l_orderkey = o_orderkey OR l_quantity > 5);
|
WHERE (l_orderkey = o_orderkey OR l_quantity > 5);
|
||||||
ERROR: complex joins are only supported when all distributed tables are joined on their distribution columns with equal operator
|
LOG: join order: [ "lineitem" ][ cartesian product(INNER) "orders" ]
|
||||||
|
ERROR: cannot perform distributed planning on this query
|
||||||
|
DETAIL: Cartesian products are currently unsupported
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM orders, lineitem_hash
|
SELECT count(*) FROM orders, lineitem_hash
|
||||||
WHERE o_orderkey = l_orderkey;
|
WHERE o_orderkey = l_orderkey;
|
||||||
|
|
|
@ -41,7 +41,7 @@ GROUP BY
|
||||||
ORDER BY
|
ORDER BY
|
||||||
l_partkey, l_suppkey
|
l_partkey, l_suppkey
|
||||||
LIMIT 10;
|
LIMIT 10;
|
||||||
LOG: join order: [ "lineitem" ][ reference join(INNER) "supplier" ][ dual partition join(INNER) "part_append" ]
|
LOG: join order: [ "lineitem" ][ dual partition join(INNER) "part_append" ][ cartesian product reference join(INNER) "supplier" ]
|
||||||
DEBUG: push down of limit count: 10
|
DEBUG: push down of limit count: 10
|
||||||
l_partkey | l_suppkey | count
|
l_partkey | l_suppkey | count
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
|
@ -69,7 +69,7 @@ GROUP BY
|
||||||
ORDER BY
|
ORDER BY
|
||||||
l_partkey, l_suppkey
|
l_partkey, l_suppkey
|
||||||
LIMIT 10;
|
LIMIT 10;
|
||||||
LOG: join order: [ "lineitem" ][ reference join(INNER) "supplier" ][ dual partition join(INNER) "part_append" ]
|
LOG: join order: [ "lineitem" ][ dual partition join(INNER) "part_append" ][ cartesian product reference join(INNER) "supplier" ]
|
||||||
DEBUG: push down of limit count: 10
|
DEBUG: push down of limit count: 10
|
||||||
l_partkey | l_suppkey | count
|
l_partkey | l_suppkey | count
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
|
@ -124,7 +124,7 @@ GROUP BY
|
||||||
ORDER BY
|
ORDER BY
|
||||||
l_partkey, l_suppkey
|
l_partkey, l_suppkey
|
||||||
LIMIT 10;
|
LIMIT 10;
|
||||||
LOG: join order: [ "lineitem" ][ reference join(INNER) "supplier" ][ dual partition join(INNER) "part_append" ]
|
LOG: join order: [ "lineitem" ][ dual partition join(INNER) "part_append" ][ cartesian product reference join(INNER) "supplier" ]
|
||||||
DEBUG: push down of limit count: 10
|
DEBUG: push down of limit count: 10
|
||||||
l_partkey | l_suppkey | count
|
l_partkey | l_suppkey | count
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
|
@ -152,7 +152,7 @@ GROUP BY
|
||||||
ORDER BY
|
ORDER BY
|
||||||
l_partkey, l_suppkey
|
l_partkey, l_suppkey
|
||||||
LIMIT 10;
|
LIMIT 10;
|
||||||
LOG: join order: [ "lineitem" ][ reference join(INNER) "supplier" ][ dual partition join(INNER) "part_append" ]
|
LOG: join order: [ "lineitem" ][ dual partition join(INNER) "part_append" ][ cartesian product reference join(INNER) "supplier" ]
|
||||||
DEBUG: push down of limit count: 10
|
DEBUG: push down of limit count: 10
|
||||||
l_partkey | l_suppkey | count
|
l_partkey | l_suppkey | count
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
|
@ -180,7 +180,7 @@ GROUP BY
|
||||||
ORDER BY
|
ORDER BY
|
||||||
l_partkey, l_suppkey
|
l_partkey, l_suppkey
|
||||||
LIMIT 10;
|
LIMIT 10;
|
||||||
LOG: join order: [ "lineitem" ][ reference join(INNER) "supplier" ][ dual partition join(INNER) "part_append" ]
|
LOG: join order: [ "lineitem" ][ dual partition join(INNER) "part_append" ][ cartesian product reference join(INNER) "supplier" ]
|
||||||
DEBUG: push down of limit count: 10
|
DEBUG: push down of limit count: 10
|
||||||
l_partkey | l_suppkey | count
|
l_partkey | l_suppkey | count
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
|
|
|
@ -1074,8 +1074,96 @@ DETAIL: Cartesian products are currently unsupported
|
||||||
-- join order planner cannot handle right recursive joins
|
-- join order planner cannot handle right recursive joins
|
||||||
SELECT t1.*, t2.* FROM test_hash1 t1 LEFT JOIN ( test_hash2 t2 JOIN test_hash3 t3 ON t2.col2 = t3.col1) ON (t1.col1 = t2.col1) ORDER BY 1,2,3,4;
|
SELECT t1.*, t2.* FROM test_hash1 t1 LEFT JOIN ( test_hash2 t2 JOIN test_hash3 t3 ON t2.col2 = t3.col1) ON (t1.col1 = t2.col1) ORDER BY 1,2,3,4;
|
||||||
ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns
|
ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns
|
||||||
|
-- sometimes join filters are pushed down and applied before join by PG
|
||||||
|
CREATE TABLE dist1 (x INT, y INT);
|
||||||
|
CREATE TABLE dist2 (x INT, y INT);
|
||||||
|
SELECT create_distributed_table('dist1','x');
|
||||||
|
create_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT create_distributed_table('dist2','x');
|
||||||
|
create_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
INSERT INTO dist1 VALUES (1,2);
|
||||||
|
INSERT INTO dist1 VALUES (3,4);
|
||||||
|
INSERT INTO dist2 VALUES (1,2);
|
||||||
|
INSERT INTO dist2 VALUES (5,6);
|
||||||
|
-- single join condition
|
||||||
|
SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.x = dist2.x) ORDER BY 1,2,3,4;
|
||||||
|
x | y | x | y
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1 | 2 | 1 | 2
|
||||||
|
3 | 4 | |
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- single join condition and dist2.x >2 will be pushed down as it is on inner part of the join. e.g. filter out dist2.x <= 2 beforehand
|
||||||
|
SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.x = dist2.x AND dist2.x >2) ORDER BY 1,2,3,4;
|
||||||
|
x | y | x | y
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1 | 2 | |
|
||||||
|
3 | 4 | |
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- single join condition and dist2.x >2 is regular filter and applied after join
|
||||||
|
SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.x = dist2.x) WHERE dist2.x >2 ORDER BY 1,2,3,4;
|
||||||
|
LOG: join order: [ "dist1" ][ local partition join(INNER) "dist2" ]
|
||||||
|
x | y | x | y
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
-- single join condition and dist1.x >2 will not be pushed down as it is on outer part of the join
|
||||||
|
SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.x = dist2.x AND dist1.x >2) ORDER BY 1,2,3,4;
|
||||||
|
x | y | x | y
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1 | 2 | |
|
||||||
|
3 | 4 | |
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- single join condition and dist1.x >2 is regular filter and applied after join
|
||||||
|
SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.x = dist2.x) WHERE dist1.x >2 ORDER BY 1,2,3,4;
|
||||||
|
x | y | x | y
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
3 | 4 | |
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- constant false filter as join filter for left join.
|
||||||
|
-- Inner table will be converted to empty result. Constant filter will be applied before join but will not be pushdowned.
|
||||||
|
SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.y = dist2.y AND false) ORDER BY 1,2,3,4;
|
||||||
|
LOG: join order: [ "dist1" ][ cartesian product(LEFT) "dist2" ]
|
||||||
|
x | y | x | y
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1 | 2 | |
|
||||||
|
3 | 4 | |
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- constant false filter as base filter for left join.
|
||||||
|
-- Both tables will be converted to empty result .e.g RTE_RESULT
|
||||||
|
SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.y = dist2.y) WHERE false ORDER BY 1,2,3,4;
|
||||||
|
x | y | x | y
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
-- constant false filter as join filter for inner join.
|
||||||
|
-- Both tables will be converted to empty result .e.g RTE_RESULT
|
||||||
|
SELECT * FROM dist1 INNER JOIN dist2 ON (dist1.y = dist2.y AND false) ORDER BY 1,2,3,4;
|
||||||
|
x | y | x | y
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
-- constant false filter as base filter for inner join.
|
||||||
|
-- Both tables will be converted to empty result .e.g RTE_RESULT
|
||||||
|
SELECT * FROM dist1 INNER JOIN dist2 ON (dist1.y = dist2.y) WHERE false ORDER BY 1,2,3,4;
|
||||||
|
x | y | x | y
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
DROP SCHEMA non_colocated_outer_joins CASCADE;
|
DROP SCHEMA non_colocated_outer_joins CASCADE;
|
||||||
NOTICE: drop cascades to 9 other objects
|
NOTICE: drop cascades to 11 other objects
|
||||||
DETAIL: drop cascades to table test_hash1
|
DETAIL: drop cascades to table test_hash1
|
||||||
drop cascades to table test_hash2
|
drop cascades to table test_hash2
|
||||||
drop cascades to table test_hash3
|
drop cascades to table test_hash3
|
||||||
|
@ -1085,6 +1173,8 @@ drop cascades to table test_append3
|
||||||
drop cascades to table test_range1
|
drop cascades to table test_range1
|
||||||
drop cascades to table test_range2
|
drop cascades to table test_range2
|
||||||
drop cascades to table test_range3
|
drop cascades to table test_range3
|
||||||
|
drop cascades to table dist1
|
||||||
|
drop cascades to table dist2
|
||||||
RESET client_min_messages;
|
RESET client_min_messages;
|
||||||
RESET citus.log_multi_join_order;
|
RESET citus.log_multi_join_order;
|
||||||
RESET citus.enable_repartition_joins;
|
RESET citus.enable_repartition_joins;
|
||||||
|
|
|
@ -98,7 +98,7 @@ FROM
|
||||||
WHERE
|
WHERE
|
||||||
r1.id = t1.id AND t2.sum = t1.id;
|
r1.id = t1.id AND t2.sum = t1.id;
|
||||||
DEBUG: Router planner cannot handle multi-shard select queries
|
DEBUG: Router planner cannot handle multi-shard select queries
|
||||||
LOG: join order: [ "single_hash_repartition_second" ][ reference join(INNER) "ref_table" ][ single hash partition join(INNER) "single_hash_repartition_first" ]
|
LOG: join order: [ "single_hash_repartition_first" ][ reference join(INNER) "ref_table" ][ single hash partition join(INNER) "single_hash_repartition_second" ]
|
||||||
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [-1073741824,-1]
|
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [-1073741824,-1]
|
||||||
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [0,1073741823]
|
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [0,1073741823]
|
||||||
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [1073741824,2147483647]
|
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [1073741824,2147483647]
|
||||||
|
@ -326,7 +326,19 @@ FROM
|
||||||
WHERE
|
WHERE
|
||||||
t1.id = t2.sum AND t2.sum = t3.id;
|
t1.id = t2.sum AND t2.sum = t3.id;
|
||||||
DEBUG: Router planner cannot handle multi-shard select queries
|
DEBUG: Router planner cannot handle multi-shard select queries
|
||||||
LOG: join order: [ "single_hash_repartition_first" ][ single hash partition join(INNER) "single_hash_repartition_second" ][ single hash partition join(INNER) "single_hash_repartition_second" ]
|
LOG: join order: [ "single_hash_repartition_first" ][ local partition join(INNER) "single_hash_repartition_second" ][ single hash partition join(INNER) "single_hash_repartition_second" ]
|
||||||
|
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [-1073741824,-1]
|
||||||
|
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [0,1073741823]
|
||||||
|
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [1073741824,2147483647]
|
||||||
|
DEBUG: join prunable for intervals [-1073741824,-1] and [-2147483648,-1073741825]
|
||||||
|
DEBUG: join prunable for intervals [-1073741824,-1] and [0,1073741823]
|
||||||
|
DEBUG: join prunable for intervals [-1073741824,-1] and [1073741824,2147483647]
|
||||||
|
DEBUG: join prunable for intervals [0,1073741823] and [-2147483648,-1073741825]
|
||||||
|
DEBUG: join prunable for intervals [0,1073741823] and [-1073741824,-1]
|
||||||
|
DEBUG: join prunable for intervals [0,1073741823] and [1073741824,2147483647]
|
||||||
|
DEBUG: join prunable for intervals [1073741824,2147483647] and [-2147483648,-1073741825]
|
||||||
|
DEBUG: join prunable for intervals [1073741824,2147483647] and [-1073741824,-1]
|
||||||
|
DEBUG: join prunable for intervals [1073741824,2147483647] and [0,1073741823]
|
||||||
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [-1073741824,-1]
|
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [-1073741824,-1]
|
||||||
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [0,1073741823]
|
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [0,1073741823]
|
||||||
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [1073741824,2147483647]
|
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [1073741824,2147483647]
|
||||||
|
@ -347,26 +359,6 @@ DEBUG: pruning merge fetch taskId 5
|
||||||
DETAIL: Creating dependency on merge taskId 15
|
DETAIL: Creating dependency on merge taskId 15
|
||||||
DEBUG: pruning merge fetch taskId 7
|
DEBUG: pruning merge fetch taskId 7
|
||||||
DETAIL: Creating dependency on merge taskId 20
|
DETAIL: Creating dependency on merge taskId 20
|
||||||
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [-1073741824,-1]
|
|
||||||
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [0,1073741823]
|
|
||||||
DEBUG: join prunable for intervals [-2147483648,-1073741825] and [1073741824,2147483647]
|
|
||||||
DEBUG: join prunable for intervals [-1073741824,-1] and [-2147483648,-1073741825]
|
|
||||||
DEBUG: join prunable for intervals [-1073741824,-1] and [0,1073741823]
|
|
||||||
DEBUG: join prunable for intervals [-1073741824,-1] and [1073741824,2147483647]
|
|
||||||
DEBUG: join prunable for intervals [0,1073741823] and [-2147483648,-1073741825]
|
|
||||||
DEBUG: join prunable for intervals [0,1073741823] and [-1073741824,-1]
|
|
||||||
DEBUG: join prunable for intervals [0,1073741823] and [1073741824,2147483647]
|
|
||||||
DEBUG: join prunable for intervals [1073741824,2147483647] and [-2147483648,-1073741825]
|
|
||||||
DEBUG: join prunable for intervals [1073741824,2147483647] and [-1073741824,-1]
|
|
||||||
DEBUG: join prunable for intervals [1073741824,2147483647] and [0,1073741823]
|
|
||||||
DEBUG: pruning merge fetch taskId 1
|
|
||||||
DETAIL: Creating dependency on merge taskId 9
|
|
||||||
DEBUG: pruning merge fetch taskId 3
|
|
||||||
DETAIL: Creating dependency on merge taskId 14
|
|
||||||
DEBUG: pruning merge fetch taskId 5
|
|
||||||
DETAIL: Creating dependency on merge taskId 19
|
|
||||||
DEBUG: pruning merge fetch taskId 7
|
|
||||||
DETAIL: Creating dependency on merge taskId 24
|
|
||||||
ERROR: the query contains a join that requires repartitioning
|
ERROR: the query contains a join that requires repartitioning
|
||||||
HINT: Set citus.enable_repartition_joins to on to enable repartitioning
|
HINT: Set citus.enable_repartition_joins to on to enable repartitioning
|
||||||
-- two single repartitions again, but this
|
-- two single repartitions again, but this
|
||||||
|
|
|
@ -65,7 +65,9 @@ reset citus.enable_repartition_joins;
|
||||||
|
|
||||||
-- although the following has the "ref LEFT JOIN dist" type of query, the LEFT JOIN is eliminated by Postgres
|
-- although the following has the "ref LEFT JOIN dist" type of query, the LEFT JOIN is eliminated by Postgres
|
||||||
-- because the INNER JOIN eliminates the LEFT JOIN
|
-- because the INNER JOIN eliminates the LEFT JOIN
|
||||||
|
SET citus.enable_repartition_joins TO on;
|
||||||
SELECT count(*) FROM users_ref_test_table ref1 CROSS JOIN users_ref_test_table ref2 LEFT JOIN users_table ON (ref1.id = users_table.user_id) JOIN users_table u2 ON (u2.user_id = users_table.user_id);
|
SELECT count(*) FROM users_ref_test_table ref1 CROSS JOIN users_ref_test_table ref2 LEFT JOIN users_table ON (ref1.id = users_table.user_id) JOIN users_table u2 ON (u2.user_id = users_table.user_id);
|
||||||
|
reset citus.enable_repartition_joins;
|
||||||
|
|
||||||
-- this is the same query as the above, but this time the outer query is also LEFT JOIN, meaning that Postgres
|
-- this is the same query as the above, but this time the outer query is also LEFT JOIN, meaning that Postgres
|
||||||
-- cannot eliminate the outer join
|
-- cannot eliminate the outer join
|
||||||
|
|
|
@ -205,6 +205,43 @@ SELECT tt1.*, t3.* FROM (SELECT t1.* FROM test_hash1 t1, test_hash2 t2) tt1 LEFT
|
||||||
|
|
||||||
SELECT t1.*, t2.* FROM test_hash1 t1 LEFT JOIN ( test_hash2 t2 JOIN test_hash3 t3 ON t2.col2 = t3.col1) ON (t1.col1 = t2.col1) ORDER BY 1,2,3,4;
|
SELECT t1.*, t2.* FROM test_hash1 t1 LEFT JOIN ( test_hash2 t2 JOIN test_hash3 t3 ON t2.col2 = t3.col1) ON (t1.col1 = t2.col1) ORDER BY 1,2,3,4;
|
||||||
|
|
||||||
|
|
||||||
|
-- sometimes join filters are pushed down and applied before join by PG
|
||||||
|
CREATE TABLE dist1 (x INT, y INT);
|
||||||
|
CREATE TABLE dist2 (x INT, y INT);
|
||||||
|
SELECT create_distributed_table('dist1','x');
|
||||||
|
SELECT create_distributed_table('dist2','x');
|
||||||
|
INSERT INTO dist1 VALUES (1,2);
|
||||||
|
INSERT INTO dist1 VALUES (3,4);
|
||||||
|
INSERT INTO dist2 VALUES (1,2);
|
||||||
|
INSERT INTO dist2 VALUES (5,6);
|
||||||
|
|
||||||
|
-- single join condition
|
||||||
|
SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.x = dist2.x) ORDER BY 1,2,3,4;
|
||||||
|
-- single join condition and dist2.x >2 will be pushed down as it is on inner part of the join. e.g. filter out dist2.x <= 2 beforehand
|
||||||
|
SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.x = dist2.x AND dist2.x >2) ORDER BY 1,2,3,4;
|
||||||
|
-- single join condition and dist2.x >2 is regular filter and applied after join
|
||||||
|
SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.x = dist2.x) WHERE dist2.x >2 ORDER BY 1,2,3,4;
|
||||||
|
-- single join condition and dist1.x >2 will not be pushed down as it is on outer part of the join
|
||||||
|
SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.x = dist2.x AND dist1.x >2) ORDER BY 1,2,3,4;
|
||||||
|
-- single join condition and dist1.x >2 is regular filter and applied after join
|
||||||
|
SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.x = dist2.x) WHERE dist1.x >2 ORDER BY 1,2,3,4;
|
||||||
|
|
||||||
|
|
||||||
|
-- constant false filter as join filter for left join.
|
||||||
|
-- Inner table will be converted to empty result. Constant filter will be applied before join but will not be pushdowned.
|
||||||
|
SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.y = dist2.y AND false) ORDER BY 1,2,3,4;
|
||||||
|
-- constant false filter as base filter for left join.
|
||||||
|
-- Both tables will be converted to empty result .e.g RTE_RESULT
|
||||||
|
SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.y = dist2.y) WHERE false ORDER BY 1,2,3,4;
|
||||||
|
-- constant false filter as join filter for inner join.
|
||||||
|
-- Both tables will be converted to empty result .e.g RTE_RESULT
|
||||||
|
SELECT * FROM dist1 INNER JOIN dist2 ON (dist1.y = dist2.y AND false) ORDER BY 1,2,3,4;
|
||||||
|
-- constant false filter as base filter for inner join.
|
||||||
|
-- Both tables will be converted to empty result .e.g RTE_RESULT
|
||||||
|
SELECT * FROM dist1 INNER JOIN dist2 ON (dist1.y = dist2.y) WHERE false ORDER BY 1,2,3,4;
|
||||||
|
|
||||||
|
|
||||||
DROP SCHEMA non_colocated_outer_joins CASCADE;
|
DROP SCHEMA non_colocated_outer_joins CASCADE;
|
||||||
RESET client_min_messages;
|
RESET client_min_messages;
|
||||||
RESET citus.log_multi_join_order;
|
RESET citus.log_multi_join_order;
|
||||||
|
|
Loading…
Reference in New Issue