diff options
Diffstat (limited to 'src/backend/utils/adt/timestamp.c')
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 111 |
1 files changed, 99 insertions, 12 deletions
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 68710928718..1525d2a1192 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -4311,6 +4311,83 @@ date2isoyearday(int year, int mon, int mday) return date2j(year, mon, mday) - isoweek2j(date2isoyear(year, mon, mday), 1) + 1; } +/* + * NonFiniteTimestampTzPart + * + * Used by timestamp_part and timestamptz_part when extracting from infinite + * timestamp[tz]. Returns +/-Infinity if that is the appropriate result, + * otherwise returns zero (which should be taken as meaning to return NULL). + * + * Errors thrown here for invalid units should exactly match those that + * would be thrown in the calling functions, else there will be unexpected + * discrepancies between finite- and infinite-input cases. + */ +static float8 +NonFiniteTimestampTzPart(int type, int unit, char *lowunits, + bool isNegative, bool isTz) +{ + if ((type != UNITS) && (type != RESERV)) + { + if (isTz) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("timestamp with time zone units \"%s\" not recognized", + lowunits))); + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("timestamp units \"%s\" not recognized", + lowunits))); + } + + switch (unit) + { + /* Oscillating units */ + case DTK_MICROSEC: + case DTK_MILLISEC: + case DTK_SECOND: + case DTK_MINUTE: + case DTK_HOUR: + case DTK_DAY: + case DTK_MONTH: + case DTK_QUARTER: + case DTK_WEEK: + case DTK_DOW: + case DTK_ISODOW: + case DTK_DOY: + case DTK_TZ: + case DTK_TZ_MINUTE: + case DTK_TZ_HOUR: + return 0.0; + + /* Monotonically-increasing units */ + case DTK_YEAR: + case DTK_DECADE: + case DTK_CENTURY: + case DTK_MILLENNIUM: + case DTK_JULIAN: + case DTK_ISOYEAR: + case DTK_EPOCH: + if (isNegative) + return -get_float8_infinity(); + else + return get_float8_infinity(); + + default: + if (isTz) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("timestamp with time zone units \"%s\" not supported", + lowunits))); + else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("timestamp units \"%s\" not supported", + lowunits))); + return 0.0; /* keep compiler quiet */ + } +} + /* timestamp_part() * Extract specified field from timestamp. */ @@ -4327,12 +4404,6 @@ timestamp_part(PG_FUNCTION_ARGS) struct pg_tm tt, *tm = &tt; - if (TIMESTAMP_NOT_FINITE(timestamp)) - { - result = 0; - PG_RETURN_FLOAT8(result); - } - lowunits = downcase_truncate_identifier(VARDATA_ANY(units), VARSIZE_ANY_EXHDR(units), false); @@ -4341,6 +4412,17 @@ timestamp_part(PG_FUNCTION_ARGS) if (type == UNKNOWN_FIELD) type = DecodeSpecial(0, lowunits, &val); + if (TIMESTAMP_NOT_FINITE(timestamp)) + { + result = NonFiniteTimestampTzPart(type, val, lowunits, + TIMESTAMP_IS_NOBEGIN(timestamp), + false); + if (result) + PG_RETURN_FLOAT8(result); + else + PG_RETURN_NULL(); + } + if (type == UNITS) { if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) @@ -4538,12 +4620,6 @@ timestamptz_part(PG_FUNCTION_ARGS) struct pg_tm tt, *tm = &tt; - if (TIMESTAMP_NOT_FINITE(timestamp)) - { - result = 0; - PG_RETURN_FLOAT8(result); - } - lowunits = downcase_truncate_identifier(VARDATA_ANY(units), VARSIZE_ANY_EXHDR(units), false); @@ -4552,6 +4628,17 @@ timestamptz_part(PG_FUNCTION_ARGS) if (type == UNKNOWN_FIELD) type = DecodeSpecial(0, lowunits, &val); + if (TIMESTAMP_NOT_FINITE(timestamp)) + { + result = NonFiniteTimestampTzPart(type, val, lowunits, + TIMESTAMP_IS_NOBEGIN(timestamp), + true); + if (result) + PG_RETURN_FLOAT8(result); + else + PG_RETURN_NULL(); + } + if (type == UNITS) { if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) |