diff options
-rw-r--r-- | src/backend/executor/nodeAgg.c | 15 | ||||
-rw-r--r-- | src/backend/utils/adt/Makefile | 2 | ||||
-rw-r--r-- | src/backend/utils/adt/expandedstring.c | 86 | ||||
-rw-r--r-- | src/backend/utils/adt/varlena.c | 77 | ||||
-rw-r--r-- | src/include/catalog/pg_aggregate.h | 2 | ||||
-rw-r--r-- | src/include/catalog/pg_proc.h | 4 | ||||
-rw-r--r-- | src/include/utils/builtins.h | 1 | ||||
-rw-r--r-- | src/include/utils/expandedstring.h | 31 |
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 |