Remove dependence on -fwrapv semantics in a few places.
This commit attempts to update a few places, such as the money, numeric, and timestamp types, to no longer rely on signed integer wrapping for correctness. This is intended to move us closer towards removing -fwrapv, which may enable some compiler optimizations. However, there is presently no plan to actually remove that compiler option in the near future. Besides using some of the existing overflow-aware routines in int.h, this commit introduces and makes use of some new ones. Specifically, it adds functions that accept a signed integer and return its absolute value as an unsigned integer with the same width (e.g., pg_abs_s64()). It also adds functions that accept an unsigned integer, store the result of negating that integer in a signed integer with the same width, and return whether the negation overflowed (e.g., pg_neg_u64_overflow()). Finally, this commit adds a couple of tests for timestamps near POSTGRES_EPOCH_JDATE. Author: Joseph Koshakow Reviewed-by: Tom Lane, Heikki Linnakangas, Jian He Discussion: https://postgr.es/m/CAAvxfHdBPOyEGS7s%2Bxf4iaW0-cgiq25jpYdWBqQqvLtLe_t6tw%40mail.gmail.com
This commit is contained in:
parent
ad89d71978
commit
9e9a2b7031
@ -387,6 +387,7 @@ Datum
|
|||||||
cash_out(PG_FUNCTION_ARGS)
|
cash_out(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Cash value = PG_GETARG_CASH(0);
|
Cash value = PG_GETARG_CASH(0);
|
||||||
|
uint64 uvalue;
|
||||||
char *result;
|
char *result;
|
||||||
char buf[128];
|
char buf[128];
|
||||||
char *bufptr;
|
char *bufptr;
|
||||||
@ -429,8 +430,6 @@ cash_out(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
if (value < 0)
|
if (value < 0)
|
||||||
{
|
{
|
||||||
/* make the amount positive for digit-reconstruction loop */
|
|
||||||
value = -value;
|
|
||||||
/* set up formatting data */
|
/* set up formatting data */
|
||||||
signsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
|
signsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
|
||||||
sign_posn = lconvert->n_sign_posn;
|
sign_posn = lconvert->n_sign_posn;
|
||||||
@ -445,6 +444,9 @@ cash_out(PG_FUNCTION_ARGS)
|
|||||||
sep_by_space = lconvert->p_sep_by_space;
|
sep_by_space = lconvert->p_sep_by_space;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* make the amount positive for digit-reconstruction loop */
|
||||||
|
uvalue = pg_abs_s64(value);
|
||||||
|
|
||||||
/* we build the digits+decimal-point+sep string right-to-left in buf[] */
|
/* we build the digits+decimal-point+sep string right-to-left in buf[] */
|
||||||
bufptr = buf + sizeof(buf) - 1;
|
bufptr = buf + sizeof(buf) - 1;
|
||||||
*bufptr = '\0';
|
*bufptr = '\0';
|
||||||
@ -470,10 +472,10 @@ cash_out(PG_FUNCTION_ARGS)
|
|||||||
memcpy(bufptr, ssymbol, strlen(ssymbol));
|
memcpy(bufptr, ssymbol, strlen(ssymbol));
|
||||||
}
|
}
|
||||||
|
|
||||||
*(--bufptr) = ((uint64) value % 10) + '0';
|
*(--bufptr) = (uvalue % 10) + '0';
|
||||||
value = ((uint64) value) / 10;
|
uvalue = uvalue / 10;
|
||||||
digit_pos--;
|
digit_pos--;
|
||||||
} while (value || digit_pos >= 0);
|
} while (uvalue || digit_pos >= 0);
|
||||||
|
|
||||||
/*----------
|
/*----------
|
||||||
* Now, attach currency symbol and sign symbol in the correct order.
|
* Now, attach currency symbol and sign symbol in the correct order.
|
||||||
|
@ -8113,7 +8113,7 @@ int64_to_numericvar(int64 val, NumericVar *var)
|
|||||||
if (val < 0)
|
if (val < 0)
|
||||||
{
|
{
|
||||||
var->sign = NUMERIC_NEG;
|
var->sign = NUMERIC_NEG;
|
||||||
uval = -val;
|
uval = pg_abs_s64(val);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -11584,7 +11584,7 @@ power_var_int(const NumericVar *base, int exp, int exp_dscale,
|
|||||||
* Now we can proceed with the multiplications.
|
* Now we can proceed with the multiplications.
|
||||||
*/
|
*/
|
||||||
neg = (exp < 0);
|
neg = (exp < 0);
|
||||||
mask = abs(exp);
|
mask = pg_abs_s32(exp);
|
||||||
|
|
||||||
init_var(&base_prod);
|
init_var(&base_prod);
|
||||||
set_var_from_var(base, &base_prod);
|
set_var_from_var(base, &base_prod);
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "common/int.h"
|
||||||
#include "port/pg_bitutils.h"
|
#include "port/pg_bitutils.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
|
||||||
@ -131,6 +132,7 @@ pg_strtoint16_safe(const char *s, Node *escontext)
|
|||||||
uint16 tmp = 0;
|
uint16 tmp = 0;
|
||||||
bool neg = false;
|
bool neg = false;
|
||||||
unsigned char digit;
|
unsigned char digit;
|
||||||
|
int16 result;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The majority of cases are likely to be base-10 digits without any
|
* The majority of cases are likely to be base-10 digits without any
|
||||||
@ -190,10 +192,9 @@ pg_strtoint16_safe(const char *s, Node *escontext)
|
|||||||
|
|
||||||
if (neg)
|
if (neg)
|
||||||
{
|
{
|
||||||
/* check the negative equivalent will fit without overflowing */
|
if (unlikely(pg_neg_u16_overflow(tmp, &result)))
|
||||||
if (unlikely(tmp > (uint16) (-(PG_INT16_MIN + 1)) + 1))
|
|
||||||
goto out_of_range;
|
goto out_of_range;
|
||||||
return -((int16) tmp);
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(tmp > PG_INT16_MAX))
|
if (unlikely(tmp > PG_INT16_MAX))
|
||||||
@ -333,10 +334,9 @@ slow:
|
|||||||
|
|
||||||
if (neg)
|
if (neg)
|
||||||
{
|
{
|
||||||
/* check the negative equivalent will fit without overflowing */
|
if (unlikely(pg_neg_u16_overflow(tmp, &result)))
|
||||||
if (tmp > (uint16) (-(PG_INT16_MIN + 1)) + 1)
|
|
||||||
goto out_of_range;
|
goto out_of_range;
|
||||||
return -((int16) tmp);
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmp > PG_INT16_MAX)
|
if (tmp > PG_INT16_MAX)
|
||||||
@ -393,6 +393,7 @@ pg_strtoint32_safe(const char *s, Node *escontext)
|
|||||||
uint32 tmp = 0;
|
uint32 tmp = 0;
|
||||||
bool neg = false;
|
bool neg = false;
|
||||||
unsigned char digit;
|
unsigned char digit;
|
||||||
|
int32 result;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The majority of cases are likely to be base-10 digits without any
|
* The majority of cases are likely to be base-10 digits without any
|
||||||
@ -452,10 +453,9 @@ pg_strtoint32_safe(const char *s, Node *escontext)
|
|||||||
|
|
||||||
if (neg)
|
if (neg)
|
||||||
{
|
{
|
||||||
/* check the negative equivalent will fit without overflowing */
|
if (unlikely(pg_neg_u32_overflow(tmp, &result)))
|
||||||
if (unlikely(tmp > (uint32) (-(PG_INT32_MIN + 1)) + 1))
|
|
||||||
goto out_of_range;
|
goto out_of_range;
|
||||||
return -((int32) tmp);
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(tmp > PG_INT32_MAX))
|
if (unlikely(tmp > PG_INT32_MAX))
|
||||||
@ -595,10 +595,9 @@ slow:
|
|||||||
|
|
||||||
if (neg)
|
if (neg)
|
||||||
{
|
{
|
||||||
/* check the negative equivalent will fit without overflowing */
|
if (unlikely(pg_neg_u32_overflow(tmp, &result)))
|
||||||
if (tmp > (uint32) (-(PG_INT32_MIN + 1)) + 1)
|
|
||||||
goto out_of_range;
|
goto out_of_range;
|
||||||
return -((int32) tmp);
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmp > PG_INT32_MAX)
|
if (tmp > PG_INT32_MAX)
|
||||||
@ -655,6 +654,7 @@ pg_strtoint64_safe(const char *s, Node *escontext)
|
|||||||
uint64 tmp = 0;
|
uint64 tmp = 0;
|
||||||
bool neg = false;
|
bool neg = false;
|
||||||
unsigned char digit;
|
unsigned char digit;
|
||||||
|
int64 result;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The majority of cases are likely to be base-10 digits without any
|
* The majority of cases are likely to be base-10 digits without any
|
||||||
@ -714,10 +714,9 @@ pg_strtoint64_safe(const char *s, Node *escontext)
|
|||||||
|
|
||||||
if (neg)
|
if (neg)
|
||||||
{
|
{
|
||||||
/* check the negative equivalent will fit without overflowing */
|
if (unlikely(pg_neg_u64_overflow(tmp, &result)))
|
||||||
if (unlikely(tmp > (uint64) (-(PG_INT64_MIN + 1)) + 1))
|
|
||||||
goto out_of_range;
|
goto out_of_range;
|
||||||
return -((int64) tmp);
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(tmp > PG_INT64_MAX))
|
if (unlikely(tmp > PG_INT64_MAX))
|
||||||
@ -857,10 +856,9 @@ slow:
|
|||||||
|
|
||||||
if (neg)
|
if (neg)
|
||||||
{
|
{
|
||||||
/* check the negative equivalent will fit without overflowing */
|
if (unlikely(pg_neg_u64_overflow(tmp, &result)))
|
||||||
if (tmp > (uint64) (-(PG_INT64_MIN + 1)) + 1)
|
|
||||||
goto out_of_range;
|
goto out_of_range;
|
||||||
return -((int64) tmp);
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmp > PG_INT64_MAX)
|
if (tmp > PG_INT64_MAX)
|
||||||
|
@ -618,19 +618,8 @@ make_timestamp_internal(int year, int month, int day,
|
|||||||
time = (((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE)
|
time = (((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE)
|
||||||
* USECS_PER_SEC) + (int64) rint(sec * USECS_PER_SEC);
|
* USECS_PER_SEC) + (int64) rint(sec * USECS_PER_SEC);
|
||||||
|
|
||||||
result = date * USECS_PER_DAY + time;
|
if (unlikely(pg_mul_s64_overflow(date, USECS_PER_DAY, &result) ||
|
||||||
/* check for major overflow */
|
pg_add_s64_overflow(result, time, &result)))
|
||||||
if ((result - time) / USECS_PER_DAY != date)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g",
|
|
||||||
year, month, day,
|
|
||||||
hour, min, sec)));
|
|
||||||
|
|
||||||
/* check for just-barely overflow (okay except time-of-day wraps) */
|
|
||||||
/* caution: we want to allow 1999-12-31 24:00:00 */
|
|
||||||
if ((result < 0 && date > 0) ||
|
|
||||||
(result > 0 && date < -1))
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g",
|
errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g",
|
||||||
@ -2010,17 +1999,8 @@ tm2timestamp(struct pg_tm *tm, fsec_t fsec, int *tzp, Timestamp *result)
|
|||||||
date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
|
date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
|
||||||
time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
|
time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
|
||||||
|
|
||||||
*result = date * USECS_PER_DAY + time;
|
if (unlikely(pg_mul_s64_overflow(date, USECS_PER_DAY, result) ||
|
||||||
/* check for major overflow */
|
pg_add_s64_overflow(*result, time, result)))
|
||||||
if ((*result - time) / USECS_PER_DAY != date)
|
|
||||||
{
|
|
||||||
*result = 0; /* keep compiler quiet */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
/* check for just-barely overflow (okay except time-of-day wraps) */
|
|
||||||
/* caution: we want to allow 1999-12-31 24:00:00 */
|
|
||||||
if ((*result < 0 && date > 0) ||
|
|
||||||
(*result > 0 && date < -1))
|
|
||||||
{
|
{
|
||||||
*result = 0; /* keep compiler quiet */
|
*result = 0; /* keep compiler quiet */
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -23,15 +23,35 @@
|
|||||||
|
|
||||||
/*---------
|
/*---------
|
||||||
* The following guidelines apply to all the overflow routines:
|
* The following guidelines apply to all the overflow routines:
|
||||||
* - If a + b overflows, return true, otherwise store the result of a + b
|
*
|
||||||
* into *result. The content of *result is implementation defined in case of
|
* If the result overflows, return true, otherwise store the result into
|
||||||
* overflow.
|
* *result. The content of *result is implementation defined in case of
|
||||||
* - If a - b overflows, return true, otherwise store the result of a - b
|
|
||||||
* into *result. The content of *result is implementation defined in case of
|
|
||||||
* overflow.
|
|
||||||
* - If a * b overflows, return true, otherwise store the result of a * b
|
|
||||||
* into *result. The content of *result is implementation defined in case of
|
|
||||||
* overflow.
|
* overflow.
|
||||||
|
*
|
||||||
|
* bool pg_add_*_overflow(a, b, *result)
|
||||||
|
*
|
||||||
|
* Calculate a + b
|
||||||
|
*
|
||||||
|
* bool pg_sub_*_overflow(a, b, *result)
|
||||||
|
*
|
||||||
|
* Calculate a - b
|
||||||
|
*
|
||||||
|
* bool pg_mul_*_overflow(a, b, *result)
|
||||||
|
*
|
||||||
|
* Calculate a * b
|
||||||
|
*
|
||||||
|
* bool pg_neg_*_overflow(a, *result)
|
||||||
|
*
|
||||||
|
* Calculate -a
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* In addition, this file contains:
|
||||||
|
*
|
||||||
|
* <unsigned int type> pg_abs_*(<signed int type> a)
|
||||||
|
*
|
||||||
|
* Calculate absolute value of a. Unlike the standard library abs()
|
||||||
|
* and labs() functions, the return type is unsigned, so the operation
|
||||||
|
* cannot overflow.
|
||||||
*---------
|
*---------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -97,6 +117,17 @@ pg_mul_s16_overflow(int16 a, int16 b, int16 *result)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline uint16
|
||||||
|
pg_abs_s16(int16 a)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This first widens the argument from int16 to int32 for use with abs().
|
||||||
|
* The result is then narrowed from int32 to uint16. This prevents any
|
||||||
|
* possibility of overflow.
|
||||||
|
*/
|
||||||
|
return (uint16) abs((int32) a);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* INT32
|
* INT32
|
||||||
*/
|
*/
|
||||||
@ -154,6 +185,17 @@ pg_mul_s32_overflow(int32 a, int32 b, int32 *result)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline uint32
|
||||||
|
pg_abs_s32(int32 a)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This first widens the argument from int32 to int64 for use with
|
||||||
|
* i64abs(). The result is then narrowed from int64 to uint32. This
|
||||||
|
* prevents any possibility of overflow.
|
||||||
|
*/
|
||||||
|
return (uint32) i64abs((int64) a);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* INT64
|
* INT64
|
||||||
*/
|
*/
|
||||||
@ -258,6 +300,14 @@ pg_mul_s64_overflow(int64 a, int64 b, int64 *result)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline uint64
|
||||||
|
pg_abs_s64(int64 a)
|
||||||
|
{
|
||||||
|
if (unlikely(a == PG_INT64_MIN))
|
||||||
|
return (uint64) PG_INT64_MAX + 1;
|
||||||
|
return (uint64) i64abs(a);
|
||||||
|
}
|
||||||
|
|
||||||
/*------------------------------------------------------------------------
|
/*------------------------------------------------------------------------
|
||||||
* Overflow routines for unsigned integers
|
* Overflow routines for unsigned integers
|
||||||
*------------------------------------------------------------------------
|
*------------------------------------------------------------------------
|
||||||
@ -318,6 +368,24 @@ pg_mul_u16_overflow(uint16 a, uint16 b, uint16 *result)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
pg_neg_u16_overflow(uint16 a, int16 *result)
|
||||||
|
{
|
||||||
|
#if defined(HAVE__BUILTIN_OP_OVERFLOW)
|
||||||
|
return __builtin_sub_overflow(0, a, result);
|
||||||
|
#else
|
||||||
|
int32 res = -((int32) a);
|
||||||
|
|
||||||
|
if (unlikely(res < PG_INT16_MIN))
|
||||||
|
{
|
||||||
|
*result = 0x5EED; /* to avoid spurious warnings */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*result = res;
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* INT32
|
* INT32
|
||||||
*/
|
*/
|
||||||
@ -373,6 +441,24 @@ pg_mul_u32_overflow(uint32 a, uint32 b, uint32 *result)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
pg_neg_u32_overflow(uint32 a, int32 *result)
|
||||||
|
{
|
||||||
|
#if defined(HAVE__BUILTIN_OP_OVERFLOW)
|
||||||
|
return __builtin_sub_overflow(0, a, result);
|
||||||
|
#else
|
||||||
|
int64 res = -((int64) a);
|
||||||
|
|
||||||
|
if (unlikely(res < PG_INT32_MIN))
|
||||||
|
{
|
||||||
|
*result = 0x5EED; /* to avoid spurious warnings */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*result = res;
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* UINT64
|
* UINT64
|
||||||
*/
|
*/
|
||||||
@ -438,6 +524,35 @@ pg_mul_u64_overflow(uint64 a, uint64 b, uint64 *result)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
pg_neg_u64_overflow(uint64 a, int64 *result)
|
||||||
|
{
|
||||||
|
#if defined(HAVE__BUILTIN_OP_OVERFLOW)
|
||||||
|
return __builtin_sub_overflow(0, a, result);
|
||||||
|
#elif defined(HAVE_INT128)
|
||||||
|
int128 res = -((int128) a);
|
||||||
|
|
||||||
|
if (unlikely(res < PG_INT64_MIN))
|
||||||
|
{
|
||||||
|
*result = 0x5EED; /* to avoid spurious warnings */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*result = res;
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
if (unlikely(a > (uint64) PG_INT64_MAX + 1))
|
||||||
|
{
|
||||||
|
*result = 0x5EED; /* to avoid spurious warnings */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (unlikely(a == (uint64) PG_INT64_MAX + 1))
|
||||||
|
*result = PG_INT64_MIN;
|
||||||
|
else
|
||||||
|
*result = -((int64) a);
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/*------------------------------------------------------------------------
|
/*------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* Comparison routines for integer types.
|
* Comparison routines for integer types.
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#error -ffast-math is known to break this code
|
#error -ffast-math is known to break this code
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "common/int.h"
|
||||||
#include "dt.h"
|
#include "dt.h"
|
||||||
#include "pgtypes_date.h"
|
#include "pgtypes_date.h"
|
||||||
#include "pgtypes_timestamp.h"
|
#include "pgtypes_timestamp.h"
|
||||||
@ -48,14 +49,8 @@ tm2timestamp(struct tm *tm, fsec_t fsec, int *tzp, timestamp * result)
|
|||||||
|
|
||||||
dDate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
|
dDate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
|
||||||
time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
|
time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
|
||||||
*result = (dDate * USECS_PER_DAY) + time;
|
if (unlikely(pg_mul_s64_overflow(dDate, USECS_PER_DAY, result) ||
|
||||||
/* check for major overflow */
|
pg_add_s64_overflow(*result, time, result)))
|
||||||
if ((*result - time) / USECS_PER_DAY != dDate)
|
|
||||||
return -1;
|
|
||||||
/* check for just-barely overflow (okay except time-of-day wraps) */
|
|
||||||
/* caution: we want to allow 1999-12-31 24:00:00 */
|
|
||||||
if ((*result < 0 && dDate > 0) ||
|
|
||||||
(*result > 0 && dDate < -1))
|
|
||||||
return -1;
|
return -1;
|
||||||
if (tzp != NULL)
|
if (tzp != NULL)
|
||||||
*result = dt2local(*result, -(*tzp));
|
*result = dt2local(*result, -(*tzp));
|
||||||
|
@ -2201,3 +2201,16 @@ select age(timestamp '-infinity', timestamp 'infinity');
|
|||||||
|
|
||||||
select age(timestamp '-infinity', timestamp '-infinity');
|
select age(timestamp '-infinity', timestamp '-infinity');
|
||||||
ERROR: interval out of range
|
ERROR: interval out of range
|
||||||
|
-- test timestamp near POSTGRES_EPOCH_JDATE
|
||||||
|
select timestamp '1999-12-31 24:00:00';
|
||||||
|
timestamp
|
||||||
|
--------------------------
|
||||||
|
Sat Jan 01 00:00:00 2000
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select make_timestamp(1999, 12, 31, 24, 0, 0);
|
||||||
|
make_timestamp
|
||||||
|
--------------------------
|
||||||
|
Sat Jan 01 00:00:00 2000
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
@ -3286,3 +3286,16 @@ SELECT age(timestamptz '-infinity', timestamptz 'infinity');
|
|||||||
|
|
||||||
SELECT age(timestamptz '-infinity', timestamptz '-infinity');
|
SELECT age(timestamptz '-infinity', timestamptz '-infinity');
|
||||||
ERROR: interval out of range
|
ERROR: interval out of range
|
||||||
|
-- test timestamp near POSTGRES_EPOCH_JDATE
|
||||||
|
select timestamptz '1999-12-31 24:00:00';
|
||||||
|
timestamptz
|
||||||
|
------------------------------
|
||||||
|
Sat Jan 01 00:00:00 2000 PST
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select make_timestamptz(1999, 12, 31, 24, 0, 0);
|
||||||
|
make_timestamptz
|
||||||
|
------------------------------
|
||||||
|
Sat Jan 01 00:00:00 2000 PST
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
@ -424,3 +424,7 @@ select age(timestamp 'infinity', timestamp 'infinity');
|
|||||||
select age(timestamp 'infinity', timestamp '-infinity');
|
select age(timestamp 'infinity', timestamp '-infinity');
|
||||||
select age(timestamp '-infinity', timestamp 'infinity');
|
select age(timestamp '-infinity', timestamp 'infinity');
|
||||||
select age(timestamp '-infinity', timestamp '-infinity');
|
select age(timestamp '-infinity', timestamp '-infinity');
|
||||||
|
|
||||||
|
-- test timestamp near POSTGRES_EPOCH_JDATE
|
||||||
|
select timestamp '1999-12-31 24:00:00';
|
||||||
|
select make_timestamp(1999, 12, 31, 24, 0, 0);
|
||||||
|
@ -668,3 +668,7 @@ SELECT age(timestamptz 'infinity', timestamptz 'infinity');
|
|||||||
SELECT age(timestamptz 'infinity', timestamptz '-infinity');
|
SELECT age(timestamptz 'infinity', timestamptz '-infinity');
|
||||||
SELECT age(timestamptz '-infinity', timestamptz 'infinity');
|
SELECT age(timestamptz '-infinity', timestamptz 'infinity');
|
||||||
SELECT age(timestamptz '-infinity', timestamptz '-infinity');
|
SELECT age(timestamptz '-infinity', timestamptz '-infinity');
|
||||||
|
|
||||||
|
-- test timestamp near POSTGRES_EPOCH_JDATE
|
||||||
|
select timestamptz '1999-12-31 24:00:00';
|
||||||
|
select make_timestamptz(1999, 12, 31, 24, 0, 0);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user