Expanded strings. contexts
authorRobert Haas <rhaas@postgresql.org>
Tue, 19 Jan 2016 03:19:14 +0000 (22:19 -0500)
committerRobert Haas <rhaas@postgresql.org>
Tue, 19 Jan 2016 03:19:14 +0000 (22:19 -0500)
src/backend/executor/nodeAgg.c
src/backend/utils/adt/Makefile
src/backend/utils/adt/expandedstring.c [new file with mode: 0644]
src/backend/utils/adt/varlena.c
src/include/catalog/pg_aggregate.h
src/include/catalog/pg_proc.h
src/include/utils/builtins.h
src/include/utils/expandedstring.h [new file with mode: 0644]

index f49114abe3b5217a2102c46a91ffe85081d97eef..a2c29c450d3e62ba2cb617a2586791c484aca6aa 100644 (file)
@@ -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)
index 2cb7baba428134ad7cb9ff0cbf96c4948ee8fcfa..61c935939c59bacd8d5f976be2bbafd0f464b2d6 100644 (file)
@@ -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 (file)
index 0000000..4e279a3
--- /dev/null
@@ -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);
+}
index 8683188df130fdb210bfe0e47bb32f18caadee37..09d7c7e8580c33bfd13f319de75e73f4d0bd786c 100644 (file)
@@ -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));
 }
 
 /*
index 28b0669b462429e2831d8b5102f6066d1daebc11..d8f9649a74f4daeb9cc85ffd6218d95229c0d622 100644 (file)
@@ -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_ ));
index 3df5ac50b607cd497b08802fdd4a328730b5e2bb..06c93f632c61e14a7dcf9546c0d5cb1f18b8d9b7 100644 (file)
@@ -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_ ));
index 477fde1f81dbbc601aedd46893fff57b2ac3968d..c1609639c8fb18986d069d07f1164f9abdcc7338 100644 (file)
@@ -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 (file)
index 0000000..bb03d89
--- /dev/null
@@ -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