array support phase 3 patch (was Re: array support patch phase 1 patch) - Mailing list pgsql-patches

From Joe Conway
Subject array support phase 3 patch (was Re: array support patch phase 1 patch)
Date
Msg-id 3ED9213F.8050607@joeconway.com
Whole thread Raw
In response to Re: array support patch phase 1 patch  (Tom Lane <tgl@sss.pgh.pa.us>)
List pgsql-patches
Tom Lane wrote:
 > Joe Conway <mail@joeconway.com> writes:
>>Maybe this takes us
>>back to Peter's suggestion:
>>   expression IN (array)
>>   expression NOT IN (array)
>>   expression operator ANY (array)
>>   expression operator SOME (array)
>>   (expression) operator (array)
>>   (expression) operator ALL (array)
>
> There's a lot to be said for that, if we can think of a way to do it.
> I believe this way would force us to integrate the operations into the
> parser, though, rather than just throwing a few polymorphic functions
> at the problem.  It's probably a lot more work :-(

The attached implements the middle 2 examples above. The IN and NOT IN
examples would be ambiguous due to the fact that we already accept
"IN (list_of_expressions)" and "NOT IN (list_of_expressions)".

Specifically implemented:
    expression1 operator ANY (expression2)
    expression1 operator SOME (expression2)
    expression1 operator ALL (expression2)

If expression2 is scalar, it simplifies down to the same as doing:
    expression1 operator ANY | SOME | ALL
     (select expression2)

If expression2 is an array, the array elements are tested as if you did
this:
    expression1 operator ANY | SOME | ALL
     (select element_a
       union all
      select element_b...
       union all
      select element_n)

Here are a few examples:

create table tse(f1 int, f2 int[], f3 text[]);
insert into tse values(1,array[69,42,54], array['g','d','e']);
insert into tse values(2,array[1,2,3], array['x','y','z']);
insert into tse values(3,array[2,99,0], array['Tom','Bruce']);
insert into tse values(4,array[1,1,1], array['a','a','a']);
insert into tse values(5,array[5,6,7], array['a','b','c']);

regression=# select * from tse where 1 = any (f2);
  f1 |   f2    |   f3
----+---------+---------
   2 | {1,2,3} | {x,y,z}
   4 | {1,1,1} | {a,a,a}
(2 rows)

regression=# select * from tse where 1 = all (f2);
  f1 |   f2    |   f3
----+---------+---------
   4 | {1,1,1} | {a,a,a}
(1 row)

regression=# select * from tse where 'a' != any (f3);
  f1 |     f2     |     f3
----+------------+-------------
   1 | {69,42,54} | {g,d,e}
   2 | {1,2,3}    | {x,y,z}
   3 | {2,99,0}   | {Tom,Bruce}
   5 | {5,6,7}    | {a,b,c}
(4 rows)

regression=# select * from tse where 'a' != all (f3);
  f1 |     f2     |     f3
----+------------+-------------
   1 | {69,42,54} | {g,d,e}
   2 | {1,2,3}    | {x,y,z}
   3 | {2,99,0}   | {Tom,Bruce}
(3 rows)


regression=# select * from tse where 'y' < some (f3);
  f1 |   f2    |   f3
----+---------+---------
   2 | {1,2,3} | {x,y,z}
(1 row)

The patch passes all regression tests. It is independent of the phase2
patch submitted earlier this week.

I am already planning to update the array related docs one more time
after the feature freeze, so I'd like to wait until then to document
this. If there are no objections, please apply.

Thanks,

Joe
Index: src/backend/executor/nodeSubplan.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
retrieving revision 1.45
diff -c -r1.45 nodeSubplan.c
*** src/backend/executor/nodeSubplan.c    8 Apr 2003 23:20:01 -0000    1.45
--- src/backend/executor/nodeSubplan.c    31 May 2003 20:38:27 -0000
***************
*** 224,229 ****
--- 224,230 ----
      PlanState  *planstate = node->planstate;
      SubLinkType subLinkType = subplan->subLinkType;
      bool        useOr = subplan->useOr;
+     bool        isExpr = subplan->isExpr;
      MemoryContext oldcontext;
      TupleTableSlot *slot;
      Datum        result;
***************
*** 292,297 ****
--- 293,303 ----
          bool        rownull = false;
          int            col = 1;
          List       *plst;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          if (subLinkType == EXISTS_SUBLINK)
          {
***************
*** 329,337 ****

          if (subLinkType == ARRAY_SUBLINK)
          {
-             Datum    dvalue;
-             bool    disnull;
-
              found = true;
              /* stash away current value */
              dvalue = heap_getattr(tup, 1, tdesc, &disnull);
--- 335,340 ----
***************
*** 349,446 ****
          found = true;

          /*
!          * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!          * operators for columns of tuple.
           */
!         plst = subplan->paramIds;
!         foreach(lst, node->exprs)
          {
!             ExprState  *exprstate = (ExprState *) lfirst(lst);
!             int            paramid = lfirsti(plst);
!             ParamExecData *prmdata;
!             Datum        expresult;
!             bool        expnull;
!
!             /*
!              * Load up the Param representing this column of the sub-select.
!              */
!             prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
!             Assert(prmdata->execPlan == NULL);
!             prmdata->value = heap_getattr(tup, col, tdesc,
!                                           &(prmdata->isnull));
!
!             /*
!              * Now we can eval the combining operator for this column.
!              */
!             expresult = ExecEvalExprSwitchContext(exprstate, econtext,
!                                                   &expnull, NULL);

!             /*
!              * Combine the result into the row result as appropriate.
!              */
!             if (col == 1)
              {
!                 rowresult = expresult;
!                 rownull = expnull;
              }
!             else if (useOr)
              {
!                 /* combine within row per OR semantics */
!                 if (expnull)
!                     rownull = true;
!                 else if (DatumGetBool(expresult))
                  {
!                     rowresult = BoolGetDatum(true);
!                     rownull = false;
!                     break;        /* needn't look at any more columns */
                  }
              }
              else
              {
!                 /* combine within row per AND semantics */
!                 if (expnull)
!                     rownull = true;
!                 else if (!DatumGetBool(expresult))
!                 {
!                     rowresult = BoolGetDatum(false);
!                     rownull = false;
!                     break;        /* needn't look at any more columns */
!                 }
              }

-             plst = lnext(plst);
-             col++;
          }

!         if (subLinkType == ANY_SUBLINK)
          {
!             /* combine across rows per OR semantics */
!             if (rownull)
!                 *isNull = true;
!             else if (DatumGetBool(rowresult))
!             {
!                 result = BoolGetDatum(true);
!                 *isNull = false;
!                 break;            /* needn't look at any more rows */
              }
!         }
!         else if (subLinkType == ALL_SUBLINK)
!         {
!             /* combine across rows per AND semantics */
!             if (rownull)
!                 *isNull = true;
!             else if (!DatumGetBool(rowresult))
!             {
!                 result = BoolGetDatum(false);
!                 *isNull = false;
!                 break;            /* needn't look at any more rows */
              }
-         }
-         else
-         {
-             /* must be MULTIEXPR_SUBLINK */
-             result = rowresult;
-             *isNull = rownull;
          }
      }

--- 352,514 ----
          found = true;

          /*
!          * When isExpr is true, we have either a scalar expression or an
!          * array. In the former case, this is no different than the !isExpr
!          * case. In the latter case, iterate over the elements as if they
!          * were from multiple input tuples.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             Oid        expr_typeid = tdesc->attrs[0]->atttypid;

!             if (expr_typeid != subplan->exprtype)
              {
!                 subplan->exprtype = expr_typeid;
!                 subplan->elemtype = get_element_type(expr_typeid);
!
!                 if (subplan->elemtype != InvalidOid)
!                     get_typlenbyvalalign(subplan->elemtype,
!                                          &subplan->elmlen,
!                                          &subplan->elmbyval,
!                                          &subplan->elmalign);
              }
!
!             /* get current value */
!             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
!
!             /* XXX this will need work if/when arrays support NULL elements */
!             if (!disnull)
              {
!                 if (subplan->elemtype != InvalidOid)
!                 {
!                     ArrayType   *v = DatumGetArrayTypeP(dvalue);
!
!                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
!                                       subplan->elmbyval, subplan->elmalign,
!                                         &dvalues, &numelems);
!                 }
!                 else
                  {
!                     numelems = 1;
!                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                     dvalues[0] = dvalue;
                  }
              }
              else
              {
!                 numelems = 1;
!                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                 dvalues[0] = (Datum) 0;
              }

          }

!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             /*
!              * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!              * operators for columns of tuple.
!              */
!             col = 1;
!             plst = subplan->paramIds;
!             foreach(lst, node->exprs)
!             {
!                 ExprState  *exprstate = (ExprState *) lfirst(lst);
!                 int            paramid = lfirsti(plst);
!                 ParamExecData *prmdata;
!                 Datum        expresult;
!                 bool        expnull;
!
!                 /*
!                  * Load up the Param representing this column of the sub-select.
!                  */
!                 prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
!                 Assert(prmdata->execPlan == NULL);
!
!                 if (!isExpr)
!                     prmdata->value = heap_getattr(tup, col, tdesc,
!                                                   &(prmdata->isnull));
!                 else
!                 {
!                     prmdata->value = dvalues[elemnum];
!                     prmdata->isnull = disnull;
!                 }
!
!                 /*
!                  * Now we can eval the combining operator for this column.
!                  */
!                 expresult = ExecEvalExprSwitchContext(exprstate, econtext,
!                                                       &expnull, NULL);
!
!                 /*
!                  * Combine the result into the row result as appropriate.
!                  */
!                 if (col == 1)
!                 {
!                     rowresult = expresult;
!                     rownull = expnull;
!                 }
!                 else if (useOr)
!                 {
!                     /* combine within row per OR semantics */
!                     if (expnull)
!                         rownull = true;
!                     else if (DatumGetBool(expresult))
!                     {
!                         rowresult = BoolGetDatum(true);
!                         rownull = false;
!                         break;        /* needn't look at any more columns */
!                     }
!                 }
!                 else
!                 {
!                     /* combine within row per AND semantics */
!                     if (expnull)
!                         rownull = true;
!                     else if (!DatumGetBool(expresult))
!                     {
!                         rowresult = BoolGetDatum(false);
!                         rownull = false;
!                         break;        /* needn't look at any more columns */
!                     }
!                 }
!
!                 plst = lnext(plst);
!                 col++;
              }
!
!             if (subLinkType == ANY_SUBLINK)
!             {
!                 /* combine across rows per OR semantics */
!                 if (rownull)
!                     *isNull = true;
!                 else if (DatumGetBool(rowresult))
!                 {
!                     result = BoolGetDatum(true);
!                     *isNull = false;
!                     break;            /* needn't look at any more rows */
!                 }
!             }
!             else if (subLinkType == ALL_SUBLINK)
!             {
!                 /* combine across rows per AND semantics */
!                 if (rownull)
!                     *isNull = true;
!                 else if (!DatumGetBool(rowresult))
!                 {
!                     result = BoolGetDatum(false);
!                     *isNull = false;
!                     break;            /* needn't look at any more rows */
!                 }
!             }
!             else
!             {
!                 /* must be MULTIEXPR_SUBLINK */
!                 result = rowresult;
!                 *isNull = rownull;
              }
          }
      }

***************
*** 478,483 ****
--- 546,552 ----
  buildSubPlanHash(SubPlanState *node)
  {
      SubPlan       *subplan = (SubPlan *) node->xprstate.expr;
+     bool        isExpr = subplan->isExpr;
      PlanState  *planstate = node->planstate;
      int            ncols = length(node->exprs);
      ExprContext *innerecontext = node->innerecontext;
***************
*** 485,490 ****
--- 554,560 ----
      MemoryContext oldcontext;
      int            nbuckets;
      TupleTableSlot *slot;
+     TupleTableSlot *arrslot = NULL;

      Assert(subplan->subLinkType == ANY_SUBLINK);
      Assert(!subplan->useOr);
***************
*** 562,604 ****
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         int            col = 1;
          List       *plst;
          bool        isnew;

          /*
!          * Load up the Params representing the raw sub-select outputs,
!          * then form the projection tuple to store in the hashtable.
           */
!         foreach(plst, subplan->paramIds)
          {
!             int            paramid = lfirsti(plst);
!             ParamExecData *prmdata;

!             prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
!             Assert(prmdata->execPlan == NULL);
!             prmdata->value = heap_getattr(tup, col, tdesc,
!                                           &(prmdata->isnull));
!             col++;
!         }
!         slot = ExecProject(node->projRight, NULL);
!         tup = slot->val;

-         /*
-          * If result contains any nulls, store separately or not at all.
-          * (Since we know the projection tuple has no junk columns, we
-          * can just look at the overall hasnull info bit, instead of
-          * groveling through the columns.)
-          */
-         if (HeapTupleNoNulls(tup))
-         {
-             (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
-             node->havehashrows = true;
          }
!         else if (node->hashnulls)
          {
!             (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
!             node->havenullrows = true;
          }

          /*
--- 632,770 ----
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         TupleDesc    arrtdesc = NULL;
          List       *plst;
          bool        isnew;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          /*
!          * When isExpr is true, we have either a scalar expression or an
!          * array. In the former case, this is no different than the !isExpr
!          * case. In the latter case, iterate over the elements as if they
!          * were from multiple input tuples.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             Oid        expr_typeid = tdesc->attrs[0]->atttypid;

!             if (expr_typeid != subplan->exprtype)
!             {
!                 subplan->exprtype = expr_typeid;
!                 subplan->elemtype = get_element_type(expr_typeid);
!
!                 if (subplan->elemtype != InvalidOid)
!                     get_typlenbyvalalign(subplan->elemtype,
!                                          &subplan->elmlen,
!                                          &subplan->elmbyval,
!                                          &subplan->elmalign);
!             }
!
!             /* get current value */
!             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
!
!             if (subplan->elemtype != InvalidOid)
!             {
!                 TupleTable    tupleTable;
!                 ArrayType   *v = DatumGetArrayTypeP(dvalue);
!
!                 arrtdesc = CreateTemplateTupleDesc(1, false);
!                 TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype,
!                                                             -1, 0, false);
!
!                 tupleTable = ExecCreateTupleTable(1);
!                 arrslot = ExecAllocTableSlot(tupleTable);
!                 ExecSetSlotDescriptor(arrslot, arrtdesc, true);
!
!                 /* XXX this will need work if/when arrays support NULL elements */
!                 if (!disnull)
!                 {
!                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
!                                       subplan->elmbyval, subplan->elmalign,
!                                         &dvalues, &numelems);
!                 }
!                 else
!                 {
!                     numelems = 1;
!                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                     dvalues[0] = (Datum) 0;
!                 }
!             }
!             else
!             {
!                 numelems = 1;
!                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                 dvalues[0] = dvalue;
!             }

          }
!
!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             int    col = 1;
!
!             if (!isExpr || subplan->elemtype == InvalidOid)
!             {
!                 /*
!                  * Load up the Params representing the raw sub-select outputs,
!                  * then form the projection tuple to store in the hashtable.
!                  */
!                 foreach(plst, subplan->paramIds)
!                 {
!                     int            paramid = lfirsti(plst);
!                     ParamExecData *prmdata;
!
!                     prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
!                     Assert(prmdata->execPlan == NULL);
!
!                     prmdata->value = heap_getattr(tup, col, tdesc,
!                                                   &(prmdata->isnull));
!
!                     col++;
!                 }
!                 slot = ExecProject(node->projRight, NULL);
!                 tup = slot->val;
!             }
!             else
!             {
!                 /*
!                  * For array type expressions, we need to build up our own
!                  * tuple and slot
!                  */
!                 char        nullflag;
!
!                 nullflag = disnull ? 'n' : ' ';
!                 tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag);
!                 arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true);
!             }
!
!             /*
!              * If result contains any nulls, store separately or not at all.
!              * (Since we know the projection tuple has no junk columns, we
!              * can just look at the overall hasnull info bit, instead of
!              * groveling through the columns.)
!              */
!             if (HeapTupleNoNulls(tup))
!             {
!                 if (!isExpr)
!                     (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
!                 else
!                     (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
!                 node->havehashrows = true;
!             }
!             else if (node->hashnulls)
!             {
!                 if (!isExpr)
!                     (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
!                 else
!                     (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
!                 node->havenullrows = true;
!             }
          }

          /*
***************
*** 615,620 ****
--- 781,788 ----
       * have the potential for a double free attempt.
       */
      ExecClearTuple(node->projRight->pi_slot);
+     if (arrslot)
+         ExecClearTuple(arrslot);

      MemoryContextSwitchTo(oldcontext);
  }
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.251
diff -c -r1.251 copyfuncs.c
*** src/backend/nodes/copyfuncs.c    28 May 2003 16:03:56 -0000    1.251
--- src/backend/nodes/copyfuncs.c    31 May 2003 20:38:27 -0000
***************
*** 825,830 ****
--- 825,831 ----

      COPY_SCALAR_FIELD(subLinkType);
      COPY_SCALAR_FIELD(useOr);
+     COPY_SCALAR_FIELD(isExpr);
      COPY_NODE_FIELD(lefthand);
      COPY_NODE_FIELD(operName);
      COPY_OIDLIST_FIELD(operOids);
***************
*** 843,848 ****
--- 844,855 ----

      COPY_SCALAR_FIELD(subLinkType);
      COPY_SCALAR_FIELD(useOr);
+     COPY_SCALAR_FIELD(isExpr);
+     COPY_SCALAR_FIELD(exprtype);
+     COPY_SCALAR_FIELD(elemtype);
+     COPY_SCALAR_FIELD(elmlen);
+     COPY_SCALAR_FIELD(elmbyval);
+     COPY_SCALAR_FIELD(elmalign);
      COPY_NODE_FIELD(exprs);
      COPY_INTLIST_FIELD(paramIds);
      COPY_NODE_FIELD(plan);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.194
diff -c -r1.194 equalfuncs.c
*** src/backend/nodes/equalfuncs.c    28 May 2003 16:03:56 -0000    1.194
--- src/backend/nodes/equalfuncs.c    31 May 2003 20:38:27 -0000
***************
*** 300,305 ****
--- 300,306 ----
  {
      COMPARE_SCALAR_FIELD(subLinkType);
      COMPARE_SCALAR_FIELD(useOr);
+     COMPARE_SCALAR_FIELD(isExpr);
      COMPARE_NODE_FIELD(lefthand);
      COMPARE_NODE_FIELD(operName);
      COMPARE_OIDLIST_FIELD(operOids);
***************
*** 313,318 ****
--- 314,325 ----
  {
      COMPARE_SCALAR_FIELD(subLinkType);
      COMPARE_SCALAR_FIELD(useOr);
+     COMPARE_SCALAR_FIELD(isExpr);
+     COMPARE_SCALAR_FIELD(exprtype);
+     COMPARE_SCALAR_FIELD(elemtype);
+     COMPARE_SCALAR_FIELD(elmlen);
+     COMPARE_SCALAR_FIELD(elmbyval);
+     COMPARE_SCALAR_FIELD(elmalign);
      COMPARE_NODE_FIELD(exprs);
      COMPARE_INTLIST_FIELD(paramIds);
      /* should compare plans, but have to settle for comparing plan IDs */
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
retrieving revision 1.206
diff -c -r1.206 outfuncs.c
*** src/backend/nodes/outfuncs.c    28 May 2003 16:03:56 -0000    1.206
--- src/backend/nodes/outfuncs.c    31 May 2003 20:38:27 -0000
***************
*** 700,705 ****
--- 700,706 ----

      WRITE_ENUM_FIELD(subLinkType, SubLinkType);
      WRITE_BOOL_FIELD(useOr);
+     WRITE_BOOL_FIELD(isExpr);
      WRITE_NODE_FIELD(lefthand);
      WRITE_NODE_FIELD(operName);
      WRITE_OIDLIST_FIELD(operOids);
***************
*** 713,718 ****
--- 714,725 ----

      WRITE_ENUM_FIELD(subLinkType, SubLinkType);
      WRITE_BOOL_FIELD(useOr);
+     WRITE_BOOL_FIELD(isExpr);
+     WRITE_OID_FIELD(exprtype);
+     WRITE_OID_FIELD(elemtype);
+     WRITE_INT_FIELD(elmlen);
+     WRITE_BOOL_FIELD(elmbyval);
+     WRITE_CHAR_FIELD(elmalign);
      WRITE_NODE_FIELD(exprs);
      WRITE_INTLIST_FIELD(paramIds);
      WRITE_NODE_FIELD(plan);
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
retrieving revision 1.153
diff -c -r1.153 readfuncs.c
*** src/backend/nodes/readfuncs.c    6 May 2003 00:20:32 -0000    1.153
--- src/backend/nodes/readfuncs.c    31 May 2003 20:38:27 -0000
***************
*** 544,549 ****
--- 544,550 ----

      READ_ENUM_FIELD(subLinkType, SubLinkType);
      READ_BOOL_FIELD(useOr);
+     READ_BOOL_FIELD(isExpr);
      READ_NODE_FIELD(lefthand);
      READ_NODE_FIELD(operName);
      READ_OIDLIST_FIELD(operOids);
Index: src/backend/optimizer/plan/subselect.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v
retrieving revision 1.75
diff -c -r1.75 subselect.c
*** src/backend/optimizer/plan/subselect.c    29 Apr 2003 22:13:09 -0000    1.75
--- src/backend/optimizer/plan/subselect.c    31 May 2003 20:38:27 -0000
***************
*** 67,73 ****

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    List **righthandIds);
  static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
  static Node *replace_correlation_vars_mutator(Node *node, void *context);
  static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
--- 67,73 ----

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    bool isExpr, List **righthandIds);
  static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
  static Node *replace_correlation_vars_mutator(Node *node, void *context);
  static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
***************
*** 240,245 ****
--- 240,251 ----
       */
      node->subLinkType = slink->subLinkType;
      node->useOr = slink->useOr;
+     node->isExpr = slink->isExpr;
+     node->exprtype = InvalidOid;
+     node->elemtype = InvalidOid;
+     node->elmlen = 0;
+     node->elmbyval = false;
+     node->elmalign = '\0';
      node->exprs = NIL;
      node->paramIds = NIL;
      node->useHashTable = false;
***************
*** 316,322 ****
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
--- 322,328 ----
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0, node->isExpr,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
***************
*** 399,405 ****
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0,
                                              &node->paramIds);

          /*
--- 405,411 ----
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0, node->isExpr,
                                              &node->paramIds);

          /*
***************
*** 444,450 ****
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
--- 450,456 ----
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       bool isExpr, List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
***************
*** 499,511 ****
           * are not expecting to have to resolve unknown Params, so
           * it's okay to pass a null pstate.)
           */
!         result = lappend(result,
!                          make_op_expr(NULL,
!                                       tup,
!                                       leftop,
!                                       rightop,
!                                       exprType(leftop),
!                                       te->resdom->restype));

          ReleaseSysCache(tup);

--- 505,542 ----
           * are not expecting to have to resolve unknown Params, so
           * it's okay to pass a null pstate.)
           */
!         if (!isExpr)
!         {
!             result = lappend(result,
!                              make_op_expr(NULL,
!                                           tup,
!                                           leftop,
!                                           rightop,
!                                           exprType(leftop),
!                                           te->resdom->restype));
!         }
!         else
!         {
!             Oid        exprtype = te->resdom->restype;
!             Oid        elemtype = get_element_type(exprtype);
!
!             if (elemtype != InvalidOid)
!                 result = lappend(result,
!                                  make_op_expr(NULL,
!                                               tup,
!                                               leftop,
!                                               rightop,
!                                               exprType(leftop),
!                                               elemtype));
!             else
!                 result = lappend(result,
!                                  make_op_expr(NULL,
!                                               tup,
!                                               leftop,
!                                               rightop,
!                                               exprType(leftop),
!                                               exprtype));
!         }

          ReleaseSysCache(tup);

***************
*** 616,628 ****
      /*
       * The sublink type must be "= ANY" --- that is, an IN operator.
       * (We require the operator name to be unqualified, which may be
!      * overly paranoid, or may not be.)
       */
      if (sublink->subLinkType != ANY_SUBLINK)
          return NULL;
      if (length(sublink->operName) != 1 ||
          strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
          return NULL;
      /*
       * The sub-select must not refer to any Vars of the parent query.
       * (Vars of higher levels should be okay, though.)
--- 647,663 ----
      /*
       * The sublink type must be "= ANY" --- that is, an IN operator.
       * (We require the operator name to be unqualified, which may be
!      * overly paranoid, or may not be.) It must not be an Expression
!      * sublink.
       */
      if (sublink->subLinkType != ANY_SUBLINK)
          return NULL;
      if (length(sublink->operName) != 1 ||
          strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
          return NULL;
+     if (sublink->isExpr)
+         return NULL;
+
      /*
       * The sub-select must not refer to any Vars of the parent query.
       * (Vars of higher levels should be okay, though.)
***************
*** 675,681 ****
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
--- 710,716 ----
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex, sublink->isExpr,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
Index: src/backend/parser/gram.y
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v
retrieving revision 2.416
diff -c -r2.416 gram.y
*** src/backend/parser/gram.y    29 May 2003 20:40:36 -0000    2.416
--- src/backend/parser/gram.y    31 May 2003 20:38:27 -0000
***************
*** 5490,5495 ****
--- 5490,5496 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ANY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = makeList1(makeString("="));
                      n->subselect = $3;
***************
*** 5500,5505 ****
--- 5501,5507 ----
                      /* Make an IN node */
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ANY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = makeList1(makeString("="));
                      n->subselect = $4;
***************
*** 5511,5516 ****
--- 5513,5519 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = $3;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $4;
***************
*** 5521,5526 ****
--- 5524,5530 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = MULTIEXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $3;
***************
*** 5904,5909 ****
--- 5908,5914 ----
                      {
                              SubLink *n = (SubLink *)$3;
                              n->subLinkType = ANY_SUBLINK;
+                             n->isExpr = false;
                              n->lefthand = makeList1($1);
                              n->operName = makeList1(makeString("="));
                              $$ = (Node *)n;
***************
*** 5931,5936 ****
--- 5936,5942 ----
                      {
                          /* Make an IN node */
                          SubLink *n = (SubLink *)$4;
+                         n->isExpr = false;
                          n->subLinkType = ANY_SUBLINK;
                          n->lefthand = makeList1($1);
                          n->operName = makeList1(makeString("="));
***************
*** 5957,5967 ****
--- 5963,6000 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = $3;
+                     n->isExpr = false;
                      n->lefthand = makeList1($1);
                      n->operName = $2;
                      n->subselect = $4;
                      $$ = (Node *)n;
                  }
+             | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
+                 {
+                     SubLink *n = makeNode(SubLink);
+                     SelectStmt *s = makeNode(SelectStmt);
+                     ResTarget *r = makeNode(ResTarget);
+
+                     r->name = NULL;
+                     r->indirection = NIL;
+                     r->val = (Node *)$5;
+
+                     s->distinctClause = NIL;
+                     s->targetList = makeList1(r);
+                     s->into = NULL;
+                     s->intoColNames = NIL;
+                     s->fromClause = NIL;
+                     s->whereClause = NULL;
+                     s->groupClause = NIL;
+                     s->havingClause = NULL;
+
+                     n->subLinkType = $3;
+                     n->isExpr = true;
+                     n->lefthand = makeList1($1);
+                     n->operName = $2;
+                     n->subselect = (Node *) s;
+                     $$ = (Node *)n;
+                 }
              | UNIQUE select_with_parens %prec Op
                  {
                      /* Not sure how to get rid of the parentheses
***************
*** 6538,6543 ****
--- 6571,6577 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $1;
***************
*** 6547,6552 ****
--- 6581,6587 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXISTS_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6556,6561 ****
--- 6591,6597 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ARRAY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6730,6735 ****
--- 6766,6772 ----
  in_expr:    select_with_parens
                  {
                      SubLink *n = makeNode(SubLink);
+                     n->isExpr = false;
                      n->subselect = $1;
                      /* other fields will be filled later */
                      $$ = (Node *)n;
Index: src/backend/parser/parse_expr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v
retrieving revision 1.148
diff -c -r1.148 parse_expr.c
*** src/backend/parser/parse_expr.c    29 Apr 2003 22:13:10 -0000    1.148
--- src/backend/parser/parse_expr.c    31 May 2003 20:38:27 -0000
***************
*** 436,441 ****
--- 436,442 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else if (sublink->subLinkType == EXPR_SUBLINK ||
                           sublink->subLinkType == ARRAY_SUBLINK)
***************
*** 463,468 ****
--- 464,470 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else
                  {
***************
*** 538,547 ****
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         optup = oper(op,
!                                      exprType(lexpr),
!                                      exprType((Node *) tent->expr),
!                                      false);
                          opform = (Form_pg_operator) GETSTRUCT(optup);

                          if (opform->oprresult != BOOLOID)
--- 540,569 ----
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         if (!sublink->isExpr)
!                         {
!                             optup = oper(op,
!                                          exprType(lexpr),
!                                          exprType((Node *) tent->expr),
!                                          false);
!                         }
!                         else
!                         {
!                             Oid        exprtype = exprType((Node *) tent->expr);
!                             Oid        elemtype = get_element_type(exprtype);
!
!                             if (elemtype != InvalidOid)
!                                 optup = oper(op,
!                                              exprType(lexpr),
!                                              elemtype,
!                                              false);
!                             else
!                                 optup = oper(op,
!                                              exprType(lexpr),
!                                              exprtype,
!                                              false);
!                         }
!
                          opform = (Form_pg_operator) GETSTRUCT(optup);

                          if (opform->oprresult != BOOLOID)
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
retrieving revision 1.82
diff -c -r1.82 primnodes.h
*** src/include/nodes/primnodes.h    6 May 2003 00:20:33 -0000    1.82
--- src/include/nodes/primnodes.h    31 May 2003 20:38:27 -0000
***************
*** 357,371 ****
  /* ----------------
   * SubLink
   *
!  * A SubLink represents a subselect appearing in an expression, and in some
!  * cases also the combining operator(s) just above it.    The subLinkType
!  * indicates the form of the expression represented:
   *    EXISTS_SUBLINK        EXISTS(SELECT ...)
   *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
   *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
   *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
   *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
   *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
   * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
   * same length as the subselect's targetlist.  MULTIEXPR will *always* have
   * a list with more than one entry; if the subselect has just one target
--- 357,375 ----
  /* ----------------
   * SubLink
   *
!  * A SubLink represents a subselect, or an expression, appearing in an
!  * expression, and in some cases also the combining operator(s) just above
!  * it.    The subLinkType indicates the form of the expression represented:
   *    EXISTS_SUBLINK        EXISTS(SELECT ...)
   *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
   *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
   *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
   *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
   *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
+  * If an expression is used in place of the subselect, it is transformed
+  * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be
+  * used as if they were the result of a single column subselect. If the
+  * expression is scalar, it is treated as a one element array.
   * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
   * same length as the subselect's targetlist.  MULTIEXPR will *always* have
   * a list with more than one entry; if the subselect has just one target
***************
*** 414,419 ****
--- 418,425 ----
      SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
      bool        useOr;            /* TRUE to combine column results with
                                   * "OR" not "AND" */
+     bool        isExpr;            /* TRUE if the subselect is really derived
+                                  * from a single expression */
      List       *lefthand;        /* list of outer-query expressions on the
                                   * left */
      List       *operName;        /* originally specified operator name */
***************
*** 455,460 ****
--- 461,475 ----
      SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
      bool        useOr;            /* TRUE to combine column results with
                                   * "OR" not "AND" */
+     bool        isExpr;            /* TRUE if the subselect is really derived
+                                  * from a single expression */
+     /* runtime cache for single array expressions */
+     Oid            exprtype;        /* array and element type, and other info
+                                  * needed deconstruct the array */
+     Oid            elemtype;
+     int16        elmlen;
+     bool        elmbyval;
+     char        elmalign;
      /* The combining operators, transformed to executable expressions: */
      List       *exprs;            /* list of OpExpr expression trees */
      List       *paramIds;        /* IDs of Params embedded in the above */

pgsql-patches by date:

Previous
From: Tom Lane
Date:
Subject: Re: [HACKERS] Are we losing momentum?
Next
From: Joe Conway
Date:
Subject: Re: array support patch phase 1 patch