@ -3870,8 +3870,9 @@ timestamp_bin(PG_FUNCTION_ARGS)
Timestamp timestamp = PG_GETARG_TIMESTAMP ( 1 ) ;
Timestamp timestamp = PG_GETARG_TIMESTAMP ( 1 ) ;
Timestamp origin = PG_GETARG_TIMESTAMP ( 2 ) ;
Timestamp origin = PG_GETARG_TIMESTAMP ( 2 ) ;
Timestamp result ,
Timestamp result ,
tm_diff ,
stride_usecs ,
stride_usecs ,
tm_diff ,
tm_modulo ,
tm_delta ;
tm_delta ;
if ( TIMESTAMP_NOT_FINITE ( timestamp ) )
if ( TIMESTAMP_NOT_FINITE ( timestamp ) )
@ -3887,24 +3888,40 @@ timestamp_bin(PG_FUNCTION_ARGS)
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " timestamps cannot be binned into intervals containing months or years " ) ) ) ;
errmsg ( " timestamps cannot be binned into intervals containing months or years " ) ) ) ;
stride_usecs = stride - > day * USECS_PER_DAY + stride - > time ;
if ( unlikely ( pg_mul_s64_overflow ( stride - > day , USECS_PER_DAY , & stride_usecs ) ) | |
unlikely ( pg_add_s64_overflow ( stride_usecs , stride - > time , & stride_usecs ) ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " interval out of range " ) ) ) ;
if ( stride_usecs < = 0 )
if ( stride_usecs < = 0 )
ereport ( ERROR ,
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " stride must be greater than zero " ) ) ) ;
errmsg ( " stride must be greater than zero " ) ) ) ;
tm_diff = timestamp - origin ;
if ( unlikely ( pg_sub_s64_overflow ( timestamp , origin , & tm_diff ) ) )
tm_delta = tm_diff - tm_diff % stride_usecs ;
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " interval out of range " ) ) ) ;
/* These calculations cannot overflow */
tm_modulo = tm_diff % stride_usecs ;
tm_delta = tm_diff - tm_modulo ;
result = origin + tm_delta ;
/*
/*
* Make sure the returned timestamp is at the start of the bin , even if
* We want to round towards - infinity , not 0 , when tm_diff is negative and
* the origin is in the future .
* not a multiple of stride_usecs . This adjustment * can * cause overflow ,
* since the result might now be out of the range origin . . timestamp .
*/
*/
if ( origin > timestamp & & stride_usecs > 1 )
if ( tm_modulo < 0 )
tm_delta - = stride_usecs ;
{
if ( unlikely ( pg_sub_s64_overflow ( result , stride_usecs , & result ) ) | |
result = origin + tm_delta ;
! IS_VALID_TIMESTAMP ( result ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " timestamp out of range " ) ) ) ;
}
PG_RETURN_TIMESTAMP ( result ) ;
PG_RETURN_TIMESTAMP ( result ) ;
}
}
@ -4055,6 +4072,7 @@ timestamptz_bin(PG_FUNCTION_ARGS)
TimestampTz result ,
TimestampTz result ,
stride_usecs ,
stride_usecs ,
tm_diff ,
tm_diff ,
tm_modulo ,
tm_delta ;
tm_delta ;
if ( TIMESTAMP_NOT_FINITE ( timestamp ) )
if ( TIMESTAMP_NOT_FINITE ( timestamp ) )
@ -4070,24 +4088,40 @@ timestamptz_bin(PG_FUNCTION_ARGS)
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " timestamps cannot be binned into intervals containing months or years " ) ) ) ;
errmsg ( " timestamps cannot be binned into intervals containing months or years " ) ) ) ;
stride_usecs = stride - > day * USECS_PER_DAY + stride - > time ;
if ( unlikely ( pg_mul_s64_overflow ( stride - > day , USECS_PER_DAY , & stride_usecs ) ) | |
unlikely ( pg_add_s64_overflow ( stride_usecs , stride - > time , & stride_usecs ) ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " interval out of range " ) ) ) ;
if ( stride_usecs < = 0 )
if ( stride_usecs < = 0 )
ereport ( ERROR ,
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " stride must be greater than zero " ) ) ) ;
errmsg ( " stride must be greater than zero " ) ) ) ;
tm_diff = timestamp - origin ;
if ( unlikely ( pg_sub_s64_overflow ( timestamp , origin , & tm_diff ) ) )
tm_delta = tm_diff - tm_diff % stride_usecs ;
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " interval out of range " ) ) ) ;
/* These calculations cannot overflow */
tm_modulo = tm_diff % stride_usecs ;
tm_delta = tm_diff - tm_modulo ;
result = origin + tm_delta ;
/*
/*
* Make sure the returned timestamp is at the start of the bin , even if
* We want to round towards - infinity , not 0 , when tm_diff is negative and
* the origin is in the future .
* not a multiple of stride_usecs . This adjustment * can * cause overflow ,
* since the result might now be out of the range origin . . timestamp .
*/
*/
if ( origin > timestamp & & stride_usecs > 1 )
if ( tm_modulo < 0 )
tm_delta - = stride_usecs ;
{
if ( unlikely ( pg_sub_s64_overflow ( result , stride_usecs , & result ) ) | |
result = origin + tm_delta ;
! IS_VALID_TIMESTAMP ( result ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " timestamp out of range " ) ) ) ;
}
PG_RETURN_TIMESTAMPTZ ( result ) ;
PG_RETURN_TIMESTAMPTZ ( result ) ;
}
}