diff options
Diffstat (limited to 'src/backend/utils/adt/timestamp.c')
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 471 |
1 files changed, 347 insertions, 124 deletions
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 194861f19e3..b2bdbcab576 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -22,6 +22,7 @@ #include "access/xact.h" #include "catalog/pg_type.h" +#include "common/int.h" #include "common/int128.h" #include "funcapi.h" #include "libpq/pqformat.h" @@ -35,6 +36,7 @@ #include "utils/date.h" #include "utils/datetime.h" #include "utils/float.h" +#include "utils/numeric.h" /* * gcc's -ffast-math switch breaks routines that expect exact results from @@ -3991,8 +3993,8 @@ timestamptz_bin(PG_FUNCTION_ARGS) { Interval *stride = PG_GETARG_INTERVAL_P(0); TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1); - TimestampTz origin = PG_GETARG_TIMESTAMPTZ(2); - TimestampTz result, + TimestampTz origin = PG_GETARG_TIMESTAMPTZ(2); + TimestampTz result, stride_usecs, tm_diff, tm_delta; @@ -4597,15 +4599,15 @@ NonFiniteTimestampTzPart(int type, int unit, char *lowunits, } } -/* timestamp_part() +/* timestamp_part() and extract_timestamp() * Extract specified field from timestamp. */ -Datum -timestamp_part(PG_FUNCTION_ARGS) +static Datum +timestamp_part_common(PG_FUNCTION_ARGS, bool retnumeric) { text *units = PG_GETARG_TEXT_PP(0); Timestamp timestamp = PG_GETARG_TIMESTAMP(1); - float8 result; + int64 intresult; Timestamp epoch; int type, val; @@ -4624,11 +4626,28 @@ timestamp_part(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) { - result = NonFiniteTimestampTzPart(type, val, lowunits, - TIMESTAMP_IS_NOBEGIN(timestamp), - false); - if (result) - PG_RETURN_FLOAT8(result); + double r = NonFiniteTimestampTzPart(type, val, lowunits, + TIMESTAMP_IS_NOBEGIN(timestamp), + false); + + if (r) + { + if (retnumeric) + { + if (r < 0) + return DirectFunctionCall3(numeric_in, + CStringGetDatum("-Infinity"), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1)); + else if (r > 0) + return DirectFunctionCall3(numeric_in, + CStringGetDatum("Infinity"), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1)); + } + else + PG_RETURN_FLOAT8(r); + } else PG_RETURN_NULL(); } @@ -4643,47 +4662,61 @@ timestamp_part(PG_FUNCTION_ARGS) switch (val) { case DTK_MICROSEC: - result = tm->tm_sec * 1000000.0 + fsec; + intresult = tm->tm_sec * 1000000.0 + fsec; break; case DTK_MILLISEC: - result = tm->tm_sec * 1000.0 + fsec / 1000.0; + if (retnumeric) + /*--- + * tm->tm_sec * 1000 + fsec / 1000 + * = (tm->tm_sec * 1'000'000 + fsec) / 1000 + */ + PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * 1000000LL + fsec, 3)); + else + PG_RETURN_FLOAT8(tm->tm_sec * 1000.0 + fsec / 1000.0); break; case DTK_SECOND: - result = tm->tm_sec + fsec / 1000000.0; + if (retnumeric) + /*--- + * tm->tm_sec + fsec / 1'000'000 + * = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000 + */ + PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * 1000000LL + fsec, 6)); + else + PG_RETURN_FLOAT8(tm->tm_sec + fsec / 1000000.0); break; case DTK_MINUTE: - result = tm->tm_min; + intresult = tm->tm_min; break; case DTK_HOUR: - result = tm->tm_hour; + intresult = tm->tm_hour; break; case DTK_DAY: - result = tm->tm_mday; + intresult = tm->tm_mday; break; case DTK_MONTH: - result = tm->tm_mon; + intresult = tm->tm_mon; break; case DTK_QUARTER: - result = (tm->tm_mon - 1) / 3 + 1; + intresult = (tm->tm_mon - 1) / 3 + 1; break; case DTK_WEEK: - result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday); + intresult = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday); break; case DTK_YEAR: if (tm->tm_year > 0) - result = tm->tm_year; + intresult = tm->tm_year; else /* there is no year 0, just 1 BC and 1 AD */ - result = tm->tm_year - 1; + intresult = tm->tm_year - 1; break; case DTK_DECADE: @@ -4694,9 +4727,9 @@ timestamp_part(PG_FUNCTION_ARGS) * is 11 BC thru 2 BC... */ if (tm->tm_year >= 0) - result = tm->tm_year / 10; + intresult = tm->tm_year / 10; else - result = -((8 - (tm->tm_year - 1)) / 10); + intresult = -((8 - (tm->tm_year - 1)) / 10); break; case DTK_CENTURY: @@ -4708,43 +4741,50 @@ timestamp_part(PG_FUNCTION_ARGS) * ---- */ if (tm->tm_year > 0) - result = (tm->tm_year + 99) / 100; + intresult = (tm->tm_year + 99) / 100; else /* caution: C division may have negative remainder */ - result = -((99 - (tm->tm_year - 1)) / 100); + intresult = -((99 - (tm->tm_year - 1)) / 100); break; case DTK_MILLENNIUM: /* see comments above. */ if (tm->tm_year > 0) - result = (tm->tm_year + 999) / 1000; + intresult = (tm->tm_year + 999) / 1000; else - result = -((999 - (tm->tm_year - 1)) / 1000); + intresult = -((999 - (tm->tm_year - 1)) / 1000); break; case DTK_JULIAN: - result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday); - result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) + - tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY; + if (retnumeric) + PG_RETURN_NUMERIC(numeric_add_opt_error(int64_to_numeric(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)), + numeric_div_opt_error(int64_to_numeric(((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) * 1000000LL + fsec), + int64_to_numeric(SECS_PER_DAY * 1000000LL), + NULL), + NULL)); + else + PG_RETURN_FLOAT8(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + + ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) + + tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY); break; case DTK_ISOYEAR: - result = date2isoyear(tm->tm_year, tm->tm_mon, tm->tm_mday); + intresult = date2isoyear(tm->tm_year, tm->tm_mon, tm->tm_mday); /* Adjust BC years */ - if (result <= 0) - result -= 1; + if (intresult <= 0) + intresult -= 1; break; case DTK_DOW: case DTK_ISODOW: - result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); - if (val == DTK_ISODOW && result == 0) - result = 7; + intresult = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); + if (val == DTK_ISODOW && intresult == 0) + intresult = 7; break; case DTK_DOY: - result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - - date2j(tm->tm_year, 1, 1) + 1); + intresult = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + - date2j(tm->tm_year, 1, 1) + 1); break; case DTK_TZ: @@ -4755,7 +4795,7 @@ timestamp_part(PG_FUNCTION_ARGS) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("timestamp units \"%s\" not supported", lowunits))); - result = 0; + intresult = 0; } } else if (type == RESERV) @@ -4764,11 +4804,37 @@ timestamp_part(PG_FUNCTION_ARGS) { case DTK_EPOCH: epoch = SetEpochTimestamp(); - /* try to avoid precision loss in subtraction */ - if (timestamp < (PG_INT64_MAX + epoch)) - result = (timestamp - epoch) / 1000000.0; + /* (timestamp - epoch) / 1000000 */ + if (retnumeric) + { + Numeric result; + + if (timestamp < (PG_INT64_MAX + epoch)) + result = int64_div_fast_to_numeric(timestamp - epoch, 6); + else + { + result = numeric_div_opt_error(numeric_sub_opt_error(int64_to_numeric(timestamp), + int64_to_numeric(epoch), + NULL), + int64_to_numeric(1000000), + NULL); + result = DatumGetNumeric(DirectFunctionCall2(numeric_round, + NumericGetDatum(result), + Int32GetDatum(6))); + } + PG_RETURN_NUMERIC(result); + } else - result = ((float8) timestamp - epoch) / 1000000.0; + { + float8 result; + + /* try to avoid precision loss in subtraction */ + if (timestamp < (PG_INT64_MAX + epoch)) + result = (timestamp - epoch) / 1000000.0; + else + result = ((float8) timestamp - epoch) / 1000000.0; + PG_RETURN_FLOAT8(result); + } break; default: @@ -4776,7 +4842,7 @@ timestamp_part(PG_FUNCTION_ARGS) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("timestamp units \"%s\" not supported", lowunits))); - result = 0; + intresult = 0; } } @@ -4785,27 +4851,41 @@ timestamp_part(PG_FUNCTION_ARGS) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("timestamp units \"%s\" not recognized", lowunits))); - result = 0; + intresult = 0; } - PG_RETURN_FLOAT8(result); + if (retnumeric) + PG_RETURN_NUMERIC(int64_to_numeric(intresult)); + else + PG_RETURN_FLOAT8(intresult); +} + +Datum +timestamp_part(PG_FUNCTION_ARGS) +{ + return timestamp_part_common(fcinfo, false); } -/* timestamptz_part() +Datum +extract_timestamp(PG_FUNCTION_ARGS) +{ + return timestamp_part_common(fcinfo, true); +} + +/* timestamptz_part() and extract_timestamptz() * Extract specified field from timestamp with time zone. */ -Datum -timestamptz_part(PG_FUNCTION_ARGS) +static Datum +timestamptz_part_common(PG_FUNCTION_ARGS, bool retnumeric) { text *units = PG_GETARG_TEXT_PP(0); TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1); - float8 result; + int64 intresult; Timestamp epoch; int tz; int type, val; char *lowunits; - double dummy; fsec_t fsec; struct pg_tm tt, *tm = &tt; @@ -4820,11 +4900,28 @@ timestamptz_part(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) { - result = NonFiniteTimestampTzPart(type, val, lowunits, - TIMESTAMP_IS_NOBEGIN(timestamp), - true); - if (result) - PG_RETURN_FLOAT8(result); + double r = NonFiniteTimestampTzPart(type, val, lowunits, + TIMESTAMP_IS_NOBEGIN(timestamp), + true); + + if (r) + { + if (retnumeric) + { + if (r < 0) + return DirectFunctionCall3(numeric_in, + CStringGetDatum("-Infinity"), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1)); + else if (r > 0) + return DirectFunctionCall3(numeric_in, + CStringGetDatum("Infinity"), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1)); + } + else + PG_RETURN_FLOAT8(r); + } else PG_RETURN_NULL(); } @@ -4839,111 +4936,129 @@ timestamptz_part(PG_FUNCTION_ARGS) switch (val) { case DTK_TZ: - result = -tz; + intresult = -tz; break; case DTK_TZ_MINUTE: - result = -tz; - result /= SECS_PER_MINUTE; - FMODULO(result, dummy, (double) MINS_PER_HOUR); + intresult = (-tz / SECS_PER_MINUTE) % MINS_PER_HOUR; break; case DTK_TZ_HOUR: - dummy = -tz; - FMODULO(dummy, result, (double) SECS_PER_HOUR); + intresult = -tz / SECS_PER_HOUR; break; case DTK_MICROSEC: - result = tm->tm_sec * 1000000.0 + fsec; + intresult = tm->tm_sec * 1000000 + fsec; break; case DTK_MILLISEC: - result = tm->tm_sec * 1000.0 + fsec / 1000.0; + if (retnumeric) + /*--- + * tm->tm_sec * 1000 + fsec / 1000 + * = (tm->tm_sec * 1'000'000 + fsec) / 1000 + */ + PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * 1000000LL + fsec, 3)); + else + PG_RETURN_FLOAT8(tm->tm_sec * 1000.0 + fsec / 1000.0); break; case DTK_SECOND: - result = tm->tm_sec + fsec / 1000000.0; + if (retnumeric) + /*--- + * tm->tm_sec + fsec / 1'000'000 + * = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000 + */ + PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * 1000000LL + fsec, 6)); + else + PG_RETURN_FLOAT8(tm->tm_sec + fsec / 1000000.0); break; case DTK_MINUTE: - result = tm->tm_min; + intresult = tm->tm_min; break; case DTK_HOUR: - result = tm->tm_hour; + intresult = tm->tm_hour; break; case DTK_DAY: - result = tm->tm_mday; + intresult = tm->tm_mday; break; case DTK_MONTH: - result = tm->tm_mon; + intresult = tm->tm_mon; break; case DTK_QUARTER: - result = (tm->tm_mon - 1) / 3 + 1; + intresult = (tm->tm_mon - 1) / 3 + 1; break; case DTK_WEEK: - result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday); + intresult = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday); break; case DTK_YEAR: if (tm->tm_year > 0) - result = tm->tm_year; + intresult = tm->tm_year; else /* there is no year 0, just 1 BC and 1 AD */ - result = tm->tm_year - 1; + intresult = tm->tm_year - 1; break; case DTK_DECADE: /* see comments in timestamp_part */ if (tm->tm_year > 0) - result = tm->tm_year / 10; + intresult = tm->tm_year / 10; else - result = -((8 - (tm->tm_year - 1)) / 10); + intresult = -((8 - (tm->tm_year - 1)) / 10); break; case DTK_CENTURY: /* see comments in timestamp_part */ if (tm->tm_year > 0) - result = (tm->tm_year + 99) / 100; + intresult = (tm->tm_year + 99) / 100; else - result = -((99 - (tm->tm_year - 1)) / 100); + intresult = -((99 - (tm->tm_year - 1)) / 100); break; case DTK_MILLENNIUM: /* see comments in timestamp_part */ if (tm->tm_year > 0) - result = (tm->tm_year + 999) / 1000; + intresult = (tm->tm_year + 999) / 1000; else - result = -((999 - (tm->tm_year - 1)) / 1000); + intresult = -((999 - (tm->tm_year - 1)) / 1000); break; case DTK_JULIAN: - result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday); - result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) + - tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY; + if (retnumeric) + PG_RETURN_NUMERIC(numeric_add_opt_error(int64_to_numeric(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)), + numeric_div_opt_error(int64_to_numeric(((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) * 1000000LL + fsec), + int64_to_numeric(SECS_PER_DAY * 1000000LL), + NULL), + NULL)); + else + PG_RETURN_FLOAT8(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + + ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) + + tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY); break; case DTK_ISOYEAR: - result = date2isoyear(tm->tm_year, tm->tm_mon, tm->tm_mday); + intresult = date2isoyear(tm->tm_year, tm->tm_mon, tm->tm_mday); /* Adjust BC years */ - if (result <= 0) - result -= 1; + if (intresult <= 0) + intresult -= 1; break; case DTK_DOW: case DTK_ISODOW: - result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); - if (val == DTK_ISODOW && result == 0) - result = 7; + intresult = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); + if (val == DTK_ISODOW && intresult == 0) + intresult = 7; break; case DTK_DOY: - result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - - date2j(tm->tm_year, 1, 1) + 1); + intresult = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + - date2j(tm->tm_year, 1, 1) + 1); break; default: @@ -4951,7 +5066,7 @@ timestamptz_part(PG_FUNCTION_ARGS) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("timestamp with time zone units \"%s\" not supported", lowunits))); - result = 0; + intresult = 0; } } @@ -4961,11 +5076,37 @@ timestamptz_part(PG_FUNCTION_ARGS) { case DTK_EPOCH: epoch = SetEpochTimestamp(); - /* try to avoid precision loss in subtraction */ - if (timestamp < (PG_INT64_MAX + epoch)) - result = (timestamp - epoch) / 1000000.0; + /* (timestamp - epoch) / 1000000 */ + if (retnumeric) + { + Numeric result; + + if (timestamp < (PG_INT64_MAX + epoch)) + result = int64_div_fast_to_numeric(timestamp - epoch, 6); + else + { + result = numeric_div_opt_error(numeric_sub_opt_error(int64_to_numeric(timestamp), + int64_to_numeric(epoch), + NULL), + int64_to_numeric(1000000), + NULL); + result = DatumGetNumeric(DirectFunctionCall2(numeric_round, + NumericGetDatum(result), + Int32GetDatum(6))); + } + PG_RETURN_NUMERIC(result); + } else - result = ((float8) timestamp - epoch) / 1000000.0; + { + float8 result; + + /* try to avoid precision loss in subtraction */ + if (timestamp < (PG_INT64_MAX + epoch)) + result = (timestamp - epoch) / 1000000.0; + else + result = ((float8) timestamp - epoch) / 1000000.0; + PG_RETURN_FLOAT8(result); + } break; default: @@ -4973,7 +5114,7 @@ timestamptz_part(PG_FUNCTION_ARGS) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("timestamp with time zone units \"%s\" not supported", lowunits))); - result = 0; + intresult = 0; } } else @@ -4983,22 +5124,37 @@ timestamptz_part(PG_FUNCTION_ARGS) errmsg("timestamp with time zone units \"%s\" not recognized", lowunits))); - result = 0; + intresult = 0; } - PG_RETURN_FLOAT8(result); + if (retnumeric) + PG_RETURN_NUMERIC(int64_to_numeric(intresult)); + else + PG_RETURN_FLOAT8(intresult); +} + +Datum +timestamptz_part(PG_FUNCTION_ARGS) +{ + return timestamptz_part_common(fcinfo, false); +} + +Datum +extract_timestamptz(PG_FUNCTION_ARGS) +{ + return timestamptz_part_common(fcinfo, true); } -/* interval_part() +/* interval_part() and extract_interval() * Extract specified field from interval. */ -Datum -interval_part(PG_FUNCTION_ARGS) +static Datum +interval_part_common(PG_FUNCTION_ARGS, bool retnumeric) { text *units = PG_GETARG_TEXT_PP(0); Interval *interval = PG_GETARG_INTERVAL_P(1); - float8 result; + int64 intresult; int type, val; char *lowunits; @@ -5021,54 +5177,68 @@ interval_part(PG_FUNCTION_ARGS) switch (val) { case DTK_MICROSEC: - result = tm->tm_sec * 1000000.0 + fsec; + intresult = tm->tm_sec * 1000000 + fsec; break; case DTK_MILLISEC: - result = tm->tm_sec * 1000.0 + fsec / 1000.0; + if (retnumeric) + /*--- + * tm->tm_sec * 1000 + fsec / 1000 + * = (tm->tm_sec * 1'000'000 + fsec) / 1000 + */ + PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * 1000000LL + fsec, 3)); + else + PG_RETURN_FLOAT8(tm->tm_sec * 1000.0 + fsec / 1000.0); break; case DTK_SECOND: - result = tm->tm_sec + fsec / 1000000.0; + if (retnumeric) + /*--- + * tm->tm_sec + fsec / 1'000'000 + * = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000 + */ + PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * 1000000LL + fsec, 6)); + else + PG_RETURN_FLOAT8(tm->tm_sec + fsec / 1000000.0); break; case DTK_MINUTE: - result = tm->tm_min; + intresult = tm->tm_min; break; case DTK_HOUR: - result = tm->tm_hour; + intresult = tm->tm_hour; break; case DTK_DAY: - result = tm->tm_mday; + intresult = tm->tm_mday; break; case DTK_MONTH: - result = tm->tm_mon; + intresult = tm->tm_mon; break; case DTK_QUARTER: - result = (tm->tm_mon / 3) + 1; + intresult = (tm->tm_mon / 3) + 1; break; case DTK_YEAR: - result = tm->tm_year; + intresult = tm->tm_year; break; case DTK_DECADE: /* caution: C division may have negative remainder */ - result = tm->tm_year / 10; + intresult = tm->tm_year / 10; break; case DTK_CENTURY: /* caution: C division may have negative remainder */ - result = tm->tm_year / 100; + intresult = tm->tm_year / 100; break; case DTK_MILLENNIUM: /* caution: C division may have negative remainder */ - result = tm->tm_year / 1000; + intresult = tm->tm_year / 1000; break; default: @@ -5076,22 +5246,60 @@ interval_part(PG_FUNCTION_ARGS) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("interval units \"%s\" not supported", lowunits))); - result = 0; + intresult = 0; } - } else { elog(ERROR, "could not convert interval to tm"); - result = 0; + intresult = 0; } } else if (type == RESERV && val == DTK_EPOCH) { - result = interval->time / 1000000.0; - result += ((double) DAYS_PER_YEAR * SECS_PER_DAY) * (interval->month / MONTHS_PER_YEAR); - result += ((double) DAYS_PER_MONTH * SECS_PER_DAY) * (interval->month % MONTHS_PER_YEAR); - result += ((double) SECS_PER_DAY) * interval->day; + if (retnumeric) + { + Numeric result; + int64 secs_from_day_month; + int64 val; + + /* this always fits into int64 */ + secs_from_day_month = ((int64) DAYS_PER_YEAR * (interval->month / MONTHS_PER_YEAR) + + (int64) DAYS_PER_MONTH * (interval->month % MONTHS_PER_YEAR) + + interval->day) * SECS_PER_DAY; + + /*--- + * result = secs_from_day_month + interval->time / 1'000'000 + * = (secs_from_day_month * 1'000'000 + interval->time) / 1'000'000 + */ + + /* + * Try the computation inside int64; if it overflows, do it in + * numeric (slower). This overflow happens around 10^9 days, so + * not common in practice. + */ + if (!pg_mul_s64_overflow(secs_from_day_month, 1000000, &val) && + !pg_add_s64_overflow(val, interval->time, &val)) + result = int64_div_fast_to_numeric(val, 6); + else + result = + numeric_add_opt_error(int64_div_fast_to_numeric(interval->time, 6), + int64_to_numeric(secs_from_day_month), + NULL); + + PG_RETURN_NUMERIC(result); + } + else + { + float8 result; + + result = interval->time / 1000000.0; + result += ((double) DAYS_PER_YEAR * SECS_PER_DAY) * (interval->month / MONTHS_PER_YEAR); + result += ((double) DAYS_PER_MONTH * SECS_PER_DAY) * (interval->month % MONTHS_PER_YEAR); + result += ((double) SECS_PER_DAY) * interval->day; + + PG_RETURN_FLOAT8(result); + } } else { @@ -5099,10 +5307,25 @@ interval_part(PG_FUNCTION_ARGS) (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("interval units \"%s\" not recognized", lowunits))); - result = 0; + intresult = 0; } - PG_RETURN_FLOAT8(result); + if (retnumeric) + PG_RETURN_NUMERIC(int64_to_numeric(intresult)); + else + PG_RETURN_FLOAT8(intresult); +} + +Datum +interval_part(PG_FUNCTION_ARGS) +{ + return interval_part_common(fcinfo, false); +} + +Datum +extract_interval(PG_FUNCTION_ARGS) +{ + return interval_part_common(fcinfo, true); } |