diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2015-05-14 12:08:40 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2015-05-14 12:08:49 -0400 |
commit | 1dc5ebc9077ab742079ce5dac9a6664248d42916 (patch) | |
tree | 68aa827a8be94c16b456d8f78263507fcff9ee4a /src/backend/utils/adt/array_userfuncs.c | |
parent | 8a2e1edd2ba0817313c1c0ef76b03a5ab819d17f (diff) | |
download | postgresql-1dc5ebc9077ab742079ce5dac9a6664248d42916.tar.gz postgresql-1dc5ebc9077ab742079ce5dac9a6664248d42916.zip |
Support "expanded" objects, particularly arrays, for better performance.
This patch introduces the ability for complex datatypes to have an
in-memory representation that is different from their on-disk format.
On-disk formats are typically optimized for minimal size, and in any case
they can't contain pointers, so they are often not well-suited for
computation. Now a datatype can invent an "expanded" in-memory format
that is better suited for its operations, and then pass that around among
the C functions that operate on the datatype. There are also provisions
(rudimentary as yet) to allow an expanded object to be modified in-place
under suitable conditions, so that operations like assignment to an element
of an array need not involve copying the entire array.
The initial application for this feature is arrays, but it is not hard
to foresee using it for other container types like JSON, XML and hstore.
I have hopes that it will be useful to PostGIS as well.
In this initial implementation, a few heuristics have been hard-wired
into plpgsql to improve performance for arrays that are stored in
plpgsql variables. We would like to generalize those hacks so that
other datatypes can obtain similar improvements, but figuring out some
appropriate APIs is left as a task for future work. (The heuristics
themselves are probably not optimal yet, either, as they sometimes
force expansion of arrays that would be better left alone.)
Preliminary performance testing shows impressive speed gains for plpgsql
functions that do element-by-element access or update of large arrays.
There are other cases that get a little slower, as a result of added array
format conversions; but we can hope to improve anything that's annoyingly
bad. In any case most applications should see a net win.
Tom Lane, reviewed by Andres Freund
Diffstat (limited to 'src/backend/utils/adt/array_userfuncs.c')
-rw-r--r-- | src/backend/utils/adt/array_userfuncs.c | 105 |
1 files changed, 56 insertions, 49 deletions
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c index 4177d2da172..f7b57da48e7 100644 --- a/src/backend/utils/adt/array_userfuncs.c +++ b/src/backend/utils/adt/array_userfuncs.c @@ -25,22 +25,36 @@ static Datum array_position_common(FunctionCallInfo fcinfo); /* * fetch_array_arg_replace_nulls * - * Fetch an array-valued argument; if it's null, construct an empty array - * value of the proper data type. Also cache basic element type information - * in fn_extra. + * Fetch an array-valued argument in expanded form; if it's null, construct an + * empty array value of the proper data type. Also cache basic element type + * information in fn_extra. + * + * Caution: if the input is a read/write pointer, this returns the input + * argument; so callers must be sure that their changes are "safe", that is + * they cannot leave the array in a corrupt state. */ -static ArrayType * +static ExpandedArrayHeader * fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno) { - ArrayType *v; + ExpandedArrayHeader *eah; Oid element_type; ArrayMetaState *my_extra; - /* First collect the array value */ + /* If first time through, create datatype cache struct */ + my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; + if (my_extra == NULL) + { + my_extra = (ArrayMetaState *) + MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + sizeof(ArrayMetaState)); + my_extra->element_type = InvalidOid; + fcinfo->flinfo->fn_extra = my_extra; + } + + /* Now collect the array value */ if (!PG_ARGISNULL(argno)) { - v = PG_GETARG_ARRAYTYPE_P(argno); - element_type = ARR_ELEMTYPE(v); + eah = PG_GETARG_EXPANDED_ARRAYX(argno, my_extra); } else { @@ -57,30 +71,12 @@ fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno) (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("input data type is not an array"))); - v = construct_empty_array(element_type); - } - - /* Now cache required info, which might change from call to call */ - my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; - if (my_extra == NULL) - { - my_extra = (ArrayMetaState *) - MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, - sizeof(ArrayMetaState)); - my_extra->element_type = InvalidOid; - fcinfo->flinfo->fn_extra = my_extra; - } - - if (my_extra->element_type != element_type) - { - get_typlenbyvalalign(element_type, - &my_extra->typlen, - &my_extra->typbyval, - &my_extra->typalign); - my_extra->element_type = element_type; + eah = construct_empty_expanded_array(element_type, + CurrentMemoryContext, + my_extra); } - return v; + return eah; } /*----------------------------------------------------------------------------- @@ -91,29 +87,29 @@ fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno) Datum array_append(PG_FUNCTION_ARGS) { - ArrayType *v; + ExpandedArrayHeader *eah; Datum newelem; bool isNull; - ArrayType *result; + Datum result; int *dimv, *lb; int indx; ArrayMetaState *my_extra; - v = fetch_array_arg_replace_nulls(fcinfo, 0); + eah = fetch_array_arg_replace_nulls(fcinfo, 0); isNull = PG_ARGISNULL(1); if (isNull) newelem = (Datum) 0; else newelem = PG_GETARG_DATUM(1); - if (ARR_NDIM(v) == 1) + if (eah->ndims == 1) { /* append newelem */ int ub; - lb = ARR_LBOUND(v); - dimv = ARR_DIMS(v); + lb = eah->lbound; + dimv = eah->dims; ub = dimv[0] + lb[0] - 1; indx = ub + 1; @@ -123,7 +119,7 @@ array_append(PG_FUNCTION_ARGS) (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); } - else if (ARR_NDIM(v) == 0) + else if (eah->ndims == 0) indx = 1; else ereport(ERROR, @@ -133,10 +129,11 @@ array_append(PG_FUNCTION_ARGS) /* Perform element insertion */ my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; - result = array_set(v, 1, &indx, newelem, isNull, + result = array_set_element(EOHPGetRWDatum(&eah->hdr), + 1, &indx, newelem, isNull, -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign); - PG_RETURN_ARRAYTYPE_P(result); + PG_RETURN_DATUM(result); } /*----------------------------------------------------------------------------- @@ -147,12 +144,13 @@ array_append(PG_FUNCTION_ARGS) Datum array_prepend(PG_FUNCTION_ARGS) { - ArrayType *v; + ExpandedArrayHeader *eah; Datum newelem; bool isNull; - ArrayType *result; + Datum result; int *lb; int indx; + int lb0; ArrayMetaState *my_extra; isNull = PG_ARGISNULL(0); @@ -160,13 +158,14 @@ array_prepend(PG_FUNCTION_ARGS) newelem = (Datum) 0; else newelem = PG_GETARG_DATUM(0); - v = fetch_array_arg_replace_nulls(fcinfo, 1); + eah = fetch_array_arg_replace_nulls(fcinfo, 1); - if (ARR_NDIM(v) == 1) + if (eah->ndims == 1) { /* prepend newelem */ - lb = ARR_LBOUND(v); + lb = eah->lbound; indx = lb[0] - 1; + lb0 = lb[0]; /* overflow? */ if (indx > lb[0]) @@ -174,8 +173,11 @@ array_prepend(PG_FUNCTION_ARGS) (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); } - else if (ARR_NDIM(v) == 0) + else if (eah->ndims == 0) + { indx = 1; + lb0 = 1; + } else ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), @@ -184,14 +186,19 @@ array_prepend(PG_FUNCTION_ARGS) /* Perform element insertion */ my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; - result = array_set(v, 1, &indx, newelem, isNull, + result = array_set_element(EOHPGetRWDatum(&eah->hdr), + 1, &indx, newelem, isNull, -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign); /* Readjust result's LB to match the input's, as expected for prepend */ - if (ARR_NDIM(v) == 1) - ARR_LBOUND(result)[0] = ARR_LBOUND(v)[0]; + Assert(result == EOHPGetRWDatum(&eah->hdr)); + if (eah->ndims == 1) + { + /* This is ok whether we've deconstructed or not */ + eah->lbound[0] = lb0; + } - PG_RETURN_ARRAYTYPE_P(result); + PG_RETURN_DATUM(result); } /*----------------------------------------------------------------------------- |