aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/array_userfuncs.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2014-11-25 12:21:22 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2014-11-25 12:21:28 -0500
commitbac27394a1c69c20ec904729c593e59485c75c69 (patch)
tree7bdf15b078bfcef745a5bb2c7c479d6f8bd45f15 /src/backend/utils/adt/array_userfuncs.c
parent25976710dfd8611d3fc79c0c1e20179ff7a940ec (diff)
downloadpostgresql-bac27394a1c69c20ec904729c593e59485c75c69.tar.gz
postgresql-bac27394a1c69c20ec904729c593e59485c75c69.zip
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
Diffstat (limited to 'src/backend/utils/adt/array_userfuncs.c')
-rw-r--r--src/backend/utils/adt/array_userfuncs.c88
1 files changed, 78 insertions, 10 deletions
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
index 831466dec91..50ea4d226b7 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -471,7 +471,7 @@ create_singleton_array(FunctionCallInfo fcinfo,
/*
- * ARRAY_AGG aggregate function
+ * ARRAY_AGG(anynonarray) aggregate function
*/
Datum
array_agg_transfn(PG_FUNCTION_ARGS)
@@ -486,6 +486,12 @@ array_agg_transfn(PG_FUNCTION_ARGS)
(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 element 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 */
@@ -516,18 +522,13 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
int dims[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 */
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;
lbs[0] = 1;
@@ -544,3 +545,70 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
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);
+}