summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/executor/nodeAgg.c15
-rw-r--r--src/backend/utils/adt/Makefile2
-rw-r--r--src/backend/utils/adt/expandedstring.c86
-rw-r--r--src/backend/utils/adt/varlena.c77
-rw-r--r--src/include/catalog/pg_aggregate.h2
-rw-r--r--src/include/catalog/pg_proc.h4
-rw-r--r--src/include/utils/builtins.h1
-rw-r--r--src/include/utils/expandedstring.h31
8 files changed, 169 insertions, 49 deletions
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index f49114abe3..a2c29c450d 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -756,11 +756,18 @@ advance_transition_function(AggState *aggstate,
aggstate->curpertrans = NULL;
/*
- * If pass-by-ref datatype, must copy the new value into aggcontext and
- * pfree the prior transValue. But if transfn returned a pointer to its
- * first input, we don't need to do anything.
+ * If we got a R/W pointer to an expanded object, we can just take over
+ * control of the object. Any other pass-by-ref value must be copied into
+ * aggcontext and the prior value freed; with the exception that if transfn
+ * returned a pointer to its first input, we don't need to do anything.
*/
- if (!pertrans->transtypeByVal &&
+ if (DatumIsReadWriteExpandedObject(newVal, fcinfo->isnull,
+ pertrans->transtypeLen))
+ {
+ newVal = TransferExpandedObject(newVal,
+ aggstate->aggcontexts[aggstate->current_set]->ecxt_per_tuple_memory);
+ }
+ else if (!pertrans->transtypeByVal &&
DatumGetPointer(newVal) != DatumGetPointer(pergroupstate->transValue))
{
if (!fcinfo->isnull)
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 2cb7baba42..61c935939c 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -12,7 +12,7 @@ include $(top_builddir)/src/Makefile.global
OBJS = acl.o arrayfuncs.o array_expanded.o array_selfuncs.o \
array_typanalyze.o array_userfuncs.o arrayutils.o ascii.o \
bool.o cash.o char.o date.o datetime.o datum.o dbsize.o domains.o \
- encode.o enum.o expandeddatum.o \
+ encode.o enum.o expandeddatum.o expandedstring.o \
float.o format_type.o formatting.o genfile.o \
geo_ops.o geo_selfuncs.o inet_cidr_ntop.o inet_net_pton.o int.o \
int8.o json.o jsonb.o jsonb_gin.o jsonb_op.o jsonb_util.o \
diff --git a/src/backend/utils/adt/expandedstring.c b/src/backend/utils/adt/expandedstring.c
new file mode 100644
index 0000000000..4e279a3aa1
--- /dev/null
+++ b/src/backend/utils/adt/expandedstring.c
@@ -0,0 +1,86 @@
+/*-------------------------------------------------------------------------
+ *
+ * expandedstring.c
+ * Expand a varlena into a StringInfo.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/expandeddatum.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "utils/expandedstring.h"
+#include "utils/memutils.h"
+
+static Size ESI_get_flat_size(ExpandedObjectHeader *eohptr);
+static void ESI_flatten_into(ExpandedObjectHeader *eohptr,
+ void *result, Size allocated_size);
+
+static const ExpandedObjectMethods ESI_methods =
+{
+ ESI_get_flat_size,
+ ESI_flatten_into
+};
+
+/*
+ * Construct an expanded datum consisting of an empty StringInfo.
+ *
+ * Caller must ensure that CurrentMemoryContext points to a context with
+ * a suitable lifetime.
+ */
+ExpandedStringInfoHeader *
+GetExpandedStringInfo(void)
+{
+ ExpandedStringInfoHeader *esih;
+ MemoryContext objcxt;
+ MemoryContext oldcxt;
+
+ objcxt = AllocSetContextCreate(CurrentMemoryContext,
+ "stringinfo expanded object",
+ ALLOCSET_SMALL_MINSIZE,
+ ALLOCSET_SMALL_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+
+ oldcxt = MemoryContextSwitchTo(objcxt);
+ esih = palloc(sizeof(ExpandedStringInfoHeader));
+ EOH_init_header(&esih->hdr, &ESI_methods, objcxt);
+ initStringInfo(&esih->buf);
+ MemoryContextSwitchTo(oldcxt);
+
+ return esih;
+}
+
+/*
+ * The space required to flatten a StringInfo back to a plain old varlena is
+ * just the number of bytes we have in the buffer, plus the size of a 4-byte
+ * header. Even if the buffer is short, we can't flatten to a packed
+ * representation.
+ */
+static Size
+ESI_get_flat_size(ExpandedObjectHeader *eohptr)
+{
+ ExpandedStringInfoHeader *esih = (ExpandedStringInfoHeader *) eohptr;
+
+ return VARHDRSZ + esih->buf.len;
+}
+
+/*
+ * Flattening a StringInfo just involves copying the data into the allocated
+ * space.
+ */
+static void
+ESI_flatten_into(ExpandedObjectHeader *eohptr,
+ void *result, Size allocated_size)
+{
+ ExpandedStringInfoHeader *esih = (ExpandedStringInfoHeader *) eohptr;
+
+ Assert(allocated_size == VARHDRSZ + esih->buf.len);
+ memcpy(VARDATA(result), esih->buf.data, esih->buf.len);
+ SET_VARSIZE(result, VARHDRSZ + esih->buf.len);
+}
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 8683188df1..09d7c7e858 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -30,6 +30,7 @@
#include "regex/regex.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/expandedstring.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/pg_locale.h"
@@ -4357,16 +4358,6 @@ 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.
- */
-
/* subroutine to initialize state */
static StringInfo
makeStringAggState(FunctionCallInfo fcinfo)
@@ -4392,46 +4383,54 @@ makeStringAggState(FunctionCallInfo fcinfo)
return state;
}
+/*
+ * 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.
+ */
Datum
string_agg_transfn(PG_FUNCTION_ARGS)
{
- StringInfo state;
+ ExpandedStringInfoHeader *state;
- state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0);
-
- /* Append the value unless null. */
- if (!PG_ARGISNULL(1))
+ /* If the second argument is NULL, just return the first argument. */
+ if (PG_ARGISNULL(1))
{
- /* On the first time through, we ignore the delimiter. */
- if (state == NULL)
- state = makeStringAggState(fcinfo);
- else if (!PG_ARGISNULL(2))
- appendStringInfoText(state, PG_GETARG_TEXT_PP(2)); /* delimiter */
-
- appendStringInfoText(state, PG_GETARG_TEXT_PP(1)); /* value */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+ PG_RETURN_DATUM(PG_GETARG_DATUM(0));
}
/*
- * The transition type for string_agg() is declared to be "internal",
- * which is a pass-by-value type the same size as a pointer.
+ * Expand the first argument if needed; then, add any delimeter (unless
+ * the first argument is NULL).
*/
- PG_RETURN_POINTER(state);
-}
-
-Datum
-string_agg_finalfn(PG_FUNCTION_ARGS)
-{
- StringInfo state;
-
- /* cannot be called directly because of internal-type argument */
- Assert(AggCheckCallContext(fcinfo, NULL));
+ if (!PG_ARGISNULL(0) && VARATT_IS_EXTERNAL_EXPANDED_RW(PG_GETARG_DATUM(0)))
+ {
+ state = (ExpandedStringInfoHeader *) DatumGetEOHP(PG_GETARG_DATUM(0));
+ if (!PG_ARGISNULL(2))
+ appendStringInfoText(&state->buf, PG_GETARG_TEXT_PP(2));
+ }
+ else
+ {
+ state = GetExpandedStringInfo();
+ /* Note that if the transition state is NULL, we skip the delimiter. */
+ if (!PG_ARGISNULL(0))
+ {
+ appendStringInfoText(&state->buf, PG_GETARG_TEXT_PP(0));
+ if (!PG_ARGISNULL(2))
+ appendStringInfoText(&state->buf, PG_GETARG_TEXT_PP(2));
+ }
+ }
- state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0);
+ /* Append second argument to first. */
+ appendStringInfoText(&state->buf, PG_GETARG_TEXT_PP(1));
- if (state != NULL)
- PG_RETURN_TEXT_P(cstring_to_text_with_len(state->data, state->len));
- else
- PG_RETURN_NULL();
+ PG_RETURN_DATUM(EOHPGetRWDatum(&state->hdr));
}
/*
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 28b0669b46..d8f9649a74 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -279,7 +279,7 @@ DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - t f 0
DATA(insert ( 4053 n 0 array_agg_array_transfn array_agg_array_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
/* 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 - - - - f f 0 25 0 0 0 _null_ _null_ ));
/* bytea */
DATA(insert ( 3545 n 0 bytea_string_agg_transfn bytea_string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 3df5ac50b6..06c93f632c 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2511,10 +2511,8 @@ DESCR("aggregate final function");
DATA(insert OID = 2817 ( float8_corr PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701 "1022" _null_ _null_ _null_ _null_ _null_ float8_corr _null_ _null_ _null_ ));
DESCR("aggregate final function");
-DATA(insert OID = 3535 ( string_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i s 3 0 2281 "2281 25 25" _null_ _null_ _null_ _null_ _null_ string_agg_transfn _null_ _null_ _null_ ));
+DATA(insert OID = 3535 ( string_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i s 3 0 25 "25 25 25" _null_ _null_ _null_ _null_ _null_ string_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
-DATA(insert OID = 3536 ( string_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 25 "2281" _null_ _null_ _null_ _null_ _null_ string_agg_finalfn _null_ _null_ _null_ ));
-DESCR("aggregate final function");
DATA(insert OID = 3538 ( string_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i s 2 0 25 "25 25" _null_ _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into a string");
DATA(insert OID = 3543 ( bytea_string_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i s 3 0 2281 "2281 17 17" _null_ _null_ _null_ _null_ _null_ bytea_string_agg_transfn _null_ _null_ _null_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 477fde1f81..c1609639c8 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -843,7 +843,6 @@ extern Datum pg_column_size(PG_FUNCTION_ARGS);
extern Datum bytea_string_agg_transfn(PG_FUNCTION_ARGS);
extern Datum bytea_string_agg_finalfn(PG_FUNCTION_ARGS);
extern Datum string_agg_transfn(PG_FUNCTION_ARGS);
-extern Datum string_agg_finalfn(PG_FUNCTION_ARGS);
extern Datum text_concat(PG_FUNCTION_ARGS);
extern Datum text_concat_ws(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/expandedstring.h b/src/include/utils/expandedstring.h
new file mode 100644
index 0000000000..bb03d89415
--- /dev/null
+++ b/src/include/utils/expandedstring.h
@@ -0,0 +1,31 @@
+/*-------------------------------------------------------------------------
+ *
+ * expandedstring.h
+ * Expand a varlena into a StringInfo for faster in-memory manipulation.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/expandedstring.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef EXPANDEDSTRING_H
+#define EXPANDEDSTRING_H
+
+#include "lib/stringinfo.h"
+#include "utils/expandeddatum.h"
+
+typedef struct ExpandedStringInfoHeader
+{
+ /* Standard header for expanded objects */
+ ExpandedObjectHeader hdr;
+
+ /* String info */
+ StringInfoData buf;
+} ExpandedStringInfoHeader;
+
+extern ExpandedStringInfoHeader *GetExpandedStringInfo(void);
+
+#endif