Re: [SQL] ARRAY() returning NULL instead of ARRAY[] resp. {} - Mailing list pgsql-patches

From Joe Conway
Subject Re: [SQL] ARRAY() returning NULL instead of ARRAY[] resp. {}
Date
Msg-id 429CD7A4.7020702@joeconway.com
Whole thread Raw
Responses Re: [SQL] ARRAY() returning NULL instead of ARRAY[] resp. {}  (Tom Lane <tgl@sss.pgh.pa.us>)
List pgsql-patches
Joe Conway wrote:
> OK, looks like I'm outnumbered.
>
> But as far as I know, we have never had a way to produce a
> one-dimensional empty array. Empty arrays thus far have been dimensionless.
>
> Assuming we really want an empty 1D array, I created the attached patch.
> This works fine, but now leaves a few oddities to be dealt with, e.g.:
>
> regression=# select array_dims(array(select 1 where false));
>  array_dims
> ------------
>  [1:0]
> (1 row)
>
> Any thoughts on how this should be handled for an empty 1D array?

Any thoughts or objections? If not, I'll commit the attached in a day or so.

Thanks,

Joe



> ------------------------------------------------------------------------
>
> Index: src/backend/executor/nodeSubplan.c
> ===================================================================
> RCS file: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v
> retrieving revision 1.69
> diff -c -r1.69 nodeSubplan.c
> *** src/backend/executor/nodeSubplan.c    6 May 2005 17:24:54 -0000    1.69
> --- src/backend/executor/nodeSubplan.c    26 May 2005 18:52:16 -0000
> ***************
> *** 215,220 ****
> --- 215,221 ----
>       ListCell   *pvar;
>       ListCell   *l;
>       ArrayBuildState *astate = NULL;
> +     Oid            element_type = planstate->ps_ResultTupleSlot->tts_tupleDescriptor->attrs[0]->atttypid;
>
>       /*
>        * We are probably in a short-lived expression-evaluation context.
> ***************
> *** 259,268 ****
>        *
>        * For EXPR_SUBLINK we require the subplan to produce no more than one
>        * tuple, else an error is raised. For ARRAY_SUBLINK we allow the
> !      * subplan to produce more than one tuple. In either case, if zero
> !      * tuples are produced, we return NULL. Assuming we get a tuple, we
> !      * just use its first column (there can be only one non-junk column in
> !      * this case).
>        */
>       result = BoolGetDatum(subLinkType == ALL_SUBLINK);
>       *isNull = false;
> --- 260,269 ----
>        *
>        * For EXPR_SUBLINK we require the subplan to produce no more than one
>        * tuple, else an error is raised. For ARRAY_SUBLINK we allow the
> !      * subplan to produce more than one tuple. In the former case, if zero
> !      * tuples are produced, we return NULL. In the latter, we return an
> !      * empty array. Assuming we get a tuple, we just use its first column
> !      * (there can be only one non-junk column in this case).
>        */
>       result = BoolGetDatum(subLinkType == ALL_SUBLINK);
>       *isNull = false;
> ***************
> *** 432,458 ****
>           }
>       }
>
> !     if (!found)
>       {
>           /*
>            * deal with empty subplan result.    result/isNull were previously
> !          * initialized correctly for all sublink types except EXPR, ARRAY,
>            * and MULTIEXPR; for those, return NULL.
>            */
>           if (subLinkType == EXPR_SUBLINK ||
> -             subLinkType == ARRAY_SUBLINK ||
>               subLinkType == MULTIEXPR_SUBLINK)
>           {
>               result = (Datum) 0;
>               *isNull = true;
>           }
>       }
> -     else if (subLinkType == ARRAY_SUBLINK)
> -     {
> -         Assert(astate != NULL);
> -         /* We return the result in the caller's context */
> -         result = makeArrayResult(astate, oldcontext);
> -     }
>
>       MemoryContextSwitchTo(oldcontext);
>
> --- 433,459 ----
>           }
>       }
>
> !     if (subLinkType == ARRAY_SUBLINK)
> !     {
> !         if (!astate)
> !             astate = initArrayResult(element_type, oldcontext);
> !         /* We return the result in the caller's context */
> !         result = makeArrayResult(astate, oldcontext);
> !     }
> !     else if (!found)
>       {
>           /*
>            * deal with empty subplan result.    result/isNull were previously
> !          * initialized correctly for all sublink types except EXPR
>            * and MULTIEXPR; for those, return NULL.
>            */
>           if (subLinkType == EXPR_SUBLINK ||
>               subLinkType == MULTIEXPR_SUBLINK)
>           {
>               result = (Datum) 0;
>               *isNull = true;
>           }
>       }
>
>       MemoryContextSwitchTo(oldcontext);
>
> ***************
> *** 925,930 ****
> --- 926,932 ----
>       ListCell   *l;
>       bool        found = false;
>       ArrayBuildState *astate = NULL;
> +     Oid            element_type = planstate->ps_ResultTupleSlot->tts_tupleDescriptor->attrs[0]->atttypid;
>
>       /*
>        * Must switch to child query's per-query memory context.
> ***************
> *** 1010,1016 ****
>           }
>       }
>
> !     if (!found)
>       {
>           if (subLinkType == EXISTS_SUBLINK)
>           {
> --- 1012,1033 ----
>           }
>       }
>
> !     if (subLinkType == ARRAY_SUBLINK)
> !     {
> !         /* There can be only one param... */
> !         int            paramid = linitial_int(subplan->setParam);
> !         ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
> !
> !         prm->execPlan = NULL;
> !
> !         if (!astate)
> !             astate = initArrayResult(element_type, oldcontext);
> !
> !         /* We build the result in query context so it won't disappear */
> !         prm->value = makeArrayResult(astate, econtext->ecxt_per_query_memory);
> !         prm->isnull = false;
> !     }
> !     else if (!found)
>       {
>           if (subLinkType == EXISTS_SUBLINK)
>           {
> ***************
> *** 1035,1052 ****
>               }
>           }
>       }
> -     else if (subLinkType == ARRAY_SUBLINK)
> -     {
> -         /* There can be only one param... */
> -         int            paramid = linitial_int(subplan->setParam);
> -         ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
> -
> -         Assert(astate != NULL);
> -         prm->execPlan = NULL;
> -         /* We build the result in query context so it won't disappear */
> -         prm->value = makeArrayResult(astate, econtext->ecxt_per_query_memory);
> -         prm->isnull = false;
> -     }
>
>       MemoryContextSwitchTo(oldcontext);
>   }
> --- 1052,1057 ----
> Index: src/backend/utils/adt/arrayfuncs.c
> ===================================================================
> RCS file: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v
> retrieving revision 1.120
> diff -c -r1.120 arrayfuncs.c
> *** src/backend/utils/adt/arrayfuncs.c    1 May 2005 18:56:18 -0000    1.120
> --- src/backend/utils/adt/arrayfuncs.c    26 May 2005 18:52:16 -0000
> ***************
> *** 3252,3257 ****
> --- 3252,3293 ----
>                        &my_extra->amstate);
>   }
>
> +
> + /*
> +  * initArrayResult - initialize an ArrayBuildState for an array result
> +  *
> +  *    rcontext is where to keep working state
> +  */
> + ArrayBuildState *
> + initArrayResult(Oid element_type, MemoryContext rcontext)
> + {
> +     ArrayBuildState       *astate;
> +     MemoryContext        arr_context,
> +                         oldcontext;
> +
> +     /* Make a temporary context to hold all the junk */
> +     arr_context = AllocSetContextCreate(rcontext,
> +                                         "accumArrayResult",
> +                                         ALLOCSET_DEFAULT_MINSIZE,
> +                                         ALLOCSET_DEFAULT_INITSIZE,
> +                                         ALLOCSET_DEFAULT_MAXSIZE);
> +     oldcontext = MemoryContextSwitchTo(arr_context);
> +     astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
> +     astate->mcontext = arr_context;
> +     astate->dvalues = (Datum *)
> +         palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
> +     astate->nelems = 0;
> +     astate->element_type = element_type;
> +     get_typlenbyvalalign(element_type,
> +                             &astate->typlen,
> +                             &astate->typbyval,
> +                             &astate->typalign);
> +
> +     MemoryContextSwitchTo(oldcontext);
> +
> +     return astate;
> + }
> +
>   /*
>    * accumArrayResult - accumulate one (more) Datum for an array result
>    *
> ***************
> *** 3264,3293 ****
>                    Oid element_type,
>                    MemoryContext rcontext)
>   {
> !     MemoryContext arr_context,
> !                 oldcontext;
>
>       if (astate == NULL)
>       {
>           /* First time through --- initialize */
> !
> !         /* Make a temporary context to hold all the junk */
> !         arr_context = AllocSetContextCreate(rcontext,
> !                                             "accumArrayResult",
> !                                             ALLOCSET_DEFAULT_MINSIZE,
> !                                             ALLOCSET_DEFAULT_INITSIZE,
> !                                             ALLOCSET_DEFAULT_MAXSIZE);
> !         oldcontext = MemoryContextSwitchTo(arr_context);
> !         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
> !         astate->mcontext = arr_context;
> !         astate->dvalues = (Datum *)
> !             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
> !         astate->nelems = 0;
> !         astate->element_type = element_type;
> !         get_typlenbyvalalign(element_type,
> !                              &astate->typlen,
> !                              &astate->typbyval,
> !                              &astate->typalign);
>       }
>       else
>       {
> --- 3300,3311 ----
>                    Oid element_type,
>                    MemoryContext rcontext)
>   {
> !     MemoryContext oldcontext;
>
>       if (astate == NULL)
>       {
>           /* First time through --- initialize */
> !         astate = initArrayResult(element_type, rcontext);
>       }
>       else
>       {
> Index: src/include/utils/array.h
> ===================================================================
> RCS file: /cvsroot/pgsql/src/include/utils/array.h,v
> retrieving revision 1.54
> diff -c -r1.54 array.h
> *** src/include/utils/array.h    29 Mar 2005 00:17:18 -0000    1.54
> --- src/include/utils/array.h    26 May 2005 18:52:16 -0000
> ***************
> *** 176,181 ****
> --- 176,182 ----
>                     Oid elmtype,
>                     int elmlen, bool elmbyval, char elmalign,
>                     Datum **elemsp, int *nelemsp);
> + extern ArrayBuildState *initArrayResult(Oid element_type, MemoryContext rcontext);
>   extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
>                    Datum dvalue, bool disnull,
>                    Oid element_type,
>
>
> ------------------------------------------------------------------------
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 6: Have you searched our list archives?
>
>                http://archives.postgresql.org


pgsql-patches by date:

Previous
From: Prospect Response Newsletter
Date:
Subject: TODAY Last Day For SuccessPub 50% Off
Next
From: Tom Lane
Date:
Subject: Re: [SQL] ARRAY() returning NULL instead of ARRAY[] resp. {}