]> granicus.if.org Git - postgresql/commitdiff
Improve performance of numeric sum(), avg(), stddev(), variance(), etc.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 16 Nov 2013 23:46:34 +0000 (18:46 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 16 Nov 2013 23:46:34 +0000 (18:46 -0500)
This patch improves performance of most built-in aggregates that formerly
used a NUMERIC or NUMERIC array as their transition type; this includes
not only aggregates on numeric inputs, but some aggregates on integer
inputs where overflow of an int8 value is a possibility.  The code now
uses a special-purpose data structure to avoid array construction and
deconstruction overhead, as well as packing and unpacking overhead for
numeric values.

These aggregates' transition type is now declared as INTERNAL, since
it doesn't correspond to any SQL data type.  To keep the planner from
thinking that that means a lot of storage will be used, we make use
of the just-added pg_aggregate.aggtransspace feature.  The space estimate
is set to 128 bytes, which is at least in the right ballpark.

Hadi Moshayedi, reviewed by Pavel Stehule and Tomas Vondra

src/backend/utils/adt/numeric.c
src/include/catalog/catversion.h
src/include/catalog/pg_aggregate.h
src/include/catalog/pg_proc.h
src/include/utils/builtins.h
src/test/regress/expected/aggregates.out
src/test/regress/sql/aggregates.sql

index b4d639428acc5fb48b45a28a759d02310f7cffab..0c28b771ffbf8fb6a929131f042d198dff1a7182 100644 (file)
@@ -2464,106 +2464,145 @@ numeric_float4(PG_FUNCTION_ARGS)
  *
  * Aggregate functions
  *
- * The transition datatype for all these aggregates is a 3-element array
- * of Numeric, holding the values N, sum(X), sum(X*X) in that order.
+ * The transition datatype for all these aggregates is declared as INTERNAL.
+ * Actually, it's a pointer to a NumericAggState allocated in the aggregate
+ * context.  The digit buffers for the NumericVars will be there too.
  *
- * We represent N as a numeric mainly to avoid having to build a special
- * datatype; it's unlikely it'd overflow an int4, but ...
+ * Note that the transition functions don't bother to create a NumericAggState
+ * until they see the first non-null input value; therefore, the final
+ * functions will never see N == 0.  (The case is represented as a NULL
+ * state pointer, instead.)
  *
  * ----------------------------------------------------------------------
  */
 
-static ArrayType *
-do_numeric_accum(ArrayType *transarray, Numeric newval)
+typedef struct NumericAggState
 {
-       Datum      *transdatums;
-       int                     ndatums;
-       Datum           N,
-                               sumX,
-                               sumX2;
-       ArrayType  *result;
-
-       /* We assume the input is array of numeric */
-       deconstruct_array(transarray,
-                                         NUMERICOID, -1, false, 'i',
-                                         &transdatums, NULL, &ndatums);
-       if (ndatums != 3)
-               elog(ERROR, "expected 3-element numeric array");
-       N = transdatums[0];
-       sumX = transdatums[1];
-       sumX2 = transdatums[2];
-
-       N = DirectFunctionCall1(numeric_inc, N);
-       sumX = DirectFunctionCall2(numeric_add, sumX,
-                                                          NumericGetDatum(newval));
-       sumX2 = DirectFunctionCall2(numeric_add, sumX2,
-                                                               DirectFunctionCall2(numeric_mul,
-                                                                                                       NumericGetDatum(newval),
-                                                                                                       NumericGetDatum(newval)));
-
-       transdatums[0] = N;
-       transdatums[1] = sumX;
-       transdatums[2] = sumX2;
-
-       result = construct_array(transdatums, 3,
-                                                        NUMERICOID, -1, false, 'i');
+       bool            calcSumX2;              /* if true, calculate sumX2 */
+       bool            isNaN;                  /* true if any processed number was NaN */
+       MemoryContext agg_context;      /* context we're calculating in */
+       int64           N;                              /* count of processed numbers */
+       NumericVar      sumX;                   /* sum of processed numbers */
+       NumericVar      sumX2;                  /* sum of squares of processed numbers */
+} NumericAggState;
 
-       return result;
+/*
+ * Prepare state data for a numeric aggregate function that needs to compute
+ * sum, count and optionally sum of squares of the input.
+ */
+static NumericAggState *
+makeNumericAggState(FunctionCallInfo fcinfo, bool calcSumX2)
+{
+       NumericAggState *state;
+       MemoryContext agg_context;
+       MemoryContext old_context;
+
+       if (!AggCheckCallContext(fcinfo, &agg_context))
+               elog(ERROR, "aggregate function called in non-aggregate context");
+
+       old_context = MemoryContextSwitchTo(agg_context);
+
+       state = (NumericAggState *) palloc0(sizeof(NumericAggState));
+       state->calcSumX2 = calcSumX2;
+       state->agg_context = agg_context;
+
+       MemoryContextSwitchTo(old_context);
+
+       return state;
 }
 
 /*
- * Improve avg performance by not caclulating sum(X*X).
+ * Accumulate a new input value for numeric aggregate functions.
  */
-static ArrayType *
-do_numeric_avg_accum(ArrayType *transarray, Numeric newval)
+static void
+do_numeric_accum(NumericAggState *state, Numeric newval)
 {
-       Datum      *transdatums;
-       int                     ndatums;
-       Datum           N,
-                               sumX;
-       ArrayType  *result;
-
-       /* We assume the input is array of numeric */
-       deconstruct_array(transarray,
-                                         NUMERICOID, -1, false, 'i',
-                                         &transdatums, NULL, &ndatums);
-       if (ndatums != 2)
-               elog(ERROR, "expected 2-element numeric array");
-       N = transdatums[0];
-       sumX = transdatums[1];
-
-       N = DirectFunctionCall1(numeric_inc, N);
-       sumX = DirectFunctionCall2(numeric_add, sumX,
-                                                          NumericGetDatum(newval));
-
-       transdatums[0] = N;
-       transdatums[1] = sumX;
-
-       result = construct_array(transdatums, 2,
-                                                        NUMERICOID, -1, false, 'i');
+       NumericVar      X;
+       NumericVar      X2;
+       MemoryContext old_context;
 
-       return result;
+       /* result is NaN if any processed number is NaN */
+       if (state->isNaN || NUMERIC_IS_NAN(newval))
+       {
+               state->isNaN = true;
+               return;
+       }
+
+       /* load processed number in short-lived context */
+       init_var_from_num(newval, &X);
+
+       /* if we need X^2, calculate that in short-lived context */
+       if (state->calcSumX2)
+       {
+               init_var(&X2);
+               mul_var(&X, &X, &X2, X.dscale * 2);
+       }
+
+       /* The rest of this needs to work in the aggregate context */
+       old_context = MemoryContextSwitchTo(state->agg_context);
+
+       if (state->N++ > 0)
+       {
+               /* Accumulate sums */
+               add_var(&X, &(state->sumX), &(state->sumX));
+
+               if (state->calcSumX2)
+                       add_var(&X2, &(state->sumX2), &(state->sumX2));
+       }
+       else
+       {
+               /* First input, so initialize sums */
+               set_var_from_var(&X, &(state->sumX));
+
+               if (state->calcSumX2)
+                       set_var_from_var(&X2, &(state->sumX2));
+       }
+
+       MemoryContextSwitchTo(old_context);
 }
 
+/*
+ * Generic transition function for numeric aggregates that require sumX2.
+ */
 Datum
 numeric_accum(PG_FUNCTION_ARGS)
 {
-       ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-       Numeric         newval = PG_GETARG_NUMERIC(1);
+       NumericAggState *state;
 
-       PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
+       state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+       if (!PG_ARGISNULL(1))
+       {
+               /* Create the state data when we see the first non-null input. */
+               if (state == NULL)
+                       state = makeNumericAggState(fcinfo, true);
+
+               do_numeric_accum(state, PG_GETARG_NUMERIC(1));
+       }
+
+       PG_RETURN_POINTER(state);
 }
 
 /*
- * Optimized case for average of numeric.
+ * Generic transition function for numeric aggregates that don't require sumX2.
  */
 Datum
 numeric_avg_accum(PG_FUNCTION_ARGS)
 {
-       ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-       Numeric         newval = PG_GETARG_NUMERIC(1);
+       NumericAggState *state;
 
-       PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
+       state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+       if (!PG_ARGISNULL(1))
+       {
+               /* Create the state data when we see the first non-null input. */
+               if (state == NULL)
+                       state = makeNumericAggState(fcinfo, false);
+
+               do_numeric_accum(state, PG_GETARG_NUMERIC(1));
+       }
+
+       PG_RETURN_POINTER(state);
 }
 
 /*
@@ -2578,87 +2617,142 @@ numeric_avg_accum(PG_FUNCTION_ARGS)
 Datum
 int2_accum(PG_FUNCTION_ARGS)
 {
-       ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-       Datum           newval2 = PG_GETARG_DATUM(1);
-       Numeric         newval;
+       NumericAggState *state;
 
-       newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, newval2));
+       state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+       if (!PG_ARGISNULL(1))
+       {
+               Numeric         newval;
+
+               newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric,
+                                                                                                        PG_GETARG_DATUM(1)));
+
+               /* Create the state data when we see the first non-null input. */
+               if (state == NULL)
+                       state = makeNumericAggState(fcinfo, true);
+
+               do_numeric_accum(state, newval);
+       }
 
-       PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
+       PG_RETURN_POINTER(state);
 }
 
 Datum
 int4_accum(PG_FUNCTION_ARGS)
 {
-       ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-       Datum           newval4 = PG_GETARG_DATUM(1);
-       Numeric         newval;
+       NumericAggState *state;
+
+       state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+       if (!PG_ARGISNULL(1))
+       {
+               Numeric         newval;
 
-       newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, newval4));
+               newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
+                                                                                                        PG_GETARG_DATUM(1)));
+
+               /* Create the state data when we see the first non-null input. */
+               if (state == NULL)
+                       state = makeNumericAggState(fcinfo, true);
+
+               do_numeric_accum(state, newval);
+       }
 
-       PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
+       PG_RETURN_POINTER(state);
 }
 
 Datum
 int8_accum(PG_FUNCTION_ARGS)
 {
-       ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-       Datum           newval8 = PG_GETARG_DATUM(1);
-       Numeric         newval;
+       NumericAggState *state;
+
+       state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+       if (!PG_ARGISNULL(1))
+       {
+               Numeric         newval;
+
+               newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
+                                                                                                        PG_GETARG_DATUM(1)));
 
-       newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
+               /* Create the state data when we see the first non-null input. */
+               if (state == NULL)
+                       state = makeNumericAggState(fcinfo, true);
 
-       PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
+               do_numeric_accum(state, newval);
+       }
+
+       PG_RETURN_POINTER(state);
 }
 
 /*
- * Optimized case for average of int8.
+ * Transition function for int8 input when we don't need sumX2.
  */
 Datum
 int8_avg_accum(PG_FUNCTION_ARGS)
 {
-       ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-       Datum           newval8 = PG_GETARG_DATUM(1);
-       Numeric         newval;
+       NumericAggState *state;
+
+       state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+       if (!PG_ARGISNULL(1))
+       {
+               Numeric         newval;
+
+               newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
+                                                                                                        PG_GETARG_DATUM(1)));
 
-       newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
+               /* Create the state data when we see the first non-null input. */
+               if (state == NULL)
+                       state = makeNumericAggState(fcinfo, false);
 
-       PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
+               do_numeric_accum(state, newval);
+       }
+
+       PG_RETURN_POINTER(state);
 }
 
 
 Datum
 numeric_avg(PG_FUNCTION_ARGS)
 {
-       ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-       Datum      *transdatums;
-       int                     ndatums;
-       Numeric         N,
-                               sumX;
-
-       /* We assume the input is array of numeric */
-       deconstruct_array(transarray,
-                                         NUMERICOID, -1, false, 'i',
-                                         &transdatums, NULL, &ndatums);
-       if (ndatums != 2)
-               elog(ERROR, "expected 2-element numeric array");
-       N = DatumGetNumeric(transdatums[0]);
-       sumX = DatumGetNumeric(transdatums[1]);
+       NumericAggState *state;
+       Datum           N_datum;
+       Datum           sumX_datum;
 
-       /* SQL defines AVG of no values to be NULL */
-       /* N is zero iff no digits (cf. numeric_uminus) */
-       if (NUMERIC_NDIGITS(N) == 0)
+       state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+       if (state == NULL)                      /* there were no non-null inputs */
                PG_RETURN_NULL();
 
-       PG_RETURN_DATUM(DirectFunctionCall2(numeric_div,
-                                                                               NumericGetDatum(sumX),
-                                                                               NumericGetDatum(N)));
+       if (state->isNaN)                       /* there was at least one NaN input */
+               PG_RETURN_NUMERIC(make_result(&const_nan));
+
+       N_datum = DirectFunctionCall1(int8_numeric, Int64GetDatum(state->N));
+       sumX_datum = NumericGetDatum(make_result(&state->sumX));
+
+       PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, sumX_datum, N_datum));
+}
+
+Datum
+numeric_sum(PG_FUNCTION_ARGS)
+{
+       NumericAggState *state;
+
+       state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+       if (state == NULL)                      /* there were no non-null inputs */
+               PG_RETURN_NULL();
+
+       if (state->isNaN)                       /* there was at least one NaN input */
+               PG_RETURN_NUMERIC(make_result(&const_nan));
+
+       PG_RETURN_NUMERIC(make_result(&(state->sumX)));
 }
 
 /*
  * Workhorse routine for the standard deviance and variance
- * aggregates. 'transarray' is the aggregate's transition
- * array. 'variance' specifies whether we should calculate the
+ * aggregates. 'state' is aggregate's transition state.
+ * 'variance' specifies whether we should calculate the
  * variance or the standard deviation. 'sample' indicates whether the
  * caller is interested in the sample or the population
  * variance/stddev.
@@ -2667,16 +2761,11 @@ numeric_avg(PG_FUNCTION_ARGS)
  * *is_null is set to true and NULL is returned.
  */
 static Numeric
-numeric_stddev_internal(ArrayType *transarray,
+numeric_stddev_internal(NumericAggState *state,
                                                bool variance, bool sample,
                                                bool *is_null)
 {
-       Datum      *transdatums;
-       int                     ndatums;
-       Numeric         N,
-                               sumX,
-                               sumX2,
-                               res;
+       Numeric         res;
        NumericVar      vN,
                                vsumX,
                                vsumX2,
@@ -2684,22 +2773,25 @@ numeric_stddev_internal(ArrayType *transarray,
        NumericVar *comp;
        int                     rscale;
 
+       /* Deal with empty input and NaN-input cases */
+       if (state == NULL)
+       {
+               *is_null = true;
+               return NULL;
+       }
+
        *is_null = false;
 
-       /* We assume the input is array of numeric */
-       deconstruct_array(transarray,
-                                         NUMERICOID, -1, false, 'i',
-                                         &transdatums, NULL, &ndatums);
-       if (ndatums != 3)
-               elog(ERROR, "expected 3-element numeric array");
-       N = DatumGetNumeric(transdatums[0]);
-       sumX = DatumGetNumeric(transdatums[1]);
-       sumX2 = DatumGetNumeric(transdatums[2]);
-
-       if (NUMERIC_IS_NAN(N) || NUMERIC_IS_NAN(sumX) || NUMERIC_IS_NAN(sumX2))
+       if (state->isNaN)
                return make_result(&const_nan);
 
-       init_var_from_num(N, &vN);
+       init_var(&vN);
+       init_var(&vsumX);
+       init_var(&vsumX2);
+
+       int8_to_numericvar(state->N, &vN);
+       set_var_from_var(&(state->sumX), &vsumX);
+       set_var_from_var(&(state->sumX2), &vsumX2);
 
        /*
         * Sample stddev and variance are undefined when N <= 1; population stddev
@@ -2719,9 +2811,6 @@ numeric_stddev_internal(ArrayType *transarray,
        init_var(&vNminus1);
        sub_var(&vN, &const_one, &vNminus1);
 
-       init_var_from_num(sumX, &vsumX);
-       init_var_from_num(sumX2, &vsumX2);
-
        /* compute rscale for mul_var calls */
        rscale = vsumX.dscale * 2;
 
@@ -2758,11 +2847,13 @@ numeric_stddev_internal(ArrayType *transarray,
 Datum
 numeric_var_samp(PG_FUNCTION_ARGS)
 {
+       NumericAggState *state;
        Numeric         res;
        bool            is_null;
 
-       res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
-                                                                 true, true, &is_null);
+       state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+       res = numeric_stddev_internal(state, true, true, &is_null);
 
        if (is_null)
                PG_RETURN_NULL();
@@ -2773,11 +2864,13 @@ numeric_var_samp(PG_FUNCTION_ARGS)
 Datum
 numeric_stddev_samp(PG_FUNCTION_ARGS)
 {
+       NumericAggState *state;
        Numeric         res;
        bool            is_null;
 
-       res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
-                                                                 false, true, &is_null);
+       state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+       res = numeric_stddev_internal(state, false, true, &is_null);
 
        if (is_null)
                PG_RETURN_NULL();
@@ -2788,11 +2881,13 @@ numeric_stddev_samp(PG_FUNCTION_ARGS)
 Datum
 numeric_var_pop(PG_FUNCTION_ARGS)
 {
+       NumericAggState *state;
        Numeric         res;
        bool            is_null;
 
-       res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
-                                                                 true, false, &is_null);
+       state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+       res = numeric_stddev_internal(state, true, false, &is_null);
 
        if (is_null)
                PG_RETURN_NULL();
@@ -2803,11 +2898,13 @@ numeric_var_pop(PG_FUNCTION_ARGS)
 Datum
 numeric_stddev_pop(PG_FUNCTION_ARGS)
 {
+       NumericAggState *state;
        Numeric         res;
        bool            is_null;
 
-       res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
-                                                                 false, false, &is_null);
+       state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+       res = numeric_stddev_internal(state, false, false, &is_null);
 
        if (is_null)
                PG_RETURN_NULL();
@@ -2930,6 +3027,9 @@ int4_sum(PG_FUNCTION_ARGS)
        }
 }
 
+/*
+ * Note: this function is obsolete, it's no longer used for SUM(int8).
+ */
 Datum
 int8_sum(PG_FUNCTION_ARGS)
 {
index cb9021c525afd7d73c84ecb5dff54114ebe83aef..b0286157f711f5fd23d9020ec5a6479830a75930 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201311161
+#define CATALOG_VERSION_NO     201311162
 
 #endif
index 416d651bf0a040dae4e3913de2a7e741a15af41a..034456a315d5327ecac19b03d8c03e92b9e2eaa9 100644 (file)
@@ -80,23 +80,23 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
  */
 
 /* avg */
-DATA(insert ( 2100     int8_avg_accum  numeric_avg             0       1231    0       "{0,0}" ));
+DATA(insert ( 2100     int8_avg_accum  numeric_avg             0       2281    128     _null_ ));
 DATA(insert ( 2101     int4_avg_accum  int8_avg                0       1016    0       "{0,0}" ));
 DATA(insert ( 2102     int2_avg_accum  int8_avg                0       1016    0       "{0,0}" ));
-DATA(insert ( 2103     numeric_avg_accum       numeric_avg 0   1231    0       "{0,0}" ));
+DATA(insert ( 2103     numeric_avg_accum       numeric_avg 0   2281    128     _null_ ));
 DATA(insert ( 2104     float4_accum    float8_avg              0       1022    0       "{0,0,0}" ));
 DATA(insert ( 2105     float8_accum    float8_avg              0       1022    0       "{0,0,0}" ));
 DATA(insert ( 2106     interval_accum  interval_avg    0       1187    0       "{0 second,0 second}" ));
 
 /* sum */
-DATA(insert ( 2107     int8_sum                -                               0       1700    0       _null_ ));
+DATA(insert ( 2107     int8_avg_accum  numeric_sum             0       2281    128     _null_ ));
 DATA(insert ( 2108     int4_sum                -                               0       20              0       _null_ ));
 DATA(insert ( 2109     int2_sum                -                               0       20              0       _null_ ));
 DATA(insert ( 2110     float4pl                -                               0       700             0       _null_ ));
 DATA(insert ( 2111     float8pl                -                               0       701             0       _null_ ));
 DATA(insert ( 2112     cash_pl                 -                               0       790             0       _null_ ));
 DATA(insert ( 2113     interval_pl             -                               0       1186    0       _null_ ));
-DATA(insert ( 2114     numeric_add             -                               0       1700    0       _null_ ));
+DATA(insert ( 2114     numeric_avg_accum       numeric_sum     0       2281    128     _null_ ));
 
 /* max */
 DATA(insert ( 2115     int8larger              -                               413             20              0       _null_ ));
@@ -147,52 +147,52 @@ DATA(insert ( 2147        int8inc_any             -                               0               20              0       "0" ));
 DATA(insert ( 2803     int8inc                 -                               0               20              0       "0" ));
 
 /* var_pop */
-DATA(insert ( 2718     int8_accum      numeric_var_pop 0       1231    0       "{0,0,0}" ));
-DATA(insert ( 2719     int4_accum      numeric_var_pop 0       1231    0       "{0,0,0}" ));
-DATA(insert ( 2720     int2_accum      numeric_var_pop 0       1231    0       "{0,0,0}" ));
+DATA(insert ( 2718     int8_accum      numeric_var_pop 0       2281    128     _null_ ));
+DATA(insert ( 2719     int4_accum      numeric_var_pop 0       2281    128     _null_ ));
+DATA(insert ( 2720     int2_accum      numeric_var_pop 0       2281    128     _null_ ));
 DATA(insert ( 2721     float4_accum    float8_var_pop 0        1022    0       "{0,0,0}" ));
 DATA(insert ( 2722     float8_accum    float8_var_pop 0        1022    0       "{0,0,0}" ));
-DATA(insert ( 2723     numeric_accum  numeric_var_pop 0        1231    0       "{0,0,0}" ));
+DATA(insert ( 2723     numeric_accum  numeric_var_pop 0        2281    128     _null_ ));
 
 /* var_samp */
-DATA(insert ( 2641     int8_accum      numeric_var_samp        0       1231    0       "{0,0,0}" ));
-DATA(insert ( 2642     int4_accum      numeric_var_samp        0       1231    0       "{0,0,0}" ));
-DATA(insert ( 2643     int2_accum      numeric_var_samp        0       1231    0       "{0,0,0}" ));
+DATA(insert ( 2641     int8_accum      numeric_var_samp        0       2281    128     _null_ ));
+DATA(insert ( 2642     int4_accum      numeric_var_samp        0       2281    128     _null_ ));
+DATA(insert ( 2643     int2_accum      numeric_var_samp        0       2281    128     _null_ ));
 DATA(insert ( 2644     float4_accum    float8_var_samp 0       1022    0       "{0,0,0}" ));
 DATA(insert ( 2645     float8_accum    float8_var_samp 0       1022    0       "{0,0,0}" ));
-DATA(insert ( 2646     numeric_accum  numeric_var_samp 0       1231    0       "{0,0,0}" ));
+DATA(insert ( 2646     numeric_accum  numeric_var_samp 0       2281    128     _null_ ));
 
 /* variance: historical Postgres syntax for var_samp */
-DATA(insert ( 2148     int8_accum      numeric_var_samp        0       1231    0       "{0,0,0}" ));
-DATA(insert ( 2149     int4_accum      numeric_var_samp        0       1231    0       "{0,0,0}" ));
-DATA(insert ( 2150     int2_accum      numeric_var_samp        0       1231    0       "{0,0,0}" ));
+DATA(insert ( 2148     int8_accum      numeric_var_samp        0       2281    128     _null_ ));
+DATA(insert ( 2149     int4_accum      numeric_var_samp        0       2281    128     _null_ ));
+DATA(insert ( 2150     int2_accum      numeric_var_samp        0       2281    128     _null_ ));
 DATA(insert ( 2151     float4_accum    float8_var_samp 0       1022    0       "{0,0,0}" ));
 DATA(insert ( 2152     float8_accum    float8_var_samp 0       1022    0       "{0,0,0}" ));
-DATA(insert ( 2153     numeric_accum  numeric_var_samp 0       1231    0       "{0,0,0}" ));
+DATA(insert ( 2153     numeric_accum  numeric_var_samp 0       2281    128     _null_ ));
 
 /* stddev_pop */
-DATA(insert ( 2724     int8_accum      numeric_stddev_pop              0       1231    0       "{0,0,0}" ));
-DATA(insert ( 2725     int4_accum      numeric_stddev_pop              0       1231    0       "{0,0,0}" ));
-DATA(insert ( 2726     int2_accum      numeric_stddev_pop              0       1231    0       "{0,0,0}" ));
+DATA(insert ( 2724     int8_accum      numeric_stddev_pop              0       2281    128     _null_ ));
+DATA(insert ( 2725     int4_accum      numeric_stddev_pop              0       2281    128     _null_ ));
+DATA(insert ( 2726     int2_accum      numeric_stddev_pop              0       2281    128     _null_ ));
 DATA(insert ( 2727     float4_accum    float8_stddev_pop       0       1022    0       "{0,0,0}" ));
 DATA(insert ( 2728     float8_accum    float8_stddev_pop       0       1022    0       "{0,0,0}" ));
-DATA(insert ( 2729     numeric_accum   numeric_stddev_pop      0       1231    0       "{0,0,0}" ));
+DATA(insert ( 2729     numeric_accum   numeric_stddev_pop      0       2281    128     _null_ ));
 
 /* stddev_samp */
-DATA(insert ( 2712     int8_accum      numeric_stddev_samp             0       1231    0       "{0,0,0}" ));
-DATA(insert ( 2713     int4_accum      numeric_stddev_samp             0       1231    0       "{0,0,0}" ));
-DATA(insert ( 2714     int2_accum      numeric_stddev_samp             0       1231    0       "{0,0,0}" ));
+DATA(insert ( 2712     int8_accum      numeric_stddev_samp             0       2281    128     _null_ ));
+DATA(insert ( 2713     int4_accum      numeric_stddev_samp             0       2281    128     _null_ ));
+DATA(insert ( 2714     int2_accum      numeric_stddev_samp             0       2281    128     _null_ ));
 DATA(insert ( 2715     float4_accum    float8_stddev_samp      0       1022    0       "{0,0,0}" ));
 DATA(insert ( 2716     float8_accum    float8_stddev_samp      0       1022    0       "{0,0,0}" ));
-DATA(insert ( 2717     numeric_accum   numeric_stddev_samp 0   1231    0       "{0,0,0}" ));
+DATA(insert ( 2717     numeric_accum   numeric_stddev_samp 0   2281    128     _null_ ));
 
 /* stddev: historical Postgres syntax for stddev_samp */
-DATA(insert ( 2154     int8_accum      numeric_stddev_samp             0       1231    0       "{0,0,0}" ));
-DATA(insert ( 2155     int4_accum      numeric_stddev_samp             0       1231    0       "{0,0,0}" ));
-DATA(insert ( 2156     int2_accum      numeric_stddev_samp             0       1231    0       "{0,0,0}" ));
+DATA(insert ( 2154     int8_accum      numeric_stddev_samp             0       2281    128     _null_ ));
+DATA(insert ( 2155     int4_accum      numeric_stddev_samp             0       2281    128     _null_ ));
+DATA(insert ( 2156     int2_accum      numeric_stddev_samp             0       2281    128     _null_ ));
 DATA(insert ( 2157     float4_accum    float8_stddev_samp      0       1022    0       "{0,0,0}" ));
 DATA(insert ( 2158     float8_accum    float8_stddev_samp      0       1022    0       "{0,0,0}" ));
-DATA(insert ( 2159     numeric_accum   numeric_stddev_samp 0   1231    0       "{0,0,0}" ));
+DATA(insert ( 2159     numeric_accum   numeric_stddev_samp 0   2281    128     _null_ ));
 
 /* SQL2003 binary regression aggregates */
 DATA(insert ( 2818     int8inc_float8_float8           -                               0       20              0       "0" ));
index ca4fc62bcdb28b012de206a6ebd062bae4959ba9..c5d5d291558e23c9b1b6768f69810d2238f48709 100644 (file)
@@ -2390,27 +2390,29 @@ DATA(insert OID = 2513 (  float8_stddev_pop PGNSP PGUID 12 1 0 0 0 f f f f t f i
 DESCR("aggregate final function");
 DATA(insert OID = 1832 (  float8_stddev_samp   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 701 "1022" _null_ _null_ _null_ _null_ float8_stddev_samp _null_ _null_ _null_ ));
 DESCR("aggregate final function");
-DATA(insert OID = 1833 (  numeric_accum    PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1231 "1231 1700" _null_ _null_ _null_ _null_ numeric_accum _null_ _null_ _null_ ));
+DATA(insert OID = 1833 (  numeric_accum    PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ numeric_accum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
-DATA(insert OID = 2858 (  numeric_avg_accum    PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1231 "1231 1700" _null_ _null_ _null_ _null_ numeric_avg_accum _null_ _null_ _null_ ));
+DATA(insert OID = 2858 (  numeric_avg_accum    PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ numeric_avg_accum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
-DATA(insert OID = 1834 (  int2_accum      PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1231 "1231 21" _null_ _null_ _null_ _null_ int2_accum _null_ _null_ _null_ ));
+DATA(insert OID = 1834 (  int2_accum      PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 21" _null_ _null_ _null_ _null_ int2_accum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
-DATA(insert OID = 1835 (  int4_accum      PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1231 "1231 23" _null_ _null_ _null_ _null_ int4_accum _null_ _null_ _null_ ));
+DATA(insert OID = 1835 (  int4_accum      PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 23" _null_ _null_ _null_ _null_ int4_accum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
-DATA(insert OID = 1836 (  int8_accum      PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1231 "1231 20" _null_ _null_ _null_ _null_ int8_accum _null_ _null_ _null_ ));
+DATA(insert OID = 1836 (  int8_accum      PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 20" _null_ _null_ _null_ _null_ int8_accum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
-DATA(insert OID = 2746 (  int8_avg_accum          PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1231 "1231 20" _null_ _null_ _null_ _null_ int8_avg_accum _null_ _null_ _null_ ));
+DATA(insert OID = 2746 (  int8_avg_accum          PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 20" _null_ _null_ _null_ _null_ int8_avg_accum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
-DATA(insert OID = 1837 (  numeric_avg     PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_ numeric_avg _null_ _null_ _null_ ));
+DATA(insert OID = 3178 (  numeric_sum     PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_sum _null_ _null_ _null_ ));
 DESCR("aggregate final function");
-DATA(insert OID = 2514 (  numeric_var_pop  PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_ numeric_var_pop _null_ _null_ _null_ ));
+DATA(insert OID = 1837 (  numeric_avg     PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_avg _null_ _null_ _null_ ));
 DESCR("aggregate final function");
-DATA(insert OID = 1838 (  numeric_var_samp PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_ numeric_var_samp _null_ _null_ _null_ ));
+DATA(insert OID = 2514 (  numeric_var_pop  PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_var_pop _null_ _null_ _null_ ));
 DESCR("aggregate final function");
-DATA(insert OID = 2596 (  numeric_stddev_pop PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_  numeric_stddev_pop _null_ _null_ _null_ ));
+DATA(insert OID = 1838 (  numeric_var_samp PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_var_samp _null_ _null_ _null_ ));
 DESCR("aggregate final function");
-DATA(insert OID = 1839 (  numeric_stddev_samp  PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_ numeric_stddev_samp _null_ _null_ _null_ ));
+DATA(insert OID = 2596 (  numeric_stddev_pop PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_  numeric_stddev_pop _null_ _null_ _null_ ));
+DESCR("aggregate final function");
+DATA(insert OID = 1839 (  numeric_stddev_samp  PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_stddev_samp _null_ _null_ _null_ ));
 DESCR("aggregate final function");
 DATA(insert OID = 1840 (  int2_sum                PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 20 "20 21" _null_ _null_ _null_ _null_ int2_sum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
index ce3f00bc12dbe68d25ce7fb07d01ebe1626ce0a8..f25a9de8d317f3157ce057afd047365337510a36 100644 (file)
@@ -989,6 +989,7 @@ extern Datum int4_accum(PG_FUNCTION_ARGS);
 extern Datum int8_accum(PG_FUNCTION_ARGS);
 extern Datum int8_avg_accum(PG_FUNCTION_ARGS);
 extern Datum numeric_avg(PG_FUNCTION_ARGS);
+extern Datum numeric_sum(PG_FUNCTION_ARGS);
 extern Datum numeric_var_pop(PG_FUNCTION_ARGS);
 extern Datum numeric_var_samp(PG_FUNCTION_ARGS);
 extern Datum numeric_stddev_pop(PG_FUNCTION_ARGS);
index 298b2e4eb9c4156b3a1f20d306a5f4319f159214..e17881c59f40d53f498528ae2f0758d1bc8059a4 100644 (file)
@@ -137,6 +137,67 @@ SELECT stddev_pop(3.0::numeric), stddev_samp(4.0::numeric);
           0 |            
 (1 row)
 
+-- verify correct results for null and NaN inputs
+select sum(null::int4) from generate_series(1,3);
+ sum 
+-----
+    
+(1 row)
+
+select sum(null::int8) from generate_series(1,3);
+ sum 
+-----
+    
+(1 row)
+
+select sum(null::numeric) from generate_series(1,3);
+ sum 
+-----
+    
+(1 row)
+
+select sum(null::float8) from generate_series(1,3);
+ sum 
+-----
+    
+(1 row)
+
+select avg(null::int4) from generate_series(1,3);
+ avg 
+-----
+    
+(1 row)
+
+select avg(null::int8) from generate_series(1,3);
+ avg 
+-----
+    
+(1 row)
+
+select avg(null::numeric) from generate_series(1,3);
+ avg 
+-----
+    
+(1 row)
+
+select avg(null::float8) from generate_series(1,3);
+ avg 
+-----
+    
+(1 row)
+
+select sum('NaN'::numeric) from generate_series(1,3);
+ sum 
+-----
+ NaN
+(1 row)
+
+select avg('NaN'::numeric) from generate_series(1,3);
+ avg 
+-----
+ NaN
+(1 row)
+
 -- SQL2003 binary aggregates
 SELECT regr_count(b, a) FROM aggtest;
  regr_count 
index 397edffd3ada13af2b3986466cd23f1559d3aa3e..1bbe073351cb1bf0ec59c4d2fd6684d53de84a0c 100644 (file)
@@ -39,6 +39,18 @@ SELECT var_samp(b::numeric) FROM aggtest;
 SELECT var_pop(1.0), var_samp(2.0);
 SELECT stddev_pop(3.0::numeric), stddev_samp(4.0::numeric);
 
+-- verify correct results for null and NaN inputs
+select sum(null::int4) from generate_series(1,3);
+select sum(null::int8) from generate_series(1,3);
+select sum(null::numeric) from generate_series(1,3);
+select sum(null::float8) from generate_series(1,3);
+select avg(null::int4) from generate_series(1,3);
+select avg(null::int8) from generate_series(1,3);
+select avg(null::numeric) from generate_series(1,3);
+select avg(null::float8) from generate_series(1,3);
+select sum('NaN'::numeric) from generate_series(1,3);
+select avg('NaN'::numeric) from generate_series(1,3);
+
 -- SQL2003 binary aggregates
 SELECT regr_count(b, a) FROM aggtest;
 SELECT regr_sxx(b, a) FROM aggtest;