On 18.03.21 09:28, Peter Eisentraut wrote:
Which leads me to: After retesting this now, with a new machine, the
performance of the numeric implementation is brutal compared to the
float implementation, for cases where we need numeric division, which is
milliseconds, seconds, and epoch. In the first two cases, I imagine we
could rewrite this a bit to avoid a lot of the numeric work, but for the
epoch case (which is what started this thread), there isn't enough space
in int64 to make this work. Perhaps int128 could be pressed into
service, optionally. I think it would also help if we cracked open the
numeric APIs a bit to avoid all the repeated unpacking and packing for
each step.
So I think we need to do a bit more thinking and work here, meaning it
will have to be postponed.
Well, I had an idea that I put to work. In most of these cases where we
need division, we divide an integer by a power of 10. That can be done
with numeric very quickly by just shifting the weight and scale around.
So I wrote a function that does that specifically (look for
int64_div_fast_to_numeric()). With that, the slow cases I mentioned now
have the same performance as the other cases that didn't have any
numeric division. You just get the overhead for constructing and
passing around a numeric instead of a double, which can't be avoided.
So here is an intermediate patch that does this. I haven't gotten rid
of all numeric_div_opt_error() calls yet, but if this seems acceptable,
I can work on the remaining ones.
From 801460c46ddf7273d2c49a9af3460f2f5614599d Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Fri, 19 Mar 2021 20:27:15 +0100
Subject: [PATCH v5] Change return type of EXTRACT to numeric
The previous implementation of EXTRACT mapped internally to
date_part(), which returned type double precision (since it was
implemented long before the numeric type existed). This can lead to
imprecise output in some cases, so returning numeric would be
preferrable. Changing the return type of an existing function is a
bit risky, so instead we do the following: We implement a new set of
functions, which are now called "extract", in parallel to the existing
date_part functions. They work the same way internally but use
numeric instead of float8. The EXTRACT construct is now mapped by the
parser to these new extract functions. That way, dumps of views
etc. from old versions (which would use date_part) continue to work
unchanged, but new uses will map to the new extract functions.
Additionally, the reverse compilation of EXTRACT now reproduces the
original syntax, using the new mechanism introduced in
40c24bfef92530bd846e111c1742c2a54441c62c.
The following minor changes of behavior result from the new
implementation:
- The column name from an isolated EXTRACT call is now "extract"
instead of "date_part".
- Extract from date now rejects inappropriate field names such as
HOUR. It was previously mapped internally to extract from
timestamp, so it would silently accept everything appropriate for
timestamp.
- Return values when extracting fields with possibly fractional
values, such as second and epoch, now have the full scale that the
value has internally (so, for example, '1.000000' instead of just
'1').
Discussion:
https://www.postgresql.org/message-id/flat/42b73d2d-da12-ba9f-570a-420e0cce1...@phystech.edu
---
doc/src/sgml/func.sgml | 10 +-
src/backend/parser/gram.y | 2 +-
src/backend/utils/adt/date.c | 370 +++++++++++
src/backend/utils/adt/numeric.c | 56 ++
src/backend/utils/adt/ruleutils.c | 21 +
src/backend/utils/adt/timestamp.c | 670 +++++++++++++++++++-
src/include/catalog/pg_proc.dat | 18 +
src/include/utils/numeric.h | 1 +
src/test/regress/expected/create_view.out | 2 +-
src/test/regress/expected/date.out | 482 +++++++-------
src/test/regress/expected/interval.out | 72 +--
src/test/regress/expected/psql_crosstab.out | 12 +-
src/test/regress/expected/time.out | 24 +-
src/test/regress/expected/timetz.out | 46 +-
src/test/regress/sql/date.sql | 6 +-
src/test/regress/sql/timetz.sql | 4 +-
16 files changed, 1431 insertions(+), 365 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 9492a3c6b9..0383cf3db9 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -8776,7 +8776,7 @@ <title>Date/Time Functions</title>
<primary>extract</primary>
</indexterm>
<function>extract</function> ( <parameter>field</parameter>
<literal>from</literal> <type>timestamp</type> )
- <returnvalue>double precision</returnvalue>
+ <returnvalue>numeric</returnvalue>
</para>
<para>
Get timestamp subfield; see <xref
linkend="functions-datetime-extract"/>
@@ -8790,7 +8790,7 @@ <title>Date/Time Functions</title>
<row>
<entry role="func_table_entry"><para role="func_signature">
<function>extract</function> ( <parameter>field</parameter>
<literal>from</literal> <type>interval</type> )
- <returnvalue>double precision</returnvalue>
+ <returnvalue>numeric</returnvalue>
</para>
<para>
Get interval subfield; see <xref
linkend="functions-datetime-extract"/>
@@ -9305,7 +9305,7 @@ <title><function>EXTRACT</function>,
<function>date_part</function></title>
well.) <replaceable>field</replaceable> is an identifier or
string that selects what field to extract from the source value.
The <function>extract</function> function returns values of type
- <type>double precision</type>.
+ <type>numeric</type>.
The following are valid field names:
<!-- alphabetical -->
@@ -9729,6 +9729,10 @@ <title><function>EXTRACT</function>,
<function>date_part</function></title>
be a string value, not a name. The valid field names for
<function>date_part</function> are the same as for
<function>extract</function>.
+ For historical reasons, the <function>date_part</function> function
+ returns values of type <type>double precision</type>. This can result in
+ a loss of precision in certain uses. Using <function>extract</function>
+ is recommended instead.
</para>
<screen>
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index fd07e7107d..4a23593613 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -13910,7 +13910,7 @@ func_expr_common_subexpr:
{ $$ = makeTypeCast($3, $5, @1); }
| EXTRACT '(' extract_list ')'
{
- $$ = (Node *)
makeFuncCall(SystemFuncName("date_part"),
+ $$ = (Node *)
makeFuncCall(SystemFuncName("extract"),
$3,
COERCE_SQL_SYNTAX,
@1);
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 68d99a5099..9da03b0ef6 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -31,6 +31,7 @@
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datetime.h"
+#include "utils/numeric.h"
#include "utils/sortsupport.h"
/*
@@ -1063,6 +1064,180 @@ in_range_date_interval(PG_FUNCTION_ARGS)
}
+/* extract_date()
+ * Extract specified field from date type.
+ */
+Datum
+extract_date(PG_FUNCTION_ARGS)
+{
+ text *units = PG_GETARG_TEXT_PP(0);
+ DateADT date = PG_GETARG_DATEADT(1);
+ int64 result;
+ int type,
+ val;
+ char *lowunits;
+ int year, mon, mday;
+
+ lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
+
VARSIZE_ANY_EXHDR(units),
+
false);
+
+ type = DecodeUnits(0, lowunits, &val);
+ if (type == UNKNOWN_FIELD)
+ type = DecodeSpecial(0, lowunits, &val);
+
+ if (DATE_NOT_FINITE(date) && (type == UNITS || type == RESERV))
+ {
+ switch (val)
+ {
+ /* Oscillating units */
+ case DTK_DAY:
+ case DTK_MONTH:
+ case DTK_QUARTER:
+ case DTK_WEEK:
+ case DTK_DOW:
+ case DTK_ISODOW:
+ case DTK_DOY:
+ PG_RETURN_NULL();
+ break;
+
+ /* 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 (DATE_IS_NOBEGIN(date))
+
PG_RETURN_NUMERIC(DatumGetNumeric(DirectFunctionCall3(numeric_in,
+
CStringGetDatum("-Infinity"),
+
ObjectIdGetDatum(InvalidOid),
+
Int32GetDatum(-1))));
+ else
+
PG_RETURN_NUMERIC(DatumGetNumeric(DirectFunctionCall3(numeric_in,
+
CStringGetDatum("Infinity"),
+
ObjectIdGetDatum(InvalidOid),
+
Int32GetDatum(-1))));
+ default:
+ ereport(ERROR,
+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("date units \"%s\" not
supported",
+ lowunits)));
+ }
+ }
+ else if (type == UNITS)
+ {
+ j2date(date + POSTGRES_EPOCH_JDATE, &year, &mon, &mday);
+
+ switch (val)
+ {
+ case DTK_DAY:
+ result = mday;
+ break;
+
+ case DTK_MONTH:
+ result = mon;
+ break;
+
+ case DTK_QUARTER:
+ result = (mon - 1) / 3 + 1;
+ break;
+
+ case DTK_WEEK:
+ result = date2isoweek(year, mon, mday);
+ break;
+
+ case DTK_YEAR:
+ if (year > 0)
+ result = year;
+ else
+ /* there is no year 0, just 1 BC and 1
AD */
+ result = year - 1;
+ break;
+
+ case DTK_DECADE:
+ /* see comments in timestamp_part */
+ if (year >= 0)
+ result = year / 10;
+ else
+ result = -((8 - (year - 1)) / 10);
+ break;
+
+ case DTK_CENTURY:
+ /* see comments in timestamp_part */
+ if (year > 0)
+ result = (year + 99) / 100;
+ else
+ result = -((99 - (year - 1)) / 100);
+ break;
+
+ case DTK_MILLENNIUM:
+ /* see comments in timestamp_part */
+ if (year > 0)
+ result = (year + 999) / 1000;
+ else
+ result = -((999 - (year - 1)) / 1000);
+ break;
+
+ case DTK_JULIAN:
+ result = date + POSTGRES_EPOCH_JDATE;
+ break;
+
+ case DTK_ISOYEAR:
+ result = date2isoyear(year, mon, mday);
+ /* Adjust BC years */
+ if (result <= 0)
+ result -= 1;
+ break;
+
+ case DTK_DOW:
+ case DTK_ISODOW:
+ result = j2day(date + POSTGRES_EPOCH_JDATE);
+ if (val == DTK_ISODOW && result == 0)
+ result = 7;
+ break;
+
+ case DTK_DOY:
+ result = date2j(year, mon, mday) - date2j(year,
1, 1) + 1;
+ break;
+
+ default:
+ ereport(ERROR,
+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("date units \"%s\" not
supported",
+ lowunits)));
+ result = 0;
+ }
+ }
+ else if (type == RESERV)
+ {
+ switch (val)
+ {
+ case DTK_EPOCH:
+ result = ((int64) date + POSTGRES_EPOCH_JDATE -
UNIX_EPOCH_JDATE) * SECS_PER_DAY;
+ break;
+
+ default:
+ ereport(ERROR,
+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("date units \"%s\" not
supported",
+ lowunits)));
+ result = 0;
+ }
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("date units \"%s\" not recognized",
lowunits)));
+ result = 0;
+ }
+
+ PG_RETURN_NUMERIC(int64_to_numeric(result));
+}
+
+
/* Add an interval to a date, giving a new date.
* Must handle both positive and negative intervals.
*
@@ -2036,6 +2211,97 @@ time_part(PG_FUNCTION_ARGS)
}
+/* extract_time()
+ * Extract specified field from time type.
+ */
+Datum
+extract_time(PG_FUNCTION_ARGS)
+{
+ text *units = PG_GETARG_TEXT_PP(0);
+ TimeADT time = PG_GETARG_TIMEADT(1);
+ Numeric result;
+ int type,
+ val;
+ char *lowunits;
+
+ lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
+
VARSIZE_ANY_EXHDR(units),
+
false);
+
+ type = DecodeUnits(0, lowunits, &val);
+ if (type == UNKNOWN_FIELD)
+ type = DecodeSpecial(0, lowunits, &val);
+
+ if (type == UNITS)
+ {
+ fsec_t fsec;
+ struct pg_tm tt,
+ *tm = &tt;
+
+ time2tm(time, tm, &fsec);
+
+ switch (val)
+ {
+ case DTK_MICROSEC:
+ result = int64_to_numeric(tm->tm_sec * 1000000
+ fsec);
+ break;
+
+ case DTK_MILLISEC:
+ /* tm->tm_sec * 1000 + fsec / 1000
+ = (tm->tm_sec * 1'000'000 + fsec) / 1000 */
+ result = int64_div_fast_to_numeric(tm->tm_sec *
1000000LL + fsec, 1000);
+ break;
+
+ case DTK_SECOND:
+ /* tm->tm_sec + fsec / 1'000'000
+ = (tm->tm_sec * 1'000'000 + fsec) /
1'000'000 */
+ result = int64_div_fast_to_numeric(tm->tm_sec *
1000000LL + fsec, 1000000);
+ break;
+
+ case DTK_MINUTE:
+ result = int64_to_numeric(tm->tm_min);
+ break;
+
+ case DTK_HOUR:
+ result = int64_to_numeric(tm->tm_hour);
+ break;
+
+ case DTK_TZ:
+ case DTK_TZ_MINUTE:
+ case DTK_TZ_HOUR:
+ case DTK_DAY:
+ case DTK_MONTH:
+ case DTK_QUARTER:
+ case DTK_YEAR:
+ case DTK_DECADE:
+ case DTK_CENTURY:
+ case DTK_MILLENNIUM:
+ case DTK_ISOYEAR:
+ default:
+ ereport(ERROR,
+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"time\" units \"%s\"
not recognized",
+ lowunits)));
+ result = 0;
+ }
+ }
+ else if (type == RESERV && val == DTK_EPOCH)
+ {
+ result = int64_div_fast_to_numeric(time, 1000000);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"time\" units \"%s\" not recognized",
+ lowunits)));
+ result = 0;
+ }
+
+ PG_RETURN_NUMERIC(result);
+}
+
+
/*****************************************************************************
* Time With Time Zone ADT
*****************************************************************************/
@@ -2785,6 +3051,110 @@ timetz_part(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+
+/* timetz_part()
+ * Extract specified field from time type.
+ */
+Datum
+extract_timetz(PG_FUNCTION_ARGS)
+{
+ text *units = PG_GETARG_TEXT_PP(0);
+ TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
+ Numeric result;
+ int type,
+ val;
+ char *lowunits;
+
+ lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
+
VARSIZE_ANY_EXHDR(units),
+
false);
+
+ type = DecodeUnits(0, lowunits, &val);
+ if (type == UNKNOWN_FIELD)
+ type = DecodeSpecial(0, lowunits, &val);
+
+ if (type == UNITS)
+ {
+ int tz;
+ fsec_t fsec;
+ struct pg_tm tt,
+ *tm = &tt;
+
+ timetz2tm(time, tm, &fsec, &tz);
+
+ switch (val)
+ {
+ case DTK_TZ:
+ result = int64_to_numeric(-tz);
+ break;
+
+ case DTK_TZ_MINUTE:
+ /* trunc(-tz / 60) % 60 */
+ result = int64_to_numeric(- (tz - tz /
SECS_PER_HOUR * SECS_PER_HOUR) / SECS_PER_MINUTE);
+ break;
+
+ case DTK_TZ_HOUR:
+ result = int64_to_numeric(- tz / SECS_PER_HOUR);
+ break;
+
+ case DTK_MICROSEC:
+ result = int64_to_numeric(tm->tm_sec * 1000000
+ fsec);
+ break;
+
+ case DTK_MILLISEC:
+ /* tm->tm_sec * 1000 + fsec / 1000
+ = (tm->tm_sec * 1'000'000 + fsec) / 1000 */
+ result = int64_div_fast_to_numeric(tm->tm_sec *
1000000LL + fsec, 1000);
+ break;
+
+ case DTK_SECOND:
+ /* tm->tm_sec + fsec / 1'000'000
+ = (tm->tm_sec * 1'000'000 + fsec) /
1'000'000 */
+ result = int64_div_fast_to_numeric(tm->tm_sec *
1000000LL + fsec, 1000000);
+ break;
+
+ case DTK_MINUTE:
+ result = int64_to_numeric(tm->tm_min);
+ break;
+
+ case DTK_HOUR:
+ result = int64_to_numeric(tm->tm_hour);
+ break;
+
+ case DTK_DAY:
+ case DTK_MONTH:
+ case DTK_QUARTER:
+ case DTK_YEAR:
+ case DTK_DECADE:
+ case DTK_CENTURY:
+ case DTK_MILLENNIUM:
+ default:
+ ereport(ERROR,
+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"time with time
zone\" units \"%s\" not recognized",
+ lowunits)));
+ result = 0;
+ }
+ }
+ else if (type == RESERV && val == DTK_EPOCH)
+ {
+ /* time->time / 1000000.0 + time->zone
+ = (time->time + time->zone * 1000000) / 1000000.0 */
+ result = int64_div_fast_to_numeric(time->time + time->zone *
1000000LL, 1000000);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"time with time zone\" units \"%s\"
not recognized",
+ lowunits)));
+ result = 0;
+ }
+
+ PG_RETURN_NUMERIC(result);
+}
+
+
/* timetz_zone()
* Encode time with time zone type with specified time zone.
* Applies DST rules as of the current date.
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 682200f636..055b02efaf 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -4092,6 +4092,62 @@ int64_to_numeric(int64 val)
return res;
}
+/*
+ * Convert val1/val2 to numeric. val2 must be a power of 10. This is much
+ * faster than normal numeric division.
+ */
+Numeric
+int64_div_fast_to_numeric(int64 val1, int64 val2)
+{
+ Numeric res;
+ NumericVar result;
+ int64 x;
+ int n;
+ int s;
+
+ x = val2;
+ n = 0;
+ s = 0;
+
+ /* count how much to decrease the weight by */
+ while (x >= NBASE)
+ {
+ x /= NBASE;
+ n++;
+ }
+
+ /*
+ * If there is anything left, multiply the dividend by what's left, then
+ * shift the weight by one more. Also remember by how much we
multiplied
+ * so we can adjust the scale below.
+ */
+ if (x > 1)
+ {
+ if (unlikely(pg_mul_s64_overflow(val1, NBASE/x, &val1)))
+ elog(ERROR, "overflow");
+ n++;
+ while (x > 1)
+ {
+ Assert((x / 10) * 10 == x); /* must be power of 10
*/
+ x /= 10;
+ s++;
+ }
+ }
+
+ init_var(&result);
+
+ int64_to_numericvar(val1, &result);
+
+ result.weight -= n;
+ result.dscale += n * DEC_DIGITS - (DEC_DIGITS - s);
+
+ res = make_result(&result);
+
+ free_var(&result);
+
+ return res;
+}
+
Datum
int4_numeric(PG_FUNCTION_ARGS)
{
diff --git a/src/backend/utils/adt/ruleutils.c
b/src/backend/utils/adt/ruleutils.c
index f0de2a25c9..8a6e265fc9 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -9617,6 +9617,27 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context
*context)
appendStringInfoString(buf, "))");
return true;
+ case F_EXTRACT_TEXT_DATE:
+ case F_EXTRACT_TEXT_TIME:
+ case F_EXTRACT_TEXT_TIMETZ:
+ case F_EXTRACT_TEXT_TIMESTAMP:
+ case F_EXTRACT_TEXT_TIMESTAMPTZ:
+ case F_EXTRACT_TEXT_INTERVAL:
+ /* EXTRACT (x FROM y) */
+ appendStringInfoString(buf, "EXTRACT(");
+ {
+ Const *con = (Const *)
linitial(expr->args);
+
+ Assert(IsA(con, Const) &&
+ con->consttype == TEXTOID &&
+ !con->constisnull);
+ appendStringInfoString(buf,
TextDatumGetCString(con->constvalue));
+ }
+ appendStringInfoString(buf, " FROM ");
+ get_rule_expr((Node *) lsecond(expr->args), context,
false);
+ appendStringInfoChar(buf, ')');
+ return true;
+
case F_IS_NORMALIZED:
/* IS xxx NORMALIZED */
appendStringInfoString(buf, "((");
diff --git a/src/backend/utils/adt/timestamp.c
b/src/backend/utils/adt/timestamp.c
index 0b1f95a5b4..d73b6353f9 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -35,6 +35,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
@@ -4447,7 +4448,7 @@ date2isoyearday(int year, int mon, int mday)
}
/*
- * NonFiniteTimestampTzPart
+ * NonFiniteTimestampTzPart_float8
*
* Used by timestamp_part and timestamptz_part when extracting from
infinite
* timestamp[tz]. Returns +/-Infinity if that is the appropriate result,
@@ -4458,8 +4459,8 @@ date2isoyearday(int year, int mon, int mday)
* discrepancies between finite- and infinite-input cases.
*/
static float8
-NonFiniteTimestampTzPart(int type, int unit, char *lowunits,
- bool isNegative, bool isTz)
+NonFiniteTimestampTzPart_float8(int type, int unit, char *lowunits,
+ bool
isNegative, bool isTz)
{
if ((type != UNITS) && (type != RESERV))
{
@@ -4523,6 +4524,89 @@ NonFiniteTimestampTzPart(int type, int unit, char
*lowunits,
}
}
+/*
+ * NonFiniteTimestampTzPart_numeric
+ *
+ * Used by extract_timestamp and extract_timestamptz when extracting from
infinite
+ * timestamp[tz]. Returns +/-Infinity if that is the appropriate result,
+ * otherwise returns NULL (which should be taken as meaning to return SQL
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 Numeric
+NonFiniteTimestampTzPart_numeric(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 NULL;
+
+ /* 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
DatumGetNumeric(DirectFunctionCall3(numeric_in,
+
CStringGetDatum("-Infinity"),
+
ObjectIdGetDatum(InvalidOid),
+
Int32GetDatum(-1)));
+ else
+ return
DatumGetNumeric(DirectFunctionCall3(numeric_in,
+
CStringGetDatum("Infinity"),
+
ObjectIdGetDatum(InvalidOid),
+
Int32GetDatum(-1)));
+
+ 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 NULL; /* keep compiler quiet */
+ }
+}
+
/* timestamp_part()
* Extract specified field from timestamp.
*/
@@ -4550,9 +4634,9 @@ timestamp_part(PG_FUNCTION_ARGS)
if (TIMESTAMP_NOT_FINITE(timestamp))
{
- result = NonFiniteTimestampTzPart(type, val, lowunits,
-
TIMESTAMP_IS_NOBEGIN(timestamp),
-
false);
+ result = NonFiniteTimestampTzPart_float8(type, val, lowunits,
+
TIMESTAMP_IS_NOBEGIN(timestamp),
+
false);
if (result)
PG_RETURN_FLOAT8(result);
else
@@ -4717,6 +4801,225 @@ timestamp_part(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+/* extract_timestamp()
+ * Extract specified field from timestamp.
+ */
+Datum
+extract_timestamp(PG_FUNCTION_ARGS)
+{
+ text *units = PG_GETARG_TEXT_PP(0);
+ Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
+ Numeric result;
+ Timestamp epoch;
+ int type,
+ val;
+ char *lowunits;
+ fsec_t fsec;
+ struct pg_tm tt,
+ *tm = &tt;
+
+ lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
+
VARSIZE_ANY_EXHDR(units),
+
false);
+
+ type = DecodeUnits(0, lowunits, &val);
+ if (type == UNKNOWN_FIELD)
+ type = DecodeSpecial(0, lowunits, &val);
+
+ if (TIMESTAMP_NOT_FINITE(timestamp))
+ {
+ result = NonFiniteTimestampTzPart_numeric(type, val, lowunits,
+
TIMESTAMP_IS_NOBEGIN(timestamp),
+
false);
+ if (result)
+ PG_RETURN_NUMERIC(result);
+ else
+ PG_RETURN_NULL();
+ }
+
+ if (type == UNITS)
+ {
+ if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
+ ereport(ERROR,
+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range")));
+
+ switch (val)
+ {
+ case DTK_MICROSEC:
+ result = int64_to_numeric(tm->tm_sec * 1000000
+ fsec);
+ break;
+
+ case DTK_MILLISEC:
+ /* tm->tm_sec * 1000 + fsec / 1000
+ = (tm->tm_sec * 1'000'000 + fsec) / 1000 */
+ result = int64_div_fast_to_numeric(tm->tm_sec *
1000000LL + fsec, 1000);
+ break;
+
+ case DTK_SECOND:
+ /* tm->tm_sec + fsec / 1'000'000
+ = (tm->tm_sec * 1'000'000 + fsec) /
1'000'000 */
+ result = int64_div_fast_to_numeric(tm->tm_sec *
1000000LL + fsec, 1000000);
+ break;
+
+ case DTK_MINUTE:
+ result = int64_to_numeric(tm->tm_min);
+ break;
+
+ case DTK_HOUR:
+ result = int64_to_numeric(tm->tm_hour);
+ break;
+
+ case DTK_DAY:
+ result = int64_to_numeric(tm->tm_mday);
+ break;
+
+ case DTK_MONTH:
+ result = int64_to_numeric(tm->tm_mon);
+ break;
+
+ case DTK_QUARTER:
+ result = int64_to_numeric((tm->tm_mon - 1) / 3
+ 1);
+ break;
+
+ case DTK_WEEK:
+ result =
int64_to_numeric(date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday));
+ break;
+
+ case DTK_YEAR:
+ if (tm->tm_year > 0)
+ result = int64_to_numeric(tm->tm_year);
+ else
+ /* there is no year 0, just 1 BC and 1
AD */
+ result = int64_to_numeric(tm->tm_year -
1);
+ break;
+
+ case DTK_DECADE:
+
+ /*
+ * what is a decade wrt dates? let us assume
that decade 199
+ * is 1990 thru 1999... decade 0 starts on year
1 BC, and -1
+ * is 11 BC thru 2 BC...
+ */
+ if (tm->tm_year >= 0)
+ result = int64_to_numeric(tm->tm_year /
10);
+ else
+ result = int64_to_numeric(-((8 -
(tm->tm_year - 1)) / 10));
+ break;
+
+ case DTK_CENTURY:
+
+ /* ----
+ * centuries AD, c>0: year in [ (c-1)* 100 + 1
: c*100 ]
+ * centuries BC, c<0: year in [ c*100 : (c+1) *
100 - 1]
+ * there is no number 0 century.
+ * ----
+ */
+ if (tm->tm_year > 0)
+ result = int64_to_numeric((tm->tm_year
+ 99) / 100);
+ else
+ /* caution: C division may have
negative remainder */
+ result = int64_to_numeric(-((99 -
(tm->tm_year - 1)) / 100));
+ break;
+
+ case DTK_MILLENNIUM:
+ /* see comments above. */
+ if (tm->tm_year > 0)
+ result = int64_to_numeric((tm->tm_year
+ 999) / 1000);
+ else
+ result = int64_to_numeric(-((999 -
(tm->tm_year - 1)) / 1000));
+ break;
+
+ case DTK_JULIAN:
+ result = 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);
+ break;
+
+ case DTK_ISOYEAR:
+ {
+ int tmp = date2isoyear(tm->tm_year, tm->tm_mon,
tm->tm_mday);
+ /* Adjust BC years */
+ if (tmp <= 0)
+ tmp -= 1;
+ result = int64_to_numeric(tmp);
+ break;
+ }
+
+ case DTK_DOW:
+ case DTK_ISODOW:
+ {
+ int tmp = j2day(date2j(tm->tm_year, tm->tm_mon,
tm->tm_mday));
+ if (val == DTK_ISODOW && tmp == 0)
+ tmp = 7;
+ result = int64_to_numeric(tmp);
+ break;
+ }
+
+ case DTK_DOY:
+ result = int64_to_numeric(date2j(tm->tm_year,
tm->tm_mon, tm->tm_mday)
+
- date2j(tm->tm_year, 1, 1) + 1);
+ break;
+
+ case DTK_TZ:
+ case DTK_TZ_MINUTE:
+ case DTK_TZ_HOUR:
+ default:
+ ereport(ERROR,
+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("timestamp units \"%s\"
not supported",
+ lowunits)));
+ result = 0;
+ }
+ }
+ else if (type == RESERV)
+ {
+ switch (val)
+ {
+ case DTK_EPOCH:
+ /* (timestamp - epoch) / 1000000 */
+ epoch = SetEpochTimestamp();
+ if (timestamp < (PG_INT64_MAX + epoch))
+ result = numeric_div_opt_error(
+ int64_to_numeric(timestamp -
epoch),
+ int64_to_numeric(1000000),
+ NULL);
+ 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)));
+ break;
+
+ default:
+ ereport(ERROR,
+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("timestamp units \"%s\"
not supported",
+ lowunits)));
+ result = 0;
+ }
+
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("timestamp units \"%s\" not
recognized", lowunits)));
+ result = 0;
+ }
+
+ PG_RETURN_NUMERIC(result);
+}
+
/* timestamptz_part()
* Extract specified field from timestamp with time zone.
*/
@@ -4746,9 +5049,9 @@ timestamptz_part(PG_FUNCTION_ARGS)
if (TIMESTAMP_NOT_FINITE(timestamp))
{
- result = NonFiniteTimestampTzPart(type, val, lowunits,
-
TIMESTAMP_IS_NOBEGIN(timestamp),
-
true);
+ result = NonFiniteTimestampTzPart_float8(type, val, lowunits,
+
TIMESTAMP_IS_NOBEGIN(timestamp),
+
true);
if (result)
PG_RETURN_FLOAT8(result);
else
@@ -4915,6 +5218,226 @@ timestamptz_part(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+/* extract_timestamptz()
+ * Extract specified field from timestamp with time zone.
+ */
+Datum
+extract_timestamptz(PG_FUNCTION_ARGS)
+{
+ text *units = PG_GETARG_TEXT_PP(0);
+ TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
+ Numeric result;
+ Timestamp epoch;
+ int tz;
+ int type,
+ val;
+ char *lowunits;
+ fsec_t fsec;
+ struct pg_tm tt,
+ *tm = &tt;
+
+ lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
+
VARSIZE_ANY_EXHDR(units),
+
false);
+
+ type = DecodeUnits(0, lowunits, &val);
+ if (type == UNKNOWN_FIELD)
+ type = DecodeSpecial(0, lowunits, &val);
+
+ if (TIMESTAMP_NOT_FINITE(timestamp))
+ {
+ result = NonFiniteTimestampTzPart_numeric(type, val, lowunits,
+
TIMESTAMP_IS_NOBEGIN(timestamp),
+
true);
+ if (result)
+ PG_RETURN_NUMERIC(result);
+ else
+ PG_RETURN_NULL();
+ }
+
+ if (type == UNITS)
+ {
+ if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
+ ereport(ERROR,
+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range")));
+
+ switch (val)
+ {
+ case DTK_TZ:
+ result = int64_to_numeric(-tz);
+ break;
+
+ case DTK_TZ_MINUTE:
+ /* trunc(-tz / 60) % 60 */
+ result = int64_to_numeric(- (tz - tz /
SECS_PER_HOUR * SECS_PER_HOUR) / SECS_PER_MINUTE);
+ break;
+
+ case DTK_TZ_HOUR:
+ result = int64_to_numeric(- tz / SECS_PER_HOUR);
+ break;
+
+ case DTK_MICROSEC:
+ result = int64_to_numeric(tm->tm_sec * 1000000
+ fsec);
+ break;
+
+ case DTK_MILLISEC:
+ /* tm->tm_sec * 1000 + fsec / 1000
+ = (tm->tm_sec * 1'000'000 + fsec) / 1000 */
+ result = int64_div_fast_to_numeric(tm->tm_sec *
1000000LL + fsec, 1000);
+ break;
+
+ case DTK_SECOND:
+ /* tm->tm_sec + fsec / 1'000'000
+ = (tm->tm_sec * 1'000'000 + fsec) /
1'000'000 */
+ result = int64_div_fast_to_numeric(tm->tm_sec *
1000000LL + fsec, 1000000);
+ break;
+
+ case DTK_MINUTE:
+ result = int64_to_numeric(tm->tm_min);
+ break;
+
+ case DTK_HOUR:
+ result = int64_to_numeric(tm->tm_hour);
+ break;
+
+ case DTK_DAY:
+ result = int64_to_numeric(tm->tm_mday);
+ break;
+
+ case DTK_MONTH:
+ result = int64_to_numeric(tm->tm_mon);
+ break;
+
+ case DTK_QUARTER:
+ result = int64_to_numeric((tm->tm_mon - 1) / 3
+ 1);
+ break;
+
+ case DTK_WEEK:
+ result =
int64_to_numeric(date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday));
+ break;
+
+ case DTK_YEAR:
+ if (tm->tm_year > 0)
+ result = int64_to_numeric(tm->tm_year);
+ else
+ /* there is no year 0, just 1 BC and 1
AD */
+ result = int64_to_numeric(tm->tm_year -
1);
+ break;
+
+ case DTK_DECADE:
+ /* see comments in timestamp_part */
+ if (tm->tm_year >= 0)
+ result = int64_to_numeric(tm->tm_year /
10);
+ else
+ result = int64_to_numeric(-((8 -
(tm->tm_year - 1)) / 10));
+ break;
+
+ case DTK_CENTURY:
+ /* see comments in timestamp_part */
+ if (tm->tm_year > 0)
+ result = int64_to_numeric((tm->tm_year
+ 99) / 100);
+ else
+ result = int64_to_numeric(-((99 -
(tm->tm_year - 1)) / 100));
+ break;
+
+ case DTK_MILLENNIUM:
+ /* see comments in timestamp_part */
+ if (tm->tm_year > 0)
+ result = int64_to_numeric((tm->tm_year
+ 999) / 1000);
+ else
+ result = int64_to_numeric(-((999 -
(tm->tm_year - 1)) / 1000));
+ break;
+
+ case DTK_JULIAN:
+ result = 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);
+ break;
+
+ case DTK_ISOYEAR:
+ {
+ int tmp = date2isoyear(tm->tm_year, tm->tm_mon,
tm->tm_mday);
+ /* Adjust BC years */
+ if (tmp <= 0)
+ tmp -= 1;
+ result = int64_to_numeric(tmp);
+ break;
+ }
+
+ case DTK_DOW:
+ case DTK_ISODOW:
+ {
+ int tmp = j2day(date2j(tm->tm_year, tm->tm_mon,
tm->tm_mday));
+ if (val == DTK_ISODOW && tmp == 0)
+ tmp = 7;
+ result = int64_to_numeric(tmp);
+ break;
+ }
+
+ case DTK_DOY:
+ result = int64_to_numeric(date2j(tm->tm_year,
tm->tm_mon, tm->tm_mday)
+
- date2j(tm->tm_year, 1, 1) + 1);
+ break;
+
+ default:
+ ereport(ERROR,
+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("timestamp with time
zone units \"%s\" not supported",
+ lowunits)));
+ result = 0;
+ }
+
+ }
+ else if (type == RESERV)
+ {
+ switch (val)
+ {
+ case DTK_EPOCH:
+ /* (timestamp - epoch) / 1000000 */
+ epoch = SetEpochTimestamp();
+ if (timestamp < (PG_INT64_MAX + epoch))
+ result = numeric_div_opt_error(
+ int64_to_numeric(timestamp -
epoch),
+ int64_to_numeric(1000000),
+ NULL);
+ 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)));
+ break;
+
+ default:
+ ereport(ERROR,
+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("timestamp with time
zone units \"%s\" not supported",
+ lowunits)));
+ result = 0;
+ }
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("timestamp with time zone units \"%s\"
not recognized",
+ lowunits)));
+
+ result = 0;
+ }
+
+ PG_RETURN_NUMERIC(result);
+}
+
/* interval_part()
* Extract specified field from interval.
@@ -5032,6 +5555,135 @@ interval_part(PG_FUNCTION_ARGS)
}
+/* extract_interval()
+ * Extract specified field from interval.
+ */
+Datum
+extract_interval(PG_FUNCTION_ARGS)
+{
+ text *units = PG_GETARG_TEXT_PP(0);
+ Interval *interval = PG_GETARG_INTERVAL_P(1);
+ Numeric result;
+ int type,
+ val;
+ char *lowunits;
+ fsec_t fsec;
+ struct pg_tm tt,
+ *tm = &tt;
+
+ lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
+
VARSIZE_ANY_EXHDR(units),
+
false);
+
+ type = DecodeUnits(0, lowunits, &val);
+ if (type == UNKNOWN_FIELD)
+ type = DecodeSpecial(0, lowunits, &val);
+
+ if (type == UNITS)
+ {
+ if (interval2tm(*interval, tm, &fsec) == 0)
+ {
+ switch (val)
+ {
+ case DTK_MICROSEC:
+ /* tm->tm_sec * 1000000 + fsec */
+ result = int64_to_numeric(tm->tm_sec *
1000000 + fsec);
+ break;
+
+ case DTK_MILLISEC:
+ /* tm->tm_sec * 1000 + fsec / 1000
+ = (tm->tm_sec * 1'000'000 + fsec) /
1000 */
+ result =
int64_div_fast_to_numeric(tm->tm_sec * 1000000LL + fsec, 1000);
+ break;
+
+ case DTK_SECOND:
+ /* tm->tm_sec + fsec / 1'000'000
+ = (tm->tm_sec * 1'000'000 + fsec) /
1'000'000 */
+ result =
int64_div_fast_to_numeric(tm->tm_sec * 1000000LL + fsec, 1000000);
+ break;
+
+ case DTK_MINUTE:
+ result = int64_to_numeric(tm->tm_min);
+ break;
+
+ case DTK_HOUR:
+ result = int64_to_numeric(tm->tm_hour);
+ break;
+
+ case DTK_DAY:
+ result = int64_to_numeric(tm->tm_mday);
+ break;
+
+ case DTK_MONTH:
+ result = int64_to_numeric(tm->tm_mon);
+ break;
+
+ case DTK_QUARTER:
+ result = int64_to_numeric((tm->tm_mon /
3) + 1);
+ break;
+
+ case DTK_YEAR:
+ result = int64_to_numeric(tm->tm_year);
+ break;
+
+ case DTK_DECADE:
+ /* caution: C division may have
negative remainder */
+ result = int64_to_numeric(tm->tm_year /
10);
+ break;
+
+ case DTK_CENTURY:
+ /* caution: C division may have
negative remainder */
+ result = int64_to_numeric(tm->tm_year /
100);
+ break;
+
+ case DTK_MILLENNIUM:
+ /* caution: C division may have
negative remainder */
+ result = int64_to_numeric(tm->tm_year /
1000);
+ break;
+
+ default:
+ ereport(ERROR,
+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("interval units
\"%s\" not supported",
+
lowunits)));
+ result = 0;
+ }
+
+ }
+ else
+ {
+ elog(ERROR, "could not convert interval to tm");
+ result = 0;
+ }
+ }
+ else if (type == RESERV && val == DTK_EPOCH)
+ {
+ result =
+ numeric_add_opt_error(
+
numeric_div_opt_error(int64_to_numeric(interval->time),
+
int64_to_numeric(1000000),
+ NULL),
+ int64_to_numeric(((int64) DAYS_PER_YEAR *
SECS_PER_DAY) * (interval->month / MONTHS_PER_YEAR) +
+ ((int64)
DAYS_PER_MONTH * SECS_PER_DAY) * (interval->month % MONTHS_PER_YEAR) +
+ ((int64)
SECS_PER_DAY) * interval->day),
+ NULL);
+ result = DatumGetNumeric(DirectFunctionCall2(numeric_round,
+
NumericGetDatum(result),
+
Int32GetDatum(6)));
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("interval units \"%s\" not recognized",
+ lowunits)));
+ result = 0;
+ }
+
+ PG_RETURN_NUMERIC(result);
+}
+
+
/* timestamp_zone()
* Encode timestamp type with specified time zone.
* This function is just timestamp2timestamptz() except instead of
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 93393fcfd4..2087315321 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -2336,9 +2336,15 @@
{ oid => '1171', descr => 'extract field from timestamp with time zone',
proname => 'date_part', provolatile => 's', prorettype => 'float8',
proargtypes => 'text timestamptz', prosrc => 'timestamptz_part' },
+{ oid => '9983', descr => 'extract field from timestamp with time zone',
+ proname => 'extract', provolatile => 's', prorettype => 'numeric',
+ proargtypes => 'text timestamptz', prosrc => 'extract_timestamptz' },
{ oid => '1172', descr => 'extract field from interval',
proname => 'date_part', prorettype => 'float8',
proargtypes => 'text interval', prosrc => 'interval_part' },
+{ oid => '9984', descr => 'extract field from interval',
+ proname => 'extract', prorettype => 'numeric',
+ proargtypes => 'text interval', prosrc => 'extract_interval' },
{ oid => '1174', descr => 'convert date to timestamp with time zone',
proname => 'timestamptz', provolatile => 's', prorettype => 'timestamptz',
proargtypes => 'date', prosrc => 'date_timestamptz' },
@@ -2486,6 +2492,9 @@
{ oid => '1273', descr => 'extract field from time with time zone',
proname => 'date_part', prorettype => 'float8', proargtypes => 'text timetz',
prosrc => 'timetz_part' },
+{ oid => '9981', descr => 'extract field from time with time zone',
+ proname => 'extract', prorettype => 'numeric', proargtypes => 'text timetz',
+ prosrc => 'extract_timetz' },
{ oid => '1274',
proname => 'int84pl', prorettype => 'int8', proargtypes => 'int8 int4',
prosrc => 'int84pl' },
@@ -2831,9 +2840,15 @@
proname => 'date_part', prolang => 'sql', prorettype => 'float8',
proargtypes => 'text date',
prosrc => 'select pg_catalog.date_part($1, cast($2 as timestamp without time
zone))' },
+{ oid => '9979', descr => 'extract field from date',
+ proname => 'extract', prorettype => 'numeric', proargtypes => 'text date',
+ prosrc => 'extract_date' },
{ oid => '1385', descr => 'extract field from time',
proname => 'date_part', prorettype => 'float8', proargtypes => 'text time',
prosrc => 'time_part' },
+{ oid => '9980', descr => 'extract field from time',
+ proname => 'extract', prorettype => 'numeric', proargtypes => 'text time',
+ prosrc => 'extract_time' },
{ oid => '1386',
descr => 'date difference from today preserving months and years',
proname => 'age', prolang => 'sql', provolatile => 's',
@@ -5810,6 +5825,9 @@
{ oid => '2021', descr => 'extract field from timestamp',
proname => 'date_part', prorettype => 'float8',
proargtypes => 'text timestamp', prosrc => 'timestamp_part' },
+{ oid => '9982', descr => 'extract field from timestamp',
+ proname => 'extract', prorettype => 'numeric',
+ proargtypes => 'text timestamp', prosrc => 'extract_timestamp' },
{ oid => '2024', descr => 'convert date to timestamp',
proname => 'timestamp', prorettype => 'timestamp', proargtypes => 'date',
prosrc => 'date_timestamp' },
diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h
index a362b5beb2..c5ae66469d 100644
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
@@ -63,6 +63,7 @@ extern char *numeric_out_sci(Numeric num, int scale);
extern char *numeric_normalize(Numeric num);
extern Numeric int64_to_numeric(int64 val);
+extern Numeric int64_div_fast_to_numeric(int64 val1, int64 val2);
extern Numeric numeric_add_opt_error(Numeric num1, Numeric num2,
bool
*have_error);
diff --git a/src/test/regress/expected/create_view.out
b/src/test/regress/expected/create_view.out
index bd5fe60450..cdce4e0727 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -1742,7 +1742,7 @@ select
select pg_get_viewdef('tt201v', true);
pg_get_viewdef
-----------------------------------------------------------------------------------------------
- SELECT date_part('day'::text, now()) AS extr,
+
+ SELECT EXTRACT(day FROM now()) AS extr,
+
((now(), '@ 1 day'::interval) OVERLAPS (CURRENT_TIMESTAMP(2), '@ 1
day'::interval)) AS o,+
(('foo'::text) IS NORMALIZED) AS isn,
+
(('foo'::text) IS NFKC NORMALIZED) AS isnn,
+
diff --git a/src/test/regress/expected/date.out
b/src/test/regress/expected/date.out
index 1b921ce215..d7677fc0b5 100644
--- a/src/test/regress/expected/date.out
+++ b/src/test/regress/expected/date.out
@@ -966,78 +966,78 @@ SELECT f1 as "date",
-- epoch
--
SELECT EXTRACT(EPOCH FROM DATE '1970-01-01'); -- 0
- date_part
------------
- 0
+ extract
+---------
+ 0
(1 row)
SELECT EXTRACT(EPOCH FROM TIMESTAMP '1970-01-01'); -- 0
- date_part
------------
- 0
+ extract
+----------
+ 0.000000
(1 row)
SELECT EXTRACT(EPOCH FROM TIMESTAMPTZ '1970-01-01+00'); -- 0
- date_part
------------
- 0
+ extract
+----------
+ 0.000000
(1 row)
--
-- century
--
SELECT EXTRACT(CENTURY FROM DATE '0101-12-31 BC'); -- -2
- date_part
------------
- -2
+ extract
+---------
+ -2
(1 row)
SELECT EXTRACT(CENTURY FROM DATE '0100-12-31 BC'); -- -1
- date_part
------------
- -1
+ extract
+---------
+ -1
(1 row)
SELECT EXTRACT(CENTURY FROM DATE '0001-12-31 BC'); -- -1
- date_part
------------
- -1
+ extract
+---------
+ -1
(1 row)
SELECT EXTRACT(CENTURY FROM DATE '0001-01-01'); -- 1
- date_part
------------
- 1
+ extract
+---------
+ 1
(1 row)
SELECT EXTRACT(CENTURY FROM DATE '0001-01-01 AD'); -- 1
- date_part
------------
- 1
+ extract
+---------
+ 1
(1 row)
SELECT EXTRACT(CENTURY FROM DATE '1900-12-31'); -- 19
- date_part
------------
- 19
+ extract
+---------
+ 19
(1 row)
SELECT EXTRACT(CENTURY FROM DATE '1901-01-01'); -- 20
- date_part
------------
- 20
+ extract
+---------
+ 20
(1 row)
SELECT EXTRACT(CENTURY FROM DATE '2000-12-31'); -- 20
- date_part
------------
- 20
+ extract
+---------
+ 20
(1 row)
SELECT EXTRACT(CENTURY FROM DATE '2001-01-01'); -- 21
- date_part
------------
- 21
+ extract
+---------
+ 21
(1 row)
SELECT EXTRACT(CENTURY FROM CURRENT_DATE)>=21 AS True; -- true
@@ -1050,91 +1050,91 @@ SELECT EXTRACT(CENTURY FROM CURRENT_DATE)>=21 AS True;
-- true
-- millennium
--
SELECT EXTRACT(MILLENNIUM FROM DATE '0001-12-31 BC'); -- -1
- date_part
------------
- -1
+ extract
+---------
+ -1
(1 row)
SELECT EXTRACT(MILLENNIUM FROM DATE '0001-01-01 AD'); -- 1
- date_part
------------
- 1
+ extract
+---------
+ 1
(1 row)
SELECT EXTRACT(MILLENNIUM FROM DATE '1000-12-31'); -- 1
- date_part
------------
- 1
+ extract
+---------
+ 1
(1 row)
SELECT EXTRACT(MILLENNIUM FROM DATE '1001-01-01'); -- 2
- date_part
------------
- 2
+ extract
+---------
+ 2
(1 row)
SELECT EXTRACT(MILLENNIUM FROM DATE '2000-12-31'); -- 2
- date_part
------------
- 2
+ extract
+---------
+ 2
(1 row)
SELECT EXTRACT(MILLENNIUM FROM DATE '2001-01-01'); -- 3
- date_part
------------
- 3
+ extract
+---------
+ 3
(1 row)
-- next test to be fixed on the turn of the next millennium;-)
SELECT EXTRACT(MILLENNIUM FROM CURRENT_DATE); -- 3
- date_part
------------
- 3
+ extract
+---------
+ 3
(1 row)
--
-- decade
--
SELECT EXTRACT(DECADE FROM DATE '1994-12-25'); -- 199
- date_part
------------
- 199
+ extract
+---------
+ 199
(1 row)
SELECT EXTRACT(DECADE FROM DATE '0010-01-01'); -- 1
- date_part
------------
- 1
+ extract
+---------
+ 1
(1 row)
SELECT EXTRACT(DECADE FROM DATE '0009-12-31'); -- 0
- date_part
------------
- 0
+ extract
+---------
+ 0
(1 row)
SELECT EXTRACT(DECADE FROM DATE '0001-01-01 BC'); -- 0
- date_part
------------
- 0
+ extract
+---------
+ 0
(1 row)
SELECT EXTRACT(DECADE FROM DATE '0002-12-31 BC'); -- -1
- date_part
------------
- -1
+ extract
+---------
+ -1
(1 row)
SELECT EXTRACT(DECADE FROM DATE '0011-01-01 BC'); -- -1
- date_part
------------
- -1
+ extract
+---------
+ -1
(1 row)
SELECT EXTRACT(DECADE FROM DATE '0012-12-31 BC'); -- -2
- date_part
------------
- -2
+ extract
+---------
+ -2
(1 row)
--
@@ -1148,135 +1148,112 @@ SELECT EXTRACT(CENTURY FROM NOW())>=21 AS True;
-- true
(1 row)
SELECT EXTRACT(CENTURY FROM TIMESTAMP '1970-03-20 04:30:00.00000'); -- 20
- date_part
------------
- 20
+ extract
+---------
+ 20
(1 row)
--
-- all possible fields
--
SELECT EXTRACT(MICROSECONDS FROM DATE '2020-08-11');
- date_part
------------
- 0
-(1 row)
-
+ERROR: date units "microseconds" not supported
SELECT EXTRACT(MILLISECONDS FROM DATE '2020-08-11');
- date_part
------------
- 0
-(1 row)
-
+ERROR: date units "milliseconds" not supported
SELECT EXTRACT(SECOND FROM DATE '2020-08-11');
- date_part
------------
- 0
-(1 row)
-
+ERROR: date units "second" not supported
SELECT EXTRACT(MINUTE FROM DATE '2020-08-11');
- date_part
------------
- 0
-(1 row)
-
+ERROR: date units "minute" not supported
SELECT EXTRACT(HOUR FROM DATE '2020-08-11');
- date_part
------------
- 0
-(1 row)
-
+ERROR: date units "hour" not supported
SELECT EXTRACT(DAY FROM DATE '2020-08-11');
- date_part
------------
- 11
+ extract
+---------
+ 11
(1 row)
SELECT EXTRACT(MONTH FROM DATE '2020-08-11');
- date_part
------------
- 8
+ extract
+---------
+ 8
(1 row)
SELECT EXTRACT(YEAR FROM DATE '2020-08-11');
- date_part
------------
- 2020
+ extract
+---------
+ 2020
(1 row)
SELECT EXTRACT(DECADE FROM DATE '2020-08-11');
- date_part
------------
- 202
+ extract
+---------
+ 202
(1 row)
SELECT EXTRACT(CENTURY FROM DATE '2020-08-11');
- date_part
------------
- 21
+ extract
+---------
+ 21
(1 row)
SELECT EXTRACT(MILLENNIUM FROM DATE '2020-08-11');
- date_part
------------
- 3
+ extract
+---------
+ 3
(1 row)
SELECT EXTRACT(ISOYEAR FROM DATE '2020-08-11');
- date_part
------------
- 2020
+ extract
+---------
+ 2020
(1 row)
SELECT EXTRACT(QUARTER FROM DATE '2020-08-11');
- date_part
------------
- 3
+ extract
+---------
+ 3
(1 row)
SELECT EXTRACT(WEEK FROM DATE '2020-08-11');
- date_part
------------
- 33
+ extract
+---------
+ 33
(1 row)
SELECT EXTRACT(DOW FROM DATE '2020-08-11');
- date_part
------------
- 2
+ extract
+---------
+ 2
(1 row)
SELECT EXTRACT(ISODOW FROM DATE '2020-08-11');
- date_part
------------
- 2
+ extract
+---------
+ 2
(1 row)
SELECT EXTRACT(DOY FROM DATE '2020-08-11');
- date_part
------------
- 224
+ extract
+---------
+ 224
(1 row)
SELECT EXTRACT(TIMEZONE FROM DATE '2020-08-11');
-ERROR: timestamp units "timezone" not supported
-CONTEXT: SQL function "date_part" statement 1
+ERROR: date units "timezone" not supported
SELECT EXTRACT(TIMEZONE_M FROM DATE '2020-08-11');
-ERROR: timestamp units "timezone_m" not supported
-CONTEXT: SQL function "date_part" statement 1
+ERROR: date units "timezone_m" not supported
SELECT EXTRACT(TIMEZONE_H FROM DATE '2020-08-11');
-ERROR: timestamp units "timezone_h" not supported
-CONTEXT: SQL function "date_part" statement 1
+ERROR: date units "timezone_h" not supported
SELECT EXTRACT(EPOCH FROM DATE '2020-08-11');
- date_part
+ extract
------------
1597104000
(1 row)
SELECT EXTRACT(JULIAN FROM DATE '2020-08-11');
- date_part
------------
- 2459073
+ extract
+---------
+ 2459073
(1 row)
--
@@ -1372,221 +1349,188 @@ select isfinite('infinity'::date),
isfinite('-infinity'::date), isfinite('today'
--
-- oscillating fields from non-finite date/timestamptz:
--
-SELECT EXTRACT(HOUR FROM DATE 'infinity'); -- NULL
- date_part
------------
-
+SELECT EXTRACT(DAY FROM DATE 'infinity'); -- NULL
+ extract
+---------
+
(1 row)
-SELECT EXTRACT(HOUR FROM DATE '-infinity'); -- NULL
- date_part
------------
-
+SELECT EXTRACT(DAY FROM DATE '-infinity'); -- NULL
+ extract
+---------
+
(1 row)
SELECT EXTRACT(HOUR FROM TIMESTAMP 'infinity'); -- NULL
- date_part
------------
-
+ extract
+---------
+
(1 row)
SELECT EXTRACT(HOUR FROM TIMESTAMP '-infinity'); -- NULL
- date_part
------------
-
+ extract
+---------
+
(1 row)
SELECT EXTRACT(HOUR FROM TIMESTAMPTZ 'infinity'); -- NULL
- date_part
------------
-
+ extract
+---------
+
(1 row)
SELECT EXTRACT(HOUR FROM TIMESTAMPTZ '-infinity'); -- NULL
- date_part
------------
-
+ extract
+---------
+
(1 row)
-- all possible fields
SELECT EXTRACT(MICROSECONDS FROM DATE 'infinity'); -- NULL
- date_part
------------
-
-(1 row)
-
+ERROR: date units "microseconds" not supported
SELECT EXTRACT(MILLISECONDS FROM DATE 'infinity'); -- NULL
- date_part
------------
-
-(1 row)
-
+ERROR: date units "milliseconds" not supported
SELECT EXTRACT(SECOND FROM DATE 'infinity'); -- NULL
- date_part
------------
-
-(1 row)
-
+ERROR: date units "second" not supported
SELECT EXTRACT(MINUTE FROM DATE 'infinity'); -- NULL
- date_part
------------
-
-(1 row)
-
+ERROR: date units "minute" not supported
SELECT EXTRACT(HOUR FROM DATE 'infinity'); -- NULL
- date_part
------------
-
-(1 row)
-
+ERROR: date units "hour" not supported
SELECT EXTRACT(DAY FROM DATE 'infinity'); -- NULL
- date_part
------------
-
+ extract
+---------
+
(1 row)
SELECT EXTRACT(MONTH FROM DATE 'infinity'); -- NULL
- date_part
------------
-
+ extract
+---------
+
(1 row)
SELECT EXTRACT(QUARTER FROM DATE 'infinity'); -- NULL
- date_part
------------
-
+ extract
+---------
+
(1 row)
SELECT EXTRACT(WEEK FROM DATE 'infinity'); -- NULL
- date_part
------------
-
+ extract
+---------
+
(1 row)
SELECT EXTRACT(DOW FROM DATE 'infinity'); -- NULL
- date_part
------------
-
+ extract
+---------
+
(1 row)
SELECT EXTRACT(ISODOW FROM DATE 'infinity'); -- NULL
- date_part
------------
-
+ extract
+---------
+
(1 row)
SELECT EXTRACT(DOY FROM DATE 'infinity'); -- NULL
- date_part
------------
-
+ extract
+---------
+
(1 row)
SELECT EXTRACT(TIMEZONE FROM DATE 'infinity'); -- NULL
- date_part
------------
-
-(1 row)
-
+ERROR: date units "timezone" not supported
SELECT EXTRACT(TIMEZONE_M FROM DATE 'infinity'); -- NULL
- date_part
------------
-
-(1 row)
-
+ERROR: date units "timezone_m" not supported
SELECT EXTRACT(TIMEZONE_H FROM DATE 'infinity'); -- NULL
- date_part
------------
-
-(1 row)
-
+ERROR: date units "timezone_h" not supported
--
-- monotonic fields from non-finite date/timestamptz:
--
SELECT EXTRACT(EPOCH FROM DATE 'infinity'); -- Infinity
- date_part
------------
- Infinity
+ extract
+----------
+ Infinity
(1 row)
SELECT EXTRACT(EPOCH FROM DATE '-infinity'); -- -Infinity
- date_part
+ extract
-----------
-Infinity
(1 row)
SELECT EXTRACT(EPOCH FROM TIMESTAMP 'infinity'); -- Infinity
- date_part
------------
- Infinity
+ extract
+----------
+ Infinity
(1 row)
SELECT EXTRACT(EPOCH FROM TIMESTAMP '-infinity'); -- -Infinity
- date_part
+ extract
-----------
-Infinity
(1 row)
SELECT EXTRACT(EPOCH FROM TIMESTAMPTZ 'infinity'); -- Infinity
- date_part
------------
- Infinity
+ extract
+----------
+ Infinity
(1 row)
SELECT EXTRACT(EPOCH FROM TIMESTAMPTZ '-infinity'); -- -Infinity
- date_part
+ extract
-----------
-Infinity
(1 row)
-- all possible fields
SELECT EXTRACT(YEAR FROM DATE 'infinity'); -- Infinity
- date_part
------------
- Infinity
+ extract
+----------
+ Infinity
(1 row)
SELECT EXTRACT(DECADE FROM DATE 'infinity'); -- Infinity
- date_part
------------
- Infinity
+ extract
+----------
+ Infinity
(1 row)
SELECT EXTRACT(CENTURY FROM DATE 'infinity'); -- Infinity
- date_part
------------
- Infinity
+ extract
+----------
+ Infinity
(1 row)
SELECT EXTRACT(MILLENNIUM FROM DATE 'infinity'); -- Infinity
- date_part
------------
- Infinity
+ extract
+----------
+ Infinity
(1 row)
SELECT EXTRACT(JULIAN FROM DATE 'infinity'); -- Infinity
- date_part
------------
- Infinity
+ extract
+----------
+ Infinity
(1 row)
SELECT EXTRACT(ISOYEAR FROM DATE 'infinity'); -- Infinity
- date_part
------------
- Infinity
+ extract
+----------
+ Infinity
(1 row)
SELECT EXTRACT(EPOCH FROM DATE 'infinity'); -- Infinity
- date_part
------------
- Infinity
+ extract
+----------
+ Infinity
(1 row)
--
-- wrong fields from non-finite date:
--
-SELECT EXTRACT(MICROSEC FROM DATE 'infinity'); -- ERROR: timestamp units
"microsec" not recognized
-ERROR: timestamp units "microsec" not recognized
-CONTEXT: SQL function "date_part" statement 1
+SELECT EXTRACT(MICROSEC FROM DATE 'infinity'); -- error
+ERROR: date units "microsec" not recognized
-- test constructors
select make_date(2013, 7, 15);
make_date
diff --git a/src/test/regress/expected/interval.out
b/src/test/regress/expected/interval.out
index acc353a0dc..80e07fe355 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -948,18 +948,18 @@ SELECT f1,
EXTRACT(MILLENNIUM FROM f1) AS MILLENNIUM,
EXTRACT(EPOCH FROM f1) AS EPOCH
FROM INTERVAL_TBL;
- f1 | microsecond | millisecond | second | minute |
hour | day | month | quarter | year | decade | century | millennium | epoch
--------------------------------+-------------+-------------+--------+--------+------+-----+-------+---------+------+--------+---------+------------+------------
- @ 1 min | 0 | 0 | 0 | 1 |
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 60
- @ 5 hours | 0 | 0 | 0 | 0 |
5 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 18000
- @ 10 days | 0 | 0 | 0 | 0 |
0 | 10 | 0 | 1 | 0 | 0 | 0 | 0 | 864000
- @ 34 years | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 1 | 34 | 3 | 0 | 0 | 1072958400
- @ 3 mons | 0 | 0 | 0 | 0 |
0 | 0 | 3 | 2 | 0 | 0 | 0 | 0 | 7776000
- @ 14 secs ago | -14000000 | -14000 | -14 | 0 |
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | -14
- @ 1 day 2 hours 3 mins 4 secs | 4000000 | 4000 | 4 | 3 |
2 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 93784
- @ 6 years | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 1 | 6 | 0 | 0 | 0 | 189345600
- @ 5 mons | 0 | 0 | 0 | 0 |
0 | 0 | 5 | 2 | 0 | 0 | 0 | 0 | 12960000
- @ 5 mons 12 hours | 0 | 0 | 0 | 0 |
12 | 0 | 5 | 2 | 0 | 0 | 0 | 0 | 13003200
+ f1 | microsecond | millisecond | second |
minute | hour | day | month | quarter | year | decade | century | millennium |
epoch
+-------------------------------+-------------+-------------+------------+--------+------+-----+-------+---------+------+--------+---------+------------+-------------------
+ @ 1 min | 0 | 0.000 | 0.000000 |
1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
60.000000
+ @ 5 hours | 0 | 0.000 | 0.000000 |
0 | 5 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
18000.000000
+ @ 10 days | 0 | 0.000 | 0.000000 |
0 | 0 | 10 | 0 | 1 | 0 | 0 | 0 | 0 |
864000.000000
+ @ 34 years | 0 | 0.000 | 0.000000 |
0 | 0 | 0 | 0 | 1 | 34 | 3 | 0 | 0 |
1072224000.000000
+ @ 3 mons | 0 | 0.000 | 0.000000 |
0 | 0 | 0 | 3 | 2 | 0 | 0 | 0 | 0 |
7776000.000000
+ @ 14 secs ago | -14000000 | -14000.000 | -14.000000 |
0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
-14.000000
+ @ 1 day 2 hours 3 mins 4 secs | 4000000 | 4000.000 | 4.000000 |
3 | 2 | 1 | 0 | 1 | 0 | 0 | 0 | 0 |
93784.000000
+ @ 6 years | 0 | 0.000 | 0.000000 |
0 | 0 | 0 | 0 | 1 | 6 | 0 | 0 | 0 |
189216000.000000
+ @ 5 mons | 0 | 0.000 | 0.000000 |
0 | 0 | 0 | 5 | 2 | 0 | 0 | 0 | 0 |
12960000.000000
+ @ 5 mons 12 hours | 0 | 0.000 | 0.000000 |
0 | 12 | 0 | 5 | 2 | 0 | 0 | 0 | 0 |
13003200.000000
(10 rows)
SELECT EXTRACT(FORTNIGHT FROM INTERVAL '2 days'); -- error
@@ -967,50 +967,50 @@ ERROR: interval units "fortnight" not recognized
SELECT EXTRACT(TIMEZONE FROM INTERVAL '2 days'); -- error
ERROR: interval units "timezone" not supported
SELECT EXTRACT(DECADE FROM INTERVAL '100 y');
- date_part
------------
- 10
+ extract
+---------
+ 10
(1 row)
SELECT EXTRACT(DECADE FROM INTERVAL '99 y');
- date_part
------------
- 9
+ extract
+---------
+ 9
(1 row)
SELECT EXTRACT(DECADE FROM INTERVAL '-99 y');
- date_part
------------
- -9
+ extract
+---------
+ -9
(1 row)
SELECT EXTRACT(DECADE FROM INTERVAL '-100 y');
- date_part
------------
- -10
+ extract
+---------
+ -10
(1 row)
SELECT EXTRACT(CENTURY FROM INTERVAL '100 y');
- date_part
------------
- 1
+ extract
+---------
+ 1
(1 row)
SELECT EXTRACT(CENTURY FROM INTERVAL '99 y');
- date_part
------------
- 0
+ extract
+---------
+ 0
(1 row)
SELECT EXTRACT(CENTURY FROM INTERVAL '-99 y');
- date_part
------------
- 0
+ extract
+---------
+ 0
(1 row)
SELECT EXTRACT(CENTURY FROM INTERVAL '-100 y');
- date_part
------------
- -1
+ extract
+---------
+ -1
(1 row)
diff --git a/src/test/regress/expected/psql_crosstab.out
b/src/test/regress/expected/psql_crosstab.out
index eae6fbd051..e09e331016 100644
--- a/src/test/regress/expected/psql_crosstab.out
+++ b/src/test/regress/expected/psql_crosstab.out
@@ -17,12 +17,12 @@ SELECT v, EXTRACT(year FROM d), count(*)
FROM ctv_data
GROUP BY 1, 2
ORDER BY 1, 2;
- v | date_part | count
-----+-----------+-------
- v0 | 2014 | 2
- v0 | 2015 | 1
- v1 | 2015 | 3
- v2 | 2015 | 1
+ v | extract | count
+----+---------+-------
+ v0 | 2014 | 2
+ v0 | 2015 | 1
+ v1 | 2015 | 3
+ v2 | 2015 | 1
(4 rows)
-- basic usage with 3 columns
diff --git a/src/test/regress/expected/time.out
b/src/test/regress/expected/time.out
index 5303cc0c94..f9786e0b4e 100644
--- a/src/test/regress/expected/time.out
+++ b/src/test/regress/expected/time.out
@@ -131,33 +131,33 @@ HINT: Could not choose a best candidate operator. You
might need to add explici
-- test EXTRACT
--
SELECT EXTRACT(MICROSECOND FROM TIME '2020-05-26 13:30:25.575401');
- date_part
------------
- 25575401
+ extract
+----------
+ 25575401
(1 row)
SELECT EXTRACT(MILLISECOND FROM TIME '2020-05-26 13:30:25.575401');
- date_part
+ extract
-----------
25575.401
(1 row)
SELECT EXTRACT(SECOND FROM TIME '2020-05-26 13:30:25.575401');
- date_part
+ extract
-----------
25.575401
(1 row)
SELECT EXTRACT(MINUTE FROM TIME '2020-05-26 13:30:25.575401');
- date_part
------------
- 30
+ extract
+---------
+ 30
(1 row)
SELECT EXTRACT(HOUR FROM TIME '2020-05-26 13:30:25.575401');
- date_part
------------
- 13
+ extract
+---------
+ 13
(1 row)
SELECT EXTRACT(DAY FROM TIME '2020-05-26 13:30:25.575401'); -- error
@@ -167,7 +167,7 @@ ERROR: "time" units "fortnight" not recognized
SELECT EXTRACT(TIMEZONE FROM TIME '2020-05-26 13:30:25.575401'); -- error
ERROR: "time" units "timezone" not recognized
SELECT EXTRACT(EPOCH FROM TIME '2020-05-26 13:30:25.575401');
- date_part
+ extract
--------------
48625.575401
(1 row)
diff --git a/src/test/regress/expected/timetz.out
b/src/test/regress/expected/timetz.out
index 1ab5ed5105..74f8a78a24 100644
--- a/src/test/regress/expected/timetz.out
+++ b/src/test/regress/expected/timetz.out
@@ -148,33 +148,33 @@ HINT: No operator matches the given name and argument
types. You might need to
-- test EXTRACT
--
SELECT EXTRACT(MICROSECOND FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04');
- date_part
------------
- 25575401
+ extract
+----------
+ 25575401
(1 row)
SELECT EXTRACT(MILLISECOND FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04');
- date_part
+ extract
-----------
25575.401
(1 row)
SELECT EXTRACT(SECOND FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04');
- date_part
+ extract
-----------
25.575401
(1 row)
SELECT EXTRACT(MINUTE FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04');
- date_part
------------
- 30
+ extract
+---------
+ 30
(1 row)
SELECT EXTRACT(HOUR FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04');
- date_part
------------
- 13
+ extract
+---------
+ 13
(1 row)
SELECT EXTRACT(DAY FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04'); -- error
@@ -182,25 +182,25 @@ ERROR: "time with time zone" units "day" not recognized
SELECT EXTRACT(FORTNIGHT FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04'); -- error
ERROR: "time with time zone" units "fortnight" not recognized
SELECT EXTRACT(TIMEZONE FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04');
- date_part
------------
- -14400
+ extract
+---------
+ -14400
(1 row)
-SELECT EXTRACT(TIMEZONE_HOUR FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04');
- date_part
------------
- -4
+SELECT EXTRACT(TIMEZONE_HOUR FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04:30');
+ extract
+---------
+ -4
(1 row)
-SELECT EXTRACT(TIMEZONE_MINUTE FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04');
- date_part
------------
- 0
+SELECT EXTRACT(TIMEZONE_MINUTE FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04:30');
+ extract
+---------
+ -30
(1 row)
SELECT EXTRACT(EPOCH FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04');
- date_part
+ extract
--------------
63025.575401
(1 row)
diff --git a/src/test/regress/sql/date.sql b/src/test/regress/sql/date.sql
index 7a734fb1a0..eb0a9d8ddd 100644
--- a/src/test/regress/sql/date.sql
+++ b/src/test/regress/sql/date.sql
@@ -329,8 +329,8 @@ CREATE TABLE DATE_TBL (f1 date);
--
-- oscillating fields from non-finite date/timestamptz:
--
-SELECT EXTRACT(HOUR FROM DATE 'infinity'); -- NULL
-SELECT EXTRACT(HOUR FROM DATE '-infinity'); -- NULL
+SELECT EXTRACT(DAY FROM DATE 'infinity'); -- NULL
+SELECT EXTRACT(DAY FROM DATE '-infinity'); -- NULL
SELECT EXTRACT(HOUR FROM TIMESTAMP 'infinity'); -- NULL
SELECT EXTRACT(HOUR FROM TIMESTAMP '-infinity'); -- NULL
SELECT EXTRACT(HOUR FROM TIMESTAMPTZ 'infinity'); -- NULL
@@ -371,7 +371,7 @@ CREATE TABLE DATE_TBL (f1 date);
--
-- wrong fields from non-finite date:
--
-SELECT EXTRACT(MICROSEC FROM DATE 'infinity'); -- ERROR: timestamp units
"microsec" not recognized
+SELECT EXTRACT(MICROSEC FROM DATE 'infinity'); -- error
-- test constructors
select make_date(2013, 7, 15);
diff --git a/src/test/regress/sql/timetz.sql b/src/test/regress/sql/timetz.sql
index ce763d89e8..307bab31fc 100644
--- a/src/test/regress/sql/timetz.sql
+++ b/src/test/regress/sql/timetz.sql
@@ -67,6 +67,6 @@ CREATE TABLE TIMETZ_TBL (f1 time(2) with time zone);
SELECT EXTRACT(DAY FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04'); -- error
SELECT EXTRACT(FORTNIGHT FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04'); -- error
SELECT EXTRACT(TIMEZONE FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04');
-SELECT EXTRACT(TIMEZONE_HOUR FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04');
-SELECT EXTRACT(TIMEZONE_MINUTE FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04');
+SELECT EXTRACT(TIMEZONE_HOUR FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04:30');
+SELECT EXTRACT(TIMEZONE_MINUTE FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04:30');
SELECT EXTRACT(EPOCH FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04');
base-commit: 27ab1981e7c9b8fcbcb143c5f6f706441a52bbc8
--
2.30.2