@ -475,10 +475,11 @@ static char *get_str_from_var(const NumericVar *var);
static char * get_str_from_var_sci ( const NumericVar * var , int rscale ) ;
static Numeric make_result ( const NumericVar * var ) ;
static Numeric make_result_opt_error ( const NumericVar * var , bool * error ) ;
static void apply_typmod ( NumericVar * var , int32 typmod ) ;
static int32 numericvar_to_int32 ( const NumericVar * var ) ;
static bool numericvar_to_int32 ( const NumericVar * var , int32 * result ) ;
static bool numericvar_to_int64 ( const NumericVar * var , int64 * result ) ;
static void int64_to_numericvar ( int64 val , NumericVar * var ) ;
# ifdef HAVE_INT128
@ -1558,7 +1559,10 @@ width_bucket_numeric(PG_FUNCTION_ARGS)
}
/* if result exceeds the range of a legal int4, we ereport here */
result = numericvar_to_int32 ( & result_var ) ;
if ( ! numericvar_to_int32 ( & result_var , & result ) )
ereport ( ERROR ,
( errcode ( ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ) ,
errmsg ( " integer out of range " ) ) ) ;
free_var ( & count_var ) ;
free_var ( & result_var ) ;
@ -2406,6 +2410,23 @@ numeric_add(PG_FUNCTION_ARGS)
{
Numeric num1 = PG_GETARG_NUMERIC ( 0 ) ;
Numeric num2 = PG_GETARG_NUMERIC ( 1 ) ;
Numeric res ;
res = numeric_add_opt_error ( num1 , num2 , NULL ) ;
PG_RETURN_NUMERIC ( res ) ;
}
/*
* numeric_add_opt_error ( ) -
*
* Internal version of numeric_add ( ) . If " *have_error " flag is provided ,
* on error it ' s set to true , NULL returned . This is helpful when caller
* need to handle errors by itself .
*/
Numeric
numeric_add_opt_error ( Numeric num1 , Numeric num2 , bool * have_error )
{
NumericVar arg1 ;
NumericVar arg2 ;
NumericVar result ;
@ -2415,7 +2436,7 @@ numeric_add(PG_FUNCTION_ARGS)
* Handle NaN
*/
if ( NUMERIC_IS_NAN ( num1 ) | | NUMERIC_IS_NAN ( num2 ) )
PG_RETURN_NUMERIC ( make_result ( & const_nan ) ) ;
return make_result ( & const_nan ) ;
/*
* Unpack the values , let add_var ( ) compute the result and return it .
@ -2426,11 +2447,11 @@ numeric_add(PG_FUNCTION_ARGS)
init_var ( & result ) ;
add_var ( & arg1 , & arg2 , & result ) ;
res = make_result ( & result ) ;
res = make_result_opt_error ( & result , have_error ) ;
free_var ( & result ) ;
PG_RETURN_NUMERIC ( res ) ;
return res ;
}
@ -2444,6 +2465,24 @@ numeric_sub(PG_FUNCTION_ARGS)
{
Numeric num1 = PG_GETARG_NUMERIC ( 0 ) ;
Numeric num2 = PG_GETARG_NUMERIC ( 1 ) ;
Numeric res ;
res = numeric_sub_opt_error ( num1 , num2 , NULL ) ;
PG_RETURN_NUMERIC ( res ) ;
}
/*
* numeric_sub_opt_error ( ) -
*
* Internal version of numeric_sub ( ) . If " *have_error " flag is provided ,
* on error it ' s set to true , NULL returned . This is helpful when caller
* need to handle errors by itself .
*/
Numeric
numeric_sub_opt_error ( Numeric num1 , Numeric num2 , bool * have_error )
{
NumericVar arg1 ;
NumericVar arg2 ;
NumericVar result ;
@ -2453,7 +2492,7 @@ numeric_sub(PG_FUNCTION_ARGS)
* Handle NaN
*/
if ( NUMERIC_IS_NAN ( num1 ) | | NUMERIC_IS_NAN ( num2 ) )
PG_RETURN_NUMERIC ( make_result ( & const_nan ) ) ;
return make_result ( & const_nan ) ;
/*
* Unpack the values , let sub_var ( ) compute the result and return it .
@ -2464,11 +2503,11 @@ numeric_sub(PG_FUNCTION_ARGS)
init_var ( & result ) ;
sub_var ( & arg1 , & arg2 , & result ) ;
res = make_result ( & result ) ;
res = make_result_opt_error ( & result , have_error ) ;
free_var ( & result ) ;
PG_RETURN_NUMERIC ( res ) ;
return res ;
}
@ -2482,6 +2521,24 @@ numeric_mul(PG_FUNCTION_ARGS)
{
Numeric num1 = PG_GETARG_NUMERIC ( 0 ) ;
Numeric num2 = PG_GETARG_NUMERIC ( 1 ) ;
Numeric res ;
res = numeric_mul_opt_error ( num1 , num2 , NULL ) ;
PG_RETURN_NUMERIC ( res ) ;
}
/*
* numeric_mul_opt_error ( ) -
*
* Internal version of numeric_mul ( ) . If " *have_error " flag is provided ,
* on error it ' s set to true , NULL returned . This is helpful when caller
* need to handle errors by itself .
*/
Numeric
numeric_mul_opt_error ( Numeric num1 , Numeric num2 , bool * have_error )
{
NumericVar arg1 ;
NumericVar arg2 ;
NumericVar result ;
@ -2491,7 +2548,7 @@ numeric_mul(PG_FUNCTION_ARGS)
* Handle NaN
*/
if ( NUMERIC_IS_NAN ( num1 ) | | NUMERIC_IS_NAN ( num2 ) )
PG_RETURN_NUMERIC ( make_result ( & const_nan ) ) ;
return make_result ( & const_nan ) ;
/*
* Unpack the values , let mul_var ( ) compute the result and return it .
@ -2506,11 +2563,11 @@ numeric_mul(PG_FUNCTION_ARGS)
init_var ( & result ) ;
mul_var ( & arg1 , & arg2 , & result , arg1 . dscale + arg2 . dscale ) ;
res = make_result ( & result ) ;
res = make_result_opt_error ( & result , have_error ) ;
free_var ( & result ) ;
PG_RETURN_NUMERIC ( res ) ;
return res ;
}
@ -2524,6 +2581,24 @@ numeric_div(PG_FUNCTION_ARGS)
{
Numeric num1 = PG_GETARG_NUMERIC ( 0 ) ;
Numeric num2 = PG_GETARG_NUMERIC ( 1 ) ;
Numeric res ;
res = numeric_div_opt_error ( num1 , num2 , NULL ) ;
PG_RETURN_NUMERIC ( res ) ;
}
/*
* numeric_div_opt_error ( ) -
*
* Internal version of numeric_div ( ) . If " *have_error " flag is provided ,
* on error it ' s set to true , NULL returned . This is helpful when caller
* need to handle errors by itself .
*/
Numeric
numeric_div_opt_error ( Numeric num1 , Numeric num2 , bool * have_error )
{
NumericVar arg1 ;
NumericVar arg2 ;
NumericVar result ;
@ -2534,7 +2609,7 @@ numeric_div(PG_FUNCTION_ARGS)
* Handle NaN
*/
if ( NUMERIC_IS_NAN ( num1 ) | | NUMERIC_IS_NAN ( num2 ) )
PG_RETURN_NUMERIC ( make_result ( & const_nan ) ) ;
return make_result ( & const_nan ) ;
/*
* Unpack the arguments
@ -2549,16 +2624,25 @@ numeric_div(PG_FUNCTION_ARGS)
*/
rscale = select_div_scale ( & arg1 , & arg2 ) ;
/*
* If " have_error " is provided , check for division by zero here
*/
if ( have_error & & ( arg2 . ndigits = = 0 | | arg2 . digits [ 0 ] = = 0 ) )
{
* have_error = true ;
return NULL ;
}
/*
* Do the divide and return the result
*/
div_var ( & arg1 , & arg2 , & result , rscale , true ) ;
res = make_result ( & result ) ;
res = make_result_opt_error ( & result , have_error ) ;
free_var ( & result ) ;
PG_RETURN_NUMERIC ( res ) ;
return res ;
}
@ -2615,25 +2699,52 @@ numeric_mod(PG_FUNCTION_ARGS)
Numeric num1 = PG_GETARG_NUMERIC ( 0 ) ;
Numeric num2 = PG_GETARG_NUMERIC ( 1 ) ;
Numeric res ;
res = numeric_mod_opt_error ( num1 , num2 , NULL ) ;
PG_RETURN_NUMERIC ( res ) ;
}
/*
* numeric_mod_opt_error ( ) -
*
* Internal version of numeric_mod ( ) . If " *have_error " flag is provided ,
* on error it ' s set to true , NULL returned . This is helpful when caller
* need to handle errors by itself .
*/
Numeric
numeric_mod_opt_error ( Numeric num1 , Numeric num2 , bool * have_error )
{
Numeric res ;
NumericVar arg1 ;
NumericVar arg2 ;
NumericVar result ;
if ( NUMERIC_IS_NAN ( num1 ) | | NUMERIC_IS_NAN ( num2 ) )
PG_RETURN_NUMERIC ( make_result ( & const_nan ) ) ;
return make_result ( & const_nan ) ;
init_var_from_num ( num1 , & arg1 ) ;
init_var_from_num ( num2 , & arg2 ) ;
init_var ( & result ) ;
/*
* If " have_error " is provided , check for division by zero here
*/
if ( have_error & & ( arg2 . ndigits = = 0 | | arg2 . digits [ 0 ] = = 0 ) )
{
* have_error = true ;
return NULL ;
}
mod_var ( & arg1 , & arg2 , & result ) ;
res = make_result ( & result ) ;
res = make_result_opt_error ( & result , NULL ) ;
free_var ( & result ) ;
PG_RETURN_NUMERIC ( res ) ;
return res ;
}
@ -3090,52 +3201,75 @@ int4_numeric(PG_FUNCTION_ARGS)
PG_RETURN_NUMERIC ( res ) ;
}
Datum
numeric_int4 ( PG_FUNCTION_ARGS )
int32
numeric_int4_opt_error ( Numeric num , bool * have_error )
{
Numeric num = PG_GETARG_NUMERIC ( 0 ) ;
NumericVar x ;
int32 result ;
/* XXX would it be better to return NULL? */
if ( NUMERIC_IS_NAN ( num ) )
{
if ( have_error )
{
* have_error = true ;
return 0 ;
}
else
{
ereport ( ERROR ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " cannot convert NaN to integer " ) ) ) ;
}
}
/* Convert to variable format, then convert to int4 */
init_var_from_num ( num , & x ) ;
result = numericvar_to_int32 ( & x ) ;
PG_RETURN_INT32 ( result ) ;
if ( ! numericvar_to_int32 ( & x , & result ) )
{
if ( have_error )
{
* have_error = true ;
return 0 ;
}
else
{
ereport ( ERROR ,
( errcode ( ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ) ,
errmsg ( " integer out of range " ) ) ) ;
}
}
return result ;
}
Datum
numeric_int4 ( PG_FUNCTION_ARGS )
{
Numeric num = PG_GETARG_NUMERIC ( 0 ) ;
PG_RETURN_INT32 ( numeric_int4_opt_error ( num , NULL ) ) ;
}
/*
* Given a NumericVar , convert it to an int32 . If the NumericVar
* exceeds the range of an int32 , raise the appropriate error via
* ereport ( ) . The input NumericVar is * not * free ' d .
* exceeds the range of an int32 , false is returned , otherwise true is returned .
* The input NumericVar is * not * free ' d .
*/
static int32
numericvar_to_int32 ( const NumericVar * var )
static bool
numericvar_to_int32 ( const NumericVar * var , int32 * result )
{
int32 result ;
int64 val ;
if ( ! numericvar_to_int64 ( var , & val ) )
ereport ( ERROR ,
( errcode ( ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ) ,
errmsg ( " integer out of range " ) ) ) ;
return false ;
/* Down-convert to int4 */
result = ( int32 ) val ;
* result = ( int32 ) val ;
/* Test for overflow by reverse-conversion. */
if ( ( int64 ) result ! = val )
ereport ( ERROR ,
( errcode ( ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ) ,
errmsg ( " integer out of range " ) ) ) ;
return result ;
return ( ( int64 ) * result = = val ) ;
}
Datum
@ -6098,13 +6232,15 @@ get_str_from_var_sci(const NumericVar *var, int rscale)
/*
* make_result ( ) -
* make_result_opt_error ( ) -
*
* Create the packed db numeric format in palloc ( ) ' d memory from
* a variable .
* a variable . If " *have_error " flag is provided , on error it ' s set to
* true , NULL returned . This is helpful when caller need to handle errors
* by itself .
*/
static Numeric
make_result ( const NumericVar * var )
make_result_opt_error ( const NumericVar * var , bool * have_erro r )
{
Numeric result ;
NumericDigit * digits = var - > digits ;
@ -6175,15 +6311,37 @@ make_result(const NumericVar *var)
/* Check for overflow of int16 fields */
if ( NUMERIC_WEIGHT ( result ) ! = weight | |
NUMERIC_DSCALE ( result ) ! = var - > dscale )
{
if ( have_error )
{
* have_error = true ;
return NULL ;
}
else
{
ereport ( ERROR ,
( errcode ( ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ) ,
errmsg ( " value overflows numeric format " ) ) ) ;
}
}
dump_numeric ( " make_result() " , result ) ;
return result ;
}
/*
* make_result ( ) -
*
* An interface to make_result_opt_error ( ) without " have_error " argument .
*/
static Numeric
make_result ( const NumericVar * var )
{
return make_result_opt_error ( var , NULL ) ;
}
/*
* apply_typmod ( ) -
*