diff options
Diffstat (limited to 'src/backend/utils/adt')
| -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 |
3 files changed, 125 insertions, 40 deletions
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)); } /* |
