Commit 0e9d75c6 authored by Jan Wieck's avatar Jan Wieck

Added NUMERIC data type with many builtin funcitons, operators

and aggregates.

Jan
parent 6059c5a1
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.40 1998/12/18 09:10:32 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.41 1998/12/30 19:56:28 wieck Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#include "utils/elog.h" #include "utils/elog.h"
#include "access/xact.h" #include "access/xact.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "utils/numeric.h"
#ifdef MULTIBYTE #ifdef MULTIBYTE
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
...@@ -229,7 +230,8 @@ Oid param_type(int t); /* used in parse_expr.c */ ...@@ -229,7 +230,8 @@ Oid param_type(int t); /* used in parse_expr.c */
%type <str> generic, numeric, character, datetime %type <str> generic, numeric, character, datetime
%type <str> extract_arg %type <str> extract_arg
%type <str> opt_charset, opt_collate %type <str> opt_charset, opt_collate
%type <str> opt_float, opt_numeric, opt_decimal %type <str> opt_float
%type <ival> opt_numeric, opt_decimal
%type <boolean> opt_varying, opt_timezone %type <boolean> opt_varying, opt_timezone
%type <ival> Iconst %type <ival> Iconst
...@@ -3018,8 +3020,7 @@ generic: IDENT { $$ = $1; } ...@@ -3018,8 +3020,7 @@ generic: IDENT { $$ = $1; }
/* SQL92 numeric data types /* SQL92 numeric data types
* Check FLOAT() precision limits assuming IEEE floating types. * Check FLOAT() precision limits assuming IEEE floating types.
* Provide rudimentary DECIMAL() and NUMERIC() implementations * Provide real DECIMAL() and NUMERIC() implementations now - Jan 1998-12-30
* by checking parameters and making sure they match what is possible with INTEGER.
* - thomas 1997-09-18 * - thomas 1997-09-18
*/ */
Numeric: FLOAT opt_float Numeric: FLOAT opt_float
...@@ -3036,14 +3037,14 @@ Numeric: FLOAT opt_float ...@@ -3036,14 +3037,14 @@ Numeric: FLOAT opt_float
| DECIMAL opt_decimal | DECIMAL opt_decimal
{ {
$$ = makeNode(TypeName); $$ = makeNode(TypeName);
$$->name = xlateSqlType("integer"); $$->name = xlateSqlType("numeric");
$$->typmod = -1; $$->typmod = -1;
} }
| NUMERIC opt_numeric | NUMERIC opt_numeric
{ {
$$ = makeNode(TypeName); $$ = makeNode(TypeName);
$$->name = xlateSqlType("integer"); $$->name = xlateSqlType("numeric");
$$->typmod = -1; $$->typmod = $2;
} }
; ;
...@@ -3052,7 +3053,7 @@ numeric: FLOAT ...@@ -3052,7 +3053,7 @@ numeric: FLOAT
| DOUBLE PRECISION | DOUBLE PRECISION
{ $$ = xlateSqlType("float8"); } { $$ = xlateSqlType("float8"); }
| DECIMAL | DECIMAL
{ $$ = xlateSqlType("decimal"); } { $$ = xlateSqlType("numeric"); }
| NUMERIC | NUMERIC
{ $$ = xlateSqlType("numeric"); } { $$ = xlateSqlType("numeric"); }
; ;
...@@ -3076,42 +3077,55 @@ opt_float: '(' Iconst ')' ...@@ -3076,42 +3077,55 @@ opt_float: '(' Iconst ')'
opt_numeric: '(' Iconst ',' Iconst ')' opt_numeric: '(' Iconst ',' Iconst ')'
{ {
if ($2 != 9) if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
elog(ERROR,"NUMERIC precision %d must be 9",$2); elog(ERROR,"NUMERIC precision %d must be beween 1 and %d",
if ($4 != 0) $2, NUMERIC_MAX_PRECISION);
elog(ERROR,"NUMERIC scale %d must be zero",$4); if ($4 < 0 || $4 > $2)
elog(ERROR,"NUMERIC scale %d must be between 0 and precision %d",
$4,$2);
$$ = (($2 << 16) | $4) + VARHDRSZ;
} }
| '(' Iconst ')' | '(' Iconst ')'
{ {
if ($2 != 9) if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
elog(ERROR,"NUMERIC precision %d must be 9",$2); elog(ERROR,"NUMERIC precision %d must be beween 1 and %d",
$2, NUMERIC_MAX_PRECISION);
$$ = ($2 << 16) + VARHDRSZ;
} }
| /*EMPTY*/ | /*EMPTY*/
{ {
$$ = NULL; $$ = ((NUMERIC_DEFAULT_PRECISION << 16) | NUMERIC_DEFAULT_SCALE) + VARHDRSZ;
} }
; ;
opt_decimal: '(' Iconst ',' Iconst ')' opt_decimal: '(' Iconst ',' Iconst ')'
{ {
if ($2 > 9) if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
elog(ERROR,"DECIMAL precision %d exceeds implementation limit of 9",$2); elog(ERROR,"DECIMAL precision %d must be beween 1 and %d",
if ($4 != 0) $2, NUMERIC_MAX_PRECISION);
elog(ERROR,"DECIMAL scale %d must be zero",$4); if ($4 < 0 || $4 > $2)
$$ = NULL; elog(ERROR,"DECIMAL scale %d must be between 0 and precision %d",
$4,$2);
$$ = (($2 << 16) | $4) + VARHDRSZ;
} }
| '(' Iconst ')' | '(' Iconst ')'
{ {
if ($2 > 9) if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
elog(ERROR,"DECIMAL precision %d exceeds implementation limit of 9",$2); elog(ERROR,"DECIMAL precision %d must be beween 1 and %d",
$$ = NULL; $2, NUMERIC_MAX_PRECISION);
$$ = ($2 << 16) + VARHDRSZ;
} }
| /*EMPTY*/ | /*EMPTY*/
{ {
$$ = NULL; $$ = ((NUMERIC_DEFAULT_PRECISION << 16) | NUMERIC_DEFAULT_SCALE) + VARHDRSZ;
} }
; ;
/* SQL92 character data types /* SQL92 character data types
* The following implements CHAR() and VARCHAR(). * The following implements CHAR() and VARCHAR().
* We do it here instead of the 'Generic' production * We do it here instead of the 'Generic' production
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# Makefile for utils/adt # Makefile for utils/adt
# #
# IDENTIFICATION # IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/utils/adt/Makefile,v 1.20 1998/10/22 20:40:44 momjian Exp $ # $Header: /cvsroot/pgsql/src/backend/utils/adt/Makefile,v 1.21 1998/12/30 19:56:29 wieck Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
...@@ -20,7 +20,7 @@ endif ...@@ -20,7 +20,7 @@ endif
OBJS = acl.o arrayfuncs.o arrayutils.o bool.o cash.o char.o chunk.o \ OBJS = acl.o arrayfuncs.o arrayutils.o bool.o cash.o char.o chunk.o \
date.o datetime.o datum.o dt.o filename.o float.o \ date.o datetime.o datum.o dt.o filename.o float.o \
geo_ops.o geo_selfuncs.o int.o int8.o like.o \ geo_ops.o geo_selfuncs.o int.o int8.o like.o \
misc.o nabstime.o name.o not_in.o numutils.o \ misc.o nabstime.o name.o not_in.o numeric.o numutils.o \
oid.o oracle_compat.o \ oid.o oracle_compat.o \
regexp.o regproc.o ruleutils.o selfuncs.o sets.o \ regexp.o regproc.o ruleutils.o selfuncs.o sets.o \
tid.o timestamp.o varchar.o varlena.o version.o \ tid.o timestamp.o varchar.o varlena.o version.o \
......
/* ----------
* numeric.c -
*
* An exact numeric data type for the Postgres database system
*
* 1998 Jan Wieck
*
* $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.1 1998/12/30 19:56:29 wieck Exp $
*
* ----------
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <float.h>
#include <math.h>
#include <nan.h>
#include <errno.h>
#include <sys/types.h>
#include "postgres.h"
#include "utils/builtins.h"
#include "utils/palloc.h"
#include "utils/numeric.h"
/* ----------
* Uncomment the following to enable compilation of dump_numeric()
* and dump_var() and to get a dump of any result produced by make_result().
* ----------
#define NUMERIC_DEBUG
*/
/* ----------
* Local definitions
* ----------
*/
#define NUMERIC_MIN_BUFSIZE 2048
#define NUMERIC_MAX_FREEBUFS 20
#ifndef MIN
# define MIN(a,b) (((a)<(b)) ? (a) : (b))
#endif
#ifndef MAX
# define MAX(a,b) (((a)>(b)) ? (a) : (b))
#endif
/* ----------
* Local data types
* ----------
*/
typedef unsigned char NumericDigit;
typedef struct NumericDigitBuf {
struct NumericDigitBuf *prev;
struct NumericDigitBuf *next;
int size;
} NumericDigitBuf;
typedef struct NumericVar {
int ndigits;
int weight;
int rscale;
int dscale;
int sign;
NumericDigitBuf *buf;
NumericDigit *digits;
} NumericVar;
/* ----------
* Local data
* ----------
*/
static NumericDigitBuf *digitbuf_freelist = NULL;
static NumericDigitBuf *digitbuf_usedlist = NULL;
static int digitbuf_nfree = 0;
static int global_rscale = NUMERIC_MIN_RESULT_SCALE;
/* ----------
* Some preinitialized variables we need often
* ----------
*/
static NumericDigit const_zero_data[1] = {0};
static NumericVar const_zero =
{0, 0, 0, 0, NUMERIC_POS, NULL, const_zero_data};
static NumericDigit const_one_data[1] = {1};
static NumericVar const_one =
{1, 0, 0, 0, NUMERIC_POS, NULL, const_one_data};
static NumericDigit const_two_data[1] = {2};
static NumericVar const_two =
{1, 0, 0, 0, NUMERIC_POS, NULL, const_two_data};
static NumericVar const_nan =
{0, 0, 0, 0, NUMERIC_NAN, NULL, NULL};
/* ----------
* Local functions
* ----------
*/
#ifdef NUMERIC_DEBUG
static void dump_numeric(char *str, Numeric num);
static void dump_var(char *str, NumericVar *var);
#else
#define dump_numeric(s,n)
#define dump_var(s,v)
#endif
static NumericDigitBuf *digitbuf_alloc(int size);
static void digitbuf_free(NumericDigitBuf *buf);
#define init_var(v) memset(v,0,sizeof(NumericVar))
static void free_var(NumericVar *var);
static void free_allvars(void);
static void set_var_from_str(char *str, NumericVar *dest);
static void set_var_from_num(Numeric value, NumericVar *dest);
static void set_var_from_var(NumericVar *value, NumericVar *dest);
static Numeric make_result(NumericVar *var);
static void apply_typmod(NumericVar *var, int32 typmod);
static int cmp_var(NumericVar *var1, NumericVar *var2);
static void add_var(NumericVar *var1, NumericVar *var2, NumericVar *result);
static void sub_var(NumericVar *var1, NumericVar *var2, NumericVar *result);
static void mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result);
static void div_var(NumericVar *var1, NumericVar *var2, NumericVar *result);
static void mod_var(NumericVar *var1, NumericVar *var2, NumericVar *result);
static void ceil_var(NumericVar *var, NumericVar *result);
static void floor_var(NumericVar *var, NumericVar *result);
static void sqrt_var(NumericVar *arg, NumericVar *result);
static void exp_var(NumericVar *arg, NumericVar *result);
static void ln_var(NumericVar *arg, NumericVar *result);
static void log_var(NumericVar *base, NumericVar *num, NumericVar *result);
static void power_var(NumericVar *base, NumericVar *exp, NumericVar *result);
static int cmp_abs(NumericVar *var1, NumericVar *var2);
static void add_abs(NumericVar *var1, NumericVar *var2, NumericVar *result);
static void sub_abs(NumericVar *var1, NumericVar *var2, NumericVar *result);
/* ----------------------------------------------------------------------
*
* Input-, output- and rounding-functions
*
* ----------------------------------------------------------------------
*/
/* ----------
* numeric_in() -
*
* Input function for numeric data type
* ----------
*/
Numeric
numeric_in(char *str, int dummy, int32 typmod)
{
NumericVar value;
Numeric res;
/* ----------
* Check for NULL
* ----------
*/
if (str == NULL)
return NULL;
if (strcmp(str, "NULL") == 0)
return NULL;
/* ----------
* Check for NaN
* ----------
*/
if (strcmp(str, "NaN") == 0)
return make_result(&const_nan);
/* ----------
* Use set_var_from_str() to parse the input string
* and return it in the packed DB storage format
* ----------
*/
init_var(&value);
set_var_from_str(str, &value);
apply_typmod(&value, typmod);
res = make_result(&value);
free_var(&value);
return res;
}
/* ----------
* numeric_out() -
*
* Output function for numeric data type
* ----------
*/
char *
numeric_out(Numeric num)
{
char *str;
char *cp;
NumericVar x;
int i;
int d;
/* ----------
* Handle NULL
* ----------
*/
if (num == NULL)
{
str = palloc(5);
strcpy(str, "NULL");
return str;
}
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num))
{
str = palloc(4);
strcpy(str, "NaN");
return str;
}
/* ----------
* Get the number in the variable format
* ----------
*/
init_var(&x);
set_var_from_num(num, &x);
/* ----------
* Allocate space for the result
* ----------
*/
str = palloc(x.dscale + MAX(0, x.weight) + 5);
cp = str;
/* ----------
* Output a dash for negative values
* ----------
*/
if (x.sign == NUMERIC_NEG)
*cp++ = '-';
/* ----------
* Check if we must round up before printing the value and
* do so.
* ----------
*/
if (x.dscale < x.rscale && (x.dscale + x.weight + 1) < x.ndigits)
{
int j;
int carry;
j = x.dscale + x.weight + 1;
carry = (x.digits[j] > 4) ? 1 : 0;
while (carry)
{
j--;
carry += x.digits[j];
x.digits[j] = carry % 10;
carry /= 10;
}
if (j < 0)
{
x.digits--;
x.weight++;
}
}
/* ----------
* Output all digits before the decimal point
* ----------
*/
i = MAX(x.weight, 0);
d = 0;
while (i >= 0)
{
if (i <= x.weight && d < x.ndigits)
*cp++ = x.digits[d++] + '0';
else
*cp++ = '0';
i--;
}
/* ----------
* If requested, output a decimal point and all the digits
* that follow it.
* ----------
*/
if (x.dscale > 0)
{
*cp++ = '.';
while (i >= -x.dscale)
{
if (i <= x.weight && d < x.ndigits)
*cp++ = x.digits[d++] + '0';
else
*cp++ = '0';
i--;
}
}
/* ----------
* Get rid of the variable, terminate the string and return it
* ----------
*/
free_var(&x);
*cp = '\0';
return str;
}
/* ----------
* numeric() -
*
* This is a special function called by the Postgres database system
* before a value is stored in a tuples attribute. The precision and
* scale of the attribute have to be applied on the value.
* ----------
*/
Numeric
numeric(Numeric num, int32 typmod)
{
Numeric new;
int32 tmp_typmod;
int precision;
int scale;
int maxweight;
NumericVar var;
/* ----------
* Handle NULL
* ----------
*/
if (num == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num))
return make_result(&const_nan);
/* ----------
* If the value isn't a valid type modifier, simply return a
* copy of the input value
* ----------
*/
if (typmod < (int32)(VARHDRSZ))
{
new = (Numeric)palloc(num->varlen);
memcpy(new, num, num->varlen);
return new;
}
/* ----------
* Get the precision and scale out of the typmod value
* ----------
*/
tmp_typmod = typmod - VARHDRSZ;
precision = (tmp_typmod >> 16) & 0xffff;
scale = tmp_typmod & 0xffff;
maxweight = precision - scale;
/* ----------
* If the number is in bounds and due to the present result scale
* no rounding could be necessary, make a copy of the input and
* modify it's header fields.
* ----------
*/
if (num->n_weight < maxweight && scale >= num->n_rscale)
{
new = (Numeric)palloc(num->varlen);
memcpy(new, num, num->varlen);
new->n_rscale = scale;
new->n_sign_dscale = NUMERIC_SIGN(new) |
((uint16)scale & ~NUMERIC_SIGN_MASK);
return new;
}
/* ----------
* We really need to fiddle with things - unpack the number into
* a variable and let apply_typmod() do it.
* ----------
*/
init_var(&var);
set_var_from_num(num, &var);
apply_typmod(&var, typmod);
new = make_result(&var);
free_var(&var);
return new;
}
/* ----------------------------------------------------------------------
*
* Rounding and the like
*
* ----------------------------------------------------------------------
*/
Numeric
numeric_abs(Numeric num)
{
Numeric res;
/* ----------
* Handle NULL
* ----------
*/
if (num == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num))
return make_result(&const_nan);
/* ----------
* Do it the easy way directly on the packed format
* ----------
*/
res = (Numeric)palloc(num->varlen);
memcpy(res, num, num->varlen);
res->n_sign_dscale = NUMERIC_POS | NUMERIC_DSCALE(num);
return res;
}
Numeric
numeric_sign(Numeric num)
{
Numeric res;
NumericVar result;
/* ----------
* Handle NULL
* ----------
*/
if (num == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num))
return make_result(&const_nan);
init_var(&result);
/* ----------
* The packed format is known to be totally zero digit trimmed
* allways. So we can identify a ZERO by the fact that there
* are no digits at all.
* ----------
*/
if (num->varlen == NUMERIC_HDRSZ)
{
set_var_from_var(&const_zero, &result);
}
else
{
/* ----------
* And if there are some, we return a copy of ONE
* with the sign of our argument
* ----------
*/
set_var_from_var(&const_one, &result);
result.sign = NUMERIC_SIGN(num);
}
res = make_result(&result);
free_var(&result);
return res;
}
/* ----------
* numeric_round() -
*
* Modify rscale and dscale of a number and round it if required.
* ----------
*/
Numeric
numeric_round(Numeric num, int32 scale)
{
int32 typmod;
/* ----------
* Handle NULL
* ----------
*/
if (num == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num))
return make_result(&const_nan);
/* ----------
* Check that the requested scale is valid
* ----------
*/
if (scale < 0 || scale > NUMERIC_MAX_DISPLAY_SCALE)
{
free_allvars();
elog(ERROR, "illegal numeric scale %d - must be between 0 and %d",
scale, NUMERIC_MAX_DISPLAY_SCALE);
}
/* ----------
* Let numeric() and in turn apply_typmod() do the job
* ----------
*/
typmod = (((num->n_weight + scale + 1) << 16) | scale) + VARHDRSZ;
return numeric(num, typmod);
}
/* ----------
* numeric_trunc() -
*
* Modify rscale and dscale of a number and cut it if required.
* ----------
*/
Numeric
numeric_trunc(Numeric num, int32 scale)
{
Numeric res;
NumericVar arg;
/* ----------
* Handle NULL
* ----------
*/
if (num == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num))
return make_result(&const_nan);
/* ----------
* Check that the requested scale is valid
* ----------
*/
if (scale < 0 || scale > NUMERIC_MAX_DISPLAY_SCALE)
{
free_allvars();
elog(ERROR, "illegal numeric scale %d - must be between 0 and %d",
scale, NUMERIC_MAX_DISPLAY_SCALE);
}
/* ----------
* Unpack the argument and truncate it
* ----------
*/
init_var(&arg);
set_var_from_num(num, &arg);
arg.rscale = scale;
arg.dscale = scale;
arg.ndigits = MIN(arg.ndigits, MAX(0, arg.weight + scale + 1));
while (arg.ndigits > 0 && arg.digits[arg.ndigits - 1] == 0)
{
arg.ndigits--;
}
/* ----------
* Return the truncated result
* ----------
*/
res = make_result(&arg);
free_var(&arg);
return res;
}
/* ----------
* numeric_ceil() -
*
* Return the smallest integer greater than or equal to the argument
* ----------
*/
Numeric
numeric_ceil(Numeric num)
{
Numeric res;
NumericVar result;
if (num == NULL)
return NULL;
if (NUMERIC_IS_NAN(num))
return make_result(&const_nan);
init_var(&result);
set_var_from_num(num, &result);
ceil_var(&result, &result);
result.dscale = 0;
res = make_result(&result);
free_var(&result);
return res;
}
/* ----------
* numeric_floor() -
*
* Return the largest integer equal to or less than the argument
* ----------
*/
Numeric
numeric_floor(Numeric num)
{
Numeric res;
NumericVar result;
if (num == NULL)
return NULL;
if (NUMERIC_IS_NAN(num))
return make_result(&const_nan);
init_var(&result);
set_var_from_num(num, &result);
floor_var(&result, &result);
result.dscale = 0;
res = make_result(&result);
free_var(&result);
return res;
}
/* ----------------------------------------------------------------------
*
* Comparision functions
*
* ----------------------------------------------------------------------
*/
bool
numeric_eq(Numeric num1, Numeric num2)
{
int result;
NumericVar arg1;
NumericVar arg2;
if (num1 == NULL || num2 == NULL)
return FALSE;
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
return FALSE;
init_var(&arg1);
init_var(&arg2);
set_var_from_num(num1, &arg1);
set_var_from_num(num2, &arg2);
result = cmp_var(&arg1, &arg2);
free_var(&arg1);
free_var(&arg2);
return (result == 0);
}
bool
numeric_ne(Numeric num1, Numeric num2)
{
int result;
NumericVar arg1;
NumericVar arg2;
if (num1 == NULL || num2 == NULL)
return FALSE;
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
return FALSE;
init_var(&arg1);
init_var(&arg2);
set_var_from_num(num1, &arg1);
set_var_from_num(num2, &arg2);
result = cmp_var(&arg1, &arg2);
free_var(&arg1);
free_var(&arg2);
return (result != 0);
}
bool
numeric_gt(Numeric num1, Numeric num2)
{
int result;
NumericVar arg1;
NumericVar arg2;
if (num1 == NULL || num2 == NULL)
return FALSE;
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
return FALSE;
init_var(&arg1);
init_var(&arg2);
set_var_from_num(num1, &arg1);
set_var_from_num(num2, &arg2);
result = cmp_var(&arg1, &arg2);
free_var(&arg1);
free_var(&arg2);
return (result > 0);
}
bool
numeric_ge(Numeric num1, Numeric num2)
{
int result;
NumericVar arg1;
NumericVar arg2;
if (num1 == NULL || num2 == NULL)
return FALSE;
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
return FALSE;
init_var(&arg1);
init_var(&arg2);
set_var_from_num(num1, &arg1);
set_var_from_num(num2, &arg2);
result = cmp_var(&arg1, &arg2);
free_var(&arg1);
free_var(&arg2);
return (result >= 0);
}
bool
numeric_lt(Numeric num1, Numeric num2)
{
int result;
NumericVar arg1;
NumericVar arg2;
if (num1 == NULL || num2 == NULL)
return FALSE;
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
return FALSE;
init_var(&arg1);
init_var(&arg2);
set_var_from_num(num1, &arg1);
set_var_from_num(num2, &arg2);
result = cmp_var(&arg1, &arg2);
free_var(&arg1);
free_var(&arg2);
return (result < 0);
}
bool
numeric_le(Numeric num1, Numeric num2)
{
int result;
NumericVar arg1;
NumericVar arg2;
if (num1 == NULL || num2 == NULL)
return FALSE;
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
return FALSE;
init_var(&arg1);
init_var(&arg2);
set_var_from_num(num1, &arg1);
set_var_from_num(num2, &arg2);
result = cmp_var(&arg1, &arg2);
free_var(&arg1);
free_var(&arg2);
return (result <= 0);
}
/* ----------------------------------------------------------------------
*
* Arithmetic base functions
*
* ----------------------------------------------------------------------
*/
/* ----------
* numeric_add() -
*
* Add two numerics
* ----------
*/
Numeric
numeric_add(Numeric num1, Numeric num2)
{
NumericVar arg1;
NumericVar arg2;
NumericVar result;
Numeric res;
/* ----------
* Handle NULL
* ----------
*/
if (num1 == NULL || num2 == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
return make_result(&const_nan);
/* ----------
* Unpack the values, let add_var() compute the result
* and return it. The internals of add_var() will automatically
* set the correct result and display scales in the result.
* ----------
*/
init_var(&arg1);
init_var(&arg2);
init_var(&result);
set_var_from_num(num1, &arg1);
set_var_from_num(num2, &arg2);
add_var(&arg1, &arg2, &result);
res = make_result(&result);
free_var(&arg1);
free_var(&arg2);
free_var(&result);
return res;
}
/* ----------
* numeric_sub() -
*
* Subtract one numeric from another
* ----------
*/
Numeric
numeric_sub(Numeric num1, Numeric num2)
{
NumericVar arg1;
NumericVar arg2;
NumericVar result;
Numeric res;
/* ----------
* Handle NULL
* ----------
*/
if (num1 == NULL || num2 == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
return make_result(&const_nan);
/* ----------
* Unpack the two arguments, let sub_var() compute the
* result and return it.
* ----------
*/
init_var(&arg1);
init_var(&arg2);
init_var(&result);
set_var_from_num(num1, &arg1);
set_var_from_num(num2, &arg2);
sub_var(&arg1, &arg2, &result);
res = make_result(&result);
free_var(&arg1);
free_var(&arg2);
free_var(&result);
return res;
}
/* ----------
* numeric_mul() -
*
* Calculate the product of two numerics
* ----------
*/
Numeric
numeric_mul(Numeric num1, Numeric num2)
{
NumericVar arg1;
NumericVar arg2;
NumericVar result;
Numeric res;
/* ----------
* Handle NULL
* ----------
*/
if (num1 == NULL || num2 == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
return make_result(&const_nan);
/* ----------
* Unpack the arguments, let mul_var() compute the result
* and return it. Unlike add_var() and sub_var(), mul_var()
* will round the result to the scale stored in global_rscale.
* In the case of numeric_mul(), which is invoked for the *
* operator on numerics, we set it to the exact representation
* for the product (rscale = sum(rscale of arg1, rscale of arg2)
* and the same for the dscale).
* ----------
*/
init_var(&arg1);
init_var(&arg2);
init_var(&result);
set_var_from_num(num1, &arg1);
set_var_from_num(num2, &arg2);
global_rscale = arg1.rscale + arg2.rscale;
mul_var(&arg1, &arg2, &result);
result.dscale = arg1.dscale + arg2.dscale;
res = make_result(&result);
free_var(&arg1);
free_var(&arg2);
free_var(&result);
return res;
}
/* ----------
* numeric_div() -
*
* Divide one numeric into another
* ----------
*/
Numeric
numeric_div(Numeric num1, Numeric num2)
{
NumericVar arg1;
NumericVar arg2;
NumericVar result;
Numeric res;
int res_dscale;
/* ----------
* Handle NULL
* ----------
*/
if (num1 == NULL || num2 == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
return make_result(&const_nan);
/* ----------
* Unpack the arguments
* ----------
*/
init_var(&arg1);
init_var(&arg2);
init_var(&result);
set_var_from_num(num1, &arg1);
set_var_from_num(num2, &arg2);
/* ----------
* The result scale of a division isn't specified in any
* SQL standard. For Postgres it is the following (where
* SR, DR are the result- and display-scales of the returned
* value, S1, D1, S2 and D2 are the scales of the two arguments,
* The minimum and maximum scales are compile time options from
* numeric.h):
*
* DR = MIN(MAX(D1 + D2, MIN_DISPLAY_SCALE))
* SR = MIN(MAX(MAX(S1 + S2, MIN_RESULT_SCALE), DR + 4), MAX_RESULT_SCALE)
*
* By default, any result is computed with a minimum of 34 digits
* after the decimal point or at least with 4 digits more than
* displayed.
* ----------
*/
res_dscale = MAX(arg1.dscale + arg2.dscale, NUMERIC_MIN_DISPLAY_SCALE);
res_dscale = MIN(res_dscale, NUMERIC_MAX_DISPLAY_SCALE);
global_rscale = MAX(arg1.rscale + arg2.rscale,
NUMERIC_MIN_RESULT_SCALE);
global_rscale = MAX(global_rscale, res_dscale + 4);
global_rscale = MIN(global_rscale, NUMERIC_MAX_RESULT_SCALE);
/* ----------
* Do the divide, set the display scale and return the result
* ----------
*/
div_var(&arg1, &arg2, &result);
result.dscale = res_dscale;
res = make_result(&result);
free_var(&arg1);
free_var(&arg2);
free_var(&result);
return res;
}
/* ----------
* numeric_mod() -
*
* Calculate the modulo of two numerics
* ----------
*/
Numeric
numeric_mod(Numeric num1, Numeric num2)
{
Numeric res;
NumericVar arg1;
NumericVar arg2;
NumericVar result;
if (num1 == NULL || num2 == NULL)
return NULL;
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
return make_result(&const_nan);
init_var(&arg1);
init_var(&arg2);
init_var(&result);
set_var_from_num(num1, &arg1);
set_var_from_num(num2, &arg2);
mod_var(&arg1, &arg2, &result);
result.dscale = result.rscale;
res = make_result(&result);
free_var(&result);
free_var(&arg2);
free_var(&arg1);
return res;
}
/* ----------
* numeric_inc() -
*
* Increment a number by one
* ----------
*/
Numeric
numeric_inc(Numeric num)
{
NumericVar arg;
Numeric res;
/* ----------
* Handle NULL
* ----------
*/
if (num == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num))
return make_result(&const_nan);
/* ----------
* Compute the result and return it
* ----------
*/
init_var(&arg);
set_var_from_num(num, &arg);
add_var(&arg, &const_one, &arg);
res = make_result(&arg);
free_var(&arg);
return res;
}
/* ----------
* numeric_dec() -
*
* Decrement a number by one
* ----------
*/
Numeric
numeric_dec(Numeric num)
{
NumericVar arg;
Numeric res;
/* ----------
* Handle NULL
* ----------
*/
if (num == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num))
return make_result(&const_nan);
/* ----------
* Compute the result and return it
* ----------
*/
init_var(&arg);
set_var_from_num(num, &arg);
sub_var(&arg, &const_one, &arg);
res = make_result(&arg);
free_var(&arg);
return res;
}
/* ----------
* numeric_smaller() -
*
* Return the smaller of two numbers
* ----------
*/
Numeric
numeric_smaller(Numeric num1, Numeric num2)
{
NumericVar arg1;
NumericVar arg2;
Numeric res;
/* ----------
* Handle NULL
* ----------
*/
if (num1 == NULL || num2 == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
return make_result(&const_nan);
/* ----------
* Unpack the values, and decide which is the smaller one
* ----------
*/
init_var(&arg1);
init_var(&arg2);
set_var_from_num(num1, &arg1);
set_var_from_num(num2, &arg2);
if (cmp_var(&arg1, &arg2) <= 0)
res = make_result(&arg1);
else
res = make_result(&arg2);
free_var(&arg1);
free_var(&arg2);
return res;
}
/* ----------
* numeric_larger() -
*
* Return the larger of two numbers
* ----------
*/
Numeric
numeric_larger(Numeric num1, Numeric num2)
{
NumericVar arg1;
NumericVar arg2;
Numeric res;
/* ----------
* Handle NULL
* ----------
*/
if (num1 == NULL || num2 == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
return make_result(&const_nan);
/* ----------
* Unpack the values, and decide which is the larger one
* ----------
*/
init_var(&arg1);
init_var(&arg2);
set_var_from_num(num1, &arg1);
set_var_from_num(num2, &arg2);
if (cmp_var(&arg1, &arg2) >= 0)
res = make_result(&arg1);
else
res = make_result(&arg2);
free_var(&arg1);
free_var(&arg2);
return res;
}
/* ----------------------------------------------------------------------
*
* Complex math functions
*
* ----------------------------------------------------------------------
*/
/* ----------
* numeric_sqrt() -
*
* Compute the square root of a numeric.
* ----------
*/
Numeric
numeric_sqrt(Numeric num)
{
Numeric res;
NumericVar arg;
NumericVar result;
int res_dscale;
/* ----------
* Handle NULL
* ----------
*/
if (num == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num))
return make_result(&const_nan);
/* ----------
* Unpack the argument, determine the scales like for divide,
* let sqrt_var() do the calculation and return the result.
* ----------
*/
init_var(&arg);
init_var(&result);
set_var_from_num(num, &arg);
res_dscale = MAX(arg.dscale, NUMERIC_MIN_DISPLAY_SCALE);
res_dscale = MIN(res_dscale, NUMERIC_MAX_DISPLAY_SCALE);
global_rscale = MAX(arg.rscale, NUMERIC_MIN_RESULT_SCALE);
global_rscale = MAX(global_rscale, res_dscale + 4);
global_rscale = MIN(global_rscale, NUMERIC_MAX_RESULT_SCALE);
sqrt_var(&arg, &result);
result.dscale = res_dscale;
res = make_result(&result);
free_var(&result);
free_var(&arg);
return res;
}
/* ----------
* numeric_exp() -
*
* Raise e to the power of x
* ----------
*/
Numeric
numeric_exp(Numeric num)
{
Numeric res;
NumericVar arg;
NumericVar result;
int res_dscale;
/* ----------
* Handle NULL
* ----------
*/
if (num == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num))
return make_result(&const_nan);
/* ----------
* Same procedure like for sqrt().
* ----------
*/
init_var(&arg);
init_var(&result);
set_var_from_num(num, &arg);
res_dscale = MAX(arg.dscale, NUMERIC_MIN_DISPLAY_SCALE);
res_dscale = MIN(res_dscale, NUMERIC_MAX_DISPLAY_SCALE);
global_rscale = MAX(arg.rscale, NUMERIC_MIN_RESULT_SCALE);
global_rscale = MAX(global_rscale, res_dscale + 4);
global_rscale = MIN(global_rscale, NUMERIC_MAX_RESULT_SCALE);
exp_var(&arg, &result);
result.dscale = res_dscale;
res = make_result(&result);
free_var(&result);
free_var(&arg);
return res;
}
/* ----------
* numeric_ln() -
*
* Compute the natural logarithm of x
* ----------
*/
Numeric
numeric_ln(Numeric num)
{
Numeric res;
NumericVar arg;
NumericVar result;
int res_dscale;
/* ----------
* Handle NULL
* ----------
*/
if (num == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num))
return make_result(&const_nan);
/* ----------
* Same procedure like for sqrt()
* ----------
*/
init_var(&arg);
init_var(&result);
set_var_from_num(num, &arg);
res_dscale = MAX(arg.dscale, NUMERIC_MIN_DISPLAY_SCALE);
res_dscale = MIN(res_dscale, NUMERIC_MAX_DISPLAY_SCALE);
global_rscale = MAX(arg.rscale, NUMERIC_MIN_RESULT_SCALE);
global_rscale = MAX(global_rscale, res_dscale + 4);
global_rscale = MIN(global_rscale, NUMERIC_MAX_RESULT_SCALE);
ln_var(&arg, &result);
result.dscale = res_dscale;
res = make_result(&result);
free_var(&result);
free_var(&arg);
return res;
}
/* ----------
* numeric_ln() -
*
* Compute the logarithm of x in a given base
* ----------
*/
Numeric
numeric_log(Numeric num1, Numeric num2)
{
Numeric res;
NumericVar arg1;
NumericVar arg2;
NumericVar result;
int res_dscale;
/* ----------
* Handle NULL
* ----------
*/
if (num1 == NULL || num2 == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
return make_result(&const_nan);
/* ----------
* Initialize things and calculate scales
* ----------
*/
init_var(&arg1);
init_var(&arg2);
init_var(&result);
set_var_from_num(num1, &arg1);
set_var_from_num(num2, &arg2);
res_dscale = MAX(arg1.dscale + arg2.dscale, NUMERIC_MIN_DISPLAY_SCALE);
res_dscale = MIN(res_dscale, NUMERIC_MAX_DISPLAY_SCALE);
global_rscale = MAX(arg1.rscale + arg2.rscale, NUMERIC_MIN_RESULT_SCALE);
global_rscale = MAX(global_rscale, res_dscale + 4);
global_rscale = MIN(global_rscale, NUMERIC_MAX_RESULT_SCALE);
/* ----------
* Call log_var() to compute and return the result
* ----------
*/
log_var(&arg1, &arg2, &result);
result.dscale = res_dscale;
res = make_result(&result);
free_var(&result);
free_var(&arg2);
free_var(&arg1);
return res;
}
/* ----------
* numeric_power() -
*
* Raise m to the power of x
* ----------
*/
Numeric
numeric_power(Numeric num1, Numeric num2)
{
Numeric res;
NumericVar arg1;
NumericVar arg2;
NumericVar result;
int res_dscale;
/* ----------
* Handle NULL
* ----------
*/
if (num1 == NULL || num2 == NULL)
return NULL;
/* ----------
* Handle NaN
* ----------
*/
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
return make_result(&const_nan);
/* ----------
* Initialize things and calculate scales
* ----------
*/
init_var(&arg1);
init_var(&arg2);
init_var(&result);
set_var_from_num(num1, &arg1);
set_var_from_num(num2, &arg2);
res_dscale = MAX(arg1.dscale + arg2.dscale, NUMERIC_MIN_DISPLAY_SCALE);
res_dscale = MIN(res_dscale, NUMERIC_MAX_DISPLAY_SCALE);
global_rscale = MAX(arg1.rscale + arg2.rscale, NUMERIC_MIN_RESULT_SCALE);
global_rscale = MAX(global_rscale, res_dscale + 4);
global_rscale = MIN(global_rscale, NUMERIC_MAX_RESULT_SCALE);
/* ----------
* Call log_var() to compute and return the result
* ----------
*/
power_var(&arg1, &arg2, &result);
result.dscale = res_dscale;
res = make_result(&result);
free_var(&result);
free_var(&arg2);
free_var(&arg1);
return res;
}
/* ----------------------------------------------------------------------
*
* Type conversion functions
*
* ----------------------------------------------------------------------
*/
Numeric
int4_numeric(int32 val)
{
Numeric res;
NumericVar result;
char *tmp;
init_var(&result);
tmp = int4out(val);
set_var_from_str(tmp, &result);
res = make_result(&result);
free_var(&result);
pfree(tmp);
return res;
}
int32
numeric_int4(Numeric num)
{
char *tmp;
int32 result;
if (num == NULL)
return 0;
if (NUMERIC_IS_NAN(num))
return 0;
tmp = numeric_out(num);
result = int4in(tmp);
pfree(tmp);
return result;
}
Numeric
float8_numeric(float64 val)
{
Numeric res;
NumericVar result;
char *tmp;
if (val == NULL)
return NULL;
if (isnan(*val))
return make_result(&const_nan);
init_var(&result);
tmp = float8out(val);
set_var_from_str(tmp, &result);
res = make_result(&result);
free_var(&result);
pfree(tmp);
return res;
}
float64
numeric_float8(Numeric num)
{
char *tmp;
float64 result;
if (num == NULL)
return NULL;
if (NUMERIC_IS_NAN(num))
{
result = (float64)palloc(sizeof(float64data));
*result = NAN;
return result;
}
tmp = numeric_out(num);
result = float8in(tmp);
pfree(tmp);
return result;
}
Numeric
float4_numeric(float32 val)
{
Numeric res;
NumericVar result;
char *tmp;
if (val == NULL)
return NULL;
if (isnan(*val))
return make_result(&const_nan);
init_var(&result);
tmp = float4out(val);
set_var_from_str(tmp, &result);
res = make_result(&result);
free_var(&result);
pfree(tmp);
return res;
}
float32
numeric_float4(Numeric num)
{
char *tmp;
float32 result;
if (num == NULL)
return NULL;
if (NUMERIC_IS_NAN(num))
{
result = (float32)palloc(sizeof(float32data));
*result = NAN;
return result;
}
tmp = numeric_out(num);
result = float4in(tmp);
pfree(tmp);
return result;
}
/* ----------------------------------------------------------------------
*
* Local functions follow
*
* ----------------------------------------------------------------------
*/
#ifdef NUMERIC_DEBUG
/* ----------
* dump_numeric() - Dump a value in the db storage format for debugging
* ----------
*/
static void
dump_numeric(char *str, Numeric num)
{
int i;
printf("%s: NUMERIC w=%d r=%d d=%d ", str, num->n_weight, num->n_rscale,
NUMERIC_DSCALE(num));
switch (NUMERIC_SIGN(num))
{
case NUMERIC_POS: printf("POS");
break;
case NUMERIC_NEG: printf("NEG");
break;
case NUMERIC_NAN: printf("NaN");
break;
default: printf("SIGN=0x%x", NUMERIC_SIGN(num));
break;
}
for (i = 0; i < num->varlen - NUMERIC_HDRSZ; i++)
{
printf(" %d %d", (num->n_data[i] >> 4) & 0x0f, num->n_data[i] & 0x0f);
}
printf("\n");
}
/* ----------
* dump_var() - Dump a value in the variable format for debugging
* ----------
*/
static void
dump_var(char *str, NumericVar *var)
{
int i;
printf("%s: VAR w=%d r=%d d=%d ", str, var->weight, var->rscale,
var->dscale);
switch (var->sign)
{
case NUMERIC_POS: printf("POS");
break;
case NUMERIC_NEG: printf("NEG");
break;
case NUMERIC_NAN: printf("NaN");
break;
default: printf("SIGN=0x%x", var->sign);
break;
}
for (i = 0; i < var->ndigits; i++)
printf(" %d", var->digits[i]);
printf("\n");
}
#endif /* NUMERIC_DEBUG */
/* ----------
* digitbuf_alloc() -
*
* All variables used in the arithmetic functions hold some base
* information (sign, scales etc.) and a digit buffer for the
* value itself. All the variable level functions are written in
* a style that makes it possible to give one and the same variable
* as argument and result destination.
*
* The two functions below manage unused buffers in a free list
* as a try to reduce the number of malloc()/free() calls.
* ----------
*/
static NumericDigitBuf *
digitbuf_alloc(int size)
{
NumericDigitBuf *buf;
int asize;
/* ----------
* Lookup the free list if there is a digit buffer of
* the required size available
* ----------
*/
for (buf = digitbuf_freelist; buf != NULL; buf = buf->next)
{
if (buf->size < size) continue;
/* ----------
* We found a free buffer that is big enough - remove it from
* the free list
* ----------
*/
if (buf->prev == NULL)
{
digitbuf_freelist = buf->next;
if (buf->next != NULL)
buf->next->prev = NULL;
}
else
{
buf->prev->next = buf->next;
if (buf->next != NULL)
buf->next->prev = buf->prev;
}
digitbuf_nfree--;
/* ----------
* Put it onto the used list
* ----------
*/
buf->prev = NULL;
buf->next = digitbuf_usedlist;
if (digitbuf_usedlist != NULL)
digitbuf_usedlist->prev = buf;
digitbuf_usedlist = buf;
/* ----------
* Return this buffer
* ----------
*/
return buf;
}
/* ----------
* There is no free buffer big enough - allocate a new one
* ----------
*/
for (asize = NUMERIC_MIN_BUFSIZE; asize < size; asize *= 2);
buf = (NumericDigitBuf *)malloc(sizeof(NumericDigitBuf) + asize);
buf->size = asize;
/* ----------
* Put it onto the used list
* ----------
*/
buf->prev = NULL;
buf->next = digitbuf_usedlist;
if (digitbuf_usedlist != NULL)
digitbuf_usedlist->prev = buf;
digitbuf_usedlist = buf;
/* ----------
* Return the new buffer
* ----------
*/
return buf;
}
/* ----------
* digitbuf_free() -
* ----------
*/
static void
digitbuf_free(NumericDigitBuf *buf)
{
NumericDigitBuf *smallest;
if (buf == NULL)
return;
/* ----------
* Remove the buffer from the used list
* ----------
*/
if (buf->prev == NULL)
{
digitbuf_usedlist = buf->next;
if (buf->next != NULL)
buf->next->prev = NULL;
}
else
{
buf->prev->next = buf->next;
if (buf->next != NULL)
buf->next->prev = buf->prev;
}
/* ----------
* Put it onto the free list
* ----------
*/
if (digitbuf_freelist != NULL)
digitbuf_freelist->prev = buf;
buf->prev = NULL;
buf->next = digitbuf_freelist;
digitbuf_freelist = buf;
digitbuf_nfree++;
/* ----------
* Check for maximum free buffers
* ----------
*/
if (digitbuf_nfree <= NUMERIC_MAX_FREEBUFS)
return;
/* ----------
* Have too many free buffers - destroy the smallest one
* ----------
*/
smallest = buf;
for (buf = digitbuf_freelist->next; buf != NULL; buf = buf->next)
{
if (buf->size < smallest->size)
smallest = buf;
}
/* ----------
* Remove it from the free list
* ----------
*/
if (smallest->prev == NULL)
{
digitbuf_freelist = smallest->next;
if (smallest->next != NULL)
smallest->next->prev = NULL;
}
else
{
smallest->prev->next = smallest->next;
if (smallest->next != NULL)
smallest->next->prev = smallest->prev;
}
digitbuf_nfree--;
/* ----------
* And destroy it
* ----------
*/
free(smallest);
}
/* ----------
* free_var() -
*
* Return the digit buffer of a variable to the pool
* ----------
*/
static void
free_var(NumericVar *var)
{
if (var->buf != NULL)
{
digitbuf_free(var->buf);
var->buf = NULL;
var->digits = NULL;
}
var->sign = NUMERIC_NAN;
}
/* ----------
* free_allvars() -
*
* Put all the currently used buffers back into the pool.
*
* Warning: the variables currently holding the buffers aren't
* cleaned! This function should only be used directly before
* a call to elog(ERROR,...) or if it is totally impossible that
* any other call to free_var() will occur. None of the variable
* level functions should call it if it might return later without
* an error.
* ----------
*/
static void
free_allvars(void)
{
NumericDigitBuf *buf;
NumericDigitBuf *next;
buf = digitbuf_usedlist;
while (buf != NULL)
{
next = buf->next;
digitbuf_free(buf);
buf = next;
}
}
/* ----------
* set_var_from_str()
*
* Parse a string and put the number into a variable
* ----------
*/
static void
set_var_from_str(char *str, NumericVar *dest)
{
char *cp = str;
bool have_dp = FALSE;
int i = 1;
while(*cp)
{
if (!isspace(*cp)) break;
cp++;
}
digitbuf_free(dest->buf);
dest->buf = digitbuf_alloc(strlen(cp) + 2);
dest->digits = (NumericDigit *)(dest->buf) + sizeof(NumericDigitBuf);
dest->digits[0] = 0;
dest->weight = 0;
dest->dscale = 0;
switch (*cp)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': dest->sign = NUMERIC_POS;
break;
case '+': dest->sign = NUMERIC_POS;
cp++;
break;
case '-': dest->sign = NUMERIC_NEG;
cp++;
break;
case '.': dest->sign = NUMERIC_POS;
have_dp = TRUE;
cp++;
break;
default: free_allvars();
elog(ERROR, "Bad numeric input format '%s'", str);
}
if (*cp == '.')
{
if (have_dp)
{
free_allvars();
elog(ERROR, "Bad numeric input format '%s'", str);
}
have_dp = TRUE;
cp++;
}
if (*cp < '0' && *cp > '9')
{
free_allvars();
elog(ERROR, "Bad numeric input format '%s'", str);
}
while (*cp)
{
if (isspace(*cp))
break;
if (*cp == 'e' || *cp == 'E')
break;
switch (*cp)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': dest->digits[i++] = *cp++ - '0';
if (!have_dp)
dest->weight++;
else
dest->dscale++;
break;
case '.': if (have_dp)
{
free_allvars();
elog(ERROR, "Bad numeric input format '%s'", str);
}
have_dp = TRUE;
cp++;
break;
default: free_allvars();
elog(ERROR, "Bad numeric input format '%s'", str);
}
}
dest->ndigits = i;
if (*cp == 'e' || *cp == 'E')
{
/* Handle ...Ennn */
}
while (dest->ndigits > 0 && *(dest->digits) == 0)
{
(dest->digits)++;
(dest->weight)--;
(dest->ndigits)--;
}
dest->rscale = dest->dscale;
}
/*
* set_var_from_num() -
*
* Parse back the packed db format into a variable
*
*/
static void
set_var_from_num(Numeric num, NumericVar *dest)
{
NumericDigit *digit;
int i;
int n;
n = num->varlen - NUMERIC_HDRSZ;
digitbuf_free(dest->buf);
dest->buf = digitbuf_alloc(n * 2 + 2);
digit = ((NumericDigit *)(dest->buf)) + sizeof(NumericDigitBuf);
*digit++ = 0;
*digit++ = 0;
dest->digits = digit;
dest->ndigits = n * 2;
dest->weight = num->n_weight;
dest->rscale = num->n_rscale;
dest->dscale = NUMERIC_DSCALE(num);
dest->sign = NUMERIC_SIGN(num);
for (i = 0; i < n; i++)
{
*digit++ = (num->n_data[i] >> 4) & 0x0f;
*digit++ = num->n_data[i] & 0x0f;
}
}
/* ----------
* set_var_from_var() -
*
* Copy one variable into another
* ----------
*/
static void
set_var_from_var(NumericVar *value, NumericVar *dest)
{
NumericDigitBuf *newbuf;
NumericDigit *newdigits;
newbuf = digitbuf_alloc(value->ndigits);
newdigits = ((NumericDigit *)newbuf) + sizeof(NumericDigitBuf);
memcpy(newdigits, value->digits, value->ndigits);
digitbuf_free(dest->buf);
memcpy(dest, value, sizeof(NumericVar));
dest->buf = newbuf;
dest->digits = newdigits;
}
/* ----------
* make_result() -
*
* Create the packed db numeric format in palloc()'d memory from
* a variable.
* ----------
*/
static Numeric
make_result(NumericVar *var)
{
Numeric result;
NumericDigit *digit = var->digits;
int n;
int weight = var->weight;
int sign = var->sign;
int i, j;
if (sign == NUMERIC_NAN)
{
result = (Numeric)palloc(NUMERIC_HDRSZ);
result->varlen = NUMERIC_HDRSZ;
result->n_weight = 0;
result->n_rscale = 0;
result->n_sign_dscale = NUMERIC_NAN;
dump_numeric("make_result()", result);
return result;
}
n = MAX(0, MIN(var->ndigits, var->weight + var->rscale + 1));
while (n > 0 && *digit == 0)
{
digit++;
weight--;
n--;
}
while (n > 0 && digit[n - 1] == 0)
n--;
if (n == 0)
{
weight = 0;
sign = NUMERIC_POS;
}
result = (Numeric)palloc(NUMERIC_HDRSZ + (n + 1) / 2);
result->varlen = NUMERIC_HDRSZ + (n + 1) / 2;
result->n_weight = weight;
result->n_rscale = var->rscale;
result->n_sign_dscale = sign | ((uint16)(var->dscale) & ~NUMERIC_SIGN_MASK);
i = 0; j = 0;
while (j < n)
{
result->n_data[i] = digit[j++] << 4;
if (j < n)
result->n_data[i] |= digit[j++];
i++;
}
dump_numeric("make_result()", result);
return result;
}
/* ----------
* apply_typmod() -
*
* Do bounds checking and rounding according to the attributes
* typmod field.
* ----------
*/
static void
apply_typmod(NumericVar *var, int32 typmod)
{
int precision;
int scale;
int maxweight;
int i;
if (typmod < (int32)(VARHDRSZ))
return;
typmod -= VARHDRSZ;
precision = (typmod >> 16) & 0xffff;
scale = typmod & 0xffff;
maxweight = precision - scale;
if (var->weight >= maxweight)
{
free_allvars();
elog(ERROR, "overflow on numeric
ABS(value) >= 10^%d for field with precision %d scale %d",
var->weight, precision, scale);
}
i = scale + var->weight + 1;
if (var->ndigits > i)
{
long carry = (var->digits[i] > 4) ? 1 : 0;
var->ndigits = i;
while (carry)
{
carry += var->digits[--i];
var->digits[i] = carry % 10;
carry /= 10;
}
if (i < 0)
{
var->digits--;
var->ndigits++;
var->weight++;
}
}
var->rscale = scale;
var->dscale = scale;
}
/* ----------
* cmp_var() -
*
* Compare two values on variable level
* ----------
*/
static int
cmp_var(NumericVar *var1, NumericVar *var2)
{
if (var1->ndigits == 0)
{
if (var2->ndigits == 0)
return 0;
if (var2->sign == NUMERIC_NEG)
return 1;
return -1;
}
if (var2->ndigits == 0)
{
if (var1->sign == NUMERIC_POS)
return 1;
return -1;
}
if (var1->sign == NUMERIC_POS)
{
if (var2->sign == NUMERIC_NEG)
return 1;
return cmp_abs(var1, var2);
}
if (var2->sign == NUMERIC_POS)
return -1;
return cmp_abs(var2, var1);
}
/* ----------
* add_var() -
*
* Full version of add functionality on variable level (handling signs).
* result might point to one of the operands too without danger.
* ----------
*/
static void
add_var(NumericVar *var1, NumericVar *var2, NumericVar *result)
{
/* ----------
* Decide on the signs of the two variables what to do
* ----------
*/
if (var1->sign == NUMERIC_POS)
{
if (var2->sign == NUMERIC_POS)
{
/* ----------
* Both are positive
* result = +(ABS(var1) + ABS(var2))
* ----------
*/
add_abs(var1, var2, result);
result->sign = NUMERIC_POS;
}
else
{
/* ----------
* var1 is positive, var2 is negative
* Must compare absolute values
* ----------
*/
switch (cmp_abs(var1, var2))
{
case 0: /* ----------
* ABS(var1) == ABS(var2)
* result = ZERO
* ----------
*/
digitbuf_free(result->buf);
result->buf = digitbuf_alloc(0);
result->ndigits = 0;
result->digits = ((NumericDigit *)(result->buf)) +
sizeof(NumericDigitBuf);
result->weight = 0;
result->rscale = MAX(var1->rscale, var2->rscale);
result->dscale = MAX(var1->dscale, var2->dscale);
result->sign = NUMERIC_POS;
break;
case 1: /* ----------
* ABS(var1) > ABS(var2)
* result = +(ABS(var1) - ABS(var2))
* ----------
*/
sub_abs(var1, var2, result);
result->sign = NUMERIC_POS;
break;
case -1: /* ----------
* ABS(var1) < ABS(var2)
* result = -(ABS(var2) - ABS(var1))
* ----------
*/
sub_abs(var2, var1, result);
result->sign = NUMERIC_NEG;
break;
}
}
}
else
{
if (var2->sign == NUMERIC_POS)
{
/* ----------
* var1 is negative, var2 is positive
* Must compare absolute values
* ----------
*/
switch (cmp_abs(var1, var2))
{
case 0: /* ----------
* ABS(var1) == ABS(var2)
* result = ZERO
* ----------
*/
digitbuf_free(result->buf);
result->buf = digitbuf_alloc(0);
result->ndigits = 0;
result->digits = ((NumericDigit *)(result->buf)) +
sizeof(NumericDigitBuf);
result->weight = 0;
result->rscale = MAX(var1->rscale, var2->rscale);
result->dscale = MAX(var1->dscale, var2->dscale);
result->sign = NUMERIC_POS;
break;
case 1: /* ----------
* ABS(var1) > ABS(var2)
* result = -(ABS(var1) - ABS(var2))
* ----------
*/
sub_abs(var1, var2, result);
result->sign = NUMERIC_NEG;
break;
case -1: /* ----------
* ABS(var1) < ABS(var2)
* result = +(ABS(var2) - ABS(var1))
* ----------
*/
sub_abs(var2, var1, result);
result->sign = NUMERIC_POS;
break;
}
}
else
{
/* ----------
* Both are negative
* result = -(ABS(var1) + ABS(var2))
* ----------
*/
add_abs(var1, var2, result);
result->sign = NUMERIC_NEG;
}
}
}
/* ----------
* sub_var() -
*
* Full version of sub functionality on variable level (handling signs).
* result might point to one of the operands too without danger.
* ----------
*/
static void
sub_var(NumericVar *var1, NumericVar *var2, NumericVar *result)
{
/* ----------
* Decide on the signs of the two variables what to do
* ----------
*/
if (var1->sign == NUMERIC_POS)
{
if (var2->sign == NUMERIC_NEG)
{
/* ----------
* var1 is positive, var2 is negative
* result = +(ABS(var1) + ABS(var2))
* ----------
*/
add_abs(var1, var2, result);
result->sign = NUMERIC_POS;
}
else
{
/* ----------
* Both are positive
* Must compare absolute values
* ----------
*/
switch (cmp_abs(var1, var2))
{
case 0: /* ----------
* ABS(var1) == ABS(var2)
* result = ZERO
* ----------
*/
digitbuf_free(result->buf);
result->buf = digitbuf_alloc(0);
result->ndigits = 0;
result->digits = ((NumericDigit *)(result->buf)) +
sizeof(NumericDigitBuf);
result->weight = 0;
result->rscale = MAX(var1->rscale, var2->rscale);
result->dscale = MAX(var1->dscale, var2->dscale);
result->sign = NUMERIC_POS;
break;
case 1: /* ----------
* ABS(var1) > ABS(var2)
* result = +(ABS(var1) - ABS(var2))
* ----------
*/
sub_abs(var1, var2, result);
result->sign = NUMERIC_POS;
break;
case -1: /* ----------
* ABS(var1) < ABS(var2)
* result = -(ABS(var2) - ABS(var1))
* ----------
*/
sub_abs(var2, var1, result);
result->sign = NUMERIC_NEG;
break;
}
}
}
else
{
if (var2->sign == NUMERIC_NEG)
{
/* ----------
* Both are negative
* Must compare absolute values
* ----------
*/
switch (cmp_abs(var1, var2))
{
case 0: /* ----------
* ABS(var1) == ABS(var2)
* result = ZERO
* ----------
*/
digitbuf_free(result->buf);
result->buf = digitbuf_alloc(0);
result->ndigits = 0;
result->digits = ((NumericDigit *)(result->buf)) +
sizeof(NumericDigitBuf);
result->weight = 0;
result->rscale = MAX(var1->rscale, var2->rscale);
result->dscale = MAX(var1->dscale, var2->dscale);
result->sign = NUMERIC_POS;
break;
case 1: /* ----------
* ABS(var1) > ABS(var2)
* result = -(ABS(var1) - ABS(var2))
* ----------
*/
sub_abs(var1, var2, result);
result->sign = NUMERIC_NEG;
break;
case -1: /* ----------
* ABS(var1) < ABS(var2)
* result = +(ABS(var2) - ABS(var1))
* ----------
*/
sub_abs(var2, var1, result);
result->sign = NUMERIC_POS;
break;
}
}
else
{
/* ----------
* var1 is negative, var2 is positive
* result = -(ABS(var1) + ABS(var2))
* ----------
*/
add_abs(var1, var2, result);
result->sign = NUMERIC_NEG;
}
}
}
/* ----------
* mul_var() -
*
* Multiplication on variable level. Product of var1 * var2 is stored
* in result.
* ----------
*/
static void
mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result)
{
NumericDigitBuf *res_buf;
NumericDigit *res_digits;
int res_ndigits;
int res_weight;
int res_sign;
int i, ri, i1, i2;
long sum = 0;
res_weight = var1->weight + var2->weight + 2;
res_ndigits = var1->ndigits + var2->ndigits + 1;
if (var1->sign == var2->sign)
res_sign = NUMERIC_POS;
else
res_sign = NUMERIC_NEG;
res_buf = digitbuf_alloc(res_ndigits);
res_digits = ((NumericDigit *)res_buf) + sizeof(NumericDigitBuf);
memset(res_digits, 0, res_ndigits);
ri = res_ndigits;
for (i1 = var1->ndigits - 1; i1 >= 0; i1--)
{
sum = 0;
i = --ri;
for (i2 = var2->ndigits - 1; i2 >= 0; i2--)
{
sum = sum + res_digits[i] + var1->digits[i1] * var2->digits[i2];
res_digits[i--] = sum % 10;
sum /= 10;
}
res_digits[i] = sum;
}
i = res_weight + global_rscale + 2;
if (i >= 0 && i < res_ndigits)
{
sum = (res_digits[i] > 4) ? 1 : 0;
res_ndigits = i;
i--;
while (sum)
{
sum += res_digits[i];
res_digits[i--] = sum % 10;
sum /= 10;
}
}
while (res_ndigits > 0 && *res_digits == 0)
{
res_digits++;
res_weight--;
res_ndigits--;
}
while (res_ndigits > 0 && res_digits[res_ndigits - 1] == 0)
{
res_ndigits--;
}
if (res_ndigits == 0)
{
res_sign = NUMERIC_POS;
res_weight = 0;
}
digitbuf_free(result->buf);
result->buf = res_buf;
result->digits = res_digits;
result->ndigits = res_ndigits;
result->weight = res_weight;
result->rscale = global_rscale;
result->sign = res_sign;
}
/* ----------
* div_var() -
*
* Division on variable level.
* ----------
*/
static void
div_var(NumericVar *var1, NumericVar *var2, NumericVar *result)
{
NumericDigit *res_digits;
int res_ndigits;
int res_sign;
int res_weight;
NumericVar dividend;
NumericVar divisor[10];
int ndigits_tmp;
int weight_tmp;
int rscale_tmp;
int ri;
int i;
long guess;
long first_have;
long first_div;
int first_nextdigit;
int stat = 0;
/* ----------
* First of all division by zero check
* ----------
*/
ndigits_tmp = var2->ndigits + 1;
if (ndigits_tmp == 1)
{
free_allvars();
elog(ERROR, "division by zero on numeric");
}
/* ----------
* Determine the result sign, weight and number of digits to calculate
* ----------
*/
if (var1->sign == var2->sign)
res_sign = NUMERIC_POS;
else
res_sign = NUMERIC_NEG;
res_weight = var1->weight - var2->weight + 1;
res_ndigits = global_rscale + res_weight;
/* ----------
* Now result zero check
* ----------
*/
if (var1->ndigits == 0)
{
digitbuf_free(result->buf);
result->buf = digitbuf_alloc(0);
result->digits = ((NumericDigit *)(result->buf)) + sizeof(NumericDigitBuf);
result->ndigits = 0;
result->weight = 0;
result->rscale = global_rscale;
result->sign = NUMERIC_POS;
return;
}
/* ----------
* Initialize local variables
* ----------
*/
init_var(&dividend);
for (i = 1; i < 10; i++)
{
init_var(&divisor[i]);
}
/* ----------
* Make a copy of the divisor which has one leading zero digit
* ----------
*/
divisor[1].ndigits = ndigits_tmp;
divisor[1].rscale = var2->ndigits;
divisor[1].sign = NUMERIC_POS;
divisor[1].buf = digitbuf_alloc(ndigits_tmp);
divisor[1].digits = ((NumericDigit *)(divisor[1].buf)) +
sizeof(NumericDigitBuf);
divisor[1].digits[0] = 0;
memcpy(&(divisor[1].digits[1]), var2->digits, ndigits_tmp - 1);
/* ----------
* Make a copy of the dividend
* ----------
*/
dividend.ndigits = var1->ndigits;
dividend.weight = 0;
dividend.rscale = var1->ndigits;
dividend.sign = NUMERIC_POS;
dividend.buf = digitbuf_alloc(var1->ndigits);
dividend.digits = ((NumericDigit *)(dividend.buf)) + sizeof(NumericDigitBuf);
memcpy(dividend.digits, var1->digits, var1->ndigits);
/* ----------
* Setup the result
* ----------
*/
digitbuf_free(result->buf);
result->buf = digitbuf_alloc(res_ndigits + 2);
res_digits = ((NumericDigit *)(result->buf)) + sizeof(NumericDigitBuf);
result->digits = res_digits;
result->ndigits = res_ndigits;
result->weight = res_weight;
result->rscale = global_rscale;
result->sign = res_sign;
res_digits[0] = 0;
first_div = divisor[1].digits[1] * 10;
if (ndigits_tmp > 2)
first_div += divisor[1].digits[2];
first_have = 0;
first_nextdigit = 0;
weight_tmp = 1;
rscale_tmp = divisor[1].rscale;
for (ri = 0; ri < res_ndigits + 1; ri++)
{
first_have = first_have * 10;
if (first_nextdigit >= 0 && first_nextdigit < dividend.ndigits)
first_have += dividend.digits[first_nextdigit];
first_nextdigit++;
guess = (first_have * 10) / first_div + 1;
if (guess > 9)
guess = 9;
while (guess > 0)
{
if (divisor[guess].buf == NULL)
{
int i;
long sum = 0;
memcpy(&divisor[guess], &divisor[1], sizeof(NumericVar));
divisor[guess].buf = digitbuf_alloc(divisor[guess].ndigits);
divisor[guess].digits = ((NumericDigit *)(divisor[guess].buf) +
sizeof(NumericDigitBuf));
for (i = divisor[1].ndigits - 1; i >= 0; i--)
{
sum += divisor[1].digits[i] * guess;
divisor[guess].digits[i] = sum % 10;
sum /= 10;
}
}
divisor[guess].weight = weight_tmp;
divisor[guess].rscale = rscale_tmp;
stat = cmp_abs(&dividend, &divisor[guess]);
if (stat >= 0) break;
guess--;
}
res_digits[ri + 1] = guess;
if (stat == 0)
{
ri++;
break;
}
weight_tmp--;
rscale_tmp++;
if (guess == 0)
continue;
sub_abs(&dividend, &divisor[guess], &dividend);
first_nextdigit = dividend.weight - weight_tmp;
first_have = 0;
if (first_nextdigit >= 0 && first_nextdigit < dividend.ndigits)
first_have = dividend.digits[first_nextdigit];
first_nextdigit++;
}
result->ndigits = ri + 1;
if (ri == res_ndigits + 1)
{
long carry = (res_digits[ri] > 4) ? 1 : 0;
result->ndigits = ri;
res_digits[ri] = 0;
while(carry && ri > 0)
{
carry += res_digits[--ri];
res_digits[ri] = carry % 10;
carry /= 10;
}
}
while (result->ndigits > 0 && *(result->digits) == 0)
{
(result->digits)++;
(result->weight)--;
(result->ndigits)--;
}
while (result->ndigits > 0 && result->digits[result->ndigits - 1] == 0)
{
(result->ndigits)--;
}
if (result->ndigits == 0)
result->sign = NUMERIC_POS;
/*
* Tidy up
*
*/
digitbuf_free(dividend.buf);
for (i = 1; i < 10; i++)
digitbuf_free(divisor[i].buf);
}
/* ----------
* mod_var() -
*
* Calculate the modulo of two numerics at variable level
* ----------
*/
static void
mod_var(NumericVar *var1, NumericVar *var2, NumericVar *result)
{
NumericVar tmp;
int save_global_rscale;
init_var(&tmp);
/* ----------
* We do it by fiddling around with global_rscale and truncating
* the result of the division.
* ----------
*/
save_global_rscale = global_rscale;
global_rscale = var2->rscale + 2;
div_var(var1, var2, &tmp);
tmp.rscale = var2->rscale;
tmp.ndigits = MAX(0, MIN(tmp.ndigits, tmp.weight + tmp.rscale + 1));
global_rscale = var2->rscale;
mul_var(var2, &tmp, &tmp);
sub_var(var1, &tmp, result);
global_rscale = save_global_rscale;
free_var(&tmp);
}
/* ----------
* ceil_var() -
*
* Return the smallest integer greater than or equal to the argument
* on variable level
* ----------
*/
static void
ceil_var(NumericVar *var, NumericVar *result)
{
NumericVar tmp;
init_var(&tmp);
set_var_from_var(var, &tmp);
tmp.rscale = 0;
tmp.ndigits = MAX(0, tmp.weight + 1);
if (tmp.sign == NUMERIC_POS && cmp_var(var, &tmp) != 0)
add_var(&tmp, &const_one, &tmp);
set_var_from_var(&tmp, result);
free_var(&tmp);
}
/* ----------
* floor_var() -
*
* Return the largest integer equal to or less than the argument
* on variable level
* ----------
*/
static void
floor_var(NumericVar *var, NumericVar *result)
{
NumericVar tmp;
init_var(&tmp);
set_var_from_var(var, &tmp);
tmp.rscale = 0;
tmp.ndigits = MAX(0, tmp.weight + 1);
if (tmp.sign == NUMERIC_NEG && cmp_var(var, &tmp) != 0)
sub_var(&tmp, &const_one, &tmp);
set_var_from_var(&tmp, result);
free_var(&tmp);
}
/* ----------
* sqrt_var() -
*
* Compute the square root of x using Newtons algorithm
* ----------
*/
static void
sqrt_var(NumericVar *arg, NumericVar *result)
{
NumericVar tmp_arg;
NumericVar tmp_val;
NumericVar last_val;
int res_rscale;
int save_global_rscale;
int stat;
save_global_rscale = global_rscale;
global_rscale += 8;
res_rscale = global_rscale;
stat = cmp_var(arg, &const_zero);
if (stat == 0)
{
set_var_from_var(&const_zero, result);
result->rscale = res_rscale;
result->sign = NUMERIC_POS;
return;
}
if (stat < 0)
{
free_allvars();
elog(ERROR, "math error on numeric - cannot compute SQRT of negative value");
}
init_var(&tmp_arg);
init_var(&tmp_val);
init_var(&last_val);
set_var_from_var(arg, &tmp_arg);
set_var_from_var(result, &last_val);
/* ----------
* Initialize the result to the first guess
* ----------
*/
digitbuf_free(result->buf);
result->buf = digitbuf_alloc(1);
result->digits = ((NumericDigit *)(result->buf)) + sizeof(NumericDigitBuf);
result->digits[0] = tmp_arg.digits[0] / 2;
if (result->digits[0] == 0)
result->digits[0] = 1;
result->ndigits = 1;
result->weight = tmp_arg.weight / 2;
result->rscale = res_rscale;
result->sign = NUMERIC_POS;
for (;;)
{
div_var(&tmp_arg, result, &tmp_val);
add_var(result, &tmp_val, result);
div_var(result, &const_two, result);
if (cmp_var(&last_val, result) == 0) break;
set_var_from_var(result, &last_val);
}
free_var(&last_val);
free_var(&tmp_val);
free_var(&tmp_arg);
global_rscale = save_global_rscale;
div_var(result, &const_one, result);
}
/* ----------
* exp_var() -
*
* Raise e to the power of x
* ----------
*/
static void
exp_var(NumericVar *arg, NumericVar *result)
{
NumericVar x;
NumericVar xpow;
NumericVar ifac;
NumericVar elem;
NumericVar ni;
int d;
int i;
int ndiv2 = 0;
bool xneg = FALSE;
int save_global_rscale;
init_var(&x);
init_var(&xpow);
init_var(&ifac);
init_var(&elem);
init_var(&ni);
set_var_from_var(arg, &x);
if (x.sign == NUMERIC_NEG)
{
xneg = TRUE;
x.sign = NUMERIC_POS;
}
save_global_rscale = global_rscale;
global_rscale = 0;
for (i = x.weight, d = 0; i >= 0; i--, d++)
{
global_rscale *= 10;
if (d < x.ndigits)
global_rscale += x.digits[d];
if (global_rscale >= 1000)
{
free_allvars();
elog(ERROR, "argument for EXP() too big");
}
}
global_rscale = global_rscale / 2 + save_global_rscale + 8;
while(cmp_var(&x, &const_one) > 0)
{
ndiv2++;
global_rscale++;
div_var(&x, &const_two, &x);
}
add_var(&const_one, &x, result);
set_var_from_var(&x, &xpow);
set_var_from_var(&const_one, &ifac);
set_var_from_var(&const_one, &ni);
for (i = 2; TRUE; i++)
{
add_var(&ni, &const_one, &ni);
mul_var(&xpow, &x, &xpow);
mul_var(&ifac, &ni, &ifac);
div_var(&xpow, &ifac, &elem);
if (elem.ndigits == 0)
break;
add_var(result, &elem, result);
}
while (ndiv2-- > 0)
mul_var(result, result, result);
global_rscale = save_global_rscale;
if (xneg)
div_var(&const_one, result, result);
else
div_var(result, &const_one, result);
result->sign = NUMERIC_POS;
free_var(&x);
free_var(&xpow);
free_var(&ifac);
free_var(&elem);
free_var(&ni);
}
/* ----------
* ln_var() -
*
* Compute the natural log of x
* ----------
*/
static void
ln_var(NumericVar *arg, NumericVar *result)
{
NumericVar x;
NumericVar xx;
NumericVar ni;
NumericVar elem;
NumericVar fact;
int i;
int save_global_rscale;
if (cmp_var(arg, &const_zero) <= 0)
{
free_allvars();
elog(ERROR, "math error on numeric - cannot compute LN of value <= zero");
}
save_global_rscale = global_rscale;
global_rscale += 8;
init_var(&x);
init_var(&xx);
init_var(&ni);
init_var(&elem);
init_var(&fact);
set_var_from_var(&const_two, &fact);
set_var_from_var(arg, &x);
while (cmp_var(&x, &const_two) >= 0)
{
sqrt_var(&x, &x);
mul_var(&fact, &const_two, &fact);
}
set_var_from_str("0.5", &elem);
while (cmp_var(&x, &elem) <= 0)
{
sqrt_var(&x, &x);
mul_var(&fact, &const_two, &fact);
}
sub_var(&x, &const_one, result);
add_var(&x, &const_one, &elem);
div_var(result, &elem, result);
set_var_from_var(result, &xx);
mul_var(result, result, &x);
set_var_from_var(&const_one, &ni);
for (i = 2; TRUE; i++)
{
add_var(&ni, &const_two, &ni);
mul_var(&xx, &x, &xx);
div_var(&xx, &ni, &elem);
if (cmp_var(&elem, &const_zero) == 0)
break;
add_var(result, &elem, result);
}
global_rscale = save_global_rscale;
mul_var(result, &fact, result);
free_var(&x);
free_var(&xx);
free_var(&ni);
free_var(&elem);
free_var(&fact);
}
/* ----------
* log_var() -
*
* Compute the logarithm of x in a given base
* ----------
*/
static void
log_var(NumericVar *base, NumericVar *num, NumericVar *result)
{
NumericVar ln_base;
NumericVar ln_num;
global_rscale += 8;
init_var(&ln_base);
init_var(&ln_num);
ln_var(base, &ln_base);
ln_var(num, &ln_num);
global_rscale -= 8;
div_var(&ln_num, &ln_base, result);
free_var(&ln_num);
free_var(&ln_base);
}
/* ----------
* log_var() -
*
* Compute the logarithm of x in a given base
* ----------
*/
static void
power_var(NumericVar *base, NumericVar *exp, NumericVar *result)
{
NumericVar ln_base;
NumericVar ln_num;
int save_global_rscale;
save_global_rscale = global_rscale;
global_rscale += 8 + MAX(0, exp->weight);
init_var(&ln_base);
init_var(&ln_num);
ln_var(base, &ln_base);
mul_var(&ln_base, exp, &ln_num);
global_rscale = save_global_rscale;
exp_var(&ln_num, result);
free_var(&ln_num);
free_var(&ln_base);
}
/* ----------------------------------------------------------------------
*
* Following are the lowest level functions that operate unsigned
* on the variable level
*
* ----------------------------------------------------------------------
*/
/* ----------
* cmp_abs() -
*
* Compare the absolute values of var1 and var2
* Returns: -1 for ABS(var1) < ABS(var2)
* 0 for ABS(var1) == ABS(var2)
* 1 for ABS(var1) > ABS(var2)
* ----------
*/
static int
cmp_abs(NumericVar *var1, NumericVar *var2)
{
int i1 = 0;
int i2 = 0;
int w1 = var1->weight;
int w2 = var2->weight;
int stat;
while (w1 > w2)
{
if (var1->digits[i1++] != 0) return 1;
w1--;
}
while (w2 > w1)
{
if (var2->digits[i2++] != 0) return -1;
w2--;
}
while (i1 < var1->ndigits && i2 < var2->ndigits)
{
stat = var1->digits[i1++] - var2->digits[i2++];
if (stat)
{
if (stat > 0)
return 1;
return -1;
}
}
while (i1 < var1->ndigits)
{
if (var1->digits[i1++] != 0)
return 1;
}
while (i2 < var2->ndigits)
{
if (var2->digits[i2++] != 0)
return -1;
}
return 0;
}
/* ----------
* add_abs() -
*
* Add the absolute values of two variables into result.
* result might point to one of the operands without danger.
* ----------
*/
static void
add_abs(NumericVar *var1, NumericVar *var2, NumericVar *result)
{
NumericDigitBuf *res_buf;
NumericDigit *res_digits;
int res_ndigits;
int res_weight;
int res_rscale;
int res_dscale;
int i, i1, i2;
int carry = 0;
res_weight = MAX(var1->weight, var2->weight) + 1;
res_rscale = MAX(var1->rscale, var2->rscale);
res_dscale = MAX(var1->dscale, var2->dscale);
res_ndigits = res_rscale + res_weight + 1;
res_buf = digitbuf_alloc(res_ndigits);
res_digits = ((NumericDigit *)res_buf) + sizeof(NumericDigitBuf);
i1 = res_rscale + var1->weight + 1;
i2 = res_rscale + var2->weight + 1;
for (i = res_ndigits - 1; i >= 0; i--)
{
i1--;
i2--;
if (i1 >= 0 && i1 < var1->ndigits)
carry += var1->digits[i1];
if (i2 >= 0 && i2 < var2->ndigits)
carry += var2->digits[i2];
res_digits[i] = carry % 10;
carry /= 10;
}
while (res_ndigits > 0 && *res_digits == 0)
{
res_digits++;
res_weight--;
res_ndigits--;
}
while (res_ndigits > 0 && res_digits[res_ndigits - 1] == 0)
{
res_ndigits--;
}
if (res_ndigits == 0)
res_weight = 0;
digitbuf_free(result->buf);
result->ndigits = res_ndigits;
result->buf = res_buf;
result->digits = res_digits;
result->weight = res_weight;
result->rscale = res_rscale;
result->dscale = res_dscale;
}
/* ----------
* sub_abs() -
*
* Subtract the absolute value of var2 from the absolute value of var1
* and store in result. result might point to one of the operands
* without danger.
*
* ABS(var1) MUST BE GREATER OR EQUAL ABS(var2) !!!
* ----------
*/
static void
sub_abs(NumericVar *var1, NumericVar *var2, NumericVar *result)
{
NumericDigitBuf *res_buf;
NumericDigit *res_digits;
int res_ndigits;
int res_weight;
int res_rscale;
int res_dscale;
int i, i1, i2;
int borrow = 0;
res_weight = var1->weight;
res_rscale = MAX(var1->rscale, var2->rscale);
res_dscale = MAX(var1->dscale, var2->dscale);
res_ndigits = res_rscale + res_weight + 1;
res_buf = digitbuf_alloc(res_ndigits);
res_digits = ((NumericDigit *)res_buf) + sizeof(NumericDigitBuf);
i1 = res_rscale + var1->weight + 1;
i2 = res_rscale + var2->weight + 1;
for (i = res_ndigits - 1; i >= 0; i--)
{
i1--;
i2--;
if (i1 >= 0 && i1 < var1->ndigits)
borrow += var1->digits[i1];
if (i2 >= 0 && i2 < var2->ndigits)
borrow -= var2->digits[i2];
if (borrow < 0)
{
res_digits[i] = borrow + 10;
borrow = -1;
}
else
{
res_digits[i] = borrow;
borrow = 0;
}
}
while (res_ndigits > 0 && *res_digits == 0)
{
res_digits++;
res_weight--;
res_ndigits--;
}
while (res_ndigits > 0 && res_digits[res_ndigits - 1] == 0)
{
res_ndigits--;
}
if (res_ndigits == 0)
res_weight = 0;
digitbuf_free(result->buf);
result->ndigits = res_ndigits;
result->buf = res_buf;
result->digits = res_digits;
result->weight = res_weight;
result->rscale = res_rscale;
result->dscale = res_dscale;
}
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_aggregate.h,v 1.18 1998/12/08 06:18:11 thomas Exp $ * $Id: pg_aggregate.h,v 1.19 1998/12/30 19:56:30 wieck Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -95,6 +95,7 @@ DATA(insert OID = 0 ( avg PGUID float4pl float4inc float4div 700 700 700 ...@@ -95,6 +95,7 @@ DATA(insert OID = 0 ( avg PGUID float4pl float4inc float4div 700 700 700
DATA(insert OID = 0 ( avg PGUID float8pl float8inc float8div 701 701 701 701 _null_ 0.0 )); DATA(insert OID = 0 ( avg PGUID float8pl float8inc float8div 701 701 701 701 _null_ 0.0 ));
DATA(insert OID = 0 ( avg PGUID cash_pl float8inc cash_div_flt8 790 790 701 790 _null_ 0.0 )); DATA(insert OID = 0 ( avg PGUID cash_pl float8inc cash_div_flt8 790 790 701 790 _null_ 0.0 ));
DATA(insert OID = 0 ( avg PGUID timespan_pl float8inc timespan_div 1186 1186 701 1186 _null_ 0.0 )); DATA(insert OID = 0 ( avg PGUID timespan_pl float8inc timespan_div 1186 1186 701 1186 _null_ 0.0 ));
DATA(insert OID = 0 ( avg PGUID numeric_add numeric_inc numeric_div 1700 1700 1700 1700 _null_ 0 ));
DATA(insert OID = 0 ( sum PGUID int8pl - - 20 20 0 20 _null_ _null_ )); DATA(insert OID = 0 ( sum PGUID int8pl - - 20 20 0 20 _null_ _null_ ));
DATA(insert OID = 0 ( sum PGUID int4pl - - 23 23 0 23 _null_ _null_ )); DATA(insert OID = 0 ( sum PGUID int4pl - - 23 23 0 23 _null_ _null_ ));
...@@ -103,6 +104,7 @@ DATA(insert OID = 0 ( sum PGUID float4pl - - 700 700 0 700 _null_ _null_ )) ...@@ -103,6 +104,7 @@ DATA(insert OID = 0 ( sum PGUID float4pl - - 700 700 0 700 _null_ _null_ ))
DATA(insert OID = 0 ( sum PGUID float8pl - - 701 701 0 701 _null_ _null_ )); DATA(insert OID = 0 ( sum PGUID float8pl - - 701 701 0 701 _null_ _null_ ));
DATA(insert OID = 0 ( sum PGUID cash_pl - - 790 790 0 790 _null_ _null_ )); DATA(insert OID = 0 ( sum PGUID cash_pl - - 790 790 0 790 _null_ _null_ ));
DATA(insert OID = 0 ( sum PGUID timespan_pl - - 1186 1186 0 1186 _null_ _null_ )); DATA(insert OID = 0 ( sum PGUID timespan_pl - - 1186 1186 0 1186 _null_ _null_ ));
DATA(insert OID = 0 ( sum PGUID numeric_add - - 1700 1700 0 1700 _null_ _null_ ));
DATA(insert OID = 0 ( max PGUID int8larger - - 20 20 0 20 _null_ _null_ )); DATA(insert OID = 0 ( max PGUID int8larger - - 20 20 0 20 _null_ _null_ ));
DATA(insert OID = 0 ( max PGUID int4larger - - 23 23 0 23 _null_ _null_ )); DATA(insert OID = 0 ( max PGUID int4larger - - 23 23 0 23 _null_ _null_ ));
...@@ -116,6 +118,7 @@ DATA(insert OID = 0 ( max PGUID cashlarger - - 790 790 0 790 _null_ _null_ ) ...@@ -116,6 +118,7 @@ DATA(insert OID = 0 ( max PGUID cashlarger - - 790 790 0 790 _null_ _null_ )
DATA(insert OID = 0 ( max PGUID datetime_larger - - 1184 1184 0 1184 _null_ _null_ )); DATA(insert OID = 0 ( max PGUID datetime_larger - - 1184 1184 0 1184 _null_ _null_ ));
DATA(insert OID = 0 ( max PGUID timespan_larger - - 1186 1186 0 1186 _null_ _null_ )); DATA(insert OID = 0 ( max PGUID timespan_larger - - 1186 1186 0 1186 _null_ _null_ ));
DATA(insert OID = 0 ( max PGUID text_larger - - 25 25 0 25 _null_ _null_ )); DATA(insert OID = 0 ( max PGUID text_larger - - 25 25 0 25 _null_ _null_ ));
DATA(insert OID = 0 ( max PGUID numeric_larger - - 1700 1700 0 1700 _null_ _null_ ));
DATA(insert OID = 0 ( min PGUID int8smaller - - 20 20 0 20 _null_ _null_ )); DATA(insert OID = 0 ( min PGUID int8smaller - - 20 20 0 20 _null_ _null_ ));
DATA(insert OID = 0 ( min PGUID int4smaller - - 23 23 0 23 _null_ _null_ )); DATA(insert OID = 0 ( min PGUID int4smaller - - 23 23 0 23 _null_ _null_ ));
...@@ -129,6 +132,7 @@ DATA(insert OID = 0 ( min PGUID cashsmaller - - 790 790 0 790 _null_ _null_ ...@@ -129,6 +132,7 @@ DATA(insert OID = 0 ( min PGUID cashsmaller - - 790 790 0 790 _null_ _null_
DATA(insert OID = 0 ( min PGUID datetime_smaller - - 1184 1184 0 1184 _null_ _null_ )); DATA(insert OID = 0 ( min PGUID datetime_smaller - - 1184 1184 0 1184 _null_ _null_ ));
DATA(insert OID = 0 ( min PGUID timespan_smaller - - 1186 1186 0 1186 _null_ _null_ )); DATA(insert OID = 0 ( min PGUID timespan_smaller - - 1186 1186 0 1186 _null_ _null_ ));
DATA(insert OID = 0 ( min PGUID text_smaller - - 25 25 0 25 _null_ _null_ )); DATA(insert OID = 0 ( min PGUID text_smaller - - 25 25 0 25 _null_ _null_ ));
DATA(insert OID = 0 ( min PGUID numeric_smaller - - 1700 1700 0 1700 _null_ _null_ ));
DATA(insert OID = 0 ( count PGUID - int4inc - 0 0 23 23 _null_ 0 )); DATA(insert OID = 0 ( count PGUID - int4inc - 0 0 23 23 _null_ 0 ));
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_operator.h,v 1.47 1998/10/29 18:10:23 thomas Exp $ * $Id: pg_operator.h,v 1.48 1998/12/30 19:56:31 wieck Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -664,6 +664,22 @@ DATA(insert OID = 827 ( "<<=" PGUID 0 b t f 650 650 16 1004 828 0 0 ne ...@@ -664,6 +664,22 @@ DATA(insert OID = 827 ( "<<=" PGUID 0 b t f 650 650 16 1004 828 0 0 ne
DATA(insert OID = 828 ( ">>" PGUID 0 b t f 650 650 16 826 827 0 0 network_sup intltsel intltjoinsel )); DATA(insert OID = 828 ( ">>" PGUID 0 b t f 650 650 16 826 827 0 0 network_sup intltsel intltjoinsel ));
DATA(insert OID = 1004 ( ">>=" PGUID 0 b t f 650 650 16 827 826 0 0 network_supeq intltsel intltjoinsel )); DATA(insert OID = 1004 ( ">>=" PGUID 0 b t f 650 650 16 827 826 0 0 network_supeq intltsel intltjoinsel ));
/* NUMERIC type - OID's 1700-1799 */
DATA(insert OID = 1752 ( "=" PGUID 0 b t t 1700 1700 16 1752 1753 0 0 numeric_eq eqsel eqjoinsel ));
DATA(insert OID = 1753 ( "<>" PGUID 0 b t f 1700 1700 16 1753 1752 0 0 numeric_ne neqsel neqjoinsel ));
DATA(insert OID = 1754 ( "<" PGUID 0 b t f 1700 1700 16 1756 1757 0 0 numeric_lt intltsel intltjoinsel ));
DATA(insert OID = 1755 ( "<=" PGUID 0 b t f 1700 1700 16 1757 1756 0 0 numeric_le intltsel intltjoinsel ));
DATA(insert OID = 1756 ( ">" PGUID 0 b t f 1700 1700 16 1754 1755 0 0 numeric_gt intltsel intltjoinsel ));
DATA(insert OID = 1757 ( ">=" PGUID 0 b t f 1700 1700 16 1755 1754 0 0 numeric_ge intltsel intltjoinsel ));
DATA(insert OID = 1758 ( "+" PGUID 0 b t f 1700 1700 1700 1758 0 0 0 numeric_add - - ));
DATA(insert OID = 1759 ( "-" PGUID 0 b t f 1700 1700 1700 0 0 0 0 numeric_sub - - ));
DATA(insert OID = 1760 ( "*" PGUID 0 b t f 1700 1700 1700 1760 0 0 0 numeric_mul - - ));
DATA(insert OID = 1761 ( "/" PGUID 0 b t f 1700 1700 1700 0 0 0 0 numeric_div - - ));
DATA(insert OID = 1762 ( "%" PGUID 0 b t f 1700 1700 1700 0 0 0 0 numeric_mod - - ));
DATA(insert OID = 1763 ( "@" PGUID 0 l t f 0 1700 1700 0 0 0 0 numeric_abs - - ));
/* /*
* function prototypes * function prototypes
*/ */
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_proc.h,v 1.86 1998/12/13 23:45:22 thomas Exp $ * $Id: pg_proc.h,v 1.87 1998/12/30 19:56:32 wieck Exp $
* *
* NOTES * NOTES
* The script catalog/genbki.sh reads this file and generates .bki * The script catalog/genbki.sh reads this file and generates .bki
...@@ -2168,6 +2168,119 @@ DESCR("host address"); ...@@ -2168,6 +2168,119 @@ DESCR("host address");
DATA(insert OID = 683 ( network PGUID 14 f t f 1 f 25 "869" 100 0 0 100 "select network_network($1)" - )); DATA(insert OID = 683 ( network PGUID 14 f t f 1 f 25 "869" 100 0 0 100 "select network_network($1)" - ));
DESCR("network address"); DESCR("network address");
/* OID's 1700 - 1799 NUMERIC data type */
DATA(insert OID = 1701 ( numeric_in PGUID 11 f t f 1 f 1700 "0" 100 0 0 100 foo bar ));
DESCR("(internal)");
DATA(insert OID = 1702 ( numeric_out PGUID 11 f t f 1 f 23 "0" 100 0 0 100 foo bar ));
DESCR("(internal)");
DATA(insert OID = 1703 ( numeric PGUID 11 f t f 2 f 1700 "1700 23" 100 0 0 100 foo bar ));
DESCR("(internal)");
DATA(insert OID = 1704 ( numeric_abs PGUID 11 f t f 1 f 1700 "1700" 100 0 0 100 foo bar ));
DESCR("absolute value");
DATA(insert OID = 1705 ( abs PGUID 14 f t f 1 f 1700 "1700" 100 0 0 100 "select numeric_abs($1)" - ));
DESCR("absolute value");
DATA(insert OID = 1706 ( numeric_sign PGUID 11 f t f 1 f 1700 "1700" 100 0 0 100 foo bar ));
DESCR("sign of value");
DATA(insert OID = 1707 ( sign PGUID 14 f t f 1 f 1700 "1700" 100 0 0 100 "select numeric_sign($1)" - ));
DESCR("sign of value");
DATA(insert OID = 1708 ( numeric_round PGUID 11 f t f 2 f 1700 "1700 23" 100 0 0 100 foo bar ));
DESCR("value rounded to 'scale'");
DATA(insert OID = 1709 ( round PGUID 14 f t f 2 f 1700 "1700 23" 100 0 0 100 "select numeric_round($1,$2)" - ));
DESCR("value rounded to 'scale'");
DATA(insert OID = 1710 ( round PGUID 14 f t f 1 f 1700 "1700" 100 0 0 100 "select numeric_round($1,0)" - ));
DESCR("value rounded to 'scale' of zero");
DATA(insert OID = 1711 ( numeric_trunc PGUID 11 f t f 2 f 1700 "1700 23" 100 0 0 100 foo bar ));
DESCR("value truncated to 'scale'");
DATA(insert OID = 1712 ( trunc PGUID 14 f t f 2 f 1700 "1700 23" 100 0 0 100 "select numeric_trunc($1,$2)" - ));
DESCR("value truncated to 'scale'");
DATA(insert OID = 1713 ( trunc PGUID 14 f t f 1 f 1700 "1700" 100 0 0 100 "select numeric_trunc($1,0)" - ));
DESCR("value truncated to 'scale' of zero");
DATA(insert OID = 1714 ( numeric_ceil PGUID 11 f t f 1 f 1700 "1700" 100 0 0 100 foo bar ));
DESCR("smallest integer >= value");
DATA(insert OID = 1715 ( ceil PGUID 14 f t f 1 f 1700 "1700" 100 0 0 100 "select numeric_ceil($1)" - ));
DESCR("smallest integer >= value");
DATA(insert OID = 1716 ( numeric_floor PGUID 11 f t f 1 f 1700 "1700" 100 0 0 100 foo bar ));
DESCR("largest integer <= value");
DATA(insert OID = 1717 ( floor PGUID 14 f t f 1 f 1700 "1700" 100 0 0 100 "select numeric_floor($1)" - ));
DESCR("largest integer <= value");
DATA(insert OID = 1718 ( numeric_eq PGUID 11 f t f 2 f 16 "1700 1700" 100 0 0 100 foo bar ));
DESCR("equal");
DATA(insert OID = 1719 ( numeric_ne PGUID 11 f t f 2 f 16 "1700 1700" 100 0 0 100 foo bar ));
DESCR("not equal");
DATA(insert OID = 1720 ( numeric_gt PGUID 11 f t f 2 f 16 "1700 1700" 100 0 0 100 foo bar ));
DESCR("greater-than");
DATA(insert OID = 1721 ( numeric_ge PGUID 11 f t f 2 f 16 "1700 1700" 100 0 0 100 foo bar ));
DESCR("greater-than-or-equal");
DATA(insert OID = 1722 ( numeric_lt PGUID 11 f t f 2 f 16 "1700 1700" 100 0 0 100 foo bar ));
DESCR("lower-than");
DATA(insert OID = 1723 ( numeric_le PGUID 11 f t f 2 f 16 "1700 1700" 100 0 0 100 foo bar ));
DESCR("lower-than-or-equal");
DATA(insert OID = 1724 ( numeric_add PGUID 11 f t f 2 f 1700 "1700 1700" 100 0 0 100 foo bar ));
DESCR("addition");
DATA(insert OID = 1725 ( numeric_sub PGUID 11 f t f 2 f 1700 "1700 1700" 100 0 0 100 foo bar ));
DESCR("subtract");
DATA(insert OID = 1726 ( numeric_mul PGUID 11 f t f 2 f 1700 "1700 1700" 100 0 0 100 foo bar ));
DESCR("multiply");
DATA(insert OID = 1727 ( numeric_div PGUID 11 f t f 2 f 1700 "1700 1700" 100 0 0 100 foo bar ));
DESCR("divide");
DATA(insert OID = 1728 ( numeric_mod PGUID 11 f t f 2 f 1700 "1700 1700" 100 0 0 100 foo bar ));
DESCR("modulus");
DATA(insert OID = 1729 ( mod PGUID 14 f t f 2 f 1700 "1700 1700" 100 0 0 100 "select numeric_mod($1,$2)" - ));
DESCR("modulus");
DATA(insert OID = 1730 ( numeric_sqrt PGUID 11 f t f 1 f 1700 "1700" 100 0 0 100 foo bar ));
DESCR("square root");
DATA(insert OID = 1731 ( sqrt PGUID 14 f t f 1 f 1700 "1700" 100 0 0 100 "select numeric_sqrt($1)" - ));
DESCR("square root");
DATA(insert OID = 1732 ( numeric_exp PGUID 11 f t f 1 f 1700 "1700" 100 0 0 100 foo bar ));
DESCR("e raised to the power of n");
DATA(insert OID = 1733 ( exp PGUID 14 f t f 1 f 1700 "1700" 100 0 0 100 "select numeric_exp($1)" - ));
DESCR("e raised to the power of n");
DATA(insert OID = 1734 ( numeric_ln PGUID 11 f t f 1 f 1700 "1700" 100 0 0 100 foo bar ));
DESCR("natural logarithm of n");
DATA(insert OID = 1735 ( ln PGUID 14 f t f 1 f 1700 "1700" 100 0 0 100 "select numeric_ln($1)" - ));
DESCR("natural logarithm of n");
DATA(insert OID = 1736 ( numeric_log PGUID 11 f t f 2 f 1700 "1700 1700" 100 0 0 100 foo bar ));
DESCR("logarithm base m of n");
DATA(insert OID = 1737 ( log PGUID 14 f t f 2 f 1700 "1700 1700" 100 0 0 100 "select numeric_log($1,$2)" - ));
DESCR("logarithm base m of n");
DATA(insert OID = 1738 ( numeric_power PGUID 11 f t f 2 f 1700 "1700 1700" 100 0 0 100 foo bar ));
DESCR("m raised to the power of n");
DATA(insert OID = 1739 ( power PGUID 14 f t f 2 f 1700 "1700 1700" 100 0 0 100 "select numeric_power($1,$2)" - ));
DESCR("m raised to the power of n");
DATA(insert OID = 1740 ( int4_numeric PGUID 11 f t f 1 f 1700 "23" 100 0 0 100 foo bar ));
DESCR("(internal)");
DATA(insert OID = 1741 ( numeric PGUID 14 f t f 1 f 1700 "23" 100 0 0 100 "select int4_numeric($1)" - ));
DESCR("(internal)");
DATA(insert OID = 1742 ( float4_numeric PGUID 11 f t f 1 f 1700 "700" 100 0 0 100 foo bar ));
DESCR("(internal)");
DATA(insert OID = 1743 ( numeric PGUID 14 f t f 1 f 1700 "700" 100 0 0 100 "select float4_numeric($1)" - ));
DESCR("(internal)");
DATA(insert OID = 1744 ( float8_numeric PGUID 11 f t f 1 f 1700 "701" 100 0 0 100 foo bar ));
DESCR("(internal)");
DATA(insert OID = 1745 ( numeric PGUID 14 f t f 1 f 1700 "701" 100 0 0 100 "select float8_numeric($1)" - ));
DESCR("(internal)");
DATA(insert OID = 1746 ( numeric_int4 PGUID 11 f t f 1 f 23 "1700" 100 0 0 100 foo bar ));
DESCR("(internal)");
DATA(insert OID = 1747 ( int4 PGUID 14 f t f 1 f 23 "1700" 100 0 0 100 "select numeric_int4($1)" - ));
DESCR("(internal)");
DATA(insert OID = 1748 ( numeric_float4 PGUID 11 f t f 1 f 700 "1700" 100 0 0 100 foo bar ));
DESCR("(internal)");
DATA(insert OID = 1749 ( float4 PGUID 14 f t f 1 f 700 "1700" 100 0 0 100 "select numeric_float4($1)" - ));
DESCR("(internal)");
DATA(insert OID = 1750 ( numeric_float8 PGUID 11 f t f 1 f 701 "1700" 100 0 0 100 foo bar ));
DESCR("(internal)");
DATA(insert OID = 1751 ( float8 PGUID 14 f t f 1 f 701 "1700" 100 0 0 100 "select numeric_float8($1)" - ));
DESCR("(internal)");
DATA(insert OID = 1764 ( numeric_inc PGUID 11 f t f 1 f 1700 "1700" 100 0 0 100 foo bar ));
DESCR("increment by one");
DATA(insert OID = 1765 ( numeric_dec PGUID 11 f t f 1 f 1700 "1700" 100 0 0 100 foo bar ));
DESCR("decrement by one");
DATA(insert OID = 1766 ( numeric_smaller PGUID 11 f t f 2 f 1700 "1700 1700" 100 0 0 100 foo bar ));
DESCR("smaller of two numbers");
DATA(insert OID = 1767 ( numeric_larger PGUID 11 f t f 2 f 1700 "1700 1700" 100 0 0 100 foo bar ));
DESCR("larger of two numbers");
/* /*
* prototypes for functions pg_proc.c * prototypes for functions pg_proc.c
*/ */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_type.h,v 1.52 1998/10/22 13:51:03 momjian Exp $ * $Id: pg_type.h,v 1.53 1998/12/30 19:56:33 wieck Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -376,6 +376,11 @@ DATA(insert OID = 1296 ( timestamp PGUID 4 19 t b t \054 0 0 timestamp_in time ...@@ -376,6 +376,11 @@ DATA(insert OID = 1296 ( timestamp PGUID 4 19 t b t \054 0 0 timestamp_in time
DESCR("limited-range ISO-format date and time"); DESCR("limited-range ISO-format date and time");
#define TIMESTAMPOID 1296 #define TIMESTAMPOID 1296
/* OIDS 1700 - 1799 */
DATA(insert OID = 1700 ( numeric PGUID -1 -1 f b t \054 0 0 numeric_in numeric_out numeric_in numeric_out i _null_ ));
DESCR("arbitrary precision exact numeric data type");
#define NUMERICOID 1700
#define VARLENA_FIXED_SIZE(attr) ((attr)->atttypid == BPCHAROID && (attr)->atttypmod > 0) #define VARLENA_FIXED_SIZE(attr) ((attr)->atttypid == BPCHAROID && (attr)->atttypmod > 0)
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: builtins.h,v 1.72 1998/12/13 23:36:48 thomas Exp $ * $Id: builtins.h,v 1.73 1998/12/30 19:56:34 wieck Exp $
* *
* NOTES * NOTES
* This should normally only be included by fmgr.h. * This should normally only be included by fmgr.h.
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <utils/cash.h> #include <utils/cash.h>
#include <utils/inet.h> #include <utils/inet.h>
#include <utils/rel.h> #include <utils/rel.h>
#include <utils/numeric.h>
/* /*
* Defined in adt/ * Defined in adt/
...@@ -560,5 +561,46 @@ bool macaddr_ne(macaddr * a1, macaddr * a2); ...@@ -560,5 +561,46 @@ bool macaddr_ne(macaddr * a1, macaddr * a2);
int4 macaddr_cmp(macaddr * a1, macaddr * a2); int4 macaddr_cmp(macaddr * a1, macaddr * a2);
text *macaddr_manuf(macaddr * addr); text *macaddr_manuf(macaddr * addr);
/* numeric.c */
Numeric numeric_in(char *str, int dummy, int32 typmod);
char *numeric_out(Numeric num);
Numeric numeric(Numeric num, int32 typmod);
Numeric numeric_abs(Numeric num);
Numeric numeric_sign(Numeric num);
Numeric numeric_round(Numeric num, int32 scale);
Numeric numeric_trunc(Numeric num, int32 scale);
Numeric numeric_ceil(Numeric num);
Numeric numeric_floor(Numeric num);
bool numeric_eq(Numeric num1, Numeric num2);
bool numeric_ne(Numeric num1, Numeric num2);
bool numeric_gt(Numeric num1, Numeric num2);
bool numeric_ge(Numeric num1, Numeric num2);
bool numeric_lt(Numeric num1, Numeric num2);
bool numeric_le(Numeric num1, Numeric num2);
Numeric numeric_add(Numeric num1, Numeric num2);
Numeric numeric_sub(Numeric num1, Numeric num2);
Numeric numeric_mul(Numeric num1, Numeric num2);
Numeric numeric_div(Numeric num1, Numeric num2);
Numeric numeric_mod(Numeric num1, Numeric num2);
Numeric numeric_inc(Numeric num);
Numeric numeric_dec(Numeric num);
Numeric numeric_smaller(Numeric num1, Numeric num2);
Numeric numeric_larger(Numeric num1, Numeric num2);
Numeric numeric_sqrt(Numeric num);
Numeric numeric_exp(Numeric num);
Numeric numeric_ln(Numeric num);
Numeric numeric_log(Numeric num1, Numeric num2);
Numeric numeric_power(Numeric num1, Numeric num2);
Numeric int4_numeric(int32 val);
int32 numeric_int4(Numeric num);
Numeric float4_numeric(float32 val);
float32 numeric_float4(Numeric num);
Numeric float8_numeric(float64 val);
float64 numeric_float8(Numeric num);
#endif /* BUILTINS_H */ #endif /* BUILTINS_H */
/* ----------
* numeric.h -
*
* Definitions for the exact numeric data type of Postgres
*
* 1998 Jan Wieck
*
* $Header: /cvsroot/pgsql/src/include/utils/numeric.h,v 1.1 1998/12/30 19:56:35 wieck Exp $
*
* ----------
*/
#ifndef _PG_NUMERIC_H_
#define _PG_NUMERIC_H_
#include "postgres.h"
/* ----------
* The hardcoded limits and defaults of the numeric data type
* ----------
*/
#define NUMERIC_MAX_PRECISION 4000
#define NUMERIC_DEFAULT_PRECISION 30
#define NUMERIC_DEFAULT_SCALE 6
#define NUMERIC_MAX_DISPLAY_SCALE NUMERIC_MAX_PRECISION
#define NUMERIC_MIN_DISPLAY_SCALE NUMERIC_DEFAULT_SCALE + 4
#define NUMERIC_MAX_RESULT_SCALE (NUMERIC_MAX_PRECISION + 4)
#define NUMERIC_MIN_RESULT_SCALE (NUMERIC_DEFAULT_PRECISION + 4)
#define NUMERIC_UNPACKED_DATASIZE (NUMERIC_MAX_PRECISION * 2 + 4)
/* ----------
* Sign values and macros to deal with n_sign_dscale
* ----------
*/
#define NUMERIC_SIGN_MASK 0xC000
#define NUMERIC_POS 0x0000
#define NUMERIC_NEG 0x4000
#define NUMERIC_NAN 0xC000
#define NUMERIC_SIGN(n) ((n)->n_sign_dscale & NUMERIC_SIGN_MASK)
#define NUMERIC_DSCALE(n) ((n)->n_sign_dscale & ~NUMERIC_SIGN_MASK)
#define NUMERIC_IS_NAN(n) (NUMERIC_SIGN(n) != NUMERIC_POS && \
NUMERIC_SIGN(n) != NUMERIC_NEG)
/* ----------
* The Numeric data type stored in the database
* ----------
*/
typedef struct NumericData {
int32 varlen; /* Variable size */
int16 n_weight; /* Weight of 1st digit */
uint16 n_rscale; /* Result scale */
uint16 n_sign_dscale; /* Sign + display scale */
unsigned char n_data[1]; /* Digit data */
} NumericData;
typedef NumericData *Numeric;
#define NUMERIC_HDRSZ (sizeof(int32) + sizeof(uint16) * 3)
#endif /* _PG_NUMERIC_H_ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment