diff options
Diffstat (limited to 'src/backend/utils/adt/timestamp.c')
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 72 |
1 files changed, 47 insertions, 25 deletions
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index ce30bb6e9fa..efc1e9b9925 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -3229,7 +3229,6 @@ interval_mi(PG_FUNCTION_ARGS) (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); - PG_RETURN_INTERVAL_P(result); } @@ -3376,12 +3375,18 @@ interval_div(PG_FUNCTION_ARGS) } /* - * interval_accum and interval_avg implement the AVG(interval) aggregate. + * interval_accum, interval_accum_inv, and interval_avg implement the + * AVG(interval) aggregate. * * The transition datatype for this aggregate is a 2-element array of * intervals, where the first is the running sum and the second contains * the number of values so far in its 'time' field. This is a bit ugly * but it beats inventing a specialized datatype for the purpose. + * + * NOTE: The inverse transition function cannot guarantee exact results + * when using float8 timestamps. However, int8 timestamps are now the + * norm, and the probable range of values is not so wide that disastrous + * cancellation is likely even with float8, so we'll ignore the risk. */ Datum @@ -3402,17 +3407,8 @@ interval_accum(PG_FUNCTION_ARGS) if (ndatums != 2) elog(ERROR, "expected 2-element interval array"); - /* - * XXX memcpy, instead of just extracting a pointer, to work around buggy - * array code: it won't ensure proper alignment of Interval objects on - * machines where double requires 8-byte alignment. That should be fixed, - * but in the meantime... - * - * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some - * compilers optimize into double-aligned load/store anyway. - */ - memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval)); - memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval)); + sumX = *(DatumGetIntervalP(transdatums[0])); + N = *(DatumGetIntervalP(transdatums[1])); newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl, IntervalPGetDatum(&sumX), @@ -3429,6 +3425,41 @@ interval_accum(PG_FUNCTION_ARGS) } Datum +interval_accum_inv(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + Interval *newval = PG_GETARG_INTERVAL_P(1); + Datum *transdatums; + int ndatums; + Interval sumX, + N; + Interval *newsum; + ArrayType *result; + + deconstruct_array(transarray, + INTERVALOID, sizeof(Interval), false, 'd', + &transdatums, NULL, &ndatums); + if (ndatums != 2) + elog(ERROR, "expected 2-element interval array"); + + sumX = *(DatumGetIntervalP(transdatums[0])); + N = *(DatumGetIntervalP(transdatums[1])); + + newsum = DatumGetIntervalP(DirectFunctionCall2(interval_mi, + IntervalPGetDatum(&sumX), + IntervalPGetDatum(newval))); + N.time -= 1; + + transdatums[0] = IntervalPGetDatum(newsum); + transdatums[1] = IntervalPGetDatum(&N); + + result = construct_array(transdatums, 2, + INTERVALOID, sizeof(Interval), false, 'd'); + + PG_RETURN_ARRAYTYPE_P(result); +} + +Datum interval_avg(PG_FUNCTION_ARGS) { ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); @@ -3443,17 +3474,8 @@ interval_avg(PG_FUNCTION_ARGS) if (ndatums != 2) elog(ERROR, "expected 2-element interval array"); - /* - * XXX memcpy, instead of just extracting a pointer, to work around buggy - * array code: it won't ensure proper alignment of Interval objects on - * machines where double requires 8-byte alignment. That should be fixed, - * but in the meantime... - * - * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some - * compilers optimize into double-aligned load/store anyway. - */ - memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval)); - memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval)); + sumX = *(DatumGetIntervalP(transdatums[0])); + N = *(DatumGetIntervalP(transdatums[1])); /* SQL defines AVG of no values to be NULL */ if (N.time == 0) @@ -3461,7 +3483,7 @@ interval_avg(PG_FUNCTION_ARGS) return DirectFunctionCall2(interval_div, IntervalPGetDatum(&sumX), - Float8GetDatum(N.time)); + Float8GetDatum((double) N.time)); } |