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 b78451d..53e7624 100644
*** a/src/backend/utils/adt/numeric.c
--- b/src/backend/utils/adt/numeric.c
*************** numeric_float4(PG_FUNCTION_ARGS)
*** 2479,2486 ****
  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 */
--- 2479,2490 ----
  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
*** 2505,2510 ****
--- 2509,2518 ----
  	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,
*** 2522,2536 ****
  	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)
  	{
--- 2530,2558 ----
  	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,
*** 2541,2553 ****
  	/* 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
  	{
--- 2563,2577 ----
  	/* 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,
*** 2556,2567 ****
--- 2580,2692 ----
  
  		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)
*** 2571,2588 ****
  
  	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.
   */
--- 2696,2734 ----
  
  	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)
*** 2593,2606 ****
  
  	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);
  }
--- 2739,2750 ----
  
  	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)
*** 2621,2637 ****
  
  	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);
  	}
  
--- 2765,2780 ----
  
  	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)
*** 2645,2661 ****
  
  	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);
  	}
  
--- 2788,2803 ----
  
  	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)
*** 2669,2686 ****
  
  	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);
--- 2811,2913 ----
  
  	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)
*** 2688,2693 ****
--- 2915,2921 ----
  
  /*
   * 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)
*** 2696,2719 ****
  
  	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)
  {
--- 2924,2945 ----
  
  	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)
*** 2722,2731 ****
  	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));
--- 2948,2958 ----
  	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)
*** 2740,2749 ****
  	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)));
--- 2967,2978 ----
  	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 
*** 2774,2780 ****
  	int			rscale;
  
  	/* Deal with empty input and NaN-input cases */
! 	if (state == NULL)
  	{
  		*is_null = true;
  		return NULL;
--- 3003,3009 ----
  	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 
*** 2782,2788 ****
  
  	*is_null = false;
  
! 	if (state->isNaN)
  		return make_result(&const_nan);
  
  	init_var(&vN);
--- 3011,3017 ----
  
  	*is_null = false;
  
! 	if (state->NaNcount > 0)
  		return make_result(&const_nan);
  
  	init_var(&vN);
*************** numeric_stddev_pop(PG_FUNCTION_ARGS)
*** 2913,2932 ****
  }
  
  /*
!  * 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
--- 3142,3155 ----
  }
  
  /*
!  * 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)
*** 3065,3074 ****
  										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
--- 3288,3302 ----
  										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)
*** 3106,3111 ****
--- 3334,3368 ----
  }
  
  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)
*** 3134,3139 ****
--- 3391,3442 ----
  }
  
  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 cf6982b..eb051ba 100644
*** a/src/backend/utils/adt/timestamp.c
--- b/src/backend/utils/adt/timestamp.c
*************** interval_accum(PG_FUNCTION_ARGS)
*** 3157,3162 ****
--- 3157,3207 ----
  }
  
  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 11c1e1a..d18a6e1 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 = 3210 (  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 = 3211 (  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 = 3212 (  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 = 3213 (  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 = 3214 (  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 = 3215 (  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 = 3216 (  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 = 3217 (  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_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index b90d88d..9e44693 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 47fb866..e852fbd 100644
*** a/src/include/utils/timestamp.h
--- b/src/include/utils/timestamp.h
*************** extern Datum interval_mul(PG_FUNCTION_AR
*** 179,184 ****
--- 179,185 ----
  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 aa29471..1d28a81 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 MIN, MAX and boolean inverse transition functions
diff --git a/src/test/regress/sql/window.sql b/src/test/regress/sql/window.sql
index 676fa45..e2af490 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 MIN, MAX and boolean inverse transition functions
