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