Commit bac27394 authored by Tom Lane's avatar Tom Lane

Support arrays as input to array_agg() and ARRAY(SELECT ...).

These cases formerly failed with errors about "could not find array type
for data type".  Now they yield arrays of the same element type and one
higher dimension.

The implementation involves creating functions with API similar to the
existing accumArrayResult() family.  I (tgl) also extended the base family
by adding an initArrayResult() function, which allows callers to avoid
special-casing the zero-inputs case if they just want an empty array as
result.  (Not all do, so the previous calling convention remains valid.)
This allowed simplifying some existing code in xml.c and plperl.c.

Ali Akbar, reviewed by Pavel Stehule, significantly modified by me
parent 25976710
...@@ -12035,7 +12035,7 @@ NULL baz</literallayout>(3 rows)</entry> ...@@ -12035,7 +12035,7 @@ NULL baz</literallayout>(3 rows)</entry>
<function>array_agg(<replaceable class="parameter">expression</replaceable>)</function> <function>array_agg(<replaceable class="parameter">expression</replaceable>)</function>
</entry> </entry>
<entry> <entry>
any any non-array type
</entry> </entry>
<entry> <entry>
array of the argument type array of the argument type
...@@ -12043,6 +12043,21 @@ NULL baz</literallayout>(3 rows)</entry> ...@@ -12043,6 +12043,21 @@ NULL baz</literallayout>(3 rows)</entry>
<entry>input values, including nulls, concatenated into an array</entry> <entry>input values, including nulls, concatenated into an array</entry>
</row> </row>
<row>
<entry>
<function>array_agg(<replaceable class="parameter">expression</replaceable>)</function>
</entry>
<entry>
any array type
</entry>
<entry>
same as argument data type
</entry>
<entry>input arrays concatenated into array of one higher dimension
(inputs must all have same dimensionality,
and cannot be empty or NULL)</entry>
</row>
<row> <row>
<entry> <entry>
<indexterm> <indexterm>
......
...@@ -2239,11 +2239,22 @@ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%'); ...@@ -2239,11 +2239,22 @@ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
----------------------------------------------------------------------- -----------------------------------------------------------------------
{2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31,2412,2413} {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31,2412,2413}
(1 row) (1 row)
SELECT ARRAY(SELECT ARRAY[i, i*2] FROM generate_series(1,5) AS a(i));
array
----------------------------------
{{1,2},{2,4},{3,6},{4,8},{5,10}}
(1 row)
</programlisting> </programlisting>
The subquery must return a single column. The resulting The subquery must return a single column.
If the subquery's output column is of a non-array type, the resulting
one-dimensional array will have an element for each row in the one-dimensional array will have an element for each row in the
subquery result, with an element type matching that of the subquery result, with an element type matching that of the
subquery's output column. subquery's output column.
If the subquery's output column is of an array type, the result will be
an array of the same type but one higher dimension; in this case all
the subquery rows must yield arrays of identical dimensionality, else
the result would not be rectangular.
</para> </para>
<para> <para>
......
...@@ -359,12 +359,12 @@ SELECT attrelid::regclass, array_accum(atttypid::regtype) ...@@ -359,12 +359,12 @@ SELECT attrelid::regclass, array_accum(atttypid::regtype)
aggregate <function>array_agg</> is equivalent to aggregate <function>array_agg</> is equivalent to
<programlisting> <programlisting>
CREATE FUNCTION array_agg_transfn(internal, anyelement) CREATE FUNCTION array_agg_transfn(internal, anynonarray)
RETURNS internal ...; RETURNS internal ...;
CREATE FUNCTION array_agg_finalfn(internal, anyelement) CREATE FUNCTION array_agg_finalfn(internal, anynonarray)
RETURNS anyarray ...; RETURNS anyarray ...;
CREATE AGGREGATE array_agg (anyelement) CREATE AGGREGATE array_agg (anynonarray)
( (
sfunc = array_agg_transfn, sfunc = array_agg_transfn,
stype = internal, stype = internal,
...@@ -376,7 +376,7 @@ CREATE AGGREGATE array_agg (anyelement) ...@@ -376,7 +376,7 @@ CREATE AGGREGATE array_agg (anyelement)
Here, the <literal>finalfunc_extra</> option specifies that the final Here, the <literal>finalfunc_extra</> option specifies that the final
function receives, in addition to the state value, extra dummy function receives, in addition to the state value, extra dummy
argument(s) corresponding to the aggregate's input argument(s). argument(s) corresponding to the aggregate's input argument(s).
The extra <type>anyelement</> argument allows the declaration The extra <type>anynonarray</> argument allows the declaration
of <function>array_agg_finalfn</> to be valid. of <function>array_agg_finalfn</> to be valid.
</para> </para>
......
...@@ -231,7 +231,7 @@ ExecScanSubPlan(SubPlanState *node, ...@@ -231,7 +231,7 @@ ExecScanSubPlan(SubPlanState *node,
bool found = false; /* TRUE if got at least one subplan tuple */ bool found = false; /* TRUE if got at least one subplan tuple */
ListCell *pvar; ListCell *pvar;
ListCell *l; ListCell *l;
ArrayBuildState *astate = NULL; ArrayBuildStateAny *astate = NULL;
/* /*
* MULTIEXPR subplans, when "executed", just return NULL; but first we * MULTIEXPR subplans, when "executed", just return NULL; but first we
...@@ -259,6 +259,11 @@ ExecScanSubPlan(SubPlanState *node, ...@@ -259,6 +259,11 @@ ExecScanSubPlan(SubPlanState *node,
return (Datum) 0; return (Datum) 0;
} }
/* Initialize ArrayBuildStateAny in caller's context, if needed */
if (subLinkType == ARRAY_SUBLINK)
astate = initArrayResultAny(subplan->firstColType,
CurrentMemoryContext);
/* /*
* We are probably in a short-lived expression-evaluation context. Switch * We are probably in a short-lived expression-evaluation context. Switch
* to the per-query context for manipulating the child plan's chgParam, * to the per-query context for manipulating the child plan's chgParam,
...@@ -366,7 +371,7 @@ ExecScanSubPlan(SubPlanState *node, ...@@ -366,7 +371,7 @@ ExecScanSubPlan(SubPlanState *node,
/* stash away current value */ /* stash away current value */
Assert(subplan->firstColType == tdesc->attrs[0]->atttypid); Assert(subplan->firstColType == tdesc->attrs[0]->atttypid);
dvalue = slot_getattr(slot, 1, &disnull); dvalue = slot_getattr(slot, 1, &disnull);
astate = accumArrayResult(astate, dvalue, disnull, astate = accumArrayResultAny(astate, dvalue, disnull,
subplan->firstColType, oldcontext); subplan->firstColType, oldcontext);
/* keep scanning subplan to collect all values */ /* keep scanning subplan to collect all values */
continue; continue;
...@@ -437,10 +442,7 @@ ExecScanSubPlan(SubPlanState *node, ...@@ -437,10 +442,7 @@ ExecScanSubPlan(SubPlanState *node,
if (subLinkType == ARRAY_SUBLINK) if (subLinkType == ARRAY_SUBLINK)
{ {
/* We return the result in the caller's context */ /* We return the result in the caller's context */
if (astate != NULL) result = makeArrayResultAny(astate, oldcontext, true);
result = makeArrayResult(astate, oldcontext);
else
result = PointerGetDatum(construct_empty_array(subplan->firstColType));
} }
else if (!found) else if (!found)
{ {
...@@ -951,7 +953,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) ...@@ -951,7 +953,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
ListCell *pvar; ListCell *pvar;
ListCell *l; ListCell *l;
bool found = false; bool found = false;
ArrayBuildState *astate = NULL; ArrayBuildStateAny *astate = NULL;
if (subLinkType == ANY_SUBLINK || if (subLinkType == ANY_SUBLINK ||
subLinkType == ALL_SUBLINK) subLinkType == ALL_SUBLINK)
...@@ -959,6 +961,11 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) ...@@ -959,6 +961,11 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
if (subLinkType == CTE_SUBLINK) if (subLinkType == CTE_SUBLINK)
elog(ERROR, "CTE subplans should not be executed via ExecSetParamPlan"); elog(ERROR, "CTE subplans should not be executed via ExecSetParamPlan");
/* Initialize ArrayBuildStateAny in caller's context, if needed */
if (subLinkType == ARRAY_SUBLINK)
astate = initArrayResultAny(subplan->firstColType,
CurrentMemoryContext);
/* /*
* Must switch to per-query memory context. * Must switch to per-query memory context.
*/ */
...@@ -1018,7 +1025,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) ...@@ -1018,7 +1025,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
/* stash away current value */ /* stash away current value */
Assert(subplan->firstColType == tdesc->attrs[0]->atttypid); Assert(subplan->firstColType == tdesc->attrs[0]->atttypid);
dvalue = slot_getattr(slot, 1, &disnull); dvalue = slot_getattr(slot, 1, &disnull);
astate = accumArrayResult(astate, dvalue, disnull, astate = accumArrayResultAny(astate, dvalue, disnull,
subplan->firstColType, oldcontext); subplan->firstColType, oldcontext);
/* keep scanning subplan to collect all values */ /* keep scanning subplan to collect all values */
continue; continue;
...@@ -1072,14 +1079,9 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) ...@@ -1072,14 +1079,9 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
*/ */
if (node->curArray != PointerGetDatum(NULL)) if (node->curArray != PointerGetDatum(NULL))
pfree(DatumGetPointer(node->curArray)); pfree(DatumGetPointer(node->curArray));
if (astate != NULL) node->curArray = makeArrayResultAny(astate,
node->curArray = makeArrayResult(astate, econtext->ecxt_per_query_memory,
econtext->ecxt_per_query_memory); true);
else
{
MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
node->curArray = PointerGetDatum(construct_empty_array(subplan->firstColType));
}
prm->execPlan = NULL; prm->execPlan = NULL;
prm->value = node->curArray; prm->value = node->curArray;
prm->isnull = false; prm->isnull = false;
......
...@@ -108,7 +108,7 @@ exprType(const Node *expr) ...@@ -108,7 +108,7 @@ exprType(const Node *expr)
type = exprType((Node *) tent->expr); type = exprType((Node *) tent->expr);
if (sublink->subLinkType == ARRAY_SUBLINK) if (sublink->subLinkType == ARRAY_SUBLINK)
{ {
type = get_array_type(type); type = get_promoted_array_type(type);
if (!OidIsValid(type)) if (!OidIsValid(type))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT), (errcode(ERRCODE_UNDEFINED_OBJECT),
...@@ -139,7 +139,7 @@ exprType(const Node *expr) ...@@ -139,7 +139,7 @@ exprType(const Node *expr)
type = subplan->firstColType; type = subplan->firstColType;
if (subplan->subLinkType == ARRAY_SUBLINK) if (subplan->subLinkType == ARRAY_SUBLINK)
{ {
type = get_array_type(type); type = get_promoted_array_type(type);
if (!OidIsValid(type)) if (!OidIsValid(type))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT), (errcode(ERRCODE_UNDEFINED_OBJECT),
......
...@@ -668,7 +668,7 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, ...@@ -668,7 +668,7 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
Assert(!te->resjunk); Assert(!te->resjunk);
Assert(testexpr == NULL); Assert(testexpr == NULL);
arraytype = get_array_type(exprType((Node *) te->expr)); arraytype = get_promoted_array_type(exprType((Node *) te->expr));
if (!OidIsValid(arraytype)) if (!OidIsValid(arraytype))
elog(ERROR, "could not find array type for datatype %s", elog(ERROR, "could not find array type for datatype %s",
format_type_be(exprType((Node *) te->expr))); format_type_be(exprType((Node *) te->expr)));
......
...@@ -471,7 +471,7 @@ create_singleton_array(FunctionCallInfo fcinfo, ...@@ -471,7 +471,7 @@ create_singleton_array(FunctionCallInfo fcinfo,
/* /*
* ARRAY_AGG aggregate function * ARRAY_AGG(anynonarray) aggregate function
*/ */
Datum Datum
array_agg_transfn(PG_FUNCTION_ARGS) array_agg_transfn(PG_FUNCTION_ARGS)
...@@ -486,6 +486,12 @@ array_agg_transfn(PG_FUNCTION_ARGS) ...@@ -486,6 +486,12 @@ array_agg_transfn(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not determine input data type"))); errmsg("could not determine input data type")));
/*
* Note: we do not need a run-time check about whether arg1_typeid is a
* valid array element type, because the parser would have verified that
* while resolving the input/result types of this polymorphic aggregate.
*/
if (!AggCheckCallContext(fcinfo, &aggcontext)) if (!AggCheckCallContext(fcinfo, &aggcontext))
{ {
/* cannot be called directly because of internal-type argument */ /* cannot be called directly because of internal-type argument */
...@@ -516,18 +522,13 @@ array_agg_finalfn(PG_FUNCTION_ARGS) ...@@ -516,18 +522,13 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
int dims[1]; int dims[1];
int lbs[1]; int lbs[1];
/*
* Test for null before Asserting we are in right context. This is to
* avoid possible Assert failure in 8.4beta installations, where it is
* possible for users to create NULL constants of type internal.
*/
if (PG_ARGISNULL(0))
PG_RETURN_NULL(); /* returns null iff no input values */
/* cannot be called directly because of internal-type argument */ /* cannot be called directly because of internal-type argument */
Assert(AggCheckCallContext(fcinfo, NULL)); Assert(AggCheckCallContext(fcinfo, NULL));
state = (ArrayBuildState *) PG_GETARG_POINTER(0); state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
if (state == NULL)
PG_RETURN_NULL(); /* returns null iff no input values */
dims[0] = state->nelems; dims[0] = state->nelems;
lbs[0] = 1; lbs[0] = 1;
...@@ -544,3 +545,70 @@ array_agg_finalfn(PG_FUNCTION_ARGS) ...@@ -544,3 +545,70 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(result); PG_RETURN_DATUM(result);
} }
/*
* ARRAY_AGG(anyarray) aggregate function
*/
Datum
array_agg_array_transfn(PG_FUNCTION_ARGS)
{
Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
MemoryContext aggcontext;
ArrayBuildStateArr *state;
if (arg1_typeid == InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not determine input data type")));
/*
* Note: we do not need a run-time check about whether arg1_typeid is a
* valid array type, because the parser would have verified that while
* resolving the input/result types of this polymorphic aggregate.
*/
if (!AggCheckCallContext(fcinfo, &aggcontext))
{
/* cannot be called directly because of internal-type argument */
elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
}
state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
state = accumArrayResultArr(state,
PG_GETARG_DATUM(1),
PG_ARGISNULL(1),
arg1_typeid,
aggcontext);
/*
* The transition type for array_agg() is declared to be "internal", which
* is a pass-by-value type the same size as a pointer. So we can safely
* pass the ArrayBuildStateArr pointer through nodeAgg.c's machinations.
*/
PG_RETURN_POINTER(state);
}
Datum
array_agg_array_finalfn(PG_FUNCTION_ARGS)
{
Datum result;
ArrayBuildStateArr *state;
/* cannot be called directly because of internal-type argument */
Assert(AggCheckCallContext(fcinfo, NULL));
state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
if (state == NULL)
PG_RETURN_NULL(); /* returns null iff no input values */
/*
* Make the result. We cannot release the ArrayBuildStateArr because
* sometimes aggregate final functions are re-executed. Rather, it is
* nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
* so.
*/
result = makeArrayResultArr(state, CurrentMemoryContext, false);
PG_RETURN_DATUM(result);
}
This diff is collapsed.
...@@ -143,7 +143,7 @@ static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg, ...@@ -143,7 +143,7 @@ static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg,
bool preserve_whitespace, int encoding); bool preserve_whitespace, int encoding);
static text *xml_xmlnodetoxmltype(xmlNodePtr cur); static text *xml_xmlnodetoxmltype(xmlNodePtr cur);
static int xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj, static int xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
ArrayBuildState **astate); ArrayBuildState *astate);
#endif /* USE_LIBXML */ #endif /* USE_LIBXML */
static StringInfo query_to_xml_internal(const char *query, char *tablename, static StringInfo query_to_xml_internal(const char *query, char *tablename,
...@@ -3648,7 +3648,7 @@ xml_xmlnodetoxmltype(xmlNodePtr cur) ...@@ -3648,7 +3648,7 @@ xml_xmlnodetoxmltype(xmlNodePtr cur)
/* /*
* Convert an XML XPath object (the result of evaluating an XPath expression) * Convert an XML XPath object (the result of evaluating an XPath expression)
* to an array of xml values, which is returned at *astate. The function * to an array of xml values, which are appended to astate. The function
* result value is the number of elements in the array. * result value is the number of elements in the array.
* *
* If "astate" is NULL then we don't generate the array value, but we still * If "astate" is NULL then we don't generate the array value, but we still
...@@ -3660,16 +3660,13 @@ xml_xmlnodetoxmltype(xmlNodePtr cur) ...@@ -3660,16 +3660,13 @@ xml_xmlnodetoxmltype(xmlNodePtr cur)
*/ */
static int static int
xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj, xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
ArrayBuildState **astate) ArrayBuildState *astate)
{ {
int result = 0; int result = 0;
Datum datum; Datum datum;
Oid datumtype; Oid datumtype;
char *result_str; char *result_str;
if (astate != NULL)
*astate = NULL;
switch (xpathobj->type) switch (xpathobj->type)
{ {
case XPATH_NODESET: case XPATH_NODESET:
...@@ -3683,9 +3680,8 @@ xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj, ...@@ -3683,9 +3680,8 @@ xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
for (i = 0; i < result; i++) for (i = 0; i < result; i++)
{ {
datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i])); datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i]));
*astate = accumArrayResult(*astate, datum, (void) accumArrayResult(astate, datum, false,
false, XMLOID, XMLOID, CurrentMemoryContext);
CurrentMemoryContext);
} }
} }
} }
...@@ -3721,9 +3717,8 @@ xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj, ...@@ -3721,9 +3717,8 @@ xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
/* Common code for scalar-value cases */ /* Common code for scalar-value cases */
result_str = map_sql_value_to_xml_value(datum, datumtype, true); result_str = map_sql_value_to_xml_value(datum, datumtype, true);
datum = PointerGetDatum(cstring_to_xmltype(result_str)); datum = PointerGetDatum(cstring_to_xmltype(result_str));
*astate = accumArrayResult(*astate, datum, (void) accumArrayResult(astate, datum, false,
false, XMLOID, XMLOID, CurrentMemoryContext);
CurrentMemoryContext);
return 1; return 1;
} }
...@@ -3741,7 +3736,7 @@ xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj, ...@@ -3741,7 +3736,7 @@ xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
*/ */
static void static void
xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces, xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
int *res_nitems, ArrayBuildState **astate) int *res_nitems, ArrayBuildState *astate)
{ {
PgXmlErrorContext *xmlerrcxt; PgXmlErrorContext *xmlerrcxt;
volatile xmlParserCtxtPtr ctxt = NULL; volatile xmlParserCtxtPtr ctxt = NULL;
...@@ -3933,15 +3928,11 @@ xpath(PG_FUNCTION_ARGS) ...@@ -3933,15 +3928,11 @@ xpath(PG_FUNCTION_ARGS)
text *xpath_expr_text = PG_GETARG_TEXT_P(0); text *xpath_expr_text = PG_GETARG_TEXT_P(0);
xmltype *data = PG_GETARG_XML_P(1); xmltype *data = PG_GETARG_XML_P(1);
ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2); ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
int res_nitems;
ArrayBuildState *astate; ArrayBuildState *astate;
astate = initArrayResult(XMLOID, CurrentMemoryContext);
xpath_internal(xpath_expr_text, data, namespaces, xpath_internal(xpath_expr_text, data, namespaces,
&res_nitems, &astate); NULL, astate);
if (res_nitems == 0)
PG_RETURN_ARRAYTYPE_P(construct_empty_array(XMLOID));
else
PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext)); PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
#else #else
NO_XML_SUPPORT(); NO_XML_SUPPORT();
......
...@@ -2354,6 +2354,27 @@ get_array_type(Oid typid) ...@@ -2354,6 +2354,27 @@ get_array_type(Oid typid)
return result; return result;
} }
/*
* get_promoted_array_type
*
* The "promoted" type is what you'd get from an ARRAY(SELECT ...)
* construct, that is, either the corresponding "true" array type
* if the input is a scalar type that has such an array type,
* or the same type if the input is already a "true" array type.
* Returns InvalidOid if neither rule is satisfied.
*/
Oid
get_promoted_array_type(Oid typid)
{
Oid array_type = get_array_type(typid);
if (OidIsValid(array_type))
return array_type;
if (OidIsValid(get_element_type(typid)))
return typid;
return InvalidOid;
}
/* /*
* get_base_element_type * get_base_element_type
* Given the type OID, get the typelem, looking "through" any domain * Given the type OID, get the typelem, looking "through" any domain
......
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 201411111 #define CATALOG_VERSION_NO 201411251
#endif #endif
...@@ -275,6 +275,7 @@ DATA(insert ( 2901 n 0 xmlconcat2 - - - - f f 0 142 0 0 0 _null_ ...@@ -275,6 +275,7 @@ DATA(insert ( 2901 n 0 xmlconcat2 - - - - f f 0 142 0 0 0 _null_
/* array */ /* array */
DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ )); DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
DATA(insert ( 4053 n 0 array_agg_array_transfn array_agg_array_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
/* text */ /* text */
DATA(insert ( 3538 n 0 string_agg_transfn string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ )); DATA(insert ( 3538 n 0 string_agg_transfn string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ ));
......
...@@ -908,11 +908,17 @@ DATA(insert OID = 3167 ( array_remove PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 ...@@ -908,11 +908,17 @@ DATA(insert OID = 3167 ( array_remove PGNSP PGUID 12 1 0 0 0 f f f f f f i 2
DESCR("remove any occurrences of an element from an array"); DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ )); DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array"); DESCR("replace any occurrences of an element in an array");
DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2283" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ )); DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2776" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function"); DESCR("aggregate transition function");
DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2283" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ )); DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2776" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function"); DESCR("aggregate final function");
DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ )); DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2776" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 4051 ( array_agg_array_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2277" _null_ _null_ _null_ _null_ array_agg_array_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
DATA(insert OID = 4052 ( array_agg_array_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2277" _null_ _null_ _null_ _null_ array_agg_array_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
DATA(insert OID = 4053 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array"); DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ )); DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds"); DESCR("bucket number of operand given a sorted array of bucket lower bounds");
......
...@@ -76,6 +76,7 @@ typedef struct ...@@ -76,6 +76,7 @@ typedef struct
/* /*
* working state for accumArrayResult() and friends * working state for accumArrayResult() and friends
* note that the input must be scalars (legal array elements)
*/ */
typedef struct ArrayBuildState typedef struct ArrayBuildState
{ {
...@@ -90,6 +91,37 @@ typedef struct ArrayBuildState ...@@ -90,6 +91,37 @@ typedef struct ArrayBuildState
char typalign; char typalign;
} ArrayBuildState; } ArrayBuildState;
/*
* working state for accumArrayResultArr() and friends
* note that the input must be arrays, and the same array type is returned
*/
typedef struct ArrayBuildStateArr
{
MemoryContext mcontext; /* where all the temp stuff is kept */
char *data; /* accumulated data */
bits8 *nullbitmap; /* bitmap of is-null flags, or NULL if none */
int abytes; /* allocated length of "data" */
int nbytes; /* number of bytes used so far */
int aitems; /* allocated length of bitmap (in elements) */
int nitems; /* total number of elements in result */
int ndims; /* current dimensions of result */
int dims[MAXDIM];
int lbs[MAXDIM];
Oid array_type; /* data type of the arrays */
Oid element_type; /* data type of the array elements */
} ArrayBuildStateArr;
/*
* working state for accumArrayResultAny() and friends
* these functions handle both cases
*/
typedef struct ArrayBuildStateAny
{
/* Exactly one of these is not NULL: */
ArrayBuildState *scalarstate;
ArrayBuildStateArr *arraystate;
} ArrayBuildStateAny;
/* /*
* structure to cache type metadata needed for array manipulation * structure to cache type metadata needed for array manipulation
*/ */
...@@ -252,6 +284,9 @@ extern void deconstruct_array(ArrayType *array, ...@@ -252,6 +284,9 @@ extern void deconstruct_array(ArrayType *array,
int elmlen, bool elmbyval, char elmalign, int elmlen, bool elmbyval, char elmalign,
Datum **elemsp, bool **nullsp, int *nelemsp); Datum **elemsp, bool **nullsp, int *nelemsp);
extern bool array_contains_nulls(ArrayType *array); extern bool array_contains_nulls(ArrayType *array);
extern ArrayBuildState *initArrayResult(Oid element_type,
MemoryContext rcontext);
extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate, extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
Datum dvalue, bool disnull, Datum dvalue, bool disnull,
Oid element_type, Oid element_type,
...@@ -261,6 +296,24 @@ extern Datum makeArrayResult(ArrayBuildState *astate, ...@@ -261,6 +296,24 @@ extern Datum makeArrayResult(ArrayBuildState *astate,
extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims, extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
int *dims, int *lbs, MemoryContext rcontext, bool release); int *dims, int *lbs, MemoryContext rcontext, bool release);
extern ArrayBuildStateArr *initArrayResultArr(Oid array_type, Oid element_type,
MemoryContext rcontext);
extern ArrayBuildStateArr *accumArrayResultArr(ArrayBuildStateArr *astate,
Datum dvalue, bool disnull,
Oid array_type,
MemoryContext rcontext);
extern Datum makeArrayResultArr(ArrayBuildStateArr *astate,
MemoryContext rcontext, bool release);
extern ArrayBuildStateAny *initArrayResultAny(Oid input_type,
MemoryContext rcontext);
extern ArrayBuildStateAny *accumArrayResultAny(ArrayBuildStateAny *astate,
Datum dvalue, bool disnull,
Oid input_type,
MemoryContext rcontext);
extern Datum makeArrayResultAny(ArrayBuildStateAny *astate,
MemoryContext rcontext, bool release);
extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim); extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim);
extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull); extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull);
extern void array_free_iterator(ArrayIterator iterator); extern void array_free_iterator(ArrayIterator iterator);
...@@ -292,6 +345,8 @@ extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo, ...@@ -292,6 +345,8 @@ extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
extern Datum array_agg_transfn(PG_FUNCTION_ARGS); extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
extern Datum array_agg_finalfn(PG_FUNCTION_ARGS); extern Datum array_agg_finalfn(PG_FUNCTION_ARGS);
extern Datum array_agg_array_transfn(PG_FUNCTION_ARGS);
extern Datum array_agg_array_finalfn(PG_FUNCTION_ARGS);
/* /*
* prototypes for functions defined in array_typanalyze.c * prototypes for functions defined in array_typanalyze.c
......
...@@ -128,6 +128,7 @@ extern void get_type_category_preferred(Oid typid, ...@@ -128,6 +128,7 @@ extern void get_type_category_preferred(Oid typid,
extern Oid get_typ_typrelid(Oid typid); extern Oid get_typ_typrelid(Oid typid);
extern Oid get_element_type(Oid typid); extern Oid get_element_type(Oid typid);
extern Oid get_array_type(Oid typid); extern Oid get_array_type(Oid typid);
extern Oid get_promoted_array_type(Oid typid);
extern Oid get_base_element_type(Oid typid); extern Oid get_base_element_type(Oid typid);
extern void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam); extern void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam);
extern void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena); extern void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena);
......
...@@ -268,7 +268,7 @@ static Datum plperl_sv_to_datum(SV *sv, Oid typid, int32 typmod, ...@@ -268,7 +268,7 @@ static Datum plperl_sv_to_datum(SV *sv, Oid typid, int32 typmod,
bool *isnull); bool *isnull);
static void _sv_to_datum_finfo(Oid typid, FmgrInfo *finfo, Oid *typioparam); static void _sv_to_datum_finfo(Oid typid, FmgrInfo *finfo, Oid *typioparam);
static Datum plperl_array_to_datum(SV *src, Oid typid, int32 typmod); static Datum plperl_array_to_datum(SV *src, Oid typid, int32 typmod);
static ArrayBuildState *array_to_datum_internal(AV *av, ArrayBuildState *astate, static void array_to_datum_internal(AV *av, ArrayBuildState *astate,
int *ndims, int *dims, int cur_depth, int *ndims, int *dims, int cur_depth,
Oid arraytypid, Oid elemtypid, int32 typmod, Oid arraytypid, Oid elemtypid, int32 typmod,
FmgrInfo *finfo, Oid typioparam); FmgrInfo *finfo, Oid typioparam);
...@@ -1127,7 +1127,7 @@ get_perl_array_ref(SV *sv) ...@@ -1127,7 +1127,7 @@ get_perl_array_ref(SV *sv)
/* /*
* helper function for plperl_array_to_datum, recurses for multi-D arrays * helper function for plperl_array_to_datum, recurses for multi-D arrays
*/ */
static ArrayBuildState * static void
array_to_datum_internal(AV *av, ArrayBuildState *astate, array_to_datum_internal(AV *av, ArrayBuildState *astate,
int *ndims, int *dims, int cur_depth, int *ndims, int *dims, int cur_depth,
Oid arraytypid, Oid elemtypid, int32 typmod, Oid arraytypid, Oid elemtypid, int32 typmod,
...@@ -1168,7 +1168,7 @@ array_to_datum_internal(AV *av, ArrayBuildState *astate, ...@@ -1168,7 +1168,7 @@ array_to_datum_internal(AV *av, ArrayBuildState *astate,
errmsg("multidimensional arrays must have array expressions with matching dimensions"))); errmsg("multidimensional arrays must have array expressions with matching dimensions")));
/* recurse to fetch elements of this sub-array */ /* recurse to fetch elements of this sub-array */
astate = array_to_datum_internal(nav, astate, array_to_datum_internal(nav, astate,
ndims, dims, cur_depth + 1, ndims, dims, cur_depth + 1,
arraytypid, elemtypid, typmod, arraytypid, elemtypid, typmod,
finfo, typioparam); finfo, typioparam);
...@@ -1192,12 +1192,10 @@ array_to_datum_internal(AV *av, ArrayBuildState *astate, ...@@ -1192,12 +1192,10 @@ array_to_datum_internal(AV *av, ArrayBuildState *astate,
typioparam, typioparam,
&isnull); &isnull);
astate = accumArrayResult(astate, dat, isnull, (void) accumArrayResult(astate, dat, isnull,
elemtypid, CurrentMemoryContext); elemtypid, CurrentMemoryContext);
} }
} }
return astate;
} }
/* /*
...@@ -1222,18 +1220,21 @@ plperl_array_to_datum(SV *src, Oid typid, int32 typmod) ...@@ -1222,18 +1220,21 @@ plperl_array_to_datum(SV *src, Oid typid, int32 typmod)
errmsg("cannot convert Perl array to non-array type %s", errmsg("cannot convert Perl array to non-array type %s",
format_type_be(typid)))); format_type_be(typid))));
astate = initArrayResult(elemtypid, CurrentMemoryContext);
_sv_to_datum_finfo(elemtypid, &finfo, &typioparam); _sv_to_datum_finfo(elemtypid, &finfo, &typioparam);
memset(dims, 0, sizeof(dims)); memset(dims, 0, sizeof(dims));
dims[0] = av_len((AV *) SvRV(src)) + 1; dims[0] = av_len((AV *) SvRV(src)) + 1;
astate = array_to_datum_internal((AV *) SvRV(src), NULL, array_to_datum_internal((AV *) SvRV(src), astate,
&ndims, dims, 1, &ndims, dims, 1,
typid, elemtypid, typmod, typid, elemtypid, typmod,
&finfo, typioparam); &finfo, typioparam);
if (!astate) /* ensure we get zero-D array for no inputs, as per PG convention */
return PointerGetDatum(construct_empty_array(elemtypid)); if (dims[0] <= 0)
ndims = 0;
for (i = 0; i < ndims; i++) for (i = 0; i < ndims; i++)
lbs[i] = 1; lbs[i] = 1;
......
...@@ -1497,6 +1497,7 @@ select cardinality('{{{1,9},{5,6}},{{2,3},{3,4}}}'::int[]); ...@@ -1497,6 +1497,7 @@ select cardinality('{{{1,9},{5,6}},{{2,3},{3,4}}}'::int[]);
8 8
(1 row) (1 row)
-- array_agg(anynonarray)
select array_agg(unique1) from (select unique1 from tenk1 where unique1 < 15 order by unique1) ss; select array_agg(unique1) from (select unique1 from tenk1 where unique1 < 15 order by unique1) ss;
array_agg array_agg
-------------------------------------- --------------------------------------
...@@ -1521,6 +1522,55 @@ select array_agg(unique1) from tenk1 where unique1 < -15; ...@@ -1521,6 +1522,55 @@ select array_agg(unique1) from tenk1 where unique1 < -15;
(1 row) (1 row)
-- array_agg(anyarray)
select array_agg(ar)
from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
array_agg
---------------
{{1,2},{3,4}}
(1 row)
select array_agg(distinct ar order by ar desc)
from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
array_agg
---------------------------
{{5},{4},{3},{2},{1},{0}}
(1 row)
select array_agg(ar)
from (select array_agg(array[i, i+1, i-1])
from generate_series(1,2) a(i)) b(ar);
array_agg
---------------------
{{{1,2,0},{2,3,1}}}
(1 row)
select array_agg(array[i+1.2, i+1.3, i+1.4]) from generate_series(1,3) g(i);
array_agg
---------------------------------------------
{{2.2,2.3,2.4},{3.2,3.3,3.4},{4.2,4.3,4.4}}
(1 row)
select array_agg(array['Hello', i::text]) from generate_series(9,11) g(i);
array_agg
-----------------------------------
{{Hello,9},{Hello,10},{Hello,11}}
(1 row)
select array_agg(array[i, nullif(i, 3), i+1]) from generate_series(1,4) g(i);
array_agg
--------------------------------------
{{1,1,2},{2,2,3},{3,NULL,4},{4,4,5}}
(1 row)
-- errors
select array_agg('{}'::int[]) from generate_series(1,2);
ERROR: cannot accumulate empty arrays
select array_agg(null::int[]) from generate_series(1,2);
ERROR: cannot accumulate null arrays
select array_agg(ar)
from (values ('{1,2}'::int[]), ('{3}'::int[])) v(ar);
ERROR: cannot accumulate arrays of different dimensionality
select unnest(array[1,2,3]); select unnest(array[1,2,3]);
unnest unnest
-------- --------
...@@ -1660,6 +1710,19 @@ select array_replace(array['AB',NULL,'CDE'],NULL,'12'); ...@@ -1660,6 +1710,19 @@ select array_replace(array['AB',NULL,'CDE'],NULL,'12');
{AB,12,CDE} {AB,12,CDE}
(1 row) (1 row)
-- array(select array-value ...)
select array(select array[i,i/2] from generate_series(1,5) i);
array
---------------------------------
{{1,0},{2,1},{3,1},{4,2},{5,2}}
(1 row)
select array(select array['Hello', i::text] from generate_series(9,11) i);
array
-----------------------------------
{{Hello,9},{Hello,10},{Hello,11}}
(1 row)
-- Insert/update on a column that is array of composite -- Insert/update on a column that is array of composite
create temp table t1 (f1 int8_tbl[]); create temp table t1 (f1 int8_tbl[]);
insert into t1 (f1[5].q1) values(42); insert into t1 (f1[5].q1) values(42);
......
...@@ -427,11 +427,29 @@ select cardinality('{{1,2}}'::int[]); ...@@ -427,11 +427,29 @@ select cardinality('{{1,2}}'::int[]);
select cardinality('{{1,2},{3,4},{5,6}}'::int[]); select cardinality('{{1,2},{3,4},{5,6}}'::int[]);
select cardinality('{{{1,9},{5,6}},{{2,3},{3,4}}}'::int[]); select cardinality('{{{1,9},{5,6}},{{2,3},{3,4}}}'::int[]);
-- array_agg(anynonarray)
select array_agg(unique1) from (select unique1 from tenk1 where unique1 < 15 order by unique1) ss; select array_agg(unique1) from (select unique1 from tenk1 where unique1 < 15 order by unique1) ss;
select array_agg(ten) from (select ten from tenk1 where unique1 < 15 order by unique1) ss; select array_agg(ten) from (select ten from tenk1 where unique1 < 15 order by unique1) ss;
select array_agg(nullif(ten, 4)) from (select ten from tenk1 where unique1 < 15 order by unique1) ss; select array_agg(nullif(ten, 4)) from (select ten from tenk1 where unique1 < 15 order by unique1) ss;
select array_agg(unique1) from tenk1 where unique1 < -15; select array_agg(unique1) from tenk1 where unique1 < -15;
-- array_agg(anyarray)
select array_agg(ar)
from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
select array_agg(distinct ar order by ar desc)
from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
select array_agg(ar)
from (select array_agg(array[i, i+1, i-1])
from generate_series(1,2) a(i)) b(ar);
select array_agg(array[i+1.2, i+1.3, i+1.4]) from generate_series(1,3) g(i);
select array_agg(array['Hello', i::text]) from generate_series(9,11) g(i);
select array_agg(array[i, nullif(i, 3), i+1]) from generate_series(1,4) g(i);
-- errors
select array_agg('{}'::int[]) from generate_series(1,2);
select array_agg(null::int[]) from generate_series(1,2);
select array_agg(ar)
from (values ('{1,2}'::int[]), ('{3}'::int[])) v(ar);
select unnest(array[1,2,3]); select unnest(array[1,2,3]);
select * from unnest(array[1,2,3]); select * from unnest(array[1,2,3]);
select unnest(array[1,2,3,4.5]::float8[]); select unnest(array[1,2,3,4.5]::float8[]);
...@@ -452,6 +470,10 @@ select array_replace(array['A','B','DD','B'],'B','CC'); ...@@ -452,6 +470,10 @@ select array_replace(array['A','B','DD','B'],'B','CC');
select array_replace(array[1,NULL,3],NULL,NULL); select array_replace(array[1,NULL,3],NULL,NULL);
select array_replace(array['AB',NULL,'CDE'],NULL,'12'); select array_replace(array['AB',NULL,'CDE'],NULL,'12');
-- array(select array-value ...)
select array(select array[i,i/2] from generate_series(1,5) i);
select array(select array['Hello', i::text] from generate_series(9,11) i);
-- Insert/update on a column that is array of composite -- Insert/update on a column that is array of composite
create temp table t1 (f1 int8_tbl[]); create temp table t1 (f1 int8_tbl[]);
......
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