1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-26 12:21:12 +03:00

Implement subselects in target lists. Also, relax requirement that

subselects can only appear on the righthand side of a binary operator.
That's still true for quantified predicates like x = ANY (SELECT ...),
but a subselect that delivers a single result can now appear anywhere
in an expression.  This is implemented by changing EXPR_SUBLINK sublinks
to represent just the (SELECT ...) expression, without any 'left hand
side' or combining operator --- so they're now more like EXISTS_SUBLINK.
To handle the case of '(x, y, z) = (SELECT ...)', I added a new sublink
type MULTIEXPR_SUBLINK, which acts just like EXPR_SUBLINK used to.
But the grammar will only generate one for a multiple-left-hand-side
row expression.
This commit is contained in:
Tom Lane
1999-11-15 02:00:15 +00:00
parent 1ecb129d20
commit f68e11f373
12 changed files with 364 additions and 566 deletions

View File

@ -6,7 +6,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.62 1999/11/01 05:18:31 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.63 1999/11/15 02:00:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -56,9 +56,9 @@ static bool attribute_used(Node *node, int rt_index, int attno,
int sublevels_up);
static bool modifyAggrefUplevel(Node *node, void *context);
static bool modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index,
int sublevels_up);
int sublevels_up, int new_sublevels_up);
static Node *modifyAggrefDropQual(Node *node, Node *targetNode);
static SubLink *modifyAggrefMakeSublink(Expr *origexp, Query *parsetree);
static SubLink *modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree);
static Node *modifyAggrefQual(Node *node, Query *parsetree);
static bool checkQueryHasAggs(Node *node);
static bool checkQueryHasAggs_walker(Node *node, void *context);
@ -357,6 +357,7 @@ typedef struct {
int rt_index;
int new_index;
int sublevels_up;
int new_sublevels_up;
} modifyAggrefChangeVarnodes_context;
static bool
@ -374,7 +375,7 @@ modifyAggrefChangeVarnodes_walker(Node *node,
{
var->varno = context->new_index;
var->varnoold = context->new_index;
var->varlevelsup = 0;
var->varlevelsup = context->new_sublevels_up;
}
return false;
}
@ -392,7 +393,8 @@ modifyAggrefChangeVarnodes_walker(Node *node,
if (modifyAggrefChangeVarnodes((Node *) (sub->subselect),
context->rt_index,
context->new_index,
context->sublevels_up + 1))
context->sublevels_up + 1,
context->new_sublevels_up + 1))
return true;
return false;
}
@ -418,13 +420,14 @@ modifyAggrefChangeVarnodes_walker(Node *node,
static bool
modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index,
int sublevels_up)
int sublevels_up, int new_sublevels_up)
{
modifyAggrefChangeVarnodes_context context;
context.rt_index = rt_index;
context.new_index = new_index;
context.sublevels_up = sublevels_up;
context.new_sublevels_up = new_sublevels_up;
return modifyAggrefChangeVarnodes_walker(node, &context);
}
@ -433,8 +436,13 @@ modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index,
* modifyAggrefDropQual -
* remove the pure aggref clause from a qualification
*
* targetNode is a boolean expression node somewhere within the given
* expression tree. When we find it, replace it with a constant TRUE.
* targetNode is an Aggref node somewhere within the given expression tree.
* Find the boolean operator that's presumably somewhere above it, and replace
* that whole operator expression with a constant TRUE. (This is NOT really
* quite the right thing, but it handles simple cases. This whole set of
* Aggref-in-qual routines needs to be thrown away when we can do subselects
* in FROM.)
*
* The return tree is a modified copy of the given tree; the given tree
* is not altered.
*
@ -448,14 +456,28 @@ modifyAggrefDropQual(Node *node, Node *targetNode)
if (node == NULL)
return NULL;
if (node == targetNode)
{
/* Oops, it's not inside an Expr we can rearrange... */
elog(ERROR, "Cannot handle aggregate function inserted at this place in WHERE clause");
}
if (IsA(node, Expr))
{
Expr *expr = (Expr *) node;
List *i;
if (! IsA(expr, Expr) || expr->typeOid != BOOLOID)
elog(ERROR,
"aggregate expression in qualification isn't of type bool");
return (Node *) makeConst(BOOLOID, 1, (Datum) true,
false, true, false, false);
foreach(i, expr->args)
{
if (((Node *) lfirst(i)) == targetNode)
{
/* Found the parent expression containing the Aggref */
if (expr->typeOid != BOOLOID)
elog(ERROR,
"aggregate function in qual must be argument of boolean operator");
return (Node *) makeConst(BOOLOID, 1, (Datum) true,
false, true, false, false);
}
}
/* else this isn't the expr we want, keep going */
}
return expression_tree_mutator(node, modifyAggrefDropQual,
(void *) targetNode);
@ -467,27 +489,17 @@ modifyAggrefDropQual(Node *node, Node *targetNode)
* uses an aggregate column of a view
*/
static SubLink *
modifyAggrefMakeSublink(Expr *origexp, Query *parsetree)
modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree)
{
SubLink *sublink;
Query *subquery;
RangeTblEntry *rte;
Aggref *aggref;
/* target and rte point to old structures: */
Var *target;
RangeTblEntry *rte;
/* these point to newly-created structures: */
Query *subquery;
SubLink *sublink;
TargetEntry *tle;
Resdom *resdom;
Expr *exp = copyObject(origexp);
if (IsA(nth(0, exp->args), Aggref))
{
if (IsA(nth(1, exp->args), Aggref))
elog(ERROR, "rewrite: comparison of 2 aggregate columns not supported");
else
elog(ERROR, "rewrite: aggregate column of view must be at right side in qual");
/* XXX could try to commute operator, instead of failing */
}
aggref = (Aggref *) nth(1, exp->args);
target = (Var *) (aggref->target);
if (! IsA(target, Var))
elog(ERROR, "rewrite: aggregates of views only allowed on simple variables for now");
@ -504,16 +516,15 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree)
tle = makeNode(TargetEntry);
tle->resdom = resdom;
tle->expr = (Node *) aggref; /* note this is from the copied expr */
tle->expr = copyObject(aggref); /* make a modifiable copy! */
subquery = makeNode(Query);
sublink = makeNode(SubLink);
sublink->subLinkType = EXPR_SUBLINK;
sublink->useor = false;
/* note lefthand and oper are made from the copied expr */
sublink->lefthand = lcons(lfirst(exp->args), NIL);
sublink->oper = lcons(exp->oper, NIL);
subquery = makeNode(Query);
sublink->lefthand = NIL;
sublink->oper = NIL;
sublink->subselect = (Node *) subquery;
subquery->commandType = CMD_SELECT;
@ -526,33 +537,31 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree)
subquery->unionall = FALSE;
subquery->uniqueFlag = NULL;
subquery->sortClause = NULL;
subquery->rtable = lcons(rte, NIL);
subquery->rtable = lcons(copyObject(rte), NIL);
subquery->targetList = lcons(tle, NIL);
subquery->qual = modifyAggrefDropQual((Node *) parsetree->qual,
(Node *) origexp);
(Node *) aggref);
/*
* If there are still aggs in the subselect's qual, give up.
* Recursing would be a bad idea --- we'd likely produce an
* infinite recursion. This whole technique is a crock, really...
*/
if (checkQueryHasAggs(subquery->qual))
elog(ERROR, "Cannot handle aggregate function inserted at this place in WHERE clause");
elog(ERROR, "Cannot handle multiple aggregate functions in WHERE clause");
subquery->groupClause = NIL;
subquery->havingQual = NULL;
subquery->hasAggs = TRUE;
subquery->hasSubLinks = FALSE;
subquery->hasSubLinks = checkQueryHasSubLink(subquery->qual);
subquery->unionClause = NULL;
/* Increment all varlevelsup fields in the new subquery */
modifyAggrefUplevel((Node *) subquery, NULL);
/*
* Note: it might appear that we should be passing target->varlevelsup+1
* here, since modifyAggrefUplevel has increased all the varlevelsup
* values in the subquery. However, target itself is a pointer to a
* Var node in the subquery, so it's been incremented too! What a kluge
* this all is ... we need to make subquery RTEs so it can go away...
/* Replace references to the target table with correct varno.
* Note +1 here to account for effects of previous line!
*/
modifyAggrefChangeVarnodes((Node *) subquery, target->varno,
1, target->varlevelsup);
1, target->varlevelsup+1, 0);
return sublink;
}
@ -572,30 +581,17 @@ modifyAggrefQual(Node *node, Query *parsetree)
{
if (node == NULL)
return NULL;
if (IsA(node, Expr))
{
Expr *expr = (Expr *) node;
if (length(expr->args) == 2 &&
(IsA(lfirst(expr->args), Aggref) ||
IsA(lsecond(expr->args), Aggref)))
{
SubLink *sub = modifyAggrefMakeSublink(expr,
parsetree);
parsetree->hasSubLinks = true;
/* check for aggs in resulting lefthand... */
sub->lefthand = (List *) modifyAggrefQual((Node *) sub->lefthand,
parsetree);
return (Node *) sub;
}
/* otherwise, fall through and copy the expr normally */
}
if (IsA(node, Aggref))
{
/* Oops, found one that's not inside an Expr we can rearrange... */
elog(ERROR, "Cannot handle aggregate function inserted at this place in WHERE clause");
SubLink *sub = modifyAggrefMakeSublink((Aggref *) node, parsetree);
parsetree->hasSubLinks = true;
return (Node *) sub;
}
/* We do NOT recurse into subselects in this routine. It's sufficient
/*
* Otherwise, fall through and copy the expr normally.
*
* We do NOT recurse into subselects in this routine. It's sufficient
* to get rid of aggregates that are in the qual expression proper.
*/
return expression_tree_mutator(node, modifyAggrefQual,
@ -1113,7 +1109,8 @@ fireRIRrules(Query *parsetree)
if (parsetree->hasSubLinks)
fireRIRonSubselect((Node *) parsetree, NULL);
parsetree->qual = modifyAggrefQual(parsetree->qual, parsetree);
if (parsetree->hasAggs)
parsetree->qual = modifyAggrefQual(parsetree->qual, parsetree);
return parsetree;
}
@ -1577,17 +1574,22 @@ BasicQueryRewrite(Query *parsetree)
/*
* If the query was marked having aggregates, check if this is
* still true after rewriting. Ditto for sublinks.
*
* This check must get expanded when someday aggregates can appear
* somewhere else than in the targetlist or the having qual.
* still true after rewriting. Ditto for sublinks. Note there
* should be no aggs in the qual at this point.
*/
if (query->hasAggs)
query->hasAggs = checkQueryHasAggs((Node *) (query->targetList))
|| checkQueryHasAggs((Node *) (query->havingQual));
{
query->hasAggs =
checkQueryHasAggs((Node *) (query->targetList)) ||
checkQueryHasAggs((Node *) (query->havingQual));
if (checkQueryHasAggs((Node *) (query->qual)))
elog(ERROR, "BasicQueryRewrite: failed to remove aggs from qual");
}
if (query->hasSubLinks)
query->hasSubLinks = checkQueryHasSubLink((Node *) (query->qual))
|| checkQueryHasSubLink((Node *) (query->havingQual));
query->hasSubLinks =
checkQueryHasSubLink((Node *) (query->targetList)) ||
checkQueryHasSubLink((Node *) (query->qual)) ||
checkQueryHasSubLink((Node *) (query->havingQual));
results = lappend(results, query);
}