* PlaceHolderVar or constructed from those, we can just add the
* varnullingrels bits to the existing nullingrels field(s); otherwise
* we have to add a PlaceHolderVar wrapper.
- *
- * NOTE: this is also used by the parser, to expand join alias Vars before
- * checking GROUP BY validity. For that use-case, root will be NULL, which
- * is why we have to pass the Query separately. We need the root itself only
- * for making PlaceHolderVars. We can avoid making PlaceHolderVars in the
- * parser's usage because it won't be dealing with arbitrary expressions:
- * so long as adjust_standard_join_alias_expression can handle everything
- * the parser would make as a join alias expression, we're OK.
*/
Node *
flatten_join_alias_vars(PlannerInfo *root, Query *query, Node *node)
return flatten_join_alias_vars_mutator(node, &context);
}
+/*
+ * flatten_join_alias_for_parser
+ *
+ * This variant of flatten_join_alias_vars is used by the parser, to expand
+ * join alias Vars before checking GROUP BY validity. In that case we lack
+ * a root structure. Fortunately, we'd only need the root for making
+ * PlaceHolderVars. We can avoid making PlaceHolderVars in the parser's
+ * usage because it won't be dealing with arbitrary expressions: so long as
+ * adjust_standard_join_alias_expression can handle everything the parser
+ * would make as a join alias expression, we're OK.
+ *
+ * The "node" might be part of a sub-query of the Query whose join alias
+ * Vars are to be expanded. "sublevels_up" indicates how far below the
+ * given query we are starting.
+ */
+Node *
+flatten_join_alias_for_parser(Query *query, Node *node, int sublevels_up)
+{
+ flatten_join_alias_vars_context context;
+
+ /*
+ * We do not expect this to be applied to the whole Query, only to
+ * expressions or LATERAL subqueries. Hence, if the top node is a Query,
+ * it's okay to immediately increment sublevels_up.
+ */
+ Assert(node != (Node *) query);
+
+ context.root = NULL;
+ context.query = query;
+ context.sublevels_up = sublevels_up;
+ /* flag whether join aliases could possibly contain SubLinks */
+ context.possible_sublink = query->hasSubLinks;
+ /* if hasSubLinks is already true, no need to work hard */
+ context.inserted_sublink = query->hasSubLinks;
+
+ return flatten_join_alias_vars_mutator(node, &context);
+}
+
static Node *
flatten_join_alias_vars_mutator(Node *node,
flatten_join_alias_vars_context *context)
ParseState *pstate;
Query *qry;
bool hasJoinRTEs;
- List *groupClauses;
- List *groupClauseCommonVars;
- List *gset_common;
+ List *groupClauses; /* list of TargetEntry */
+ List *groupClauseCommonVars; /* list of Vars */
+ List *groupClauseSubLevels; /* list of lists of TargetEntry */
+ List *gset_common; /* integer list of sortgrouprefs */
bool have_non_var_grouping;
List **func_grouped_rels;
int sublevels_up;
* entries are RTE_JOIN kind.
*/
if (hasJoinRTEs)
- groupClauses = (List *) flatten_join_alias_vars(NULL, qry,
- (Node *) groupClauses);
+ groupClauses = (List *)
+ flatten_join_alias_for_parser(qry, (Node *) groupClauses, 0);
/*
* Detect whether any of the grouping expressions aren't simple Vars; if
groupClauses, hasJoinRTEs,
have_non_var_grouping);
if (hasJoinRTEs)
- clause = flatten_join_alias_vars(NULL, qry, clause);
+ clause = flatten_join_alias_for_parser(qry, clause, 0);
qry->targetList = (List *)
substitute_grouped_columns(clause, pstate, qry,
groupClauses, groupClauseCommonVars,
groupClauses, hasJoinRTEs,
have_non_var_grouping);
if (hasJoinRTEs)
- clause = flatten_join_alias_vars(NULL, qry, clause);
+ clause = flatten_join_alias_for_parser(qry, clause, 0);
qry->havingQual =
substitute_grouped_columns(clause, pstate, qry,
groupClauses, groupClauseCommonVars,
*
* NOTE: we assume that the given clause has been transformed suitably for
* parser output. This means we can use expression_tree_mutator.
- *
- * NOTE: we recognize grouping expressions in the main query, but only
- * grouping Vars in subqueries. For example, this will be rejected,
- * although it could be allowed:
- * SELECT
- * (SELECT x FROM bar where y = (foo.a + foo.b))
- * FROM foo
- * GROUP BY a + b;
- * The difficulty is the need to account for different sublevels_up.
- * This appears to require a whole custom version of equal(), which is
- * way more pain than the feature seems worth.
*/
static Node *
substitute_grouped_columns(Node *node, ParseState *pstate, Query *qry,
context.hasJoinRTEs = false; /* assume caller flattened join Vars */
context.groupClauses = groupClauses;
context.groupClauseCommonVars = groupClauseCommonVars;
+ context.groupClauseSubLevels = NIL;
context.gset_common = gset_common;
context.have_non_var_grouping = have_non_var_grouping;
context.func_grouped_rels = func_grouped_rels;
* If we have any GROUP BY items that are not simple Vars, check to see if
* subexpression as a whole matches any GROUP BY item. We need to do this
* at every recursion level so that we recognize GROUPed-BY expressions
- * before reaching variables within them. But this only works at the outer
- * query level, as noted above.
+ * before reaching variables within them. (Since this approach is pretty
+ * expensive, we don't do it this way if the items are all simple Vars.)
*/
- if (context->have_non_var_grouping && context->sublevels_up == 0)
+ if (context->have_non_var_grouping)
{
+ List *groupClauses;
int attnum = 0;
- foreach(gl, context->groupClauses)
+ /* Within a subquery, we need a mutated version of the groupClauses */
+ if (context->sublevels_up == 0)
+ groupClauses = context->groupClauses;
+ else
+ groupClauses = list_nth(context->groupClauseSubLevels,
+ context->sublevels_up - 1);
+
+ foreach(gl, groupClauses)
{
TargetEntry *tle = (TargetEntry *) lfirst(gl);
/*
* Check for a match, if we didn't do it above.
*/
- if (!context->have_non_var_grouping || context->sublevels_up != 0)
+ if (!context->have_non_var_grouping)
{
int attnum = 0;
Query *newnode;
context->sublevels_up++;
+
+ /*
+ * If we have non-Var grouping expressions, we'll need a copy of the
+ * groupClauses list that's mutated to match this sublevels_up depth.
+ * Build one if we've not yet visited a subquery at this depth.
+ */
+ if (context->have_non_var_grouping &&
+ context->sublevels_up > list_length(context->groupClauseSubLevels))
+ {
+ List *subGroupClauses = copyObject(context->groupClauses);
+
+ IncrementVarSublevelsUp((Node *) subGroupClauses,
+ context->sublevels_up, 0);
+ context->groupClauseSubLevels =
+ lappend(context->groupClauseSubLevels, subGroupClauses);
+ Assert(context->sublevels_up == list_length(context->groupClauseSubLevels));
+ }
+
newnode = query_tree_mutator((Query *) node,
substitute_grouped_columns_mutator,
context,
context.hasJoinRTEs = hasJoinRTEs;
context.groupClauses = groupClauses;
context.groupClauseCommonVars = NIL;
+ context.groupClauseSubLevels = NIL;
context.gset_common = NIL;
context.have_non_var_grouping = have_non_var_grouping;
context.func_grouped_rels = NULL;
Index ref = 0;
if (context->hasJoinRTEs)
- expr = flatten_join_alias_vars(NULL, context->qry, expr);
+ expr = flatten_join_alias_for_parser(context->qry,
+ expr,
+ context->sublevels_up);
/*
* Each expression must match a grouping entry at the current
}
}
}
- else if (context->have_non_var_grouping &&
- context->sublevels_up == 0)
+ else if (context->have_non_var_grouping)
{
- foreach(gl, context->groupClauses)
+ List *groupClauses;
+
+ /*
+ * Within a subquery, we need a mutated version of the
+ * groupClauses
+ */
+ if (context->sublevels_up == 0)
+ groupClauses = context->groupClauses;
+ else
+ groupClauses = list_nth(context->groupClauseSubLevels,
+ context->sublevels_up - 1);
+
+ foreach(gl, groupClauses)
{
TargetEntry *tle = lfirst(gl);
bool result;
context->sublevels_up++;
+
+ /*
+ * If we have non-Var grouping expressions, we'll need a copy of the
+ * groupClauses list that's mutated to match this sublevels_up depth.
+ * Build one if we've not yet visited a subquery at this depth.
+ */
+ if (context->have_non_var_grouping &&
+ context->sublevels_up > list_length(context->groupClauseSubLevels))
+ {
+ List *subGroupClauses = copyObject(context->groupClauses);
+
+ IncrementVarSublevelsUp((Node *) subGroupClauses,
+ context->sublevels_up, 0);
+ context->groupClauseSubLevels =
+ lappend(context->groupClauseSubLevels, subGroupClauses);
+ Assert(context->sublevels_up == list_length(context->groupClauseSubLevels));
+ }
+
result = query_tree_walker((Query *) node,
finalize_grouping_exprs_walker,
context,