Provide moving-aggregate support for a bunch of numerical aggregates.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 13 Apr 2014 00:33:09 +0000 (20:33 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 13 Apr 2014 00:33:09 +0000 (20:33 -0400)
First installment of the promised moving-aggregate support in built-in
aggregates: count(), sum(), avg(), stddev() and variance() for
assorted datatypes, though not for float4/float8.

In passing, remove a 2001-vintage kluge in interval_accum(): interval
array elements have been properly aligned since around 2003, but
nobody remembered to take out this workaround.  Also, fix a thinko
in the opr_sanity tests for moving-aggregate catalog entries.

David Rowley and Florian Pflug, reviewed by Dean Rasheed

13 files changed:
src/backend/utils/adt/int8.c
src/backend/utils/adt/numeric.c
src/backend/utils/adt/timestamp.c
src/include/catalog/catversion.h
src/include/catalog/pg_aggregate.h
src/include/catalog/pg_proc.h
src/include/utils/builtins.h
src/include/utils/int8.h
src/include/utils/timestamp.h
src/test/regress/expected/opr_sanity.out
src/test/regress/expected/window.out
src/test/regress/sql/opr_sanity.sql
src/test/regress/sql/window.sql

index 5e1be90dac3e160b45f5e182f9a5133fe102595e..e78eb2a2022ece8f4a41c46dc9e316266e68385e 100644 (file)
@@ -717,13 +717,58 @@ int8inc(PG_FUNCTION_ARGS)
    }
 }
 
+Datum
+int8dec(PG_FUNCTION_ARGS)
+{
+   /*
+    * When int8 is pass-by-reference, we provide this special case to avoid
+    * palloc overhead for COUNT(): when called as an aggregate, we know that
+    * the argument is modifiable local storage, so just update it in-place.
+    * (If int8 is pass-by-value, then of course this is useless as well as
+    * incorrect, so just ifdef it out.)
+    */
+#ifndef USE_FLOAT8_BYVAL       /* controls int8 too */
+   if (AggCheckCallContext(fcinfo, NULL))
+   {
+       int64      *arg = (int64 *) PG_GETARG_POINTER(0);
+       int64       result;
+
+       result = *arg - 1;
+       /* Overflow check */
+       if (result > 0 && *arg < 0)
+           ereport(ERROR,
+                   (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                    errmsg("bigint out of range")));
+
+       *arg = result;
+       PG_RETURN_POINTER(arg);
+   }
+   else
+#endif
+   {
+       /* Not called as an aggregate, so just do it the dumb way */
+       int64       arg = PG_GETARG_INT64(0);
+       int64       result;
+
+       result = arg - 1;
+       /* Overflow check */
+       if (result > 0 && arg < 0)
+           ereport(ERROR,
+                   (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                    errmsg("bigint out of range")));
+
+       PG_RETURN_INT64(result);
+   }
+}
+
+
 /*
- * These functions are exactly like int8inc but are used for aggregates that
- * count only non-null values. Since the functions are declared strict,
- * the null checks happen before we ever get here, and all we need do is
- * increment the state value.  We could actually make these pg_proc entries
- * point right at int8inc, but then the opr_sanity regression test would
- * complain about mismatched entries for a built-in function.
+ * These functions are exactly like int8inc/int8dec but are used for
+ * aggregates that count only non-null values. Since the functions are
+ * declared strict, the null checks happen before we ever get here, and all we
+ * need do is increment the state value.  We could actually make these pg_proc
+ * entries point right at int8inc/int8dec, but then the opr_sanity regression
+ * test would complain about mismatched entries for a built-in function.
  */
 
 Datum
@@ -738,6 +783,12 @@ int8inc_float8_float8(PG_FUNCTION_ARGS)
    return int8inc(fcinfo);
 }
 
+Datum
+int8dec_any(PG_FUNCTION_ARGS)
+{
+   return int8dec(fcinfo);
+}
+
 
 Datum
 int8larger(PG_FUNCTION_ARGS)
index 64eb0f8d16e668adfaca3e6527d4d97b82ecbe34..bf4f29d14d70471dd8a5345ae9e3b586f28edb88 100644 (file)
@@ -2506,22 +2506,19 @@ numeric_float4(PG_FUNCTION_ARGS)
  * Actually, it's a pointer to a NumericAggState allocated in the aggregate
  * context.  The digit buffers for the NumericVars will be there too.
  *
- * 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.)
- *
  * ----------------------------------------------------------------------
  */
 
 typedef struct NumericAggState
 {
    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 */
+   int         maxScale;       /* maximum scale seen so far */
+   int64       maxScaleCount;  /* number of values seen with maximum scale */
+   int64       NaNcount;       /* count of NaN values (not included in N!) */
 } NumericAggState;
 
 /*
@@ -2559,16 +2556,28 @@ do_numeric_accum(NumericAggState *state, Numeric newval)
    NumericVar  X2;
    MemoryContext old_context;
 
-   /* result is NaN if any processed number is NaN */
-   if (state->isNaN || NUMERIC_IS_NAN(newval))
+   /* Count NaN inputs separately from all else */
+   if (NUMERIC_IS_NAN(newval))
    {
-       state->isNaN = true;
+       state->NaNcount++;
        return;
    }
 
    /* load processed number in short-lived context */
    init_var_from_num(newval, &X);
 
+   /*
+    * Track the highest input dscale that we've seen, to support inverse
+    * transitions (see do_numeric_discard).
+    */
+   if (X.dscale > state->maxScale)
+   {
+       state->maxScale = X.dscale;
+       state->maxScaleCount = 1;
+   }
+   else if (X.dscale == state->maxScale)
+       state->maxScaleCount++;
+
    /* if we need X^2, calculate that in short-lived context */
    if (state->calcSumX2)
    {
@@ -2599,6 +2608,97 @@ do_numeric_accum(NumericAggState *state, Numeric newval)
    MemoryContextSwitchTo(old_context);
 }
 
+/*
+ * Attempt to remove an input value from the aggregated state.
+ *
+ * If the value cannot be removed then the function will return false; the
+ * possible reasons for failing are described below.
+ *
+ * If we aggregate the values 1.01 and 2 then the result will be 3.01.
+ * If we are then asked to un-aggregate the 1.01 then we must fail as we
+ * won't be able to tell what the new aggregated value's dscale should be.
+ * We don't want to return 2.00 (dscale = 2), since the sum's dscale would
+ * have been zero if we'd really aggregated only 2.
+ *
+ * Note: alternatively, we could count the number of inputs with each possible
+ * dscale (up to some sane limit).  Not yet clear if it's worth the trouble.
+ */
+static bool
+do_numeric_discard(NumericAggState *state, Numeric newval)
+{
+   NumericVar  X;
+   NumericVar  X2;
+   MemoryContext old_context;
+
+   /* Count NaN inputs separately from all else */
+   if (NUMERIC_IS_NAN(newval))
+   {
+       state->NaNcount--;
+       return true;
+   }
+
+   /* load processed number in short-lived context */
+   init_var_from_num(newval, &X);
+
+   /*
+    * state->sumX's dscale is the maximum dscale of any of the inputs.
+    * Removing the last input with that dscale would require us to recompute
+    * the maximum dscale of the *remaining* inputs, which we cannot do unless
+    * no more non-NaN inputs remain at all.  So we report a failure instead,
+    * and force the aggregation to be redone from scratch.
+    */
+   if (X.dscale == state->maxScale)
+   {
+       if (state->maxScaleCount > 1 || state->maxScale == 0)
+       {
+           /*
+            * Some remaining inputs have same dscale, or dscale hasn't
+            * gotten above zero anyway
+            */
+           state->maxScaleCount--;
+       }
+       else if (state->N == 1)
+       {
+           /* No remaining non-NaN inputs at all, so reset maxScale */
+           state->maxScale = 0;
+           state->maxScaleCount = 0;
+       }
+       else
+       {
+           /* Correct new maxScale is uncertain, must fail */
+           return false;
+       }
+   }
+
+   /* 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-- > 1)
+   {
+       /* De-accumulate sums */
+       sub_var(&(state->sumX), &X, &(state->sumX));
+
+       if (state->calcSumX2)
+           sub_var(&(state->sumX2), &X2, &(state->sumX2));
+   }
+   else
+   {
+       /* Sums will be reset by next call to do_numeric_accum */
+       Assert(state->N == 0);
+   }
+
+   MemoryContextSwitchTo(old_context);
+
+   return true;
+}
+
 /*
  * Generic transition function for numeric aggregates that require sumX2.
  */
@@ -2609,14 +2709,12 @@ numeric_accum(PG_FUNCTION_ARGS)
 
    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);
+   /* Create the state data on the first call */
+   if (state == NULL)
+       state = makeNumericAggState(fcinfo, true);
 
+   if (!PG_ARGISNULL(1))
        do_numeric_accum(state, PG_GETARG_NUMERIC(1));
-   }
 
    PG_RETURN_POINTER(state);
 }
@@ -2631,18 +2729,42 @@ numeric_avg_accum(PG_FUNCTION_ARGS)
 
    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);
+   /* Create the state data on the first call */
+   if (state == NULL)
+       state = makeNumericAggState(fcinfo, false);
 
+   if (!PG_ARGISNULL(1))
        do_numeric_accum(state, PG_GETARG_NUMERIC(1));
+
+   PG_RETURN_POINTER(state);
+}
+
+/*
+ * Generic inverse transition function for numeric aggregates
+ * (with or without requirement for X^2).
+ */
+Datum
+numeric_accum_inv(PG_FUNCTION_ARGS)
+{
+   NumericAggState *state;
+
+   state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+   /* Should not get here with no state */
+   if (state == NULL)
+       elog(ERROR, "numeric_accum_inv called with NULL state");
+
+   if (!PG_ARGISNULL(1))
+   {
+       /* If we fail to perform the inverse transition, return NULL */
+       if (!do_numeric_discard(state, PG_GETARG_NUMERIC(1)))
+           PG_RETURN_NULL();
    }
 
    PG_RETURN_POINTER(state);
 }
 
+
 /*
  * Integer data types all use Numeric accumulators to share code and
  * avoid risk of overflow. For int2 and int4 inputs, Numeric accumulation
@@ -2659,17 +2781,16 @@ int2_accum(PG_FUNCTION_ARGS)
 
    state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
 
+   /* Create the state data on the first call */
+   if (state == NULL)
+       state = makeNumericAggState(fcinfo, true);
+
    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);
    }
 
@@ -2683,17 +2804,16 @@ int4_accum(PG_FUNCTION_ARGS)
 
    state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
 
+   /* Create the state data on the first call */
+   if (state == NULL)
+       state = makeNumericAggState(fcinfo, true);
+
    if (!PG_ARGISNULL(1))
    {
        Numeric     newval;
 
        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);
    }
 
@@ -2707,17 +2827,16 @@ int8_accum(PG_FUNCTION_ARGS)
 
    state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
 
+   /* Create the state data on the first call */
+   if (state == NULL)
+       state = makeNumericAggState(fcinfo, true);
+
    if (!PG_ARGISNULL(1))
    {
        Numeric     newval;
 
        newval = DatumGetNumeric(DirectFunctionCall1(int8_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);
    }
 
@@ -2734,23 +2853,104 @@ int8_avg_accum(PG_FUNCTION_ARGS)
 
    state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
 
+   /* Create the state data on the first call */
+   if (state == NULL)
+       state = makeNumericAggState(fcinfo, false);
+
    if (!PG_ARGISNULL(1))
    {
        Numeric     newval;
 
        newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
                                                     PG_GETARG_DATUM(1)));
+       do_numeric_accum(state, newval);
+   }
 
-       /* Create the state data when we see the first non-null input. */
-       if (state == NULL)
-           state = makeNumericAggState(fcinfo, false);
+   PG_RETURN_POINTER(state);
+}
 
-       do_numeric_accum(state, newval);
+
+/*
+ * Inverse transition functions to go with the above.
+ */
+
+Datum
+int2_accum_inv(PG_FUNCTION_ARGS)
+{
+   NumericAggState *state;
+
+   state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+   /* Should not get here with no state */
+   if (state == NULL)
+       elog(ERROR, "int2_accum_inv called with NULL state");
+
+   if (!PG_ARGISNULL(1))
+   {
+       Numeric     newval;
+
+       newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric,
+                                                    PG_GETARG_DATUM(1)));
+
+       /* Should never fail, all inputs have dscale 0 */
+       if (!do_numeric_discard(state, newval))
+           elog(ERROR, "do_numeric_discard failed unexpectedly");
    }
 
    PG_RETURN_POINTER(state);
 }
 
+Datum
+int4_accum_inv(PG_FUNCTION_ARGS)
+{
+   NumericAggState *state;
+
+   state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+   /* Should not get here with no state */
+   if (state == NULL)
+       elog(ERROR, "int4_accum_inv called with NULL state");
+
+   if (!PG_ARGISNULL(1))
+   {
+       Numeric     newval;
+
+       newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
+                                                    PG_GETARG_DATUM(1)));
+
+       /* Should never fail, all inputs have dscale 0 */
+       if (!do_numeric_discard(state, newval))
+           elog(ERROR, "do_numeric_discard failed unexpectedly");
+   }
+
+   PG_RETURN_POINTER(state);
+}
+
+Datum
+int8_accum_inv(PG_FUNCTION_ARGS)
+{
+   NumericAggState *state;
+
+   state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+   /* Should not get here with no state */
+   if (state == NULL)
+       elog(ERROR, "int8_accum_inv called with NULL state");
+
+   if (!PG_ARGISNULL(1))
+   {
+       Numeric     newval;
+
+       newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
+                                                    PG_GETARG_DATUM(1)));
+
+       /* Should never fail, all inputs have dscale 0 */
+       if (!do_numeric_discard(state, newval))
+           elog(ERROR, "do_numeric_discard failed unexpectedly");
+   }
+
+   PG_RETURN_POINTER(state);
+}
 
 Datum
 numeric_avg(PG_FUNCTION_ARGS)
@@ -2760,10 +2960,12 @@ numeric_avg(PG_FUNCTION_ARGS)
    Datum       sumX_datum;
 
    state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
-   if (state == NULL)          /* there were no non-null inputs */
+
+   /* If there were no non-null inputs, return NULL */
+   if (state == NULL || (state->N + state->NaNcount) == 0)
        PG_RETURN_NULL();
 
-   if (state->isNaN)           /* there was at least one NaN input */
+   if (state->NaNcount > 0)        /* there was at least one NaN input */
        PG_RETURN_NUMERIC(make_result(&const_nan));
 
    N_datum = DirectFunctionCall1(int8_numeric, Int64GetDatum(state->N));
@@ -2778,10 +2980,12 @@ 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 */
+
+   /* If there were no non-null inputs, return NULL */
+   if (state == NULL || (state->N + state->NaNcount) == 0)
        PG_RETURN_NULL();
 
-   if (state->isNaN)           /* there was at least one NaN input */
+   if (state->NaNcount > 0)        /* there was at least one NaN input */
        PG_RETURN_NUMERIC(make_result(&const_nan));
 
    PG_RETURN_NUMERIC(make_result(&(state->sumX)));
@@ -2812,7 +3016,7 @@ numeric_stddev_internal(NumericAggState *state,
    int         rscale;
 
    /* Deal with empty input and NaN-input cases */
-   if (state == NULL)
+   if (state == NULL || (state->N + state->NaNcount) == 0)
    {
        *is_null = true;
        return NULL;
@@ -2820,7 +3024,7 @@ numeric_stddev_internal(NumericAggState *state,
 
    *is_null = false;
 
-   if (state->isNaN)
+   if (state->NaNcount > 0)
        return make_result(&const_nan);
 
    init_var(&vN);
@@ -2965,6 +3169,9 @@ numeric_stddev_pop(PG_FUNCTION_ARGS)
  * data value into the transition data: it doesn't know how to do the type
  * conversion. The upshot is that these routines have to be marked non-strict
  * and handle substitution of the first non-null input themselves.
+ *
+ * Note: these functions are used only in plain aggregation mode.
+ * In moving-aggregate mode, we use intX_avg_accum and intX_avg_accum_inv.
  */
 
 Datum
@@ -3107,6 +3314,10 @@ int8_sum(PG_FUNCTION_ARGS)
 /*
  * Routines for avg(int2) and avg(int4).  The transition datatype
  * is a two-element int8 array, holding count and sum.
+ *
+ * These functions are also used for sum(int2) and sum(int4) when
+ * operating in moving-aggregate mode, since for correct inverse transitions
+ * we need to count the inputs.
  */
 
 typedef struct Int8TransTypeData
@@ -3171,6 +3382,62 @@ int4_avg_accum(PG_FUNCTION_ARGS)
    PG_RETURN_ARRAYTYPE_P(transarray);
 }
 
+Datum
+int2_avg_accum_inv(PG_FUNCTION_ARGS)
+{
+   ArrayType  *transarray;
+   int16       newval = PG_GETARG_INT16(1);
+   Int8TransTypeData *transdata;
+
+   /*
+    * If we're invoked as an aggregate, we can cheat and modify our first
+    * parameter in-place to reduce palloc overhead. Otherwise we need to make
+    * a copy of it before scribbling on it.
+    */
+   if (AggCheckCallContext(fcinfo, NULL))
+       transarray = PG_GETARG_ARRAYTYPE_P(0);
+   else
+       transarray = PG_GETARG_ARRAYTYPE_P_COPY(0);
+
+   if (ARR_HASNULL(transarray) ||
+       ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
+       elog(ERROR, "expected 2-element int8 array");
+
+   transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);
+   transdata->count--;
+   transdata->sum -= newval;
+
+   PG_RETURN_ARRAYTYPE_P(transarray);
+}
+
+Datum
+int4_avg_accum_inv(PG_FUNCTION_ARGS)
+{
+   ArrayType  *transarray;
+   int32       newval = PG_GETARG_INT32(1);
+   Int8TransTypeData *transdata;
+
+   /*
+    * If we're invoked as an aggregate, we can cheat and modify our first
+    * parameter in-place to reduce palloc overhead. Otherwise we need to make
+    * a copy of it before scribbling on it.
+    */
+   if (AggCheckCallContext(fcinfo, NULL))
+       transarray = PG_GETARG_ARRAYTYPE_P(0);
+   else
+       transarray = PG_GETARG_ARRAYTYPE_P_COPY(0);
+
+   if (ARR_HASNULL(transarray) ||
+       ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
+       elog(ERROR, "expected 2-element int8 array");
+
+   transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);
+   transdata->count--;
+   transdata->sum -= newval;
+
+   PG_RETURN_ARRAYTYPE_P(transarray);
+}
+
 Datum
 int8_avg(PG_FUNCTION_ARGS)
 {
@@ -3196,6 +3463,28 @@ int8_avg(PG_FUNCTION_ARGS)
    PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, sumd, countd));
 }
 
+/*
+ * SUM(int2) and SUM(int4) both return int8, so we can use this
+ * final function for both.
+ */
+Datum
+int2int4_sum(PG_FUNCTION_ARGS)
+{
+   ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
+   Int8TransTypeData *transdata;
+
+   if (ARR_HASNULL(transarray) ||
+       ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
+       elog(ERROR, "expected 2-element int8 array");
+   transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);
+
+   /* SQL defines SUM of no values to be NULL */
+   if (transdata->count == 0)
+       PG_RETURN_NULL();
+
+   PG_RETURN_DATUM(Int64GetDatumFast(transdata->sum));
+}
+
 
 /* ----------------------------------------------------------------------
  *
index ce30bb6e9fa65e503beeea7ccd9cf82bf050bd09..efc1e9b99257a548d04b36e3882b91f8fa3682d7 100644 (file)
@@ -3229,7 +3229,6 @@ interval_mi(PG_FUNCTION_ARGS)
                (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                 errmsg("interval out of range")));
 
-
    PG_RETURN_INTERVAL_P(result);
 }
 
@@ -3376,12 +3375,18 @@ interval_div(PG_FUNCTION_ARGS)
 }
 
 /*
- * interval_accum and interval_avg implement the AVG(interval) aggregate.
+ * interval_accum, interval_accum_inv, and interval_avg implement the
+ * AVG(interval) aggregate.
  *
  * The transition datatype for this aggregate is a 2-element array of
  * intervals, where the first is the running sum and the second contains
  * the number of values so far in its 'time' field.  This is a bit ugly
  * but it beats inventing a specialized datatype for the purpose.
+ *
+ * NOTE: The inverse transition function cannot guarantee exact results
+ * when using float8 timestamps.  However, int8 timestamps are now the
+ * norm, and the probable range of values is not so wide that disastrous
+ * cancellation is likely even with float8, so we'll ignore the risk.
  */
 
 Datum
@@ -3402,17 +3407,8 @@ interval_accum(PG_FUNCTION_ARGS)
    if (ndatums != 2)
        elog(ERROR, "expected 2-element interval array");
 
-   /*
-    * XXX memcpy, instead of just extracting a pointer, to work around buggy
-    * array code: it won't ensure proper alignment of Interval objects on
-    * machines where double requires 8-byte alignment. That should be fixed,
-    * but in the meantime...
-    *
-    * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
-    * compilers optimize into double-aligned load/store anyway.
-    */
-   memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval));
-   memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval));
+   sumX = *(DatumGetIntervalP(transdatums[0]));
+   N = *(DatumGetIntervalP(transdatums[1]));
 
    newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl,
                                                   IntervalPGetDatum(&sumX),
@@ -3428,6 +3424,41 @@ interval_accum(PG_FUNCTION_ARGS)
    PG_RETURN_ARRAYTYPE_P(result);
 }
 
+Datum
+interval_accum_inv(PG_FUNCTION_ARGS)
+{
+   ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
+   Interval   *newval = PG_GETARG_INTERVAL_P(1);
+   Datum      *transdatums;
+   int         ndatums;
+   Interval    sumX,
+               N;
+   Interval   *newsum;
+   ArrayType  *result;
+
+   deconstruct_array(transarray,
+                     INTERVALOID, sizeof(Interval), false, 'd',
+                     &transdatums, NULL, &ndatums);
+   if (ndatums != 2)
+       elog(ERROR, "expected 2-element interval array");
+
+   sumX = *(DatumGetIntervalP(transdatums[0]));
+   N = *(DatumGetIntervalP(transdatums[1]));
+
+   newsum = DatumGetIntervalP(DirectFunctionCall2(interval_mi,
+                                                  IntervalPGetDatum(&sumX),
+                                                IntervalPGetDatum(newval)));
+   N.time -= 1;
+
+   transdatums[0] = IntervalPGetDatum(newsum);
+   transdatums[1] = IntervalPGetDatum(&N);
+
+   result = construct_array(transdatums, 2,
+                            INTERVALOID, sizeof(Interval), false, 'd');
+
+   PG_RETURN_ARRAYTYPE_P(result);
+}
+
 Datum
 interval_avg(PG_FUNCTION_ARGS)
 {
@@ -3443,17 +3474,8 @@ interval_avg(PG_FUNCTION_ARGS)
    if (ndatums != 2)
        elog(ERROR, "expected 2-element interval array");
 
-   /*
-    * XXX memcpy, instead of just extracting a pointer, to work around buggy
-    * array code: it won't ensure proper alignment of Interval objects on
-    * machines where double requires 8-byte alignment. That should be fixed,
-    * but in the meantime...
-    *
-    * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
-    * compilers optimize into double-aligned load/store anyway.
-    */
-   memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval));
-   memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval));
+   sumX = *(DatumGetIntervalP(transdatums[0]));
+   N = *(DatumGetIntervalP(transdatums[1]));
 
    /* SQL defines AVG of no values to be NULL */
    if (N.time == 0)
@@ -3461,7 +3483,7 @@ interval_avg(PG_FUNCTION_ARGS)
 
    return DirectFunctionCall2(interval_div,
                               IntervalPGetDatum(&sumX),
-                              Float8GetDatum(N.time));
+                              Float8GetDatum((double) N.time));
 }
 
 
index 2fb0ce8656352ef7baede4479c2dbd28ae570e69..0d301cf1ace0db124e85277c635a4e6bf10045cd 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 201404121
+#define CATALOG_VERSION_NO 201404122
 
 #endif
index 3cb0d754e7d0cd35183b6f2be07932bd9529aa1a..5116beb62a901e2995d4d638013e3768ddeadaa6 100644 (file)
@@ -119,177 +119,177 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
  */
 
 /* avg */
-DATA(insert ( 2100 n 0 int8_avg_accum  numeric_avg     -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2101 n 0 int4_avg_accum  int8_avg        -               -               -               0   1016    0   0   0   "{0,0}" _null_ ));
-DATA(insert ( 2102 n 0 int2_avg_accum  int8_avg        -               -               -               0   1016    0   0   0   "{0,0}" _null_ ));
-DATA(insert ( 2103 n 0 numeric_avg_accum   numeric_avg -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2104 n 0 float4_accum    float8_avg      -               -               -               0   1022    0   0   0   "{0,0,0}" _null_ ));
-DATA(insert ( 2105 n 0 float8_accum    float8_avg      -               -               -               0   1022    0   0   0   "{0,0,0}" _null_ ));
-DATA(insert ( 2106 n 0 interval_accum  interval_avg    -               -               -               0   1187    0   0   0   "{0 second,0 second}" _null_ ));
+DATA(insert ( 2100 n 0 int8_avg_accum  numeric_avg     int8_avg_accum  int8_accum_inv  numeric_avg     0   2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2101 n 0 int4_avg_accum  int8_avg        int4_avg_accum  int4_avg_accum_inv  int8_avg    0   1016    0   1016    0   "{0,0}" "{0,0}" ));
+DATA(insert ( 2102 n 0 int2_avg_accum  int8_avg        int2_avg_accum  int2_avg_accum_inv  int8_avg    0   1016    0   1016    0   "{0,0}" "{0,0}" ));
+DATA(insert ( 2103 n 0 numeric_avg_accum numeric_avg   numeric_avg_accum numeric_accum_inv numeric_avg 0   2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2104 n 0 float4_accum    float8_avg      -               -               -               0   1022    0   0       0   "{0,0,0}" _null_ ));
+DATA(insert ( 2105 n 0 float8_accum    float8_avg      -               -               -               0   1022    0   0       0   "{0,0,0}" _null_ ));
+DATA(insert ( 2106 n 0 interval_accum  interval_avg    interval_accum  interval_accum_inv interval_avg 0   1187    0   1187    0   "{0 second,0 second}" "{0 second,0 second}" ));
 
 /* sum */
-DATA(insert ( 2107 n 0 int8_avg_accum  numeric_sum     -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2108 n 0 int4_sum        -               -               -               -               0   20      0   0   0   _null_ _null_ ));
-DATA(insert ( 2109 n 0 int2_sum        -               -               -               -               0   20      0   0   0   _null_ _null_ ));
-DATA(insert ( 2110 n 0 float4pl        -               -               -               -               0   700     0   0   0   _null_ _null_ ));
-DATA(insert ( 2111 n 0 float8pl        -               -               -               -               0   701     0   0   0   _null_ _null_ ));
-DATA(insert ( 2112 n 0 cash_pl         -               -               -               -               0   790     0   0   0   _null_ _null_ ));
-DATA(insert ( 2113 n 0 interval_pl     -               -               -               -               0   1186    0   0   0   _null_ _null_ ));
-DATA(insert ( 2114 n 0 numeric_avg_accum   numeric_sum -               -               -               0   2281    128 0   0   _null_ _null_ ));
+DATA(insert ( 2107 n 0 int8_avg_accum  numeric_sum     int8_avg_accum  int8_accum_inv  numeric_sum     0   2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2108 n 0 int4_sum        -               int4_avg_accum  int4_avg_accum_inv int2int4_sum 0   20      0   1016    0   _null_ "{0,0}" ));
+DATA(insert ( 2109 n 0 int2_sum        -               int2_avg_accum  int2_avg_accum_inv int2int4_sum 0   20      0   1016    0   _null_ "{0,0}" ));
+DATA(insert ( 2110 n 0 float4pl        -               -               -               -               0   700     0   0       0   _null_ _null_ ));
+DATA(insert ( 2111 n 0 float8pl        -               -               -               -               0   701     0   0       0   _null_ _null_ ));
+DATA(insert ( 2112 n 0 cash_pl         -               cash_pl         cash_mi         -               0   790     0   790     0   _null_ _null_ ));
+DATA(insert ( 2113 n 0 interval_pl     -               interval_pl     interval_mi     -               0   1186    0   1186    0   _null_ _null_ ));
+DATA(insert ( 2114 n 0 numeric_avg_accum   numeric_sum numeric_avg_accum numeric_accum_inv numeric_sum 0   2281    128 2281    128 _null_ _null_ ));
 
 /* max */
-DATA(insert ( 2115 n 0 int8larger      -               -               -               -               413     20      0   0   0   _null_ _null_ ));
-DATA(insert ( 2116 n 0 int4larger      -               -               -               -               521     23      0   0   0   _null_ _null_ ));
-DATA(insert ( 2117 n 0 int2larger      -               -               -               -               520     21      0   0   0   _null_ _null_ ));
-DATA(insert ( 2118 n 0 oidlarger       -               -               -               -               610     26      0   0   0   _null_ _null_ ));
-DATA(insert ( 2119 n 0 float4larger    -               -               -               -               623     700     0   0   0   _null_ _null_ ));
-DATA(insert ( 2120 n 0 float8larger    -               -               -               -               674     701     0   0   0   _null_ _null_ ));
-DATA(insert ( 2121 n 0 int4larger      -               -               -               -               563     702     0   0   0   _null_ _null_ ));
-DATA(insert ( 2122 n 0 date_larger     -               -               -               -               1097    1082    0   0   0   _null_ _null_ ));
-DATA(insert ( 2123 n 0 time_larger     -               -               -               -               1112    1083    0   0   0   _null_ _null_ ));
-DATA(insert ( 2124 n 0 timetz_larger   -               -               -               -               1554    1266    0   0   0   _null_ _null_ ));
-DATA(insert ( 2125 n 0 cashlarger      -               -               -               -               903     790     0   0   0   _null_ _null_ ));
-DATA(insert ( 2126 n 0 timestamp_larger    -           -               -               -               2064    1114    0   0   0   _null_ _null_ ));
-DATA(insert ( 2127 n 0 timestamptz_larger  -           -               -               -               1324    1184    0   0   0   _null_ _null_ ));
-DATA(insert ( 2128 n 0 interval_larger -               -               -               -               1334    1186    0   0   0   _null_ _null_ ));
-DATA(insert ( 2129 n 0 text_larger     -               -               -               -               666     25      0   0   0   _null_ _null_ ));
-DATA(insert ( 2130 n 0 numeric_larger  -               -               -               -               1756    1700    0   0   0   _null_ _null_ ));
-DATA(insert ( 2050 n 0 array_larger    -               -               -               -               1073    2277    0   0   0   _null_ _null_ ));
-DATA(insert ( 2244 n 0 bpchar_larger   -               -               -               -               1060    1042    0   0   0   _null_ _null_ ));
-DATA(insert ( 2797 n 0 tidlarger       -               -               -               -               2800    27      0   0   0   _null_ _null_ ));
-DATA(insert ( 3526 n 0 enum_larger     -               -               -               -               3519    3500    0   0   0   _null_ _null_ ));
+DATA(insert ( 2115 n 0 int8larger      -               -               -               -               413     20      0   0       0   _null_ _null_ ));
+DATA(insert ( 2116 n 0 int4larger      -               -               -               -               521     23      0   0       0   _null_ _null_ ));
+DATA(insert ( 2117 n 0 int2larger      -               -               -               -               520     21      0   0       0   _null_ _null_ ));
+DATA(insert ( 2118 n 0 oidlarger       -               -               -               -               610     26      0   0       0   _null_ _null_ ));
+DATA(insert ( 2119 n 0 float4larger    -               -               -               -               623     700     0   0       0   _null_ _null_ ));
+DATA(insert ( 2120 n 0 float8larger    -               -               -               -               674     701     0   0       0   _null_ _null_ ));
+DATA(insert ( 2121 n 0 int4larger      -               -               -               -               563     702     0   0       0   _null_ _null_ ));
+DATA(insert ( 2122 n 0 date_larger     -               -               -               -               1097    1082    0   0       0   _null_ _null_ ));
+DATA(insert ( 2123 n 0 time_larger     -               -               -               -               1112    1083    0   0       0   _null_ _null_ ));
+DATA(insert ( 2124 n 0 timetz_larger   -               -               -               -               1554    1266    0   0       0   _null_ _null_ ));
+DATA(insert ( 2125 n 0 cashlarger      -               -               -               -               903     790     0   0       0   _null_ _null_ ));
+DATA(insert ( 2126 n 0 timestamp_larger    -           -               -               -               2064    1114    0   0       0   _null_ _null_ ));
+DATA(insert ( 2127 n 0 timestamptz_larger  -           -               -               -               1324    1184    0   0       0   _null_ _null_ ));
+DATA(insert ( 2128 n 0 interval_larger -               -               -               -               1334    1186    0   0       0   _null_ _null_ ));
+DATA(insert ( 2129 n 0 text_larger     -               -               -               -               666     25      0   0       0   _null_ _null_ ));
+DATA(insert ( 2130 n 0 numeric_larger  -               -               -               -               1756    1700    0   0       0   _null_ _null_ ));
+DATA(insert ( 2050 n 0 array_larger    -               -               -               -               1073    2277    0   0       0   _null_ _null_ ));
+DATA(insert ( 2244 n 0 bpchar_larger   -               -               -               -               1060    1042    0   0       0   _null_ _null_ ));
+DATA(insert ( 2797 n 0 tidlarger       -               -               -               -               2800    27      0   0       0   _null_ _null_ ));
+DATA(insert ( 3526 n 0 enum_larger     -               -               -               -               3519    3500    0   0       0   _null_ _null_ ));
 
 /* min */
-DATA(insert ( 2131 n 0 int8smaller     -               -               -               -               412     20      0   0   0   _null_ _null_ ));
-DATA(insert ( 2132 n 0 int4smaller     -               -               -               -               97      23      0   0   0   _null_ _null_ ));
-DATA(insert ( 2133 n 0 int2smaller     -               -               -               -               95      21      0   0   0   _null_ _null_ ));
-DATA(insert ( 2134 n 0 oidsmaller      -               -               -               -               609     26      0   0   0   _null_ _null_ ));
-DATA(insert ( 2135 n 0 float4smaller   -               -               -               -               622     700     0   0   0   _null_ _null_ ));
-DATA(insert ( 2136 n 0 float8smaller   -               -               -               -               672     701     0   0   0   _null_ _null_ ));
-DATA(insert ( 2137 n 0 int4smaller     -               -               -               -               562     702     0   0   0   _null_ _null_ ));
-DATA(insert ( 2138 n 0 date_smaller    -               -               -               -               1095    1082    0   0   0   _null_ _null_ ));
-DATA(insert ( 2139 n 0 time_smaller    -               -               -               -               1110    1083    0   0   0   _null_ _null_ ));
-DATA(insert ( 2140 n 0 timetz_smaller  -               -               -               -               1552    1266    0   0   0   _null_ _null_ ));
-DATA(insert ( 2141 n 0 cashsmaller     -               -               -               -               902     790     0   0   0   _null_ _null_ ));
-DATA(insert ( 2142 n 0 timestamp_smaller   -           -               -               -               2062    1114    0   0   0   _null_ _null_ ));
-DATA(insert ( 2143 n 0 timestamptz_smaller -           -               -               -               1322    1184    0   0   0   _null_ _null_ ));
-DATA(insert ( 2144 n 0 interval_smaller    -           -               -               -               1332    1186    0   0   0   _null_ _null_ ));
-DATA(insert ( 2145 n 0 text_smaller    -               -               -               -               664     25      0   0   0   _null_ _null_ ));
-DATA(insert ( 2146 n 0 numeric_smaller -               -               -               -               1754    1700    0   0   0   _null_ _null_ ));
-DATA(insert ( 2051 n 0 array_smaller   -               -               -               -               1072    2277    0   0   0   _null_ _null_ ));
-DATA(insert ( 2245 n 0 bpchar_smaller  -               -               -               -               1058    1042    0   0   0   _null_ _null_ ));
-DATA(insert ( 2798 n 0 tidsmaller      -               -               -               -               2799    27      0   0   0   _null_ _null_ ));
-DATA(insert ( 3527 n 0 enum_smaller    -               -               -               -               3518    3500    0   0   0   _null_ _null_ ));
+DATA(insert ( 2131 n 0 int8smaller     -               -               -               -               412     20      0   0       0   _null_ _null_ ));
+DATA(insert ( 2132 n 0 int4smaller     -               -               -               -               97      23      0   0       0   _null_ _null_ ));
+DATA(insert ( 2133 n 0 int2smaller     -               -               -               -               95      21      0   0       0   _null_ _null_ ));
+DATA(insert ( 2134 n 0 oidsmaller      -               -               -               -               609     26      0   0       0   _null_ _null_ ));
+DATA(insert ( 2135 n 0 float4smaller   -               -               -               -               622     700     0   0       0   _null_ _null_ ));
+DATA(insert ( 2136 n 0 float8smaller   -               -               -               -               672     701     0   0       0   _null_ _null_ ));
+DATA(insert ( 2137 n 0 int4smaller     -               -               -               -               562     702     0   0       0   _null_ _null_ ));
+DATA(insert ( 2138 n 0 date_smaller    -               -               -               -               1095    1082    0   0       0   _null_ _null_ ));
+DATA(insert ( 2139 n 0 time_smaller    -               -               -               -               1110    1083    0   0       0   _null_ _null_ ));
+DATA(insert ( 2140 n 0 timetz_smaller  -               -               -               -               1552    1266    0   0       0   _null_ _null_ ));
+DATA(insert ( 2141 n 0 cashsmaller     -               -               -               -               902     790     0   0       0   _null_ _null_ ));
+DATA(insert ( 2142 n 0 timestamp_smaller   -           -               -               -               2062    1114    0   0       0   _null_ _null_ ));
+DATA(insert ( 2143 n 0 timestamptz_smaller -           -               -               -               1322    1184    0   0       0   _null_ _null_ ));
+DATA(insert ( 2144 n 0 interval_smaller    -           -               -               -               1332    1186    0   0       0   _null_ _null_ ));
+DATA(insert ( 2145 n 0 text_smaller    -               -               -               -               664     25      0   0       0   _null_ _null_ ));
+DATA(insert ( 2146 n 0 numeric_smaller -               -               -               -               1754    1700    0   0       0   _null_ _null_ ));
+DATA(insert ( 2051 n 0 array_smaller   -               -               -               -               1072    2277    0   0       0   _null_ _null_ ));
+DATA(insert ( 2245 n 0 bpchar_smaller  -               -               -               -               1058    1042    0   0       0   _null_ _null_ ));
+DATA(insert ( 2798 n 0 tidsmaller      -               -               -               -               2799    27      0   0       0   _null_ _null_ ));
+DATA(insert ( 3527 n 0 enum_smaller    -               -               -               -               3518    3500    0   0       0   _null_ _null_ ));
 
 /* count */
-DATA(insert ( 2147 n 0 int8inc_any     -               -               -               -               0       20      0   0   0   "0" _null_ ));
-DATA(insert ( 2803 n 0 int8inc         -               -               -               -               0       20      0   0   0   "0" _null_ ));
+DATA(insert ( 2147 n 0 int8inc_any     -               int8inc_any     int8dec_any     -               0       20      0   20      0   "0" "0" ));
+DATA(insert ( 2803 n 0 int8inc         -               int8inc         int8dec         -               0       20      0   20      0   "0" "0" ));
 
 /* var_pop */
-DATA(insert ( 2718 n 0 int8_accum  numeric_var_pop     -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2719 n 0 int4_accum  numeric_var_pop     -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2720 n 0 int2_accum  numeric_var_pop     -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2721 n 0 float4_accum    float8_var_pop  -               -               -               0   1022    0   0   0   "{0,0,0}" _null_ ));
-DATA(insert ( 2722 n 0 float8_accum    float8_var_pop  -               -               -               0   1022    0   0   0   "{0,0,0}" _null_ ));
-DATA(insert ( 2723 n 0 numeric_accum   numeric_var_pop -               -               -               0   2281    128 0   0   _null_ _null_ ));
+DATA(insert ( 2718 n 0 int8_accum  numeric_var_pop     int8_accum      int8_accum_inv  numeric_var_pop 0   2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2719 n 0 int4_accum  numeric_var_pop     int4_accum      int4_accum_inv  numeric_var_pop 0   2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2720 n 0 int2_accum  numeric_var_pop     int2_accum      int2_accum_inv  numeric_var_pop 0   2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2721 n 0 float4_accum    float8_var_pop  -               -               -               0   1022    0   0       0   "{0,0,0}" _null_ ));
+DATA(insert ( 2722 n 0 float8_accum    float8_var_pop  -               -               -               0   1022    0   0       0   "{0,0,0}" _null_ ));
+DATA(insert ( 2723 n 0 numeric_accum   numeric_var_pop numeric_accum numeric_accum_inv numeric_var_pop 0   2281    128 2281    128 _null_ _null_ ));
 
 /* var_samp */
-DATA(insert ( 2641 n 0 int8_accum  numeric_var_samp    -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2642 n 0 int4_accum  numeric_var_samp    -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2643 n 0 int2_accum  numeric_var_samp    -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2644 n 0 float4_accum    float8_var_samp -               -               -               0   1022    0   0   0   "{0,0,0}" _null_ ));
-DATA(insert ( 2645 n 0 float8_accum    float8_var_samp -               -               -               0   1022    0   0   0   "{0,0,0}" _null_ ));
-DATA(insert ( 2646 n 0 numeric_accum   numeric_var_samp -              -               -               0   2281    128 0   0   _null_ _null_ ));
+DATA(insert ( 2641 n 0 int8_accum  numeric_var_samp    int8_accum      int8_accum_inv  numeric_var_samp 0  2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2642 n 0 int4_accum  numeric_var_samp    int4_accum      int4_accum_inv  numeric_var_samp 0  2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2643 n 0 int2_accum  numeric_var_samp    int2_accum      int2_accum_inv  numeric_var_samp 0  2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2644 n 0 float4_accum    float8_var_samp -               -               -               0   1022    0   0       0   "{0,0,0}" _null_ ));
+DATA(insert ( 2645 n 0 float8_accum    float8_var_samp -               -               -               0   1022    0   0       0   "{0,0,0}" _null_ ));
+DATA(insert ( 2646 n 0 numeric_accum   numeric_var_samp numeric_accum numeric_accum_inv numeric_var_samp 0 2281    128 2281    128 _null_ _null_ ));
 
 /* variance: historical Postgres syntax for var_samp */
-DATA(insert ( 2148 n 0 int8_accum  numeric_var_samp    -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2149 n 0 int4_accum  numeric_var_samp    -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2150 n 0 int2_accum  numeric_var_samp    -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2151 n 0 float4_accum    float8_var_samp -               -               -               0   1022    0   0   0   "{0,0,0}" _null_ ));
-DATA(insert ( 2152 n 0 float8_accum    float8_var_samp -               -               -               0   1022    0   0   0   "{0,0,0}" _null_ ));
-DATA(insert ( 2153 n 0 numeric_accum   numeric_var_samp -              -               -               0   2281    128 0   0   _null_ _null_ ));
+DATA(insert ( 2148 n 0 int8_accum  numeric_var_samp    int8_accum      int8_accum_inv  numeric_var_samp 0  2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2149 n 0 int4_accum  numeric_var_samp    int4_accum      int4_accum_inv  numeric_var_samp 0  2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2150 n 0 int2_accum  numeric_var_samp    int2_accum      int2_accum_inv  numeric_var_samp 0  2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2151 n 0 float4_accum    float8_var_samp -               -               -               0   1022    0   0       0   "{0,0,0}" _null_ ));
+DATA(insert ( 2152 n 0 float8_accum    float8_var_samp -               -               -               0   1022    0   0       0   "{0,0,0}" _null_ ));
+DATA(insert ( 2153 n 0 numeric_accum   numeric_var_samp numeric_accum numeric_accum_inv numeric_var_samp 0 2281    128 2281    128 _null_ _null_ ));
 
 /* stddev_pop */
-DATA(insert ( 2724 n 0 int8_accum  numeric_stddev_pop      -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2725 n 0 int4_accum  numeric_stddev_pop      -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2726 n 0 int2_accum  numeric_stddev_pop      -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2727 n 0 float4_accum    float8_stddev_pop   -               -               -               0   1022    0   0   0   "{0,0,0}" _null_ ));
-DATA(insert ( 2728 n 0 float8_accum    float8_stddev_pop   -               -               -               0   1022    0   0   0   "{0,0,0}" _null_ ));
-DATA(insert ( 2729 n 0 numeric_accum   numeric_stddev_pop  -               -               -               0   2281    128 0   0   _null_ _null_ ));
+DATA(insert ( 2724 n 0 int8_accum  numeric_stddev_pop      int8_accum  int8_accum_inv  numeric_stddev_pop  0   2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2725 n 0 int4_accum  numeric_stddev_pop      int4_accum  int4_accum_inv  numeric_stddev_pop  0   2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2726 n 0 int2_accum  numeric_stddev_pop      int2_accum  int2_accum_inv  numeric_stddev_pop  0   2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2727 n 0 float4_accum    float8_stddev_pop   -               -               -               0   1022    0   0       0   "{0,0,0}" _null_ ));
+DATA(insert ( 2728 n 0 float8_accum    float8_stddev_pop   -               -               -               0   1022    0   0       0   "{0,0,0}" _null_ ));
+DATA(insert ( 2729 n 0 numeric_accum   numeric_stddev_pop numeric_accum numeric_accum_inv numeric_stddev_pop 0 2281    128 2281    128 _null_ _null_ ));
 
 /* stddev_samp */
-DATA(insert ( 2712 n 0 int8_accum  numeric_stddev_samp     -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2713 n 0 int4_accum  numeric_stddev_samp     -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2714 n 0 int2_accum  numeric_stddev_samp     -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2715 n 0 float4_accum    float8_stddev_samp  -               -               -               0   1022    0   0   0   "{0,0,0}" _null_ ));
-DATA(insert ( 2716 n 0 float8_accum    float8_stddev_samp  -               -               -               0   1022    0   0   0   "{0,0,0}" _null_ ));
-DATA(insert ( 2717 n 0 numeric_accum   numeric_stddev_samp -               -               -               0   2281    128 0   0   _null_ _null_ ));
+DATA(insert ( 2712 n 0 int8_accum  numeric_stddev_samp     int8_accum  int8_accum_inv  numeric_stddev_samp 0   2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2713 n 0 int4_accum  numeric_stddev_samp     int4_accum  int4_accum_inv  numeric_stddev_samp 0   2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2714 n 0 int2_accum  numeric_stddev_samp     int2_accum  int2_accum_inv  numeric_stddev_samp 0   2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2715 n 0 float4_accum    float8_stddev_samp  -               -               -               0   1022    0   0       0   "{0,0,0}" _null_ ));
+DATA(insert ( 2716 n 0 float8_accum    float8_stddev_samp  -               -               -               0   1022    0   0       0   "{0,0,0}" _null_ ));
+DATA(insert ( 2717 n 0 numeric_accum   numeric_stddev_samp numeric_accum numeric_accum_inv numeric_stddev_samp 0 2281  128 2281    128 _null_ _null_ ));
 
 /* stddev: historical Postgres syntax for stddev_samp */
-DATA(insert ( 2154 n 0 int8_accum  numeric_stddev_samp     -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2155 n 0 int4_accum  numeric_stddev_samp     -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2156 n 0 int2_accum  numeric_stddev_samp     -               -               -               0   2281    128 0   0   _null_ _null_ ));
-DATA(insert ( 2157 n 0 float4_accum    float8_stddev_samp  -               -               -               0   1022    0   0   0   "{0,0,0}" _null_ ));
-DATA(insert ( 2158 n 0 float8_accum    float8_stddev_samp  -               -               -               0   1022    0   0   0   "{0,0,0}" _null_ ));
-DATA(insert ( 2159 n 0 numeric_accum   numeric_stddev_samp -               -               -               0   2281    128 0   0   _null_ _null_ ));
+DATA(insert ( 2154 n 0 int8_accum  numeric_stddev_samp     int8_accum  int8_accum_inv  numeric_stddev_samp 0   2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2155 n 0 int4_accum  numeric_stddev_samp     int4_accum  int4_accum_inv  numeric_stddev_samp 0   2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2156 n 0 int2_accum  numeric_stddev_samp     int2_accum  int2_accum_inv  numeric_stddev_samp 0   2281    128 2281    128 _null_ _null_ ));
+DATA(insert ( 2157 n 0 float4_accum    float8_stddev_samp  -               -               -               0   1022    0   0       0   "{0,0,0}" _null_ ));
+DATA(insert ( 2158 n 0 float8_accum    float8_stddev_samp  -               -               -               0   1022    0   0       0   "{0,0,0}" _null_ ));
+DATA(insert ( 2159 n 0 numeric_accum   numeric_stddev_samp numeric_accum numeric_accum_inv numeric_stddev_samp 0 2281  128 2281    128 _null_ _null_ ));
 
 /* SQL2003 binary regression aggregates */
-DATA(insert ( 2818 n 0 int8inc_float8_float8   -                   -               -               -               0   20      0   0   0   "0" _null_ ));
-DATA(insert ( 2819 n 0 float8_regr_accum   float8_regr_sxx         -               -               -               0   1022    0   0   0   "{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2820 n 0 float8_regr_accum   float8_regr_syy         -               -               -               0   1022    0   0   0   "{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2821 n 0 float8_regr_accum   float8_regr_sxy         -               -               -               0   1022    0   0   0   "{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2822 n 0 float8_regr_accum   float8_regr_avgx        -               -               -               0   1022    0   0   0   "{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2823 n 0 float8_regr_accum   float8_regr_avgy        -               -               -               0   1022    0   0   0   "{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2824 n 0 float8_regr_accum   float8_regr_r2          -               -               -               0   1022    0   0   0   "{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2825 n 0 float8_regr_accum   float8_regr_slope       -               -               -               0   1022    0   0   0   "{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2826 n 0 float8_regr_accum   float8_regr_intercept   -               -               -               0   1022    0   0   0   "{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2827 n 0 float8_regr_accum   float8_covar_pop        -               -               -               0   1022    0   0   0   "{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2828 n 0 float8_regr_accum   float8_covar_samp       -               -               -               0   1022    0   0   0   "{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2829 n 0 float8_regr_accum   float8_corr             -               -               -               0   1022    0   0   0   "{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2818 n 0 int8inc_float8_float8   -                   -               -               -               0   20      0   0       0   "0" _null_ ));
+DATA(insert ( 2819 n 0 float8_regr_accum   float8_regr_sxx         -               -               -               0   1022    0   0       0   "{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2820 n 0 float8_regr_accum   float8_regr_syy         -               -               -               0   1022    0   0       0   "{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2821 n 0 float8_regr_accum   float8_regr_sxy         -               -               -               0   1022    0   0       0   "{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2822 n 0 float8_regr_accum   float8_regr_avgx        -               -               -               0   1022    0   0       0   "{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2823 n 0 float8_regr_accum   float8_regr_avgy        -               -               -               0   1022    0   0       0   "{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2824 n 0 float8_regr_accum   float8_regr_r2          -               -               -               0   1022    0   0       0   "{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2825 n 0 float8_regr_accum   float8_regr_slope       -               -               -               0   1022    0   0       0   "{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2826 n 0 float8_regr_accum   float8_regr_intercept   -               -               -               0   1022    0   0       0   "{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2827 n 0 float8_regr_accum   float8_covar_pop        -               -               -               0   1022    0   0       0   "{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2828 n 0 float8_regr_accum   float8_covar_samp       -               -               -               0   1022    0   0       0   "{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2829 n 0 float8_regr_accum   float8_corr             -               -               -               0   1022    0   0       0   "{0,0,0,0,0,0}" _null_ ));
 
 /* boolean-and and boolean-or */
-DATA(insert ( 2517 n 0 booland_statefunc   -           -               -               -               58  16      0   0   0   _null_ _null_ ));
-DATA(insert ( 2518 n 0 boolor_statefunc    -           -               -               -               59  16      0   0   0   _null_ _null_ ));
-DATA(insert ( 2519 n 0 booland_statefunc   -           -               -               -               58  16      0   0   0   _null_ _null_ ));
+DATA(insert ( 2517 n 0 booland_statefunc   -           -               -               -               58  16      0   0       0   _null_ _null_ ));
+DATA(insert ( 2518 n 0 boolor_statefunc    -           -               -               -               59  16      0   0       0   _null_ _null_ ));
+DATA(insert ( 2519 n 0 booland_statefunc   -           -               -               -               58  16      0   0       0   _null_ _null_ ));
 
 /* bitwise integer */
-DATA(insert ( 2236 n 0 int2and     -                   -               -               -               0   21      0   0   0   _null_ _null_ ));
-DATA(insert ( 2237 n 0 int2or      -                   -               -               -               0   21      0   0   0   _null_ _null_ ));
-DATA(insert ( 2238 n 0 int4and     -                   -               -               -               0   23      0   0   0   _null_ _null_ ));
-DATA(insert ( 2239 n 0 int4or      -                   -               -               -               0   23      0   0   0   _null_ _null_ ));
-DATA(insert ( 2240 n 0 int8and     -                   -               -               -               0   20      0   0   0   _null_ _null_ ));
-DATA(insert ( 2241 n 0 int8or      -                   -               -               -               0   20      0   0   0   _null_ _null_ ));
-DATA(insert ( 2242 n 0 bitand      -                   -               -               -               0   1560    0   0   0   _null_ _null_ ));
-DATA(insert ( 2243 n 0 bitor       -                   -               -               -               0   1560    0   0   0   _null_ _null_ ));
+DATA(insert ( 2236 n 0 int2and     -                   -               -               -               0   21      0   0       0   _null_ _null_ ));
+DATA(insert ( 2237 n 0 int2or      -                   -               -               -               0   21      0   0       0   _null_ _null_ ));
+DATA(insert ( 2238 n 0 int4and     -                   -               -               -               0   23      0   0       0   _null_ _null_ ));
+DATA(insert ( 2239 n 0 int4or      -                   -               -               -               0   23      0   0       0   _null_ _null_ ));
+DATA(insert ( 2240 n 0 int8and     -                   -               -               -               0   20      0   0       0   _null_ _null_ ));
+DATA(insert ( 2241 n 0 int8or      -                   -               -               -               0   20      0   0       0   _null_ _null_ ));
+DATA(insert ( 2242 n 0 bitand      -                   -               -               -               0   1560    0   0       0   _null_ _null_ ));
+DATA(insert ( 2243 n 0 bitor       -                   -               -               -               0   1560    0   0       0   _null_ _null_ ));
 
 /* xml */
-DATA(insert ( 2901 n 0 xmlconcat2  -                   -               -               -               0   142     0   0   0   _null_ _null_ ));
+DATA(insert ( 2901 n 0 xmlconcat2  -                   -               -               -               0   142     0   0       0   _null_ _null_ ));
 
 /* array */
-DATA(insert ( 2335 n 0 array_agg_transfn   array_agg_finalfn   -               -               -               0   2281    0   0   0   _null_ _null_ ));
+DATA(insert ( 2335 n 0 array_agg_transfn   array_agg_finalfn   -               -               -               0   2281    0   0       0   _null_ _null_ ));
 
 /* text */
-DATA(insert ( 3538 n 0 string_agg_transfn  string_agg_finalfn  -               -               -               0   2281    0   0   0   _null_ _null_ ));
+DATA(insert ( 3538 n 0 string_agg_transfn  string_agg_finalfn  -               -               -               0   2281    0   0       0   _null_ _null_ ));
 
 /* bytea */
-DATA(insert ( 3545 n 0 bytea_string_agg_transfn    bytea_string_agg_finalfn    -               -               -               0   2281    0   0   0   _null_ _null_ ));
+DATA(insert ( 3545 n 0 bytea_string_agg_transfn    bytea_string_agg_finalfn    -               -               -       0   2281    0   0       0   _null_ _null_ ));
 
 /* json */
-DATA(insert ( 3175 n 0 json_agg_transfn    json_agg_finalfn            -               -               -               0   2281    0   0   0   _null_ _null_ ));
-DATA(insert ( 3197 n 0 json_object_agg_transfn json_object_agg_finalfn -               -               -               0   2281    0   0   0   _null_ _null_ ));
+DATA(insert ( 3175 n 0 json_agg_transfn    json_agg_finalfn            -               -               -               0   2281    0   0       0   _null_ _null_ ));
+DATA(insert ( 3197 n 0 json_object_agg_transfn json_object_agg_finalfn -               -               -               0   2281    0   0       0   _null_ _null_ ));
 
 /* ordered-set and hypothetical-set aggregates */
-DATA(insert ( 3972 o 1 ordered_set_transition          percentile_disc_final                   -               -               -               0   2281    0   0   0   _null_ _null_ ));
-DATA(insert ( 3974 o 1 ordered_set_transition          percentile_cont_float8_final            -               -               -               0   2281    0   0   0   _null_ _null_ ));
-DATA(insert ( 3976 o 1 ordered_set_transition          percentile_cont_interval_final          -               -               -               0   2281    0   0   0   _null_ _null_ ));
-DATA(insert ( 3978 o 1 ordered_set_transition          percentile_disc_multi_final             -               -               -               0   2281    0   0   0   _null_ _null_ ));
-DATA(insert ( 3980 o 1 ordered_set_transition          percentile_cont_float8_multi_final      -               -               -               0   2281    0   0   0   _null_ _null_ ));
-DATA(insert ( 3982 o 1 ordered_set_transition          percentile_cont_interval_multi_final    -               -               -               0   2281    0   0   0   _null_ _null_ ));
-DATA(insert ( 3984 o 0 ordered_set_transition          mode_final                              -               -               -               0   2281    0   0   0   _null_ _null_ ));
-DATA(insert ( 3986 h 1 ordered_set_transition_multi    rank_final                              -               -               -               0   2281    0   0   0   _null_ _null_ ));
-DATA(insert ( 3988 h 1 ordered_set_transition_multi    percent_rank_final                      -               -               -               0   2281    0   0   0   _null_ _null_ ));
-DATA(insert ( 3990 h 1 ordered_set_transition_multi    cume_dist_final                         -               -               -               0   2281    0   0   0   _null_ _null_ ));
-DATA(insert ( 3992 h 1 ordered_set_transition_multi    dense_rank_final                        -               -               -               0   2281    0   0   0   _null_ _null_ ));
+DATA(insert ( 3972 o 1 ordered_set_transition          percentile_disc_final                   -       -       -       0   2281    0   0       0   _null_ _null_ ));
+DATA(insert ( 3974 o 1 ordered_set_transition          percentile_cont_float8_final            -       -       -       0   2281    0   0       0   _null_ _null_ ));
+DATA(insert ( 3976 o 1 ordered_set_transition          percentile_cont_interval_final          -       -       -       0   2281    0   0       0   _null_ _null_ ));
+DATA(insert ( 3978 o 1 ordered_set_transition          percentile_disc_multi_final             -       -       -       0   2281    0   0       0   _null_ _null_ ));
+DATA(insert ( 3980 o 1 ordered_set_transition          percentile_cont_float8_multi_final      -       -       -       0   2281    0   0       0   _null_ _null_ ));
+DATA(insert ( 3982 o 1 ordered_set_transition          percentile_cont_interval_multi_final    -       -       -       0   2281    0   0       0   _null_ _null_ ));
+DATA(insert ( 3984 o 0 ordered_set_transition          mode_final                              -       -       -       0   2281    0   0       0   _null_ _null_ ));
+DATA(insert ( 3986 h 1 ordered_set_transition_multi    rank_final                              -       -       -       0   2281    0   0       0   _null_ _null_ ));
+DATA(insert ( 3988 h 1 ordered_set_transition_multi    percent_rank_final                      -       -       -       0   2281    0   0       0   _null_ _null_ ));
+DATA(insert ( 3990 h 1 ordered_set_transition_multi    cume_dist_final                         -       -       -       0   2281    0   0       0   _null_ _null_ ));
+DATA(insert ( 3992 h 1 ordered_set_transition_multi    dense_rank_final                        -       -       -       0   2281    0   0       0   _null_ _null_ ));
 
 
 /*
index 7b9c5870fac6c86b2cc42bbe46d938412fed709a..a24f4e02f96198dac7664a532d8e4c9086d0dedd 100644 (file)
@@ -1311,8 +1311,12 @@ DESCR("truncate interval to specified units");
 
 DATA(insert OID = 1219 (  int8inc         PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 20 "20" _null_ _null_ _null_ _null_ int8inc _null_ _null_ _null_ ));
 DESCR("increment");
+DATA(insert OID = 3546 (  int8dec         PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 20 "20" _null_ _null_ _null_ _null_ int8dec _null_ _null_ _null_ ));
+DESCR("decrement");
 DATA(insert OID = 2804 (  int8inc_any     PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 20 "20 2276" _null_ _null_ _null_ _null_ int8inc_any _null_ _null_ _null_ ));
 DESCR("increment, ignores second argument");
+DATA(insert OID = 3547 (  int8dec_any     PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 20 "20 2276" _null_ _null_ _null_ _null_ int8dec_any _null_ _null_ _null_ ));
+DESCR("decrement, ignores second argument");
 DATA(insert OID = 1230 (  int8abs         PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 20 "20" _null_ _null_ _null_ _null_ int8abs _null_ _null_ _null_ ));
 
 DATA(insert OID = 1236 (  int8larger      PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 20 "20 20" _null_ _null_ _null_ _null_ int8larger _null_ _null_ _null_ ));
@@ -2423,6 +2427,8 @@ DATA(insert OID = 1833 (  numeric_accum    PGNSP PGUID 12 1 0 0 0 f f f f f f i
 DESCR("aggregate transition function");
 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 = 3548 (  numeric_accum_inv    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_inv _null_ _null_ _null_ ));
+DESCR("aggregate transition function");
 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 f f i 2 0 2281 "2281 23" _null_ _null_ _null_ _null_ int4_accum _null_ _null_ _null_ ));
@@ -2431,6 +2437,12 @@ DATA(insert OID = 1836 (  int8_accum    PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0
 DESCR("aggregate transition function");
 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 = 3567 (  int2_accum_inv   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_inv _null_ _null_ _null_ ));
+DESCR("aggregate transition function");
+DATA(insert OID = 3568 (  int4_accum_inv   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_inv _null_ _null_ _null_ ));
+DESCR("aggregate transition function");
+DATA(insert OID = 3569 (  int8_accum_inv   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_inv _null_ _null_ _null_ ));
+DESCR("aggregate transition function");
 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 = 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_ ));
@@ -2451,14 +2463,22 @@ DATA(insert OID = 1842 (  int8_sum         PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0
 DESCR("aggregate transition function");
 DATA(insert OID = 1843 (  interval_accum   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1187 "1187 1186" _null_ _null_ _null_ _null_ interval_accum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
+DATA(insert OID = 3549 (  interval_accum_inv   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1187 "1187 1186" _null_ _null_ _null_ _null_ interval_accum_inv _null_ _null_ _null_ ));
+DESCR("aggregate transition function");
 DATA(insert OID = 1844 (  interval_avg    PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1186 "1187" _null_ _null_ _null_ _null_ interval_avg _null_ _null_ _null_ ));
 DESCR("aggregate final function");
 DATA(insert OID = 1962 (  int2_avg_accum   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1016 "1016 21" _null_ _null_ _null_ _null_ int2_avg_accum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
 DATA(insert OID = 1963 (  int4_avg_accum   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1016 "1016 23" _null_ _null_ _null_ _null_ int4_avg_accum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
+DATA(insert OID = 3570 (  int2_avg_accum_inv   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1016 "1016 21" _null_ _null_ _null_ _null_ int2_avg_accum_inv _null_ _null_ _null_ ));
+DESCR("aggregate transition function");
+DATA(insert OID = 3571 (  int4_avg_accum_inv   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1016 "1016 23" _null_ _null_ _null_ _null_ int4_avg_accum_inv _null_ _null_ _null_ ));
+DESCR("aggregate transition function");
 DATA(insert OID = 1964 (  int8_avg        PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1016" _null_ _null_ _null_ _null_ int8_avg _null_ _null_ _null_ ));
 DESCR("aggregate final function");
+DATA(insert OID = 3572 (  int2int4_sum    PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 20 "1016" _null_ _null_ _null_ _null_ int2int4_sum _null_ _null_ _null_ ));
+DESCR("aggregate final function");
 DATA(insert OID = 2805 (  int8inc_float8_float8        PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 20 "20 701 701" _null_ _null_ _null_ _null_ int8inc_float8_float8 _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
 DATA(insert OID = 2806 (  float8_regr_accum            PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 1022 "1022 701 701" _null_ _null_ _null_ _null_ float8_regr_accum _null_ _null_ _null_ ));
index 5907cb13fd31d0b5cd0ca5a61311c32c5864f7eb..f7cc1af2caf9c8de492ea01419d06a1eb84f51f0 100644 (file)
@@ -1005,9 +1005,13 @@ extern Datum float4_numeric(PG_FUNCTION_ARGS);
 extern Datum numeric_float4(PG_FUNCTION_ARGS);
 extern Datum numeric_accum(PG_FUNCTION_ARGS);
 extern Datum numeric_avg_accum(PG_FUNCTION_ARGS);
+extern Datum numeric_accum_inv(PG_FUNCTION_ARGS);
 extern Datum int2_accum(PG_FUNCTION_ARGS);
 extern Datum int4_accum(PG_FUNCTION_ARGS);
 extern Datum int8_accum(PG_FUNCTION_ARGS);
+extern Datum int2_accum_inv(PG_FUNCTION_ARGS);
+extern Datum int4_accum_inv(PG_FUNCTION_ARGS);
+extern Datum int8_accum_inv(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);
@@ -1020,7 +1024,10 @@ extern Datum int4_sum(PG_FUNCTION_ARGS);
 extern Datum int8_sum(PG_FUNCTION_ARGS);
 extern Datum int2_avg_accum(PG_FUNCTION_ARGS);
 extern Datum int4_avg_accum(PG_FUNCTION_ARGS);
+extern Datum int2_avg_accum_inv(PG_FUNCTION_ARGS);
+extern Datum int4_avg_accum_inv(PG_FUNCTION_ARGS);
 extern Datum int8_avg(PG_FUNCTION_ARGS);
+extern Datum int2int4_sum(PG_FUNCTION_ARGS);
 extern Datum width_bucket_numeric(PG_FUNCTION_ARGS);
 extern Datum hash_numeric(PG_FUNCTION_ARGS);
 
index d63e3a9a5a462629c635c56b28b2839da6f42ff8..0e4b949946762303956a18b0586cf04f583801e4 100644 (file)
@@ -74,8 +74,10 @@ extern Datum int8div(PG_FUNCTION_ARGS);
 extern Datum int8abs(PG_FUNCTION_ARGS);
 extern Datum int8mod(PG_FUNCTION_ARGS);
 extern Datum int8inc(PG_FUNCTION_ARGS);
+extern Datum int8dec(PG_FUNCTION_ARGS);
 extern Datum int8inc_any(PG_FUNCTION_ARGS);
 extern Datum int8inc_float8_float8(PG_FUNCTION_ARGS);
+extern Datum int8dec_any(PG_FUNCTION_ARGS);
 extern Datum int8larger(PG_FUNCTION_ARGS);
 extern Datum int8smaller(PG_FUNCTION_ARGS);
 
index 2731c6a25f4f58cf8b696ccebb395916c816d15b..94328b319368124a903fdce221c4978cf6d4b035 100644 (file)
@@ -184,6 +184,7 @@ extern Datum interval_mul(PG_FUNCTION_ARGS);
 extern Datum mul_d_interval(PG_FUNCTION_ARGS);
 extern Datum interval_div(PG_FUNCTION_ARGS);
 extern Datum interval_accum(PG_FUNCTION_ARGS);
+extern Datum interval_accum_inv(PG_FUNCTION_ARGS);
 extern Datum interval_avg(PG_FUNCTION_ARGS);
 
 extern Datum timestamp_mi(PG_FUNCTION_ARGS);
index 93ff18d589331a79b03ff864cb6862cec4499935..b2d671a08c605f35b0190b49e0399fdfa2fdf1be 100644 (file)
@@ -936,13 +936,13 @@ WHERE a.aggfnoid = p.oid AND
 ----------+---------+-----+---------
 (0 rows)
 
--- transfn and mtransfn should have same strictness setting.
-SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname, mptr.oid, mptr.proname
-FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr, pg_proc AS mptr
+-- mtransfn and minvtransfn should have same strictness setting.
+SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname, iptr.oid, iptr.proname
+FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr, pg_proc AS iptr
 WHERE a.aggfnoid = p.oid AND
-    a.aggtransfn = ptr.oid AND
-    a.aggmtransfn = mptr.oid AND
-    ptr.proisstrict != mptr.proisstrict;
+    a.aggmtransfn = ptr.oid AND
+    a.aggminvtransfn = iptr.oid AND
+    ptr.proisstrict != iptr.proisstrict;
  aggfnoid | proname | oid | proname | oid | proname 
 ----------+---------+-----+---------+-----+---------
 (0 rows)
index d9cb0addb32a11345b06e04d2056819bb50d8415..f08bd9c9adce24e620c4f98e3d4f14361e1510cc 100644 (file)
@@ -1294,3 +1294,478 @@ WINDOW fwd AS (
  t   | t   | t
 (1 row)
 
+--
+-- Test various built-in aggregates that have moving-aggregate support
+--
+-- test inverse transition functions handle NULLs properly
+SELECT i,AVG(v::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+ i |        avg         
+---+--------------------
+ 1 | 1.5000000000000000
+ 2 | 2.0000000000000000
+ 3 |                   
+ 4 |                   
+(4 rows)
+
+SELECT i,AVG(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+ i |        avg         
+---+--------------------
+ 1 | 1.5000000000000000
+ 2 | 2.0000000000000000
+ 3 |                   
+ 4 |                   
+(4 rows)
+
+SELECT i,AVG(v::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+ i |        avg         
+---+--------------------
+ 1 | 1.5000000000000000
+ 2 | 2.0000000000000000
+ 3 |                   
+ 4 |                   
+(4 rows)
+
+SELECT i,AVG(v::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1.5),(2,2.5),(3,NULL),(4,NULL)) t(i,v);
+ i |        avg         
+---+--------------------
+ 1 | 2.0000000000000000
+ 2 | 2.5000000000000000
+ 3 |                   
+ 4 |                   
+(4 rows)
+
+SELECT i,AVG(v::interval) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,'1 sec'),(2,'2 sec'),(3,NULL),(4,NULL)) t(i,v);
+ i |    avg     
+---+------------
+ 1 | @ 1.5 secs
+ 2 | @ 2 secs
+ 3 | 
+ 4 | 
+(4 rows)
+
+SELECT i,SUM(v::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+ i | sum 
+---+-----
+ 1 |   3
+ 2 |   2
+ 3 |    
+ 4 |    
+(4 rows)
+
+SELECT i,SUM(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+ i | sum 
+---+-----
+ 1 |   3
+ 2 |   2
+ 3 |    
+ 4 |    
+(4 rows)
+
+SELECT i,SUM(v::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+ i | sum 
+---+-----
+ 1 |   3
+ 2 |   2
+ 3 |    
+ 4 |    
+(4 rows)
+
+SELECT i,SUM(v::money) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,'1.10'),(2,'2.20'),(3,NULL),(4,NULL)) t(i,v);
+ i |  sum  
+---+-------
+ 1 | $3.30
+ 2 | $2.20
+ 3 |      
+ 4 |      
+(4 rows)
+
+SELECT i,SUM(v::interval) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,'1 sec'),(2,'2 sec'),(3,NULL),(4,NULL)) t(i,v);
+ i |   sum    
+---+----------
+ 1 | @ 3 secs
+ 2 | @ 2 secs
+ 3 | 
+ 4 | 
+(4 rows)
+
+SELECT i,SUM(v::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1.1),(2,2.2),(3,NULL),(4,NULL)) t(i,v);
+ i | sum 
+---+-----
+ 1 | 3.3
+ 2 | 2.2
+ 3 |    
+ 4 |    
+(4 rows)
+
+SELECT SUM(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1.01),(2,2),(3,3)) v(i,n);
+ sum  
+------
+ 6.01
+    5
+    3
+(3 rows)
+
+SELECT i,COUNT(v) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+ i | count 
+---+-------
+ 1 |     2
+ 2 |     1
+ 3 |     0
+ 4 |     0
+(4 rows)
+
+SELECT i,COUNT(*) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+ i | count 
+---+-------
+ 1 |     4
+ 2 |     3
+ 3 |     2
+ 4 |     1
+(4 rows)
+
+SELECT VAR_POP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+        var_pop        
+-----------------------
+    21704.000000000000
+    13868.750000000000
+    11266.666666666667
+ 4225.0000000000000000
+                     0
+(5 rows)
+
+SELECT VAR_POP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+        var_pop        
+-----------------------
+    21704.000000000000
+    13868.750000000000
+    11266.666666666667
+ 4225.0000000000000000
+                     0
+(5 rows)
+
+SELECT VAR_POP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+        var_pop        
+-----------------------
+    21704.000000000000
+    13868.750000000000
+    11266.666666666667
+ 4225.0000000000000000
+                     0
+(5 rows)
+
+SELECT VAR_POP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+        var_pop        
+-----------------------
+    21704.000000000000
+    13868.750000000000
+    11266.666666666667
+ 4225.0000000000000000
+                     0
+(5 rows)
+
+SELECT VAR_SAMP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+       var_samp        
+-----------------------
+    27130.000000000000
+    18491.666666666667
+    16900.000000000000
+ 8450.0000000000000000
+                      
+(5 rows)
+
+SELECT VAR_SAMP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+       var_samp        
+-----------------------
+    27130.000000000000
+    18491.666666666667
+    16900.000000000000
+ 8450.0000000000000000
+                      
+(5 rows)
+
+SELECT VAR_SAMP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+       var_samp        
+-----------------------
+    27130.000000000000
+    18491.666666666667
+    16900.000000000000
+ 8450.0000000000000000
+                      
+(5 rows)
+
+SELECT VAR_SAMP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+       var_samp        
+-----------------------
+    27130.000000000000
+    18491.666666666667
+    16900.000000000000
+ 8450.0000000000000000
+                      
+(5 rows)
+
+SELECT VARIANCE(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+       variance        
+-----------------------
+    27130.000000000000
+    18491.666666666667
+    16900.000000000000
+ 8450.0000000000000000
+                      
+(5 rows)
+
+SELECT VARIANCE(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+       variance        
+-----------------------
+    27130.000000000000
+    18491.666666666667
+    16900.000000000000
+ 8450.0000000000000000
+                      
+(5 rows)
+
+SELECT VARIANCE(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+       variance        
+-----------------------
+    27130.000000000000
+    18491.666666666667
+    16900.000000000000
+ 8450.0000000000000000
+                      
+(5 rows)
+
+SELECT VARIANCE(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+       variance        
+-----------------------
+    27130.000000000000
+    18491.666666666667
+    16900.000000000000
+ 8450.0000000000000000
+                      
+(5 rows)
+
+SELECT STDDEV_POP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+     stddev_pop      
+---------------------
+    147.322774885623
+    147.322774885623
+    117.765657133139
+    106.144555520604
+ 65.0000000000000000
+                   0
+(6 rows)
+
+SELECT STDDEV_POP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+     stddev_pop      
+---------------------
+    147.322774885623
+    147.322774885623
+    117.765657133139
+    106.144555520604
+ 65.0000000000000000
+                   0
+(6 rows)
+
+SELECT STDDEV_POP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+     stddev_pop      
+---------------------
+    147.322774885623
+    147.322774885623
+    117.765657133139
+    106.144555520604
+ 65.0000000000000000
+                   0
+(6 rows)
+
+SELECT STDDEV_POP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+     stddev_pop      
+---------------------
+    147.322774885623
+    147.322774885623
+    117.765657133139
+    106.144555520604
+ 65.0000000000000000
+                   0
+(6 rows)
+
+SELECT STDDEV_SAMP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+     stddev_samp     
+---------------------
+    164.711869639076
+    164.711869639076
+    135.984067694222
+    130.000000000000
+ 91.9238815542511782
+                    
+(6 rows)
+
+SELECT STDDEV_SAMP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+     stddev_samp     
+---------------------
+    164.711869639076
+    164.711869639076
+    135.984067694222
+    130.000000000000
+ 91.9238815542511782
+                    
+(6 rows)
+
+SELECT STDDEV_SAMP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+     stddev_samp     
+---------------------
+    164.711869639076
+    164.711869639076
+    135.984067694222
+    130.000000000000
+ 91.9238815542511782
+                    
+(6 rows)
+
+SELECT STDDEV_SAMP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+     stddev_samp     
+---------------------
+    164.711869639076
+    164.711869639076
+    135.984067694222
+    130.000000000000
+ 91.9238815542511782
+                    
+(6 rows)
+
+SELECT STDDEV(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(0,NULL),(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+       stddev        
+---------------------
+    164.711869639076
+    164.711869639076
+    135.984067694222
+    130.000000000000
+ 91.9238815542511782
+                    
+(6 rows)
+
+SELECT STDDEV(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(0,NULL),(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+       stddev        
+---------------------
+    164.711869639076
+    164.711869639076
+    135.984067694222
+    130.000000000000
+ 91.9238815542511782
+                    
+(6 rows)
+
+SELECT STDDEV(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(0,NULL),(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+       stddev        
+---------------------
+    164.711869639076
+    164.711869639076
+    135.984067694222
+    130.000000000000
+ 91.9238815542511782
+                    
+(6 rows)
+
+SELECT STDDEV(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(0,NULL),(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+       stddev        
+---------------------
+    164.711869639076
+    164.711869639076
+    135.984067694222
+    130.000000000000
+ 91.9238815542511782
+                    
+(6 rows)
+
+-- test that inverse transition functions work with various frame options
+SELECT i,SUM(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND CURRENT ROW)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+ i | sum 
+---+-----
+ 1 |   1
+ 2 |   2
+ 3 |    
+ 4 |    
+(4 rows)
+
+SELECT i,SUM(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+ i | sum 
+---+-----
+ 1 |   3
+ 2 |   2
+ 3 |    
+ 4 |    
+(4 rows)
+
+SELECT i,SUM(v::int) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,3),(4,4)) t(i,v);
+ i | sum 
+---+-----
+ 1 |   3
+ 2 |   6
+ 3 |   9
+ 4 |   7
+(4 rows)
+
+-- ensure aggregate over numeric properly recovers from NaN values
+SELECT a, b,
+       SUM(b) OVER(ORDER BY A ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)
+FROM (VALUES(1,1::numeric),(2,2),(3,'NaN'),(4,3),(5,4)) t(a,b);
+ a |  b  | sum 
+---+-----+-----
+ 1 |   1 |   1
+ 2 |   2 |   3
+ 3 | NaN | NaN
+ 4 |   3 | NaN
+ 5 |   4 |   7
+(5 rows)
+
+-- It might be tempting for someone to add an inverse trans function for
+-- float and double precision. This should not be done as it can give incorrect
+-- results. This test should fail if anyone ever does this without thinking too
+-- hard about it.
+SELECT to_char(SUM(n::float8) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING),'999999999999999999999D9')
+  FROM (VALUES(1,1e20),(2,1)) n(i,n);
+         to_char          
+--------------------------
+  100000000000000000000
+                      1.0
+(2 rows)
+
index 22998a553caa294971044c82fbd7448eb9ba3d34..dc1acb9eaace45e7522cc2069d0866049243bcc4 100644 (file)
@@ -760,14 +760,14 @@ WHERE a.aggfnoid = p.oid AND
     a.aggminitval IS NULL AND
     NOT binary_coercible(p.proargtypes[0], a.aggmtranstype);
 
--- transfn and mtransfn should have same strictness setting.
+-- mtransfn and minvtransfn should have same strictness setting.
 
-SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname, mptr.oid, mptr.proname
-FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr, pg_proc AS mptr
+SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname, iptr.oid, iptr.proname
+FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr, pg_proc AS iptr
 WHERE a.aggfnoid = p.oid AND
-    a.aggtransfn = ptr.oid AND
-    a.aggmtransfn = mptr.oid AND
-    ptr.proisstrict != mptr.proisstrict;
+    a.aggmtransfn = ptr.oid AND
+    a.aggminvtransfn = iptr.oid AND
+    ptr.proisstrict != iptr.proisstrict;
 
 -- Cross-check aggsortop (if present) against pg_operator.
 -- We expect to find entries for bool_and, bool_or, every, max, and min.
index 5bae12bd33d0aa613de9c7036f0e313ec1042b05..11c96aa8bc057e8ab21cb0e2ace1208ea7398e2f 100644 (file)
@@ -476,3 +476,144 @@ JOIN sum_following ON sum_following.i = vs.i
 WINDOW fwd AS (
    ORDER BY vs.i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
 );
+
+--
+-- Test various built-in aggregates that have moving-aggregate support
+--
+
+-- test inverse transition functions handle NULLs properly
+SELECT i,AVG(v::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+
+SELECT i,AVG(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+
+SELECT i,AVG(v::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+
+SELECT i,AVG(v::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1.5),(2,2.5),(3,NULL),(4,NULL)) t(i,v);
+
+SELECT i,AVG(v::interval) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,'1 sec'),(2,'2 sec'),(3,NULL),(4,NULL)) t(i,v);
+
+SELECT i,SUM(v::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+
+SELECT i,SUM(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+
+SELECT i,SUM(v::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+
+SELECT i,SUM(v::money) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,'1.10'),(2,'2.20'),(3,NULL),(4,NULL)) t(i,v);
+
+SELECT i,SUM(v::interval) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,'1 sec'),(2,'2 sec'),(3,NULL),(4,NULL)) t(i,v);
+
+SELECT i,SUM(v::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1.1),(2,2.2),(3,NULL),(4,NULL)) t(i,v);
+
+SELECT SUM(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1.01),(2,2),(3,3)) v(i,n);
+
+SELECT i,COUNT(v) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+
+SELECT i,COUNT(*) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+
+SELECT VAR_POP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+SELECT VAR_POP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+SELECT VAR_POP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+SELECT VAR_POP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+SELECT VAR_SAMP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+SELECT VAR_SAMP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+SELECT VAR_SAMP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+SELECT VAR_SAMP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+SELECT VARIANCE(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+SELECT VARIANCE(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+SELECT VARIANCE(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+SELECT VARIANCE(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+SELECT STDDEV_POP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+
+SELECT STDDEV_POP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+
+SELECT STDDEV_POP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+
+SELECT STDDEV_POP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+
+SELECT STDDEV_SAMP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+
+SELECT STDDEV_SAMP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+
+SELECT STDDEV_SAMP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+
+SELECT STDDEV_SAMP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n);
+
+SELECT STDDEV(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(0,NULL),(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+SELECT STDDEV(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(0,NULL),(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+SELECT STDDEV(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(0,NULL),(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+SELECT STDDEV(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM (VALUES(0,NULL),(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n);
+
+-- test that inverse transition functions work with various frame options
+SELECT i,SUM(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND CURRENT ROW)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+
+SELECT i,SUM(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v);
+
+SELECT i,SUM(v::int) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
+  FROM (VALUES(1,1),(2,2),(3,3),(4,4)) t(i,v);
+
+-- ensure aggregate over numeric properly recovers from NaN values
+SELECT a, b,
+       SUM(b) OVER(ORDER BY A ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)
+FROM (VALUES(1,1::numeric),(2,2),(3,'NaN'),(4,3),(5,4)) t(a,b);
+
+-- It might be tempting for someone to add an inverse trans function for
+-- float and double precision. This should not be done as it can give incorrect
+-- results. This test should fail if anyone ever does this without thinking too
+-- hard about it.
+SELECT to_char(SUM(n::float8) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING),'999999999999999999999D9')
+  FROM (VALUES(1,1e20),(2,1)) n(i,n);