diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:15:05 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:15:05 +0000 |
commit | 46651ce6fe013220ed397add242004d764fc0153 (patch) | |
tree | 6e5299f990f88e60174a1d3ae6e48eedd2688b2b /src/backend/utils/adt/numeric.c | |
parent | Initial commit. (diff) | |
download | postgresql-14-46651ce6fe013220ed397add242004d764fc0153.tar.xz postgresql-14-46651ce6fe013220ed397add242004d764fc0153.zip |
Adding upstream version 14.5.upstream/14.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/utils/adt/numeric.c')
-rw-r--r-- | src/backend/utils/adt/numeric.c | 11279 |
1 files changed, 11279 insertions, 0 deletions
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c new file mode 100644 index 0000000..e78e0b9 --- /dev/null +++ b/src/backend/utils/adt/numeric.c @@ -0,0 +1,11279 @@ +/*------------------------------------------------------------------------- + * + * numeric.c + * An exact numeric data type for the Postgres database system + * + * Original coding 1998, Jan Wieck. Heavily revised 2003, Tom Lane. + * + * Many of the algorithmic ideas are borrowed from David M. Smith's "FM" + * multiple-precision math library, most recently published as Algorithm + * 786: Multiple-Precision Complex Arithmetic and Functions, ACM + * Transactions on Mathematical Software, Vol. 24, No. 4, December 1998, + * pages 359-367. + * + * Copyright (c) 1998-2021, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/utils/adt/numeric.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include <ctype.h> +#include <float.h> +#include <limits.h> +#include <math.h> + +#include "catalog/pg_type.h" +#include "common/hashfn.h" +#include "common/int.h" +#include "funcapi.h" +#include "lib/hyperloglog.h" +#include "libpq/pqformat.h" +#include "miscadmin.h" +#include "nodes/nodeFuncs.h" +#include "nodes/supportnodes.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/float.h" +#include "utils/guc.h" +#include "utils/int8.h" +#include "utils/numeric.h" +#include "utils/pg_lsn.h" +#include "utils/sortsupport.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 0 +#define NBASE 10 +#define HALF_NBASE 5 +#define DEC_DIGITS 1 /* decimal digits per NBASE digit */ +#define MUL_GUARD_DIGITS 4 /* these are measured in NBASE digits */ +#define DIV_GUARD_DIGITS 8 + +typedef signed char NumericDigit; +#endif + +#if 0 +#define NBASE 100 +#define HALF_NBASE 50 +#define DEC_DIGITS 2 /* decimal digits per NBASE digit */ +#define MUL_GUARD_DIGITS 3 /* these are measured in NBASE digits */ +#define DIV_GUARD_DIGITS 6 + +typedef signed char NumericDigit; +#endif + +#if 1 +#define NBASE 10000 +#define HALF_NBASE 5000 +#define DEC_DIGITS 4 /* decimal digits per NBASE digit */ +#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_SPECIAL, + * the value is a NaN or Infinity. We currently always store SPECIAL values + * using just two bytes (i.e. only n_header), but previous releases used only + * the NumericLong format, so we might find 4-byte NaNs (though not infinities) + * on disk if a database has been migrated using pg_upgrade. In either case, + * the low-order bits of a special value's header are reserved and currently + * should always be set to zero. + * + * 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[FLEXIBLE_ARRAY_MEMBER]; /* Digits */ +}; + +struct NumericLong +{ + uint16 n_sign_dscale; /* Sign + display scale */ + int16 n_weight; /* Weight of 1st digit */ + NumericDigit n_data[FLEXIBLE_ARRAY_MEMBER]; /* Digits */ +}; + +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 NumericData +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + union NumericChoice choice; /* choice of format */ +}; + + +/* + * Interpretation of high bits. + */ + +#define NUMERIC_SIGN_MASK 0xC000 +#define NUMERIC_POS 0x0000 +#define NUMERIC_NEG 0x4000 +#define NUMERIC_SHORT 0x8000 +#define NUMERIC_SPECIAL 0xC000 + +#define NUMERIC_FLAGBITS(n) ((n)->choice.n_header & NUMERIC_SIGN_MASK) +#define NUMERIC_IS_SHORT(n) (NUMERIC_FLAGBITS(n) == NUMERIC_SHORT) +#define NUMERIC_IS_SPECIAL(n) (NUMERIC_FLAGBITS(n) == NUMERIC_SPECIAL) + +#define NUMERIC_HDRSZ (VARHDRSZ + sizeof(uint16) + sizeof(int16)) +#define NUMERIC_HDRSZ_SHORT (VARHDRSZ + sizeof(uint16)) + +/* + * If the flag bits are NUMERIC_SHORT or NUMERIC_SPECIAL, we want the short + * header; otherwise, we want the long one. Instead of testing against each + * value, we can just look at the high bit, for a slight efficiency gain. + */ +#define NUMERIC_HEADER_IS_SHORT(n) (((n)->choice.n_header & 0x8000) != 0) +#define NUMERIC_HEADER_SIZE(n) \ + (VARHDRSZ + sizeof(uint16) + \ + (NUMERIC_HEADER_IS_SHORT(n) ? 0 : sizeof(int16))) + +/* + * Definitions for special values (NaN, positive infinity, negative infinity). + * + * The two bits after the NUMERIC_SPECIAL bits are 00 for NaN, 01 for positive + * infinity, 11 for negative infinity. (This makes the sign bit match where + * it is in a short-format value, though we make no use of that at present.) + * We could mask off the remaining bits before testing the active bits, but + * currently those bits must be zeroes, so masking would just add cycles. + */ +#define NUMERIC_EXT_SIGN_MASK 0xF000 /* high bits plus NaN/Inf flag bits */ +#define NUMERIC_NAN 0xC000 +#define NUMERIC_PINF 0xD000 +#define NUMERIC_NINF 0xF000 +#define NUMERIC_INF_SIGN_MASK 0x2000 + +#define NUMERIC_EXT_FLAGBITS(n) ((n)->choice.n_header & NUMERIC_EXT_SIGN_MASK) +#define NUMERIC_IS_NAN(n) ((n)->choice.n_header == NUMERIC_NAN) +#define NUMERIC_IS_PINF(n) ((n)->choice.n_header == NUMERIC_PINF) +#define NUMERIC_IS_NINF(n) ((n)->choice.n_header == NUMERIC_NINF) +#define NUMERIC_IS_INF(n) \ + (((n)->choice.n_header & ~NUMERIC_INF_SIGN_MASK) == NUMERIC_PINF) + +/* + * Short format definitions. + */ + +#define NUMERIC_SHORT_SIGN_MASK 0x2000 +#define NUMERIC_SHORT_DSCALE_MASK 0x1F80 +#define NUMERIC_SHORT_DSCALE_SHIFT 7 +#define NUMERIC_SHORT_DSCALE_MAX \ + (NUMERIC_SHORT_DSCALE_MASK >> NUMERIC_SHORT_DSCALE_SHIFT) +#define NUMERIC_SHORT_WEIGHT_SIGN_MASK 0x0040 +#define NUMERIC_SHORT_WEIGHT_MASK 0x003F +#define NUMERIC_SHORT_WEIGHT_MAX NUMERIC_SHORT_WEIGHT_MASK +#define NUMERIC_SHORT_WEIGHT_MIN (-(NUMERIC_SHORT_WEIGHT_MASK+1)) + +/* + * Extract sign, display scale, weight. These macros extract field values + * suitable for the NumericVar format from the Numeric (on-disk) format. + * + * Note that we don't trouble to ensure that dscale and weight read as zero + * for an infinity; however, that doesn't matter since we never convert + * "special" numerics to NumericVar form. Only the constants defined below + * (const_nan, etc) ever represent a non-finite value as a NumericVar. + */ + +#define NUMERIC_DSCALE_MASK 0x3FFF +#define NUMERIC_DSCALE_MAX NUMERIC_DSCALE_MASK + +#define NUMERIC_SIGN(n) \ + (NUMERIC_IS_SHORT(n) ? \ + (((n)->choice.n_short.n_header & NUMERIC_SHORT_SIGN_MASK) ? \ + NUMERIC_NEG : NUMERIC_POS) : \ + (NUMERIC_IS_SPECIAL(n) ? \ + NUMERIC_EXT_FLAGBITS(n) : NUMERIC_FLAGBITS(n))) +#define NUMERIC_DSCALE(n) (NUMERIC_HEADER_IS_SHORT((n)) ? \ + ((n)->choice.n_short.n_header & NUMERIC_SHORT_DSCALE_MASK) \ + >> NUMERIC_SHORT_DSCALE_SHIFT \ + : ((n)->choice.n_long.n_sign_dscale & NUMERIC_DSCALE_MASK)) +#define NUMERIC_WEIGHT(n) (NUMERIC_HEADER_IS_SHORT((n)) ? \ + (((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_SIGN_MASK ? \ + ~NUMERIC_SHORT_WEIGHT_MASK : 0) \ + | ((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_MASK)) \ + : ((n)->choice.n_long.n_weight)) + +/* ---------- + * 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. If it is a "special" value (NaN or Inf) + * then only the sign field matters; ndigits should be zero, and the weight + * and dscale fields are ignored. + * + * 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. + * ---------- + */ +typedef struct NumericVar +{ + int ndigits; /* # of digits in digits[] - can be 0! */ + int weight; /* weight of first digit */ + int sign; /* NUMERIC_POS, _NEG, _NAN, _PINF, or _NINF */ + int dscale; /* display scale */ + NumericDigit *buf; /* start of palloc'd space for digits[] */ + NumericDigit *digits; /* base-NBASE digits */ +} NumericVar; + + +/* ---------- + * Data for generate_series + * ---------- + */ +typedef struct +{ + NumericVar current; + NumericVar stop; + NumericVar step; +} generate_series_numeric_fctx; + + +/* ---------- + * Sort support. + * ---------- + */ +typedef struct +{ + void *buf; /* buffer for short varlenas */ + int64 input_count; /* number of non-null values seen */ + bool estimating; /* true if estimating cardinality */ + + hyperLogLogState abbr_card; /* cardinality estimator */ +} NumericSortSupport; + + +/* ---------- + * Fast sum accumulator. + * + * NumericSumAccum is used to implement SUM(), and other standard aggregates + * that track the sum of input values. It uses 32-bit integers to store the + * digits, instead of the normal 16-bit integers (with NBASE=10000). This + * way, we can safely accumulate up to NBASE - 1 values without propagating + * carry, before risking overflow of any of the digits. 'num_uncarried' + * tracks how many values have been accumulated without propagating carry. + * + * Positive and negative values are accumulated separately, in 'pos_digits' + * and 'neg_digits'. This is simpler and faster than deciding whether to add + * or subtract from the current value, for each new value (see sub_var() for + * the logic we avoid by doing this). Both buffers are of same size, and + * have the same weight and scale. In accum_sum_final(), the positive and + * negative sums are added together to produce the final result. + * + * When a new value has a larger ndigits or weight than the accumulator + * currently does, the accumulator is enlarged to accommodate the new value. + * We normally have one zero digit reserved for carry propagation, and that + * is indicated by the 'have_carry_space' flag. When accum_sum_carry() uses + * up the reserved digit, it clears the 'have_carry_space' flag. The next + * call to accum_sum_add() will enlarge the buffer, to make room for the + * extra digit, and set the flag again. + * + * To initialize a new accumulator, simply reset all fields to zeros. + * + * The accumulator does not handle NaNs. + * ---------- + */ +typedef struct NumericSumAccum +{ + int ndigits; + int weight; + int dscale; + int num_uncarried; + bool have_carry_space; + int32 *pos_digits; + int32 *neg_digits; +} NumericSumAccum; + + +/* + * We define our own macros for packing and unpacking abbreviated-key + * representations for numeric values in order to avoid depending on + * USE_FLOAT8_BYVAL. The type of abbreviation we use is based only on + * the size of a datum, not the argument-passing convention for float8. + * + * The range of abbreviations for finite values is from +PG_INT64/32_MAX + * to -PG_INT64/32_MAX. NaN has the abbreviation PG_INT64/32_MIN, and we + * define the sort ordering to make that work out properly (see further + * comments below). PINF and NINF share the abbreviations of the largest + * and smallest finite abbreviation classes. + */ +#define NUMERIC_ABBREV_BITS (SIZEOF_DATUM * BITS_PER_BYTE) +#if SIZEOF_DATUM == 8 +#define NumericAbbrevGetDatum(X) ((Datum) (X)) +#define DatumGetNumericAbbrev(X) ((int64) (X)) +#define NUMERIC_ABBREV_NAN NumericAbbrevGetDatum(PG_INT64_MIN) +#define NUMERIC_ABBREV_PINF NumericAbbrevGetDatum(-PG_INT64_MAX) +#define NUMERIC_ABBREV_NINF NumericAbbrevGetDatum(PG_INT64_MAX) +#else +#define NumericAbbrevGetDatum(X) ((Datum) (X)) +#define DatumGetNumericAbbrev(X) ((int32) (X)) +#define NUMERIC_ABBREV_NAN NumericAbbrevGetDatum(PG_INT32_MIN) +#define NUMERIC_ABBREV_PINF NumericAbbrevGetDatum(-PG_INT32_MAX) +#define NUMERIC_ABBREV_NINF NumericAbbrevGetDatum(PG_INT32_MAX) +#endif + + +/* ---------- + * Some preinitialized constants + * ---------- + */ +static const NumericDigit const_zero_data[1] = {0}; +static const NumericVar const_zero = +{0, 0, NUMERIC_POS, 0, NULL, (NumericDigit *) const_zero_data}; + +static const NumericDigit const_one_data[1] = {1}; +static const NumericVar const_one = +{1, 0, NUMERIC_POS, 0, NULL, (NumericDigit *) const_one_data}; + +static const NumericVar const_minus_one = +{1, 0, NUMERIC_NEG, 0, NULL, (NumericDigit *) const_one_data}; + +static const NumericDigit const_two_data[1] = {2}; +static const NumericVar const_two = +{1, 0, NUMERIC_POS, 0, NULL, (NumericDigit *) const_two_data}; + +#if DEC_DIGITS == 4 +static const NumericDigit const_zero_point_nine_data[1] = {9000}; +#elif DEC_DIGITS == 2 +static const NumericDigit const_zero_point_nine_data[1] = {90}; +#elif DEC_DIGITS == 1 +static const NumericDigit const_zero_point_nine_data[1] = {9}; +#endif +static const NumericVar const_zero_point_nine = +{1, -1, NUMERIC_POS, 1, NULL, (NumericDigit *) const_zero_point_nine_data}; + +#if DEC_DIGITS == 4 +static const NumericDigit const_one_point_one_data[2] = {1, 1000}; +#elif DEC_DIGITS == 2 +static const NumericDigit const_one_point_one_data[2] = {1, 10}; +#elif DEC_DIGITS == 1 +static const NumericDigit const_one_point_one_data[2] = {1, 1}; +#endif +static const NumericVar const_one_point_one = +{2, 0, NUMERIC_POS, 1, NULL, (NumericDigit *) const_one_point_one_data}; + +static const NumericVar const_nan = +{0, 0, NUMERIC_NAN, 0, NULL, NULL}; + +static const NumericVar const_pinf = +{0, 0, NUMERIC_PINF, 0, NULL, NULL}; + +static const NumericVar const_ninf = +{0, 0, NUMERIC_NINF, 0, NULL, NULL}; + +#if DEC_DIGITS == 4 +static const int round_powers[4] = {0, 1000, 100, 10}; +#endif + + +/* ---------- + * Local functions + * ---------- + */ + +#ifdef NUMERIC_DEBUG +static void dump_numeric(const char *str, Numeric num); +static void dump_var(const char *str, NumericVar *var); +#else +#define dump_numeric(s,n) +#define dump_var(s,v) +#endif + +#define digitbuf_alloc(ndigits) \ + ((NumericDigit *) palloc((ndigits) * sizeof(NumericDigit))) +#define digitbuf_free(buf) \ + do { \ + if ((buf) != NULL) \ + pfree(buf); \ + } while (0) + +#define init_var(v) memset(v, 0, sizeof(NumericVar)) + +#define NUMERIC_DIGITS(num) (NUMERIC_HEADER_IS_SHORT(num) ? \ + (num)->choice.n_short.n_data : (num)->choice.n_long.n_data) +#define NUMERIC_NDIGITS(num) \ + ((VARSIZE(num) - NUMERIC_HEADER_SIZE(num)) / sizeof(NumericDigit)) +#define NUMERIC_CAN_BE_SHORT(scale,weight) \ + ((scale) <= NUMERIC_SHORT_DSCALE_MAX && \ + (weight) <= NUMERIC_SHORT_WEIGHT_MAX && \ + (weight) >= NUMERIC_SHORT_WEIGHT_MIN) + +static void alloc_var(NumericVar *var, int ndigits); +static void free_var(NumericVar *var); +static void zero_var(NumericVar *var); + +static const char *set_var_from_str(const char *str, const char *cp, + NumericVar *dest); +static void set_var_from_num(Numeric value, NumericVar *dest); +static void init_var_from_num(Numeric num, NumericVar *dest); +static void set_var_from_var(const NumericVar *value, NumericVar *dest); +static char *get_str_from_var(const NumericVar *var); +static char *get_str_from_var_sci(const NumericVar *var, int rscale); + +static Numeric duplicate_numeric(Numeric num); +static Numeric make_result(const NumericVar *var); +static Numeric make_result_opt_error(const NumericVar *var, bool *error); + +static void apply_typmod(NumericVar *var, int32 typmod); +static void apply_typmod_special(Numeric num, int32 typmod); + +static bool numericvar_to_int32(const NumericVar *var, int32 *result); +static bool numericvar_to_int64(const NumericVar *var, int64 *result); +static void int64_to_numericvar(int64 val, NumericVar *var); +static bool numericvar_to_uint64(const NumericVar *var, uint64 *result); +#ifdef HAVE_INT128 +static bool numericvar_to_int128(const NumericVar *var, int128 *result); +static void int128_to_numericvar(int128 val, NumericVar *var); +#endif +static double numericvar_to_double_no_overflow(const NumericVar *var); + +static Datum numeric_abbrev_convert(Datum original_datum, SortSupport ssup); +static bool numeric_abbrev_abort(int memtupcount, SortSupport ssup); +static int numeric_fast_cmp(Datum x, Datum y, SortSupport ssup); +static int numeric_cmp_abbrev(Datum x, Datum y, SortSupport ssup); + +static Datum numeric_abbrev_convert_var(const NumericVar *var, + NumericSortSupport *nss); + +static int cmp_numerics(Numeric num1, Numeric num2); +static int cmp_var(const NumericVar *var1, const NumericVar *var2); +static int cmp_var_common(const NumericDigit *var1digits, int var1ndigits, + int var1weight, int var1sign, + const NumericDigit *var2digits, int var2ndigits, + int var2weight, int var2sign); +static void add_var(const NumericVar *var1, const NumericVar *var2, + NumericVar *result); +static void sub_var(const NumericVar *var1, const NumericVar *var2, + NumericVar *result); +static void mul_var(const NumericVar *var1, const NumericVar *var2, + NumericVar *result, + int rscale); +static void div_var(const NumericVar *var1, const NumericVar *var2, + NumericVar *result, + int rscale, bool round); +static void div_var_fast(const NumericVar *var1, const NumericVar *var2, + NumericVar *result, int rscale, bool round); +static int select_div_scale(const NumericVar *var1, const NumericVar *var2); +static void mod_var(const NumericVar *var1, const NumericVar *var2, + NumericVar *result); +static void div_mod_var(const NumericVar *var1, const NumericVar *var2, + NumericVar *quot, NumericVar *rem); +static void ceil_var(const NumericVar *var, NumericVar *result); +static void floor_var(const NumericVar *var, NumericVar *result); + +static void gcd_var(const NumericVar *var1, const NumericVar *var2, + NumericVar *result); +static void sqrt_var(const NumericVar *arg, NumericVar *result, int rscale); +static void exp_var(const NumericVar *arg, NumericVar *result, int rscale); +static int estimate_ln_dweight(const NumericVar *var); +static void ln_var(const NumericVar *arg, NumericVar *result, int rscale); +static void log_var(const NumericVar *base, const NumericVar *num, + NumericVar *result); +static void power_var(const NumericVar *base, const NumericVar *exp, + NumericVar *result); +static void power_var_int(const NumericVar *base, int exp, NumericVar *result, + int rscale); +static void power_ten_int(int exp, NumericVar *result); + +static int cmp_abs(const NumericVar *var1, const NumericVar *var2); +static int cmp_abs_common(const NumericDigit *var1digits, int var1ndigits, + int var1weight, + const NumericDigit *var2digits, int var2ndigits, + int var2weight); +static void add_abs(const NumericVar *var1, const NumericVar *var2, + NumericVar *result); +static void sub_abs(const NumericVar *var1, const NumericVar *var2, + NumericVar *result); +static void round_var(NumericVar *var, int rscale); +static void trunc_var(NumericVar *var, int rscale); +static void strip_var(NumericVar *var); +static void compute_bucket(Numeric operand, Numeric bound1, Numeric bound2, + const NumericVar *count_var, bool reversed_bounds, + NumericVar *result_var); + +static void accum_sum_add(NumericSumAccum *accum, const NumericVar *var1); +static void accum_sum_rescale(NumericSumAccum *accum, const NumericVar *val); +static void accum_sum_carry(NumericSumAccum *accum); +static void accum_sum_reset(NumericSumAccum *accum); +static void accum_sum_final(NumericSumAccum *accum, NumericVar *result); +static void accum_sum_copy(NumericSumAccum *dst, NumericSumAccum *src); +static void accum_sum_combine(NumericSumAccum *accum, NumericSumAccum *accum2); + + +/* ---------------------------------------------------------------------- + * + * Input-, output- and rounding-functions + * + * ---------------------------------------------------------------------- + */ + + +/* + * numeric_in() - + * + * Input function for numeric data type + */ +Datum +numeric_in(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + +#ifdef NOT_USED + Oid typelem = PG_GETARG_OID(1); +#endif + int32 typmod = PG_GETARG_INT32(2); + Numeric res; + const char *cp; + + /* Skip leading spaces */ + cp = str; + while (*cp) + { + if (!isspace((unsigned char) *cp)) + break; + cp++; + } + + /* + * Check for NaN and infinities. We recognize the same strings allowed by + * float8in(). + */ + if (pg_strncasecmp(cp, "NaN", 3) == 0) + { + res = make_result(&const_nan); + cp += 3; + } + else if (pg_strncasecmp(cp, "Infinity", 8) == 0) + { + res = make_result(&const_pinf); + cp += 8; + } + else if (pg_strncasecmp(cp, "+Infinity", 9) == 0) + { + res = make_result(&const_pinf); + cp += 9; + } + else if (pg_strncasecmp(cp, "-Infinity", 9) == 0) + { + res = make_result(&const_ninf); + cp += 9; + } + else if (pg_strncasecmp(cp, "inf", 3) == 0) + { + res = make_result(&const_pinf); + cp += 3; + } + else if (pg_strncasecmp(cp, "+inf", 4) == 0) + { + res = make_result(&const_pinf); + cp += 4; + } + else if (pg_strncasecmp(cp, "-inf", 4) == 0) + { + res = make_result(&const_ninf); + cp += 4; + } + else + { + /* + * Use set_var_from_str() to parse a normal numeric value + */ + NumericVar value; + + init_var(&value); + + cp = set_var_from_str(str, cp, &value); + + /* + * We duplicate a few lines of code here because we would like to + * throw any trailing-junk syntax error before any semantic error + * resulting from apply_typmod. We can't easily fold the two cases + * together because we mustn't apply apply_typmod to a NaN/Inf. + */ + while (*cp) + { + if (!isspace((unsigned char) *cp)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "numeric", str))); + cp++; + } + + apply_typmod(&value, typmod); + + res = make_result(&value); + free_var(&value); + + PG_RETURN_NUMERIC(res); + } + + /* Should be nothing left but spaces */ + while (*cp) + { + if (!isspace((unsigned char) *cp)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "numeric", str))); + cp++; + } + + /* As above, throw any typmod error after finishing syntax check */ + apply_typmod_special(res, typmod); + + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_out() - + * + * Output function for numeric data type + */ +Datum +numeric_out(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + NumericVar x; + char *str; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num)) + { + if (NUMERIC_IS_PINF(num)) + PG_RETURN_CSTRING(pstrdup("Infinity")); + else if (NUMERIC_IS_NINF(num)) + PG_RETURN_CSTRING(pstrdup("-Infinity")); + else + PG_RETURN_CSTRING(pstrdup("NaN")); + } + + /* + * Get the number in the variable format. + */ + init_var_from_num(num, &x); + + str = get_str_from_var(&x); + + PG_RETURN_CSTRING(str); +} + +/* + * numeric_is_nan() - + * + * Is Numeric value a NaN? + */ +bool +numeric_is_nan(Numeric num) +{ + return NUMERIC_IS_NAN(num); +} + +/* + * numeric_is_inf() - + * + * Is Numeric value an infinity? + */ +bool +numeric_is_inf(Numeric num) +{ + return NUMERIC_IS_INF(num); +} + +/* + * numeric_is_integral() - + * + * Is Numeric value integral? + */ +static bool +numeric_is_integral(Numeric num) +{ + NumericVar arg; + + /* Reject NaN, but infinities are considered integral */ + if (NUMERIC_IS_SPECIAL(num)) + { + if (NUMERIC_IS_NAN(num)) + return false; + return true; + } + + /* Integral if there are no digits to the right of the decimal point */ + init_var_from_num(num, &arg); + + return (arg.ndigits == 0 || arg.ndigits <= arg.weight + 1); +} + +/* + * numeric_maximum_size() - + * + * Maximum size of a numeric with given typmod, or -1 if unlimited/unknown. + */ +int32 +numeric_maximum_size(int32 typmod) +{ + int precision; + int numeric_digits; + + if (typmod < (int32) (VARHDRSZ)) + return -1; + + /* precision (ie, max # of digits) is in upper bits of typmod */ + precision = ((typmod - VARHDRSZ) >> 16) & 0xffff; + + /* + * This formula computes the maximum number of NumericDigits we could need + * in order to store the specified number of decimal digits. Because the + * weight is stored as a number of NumericDigits rather than a number of + * decimal digits, it's possible that the first NumericDigit will contain + * only a single decimal digit. Thus, the first two decimal digits can + * require two NumericDigits to store, but it isn't until we reach + * DEC_DIGITS + 2 decimal digits that we potentially need a third + * NumericDigit. + */ + numeric_digits = (precision + 2 * (DEC_DIGITS - 1)) / DEC_DIGITS; + + /* + * In most cases, the size of a numeric will be smaller than the value + * computed below, because the varlena header will typically get toasted + * down to a single byte before being stored on disk, and it may also be + * possible to use a short numeric header. But our job here is to compute + * the worst case. + */ + return NUMERIC_HDRSZ + (numeric_digits * sizeof(NumericDigit)); +} + +/* + * numeric_out_sci() - + * + * Output function for numeric data type in scientific notation. + */ +char * +numeric_out_sci(Numeric num, int scale) +{ + NumericVar x; + char *str; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num)) + { + if (NUMERIC_IS_PINF(num)) + return pstrdup("Infinity"); + else if (NUMERIC_IS_NINF(num)) + return pstrdup("-Infinity"); + else + return pstrdup("NaN"); + } + + init_var_from_num(num, &x); + + str = get_str_from_var_sci(&x, scale); + + return str; +} + +/* + * numeric_normalize() - + * + * Output function for numeric data type, suppressing insignificant trailing + * zeroes and then any trailing decimal point. The intent of this is to + * produce strings that are equal if and only if the input numeric values + * compare equal. + */ +char * +numeric_normalize(Numeric num) +{ + NumericVar x; + char *str; + int last; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num)) + { + if (NUMERIC_IS_PINF(num)) + return pstrdup("Infinity"); + else if (NUMERIC_IS_NINF(num)) + return pstrdup("-Infinity"); + else + return pstrdup("NaN"); + } + + init_var_from_num(num, &x); + + str = get_str_from_var(&x); + + /* If there's no decimal point, there's certainly nothing to remove. */ + if (strchr(str, '.') != NULL) + { + /* + * Back up over trailing fractional zeroes. Since there is a decimal + * point, this loop will terminate safely. + */ + last = strlen(str) - 1; + while (str[last] == '0') + last--; + + /* We want to get rid of the decimal point too, if it's now last. */ + if (str[last] == '.') + last--; + + /* Delete whatever we backed up over. */ + str[last + 1] = '\0'; + } + + return str; +} + +/* + * numeric_recv - converts external binary format to numeric + * + * External format is a sequence of int16's: + * ndigits, weight, sign, dscale, NumericDigits. + */ +Datum +numeric_recv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + +#ifdef NOT_USED + Oid typelem = PG_GETARG_OID(1); +#endif + int32 typmod = PG_GETARG_INT32(2); + NumericVar value; + Numeric res; + int len, + i; + + init_var(&value); + + len = (uint16) pq_getmsgint(buf, sizeof(uint16)); + + alloc_var(&value, len); + + value.weight = (int16) pq_getmsgint(buf, sizeof(int16)); + /* we allow any int16 for weight --- OK? */ + + value.sign = (uint16) pq_getmsgint(buf, sizeof(uint16)); + if (!(value.sign == NUMERIC_POS || + value.sign == NUMERIC_NEG || + value.sign == NUMERIC_NAN || + value.sign == NUMERIC_PINF || + value.sign == NUMERIC_NINF)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), + errmsg("invalid sign in external \"numeric\" value"))); + + value.dscale = (uint16) pq_getmsgint(buf, sizeof(uint16)); + if ((value.dscale & NUMERIC_DSCALE_MASK) != value.dscale) + ereport(ERROR, + (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), + errmsg("invalid scale in external \"numeric\" value"))); + + for (i = 0; i < len; i++) + { + NumericDigit d = pq_getmsgint(buf, sizeof(NumericDigit)); + + if (d < 0 || d >= NBASE) + ereport(ERROR, + (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), + errmsg("invalid digit in external \"numeric\" value"))); + value.digits[i] = d; + } + + /* + * If the given dscale would hide any digits, truncate those digits away. + * We could alternatively throw an error, but that would take a bunch of + * extra code (about as much as trunc_var involves), and it might cause + * client compatibility issues. Be careful not to apply trunc_var to + * special values, as it could do the wrong thing; we don't need it + * anyway, since make_result will ignore all but the sign field. + * + * After doing that, be sure to check the typmod restriction. + */ + if (value.sign == NUMERIC_POS || + value.sign == NUMERIC_NEG) + { + trunc_var(&value, value.dscale); + + apply_typmod(&value, typmod); + + res = make_result(&value); + } + else + { + /* apply_typmod_special wants us to make the Numeric first */ + res = make_result(&value); + + apply_typmod_special(res, typmod); + } + + free_var(&value); + + PG_RETURN_NUMERIC(res); +} + +/* + * numeric_send - converts numeric to binary format + */ +Datum +numeric_send(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + NumericVar x; + StringInfoData buf; + int i; + + init_var_from_num(num, &x); + + pq_begintypsend(&buf); + + pq_sendint16(&buf, x.ndigits); + pq_sendint16(&buf, x.weight); + pq_sendint16(&buf, x.sign); + pq_sendint16(&buf, x.dscale); + for (i = 0; i < x.ndigits; i++) + pq_sendint16(&buf, x.digits[i]); + + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); +} + + +/* + * numeric_support() + * + * Planner support function for the numeric() length coercion function. + * + * Flatten calls that solely represent increases in allowable precision. + * Scale changes mutate every datum, so they are unoptimizable. Some values, + * e.g. 1E-1001, can only fit into an unconstrained numeric, so a change from + * an unconstrained numeric to any constrained numeric is also unoptimizable. + */ +Datum +numeric_support(PG_FUNCTION_ARGS) +{ + Node *rawreq = (Node *) PG_GETARG_POINTER(0); + Node *ret = NULL; + + if (IsA(rawreq, SupportRequestSimplify)) + { + SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq; + FuncExpr *expr = req->fcall; + Node *typmod; + + Assert(list_length(expr->args) >= 2); + + typmod = (Node *) lsecond(expr->args); + + if (IsA(typmod, Const) && !((Const *) typmod)->constisnull) + { + Node *source = (Node *) linitial(expr->args); + int32 old_typmod = exprTypmod(source); + int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue); + int32 old_scale = (old_typmod - VARHDRSZ) & 0xffff; + int32 new_scale = (new_typmod - VARHDRSZ) & 0xffff; + int32 old_precision = (old_typmod - VARHDRSZ) >> 16 & 0xffff; + int32 new_precision = (new_typmod - VARHDRSZ) >> 16 & 0xffff; + + /* + * If new_typmod < VARHDRSZ, the destination is unconstrained; + * that's always OK. If old_typmod >= VARHDRSZ, the source is + * constrained, and we're OK if the scale is unchanged and the + * precision is not decreasing. See further notes in function + * header comment. + */ + if (new_typmod < (int32) VARHDRSZ || + (old_typmod >= (int32) VARHDRSZ && + new_scale == old_scale && new_precision >= old_precision)) + ret = relabel_to_typmod(source, new_typmod); + } + } + + PG_RETURN_POINTER(ret); +} + +/* + * numeric() - + * + * This is a special function called by the Postgres database system + * before a value is stored in a tuple's attribute. The precision and + * scale of the attribute have to be applied on the value. + */ +Datum +numeric (PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + int32 typmod = PG_GETARG_INT32(1); + Numeric new; + int32 tmp_typmod; + int precision; + int scale; + int ddigits; + int maxdigits; + NumericVar var; + + /* + * Handle NaN and infinities: if apply_typmod_special doesn't complain, + * just return a copy of the input. + */ + if (NUMERIC_IS_SPECIAL(num)) + { + apply_typmod_special(num, typmod); + PG_RETURN_NUMERIC(duplicate_numeric(num)); + } + + /* + * If the value isn't a valid type modifier, simply return a copy of the + * input value + */ + if (typmod < (int32) (VARHDRSZ)) + PG_RETURN_NUMERIC(duplicate_numeric(num)); + + /* + * Get the precision and scale out of the typmod value + */ + tmp_typmod = typmod - VARHDRSZ; + precision = (tmp_typmod >> 16) & 0xffff; + scale = tmp_typmod & 0xffff; + maxdigits = precision - scale; + + /* + * If the number is certainly in bounds and due to the target scale no + * rounding could be necessary, just make a copy of the input and modify + * its scale fields, unless the larger scale forces us to abandon the + * short representation. (Note we assume the existing dscale is + * honest...) + */ + ddigits = (NUMERIC_WEIGHT(num) + 1) * DEC_DIGITS; + if (ddigits <= maxdigits && scale >= NUMERIC_DSCALE(num) + && (NUMERIC_CAN_BE_SHORT(scale, NUMERIC_WEIGHT(num)) + || !NUMERIC_IS_SHORT(num))) + { + new = duplicate_numeric(num); + if (NUMERIC_IS_SHORT(num)) + new->choice.n_short.n_header = + (num->choice.n_short.n_header & ~NUMERIC_SHORT_DSCALE_MASK) + | (scale << NUMERIC_SHORT_DSCALE_SHIFT); + else + new->choice.n_long.n_sign_dscale = NUMERIC_SIGN(new) | + ((uint16) scale & NUMERIC_DSCALE_MASK); + PG_RETURN_NUMERIC(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); + + PG_RETURN_NUMERIC(new); +} + +Datum +numerictypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + int32 *tl; + int n; + int32 typmod; + + tl = ArrayGetIntegerTypmods(ta, &n); + + if (n == 2) + { + if (tl[0] < 1 || tl[0] > NUMERIC_MAX_PRECISION) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("NUMERIC precision %d must be between 1 and %d", + tl[0], NUMERIC_MAX_PRECISION))); + if (tl[1] < 0 || tl[1] > tl[0]) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("NUMERIC scale %d must be between 0 and precision %d", + tl[1], tl[0]))); + typmod = ((tl[0] << 16) | tl[1]) + VARHDRSZ; + } + else if (n == 1) + { + if (tl[0] < 1 || tl[0] > NUMERIC_MAX_PRECISION) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("NUMERIC precision %d must be between 1 and %d", + tl[0], NUMERIC_MAX_PRECISION))); + /* scale defaults to zero */ + typmod = (tl[0] << 16) + VARHDRSZ; + } + else + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid NUMERIC type modifier"))); + typmod = 0; /* keep compiler quiet */ + } + + PG_RETURN_INT32(typmod); +} + +Datum +numerictypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + char *res = (char *) palloc(64); + + if (typmod >= 0) + snprintf(res, 64, "(%d,%d)", + ((typmod - VARHDRSZ) >> 16) & 0xffff, + (typmod - VARHDRSZ) & 0xffff); + else + *res = '\0'; + + PG_RETURN_CSTRING(res); +} + + +/* ---------------------------------------------------------------------- + * + * Sign manipulation, rounding and the like + * + * ---------------------------------------------------------------------- + */ + +Datum +numeric_abs(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + Numeric res; + + /* + * Do it the easy way directly on the packed format + */ + res = duplicate_numeric(num); + + if (NUMERIC_IS_SHORT(num)) + res->choice.n_short.n_header = + num->choice.n_short.n_header & ~NUMERIC_SHORT_SIGN_MASK; + else if (NUMERIC_IS_SPECIAL(num)) + { + /* This changes -Inf to Inf, and doesn't affect NaN */ + res->choice.n_short.n_header = + num->choice.n_short.n_header & ~NUMERIC_INF_SIGN_MASK; + } + else + res->choice.n_long.n_sign_dscale = NUMERIC_POS | NUMERIC_DSCALE(num); + + PG_RETURN_NUMERIC(res); +} + + +Datum +numeric_uminus(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + Numeric res; + + /* + * Do it the easy way directly on the packed format + */ + res = duplicate_numeric(num); + + if (NUMERIC_IS_SPECIAL(num)) + { + /* Flip the sign, if it's Inf or -Inf */ + if (!NUMERIC_IS_NAN(num)) + res->choice.n_short.n_header = + num->choice.n_short.n_header ^ NUMERIC_INF_SIGN_MASK; + } + + /* + * The packed format is known to be totally zero digit trimmed always. So + * once we've eliminated specials, we can identify a zero by the fact that + * there are no digits at all. Do nothing to a zero. + */ + else if (NUMERIC_NDIGITS(num) != 0) + { + /* Else, flip the sign */ + if (NUMERIC_IS_SHORT(num)) + res->choice.n_short.n_header = + num->choice.n_short.n_header ^ NUMERIC_SHORT_SIGN_MASK; + else if (NUMERIC_SIGN(num) == NUMERIC_POS) + res->choice.n_long.n_sign_dscale = + NUMERIC_NEG | NUMERIC_DSCALE(num); + else + res->choice.n_long.n_sign_dscale = + NUMERIC_POS | NUMERIC_DSCALE(num); + } + + PG_RETURN_NUMERIC(res); +} + + +Datum +numeric_uplus(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + + PG_RETURN_NUMERIC(duplicate_numeric(num)); +} + + +/* + * numeric_sign_internal() - + * + * Returns -1 if the argument is less than 0, 0 if the argument is equal + * to 0, and 1 if the argument is greater than zero. Caller must have + * taken care of the NaN case, but we can handle infinities here. + */ +static int +numeric_sign_internal(Numeric num) +{ + if (NUMERIC_IS_SPECIAL(num)) + { + Assert(!NUMERIC_IS_NAN(num)); + /* Must be Inf or -Inf */ + if (NUMERIC_IS_PINF(num)) + return 1; + else + return -1; + } + + /* + * The packed format is known to be totally zero digit trimmed always. So + * once we've eliminated specials, we can identify a zero by the fact that + * there are no digits at all. + */ + else if (NUMERIC_NDIGITS(num) == 0) + return 0; + else if (NUMERIC_SIGN(num) == NUMERIC_NEG) + return -1; + else + return 1; +} + +/* + * numeric_sign() - + * + * returns -1 if the argument is less than 0, 0 if the argument is equal + * to 0, and 1 if the argument is greater than zero. + */ +Datum +numeric_sign(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + + /* + * Handle NaN (infinities can be handled normally) + */ + if (NUMERIC_IS_NAN(num)) + PG_RETURN_NUMERIC(make_result(&const_nan)); + + switch (numeric_sign_internal(num)) + { + case 0: + PG_RETURN_NUMERIC(make_result(&const_zero)); + case 1: + PG_RETURN_NUMERIC(make_result(&const_one)); + case -1: + PG_RETURN_NUMERIC(make_result(&const_minus_one)); + } + + Assert(false); + return (Datum) 0; +} + + +/* + * numeric_round() - + * + * Round a value to have 'scale' digits after the decimal point. + * We allow negative 'scale', implying rounding before the decimal + * point --- Oracle interprets rounding that way. + */ +Datum +numeric_round(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + int32 scale = PG_GETARG_INT32(1); + Numeric res; + NumericVar arg; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num)) + PG_RETURN_NUMERIC(duplicate_numeric(num)); + + /* + * Limit the scale value to avoid possible overflow in calculations + */ + scale = Max(scale, -NUMERIC_MAX_RESULT_SCALE); + scale = Min(scale, NUMERIC_MAX_RESULT_SCALE); + + /* + * Unpack the argument and round it at the proper digit position + */ + init_var(&arg); + set_var_from_num(num, &arg); + + round_var(&arg, scale); + + /* We don't allow negative output dscale */ + if (scale < 0) + arg.dscale = 0; + + /* + * Return the rounded result + */ + res = make_result(&arg); + + free_var(&arg); + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_trunc() - + * + * Truncate a value to have 'scale' digits after the decimal point. + * We allow negative 'scale', implying a truncation before the decimal + * point --- Oracle interprets truncation that way. + */ +Datum +numeric_trunc(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + int32 scale = PG_GETARG_INT32(1); + Numeric res; + NumericVar arg; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num)) + PG_RETURN_NUMERIC(duplicate_numeric(num)); + + /* + * Limit the scale value to avoid possible overflow in calculations + */ + scale = Max(scale, -NUMERIC_MAX_RESULT_SCALE); + scale = Min(scale, NUMERIC_MAX_RESULT_SCALE); + + /* + * Unpack the argument and truncate it at the proper digit position + */ + init_var(&arg); + set_var_from_num(num, &arg); + + trunc_var(&arg, scale); + + /* We don't allow negative output dscale */ + if (scale < 0) + arg.dscale = 0; + + /* + * Return the truncated result + */ + res = make_result(&arg); + + free_var(&arg); + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_ceil() - + * + * Return the smallest integer greater than or equal to the argument + */ +Datum +numeric_ceil(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + Numeric res; + NumericVar result; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num)) + PG_RETURN_NUMERIC(duplicate_numeric(num)); + + init_var_from_num(num, &result); + ceil_var(&result, &result); + + res = make_result(&result); + free_var(&result); + + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_floor() - + * + * Return the largest integer equal to or less than the argument + */ +Datum +numeric_floor(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + Numeric res; + NumericVar result; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num)) + PG_RETURN_NUMERIC(duplicate_numeric(num)); + + init_var_from_num(num, &result); + floor_var(&result, &result); + + res = make_result(&result); + free_var(&result); + + PG_RETURN_NUMERIC(res); +} + + +/* + * generate_series_numeric() - + * + * Generate series of numeric. + */ +Datum +generate_series_numeric(PG_FUNCTION_ARGS) +{ + return generate_series_step_numeric(fcinfo); +} + +Datum +generate_series_step_numeric(PG_FUNCTION_ARGS) +{ + generate_series_numeric_fctx *fctx; + FuncCallContext *funcctx; + MemoryContext oldcontext; + + if (SRF_IS_FIRSTCALL()) + { + Numeric start_num = PG_GETARG_NUMERIC(0); + Numeric stop_num = PG_GETARG_NUMERIC(1); + NumericVar steploc = const_one; + + /* Reject NaN and infinities in start and stop values */ + if (NUMERIC_IS_SPECIAL(start_num)) + { + if (NUMERIC_IS_NAN(start_num)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("start value cannot be NaN"))); + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("start value cannot be infinity"))); + } + if (NUMERIC_IS_SPECIAL(stop_num)) + { + if (NUMERIC_IS_NAN(stop_num)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("stop value cannot be NaN"))); + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("stop value cannot be infinity"))); + } + + /* see if we were given an explicit step size */ + if (PG_NARGS() == 3) + { + Numeric step_num = PG_GETARG_NUMERIC(2); + + if (NUMERIC_IS_SPECIAL(step_num)) + { + if (NUMERIC_IS_NAN(step_num)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("step size cannot be NaN"))); + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("step size cannot be infinity"))); + } + + init_var_from_num(step_num, &steploc); + + if (cmp_var(&steploc, &const_zero) == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("step size cannot equal zero"))); + } + + /* create a function context for cross-call persistence */ + funcctx = SRF_FIRSTCALL_INIT(); + + /* + * Switch to memory context appropriate for multiple function calls. + */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* allocate memory for user context */ + fctx = (generate_series_numeric_fctx *) + palloc(sizeof(generate_series_numeric_fctx)); + + /* + * Use fctx to keep state from call to call. Seed current with the + * original start value. We must copy the start_num and stop_num + * values rather than pointing to them, since we may have detoasted + * them in the per-call context. + */ + init_var(&fctx->current); + init_var(&fctx->stop); + init_var(&fctx->step); + + set_var_from_num(start_num, &fctx->current); + set_var_from_num(stop_num, &fctx->stop); + set_var_from_var(&steploc, &fctx->step); + + funcctx->user_fctx = fctx; + MemoryContextSwitchTo(oldcontext); + } + + /* stuff done on every call of the function */ + funcctx = SRF_PERCALL_SETUP(); + + /* + * Get the saved state and use current state as the result of this + * iteration. + */ + fctx = funcctx->user_fctx; + + if ((fctx->step.sign == NUMERIC_POS && + cmp_var(&fctx->current, &fctx->stop) <= 0) || + (fctx->step.sign == NUMERIC_NEG && + cmp_var(&fctx->current, &fctx->stop) >= 0)) + { + Numeric result = make_result(&fctx->current); + + /* switch to memory context appropriate for iteration calculation */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* increment current in preparation for next iteration */ + add_var(&fctx->current, &fctx->step, &fctx->current); + MemoryContextSwitchTo(oldcontext); + + /* do when there is more left to send */ + SRF_RETURN_NEXT(funcctx, NumericGetDatum(result)); + } + else + /* do when there is no more left */ + SRF_RETURN_DONE(funcctx); +} + + +/* + * Implements the numeric version of the width_bucket() function + * defined by SQL2003. See also width_bucket_float8(). + * + * 'bound1' and 'bound2' are the lower and upper bounds of the + * histogram's range, respectively. 'count' is the number of buckets + * in the histogram. width_bucket() returns an integer indicating the + * bucket number that 'operand' belongs to in an equiwidth histogram + * with the specified characteristics. An operand smaller than the + * lower bound is assigned to bucket 0. An operand greater than the + * upper bound is assigned to an additional bucket (with number + * count+1). We don't allow "NaN" for any of the numeric arguments. + */ +Datum +width_bucket_numeric(PG_FUNCTION_ARGS) +{ + Numeric operand = PG_GETARG_NUMERIC(0); + Numeric bound1 = PG_GETARG_NUMERIC(1); + Numeric bound2 = PG_GETARG_NUMERIC(2); + int32 count = PG_GETARG_INT32(3); + NumericVar count_var; + NumericVar result_var; + int32 result; + + if (count <= 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION), + errmsg("count must be greater than zero"))); + + if (NUMERIC_IS_SPECIAL(operand) || + NUMERIC_IS_SPECIAL(bound1) || + NUMERIC_IS_SPECIAL(bound2)) + { + if (NUMERIC_IS_NAN(operand) || + NUMERIC_IS_NAN(bound1) || + NUMERIC_IS_NAN(bound2)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION), + errmsg("operand, lower bound, and upper bound cannot be NaN"))); + /* We allow "operand" to be infinite; cmp_numerics will cope */ + if (NUMERIC_IS_INF(bound1) || NUMERIC_IS_INF(bound2)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION), + errmsg("lower and upper bounds must be finite"))); + } + + init_var(&result_var); + init_var(&count_var); + + /* Convert 'count' to a numeric, for ease of use later */ + int64_to_numericvar((int64) count, &count_var); + + switch (cmp_numerics(bound1, bound2)) + { + case 0: + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION), + errmsg("lower bound cannot equal upper bound"))); + break; + + /* bound1 < bound2 */ + case -1: + if (cmp_numerics(operand, bound1) < 0) + set_var_from_var(&const_zero, &result_var); + else if (cmp_numerics(operand, bound2) >= 0) + add_var(&count_var, &const_one, &result_var); + else + compute_bucket(operand, bound1, bound2, &count_var, false, + &result_var); + break; + + /* bound1 > bound2 */ + case 1: + if (cmp_numerics(operand, bound1) > 0) + set_var_from_var(&const_zero, &result_var); + else if (cmp_numerics(operand, bound2) <= 0) + add_var(&count_var, &const_one, &result_var); + else + compute_bucket(operand, bound1, bound2, &count_var, true, + &result_var); + break; + } + + /* if result exceeds the range of a legal int4, we ereport here */ + if (!numericvar_to_int32(&result_var, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + + free_var(&count_var); + free_var(&result_var); + + PG_RETURN_INT32(result); +} + +/* + * If 'operand' is not outside the bucket range, determine the correct + * bucket for it to go. The calculations performed by this function + * are derived directly from the SQL2003 spec. Note however that we + * multiply by count before dividing, to avoid unnecessary roundoff error. + */ +static void +compute_bucket(Numeric operand, Numeric bound1, Numeric bound2, + const NumericVar *count_var, bool reversed_bounds, + NumericVar *result_var) +{ + NumericVar bound1_var; + NumericVar bound2_var; + NumericVar operand_var; + + init_var_from_num(bound1, &bound1_var); + init_var_from_num(bound2, &bound2_var); + init_var_from_num(operand, &operand_var); + + if (!reversed_bounds) + { + sub_var(&operand_var, &bound1_var, &operand_var); + sub_var(&bound2_var, &bound1_var, &bound2_var); + } + else + { + sub_var(&bound1_var, &operand_var, &operand_var); + sub_var(&bound1_var, &bound2_var, &bound2_var); + } + + mul_var(&operand_var, count_var, &operand_var, + operand_var.dscale + count_var->dscale); + div_var(&operand_var, &bound2_var, result_var, + select_div_scale(&operand_var, &bound2_var), true); + add_var(result_var, &const_one, result_var); + floor_var(result_var, result_var); + + free_var(&bound1_var); + free_var(&bound2_var); + free_var(&operand_var); +} + +/* ---------------------------------------------------------------------- + * + * Comparison functions + * + * Note: btree indexes need these routines not to leak memory; therefore, + * be careful to free working copies of toasted datums. Most places don't + * need to be so careful. + * + * Sort support: + * + * We implement the sortsupport strategy routine in order to get the benefit of + * abbreviation. The ordinary numeric comparison can be quite slow as a result + * of palloc/pfree cycles (due to detoasting packed values for alignment); + * while this could be worked on itself, the abbreviation strategy gives more + * speedup in many common cases. + * + * Two different representations are used for the abbreviated form, one in + * int32 and one in int64, whichever fits into a by-value Datum. In both cases + * the representation is negated relative to the original value, because we use + * the largest negative value for NaN, which sorts higher than other values. We + * convert the absolute value of the numeric to a 31-bit or 63-bit positive + * value, and then negate it if the original number was positive. + * + * We abort the abbreviation process if the abbreviation cardinality is below + * 0.01% of the row count (1 per 10k non-null rows). The actual break-even + * point is somewhat below that, perhaps 1 per 30k (at 1 per 100k there's a + * very small penalty), but we don't want to build up too many abbreviated + * values before first testing for abort, so we take the slightly pessimistic + * number. We make no attempt to estimate the cardinality of the real values, + * since it plays no part in the cost model here (if the abbreviation is equal, + * the cost of comparing equal and unequal underlying values is comparable). + * We discontinue even checking for abort (saving us the hashing overhead) if + * the estimated cardinality gets to 100k; that would be enough to support many + * billions of rows while doing no worse than breaking even. + * + * ---------------------------------------------------------------------- + */ + +/* + * Sort support strategy routine. + */ +Datum +numeric_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = numeric_fast_cmp; + + if (ssup->abbreviate) + { + NumericSortSupport *nss; + MemoryContext oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt); + + nss = palloc(sizeof(NumericSortSupport)); + + /* + * palloc a buffer for handling unaligned packed values in addition to + * the support struct + */ + nss->buf = palloc(VARATT_SHORT_MAX + VARHDRSZ + 1); + + nss->input_count = 0; + nss->estimating = true; + initHyperLogLog(&nss->abbr_card, 10); + + ssup->ssup_extra = nss; + + ssup->abbrev_full_comparator = ssup->comparator; + ssup->comparator = numeric_cmp_abbrev; + ssup->abbrev_converter = numeric_abbrev_convert; + ssup->abbrev_abort = numeric_abbrev_abort; + + MemoryContextSwitchTo(oldcontext); + } + + PG_RETURN_VOID(); +} + +/* + * Abbreviate a numeric datum, handling NaNs and detoasting + * (must not leak memory!) + */ +static Datum +numeric_abbrev_convert(Datum original_datum, SortSupport ssup) +{ + NumericSortSupport *nss = ssup->ssup_extra; + void *original_varatt = PG_DETOAST_DATUM_PACKED(original_datum); + Numeric value; + Datum result; + + nss->input_count += 1; + + /* + * This is to handle packed datums without needing a palloc/pfree cycle; + * we keep and reuse a buffer large enough to handle any short datum. + */ + if (VARATT_IS_SHORT(original_varatt)) + { + void *buf = nss->buf; + Size sz = VARSIZE_SHORT(original_varatt) - VARHDRSZ_SHORT; + + Assert(sz <= VARATT_SHORT_MAX - VARHDRSZ_SHORT); + + SET_VARSIZE(buf, VARHDRSZ + sz); + memcpy(VARDATA(buf), VARDATA_SHORT(original_varatt), sz); + + value = (Numeric) buf; + } + else + value = (Numeric) original_varatt; + + if (NUMERIC_IS_SPECIAL(value)) + { + if (NUMERIC_IS_PINF(value)) + result = NUMERIC_ABBREV_PINF; + else if (NUMERIC_IS_NINF(value)) + result = NUMERIC_ABBREV_NINF; + else + result = NUMERIC_ABBREV_NAN; + } + else + { + NumericVar var; + + init_var_from_num(value, &var); + + result = numeric_abbrev_convert_var(&var, nss); + } + + /* should happen only for external/compressed toasts */ + if ((Pointer) original_varatt != DatumGetPointer(original_datum)) + pfree(original_varatt); + + return result; +} + +/* + * Consider whether to abort abbreviation. + * + * We pay no attention to the cardinality of the non-abbreviated data. There is + * no reason to do so: unlike text, we have no fast check for equal values, so + * we pay the full overhead whenever the abbreviations are equal regardless of + * whether the underlying values are also equal. + */ +static bool +numeric_abbrev_abort(int memtupcount, SortSupport ssup) +{ + NumericSortSupport *nss = ssup->ssup_extra; + double abbr_card; + + if (memtupcount < 10000 || nss->input_count < 10000 || !nss->estimating) + return false; + + abbr_card = estimateHyperLogLog(&nss->abbr_card); + + /* + * If we have >100k distinct values, then even if we were sorting many + * billion rows we'd likely still break even, and the penalty of undoing + * that many rows of abbrevs would probably not be worth it. Stop even + * counting at that point. + */ + if (abbr_card > 100000.0) + { +#ifdef TRACE_SORT + if (trace_sort) + elog(LOG, + "numeric_abbrev: estimation ends at cardinality %f" + " after " INT64_FORMAT " values (%d rows)", + abbr_card, nss->input_count, memtupcount); +#endif + nss->estimating = false; + return false; + } + + /* + * Target minimum cardinality is 1 per ~10k of non-null inputs. (The + * break even point is somewhere between one per 100k rows, where + * abbreviation has a very slight penalty, and 1 per 10k where it wins by + * a measurable percentage.) We use the relatively pessimistic 10k + * threshold, and add a 0.5 row fudge factor, because it allows us to + * abort earlier on genuinely pathological data where we've had exactly + * one abbreviated value in the first 10k (non-null) rows. + */ + if (abbr_card < nss->input_count / 10000.0 + 0.5) + { +#ifdef TRACE_SORT + if (trace_sort) + elog(LOG, + "numeric_abbrev: aborting abbreviation at cardinality %f" + " below threshold %f after " INT64_FORMAT " values (%d rows)", + abbr_card, nss->input_count / 10000.0 + 0.5, + nss->input_count, memtupcount); +#endif + return true; + } + +#ifdef TRACE_SORT + if (trace_sort) + elog(LOG, + "numeric_abbrev: cardinality %f" + " after " INT64_FORMAT " values (%d rows)", + abbr_card, nss->input_count, memtupcount); +#endif + + return false; +} + +/* + * Non-fmgr interface to the comparison routine to allow sortsupport to elide + * the fmgr call. The saving here is small given how slow numeric comparisons + * are, but it is a required part of the sort support API when abbreviations + * are performed. + * + * Two palloc/pfree cycles could be saved here by using persistent buffers for + * aligning short-varlena inputs, but this has not so far been considered to + * be worth the effort. + */ +static int +numeric_fast_cmp(Datum x, Datum y, SortSupport ssup) +{ + Numeric nx = DatumGetNumeric(x); + Numeric ny = DatumGetNumeric(y); + int result; + + result = cmp_numerics(nx, ny); + + if ((Pointer) nx != DatumGetPointer(x)) + pfree(nx); + if ((Pointer) ny != DatumGetPointer(y)) + pfree(ny); + + return result; +} + +/* + * Compare abbreviations of values. (Abbreviations may be equal where the true + * values differ, but if the abbreviations differ, they must reflect the + * ordering of the true values.) + */ +static int +numeric_cmp_abbrev(Datum x, Datum y, SortSupport ssup) +{ + /* + * NOTE WELL: this is intentionally backwards, because the abbreviation is + * negated relative to the original value, to handle NaN/infinity cases. + */ + if (DatumGetNumericAbbrev(x) < DatumGetNumericAbbrev(y)) + return 1; + if (DatumGetNumericAbbrev(x) > DatumGetNumericAbbrev(y)) + return -1; + return 0; +} + +/* + * Abbreviate a NumericVar according to the available bit size. + * + * The 31-bit value is constructed as: + * + * 0 + 7bits digit weight + 24 bits digit value + * + * where the digit weight is in single decimal digits, not digit words, and + * stored in excess-44 representation[1]. The 24-bit digit value is the 7 most + * significant decimal digits of the value converted to binary. Values whose + * weights would fall outside the representable range are rounded off to zero + * (which is also used to represent actual zeros) or to 0x7FFFFFFF (which + * otherwise cannot occur). Abbreviation therefore fails to gain any advantage + * where values are outside the range 10^-44 to 10^83, which is not considered + * to be a serious limitation, or when values are of the same magnitude and + * equal in the first 7 decimal digits, which is considered to be an + * unavoidable limitation given the available bits. (Stealing three more bits + * to compare another digit would narrow the range of representable weights by + * a factor of 8, which starts to look like a real limiting factor.) + * + * (The value 44 for the excess is essentially arbitrary) + * + * The 63-bit value is constructed as: + * + * 0 + 7bits weight + 4 x 14-bit packed digit words + * + * The weight in this case is again stored in excess-44, but this time it is + * the original weight in digit words (i.e. powers of 10000). The first four + * digit words of the value (if present; trailing zeros are assumed as needed) + * are packed into 14 bits each to form the rest of the value. Again, + * out-of-range values are rounded off to 0 or 0x7FFFFFFFFFFFFFFF. The + * representable range in this case is 10^-176 to 10^332, which is considered + * to be good enough for all practical purposes, and comparison of 4 words + * means that at least 13 decimal digits are compared, which is considered to + * be a reasonable compromise between effectiveness and efficiency in computing + * the abbreviation. + * + * (The value 44 for the excess is even more arbitrary here, it was chosen just + * to match the value used in the 31-bit case) + * + * [1] - Excess-k representation means that the value is offset by adding 'k' + * and then treated as unsigned, so the smallest representable value is stored + * with all bits zero. This allows simple comparisons to work on the composite + * value. + */ + +#if NUMERIC_ABBREV_BITS == 64 + +static Datum +numeric_abbrev_convert_var(const NumericVar *var, NumericSortSupport *nss) +{ + int ndigits = var->ndigits; + int weight = var->weight; + int64 result; + + if (ndigits == 0 || weight < -44) + { + result = 0; + } + else if (weight > 83) + { + result = PG_INT64_MAX; + } + else + { + result = ((int64) (weight + 44) << 56); + + switch (ndigits) + { + default: + result |= ((int64) var->digits[3]); + /* FALLTHROUGH */ + case 3: + result |= ((int64) var->digits[2]) << 14; + /* FALLTHROUGH */ + case 2: + result |= ((int64) var->digits[1]) << 28; + /* FALLTHROUGH */ + case 1: + result |= ((int64) var->digits[0]) << 42; + break; + } + } + + /* the abbrev is negated relative to the original */ + if (var->sign == NUMERIC_POS) + result = -result; + + if (nss->estimating) + { + uint32 tmp = ((uint32) result + ^ (uint32) ((uint64) result >> 32)); + + addHyperLogLog(&nss->abbr_card, DatumGetUInt32(hash_uint32(tmp))); + } + + return NumericAbbrevGetDatum(result); +} + +#endif /* NUMERIC_ABBREV_BITS == 64 */ + +#if NUMERIC_ABBREV_BITS == 32 + +static Datum +numeric_abbrev_convert_var(const NumericVar *var, NumericSortSupport *nss) +{ + int ndigits = var->ndigits; + int weight = var->weight; + int32 result; + + if (ndigits == 0 || weight < -11) + { + result = 0; + } + else if (weight > 20) + { + result = PG_INT32_MAX; + } + else + { + NumericDigit nxt1 = (ndigits > 1) ? var->digits[1] : 0; + + weight = (weight + 11) * 4; + + result = var->digits[0]; + + /* + * "result" now has 1 to 4 nonzero decimal digits. We pack in more + * digits to make 7 in total (largest we can fit in 24 bits) + */ + + if (result > 999) + { + /* already have 4 digits, add 3 more */ + result = (result * 1000) + (nxt1 / 10); + weight += 3; + } + else if (result > 99) + { + /* already have 3 digits, add 4 more */ + result = (result * 10000) + nxt1; + weight += 2; + } + else if (result > 9) + { + NumericDigit nxt2 = (ndigits > 2) ? var->digits[2] : 0; + + /* already have 2 digits, add 5 more */ + result = (result * 100000) + (nxt1 * 10) + (nxt2 / 1000); + weight += 1; + } + else + { + NumericDigit nxt2 = (ndigits > 2) ? var->digits[2] : 0; + + /* already have 1 digit, add 6 more */ + result = (result * 1000000) + (nxt1 * 100) + (nxt2 / 100); + } + + result = result | (weight << 24); + } + + /* the abbrev is negated relative to the original */ + if (var->sign == NUMERIC_POS) + result = -result; + + if (nss->estimating) + { + uint32 tmp = (uint32) result; + + addHyperLogLog(&nss->abbr_card, DatumGetUInt32(hash_uint32(tmp))); + } + + return NumericAbbrevGetDatum(result); +} + +#endif /* NUMERIC_ABBREV_BITS == 32 */ + +/* + * Ordinary (non-sortsupport) comparisons follow. + */ + +Datum +numeric_cmp(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + int result; + + result = cmp_numerics(num1, num2); + + PG_FREE_IF_COPY(num1, 0); + PG_FREE_IF_COPY(num2, 1); + + PG_RETURN_INT32(result); +} + + +Datum +numeric_eq(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + bool result; + + result = cmp_numerics(num1, num2) == 0; + + PG_FREE_IF_COPY(num1, 0); + PG_FREE_IF_COPY(num2, 1); + + PG_RETURN_BOOL(result); +} + +Datum +numeric_ne(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + bool result; + + result = cmp_numerics(num1, num2) != 0; + + PG_FREE_IF_COPY(num1, 0); + PG_FREE_IF_COPY(num2, 1); + + PG_RETURN_BOOL(result); +} + +Datum +numeric_gt(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + bool result; + + result = cmp_numerics(num1, num2) > 0; + + PG_FREE_IF_COPY(num1, 0); + PG_FREE_IF_COPY(num2, 1); + + PG_RETURN_BOOL(result); +} + +Datum +numeric_ge(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + bool result; + + result = cmp_numerics(num1, num2) >= 0; + + PG_FREE_IF_COPY(num1, 0); + PG_FREE_IF_COPY(num2, 1); + + PG_RETURN_BOOL(result); +} + +Datum +numeric_lt(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + bool result; + + result = cmp_numerics(num1, num2) < 0; + + PG_FREE_IF_COPY(num1, 0); + PG_FREE_IF_COPY(num2, 1); + + PG_RETURN_BOOL(result); +} + +Datum +numeric_le(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + bool result; + + result = cmp_numerics(num1, num2) <= 0; + + PG_FREE_IF_COPY(num1, 0); + PG_FREE_IF_COPY(num2, 1); + + PG_RETURN_BOOL(result); +} + +static int +cmp_numerics(Numeric num1, Numeric num2) +{ + int result; + + /* + * We consider all NANs to be equal and larger than any non-NAN (including + * Infinity). This is somewhat arbitrary; the important thing is to have + * a consistent sort order. + */ + if (NUMERIC_IS_SPECIAL(num1)) + { + if (NUMERIC_IS_NAN(num1)) + { + if (NUMERIC_IS_NAN(num2)) + result = 0; /* NAN = NAN */ + else + result = 1; /* NAN > non-NAN */ + } + else if (NUMERIC_IS_PINF(num1)) + { + if (NUMERIC_IS_NAN(num2)) + result = -1; /* PINF < NAN */ + else if (NUMERIC_IS_PINF(num2)) + result = 0; /* PINF = PINF */ + else + result = 1; /* PINF > anything else */ + } + else /* num1 must be NINF */ + { + if (NUMERIC_IS_NINF(num2)) + result = 0; /* NINF = NINF */ + else + result = -1; /* NINF < anything else */ + } + } + else if (NUMERIC_IS_SPECIAL(num2)) + { + if (NUMERIC_IS_NINF(num2)) + result = 1; /* normal > NINF */ + else + result = -1; /* normal < NAN or PINF */ + } + else + { + result = cmp_var_common(NUMERIC_DIGITS(num1), NUMERIC_NDIGITS(num1), + NUMERIC_WEIGHT(num1), NUMERIC_SIGN(num1), + NUMERIC_DIGITS(num2), NUMERIC_NDIGITS(num2), + NUMERIC_WEIGHT(num2), NUMERIC_SIGN(num2)); + } + + return result; +} + +/* + * in_range support function for numeric. + */ +Datum +in_range_numeric_numeric(PG_FUNCTION_ARGS) +{ + Numeric val = PG_GETARG_NUMERIC(0); + Numeric base = PG_GETARG_NUMERIC(1); + Numeric offset = PG_GETARG_NUMERIC(2); + bool sub = PG_GETARG_BOOL(3); + bool less = PG_GETARG_BOOL(4); + bool result; + + /* + * Reject negative (including -Inf) or NaN offset. Negative is per spec, + * and NaN is because appropriate semantics for that seem non-obvious. + */ + if (NUMERIC_IS_NAN(offset) || + NUMERIC_IS_NINF(offset) || + NUMERIC_SIGN(offset) == NUMERIC_NEG) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE), + errmsg("invalid preceding or following size in window function"))); + + /* + * Deal with cases where val and/or base is NaN, following the rule that + * NaN sorts after non-NaN (cf cmp_numerics). The offset cannot affect + * the conclusion. + */ + if (NUMERIC_IS_NAN(val)) + { + if (NUMERIC_IS_NAN(base)) + result = true; /* NAN = NAN */ + else + result = !less; /* NAN > non-NAN */ + } + else if (NUMERIC_IS_NAN(base)) + { + result = less; /* non-NAN < NAN */ + } + + /* + * Deal with infinite offset (necessarily +Inf, at this point). + */ + else if (NUMERIC_IS_SPECIAL(offset)) + { + Assert(NUMERIC_IS_PINF(offset)); + if (sub ? NUMERIC_IS_PINF(base) : NUMERIC_IS_NINF(base)) + { + /* + * base +/- offset would produce NaN, so return true for any val + * (see in_range_float8_float8() for reasoning). + */ + result = true; + } + else if (sub) + { + /* base - offset must be -inf */ + if (less) + result = NUMERIC_IS_NINF(val); /* only -inf is <= sum */ + else + result = true; /* any val is >= sum */ + } + else + { + /* base + offset must be +inf */ + if (less) + result = true; /* any val is <= sum */ + else + result = NUMERIC_IS_PINF(val); /* only +inf is >= sum */ + } + } + + /* + * Deal with cases where val and/or base is infinite. The offset, being + * now known finite, cannot affect the conclusion. + */ + else if (NUMERIC_IS_SPECIAL(val)) + { + if (NUMERIC_IS_PINF(val)) + { + if (NUMERIC_IS_PINF(base)) + result = true; /* PINF = PINF */ + else + result = !less; /* PINF > any other non-NAN */ + } + else /* val must be NINF */ + { + if (NUMERIC_IS_NINF(base)) + result = true; /* NINF = NINF */ + else + result = less; /* NINF < anything else */ + } + } + else if (NUMERIC_IS_SPECIAL(base)) + { + if (NUMERIC_IS_NINF(base)) + result = !less; /* normal > NINF */ + else + result = less; /* normal < PINF */ + } + else + { + /* + * Otherwise go ahead and compute base +/- offset. While it's + * possible for this to overflow the numeric format, it's unlikely + * enough that we don't take measures to prevent it. + */ + NumericVar valv; + NumericVar basev; + NumericVar offsetv; + NumericVar sum; + + init_var_from_num(val, &valv); + init_var_from_num(base, &basev); + init_var_from_num(offset, &offsetv); + init_var(&sum); + + if (sub) + sub_var(&basev, &offsetv, &sum); + else + add_var(&basev, &offsetv, &sum); + + if (less) + result = (cmp_var(&valv, &sum) <= 0); + else + result = (cmp_var(&valv, &sum) >= 0); + + free_var(&sum); + } + + PG_FREE_IF_COPY(val, 0); + PG_FREE_IF_COPY(base, 1); + PG_FREE_IF_COPY(offset, 2); + + PG_RETURN_BOOL(result); +} + +Datum +hash_numeric(PG_FUNCTION_ARGS) +{ + Numeric key = PG_GETARG_NUMERIC(0); + Datum digit_hash; + Datum result; + int weight; + int start_offset; + int end_offset; + int i; + int hash_len; + NumericDigit *digits; + + /* If it's NaN or infinity, don't try to hash the rest of the fields */ + if (NUMERIC_IS_SPECIAL(key)) + PG_RETURN_UINT32(0); + + weight = NUMERIC_WEIGHT(key); + start_offset = 0; + end_offset = 0; + + /* + * Omit any leading or trailing zeros from the input to the hash. The + * numeric implementation *should* guarantee that leading and trailing + * zeros are suppressed, but we're paranoid. Note that we measure the + * starting and ending offsets in units of NumericDigits, not bytes. + */ + digits = NUMERIC_DIGITS(key); + for (i = 0; i < NUMERIC_NDIGITS(key); i++) + { + if (digits[i] != (NumericDigit) 0) + break; + + start_offset++; + + /* + * The weight is effectively the # of digits before the decimal point, + * so decrement it for each leading zero we skip. + */ + weight--; + } + + /* + * If there are no non-zero digits, then the value of the number is zero, + * regardless of any other fields. + */ + if (NUMERIC_NDIGITS(key) == start_offset) + PG_RETURN_UINT32(-1); + + for (i = NUMERIC_NDIGITS(key) - 1; i >= 0; i--) + { + if (digits[i] != (NumericDigit) 0) + break; + + end_offset++; + } + + /* If we get here, there should be at least one non-zero digit */ + Assert(start_offset + end_offset < NUMERIC_NDIGITS(key)); + + /* + * Note that we don't hash on the Numeric's scale, since two numerics can + * compare equal but have different scales. We also don't hash on the + * sign, although we could: since a sign difference implies inequality, + * this shouldn't affect correctness. + */ + hash_len = NUMERIC_NDIGITS(key) - start_offset - end_offset; + digit_hash = hash_any((unsigned char *) (NUMERIC_DIGITS(key) + start_offset), + hash_len * sizeof(NumericDigit)); + + /* Mix in the weight, via XOR */ + result = digit_hash ^ weight; + + PG_RETURN_DATUM(result); +} + +/* + * Returns 64-bit value by hashing a value to a 64-bit value, with a seed. + * Otherwise, similar to hash_numeric. + */ +Datum +hash_numeric_extended(PG_FUNCTION_ARGS) +{ + Numeric key = PG_GETARG_NUMERIC(0); + uint64 seed = PG_GETARG_INT64(1); + Datum digit_hash; + Datum result; + int weight; + int start_offset; + int end_offset; + int i; + int hash_len; + NumericDigit *digits; + + /* If it's NaN or infinity, don't try to hash the rest of the fields */ + if (NUMERIC_IS_SPECIAL(key)) + PG_RETURN_UINT64(seed); + + weight = NUMERIC_WEIGHT(key); + start_offset = 0; + end_offset = 0; + + digits = NUMERIC_DIGITS(key); + for (i = 0; i < NUMERIC_NDIGITS(key); i++) + { + if (digits[i] != (NumericDigit) 0) + break; + + start_offset++; + + weight--; + } + + if (NUMERIC_NDIGITS(key) == start_offset) + PG_RETURN_UINT64(seed - 1); + + for (i = NUMERIC_NDIGITS(key) - 1; i >= 0; i--) + { + if (digits[i] != (NumericDigit) 0) + break; + + end_offset++; + } + + Assert(start_offset + end_offset < NUMERIC_NDIGITS(key)); + + hash_len = NUMERIC_NDIGITS(key) - start_offset - end_offset; + digit_hash = hash_any_extended((unsigned char *) (NUMERIC_DIGITS(key) + + start_offset), + hash_len * sizeof(NumericDigit), + seed); + + result = UInt64GetDatum(DatumGetUInt64(digit_hash) ^ weight); + + PG_RETURN_DATUM(result); +} + + +/* ---------------------------------------------------------------------- + * + * Basic arithmetic functions + * + * ---------------------------------------------------------------------- + */ + + +/* + * numeric_add() - + * + * Add two numerics + */ +Datum +numeric_add(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + Numeric res; + + res = numeric_add_opt_error(num1, num2, NULL); + + PG_RETURN_NUMERIC(res); +} + +/* + * numeric_add_opt_error() - + * + * Internal version of numeric_add(). If "*have_error" flag is provided, + * on error it's set to true, NULL returned. This is helpful when caller + * need to handle errors by itself. + */ +Numeric +numeric_add_opt_error(Numeric num1, Numeric num2, bool *have_error) +{ + NumericVar arg1; + NumericVar arg2; + NumericVar result; + Numeric res; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2)) + { + if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2)) + return make_result(&const_nan); + if (NUMERIC_IS_PINF(num1)) + { + if (NUMERIC_IS_NINF(num2)) + return make_result(&const_nan); /* Inf + -Inf */ + else + return make_result(&const_pinf); + } + if (NUMERIC_IS_NINF(num1)) + { + if (NUMERIC_IS_PINF(num2)) + return make_result(&const_nan); /* -Inf + Inf */ + else + return make_result(&const_ninf); + } + /* by here, num1 must be finite, so num2 is not */ + if (NUMERIC_IS_PINF(num2)) + return make_result(&const_pinf); + Assert(NUMERIC_IS_NINF(num2)); + return make_result(&const_ninf); + } + + /* + * Unpack the values, let add_var() compute the result and return it. + */ + init_var_from_num(num1, &arg1); + init_var_from_num(num2, &arg2); + + init_var(&result); + add_var(&arg1, &arg2, &result); + + res = make_result_opt_error(&result, have_error); + + free_var(&result); + + return res; +} + + +/* + * numeric_sub() - + * + * Subtract one numeric from another + */ +Datum +numeric_sub(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + Numeric res; + + res = numeric_sub_opt_error(num1, num2, NULL); + + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_sub_opt_error() - + * + * Internal version of numeric_sub(). If "*have_error" flag is provided, + * on error it's set to true, NULL returned. This is helpful when caller + * need to handle errors by itself. + */ +Numeric +numeric_sub_opt_error(Numeric num1, Numeric num2, bool *have_error) +{ + NumericVar arg1; + NumericVar arg2; + NumericVar result; + Numeric res; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2)) + { + if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2)) + return make_result(&const_nan); + if (NUMERIC_IS_PINF(num1)) + { + if (NUMERIC_IS_PINF(num2)) + return make_result(&const_nan); /* Inf - Inf */ + else + return make_result(&const_pinf); + } + if (NUMERIC_IS_NINF(num1)) + { + if (NUMERIC_IS_NINF(num2)) + return make_result(&const_nan); /* -Inf - -Inf */ + else + return make_result(&const_ninf); + } + /* by here, num1 must be finite, so num2 is not */ + if (NUMERIC_IS_PINF(num2)) + return make_result(&const_ninf); + Assert(NUMERIC_IS_NINF(num2)); + return make_result(&const_pinf); + } + + /* + * Unpack the values, let sub_var() compute the result and return it. + */ + init_var_from_num(num1, &arg1); + init_var_from_num(num2, &arg2); + + init_var(&result); + sub_var(&arg1, &arg2, &result); + + res = make_result_opt_error(&result, have_error); + + free_var(&result); + + return res; +} + + +/* + * numeric_mul() - + * + * Calculate the product of two numerics + */ +Datum +numeric_mul(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + Numeric res; + + res = numeric_mul_opt_error(num1, num2, NULL); + + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_mul_opt_error() - + * + * Internal version of numeric_mul(). If "*have_error" flag is provided, + * on error it's set to true, NULL returned. This is helpful when caller + * need to handle errors by itself. + */ +Numeric +numeric_mul_opt_error(Numeric num1, Numeric num2, bool *have_error) +{ + NumericVar arg1; + NumericVar arg2; + NumericVar result; + Numeric res; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2)) + { + if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2)) + return make_result(&const_nan); + if (NUMERIC_IS_PINF(num1)) + { + switch (numeric_sign_internal(num2)) + { + case 0: + return make_result(&const_nan); /* Inf * 0 */ + case 1: + return make_result(&const_pinf); + case -1: + return make_result(&const_ninf); + } + Assert(false); + } + if (NUMERIC_IS_NINF(num1)) + { + switch (numeric_sign_internal(num2)) + { + case 0: + return make_result(&const_nan); /* -Inf * 0 */ + case 1: + return make_result(&const_ninf); + case -1: + return make_result(&const_pinf); + } + Assert(false); + } + /* by here, num1 must be finite, so num2 is not */ + if (NUMERIC_IS_PINF(num2)) + { + switch (numeric_sign_internal(num1)) + { + case 0: + return make_result(&const_nan); /* 0 * Inf */ + case 1: + return make_result(&const_pinf); + case -1: + return make_result(&const_ninf); + } + Assert(false); + } + Assert(NUMERIC_IS_NINF(num2)); + switch (numeric_sign_internal(num1)) + { + case 0: + return make_result(&const_nan); /* 0 * -Inf */ + case 1: + return make_result(&const_ninf); + case -1: + return make_result(&const_pinf); + } + Assert(false); + } + + /* + * Unpack the values, let mul_var() compute the result and return it. + * Unlike add_var() and sub_var(), mul_var() will round its result. In the + * case of numeric_mul(), which is invoked for the * operator on numerics, + * we request exact representation for the product (rscale = sum(dscale of + * arg1, dscale of arg2)). If the exact result has more digits after the + * decimal point than can be stored in a numeric, we round it. Rounding + * after computing the exact result ensures that the final result is + * correctly rounded (rounding in mul_var() using a truncated product + * would not guarantee this). + */ + init_var_from_num(num1, &arg1); + init_var_from_num(num2, &arg2); + + init_var(&result); + mul_var(&arg1, &arg2, &result, arg1.dscale + arg2.dscale); + + if (result.dscale > NUMERIC_DSCALE_MAX) + round_var(&result, NUMERIC_DSCALE_MAX); + + res = make_result_opt_error(&result, have_error); + + free_var(&result); + + return res; +} + + +/* + * numeric_div() - + * + * Divide one numeric into another + */ +Datum +numeric_div(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + Numeric res; + + res = numeric_div_opt_error(num1, num2, NULL); + + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_div_opt_error() - + * + * Internal version of numeric_div(). If "*have_error" flag is provided, + * on error it's set to true, NULL returned. This is helpful when caller + * need to handle errors by itself. + */ +Numeric +numeric_div_opt_error(Numeric num1, Numeric num2, bool *have_error) +{ + NumericVar arg1; + NumericVar arg2; + NumericVar result; + Numeric res; + int rscale; + + if (have_error) + *have_error = false; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2)) + { + if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2)) + return make_result(&const_nan); + if (NUMERIC_IS_PINF(num1)) + { + if (NUMERIC_IS_SPECIAL(num2)) + return make_result(&const_nan); /* Inf / [-]Inf */ + switch (numeric_sign_internal(num2)) + { + case 0: + if (have_error) + { + *have_error = true; + return NULL; + } + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + break; + case 1: + return make_result(&const_pinf); + case -1: + return make_result(&const_ninf); + } + Assert(false); + } + if (NUMERIC_IS_NINF(num1)) + { + if (NUMERIC_IS_SPECIAL(num2)) + return make_result(&const_nan); /* -Inf / [-]Inf */ + switch (numeric_sign_internal(num2)) + { + case 0: + if (have_error) + { + *have_error = true; + return NULL; + } + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + break; + case 1: + return make_result(&const_ninf); + case -1: + return make_result(&const_pinf); + } + Assert(false); + } + /* by here, num1 must be finite, so num2 is not */ + + /* + * POSIX would have us return zero or minus zero if num1 is zero, and + * otherwise throw an underflow error. But the numeric type doesn't + * really do underflow, so let's just return zero. + */ + return make_result(&const_zero); + } + + /* + * Unpack the arguments + */ + init_var_from_num(num1, &arg1); + init_var_from_num(num2, &arg2); + + init_var(&result); + + /* + * Select scale for division result + */ + rscale = select_div_scale(&arg1, &arg2); + + /* + * If "have_error" is provided, check for division by zero here + */ + if (have_error && (arg2.ndigits == 0 || arg2.digits[0] == 0)) + { + *have_error = true; + return NULL; + } + + /* + * Do the divide and return the result + */ + div_var(&arg1, &arg2, &result, rscale, true); + + res = make_result_opt_error(&result, have_error); + + free_var(&result); + + return res; +} + + +/* + * numeric_div_trunc() - + * + * Divide one numeric into another, truncating the result to an integer + */ +Datum +numeric_div_trunc(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + NumericVar arg1; + NumericVar arg2; + NumericVar result; + Numeric res; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2)) + { + if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2)) + PG_RETURN_NUMERIC(make_result(&const_nan)); + if (NUMERIC_IS_PINF(num1)) + { + if (NUMERIC_IS_SPECIAL(num2)) + PG_RETURN_NUMERIC(make_result(&const_nan)); /* Inf / [-]Inf */ + switch (numeric_sign_internal(num2)) + { + case 0: + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + break; + case 1: + PG_RETURN_NUMERIC(make_result(&const_pinf)); + case -1: + PG_RETURN_NUMERIC(make_result(&const_ninf)); + } + Assert(false); + } + if (NUMERIC_IS_NINF(num1)) + { + if (NUMERIC_IS_SPECIAL(num2)) + PG_RETURN_NUMERIC(make_result(&const_nan)); /* -Inf / [-]Inf */ + switch (numeric_sign_internal(num2)) + { + case 0: + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + break; + case 1: + PG_RETURN_NUMERIC(make_result(&const_ninf)); + case -1: + PG_RETURN_NUMERIC(make_result(&const_pinf)); + } + Assert(false); + } + /* by here, num1 must be finite, so num2 is not */ + + /* + * POSIX would have us return zero or minus zero if num1 is zero, and + * otherwise throw an underflow error. But the numeric type doesn't + * really do underflow, so let's just return zero. + */ + PG_RETURN_NUMERIC(make_result(&const_zero)); + } + + /* + * Unpack the arguments + */ + init_var_from_num(num1, &arg1); + init_var_from_num(num2, &arg2); + + init_var(&result); + + /* + * Do the divide and return the result + */ + div_var(&arg1, &arg2, &result, 0, false); + + res = make_result(&result); + + free_var(&result); + + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_mod() - + * + * Calculate the modulo of two numerics + */ +Datum +numeric_mod(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + Numeric res; + + res = numeric_mod_opt_error(num1, num2, NULL); + + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_mod_opt_error() - + * + * Internal version of numeric_mod(). If "*have_error" flag is provided, + * on error it's set to true, NULL returned. This is helpful when caller + * need to handle errors by itself. + */ +Numeric +numeric_mod_opt_error(Numeric num1, Numeric num2, bool *have_error) +{ + Numeric res; + NumericVar arg1; + NumericVar arg2; + NumericVar result; + + if (have_error) + *have_error = false; + + /* + * Handle NaN and infinities. We follow POSIX fmod() on this, except that + * POSIX treats x-is-infinite and y-is-zero identically, raising EDOM and + * returning NaN. We choose to throw error only for y-is-zero. + */ + if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2)) + { + if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2)) + return make_result(&const_nan); + if (NUMERIC_IS_INF(num1)) + { + if (numeric_sign_internal(num2) == 0) + { + if (have_error) + { + *have_error = true; + return NULL; + } + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + } + /* Inf % any nonzero = NaN */ + return make_result(&const_nan); + } + /* num2 must be [-]Inf; result is num1 regardless of sign of num2 */ + return duplicate_numeric(num1); + } + + init_var_from_num(num1, &arg1); + init_var_from_num(num2, &arg2); + + init_var(&result); + + /* + * If "have_error" is provided, check for division by zero here + */ + if (have_error && (arg2.ndigits == 0 || arg2.digits[0] == 0)) + { + *have_error = true; + return NULL; + } + + mod_var(&arg1, &arg2, &result); + + res = make_result_opt_error(&result, NULL); + + free_var(&result); + + return res; +} + + +/* + * numeric_inc() - + * + * Increment a number by one + */ +Datum +numeric_inc(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + NumericVar arg; + Numeric res; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num)) + PG_RETURN_NUMERIC(duplicate_numeric(num)); + + /* + * Compute the result and return it + */ + init_var_from_num(num, &arg); + + add_var(&arg, &const_one, &arg); + + res = make_result(&arg); + + free_var(&arg); + + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_smaller() - + * + * Return the smaller of two numbers + */ +Datum +numeric_smaller(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + + /* + * Use cmp_numerics so that this will agree with the comparison operators, + * particularly as regards comparisons involving NaN. + */ + if (cmp_numerics(num1, num2) < 0) + PG_RETURN_NUMERIC(num1); + else + PG_RETURN_NUMERIC(num2); +} + + +/* + * numeric_larger() - + * + * Return the larger of two numbers + */ +Datum +numeric_larger(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + + /* + * Use cmp_numerics so that this will agree with the comparison operators, + * particularly as regards comparisons involving NaN. + */ + if (cmp_numerics(num1, num2) > 0) + PG_RETURN_NUMERIC(num1); + else + PG_RETURN_NUMERIC(num2); +} + + +/* ---------------------------------------------------------------------- + * + * Advanced math functions + * + * ---------------------------------------------------------------------- + */ + +/* + * numeric_gcd() - + * + * Calculate the greatest common divisor of two numerics + */ +Datum +numeric_gcd(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + NumericVar arg1; + NumericVar arg2; + NumericVar result; + Numeric res; + + /* + * Handle NaN and infinities: we consider the result to be NaN in all such + * cases. + */ + if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2)) + PG_RETURN_NUMERIC(make_result(&const_nan)); + + /* + * Unpack the arguments + */ + init_var_from_num(num1, &arg1); + init_var_from_num(num2, &arg2); + + init_var(&result); + + /* + * Find the GCD and return the result + */ + gcd_var(&arg1, &arg2, &result); + + res = make_result(&result); + + free_var(&result); + + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_lcm() - + * + * Calculate the least common multiple of two numerics + */ +Datum +numeric_lcm(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + NumericVar arg1; + NumericVar arg2; + NumericVar result; + Numeric res; + + /* + * Handle NaN and infinities: we consider the result to be NaN in all such + * cases. + */ + if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2)) + PG_RETURN_NUMERIC(make_result(&const_nan)); + + /* + * Unpack the arguments + */ + init_var_from_num(num1, &arg1); + init_var_from_num(num2, &arg2); + + init_var(&result); + + /* + * Compute the result using lcm(x, y) = abs(x / gcd(x, y) * y), returning + * zero if either input is zero. + * + * Note that the division is guaranteed to be exact, returning an integer + * result, so the LCM is an integral multiple of both x and y. A display + * scale of Min(x.dscale, y.dscale) would be sufficient to represent it, + * but as with other numeric functions, we choose to return a result whose + * display scale is no smaller than either input. + */ + if (arg1.ndigits == 0 || arg2.ndigits == 0) + set_var_from_var(&const_zero, &result); + else + { + gcd_var(&arg1, &arg2, &result); + div_var(&arg1, &result, &result, 0, false); + mul_var(&arg2, &result, &result, arg2.dscale); + result.sign = NUMERIC_POS; + } + + result.dscale = Max(arg1.dscale, arg2.dscale); + + res = make_result(&result); + + free_var(&result); + + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_fac() + * + * Compute factorial + */ +Datum +numeric_fac(PG_FUNCTION_ARGS) +{ + int64 num = PG_GETARG_INT64(0); + Numeric res; + NumericVar fact; + NumericVar result; + + if (num < 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("factorial of a negative number is undefined"))); + if (num <= 1) + { + res = make_result(&const_one); + PG_RETURN_NUMERIC(res); + } + /* Fail immediately if the result would overflow */ + if (num > 32177) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value overflows numeric format"))); + + init_var(&fact); + init_var(&result); + + int64_to_numericvar(num, &result); + + for (num = num - 1; num > 1; num--) + { + /* this loop can take awhile, so allow it to be interrupted */ + CHECK_FOR_INTERRUPTS(); + + int64_to_numericvar(num, &fact); + + mul_var(&result, &fact, &result, 0); + } + + res = make_result(&result); + + free_var(&fact); + free_var(&result); + + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_sqrt() - + * + * Compute the square root of a numeric. + */ +Datum +numeric_sqrt(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + Numeric res; + NumericVar arg; + NumericVar result; + int sweight; + int rscale; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num)) + { + /* error should match that in sqrt_var() */ + if (NUMERIC_IS_NINF(num)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION), + errmsg("cannot take square root of a negative number"))); + /* For NAN or PINF, just duplicate the input */ + PG_RETURN_NUMERIC(duplicate_numeric(num)); + } + + /* + * Unpack the argument and determine the result scale. We choose a scale + * to give at least NUMERIC_MIN_SIG_DIGITS significant digits; but in any + * case not less than the input's dscale. + */ + init_var_from_num(num, &arg); + + init_var(&result); + + /* Assume the input was normalized, so arg.weight is accurate */ + sweight = (arg.weight + 1) * DEC_DIGITS / 2 - 1; + + rscale = NUMERIC_MIN_SIG_DIGITS - sweight; + rscale = Max(rscale, arg.dscale); + rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE); + rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE); + + /* + * Let sqrt_var() do the calculation and return the result. + */ + sqrt_var(&arg, &result, rscale); + + res = make_result(&result); + + free_var(&result); + + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_exp() - + * + * Raise e to the power of x + */ +Datum +numeric_exp(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + Numeric res; + NumericVar arg; + NumericVar result; + int rscale; + double val; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num)) + { + /* Per POSIX, exp(-Inf) is zero */ + if (NUMERIC_IS_NINF(num)) + PG_RETURN_NUMERIC(make_result(&const_zero)); + /* For NAN or PINF, just duplicate the input */ + PG_RETURN_NUMERIC(duplicate_numeric(num)); + } + + /* + * Unpack the argument and determine the result scale. We choose a scale + * to give at least NUMERIC_MIN_SIG_DIGITS significant digits; but in any + * case not less than the input's dscale. + */ + init_var_from_num(num, &arg); + + init_var(&result); + + /* convert input to float8, ignoring overflow */ + val = numericvar_to_double_no_overflow(&arg); + + /* + * log10(result) = num * log10(e), so this is approximately the decimal + * weight of the result: + */ + val *= 0.434294481903252; + + /* limit to something that won't cause integer overflow */ + val = Max(val, -NUMERIC_MAX_RESULT_SCALE); + val = Min(val, NUMERIC_MAX_RESULT_SCALE); + + rscale = NUMERIC_MIN_SIG_DIGITS - (int) val; + rscale = Max(rscale, arg.dscale); + rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE); + rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE); + + /* + * Let exp_var() do the calculation and return the result. + */ + exp_var(&arg, &result, rscale); + + res = make_result(&result); + + free_var(&result); + + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_ln() - + * + * Compute the natural logarithm of x + */ +Datum +numeric_ln(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + Numeric res; + NumericVar arg; + NumericVar result; + int ln_dweight; + int rscale; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num)) + { + if (NUMERIC_IS_NINF(num)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG), + errmsg("cannot take logarithm of a negative number"))); + /* For NAN or PINF, just duplicate the input */ + PG_RETURN_NUMERIC(duplicate_numeric(num)); + } + + init_var_from_num(num, &arg); + init_var(&result); + + /* Estimated dweight of logarithm */ + ln_dweight = estimate_ln_dweight(&arg); + + rscale = NUMERIC_MIN_SIG_DIGITS - ln_dweight; + rscale = Max(rscale, arg.dscale); + rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE); + rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE); + + ln_var(&arg, &result, rscale); + + res = make_result(&result); + + free_var(&result); + + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_log() - + * + * Compute the logarithm of x in a given base + */ +Datum +numeric_log(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + Numeric res; + NumericVar arg1; + NumericVar arg2; + NumericVar result; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2)) + { + int sign1, + sign2; + + if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2)) + PG_RETURN_NUMERIC(make_result(&const_nan)); + /* fail on negative inputs including -Inf, as log_var would */ + sign1 = numeric_sign_internal(num1); + sign2 = numeric_sign_internal(num2); + if (sign1 < 0 || sign2 < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG), + errmsg("cannot take logarithm of a negative number"))); + /* fail on zero inputs, as log_var would */ + if (sign1 == 0 || sign2 == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG), + errmsg("cannot take logarithm of zero"))); + if (NUMERIC_IS_PINF(num1)) + { + /* log(Inf, Inf) reduces to Inf/Inf, so it's NaN */ + if (NUMERIC_IS_PINF(num2)) + PG_RETURN_NUMERIC(make_result(&const_nan)); + /* log(Inf, finite-positive) is zero (we don't throw underflow) */ + PG_RETURN_NUMERIC(make_result(&const_zero)); + } + Assert(NUMERIC_IS_PINF(num2)); + /* log(finite-positive, Inf) is Inf */ + PG_RETURN_NUMERIC(make_result(&const_pinf)); + } + + /* + * Initialize things + */ + init_var_from_num(num1, &arg1); + init_var_from_num(num2, &arg2); + init_var(&result); + + /* + * Call log_var() to compute and return the result; note it handles scale + * selection itself. + */ + log_var(&arg1, &arg2, &result); + + res = make_result(&result); + + free_var(&result); + + PG_RETURN_NUMERIC(res); +} + + +/* + * numeric_power() - + * + * Raise x to the power of y + */ +Datum +numeric_power(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + Numeric res; + NumericVar arg1; + NumericVar arg2; + NumericVar result; + int sign1, + sign2; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2)) + { + /* + * We follow the POSIX spec for pow(3), which says that NaN ^ 0 = 1, + * and 1 ^ NaN = 1, while all other cases with NaN inputs yield NaN + * (with no error). + */ + if (NUMERIC_IS_NAN(num1)) + { + if (!NUMERIC_IS_SPECIAL(num2)) + { + init_var_from_num(num2, &arg2); + if (cmp_var(&arg2, &const_zero) == 0) + PG_RETURN_NUMERIC(make_result(&const_one)); + } + PG_RETURN_NUMERIC(make_result(&const_nan)); + } + if (NUMERIC_IS_NAN(num2)) + { + if (!NUMERIC_IS_SPECIAL(num1)) + { + init_var_from_num(num1, &arg1); + if (cmp_var(&arg1, &const_one) == 0) + PG_RETURN_NUMERIC(make_result(&const_one)); + } + PG_RETURN_NUMERIC(make_result(&const_nan)); + } + /* At least one input is infinite, but error rules still apply */ + sign1 = numeric_sign_internal(num1); + sign2 = numeric_sign_internal(num2); + if (sign1 == 0 && sign2 < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION), + errmsg("zero raised to a negative power is undefined"))); + if (sign1 < 0 && !numeric_is_integral(num2)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION), + errmsg("a negative number raised to a non-integer power yields a complex result"))); + + /* + * POSIX gives this series of rules for pow(3) with infinite inputs: + * + * For any value of y, if x is +1, 1.0 shall be returned. + */ + if (!NUMERIC_IS_SPECIAL(num1)) + { + init_var_from_num(num1, &arg1); + if (cmp_var(&arg1, &const_one) == 0) + PG_RETURN_NUMERIC(make_result(&const_one)); + } + + /* + * For any value of x, if y is [-]0, 1.0 shall be returned. + */ + if (sign2 == 0) + PG_RETURN_NUMERIC(make_result(&const_one)); + + /* + * For any odd integer value of y > 0, if x is [-]0, [-]0 shall be + * returned. For y > 0 and not an odd integer, if x is [-]0, +0 shall + * be returned. (Since we don't deal in minus zero, we need not + * distinguish these two cases.) + */ + if (sign1 == 0 && sign2 > 0) + PG_RETURN_NUMERIC(make_result(&const_zero)); + + /* + * If x is -1, and y is [-]Inf, 1.0 shall be returned. + * + * For |x| < 1, if y is -Inf, +Inf shall be returned. + * + * For |x| > 1, if y is -Inf, +0 shall be returned. + * + * For |x| < 1, if y is +Inf, +0 shall be returned. + * + * For |x| > 1, if y is +Inf, +Inf shall be returned. + */ + if (NUMERIC_IS_INF(num2)) + { + bool abs_x_gt_one; + + if (NUMERIC_IS_SPECIAL(num1)) + abs_x_gt_one = true; /* x is either Inf or -Inf */ + else + { + init_var_from_num(num1, &arg1); + if (cmp_var(&arg1, &const_minus_one) == 0) + PG_RETURN_NUMERIC(make_result(&const_one)); + arg1.sign = NUMERIC_POS; /* now arg1 = abs(x) */ + abs_x_gt_one = (cmp_var(&arg1, &const_one) > 0); + } + if (abs_x_gt_one == (sign2 > 0)) + PG_RETURN_NUMERIC(make_result(&const_pinf)); + else + PG_RETURN_NUMERIC(make_result(&const_zero)); + } + + /* + * For y < 0, if x is +Inf, +0 shall be returned. + * + * For y > 0, if x is +Inf, +Inf shall be returned. + */ + if (NUMERIC_IS_PINF(num1)) + { + if (sign2 > 0) + PG_RETURN_NUMERIC(make_result(&const_pinf)); + else + PG_RETURN_NUMERIC(make_result(&const_zero)); + } + + Assert(NUMERIC_IS_NINF(num1)); + + /* + * For y an odd integer < 0, if x is -Inf, -0 shall be returned. For + * y < 0 and not an odd integer, if x is -Inf, +0 shall be returned. + * (Again, we need not distinguish these two cases.) + */ + if (sign2 < 0) + PG_RETURN_NUMERIC(make_result(&const_zero)); + + /* + * For y an odd integer > 0, if x is -Inf, -Inf shall be returned. For + * y > 0 and not an odd integer, if x is -Inf, +Inf shall be returned. + */ + init_var_from_num(num2, &arg2); + if (arg2.ndigits > 0 && arg2.ndigits == arg2.weight + 1 && + (arg2.digits[arg2.ndigits - 1] & 1)) + PG_RETURN_NUMERIC(make_result(&const_ninf)); + else + PG_RETURN_NUMERIC(make_result(&const_pinf)); + } + + /* + * The SQL spec requires that we emit a particular SQLSTATE error code for + * certain error conditions. Specifically, we don't return a + * divide-by-zero error code for 0 ^ -1. Raising a negative number to a + * non-integer power must produce the same error code, but that case is + * handled in power_var(). + */ + sign1 = numeric_sign_internal(num1); + sign2 = numeric_sign_internal(num2); + + if (sign1 == 0 && sign2 < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION), + errmsg("zero raised to a negative power is undefined"))); + + /* + * Initialize things + */ + init_var(&result); + init_var_from_num(num1, &arg1); + init_var_from_num(num2, &arg2); + + /* + * Call power_var() to compute and return the result; note it handles + * scale selection itself. + */ + power_var(&arg1, &arg2, &result); + + res = make_result(&result); + + free_var(&result); + + PG_RETURN_NUMERIC(res); +} + +/* + * numeric_scale() - + * + * Returns the scale, i.e. the count of decimal digits in the fractional part + */ +Datum +numeric_scale(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + + if (NUMERIC_IS_SPECIAL(num)) + PG_RETURN_NULL(); + + PG_RETURN_INT32(NUMERIC_DSCALE(num)); +} + +/* + * Calculate minimum scale for value. + */ +static int +get_min_scale(NumericVar *var) +{ + int min_scale; + int last_digit_pos; + + /* + * Ordinarily, the input value will be "stripped" so that the last + * NumericDigit is nonzero. But we don't want to get into an infinite + * loop if it isn't, so explicitly find the last nonzero digit. + */ + last_digit_pos = var->ndigits - 1; + while (last_digit_pos >= 0 && + var->digits[last_digit_pos] == 0) + last_digit_pos--; + + if (last_digit_pos >= 0) + { + /* compute min_scale assuming that last ndigit has no zeroes */ + min_scale = (last_digit_pos - var->weight) * DEC_DIGITS; + + /* + * We could get a negative result if there are no digits after the + * decimal point. In this case the min_scale must be zero. + */ + if (min_scale > 0) + { + /* + * Reduce min_scale if trailing digit(s) in last NumericDigit are + * zero. + */ + NumericDigit last_digit = var->digits[last_digit_pos]; + + while (last_digit % 10 == 0) + { + min_scale--; + last_digit /= 10; + } + } + else + min_scale = 0; + } + else + min_scale = 0; /* result if input is zero */ + + return min_scale; +} + +/* + * Returns minimum scale required to represent supplied value without loss. + */ +Datum +numeric_min_scale(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + NumericVar arg; + int min_scale; + + if (NUMERIC_IS_SPECIAL(num)) + PG_RETURN_NULL(); + + init_var_from_num(num, &arg); + min_scale = get_min_scale(&arg); + free_var(&arg); + + PG_RETURN_INT32(min_scale); +} + +/* + * Reduce scale of numeric value to represent supplied value without loss. + */ +Datum +numeric_trim_scale(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + Numeric res; + NumericVar result; + + if (NUMERIC_IS_SPECIAL(num)) + PG_RETURN_NUMERIC(duplicate_numeric(num)); + + init_var_from_num(num, &result); + result.dscale = get_min_scale(&result); + res = make_result(&result); + free_var(&result); + + PG_RETURN_NUMERIC(res); +} + + +/* ---------------------------------------------------------------------- + * + * Type conversion functions + * + * ---------------------------------------------------------------------- + */ + +Numeric +int64_to_numeric(int64 val) +{ + Numeric res; + NumericVar result; + + init_var(&result); + + int64_to_numericvar(val, &result); + + res = make_result(&result); + + free_var(&result); + + return res; +} + +/* + * Convert val1/(10**val2) to numeric. This is much faster than normal + * numeric division. + */ +Numeric +int64_div_fast_to_numeric(int64 val1, int log10val2) +{ + Numeric res; + NumericVar result; + int64 saved_val1 = val1; + int w; + int m; + + /* how much to decrease the weight by */ + w = log10val2 / DEC_DIGITS; + /* how much is left */ + m = log10val2 % DEC_DIGITS; + + /* + * If there is anything left, multiply the dividend by what's left, then + * shift the weight by one more. + */ + if (m > 0) + { + static int pow10[] = {1, 10, 100, 1000}; + + StaticAssertStmt(lengthof(pow10) == DEC_DIGITS, "mismatch with DEC_DIGITS"); + if (unlikely(pg_mul_s64_overflow(val1, pow10[DEC_DIGITS - m], &val1))) + { + /* + * If it doesn't fit, do the whole computation in numeric the slow + * way. Note that va1l may have been overwritten, so use + * saved_val1 instead. + */ + int val2 = 1; + + for (int i = 0; i < log10val2; i++) + val2 *= 10; + res = numeric_div_opt_error(int64_to_numeric(saved_val1), int64_to_numeric(val2), NULL); + res = DatumGetNumeric(DirectFunctionCall2(numeric_round, + NumericGetDatum(res), + Int32GetDatum(log10val2))); + return res; + } + w++; + } + + init_var(&result); + + int64_to_numericvar(val1, &result); + + result.weight -= w; + result.dscale += w * DEC_DIGITS - (DEC_DIGITS - m); + + res = make_result(&result); + + free_var(&result); + + return res; +} + +Datum +int4_numeric(PG_FUNCTION_ARGS) +{ + int32 val = PG_GETARG_INT32(0); + + PG_RETURN_NUMERIC(int64_to_numeric(val)); +} + +int32 +numeric_int4_opt_error(Numeric num, bool *have_error) +{ + NumericVar x; + int32 result; + + if (have_error) + *have_error = false; + + if (NUMERIC_IS_SPECIAL(num)) + { + if (have_error) + { + *have_error = true; + return 0; + } + else + { + if (NUMERIC_IS_NAN(num)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert NaN to %s", "integer"))); + else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert infinity to %s", "integer"))); + } + } + + /* Convert to variable format, then convert to int4 */ + init_var_from_num(num, &x); + + if (!numericvar_to_int32(&x, &result)) + { + if (have_error) + { + *have_error = true; + return 0; + } + else + { + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + } + } + + return result; +} + +Datum +numeric_int4(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + + PG_RETURN_INT32(numeric_int4_opt_error(num, NULL)); +} + +/* + * Given a NumericVar, convert it to an int32. If the NumericVar + * exceeds the range of an int32, false is returned, otherwise true is returned. + * The input NumericVar is *not* free'd. + */ +static bool +numericvar_to_int32(const NumericVar *var, int32 *result) +{ + int64 val; + + if (!numericvar_to_int64(var, &val)) + return false; + + if (unlikely(val < PG_INT32_MIN) || unlikely(val > PG_INT32_MAX)) + return false; + + /* Down-convert to int4 */ + *result = (int32) val; + + return true; +} + +Datum +int8_numeric(PG_FUNCTION_ARGS) +{ + int64 val = PG_GETARG_INT64(0); + + PG_RETURN_NUMERIC(int64_to_numeric(val)); +} + + +Datum +numeric_int8(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + NumericVar x; + int64 result; + + if (NUMERIC_IS_SPECIAL(num)) + { + if (NUMERIC_IS_NAN(num)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert NaN to %s", "bigint"))); + else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert infinity to %s", "bigint"))); + } + + /* Convert to variable format and thence to int8 */ + init_var_from_num(num, &x); + + if (!numericvar_to_int64(&x, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + + PG_RETURN_INT64(result); +} + + +Datum +int2_numeric(PG_FUNCTION_ARGS) +{ + int16 val = PG_GETARG_INT16(0); + + PG_RETURN_NUMERIC(int64_to_numeric(val)); +} + + +Datum +numeric_int2(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + NumericVar x; + int64 val; + int16 result; + + if (NUMERIC_IS_SPECIAL(num)) + { + if (NUMERIC_IS_NAN(num)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert NaN to %s", "smallint"))); + else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert infinity to %s", "smallint"))); + } + + /* Convert to variable format and thence to int8 */ + init_var_from_num(num, &x); + + if (!numericvar_to_int64(&x, &val)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("smallint out of range"))); + + if (unlikely(val < PG_INT16_MIN) || unlikely(val > PG_INT16_MAX)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("smallint out of range"))); + + /* Down-convert to int2 */ + result = (int16) val; + + PG_RETURN_INT16(result); +} + + +Datum +float8_numeric(PG_FUNCTION_ARGS) +{ + float8 val = PG_GETARG_FLOAT8(0); + Numeric res; + NumericVar result; + char buf[DBL_DIG + 100]; + + if (isnan(val)) + PG_RETURN_NUMERIC(make_result(&const_nan)); + + if (isinf(val)) + { + if (val < 0) + PG_RETURN_NUMERIC(make_result(&const_ninf)); + else + PG_RETURN_NUMERIC(make_result(&const_pinf)); + } + + snprintf(buf, sizeof(buf), "%.*g", DBL_DIG, val); + + init_var(&result); + + /* Assume we need not worry about leading/trailing spaces */ + (void) set_var_from_str(buf, buf, &result); + + res = make_result(&result); + + free_var(&result); + + PG_RETURN_NUMERIC(res); +} + + +Datum +numeric_float8(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + char *tmp; + Datum result; + + if (NUMERIC_IS_SPECIAL(num)) + { + if (NUMERIC_IS_PINF(num)) + PG_RETURN_FLOAT8(get_float8_infinity()); + else if (NUMERIC_IS_NINF(num)) + PG_RETURN_FLOAT8(-get_float8_infinity()); + else + PG_RETURN_FLOAT8(get_float8_nan()); + } + + tmp = DatumGetCString(DirectFunctionCall1(numeric_out, + NumericGetDatum(num))); + + result = DirectFunctionCall1(float8in, CStringGetDatum(tmp)); + + pfree(tmp); + + PG_RETURN_DATUM(result); +} + + +/* + * Convert numeric to float8; if out of range, return +/- HUGE_VAL + * + * (internal helper function, not directly callable from SQL) + */ +Datum +numeric_float8_no_overflow(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + double val; + + if (NUMERIC_IS_SPECIAL(num)) + { + if (NUMERIC_IS_PINF(num)) + val = HUGE_VAL; + else if (NUMERIC_IS_NINF(num)) + val = -HUGE_VAL; + else + val = get_float8_nan(); + } + else + { + NumericVar x; + + init_var_from_num(num, &x); + val = numericvar_to_double_no_overflow(&x); + } + + PG_RETURN_FLOAT8(val); +} + +Datum +float4_numeric(PG_FUNCTION_ARGS) +{ + float4 val = PG_GETARG_FLOAT4(0); + Numeric res; + NumericVar result; + char buf[FLT_DIG + 100]; + + if (isnan(val)) + PG_RETURN_NUMERIC(make_result(&const_nan)); + + if (isinf(val)) + { + if (val < 0) + PG_RETURN_NUMERIC(make_result(&const_ninf)); + else + PG_RETURN_NUMERIC(make_result(&const_pinf)); + } + + snprintf(buf, sizeof(buf), "%.*g", FLT_DIG, val); + + init_var(&result); + + /* Assume we need not worry about leading/trailing spaces */ + (void) set_var_from_str(buf, buf, &result); + + res = make_result(&result); + + free_var(&result); + + PG_RETURN_NUMERIC(res); +} + + +Datum +numeric_float4(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + char *tmp; + Datum result; + + if (NUMERIC_IS_SPECIAL(num)) + { + if (NUMERIC_IS_PINF(num)) + PG_RETURN_FLOAT4(get_float4_infinity()); + else if (NUMERIC_IS_NINF(num)) + PG_RETURN_FLOAT4(-get_float4_infinity()); + else + PG_RETURN_FLOAT4(get_float4_nan()); + } + + tmp = DatumGetCString(DirectFunctionCall1(numeric_out, + NumericGetDatum(num))); + + result = DirectFunctionCall1(float4in, CStringGetDatum(tmp)); + + pfree(tmp); + + PG_RETURN_DATUM(result); +} + + +Datum +numeric_pg_lsn(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + NumericVar x; + XLogRecPtr result; + + if (NUMERIC_IS_SPECIAL(num)) + { + if (NUMERIC_IS_NAN(num)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert NaN to %s", "pg_lsn"))); + else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert infinity to %s", "pg_lsn"))); + } + + /* Convert to variable format and thence to pg_lsn */ + init_var_from_num(num, &x); + + if (!numericvar_to_uint64(&x, (uint64 *) &result)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("pg_lsn out of range"))); + + PG_RETURN_LSN(result); +} + + +/* ---------------------------------------------------------------------- + * + * Aggregate functions + * + * The transition datatype for all these aggregates is declared as INTERNAL. + * Actually, it's a pointer to a NumericAggState allocated in the aggregate + * context. The digit buffers for the NumericVars will be there too. + * + * On platforms which support 128-bit integers some aggregates instead use a + * 128-bit integer based transition datatype to speed up calculations. + * + * ---------------------------------------------------------------------- + */ + +typedef struct NumericAggState +{ + bool calcSumX2; /* if true, calculate sumX2 */ + MemoryContext agg_context; /* context we're calculating in */ + int64 N; /* count of processed numbers */ + NumericSumAccum sumX; /* sum of processed numbers */ + NumericSumAccum sumX2; /* sum of squares of processed numbers */ + int maxScale; /* maximum scale seen so far */ + int64 maxScaleCount; /* number of values seen with maximum scale */ + /* These counts are *not* included in N! Use NA_TOTAL_COUNT() as needed */ + int64 NaNcount; /* count of NaN values */ + int64 pInfcount; /* count of +Inf values */ + int64 nInfcount; /* count of -Inf values */ +} NumericAggState; + +#define NA_TOTAL_COUNT(na) \ + ((na)->N + (na)->NaNcount + (na)->pInfcount + (na)->nInfcount) + +/* + * Prepare state data for a numeric aggregate function that needs to compute + * sum, count and optionally sum of squares of the input. + */ +static NumericAggState * +makeNumericAggState(FunctionCallInfo fcinfo, bool calcSumX2) +{ + NumericAggState *state; + MemoryContext agg_context; + MemoryContext old_context; + + if (!AggCheckCallContext(fcinfo, &agg_context)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + old_context = MemoryContextSwitchTo(agg_context); + + state = (NumericAggState *) palloc0(sizeof(NumericAggState)); + state->calcSumX2 = calcSumX2; + state->agg_context = agg_context; + + MemoryContextSwitchTo(old_context); + + return state; +} + +/* + * Like makeNumericAggState(), but allocate the state in the current memory + * context. + */ +static NumericAggState * +makeNumericAggStateCurrentContext(bool calcSumX2) +{ + NumericAggState *state; + + state = (NumericAggState *) palloc0(sizeof(NumericAggState)); + state->calcSumX2 = calcSumX2; + state->agg_context = CurrentMemoryContext; + + return state; +} + +/* + * Accumulate a new input value for numeric aggregate functions. + */ +static void +do_numeric_accum(NumericAggState *state, Numeric newval) +{ + NumericVar X; + NumericVar X2; + MemoryContext old_context; + + /* Count NaN/infinity inputs separately from all else */ + if (NUMERIC_IS_SPECIAL(newval)) + { + if (NUMERIC_IS_PINF(newval)) + state->pInfcount++; + else if (NUMERIC_IS_NINF(newval)) + state->nInfcount++; + else + state->NaNcount++; + return; + } + + /* load processed number in short-lived context */ + init_var_from_num(newval, &X); + + /* + * Track the highest input dscale that we've seen, to support inverse + * transitions (see do_numeric_discard). + */ + if (X.dscale > state->maxScale) + { + state->maxScale = X.dscale; + state->maxScaleCount = 1; + } + else if (X.dscale == state->maxScale) + state->maxScaleCount++; + + /* if we need X^2, calculate that in short-lived context */ + if (state->calcSumX2) + { + init_var(&X2); + mul_var(&X, &X, &X2, X.dscale * 2); + } + + /* The rest of this needs to work in the aggregate context */ + old_context = MemoryContextSwitchTo(state->agg_context); + + state->N++; + + /* Accumulate sums */ + accum_sum_add(&(state->sumX), &X); + + if (state->calcSumX2) + accum_sum_add(&(state->sumX2), &X2); + + MemoryContextSwitchTo(old_context); +} + +/* + * Attempt to remove an input value from the aggregated state. + * + * If the value cannot be removed then the function will return false; the + * possible reasons for failing are described below. + * + * If we aggregate the values 1.01 and 2 then the result will be 3.01. + * If we are then asked to un-aggregate the 1.01 then we must fail as we + * won't be able to tell what the new aggregated value's dscale should be. + * We don't want to return 2.00 (dscale = 2), since the sum's dscale would + * have been zero if we'd really aggregated only 2. + * + * Note: alternatively, we could count the number of inputs with each possible + * dscale (up to some sane limit). Not yet clear if it's worth the trouble. + */ +static bool +do_numeric_discard(NumericAggState *state, Numeric newval) +{ + NumericVar X; + NumericVar X2; + MemoryContext old_context; + + /* Count NaN/infinity inputs separately from all else */ + if (NUMERIC_IS_SPECIAL(newval)) + { + if (NUMERIC_IS_PINF(newval)) + state->pInfcount--; + else if (NUMERIC_IS_NINF(newval)) + state->nInfcount--; + else + state->NaNcount--; + return true; + } + + /* load processed number in short-lived context */ + init_var_from_num(newval, &X); + + /* + * state->sumX's dscale is the maximum dscale of any of the inputs. + * Removing the last input with that dscale would require us to recompute + * the maximum dscale of the *remaining* inputs, which we cannot do unless + * no more non-NaN inputs remain at all. So we report a failure instead, + * and force the aggregation to be redone from scratch. + */ + if (X.dscale == state->maxScale) + { + if (state->maxScaleCount > 1 || state->maxScale == 0) + { + /* + * Some remaining inputs have same dscale, or dscale hasn't gotten + * above zero anyway + */ + state->maxScaleCount--; + } + else if (state->N == 1) + { + /* No remaining non-NaN inputs at all, so reset maxScale */ + state->maxScale = 0; + state->maxScaleCount = 0; + } + else + { + /* Correct new maxScale is uncertain, must fail */ + return false; + } + } + + /* if we need X^2, calculate that in short-lived context */ + if (state->calcSumX2) + { + init_var(&X2); + mul_var(&X, &X, &X2, X.dscale * 2); + } + + /* The rest of this needs to work in the aggregate context */ + old_context = MemoryContextSwitchTo(state->agg_context); + + if (state->N-- > 1) + { + /* Negate X, to subtract it from the sum */ + X.sign = (X.sign == NUMERIC_POS ? NUMERIC_NEG : NUMERIC_POS); + accum_sum_add(&(state->sumX), &X); + + if (state->calcSumX2) + { + /* Negate X^2. X^2 is always positive */ + X2.sign = NUMERIC_NEG; + accum_sum_add(&(state->sumX2), &X2); + } + } + else + { + /* Zero the sums */ + Assert(state->N == 0); + + accum_sum_reset(&state->sumX); + if (state->calcSumX2) + accum_sum_reset(&state->sumX2); + } + + MemoryContextSwitchTo(old_context); + + return true; +} + +/* + * Generic transition function for numeric aggregates that require sumX2. + */ +Datum +numeric_accum(PG_FUNCTION_ARGS) +{ + NumericAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); + + /* Create the state data on the first call */ + if (state == NULL) + state = makeNumericAggState(fcinfo, true); + + if (!PG_ARGISNULL(1)) + do_numeric_accum(state, PG_GETARG_NUMERIC(1)); + + PG_RETURN_POINTER(state); +} + +/* + * Generic combine function for numeric aggregates which require sumX2 + */ +Datum +numeric_combine(PG_FUNCTION_ARGS) +{ + NumericAggState *state1; + NumericAggState *state2; + MemoryContext agg_context; + MemoryContext old_context; + + if (!AggCheckCallContext(fcinfo, &agg_context)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + state1 = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); + state2 = PG_ARGISNULL(1) ? NULL : (NumericAggState *) PG_GETARG_POINTER(1); + + if (state2 == NULL) + PG_RETURN_POINTER(state1); + + /* manually copy all fields from state2 to state1 */ + if (state1 == NULL) + { + old_context = MemoryContextSwitchTo(agg_context); + + state1 = makeNumericAggStateCurrentContext(true); + state1->N = state2->N; + state1->NaNcount = state2->NaNcount; + state1->pInfcount = state2->pInfcount; + state1->nInfcount = state2->nInfcount; + state1->maxScale = state2->maxScale; + state1->maxScaleCount = state2->maxScaleCount; + + accum_sum_copy(&state1->sumX, &state2->sumX); + accum_sum_copy(&state1->sumX2, &state2->sumX2); + + MemoryContextSwitchTo(old_context); + + PG_RETURN_POINTER(state1); + } + + state1->N += state2->N; + state1->NaNcount += state2->NaNcount; + state1->pInfcount += state2->pInfcount; + state1->nInfcount += state2->nInfcount; + + if (state2->N > 0) + { + /* + * These are currently only needed for moving aggregates, but let's do + * the right thing anyway... + */ + if (state2->maxScale > state1->maxScale) + { + state1->maxScale = state2->maxScale; + state1->maxScaleCount = state2->maxScaleCount; + } + else if (state2->maxScale == state1->maxScale) + state1->maxScaleCount += state2->maxScaleCount; + + /* The rest of this needs to work in the aggregate context */ + old_context = MemoryContextSwitchTo(agg_context); + + /* Accumulate sums */ + accum_sum_combine(&state1->sumX, &state2->sumX); + accum_sum_combine(&state1->sumX2, &state2->sumX2); + + MemoryContextSwitchTo(old_context); + } + PG_RETURN_POINTER(state1); +} + +/* + * Generic transition function for numeric aggregates that don't require sumX2. + */ +Datum +numeric_avg_accum(PG_FUNCTION_ARGS) +{ + NumericAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); + + /* Create the state data on the first call */ + if (state == NULL) + state = makeNumericAggState(fcinfo, false); + + if (!PG_ARGISNULL(1)) + do_numeric_accum(state, PG_GETARG_NUMERIC(1)); + + PG_RETURN_POINTER(state); +} + +/* + * Combine function for numeric aggregates which don't require sumX2 + */ +Datum +numeric_avg_combine(PG_FUNCTION_ARGS) +{ + NumericAggState *state1; + NumericAggState *state2; + MemoryContext agg_context; + MemoryContext old_context; + + if (!AggCheckCallContext(fcinfo, &agg_context)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + state1 = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); + state2 = PG_ARGISNULL(1) ? NULL : (NumericAggState *) PG_GETARG_POINTER(1); + + if (state2 == NULL) + PG_RETURN_POINTER(state1); + + /* manually copy all fields from state2 to state1 */ + if (state1 == NULL) + { + old_context = MemoryContextSwitchTo(agg_context); + + state1 = makeNumericAggStateCurrentContext(false); + state1->N = state2->N; + state1->NaNcount = state2->NaNcount; + state1->pInfcount = state2->pInfcount; + state1->nInfcount = state2->nInfcount; + state1->maxScale = state2->maxScale; + state1->maxScaleCount = state2->maxScaleCount; + + accum_sum_copy(&state1->sumX, &state2->sumX); + + MemoryContextSwitchTo(old_context); + + PG_RETURN_POINTER(state1); + } + + state1->N += state2->N; + state1->NaNcount += state2->NaNcount; + state1->pInfcount += state2->pInfcount; + state1->nInfcount += state2->nInfcount; + + if (state2->N > 0) + { + /* + * These are currently only needed for moving aggregates, but let's do + * the right thing anyway... + */ + if (state2->maxScale > state1->maxScale) + { + state1->maxScale = state2->maxScale; + state1->maxScaleCount = state2->maxScaleCount; + } + else if (state2->maxScale == state1->maxScale) + state1->maxScaleCount += state2->maxScaleCount; + + /* The rest of this needs to work in the aggregate context */ + old_context = MemoryContextSwitchTo(agg_context); + + /* Accumulate sums */ + accum_sum_combine(&state1->sumX, &state2->sumX); + + MemoryContextSwitchTo(old_context); + } + PG_RETURN_POINTER(state1); +} + +/* + * numeric_avg_serialize + * Serialize NumericAggState for numeric aggregates that don't require + * sumX2. + */ +Datum +numeric_avg_serialize(PG_FUNCTION_ARGS) +{ + NumericAggState *state; + StringInfoData buf; + Datum temp; + bytea *sumX; + bytea *result; + NumericVar tmp_var; + + /* Ensure we disallow calling when not in aggregate context */ + if (!AggCheckCallContext(fcinfo, NULL)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + state = (NumericAggState *) PG_GETARG_POINTER(0); + + /* + * This is a little wasteful since make_result converts the NumericVar + * into a Numeric and numeric_send converts it back again. Is it worth + * splitting the tasks in numeric_send into separate functions to stop + * this? Doing so would also remove the fmgr call overhead. + */ + init_var(&tmp_var); + accum_sum_final(&state->sumX, &tmp_var); + + temp = DirectFunctionCall1(numeric_send, + NumericGetDatum(make_result(&tmp_var))); + sumX = DatumGetByteaPP(temp); + free_var(&tmp_var); + + pq_begintypsend(&buf); + + /* N */ + pq_sendint64(&buf, state->N); + + /* sumX */ + pq_sendbytes(&buf, VARDATA_ANY(sumX), VARSIZE_ANY_EXHDR(sumX)); + + /* maxScale */ + pq_sendint32(&buf, state->maxScale); + + /* maxScaleCount */ + pq_sendint64(&buf, state->maxScaleCount); + + /* NaNcount */ + pq_sendint64(&buf, state->NaNcount); + + /* pInfcount */ + pq_sendint64(&buf, state->pInfcount); + + /* nInfcount */ + pq_sendint64(&buf, state->nInfcount); + + result = pq_endtypsend(&buf); + + PG_RETURN_BYTEA_P(result); +} + +/* + * numeric_avg_deserialize + * Deserialize bytea into NumericAggState for numeric aggregates that + * don't require sumX2. + */ +Datum +numeric_avg_deserialize(PG_FUNCTION_ARGS) +{ + bytea *sstate; + NumericAggState *result; + Datum temp; + NumericVar tmp_var; + StringInfoData buf; + + if (!AggCheckCallContext(fcinfo, NULL)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + sstate = PG_GETARG_BYTEA_PP(0); + + /* + * Copy the bytea into a StringInfo so that we can "receive" it using the + * standard recv-function infrastructure. + */ + initStringInfo(&buf); + appendBinaryStringInfo(&buf, + VARDATA_ANY(sstate), VARSIZE_ANY_EXHDR(sstate)); + + result = makeNumericAggStateCurrentContext(false); + + /* N */ + result->N = pq_getmsgint64(&buf); + + /* sumX */ + temp = DirectFunctionCall3(numeric_recv, + PointerGetDatum(&buf), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1)); + init_var_from_num(DatumGetNumeric(temp), &tmp_var); + accum_sum_add(&(result->sumX), &tmp_var); + + /* maxScale */ + result->maxScale = pq_getmsgint(&buf, 4); + + /* maxScaleCount */ + result->maxScaleCount = pq_getmsgint64(&buf); + + /* NaNcount */ + result->NaNcount = pq_getmsgint64(&buf); + + /* pInfcount */ + result->pInfcount = pq_getmsgint64(&buf); + + /* nInfcount */ + result->nInfcount = pq_getmsgint64(&buf); + + pq_getmsgend(&buf); + pfree(buf.data); + + PG_RETURN_POINTER(result); +} + +/* + * numeric_serialize + * Serialization function for NumericAggState for numeric aggregates that + * require sumX2. + */ +Datum +numeric_serialize(PG_FUNCTION_ARGS) +{ + NumericAggState *state; + StringInfoData buf; + Datum temp; + bytea *sumX; + NumericVar tmp_var; + bytea *sumX2; + bytea *result; + + /* Ensure we disallow calling when not in aggregate context */ + if (!AggCheckCallContext(fcinfo, NULL)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + state = (NumericAggState *) PG_GETARG_POINTER(0); + + /* + * This is a little wasteful since make_result converts the NumericVar + * into a Numeric and numeric_send converts it back again. Is it worth + * splitting the tasks in numeric_send into separate functions to stop + * this? Doing so would also remove the fmgr call overhead. + */ + init_var(&tmp_var); + + accum_sum_final(&state->sumX, &tmp_var); + temp = DirectFunctionCall1(numeric_send, + NumericGetDatum(make_result(&tmp_var))); + sumX = DatumGetByteaPP(temp); + + accum_sum_final(&state->sumX2, &tmp_var); + temp = DirectFunctionCall1(numeric_send, + NumericGetDatum(make_result(&tmp_var))); + sumX2 = DatumGetByteaPP(temp); + + free_var(&tmp_var); + + pq_begintypsend(&buf); + + /* N */ + pq_sendint64(&buf, state->N); + + /* sumX */ + pq_sendbytes(&buf, VARDATA_ANY(sumX), VARSIZE_ANY_EXHDR(sumX)); + + /* sumX2 */ + pq_sendbytes(&buf, VARDATA_ANY(sumX2), VARSIZE_ANY_EXHDR(sumX2)); + + /* maxScale */ + pq_sendint32(&buf, state->maxScale); + + /* maxScaleCount */ + pq_sendint64(&buf, state->maxScaleCount); + + /* NaNcount */ + pq_sendint64(&buf, state->NaNcount); + + /* pInfcount */ + pq_sendint64(&buf, state->pInfcount); + + /* nInfcount */ + pq_sendint64(&buf, state->nInfcount); + + result = pq_endtypsend(&buf); + + PG_RETURN_BYTEA_P(result); +} + +/* + * numeric_deserialize + * Deserialization function for NumericAggState for numeric aggregates that + * require sumX2. + */ +Datum +numeric_deserialize(PG_FUNCTION_ARGS) +{ + bytea *sstate; + NumericAggState *result; + Datum temp; + NumericVar sumX_var; + NumericVar sumX2_var; + StringInfoData buf; + + if (!AggCheckCallContext(fcinfo, NULL)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + sstate = PG_GETARG_BYTEA_PP(0); + + /* + * Copy the bytea into a StringInfo so that we can "receive" it using the + * standard recv-function infrastructure. + */ + initStringInfo(&buf); + appendBinaryStringInfo(&buf, + VARDATA_ANY(sstate), VARSIZE_ANY_EXHDR(sstate)); + + result = makeNumericAggStateCurrentContext(false); + + /* N */ + result->N = pq_getmsgint64(&buf); + + /* sumX */ + temp = DirectFunctionCall3(numeric_recv, + PointerGetDatum(&buf), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1)); + init_var_from_num(DatumGetNumeric(temp), &sumX_var); + accum_sum_add(&(result->sumX), &sumX_var); + + /* sumX2 */ + temp = DirectFunctionCall3(numeric_recv, + PointerGetDatum(&buf), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1)); + init_var_from_num(DatumGetNumeric(temp), &sumX2_var); + accum_sum_add(&(result->sumX2), &sumX2_var); + + /* maxScale */ + result->maxScale = pq_getmsgint(&buf, 4); + + /* maxScaleCount */ + result->maxScaleCount = pq_getmsgint64(&buf); + + /* NaNcount */ + result->NaNcount = pq_getmsgint64(&buf); + + /* pInfcount */ + result->pInfcount = pq_getmsgint64(&buf); + + /* nInfcount */ + result->nInfcount = pq_getmsgint64(&buf); + + pq_getmsgend(&buf); + pfree(buf.data); + + PG_RETURN_POINTER(result); +} + +/* + * Generic inverse transition function for numeric aggregates + * (with or without requirement for X^2). + */ +Datum +numeric_accum_inv(PG_FUNCTION_ARGS) +{ + NumericAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); + + /* Should not get here with no state */ + if (state == NULL) + elog(ERROR, "numeric_accum_inv called with NULL state"); + + if (!PG_ARGISNULL(1)) + { + /* If we fail to perform the inverse transition, return NULL */ + if (!do_numeric_discard(state, PG_GETARG_NUMERIC(1))) + PG_RETURN_NULL(); + } + + PG_RETURN_POINTER(state); +} + + +/* + * Integer data types in general use Numeric accumulators to share code + * and avoid risk of overflow. + * + * However for performance reasons optimized special-purpose accumulator + * routines are used when possible. + * + * On platforms with 128-bit integer support, the 128-bit routines will be + * used when sum(X) or sum(X*X) fit into 128-bit. + * + * For 16 and 32 bit inputs, the N and sum(X) fit into 64-bit so the 64-bit + * accumulators will be used for SUM and AVG of these data types. + */ + +#ifdef HAVE_INT128 +typedef struct Int128AggState +{ + bool calcSumX2; /* if true, calculate sumX2 */ + int64 N; /* count of processed numbers */ + int128 sumX; /* sum of processed numbers */ + int128 sumX2; /* sum of squares of processed numbers */ +} Int128AggState; + +/* + * Prepare state data for a 128-bit aggregate function that needs to compute + * sum, count and optionally sum of squares of the input. + */ +static Int128AggState * +makeInt128AggState(FunctionCallInfo fcinfo, bool calcSumX2) +{ + Int128AggState *state; + MemoryContext agg_context; + MemoryContext old_context; + + if (!AggCheckCallContext(fcinfo, &agg_context)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + old_context = MemoryContextSwitchTo(agg_context); + + state = (Int128AggState *) palloc0(sizeof(Int128AggState)); + state->calcSumX2 = calcSumX2; + + MemoryContextSwitchTo(old_context); + + return state; +} + +/* + * Like makeInt128AggState(), but allocate the state in the current memory + * context. + */ +static Int128AggState * +makeInt128AggStateCurrentContext(bool calcSumX2) +{ + Int128AggState *state; + + state = (Int128AggState *) palloc0(sizeof(Int128AggState)); + state->calcSumX2 = calcSumX2; + + return state; +} + +/* + * Accumulate a new input value for 128-bit aggregate functions. + */ +static void +do_int128_accum(Int128AggState *state, int128 newval) +{ + if (state->calcSumX2) + state->sumX2 += newval * newval; + + state->sumX += newval; + state->N++; +} + +/* + * Remove an input value from the aggregated state. + */ +static void +do_int128_discard(Int128AggState *state, int128 newval) +{ + if (state->calcSumX2) + state->sumX2 -= newval * newval; + + state->sumX -= newval; + state->N--; +} + +typedef Int128AggState PolyNumAggState; +#define makePolyNumAggState makeInt128AggState +#define makePolyNumAggStateCurrentContext makeInt128AggStateCurrentContext +#else +typedef NumericAggState PolyNumAggState; +#define makePolyNumAggState makeNumericAggState +#define makePolyNumAggStateCurrentContext makeNumericAggStateCurrentContext +#endif + +Datum +int2_accum(PG_FUNCTION_ARGS) +{ + PolyNumAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0); + + /* Create the state data on the first call */ + if (state == NULL) + state = makePolyNumAggState(fcinfo, true); + + if (!PG_ARGISNULL(1)) + { +#ifdef HAVE_INT128 + do_int128_accum(state, (int128) PG_GETARG_INT16(1)); +#else + do_numeric_accum(state, int64_to_numeric(PG_GETARG_INT16(1))); +#endif + } + + PG_RETURN_POINTER(state); +} + +Datum +int4_accum(PG_FUNCTION_ARGS) +{ + PolyNumAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0); + + /* Create the state data on the first call */ + if (state == NULL) + state = makePolyNumAggState(fcinfo, true); + + if (!PG_ARGISNULL(1)) + { +#ifdef HAVE_INT128 + do_int128_accum(state, (int128) PG_GETARG_INT32(1)); +#else + do_numeric_accum(state, int64_to_numeric(PG_GETARG_INT32(1))); +#endif + } + + PG_RETURN_POINTER(state); +} + +Datum +int8_accum(PG_FUNCTION_ARGS) +{ + NumericAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); + + /* Create the state data on the first call */ + if (state == NULL) + state = makeNumericAggState(fcinfo, true); + + if (!PG_ARGISNULL(1)) + do_numeric_accum(state, int64_to_numeric(PG_GETARG_INT64(1))); + + PG_RETURN_POINTER(state); +} + +/* + * Combine function for numeric aggregates which require sumX2 + */ +Datum +numeric_poly_combine(PG_FUNCTION_ARGS) +{ + PolyNumAggState *state1; + PolyNumAggState *state2; + MemoryContext agg_context; + MemoryContext old_context; + + if (!AggCheckCallContext(fcinfo, &agg_context)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + state1 = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0); + state2 = PG_ARGISNULL(1) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(1); + + if (state2 == NULL) + PG_RETURN_POINTER(state1); + + /* manually copy all fields from state2 to state1 */ + if (state1 == NULL) + { + old_context = MemoryContextSwitchTo(agg_context); + + state1 = makePolyNumAggState(fcinfo, true); + state1->N = state2->N; + +#ifdef HAVE_INT128 + state1->sumX = state2->sumX; + state1->sumX2 = state2->sumX2; +#else + accum_sum_copy(&state1->sumX, &state2->sumX); + accum_sum_copy(&state1->sumX2, &state2->sumX2); +#endif + + MemoryContextSwitchTo(old_context); + + PG_RETURN_POINTER(state1); + } + + if (state2->N > 0) + { + state1->N += state2->N; + +#ifdef HAVE_INT128 + state1->sumX += state2->sumX; + state1->sumX2 += state2->sumX2; +#else + /* The rest of this needs to work in the aggregate context */ + old_context = MemoryContextSwitchTo(agg_context); + + /* Accumulate sums */ + accum_sum_combine(&state1->sumX, &state2->sumX); + accum_sum_combine(&state1->sumX2, &state2->sumX2); + + MemoryContextSwitchTo(old_context); +#endif + + } + PG_RETURN_POINTER(state1); +} + +/* + * numeric_poly_serialize + * Serialize PolyNumAggState into bytea for aggregate functions which + * require sumX2. + */ +Datum +numeric_poly_serialize(PG_FUNCTION_ARGS) +{ + PolyNumAggState *state; + StringInfoData buf; + bytea *sumX; + bytea *sumX2; + bytea *result; + + /* Ensure we disallow calling when not in aggregate context */ + if (!AggCheckCallContext(fcinfo, NULL)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + state = (PolyNumAggState *) PG_GETARG_POINTER(0); + + /* + * If the platform supports int128 then sumX and sumX2 will be a 128 bit + * integer type. Here we'll convert that into a numeric type so that the + * combine state is in the same format for both int128 enabled machines + * and machines which don't support that type. The logic here is that one + * day we might like to send these over to another server for further + * processing and we want a standard format to work with. + */ + { + Datum temp; + NumericVar num; + + init_var(&num); + +#ifdef HAVE_INT128 + int128_to_numericvar(state->sumX, &num); +#else + accum_sum_final(&state->sumX, &num); +#endif + temp = DirectFunctionCall1(numeric_send, + NumericGetDatum(make_result(&num))); + sumX = DatumGetByteaPP(temp); + +#ifdef HAVE_INT128 + int128_to_numericvar(state->sumX2, &num); +#else + accum_sum_final(&state->sumX2, &num); +#endif + temp = DirectFunctionCall1(numeric_send, + NumericGetDatum(make_result(&num))); + sumX2 = DatumGetByteaPP(temp); + + free_var(&num); + } + + pq_begintypsend(&buf); + + /* N */ + pq_sendint64(&buf, state->N); + + /* sumX */ + pq_sendbytes(&buf, VARDATA_ANY(sumX), VARSIZE_ANY_EXHDR(sumX)); + + /* sumX2 */ + pq_sendbytes(&buf, VARDATA_ANY(sumX2), VARSIZE_ANY_EXHDR(sumX2)); + + result = pq_endtypsend(&buf); + + PG_RETURN_BYTEA_P(result); +} + +/* + * numeric_poly_deserialize + * Deserialize PolyNumAggState from bytea for aggregate functions which + * require sumX2. + */ +Datum +numeric_poly_deserialize(PG_FUNCTION_ARGS) +{ + bytea *sstate; + PolyNumAggState *result; + Datum sumX; + NumericVar sumX_var; + Datum sumX2; + NumericVar sumX2_var; + StringInfoData buf; + + if (!AggCheckCallContext(fcinfo, NULL)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + sstate = PG_GETARG_BYTEA_PP(0); + + /* + * Copy the bytea into a StringInfo so that we can "receive" it using the + * standard recv-function infrastructure. + */ + initStringInfo(&buf); + appendBinaryStringInfo(&buf, + VARDATA_ANY(sstate), VARSIZE_ANY_EXHDR(sstate)); + + result = makePolyNumAggStateCurrentContext(false); + + /* N */ + result->N = pq_getmsgint64(&buf); + + /* sumX */ + sumX = DirectFunctionCall3(numeric_recv, + PointerGetDatum(&buf), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1)); + + /* sumX2 */ + sumX2 = DirectFunctionCall3(numeric_recv, + PointerGetDatum(&buf), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1)); + + init_var_from_num(DatumGetNumeric(sumX), &sumX_var); +#ifdef HAVE_INT128 + numericvar_to_int128(&sumX_var, &result->sumX); +#else + accum_sum_add(&result->sumX, &sumX_var); +#endif + + init_var_from_num(DatumGetNumeric(sumX2), &sumX2_var); +#ifdef HAVE_INT128 + numericvar_to_int128(&sumX2_var, &result->sumX2); +#else + accum_sum_add(&result->sumX2, &sumX2_var); +#endif + + pq_getmsgend(&buf); + pfree(buf.data); + + PG_RETURN_POINTER(result); +} + +/* + * Transition function for int8 input when we don't need sumX2. + */ +Datum +int8_avg_accum(PG_FUNCTION_ARGS) +{ + PolyNumAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0); + + /* Create the state data on the first call */ + if (state == NULL) + state = makePolyNumAggState(fcinfo, false); + + if (!PG_ARGISNULL(1)) + { +#ifdef HAVE_INT128 + do_int128_accum(state, (int128) PG_GETARG_INT64(1)); +#else + do_numeric_accum(state, int64_to_numeric(PG_GETARG_INT64(1))); +#endif + } + + PG_RETURN_POINTER(state); +} + +/* + * Combine function for PolyNumAggState for aggregates which don't require + * sumX2 + */ +Datum +int8_avg_combine(PG_FUNCTION_ARGS) +{ + PolyNumAggState *state1; + PolyNumAggState *state2; + MemoryContext agg_context; + MemoryContext old_context; + + if (!AggCheckCallContext(fcinfo, &agg_context)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + state1 = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0); + state2 = PG_ARGISNULL(1) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(1); + + if (state2 == NULL) + PG_RETURN_POINTER(state1); + + /* manually copy all fields from state2 to state1 */ + if (state1 == NULL) + { + old_context = MemoryContextSwitchTo(agg_context); + + state1 = makePolyNumAggState(fcinfo, false); + state1->N = state2->N; + +#ifdef HAVE_INT128 + state1->sumX = state2->sumX; +#else + accum_sum_copy(&state1->sumX, &state2->sumX); +#endif + MemoryContextSwitchTo(old_context); + + PG_RETURN_POINTER(state1); + } + + if (state2->N > 0) + { + state1->N += state2->N; + +#ifdef HAVE_INT128 + state1->sumX += state2->sumX; +#else + /* The rest of this needs to work in the aggregate context */ + old_context = MemoryContextSwitchTo(agg_context); + + /* Accumulate sums */ + accum_sum_combine(&state1->sumX, &state2->sumX); + + MemoryContextSwitchTo(old_context); +#endif + + } + PG_RETURN_POINTER(state1); +} + +/* + * int8_avg_serialize + * Serialize PolyNumAggState into bytea using the standard + * recv-function infrastructure. + */ +Datum +int8_avg_serialize(PG_FUNCTION_ARGS) +{ + PolyNumAggState *state; + StringInfoData buf; + bytea *sumX; + bytea *result; + + /* Ensure we disallow calling when not in aggregate context */ + if (!AggCheckCallContext(fcinfo, NULL)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + state = (PolyNumAggState *) PG_GETARG_POINTER(0); + + /* + * If the platform supports int128 then sumX will be a 128 integer type. + * Here we'll convert that into a numeric type so that the combine state + * is in the same format for both int128 enabled machines and machines + * which don't support that type. The logic here is that one day we might + * like to send these over to another server for further processing and we + * want a standard format to work with. + */ + { + Datum temp; + NumericVar num; + + init_var(&num); + +#ifdef HAVE_INT128 + int128_to_numericvar(state->sumX, &num); +#else + accum_sum_final(&state->sumX, &num); +#endif + temp = DirectFunctionCall1(numeric_send, + NumericGetDatum(make_result(&num))); + sumX = DatumGetByteaPP(temp); + + free_var(&num); + } + + pq_begintypsend(&buf); + + /* N */ + pq_sendint64(&buf, state->N); + + /* sumX */ + pq_sendbytes(&buf, VARDATA_ANY(sumX), VARSIZE_ANY_EXHDR(sumX)); + + result = pq_endtypsend(&buf); + + PG_RETURN_BYTEA_P(result); +} + +/* + * int8_avg_deserialize + * Deserialize bytea back into PolyNumAggState. + */ +Datum +int8_avg_deserialize(PG_FUNCTION_ARGS) +{ + bytea *sstate; + PolyNumAggState *result; + StringInfoData buf; + Datum temp; + NumericVar num; + + if (!AggCheckCallContext(fcinfo, NULL)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + sstate = PG_GETARG_BYTEA_PP(0); + + /* + * Copy the bytea into a StringInfo so that we can "receive" it using the + * standard recv-function infrastructure. + */ + initStringInfo(&buf); + appendBinaryStringInfo(&buf, + VARDATA_ANY(sstate), VARSIZE_ANY_EXHDR(sstate)); + + result = makePolyNumAggStateCurrentContext(false); + + /* N */ + result->N = pq_getmsgint64(&buf); + + /* sumX */ + temp = DirectFunctionCall3(numeric_recv, + PointerGetDatum(&buf), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1)); + init_var_from_num(DatumGetNumeric(temp), &num); +#ifdef HAVE_INT128 + numericvar_to_int128(&num, &result->sumX); +#else + accum_sum_add(&result->sumX, &num); +#endif + + pq_getmsgend(&buf); + pfree(buf.data); + + PG_RETURN_POINTER(result); +} + +/* + * Inverse transition functions to go with the above. + */ + +Datum +int2_accum_inv(PG_FUNCTION_ARGS) +{ + PolyNumAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0); + + /* Should not get here with no state */ + if (state == NULL) + elog(ERROR, "int2_accum_inv called with NULL state"); + + if (!PG_ARGISNULL(1)) + { +#ifdef HAVE_INT128 + do_int128_discard(state, (int128) PG_GETARG_INT16(1)); +#else + /* Should never fail, all inputs have dscale 0 */ + if (!do_numeric_discard(state, int64_to_numeric(PG_GETARG_INT16(1)))) + elog(ERROR, "do_numeric_discard failed unexpectedly"); +#endif + } + + PG_RETURN_POINTER(state); +} + +Datum +int4_accum_inv(PG_FUNCTION_ARGS) +{ + PolyNumAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0); + + /* Should not get here with no state */ + if (state == NULL) + elog(ERROR, "int4_accum_inv called with NULL state"); + + if (!PG_ARGISNULL(1)) + { +#ifdef HAVE_INT128 + do_int128_discard(state, (int128) PG_GETARG_INT32(1)); +#else + /* Should never fail, all inputs have dscale 0 */ + if (!do_numeric_discard(state, int64_to_numeric(PG_GETARG_INT32(1)))) + elog(ERROR, "do_numeric_discard failed unexpectedly"); +#endif + } + + PG_RETURN_POINTER(state); +} + +Datum +int8_accum_inv(PG_FUNCTION_ARGS) +{ + NumericAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); + + /* Should not get here with no state */ + if (state == NULL) + elog(ERROR, "int8_accum_inv called with NULL state"); + + if (!PG_ARGISNULL(1)) + { + /* Should never fail, all inputs have dscale 0 */ + if (!do_numeric_discard(state, int64_to_numeric(PG_GETARG_INT64(1)))) + elog(ERROR, "do_numeric_discard failed unexpectedly"); + } + + PG_RETURN_POINTER(state); +} + +Datum +int8_avg_accum_inv(PG_FUNCTION_ARGS) +{ + PolyNumAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0); + + /* Should not get here with no state */ + if (state == NULL) + elog(ERROR, "int8_avg_accum_inv called with NULL state"); + + if (!PG_ARGISNULL(1)) + { +#ifdef HAVE_INT128 + do_int128_discard(state, (int128) PG_GETARG_INT64(1)); +#else + /* Should never fail, all inputs have dscale 0 */ + if (!do_numeric_discard(state, int64_to_numeric(PG_GETARG_INT64(1)))) + elog(ERROR, "do_numeric_discard failed unexpectedly"); +#endif + } + + PG_RETURN_POINTER(state); +} + +Datum +numeric_poly_sum(PG_FUNCTION_ARGS) +{ +#ifdef HAVE_INT128 + PolyNumAggState *state; + Numeric res; + NumericVar result; + + state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0); + + /* If there were no non-null inputs, return NULL */ + if (state == NULL || state->N == 0) + PG_RETURN_NULL(); + + init_var(&result); + + int128_to_numericvar(state->sumX, &result); + + res = make_result(&result); + + free_var(&result); + + PG_RETURN_NUMERIC(res); +#else + return numeric_sum(fcinfo); +#endif +} + +Datum +numeric_poly_avg(PG_FUNCTION_ARGS) +{ +#ifdef HAVE_INT128 + PolyNumAggState *state; + NumericVar result; + Datum countd, + sumd; + + state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0); + + /* If there were no non-null inputs, return NULL */ + if (state == NULL || state->N == 0) + PG_RETURN_NULL(); + + init_var(&result); + + int128_to_numericvar(state->sumX, &result); + + countd = NumericGetDatum(int64_to_numeric(state->N)); + sumd = NumericGetDatum(make_result(&result)); + + free_var(&result); + + PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, sumd, countd)); +#else + return numeric_avg(fcinfo); +#endif +} + +Datum +numeric_avg(PG_FUNCTION_ARGS) +{ + NumericAggState *state; + Datum N_datum; + Datum sumX_datum; + NumericVar sumX_var; + + state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); + + /* If there were no non-null inputs, return NULL */ + if (state == NULL || NA_TOTAL_COUNT(state) == 0) + PG_RETURN_NULL(); + + if (state->NaNcount > 0) /* there was at least one NaN input */ + PG_RETURN_NUMERIC(make_result(&const_nan)); + + /* adding plus and minus infinities gives NaN */ + if (state->pInfcount > 0 && state->nInfcount > 0) + PG_RETURN_NUMERIC(make_result(&const_nan)); + if (state->pInfcount > 0) + PG_RETURN_NUMERIC(make_result(&const_pinf)); + if (state->nInfcount > 0) + PG_RETURN_NUMERIC(make_result(&const_ninf)); + + N_datum = NumericGetDatum(int64_to_numeric(state->N)); + + init_var(&sumX_var); + accum_sum_final(&state->sumX, &sumX_var); + sumX_datum = NumericGetDatum(make_result(&sumX_var)); + free_var(&sumX_var); + + PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, sumX_datum, N_datum)); +} + +Datum +numeric_sum(PG_FUNCTION_ARGS) +{ + NumericAggState *state; + NumericVar sumX_var; + Numeric result; + + state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); + + /* If there were no non-null inputs, return NULL */ + if (state == NULL || NA_TOTAL_COUNT(state) == 0) + PG_RETURN_NULL(); + + if (state->NaNcount > 0) /* there was at least one NaN input */ + PG_RETURN_NUMERIC(make_result(&const_nan)); + + /* adding plus and minus infinities gives NaN */ + if (state->pInfcount > 0 && state->nInfcount > 0) + PG_RETURN_NUMERIC(make_result(&const_nan)); + if (state->pInfcount > 0) + PG_RETURN_NUMERIC(make_result(&const_pinf)); + if (state->nInfcount > 0) + PG_RETURN_NUMERIC(make_result(&const_ninf)); + + init_var(&sumX_var); + accum_sum_final(&state->sumX, &sumX_var); + result = make_result(&sumX_var); + free_var(&sumX_var); + + PG_RETURN_NUMERIC(result); +} + +/* + * Workhorse routine for the standard deviance and variance + * aggregates. 'state' is aggregate's transition state. + * 'variance' specifies whether we should calculate the + * variance or the standard deviation. 'sample' indicates whether the + * caller is interested in the sample or the population + * variance/stddev. + * + * If appropriate variance statistic is undefined for the input, + * *is_null is set to true and NULL is returned. + */ +static Numeric +numeric_stddev_internal(NumericAggState *state, + bool variance, bool sample, + bool *is_null) +{ + Numeric res; + NumericVar vN, + vsumX, + vsumX2, + vNminus1; + int64 totCount; + int rscale; + + /* + * Sample stddev and variance are undefined when N <= 1; population stddev + * is undefined when N == 0. Return NULL in either case (note that NaNs + * and infinities count as normal inputs for this purpose). + */ + if (state == NULL || (totCount = NA_TOTAL_COUNT(state)) == 0) + { + *is_null = true; + return NULL; + } + + if (sample && totCount <= 1) + { + *is_null = true; + return NULL; + } + + *is_null = false; + + /* + * Deal with NaN and infinity cases. By analogy to the behavior of the + * float8 functions, any infinity input produces NaN output. + */ + if (state->NaNcount > 0 || state->pInfcount > 0 || state->nInfcount > 0) + return make_result(&const_nan); + + /* OK, normal calculation applies */ + init_var(&vN); + init_var(&vsumX); + init_var(&vsumX2); + + int64_to_numericvar(state->N, &vN); + accum_sum_final(&(state->sumX), &vsumX); + accum_sum_final(&(state->sumX2), &vsumX2); + + init_var(&vNminus1); + sub_var(&vN, &const_one, &vNminus1); + + /* compute rscale for mul_var calls */ + rscale = vsumX.dscale * 2; + + mul_var(&vsumX, &vsumX, &vsumX, rscale); /* vsumX = sumX * sumX */ + mul_var(&vN, &vsumX2, &vsumX2, rscale); /* vsumX2 = N * sumX2 */ + sub_var(&vsumX2, &vsumX, &vsumX2); /* N * sumX2 - sumX * sumX */ + + if (cmp_var(&vsumX2, &const_zero) <= 0) + { + /* Watch out for roundoff error producing a negative numerator */ + res = make_result(&const_zero); + } + else + { + if (sample) + mul_var(&vN, &vNminus1, &vNminus1, 0); /* N * (N - 1) */ + else + mul_var(&vN, &vN, &vNminus1, 0); /* N * N */ + rscale = select_div_scale(&vsumX2, &vNminus1); + div_var(&vsumX2, &vNminus1, &vsumX, rscale, true); /* variance */ + if (!variance) + sqrt_var(&vsumX, &vsumX, rscale); /* stddev */ + + res = make_result(&vsumX); + } + + free_var(&vNminus1); + free_var(&vsumX); + free_var(&vsumX2); + + return res; +} + +Datum +numeric_var_samp(PG_FUNCTION_ARGS) +{ + NumericAggState *state; + Numeric res; + bool is_null; + + state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); + + res = numeric_stddev_internal(state, true, true, &is_null); + + if (is_null) + PG_RETURN_NULL(); + else + PG_RETURN_NUMERIC(res); +} + +Datum +numeric_stddev_samp(PG_FUNCTION_ARGS) +{ + NumericAggState *state; + Numeric res; + bool is_null; + + state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); + + res = numeric_stddev_internal(state, false, true, &is_null); + + if (is_null) + PG_RETURN_NULL(); + else + PG_RETURN_NUMERIC(res); +} + +Datum +numeric_var_pop(PG_FUNCTION_ARGS) +{ + NumericAggState *state; + Numeric res; + bool is_null; + + state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); + + res = numeric_stddev_internal(state, true, false, &is_null); + + if (is_null) + PG_RETURN_NULL(); + else + PG_RETURN_NUMERIC(res); +} + +Datum +numeric_stddev_pop(PG_FUNCTION_ARGS) +{ + NumericAggState *state; + Numeric res; + bool is_null; + + state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); + + res = numeric_stddev_internal(state, false, false, &is_null); + + if (is_null) + PG_RETURN_NULL(); + else + PG_RETURN_NUMERIC(res); +} + +#ifdef HAVE_INT128 +static Numeric +numeric_poly_stddev_internal(Int128AggState *state, + bool variance, bool sample, + bool *is_null) +{ + NumericAggState numstate; + Numeric res; + + /* Initialize an empty agg state */ + memset(&numstate, 0, sizeof(NumericAggState)); + + if (state) + { + NumericVar tmp_var; + + numstate.N = state->N; + + init_var(&tmp_var); + + int128_to_numericvar(state->sumX, &tmp_var); + accum_sum_add(&numstate.sumX, &tmp_var); + + int128_to_numericvar(state->sumX2, &tmp_var); + accum_sum_add(&numstate.sumX2, &tmp_var); + + free_var(&tmp_var); + } + + res = numeric_stddev_internal(&numstate, variance, sample, is_null); + + if (numstate.sumX.ndigits > 0) + { + pfree(numstate.sumX.pos_digits); + pfree(numstate.sumX.neg_digits); + } + if (numstate.sumX2.ndigits > 0) + { + pfree(numstate.sumX2.pos_digits); + pfree(numstate.sumX2.neg_digits); + } + + return res; +} +#endif + +Datum +numeric_poly_var_samp(PG_FUNCTION_ARGS) +{ +#ifdef HAVE_INT128 + PolyNumAggState *state; + Numeric res; + bool is_null; + + state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0); + + res = numeric_poly_stddev_internal(state, true, true, &is_null); + + if (is_null) + PG_RETURN_NULL(); + else + PG_RETURN_NUMERIC(res); +#else + return numeric_var_samp(fcinfo); +#endif +} + +Datum +numeric_poly_stddev_samp(PG_FUNCTION_ARGS) +{ +#ifdef HAVE_INT128 + PolyNumAggState *state; + Numeric res; + bool is_null; + + state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0); + + res = numeric_poly_stddev_internal(state, false, true, &is_null); + + if (is_null) + PG_RETURN_NULL(); + else + PG_RETURN_NUMERIC(res); +#else + return numeric_stddev_samp(fcinfo); +#endif +} + +Datum +numeric_poly_var_pop(PG_FUNCTION_ARGS) +{ +#ifdef HAVE_INT128 + PolyNumAggState *state; + Numeric res; + bool is_null; + + state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0); + + res = numeric_poly_stddev_internal(state, true, false, &is_null); + + if (is_null) + PG_RETURN_NULL(); + else + PG_RETURN_NUMERIC(res); +#else + return numeric_var_pop(fcinfo); +#endif +} + +Datum +numeric_poly_stddev_pop(PG_FUNCTION_ARGS) +{ +#ifdef HAVE_INT128 + PolyNumAggState *state; + Numeric res; + bool is_null; + + state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0); + + res = numeric_poly_stddev_internal(state, false, false, &is_null); + + if (is_null) + PG_RETURN_NULL(); + else + PG_RETURN_NUMERIC(res); +#else + return numeric_stddev_pop(fcinfo); +#endif +} + +/* + * SUM transition functions for integer datatypes. + * + * To avoid overflow, we use accumulators wider than the input datatype. + * A Numeric accumulator is needed for int8 input; for int4 and int2 + * inputs, we use int8 accumulators which should be sufficient for practical + * purposes. (The latter two therefore don't really belong in this file, + * but we keep them here anyway.) + * + * Because SQL defines the SUM() of no values to be NULL, not zero, + * the initial condition of the transition data value needs to be NULL. This + * means we can't rely on ExecAgg to automatically insert the first non-null + * data value into the transition data: it doesn't know how to do the type + * conversion. The upshot is that these routines have to be marked non-strict + * and handle substitution of the first non-null input themselves. + * + * Note: these functions are used only in plain aggregation mode. + * In moving-aggregate mode, we use intX_avg_accum and intX_avg_accum_inv. + */ + +Datum +int2_sum(PG_FUNCTION_ARGS) +{ + int64 newval; + + if (PG_ARGISNULL(0)) + { + /* No non-null input seen so far... */ + if (PG_ARGISNULL(1)) + PG_RETURN_NULL(); /* still no non-null */ + /* This is the first non-null input. */ + newval = (int64) PG_GETARG_INT16(1); + PG_RETURN_INT64(newval); + } + + /* + * If we're invoked as an aggregate, we can cheat and modify our first + * parameter in-place to avoid palloc overhead. If not, we need to return + * the new value of the transition variable. (If int8 is pass-by-value, + * then of course this is useless as well as incorrect, so just ifdef it + * out.) + */ +#ifndef USE_FLOAT8_BYVAL /* controls int8 too */ + if (AggCheckCallContext(fcinfo, NULL)) + { + int64 *oldsum = (int64 *) PG_GETARG_POINTER(0); + + /* Leave the running sum unchanged in the new input is null */ + if (!PG_ARGISNULL(1)) + *oldsum = *oldsum + (int64) PG_GETARG_INT16(1); + + PG_RETURN_POINTER(oldsum); + } + else +#endif + { + int64 oldsum = PG_GETARG_INT64(0); + + /* Leave sum unchanged if new input is null. */ + if (PG_ARGISNULL(1)) + PG_RETURN_INT64(oldsum); + + /* OK to do the addition. */ + newval = oldsum + (int64) PG_GETARG_INT16(1); + + PG_RETURN_INT64(newval); + } +} + +Datum +int4_sum(PG_FUNCTION_ARGS) +{ + int64 newval; + + if (PG_ARGISNULL(0)) + { + /* No non-null input seen so far... */ + if (PG_ARGISNULL(1)) + PG_RETURN_NULL(); /* still no non-null */ + /* This is the first non-null input. */ + newval = (int64) PG_GETARG_INT32(1); + PG_RETURN_INT64(newval); + } + + /* + * If we're invoked as an aggregate, we can cheat and modify our first + * parameter in-place to avoid palloc overhead. If not, we need to return + * the new value of the transition variable. (If int8 is pass-by-value, + * then of course this is useless as well as incorrect, so just ifdef it + * out.) + */ +#ifndef USE_FLOAT8_BYVAL /* controls int8 too */ + if (AggCheckCallContext(fcinfo, NULL)) + { + int64 *oldsum = (int64 *) PG_GETARG_POINTER(0); + + /* Leave the running sum unchanged in the new input is null */ + if (!PG_ARGISNULL(1)) + *oldsum = *oldsum + (int64) PG_GETARG_INT32(1); + + PG_RETURN_POINTER(oldsum); + } + else +#endif + { + int64 oldsum = PG_GETARG_INT64(0); + + /* Leave sum unchanged if new input is null. */ + if (PG_ARGISNULL(1)) + PG_RETURN_INT64(oldsum); + + /* OK to do the addition. */ + newval = oldsum + (int64) PG_GETARG_INT32(1); + + PG_RETURN_INT64(newval); + } +} + +/* + * Note: this function is obsolete, it's no longer used for SUM(int8). + */ +Datum +int8_sum(PG_FUNCTION_ARGS) +{ + Numeric oldsum; + + if (PG_ARGISNULL(0)) + { + /* No non-null input seen so far... */ + if (PG_ARGISNULL(1)) + PG_RETURN_NULL(); /* still no non-null */ + /* This is the first non-null input. */ + PG_RETURN_NUMERIC(int64_to_numeric(PG_GETARG_INT64(1))); + } + + /* + * Note that we cannot special-case the aggregate case here, as we do for + * int2_sum and int4_sum: numeric is of variable size, so we cannot modify + * our first parameter in-place. + */ + + oldsum = PG_GETARG_NUMERIC(0); + + /* Leave sum unchanged if new input is null. */ + if (PG_ARGISNULL(1)) + PG_RETURN_NUMERIC(oldsum); + + /* OK to do the addition. */ + PG_RETURN_DATUM(DirectFunctionCall2(numeric_add, + NumericGetDatum(oldsum), + NumericGetDatum(int64_to_numeric(PG_GETARG_INT64(1))))); +} + + +/* + * Routines for avg(int2) and avg(int4). The transition datatype + * is a two-element int8 array, holding count and sum. + * + * These functions are also used for sum(int2) and sum(int4) when + * operating in moving-aggregate mode, since for correct inverse transitions + * we need to count the inputs. + */ + +typedef struct Int8TransTypeData +{ + int64 count; + int64 sum; +} Int8TransTypeData; + +Datum +int2_avg_accum(PG_FUNCTION_ARGS) +{ + ArrayType *transarray; + int16 newval = PG_GETARG_INT16(1); + Int8TransTypeData *transdata; + + /* + * If we're invoked as an aggregate, we can cheat and modify our first + * parameter in-place to reduce palloc overhead. Otherwise we need to make + * a copy of it before scribbling on it. + */ + if (AggCheckCallContext(fcinfo, NULL)) + transarray = PG_GETARG_ARRAYTYPE_P(0); + else + transarray = PG_GETARG_ARRAYTYPE_P_COPY(0); + + if (ARR_HASNULL(transarray) || + ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData)) + elog(ERROR, "expected 2-element int8 array"); + + transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray); + transdata->count++; + transdata->sum += newval; + + PG_RETURN_ARRAYTYPE_P(transarray); +} + +Datum +int4_avg_accum(PG_FUNCTION_ARGS) +{ + ArrayType *transarray; + int32 newval = PG_GETARG_INT32(1); + Int8TransTypeData *transdata; + + /* + * If we're invoked as an aggregate, we can cheat and modify our first + * parameter in-place to reduce palloc overhead. Otherwise we need to make + * a copy of it before scribbling on it. + */ + if (AggCheckCallContext(fcinfo, NULL)) + transarray = PG_GETARG_ARRAYTYPE_P(0); + else + transarray = PG_GETARG_ARRAYTYPE_P_COPY(0); + + if (ARR_HASNULL(transarray) || + ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData)) + elog(ERROR, "expected 2-element int8 array"); + + transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray); + transdata->count++; + transdata->sum += newval; + + PG_RETURN_ARRAYTYPE_P(transarray); +} + +Datum +int4_avg_combine(PG_FUNCTION_ARGS) +{ + ArrayType *transarray1; + ArrayType *transarray2; + Int8TransTypeData *state1; + Int8TransTypeData *state2; + + if (!AggCheckCallContext(fcinfo, NULL)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + transarray1 = PG_GETARG_ARRAYTYPE_P(0); + transarray2 = PG_GETARG_ARRAYTYPE_P(1); + + if (ARR_HASNULL(transarray1) || + ARR_SIZE(transarray1) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData)) + elog(ERROR, "expected 2-element int8 array"); + + if (ARR_HASNULL(transarray2) || + ARR_SIZE(transarray2) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData)) + elog(ERROR, "expected 2-element int8 array"); + + state1 = (Int8TransTypeData *) ARR_DATA_PTR(transarray1); + state2 = (Int8TransTypeData *) ARR_DATA_PTR(transarray2); + + state1->count += state2->count; + state1->sum += state2->sum; + + PG_RETURN_ARRAYTYPE_P(transarray1); +} + +Datum +int2_avg_accum_inv(PG_FUNCTION_ARGS) +{ + ArrayType *transarray; + int16 newval = PG_GETARG_INT16(1); + Int8TransTypeData *transdata; + + /* + * If we're invoked as an aggregate, we can cheat and modify our first + * parameter in-place to reduce palloc overhead. Otherwise we need to make + * a copy of it before scribbling on it. + */ + if (AggCheckCallContext(fcinfo, NULL)) + transarray = PG_GETARG_ARRAYTYPE_P(0); + else + transarray = PG_GETARG_ARRAYTYPE_P_COPY(0); + + if (ARR_HASNULL(transarray) || + ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData)) + elog(ERROR, "expected 2-element int8 array"); + + transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray); + transdata->count--; + transdata->sum -= newval; + + PG_RETURN_ARRAYTYPE_P(transarray); +} + +Datum +int4_avg_accum_inv(PG_FUNCTION_ARGS) +{ + ArrayType *transarray; + int32 newval = PG_GETARG_INT32(1); + Int8TransTypeData *transdata; + + /* + * If we're invoked as an aggregate, we can cheat and modify our first + * parameter in-place to reduce palloc overhead. Otherwise we need to make + * a copy of it before scribbling on it. + */ + if (AggCheckCallContext(fcinfo, NULL)) + transarray = PG_GETARG_ARRAYTYPE_P(0); + else + transarray = PG_GETARG_ARRAYTYPE_P_COPY(0); + + if (ARR_HASNULL(transarray) || + ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData)) + elog(ERROR, "expected 2-element int8 array"); + + transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray); + transdata->count--; + transdata->sum -= newval; + + PG_RETURN_ARRAYTYPE_P(transarray); +} + +Datum +int8_avg(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + Int8TransTypeData *transdata; + Datum countd, + sumd; + + if (ARR_HASNULL(transarray) || + ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData)) + elog(ERROR, "expected 2-element int8 array"); + transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray); + + /* SQL defines AVG of no values to be NULL */ + if (transdata->count == 0) + PG_RETURN_NULL(); + + countd = NumericGetDatum(int64_to_numeric(transdata->count)); + sumd = NumericGetDatum(int64_to_numeric(transdata->sum)); + + PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, sumd, countd)); +} + +/* + * SUM(int2) and SUM(int4) both return int8, so we can use this + * final function for both. + */ +Datum +int2int4_sum(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + Int8TransTypeData *transdata; + + if (ARR_HASNULL(transarray) || + ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData)) + elog(ERROR, "expected 2-element int8 array"); + transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray); + + /* SQL defines SUM of no values to be NULL */ + if (transdata->count == 0) + PG_RETURN_NULL(); + + PG_RETURN_DATUM(Int64GetDatumFast(transdata->sum)); +} + + +/* ---------------------------------------------------------------------- + * + * Debug support + * + * ---------------------------------------------------------------------- + */ + +#ifdef NUMERIC_DEBUG + +/* + * dump_numeric() - Dump a value in the db storage format for debugging + */ +static void +dump_numeric(const char *str, Numeric num) +{ + NumericDigit *digits = NUMERIC_DIGITS(num); + int ndigits; + int i; + + ndigits = NUMERIC_NDIGITS(num); + + printf("%s: NUMERIC w=%d d=%d ", str, + NUMERIC_WEIGHT(num), 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; + case NUMERIC_PINF: + printf("Infinity"); + break; + case NUMERIC_NINF: + printf("-Infinity"); + break; + default: + printf("SIGN=0x%x", NUMERIC_SIGN(num)); + break; + } + + for (i = 0; i < ndigits; i++) + printf(" %0*d", DEC_DIGITS, digits[i]); + printf("\n"); +} + + +/* + * dump_var() - Dump a value in the variable format for debugging + */ +static void +dump_var(const char *str, NumericVar *var) +{ + int i; + + printf("%s: VAR w=%d d=%d ", str, var->weight, var->dscale); + switch (var->sign) + { + case NUMERIC_POS: + printf("POS"); + break; + case NUMERIC_NEG: + printf("NEG"); + break; + case NUMERIC_NAN: + printf("NaN"); + break; + case NUMERIC_PINF: + printf("Infinity"); + break; + case NUMERIC_NINF: + printf("-Infinity"); + break; + default: + printf("SIGN=0x%x", var->sign); + break; + } + + for (i = 0; i < var->ndigits; i++) + printf(" %0*d", DEC_DIGITS, var->digits[i]); + + printf("\n"); +} +#endif /* NUMERIC_DEBUG */ + + +/* ---------------------------------------------------------------------- + * + * Local functions follow + * + * In general, these do not support "special" (NaN or infinity) inputs; + * callers should handle those possibilities first. + * (There are one or two exceptions, noted in their header comments.) + * + * ---------------------------------------------------------------------- + */ + + +/* + * alloc_var() - + * + * Allocate a digit buffer of ndigits digits (plus a spare digit for rounding) + */ +static void +alloc_var(NumericVar *var, int ndigits) +{ + digitbuf_free(var->buf); + var->buf = digitbuf_alloc(ndigits + 1); + var->buf[0] = 0; /* spare digit for rounding */ + var->digits = var->buf + 1; + var->ndigits = ndigits; +} + + +/* + * free_var() - + * + * Return the digit buffer of a variable to the free pool + */ +static void +free_var(NumericVar *var) +{ + digitbuf_free(var->buf); + var->buf = NULL; + var->digits = NULL; + var->sign = NUMERIC_NAN; +} + + +/* + * zero_var() - + * + * Set a variable to ZERO. + * Note: its dscale is not touched. + */ +static void +zero_var(NumericVar *var) +{ + digitbuf_free(var->buf); + var->buf = NULL; + var->digits = NULL; + var->ndigits = 0; + var->weight = 0; /* by convention; doesn't really matter */ + var->sign = NUMERIC_POS; /* anything but NAN... */ +} + + +/* + * set_var_from_str() + * + * Parse a string and put the number into a variable + * + * This function does not handle leading or trailing spaces. It returns + * the end+1 position parsed, so that caller can check for trailing + * spaces/garbage if deemed necessary. + * + * cp is the place to actually start parsing; str is what to use in error + * reports. (Typically cp would be the same except advanced over spaces.) + */ +static const char * +set_var_from_str(const char *str, const char *cp, NumericVar *dest) +{ + bool have_dp = false; + int i; + unsigned char *decdigits; + int sign = NUMERIC_POS; + int dweight = -1; + int ddigits; + int dscale = 0; + int weight; + int ndigits; + int offset; + NumericDigit *digits; + + /* + * We first parse the string to extract decimal digits and determine the + * correct decimal weight. Then convert to NBASE representation. + */ + switch (*cp) + { + case '+': + sign = NUMERIC_POS; + cp++; + break; + + case '-': + sign = NUMERIC_NEG; + cp++; + break; + } + + if (*cp == '.') + { + have_dp = true; + cp++; + } + + if (!isdigit((unsigned char) *cp)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "numeric", str))); + + decdigits = (unsigned char *) palloc(strlen(cp) + DEC_DIGITS * 2); + + /* leading padding for digit alignment later */ + memset(decdigits, 0, DEC_DIGITS); + i = DEC_DIGITS; + + while (*cp) + { + if (isdigit((unsigned char) *cp)) + { + decdigits[i++] = *cp++ - '0'; + if (!have_dp) + dweight++; + else + dscale++; + } + else if (*cp == '.') + { + if (have_dp) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "numeric", str))); + have_dp = true; + cp++; + } + else + break; + } + + ddigits = i - DEC_DIGITS; + /* trailing padding for digit alignment later */ + memset(decdigits + i, 0, DEC_DIGITS - 1); + + /* Handle exponent, if any */ + if (*cp == 'e' || *cp == 'E') + { + long exponent; + char *endptr; + + cp++; + exponent = strtol(cp, &endptr, 10); + if (endptr == cp) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "numeric", str))); + cp = endptr; + + /* + * At this point, dweight and dscale can't be more than about + * INT_MAX/2 due to the MaxAllocSize limit on string length, so + * constraining the exponent similarly should be enough to prevent + * integer overflow in this function. If the value is too large to + * fit in storage format, make_result() will complain about it later; + * for consistency use the same ereport errcode/text as make_result(). + */ + if (exponent >= INT_MAX / 2 || exponent <= -(INT_MAX / 2)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value overflows numeric format"))); + dweight += (int) exponent; + dscale -= (int) exponent; + if (dscale < 0) + dscale = 0; + } + + /* + * Okay, convert pure-decimal representation to base NBASE. First we need + * to determine the converted weight and ndigits. offset is the number of + * decimal zeroes to insert before the first given digit to have a + * correctly aligned first NBASE digit. + */ + if (dweight >= 0) + weight = (dweight + 1 + DEC_DIGITS - 1) / DEC_DIGITS - 1; + else + weight = -((-dweight - 1) / DEC_DIGITS + 1); + offset = (weight + 1) * DEC_DIGITS - (dweight + 1); + ndigits = (ddigits + offset + DEC_DIGITS - 1) / DEC_DIGITS; + + alloc_var(dest, ndigits); + dest->sign = sign; + dest->weight = weight; + dest->dscale = dscale; + + i = DEC_DIGITS - offset; + digits = dest->digits; + + while (ndigits-- > 0) + { +#if DEC_DIGITS == 4 + *digits++ = ((decdigits[i] * 10 + decdigits[i + 1]) * 10 + + decdigits[i + 2]) * 10 + decdigits[i + 3]; +#elif DEC_DIGITS == 2 + *digits++ = decdigits[i] * 10 + decdigits[i + 1]; +#elif DEC_DIGITS == 1 + *digits++ = decdigits[i]; +#else +#error unsupported NBASE +#endif + i += DEC_DIGITS; + } + + pfree(decdigits); + + /* Strip any leading/trailing zeroes, and normalize weight if zero */ + strip_var(dest); + + /* Return end+1 position for caller */ + return cp; +} + + +/* + * set_var_from_num() - + * + * Convert the packed db format into a variable + */ +static void +set_var_from_num(Numeric num, NumericVar *dest) +{ + int ndigits; + + ndigits = NUMERIC_NDIGITS(num); + + alloc_var(dest, ndigits); + + dest->weight = NUMERIC_WEIGHT(num); + dest->sign = NUMERIC_SIGN(num); + dest->dscale = NUMERIC_DSCALE(num); + + memcpy(dest->digits, NUMERIC_DIGITS(num), ndigits * sizeof(NumericDigit)); +} + + +/* + * init_var_from_num() - + * + * Initialize a variable from packed db format. The digits array is not + * copied, which saves some cycles when the resulting var is not modified. + * Also, there's no need to call free_var(), as long as you don't assign any + * other value to it (with set_var_* functions, or by using the var as the + * destination of a function like add_var()) + * + * CAUTION: Do not modify the digits buffer of a var initialized with this + * function, e.g by calling round_var() or trunc_var(), as the changes will + * propagate to the original Numeric! It's OK to use it as the destination + * argument of one of the calculational functions, though. + */ +static void +init_var_from_num(Numeric num, NumericVar *dest) +{ + dest->ndigits = NUMERIC_NDIGITS(num); + dest->weight = NUMERIC_WEIGHT(num); + dest->sign = NUMERIC_SIGN(num); + dest->dscale = NUMERIC_DSCALE(num); + dest->digits = NUMERIC_DIGITS(num); + dest->buf = NULL; /* digits array is not palloc'd */ +} + + +/* + * set_var_from_var() - + * + * Copy one variable into another + */ +static void +set_var_from_var(const NumericVar *value, NumericVar *dest) +{ + NumericDigit *newbuf; + + newbuf = digitbuf_alloc(value->ndigits + 1); + newbuf[0] = 0; /* spare digit for rounding */ + if (value->ndigits > 0) /* else value->digits might be null */ + memcpy(newbuf + 1, value->digits, + value->ndigits * sizeof(NumericDigit)); + + digitbuf_free(dest->buf); + + memmove(dest, value, sizeof(NumericVar)); + dest->buf = newbuf; + dest->digits = newbuf + 1; +} + + +/* + * get_str_from_var() - + * + * Convert a var to text representation (guts of numeric_out). + * The var is displayed to the number of digits indicated by its dscale. + * Returns a palloc'd string. + */ +static char * +get_str_from_var(const NumericVar *var) +{ + int dscale; + char *str; + char *cp; + char *endcp; + int i; + int d; + NumericDigit dig; + +#if DEC_DIGITS > 1 + NumericDigit d1; +#endif + + dscale = var->dscale; + + /* + * Allocate space for the result. + * + * i is set to the # of decimal digits before decimal point. dscale is the + * # of decimal digits we will print after decimal point. We may generate + * as many as DEC_DIGITS-1 excess digits at the end, and in addition we + * need room for sign, decimal point, null terminator. + */ + i = (var->weight + 1) * DEC_DIGITS; + if (i <= 0) + i = 1; + + str = palloc(i + dscale + DEC_DIGITS + 2); + cp = str; + + /* + * Output a dash for negative values + */ + if (var->sign == NUMERIC_NEG) + *cp++ = '-'; + + /* + * Output all digits before the decimal point + */ + if (var->weight < 0) + { + d = var->weight + 1; + *cp++ = '0'; + } + else + { + for (d = 0; d <= var->weight; d++) + { + dig = (d < var->ndigits) ? var->digits[d] : 0; + /* In the first digit, suppress extra leading decimal zeroes */ +#if DEC_DIGITS == 4 + { + bool putit = (d > 0); + + d1 = dig / 1000; + dig -= d1 * 1000; + putit |= (d1 > 0); + if (putit) + *cp++ = d1 + '0'; + d1 = dig / 100; + dig -= d1 * 100; + putit |= (d1 > 0); + if (putit) + *cp++ = d1 + '0'; + d1 = dig / 10; + dig -= d1 * 10; + putit |= (d1 > 0); + if (putit) + *cp++ = d1 + '0'; + *cp++ = dig + '0'; + } +#elif DEC_DIGITS == 2 + d1 = dig / 10; + dig -= d1 * 10; + if (d1 > 0 || d > 0) + *cp++ = d1 + '0'; + *cp++ = dig + '0'; +#elif DEC_DIGITS == 1 + *cp++ = dig + '0'; +#else +#error unsupported NBASE +#endif + } + } + + /* + * If requested, output a decimal point and all the digits that follow it. + * We initially put out a multiple of DEC_DIGITS digits, then truncate if + * needed. + */ + if (dscale > 0) + { + *cp++ = '.'; + endcp = cp + dscale; + for (i = 0; i < dscale; d++, i += DEC_DIGITS) + { + dig = (d >= 0 && d < var->ndigits) ? var->digits[d] : 0; +#if DEC_DIGITS == 4 + d1 = dig / 1000; + dig -= d1 * 1000; + *cp++ = d1 + '0'; + d1 = dig / 100; + dig -= d1 * 100; + *cp++ = d1 + '0'; + d1 = dig / 10; + dig -= d1 * 10; + *cp++ = d1 + '0'; + *cp++ = dig + '0'; +#elif DEC_DIGITS == 2 + d1 = dig / 10; + dig -= d1 * 10; + *cp++ = d1 + '0'; + *cp++ = dig + '0'; +#elif DEC_DIGITS == 1 + *cp++ = dig + '0'; +#else +#error unsupported NBASE +#endif + } + cp = endcp; + } + + /* + * terminate the string and return it + */ + *cp = '\0'; + return str; +} + +/* + * get_str_from_var_sci() - + * + * Convert a var to a normalised scientific notation text representation. + * This function does the heavy lifting for numeric_out_sci(). + * + * This notation has the general form a * 10^b, where a is known as the + * "significand" and b is known as the "exponent". + * + * Because we can't do superscript in ASCII (and because we want to copy + * printf's behaviour) we display the exponent using E notation, with a + * minimum of two exponent digits. + * + * For example, the value 1234 could be output as 1.2e+03. + * + * We assume that the exponent can fit into an int32. + * + * rscale is the number of decimal digits desired after the decimal point in + * the output, negative values will be treated as meaning zero. + * + * Returns a palloc'd string. + */ +static char * +get_str_from_var_sci(const NumericVar *var, int rscale) +{ + int32 exponent; + NumericVar tmp_var; + size_t len; + char *str; + char *sig_out; + + if (rscale < 0) + rscale = 0; + + /* + * Determine the exponent of this number in normalised form. + * + * This is the exponent required to represent the number with only one + * significant digit before the decimal place. + */ + if (var->ndigits > 0) + { + exponent = (var->weight + 1) * DEC_DIGITS; + + /* + * Compensate for leading decimal zeroes in the first numeric digit by + * decrementing the exponent. + */ + exponent -= DEC_DIGITS - (int) log10(var->digits[0]); + } + else + { + /* + * If var has no digits, then it must be zero. + * + * Zero doesn't technically have a meaningful exponent in normalised + * notation, but we just display the exponent as zero for consistency + * of output. + */ + exponent = 0; + } + + /* + * Divide var by 10^exponent to get the significand, rounding to rscale + * decimal digits in the process. + */ + init_var(&tmp_var); + + power_ten_int(exponent, &tmp_var); + div_var(var, &tmp_var, &tmp_var, rscale, true); + sig_out = get_str_from_var(&tmp_var); + + free_var(&tmp_var); + + /* + * Allocate space for the result. + * + * In addition to the significand, we need room for the exponent + * decoration ("e"), the sign of the exponent, up to 10 digits for the + * exponent itself, and of course the null terminator. + */ + len = strlen(sig_out) + 13; + str = palloc(len); + snprintf(str, len, "%se%+03d", sig_out, exponent); + + pfree(sig_out); + + return str; +} + + +/* + * duplicate_numeric() - copy a packed-format Numeric + * + * This will handle NaN and Infinity cases. + */ +static Numeric +duplicate_numeric(Numeric num) +{ + Numeric res; + + res = (Numeric) palloc(VARSIZE(num)); + memcpy(res, num, VARSIZE(num)); + return res; +} + +/* + * make_result_opt_error() - + * + * Create the packed db numeric format in palloc()'d memory from + * a variable. This will handle NaN and Infinity cases. + * + * If "have_error" isn't NULL, on overflow *have_error is set to true and + * NULL is returned. This is helpful when caller needs to handle errors. + */ +static Numeric +make_result_opt_error(const NumericVar *var, bool *have_error) +{ + Numeric result; + NumericDigit *digits = var->digits; + int weight = var->weight; + int sign = var->sign; + int n; + Size len; + + if (have_error) + *have_error = false; + + if ((sign & NUMERIC_SIGN_MASK) == NUMERIC_SPECIAL) + { + /* + * Verify valid special value. This could be just an Assert, perhaps, + * but it seems worthwhile to expend a few cycles to ensure that we + * never write any nonzero reserved bits to disk. + */ + if (!(sign == NUMERIC_NAN || + sign == NUMERIC_PINF || + sign == NUMERIC_NINF)) + elog(ERROR, "invalid numeric sign value 0x%x", sign); + + result = (Numeric) palloc(NUMERIC_HDRSZ_SHORT); + + SET_VARSIZE(result, NUMERIC_HDRSZ_SHORT); + result->choice.n_header = sign; + /* the header word is all we need */ + + dump_numeric("make_result()", result); + return result; + } + + n = var->ndigits; + + /* truncate leading zeroes */ + while (n > 0 && *digits == 0) + { + digits++; + weight--; + n--; + } + /* truncate trailing zeroes */ + while (n > 0 && digits[n - 1] == 0) + n--; + + /* If zero result, force to weight=0 and positive sign */ + if (n == 0) + { + weight = 0; + sign = NUMERIC_POS; + } + + /* Build the result */ + if (NUMERIC_CAN_BE_SHORT(var->dscale, weight)) + { + len = NUMERIC_HDRSZ_SHORT + n * sizeof(NumericDigit); + result = (Numeric) palloc(len); + SET_VARSIZE(result, len); + result->choice.n_short.n_header = + (sign == NUMERIC_NEG ? (NUMERIC_SHORT | NUMERIC_SHORT_SIGN_MASK) + : NUMERIC_SHORT) + | (var->dscale << NUMERIC_SHORT_DSCALE_SHIFT) + | (weight < 0 ? NUMERIC_SHORT_WEIGHT_SIGN_MASK : 0) + | (weight & NUMERIC_SHORT_WEIGHT_MASK); + } + else + { + len = NUMERIC_HDRSZ + n * sizeof(NumericDigit); + result = (Numeric) palloc(len); + SET_VARSIZE(result, len); + result->choice.n_long.n_sign_dscale = + sign | (var->dscale & NUMERIC_DSCALE_MASK); + result->choice.n_long.n_weight = weight; + } + + Assert(NUMERIC_NDIGITS(result) == n); + if (n > 0) + memcpy(NUMERIC_DIGITS(result), digits, n * sizeof(NumericDigit)); + + /* Check for overflow of int16 fields */ + if (NUMERIC_WEIGHT(result) != weight || + NUMERIC_DSCALE(result) != var->dscale) + { + if (have_error) + { + *have_error = true; + return NULL; + } + else + { + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value overflows numeric format"))); + } + } + + dump_numeric("make_result()", result); + return result; +} + + +/* + * make_result() - + * + * An interface to make_result_opt_error() without "have_error" argument. + */ +static Numeric +make_result(const NumericVar *var) +{ + return make_result_opt_error(var, NULL); +} + + +/* + * apply_typmod() - + * + * Do bounds checking and rounding according to the specified typmod. + * Note that this is only applied to normal finite values. + */ +static void +apply_typmod(NumericVar *var, int32 typmod) +{ + int precision; + int scale; + int maxdigits; + int ddigits; + int i; + + /* Do nothing if we have a default typmod (-1) */ + if (typmod < (int32) (VARHDRSZ)) + return; + + typmod -= VARHDRSZ; + precision = (typmod >> 16) & 0xffff; + scale = typmod & 0xffff; + maxdigits = precision - scale; + + /* Round to target scale (and set var->dscale) */ + round_var(var, scale); + + /* + * Check for overflow - note we can't do this before rounding, because + * rounding could raise the weight. Also note that the var's weight could + * be inflated by leading zeroes, which will be stripped before storage + * but perhaps might not have been yet. In any case, we must recognize a + * true zero, whose weight doesn't mean anything. + */ + ddigits = (var->weight + 1) * DEC_DIGITS; + if (ddigits > maxdigits) + { + /* Determine true weight; and check for all-zero result */ + for (i = 0; i < var->ndigits; i++) + { + NumericDigit dig = var->digits[i]; + + if (dig) + { + /* Adjust for any high-order decimal zero digits */ +#if DEC_DIGITS == 4 + if (dig < 10) + ddigits -= 3; + else if (dig < 100) + ddigits -= 2; + else if (dig < 1000) + ddigits -= 1; +#elif DEC_DIGITS == 2 + if (dig < 10) + ddigits -= 1; +#elif DEC_DIGITS == 1 + /* no adjustment */ +#else +#error unsupported NBASE +#endif + if (ddigits > maxdigits) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("numeric field overflow"), + errdetail("A field with precision %d, scale %d must round to an absolute value less than %s%d.", + precision, scale, + /* Display 10^0 as 1 */ + maxdigits ? "10^" : "", + maxdigits ? maxdigits : 1 + ))); + break; + } + ddigits -= DEC_DIGITS; + } + } +} + +/* + * apply_typmod_special() - + * + * Do bounds checking according to the specified typmod, for an Inf or NaN. + * For convenience of most callers, the value is presented in packed form. + */ +static void +apply_typmod_special(Numeric num, int32 typmod) +{ + int precision; + int scale; + + Assert(NUMERIC_IS_SPECIAL(num)); /* caller error if not */ + + /* + * NaN is allowed regardless of the typmod; that's rather dubious perhaps, + * but it's a longstanding behavior. Inf is rejected if we have any + * typmod restriction, since an infinity shouldn't be claimed to fit in + * any finite number of digits. + */ + if (NUMERIC_IS_NAN(num)) + return; + + /* Do nothing if we have a default typmod (-1) */ + if (typmod < (int32) (VARHDRSZ)) + return; + + typmod -= VARHDRSZ; + precision = (typmod >> 16) & 0xffff; + scale = typmod & 0xffff; + + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("numeric field overflow"), + errdetail("A field with precision %d, scale %d cannot hold an infinite value.", + precision, scale))); +} + + +/* + * Convert numeric to int8, rounding if needed. + * + * If overflow, return false (no error is raised). Return true if okay. + */ +static bool +numericvar_to_int64(const NumericVar *var, int64 *result) +{ + NumericDigit *digits; + int ndigits; + int weight; + int i; + int64 val; + bool neg; + NumericVar rounded; + + /* Round to nearest integer */ + init_var(&rounded); + set_var_from_var(var, &rounded); + round_var(&rounded, 0); + + /* Check for zero input */ + strip_var(&rounded); + ndigits = rounded.ndigits; + if (ndigits == 0) + { + *result = 0; + free_var(&rounded); + return true; + } + + /* + * For input like 10000000000, we must treat stripped digits as real. So + * the loop assumes there are weight+1 digits before the decimal point. + */ + weight = rounded.weight; + Assert(weight >= 0 && ndigits <= weight + 1); + + /* + * Construct the result. To avoid issues with converting a value + * corresponding to INT64_MIN (which can't be represented as a positive 64 + * bit two's complement integer), accumulate value as a negative number. + */ + digits = rounded.digits; + neg = (rounded.sign == NUMERIC_NEG); + val = -digits[0]; + for (i = 1; i <= weight; i++) + { + if (unlikely(pg_mul_s64_overflow(val, NBASE, &val))) + { + free_var(&rounded); + return false; + } + + if (i < ndigits) + { + if (unlikely(pg_sub_s64_overflow(val, digits[i], &val))) + { + free_var(&rounded); + return false; + } + } + } + + free_var(&rounded); + + if (!neg) + { + if (unlikely(val == PG_INT64_MIN)) + return false; + val = -val; + } + *result = val; + + return true; +} + +/* + * Convert int8 value to numeric. + */ +static void +int64_to_numericvar(int64 val, NumericVar *var) +{ + uint64 uval, + newuval; + NumericDigit *ptr; + int ndigits; + + /* int64 can require at most 19 decimal digits; add one for safety */ + alloc_var(var, 20 / DEC_DIGITS); + if (val < 0) + { + var->sign = NUMERIC_NEG; + uval = -val; + } + else + { + var->sign = NUMERIC_POS; + uval = val; + } + var->dscale = 0; + if (val == 0) + { + var->ndigits = 0; + var->weight = 0; + return; + } + ptr = var->digits + var->ndigits; + ndigits = 0; + do + { + ptr--; + ndigits++; + newuval = uval / NBASE; + *ptr = uval - newuval * NBASE; + uval = newuval; + } while (uval); + var->digits = ptr; + var->ndigits = ndigits; + var->weight = ndigits - 1; +} + +/* + * Convert numeric to uint64, rounding if needed. + * + * If overflow, return false (no error is raised). Return true if okay. + */ +static bool +numericvar_to_uint64(const NumericVar *var, uint64 *result) +{ + NumericDigit *digits; + int ndigits; + int weight; + int i; + uint64 val; + NumericVar rounded; + + /* Round to nearest integer */ + init_var(&rounded); + set_var_from_var(var, &rounded); + round_var(&rounded, 0); + + /* Check for zero input */ + strip_var(&rounded); + ndigits = rounded.ndigits; + if (ndigits == 0) + { + *result = 0; + free_var(&rounded); + return true; + } + + /* Check for negative input */ + if (rounded.sign == NUMERIC_NEG) + { + free_var(&rounded); + return false; + } + + /* + * For input like 10000000000, we must treat stripped digits as real. So + * the loop assumes there are weight+1 digits before the decimal point. + */ + weight = rounded.weight; + Assert(weight >= 0 && ndigits <= weight + 1); + + /* Construct the result */ + digits = rounded.digits; + val = digits[0]; + for (i = 1; i <= weight; i++) + { + if (unlikely(pg_mul_u64_overflow(val, NBASE, &val))) + { + free_var(&rounded); + return false; + } + + if (i < ndigits) + { + if (unlikely(pg_add_u64_overflow(val, digits[i], &val))) + { + free_var(&rounded); + return false; + } + } + } + + free_var(&rounded); + + *result = val; + + return true; +} + +#ifdef HAVE_INT128 +/* + * Convert numeric to int128, rounding if needed. + * + * If overflow, return false (no error is raised). Return true if okay. + */ +static bool +numericvar_to_int128(const NumericVar *var, int128 *result) +{ + NumericDigit *digits; + int ndigits; + int weight; + int i; + int128 val, + oldval; + bool neg; + NumericVar rounded; + + /* Round to nearest integer */ + init_var(&rounded); + set_var_from_var(var, &rounded); + round_var(&rounded, 0); + + /* Check for zero input */ + strip_var(&rounded); + ndigits = rounded.ndigits; + if (ndigits == 0) + { + *result = 0; + free_var(&rounded); + return true; + } + + /* + * For input like 10000000000, we must treat stripped digits as real. So + * the loop assumes there are weight+1 digits before the decimal point. + */ + weight = rounded.weight; + Assert(weight >= 0 && ndigits <= weight + 1); + + /* Construct the result */ + digits = rounded.digits; + neg = (rounded.sign == NUMERIC_NEG); + val = digits[0]; + for (i = 1; i <= weight; i++) + { + oldval = val; + val *= NBASE; + if (i < ndigits) + val += digits[i]; + + /* + * The overflow check is a bit tricky because we want to accept + * INT128_MIN, which will overflow the positive accumulator. We can + * detect this case easily though because INT128_MIN is the only + * nonzero value for which -val == val (on a two's complement machine, + * anyway). + */ + if ((val / NBASE) != oldval) /* possible overflow? */ + { + if (!neg || (-val) != val || val == 0 || oldval < 0) + { + free_var(&rounded); + return false; + } + } + } + + free_var(&rounded); + + *result = neg ? -val : val; + return true; +} + +/* + * Convert 128 bit integer to numeric. + */ +static void +int128_to_numericvar(int128 val, NumericVar *var) +{ + uint128 uval, + newuval; + NumericDigit *ptr; + int ndigits; + + /* int128 can require at most 39 decimal digits; add one for safety */ + alloc_var(var, 40 / DEC_DIGITS); + if (val < 0) + { + var->sign = NUMERIC_NEG; + uval = -val; + } + else + { + var->sign = NUMERIC_POS; + uval = val; + } + var->dscale = 0; + if (val == 0) + { + var->ndigits = 0; + var->weight = 0; + return; + } + ptr = var->digits + var->ndigits; + ndigits = 0; + do + { + ptr--; + ndigits++; + newuval = uval / NBASE; + *ptr = uval - newuval * NBASE; + uval = newuval; + } while (uval); + var->digits = ptr; + var->ndigits = ndigits; + var->weight = ndigits - 1; +} +#endif + +/* + * Convert a NumericVar to float8; if out of range, return +/- HUGE_VAL + */ +static double +numericvar_to_double_no_overflow(const NumericVar *var) +{ + char *tmp; + double val; + char *endptr; + + tmp = get_str_from_var(var); + + /* unlike float8in, we ignore ERANGE from strtod */ + val = strtod(tmp, &endptr); + if (*endptr != '\0') + { + /* shouldn't happen ... */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "double precision", tmp))); + } + + pfree(tmp); + + return val; +} + + +/* + * cmp_var() - + * + * Compare two values on variable level. We assume zeroes have been + * truncated to no digits. + */ +static int +cmp_var(const NumericVar *var1, const NumericVar *var2) +{ + return cmp_var_common(var1->digits, var1->ndigits, + var1->weight, var1->sign, + var2->digits, var2->ndigits, + var2->weight, var2->sign); +} + +/* + * cmp_var_common() - + * + * Main routine of cmp_var(). This function can be used by both + * NumericVar and Numeric. + */ +static int +cmp_var_common(const NumericDigit *var1digits, int var1ndigits, + int var1weight, int var1sign, + const NumericDigit *var2digits, int var2ndigits, + int var2weight, int var2sign) +{ + if (var1ndigits == 0) + { + if (var2ndigits == 0) + return 0; + if (var2sign == NUMERIC_NEG) + return 1; + return -1; + } + if (var2ndigits == 0) + { + if (var1sign == NUMERIC_POS) + return 1; + return -1; + } + + if (var1sign == NUMERIC_POS) + { + if (var2sign == NUMERIC_NEG) + return 1; + return cmp_abs_common(var1digits, var1ndigits, var1weight, + var2digits, var2ndigits, var2weight); + } + + if (var2sign == NUMERIC_POS) + return -1; + + return cmp_abs_common(var2digits, var2ndigits, var2weight, + var1digits, var1ndigits, var1weight); +} + + +/* + * 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(const NumericVar *var1, const 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 + * ---------- + */ + zero_var(result); + result->dscale = Max(var1->dscale, var2->dscale); + 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 + * ---------- + */ + zero_var(result); + result->dscale = Max(var1->dscale, var2->dscale); + 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(const NumericVar *var1, const 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 + * ---------- + */ + zero_var(result); + result->dscale = Max(var1->dscale, var2->dscale); + 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 + * ---------- + */ + zero_var(result); + result->dscale = Max(var1->dscale, var2->dscale); + 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. Result is rounded to no more than rscale fractional digits. + */ +static void +mul_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result, + int rscale) +{ + int res_ndigits; + int res_sign; + int res_weight; + int maxdigits; + int *dig; + int carry; + int maxdig; + int newdig; + int var1ndigits; + int var2ndigits; + NumericDigit *var1digits; + NumericDigit *var2digits; + NumericDigit *res_digits; + int i, + i1, + i2; + + /* + * Arrange for var1 to be the shorter of the two numbers. This improves + * performance because the inner multiplication loop is much simpler than + * the outer loop, so it's better to have a smaller number of iterations + * of the outer loop. This also reduces the number of times that the + * accumulator array needs to be normalized. + */ + if (var1->ndigits > var2->ndigits) + { + const NumericVar *tmp = var1; + + var1 = var2; + var2 = tmp; + } + + /* copy these values into local vars for speed in inner loop */ + var1ndigits = var1->ndigits; + var2ndigits = var2->ndigits; + var1digits = var1->digits; + var2digits = var2->digits; + + if (var1ndigits == 0 || var2ndigits == 0) + { + /* one or both inputs is zero; so is result */ + zero_var(result); + result->dscale = rscale; + return; + } + + /* Determine result sign and (maximum possible) weight */ + if (var1->sign == var2->sign) + res_sign = NUMERIC_POS; + else + res_sign = NUMERIC_NEG; + res_weight = var1->weight + var2->weight + 2; + + /* + * Determine the number of result digits to compute. If the exact result + * would have more than rscale fractional digits, truncate the computation + * with MUL_GUARD_DIGITS guard digits, i.e., ignore input digits that + * would only contribute to the right of that. (This will give the exact + * rounded-to-rscale answer unless carries out of the ignored positions + * would have propagated through more than MUL_GUARD_DIGITS digits.) + * + * Note: an exact computation could not produce more than var1ndigits + + * var2ndigits digits, but we allocate one extra output digit in case + * rscale-driven rounding produces a carry out of the highest exact digit. + */ + res_ndigits = var1ndigits + var2ndigits + 1; + maxdigits = res_weight + 1 + (rscale + DEC_DIGITS - 1) / DEC_DIGITS + + MUL_GUARD_DIGITS; + res_ndigits = Min(res_ndigits, maxdigits); + + if (res_ndigits < 3) + { + /* All input digits will be ignored; so result is zero */ + zero_var(result); + result->dscale = rscale; + return; + } + + /* + * We do the arithmetic in an array "dig[]" of signed int's. Since + * INT_MAX is noticeably larger than NBASE*NBASE, this gives us headroom + * to avoid normalizing carries immediately. + * + * maxdig tracks the maximum possible value of any dig[] entry; when this + * threatens to exceed INT_MAX, we take the time to propagate carries. + * Furthermore, we need to ensure that overflow doesn't occur during the + * carry propagation passes either. The carry values could be as much as + * INT_MAX/NBASE, so really we must normalize when digits threaten to + * exceed INT_MAX - INT_MAX/NBASE. + * + * To avoid overflow in maxdig itself, it actually represents the max + * possible value divided by NBASE-1, ie, at the top of the loop it is + * known that no dig[] entry exceeds maxdig * (NBASE-1). + */ + dig = (int *) palloc0(res_ndigits * sizeof(int)); + maxdig = 0; + + /* + * The least significant digits of var1 should be ignored if they don't + * contribute directly to the first res_ndigits digits of the result that + * we are computing. + * + * Digit i1 of var1 and digit i2 of var2 are multiplied and added to digit + * i1+i2+2 of the accumulator array, so we need only consider digits of + * var1 for which i1 <= res_ndigits - 3. + */ + for (i1 = Min(var1ndigits - 1, res_ndigits - 3); i1 >= 0; i1--) + { + int var1digit = var1digits[i1]; + + if (var1digit == 0) + continue; + + /* Time to normalize? */ + maxdig += var1digit; + if (maxdig > (INT_MAX - INT_MAX / NBASE) / (NBASE - 1)) + { + /* Yes, do it */ + carry = 0; + for (i = res_ndigits - 1; i >= 0; i--) + { + newdig = dig[i] + carry; + if (newdig >= NBASE) + { + carry = newdig / NBASE; + newdig -= carry * NBASE; + } + else + carry = 0; + dig[i] = newdig; + } + Assert(carry == 0); + /* Reset maxdig to indicate new worst-case */ + maxdig = 1 + var1digit; + } + + /* + * Add the appropriate multiple of var2 into the accumulator. + * + * As above, digits of var2 can be ignored if they don't contribute, + * so we only include digits for which i1+i2+2 < res_ndigits. + * + * This inner loop is the performance bottleneck for multiplication, + * so we want to keep it simple enough so that it can be + * auto-vectorized. Accordingly, process the digits left-to-right + * even though schoolbook multiplication would suggest right-to-left. + * Since we aren't propagating carries in this loop, the order does + * not matter. + */ + { + int i2limit = Min(var2ndigits, res_ndigits - i1 - 2); + int *dig_i1_2 = &dig[i1 + 2]; + + for (i2 = 0; i2 < i2limit; i2++) + dig_i1_2[i2] += var1digit * var2digits[i2]; + } + } + + /* + * Now we do a final carry propagation pass to normalize the result, which + * we combine with storing the result digits into the output. Note that + * this is still done at full precision w/guard digits. + */ + alloc_var(result, res_ndigits); + res_digits = result->digits; + carry = 0; + for (i = res_ndigits - 1; i >= 0; i--) + { + newdig = dig[i] + carry; + if (newdig >= NBASE) + { + carry = newdig / NBASE; + newdig -= carry * NBASE; + } + else + carry = 0; + res_digits[i] = newdig; + } + Assert(carry == 0); + + pfree(dig); + + /* + * Finally, round the result to the requested precision. + */ + result->weight = res_weight; + result->sign = res_sign; + + /* Round to target rscale (and set result->dscale) */ + round_var(result, rscale); + + /* Strip leading and trailing zeroes */ + strip_var(result); +} + + +/* + * div_var() - + * + * Division on variable level. Quotient of var1 / var2 is stored in result. + * The quotient is figured to exactly rscale fractional digits. + * If round is true, it is rounded at the rscale'th digit; if false, it + * is truncated (towards zero) at that digit. + */ +static void +div_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result, + int rscale, bool round) +{ + int div_ndigits; + int res_ndigits; + int res_sign; + int res_weight; + int carry; + int borrow; + int divisor1; + int divisor2; + NumericDigit *dividend; + NumericDigit *divisor; + NumericDigit *res_digits; + int i; + int j; + + /* copy these values into local vars for speed in inner loop */ + int var1ndigits = var1->ndigits; + int var2ndigits = var2->ndigits; + + /* + * First of all division by zero check; we must not be handed an + * unnormalized divisor. + */ + if (var2ndigits == 0 || var2->digits[0] == 0) + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + + /* + * Now result zero check + */ + if (var1ndigits == 0) + { + zero_var(result); + result->dscale = rscale; + return; + } + + /* + * Determine the result sign, weight and number of digits to calculate. + * The weight figured here is correct if the emitted quotient has no + * leading zero digits; otherwise strip_var() will fix things up. + */ + if (var1->sign == var2->sign) + res_sign = NUMERIC_POS; + else + res_sign = NUMERIC_NEG; + res_weight = var1->weight - var2->weight; + /* The number of accurate result digits we need to produce: */ + res_ndigits = res_weight + 1 + (rscale + DEC_DIGITS - 1) / DEC_DIGITS; + /* ... but always at least 1 */ + res_ndigits = Max(res_ndigits, 1); + /* If rounding needed, figure one more digit to ensure correct result */ + if (round) + res_ndigits++; + + /* + * The working dividend normally requires res_ndigits + var2ndigits + * digits, but make it at least var1ndigits so we can load all of var1 + * into it. (There will be an additional digit dividend[0] in the + * dividend space, but for consistency with Knuth's notation we don't + * count that in div_ndigits.) + */ + div_ndigits = res_ndigits + var2ndigits; + div_ndigits = Max(div_ndigits, var1ndigits); + + /* + * We need a workspace with room for the working dividend (div_ndigits+1 + * digits) plus room for the possibly-normalized divisor (var2ndigits + * digits). It is convenient also to have a zero at divisor[0] with the + * actual divisor data in divisor[1 .. var2ndigits]. Transferring the + * digits into the workspace also allows us to realloc the result (which + * might be the same as either input var) before we begin the main loop. + * Note that we use palloc0 to ensure that divisor[0], dividend[0], and + * any additional dividend positions beyond var1ndigits, start out 0. + */ + dividend = (NumericDigit *) + palloc0((div_ndigits + var2ndigits + 2) * sizeof(NumericDigit)); + divisor = dividend + (div_ndigits + 1); + memcpy(dividend + 1, var1->digits, var1ndigits * sizeof(NumericDigit)); + memcpy(divisor + 1, var2->digits, var2ndigits * sizeof(NumericDigit)); + + /* + * Now we can realloc the result to hold the generated quotient digits. + */ + alloc_var(result, res_ndigits); + res_digits = result->digits; + + if (var2ndigits == 1) + { + /* + * If there's only a single divisor digit, we can use a fast path (cf. + * Knuth section 4.3.1 exercise 16). + */ + divisor1 = divisor[1]; + carry = 0; + for (i = 0; i < res_ndigits; i++) + { + carry = carry * NBASE + dividend[i + 1]; + res_digits[i] = carry / divisor1; + carry = carry % divisor1; + } + } + else + { + /* + * The full multiple-place algorithm is taken from Knuth volume 2, + * Algorithm 4.3.1D. + * + * We need the first divisor digit to be >= NBASE/2. If it isn't, + * make it so by scaling up both the divisor and dividend by the + * factor "d". (The reason for allocating dividend[0] above is to + * leave room for possible carry here.) + */ + if (divisor[1] < HALF_NBASE) + { + int d = NBASE / (divisor[1] + 1); + + carry = 0; + for (i = var2ndigits; i > 0; i--) + { + carry += divisor[i] * d; + divisor[i] = carry % NBASE; + carry = carry / NBASE; + } + Assert(carry == 0); + carry = 0; + /* at this point only var1ndigits of dividend can be nonzero */ + for (i = var1ndigits; i >= 0; i--) + { + carry += dividend[i] * d; + dividend[i] = carry % NBASE; + carry = carry / NBASE; + } + Assert(carry == 0); + Assert(divisor[1] >= HALF_NBASE); + } + /* First 2 divisor digits are used repeatedly in main loop */ + divisor1 = divisor[1]; + divisor2 = divisor[2]; + + /* + * Begin the main loop. Each iteration of this loop produces the j'th + * quotient digit by dividing dividend[j .. j + var2ndigits] by the + * divisor; this is essentially the same as the common manual + * procedure for long division. + */ + for (j = 0; j < res_ndigits; j++) + { + /* Estimate quotient digit from the first two dividend digits */ + int next2digits = dividend[j] * NBASE + dividend[j + 1]; + int qhat; + + /* + * If next2digits are 0, then quotient digit must be 0 and there's + * no need to adjust the working dividend. It's worth testing + * here to fall out ASAP when processing trailing zeroes in a + * dividend. + */ + if (next2digits == 0) + { + res_digits[j] = 0; + continue; + } + + if (dividend[j] == divisor1) + qhat = NBASE - 1; + else + qhat = next2digits / divisor1; + + /* + * Adjust quotient digit if it's too large. Knuth proves that + * after this step, the quotient digit will be either correct or + * just one too large. (Note: it's OK to use dividend[j+2] here + * because we know the divisor length is at least 2.) + */ + while (divisor2 * qhat > + (next2digits - qhat * divisor1) * NBASE + dividend[j + 2]) + qhat--; + + /* As above, need do nothing more when quotient digit is 0 */ + if (qhat > 0) + { + /* + * Multiply the divisor by qhat, and subtract that from the + * working dividend. "carry" tracks the multiplication, + * "borrow" the subtraction (could we fold these together?) + */ + carry = 0; + borrow = 0; + for (i = var2ndigits; i >= 0; i--) + { + carry += divisor[i] * qhat; + borrow -= carry % NBASE; + carry = carry / NBASE; + borrow += dividend[j + i]; + if (borrow < 0) + { + dividend[j + i] = borrow + NBASE; + borrow = -1; + } + else + { + dividend[j + i] = borrow; + borrow = 0; + } + } + Assert(carry == 0); + + /* + * If we got a borrow out of the top dividend digit, then + * indeed qhat was one too large. Fix it, and add back the + * divisor to correct the working dividend. (Knuth proves + * that this will occur only about 3/NBASE of the time; hence, + * it's a good idea to test this code with small NBASE to be + * sure this section gets exercised.) + */ + if (borrow) + { + qhat--; + carry = 0; + for (i = var2ndigits; i >= 0; i--) + { + carry += dividend[j + i] + divisor[i]; + if (carry >= NBASE) + { + dividend[j + i] = carry - NBASE; + carry = 1; + } + else + { + dividend[j + i] = carry; + carry = 0; + } + } + /* A carry should occur here to cancel the borrow above */ + Assert(carry == 1); + } + } + + /* And we're done with this quotient digit */ + res_digits[j] = qhat; + } + } + + pfree(dividend); + + /* + * Finally, round or truncate the result to the requested precision. + */ + result->weight = res_weight; + result->sign = res_sign; + + /* Round or truncate to target rscale (and set result->dscale) */ + if (round) + round_var(result, rscale); + else + trunc_var(result, rscale); + + /* Strip leading and trailing zeroes */ + strip_var(result); +} + + +/* + * div_var_fast() - + * + * This has the same API as div_var, but is implemented using the division + * algorithm from the "FM" library, rather than Knuth's schoolbook-division + * approach. This is significantly faster but can produce inaccurate + * results, because it sometimes has to propagate rounding to the left, + * and so we can never be entirely sure that we know the requested digits + * exactly. We compute DIV_GUARD_DIGITS extra digits, but there is + * no certainty that that's enough. We use this only in the transcendental + * function calculation routines, where everything is approximate anyway. + * + * Although we provide a "round" argument for consistency with div_var, + * it is unwise to use this function with round=false. In truncation mode + * it is possible to get a result with no significant digits, for example + * with rscale=0 we might compute 0.99999... and truncate that to 0 when + * the correct answer is 1. + */ +static void +div_var_fast(const NumericVar *var1, const NumericVar *var2, + NumericVar *result, int rscale, bool round) +{ + int div_ndigits; + int load_ndigits; + int res_sign; + int res_weight; + int *div; + int qdigit; + int carry; + int maxdiv; + int newdig; + NumericDigit *res_digits; + double fdividend, + fdivisor, + fdivisorinverse, + fquotient; + int qi; + int i; + + /* copy these values into local vars for speed in inner loop */ + int var1ndigits = var1->ndigits; + int var2ndigits = var2->ndigits; + NumericDigit *var1digits = var1->digits; + NumericDigit *var2digits = var2->digits; + + /* + * First of all division by zero check; we must not be handed an + * unnormalized divisor. + */ + if (var2ndigits == 0 || var2digits[0] == 0) + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + + /* + * Now result zero check + */ + if (var1ndigits == 0) + { + zero_var(result); + result->dscale = rscale; + return; + } + + /* + * 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; + /* The number of accurate result digits we need to produce: */ + div_ndigits = res_weight + 1 + (rscale + DEC_DIGITS - 1) / DEC_DIGITS; + /* Add guard digits for roundoff error */ + div_ndigits += DIV_GUARD_DIGITS; + if (div_ndigits < DIV_GUARD_DIGITS) + div_ndigits = DIV_GUARD_DIGITS; + + /* + * We do the arithmetic in an array "div[]" of signed int's. Since + * INT_MAX is noticeably larger than NBASE*NBASE, this gives us headroom + * to avoid normalizing carries immediately. + * + * We start with div[] containing one zero digit followed by the + * dividend's digits (plus appended zeroes to reach the desired precision + * including guard digits). Each step of the main loop computes an + * (approximate) quotient digit and stores it into div[], removing one + * position of dividend space. A final pass of carry propagation takes + * care of any mistaken quotient digits. + * + * Note that div[] doesn't necessarily contain all of the digits from the + * dividend --- the desired precision plus guard digits might be less than + * the dividend's precision. This happens, for example, in the square + * root algorithm, where we typically divide a 2N-digit number by an + * N-digit number, and only require a result with N digits of precision. + */ + div = (int *) palloc0((div_ndigits + 1) * sizeof(int)); + load_ndigits = Min(div_ndigits, var1ndigits); + for (i = 0; i < load_ndigits; i++) + div[i + 1] = var1digits[i]; + + /* + * We estimate each quotient digit using floating-point arithmetic, taking + * the first four digits of the (current) dividend and divisor. This must + * be float to avoid overflow. The quotient digits will generally be off + * by no more than one from the exact answer. + */ + fdivisor = (double) var2digits[0]; + for (i = 1; i < 4; i++) + { + fdivisor *= NBASE; + if (i < var2ndigits) + fdivisor += (double) var2digits[i]; + } + fdivisorinverse = 1.0 / fdivisor; + + /* + * maxdiv tracks the maximum possible absolute value of any div[] entry; + * when this threatens to exceed INT_MAX, we take the time to propagate + * carries. Furthermore, we need to ensure that overflow doesn't occur + * during the carry propagation passes either. The carry values may have + * an absolute value as high as INT_MAX/NBASE + 1, so really we must + * normalize when digits threaten to exceed INT_MAX - INT_MAX/NBASE - 1. + * + * To avoid overflow in maxdiv itself, it represents the max absolute + * value divided by NBASE-1, ie, at the top of the loop it is known that + * no div[] entry has an absolute value exceeding maxdiv * (NBASE-1). + * + * Actually, though, that holds good only for div[] entries after div[qi]; + * the adjustment done at the bottom of the loop may cause div[qi + 1] to + * exceed the maxdiv limit, so that div[qi] in the next iteration is + * beyond the limit. This does not cause problems, as explained below. + */ + maxdiv = 1; + + /* + * Outer loop computes next quotient digit, which will go into div[qi] + */ + for (qi = 0; qi < div_ndigits; qi++) + { + /* Approximate the current dividend value */ + fdividend = (double) div[qi]; + for (i = 1; i < 4; i++) + { + fdividend *= NBASE; + if (qi + i <= div_ndigits) + fdividend += (double) div[qi + i]; + } + /* Compute the (approximate) quotient digit */ + fquotient = fdividend * fdivisorinverse; + qdigit = (fquotient >= 0.0) ? ((int) fquotient) : + (((int) fquotient) - 1); /* truncate towards -infinity */ + + if (qdigit != 0) + { + /* Do we need to normalize now? */ + maxdiv += Abs(qdigit); + if (maxdiv > (INT_MAX - INT_MAX / NBASE - 1) / (NBASE - 1)) + { + /* + * Yes, do it. Note that if var2ndigits is much smaller than + * div_ndigits, we can save a significant amount of effort + * here by noting that we only need to normalise those div[] + * entries touched where prior iterations subtracted multiples + * of the divisor. + */ + carry = 0; + for (i = Min(qi + var2ndigits - 2, div_ndigits); i > qi; i--) + { + newdig = div[i] + carry; + if (newdig < 0) + { + carry = -((-newdig - 1) / NBASE) - 1; + newdig -= carry * NBASE; + } + else if (newdig >= NBASE) + { + carry = newdig / NBASE; + newdig -= carry * NBASE; + } + else + carry = 0; + div[i] = newdig; + } + newdig = div[qi] + carry; + div[qi] = newdig; + + /* + * All the div[] digits except possibly div[qi] are now in the + * range 0..NBASE-1. We do not need to consider div[qi] in + * the maxdiv value anymore, so we can reset maxdiv to 1. + */ + maxdiv = 1; + + /* + * Recompute the quotient digit since new info may have + * propagated into the top four dividend digits + */ + fdividend = (double) div[qi]; + for (i = 1; i < 4; i++) + { + fdividend *= NBASE; + if (qi + i <= div_ndigits) + fdividend += (double) div[qi + i]; + } + /* Compute the (approximate) quotient digit */ + fquotient = fdividend * fdivisorinverse; + qdigit = (fquotient >= 0.0) ? ((int) fquotient) : + (((int) fquotient) - 1); /* truncate towards -infinity */ + maxdiv += Abs(qdigit); + } + + /* + * Subtract off the appropriate multiple of the divisor. + * + * The digits beyond div[qi] cannot overflow, because we know they + * will fall within the maxdiv limit. As for div[qi] itself, note + * that qdigit is approximately trunc(div[qi] / vardigits[0]), + * which would make the new value simply div[qi] mod vardigits[0]. + * The lower-order terms in qdigit can change this result by not + * more than about twice INT_MAX/NBASE, so overflow is impossible. + */ + if (qdigit != 0) + { + int istop = Min(var2ndigits, div_ndigits - qi + 1); + + for (i = 0; i < istop; i++) + div[qi + i] -= qdigit * var2digits[i]; + } + } + + /* + * The dividend digit we are about to replace might still be nonzero. + * Fold it into the next digit position. + * + * There is no risk of overflow here, although proving that requires + * some care. Much as with the argument for div[qi] not overflowing, + * if we consider the first two terms in the numerator and denominator + * of qdigit, we can see that the final value of div[qi + 1] will be + * approximately a remainder mod (vardigits[0]*NBASE + vardigits[1]). + * Accounting for the lower-order terms is a bit complicated but ends + * up adding not much more than INT_MAX/NBASE to the possible range. + * Thus, div[qi + 1] cannot overflow here, and in its role as div[qi] + * in the next loop iteration, it can't be large enough to cause + * overflow in the carry propagation step (if any), either. + * + * But having said that: div[qi] can be more than INT_MAX/NBASE, as + * noted above, which means that the product div[qi] * NBASE *can* + * overflow. When that happens, adding it to div[qi + 1] will always + * cause a canceling overflow so that the end result is correct. We + * could avoid the intermediate overflow by doing the multiplication + * and addition in int64 arithmetic, but so far there appears no need. + */ + div[qi + 1] += div[qi] * NBASE; + + div[qi] = qdigit; + } + + /* + * Approximate and store the last quotient digit (div[div_ndigits]) + */ + fdividend = (double) div[qi]; + for (i = 1; i < 4; i++) + fdividend *= NBASE; + fquotient = fdividend * fdivisorinverse; + qdigit = (fquotient >= 0.0) ? ((int) fquotient) : + (((int) fquotient) - 1); /* truncate towards -infinity */ + div[qi] = qdigit; + + /* + * Because the quotient digits might be off by one, some of them might be + * -1 or NBASE at this point. The represented value is correct in a + * mathematical sense, but it doesn't look right. We do a final carry + * propagation pass to normalize the digits, which we combine with storing + * the result digits into the output. Note that this is still done at + * full precision w/guard digits. + */ + alloc_var(result, div_ndigits + 1); + res_digits = result->digits; + carry = 0; + for (i = div_ndigits; i >= 0; i--) + { + newdig = div[i] + carry; + if (newdig < 0) + { + carry = -((-newdig - 1) / NBASE) - 1; + newdig -= carry * NBASE; + } + else if (newdig >= NBASE) + { + carry = newdig / NBASE; + newdig -= carry * NBASE; + } + else + carry = 0; + res_digits[i] = newdig; + } + Assert(carry == 0); + + pfree(div); + + /* + * Finally, round the result to the requested precision. + */ + result->weight = res_weight; + result->sign = res_sign; + + /* Round to target rscale (and set result->dscale) */ + if (round) + round_var(result, rscale); + else + trunc_var(result, rscale); + + /* Strip leading and trailing zeroes */ + strip_var(result); +} + + +/* + * Default scale selection for division + * + * Returns the appropriate result scale for the division result. + */ +static int +select_div_scale(const NumericVar *var1, const NumericVar *var2) +{ + int weight1, + weight2, + qweight, + i; + NumericDigit firstdigit1, + firstdigit2; + int rscale; + + /* + * The result scale of a division isn't specified in any SQL standard. For + * PostgreSQL we select a result scale that will give at least + * NUMERIC_MIN_SIG_DIGITS significant digits, so that numeric gives a + * result no less accurate than float8; but use a scale not less than + * either input's display scale. + */ + + /* Get the actual (normalized) weight and first digit of each input */ + + weight1 = 0; /* values to use if var1 is zero */ + firstdigit1 = 0; + for (i = 0; i < var1->ndigits; i++) + { + firstdigit1 = var1->digits[i]; + if (firstdigit1 != 0) + { + weight1 = var1->weight - i; + break; + } + } + + weight2 = 0; /* values to use if var2 is zero */ + firstdigit2 = 0; + for (i = 0; i < var2->ndigits; i++) + { + firstdigit2 = var2->digits[i]; + if (firstdigit2 != 0) + { + weight2 = var2->weight - i; + break; + } + } + + /* + * Estimate weight of quotient. If the two first digits are equal, we + * can't be sure, but assume that var1 is less than var2. + */ + qweight = weight1 - weight2; + if (firstdigit1 <= firstdigit2) + qweight--; + + /* Select result scale */ + rscale = NUMERIC_MIN_SIG_DIGITS - qweight * DEC_DIGITS; + rscale = Max(rscale, var1->dscale); + rscale = Max(rscale, var2->dscale); + rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE); + rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE); + + return rscale; +} + + +/* + * mod_var() - + * + * Calculate the modulo of two numerics at variable level + */ +static void +mod_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result) +{ + NumericVar tmp; + + init_var(&tmp); + + /* --------- + * We do this using the equation + * mod(x,y) = x - trunc(x/y)*y + * div_var can be persuaded to give us trunc(x/y) directly. + * ---------- + */ + div_var(var1, var2, &tmp, 0, false); + + mul_var(var2, &tmp, &tmp, var2->dscale); + + sub_var(var1, &tmp, result); + + free_var(&tmp); +} + + +/* + * div_mod_var() - + * + * Calculate the truncated integer quotient and numeric remainder of two + * numeric variables. The remainder is precise to var2's dscale. + */ +static void +div_mod_var(const NumericVar *var1, const NumericVar *var2, + NumericVar *quot, NumericVar *rem) +{ + NumericVar q; + NumericVar r; + + init_var(&q); + init_var(&r); + + /* + * Use div_var_fast() to get an initial estimate for the integer quotient. + * This might be inaccurate (per the warning in div_var_fast's comments), + * but we can correct it below. + */ + div_var_fast(var1, var2, &q, 0, false); + + /* Compute initial estimate of remainder using the quotient estimate. */ + mul_var(var2, &q, &r, var2->dscale); + sub_var(var1, &r, &r); + + /* + * Adjust the results if necessary --- the remainder should have the same + * sign as var1, and its absolute value should be less than the absolute + * value of var2. + */ + while (r.ndigits != 0 && r.sign != var1->sign) + { + /* The absolute value of the quotient is too large */ + if (var1->sign == var2->sign) + { + sub_var(&q, &const_one, &q); + add_var(&r, var2, &r); + } + else + { + add_var(&q, &const_one, &q); + sub_var(&r, var2, &r); + } + } + + while (cmp_abs(&r, var2) >= 0) + { + /* The absolute value of the quotient is too small */ + if (var1->sign == var2->sign) + { + add_var(&q, &const_one, &q); + sub_var(&r, var2, &r); + } + else + { + sub_var(&q, &const_one, &q); + add_var(&r, var2, &r); + } + } + + set_var_from_var(&q, quot); + set_var_from_var(&r, rem); + + free_var(&q); + free_var(&r); +} + + +/* + * ceil_var() - + * + * Return the smallest integer greater than or equal to the argument + * on variable level + */ +static void +ceil_var(const NumericVar *var, NumericVar *result) +{ + NumericVar tmp; + + init_var(&tmp); + set_var_from_var(var, &tmp); + + trunc_var(&tmp, 0); + + if (var->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(const NumericVar *var, NumericVar *result) +{ + NumericVar tmp; + + init_var(&tmp); + set_var_from_var(var, &tmp); + + trunc_var(&tmp, 0); + + if (var->sign == NUMERIC_NEG && cmp_var(var, &tmp) != 0) + sub_var(&tmp, &const_one, &tmp); + + set_var_from_var(&tmp, result); + free_var(&tmp); +} + + +/* + * gcd_var() - + * + * Calculate the greatest common divisor of two numerics at variable level + */ +static void +gcd_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result) +{ + int res_dscale; + int cmp; + NumericVar tmp_arg; + NumericVar mod; + + res_dscale = Max(var1->dscale, var2->dscale); + + /* + * Arrange for var1 to be the number with the greater absolute value. + * + * This would happen automatically in the loop below, but avoids an + * expensive modulo operation. + */ + cmp = cmp_abs(var1, var2); + if (cmp < 0) + { + const NumericVar *tmp = var1; + + var1 = var2; + var2 = tmp; + } + + /* + * Also avoid the taking the modulo if the inputs have the same absolute + * value, or if the smaller input is zero. + */ + if (cmp == 0 || var2->ndigits == 0) + { + set_var_from_var(var1, result); + result->sign = NUMERIC_POS; + result->dscale = res_dscale; + return; + } + + init_var(&tmp_arg); + init_var(&mod); + + /* Use the Euclidean algorithm to find the GCD */ + set_var_from_var(var1, &tmp_arg); + set_var_from_var(var2, result); + + for (;;) + { + /* this loop can take a while, so allow it to be interrupted */ + CHECK_FOR_INTERRUPTS(); + + mod_var(&tmp_arg, result, &mod); + if (mod.ndigits == 0) + break; + set_var_from_var(result, &tmp_arg); + set_var_from_var(&mod, result); + } + result->sign = NUMERIC_POS; + result->dscale = res_dscale; + + free_var(&tmp_arg); + free_var(&mod); +} + + +/* + * sqrt_var() - + * + * Compute the square root of x using the Karatsuba Square Root algorithm. + * NOTE: we allow rscale < 0 here, implying rounding before the decimal + * point. + */ +static void +sqrt_var(const NumericVar *arg, NumericVar *result, int rscale) +{ + int stat; + int res_weight; + int res_ndigits; + int src_ndigits; + int step; + int ndigits[32]; + int blen; + int64 arg_int64; + int src_idx; + int64 s_int64; + int64 r_int64; + NumericVar s_var; + NumericVar r_var; + NumericVar a0_var; + NumericVar a1_var; + NumericVar q_var; + NumericVar u_var; + + stat = cmp_var(arg, &const_zero); + if (stat == 0) + { + zero_var(result); + result->dscale = rscale; + return; + } + + /* + * SQL2003 defines sqrt() in terms of power, so we need to emit the right + * SQLSTATE error code if the operand is negative. + */ + if (stat < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION), + errmsg("cannot take square root of a negative number"))); + + init_var(&s_var); + init_var(&r_var); + init_var(&a0_var); + init_var(&a1_var); + init_var(&q_var); + init_var(&u_var); + + /* + * The result weight is half the input weight, rounded towards minus + * infinity --- res_weight = floor(arg->weight / 2). + */ + if (arg->weight >= 0) + res_weight = arg->weight / 2; + else + res_weight = -((-arg->weight - 1) / 2 + 1); + + /* + * Number of NBASE digits to compute. To ensure correct rounding, compute + * at least 1 extra decimal digit. We explicitly allow rscale to be + * negative here, but must always compute at least 1 NBASE digit. Thus + * res_ndigits = res_weight + 1 + ceil((rscale + 1) / DEC_DIGITS) or 1. + */ + if (rscale + 1 >= 0) + res_ndigits = res_weight + 1 + (rscale + DEC_DIGITS) / DEC_DIGITS; + else + res_ndigits = res_weight + 1 - (-rscale - 1) / DEC_DIGITS; + res_ndigits = Max(res_ndigits, 1); + + /* + * Number of source NBASE digits logically required to produce a result + * with this precision --- every digit before the decimal point, plus 2 + * for each result digit after the decimal point (or minus 2 for each + * result digit we round before the decimal point). + */ + src_ndigits = arg->weight + 1 + (res_ndigits - res_weight - 1) * 2; + src_ndigits = Max(src_ndigits, 1); + + /* ---------- + * From this point on, we treat the input and the result as integers and + * compute the integer square root and remainder using the Karatsuba + * Square Root algorithm, which may be written recursively as follows: + * + * SqrtRem(n = a3*b^3 + a2*b^2 + a1*b + a0): + * [ for some base b, and coefficients a0,a1,a2,a3 chosen so that + * 0 <= a0,a1,a2 < b and a3 >= b/4 ] + * Let (s,r) = SqrtRem(a3*b + a2) + * Let (q,u) = DivRem(r*b + a1, 2*s) + * Let s = s*b + q + * Let r = u*b + a0 - q^2 + * If r < 0 Then + * Let r = r + s + * Let s = s - 1 + * Let r = r + s + * Return (s,r) + * + * See "Karatsuba Square Root", Paul Zimmermann, INRIA Research Report + * RR-3805, November 1999. At the time of writing this was available + * on the net at <https://hal.inria.fr/inria-00072854>. + * + * The way to read the assumption "n = a3*b^3 + a2*b^2 + a1*b + a0" is + * "choose a base b such that n requires at least four base-b digits to + * express; then those digits are a3,a2,a1,a0, with a3 possibly larger + * than b". For optimal performance, b should have approximately a + * quarter the number of digits in the input, so that the outer square + * root computes roughly twice as many digits as the inner one. For + * simplicity, we choose b = NBASE^blen, an integer power of NBASE. + * + * We implement the algorithm iteratively rather than recursively, to + * allow the working variables to be reused. With this approach, each + * digit of the input is read precisely once --- src_idx tracks the number + * of input digits used so far. + * + * The array ndigits[] holds the number of NBASE digits of the input that + * will have been used at the end of each iteration, which roughly doubles + * each time. Note that the array elements are stored in reverse order, + * so if the final iteration requires src_ndigits = 37 input digits, the + * array will contain [37,19,11,7,5,3], and we would start by computing + * the square root of the 3 most significant NBASE digits. + * + * In each iteration, we choose blen to be the largest integer for which + * the input number has a3 >= b/4, when written in the form above. In + * general, this means blen = src_ndigits / 4 (truncated), but if + * src_ndigits is a multiple of 4, that might lead to the coefficient a3 + * being less than b/4 (if the first input digit is less than NBASE/4), in + * which case we choose blen = src_ndigits / 4 - 1. The number of digits + * in the inner square root is then src_ndigits - 2*blen. So, for + * example, if we have src_ndigits = 26 initially, the array ndigits[] + * will be either [26,14,8,4] or [26,14,8,6,4], depending on the size of + * the first input digit. + * + * Additionally, we can put an upper bound on the number of steps required + * as follows --- suppose that the number of source digits is an n-bit + * number in the range [2^(n-1), 2^n-1], then blen will be in the range + * [2^(n-3)-1, 2^(n-2)-1] and the number of digits in the inner square + * root will be in the range [2^(n-2), 2^(n-1)+1]. In the next step, blen + * will be in the range [2^(n-4)-1, 2^(n-3)] and the number of digits in + * the next inner square root will be in the range [2^(n-3), 2^(n-2)+1]. + * This pattern repeats, and in the worst case the array ndigits[] will + * contain [2^n-1, 2^(n-1)+1, 2^(n-2)+1, ... 9, 5, 3], and the computation + * will require n steps. Therefore, since all digit array sizes are + * signed 32-bit integers, the number of steps required is guaranteed to + * be less than 32. + * ---------- + */ + step = 0; + while ((ndigits[step] = src_ndigits) > 4) + { + /* Choose b so that a3 >= b/4, as described above */ + blen = src_ndigits / 4; + if (blen * 4 == src_ndigits && arg->digits[0] < NBASE / 4) + blen--; + + /* Number of digits in the next step (inner square root) */ + src_ndigits -= 2 * blen; + step++; + } + + /* + * First iteration (innermost square root and remainder): + * + * Here src_ndigits <= 4, and the input fits in an int64. Its square root + * has at most 9 decimal digits, so estimate it using double precision + * arithmetic, which will in fact almost certainly return the correct + * result with no further correction required. + */ + arg_int64 = arg->digits[0]; + for (src_idx = 1; src_idx < src_ndigits; src_idx++) + { + arg_int64 *= NBASE; + if (src_idx < arg->ndigits) + arg_int64 += arg->digits[src_idx]; + } + + s_int64 = (int64) sqrt((double) arg_int64); + r_int64 = arg_int64 - s_int64 * s_int64; + + /* + * Use Newton's method to correct the result, if necessary. + * + * This uses integer division with truncation to compute the truncated + * integer square root by iterating using the formula x -> (x + n/x) / 2. + * This is known to converge to isqrt(n), unless n+1 is a perfect square. + * If n+1 is a perfect square, the sequence will oscillate between the two + * values isqrt(n) and isqrt(n)+1, so we can be assured of convergence by + * checking the remainder. + */ + while (r_int64 < 0 || r_int64 > 2 * s_int64) + { + s_int64 = (s_int64 + arg_int64 / s_int64) / 2; + r_int64 = arg_int64 - s_int64 * s_int64; + } + + /* + * Iterations with src_ndigits <= 8: + * + * The next 1 or 2 iterations compute larger (outer) square roots with + * src_ndigits <= 8, so the result still fits in an int64 (even though the + * input no longer does) and we can continue to compute using int64 + * variables to avoid more expensive numeric computations. + * + * It is fairly easy to see that there is no risk of the intermediate + * values below overflowing 64-bit integers. In the worst case, the + * previous iteration will have computed a 3-digit square root (of a + * 6-digit input less than NBASE^6 / 4), so at the start of this + * iteration, s will be less than NBASE^3 / 2 = 10^12 / 2, and r will be + * less than 10^12. In this case, blen will be 1, so numer will be less + * than 10^17, and denom will be less than 10^12 (and hence u will also be + * less than 10^12). Finally, since q^2 = u*b + a0 - r, we can also be + * sure that q^2 < 10^17. Therefore all these quantities fit comfortably + * in 64-bit integers. + */ + step--; + while (step >= 0 && (src_ndigits = ndigits[step]) <= 8) + { + int b; + int a0; + int a1; + int i; + int64 numer; + int64 denom; + int64 q; + int64 u; + + blen = (src_ndigits - src_idx) / 2; + + /* Extract a1 and a0, and compute b */ + a0 = 0; + a1 = 0; + b = 1; + + for (i = 0; i < blen; i++, src_idx++) + { + b *= NBASE; + a1 *= NBASE; + if (src_idx < arg->ndigits) + a1 += arg->digits[src_idx]; + } + + for (i = 0; i < blen; i++, src_idx++) + { + a0 *= NBASE; + if (src_idx < arg->ndigits) + a0 += arg->digits[src_idx]; + } + + /* Compute (q,u) = DivRem(r*b + a1, 2*s) */ + numer = r_int64 * b + a1; + denom = 2 * s_int64; + q = numer / denom; + u = numer - q * denom; + + /* Compute s = s*b + q and r = u*b + a0 - q^2 */ + s_int64 = s_int64 * b + q; + r_int64 = u * b + a0 - q * q; + + if (r_int64 < 0) + { + /* s is too large by 1; set r += s, s--, r += s */ + r_int64 += s_int64; + s_int64--; + r_int64 += s_int64; + } + + Assert(src_idx == src_ndigits); /* All input digits consumed */ + step--; + } + + /* + * On platforms with 128-bit integer support, we can further delay the + * need to use numeric variables. + */ +#ifdef HAVE_INT128 + if (step >= 0) + { + int128 s_int128; + int128 r_int128; + + s_int128 = s_int64; + r_int128 = r_int64; + + /* + * Iterations with src_ndigits <= 16: + * + * The result fits in an int128 (even though the input doesn't) so we + * use int128 variables to avoid more expensive numeric computations. + */ + while (step >= 0 && (src_ndigits = ndigits[step]) <= 16) + { + int64 b; + int64 a0; + int64 a1; + int64 i; + int128 numer; + int128 denom; + int128 q; + int128 u; + + blen = (src_ndigits - src_idx) / 2; + + /* Extract a1 and a0, and compute b */ + a0 = 0; + a1 = 0; + b = 1; + + for (i = 0; i < blen; i++, src_idx++) + { + b *= NBASE; + a1 *= NBASE; + if (src_idx < arg->ndigits) + a1 += arg->digits[src_idx]; + } + + for (i = 0; i < blen; i++, src_idx++) + { + a0 *= NBASE; + if (src_idx < arg->ndigits) + a0 += arg->digits[src_idx]; + } + + /* Compute (q,u) = DivRem(r*b + a1, 2*s) */ + numer = r_int128 * b + a1; + denom = 2 * s_int128; + q = numer / denom; + u = numer - q * denom; + + /* Compute s = s*b + q and r = u*b + a0 - q^2 */ + s_int128 = s_int128 * b + q; + r_int128 = u * b + a0 - q * q; + + if (r_int128 < 0) + { + /* s is too large by 1; set r += s, s--, r += s */ + r_int128 += s_int128; + s_int128--; + r_int128 += s_int128; + } + + Assert(src_idx == src_ndigits); /* All input digits consumed */ + step--; + } + + /* + * All remaining iterations require numeric variables. Convert the + * integer values to NumericVar and continue. Note that in the final + * iteration we don't need the remainder, so we can save a few cycles + * there by not fully computing it. + */ + int128_to_numericvar(s_int128, &s_var); + if (step >= 0) + int128_to_numericvar(r_int128, &r_var); + } + else + { + int64_to_numericvar(s_int64, &s_var); + /* step < 0, so we certainly don't need r */ + } +#else /* !HAVE_INT128 */ + int64_to_numericvar(s_int64, &s_var); + if (step >= 0) + int64_to_numericvar(r_int64, &r_var); +#endif /* HAVE_INT128 */ + + /* + * The remaining iterations with src_ndigits > 8 (or 16, if have int128) + * use numeric variables. + */ + while (step >= 0) + { + int tmp_len; + + src_ndigits = ndigits[step]; + blen = (src_ndigits - src_idx) / 2; + + /* Extract a1 and a0 */ + if (src_idx < arg->ndigits) + { + tmp_len = Min(blen, arg->ndigits - src_idx); + alloc_var(&a1_var, tmp_len); + memcpy(a1_var.digits, arg->digits + src_idx, + tmp_len * sizeof(NumericDigit)); + a1_var.weight = blen - 1; + a1_var.sign = NUMERIC_POS; + a1_var.dscale = 0; + strip_var(&a1_var); + } + else + { + zero_var(&a1_var); + a1_var.dscale = 0; + } + src_idx += blen; + + if (src_idx < arg->ndigits) + { + tmp_len = Min(blen, arg->ndigits - src_idx); + alloc_var(&a0_var, tmp_len); + memcpy(a0_var.digits, arg->digits + src_idx, + tmp_len * sizeof(NumericDigit)); + a0_var.weight = blen - 1; + a0_var.sign = NUMERIC_POS; + a0_var.dscale = 0; + strip_var(&a0_var); + } + else + { + zero_var(&a0_var); + a0_var.dscale = 0; + } + src_idx += blen; + + /* Compute (q,u) = DivRem(r*b + a1, 2*s) */ + set_var_from_var(&r_var, &q_var); + q_var.weight += blen; + add_var(&q_var, &a1_var, &q_var); + add_var(&s_var, &s_var, &u_var); + div_mod_var(&q_var, &u_var, &q_var, &u_var); + + /* Compute s = s*b + q */ + s_var.weight += blen; + add_var(&s_var, &q_var, &s_var); + + /* + * Compute r = u*b + a0 - q^2. + * + * In the final iteration, we don't actually need r; we just need to + * know whether it is negative, so that we know whether to adjust s. + * So instead of the final subtraction we can just compare. + */ + u_var.weight += blen; + add_var(&u_var, &a0_var, &u_var); + mul_var(&q_var, &q_var, &q_var, 0); + + if (step > 0) + { + /* Need r for later iterations */ + sub_var(&u_var, &q_var, &r_var); + if (r_var.sign == NUMERIC_NEG) + { + /* s is too large by 1; set r += s, s--, r += s */ + add_var(&r_var, &s_var, &r_var); + sub_var(&s_var, &const_one, &s_var); + add_var(&r_var, &s_var, &r_var); + } + } + else + { + /* Don't need r anymore, except to test if s is too large by 1 */ + if (cmp_var(&u_var, &q_var) < 0) + sub_var(&s_var, &const_one, &s_var); + } + + Assert(src_idx == src_ndigits); /* All input digits consumed */ + step--; + } + + /* + * Construct the final result, rounding it to the requested precision. + */ + set_var_from_var(&s_var, result); + result->weight = res_weight; + result->sign = NUMERIC_POS; + + /* Round to target rscale (and set result->dscale) */ + round_var(result, rscale); + + /* Strip leading and trailing zeroes */ + strip_var(result); + + free_var(&s_var); + free_var(&r_var); + free_var(&a0_var); + free_var(&a1_var); + free_var(&q_var); + free_var(&u_var); +} + + +/* + * exp_var() - + * + * Raise e to the power of x, computed to rscale fractional digits + */ +static void +exp_var(const NumericVar *arg, NumericVar *result, int rscale) +{ + NumericVar x; + NumericVar elem; + NumericVar ni; + double val; + int dweight; + int ndiv2; + int sig_digits; + int local_rscale; + + init_var(&x); + init_var(&elem); + init_var(&ni); + + set_var_from_var(arg, &x); + + /* + * Estimate the dweight of the result using floating point arithmetic, so + * that we can choose an appropriate local rscale for the calculation. + */ + val = numericvar_to_double_no_overflow(&x); + + /* Guard against overflow/underflow */ + /* If you change this limit, see also power_var()'s limit */ + if (Abs(val) >= NUMERIC_MAX_RESULT_SCALE * 3) + { + if (val > 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value overflows numeric format"))); + zero_var(result); + result->dscale = rscale; + return; + } + + /* decimal weight = log10(e^x) = x * log10(e) */ + dweight = (int) (val * 0.434294481903252); + + /* + * Reduce x to the range -0.01 <= x <= 0.01 (approximately) by dividing by + * 2^n, to improve the convergence rate of the Taylor series. + */ + if (Abs(val) > 0.01) + { + NumericVar tmp; + + init_var(&tmp); + set_var_from_var(&const_two, &tmp); + + ndiv2 = 1; + val /= 2; + + while (Abs(val) > 0.01) + { + ndiv2++; + val /= 2; + add_var(&tmp, &tmp, &tmp); + } + + local_rscale = x.dscale + ndiv2; + div_var_fast(&x, &tmp, &x, local_rscale, true); + + free_var(&tmp); + } + else + ndiv2 = 0; + + /* + * Set the scale for the Taylor series expansion. The final result has + * (dweight + rscale + 1) significant digits. In addition, we have to + * raise the Taylor series result to the power 2^ndiv2, which introduces + * an error of up to around log10(2^ndiv2) digits, so work with this many + * extra digits of precision (plus a few more for good measure). + */ + sig_digits = 1 + dweight + rscale + (int) (ndiv2 * 0.301029995663981); + sig_digits = Max(sig_digits, 0) + 8; + + local_rscale = sig_digits - 1; + + /* + * Use the Taylor series + * + * exp(x) = 1 + x + x^2/2! + x^3/3! + ... + * + * Given the limited range of x, this should converge reasonably quickly. + * We run the series until the terms fall below the local_rscale limit. + */ + add_var(&const_one, &x, result); + + mul_var(&x, &x, &elem, local_rscale); + set_var_from_var(&const_two, &ni); + div_var_fast(&elem, &ni, &elem, local_rscale, true); + + while (elem.ndigits != 0) + { + add_var(result, &elem, result); + + mul_var(&elem, &x, &elem, local_rscale); + add_var(&ni, &const_one, &ni); + div_var_fast(&elem, &ni, &elem, local_rscale, true); + } + + /* + * Compensate for the argument range reduction. Since the weight of the + * result doubles with each multiplication, we can reduce the local rscale + * as we proceed. + */ + while (ndiv2-- > 0) + { + local_rscale = sig_digits - result->weight * 2 * DEC_DIGITS; + local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE); + mul_var(result, result, result, local_rscale); + } + + /* Round to requested rscale */ + round_var(result, rscale); + + free_var(&x); + free_var(&elem); + free_var(&ni); +} + + +/* + * Estimate the dweight of the most significant decimal digit of the natural + * logarithm of a number. + * + * Essentially, we're approximating log10(abs(ln(var))). This is used to + * determine the appropriate rscale when computing natural logarithms. + * + * Note: many callers call this before range-checking the input. Therefore, + * we must be robust against values that are invalid to apply ln() to. + * We don't wish to throw an error here, so just return zero in such cases. + */ +static int +estimate_ln_dweight(const NumericVar *var) +{ + int ln_dweight; + + /* Caller should fail on ln(negative), but for the moment return zero */ + if (var->sign != NUMERIC_POS) + return 0; + + if (cmp_var(var, &const_zero_point_nine) >= 0 && + cmp_var(var, &const_one_point_one) <= 0) + { + /* + * 0.9 <= var <= 1.1 + * + * ln(var) has a negative weight (possibly very large). To get a + * reasonably accurate result, estimate it using ln(1+x) ~= x. + */ + NumericVar x; + + init_var(&x); + sub_var(var, &const_one, &x); + + if (x.ndigits > 0) + { + /* Use weight of most significant decimal digit of x */ + ln_dweight = x.weight * DEC_DIGITS + (int) log10(x.digits[0]); + } + else + { + /* x = 0. Since ln(1) = 0 exactly, we don't need extra digits */ + ln_dweight = 0; + } + + free_var(&x); + } + else + { + /* + * Estimate the logarithm using the first couple of digits from the + * input number. This will give an accurate result whenever the input + * is not too close to 1. + */ + if (var->ndigits > 0) + { + int digits; + int dweight; + double ln_var; + + digits = var->digits[0]; + dweight = var->weight * DEC_DIGITS; + + if (var->ndigits > 1) + { + digits = digits * NBASE + var->digits[1]; + dweight -= DEC_DIGITS; + } + + /*---------- + * We have var ~= digits * 10^dweight + * so ln(var) ~= ln(digits) + dweight * ln(10) + *---------- + */ + ln_var = log((double) digits) + dweight * 2.302585092994046; + ln_dweight = (int) log10(Abs(ln_var)); + } + else + { + /* Caller should fail on ln(0), but for the moment return zero */ + ln_dweight = 0; + } + } + + return ln_dweight; +} + + +/* + * ln_var() - + * + * Compute the natural log of x + */ +static void +ln_var(const NumericVar *arg, NumericVar *result, int rscale) +{ + NumericVar x; + NumericVar xx; + NumericVar ni; + NumericVar elem; + NumericVar fact; + int nsqrt; + int local_rscale; + int cmp; + + cmp = cmp_var(arg, &const_zero); + if (cmp == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG), + errmsg("cannot take logarithm of zero"))); + else if (cmp < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG), + errmsg("cannot take logarithm of a negative number"))); + + init_var(&x); + init_var(&xx); + init_var(&ni); + init_var(&elem); + init_var(&fact); + + set_var_from_var(arg, &x); + set_var_from_var(&const_two, &fact); + + /* + * Reduce input into range 0.9 < x < 1.1 with repeated sqrt() operations. + * + * The final logarithm will have up to around rscale+6 significant digits. + * Each sqrt() will roughly halve the weight of x, so adjust the local + * rscale as we work so that we keep this many significant digits at each + * step (plus a few more for good measure). + * + * Note that we allow local_rscale < 0 during this input reduction + * process, which implies rounding before the decimal point. sqrt_var() + * explicitly supports this, and it significantly reduces the work + * required to reduce very large inputs to the required range. Once the + * input reduction is complete, x.weight will be 0 and its display scale + * will be non-negative again. + */ + nsqrt = 0; + while (cmp_var(&x, &const_zero_point_nine) <= 0) + { + local_rscale = rscale - x.weight * DEC_DIGITS / 2 + 8; + sqrt_var(&x, &x, local_rscale); + mul_var(&fact, &const_two, &fact, 0); + nsqrt++; + } + while (cmp_var(&x, &const_one_point_one) >= 0) + { + local_rscale = rscale - x.weight * DEC_DIGITS / 2 + 8; + sqrt_var(&x, &x, local_rscale); + mul_var(&fact, &const_two, &fact, 0); + nsqrt++; + } + + /* + * We use the Taylor series for 0.5 * ln((1+z)/(1-z)), + * + * z + z^3/3 + z^5/5 + ... + * + * where z = (x-1)/(x+1) is in the range (approximately) -0.053 .. 0.048 + * due to the above range-reduction of x. + * + * The convergence of this is not as fast as one would like, but is + * tolerable given that z is small. + * + * The Taylor series result will be multiplied by 2^(nsqrt+1), which has a + * decimal weight of (nsqrt+1) * log10(2), so work with this many extra + * digits of precision (plus a few more for good measure). + */ + local_rscale = rscale + (int) ((nsqrt + 1) * 0.301029995663981) + 8; + + sub_var(&x, &const_one, result); + add_var(&x, &const_one, &elem); + div_var_fast(result, &elem, result, local_rscale, true); + set_var_from_var(result, &xx); + mul_var(result, result, &x, local_rscale); + + set_var_from_var(&const_one, &ni); + + for (;;) + { + add_var(&ni, &const_two, &ni); + mul_var(&xx, &x, &xx, local_rscale); + div_var_fast(&xx, &ni, &elem, local_rscale, true); + + if (elem.ndigits == 0) + break; + + add_var(result, &elem, result); + + if (elem.weight < (result->weight - local_rscale * 2 / DEC_DIGITS)) + break; + } + + /* Compensate for argument range reduction, round to requested rscale */ + mul_var(result, &fact, result, rscale); + + free_var(&x); + free_var(&xx); + free_var(&ni); + free_var(&elem); + free_var(&fact); +} + + +/* + * log_var() - + * + * Compute the logarithm of num in a given base. + * + * Note: this routine chooses dscale of the result. + */ +static void +log_var(const NumericVar *base, const NumericVar *num, NumericVar *result) +{ + NumericVar ln_base; + NumericVar ln_num; + int ln_base_dweight; + int ln_num_dweight; + int result_dweight; + int rscale; + int ln_base_rscale; + int ln_num_rscale; + + init_var(&ln_base); + init_var(&ln_num); + + /* Estimated dweights of ln(base), ln(num) and the final result */ + ln_base_dweight = estimate_ln_dweight(base); + ln_num_dweight = estimate_ln_dweight(num); + result_dweight = ln_num_dweight - ln_base_dweight; + + /* + * Select the scale of the result so that it will have at least + * NUMERIC_MIN_SIG_DIGITS significant digits and is not less than either + * input's display scale. + */ + rscale = NUMERIC_MIN_SIG_DIGITS - result_dweight; + rscale = Max(rscale, base->dscale); + rscale = Max(rscale, num->dscale); + rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE); + rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE); + + /* + * Set the scales for ln(base) and ln(num) so that they each have more + * significant digits than the final result. + */ + ln_base_rscale = rscale + result_dweight - ln_base_dweight + 8; + ln_base_rscale = Max(ln_base_rscale, NUMERIC_MIN_DISPLAY_SCALE); + + ln_num_rscale = rscale + result_dweight - ln_num_dweight + 8; + ln_num_rscale = Max(ln_num_rscale, NUMERIC_MIN_DISPLAY_SCALE); + + /* Form natural logarithms */ + ln_var(base, &ln_base, ln_base_rscale); + ln_var(num, &ln_num, ln_num_rscale); + + /* Divide and round to the required scale */ + div_var_fast(&ln_num, &ln_base, result, rscale, true); + + free_var(&ln_num); + free_var(&ln_base); +} + + +/* + * power_var() - + * + * Raise base to the power of exp + * + * Note: this routine chooses dscale of the result. + */ +static void +power_var(const NumericVar *base, const NumericVar *exp, NumericVar *result) +{ + int res_sign; + NumericVar abs_base; + NumericVar ln_base; + NumericVar ln_num; + int ln_dweight; + int rscale; + int sig_digits; + int local_rscale; + double val; + + /* If exp can be represented as an integer, use power_var_int */ + if (exp->ndigits == 0 || exp->ndigits <= exp->weight + 1) + { + /* exact integer, but does it fit in int? */ + int64 expval64; + + if (numericvar_to_int64(exp, &expval64)) + { + if (expval64 >= PG_INT32_MIN && expval64 <= PG_INT32_MAX) + { + /* Okay, select rscale */ + rscale = NUMERIC_MIN_SIG_DIGITS; + rscale = Max(rscale, base->dscale); + rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE); + rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE); + + power_var_int(base, (int) expval64, result, rscale); + return; + } + } + } + + /* + * This avoids log(0) for cases of 0 raised to a non-integer. 0 ^ 0 is + * handled by power_var_int(). + */ + if (cmp_var(base, &const_zero) == 0) + { + set_var_from_var(&const_zero, result); + result->dscale = NUMERIC_MIN_SIG_DIGITS; /* no need to round */ + return; + } + + init_var(&abs_base); + init_var(&ln_base); + init_var(&ln_num); + + /* + * If base is negative, insist that exp be an integer. The result is then + * positive if exp is even and negative if exp is odd. + */ + if (base->sign == NUMERIC_NEG) + { + /* + * Check that exp is an integer. This error code is defined by the + * SQL standard, and matches other errors in numeric_power(). + */ + if (exp->ndigits > 0 && exp->ndigits > exp->weight + 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION), + errmsg("a negative number raised to a non-integer power yields a complex result"))); + + /* Test if exp is odd or even */ + if (exp->ndigits > 0 && exp->ndigits == exp->weight + 1 && + (exp->digits[exp->ndigits - 1] & 1)) + res_sign = NUMERIC_NEG; + else + res_sign = NUMERIC_POS; + + /* Then work with abs(base) below */ + set_var_from_var(base, &abs_base); + abs_base.sign = NUMERIC_POS; + base = &abs_base; + } + else + res_sign = NUMERIC_POS; + + /*---------- + * Decide on the scale for the ln() calculation. For this we need an + * estimate of the weight of the result, which we obtain by doing an + * initial low-precision calculation of exp * ln(base). + * + * We want result = e ^ (exp * ln(base)) + * so result dweight = log10(result) = exp * ln(base) * log10(e) + * + * We also perform a crude overflow test here so that we can exit early if + * the full-precision result is sure to overflow, and to guard against + * integer overflow when determining the scale for the real calculation. + * exp_var() supports inputs up to NUMERIC_MAX_RESULT_SCALE * 3, so the + * result will overflow if exp * ln(base) >= NUMERIC_MAX_RESULT_SCALE * 3. + * Since the values here are only approximations, we apply a small fuzz + * factor to this overflow test and let exp_var() determine the exact + * overflow threshold so that it is consistent for all inputs. + *---------- + */ + ln_dweight = estimate_ln_dweight(base); + + /* + * Set the scale for the low-precision calculation, computing ln(base) to + * around 8 significant digits. Note that ln_dweight may be as small as + * -SHRT_MAX, so the scale may exceed NUMERIC_MAX_DISPLAY_SCALE here. + */ + local_rscale = 8 - ln_dweight; + local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE); + + ln_var(base, &ln_base, local_rscale); + + mul_var(&ln_base, exp, &ln_num, local_rscale); + + val = numericvar_to_double_no_overflow(&ln_num); + + /* initial overflow/underflow test with fuzz factor */ + if (Abs(val) > NUMERIC_MAX_RESULT_SCALE * 3.01) + { + if (val > 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value overflows numeric format"))); + zero_var(result); + result->dscale = NUMERIC_MAX_DISPLAY_SCALE; + return; + } + + val *= 0.434294481903252; /* approximate decimal result weight */ + + /* choose the result scale */ + rscale = NUMERIC_MIN_SIG_DIGITS - (int) val; + rscale = Max(rscale, base->dscale); + rscale = Max(rscale, exp->dscale); + rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE); + rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE); + + /* significant digits required in the result */ + sig_digits = rscale + (int) val; + sig_digits = Max(sig_digits, 0); + + /* set the scale for the real exp * ln(base) calculation */ + local_rscale = sig_digits - ln_dweight + 8; + local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE); + + /* and do the real calculation */ + + ln_var(base, &ln_base, local_rscale); + + mul_var(&ln_base, exp, &ln_num, local_rscale); + + exp_var(&ln_num, result, rscale); + + if (res_sign == NUMERIC_NEG && result->ndigits > 0) + result->sign = NUMERIC_NEG; + + free_var(&ln_num); + free_var(&ln_base); + free_var(&abs_base); +} + +/* + * power_var_int() - + * + * Raise base to the power of exp, where exp is an integer. + */ +static void +power_var_int(const NumericVar *base, int exp, NumericVar *result, int rscale) +{ + double f; + int p; + int i; + int sig_digits; + unsigned int mask; + bool neg; + NumericVar base_prod; + int local_rscale; + + /* Handle some common special cases, as well as corner cases */ + switch (exp) + { + case 0: + + /* + * While 0 ^ 0 can be either 1 or indeterminate (error), we treat + * it as 1 because most programming languages do this. SQL:2003 + * also requires a return value of 1. + * https://en.wikipedia.org/wiki/Exponentiation#Zero_to_the_zero_power + */ + set_var_from_var(&const_one, result); + result->dscale = rscale; /* no need to round */ + return; + case 1: + set_var_from_var(base, result); + round_var(result, rscale); + return; + case -1: + div_var(&const_one, base, result, rscale, true); + return; + case 2: + mul_var(base, base, result, rscale); + return; + default: + break; + } + + /* Handle the special case where the base is zero */ + if (base->ndigits == 0) + { + if (exp < 0) + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + zero_var(result); + result->dscale = rscale; + return; + } + + /* + * The general case repeatedly multiplies base according to the bit + * pattern of exp. + * + * First we need to estimate the weight of the result so that we know how + * many significant digits are needed. + */ + f = base->digits[0]; + p = base->weight * DEC_DIGITS; + + for (i = 1; i < base->ndigits && i * DEC_DIGITS < 16; i++) + { + f = f * NBASE + base->digits[i]; + p -= DEC_DIGITS; + } + + /*---------- + * We have base ~= f * 10^p + * so log10(result) = log10(base^exp) ~= exp * (log10(f) + p) + *---------- + */ + f = exp * (log10(f) + p); + + /* + * Apply crude overflow/underflow tests so we can exit early if the result + * certainly will overflow/underflow. + */ + if (f > 3 * SHRT_MAX * DEC_DIGITS) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value overflows numeric format"))); + if (f + 1 < -rscale || f + 1 < -NUMERIC_MAX_DISPLAY_SCALE) + { + zero_var(result); + result->dscale = rscale; + return; + } + + /* + * Approximate number of significant digits in the result. Note that the + * underflow test above means that this is necessarily >= 0. + */ + sig_digits = 1 + rscale + (int) f; + + /* + * The multiplications to produce the result may introduce an error of up + * to around log10(abs(exp)) digits, so work with this many extra digits + * of precision (plus a few more for good measure). + */ + sig_digits += (int) log(fabs((double) exp)) + 8; + + /* + * Now we can proceed with the multiplications. + */ + neg = (exp < 0); + mask = Abs(exp); + + init_var(&base_prod); + set_var_from_var(base, &base_prod); + + if (mask & 1) + set_var_from_var(base, result); + else + set_var_from_var(&const_one, result); + + while ((mask >>= 1) > 0) + { + /* + * Do the multiplications using rscales large enough to hold the + * results to the required number of significant digits, but don't + * waste time by exceeding the scales of the numbers themselves. + */ + local_rscale = sig_digits - 2 * base_prod.weight * DEC_DIGITS; + local_rscale = Min(local_rscale, 2 * base_prod.dscale); + local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE); + + mul_var(&base_prod, &base_prod, &base_prod, local_rscale); + + if (mask & 1) + { + local_rscale = sig_digits - + (base_prod.weight + result->weight) * DEC_DIGITS; + local_rscale = Min(local_rscale, + base_prod.dscale + result->dscale); + local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE); + + mul_var(&base_prod, result, result, local_rscale); + } + + /* + * When abs(base) > 1, the number of digits to the left of the decimal + * point in base_prod doubles at each iteration, so if exp is large we + * could easily spend large amounts of time and memory space doing the + * multiplications. But once the weight exceeds what will fit in + * int16, the final result is guaranteed to overflow (or underflow, if + * exp < 0), so we can give up before wasting too many cycles. + */ + if (base_prod.weight > SHRT_MAX || result->weight > SHRT_MAX) + { + /* overflow, unless neg, in which case result should be 0 */ + if (!neg) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value overflows numeric format"))); + zero_var(result); + neg = false; + break; + } + } + + free_var(&base_prod); + + /* Compensate for input sign, and round to requested rscale */ + if (neg) + div_var_fast(&const_one, result, result, rscale, true); + else + round_var(result, rscale); +} + +/* + * power_ten_int() - + * + * Raise ten to the power of exp, where exp is an integer. Note that unlike + * power_var_int(), this does no overflow/underflow checking or rounding. + */ +static void +power_ten_int(int exp, NumericVar *result) +{ + /* Construct the result directly, starting from 10^0 = 1 */ + set_var_from_var(&const_one, result); + + /* Scale needed to represent the result exactly */ + result->dscale = exp < 0 ? -exp : 0; + + /* Base-NBASE weight of result and remaining exponent */ + if (exp >= 0) + result->weight = exp / DEC_DIGITS; + else + result->weight = (exp + 1) / DEC_DIGITS - 1; + + exp -= result->weight * DEC_DIGITS; + + /* Final adjustment of the result's single NBASE digit */ + while (exp-- > 0) + result->digits[0] *= 10; +} + + +/* ---------------------------------------------------------------------- + * + * 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(const NumericVar *var1, const NumericVar *var2) +{ + return cmp_abs_common(var1->digits, var1->ndigits, var1->weight, + var2->digits, var2->ndigits, var2->weight); +} + +/* ---------- + * cmp_abs_common() - + * + * Main routine of cmp_abs(). This function can be used by both + * NumericVar and Numeric. + * ---------- + */ +static int +cmp_abs_common(const NumericDigit *var1digits, int var1ndigits, int var1weight, + const NumericDigit *var2digits, int var2ndigits, int var2weight) +{ + int i1 = 0; + int i2 = 0; + + /* Check any digits before the first common digit */ + + while (var1weight > var2weight && i1 < var1ndigits) + { + if (var1digits[i1++] != 0) + return 1; + var1weight--; + } + while (var2weight > var1weight && i2 < var2ndigits) + { + if (var2digits[i2++] != 0) + return -1; + var2weight--; + } + + /* At this point, either w1 == w2 or we've run out of digits */ + + if (var1weight == var2weight) + { + while (i1 < var1ndigits && i2 < var2ndigits) + { + int stat = var1digits[i1++] - var2digits[i2++]; + + if (stat) + { + if (stat > 0) + return 1; + return -1; + } + } + } + + /* + * At this point, we've run out of digits on one side or the other; so any + * remaining nonzero digits imply that side is larger + */ + while (i1 < var1ndigits) + { + if (var1digits[i1++] != 0) + return 1; + } + while (i2 < var2ndigits) + { + if (var2digits[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(const NumericVar *var1, const NumericVar *var2, NumericVar *result) +{ + NumericDigit *res_buf; + NumericDigit *res_digits; + int res_ndigits; + int res_weight; + int res_rscale, + rscale1, + rscale2; + int res_dscale; + int i, + i1, + i2; + int carry = 0; + + /* copy these values into local vars for speed in inner loop */ + int var1ndigits = var1->ndigits; + int var2ndigits = var2->ndigits; + NumericDigit *var1digits = var1->digits; + NumericDigit *var2digits = var2->digits; + + res_weight = Max(var1->weight, var2->weight) + 1; + + res_dscale = Max(var1->dscale, var2->dscale); + + /* Note: here we are figuring rscale in base-NBASE digits */ + rscale1 = var1->ndigits - var1->weight - 1; + rscale2 = var2->ndigits - var2->weight - 1; + res_rscale = Max(rscale1, rscale2); + + res_ndigits = res_rscale + res_weight + 1; + if (res_ndigits <= 0) + res_ndigits = 1; + + res_buf = digitbuf_alloc(res_ndigits + 1); + res_buf[0] = 0; /* spare digit for later rounding */ + res_digits = res_buf + 1; + + 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 < var1ndigits) + carry += var1digits[i1]; + if (i2 >= 0 && i2 < var2ndigits) + carry += var2digits[i2]; + + if (carry >= NBASE) + { + res_digits[i] = carry - NBASE; + carry = 1; + } + else + { + res_digits[i] = carry; + carry = 0; + } + } + + Assert(carry == 0); /* else we failed to allow for carry out */ + + digitbuf_free(result->buf); + result->ndigits = res_ndigits; + result->buf = res_buf; + result->digits = res_digits; + result->weight = res_weight; + result->dscale = res_dscale; + + /* Remove leading/trailing zeroes */ + strip_var(result); +} + + +/* + * 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(const NumericVar *var1, const NumericVar *var2, NumericVar *result) +{ + NumericDigit *res_buf; + NumericDigit *res_digits; + int res_ndigits; + int res_weight; + int res_rscale, + rscale1, + rscale2; + int res_dscale; + int i, + i1, + i2; + int borrow = 0; + + /* copy these values into local vars for speed in inner loop */ + int var1ndigits = var1->ndigits; + int var2ndigits = var2->ndigits; + NumericDigit *var1digits = var1->digits; + NumericDigit *var2digits = var2->digits; + + res_weight = var1->weight; + + res_dscale = Max(var1->dscale, var2->dscale); + + /* Note: here we are figuring rscale in base-NBASE digits */ + rscale1 = var1->ndigits - var1->weight - 1; + rscale2 = var2->ndigits - var2->weight - 1; + res_rscale = Max(rscale1, rscale2); + + res_ndigits = res_rscale + res_weight + 1; + if (res_ndigits <= 0) + res_ndigits = 1; + + res_buf = digitbuf_alloc(res_ndigits + 1); + res_buf[0] = 0; /* spare digit for later rounding */ + res_digits = res_buf + 1; + + 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 < var1ndigits) + borrow += var1digits[i1]; + if (i2 >= 0 && i2 < var2ndigits) + borrow -= var2digits[i2]; + + if (borrow < 0) + { + res_digits[i] = borrow + NBASE; + borrow = -1; + } + else + { + res_digits[i] = borrow; + borrow = 0; + } + } + + Assert(borrow == 0); /* else caller gave us var1 < var2 */ + + digitbuf_free(result->buf); + result->ndigits = res_ndigits; + result->buf = res_buf; + result->digits = res_digits; + result->weight = res_weight; + result->dscale = res_dscale; + + /* Remove leading/trailing zeroes */ + strip_var(result); +} + +/* + * round_var + * + * Round the value of a variable to no more than rscale decimal digits + * after the decimal point. NOTE: we allow rscale < 0 here, implying + * rounding before the decimal point. + */ +static void +round_var(NumericVar *var, int rscale) +{ + NumericDigit *digits = var->digits; + int di; + int ndigits; + int carry; + + var->dscale = rscale; + + /* decimal digits wanted */ + di = (var->weight + 1) * DEC_DIGITS + rscale; + + /* + * If di = 0, the value loses all digits, but could round up to 1 if its + * first extra digit is >= 5. If di < 0 the result must be 0. + */ + if (di < 0) + { + var->ndigits = 0; + var->weight = 0; + var->sign = NUMERIC_POS; + } + else + { + /* NBASE digits wanted */ + ndigits = (di + DEC_DIGITS - 1) / DEC_DIGITS; + + /* 0, or number of decimal digits to keep in last NBASE digit */ + di %= DEC_DIGITS; + + if (ndigits < var->ndigits || + (ndigits == var->ndigits && di > 0)) + { + var->ndigits = ndigits; + +#if DEC_DIGITS == 1 + /* di must be zero */ + carry = (digits[ndigits] >= HALF_NBASE) ? 1 : 0; +#else + if (di == 0) + carry = (digits[ndigits] >= HALF_NBASE) ? 1 : 0; + else + { + /* Must round within last NBASE digit */ + int extra, + pow10; + +#if DEC_DIGITS == 4 + pow10 = round_powers[di]; +#elif DEC_DIGITS == 2 + pow10 = 10; +#else +#error unsupported NBASE +#endif + extra = digits[--ndigits] % pow10; + digits[ndigits] -= extra; + carry = 0; + if (extra >= pow10 / 2) + { + pow10 += digits[ndigits]; + if (pow10 >= NBASE) + { + pow10 -= NBASE; + carry = 1; + } + digits[ndigits] = pow10; + } + } +#endif + + /* Propagate carry if needed */ + while (carry) + { + carry += digits[--ndigits]; + if (carry >= NBASE) + { + digits[ndigits] = carry - NBASE; + carry = 1; + } + else + { + digits[ndigits] = carry; + carry = 0; + } + } + + if (ndigits < 0) + { + Assert(ndigits == -1); /* better not have added > 1 digit */ + Assert(var->digits > var->buf); + var->digits--; + var->ndigits++; + var->weight++; + } + } + } +} + +/* + * trunc_var + * + * Truncate (towards zero) the value of a variable at rscale decimal digits + * after the decimal point. NOTE: we allow rscale < 0 here, implying + * truncation before the decimal point. + */ +static void +trunc_var(NumericVar *var, int rscale) +{ + int di; + int ndigits; + + var->dscale = rscale; + + /* decimal digits wanted */ + di = (var->weight + 1) * DEC_DIGITS + rscale; + + /* + * If di <= 0, the value loses all digits. + */ + if (di <= 0) + { + var->ndigits = 0; + var->weight = 0; + var->sign = NUMERIC_POS; + } + else + { + /* NBASE digits wanted */ + ndigits = (di + DEC_DIGITS - 1) / DEC_DIGITS; + + if (ndigits <= var->ndigits) + { + var->ndigits = ndigits; + +#if DEC_DIGITS == 1 + /* no within-digit stuff to worry about */ +#else + /* 0, or number of decimal digits to keep in last NBASE digit */ + di %= DEC_DIGITS; + + if (di > 0) + { + /* Must truncate within last NBASE digit */ + NumericDigit *digits = var->digits; + int extra, + pow10; + +#if DEC_DIGITS == 4 + pow10 = round_powers[di]; +#elif DEC_DIGITS == 2 + pow10 = 10; +#else +#error unsupported NBASE +#endif + extra = digits[--ndigits] % pow10; + digits[ndigits] -= extra; + } +#endif + } + } +} + +/* + * strip_var + * + * Strip any leading and trailing zeroes from a numeric variable + */ +static void +strip_var(NumericVar *var) +{ + NumericDigit *digits = var->digits; + int ndigits = var->ndigits; + + /* Strip leading zeroes */ + while (ndigits > 0 && *digits == 0) + { + digits++; + var->weight--; + ndigits--; + } + + /* Strip trailing zeroes */ + while (ndigits > 0 && digits[ndigits - 1] == 0) + ndigits--; + + /* If it's zero, normalize the sign and weight */ + if (ndigits == 0) + { + var->sign = NUMERIC_POS; + var->weight = 0; + } + + var->digits = digits; + var->ndigits = ndigits; +} + + +/* ---------------------------------------------------------------------- + * + * Fast sum accumulator functions + * + * ---------------------------------------------------------------------- + */ + +/* + * Reset the accumulator's value to zero. The buffers to hold the digits + * are not free'd. + */ +static void +accum_sum_reset(NumericSumAccum *accum) +{ + int i; + + accum->dscale = 0; + for (i = 0; i < accum->ndigits; i++) + { + accum->pos_digits[i] = 0; + accum->neg_digits[i] = 0; + } +} + +/* + * Accumulate a new value. + */ +static void +accum_sum_add(NumericSumAccum *accum, const NumericVar *val) +{ + int32 *accum_digits; + int i, + val_i; + int val_ndigits; + NumericDigit *val_digits; + + /* + * If we have accumulated too many values since the last carry + * propagation, do it now, to avoid overflowing. (We could allow more + * than NBASE - 1, if we reserved two extra digits, rather than one, for + * carry propagation. But even with NBASE - 1, this needs to be done so + * seldom, that the performance difference is negligible.) + */ + if (accum->num_uncarried == NBASE - 1) + accum_sum_carry(accum); + + /* + * Adjust the weight or scale of the old value, so that it can accommodate + * the new value. + */ + accum_sum_rescale(accum, val); + + /* */ + if (val->sign == NUMERIC_POS) + accum_digits = accum->pos_digits; + else + accum_digits = accum->neg_digits; + + /* copy these values into local vars for speed in loop */ + val_ndigits = val->ndigits; + val_digits = val->digits; + + i = accum->weight - val->weight; + for (val_i = 0; val_i < val_ndigits; val_i++) + { + accum_digits[i] += (int32) val_digits[val_i]; + i++; + } + + accum->num_uncarried++; +} + +/* + * Propagate carries. + */ +static void +accum_sum_carry(NumericSumAccum *accum) +{ + int i; + int ndigits; + int32 *dig; + int32 carry; + int32 newdig = 0; + + /* + * If no new values have been added since last carry propagation, nothing + * to do. + */ + if (accum->num_uncarried == 0) + return; + + /* + * We maintain that the weight of the accumulator is always one larger + * than needed to hold the current value, before carrying, to make sure + * there is enough space for the possible extra digit when carry is + * propagated. We cannot expand the buffer here, unless we require + * callers of accum_sum_final() to switch to the right memory context. + */ + Assert(accum->pos_digits[0] == 0 && accum->neg_digits[0] == 0); + + ndigits = accum->ndigits; + + /* Propagate carry in the positive sum */ + dig = accum->pos_digits; + carry = 0; + for (i = ndigits - 1; i >= 0; i--) + { + newdig = dig[i] + carry; + if (newdig >= NBASE) + { + carry = newdig / NBASE; + newdig -= carry * NBASE; + } + else + carry = 0; + dig[i] = newdig; + } + /* Did we use up the digit reserved for carry propagation? */ + if (newdig > 0) + accum->have_carry_space = false; + + /* And the same for the negative sum */ + dig = accum->neg_digits; + carry = 0; + for (i = ndigits - 1; i >= 0; i--) + { + newdig = dig[i] + carry; + if (newdig >= NBASE) + { + carry = newdig / NBASE; + newdig -= carry * NBASE; + } + else + carry = 0; + dig[i] = newdig; + } + if (newdig > 0) + accum->have_carry_space = false; + + accum->num_uncarried = 0; +} + +/* + * Re-scale accumulator to accommodate new value. + * + * If the new value has more digits than the current digit buffers in the + * accumulator, enlarge the buffers. + */ +static void +accum_sum_rescale(NumericSumAccum *accum, const NumericVar *val) +{ + int old_weight = accum->weight; + int old_ndigits = accum->ndigits; + int accum_ndigits; + int accum_weight; + int accum_rscale; + int val_rscale; + + accum_weight = old_weight; + accum_ndigits = old_ndigits; + + /* + * Does the new value have a larger weight? If so, enlarge the buffers, + * and shift the existing value to the new weight, by adding leading + * zeros. + * + * We enforce that the accumulator always has a weight one larger than + * needed for the inputs, so that we have space for an extra digit at the + * final carry-propagation phase, if necessary. + */ + if (val->weight >= accum_weight) + { + accum_weight = val->weight + 1; + accum_ndigits = accum_ndigits + (accum_weight - old_weight); + } + + /* + * Even though the new value is small, we might've used up the space + * reserved for the carry digit in the last call to accum_sum_carry(). If + * so, enlarge to make room for another one. + */ + else if (!accum->have_carry_space) + { + accum_weight++; + accum_ndigits++; + } + + /* Is the new value wider on the right side? */ + accum_rscale = accum_ndigits - accum_weight - 1; + val_rscale = val->ndigits - val->weight - 1; + if (val_rscale > accum_rscale) + accum_ndigits = accum_ndigits + (val_rscale - accum_rscale); + + if (accum_ndigits != old_ndigits || + accum_weight != old_weight) + { + int32 *new_pos_digits; + int32 *new_neg_digits; + int weightdiff; + + weightdiff = accum_weight - old_weight; + + new_pos_digits = palloc0(accum_ndigits * sizeof(int32)); + new_neg_digits = palloc0(accum_ndigits * sizeof(int32)); + + if (accum->pos_digits) + { + memcpy(&new_pos_digits[weightdiff], accum->pos_digits, + old_ndigits * sizeof(int32)); + pfree(accum->pos_digits); + + memcpy(&new_neg_digits[weightdiff], accum->neg_digits, + old_ndigits * sizeof(int32)); + pfree(accum->neg_digits); + } + + accum->pos_digits = new_pos_digits; + accum->neg_digits = new_neg_digits; + + accum->weight = accum_weight; + accum->ndigits = accum_ndigits; + + Assert(accum->pos_digits[0] == 0 && accum->neg_digits[0] == 0); + accum->have_carry_space = true; + } + + if (val->dscale > accum->dscale) + accum->dscale = val->dscale; +} + +/* + * Return the current value of the accumulator. This perform final carry + * propagation, and adds together the positive and negative sums. + * + * Unlike all the other routines, the caller is not required to switch to + * the memory context that holds the accumulator. + */ +static void +accum_sum_final(NumericSumAccum *accum, NumericVar *result) +{ + int i; + NumericVar pos_var; + NumericVar neg_var; + + if (accum->ndigits == 0) + { + set_var_from_var(&const_zero, result); + return; + } + + /* Perform final carry */ + accum_sum_carry(accum); + + /* Create NumericVars representing the positive and negative sums */ + init_var(&pos_var); + init_var(&neg_var); + + pos_var.ndigits = neg_var.ndigits = accum->ndigits; + pos_var.weight = neg_var.weight = accum->weight; + pos_var.dscale = neg_var.dscale = accum->dscale; + pos_var.sign = NUMERIC_POS; + neg_var.sign = NUMERIC_NEG; + + pos_var.buf = pos_var.digits = digitbuf_alloc(accum->ndigits); + neg_var.buf = neg_var.digits = digitbuf_alloc(accum->ndigits); + + for (i = 0; i < accum->ndigits; i++) + { + Assert(accum->pos_digits[i] < NBASE); + pos_var.digits[i] = (int16) accum->pos_digits[i]; + + Assert(accum->neg_digits[i] < NBASE); + neg_var.digits[i] = (int16) accum->neg_digits[i]; + } + + /* And add them together */ + add_var(&pos_var, &neg_var, result); + + /* Remove leading/trailing zeroes */ + strip_var(result); +} + +/* + * Copy an accumulator's state. + * + * 'dst' is assumed to be uninitialized beforehand. No attempt is made at + * freeing old values. + */ +static void +accum_sum_copy(NumericSumAccum *dst, NumericSumAccum *src) +{ + dst->pos_digits = palloc(src->ndigits * sizeof(int32)); + dst->neg_digits = palloc(src->ndigits * sizeof(int32)); + + memcpy(dst->pos_digits, src->pos_digits, src->ndigits * sizeof(int32)); + memcpy(dst->neg_digits, src->neg_digits, src->ndigits * sizeof(int32)); + dst->num_uncarried = src->num_uncarried; + dst->ndigits = src->ndigits; + dst->weight = src->weight; + dst->dscale = src->dscale; +} + +/* + * Add the current value of 'accum2' into 'accum'. + */ +static void +accum_sum_combine(NumericSumAccum *accum, NumericSumAccum *accum2) +{ + NumericVar tmp_var; + + init_var(&tmp_var); + + accum_sum_final(accum2, &tmp_var); + accum_sum_add(accum, &tmp_var); + + free_var(&tmp_var); +} |