Tom Lane wrote:
I think he's got a good point, actually. We document the ARRAY-with-
parens-around-a-SELECT syntax as
The resulting one-dimensional array will have an element for
each row in the subquery result, with an element type matching
that of the subquery's output column.
To me, that implies that a subquery result of no rows generates a
one-dimensional array of no elements, not a null array.
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?
The point Markus is complaining about seems like it should
be easily fixable.
Well, "easily" is a relative term. My Postgres hacking neurons have
gotten kind of rusty lately -- but then maybe that was your underlying
point ;-)
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