aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/varlena.c
diff options
context:
space:
mode:
authorItagaki Takahiro <itagaki.takahiro@gmail.com>2010-02-01 03:14:45 +0000
committerItagaki Takahiro <itagaki.takahiro@gmail.com>2010-02-01 03:14:45 +0000
commit9ea9918e37689fbc9ed43532b8828652b5ea90cd (patch)
treef512d7e150549dd403f0266374d7dbcfa460d8ed /src/backend/utils/adt/varlena.c
parentee3a81f0a03d0da128689df92a84a24a337645ec (diff)
downloadpostgresql-9ea9918e37689fbc9ed43532b8828652b5ea90cd.tar.gz
postgresql-9ea9918e37689fbc9ed43532b8828652b5ea90cd.zip
Add string_agg aggregate functions. The one argument version concatenates
the input values into a string. The two argument version also does the same thing, but inserts delimiters between elements. Original patch by Pavel Stehule, reviewed by David E. Wheeler and me.
Diffstat (limited to 'src/backend/utils/adt/varlena.c')
-rw-r--r--src/backend/utils/adt/varlena.c106
1 files changed, 105 insertions, 1 deletions
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 7a8abf14a83..096ca75bf93 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/varlena.c,v 1.174 2010/01/25 20:55:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/varlena.c,v 1.175 2010/02/01 03:14:43 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
@@ -21,6 +21,7 @@
#include "libpq/md5.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
+#include "nodes/execnodes.h"
#include "parser/scansup.h"
#include "regex/regex.h"
#include "utils/builtins.h"
@@ -73,6 +74,7 @@ static bytea *bytea_substring(Datum str,
int L,
bool length_not_specified);
static bytea *bytea_overlay(bytea *t1, bytea *t2, int sp, int sl);
+static StringInfo makeStringAggState(fmNodePtr context);
/*****************************************************************************
@@ -3315,3 +3317,105 @@ pg_column_size(PG_FUNCTION_ARGS)
PG_RETURN_INT32(result);
}
+
+/*
+ * string_agg - Concatenates values and returns string.
+ *
+ * Syntax: string_agg(value text, delimiter text = '') RETURNS text
+ *
+ * Note: Any NULL values are ignored. The first-call delimiter isn't
+ * actually used at all, and on subsequent calls the delimiter precedes
+ * the associated value.
+ */
+static StringInfo
+makeStringAggState(fmNodePtr context)
+{
+ StringInfo state;
+ MemoryContext aggcontext;
+ MemoryContext oldcontext;
+
+ if (context && IsA(context, AggState))
+ aggcontext = ((AggState *) context)->aggcontext;
+ else if (context && IsA(context, WindowAggState))
+ aggcontext = ((WindowAggState *) context)->wincontext;
+ else
+ {
+ /* cannot be called directly because of internal-type argument */
+ elog(ERROR, "string_agg_transfn called in non-aggregate context");
+ aggcontext = NULL; /* keep compiler quiet */
+ }
+
+ /* Create state in aggregate context */
+ oldcontext = MemoryContextSwitchTo(aggcontext);
+ state = makeStringInfo();
+ MemoryContextSwitchTo(oldcontext);
+
+ return state;
+}
+
+Datum
+string_agg_transfn(PG_FUNCTION_ARGS)
+{
+ StringInfo state;
+
+ state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0);
+
+ /* Append the element unless not null. */
+ if (!PG_ARGISNULL(1))
+ {
+ if (state == NULL)
+ state = makeStringAggState(fcinfo->context);
+ appendStringInfoText(state, PG_GETARG_TEXT_PP(1)); /* value */
+ }
+
+ /*
+ * The transition type for string_agg() is declared to be "internal", which
+ * is a pass-by-value type the same size as a pointer.
+ */
+ PG_RETURN_POINTER(state);
+}
+
+Datum
+string_agg_delim_transfn(PG_FUNCTION_ARGS)
+{
+ StringInfo state;
+
+ state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0);
+
+ /* Append the value unless not null. */
+ if (!PG_ARGISNULL(1))
+ {
+ if (state == NULL)
+ state = makeStringAggState(fcinfo->context);
+ else if (!PG_ARGISNULL(2))
+ appendStringInfoText(state, PG_GETARG_TEXT_PP(2)); /* delimiter */
+
+ appendStringInfoText(state, PG_GETARG_TEXT_PP(1)); /* value */
+ }
+
+ /*
+ * The transition type for string_agg() is declared to be "internal", which
+ * is a pass-by-value type the same size as a pointer.
+ */
+ PG_RETURN_POINTER(state);
+}
+
+Datum
+string_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ StringInfo state;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ /* cannot be called directly because of internal-type argument */
+ Assert(fcinfo->context &&
+ (IsA(fcinfo->context, AggState) ||
+ IsA(fcinfo->context, WindowAggState)));
+
+ state = (StringInfo) PG_GETARG_POINTER(0);
+ if (state != NULL)
+ PG_RETURN_TEXT_P(cstring_to_text(state->data));
+ else
+ PG_RETURN_NULL();
+}