mirror of https://github.com/citusdata/citus.git
Merge pull request #1805 from citusdata/immutable_functions
Support immutable table functions as reference tablespull/1820/head
commit
e3bd34727f
|
@ -56,6 +56,21 @@ typedef struct QualifierWalkerContext
|
||||||
} QualifierWalkerContext;
|
} QualifierWalkerContext;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RecurringTuplesType is used to distinguish different types of expressions
|
||||||
|
* that always produce the same set of tuples when a shard is queried. We make
|
||||||
|
* this distinction to produce relevant error messages when recurring tuples
|
||||||
|
* are used in a way that would give incorrect results.
|
||||||
|
*/
|
||||||
|
typedef enum RecurringTuplesType
|
||||||
|
{
|
||||||
|
RECURRING_TUPLES_INVALID = 0,
|
||||||
|
RECURRING_TUPLES_REFERENCE_TABLE,
|
||||||
|
RECURRING_TUPLES_FUNCTION,
|
||||||
|
RECURRING_TUPLES_EMPTY_JOIN_TREE
|
||||||
|
} RecurringTuplesType;
|
||||||
|
|
||||||
|
|
||||||
/* Function pointer type definition for apply join rule functions */
|
/* Function pointer type definition for apply join rule functions */
|
||||||
typedef MultiNode *(*RuleApplyFunction) (MultiNode *leftNode, MultiNode *rightNode,
|
typedef MultiNode *(*RuleApplyFunction) (MultiNode *leftNode, MultiNode *rightNode,
|
||||||
Var *partitionColumn, JoinType joinType,
|
Var *partitionColumn, JoinType joinType,
|
||||||
|
@ -90,9 +105,9 @@ static FieldSelect * CompositeFieldRecursive(Expr *expression, Query *query);
|
||||||
static bool FullCompositeFieldList(List *compositeFieldList);
|
static bool FullCompositeFieldList(List *compositeFieldList);
|
||||||
static MultiNode * MultiPlanTree(Query *queryTree);
|
static MultiNode * MultiPlanTree(Query *queryTree);
|
||||||
static void ErrorIfQueryNotSupported(Query *queryTree);
|
static void ErrorIfQueryNotSupported(Query *queryTree);
|
||||||
static bool HasUnsupportedReferenceTableJoin(
|
static DeferredErrorMessage * DeferredErrorIfUnsupportedRecurringTuplesJoin(
|
||||||
PlannerRestrictionContext *plannerRestrictionContext);
|
PlannerRestrictionContext *plannerRestrictionContext);
|
||||||
static bool ShouldRecurseForReferenceTableJoinChecks(RelOptInfo *relOptInfo);
|
static bool ShouldRecurseForRecurringTuplesJoinChecks(RelOptInfo *relOptInfo);
|
||||||
static bool HasUnsupportedJoinWalker(Node *node, void *context);
|
static bool HasUnsupportedJoinWalker(Node *node, void *context);
|
||||||
static bool ErrorHintRequired(const char *errorHint, Query *queryTree);
|
static bool ErrorHintRequired(const char *errorHint, Query *queryTree);
|
||||||
static DeferredErrorMessage * DeferErrorIfUnsupportedSubqueryRepartition(Query *
|
static DeferredErrorMessage * DeferErrorIfUnsupportedSubqueryRepartition(Query *
|
||||||
|
@ -102,8 +117,10 @@ static bool HasOuterJoin(Query *queryTree);
|
||||||
static bool HasOuterJoinWalker(Node *node, void *maxJoinLevel);
|
static bool HasOuterJoinWalker(Node *node, void *maxJoinLevel);
|
||||||
static bool HasComplexJoinOrder(Query *queryTree);
|
static bool HasComplexJoinOrder(Query *queryTree);
|
||||||
static bool HasComplexRangeTableType(Query *queryTree);
|
static bool HasComplexRangeTableType(Query *queryTree);
|
||||||
static bool RelationInfoContainsReferenceTable(PlannerInfo *plannerInfo,
|
static bool RelationInfoContainsRecurringTuples(PlannerInfo *plannerInfo,
|
||||||
RelOptInfo *relationInfo);
|
RelOptInfo *relationInfo,
|
||||||
|
RecurringTuplesType *recurType);
|
||||||
|
static bool HasRecurringTuples(Node *node, RecurringTuplesType *recurType);
|
||||||
static void ValidateClauseList(List *clauseList);
|
static void ValidateClauseList(List *clauseList);
|
||||||
static void ValidateSubqueryPushdownClauseList(List *clauseList);
|
static void ValidateSubqueryPushdownClauseList(List *clauseList);
|
||||||
static bool ExtractFromExpressionWalker(Node *node,
|
static bool ExtractFromExpressionWalker(Node *node,
|
||||||
|
@ -564,13 +581,10 @@ DeferErrorIfUnsupportedSubqueryPushdown(Query *originalQuery,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we shouldn't allow reference tables in the outer part of outer joins */
|
/* we shouldn't allow reference tables in the outer part of outer joins */
|
||||||
if (HasUnsupportedReferenceTableJoin(plannerRestrictionContext))
|
error = DeferredErrorIfUnsupportedRecurringTuplesJoin(plannerRestrictionContext);
|
||||||
|
if (error)
|
||||||
{
|
{
|
||||||
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
return error;
|
||||||
"cannot pushdown the subquery",
|
|
||||||
"There exist a reference table in the outer part of the "
|
|
||||||
"outer join",
|
|
||||||
NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -612,7 +626,7 @@ DeferErrorIfUnsupportedSubqueryPushdown(Query *originalQuery,
|
||||||
* sublinks into joins.
|
* sublinks into joins.
|
||||||
*
|
*
|
||||||
* In some cases, sublinks are pulled up and converted into outer joins. Those cases
|
* In some cases, sublinks are pulled up and converted into outer joins. Those cases
|
||||||
* are already handled with HasUnsupportedReferenceTableJoin().
|
* are already handled with DeferredErrorIfUnsupportedRecurringTuplesJoin().
|
||||||
*
|
*
|
||||||
* If the sublinks are not pulled up, we should still error out in if any reference table
|
* If the sublinks are not pulled up, we should still error out in if any reference table
|
||||||
* appears in the FROM clause of a subquery.
|
* appears in the FROM clause of a subquery.
|
||||||
|
@ -622,19 +636,39 @@ DeferErrorIfUnsupportedSubqueryPushdown(Query *originalQuery,
|
||||||
static DeferredErrorMessage *
|
static DeferredErrorMessage *
|
||||||
DeferErrorIfUnsupportedSublinkAndReferenceTable(Query *queryTree)
|
DeferErrorIfUnsupportedSublinkAndReferenceTable(Query *queryTree)
|
||||||
{
|
{
|
||||||
|
RecurringTuplesType recurType = RECURRING_TUPLES_INVALID;
|
||||||
|
|
||||||
if (!queryTree->hasSubLinks)
|
if (!queryTree->hasSubLinks)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasReferenceTable((Node *) queryTree->rtable))
|
if (HasRecurringTuples((Node *) queryTree->rtable, &recurType))
|
||||||
{
|
{
|
||||||
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
if (recurType == RECURRING_TUPLES_REFERENCE_TABLE)
|
||||||
"cannot pushdown the subquery",
|
{
|
||||||
"Reference tables are not allowed in FROM "
|
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
||||||
"clause when the query has subqueries in "
|
"cannot pushdown the subquery",
|
||||||
"WHERE clause",
|
"Reference tables are not allowed in FROM "
|
||||||
NULL);
|
"clause when the query has subqueries in "
|
||||||
|
"WHERE clause", NULL);
|
||||||
|
}
|
||||||
|
else if (recurType == RECURRING_TUPLES_FUNCTION)
|
||||||
|
{
|
||||||
|
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
||||||
|
"cannot pushdown the subquery",
|
||||||
|
"Functions are not allowed in FROM "
|
||||||
|
"clause when the query has subqueries in "
|
||||||
|
"WHERE clause", NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
||||||
|
"cannot pushdown the subquery",
|
||||||
|
"Subqueries without FROM are not allowed in FROM "
|
||||||
|
"clause when the outer query has subqueries in "
|
||||||
|
"WHERE clause", NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -800,10 +834,12 @@ DeferErrorIfCannotPushdownSubquery(Query *subqueryTree, bool outerMostQueryHasLi
|
||||||
return deferredError;
|
return deferredError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subqueryTree->rtable == NIL)
|
if (subqueryTree->rtable == NIL &&
|
||||||
|
contain_mutable_functions((Node *) subqueryTree->targetList))
|
||||||
{
|
{
|
||||||
preconditionsSatisfied = false;
|
preconditionsSatisfied = false;
|
||||||
errorDetail = "Subqueries without relations are unsupported";
|
errorDetail = "Subqueries without a FROM clause can only contain immutable "
|
||||||
|
"functions";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subqueryTree->limitOffset)
|
if (subqueryTree->limitOffset)
|
||||||
|
@ -948,6 +984,7 @@ DeferErrorIfUnsupportedUnionQuery(Query *subqueryTree,
|
||||||
{
|
{
|
||||||
List *setOperationStatementList = NIL;
|
List *setOperationStatementList = NIL;
|
||||||
ListCell *setOperationStatmentCell = NULL;
|
ListCell *setOperationStatmentCell = NULL;
|
||||||
|
RecurringTuplesType recurType = RECURRING_TUPLES_INVALID;
|
||||||
|
|
||||||
ExtractSetOperationStatmentWalker((Node *) subqueryTree->setOperations,
|
ExtractSetOperationStatmentWalker((Node *) subqueryTree->setOperations,
|
||||||
&setOperationStatementList);
|
&setOperationStatementList);
|
||||||
|
@ -973,12 +1010,9 @@ DeferErrorIfUnsupportedUnionQuery(Query *subqueryTree,
|
||||||
leftArgRTI = ((RangeTblRef *) leftArg)->rtindex;
|
leftArgRTI = ((RangeTblRef *) leftArg)->rtindex;
|
||||||
leftArgSubquery = (Node *) rt_fetch(leftArgRTI,
|
leftArgSubquery = (Node *) rt_fetch(leftArgRTI,
|
||||||
subqueryTree->rtable)->subquery;
|
subqueryTree->rtable)->subquery;
|
||||||
if (HasReferenceTable(leftArgSubquery))
|
if (HasRecurringTuples(leftArgSubquery, &recurType))
|
||||||
{
|
{
|
||||||
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
break;
|
||||||
"cannot push down this subquery ",
|
|
||||||
"Reference tables are not supported with union"
|
|
||||||
" operator", NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -988,16 +1022,36 @@ DeferErrorIfUnsupportedUnionQuery(Query *subqueryTree,
|
||||||
rightArgRTI = ((RangeTblRef *) rightArg)->rtindex;
|
rightArgRTI = ((RangeTblRef *) rightArg)->rtindex;
|
||||||
rightArgSubquery = (Node *) rt_fetch(rightArgRTI,
|
rightArgSubquery = (Node *) rt_fetch(rightArgRTI,
|
||||||
subqueryTree->rtable)->subquery;
|
subqueryTree->rtable)->subquery;
|
||||||
if (HasReferenceTable(rightArgSubquery))
|
if (HasRecurringTuples(rightArgSubquery, &recurType))
|
||||||
{
|
{
|
||||||
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
break;
|
||||||
"cannot push down this subquery",
|
|
||||||
"Reference tables are not supported with union"
|
|
||||||
" operator", NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (recurType == RECURRING_TUPLES_REFERENCE_TABLE)
|
||||||
|
{
|
||||||
|
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
||||||
|
"cannot push down this subquery",
|
||||||
|
"Reference tables are not supported with union operator",
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
else if (recurType == RECURRING_TUPLES_FUNCTION)
|
||||||
|
{
|
||||||
|
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
||||||
|
"cannot push down this subquery",
|
||||||
|
"Table functions are not supported with union operator",
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
else if (recurType == RECURRING_TUPLES_EMPTY_JOIN_TREE)
|
||||||
|
{
|
||||||
|
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
||||||
|
"cannot push down this subquery",
|
||||||
|
"Subqueries without a FROM clause are not supported with "
|
||||||
|
"union operator", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1043,16 +1097,15 @@ DeferErrorIfUnsupportedTableCombination(Query *queryTree)
|
||||||
List *rangeTableList = queryTree->rtable;
|
List *rangeTableList = queryTree->rtable;
|
||||||
List *joinTreeTableIndexList = NIL;
|
List *joinTreeTableIndexList = NIL;
|
||||||
ListCell *joinTreeTableIndexCell = NULL;
|
ListCell *joinTreeTableIndexCell = NULL;
|
||||||
bool unsupporteTableCombination = false;
|
bool unsupportedTableCombination = false;
|
||||||
char *errorDetail = NULL;
|
char *errorDetail = NULL;
|
||||||
uint32 relationRangeTableCount = 0;
|
|
||||||
uint32 subqueryRangeTableCount = 0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extract all range table indexes from the join tree. Note that sub-queries
|
* Extract all range table indexes from the join tree. Note that sub-queries
|
||||||
* that get pulled up by PostgreSQL don't appear in this join tree.
|
* that get pulled up by PostgreSQL don't appear in this join tree.
|
||||||
*/
|
*/
|
||||||
ExtractRangeTableIndexWalker((Node *) queryTree->jointree, &joinTreeTableIndexList);
|
ExtractRangeTableIndexWalker((Node *) queryTree->jointree, &joinTreeTableIndexList);
|
||||||
|
|
||||||
foreach(joinTreeTableIndexCell, joinTreeTableIndexList)
|
foreach(joinTreeTableIndexCell, joinTreeTableIndexList)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -1066,28 +1119,50 @@ DeferErrorIfUnsupportedTableCombination(Query *queryTree)
|
||||||
(RangeTblEntry *) list_nth(rangeTableList, rangeTableListIndex);
|
(RangeTblEntry *) list_nth(rangeTableList, rangeTableListIndex);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the range table in the join tree is a simple relation or a
|
* Check if the range table in the join tree is a simple relation, a
|
||||||
* subquery.
|
* subquery, or immutable function.
|
||||||
*/
|
*/
|
||||||
if (rangeTableEntry->rtekind == RTE_RELATION)
|
if (rangeTableEntry->rtekind == RTE_RELATION ||
|
||||||
|
rangeTableEntry->rtekind == RTE_SUBQUERY)
|
||||||
{
|
{
|
||||||
relationRangeTableCount++;
|
/* accepted */
|
||||||
}
|
}
|
||||||
else if (rangeTableEntry->rtekind == RTE_SUBQUERY)
|
else if (rangeTableEntry->rtekind == RTE_FUNCTION)
|
||||||
{
|
{
|
||||||
subqueryRangeTableCount++;
|
if (contain_mutable_functions((Node *) rangeTableEntry->functions))
|
||||||
|
{
|
||||||
|
unsupportedTableCombination = true;
|
||||||
|
errorDetail = "Only immutable functions can be used as a table "
|
||||||
|
"expressions in a multi-shard query";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* immutable function RTEs are treated as reference tables */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rangeTableEntry->rtekind == RTE_CTE)
|
||||||
|
{
|
||||||
|
unsupportedTableCombination = true;
|
||||||
|
errorDetail = "CTEs in multi-shard queries are currently unsupported";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (rangeTableEntry->rtekind == RTE_VALUES)
|
||||||
|
{
|
||||||
|
unsupportedTableCombination = true;
|
||||||
|
errorDetail = "VALUES in multi-shard queries is currently unsupported";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
unsupporteTableCombination = true;
|
unsupportedTableCombination = true;
|
||||||
errorDetail = "Table expressions other than simple relations and "
|
errorDetail = "Table expressions other than relations, subqueries, "
|
||||||
"subqueries are currently unsupported";
|
"and immutable functions are currently unsupported";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* finally check and error out if not satisfied */
|
/* finally check and error out if not satisfied */
|
||||||
if (unsupporteTableCombination)
|
if (unsupportedTableCombination)
|
||||||
{
|
{
|
||||||
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
||||||
"cannot push down this subquery",
|
"cannot push down this subquery",
|
||||||
|
@ -1685,7 +1760,7 @@ MultiPlanTree(Query *queryTree)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* HasUnsupportedReferenceTableJoin returns true if there exists a outer join
|
* DeferredErrorIfUnsupportedRecurringTuplesJoin returns true if there exists a outer join
|
||||||
* between reference table and distributed tables which does not follow
|
* between reference table and distributed tables which does not follow
|
||||||
* the rules :
|
* the rules :
|
||||||
* - Reference tables can not be located in the outer part of the semi join or the
|
* - Reference tables can not be located in the outer part of the semi join or the
|
||||||
|
@ -1700,12 +1775,14 @@ MultiPlanTree(Query *queryTree)
|
||||||
* definitely have duplicate rows. Beside, reference tables can not be used
|
* definitely have duplicate rows. Beside, reference tables can not be used
|
||||||
* with full outer joins because of the same reason.
|
* with full outer joins because of the same reason.
|
||||||
*/
|
*/
|
||||||
static bool
|
static DeferredErrorMessage *
|
||||||
HasUnsupportedReferenceTableJoin(PlannerRestrictionContext *plannerRestrictionContext)
|
DeferredErrorIfUnsupportedRecurringTuplesJoin(
|
||||||
|
PlannerRestrictionContext *plannerRestrictionContext)
|
||||||
{
|
{
|
||||||
List *joinRestrictionList =
|
List *joinRestrictionList =
|
||||||
plannerRestrictionContext->joinRestrictionContext->joinRestrictionList;
|
plannerRestrictionContext->joinRestrictionContext->joinRestrictionList;
|
||||||
ListCell *joinRestrictionCell = NULL;
|
ListCell *joinRestrictionCell = NULL;
|
||||||
|
RecurringTuplesType recurType = RECURRING_TUPLES_INVALID;
|
||||||
|
|
||||||
foreach(joinRestrictionCell, joinRestrictionList)
|
foreach(joinRestrictionCell, joinRestrictionList)
|
||||||
{
|
{
|
||||||
|
@ -1718,36 +1795,62 @@ HasUnsupportedReferenceTableJoin(PlannerRestrictionContext *plannerRestrictionCo
|
||||||
|
|
||||||
if (joinType == JOIN_SEMI || joinType == JOIN_ANTI || joinType == JOIN_LEFT)
|
if (joinType == JOIN_SEMI || joinType == JOIN_ANTI || joinType == JOIN_LEFT)
|
||||||
{
|
{
|
||||||
if (ShouldRecurseForReferenceTableJoinChecks(outerrel) &&
|
if (ShouldRecurseForRecurringTuplesJoinChecks(outerrel) &&
|
||||||
RelationInfoContainsReferenceTable(plannerInfo, outerrel))
|
RelationInfoContainsRecurringTuples(plannerInfo, outerrel, &recurType))
|
||||||
{
|
{
|
||||||
return true;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (joinType == JOIN_FULL)
|
else if (joinType == JOIN_FULL)
|
||||||
{
|
{
|
||||||
if ((ShouldRecurseForReferenceTableJoinChecks(innerrel) &&
|
if ((ShouldRecurseForRecurringTuplesJoinChecks(innerrel) &&
|
||||||
RelationInfoContainsReferenceTable(plannerInfo, innerrel)) ||
|
RelationInfoContainsRecurringTuples(plannerInfo, innerrel,
|
||||||
(ShouldRecurseForReferenceTableJoinChecks(outerrel) &&
|
&recurType)) ||
|
||||||
RelationInfoContainsReferenceTable(plannerInfo, outerrel)))
|
(ShouldRecurseForRecurringTuplesJoinChecks(outerrel) &&
|
||||||
|
RelationInfoContainsRecurringTuples(plannerInfo, outerrel, &recurType)))
|
||||||
{
|
{
|
||||||
return true;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
if (recurType == RECURRING_TUPLES_REFERENCE_TABLE)
|
||||||
|
{
|
||||||
|
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
||||||
|
"cannot pushdown the subquery",
|
||||||
|
"There exist a reference table in the outer "
|
||||||
|
"part of the outer join", NULL);
|
||||||
|
}
|
||||||
|
else if (recurType == RECURRING_TUPLES_FUNCTION)
|
||||||
|
{
|
||||||
|
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
||||||
|
"cannot pushdown the subquery",
|
||||||
|
"There exist a table function in the outer "
|
||||||
|
"part of the outer join", NULL);
|
||||||
|
}
|
||||||
|
else if (recurType == RECURRING_TUPLES_EMPTY_JOIN_TREE)
|
||||||
|
{
|
||||||
|
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
|
||||||
|
"cannot pushdown the subquery",
|
||||||
|
"There exist a subquery without FROM in the outer "
|
||||||
|
"part of the outer join", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ShouldRecurseForReferenceTableJoinChecks is a helper function for deciding
|
* ShouldRecurseForRecurringTuplesJoinChecks is a helper function for deciding
|
||||||
* on whether the input relOptInfo should be checked for unsupported reference
|
* on whether the input relOptInfo should be checked for table expressions that
|
||||||
* tables.
|
* generate the same tuples in every query on a shard. We use this to avoid
|
||||||
|
* redundant checks and false positives in complex join trees.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
ShouldRecurseForReferenceTableJoinChecks(RelOptInfo *relOptInfo)
|
ShouldRecurseForRecurringTuplesJoinChecks(RelOptInfo *relOptInfo)
|
||||||
{
|
{
|
||||||
|
bool shouldRecurse = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We shouldn't recursively go down for joins since we're already
|
* We shouldn't recursively go down for joins since we're already
|
||||||
* going to process each join seperately. Otherwise we'd restrict
|
* going to process each join seperately. Otherwise we'd restrict
|
||||||
|
@ -1784,23 +1887,39 @@ ShouldRecurseForReferenceTableJoinChecks(RelOptInfo *relOptInfo)
|
||||||
|
|
||||||
if (list_length(subroot->join_rel_list) > 0)
|
if (list_length(subroot->join_rel_list) > 0)
|
||||||
{
|
{
|
||||||
return false;
|
RelOptInfo *subqueryJoin = linitial(subroot->join_rel_list);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Subqueries without relations (e.g. SELECT 1) are a little funny.
|
||||||
|
* They are treated as having a join, but the join is between 0
|
||||||
|
* relations and won't be in the join restriction list and therefore
|
||||||
|
* won't be revisited in DeferredErrorIfUnsupportedRecurringTuplesJoin.
|
||||||
|
*
|
||||||
|
* We therefore only skip joins with >0 relations.
|
||||||
|
*/
|
||||||
|
if (bms_num_members(subqueryJoin->relids) > 0)
|
||||||
|
{
|
||||||
|
shouldRecurse = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return shouldRecurse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* RelationInfoContainsReferenceTable checks whether the relationInfo
|
* RelationInfoContainsRecurringTuples checks whether the relationInfo
|
||||||
* contains any reference tables. If found, the function returns true.
|
* contains any recurring table expression, namely a reference table,
|
||||||
|
* or immutable function. If found, RelationInfoContainsRecurringTuples
|
||||||
|
* returns true.
|
||||||
*
|
*
|
||||||
* Note that since relation ids of relationInfo indexes to the range
|
* Note that since relation ids of relationInfo indexes to the range
|
||||||
* table entry list of planner info, planner info is also passed.
|
* table entry list of planner info, planner info is also passed.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
RelationInfoContainsReferenceTable(PlannerInfo *plannerInfo, RelOptInfo *relationInfo)
|
RelationInfoContainsRecurringTuples(PlannerInfo *plannerInfo, RelOptInfo *relationInfo,
|
||||||
|
RecurringTuplesType *recurType)
|
||||||
{
|
{
|
||||||
Relids relids = bms_copy(relationInfo->relids);
|
Relids relids = bms_copy(relationInfo->relids);
|
||||||
int relationId = -1;
|
int relationId = -1;
|
||||||
|
@ -1810,7 +1929,7 @@ RelationInfoContainsReferenceTable(PlannerInfo *plannerInfo, RelOptInfo *relatio
|
||||||
RangeTblEntry *rangeTableEntry = plannerInfo->simple_rte_array[relationId];
|
RangeTblEntry *rangeTableEntry = plannerInfo->simple_rte_array[relationId];
|
||||||
|
|
||||||
/* relationInfo has this range table entry */
|
/* relationInfo has this range table entry */
|
||||||
if (HasReferenceTable((Node *) rangeTableEntry))
|
if (HasRecurringTuples((Node *) rangeTableEntry, recurType))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1821,29 +1940,71 @@ RelationInfoContainsReferenceTable(PlannerInfo *plannerInfo, RelOptInfo *relatio
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* HasReferenceTable checks whether there exist a reference table in the
|
* HasRecurringTuples returns whether any part of the expression will generate
|
||||||
* given node.
|
* the same set of tuples in every query on shards when executing a distributed
|
||||||
|
* query.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
HasReferenceTable(Node *node)
|
HasRecurringTuples(Node *node, RecurringTuplesType *recurType)
|
||||||
{
|
{
|
||||||
List *relationList = NIL;
|
if (node == NULL)
|
||||||
ListCell *relationCell = NULL;
|
|
||||||
ExtractRangeTableRelationWalkerWithRTEExpand(node, &relationList);
|
|
||||||
|
|
||||||
foreach(relationCell, relationList)
|
|
||||||
{
|
{
|
||||||
RangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(relationCell);
|
return false;
|
||||||
Oid relationId = rangeTableEntry->relid;
|
|
||||||
|
|
||||||
if (IsDistributedTable(relationId) && PartitionMethod(relationId) ==
|
|
||||||
DISTRIBUTE_BY_NONE)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
if (IsA(node, RangeTblEntry))
|
||||||
|
{
|
||||||
|
RangeTblEntry *rangeTableEntry = (RangeTblEntry *) node;
|
||||||
|
|
||||||
|
if (rangeTableEntry->rtekind == RTE_RELATION)
|
||||||
|
{
|
||||||
|
Oid relationId = rangeTableEntry->relid;
|
||||||
|
if (IsDistributedTable(relationId) &&
|
||||||
|
PartitionMethod(relationId) == DISTRIBUTE_BY_NONE)
|
||||||
|
{
|
||||||
|
*recurType = RECURRING_TUPLES_REFERENCE_TABLE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tuples from reference tables will recur in every query on shards
|
||||||
|
* that includes it.
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rangeTableEntry->rtekind == RTE_FUNCTION)
|
||||||
|
{
|
||||||
|
*recurType = RECURRING_TUPLES_FUNCTION;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tuples from functions will recur in every query on shards that includes
|
||||||
|
* it.
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return range_table_walker(list_make1(rangeTableEntry), HasRecurringTuples,
|
||||||
|
recurType, 0);
|
||||||
|
}
|
||||||
|
else if (IsA(node, Query))
|
||||||
|
{
|
||||||
|
Query *query = (Query *) node;
|
||||||
|
|
||||||
|
if (query->rtable == NIL)
|
||||||
|
{
|
||||||
|
*recurType = RECURRING_TUPLES_EMPTY_JOIN_TREE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Queries with empty join trees will recur in every query on shards
|
||||||
|
* that includes it.
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return query_tree_walker((Query *) node, HasRecurringTuples,
|
||||||
|
recurType, QTW_EXAMINE_RTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
return expression_tree_walker(node, HasRecurringTuples, recurType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3212,54 +3373,6 @@ ExtractRangeTableRelationWalker(Node *node, List **rangeTableRelationList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ExtractRangeTableRelationWalkerWithRTEExpand obtains the list of relations
|
|
||||||
* from the given node. Note that the difference between this function and
|
|
||||||
* ExtractRangeTableRelationWalker is that this one recursively
|
|
||||||
* walk into range table entries if it can.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
ExtractRangeTableRelationWalkerWithRTEExpand(Node *node, List **rangeTableRelationList)
|
|
||||||
{
|
|
||||||
bool walkIsComplete = false;
|
|
||||||
|
|
||||||
if (node == NULL)
|
|
||||||
{
|
|
||||||
return walkIsComplete;
|
|
||||||
}
|
|
||||||
else if (IsA(node, RangeTblEntry))
|
|
||||||
{
|
|
||||||
RangeTblEntry *rangeTableEntry = (RangeTblEntry *) node;
|
|
||||||
List *rangeTableList = list_make1(rangeTableEntry);
|
|
||||||
|
|
||||||
if (rangeTableEntry->rtekind == RTE_RELATION)
|
|
||||||
{
|
|
||||||
(*rangeTableRelationList) = lappend(*rangeTableRelationList, rangeTableEntry);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
walkIsComplete = range_table_walker(rangeTableList,
|
|
||||||
ExtractRangeTableRelationWalkerWithRTEExpand,
|
|
||||||
rangeTableRelationList, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (IsA(node, Query))
|
|
||||||
{
|
|
||||||
walkIsComplete = query_tree_walker((Query *) node,
|
|
||||||
ExtractRangeTableRelationWalkerWithRTEExpand,
|
|
||||||
rangeTableRelationList, QTW_EXAMINE_RTES);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
walkIsComplete = expression_tree_walker(node,
|
|
||||||
ExtractRangeTableRelationWalkerWithRTEExpand,
|
|
||||||
rangeTableRelationList);
|
|
||||||
}
|
|
||||||
|
|
||||||
return walkIsComplete;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ExtractRangeTableEntryWalker walks over a query tree, and finds all range
|
* ExtractRangeTableEntryWalker walks over a query tree, and finds all range
|
||||||
* table entries. For recursing into the query tree, this function uses the
|
* table entries. For recursing into the query tree, this function uses the
|
||||||
|
|
|
@ -1314,6 +1314,12 @@ ContainsUnionSubquery(Query *queryTree)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* subquery without FROM */
|
||||||
|
if (joiningRangeTableCount == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
subqueryRteIndex = linitial_int(joinTreeTableIndexList);
|
subqueryRteIndex = linitial_int(joinTreeTableIndexList);
|
||||||
rangeTableEntry = rt_fetch(subqueryRteIndex, rangeTableList);
|
rangeTableEntry = rt_fetch(subqueryRteIndex, rangeTableList);
|
||||||
if (rangeTableEntry->rtekind != RTE_SUBQUERY)
|
if (rangeTableEntry->rtekind != RTE_SUBQUERY)
|
||||||
|
|
|
@ -210,8 +210,6 @@ extern List * TableEntryList(List *rangeTableList);
|
||||||
extern List * UsedTableEntryList(Query *query);
|
extern List * UsedTableEntryList(Query *query);
|
||||||
extern bool ExtractRangeTableRelationWalker(Node *node, List **rangeTableList);
|
extern bool ExtractRangeTableRelationWalker(Node *node, List **rangeTableList);
|
||||||
extern bool ExtractRangeTableEntryWalker(Node *node, List **rangeTableList);
|
extern bool ExtractRangeTableEntryWalker(Node *node, List **rangeTableList);
|
||||||
extern bool ExtractRangeTableRelationWalkerWithRTEExpand(Node *node,
|
|
||||||
List **rangeTableList);
|
|
||||||
extern List * pull_var_clause_default(Node *node);
|
extern List * pull_var_clause_default(Node *node);
|
||||||
extern bool OperatorImplementsEquality(Oid opno);
|
extern bool OperatorImplementsEquality(Oid opno);
|
||||||
|
|
||||||
|
|
|
@ -287,7 +287,6 @@ extern Const * MakeInt4Constant(Datum constantValue);
|
||||||
extern int CompareShardPlacements(const void *leftElement, const void *rightElement);
|
extern int CompareShardPlacements(const void *leftElement, const void *rightElement);
|
||||||
extern bool ShardIntervalsOverlap(ShardInterval *firstInterval,
|
extern bool ShardIntervalsOverlap(ShardInterval *firstInterval,
|
||||||
ShardInterval *secondInterval);
|
ShardInterval *secondInterval);
|
||||||
extern bool HasReferenceTable(Node *node);
|
|
||||||
|
|
||||||
/* function declarations for Task and Task list operations */
|
/* function declarations for Task and Task list operations */
|
||||||
extern bool TasksEqual(const Task *a, const Task *b);
|
extern bool TasksEqual(const Task *a, const Task *b);
|
||||||
|
|
|
@ -694,7 +694,7 @@ FROM (
|
||||||
users_table.value_1 > 10 AND users_table.value_1 < 12
|
users_table.value_1 > 10 AND users_table.value_1 < 12
|
||||||
) u LEFT JOIN LATERAL (
|
) u LEFT JOIN LATERAL (
|
||||||
SELECT event_type, time
|
SELECT event_type, time
|
||||||
FROM events_table, (SELECT 1 as x) as f
|
FROM events_table, (SELECT random()::int as x) as f
|
||||||
WHERE user_id = u.user_id AND
|
WHERE user_id = u.user_id AND
|
||||||
events_table.event_type > 10 AND events_table.event_type < 12
|
events_table.event_type > 10 AND events_table.event_type < 12
|
||||||
) t ON true
|
) t ON true
|
||||||
|
@ -702,4 +702,4 @@ FROM (
|
||||||
) AS shard_union
|
) AS shard_union
|
||||||
ORDER BY user_lastseen DESC;
|
ORDER BY user_lastseen DESC;
|
||||||
ERROR: cannot push down this subquery
|
ERROR: cannot push down this subquery
|
||||||
DETAIL: Subqueries without relations are unsupported
|
DETAIL: Subqueries without a FROM clause can only contain immutable functions
|
||||||
|
|
|
@ -71,6 +71,12 @@ FROM
|
||||||
l_orderkey) AS unit_prices;
|
l_orderkey) AS unit_prices;
|
||||||
ERROR: cannot pushdown the subquery since all relations are not joined using distribution keys
|
ERROR: cannot pushdown the subquery since all relations are not joined using distribution keys
|
||||||
DETAIL: Each relation should be joined with at least one another relation using distribution keys and equality operator.
|
DETAIL: Each relation should be joined with at least one another relation using distribution keys and equality operator.
|
||||||
|
-- Subqueries without relation with a volatile functions (non-constant)
|
||||||
|
SELECT count(*) FROM (
|
||||||
|
SELECT l_orderkey FROM lineitem_subquery JOIN (SELECT random()::int r) sub ON (l_orderkey = r)
|
||||||
|
) b;
|
||||||
|
ERROR: cannot push down this subquery
|
||||||
|
DETAIL: Subqueries without a FROM clause can only contain immutable functions
|
||||||
-- Check that we error out if there is non relation subqueries
|
-- Check that we error out if there is non relation subqueries
|
||||||
SELECT count(*) FROM
|
SELECT count(*) FROM
|
||||||
(
|
(
|
||||||
|
@ -78,7 +84,7 @@ SELECT count(*) FROM
|
||||||
(SELECT 1::bigint)
|
(SELECT 1::bigint)
|
||||||
) b;
|
) b;
|
||||||
ERROR: cannot push down this subquery
|
ERROR: cannot push down this subquery
|
||||||
DETAIL: Subqueries without relations are unsupported
|
DETAIL: Subqueries without a FROM clause are not supported with union operator
|
||||||
-- Check that we error out if queries in union do not include partition columns.
|
-- Check that we error out if queries in union do not include partition columns.
|
||||||
SELECT count(*) FROM
|
SELECT count(*) FROM
|
||||||
(
|
(
|
||||||
|
|
|
@ -1803,7 +1803,7 @@ ORDER BY 1,2;
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
DROP FUNCTION array_index(ANYARRAY, ANYELEMENT);
|
DROP FUNCTION array_index(ANYARRAY, ANYELEMENT);
|
||||||
-- a not supported query due to constant range table entry
|
-- a query with a constant subquery
|
||||||
SELECT count(*) as subquery_count
|
SELECT count(*) as subquery_count
|
||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -1822,9 +1822,12 @@ FROM (
|
||||||
ON a.user_id = b.user_id
|
ON a.user_id = b.user_id
|
||||||
WHERE b.user_id IS NULL
|
WHERE b.user_id IS NULL
|
||||||
GROUP BY a.user_id;
|
GROUP BY a.user_id;
|
||||||
ERROR: cannot push down this subquery
|
subquery_count
|
||||||
DETAIL: Subqueries without relations are unsupported
|
----------------
|
||||||
-- same with INNER JOIN
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- volatile function in the subquery
|
||||||
SELECT count(*) as subquery_count
|
SELECT count(*) as subquery_count
|
||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -1838,13 +1841,13 @@ FROM (
|
||||||
) as a
|
) as a
|
||||||
INNER JOIN (
|
INNER JOIN (
|
||||||
SELECT
|
SELECT
|
||||||
1 as user_id
|
random()::int as user_id
|
||||||
) AS b
|
) AS b
|
||||||
ON a.user_id = b.user_id
|
ON a.user_id = b.user_id
|
||||||
WHERE b.user_id IS NULL
|
WHERE b.user_id IS NULL
|
||||||
GROUP BY a.user_id;
|
GROUP BY a.user_id;
|
||||||
ERROR: cannot push down this subquery
|
ERROR: cannot push down this subquery
|
||||||
DETAIL: Subqueries without relations are unsupported
|
DETAIL: Subqueries without a FROM clause can only contain immutable functions
|
||||||
-- this is slightly different, we use RTE_VALUEs here
|
-- this is slightly different, we use RTE_VALUEs here
|
||||||
SELECT Count(*) AS subquery_count
|
SELECT Count(*) AS subquery_count
|
||||||
FROM (SELECT
|
FROM (SELECT
|
||||||
|
@ -1866,7 +1869,7 @@ FROM (SELECT
|
||||||
WHERE b.user_id IS NULL
|
WHERE b.user_id IS NULL
|
||||||
GROUP BY a.user_id;
|
GROUP BY a.user_id;
|
||||||
ERROR: cannot push down this subquery
|
ERROR: cannot push down this subquery
|
||||||
DETAIL: Table expressions other than simple relations and subqueries are currently unsupported
|
DETAIL: VALUES in multi-shard queries is currently unsupported
|
||||||
-- same query without LIMIT/OFFSET returns 30 rows
|
-- same query without LIMIT/OFFSET returns 30 rows
|
||||||
SET client_min_messages TO DEBUG1;
|
SET client_min_messages TO DEBUG1;
|
||||||
-- now, lets use a simple expression on the LIMIT and explicit coercion on the OFFSET
|
-- now, lets use a simple expression on the LIMIT and explicit coercion on the OFFSET
|
||||||
|
|
|
@ -2440,7 +2440,7 @@ GROUP BY
|
||||||
ORDER BY
|
ORDER BY
|
||||||
types;
|
types;
|
||||||
ERROR: cannot push down this subquery
|
ERROR: cannot push down this subquery
|
||||||
DETAIL: Subqueries without relations are unsupported
|
DETAIL: Subqueries without a FROM clause are not supported with union operator
|
||||||
-- similar to the above, but constant rte is on the right side of the query
|
-- similar to the above, but constant rte is on the right side of the query
|
||||||
SELECT ("final_query"."event_types") as types, count(*) AS sumOfEventType
|
SELECT ("final_query"."event_types") as types, count(*) AS sumOfEventType
|
||||||
FROM
|
FROM
|
||||||
|
@ -2495,5 +2495,5 @@ GROUP BY
|
||||||
ORDER BY
|
ORDER BY
|
||||||
types;
|
types;
|
||||||
ERROR: cannot push down this subquery
|
ERROR: cannot push down this subquery
|
||||||
DETAIL: Subqueries without relations are unsupported
|
DETAIL: Subqueries without a FROM clause are not supported with union operator
|
||||||
SET citus.enable_router_execution TO TRUE;
|
SET citus.enable_router_execution TO TRUE;
|
||||||
|
|
|
@ -228,6 +228,91 @@ FROM
|
||||||
97 | 71002659
|
97 | 71002659
|
||||||
(10 rows)
|
(10 rows)
|
||||||
|
|
||||||
|
-- table function can be the inner relationship in a join
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT random() FROM user_buy_test_table JOIN generate_series(1,10) AS users_ref_test_table(id)
|
||||||
|
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
10
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- table function cannot be used without subquery pushdown
|
||||||
|
SELECT count(*) FROM user_buy_test_table JOIN generate_series(1,10) AS users_ref_test_table(id)
|
||||||
|
ON user_buy_test_table.item_id = users_ref_test_table.id;
|
||||||
|
ERROR: could not run distributed query with complex table expressions
|
||||||
|
HINT: Consider using an equality filter on the distributed table's partition column.
|
||||||
|
-- table function can be the inner relationship in an outer join
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT random() FROM user_buy_test_table LEFT JOIN generate_series(1,10) AS users_ref_test_table(id)
|
||||||
|
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
10
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- table function cannot be the outer relationship in an outer join
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT random() FROM user_buy_test_table RIGHT JOIN generate_series(1,10) AS users_ref_test_table(id)
|
||||||
|
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
|
||||||
|
ERROR: cannot pushdown the subquery
|
||||||
|
DETAIL: There exist a table function in the outer part of the outer join
|
||||||
|
-- volatile functions cannot be used as table expressions
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT random() FROM user_buy_test_table JOIN random() AS users_ref_test_table(id)
|
||||||
|
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
|
||||||
|
ERROR: cannot push down this subquery
|
||||||
|
DETAIL: Only immutable functions can be used as a table expressions in a multi-shard query
|
||||||
|
-- cannot sneak in a volatile function as a parameter
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT random() FROM user_buy_test_table JOIN generate_series(random()::int,10) AS users_ref_test_table(id)
|
||||||
|
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
|
||||||
|
ERROR: cannot push down this subquery
|
||||||
|
DETAIL: Only immutable functions can be used as a table expressions in a multi-shard query
|
||||||
|
-- cannot perform a union with table function
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT user_id FROM user_buy_test_table
|
||||||
|
UNION ALL
|
||||||
|
SELECT id FROM generate_series(1,10) AS users_ref_test_table(id)) subquery_1;
|
||||||
|
ERROR: cannot push down this subquery
|
||||||
|
DETAIL: Table functions are not supported with union operator
|
||||||
|
-- subquery without FROM can be the inner relationship in a join
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT random() FROM user_buy_test_table JOIN (SELECT 4 AS id) users_ref_test_table
|
||||||
|
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- subquery without FROM triggers subquery pushdown
|
||||||
|
SELECT count(*) FROM user_buy_test_table JOIN (SELECT 5 AS id) users_ref_test_table
|
||||||
|
ON user_buy_test_table.item_id = users_ref_test_table.id;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- subquery without FROM can be the inner relationship in an outer join
|
||||||
|
SELECT count(*) FROM user_buy_test_table LEFT JOIN (SELECT 5 AS id) users_ref_test_table
|
||||||
|
ON user_buy_test_table.item_id = users_ref_test_table.id;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- subquery without FROM cannot be the outer relationship in an outer join
|
||||||
|
SELECT count(*) FROM user_buy_test_table RIGHT JOIN (SELECT 5 AS id) users_ref_test_table
|
||||||
|
ON user_buy_test_table.item_id = users_ref_test_table.id;
|
||||||
|
ERROR: cannot pushdown the subquery
|
||||||
|
DETAIL: There exist a subquery without FROM in the outer part of the outer join
|
||||||
|
-- cannot perform a union with subquery without FROM
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT user_id FROM user_buy_test_table
|
||||||
|
UNION ALL
|
||||||
|
SELECT id FROM (SELECT 5 AS id) users_ref_test_table) subquery_1;
|
||||||
|
ERROR: cannot push down this subquery
|
||||||
|
DETAIL: Subqueries without a FROM clause are not supported with union operator
|
||||||
-- should be able to pushdown since reference table is in the
|
-- should be able to pushdown since reference table is in the
|
||||||
-- inner part of the left join
|
-- inner part of the left join
|
||||||
SELECT
|
SELECT
|
||||||
|
|
|
@ -499,16 +499,34 @@ FROM (
|
||||||
ORDER BY 2 DESC, 1;
|
ORDER BY 2 DESC, 1;
|
||||||
ERROR: cannot pushdown the subquery since all relations are not joined using distribution keys
|
ERROR: cannot pushdown the subquery since all relations are not joined using distribution keys
|
||||||
DETAIL: Each relation should be joined with at least one another relation using distribution keys and equality operator.
|
DETAIL: Each relation should be joined with at least one another relation using distribution keys and equality operator.
|
||||||
-- subquery in where clause doesn't have a relation
|
-- subquery in where clause doesn't have a relation, but is constant
|
||||||
SELECT
|
SELECT
|
||||||
user_id
|
user_id
|
||||||
FROM
|
FROM
|
||||||
users_table
|
users_table
|
||||||
WHERE
|
WHERE
|
||||||
value_2 >
|
value_2 >
|
||||||
(SELECT 1);
|
(SELECT 1)
|
||||||
|
ORDER BY 1 ASC
|
||||||
|
LIMIT 2;
|
||||||
|
user_id
|
||||||
|
---------
|
||||||
|
0
|
||||||
|
0
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- subquery in where clause has a volatile function and no relation
|
||||||
|
SELECT
|
||||||
|
user_id
|
||||||
|
FROM
|
||||||
|
users_table
|
||||||
|
WHERE
|
||||||
|
value_2 >
|
||||||
|
(SELECT random())
|
||||||
|
ORDER BY 1 ASC
|
||||||
|
LIMIT 2;
|
||||||
ERROR: cannot push down this subquery
|
ERROR: cannot push down this subquery
|
||||||
DETAIL: Subqueries without relations are unsupported
|
DETAIL: Subqueries without a FROM clause can only contain immutable functions
|
||||||
-- OFFSET is not supported in the subquey
|
-- OFFSET is not supported in the subquey
|
||||||
SELECT
|
SELECT
|
||||||
user_id
|
user_id
|
||||||
|
|
|
@ -64,6 +64,40 @@ WHERE
|
||||||
LIMIT 3;
|
LIMIT 3;
|
||||||
ERROR: cannot pushdown the subquery
|
ERROR: cannot pushdown the subquery
|
||||||
DETAIL: Reference tables are not allowed in FROM clause when the query has subqueries in WHERE clause
|
DETAIL: Reference tables are not allowed in FROM clause when the query has subqueries in WHERE clause
|
||||||
|
-- immutable functions are also treated as reference tables
|
||||||
|
SELECT
|
||||||
|
user_id
|
||||||
|
FROM
|
||||||
|
(SELECT user_id FROM generate_series(1,10) AS series(user_id)) users_reference_table
|
||||||
|
WHERE
|
||||||
|
NOT EXISTS
|
||||||
|
(SELECT
|
||||||
|
value_2
|
||||||
|
FROM
|
||||||
|
events_table
|
||||||
|
WHERE
|
||||||
|
users_reference_table.user_id = events_table.user_id
|
||||||
|
)
|
||||||
|
LIMIT 3;
|
||||||
|
ERROR: cannot pushdown the subquery
|
||||||
|
DETAIL: Functions are not allowed in FROM clause when the query has subqueries in WHERE clause
|
||||||
|
-- subqueries without FROM are also treated as reference tables
|
||||||
|
SELECT
|
||||||
|
user_id
|
||||||
|
FROM
|
||||||
|
(SELECT 5 AS user_id) users_reference_table
|
||||||
|
WHERE
|
||||||
|
NOT EXISTS
|
||||||
|
(SELECT
|
||||||
|
value_2
|
||||||
|
FROM
|
||||||
|
events_table
|
||||||
|
WHERE
|
||||||
|
users_reference_table.user_id = events_table.user_id
|
||||||
|
)
|
||||||
|
LIMIT 3;
|
||||||
|
ERROR: cannot pushdown the subquery
|
||||||
|
DETAIL: Subqueries without FROM are not allowed in FROM clause when the outer query has subqueries in WHERE clause
|
||||||
-- subqueries in WHERE with IN operator without equality
|
-- subqueries in WHERE with IN operator without equality
|
||||||
SELECT
|
SELECT
|
||||||
users_table.user_id, count(*)
|
users_table.user_id, count(*)
|
||||||
|
@ -88,6 +122,54 @@ LIMIT 3;
|
||||||
46 | 115
|
46 | 115
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
-- immutable functions are also treated as reference tables
|
||||||
|
SELECT
|
||||||
|
users_table.user_id, count(*)
|
||||||
|
FROM
|
||||||
|
users_table
|
||||||
|
WHERE
|
||||||
|
value_2 IN
|
||||||
|
(SELECT
|
||||||
|
value_2
|
||||||
|
FROM
|
||||||
|
generate_series(1,10) AS events_reference_table(user_id)
|
||||||
|
WHERE
|
||||||
|
users_table.user_id > events_reference_table.user_id
|
||||||
|
)
|
||||||
|
GROUP BY users_table.user_id
|
||||||
|
ORDER BY 2 DESC, 1 DESC
|
||||||
|
LIMIT 3;
|
||||||
|
user_id | count
|
||||||
|
---------+-------
|
||||||
|
12 | 121
|
||||||
|
87 | 117
|
||||||
|
59 | 115
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
-- immutable functions are also treated as reference tables
|
||||||
|
SELECT
|
||||||
|
users_table.user_id, count(*)
|
||||||
|
FROM
|
||||||
|
users_table
|
||||||
|
WHERE
|
||||||
|
value_2 IN
|
||||||
|
(SELECT
|
||||||
|
value_2
|
||||||
|
FROM
|
||||||
|
(SELECT 5 AS user_id) AS events_reference_table
|
||||||
|
WHERE
|
||||||
|
users_table.user_id > events_reference_table.user_id
|
||||||
|
)
|
||||||
|
GROUP BY users_table.user_id
|
||||||
|
ORDER BY 2 DESC, 1 DESC
|
||||||
|
LIMIT 3;
|
||||||
|
user_id | count
|
||||||
|
---------+-------
|
||||||
|
12 | 121
|
||||||
|
87 | 117
|
||||||
|
59 | 115
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
-- should error out since reference table exist on the left side
|
-- should error out since reference table exist on the left side
|
||||||
-- of the left lateral join
|
-- of the left lateral join
|
||||||
SELECT user_id, value_2 FROM users_table WHERE
|
SELECT user_id, value_2 FROM users_table WHERE
|
||||||
|
|
|
@ -914,7 +914,7 @@ FROM
|
||||||
(SELECT 1)
|
(SELECT 1)
|
||||||
) b;
|
) b;
|
||||||
ERROR: cannot push down this subquery
|
ERROR: cannot push down this subquery
|
||||||
DETAIL: Subqueries without relations are unsupported
|
DETAIL: Subqueries without a FROM clause are not supported with union operator
|
||||||
-- we don't support subqueries without relations
|
-- we don't support subqueries without relations
|
||||||
SELECT
|
SELECT
|
||||||
*
|
*
|
||||||
|
@ -925,7 +925,7 @@ FROM
|
||||||
(SELECT (random() * 100)::int)
|
(SELECT (random() * 100)::int)
|
||||||
) b;
|
) b;
|
||||||
ERROR: cannot push down this subquery
|
ERROR: cannot push down this subquery
|
||||||
DETAIL: Subqueries without relations are unsupported
|
DETAIL: Subqueries without a FROM clause are not supported with union operator
|
||||||
-- we don't support subqueries without relations
|
-- we don't support subqueries without relations
|
||||||
SELECT
|
SELECT
|
||||||
user_id, value_3
|
user_id, value_3
|
||||||
|
@ -946,7 +946,7 @@ FROM
|
||||||
ORDER BY 1 DESC, 2 DESC
|
ORDER BY 1 DESC, 2 DESC
|
||||||
LIMIT 5;
|
LIMIT 5;
|
||||||
ERROR: cannot push down this subquery
|
ERROR: cannot push down this subquery
|
||||||
DETAIL: Subqueries without relations are unsupported
|
DETAIL: Subqueries without a FROM clause are not supported with union operator
|
||||||
SELECT ("final_query"."event_types") as types, count(*) AS sumOfEventType
|
SELECT ("final_query"."event_types") as types, count(*) AS sumOfEventType
|
||||||
FROM
|
FROM
|
||||||
( SELECT *, random()
|
( SELECT *, random()
|
||||||
|
@ -990,7 +990,7 @@ FROM
|
||||||
GROUP BY types
|
GROUP BY types
|
||||||
ORDER BY types;
|
ORDER BY types;
|
||||||
ERROR: cannot push down this subquery
|
ERROR: cannot push down this subquery
|
||||||
DETAIL: Subqueries without relations are unsupported
|
DETAIL: Subqueries without a FROM clause are not supported with union operator
|
||||||
SET citus.enable_router_execution TO true;
|
SET citus.enable_router_execution TO true;
|
||||||
DROP TABLE events_reference_table;
|
DROP TABLE events_reference_table;
|
||||||
DROP TABLE users_reference_table;
|
DROP TABLE users_reference_table;
|
||||||
|
|
|
@ -715,12 +715,12 @@ CREATE VIEW cte_view_1 AS
|
||||||
WITH c1 AS (SELECT * FROM users_table WHERE value_1 = 15) SELECT * FROM c1 WHERE value_2 < 500;
|
WITH c1 AS (SELECT * FROM users_table WHERE value_1 = 15) SELECT * FROM c1 WHERE value_2 < 500;
|
||||||
SELECT * FROM cte_view_1;
|
SELECT * FROM cte_view_1;
|
||||||
ERROR: cannot push down this subquery
|
ERROR: cannot push down this subquery
|
||||||
DETAIL: Table expressions other than simple relations and subqueries are currently unsupported
|
DETAIL: CTEs in multi-shard queries are currently unsupported
|
||||||
-- this is single shard query but still not supported since it has view + cte
|
-- this is single shard query but still not supported since it has view + cte
|
||||||
-- router planner can't detect it
|
-- router planner can't detect it
|
||||||
SELECT * FROM cte_view_1 WHERE user_id = 8;
|
SELECT * FROM cte_view_1 WHERE user_id = 8;
|
||||||
ERROR: cannot push down this subquery
|
ERROR: cannot push down this subquery
|
||||||
DETAIL: Table expressions other than simple relations and subqueries are currently unsupported
|
DETAIL: CTEs in multi-shard queries are currently unsupported
|
||||||
-- if CTE itself prunes down to a single shard than the view is supported (router plannable)
|
-- if CTE itself prunes down to a single shard than the view is supported (router plannable)
|
||||||
CREATE VIEW cte_view_2 AS
|
CREATE VIEW cte_view_2 AS
|
||||||
WITH c1 AS (SELECT * FROM users_table WHERE user_id = 8) SELECT * FROM c1 WHERE value_1 = 15;
|
WITH c1 AS (SELECT * FROM users_table WHERE user_id = 8) SELECT * FROM c1 WHERE value_1 = 15;
|
||||||
|
|
|
@ -681,7 +681,7 @@ FROM (
|
||||||
users_table.value_1 > 10 AND users_table.value_1 < 12
|
users_table.value_1 > 10 AND users_table.value_1 < 12
|
||||||
) u LEFT JOIN LATERAL (
|
) u LEFT JOIN LATERAL (
|
||||||
SELECT event_type, time
|
SELECT event_type, time
|
||||||
FROM events_table, (SELECT 1 as x) as f
|
FROM events_table, (SELECT random()::int as x) as f
|
||||||
WHERE user_id = u.user_id AND
|
WHERE user_id = u.user_id AND
|
||||||
events_table.event_type > 10 AND events_table.event_type < 12
|
events_table.event_type > 10 AND events_table.event_type < 12
|
||||||
) t ON true
|
) t ON true
|
||||||
|
|
|
@ -70,6 +70,11 @@ FROM
|
||||||
GROUP BY
|
GROUP BY
|
||||||
l_orderkey) AS unit_prices;
|
l_orderkey) AS unit_prices;
|
||||||
|
|
||||||
|
-- Subqueries without relation with a volatile functions (non-constant)
|
||||||
|
SELECT count(*) FROM (
|
||||||
|
SELECT l_orderkey FROM lineitem_subquery JOIN (SELECT random()::int r) sub ON (l_orderkey = r)
|
||||||
|
) b;
|
||||||
|
|
||||||
-- Check that we error out if there is non relation subqueries
|
-- Check that we error out if there is non relation subqueries
|
||||||
SELECT count(*) FROM
|
SELECT count(*) FROM
|
||||||
(
|
(
|
||||||
|
|
|
@ -1432,7 +1432,7 @@ SELECT * FROM run_command_on_workers('DROP FUNCTION array_index(ANYARRAY, ANYELE
|
||||||
ORDER BY 1,2;
|
ORDER BY 1,2;
|
||||||
DROP FUNCTION array_index(ANYARRAY, ANYELEMENT);
|
DROP FUNCTION array_index(ANYARRAY, ANYELEMENT);
|
||||||
|
|
||||||
-- a not supported query due to constant range table entry
|
-- a query with a constant subquery
|
||||||
SELECT count(*) as subquery_count
|
SELECT count(*) as subquery_count
|
||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -1452,7 +1452,7 @@ FROM (
|
||||||
WHERE b.user_id IS NULL
|
WHERE b.user_id IS NULL
|
||||||
GROUP BY a.user_id;
|
GROUP BY a.user_id;
|
||||||
|
|
||||||
-- same with INNER JOIN
|
-- volatile function in the subquery
|
||||||
SELECT count(*) as subquery_count
|
SELECT count(*) as subquery_count
|
||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -1466,7 +1466,7 @@ FROM (
|
||||||
) as a
|
) as a
|
||||||
INNER JOIN (
|
INNER JOIN (
|
||||||
SELECT
|
SELECT
|
||||||
1 as user_id
|
random()::int as user_id
|
||||||
) AS b
|
) AS b
|
||||||
ON a.user_id = b.user_id
|
ON a.user_id = b.user_id
|
||||||
WHERE b.user_id IS NULL
|
WHERE b.user_id IS NULL
|
||||||
|
|
|
@ -136,6 +136,64 @@ FROM
|
||||||
) as foo
|
) as foo
|
||||||
GROUP BY user_id ORDER BY 2 DESC LIMIT 10;
|
GROUP BY user_id ORDER BY 2 DESC LIMIT 10;
|
||||||
|
|
||||||
|
-- table function can be the inner relationship in a join
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT random() FROM user_buy_test_table JOIN generate_series(1,10) AS users_ref_test_table(id)
|
||||||
|
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
|
||||||
|
|
||||||
|
-- table function cannot be used without subquery pushdown
|
||||||
|
SELECT count(*) FROM user_buy_test_table JOIN generate_series(1,10) AS users_ref_test_table(id)
|
||||||
|
ON user_buy_test_table.item_id = users_ref_test_table.id;
|
||||||
|
|
||||||
|
-- table function can be the inner relationship in an outer join
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT random() FROM user_buy_test_table LEFT JOIN generate_series(1,10) AS users_ref_test_table(id)
|
||||||
|
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
|
||||||
|
|
||||||
|
-- table function cannot be the outer relationship in an outer join
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT random() FROM user_buy_test_table RIGHT JOIN generate_series(1,10) AS users_ref_test_table(id)
|
||||||
|
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
|
||||||
|
|
||||||
|
-- volatile functions cannot be used as table expressions
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT random() FROM user_buy_test_table JOIN random() AS users_ref_test_table(id)
|
||||||
|
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
|
||||||
|
|
||||||
|
-- cannot sneak in a volatile function as a parameter
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT random() FROM user_buy_test_table JOIN generate_series(random()::int,10) AS users_ref_test_table(id)
|
||||||
|
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
|
||||||
|
|
||||||
|
-- cannot perform a union with table function
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT user_id FROM user_buy_test_table
|
||||||
|
UNION ALL
|
||||||
|
SELECT id FROM generate_series(1,10) AS users_ref_test_table(id)) subquery_1;
|
||||||
|
|
||||||
|
-- subquery without FROM can be the inner relationship in a join
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT random() FROM user_buy_test_table JOIN (SELECT 4 AS id) users_ref_test_table
|
||||||
|
ON user_buy_test_table.item_id > users_ref_test_table.id) subquery_1;
|
||||||
|
|
||||||
|
-- subquery without FROM triggers subquery pushdown
|
||||||
|
SELECT count(*) FROM user_buy_test_table JOIN (SELECT 5 AS id) users_ref_test_table
|
||||||
|
ON user_buy_test_table.item_id = users_ref_test_table.id;
|
||||||
|
|
||||||
|
-- subquery without FROM can be the inner relationship in an outer join
|
||||||
|
SELECT count(*) FROM user_buy_test_table LEFT JOIN (SELECT 5 AS id) users_ref_test_table
|
||||||
|
ON user_buy_test_table.item_id = users_ref_test_table.id;
|
||||||
|
|
||||||
|
-- subquery without FROM cannot be the outer relationship in an outer join
|
||||||
|
SELECT count(*) FROM user_buy_test_table RIGHT JOIN (SELECT 5 AS id) users_ref_test_table
|
||||||
|
ON user_buy_test_table.item_id = users_ref_test_table.id;
|
||||||
|
|
||||||
|
-- cannot perform a union with subquery without FROM
|
||||||
|
SELECT count(*) FROM
|
||||||
|
(SELECT user_id FROM user_buy_test_table
|
||||||
|
UNION ALL
|
||||||
|
SELECT id FROM (SELECT 5 AS id) users_ref_test_table) subquery_1;
|
||||||
|
|
||||||
-- should be able to pushdown since reference table is in the
|
-- should be able to pushdown since reference table is in the
|
||||||
-- inner part of the left join
|
-- inner part of the left join
|
||||||
SELECT
|
SELECT
|
||||||
|
|
|
@ -433,14 +433,27 @@ FROM (
|
||||||
) q
|
) q
|
||||||
ORDER BY 2 DESC, 1;
|
ORDER BY 2 DESC, 1;
|
||||||
|
|
||||||
-- subquery in where clause doesn't have a relation
|
-- subquery in where clause doesn't have a relation, but is constant
|
||||||
SELECT
|
SELECT
|
||||||
user_id
|
user_id
|
||||||
FROM
|
FROM
|
||||||
users_table
|
users_table
|
||||||
WHERE
|
WHERE
|
||||||
value_2 >
|
value_2 >
|
||||||
(SELECT 1);
|
(SELECT 1)
|
||||||
|
ORDER BY 1 ASC
|
||||||
|
LIMIT 2;
|
||||||
|
|
||||||
|
-- subquery in where clause has a volatile function and no relation
|
||||||
|
SELECT
|
||||||
|
user_id
|
||||||
|
FROM
|
||||||
|
users_table
|
||||||
|
WHERE
|
||||||
|
value_2 >
|
||||||
|
(SELECT random())
|
||||||
|
ORDER BY 1 ASC
|
||||||
|
LIMIT 2;
|
||||||
|
|
||||||
-- OFFSET is not supported in the subquey
|
-- OFFSET is not supported in the subquey
|
||||||
SELECT
|
SELECT
|
||||||
|
|
|
@ -55,6 +55,38 @@ WHERE
|
||||||
)
|
)
|
||||||
LIMIT 3;
|
LIMIT 3;
|
||||||
|
|
||||||
|
-- immutable functions are also treated as reference tables
|
||||||
|
SELECT
|
||||||
|
user_id
|
||||||
|
FROM
|
||||||
|
(SELECT user_id FROM generate_series(1,10) AS series(user_id)) users_reference_table
|
||||||
|
WHERE
|
||||||
|
NOT EXISTS
|
||||||
|
(SELECT
|
||||||
|
value_2
|
||||||
|
FROM
|
||||||
|
events_table
|
||||||
|
WHERE
|
||||||
|
users_reference_table.user_id = events_table.user_id
|
||||||
|
)
|
||||||
|
LIMIT 3;
|
||||||
|
|
||||||
|
-- subqueries without FROM are also treated as reference tables
|
||||||
|
SELECT
|
||||||
|
user_id
|
||||||
|
FROM
|
||||||
|
(SELECT 5 AS user_id) users_reference_table
|
||||||
|
WHERE
|
||||||
|
NOT EXISTS
|
||||||
|
(SELECT
|
||||||
|
value_2
|
||||||
|
FROM
|
||||||
|
events_table
|
||||||
|
WHERE
|
||||||
|
users_reference_table.user_id = events_table.user_id
|
||||||
|
)
|
||||||
|
LIMIT 3;
|
||||||
|
|
||||||
-- subqueries in WHERE with IN operator without equality
|
-- subqueries in WHERE with IN operator without equality
|
||||||
SELECT
|
SELECT
|
||||||
users_table.user_id, count(*)
|
users_table.user_id, count(*)
|
||||||
|
@ -73,6 +105,43 @@ GROUP BY users_table.user_id
|
||||||
ORDER BY 2 DESC, 1 DESC
|
ORDER BY 2 DESC, 1 DESC
|
||||||
LIMIT 3;
|
LIMIT 3;
|
||||||
|
|
||||||
|
-- immutable functions are also treated as reference tables
|
||||||
|
SELECT
|
||||||
|
users_table.user_id, count(*)
|
||||||
|
FROM
|
||||||
|
users_table
|
||||||
|
WHERE
|
||||||
|
value_2 IN
|
||||||
|
(SELECT
|
||||||
|
value_2
|
||||||
|
FROM
|
||||||
|
generate_series(1,10) AS events_reference_table(user_id)
|
||||||
|
WHERE
|
||||||
|
users_table.user_id > events_reference_table.user_id
|
||||||
|
)
|
||||||
|
GROUP BY users_table.user_id
|
||||||
|
ORDER BY 2 DESC, 1 DESC
|
||||||
|
LIMIT 3;
|
||||||
|
|
||||||
|
-- immutable functions are also treated as reference tables
|
||||||
|
SELECT
|
||||||
|
users_table.user_id, count(*)
|
||||||
|
FROM
|
||||||
|
users_table
|
||||||
|
WHERE
|
||||||
|
value_2 IN
|
||||||
|
(SELECT
|
||||||
|
value_2
|
||||||
|
FROM
|
||||||
|
(SELECT 5 AS user_id) AS events_reference_table
|
||||||
|
WHERE
|
||||||
|
users_table.user_id > events_reference_table.user_id
|
||||||
|
)
|
||||||
|
GROUP BY users_table.user_id
|
||||||
|
ORDER BY 2 DESC, 1 DESC
|
||||||
|
LIMIT 3;
|
||||||
|
|
||||||
|
|
||||||
-- should error out since reference table exist on the left side
|
-- should error out since reference table exist on the left side
|
||||||
-- of the left lateral join
|
-- of the left lateral join
|
||||||
SELECT user_id, value_2 FROM users_table WHERE
|
SELECT user_id, value_2 FROM users_table WHERE
|
||||||
|
|
Loading…
Reference in New Issue