Fix problems seen when result of a subselect was used in an
authorTom Lane
Mon, 19 Apr 1999 04:17:11 +0000 (04:17 +0000)
committerTom Lane
Mon, 19 Apr 1999 04:17:11 +0000 (04:17 +0000)
expression context (ie, not at the top level of a WHERE clause).  Examples
like this one work now:
SELECT name, value FROM t1 as touter WHERE
(value/(SELECT AVG(value) FROM t1 WHERE name = touter.name)) > 0.75;

src/backend/executor/nodeSubplan.c
src/backend/parser/parse_expr.c

index 9f872c606820def4da1f8e434cd0e5b496b13005..2eebbf1f1e2524185662f3c5c7d3b2b7e4ca2745 100644 (file)
@@ -29,10 +29,11 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
 {
    Plan       *plan = node->plan;
    SubLink    *sublink = node->sublink;
+   SubLinkType subLinkType = sublink->subLinkType;
    TupleTableSlot *slot;
    List       *lst;
-   bool        result = false;
-   bool        found = false;
+   Datum       result = (Datum) false;
+   bool        found = false;  /* TRUE if got at least one subplan tuple */
 
    if (node->setParam != NULL)
        elog(ERROR, "ExecSubPlan: can't set parent params from subquery");
@@ -56,6 +57,18 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
 
    ExecReScan(plan, (ExprContext *) NULL, plan);
 
+   /*
+    * For all sublink types except EXPR_SUBLINK, the result type is boolean,
+    * and we have a fairly clear idea of how to combine multiple subitems
+    * and deal with NULL values or an empty subplan result.
+    *
+    * For EXPR_SUBLINK, the result type is whatever the combining operator
+    * returns.  We have no way to deal with more than one column in the
+    * subplan result --- hopefully the parser forbids that.  More seriously,
+    * it's unclear what to do with NULL values or an empty subplan result.
+    * For now, we error out, but should something else happen?
+    */
+
    for (slot = ExecProcNode(plan, plan);
         !TupIsNull(slot);
         slot = ExecProcNode(plan, plan))
@@ -64,13 +77,13 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
        TupleDesc   tdesc = slot->ttc_tupleDescriptor;
        int         i = 1;
 
-       if (sublink->subLinkType == EXPR_SUBLINK && found)
+       if (subLinkType == EXPR_SUBLINK && found)
        {
            elog(ERROR, "ExecSubPlan: more than one tuple returned by expression subselect");
            return (Datum) false;
        }
 
-       if (sublink->subLinkType == EXISTS_SUBLINK)
+       if (subLinkType == EXISTS_SUBLINK)
            return (Datum) true;
 
        found = true;
@@ -82,23 +95,39 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
            bool        isnull;
 
            con->constvalue = heap_getattr(tup, i, tdesc, &(con->constisnull));
-           result = (bool) ExecEvalExpr((Node *) expr, econtext, &isnull, (bool *) NULL);
+           result = ExecEvalExpr((Node *) expr, econtext, &isnull, (bool *) NULL);
            if (isnull)
-               result = false;
-           if ((!result && !(sublink->useor)) || (result && sublink->useor))
-               break;
+           {
+               if (subLinkType == EXPR_SUBLINK)
+                   elog(ERROR, "ExecSubPlan: null value returned by expression subselect");
+               else
+                   result = (Datum) false;
+           }
+           if (subLinkType != EXPR_SUBLINK)
+           {
+               if ((! (bool) result && !(sublink->useor)) ||
+                   ((bool) result && sublink->useor))
+                   break;
+           }
            i++;
        }
 
-       if ((!result && sublink->subLinkType == ALL_SUBLINK) ||
-           (result && sublink->subLinkType == ANY_SUBLINK))
+       if (subLinkType == ALL_SUBLINK && ! (bool) result)
+           break;
+       if (subLinkType == ANY_SUBLINK && (bool) result)
            break;
    }
 
-   if (!found && sublink->subLinkType == ALL_SUBLINK)
-       return (Datum) true;
+   if (!found)
+   {
+       /* deal with empty subplan result.  Note default result is 'false' */
+       if (subLinkType == ALL_SUBLINK)
+           result = (Datum) true;
+       else if (subLinkType == EXPR_SUBLINK)
+           elog(ERROR, "ExecSubPlan: no tuples returned by expression subselect");
+   }
 
-   return (Datum) result;
+   return result;
 }
 
 /* ----------------------------------------------------------------
index d23d7214a34bbb3c56e455e08891b2b2e1dbffcd..f18fbd46eed09f431e457ce73cb80c195e61a7fa 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.41 1999/04/18 17:35:51 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.42 1999/04/19 04:17:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -312,14 +312,6 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 
                        op_expr = make_op(op, lexpr, tent->expr);
 
-                       /*
-                        * HACK! Second IF is more valid but currently we
-                        * don't support EXPR subqueries inside
-                        * expressions generally, only in WHERE clauses.
-                        * After fixing this, first IF must be removed.
-                        */
-                       if (op_expr->typeOid != BOOLOID)
-                           elog(ERROR, "parser: '%s' must return 'bool' to be used with subquery", op);
                        if (op_expr->typeOid != BOOLOID &&
                            sublink->subLinkType != EXPR_SUBLINK)
                            elog(ERROR, "parser: '%s' must return 'bool' to be used with quantified predicate subquery", op);
@@ -598,7 +590,20 @@ exprType(Node *expr)
            type = ((Param *) expr)->paramtype;
            break;
        case T_SubLink:
-           type = BOOLOID;
+           {
+               SubLink *sublink = (SubLink *) expr;
+               if (sublink->subLinkType == EXPR_SUBLINK)
+               {
+                   /* return the result type of the combining operator */
+                   Expr *op_expr = (Expr *) lfirst(sublink->oper);
+                   type = op_expr->typeOid;
+               }
+               else
+               {
+                   /* for all other sublink types, result is boolean */
+                   type = BOOLOID;
+               }
+           }
            break;
        case T_CaseExpr:
            type = ((CaseExpr *) expr)->casetype;