Re: [HACKERS] Interval aggregate regression failure - Mailing list pgsql-patches

From Bruce Momjian
Subject Re: [HACKERS] Interval aggregate regression failure
Date
Msg-id 200609050114.k851Eom11449@momjian.us
Whole thread Raw
In response to Re: [HACKERS] Interval aggregate regression failure  (Bruce Momjian <bruce@momjian.us>)
Responses Re: [HACKERS] Interval aggregate regression failure  (Michael Glaesemann <grzm@seespotcode.net>)
List pgsql-patches
Bruce Momjian wrote:
> OK, updated patch.  It will fix the >=24:00:00 case because it cascades
> up if the remainder number of seconds is greater or equal to one day.
> One open item is that it still might show >24 hours if the seconds
> computation combined with the remaning seconds >24 hours.  Not sure if
> that should be handled or not.  If you fix that, you really are
> cascading up because the resulting seconds might be less than the
> computed value, e.g. result is 23:00:00, remainder is 02:00:00, cascade
> up would be 1 day, 01:00:00.  I am unsure we want to do that.  Right
> now, this will show 25:00:00.

Updated patch that uses TSROUND for partial month and days.  Michael
says the tests look good on his system.  I think we are done with this
bug.  :-)

--
  Bruce Momjian   bruce@momjian.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +
Index: src/backend/utils/adt/timestamp.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v
retrieving revision 1.166
diff -c -c -r1.166 timestamp.c
*** src/backend/utils/adt/timestamp.c    3 Sep 2006 03:34:04 -0000    1.166
--- src/backend/utils/adt/timestamp.c    4 Sep 2006 17:54:25 -0000
***************
*** 2514,2541 ****
      /*
       *    Fractional months full days into days.
       *
!      *    The remainders suffer from float rounding, so instead of
!      *    doing the computation using just the remainder, we calculate
!      *    the total number of days and subtract.  Specifically, we are
!      *    multipling by DAYS_PER_MONTH before dividing by factor.
!      *    This greatly reduces rounding errors.
       */
!     month_remainder_days = (orig_month * (double)DAYS_PER_MONTH) * factor -
!             result->month * (double)DAYS_PER_MONTH;
!     sec_remainder = (orig_day * (double)SECS_PER_DAY) * factor -
!             result->day * (double)SECS_PER_DAY +
!             (month_remainder_days - (int32) month_remainder_days) * SECS_PER_DAY;

      /* cascade units down */
      result->day += (int32) month_remainder_days;
  #ifdef HAVE_INT64_TIMESTAMP
      result->time = rint(span->time * factor + sec_remainder * USECS_PER_SEC);
  #else
!     /*
!      *    TSROUND() needed to prevent -146:23:60.00 output on PowerPC for
!      *    SELECT interval '-41 mon -12 days -360:00' * 0.3;
!      */
!     result->time = span->time * factor + TSROUND(sec_remainder);
  #endif

      PG_RETURN_INTERVAL_P(result);
--- 2514,2547 ----
      /*
       *    Fractional months full days into days.
       *
!      *    Floating point calculation are inherently inprecise, so these
!      *    calculations are crafted to produce the most reliable result
!      *    possible.  TSROUND() is needed to more accurately produce whole
!      *    numbers where appropriate.
       */
!     month_remainder_days = (orig_month * factor - result->month) * DAYS_PER_MONTH;
!     month_remainder_days = TSROUND(month_remainder_days);
!     sec_remainder = (orig_day * factor - result->day +
!             month_remainder_days - (int)month_remainder_days) * SECS_PER_DAY;
!     sec_remainder = TSROUND(sec_remainder);
!
!     /*
!      *    Might have 24:00:00 hours due to rounding, or >24 hours because of
!      *    time cascade from months and days.  It might still be >24 if the
!      *    combination of cascade and the seconds factor operation itself.
!      */
!     if (Abs(sec_remainder) >= SECS_PER_DAY)
!     {
!         result->day += (int)(sec_remainder / SECS_PER_DAY);
!         sec_remainder -= (int)(sec_remainder / SECS_PER_DAY) * SECS_PER_DAY;
!     }

      /* cascade units down */
      result->day += (int32) month_remainder_days;
  #ifdef HAVE_INT64_TIMESTAMP
      result->time = rint(span->time * factor + sec_remainder * USECS_PER_SEC);
  #else
!     result->time = span->time * factor + sec_remainder;
  #endif

      PG_RETURN_INTERVAL_P(result);
***************
*** 2574,2584 ****
       *    Fractional months full days into days.  See comment in
       *    interval_mul().
       */
!     month_remainder_days = (orig_month * (double)DAYS_PER_MONTH) / factor -
!             result->month * (double)DAYS_PER_MONTH;
!     sec_remainder = (orig_day * (double)SECS_PER_DAY) / factor -
!             result->day * (double)SECS_PER_DAY +
!             (month_remainder_days - (int32) month_remainder_days) * SECS_PER_DAY;

      /* cascade units down */
      result->day += (int32) month_remainder_days;
--- 2580,2595 ----
       *    Fractional months full days into days.  See comment in
       *    interval_mul().
       */
!     month_remainder_days = (orig_month / factor - result->month) * DAYS_PER_MONTH;
!     month_remainder_days = TSROUND(month_remainder_days);
!     sec_remainder = (orig_day / factor - result->day +
!             month_remainder_days - (int)month_remainder_days) * SECS_PER_DAY;
!     sec_remainder = TSROUND(sec_remainder);
!     if (Abs(sec_remainder) >= SECS_PER_DAY)
!     {
!         result->day += (int)(sec_remainder / SECS_PER_DAY);
!         sec_remainder -= (int)(sec_remainder / SECS_PER_DAY) * SECS_PER_DAY;
!     }

      /* cascade units down */
      result->day += (int32) month_remainder_days;
***************
*** 2586,2592 ****
      result->time = rint(span->time / factor + sec_remainder * USECS_PER_SEC);
  #else
      /* See TSROUND comment in interval_mul(). */
!     result->time = span->time / factor + TSROUND(sec_remainder);
  #endif

      PG_RETURN_INTERVAL_P(result);
--- 2597,2603 ----
      result->time = rint(span->time / factor + sec_remainder * USECS_PER_SEC);
  #else
      /* See TSROUND comment in interval_mul(). */
!     result->time = span->time / factor + sec_remainder;
  #endif

      PG_RETURN_INTERVAL_P(result);
Index: src/include/utils/timestamp.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/utils/timestamp.h,v
retrieving revision 1.62
diff -c -c -r1.62 timestamp.h
*** src/include/utils/timestamp.h    13 Jul 2006 16:49:20 -0000    1.62
--- src/include/utils/timestamp.h    4 Sep 2006 17:54:26 -0000
***************
*** 161,171 ****

  typedef double fsec_t;

! /* round off to MAX_TIMESTAMP_PRECISION decimal places */
! /* note: this is also used for rounding off intervals */
  #define TS_PREC_INV 1000000.0
  #define TSROUND(j) (rint(((double) (j)) * TS_PREC_INV) / TS_PREC_INV)
- #endif

  #define TIMESTAMP_MASK(b) (1 << (b))
  #define INTERVAL_MASK(b) (1 << (b))
--- 161,174 ----

  typedef double fsec_t;

! #endif
!
! /*
!  *    Round off to MAX_TIMESTAMP_PRECISION decimal places.
!  *    Note: this is also used for rounding off intervals.
!  */
  #define TS_PREC_INV 1000000.0
  #define TSROUND(j) (rint(((double) (j)) * TS_PREC_INV) / TS_PREC_INV)

  #define TIMESTAMP_MASK(b) (1 << (b))
  #define INTERVAL_MASK(b) (1 << (b))

pgsql-patches by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: contrib/otherlock
Next
From: Bruce Momjian
Date:
Subject: XML syntax patch