Here is a new patch for this. This now follows the implementation that
Tom has suggested: Leave date_part() alone, add a new set of extract()
functions, and map the SQL EXTRACT construct to those. I have basically
just copied over the implementations from my previous patch and placed
them next to the existing date_part() implementations. So all the
behavior is still the same as in the previous patches.
One thing I still need to look into is how to not lose all the test
coverage for date_part(). But that should be fairly mechanical, so I'm
leaving it off in this version.
--
Peter Eisentraut
2ndQuadrant, an EDB company
https://www.2ndquadrant.com/
>From b40213b0d31b8ac55cda4658c21d06e701b6e273 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Tue, 15 Dec 2020 14:27:58 +0100
Subject: [PATCH v4] 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 | 407 +++++++++++
src/backend/utils/adt/ruleutils.c | 21 +
src/backend/utils/adt/timestamp.c | 708 +++++++++++++++++++-
src/include/catalog/pg_proc.dat | 18 +
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 | 42 +-
src/test/regress/sql/date.sql | 6 +-
13 files changed, 1445 insertions(+), 361 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index df29af6371..a42993c4a5 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -8705,7 +8705,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"/>
@@ -8719,7 +8719,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"/>
@@ -9234,7 +9234,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 -->
@@ -9645,6 +9645,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 8f341ac006..2e8924aff8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -13768,7 +13768,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 a470cf890a..61fd69829c 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,114 @@ 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 */
+ result =
numeric_add_opt_error(int64_to_numeric(tm->tm_sec * 1000),
+
numeric_div_opt_error(int64_to_numeric(fsec),
+
int64_to_numeric(1000),
+
NULL),
+
NULL);
+ result =
DatumGetNumeric(DirectFunctionCall2(numeric_round,
+
NumericGetDatum(result),
+
Int32GetDatum(3)));
+ break;
+
+ case DTK_SECOND:
+ /* tm->tm_sec + fsec / 1000000 */
+ result =
numeric_add_opt_error(int64_to_numeric(tm->tm_sec),
+
numeric_div_opt_error(int64_to_numeric(fsec),
+
int64_to_numeric(1000000),
+
NULL),
+
NULL);
+ result =
DatumGetNumeric(DirectFunctionCall2(numeric_round,
+
NumericGetDatum(result),
+
Int32GetDatum(6)));
+ 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 = numeric_div_opt_error(int64_to_numeric(time),
+
int64_to_numeric(1000000),
+
NULL);
+ result = DatumGetNumeric(DirectFunctionCall2(numeric_round,
+
NumericGetDatum(result),
+
Int32GetDatum(6)));
+ }
+ 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 +3068,130 @@ 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 =
numeric_mod_opt_error(int64_to_numeric(-tz / SECS_PER_MINUTE),
+
int64_to_numeric(SECS_PER_MINUTE),
+
NULL);
+ 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 */
+ result =
numeric_add_opt_error(int64_to_numeric(tm->tm_sec * 1000),
+
numeric_div_opt_error(int64_to_numeric(fsec),
+
int64_to_numeric(1000),
+
NULL),
+
NULL);
+ result =
DatumGetNumeric(DirectFunctionCall2(numeric_round,
+
NumericGetDatum(result),
+
Int32GetDatum(3)));
+ break;
+
+ case DTK_SECOND:
+ /* tm->tm_sec + fsec / 1000000 */
+ result =
numeric_add_opt_error(int64_to_numeric(tm->tm_sec),
+
numeric_div_opt_error(int64_to_numeric(fsec),
+
int64_to_numeric(1000000),
+
NULL),
+
NULL);
+ result =
DatumGetNumeric(DirectFunctionCall2(numeric_round,
+
NumericGetDatum(result),
+
Int32GetDatum(6)));
+ 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 */
+ result =
numeric_add_opt_error(numeric_div_opt_error(int64_to_numeric(time->time),
+
int64_to_numeric(1000000),
+
NULL),
+
int64_to_numeric(time->zone),
+
NULL);
+ result = DatumGetNumeric(DirectFunctionCall2(numeric_round,
+
NumericGetDatum(result),
+
Int32GetDatum(6)));
+ }
+ 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/ruleutils.c
b/src/backend/utils/adt/ruleutils.c
index ad582f99a5..d312dfa756 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -9556,6 +9556,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 2dbd309122..4c643033ad 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,237 @@ 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 */
+ result =
numeric_add_opt_error(int64_to_numeric(tm->tm_sec * 1000),
+
numeric_div_opt_error(int64_to_numeric(fsec),
+
int64_to_numeric(1000),
+
NULL),
+
NULL);
+ result =
DatumGetNumeric(DirectFunctionCall2(numeric_round,
+
NumericGetDatum(result),
+
Int32GetDatum(3)));
+ break;
+
+ case DTK_SECOND:
+ /* tm->tm_sec + fsec / 1000000 */
+ result =
numeric_add_opt_error(int64_to_numeric(tm->tm_sec),
+
numeric_div_opt_error(int64_to_numeric(fsec),
+
int64_to_numeric(1000000),
+
NULL),
+
NULL);
+ result =
DatumGetNumeric(DirectFunctionCall2(numeric_round,
+
NumericGetDatum(result),
+
Int32GetDatum(6)));
+ 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 +5061,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 +5230,240 @@ 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 =
numeric_mod_opt_error(int64_to_numeric(-tz / SECS_PER_MINUTE),
+
int64_to_numeric(SECS_PER_MINUTE),
+
NULL);
+ 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 */
+ result =
numeric_add_opt_error(int64_to_numeric(tm->tm_sec * 1000),
+
numeric_div_opt_error(int64_to_numeric(fsec),
+
int64_to_numeric(1000),
+
NULL),
+
NULL);
+ result =
DatumGetNumeric(DirectFunctionCall2(numeric_round,
+
NumericGetDatum(result),
+
Int32GetDatum(3)));
+ break;
+
+ case DTK_SECOND:
+ /* tm->tm_sec + fsec / 1000000 */
+ result =
numeric_add_opt_error(int64_to_numeric(tm->tm_sec),
+
numeric_div_opt_error(int64_to_numeric(fsec),
+
int64_to_numeric(1000000),
+
NULL),
+
NULL);
+ result =
DatumGetNumeric(DirectFunctionCall2(numeric_round,
+
NumericGetDatum(result),
+
Int32GetDatum(6)));
+ 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 +5581,147 @@ 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 */
+ result =
numeric_add_opt_error(int64_to_numeric(tm->tm_sec * 1000),
+
numeric_div_opt_error(int64_to_numeric(fsec),
+
int64_to_numeric(1000),
+
NULL),
+
NULL);
+ result =
DatumGetNumeric(DirectFunctionCall2(numeric_round,
+
NumericGetDatum(result),
+
Int32GetDatum(3)));
+ break;
+
+ case DTK_SECOND:
+ /* tm->tm_sec + fsec / 1000000 */
+ result =
numeric_add_opt_error(int64_to_numeric(tm->tm_sec),
+
numeric_div_opt_error(int64_to_numeric(fsec),
+
int64_to_numeric(1000000),
+
NULL),
+
NULL);
+ result =
DatumGetNumeric(DirectFunctionCall2(numeric_round,
+
NumericGetDatum(result),
+
Int32GetDatum(6)));
+ 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 e6c7b070f6..81e6dcc312 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -2333,9 +2333,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' },
@@ -2483,6 +2489,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' },
@@ -2828,9 +2837,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',
@@ -5758,6 +5773,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/test/regress/expected/create_view.out
b/src/test/regress/expected/create_view.out
index b234d2d4f9..eb11af99a7 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -1739,7 +1739,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 fde4be5271..5f864df194 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..f4a0a760f7 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
+ extract
+---------
+ -4
(1 row)
SELECT EXTRACT(TIMEZONE_MINUTE FROM TIME WITH TIME ZONE '2020-05-26
13:30:25.575401-04');
- date_part
------------
- 0
+ extract
+---------
+ 0
(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);
base-commit: a58db3aa10e62e4228aa409ba006014fa07a8ca2
--
2.29.2