Commit 42af56e1 authored by Tom Lane's avatar Tom Lane

Revise implementation of SubLinks so that there is a consistent,

documented intepretation of the lefthand and oper fields.  Fix a number of
obscure problems while at it --- for example, the old code failed if the parser
decided to insert a type-coercion function just below the operator of a
SubLink.
CAUTION: this will break stored rules that contain subplans.  You may
need to initdb.
parent edda70c0
......@@ -94,8 +94,25 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
Const *con = lsecond(expr->args);
bool isnull;
/*
* The righthand side of the expression should be either a Const
* or a function call taking a Const as arg (the function would
* be a run-time type coercion inserted by the parser to get to
* the input type needed by the operator). Find the Const node
* and insert the actual righthand side value into it.
*/
if (! IsA(con, Const))
{
Assert(IsA(con, Expr));
con = lfirst(((Expr *) con)->args);
Assert(IsA(con, Const));
}
con->constvalue = heap_getattr(tup, i, tdesc, &(con->constisnull));
result = ExecEvalExpr((Node *) expr, econtext, &isnull, (bool *) NULL);
/*
* Now we can eval the expression.
*/
result = ExecEvalExpr((Node *) expr, econtext, &isnull,
(bool *) NULL);
if (isnull)
{
if (subLinkType == EXPR_SUBLINK)
......
This diff is collapsed.
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.48 1999/08/22 20:14:53 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.49 1999/08/25 23:21:41 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -817,8 +817,8 @@ CommuteClause(Expr *clause)
* the args and slink->oper lists (which belong to the outer plan), but it
* will *not* visit the inner plan, since that's typically what expression
* tree walkers want. A walker that wants to visit the subplan can force
* appropriate behavior by recognizing subplan nodes and doing the right
* thing.
* appropriate behavior by recognizing subplan expression nodes and doing
* the right thing.
*
* Bare SubLink nodes (without a SUBPLAN_EXPR) are handled by recursing into
* the "lefthand" argument list only. (A bare SubLink should be seen only if
......@@ -854,27 +854,19 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context)
case T_Expr:
{
Expr *expr = (Expr *) node;
if (expr->opType == SUBPLAN_EXPR)
{
/* examine args list (params to be passed to subplan) */
if (expression_tree_walker((Node *) expr->args,
walker, context))
return true;
/* examine oper list as well */
if (expression_tree_walker(
(Node *) ((SubPlan *) expr->oper)->sublink->oper,
walker, context))
/* recurse to the SubLink node (skipping SubPlan!) */
if (walker((Node *) ((SubPlan *) expr->oper)->sublink,
context))
return true;
/* but not the subplan itself */
}
else
{
/* for other Expr node types, just examine args list */
/* for all Expr node types, examine args list */
if (expression_tree_walker((Node *) expr->args,
walker, context))
return true;
}
}
break;
case T_Aggref:
return walker(((Aggref *) node)->target, context);
......@@ -918,11 +910,20 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context)
}
break;
case T_SubLink:
/* A "bare" SubLink (note we will not come here if we found
* a SUBPLAN_EXPR node above it). Examine the lefthand side,
* but not the oper list nor the subquery.
{
SubLink *sublink = (SubLink *) node;
/* If the SubLink has already been processed by subselect.c,
* it will have lefthand=NIL, and we only need to look at
* the oper list. Otherwise we only need to look at lefthand
* (the Oper nodes in the oper list are deemed uninteresting).
*/
return walker(((SubLink *) node)->lefthand, context);
if (sublink->lefthand)
return walker((Node *) sublink->lefthand, context);
else
return walker((Node *) sublink->oper, context);
}
break;
case T_List:
foreach(temp, (List *) node)
{
......@@ -988,8 +989,8 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context)
* the args and slink->oper lists (which belong to the outer plan), but it
* will simply copy the link to the inner plan, since that's typically what
* expression tree mutators want. A mutator that wants to modify the subplan
* can force appropriate behavior by recognizing subplan nodes and doing the
* right thing.
* can force appropriate behavior by recognizing subplan expression nodes
* and doing the right thing.
*
* Bare SubLink nodes (without a SUBPLAN_EXPR) are handled by recursing into
* the "lefthand" argument list only. (A bare SubLink should be seen only if
......
......@@ -7,12 +7,14 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.56 1999/08/05 02:33:53 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.57 1999/08/25 23:21:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/pg_operator.h"
#include "nodes/makefuncs.h"
#include "nodes/params.h"
#include "nodes/relation.h"
......@@ -22,6 +24,7 @@
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "utils/builtins.h"
......@@ -209,7 +212,15 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
elog(ERROR, "parser: bad query in subselect");
sublink->subselect = (Node *) qtree;
if (sublink->subLinkType != EXISTS_SUBLINK)
if (sublink->subLinkType == EXISTS_SUBLINK)
{
/* EXISTS needs no lefthand or combining operator.
* These fields should be NIL already, but make sure.
*/
sublink->lefthand = NIL;
sublink->oper = NIL;
}
else
{
char *op = lfirst(sublink->oper);
List *left_list = sublink->lefthand;
......@@ -236,27 +247,39 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
{
TargetEntry *tent = (TargetEntry *) lfirst(right_list);
Node *lexpr;
Expr *op_expr;
Operator optup;
Form_pg_operator opform;
Oper *newop;
right_list = lnext(right_list);
if (tent->resdom->resjunk)
continue;
if (! tent->resdom->resjunk)
{
if (left_list == NIL)
elog(ERROR, "parser: Subselect has too many fields.");
lexpr = lfirst(left_list);
left_list = lnext(left_list);
op_expr = make_op(op, lexpr, tent->expr);
if (op_expr->typeOid != BOOLOID &&
optup = oper(op,
exprType(lexpr),
exprType(tent->expr),
FALSE);
opform = (Form_pg_operator) GETSTRUCT(optup);
if (opform->oprresult != BOOLOID &&
sublink->subLinkType != EXPR_SUBLINK)
elog(ERROR, "parser: '%s' must return 'bool' to be used with quantified predicate subquery", op);
sublink->oper = lappend(sublink->oper, op_expr);
}
right_list = lnext(right_list);
newop = makeOper(oprid(optup),/* opno */
InvalidOid, /* opid */
opform->oprresult,
0,
NULL);
sublink->oper = lappend(sublink->oper, newop);
}
if (left_list != NIL)
elog(ERROR, "parser: Subselect has too few fields.");
}
else
sublink->oper = NIL;
result = (Node *) expr;
break;
}
......@@ -565,10 +588,13 @@ exprType(Node *expr)
if (sublink->subLinkType == EXPR_SUBLINK)
{
/* return the result type of the combining operator */
Expr *op_expr = (Expr *) lfirst(sublink->oper);
/* return the result type of the combining operator;
* should only be one...
*/
Oper *op = (Oper *) lfirst(sublink->oper);
type = op_expr->typeOid;
Assert(IsA(op, Oper));
type = op->opresulttype;
}
else
{
......
......@@ -6,19 +6,23 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.54 1999/07/17 20:17:37 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.55 1999/08/25 23:21:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/prep.h"
#include "parser/analyze.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parse_oper.h"
#include "parser/parse_target.h"
#include "parser/parse_type.h"
#include "parser/parsetree.h"
......@@ -642,9 +646,6 @@ modifyAggrefUplevel(Node *node)
modifyAggrefUplevel(
(Node *) (sub->lefthand));
modifyAggrefUplevel(
(Node *) (sub->oper));
modifyAggrefUplevel(
(Node *) (sub->subselect));
}
......@@ -816,12 +817,6 @@ modifyAggrefChangeVarnodes(Node **nodePtr, int rt_index, int new_index, int subl
new_index,
sublevels_up);
modifyAggrefChangeVarnodes(
(Node **) (&(sub->oper)),
rt_index,
new_index,
sublevels_up);
modifyAggrefChangeVarnodes(
(Node **) (&(sub->subselect)),
rt_index,
......@@ -989,6 +984,7 @@ modifyAggrefDropQual(Node **nodePtr, Node *orignode, Expr *expr)
SubLink *sub = (SubLink *) node;
SubLink *osub = (SubLink *) orignode;
/* what about the lefthand? */
modifyAggrefDropQual(
(Node **) (&(sub->subselect)),
(Node *) (osub->subselect),
......@@ -1046,19 +1042,21 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree)
if (nodeTag(nth(1, exp->args)) == T_Aggref)
elog(ERROR, "rewrite: comparision of 2 aggregate columns not supported");
else
elog(ERROR, "rewrite: aggregate column of view must be at rigth side in qual");
elog(ERROR, "rewrite: aggregate column of view must be at right side in qual");
}
aggref = (Aggref *) nth(1, exp->args);
target = (Var *) (aggref->target);
/* XXX bogus --- agg's target might not be a Var! */
rte = (RangeTblEntry *) nth(target->varno - 1, parsetree->rtable);
tle = makeNode(TargetEntry);
resdom = makeNode(Resdom);
aggref->usenulls = TRUE;
aggref->usenulls = TRUE; /* XXX safe for all aggs?? */
resdom->resno = 1;
resdom->restype = ((Oper *) (exp->oper))->opresulttype;
resdom->restype = aggref->aggtype;
resdom->restypmod = -1;
resdom->resname = pstrdup("<noname>");
resdom->reskey = 0;
......@@ -1074,9 +1072,8 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree)
sublink = makeNode(SubLink);
sublink->subLinkType = EXPR_SUBLINK;
sublink->useor = FALSE;
sublink->lefthand = lappend(NIL, copyObject(lfirst(exp->args)));
sublink->oper = lappend(NIL, copyObject(exp));
sublink->subselect = NULL;
sublink->lefthand = lcons(lfirst(exp->args), NIL);
sublink->oper = lcons(exp->oper, NIL);
subquery = makeNode(Query);
sublink->subselect = (Node *) subquery;
......@@ -1105,8 +1102,6 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree)
modifyAggrefChangeVarnodes((Node **) &(sublink->lefthand), target->varno,
1, target->varlevelsup);
modifyAggrefChangeVarnodes((Node **) &(sublink->oper), target->varno,
1, target->varlevelsup);
modifyAggrefChangeVarnodes((Node **) &(sublink->subselect), target->varno,
1, target->varlevelsup);
......@@ -1249,6 +1244,7 @@ modifyAggrefQual(Node **nodePtr, Query *parsetree)
{
SubLink *sub = (SubLink *) node;
/* lefthand ??? */
modifyAggrefQual(
(Node **) (&(sub->subselect)),
(Query *) (sub->subselect));
......@@ -1318,9 +1314,6 @@ checkQueryHasSubLink_walker(Node *node, void *context)
return false;
if (IsA(node, SubLink))
return true; /* abort the tree traversal and return true */
/* Note: we assume the tree has not yet been rewritten by subselect.c,
* therefore we will find bare SubLink nodes and not SUBPLAN nodes.
*/
return expression_tree_walker(node, checkQueryHasSubLink_walker, context);
}
......@@ -1654,8 +1647,6 @@ apply_RIR_view(Node **nodePtr, int rt_index, RangeTblEntry *rte, List *tlist, in
case T_SubLink:
{
SubLink *sub = (SubLink *) node;
List *tmp_lefthand,
*tmp_oper;
apply_RIR_view(
(Node **) (&(sub->lefthand)),
......@@ -1672,14 +1663,6 @@ apply_RIR_view(Node **nodePtr, int rt_index, RangeTblEntry *rte, List *tlist, in
tlist,
modified,
sublevels_up + 1);
tmp_lefthand = sub->lefthand;
foreach(tmp_oper, sub->oper)
{
lfirst(((Expr *) lfirst(tmp_oper))->args) =
lfirst(tmp_lefthand);
tmp_lefthand = lnext(tmp_lefthand);
}
}
break;
......@@ -3014,31 +2997,47 @@ Except_Intersect_Rewrite(Query *parsetree)
* of the targetlist must be (IN) or must not be (NOT IN) the
* subselect
*/
n->lefthand = NIL;
foreach(elist, intersect_node->targetList)
{
Node *expr = lfirst(elist);
TargetEntry *tent = (TargetEntry *) expr;
TargetEntry *tent = (TargetEntry *) lfirst(elist);
n->lefthand = lappend(n->lefthand, tent->expr);
}
/*
* The first arguments of oper also have to be created for the
* sublink (they are the same as the lefthand side!)
* Also prepare the list of Opers that must be used for the
* comparisons (they depend on the specific datatypes involved!)
*/
left_expr = n->lefthand;
right_expr = ((Query *) (n->subselect))->targetList;
n->oper = NIL;
foreach(elist, left_expr)
{
Node *lexpr = lfirst(elist);
Node *rexpr = lfirst(right_expr);
TargetEntry *tent = (TargetEntry *) rexpr;
Expr *op_expr;
TargetEntry *tent = (TargetEntry *) lfirst(right_expr);
Operator optup;
Form_pg_operator opform;
Oper *newop;
optup = oper(op,
exprType(lexpr),
exprType(tent->expr),
FALSE);
opform = (Form_pg_operator) GETSTRUCT(optup);
if (opform->oprresult != BOOLOID)
elog(ERROR, "parser: '%s' must return 'bool' to be used with quantified predicate subquery", op);
newop = makeOper(oprid(optup),/* opno */
InvalidOid, /* opid */
opform->oprresult,
0,
NULL);
op_expr = make_op(op, lexpr, tent->expr);
n->oper = lappend(n->oper, newop);
n->oper = lappend(n->oper, op_expr);
right_expr = lnext(right_expr);
}
......
......@@ -6,7 +6,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.39 1999/08/21 03:49:13 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.40 1999/08/25 23:21:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -137,13 +137,7 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
case T_SubLink:
{
SubLink *sub = (SubLink *) node;
List *tmp_oper,
*tmp_lefthand;
/*
* We also have to adapt the variables used in
* sub->lefthand and sub->oper
*/
OffsetVarNodes(
(Node *) (sub->lefthand),
offset,
......@@ -153,20 +147,6 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
(Node *) (sub->subselect),
offset,
sublevels_up + 1);
/*
* Make sure the first argument of sub->oper points to the
* same var as sub->lefthand does otherwise we will run
* into troubles using aggregates (aggno will not be set
* correctly)
*/
tmp_lefthand = sub->lefthand;
foreach(tmp_oper, sub->oper)
{
lfirst(((Expr *) lfirst(tmp_oper))->args) =
lfirst(tmp_lefthand);
tmp_lefthand = lnext(tmp_lefthand);
}
}
break;
......@@ -357,8 +337,6 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
case T_SubLink:
{
SubLink *sub = (SubLink *) node;
List *tmp_oper,
*tmp_lefthand;
ChangeVarNodes(
(Node *) (sub->lefthand),
......@@ -371,20 +349,6 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
rt_index,
new_index,
sublevels_up + 1);
/*
* Make sure the first argument of sub->oper points to the
* same var as sub->lefthand does otherwise we will run
* into troubles using aggregates (aggno will not be set
* correctly)
*/
tmp_lefthand = sub->lefthand;
foreach(tmp_oper, sub->oper)
{
lfirst(((Expr *) lfirst(tmp_oper))->args) =
lfirst(tmp_lefthand);
tmp_lefthand = lnext(tmp_lefthand);
}
}
break;
......@@ -732,6 +696,7 @@ ResolveNew(RewriteInfo *info, List *targetlist, Node **nodePtr,
SubLink *sublink = (SubLink *) node;
Query *query = (Query *) sublink->subselect;
/* XXX what about lefthand? What about rest of subquery? */
ResolveNew(info, targetlist, (Node **) &(query->qual), sublevels_up + 1);
}
break;
......@@ -888,6 +853,7 @@ nodeHandleRIRAttributeRule(Node **nodePtr,
SubLink *sublink = (SubLink *) node;
Query *query = (Query *) sublink->subselect;
/* XXX what about lefthand? What about rest of subquery? */
nodeHandleRIRAttributeRule((Node **) &(query->qual), rtable, targetlist,
rt_index, attr_num, modified, badsql,
sublevels_up + 1);
......@@ -1062,9 +1028,6 @@ nodeHandleViewRule(Node **nodePtr,
{
SubLink *sublink = (SubLink *) node;
Query *query = (Query *) sublink->subselect;
List *tmp_lefthand,
*tmp_oper;
nodeHandleViewRule((Node **) &(query->qual), rtable, targetlist,
rt_index, modified, sublevels_up + 1);
......@@ -1078,30 +1041,10 @@ nodeHandleViewRule(Node **nodePtr,
/*
* We also have to adapt the variables used in
* sublink->lefthand and sublink->oper
* sublink->lefthand
*/
nodeHandleViewRule((Node **) &(sublink->lefthand), rtable,
targetlist, rt_index, modified, sublevels_up);
/*
* Make sure the first argument of sublink->oper points to
* the same var as sublink->lefthand does otherwise we
* will run into troubles using aggregates (aggno will not
* be set correctly
*/
pfree(lfirst(((Expr *) lfirst(sublink->oper))->args));
lfirst(((Expr *) lfirst(sublink->oper))->args) =
lfirst(sublink->lefthand);
/* INTERSECT want's this - Jan */
/*
* tmp_lefthand = sublink->lefthand; foreach(tmp_oper,
* sublink->oper) { lfirst(((Expr *)
* lfirst(tmp_oper))->args) = lfirst(tmp_lefthand);
* tmp_lefthand = lnext(tmp_lefthand); }
*/
}
break;
default:
......
......@@ -3,7 +3,7 @@
* out of it's tuple
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.22 1999/08/21 03:48:53 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.23 1999/08/25 23:21:35 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
......@@ -1586,7 +1586,7 @@ get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
{
SubLink *sublink = (SubLink *) node;
Query *query = (Query *) (sublink->subselect);
Expr *expr;
Oper *oper;
List *l;
char *sep;
char buf[BUFSIZE];
......@@ -1620,20 +1620,20 @@ get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
break;
case ANY_SUBLINK:
expr = (Expr *) lfirst(sublink->oper);
strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
oper = (Oper *) lfirst(sublink->oper);
strcat(buf, get_opname(oper->opno));
strcat(buf, " ANY ");
break;
case ALL_SUBLINK:
expr = (Expr *) lfirst(sublink->oper);
strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
oper = (Oper *) lfirst(sublink->oper);
strcat(buf, get_opname(oper->opno));
strcat(buf, " ALL ");
break;
case EXPR_SUBLINK:
expr = (Expr *) lfirst(sublink->oper);
strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
oper = (Oper *) lfirst(sublink->oper);
strcat(buf, get_opname(oper->opno));
strcat(buf, " ");
break;
......@@ -1766,6 +1766,7 @@ check_if_rte_used(int rt_index, Node *node, int sup)
if (check_if_rte_used(rt_index, (Node *) (query->qual), sup + 1))
return TRUE;
/* why aren't we looking at query->targetlist, havingQual? */
if (check_if_rte_used(rt_index, (Node *) (sublink->lefthand), sup))
return TRUE;
......
......@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: primnodes.h,v 1.35 1999/08/22 20:15:00 tgl Exp $
* $Id: primnodes.h,v 1.36 1999/08/25 23:21:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -319,10 +319,36 @@ typedef struct Aggref
/* ----------------
* SubLink
* subLinkType - EXISTS, ALL, ANY, EXPR
* useor - TRUE for <>
* lefthand - list of Var/Const nodes on the left
* useor - TRUE for <> (combine op results with "or" not "and")
* lefthand - list of outer-query expressions on the left
* oper - list of Oper nodes
* subselect - subselect as Query* or parsetree
*
* NOTE: lefthand and oper have varying meanings depending on where you look
* in the parse/plan pipeline:
* 1. gram.y delivers a list of the (untransformed) lefthand expressions in
* lefthand, and sets oper to a one-element list containing the string
* name of the operator.
* 2. The parser's expression transformation transforms lefthand normally,
* and replaces oper with a list of Oper nodes, one per lefthand
* expression. These nodes represent the parser's resolution of exactly
* which operator to apply to each pair of lefthand and targetlist
* expressions. However, we have not constructed actual Expr trees for
* these operators yet. This is the representation seen in saved rules
* and in the rewriter.
* 3. Finally, the planner converts the oper list to a list of normal Expr
* nodes representing the application of the operator(s) to the lefthand
* expressions and values from the inner targetlist. The inner
* targetlist items are represented by placeholder Param or Const nodes.
* The lefthand field is set to NIL, since its expressions are now in
* the Expr list. This representation is passed to the executor.
*
* Planner routines that might see either representation 2 or 3 can tell
* the difference by checking whether lefthand is NIL or not. Also,
* representation 2 appears in a "bare" SubLink, while representation 3 is
* found in SubLinks that are children of SubPlan nodes.
*
* In an EXISTS SubLink, both lefthand and oper are unused and are always NIL.
* ----------------
*/
typedef enum SubLinkType
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment