diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c index 5e1be90..e62f2a3 100644 *** a/src/backend/utils/adt/int8.c --- b/src/backend/utils/adt/int8.c *************** int8inc(PG_FUNCTION_ARGS) *** 717,722 **** --- 717,767 ---- } } + 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 inverse transition + * 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; + /* Underflow 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; + /* Underflow 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, *************** int8inc_any(PG_FUNCTION_ARGS) *** 733,738 **** --- 778,789 ---- } Datum + int8inc_any_inv(PG_FUNCTION_ARGS) + { + return int8dec(fcinfo); + } + + Datum int8inc_float8_float8(PG_FUNCTION_ARGS) { return int8inc(fcinfo); diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 64eb0f8..2a9e093 100644 *** a/src/backend/utils/adt/numeric.c --- b/src/backend/utils/adt/numeric.c *************** numeric_float4(PG_FUNCTION_ARGS) *** 2517,2524 **** 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 */ --- 2517,2528 ---- typedef struct NumericAggState { bool calcSumX2; /* if true, calculate sumX2 */ ! int maxScale; /* stores the maximum scale seen so far. */ ! int64 maxScaleCount; /* tracks the number of values we've ! * seen with the maximum scale */ ! MemoryContext agg_context; /* context we're calculating in */ + int64 NaNcount; /* Count of NaN values that are aggregated */ int64 N; /* count of processed numbers */ NumericVar sumX; /* sum of processed numbers */ NumericVar sumX2; /* sum of squares of processed numbers */ *************** makeNumericAggState(FunctionCallInfo fci *** 2543,2548 **** --- 2547,2556 ---- state = (NumericAggState *) palloc0(sizeof(NumericAggState)); state->calcSumX2 = calcSumX2; state->agg_context = agg_context; + state->NaNcount = 0; + + state->maxScale = 0; + state->maxScaleCount = 0; MemoryContextSwitchTo(old_context); *************** do_numeric_accum(NumericAggState *state, *** 2560,2574 **** MemoryContext old_context; /* result is NaN if any processed number is NaN */ ! if (state->isNaN || NUMERIC_IS_NAN(newval)) { ! state->isNaN = true; return; } /* load processed number in short-lived context */ init_var_from_num(newval, &X); /* if we need X^2, calculate that in short-lived context */ if (state->calcSumX2) { --- 2568,2596 ---- MemoryContext old_context; /* result is NaN if any processed number is NaN */ ! if (NUMERIC_IS_NAN(newval)) { ! state->NaNcount++; return; } /* load processed number in short-lived context */ init_var_from_num(newval, &X); + /* + * Track the highest scale that we've seen as if we ever perform an inverse + * transition and remove the last numeric with the highest scale that we've + * seen then we can no longer perform inverse transitions without risking + * having the wrong dscale in the result value. + */ + if (state->maxScale == X.dscale) + state->maxScaleCount++; + else if (X.dscale > state->maxScale) + { + state->maxScale = X.dscale; + state->maxScaleCount = 1; + } + /* if we need X^2, calculate that in short-lived context */ if (state->calcSumX2) { *************** do_numeric_accum(NumericAggState *state, *** 2579,2591 **** /* The rest of this needs to work in the aggregate context */ old_context = MemoryContextSwitchTo(state->agg_context); ! if (state->N++ > 0) { /* Accumulate sums */ add_var(&X, &(state->sumX), &(state->sumX)); if (state->calcSumX2) add_var(&X2, &(state->sumX2), &(state->sumX2)); } else { --- 2601,2615 ---- /* The rest of this needs to work in the aggregate context */ old_context = MemoryContextSwitchTo(state->agg_context); ! if (state->N > 0) { /* Accumulate sums */ add_var(&X, &(state->sumX), &(state->sumX)); if (state->calcSumX2) add_var(&X2, &(state->sumX2), &(state->sumX2)); + + state->N++; } else { *************** do_numeric_accum(NumericAggState *state, *** 2594,2605 **** --- 2618,2730 ---- if (state->calcSumX2) set_var_from_var(&X2, &(state->sumX2)); + + state->N = 1; } MemoryContextSwitchTo(old_context); } /* + * do_numeric_discard + * Attempts to remove a 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 reject this case as we + * won't be able to tell what the new aggregated value's dscale should be. + * We can't return 2.00 (dscale = 2) as we really should return just 2, but + * since we're not tracking any previous highest scales then we must just fail + * to perform the inverse transition and just return false. + * + * Values that are no longer aggregated should not be able to effect the dscale + * of the result of the values that *are* still aggregated. + * + * Note it may be better to track the number of times we've aggregated a + * numeric with each scale, then if we ever remove final highest scaled value + * then we can step the result's dscale down to the next highest value. This is + * perhaps slightly more work than we can afford to do here, but doing it this + * way would mean that we could always perform the inverse transition. + */ + static bool + do_numeric_discard(NumericAggState *state, Numeric newval) + { + NumericVar X; + NumericVar X2; + MemoryContext old_context; + + /* result is NaN if any processed number is NaN */ + 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 matches 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 (state->maxScale == X.dscale) + { + if (state->maxScaleCount > 1) + { + /* Some remaining inputs have same dscale */ + --state->maxScaleCount; + } + else if (state->N == 1) + { + /* No remaining non-NaN inputs at all */ + state->maxScale = 0; + state->maxScaleCount = 0; + } + else + { + /* No remaining inputs have same dscale */ + 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) + { + sub_var(&(state->sumX), &X, &(state->sumX)); + if (state->calcSumX2) + sub_var(&(state->sumX2), &X2, &(state->sumX2)); + state->N--; + } + else if (state->N == 1) + { + /* Sums will be reset by next call to do_numeric_accum */ + state->N = 0; + } + else + { + MemoryContextSwitchTo(old_context); + elog(ERROR, "cannot discard more values than were accumulated"); + } + + MemoryContextSwitchTo(old_context); + return true; + } + + + /* * Generic transition function for numeric aggregates that require sumX2. */ Datum *************** numeric_accum(PG_FUNCTION_ARGS) *** 2609,2626 **** state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); ! if (!PG_ARGISNULL(1)) ! { ! /* Create the state data when we see the first non-null input. */ ! if (state == NULL) ! state = makeNumericAggState(fcinfo, true); do_numeric_accum(state, PG_GETARG_NUMERIC(1)); } PG_RETURN_POINTER(state); } /* * Generic transition function for numeric aggregates that don't require sumX2. */ --- 2734,2772 ---- 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)) do_numeric_accum(state, PG_GETARG_NUMERIC(1)); + + PG_RETURN_POINTER(state); + } + + /* + * Generic inverse transition function for numeric aggregates + */ + Datum + numeric_accum_inv(PG_FUNCTION_ARGS) + { + NumericAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); + if (state == NULL) + elog(ERROR, "numeric_accum_inv called with NULL state"); + + if (!PG_ARGISNULL(1)) + { + /* Can we perform an inverse transition? if not return NULL. */ + if (!do_numeric_discard(state, PG_GETARG_NUMERIC(1))) + PG_RETURN_NULL(); } PG_RETURN_POINTER(state); } + /* * Generic transition function for numeric aggregates that don't require sumX2. */ *************** numeric_avg_accum(PG_FUNCTION_ARGS) *** 2631,2644 **** state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); ! if (!PG_ARGISNULL(1)) ! { ! /* Create the state data when we see the first non-null input. */ ! if (state == NULL) ! state = makeNumericAggState(fcinfo, false); do_numeric_accum(state, PG_GETARG_NUMERIC(1)); - } PG_RETURN_POINTER(state); } --- 2777,2788 ---- 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)) do_numeric_accum(state, PG_GETARG_NUMERIC(1)); PG_RETURN_POINTER(state); } *************** int2_accum(PG_FUNCTION_ARGS) *** 2659,2675 **** state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); if (!PG_ARGISNULL(1)) { Numeric newval; newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, PG_GETARG_DATUM(1))); - - /* Create the state data when we see the first non-null input. */ - if (state == NULL) - state = makeNumericAggState(fcinfo, true); - do_numeric_accum(state, newval); } --- 2803,2818 ---- 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))); do_numeric_accum(state, newval); } *************** int4_accum(PG_FUNCTION_ARGS) *** 2683,2699 **** state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); 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); } --- 2826,2841 ---- 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))); do_numeric_accum(state, newval); } *************** int8_accum(PG_FUNCTION_ARGS) *** 2707,2724 **** state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); if (!PG_ARGISNULL(1)) { Numeric newval; newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, PG_GETARG_DATUM(1))); ! /* Create the state data when we see the first non-null input. */ ! if (state == NULL) ! state = makeNumericAggState(fcinfo, true); ! do_numeric_accum(state, newval); } PG_RETURN_POINTER(state); --- 2849,2951 ---- 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))); + do_numeric_accum(state, newval); + } ! PG_RETURN_POINTER(state); ! } ! ! /* ! * int2_accum_inv ! * aggregate inverse transition function. ! */ ! Datum ! int2_accum_inv(PG_FUNCTION_ARGS) ! { ! NumericAggState *state; ! ! state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); ! if (state == NULL) ! elog(ERROR, "numeric_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); ! } ! ! /* ! * int4_accum_inv ! * aggregate inverse transition function. ! */ ! Datum ! int4_accum_inv(PG_FUNCTION_ARGS) ! { ! NumericAggState *state; ! ! state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); ! if (state == NULL) ! elog(ERROR, "numeric_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); ! } ! ! /* ! * int8_accum_inv ! * aggregate inverse transition function. ! * This function must be declared as strict. ! */ ! Datum ! int8_accum_inv(PG_FUNCTION_ARGS) ! { ! NumericAggState *state; ! ! state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); ! if (state == NULL) ! elog(ERROR, "numeric_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); *************** int8_accum(PG_FUNCTION_ARGS) *** 2726,2731 **** --- 2953,2959 ---- /* * Transition function for int8 input when we don't need sumX2. + * For the inverse, we use int8_accum_inv. */ Datum int8_avg_accum(PG_FUNCTION_ARGS) *************** int8_avg_accum(PG_FUNCTION_ARGS) *** 2734,2757 **** state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); if (!PG_ARGISNULL(1)) { Numeric newval; newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, PG_GETARG_DATUM(1))); - - /* Create the state data when we see the first non-null input. */ - if (state == NULL) - state = makeNumericAggState(fcinfo, false); - do_numeric_accum(state, newval); } PG_RETURN_POINTER(state); } - Datum numeric_avg(PG_FUNCTION_ARGS) { --- 2962,2983 ---- 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); } PG_RETURN_POINTER(state); } Datum numeric_avg(PG_FUNCTION_ARGS) { *************** numeric_avg(PG_FUNCTION_ARGS) *** 2760,2769 **** Datum sumX_datum; state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); ! if (state == NULL) /* there were no non-null inputs */ PG_RETURN_NULL(); ! if (state->isNaN) /* there was at least one NaN input */ PG_RETURN_NUMERIC(make_result(&const_nan)); N_datum = DirectFunctionCall1(int8_numeric, Int64GetDatum(state->N)); --- 2986,2996 ---- Datum sumX_datum; state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); ! if (state == NULL || (state->N + state->NaNcount) == 0) ! /* there were no non-null inputs */ PG_RETURN_NULL(); ! 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)); *************** numeric_sum(PG_FUNCTION_ARGS) *** 2778,2787 **** NumericAggState *state; state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); ! if (state == NULL) /* there were no non-null inputs */ PG_RETURN_NULL(); ! if (state->isNaN) /* there was at least one NaN input */ PG_RETURN_NUMERIC(make_result(&const_nan)); PG_RETURN_NUMERIC(make_result(&(state->sumX))); --- 3005,3016 ---- NumericAggState *state; state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); ! if (state == NULL || (state->N + state->NaNcount) == 0) ! /* there were no non-null inputs */ PG_RETURN_NULL(); ! 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))); *************** numeric_stddev_internal(NumericAggState *** 2812,2818 **** int rscale; /* Deal with empty input and NaN-input cases */ ! if (state == NULL) { *is_null = true; return NULL; --- 3041,3047 ---- int rscale; /* Deal with empty input and NaN-input cases */ ! if (state == NULL || (state->N + state->NaNcount) == 0) { *is_null = true; return NULL; *************** numeric_stddev_internal(NumericAggState *** 2820,2826 **** *is_null = false; ! if (state->isNaN) return make_result(&const_nan); init_var(&vN); --- 3049,3055 ---- *is_null = false; ! if (state->NaNcount > 0) return make_result(&const_nan); init_var(&vN); *************** numeric_stddev_pop(PG_FUNCTION_ARGS) *** 2951,2970 **** } /* ! * SUM transition functions for integer datatypes. ! * ! * To avoid overflow, we use accumulators wider than the input datatype. ! * A Numeric accumulator is needed for int8 input; for int4 and int2 ! * inputs, we use int8 accumulators which should be sufficient for practical ! * purposes. (The latter two therefore don't really belong in this file, ! * but we keep them here anyway.) * ! * Because SQL defines the SUM() of no values to be NULL, not zero, ! * the initial condition of the transition data value needs to be NULL. This ! * means we can't rely on ExecAgg to automatically insert the first non-null ! * 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. */ Datum --- 3180,3193 ---- } /* ! * Obsolete SUM transition functions for integer datatypes. * ! * These were used to implement SUM aggregates before inverse transition ! * functions were added. For inverse transitions, we need to know the number ! * of summands to be able to return NULL whenenver the number of non-NULL ! * inputs becomes zero. We therefore now use the intX_avg_accum and ! * intX_avg_accum_inv transition functions, which use int8[] is their ! * transition type to be able to count the number of inputs. */ Datum *************** int8_sum(PG_FUNCTION_ARGS) *** 3103,3112 **** NumericGetDatum(oldsum), newval)); } - /* ! * Routines for avg(int2) and avg(int4). The transition datatype ! * is a two-element int8 array, holding count and sum. */ typedef struct Int8TransTypeData --- 3326,3340 ---- NumericGetDatum(oldsum), newval)); } /* ! * Routines for sum(int2), sum(int4), avg(int2) and avg(int4). The transition ! * datatype is a two-element int8 array, holding count and sum. ! * ! * To avoid overflow, we use accumulators wider than the input datatype. ! * A Numeric accumulator is needed for int8 input; for int4 and int2 ! * inputs, we use int8 accumulators which should be sufficient for practical ! * purposes. (The latter two therefore don't really belong in this file, ! * but we keep them here anyway.) */ typedef struct Int8TransTypeData *************** int2_avg_accum(PG_FUNCTION_ARGS) *** 3144,3149 **** --- 3372,3406 ---- } 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(PG_FUNCTION_ARGS) { ArrayType *transarray; *************** int4_avg_accum(PG_FUNCTION_ARGS) *** 3172,3177 **** --- 3429,3480 ---- } 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 + 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)); + } + + Datum int8_avg(PG_FUNCTION_ARGS) { ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index ce30bb6..af0822a 100644 *** a/src/backend/utils/adt/timestamp.c --- b/src/backend/utils/adt/timestamp.c *************** interval_accum(PG_FUNCTION_ARGS) *** 3429,3434 **** --- 3429,3479 ---- } 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"); + + /* + * 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)); + + 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) { ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h index 3412661..874aeec 100644 *** a/src/include/catalog/pg_aggregate.h --- b/src/include/catalog/pg_aggregate.h *************** typedef FormData_pg_aggregate *Form_pg_a *** 103,125 **** */ /* avg */ ! DATA(insert ( 2100 n 0 int8_avg_accum - numeric_avg 0 2281 128 _null_ )); ! DATA(insert ( 2101 n 0 int4_avg_accum - int8_avg 0 1016 0 "{0,0}" )); ! DATA(insert ( 2102 n 0 int2_avg_accum - int8_avg 0 1016 0 "{0,0}" )); ! DATA(insert ( 2103 n 0 numeric_avg_accum - numeric_avg 0 2281 128 _null_ )); ! DATA(insert ( 2104 n 0 float4_accum - float8_avg 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2105 n 0 float8_accum - float8_avg 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2106 n 0 interval_accum - interval_avg 0 1187 0 "{0 second,0 second}" )); /* sum */ ! DATA(insert ( 2107 n 0 int8_avg_accum - numeric_sum 0 2281 128 _null_ )); ! DATA(insert ( 2108 n 0 int4_sum - - 0 20 0 _null_ )); ! DATA(insert ( 2109 n 0 int2_sum - - 0 20 0 _null_ )); ! DATA(insert ( 2110 n 0 float4pl - - 0 700 0 _null_ )); ! DATA(insert ( 2111 n 0 float8pl - - 0 701 0 _null_ )); ! DATA(insert ( 2112 n 0 cash_pl - - 0 790 0 _null_ )); ! DATA(insert ( 2113 n 0 interval_pl - - 0 1186 0 _null_ )); ! DATA(insert ( 2114 n 0 numeric_avg_accum - numeric_sum 0 2281 128 _null_ )); /* max */ DATA(insert ( 2115 n 0 int8larger - - 413 20 0 _null_ )); --- 103,125 ---- */ /* avg */ ! DATA(insert ( 2100 n 0 int8_avg_accum int8_accum_inv numeric_avg 0 2281 128 _null_ )); ! DATA(insert ( 2101 n 0 int4_avg_accum int4_avg_accum_inv int8_avg 0 1016 0 "{0,0}" )); ! DATA(insert ( 2102 n 0 int2_avg_accum int2_avg_accum_inv int8_avg 0 1016 0 "{0,0}" )); ! DATA(insert ( 2103 n 0 numeric_avg_accum numeric_accum_inv numeric_avg 0 2281 128 _null_ )); ! DATA(insert ( 2104 n 0 float4_accum - float8_avg 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2105 n 0 float8_accum - float8_avg 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2106 n 0 interval_accum interval_accum_inv interval_avg 0 1187 0 "{0 second,0 second}" )); /* sum */ ! DATA(insert ( 2107 n 0 int8_avg_accum int8_accum_inv numeric_sum 0 2281 128 _null_ )); ! DATA(insert ( 2108 n 0 int4_avg_accum int4_avg_accum_inv int2int4_sum 0 1016 0 "{0,0}" )); ! DATA(insert ( 2109 n 0 int2_avg_accum int2_avg_accum_inv int2int4_sum 0 1016 0 "{0,0}" )); ! DATA(insert ( 2110 n 0 float4pl - - 0 700 0 _null_ )); ! DATA(insert ( 2111 n 0 float8pl - - 0 701 0 _null_ )); ! DATA(insert ( 2112 n 0 cash_pl cash_mi - 0 790 0 _null_ )); ! DATA(insert ( 2113 n 0 interval_pl interval_mi - 0 1186 0 _null_ )); ! DATA(insert ( 2114 n 0 numeric_avg_accum numeric_accum_inv numeric_sum 0 2281 128 _null_ )); /* max */ DATA(insert ( 2115 n 0 int8larger - - 413 20 0 _null_ )); *************** DATA(insert ( 2798 n 0 tidsmaller - - *** 166,221 **** DATA(insert ( 3527 n 0 enum_smaller - - 3518 3500 0 _null_ )); /* count */ ! DATA(insert ( 2147 n 0 int8inc_any - - 0 20 0 "0" )); ! DATA(insert ( 2803 n 0 int8inc - - 0 20 0 "0" )); /* var_pop */ ! DATA(insert ( 2718 n 0 int8_accum - numeric_var_pop 0 2281 128 _null_ )); ! DATA(insert ( 2719 n 0 int4_accum - numeric_var_pop 0 2281 128 _null_ )); ! DATA(insert ( 2720 n 0 int2_accum - numeric_var_pop 0 2281 128 _null_ )); ! DATA(insert ( 2721 n 0 float4_accum - float8_var_pop 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2722 n 0 float8_accum - float8_var_pop 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2723 n 0 numeric_accum - numeric_var_pop 0 2281 128 _null_ )); /* var_samp */ ! DATA(insert ( 2641 n 0 int8_accum - numeric_var_samp 0 2281 128 _null_ )); ! DATA(insert ( 2642 n 0 int4_accum - numeric_var_samp 0 2281 128 _null_ )); ! DATA(insert ( 2643 n 0 int2_accum - numeric_var_samp 0 2281 128 _null_ )); ! DATA(insert ( 2644 n 0 float4_accum - float8_var_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2645 n 0 float8_accum - float8_var_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2646 n 0 numeric_accum - numeric_var_samp 0 2281 128 _null_ )); /* variance: historical Postgres syntax for var_samp */ ! DATA(insert ( 2148 n 0 int8_accum - numeric_var_samp 0 2281 128 _null_ )); ! DATA(insert ( 2149 n 0 int4_accum - numeric_var_samp 0 2281 128 _null_ )); ! DATA(insert ( 2150 n 0 int2_accum - numeric_var_samp 0 2281 128 _null_ )); ! DATA(insert ( 2151 n 0 float4_accum - float8_var_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2152 n 0 float8_accum - float8_var_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2153 n 0 numeric_accum - numeric_var_samp 0 2281 128 _null_ )); /* stddev_pop */ ! DATA(insert ( 2724 n 0 int8_accum - numeric_stddev_pop 0 2281 128 _null_ )); ! DATA(insert ( 2725 n 0 int4_accum - numeric_stddev_pop 0 2281 128 _null_ )); ! DATA(insert ( 2726 n 0 int2_accum - numeric_stddev_pop 0 2281 128 _null_ )); ! DATA(insert ( 2727 n 0 float4_accum - float8_stddev_pop 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2728 n 0 float8_accum - float8_stddev_pop 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2729 n 0 numeric_accum - numeric_stddev_pop 0 2281 128 _null_ )); /* stddev_samp */ ! DATA(insert ( 2712 n 0 int8_accum - numeric_stddev_samp 0 2281 128 _null_ )); ! DATA(insert ( 2713 n 0 int4_accum - numeric_stddev_samp 0 2281 128 _null_ )); ! DATA(insert ( 2714 n 0 int2_accum - numeric_stddev_samp 0 2281 128 _null_ )); ! DATA(insert ( 2715 n 0 float4_accum - float8_stddev_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2716 n 0 float8_accum - float8_stddev_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2717 n 0 numeric_accum - numeric_stddev_samp 0 2281 128 _null_ )); /* stddev: historical Postgres syntax for stddev_samp */ ! DATA(insert ( 2154 n 0 int8_accum - numeric_stddev_samp 0 2281 128 _null_ )); ! DATA(insert ( 2155 n 0 int4_accum - numeric_stddev_samp 0 2281 128 _null_ )); ! DATA(insert ( 2156 n 0 int2_accum - numeric_stddev_samp 0 2281 128 _null_ )); ! DATA(insert ( 2157 n 0 float4_accum - float8_stddev_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2158 n 0 float8_accum - float8_stddev_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2159 n 0 numeric_accum - numeric_stddev_samp 0 2281 128 _null_ )); /* SQL2003 binary regression aggregates */ DATA(insert ( 2818 n 0 int8inc_float8_float8 - - 0 20 0 "0" )); --- 166,221 ---- DATA(insert ( 3527 n 0 enum_smaller - - 3518 3500 0 _null_ )); /* count */ ! DATA(insert ( 2147 n 0 int8inc_any int8inc_any_inv - 0 20 0 "0" )); ! DATA(insert ( 2803 n 0 int8inc int8dec - 0 20 0 "0" )); /* var_pop */ ! DATA(insert ( 2718 n 0 int8_accum int8_accum_inv numeric_var_pop 0 2281 128 _null_ )); ! DATA(insert ( 2719 n 0 int4_accum int4_accum_inv numeric_var_pop 0 2281 128 _null_ )); ! DATA(insert ( 2720 n 0 int2_accum int2_accum_inv numeric_var_pop 0 2281 128 _null_ )); ! DATA(insert ( 2721 n 0 float4_accum - float8_var_pop 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2722 n 0 float8_accum - float8_var_pop 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2723 n 0 numeric_accum numeric_accum_inv numeric_var_pop 0 2281 128 _null_ )); /* var_samp */ ! DATA(insert ( 2641 n 0 int8_accum int8_accum_inv numeric_var_samp 0 2281 128 _null_ )); ! DATA(insert ( 2642 n 0 int4_accum int4_accum_inv numeric_var_samp 0 2281 128 _null_ )); ! DATA(insert ( 2643 n 0 int2_accum int2_accum_inv numeric_var_samp 0 2281 128 _null_ )); ! DATA(insert ( 2644 n 0 float4_accum - float8_var_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2645 n 0 float8_accum - float8_var_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2646 n 0 numeric_accum numeric_accum_inv numeric_var_samp 0 2281 128 _null_ )); /* variance: historical Postgres syntax for var_samp */ ! DATA(insert ( 2148 n 0 int8_accum int8_accum_inv numeric_var_samp 0 2281 128 _null_ )); ! DATA(insert ( 2149 n 0 int4_accum int4_accum_inv numeric_var_samp 0 2281 128 _null_ )); ! DATA(insert ( 2150 n 0 int2_accum int2_accum_inv numeric_var_samp 0 2281 128 _null_ )); ! DATA(insert ( 2151 n 0 float4_accum - float8_var_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2152 n 0 float8_accum - float8_var_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2153 n 0 numeric_accum numeric_accum_inv numeric_var_samp 0 2281 128 _null_ )); /* stddev_pop */ ! DATA(insert ( 2724 n 0 int8_accum int8_accum_inv numeric_stddev_pop 0 2281 128 _null_ )); ! DATA(insert ( 2725 n 0 int4_accum int4_accum_inv numeric_stddev_pop 0 2281 128 _null_ )); ! DATA(insert ( 2726 n 0 int2_accum int2_accum_inv numeric_stddev_pop 0 2281 128 _null_ )); ! DATA(insert ( 2727 n 0 float4_accum - float8_stddev_pop 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2728 n 0 float8_accum - float8_stddev_pop 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2729 n 0 numeric_accum numeric_accum_inv numeric_stddev_pop 0 2281 128 _null_ )); /* stddev_samp */ ! DATA(insert ( 2712 n 0 int8_accum int8_accum_inv numeric_stddev_samp 0 2281 128 _null_ )); ! DATA(insert ( 2713 n 0 int4_accum int4_accum_inv numeric_stddev_samp 0 2281 128 _null_ )); ! DATA(insert ( 2714 n 0 int2_accum int2_accum_inv numeric_stddev_samp 0 2281 128 _null_ )); ! DATA(insert ( 2715 n 0 float4_accum - float8_stddev_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2716 n 0 float8_accum - float8_stddev_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2717 n 0 numeric_accum numeric_accum_inv numeric_stddev_samp 0 2281 128 _null_ )); /* stddev: historical Postgres syntax for stddev_samp */ ! DATA(insert ( 2154 n 0 int8_accum int8_accum_inv numeric_stddev_samp 0 2281 128 _null_ )); ! DATA(insert ( 2155 n 0 int4_accum int4_accum_inv numeric_stddev_samp 0 2281 128 _null_ )); ! DATA(insert ( 2156 n 0 int2_accum int2_accum_inv numeric_stddev_samp 0 2281 128 _null_ )); ! DATA(insert ( 2157 n 0 float4_accum - float8_stddev_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2158 n 0 float8_accum - float8_stddev_samp 0 1022 0 "{0,0,0}" )); ! DATA(insert ( 2159 n 0 numeric_accum numeric_accum_inv numeric_stddev_samp 0 2281 128 _null_ )); /* SQL2003 binary regression aggregates */ DATA(insert ( 2818 n 0 int8inc_float8_float8 - - 0 20 0 "0" )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 334e6b8..59d8ac6 100644 *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** DESCR("truncate interval to specified un *** 1309,1316 **** --- 1309,1320 ---- 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 ( int8inc_any_inv 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_inv _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_ )); *************** DATA(insert OID = 1832 ( float8_stddev_ *** 2396,2401 **** --- 2400,2407 ---- DESCR("aggregate final function"); DATA(insert OID = 1833 ( numeric_accum PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ numeric_accum _null_ _null_ _null_ )); DESCR("aggregate transition function"); + DATA(insert OID = 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("inverse 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 = 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_ )); *************** DATA(insert OID = 1836 ( int8_accum *** 2406,2411 **** --- 2412,2423 ---- 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 = 3549 ( 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("inverse aggregate transition function"); + DATA(insert OID = 3550 ( 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("inverse aggregate transition function"); + DATA(insert OID = 3551 ( 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("inverse 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_ )); *************** DATA(insert OID = 1842 ( int8_sum P *** 2426,2439 **** --- 2438,2459 ---- 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 = 3552 ( 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("inverse 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 = 3553 ( 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 = 3218 ( 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 = 3219 ( 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_ )); *************** DESCR("get value from jsonb with path el *** 4529,4536 **** DATA(insert OID = 3939 ( jsonb_extract_path_op PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ )); DATA(insert OID = 3940 ( jsonb_extract_path_text PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 25 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ )); DESCR("get value from jsonb as text with path elements"); ! DATA(insert OID = 3218 ( jsonb_extract_path_text_op PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ )); ! DATA(insert OID = 3219 ( jsonb_array_elements PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 3802 "3802" "{3802,3802}" "{i,o}" "{from_json,value}" _null_ jsonb_array_elements _null_ _null_ _null_ )); DESCR("elements of a jsonb array"); DATA(insert OID = 3465 ( jsonb_array_elements_text PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 25 "3802" "{3802,25}" "{i,o}" "{from_json,value}" _null_ jsonb_array_elements_text _null_ _null_ _null_ )); DESCR("elements of jsonb array"); --- 4549,4556 ---- DATA(insert OID = 3939 ( jsonb_extract_path_op PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ )); DATA(insert OID = 3940 ( jsonb_extract_path_text PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 25 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ )); DESCR("get value from jsonb as text with path elements"); ! DATA(insert OID = 3554 ( jsonb_extract_path_text_op PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ )); ! DATA(insert OID = 3555 ( jsonb_array_elements PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 3802 "3802" "{3802,3802}" "{i,o}" "{from_json,value}" _null_ jsonb_array_elements _null_ _null_ _null_ )); DESCR("elements of a jsonb array"); DATA(insert OID = 3465 ( jsonb_array_elements_text PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 25 "3802" "{3802,25}" "{i,o}" "{from_json,value}" _null_ jsonb_array_elements_text _null_ _null_ _null_ )); DESCR("elements of jsonb array"); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 031a43a..2f77dc1 100644 *** a/src/include/utils/builtins.h --- b/src/include/utils/builtins.h *************** extern Datum numeric_float8_no_overflow( *** 999,1008 **** --- 999,1012 ---- extern Datum float4_numeric(PG_FUNCTION_ARGS); extern Datum numeric_float4(PG_FUNCTION_ARGS); extern Datum numeric_accum(PG_FUNCTION_ARGS); + extern Datum numeric_accum_inv(PG_FUNCTION_ARGS); extern Datum numeric_avg_accum(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); *************** extern Datum int2_sum(PG_FUNCTION_ARGS); *** 1014,1020 **** --- 1018,1027 ---- extern Datum int4_sum(PG_FUNCTION_ARGS); extern Datum int8_sum(PG_FUNCTION_ARGS); extern Datum int2_avg_accum(PG_FUNCTION_ARGS); + extern Datum int2_avg_accum_inv(PG_FUNCTION_ARGS); extern Datum int4_avg_accum(PG_FUNCTION_ARGS); + extern Datum int4_avg_accum_inv(PG_FUNCTION_ARGS); + extern Datum int2int4_sum(PG_FUNCTION_ARGS); extern Datum int8_avg(PG_FUNCTION_ARGS); extern Datum width_bucket_numeric(PG_FUNCTION_ARGS); extern Datum hash_numeric(PG_FUNCTION_ARGS); diff --git a/src/include/utils/int8.h b/src/include/utils/int8.h index d63e3a9..5078e4a 100644 *** a/src/include/utils/int8.h --- b/src/include/utils/int8.h *************** extern Datum int8div(PG_FUNCTION_ARGS); *** 74,80 **** --- 74,82 ---- 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_any_inv(PG_FUNCTION_ARGS); extern Datum int8inc_float8_float8(PG_FUNCTION_ARGS); extern Datum int8larger(PG_FUNCTION_ARGS); extern Datum int8smaller(PG_FUNCTION_ARGS); diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h index 2731c6a..94328b3 100644 *** a/src/include/utils/timestamp.h --- b/src/include/utils/timestamp.h *************** extern Datum interval_mul(PG_FUNCTION_AR *** 184,189 **** --- 184,190 ---- 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); diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index e3eba47..394e3d5 100644 *** a/src/test/regress/expected/window.out --- b/src/test/regress/expected/window.out *************** DROP FUNCTION logging_sfunc_nonstrict(te *** 1295,1300 **** --- 1295,1783 ---- -- Test the arithmetic inverse transition functions -- -- + -- test inverse transition funtions 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) + + -- 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) + + -- inverse transition function with filter + SELECT i,SUM(v::int) FILTER (WHERE i < 4) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) + FROM (VALUES(1,1),(2,2),(3,NULL),(4,4)) t(i,v); + i | sum + ---+----- + 1 | 3 + 2 | 2 + 3 | + 4 | + (4 rows) + + -- ensure aggregate context properly recovers from having 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) + -- -- -- Test the boolean inverse transition functions diff --git a/src/test/regress/sql/window.sql b/src/test/regress/sql/window.sql index bd8dbdc..3cbd888 100644 *** a/src/test/regress/sql/window.sql --- b/src/test/regress/sql/window.sql *************** DROP FUNCTION logging_sfunc_nonstrict(te *** 476,481 **** --- 476,622 ---- -- -- + -- test inverse transition funtions 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); + + -- 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); + + -- inverse transition function with filter + SELECT i,SUM(v::int) FILTER (WHERE i < 4) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) + FROM (VALUES(1,1),(2,2),(3,NULL),(4,4)) t(i,v); + + -- ensure aggregate context properly recovers from having 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); + -- -- -- Test the boolean inverse transition functions