Commit 5bc45062 authored by Alexander Korotkov's avatar Alexander Korotkov

Error suppression support for upcoming jsonpath .datetime() method

Add support of error suppression in some date and time manipulation functions
as it's required for jsonpath .datetime() method support.  This commit doesn't
use PG_TRY()/PG_CATCH() in order to implement that.  Instead, it provides
internal versions of date and time functions used, which support error
suppression.

Discussion: https://postgr.es/m/CAPpHfdsZgYEra_PeCLGNoXOWYx6iU-S3wF8aX0ObQUcZU%2B4XTw%40mail.gmail.com
Author: Alexander Korotkov, Nikita Glukhov
Reviewed-by: Anastasia Lubennikova, Peter Eisentraut
parent 66c74f8b
...@@ -550,13 +550,15 @@ date_mii(PG_FUNCTION_ARGS) ...@@ -550,13 +550,15 @@ date_mii(PG_FUNCTION_ARGS)
PG_RETURN_DATEADT(result); PG_RETURN_DATEADT(result);
} }
/* /*
* Internal routines for promoting date to timestamp and timestamp with * Promote date to timestamp.
* time zone *
* If 'have_error' is NULL, then errors are thrown, else '*have_error' is set
* and zero is returned.
*/ */
Timestamp
static Timestamp date2timestamp_opt_error(DateADT dateVal, bool *have_error)
date2timestamp(DateADT dateVal)
{ {
Timestamp result; Timestamp result;
...@@ -572,9 +574,19 @@ date2timestamp(DateADT dateVal) ...@@ -572,9 +574,19 @@ date2timestamp(DateADT dateVal)
* boundary need be checked for overflow. * boundary need be checked for overflow.
*/ */
if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE)) if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
ereport(ERROR, {
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), if (have_error)
errmsg("date out of range for timestamp"))); {
*have_error = true;
return (Timestamp) 0;
}
else
{
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range for timestamp")));
}
}
/* date is days since 2000, timestamp is microseconds since same... */ /* date is days since 2000, timestamp is microseconds since same... */
result = dateVal * USECS_PER_DAY; result = dateVal * USECS_PER_DAY;
...@@ -583,8 +595,23 @@ date2timestamp(DateADT dateVal) ...@@ -583,8 +595,23 @@ date2timestamp(DateADT dateVal)
return result; return result;
} }
/*
* Single-argument version of date2timestamp_opt_error().
*/
static TimestampTz static TimestampTz
date2timestamptz(DateADT dateVal) date2timestamp(DateADT dateVal)
{
return date2timestamp_opt_error(dateVal, NULL);
}
/*
* Promote date to timestamp with time zone.
*
* If 'have_error' is NULL, then errors are thrown, else '*have_error' is set
* and zero is returned.
*/
TimestampTz
date2timestamptz_opt_error(DateADT dateVal, bool *have_error)
{ {
TimestampTz result; TimestampTz result;
struct pg_tm tt, struct pg_tm tt,
...@@ -603,9 +630,19 @@ date2timestamptz(DateADT dateVal) ...@@ -603,9 +630,19 @@ date2timestamptz(DateADT dateVal)
* boundary need be checked for overflow. * boundary need be checked for overflow.
*/ */
if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE)) if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
ereport(ERROR, {
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), if (have_error)
errmsg("date out of range for timestamp"))); {
*have_error = true;
return (TimestampTz) 0;
}
else
{
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range for timestamp")));
}
}
j2date(dateVal + POSTGRES_EPOCH_JDATE, j2date(dateVal + POSTGRES_EPOCH_JDATE,
&(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday)); &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
...@@ -621,14 +658,33 @@ date2timestamptz(DateADT dateVal) ...@@ -621,14 +658,33 @@ date2timestamptz(DateADT dateVal)
* of time zone, check for allowed timestamp range after adding tz. * of time zone, check for allowed timestamp range after adding tz.
*/ */
if (!IS_VALID_TIMESTAMP(result)) if (!IS_VALID_TIMESTAMP(result))
ereport(ERROR, {
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), if (have_error)
errmsg("date out of range for timestamp"))); {
*have_error = true;
return (TimestampTz) 0;
}
else
{
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range for timestamp")));
}
}
} }
return result; return result;
} }
/*
* Single-argument version of date2timestamptz_opt_error().
*/
static TimestampTz
date2timestamptz(DateADT dateVal)
{
return date2timestamptz_opt_error(dateVal, NULL);
}
/* /*
* date2timestamp_no_overflow * date2timestamp_no_overflow
* *
......
This diff is collapsed.
...@@ -329,11 +329,11 @@ timestamp_scale(PG_FUNCTION_ARGS) ...@@ -329,11 +329,11 @@ timestamp_scale(PG_FUNCTION_ARGS)
} }
/* /*
* AdjustTimestampForTypmod --- round off a timestamp to suit given typmod * AdjustTimestampForTypmodError --- round off a timestamp to suit given typmod
* Works for either timestamp or timestamptz. * Works for either timestamp or timestamptz.
*/ */
void bool
AdjustTimestampForTypmod(Timestamp *time, int32 typmod) AdjustTimestampForTypmodError(Timestamp *time, int32 typmod, bool *error)
{ {
static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = { static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {
INT64CONST(1000000), INT64CONST(1000000),
...@@ -359,10 +359,18 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod) ...@@ -359,10 +359,18 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
&& (typmod != -1) && (typmod != MAX_TIMESTAMP_PRECISION)) && (typmod != -1) && (typmod != MAX_TIMESTAMP_PRECISION))
{ {
if (typmod < 0 || typmod > MAX_TIMESTAMP_PRECISION) if (typmod < 0 || typmod > MAX_TIMESTAMP_PRECISION)
{
if (error)
{
*error = true;
return false;
}
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("timestamp(%d) precision must be between %d and %d", errmsg("timestamp(%d) precision must be between %d and %d",
typmod, 0, MAX_TIMESTAMP_PRECISION))); typmod, 0, MAX_TIMESTAMP_PRECISION)));
}
if (*time >= INT64CONST(0)) if (*time >= INT64CONST(0))
{ {
...@@ -375,8 +383,15 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod) ...@@ -375,8 +383,15 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
* TimestampScales[typmod]); * TimestampScales[typmod]);
} }
} }
return true;
} }
void
AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
{
(void) AdjustTimestampForTypmodError(time, typmod, NULL);
}
/* timestamptz_in() /* timestamptz_in()
* Convert a string to internal form. * Convert a string to internal form.
...@@ -5172,8 +5187,15 @@ timestamp_timestamptz(PG_FUNCTION_ARGS) ...@@ -5172,8 +5187,15 @@ timestamp_timestamptz(PG_FUNCTION_ARGS)
PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp)); PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp));
} }
static TimestampTz /*
timestamp2timestamptz(Timestamp timestamp) * Convert timestamp to timestamp with time zone.
*
* If 'have_error' is NULL, then errors are thrown, else '*have_error' is set
* and zero is returned.
*/
TimestampTz
timestamp2timestamptz_opt_error(Timestamp timestamp, bool *have_error)
{ {
TimestampTz result; TimestampTz result;
struct pg_tm tt, struct pg_tm tt,
...@@ -5182,23 +5204,33 @@ timestamp2timestamptz(Timestamp timestamp) ...@@ -5182,23 +5204,33 @@ timestamp2timestamptz(Timestamp timestamp)
int tz; int tz;
if (TIMESTAMP_NOT_FINITE(timestamp)) if (TIMESTAMP_NOT_FINITE(timestamp))
result = timestamp; return timestamp;
else
{
if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
if (!timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL))
{
tz = DetermineTimeZoneOffset(tm, session_timezone); tz = DetermineTimeZoneOffset(tm, session_timezone);
if (tm2timestamp(tm, fsec, &tz, &result) != 0) if (!tm2timestamp(tm, fsec, &tz, &result))
ereport(ERROR, return result;
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
} }
return result; if (have_error)
*have_error = true;
else
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
return 0;
}
/*
* Single-argument version of timestamp2timestamptz_opt_error().
*/
static TimestampTz
timestamp2timestamptz(Timestamp timestamp)
{
return timestamp2timestamptz_opt_error(timestamp, NULL);
} }
/* timestamptz_timestamp() /* timestamptz_timestamp()
......
...@@ -70,6 +70,8 @@ typedef struct ...@@ -70,6 +70,8 @@ typedef struct
/* date.c */ /* date.c */
extern int32 anytime_typmod_check(bool istz, int32 typmod); extern int32 anytime_typmod_check(bool istz, int32 typmod);
extern double date2timestamp_no_overflow(DateADT dateVal); extern double date2timestamp_no_overflow(DateADT dateVal);
extern Timestamp date2timestamp_opt_error(DateADT dateVal, bool *have_error);
extern TimestampTz date2timestamptz_opt_error(DateADT dateVal, bool *have_error);
extern void EncodeSpecialDate(DateADT dt, char *str); extern void EncodeSpecialDate(DateADT dt, char *str);
extern DateADT GetSQLCurrentDate(void); extern DateADT GetSQLCurrentDate(void);
extern TimeTzADT *GetSQLCurrentTime(int32 typmod); extern TimeTzADT *GetSQLCurrentTime(int32 typmod);
......
...@@ -337,5 +337,7 @@ extern TimeZoneAbbrevTable *ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs, ...@@ -337,5 +337,7 @@ extern TimeZoneAbbrevTable *ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs,
extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl); extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl);
extern void AdjustTimestampForTypmod(Timestamp *time, int32 typmod); extern void AdjustTimestampForTypmod(Timestamp *time, int32 typmod);
extern bool AdjustTimestampForTypmodError(Timestamp *time, int32 typmod,
bool *error);
#endif /* DATETIME_H */ #endif /* DATETIME_H */
...@@ -27,6 +27,7 @@ extern char *asc_toupper(const char *buff, size_t nbytes); ...@@ -27,6 +27,7 @@ extern char *asc_toupper(const char *buff, size_t nbytes);
extern char *asc_initcap(const char *buff, size_t nbytes); extern char *asc_initcap(const char *buff, size_t nbytes);
extern Datum parse_datetime(text *date_txt, text *fmt, bool std, extern Datum parse_datetime(text *date_txt, text *fmt, bool std,
Oid *typid, int32 *typmod, int *tz); Oid *typid, int32 *typmod, int *tz,
bool *have_error);
#endif #endif
...@@ -97,6 +97,9 @@ extern int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2); ...@@ -97,6 +97,9 @@ extern int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2);
/* timestamp comparison works for timestamptz also */ /* timestamp comparison works for timestamptz also */
#define timestamptz_cmp_internal(dt1,dt2) timestamp_cmp_internal(dt1, dt2) #define timestamptz_cmp_internal(dt1,dt2) timestamp_cmp_internal(dt1, dt2)
extern TimestampTz timestamp2timestamptz_opt_error(Timestamp timestamp,
bool *have_error);
extern int isoweek2j(int year, int week); extern int isoweek2j(int year, int week);
extern void isoweek2date(int woy, int *year, int *mon, int *mday); extern void isoweek2date(int woy, int *year, int *mon, int *mday);
extern void isoweekdate2date(int isoweek, int wday, int *year, int *mon, int *mday); extern void isoweekdate2date(int isoweek, int wday, int *year, int *mon, int *mday);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment