365 lines
14 KiB
C
365 lines
14 KiB
C
/* -------------------------------------------------------------------------
|
|
*
|
|
* numeric.h
|
|
* Definitions for the exact numeric data type of openGauss
|
|
*
|
|
* Original coding 1998, Jan Wieck. Heavily revised 2003, Tom Lane.
|
|
*
|
|
* Copyright (c) 1998-2012, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 2021, openGauss Contributors
|
|
*
|
|
* src/include/utils/numeric.h
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
#ifndef _PG_NUMERIC_H_
|
|
#define _PG_NUMERIC_H_
|
|
|
|
#include "fmgr.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 data types
|
|
*
|
|
* Numeric values are represented in a base-NBASE floating point format.
|
|
* Each "digit" ranges from 0 to NBASE-1. The type NumericDigit is signed
|
|
* and wide enough to store a digit. We assume that NBASE*NBASE can fit in
|
|
* an int. Although the purely calculational routines could handle any even
|
|
* NBASE that's less than sqrt(INT_MAX), in practice we are only interested
|
|
* in NBASE a power of ten, so that I/O conversions and decimal rounding
|
|
* are easy. Also, it's actually more efficient if NBASE is rather less than
|
|
* sqrt(INT_MAX), so that there is "headroom" for mul_var and div_var_fast to
|
|
* postpone processing carries.
|
|
*
|
|
* Values of NBASE other than 10000 are considered of historical interest only
|
|
* and are no longer supported in any sense; no mechanism exists for the client
|
|
* to discover the base, so every client supporting binary mode expects the
|
|
* base-10000 format. If you plan to change this, also note the numeric
|
|
* abbreviation code, which assumes NBASE=10000.
|
|
* ----------
|
|
*/
|
|
|
|
#if 1
|
|
#define NBASE 10000
|
|
#define HALF_NBASE 5000
|
|
#define DEC_DIGITS 4 /* decimal digits per NBASE digit */
|
|
#define NUMERIC_SCALE_ADJUST(scale) ((int64)(scale + DEC_DIGITS - 1) / DEC_DIGITS)
|
|
#define MUL_GUARD_DIGITS 2 /* these are measured in NBASE digits */
|
|
#define DIV_GUARD_DIGITS 4
|
|
|
|
typedef int16 NumericDigit;
|
|
#endif
|
|
|
|
/*
|
|
* The Numeric type as stored on disk.
|
|
*
|
|
* If the high bits of the first word of a NumericChoice (n_header, or
|
|
* n_short.n_header, or n_long.n_sign_dscale) are NUMERIC_SHORT, then the
|
|
* numeric follows the NumericShort format; if they are NUMERIC_POS or
|
|
* NUMERIC_NEG, it follows the NumericLong format. If they are NUMERIC_NAN,
|
|
* it is a NaN. We currently always store a NaN using just two bytes (i.e.
|
|
* only n_header), but previous releases used only the NumericLong format,
|
|
* so we might find 4-byte NaNs on disk if a database has been migrated using
|
|
* pg_upgrade. In either case, when the high bits indicate a NaN, the
|
|
* remaining bits are never examined. Currently, we always initialize these
|
|
* to zero, but it might be possible to use them for some other purpose in
|
|
* the future.
|
|
*
|
|
* In the NumericShort format, the remaining 14 bits of the header word
|
|
* (n_short.n_header) are allocated as follows: 1 for sign (positive or
|
|
* negative), 6 for dynamic scale, and 7 for weight. In practice, most
|
|
* commonly-encountered values can be represented this way.
|
|
*
|
|
* In the NumericLong format, the remaining 14 bits of the header word
|
|
* (n_long.n_sign_dscale) represent the display scale; and the weight is
|
|
* stored separately in n_weight.
|
|
*
|
|
* NOTE: by convention, values in the packed form have been stripped of
|
|
* all leading and trailing zero digits (where a "digit" is of base NBASE).
|
|
* In particular, if the value is zero, there will be no digits at all!
|
|
* The weight is arbitrary in that case, but we normally set it to zero.
|
|
*/
|
|
|
|
struct NumericShort {
|
|
uint16 n_header; /* Sign + display scale + weight */
|
|
NumericDigit n_data[1]; /* Digits */
|
|
};
|
|
|
|
struct NumericLong {
|
|
uint16 n_sign_dscale; /* Sign + display scale */
|
|
int16 n_weight; /* Weight of 1st digit */
|
|
NumericDigit n_data[1]; /* Digits */
|
|
};
|
|
|
|
/*
|
|
* NumericBi is used for big integer(bi64 or bi128)
|
|
* n_header stores mark bits and scale of big integer, first 4 bits to
|
|
* distinguish bi64 and bi128, next 4 bits are not used, the last 8 bits
|
|
* store the scale of bit integer, scale value is between 0 and 38.
|
|
* n_data store big integer value(int64 or int128)
|
|
*
|
|
*/
|
|
struct NumericBi {
|
|
uint16 n_header;
|
|
uint8 n_data[1];
|
|
};
|
|
|
|
/*
|
|
* Add NumericBi struct to NumericChoice
|
|
*/
|
|
union NumericChoice {
|
|
uint16 n_header; /* Header word */
|
|
struct NumericLong n_long; /* Long form (4-byte header) */
|
|
struct NumericShort n_short; /* Short form (2-byte header) */
|
|
struct NumericBi n_bi; /* Short form (2-byte header) */
|
|
};
|
|
|
|
struct NumericData {
|
|
int32 vl_len_; /* varlena header (do not touch directly!) */
|
|
union NumericChoice choice; /* choice of format */
|
|
};
|
|
|
|
/* The actual contents of Numeric are private to numeric.c */
|
|
struct NumericData;
|
|
typedef struct NumericData* Numeric;
|
|
|
|
/*
|
|
* Interpretation of high bits.
|
|
*/
|
|
|
|
#define NUMERIC_SIGN_MASK 0xC000
|
|
#define NUMERIC_POS 0x0000
|
|
#define NUMERIC_NEG 0x4000
|
|
#define NUMERIC_SHORT 0x8000
|
|
#define NUMERIC_NAN 0xC000
|
|
|
|
/*
|
|
* big integer macro
|
|
* n_header is the mark bits of numeric struct, when numeric is NAN, n_header
|
|
* marked 0xC000. To distinguish bi64, bi128 and numeric, we use 0xD000 to mark
|
|
* bi64, 0xE000 marks bi128, others are numeric type.
|
|
*/
|
|
#define NUMERIC_64 0xD000
|
|
#define NUMERIC_128 0xE000
|
|
#define NUMERIC_BI_MASK 0xF000
|
|
#define NUMERIC_BI_SCALEMASK 0x00FF
|
|
/*
|
|
* Hardcoded precision limit - arbitrary, but must be small enough that
|
|
* dscale values will fit in 14 bits.
|
|
*/
|
|
#define NUMERIC_MAX_PRECISION 1000
|
|
#define NUMERIC_MAX_SCALE 1000
|
|
#define NUMERIC_MIN_SCALE -84
|
|
|
|
/*
|
|
* Internal limits on the scales chosen for calculation results
|
|
*/
|
|
#define NUMERIC_MAX_DISPLAY_SCALE NUMERIC_MAX_PRECISION
|
|
#define NUMERIC_MIN_DISPLAY_SCALE 0
|
|
|
|
#define NUMERIC_MAX_RESULT_SCALE (NUMERIC_MAX_PRECISION * 2)
|
|
|
|
/*
|
|
* For inherently inexact calculations such as division and square root,
|
|
* we try to get at least this many significant digits; the idea is to
|
|
* deliver a result no worse than float8 would.
|
|
*/
|
|
#define NUMERIC_MIN_SIG_DIGITS 16
|
|
|
|
/*
|
|
* numeric nan data length exclude header
|
|
*/
|
|
#define NUMERIC_NAN_DATALENGTH 0
|
|
#define NUMERIC_ZERO_DATALENGTH 2
|
|
|
|
/*
|
|
* fmgr interface macros
|
|
* DatumGetNumeric function rebuild
|
|
*/
|
|
#define DatumGetNumeric(X) ((Numeric)PG_DETOAST_DATUM(X))
|
|
#define DatumGetNumericCopy(X) ((Numeric)PG_DETOAST_DATUM_COPY(X))
|
|
#define NumericGetDatum(X) PointerGetDatum(X)
|
|
#define PG_GETARG_NUMERIC(n) DatumGetNumeric(PG_GETARG_DATUM(n))
|
|
#define PG_GETARG_NUMERIC_COPY(n) DatumGetNumericCopy(PG_GETARG_DATUM(n))
|
|
#define PG_RETURN_NUMERIC(x) return NumericGetDatum(x)
|
|
|
|
/*
|
|
* Utility functions in numeric.c
|
|
*/
|
|
extern bool numeric_is_nan(Numeric num);
|
|
int32 numeric_maximum_size(int32 typmod);
|
|
extern char* numeric_out_sci(Numeric num, int scale);
|
|
extern Datum numtodsinterval(PG_FUNCTION_ARGS);
|
|
extern int cmp_numerics(Numeric num1, Numeric num2);
|
|
extern int128 numeric_int16_internal(Numeric num);
|
|
extern char* output_numeric_out(Numeric num);
|
|
|
|
//
|
|
// Numeric Compression Codes Area
|
|
//
|
|
#define INT32_MIN_ASCALE (-2)
|
|
#define INT32_MAX_ASCALE (2)
|
|
#define INT64_MIN_ASCALE (-4)
|
|
#define INT64_MAX_ASCALE (4)
|
|
|
|
extern const int16 INT16_MIN_VALUE;
|
|
extern const int16 INT16_MAX_VALUE;
|
|
extern const int32 INT32_MIN_VALUE;
|
|
extern const int32 INT32_MAX_VALUE;
|
|
extern const int64 INT64_MIN_VALUE;
|
|
extern const int64 INT64_MAX_VALUE;
|
|
|
|
#ifdef ENABLE_UT
|
|
extern void test_dump_numeric_to_file(_out_ FILE* fd, _in_ const char* str, _in_ Numeric num);
|
|
extern bool test_numeric_dscale_equal(_in_ Numeric v1, _in_ Numeric v2);
|
|
extern void test_dump_compressed_numeric_to_file(_out_ FILE* fd, _in_ const char* str, _in_ int64 v, _in_ char ascale);
|
|
extern char number_of_tail_zeros[10000];
|
|
#endif
|
|
|
|
/*
|
|
* convert functions between int32|int64|int128 and numerc
|
|
* convert numeric to int32, int64 or int128
|
|
* convert int32, int64, int128 to numeric
|
|
*/
|
|
extern bool convert_short_numeric_to_int32(_in_ int64 v, _in_ char ascale);
|
|
extern bool convert_short_numeric_to_int64(_in_ char* inBuf, _out_ int64* v, _out_ char* ascale);
|
|
extern int convert_int64_to_short_numeric(_out_ char* outBuf, _in_ int64 v, _in_ char ascale, _in_ int32 typmod);
|
|
extern int batch_convert_short_numeric_to_int64(_in_ Datum* batchValues, _in_ char* batchNulls, _in_ int batchRows,
|
|
_in_ bool hasNull, _out_ int64* outInt, _out_ char* outAscales, _out_ bool* outSuccess, _out_ int* outNullCount);
|
|
extern int convert_int64_to_short_numeric_byscale(
|
|
_out_ char* outBuf, _in_ __int128_t v, _in_ int32 typmod, _in_ int32 vscale);
|
|
extern int convert_int128_to_short_numeric_byscale(
|
|
_out_ char* outBuf, _in_ int128 v, _in_ int32 typmod, _in_ int32 vscale);
|
|
extern Datum convert_short_numeric_to_int64(_in_ Numeric inNum, _out_ bool* outSuccess);
|
|
extern Datum convert_short_numeric_to_int128(_in_ Numeric inNum, _out_ bool* outSuccess);
|
|
extern Datum try_convert_numeric_normal_to_fast(Datum value, ScalarVector *arr = NULL);
|
|
extern Datum try_direct_convert_numeric_normal_to_fast(Datum value, ScalarVector *arr);
|
|
extern int64 convert_short_numeric_to_int64_byscale(_in_ Numeric n, _in_ int scale);
|
|
extern void convert_short_numeric_to_int128_byscale(_in_ Numeric n, _in_ int scale, _out_ int128& result);
|
|
extern int32 get_ndigit_from_numeric(_in_ Numeric num);
|
|
|
|
/* ----------
|
|
* NumericVar is the format we use for arithmetic. The digit-array part
|
|
* is the same as the NumericData storage format, but the header is more
|
|
* complex.
|
|
*
|
|
* The value represented by a NumericVar is determined by the sign, weight,
|
|
* ndigits, and digits[] array.
|
|
* Note: the first digit of a NumericVar's value is assumed to be multiplied
|
|
* by NBASE ** weight. Another way to say it is that there are weight+1
|
|
* digits before the decimal point. It is possible to have weight < 0.
|
|
*
|
|
* buf points at the physical start of the palloc'd digit buffer for the
|
|
* NumericVar. digits points at the first digit in actual use (the one
|
|
* with the specified weight). We normally leave an unused digit or two
|
|
* (preset to zeroes) between buf and digits, so that there is room to store
|
|
* a carry out of the top digit without reallocating space. We just need to
|
|
* decrement digits (and increment weight) to make room for the carry digit.
|
|
* (There is no such extra space in a numeric value stored in the database,
|
|
* only in a NumericVar in memory.)
|
|
*
|
|
* If buf is NULL then the digit buffer isn't actually palloc'd and should
|
|
* not be freed --- see the constants below for an example.
|
|
*
|
|
* dscale, or display scale, is the nominal precision expressed as number
|
|
* of digits after the decimal point (it must always be >= 0 at present).
|
|
* dscale may be more than the number of physically stored fractional digits,
|
|
* implying that we have suppressed storage of significant trailing zeroes.
|
|
* It should never be less than the number of stored digits, since that would
|
|
* imply hiding digits that are present. NOTE that dscale is always expressed
|
|
* in *decimal* digits, and so it may correspond to a fractional number of
|
|
* base-NBASE digits --- divide by DEC_DIGITS to convert to NBASE digits.
|
|
*
|
|
* rscale, or result scale, is the target precision for a computation.
|
|
* Like dscale it is expressed as number of *decimal* digits after the decimal
|
|
* point, and is always >= 0 at present.
|
|
* Note that rscale is not stored in variables --- it's figured on-the-fly
|
|
* from the dscales of the inputs.
|
|
*
|
|
* While we consistently use "weight" to refer to the base-NBASE weight of
|
|
* a numeric value, it is convenient in some scale-related calculations to
|
|
* make use of the base-10 weight (ie, the approximate log10 of the value).
|
|
* To avoid confusion, such a decimal-units weight is called a "dweight".
|
|
*
|
|
* NB: All the variable-level functions are written in a style that makes it
|
|
* possible to give one and the same variable as argument and destination.
|
|
* This is feasible because the digit buffer is separate from the variable.
|
|
* ----------
|
|
*/
|
|
#define NUMERIC_LOCAL_NDIG 36 /* number of 'digits' in local digits[] */
|
|
#define NUMERIC_LOCAL_NMAX (NUMERIC_LOCAL_NDIG - 2)
|
|
typedef struct NumericVar {
|
|
int ndigits; /* # of digits in digits[] - can be 0! */
|
|
int weight; /* weight of first digit */
|
|
int sign; /* NUMERIC_POS, NUMERIC_NEG, or NUMERIC_NAN */
|
|
int dscale; /* display scale */
|
|
NumericDigit* buf; /* start of palloc'd space for digits[] */
|
|
NumericDigit* digits; /* base-NBASE digits */
|
|
NumericDigit ndb[NUMERIC_LOCAL_NDIG]; /* local space for digits[] */
|
|
} NumericVar;
|
|
|
|
#define quick_init_var(v) \
|
|
do { \
|
|
(v)->buf = (v)->ndb; \
|
|
(v)->digits = NULL; \
|
|
} while (0)
|
|
|
|
#define init_var(v) \
|
|
do { \
|
|
quick_init_var((v)); \
|
|
(v)->ndigits = 0; \
|
|
(v)->weight = 0; \
|
|
(v)->sign = 0; \
|
|
(v)->dscale = 0; \
|
|
} while (0)
|
|
|
|
#define digitbuf_alloc(ndigits) \
|
|
((NumericDigit*) palloc((ndigits) * sizeof(NumericDigit)))
|
|
|
|
#define digitbuf_free(v) \
|
|
do { \
|
|
if ((v)->buf != (v)->ndb) { \
|
|
pfree((v)->buf); \
|
|
(v)->buf = (v)->ndb; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define free_var(v) digitbuf_free((v));
|
|
|
|
/*
|
|
* Init a var and allocate digit buffer of ndigits digits (plus a spare digit for rounding).
|
|
* Called when first using a var.
|
|
*/
|
|
#define init_alloc_var(v, n) \
|
|
do { \
|
|
(v)->buf = (v)->ndb; \
|
|
(v)->ndigits = (n); \
|
|
if ((n) > NUMERIC_LOCAL_NMAX) { \
|
|
(v)->buf = digitbuf_alloc((n) + 1); \
|
|
} \
|
|
(v)->buf[0] = 0; \
|
|
(v)->digits = (v)->buf + 1; \
|
|
} while (0)
|
|
|
|
Numeric makeNumeric(NumericVar* var);
|
|
extern Numeric make_result(NumericVar *var);
|
|
extern void init_var_from_num(Numeric num, NumericVar* dest);
|
|
extern bool numericvar_to_int64(const NumericVar* var, int64* result, bool can_ignore = false);
|
|
extern void int64_to_numericvar(int64 val, NumericVar *var);
|
|
extern void add_var(NumericVar *var1, NumericVar *var2, NumericVar *result);
|
|
extern char *numeric_normalize(Numeric num);
|
|
extern double numeric_to_double_no_overflow(Numeric num);
|
|
|
|
bool numeric_agg_trans_initvalisnull(Oid transfn_oid, bool initvalisnull);
|
|
void numeric_transfn_info_change(Oid aggfn_oid, Oid *transfn_oid, Oid *transtype);
|
|
void numeric_finalfn_info_change(Oid aggdn_oid, Oid *finalfn_oid);
|
|
void numeric_aggfn_info_change(Oid aggfn_oid, Oid *transfn_oid, Oid *transtype, Oid *finalfn_oid);
|
|
|
|
#endif /* _PG_NUMERIC_H_ */
|