/* ------------------------------------------------------------------------- * * numeric.c * An exact numeric data type for the openGauss 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-2012, PostgreSQL Global Development Group * * IDENTIFICATION * src/backend/utils/adt/numeric.c * * ------------------------------------------------------------------------- */ #include "postgres.h" #include "knl/knl_variable.h" #include #include #include #include "access/hash.h" #include "catalog/pg_type.h" #include "funcapi.h" #include "common/int.h" #include "lib/hyperloglog.h" #include "libpq/pqformat.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/biginteger.h" #include "utils/gs_bitmap.h" #include "utils/guc.h" #include "utils/int8.h" #include "utils/numeric.h" #include "utils/sortsupport.h" #include "vecexecutor/vechashtable.h" #include "vecexecutor/vechashagg.h" #include "vectorsonic/vsonichashagg.h" /* ---------- * 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; #define NUMERIC_ABBREV_BITS (SIZEOF_DATUM * BITS_PER_BYTE) #if SIZEOF_DATUM == 8 #define DatumGetNumericAbbrev(d) ((int64)d) #define NUMERIC_ABBREV_NAN Int64GetDatum(PG_INT64_MIN) #else #define DatumGetNumericAbbrev(d) ((int32)(d)) #define NUMERIC_ABBREV_NAN Int32GetDatum(PG_INT32_MIN) #endif /* ---------- * Some preinitialized constants * ---------- */ static NumericDigit const_zero_data[1] = {0}; static NumericVar const_zero = {0, 0, NUMERIC_POS, 0, NULL, const_zero_data}; static NumericDigit const_one_data[1] = {1}; static NumericVar const_one = {1, 0, NUMERIC_POS, 0, NULL, const_one_data}; static NumericDigit const_two_data[1] = {2}; static NumericVar const_two = {1, 0, NUMERIC_POS, 0, NULL, const_two_data}; #if DEC_DIGITS == 4 || DEC_DIGITS == 2 static NumericDigit const_ten_data[1] = {10}; static NumericVar const_ten = {1, 0, NUMERIC_POS, 0, NULL, const_ten_data}; #elif DEC_DIGITS == 1 static NumericDigit const_ten_data[1] = {1}; static NumericVar const_ten = {1, 1, NUMERIC_POS, 0, NULL, const_ten_data}; #endif #if DEC_DIGITS == 4 static NumericDigit const_zero_point_five_data[1] = {5000}; #elif DEC_DIGITS == 2 static NumericDigit const_zero_point_five_data[1] = {50}; #elif DEC_DIGITS == 1 static NumericDigit const_zero_point_five_data[1] = {5}; #endif static NumericVar const_zero_point_five = {1, -1, NUMERIC_POS, 1, NULL, const_zero_point_five_data}; #if DEC_DIGITS == 4 static NumericDigit const_zero_point_nine_data[1] = {9000}; #elif DEC_DIGITS == 2 static NumericDigit const_zero_point_nine_data[1] = {90}; #elif DEC_DIGITS == 1 static NumericDigit const_zero_point_nine_data[1] = {9}; #endif static NumericVar const_zero_point_nine = {1, -1, NUMERIC_POS, 1, NULL, const_zero_point_nine_data}; #if DEC_DIGITS == 4 static NumericDigit const_one_point_one_data[2] = {1, 1000}; #elif DEC_DIGITS == 2 static NumericDigit const_one_point_one_data[2] = {1, 10}; #elif DEC_DIGITS == 1 static NumericDigit const_one_point_one_data[2] = {1, 1}; #endif static NumericVar const_one_point_one = {2, 0, NUMERIC_POS, 1, NULL, const_one_point_one_data}; static NumericVar const_nan = {0, 0, NUMERIC_NAN, 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 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 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 set_var_from_var(const NumericVar* value, NumericVar* dest); static void init_var_from_var(const NumericVar *value, NumericVar *dest); static char* get_str_from_var(NumericVar* var); static char* get_str_from_var_sci(NumericVar* var, int rscale); static void apply_typmod(NumericVar* var, int32 typmod); static int32 numericvar_to_int32(const NumericVar* var, bool can_ignore = false); static double numeric_to_double_no_overflow(Numeric num); static double numericvar_to_double_no_overflow(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(NumericVar* var, NumericSortSupport* nss); static int cmp_var(NumericVar* var1, 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 sub_var(NumericVar* var1, NumericVar* var2, NumericVar* result); static void mul_var(NumericVar* var1, NumericVar* var2, NumericVar* result, int rscale); static void div_var(NumericVar* var1, NumericVar* var2, NumericVar* result, int rscale, bool round); static void div_var_fast(NumericVar* var1, NumericVar* var2, NumericVar* result, int rscale, bool round); static int select_div_scale(NumericVar* var1, NumericVar* var2); static void mod_var(NumericVar* var1, NumericVar* var2, NumericVar* result); static void ceil_var(NumericVar* var, NumericVar* result); static void floor_var(NumericVar* var, NumericVar* result); static void sqrt_var(NumericVar* arg, NumericVar* result, int rscale); static void exp_var(NumericVar* arg, NumericVar* result, int rscale); static int estimate_ln_dweight(NumericVar* var); static void ln_var(NumericVar* arg, NumericVar* result, int rscale); static void log_var(NumericVar* base, NumericVar* num, NumericVar* result); static void power_var(NumericVar* base, NumericVar* exp, NumericVar* result); static void power_var_int(NumericVar* base, int exp, NumericVar* result, int rscale); static int cmp_abs(NumericVar* var1, 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(NumericVar* var1, NumericVar* var2, NumericVar* result); static void sub_abs(NumericVar* var1, 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, NumericVar* count_var, NumericVar* result_var); /* * @Description: call corresponding big integer operator functions. * * @IN op: template parameter, assign the operation name, e.g. add, sub, etc. * @IN larg: left-hand operand of operator. * @IN rarg: right-hand operand of operator. * @return: Datum - the datum data points to result of letfc op rightc. */ template inline Datum bipickfun(Numeric leftc, Numeric rightc) { Assert(NUMERIC_IS_BI(leftc)); Assert(NUMERIC_IS_BI(rightc)); int left_type = NUMERIC_IS_BI128(leftc); int right_type = NUMERIC_IS_BI128(rightc); biopfun func = BiFunMatrix[op][left_type][right_type]; Assert(func != NULL); /* call big integer fast calculate function */ return func(leftc, rightc, NULL); } /* ---------------------------------------------------------------------- * * 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 = NULL; /* Skip leading spaces */ cp = str; while (*cp) { if (!isspace((unsigned char)*cp)) break; cp++; } /* the first parameter is null, we should convert to 0 if u_sess->attr.attr_sql.sql_compatibility == C_FORMAT */ if (u_sess->attr.attr_sql.sql_compatibility == C_FORMAT && '\0' == *cp) { NumericVar value; init_var(&value); zero_var(&value); res = make_result(&value); PG_RETURN_NUMERIC(res); } /* * Check for NaN */ if (pg_strncasecmp(cp, "NaN", 3) == 0) { res = make_result(&const_nan); /* Should be nothing left but spaces */ cp += 3; while (*cp) { if (!isspace((unsigned char)*cp)) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type numeric: \"%s\"", str))); cp++; } } 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. */ while (*cp) { if (!isspace((unsigned char)*cp)) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type numeric: \"%s\"", str))); cp++; } apply_typmod(&value, typmod); res = make_result(&value); free_var(&value); } PG_RETURN_NUMERIC(res); } /* * numeric_out() - * * Output function for numeric data type. * include bi64 and bi128 type */ Datum numeric_out(PG_FUNCTION_ARGS) { Numeric num = PG_GETARG_NUMERIC(0); NumericVar x; char* str = NULL; int scale = 0; /* * Handle NaN */ if (NUMERIC_IS_NAN(num)) PG_RETURN_CSTRING(pstrdup("NaN")); /* * If numeric is big integer, call int64_out/int128_out */ uint16 numFlags = NUMERIC_NB_FLAGBITS(num); if (NUMERIC_FLAG_IS_BI64(numFlags)) { int64 val64 = NUMERIC_64VALUE(num); scale = NUMERIC_BI_SCALE(num); return bi64_out(val64, scale); } else if (NUMERIC_FLAG_IS_BI128(numFlags)) { int128 val128 = 0; errno_t rc = memcpy_s(&val128, sizeof(int128), (num)->choice.n_bi.n_data, sizeof(int128)); securec_check(rc, "\0", "\0"); scale = NUMERIC_BI_SCALE(num); return bi128_out(val128, scale); } /* * Get the number in the variable format */ init_var_from_num(num, &x); str = get_str_from_var(&x); /* * free memory if allocated by the toaster */ PG_FREE_IF_COPY(num, 0); PG_RETURN_CSTRING(str); } /* * numeric_is_nan() - * * Is Numeric value a NaN? */ bool numeric_is_nan(Numeric num) { return NUMERIC_IS_NAN(num); } /* * 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 = (int32)((((uint32)(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 = NULL; if (NUMERIC_IS_NANORBI(num)) { /* * Handle Big Integer */ if (NUMERIC_IS_BI(num)) num = makeNumericNormal(num); /* * Handle NaN */ 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 without trailing zeroes. */ char *numeric_normalize(Numeric num) { NumericVar x; char *str = NULL; int orig, last; /* * Handle NaN */ if (NUMERIC_IS_NAN(num)) { return pstrdup("NaN"); } init_var_from_num(num, &x); str = get_str_from_var(&x); orig = last = strlen(str) - 1; for (;;) { if (last == 0 || str[last] != '0') { break; } last--; } if (last > 0 && last != orig) { str[last] = '\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)); if (len < 0 || len > NUMERIC_MAX_PRECISION + NUMERIC_MAX_RESULT_SCALE) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("invalid length in external \"numeric\" value"))); init_alloc_var(&value, len); value.weight = (int16)pq_getmsgint(buf, sizeof(int16)); value.sign = (uint16)pq_getmsgint(buf, sizeof(uint16)); if (!(value.sign == NUMERIC_POS || value.sign == NUMERIC_NEG || value.sign == NUMERIC_NAN)) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("invalid sign in external \"numeric\" value"))); value.dscale = (uint16)pq_getmsgint(buf, sizeof(uint16)); 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; } apply_typmod(&value, typmod); res = make_result(&value); 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; /* * Handle Big Integer */ if (NUMERIC_IS_BI(num)) num = makeNumericNormal(num); 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_transform() - * * Flatten calls to numeric's length coercion function 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_transform(PG_FUNCTION_ARGS) { FuncExpr* expr = (FuncExpr*)PG_GETARG_POINTER(0); Node* ret = NULL; Node* typmod = NULL; Assert(IsA(expr, FuncExpr)); 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 = (int32)(((uint32)(old_typmod - VARHDRSZ)) & 0xffff); int32 new_scale = (int32)(((uint32)(new_typmod - VARHDRSZ)) & 0xffff); int32 old_precision = (int32)(((uint32)(old_typmod - VARHDRSZ)) >> 16 & 0xffff); int32 new_precision = (int32)(((uint32)(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 openGauss 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 newm; int32 tmp_typmod; int precision; int scale; int ddigits; int maxdigits; NumericVar var; if (NUMERIC_IS_NANORBI(num)) { /* * Handle Big Integer */ if (NUMERIC_IS_BI(num)) num = makeNumericNormal(num); /* * Handle NaN */ else PG_RETURN_NUMERIC(make_result(&const_nan)); } /* * If the value isn't a valid type modifier, simply return a copy of the * input value */ if (typmod < (int32)(VARHDRSZ)) { newm = (Numeric)palloc(VARSIZE(num)); errno_t rc = memcpy_s(newm, VARSIZE(num), num, VARSIZE(num)); securec_check(rc, "\0", "\0"); PG_RETURN_NUMERIC(newm); } /* * 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))) { newm = (Numeric)palloc(VARSIZE(num)); errno_t rc = memcpy_s(newm, VARSIZE(num), num, VARSIZE(num)); securec_check(rc, "\0", "\0"); if (NUMERIC_IS_SHORT(num)) newm->choice.n_short.n_header = (num->choice.n_short.n_header & ~NUMERIC_SHORT_DSCALE_MASK) | (scale << NUMERIC_SHORT_DSCALE_SHIFT); else newm->choice.n_long.n_sign_dscale = NUMERIC_SIGN(newm) | ((uint16)scale & NUMERIC_DSCALE_MASK); PG_RETURN_NUMERIC(newm); } /* * 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); newm = make_result(&var); free_var(&var); PG_RETURN_NUMERIC(newm); } Datum numerictypmodin(PG_FUNCTION_ARGS) { ArrayType* ta = PG_GETARG_ARRAYTYPE_P(0); int32* tl = NULL; 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 = (int32)(((uint32)(tl[0]) << 16) | (uint32)(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 = (((uint32)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) { const size_t len = 64; int32 typmod = PG_GETARG_INT32(0); char* res = (char*)palloc(len + 1); if (typmod >= 0) { errno_t ret = snprintf_s(res, len + 1, len, "(%d,%d)", (int32)((((uint32)(typmod - VARHDRSZ)) >> 16) & 0xffff), (int32)(((uint32)(typmod - VARHDRSZ)) & 0xffff)); securec_check_ss(ret, "", ""); } 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; uint16 numFlags = NUMERIC_NB_FLAGBITS(num); if (NUMERIC_FLAG_IS_NANORBI(numFlags)) { /* * Handle Big Integer */ if (NUMERIC_FLAG_IS_BI(numFlags)) res = makeNumericNormal(num); /* * Handle NaN */ else { PG_RETURN_NUMERIC(make_result(&const_nan)); } } else {/* Handle original numeric type */ /* * Do it the easy way directly on the packed format */ res = (Numeric)palloc(VARSIZE(num)); errno_t rc = memcpy_s(res, VARSIZE(num), num, VARSIZE(num)); securec_check(rc, "\0", "\0"); } if (NUMERIC_IS_SHORT(res)) { res->choice.n_short.n_header = res->choice.n_short.n_header & ~NUMERIC_SHORT_SIGN_MASK; } else { res->choice.n_long.n_sign_dscale = NUMERIC_POS | NUMERIC_DSCALE(res); } PG_RETURN_NUMERIC(res); } Datum numeric_uminus(PG_FUNCTION_ARGS) { Numeric num = PG_GETARG_NUMERIC(0); Numeric res; uint16 numFlags = NUMERIC_NB_FLAGBITS(num); if (NUMERIC_FLAG_IS_NANORBI(numFlags)) { /* * Handle Big Integer */ if (NUMERIC_FLAG_IS_BI(numFlags)) { res = makeNumericNormal(num); } else { /* Handle NaN */ PG_RETURN_NUMERIC(make_result(&const_nan)); } } else { /* Handle original numeric type */ /* * Do it the easy way directly on the packed format */ res = (Numeric)palloc(VARSIZE(num)); errno_t rc = memcpy_s(res, VARSIZE(num), num, VARSIZE(num)); securec_check(rc, "\0", "\0"); } /* * The packed format is known to be totally zero digit trimmed always. So * we can identify a ZERO by the fact that there are no digits at all. Do * nothing to a zero. */ if (NUMERIC_NDIGITS(res) != 0) { /* Else, flip the sign */ if (NUMERIC_IS_SHORT(res)) { res->choice.n_short.n_header = res->choice.n_short.n_header ^ NUMERIC_SHORT_SIGN_MASK; } else if (NUMERIC_SIGN(res) == NUMERIC_POS) { res->choice.n_long.n_sign_dscale = NUMERIC_NEG | NUMERIC_DSCALE(res); } else { res->choice.n_long.n_sign_dscale = NUMERIC_POS | NUMERIC_DSCALE(res); } } PG_RETURN_NUMERIC(res); } Datum numeric_uplus(PG_FUNCTION_ARGS) { Numeric num = PG_GETARG_NUMERIC(0); Numeric res; res = (Numeric)palloc(VARSIZE(num)); errno_t rc = memcpy_s(res, VARSIZE(num), num, VARSIZE(num)); securec_check(rc, "\0", "\0"); PG_RETURN_NUMERIC(res); } /* * 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); Numeric res; NumericVar result; if (NUMERIC_IS_NANORBI(num)) { /* * Handle Big Integer */ if (NUMERIC_IS_BI(num)) num = makeNumericNormal(num); /* * Handle NaN */ else PG_RETURN_NUMERIC(make_result(&const_nan)); } init_var(&result); /* * The packed format is known to be totally zero digit trimmed always. So * we can identify a ZERO by the fact that there are no digits at all. */ if (NUMERIC_NDIGITS(num) == 0) set_var_from_var(&const_zero, &result); else { /* * And if there are some, we return a copy of ONE with the sign of our * argument */ set_var_from_var(&const_one, &result); result.sign = NUMERIC_SIGN(num); } res = make_result(&result); free_var(&result); PG_RETURN_NUMERIC(res); } /* * numeric_round() - * * Round a value to have 'scale' digits after the decimal point. * We allow negative 'scale', implying rounding before the decimal * point --- A db 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; if (NUMERIC_IS_NANORBI(num)) { /* * Handle Big Integer */ if (NUMERIC_IS_BI(num)) num = makeNumericNormal(num); /* * Handle NaN */ else PG_RETURN_NUMERIC(make_result(&const_nan)); } /* * 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 --- A db 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; if (NUMERIC_IS_NANORBI(num)) { /* * Handle Big Integer */ if (NUMERIC_IS_BI(num)) num = makeNumericNormal(num); /* * Handle NaN */ else PG_RETURN_NUMERIC(make_result(&const_nan)); } /* * 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; if (NUMERIC_IS_NANORBI(num)) { /* * Handle Big Integer */ if (NUMERIC_IS_BI(num)) num = makeNumericNormal(num); /* * Handle NaN */ else PG_RETURN_NUMERIC(make_result(&const_nan)); } 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; if (NUMERIC_IS_NANORBI(num)) { /* * Handle Big Integer */ if (NUMERIC_IS_BI(num)) num = makeNumericNormal(num); /* * Handle NaN */ else PG_RETURN_NUMERIC(make_result(&const_nan)); } 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_step_numeric(PG_FUNCTION_ARGS) { generate_series_numeric_fctx* fctx = NULL; FuncCallContext* funcctx = NULL; MemoryContext oldcontext; if (SRF_IS_FIRSTCALL()) { Numeric start_num = PG_GETARG_NUMERIC(0); Numeric stop_num = PG_GETARG_NUMERIC(1); NumericVar steploc = const_one; /* handle NaN in start and stop values */ if (NUMERIC_IS_NAN(start_num)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("start value cannot be NaN"))); if (NUMERIC_IS_NAN(stop_num)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("stop value cannot be NaN"))); /* see if we were given an explicit step size */ if (PG_NARGS() == 3) { Numeric step_num = PG_GETARG_NUMERIC(2); if (NUMERIC_IS_NAN(step_num)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("step size cannot be NaN"))); 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 = (generate_series_numeric_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); } Datum generate_series_numeric(PG_FUNCTION_ARGS) { return generate_series_step_numeric(fcinfo); } /* * 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_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"))); /* * Handle Big Integer */ if (NUMERIC_IS_BI(operand)) { operand = makeNumericNormal(operand); } if (NUMERIC_IS_BI(bound1)) { bound1 = makeNumericNormal(bound1); } if (NUMERIC_IS_BI(bound2)) { bound2 = makeNumericNormal(bound2); } 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"))); /* 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, &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, &result_var); break; default: break; } /* if result exceeds the range of a legal int4, we ereport here */ result = numericvar_to_int32(&result_var); 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. */ static void compute_bucket( Numeric operand, Numeric bound1, Numeric bound2, NumericVar* count_var, 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 (cmp_var(&bound1_var, &bound2_var) < 0) { sub_var(&operand_var, &bound1_var, &operand_var); sub_var(&bound2_var, &bound1_var, &bound2_var); div_var(&operand_var, &bound2_var, result_var, select_div_scale(&operand_var, &bound2_var), true); } else { sub_var(&bound1_var, &operand_var, &operand_var); sub_var(&bound1_var, &bound2_var, &bound1_var); div_var(&operand_var, &bound1_var, result_var, select_div_scale(&operand_var, &bound1_var), true); } mul_var(result_var, count_var, result_var, result_var->dscale + count_var->dscale); 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 = NULL; MemoryContext oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt); nss = (NumericSortSupport*)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 = (NumericSortSupport*)ssup->ssup_extra; void* original_varatt = PG_DETOAST_DATUM_PACKED(original_datum); Numeric value; Datum result; errno_t rc = EOK; 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_HUGE_TOAST_POINTER(original_varatt) && 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); rc = memcpy_s(VARDATA(buf), VARATT_SHORT_MAX - VARHDRSZ_SHORT, VARDATA_SHORT(original_varatt), sz); securec_check(rc, "\0", "\0"); value = (Numeric)buf; } else value = (Numeric)original_varatt; if (NUMERIC_IS_NAN(value)) { result = NUMERIC_ABBREV_NAN; } else { NumericVar var; Numeric tmp_value = NULL; /* * convert bi64/bi128 to numeric, so we can get the * abbrev value of numeric */ if (NUMERIC_IS_BI(value)) { tmp_value = makeNumericNormal(value); init_var_from_num(tmp_value, &var); } else { init_var_from_num(value, &var); } result = numeric_abbrev_convert_var(&var, nss); /* tmp_value should free for avoid heap-use-after-free */ if (tmp_value != NULL) { pfree_ext(tmp_value); tmp_value = NULL; } } /* should happen only for external/compressed toasts */ if ((Pointer)original_varatt != DatumGetPointer(original_datum)) pfree_ext(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 = (NumericSortSupport*)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 (u_sess->attr.attr_common.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 (u_sess->attr.attr_common.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 (u_sess->attr.attr_common.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. * * Optimize numeric_fast_cmp of pg9.5, numeric_fast_cmp is only invoked by sort, * add fast pre-check for equality and refuce function calls to increase speed. */ static int numeric_fast_cmp(Datum x, Datum y, SortSupport ssup) { Numeric nx = DatumGetNumeric(x); Numeric ny = DatumGetNumeric(y); int result = 0; /* * We consider all NANs to be equal and larger than any non-NAN. This is * somewhat arbitrary; the important thing is to have a consistent sort * order. */ if (NUMERIC_IS_NAN(nx)) { if (NUMERIC_IS_NAN(ny)) result = 0; /* NAN = NAN */ else result = 1; /* NAN > non-NAN */ } else if (NUMERIC_IS_NAN(ny)) { result = -1; /* non-NAN < NAN */ } else { /* Get the flags of num1/num2 */ uint16 num1Flags = NUMERIC_NB_FLAGBITS(nx); uint16 num2Flags = NUMERIC_NB_FLAGBITS(ny); if (NUMERIC_FLAG_IS_BI(num1Flags) && NUMERIC_FLAG_IS_BI(num2Flags)) { /* call biginteger compare function */ result = DatumGetInt32(bipickfun(nx, ny)); } else { Numeric leftarg = NUMERIC_FLAG_IS_BI(num1Flags) ? makeNumericNormal(nx) : nx; Numeric rightarg = NUMERIC_FLAG_IS_BI(num2Flags) ? makeNumericNormal(ny) : ny; /* Compare the size between leftarg and rightarg. */ result = cmp_var_common(NUMERIC_DIGITS(leftarg), NUMERIC_NDIGITS(leftarg), NUMERIC_WEIGHT(leftarg), NUMERIC_SIGN(leftarg), NUMERIC_DIGITS(rightarg), NUMERIC_NDIGITS(rightarg), NUMERIC_WEIGHT(rightarg), NUMERIC_SIGN(rightarg)); /* Free the template malloc space */ if (leftarg != nx) pfree_ext(leftarg); if (rightarg != ny) pfree_ext(rightarg); } } if (DatumGetPointer(nx) != DatumGetPointer(x)) pfree_ext(nx); if (DatumGetPointer(ny) != DatumGetPointer(y)) pfree_ext(ny); PG_RETURN_INT32(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. */ 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(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)((uint64)(weight + 44) << 56); switch (ndigits) { default: result = (int64)(((uint64)result) | ((uint64)var->digits[3])); /* fall through */ case 3: result = (int64)((uint64)result | (((uint64)var->digits[2]) << 14)); /* fall through */ case 2: result = (int64)((uint64)result | (((uint64)var->digits[1]) << 28)); /* fall through */ case 1: result = (int64)((uint64)result | (((uint64)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 Int64GetDatum(result); } #endif /* NUMERIC_ABBREV_BITS == 64 */ #if NUMERIC_ABBREV_BITS == 32 static Datum numeric_abbrev_convert_var(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 Int32GetDatum(result); } #endif /* NUMERIC_ABBREV_BITS == 32 */ /* * Ordinary (non-sortsupport) comparisons follow. * Numeric compare function, return -1/0/1. */ Datum numeric_cmp(PG_FUNCTION_ARGS) { Numeric num1 = PG_GETARG_NUMERIC(0); Numeric num2 = PG_GETARG_NUMERIC(1); int result = 0; /* * We consider all NANs to be equal and larger than any non-NAN. This is * somewhat arbitrary; the important thing is to have a consistent sort * order. */ if (NUMERIC_IS_NAN(num1)) { if (NUMERIC_IS_NAN(num2)) result = 0; /* NAN = NAN */ else result = 1; /* NAN > non-NAN */ } else if (NUMERIC_IS_NAN(num2)) { result = -1; /* non-NAN < NAN */ } else { /* Get the flags of num1/num2 */ uint16 num1Flags = NUMERIC_NB_FLAGBITS(num1); uint16 num2Flags = NUMERIC_NB_FLAGBITS(num2); if (NUMERIC_FLAG_IS_BI(num1Flags) && NUMERIC_FLAG_IS_BI(num2Flags)) { /* call biginteger compare function */ result = DatumGetInt32(bipickfun(num1, num2)); } else { Numeric leftarg = NUMERIC_FLAG_IS_BI(num1Flags) ? makeNumericNormal(num1) : num1; Numeric rightarg = NUMERIC_FLAG_IS_BI(num2Flags) ? makeNumericNormal(num2) : num2; /* Compare the size between leftarg and rightarg. */ result = cmp_var_common(NUMERIC_DIGITS(leftarg), NUMERIC_NDIGITS(leftarg), NUMERIC_WEIGHT(leftarg), NUMERIC_SIGN(leftarg), NUMERIC_DIGITS(rightarg), NUMERIC_NDIGITS(rightarg), NUMERIC_WEIGHT(rightarg), NUMERIC_SIGN(rightarg)); /* Free the template malloc space */ if (leftarg != num1) pfree_ext(leftarg); if (rightarg != num2) pfree_ext(rightarg); } } PG_FREE_IF_COPY(num1, 0); PG_FREE_IF_COPY(num2, 1); PG_RETURN_INT32(result); } /* * @Description: Numeric compare function, if num1 equals to num2 * then return true, else return false. * * @IN PG_FUNCTION_ARGS: Numeric data. * @return: Datum - the result of (num1 == num2). */ Datum numeric_eq(PG_FUNCTION_ARGS) { Numeric num1 = PG_GETARG_NUMERIC(0); Numeric num2 = PG_GETARG_NUMERIC(1); bool result = false; if (NUMERIC_IS_BI(num1) && NUMERIC_IS_BI(num2)) { /*call biginteger function*/ result = DatumGetInt32(bipickfun(num1, num2)); } else { /* handle NAN in cmp_numerics */ result = cmp_numerics(num1, num2) == 0; } PG_FREE_IF_COPY(num1, 0); PG_FREE_IF_COPY(num2, 1); PG_RETURN_BOOL(result); } /* * @Description: Numeric compare function, if num1 not equals to num2 * then return true, else return false. * * @IN PG_FUNCTION_ARGS: Numeric data. * @return: Datum - the result of (num1 != num2). */ Datum numeric_ne(PG_FUNCTION_ARGS) { Numeric num1 = PG_GETARG_NUMERIC(0); Numeric num2 = PG_GETARG_NUMERIC(1); bool result = false; if (NUMERIC_IS_BI(num1) && NUMERIC_IS_BI(num2)) { /*call biginteger function*/ result = DatumGetInt32(bipickfun(num1, num2)); } else { /* handle NAN in cmp_numerics */ result = cmp_numerics(num1, num2) != 0; } PG_FREE_IF_COPY(num1, 0); PG_FREE_IF_COPY(num2, 1); PG_RETURN_BOOL(result); } /* * @Description: Numeric compare function, if num1 great than num2 * then return true, else return false. * * @IN PG_FUNCTION_ARGS: Numeric data. * @return: Datum - the result of (num1 > num2). */ Datum numeric_gt(PG_FUNCTION_ARGS) { Numeric num1 = PG_GETARG_NUMERIC(0); Numeric num2 = PG_GETARG_NUMERIC(1); bool result = false; if (NUMERIC_IS_BI(num1) && NUMERIC_IS_BI(num2)) { /*call biginteger function*/ result = DatumGetInt32(bipickfun(num1, num2)); } else { /* handle NAN in cmp_numerics */ result = cmp_numerics(num1, num2) > 0; } PG_FREE_IF_COPY(num1, 0); PG_FREE_IF_COPY(num2, 1); PG_RETURN_BOOL(result); } /* * @Description: Numeric compare function, if num1 great than or equal to num2 * then return true, else return false. * * @IN PG_FUNCTION_ARGS: Numeric data. * @return: Datum - the result of (num1 >= num2). */ Datum numeric_ge(PG_FUNCTION_ARGS) { Numeric num1 = PG_GETARG_NUMERIC(0); Numeric num2 = PG_GETARG_NUMERIC(1); bool result = false; if (NUMERIC_IS_BI(num1) && NUMERIC_IS_BI(num2)) { /*call biginteger function*/ result = DatumGetInt32(bipickfun(num1, num2)); } else { /* handle NAN in cmp_numerics */ result = cmp_numerics(num1, num2) >= 0; } PG_FREE_IF_COPY(num1, 0); PG_FREE_IF_COPY(num2, 1); PG_RETURN_BOOL(result); } /* * @Description: Numeric compare function, if num1 less than num2 * then return true, else return false. * * @IN PG_FUNCTION_ARGS: Numeric data. * @return: Datum - the result of (num1 < num2). */ Datum numeric_lt(PG_FUNCTION_ARGS) { Numeric num1 = PG_GETARG_NUMERIC(0); Numeric num2 = PG_GETARG_NUMERIC(1); bool result = false; if (NUMERIC_IS_BI(num1) && NUMERIC_IS_BI(num2)) { /*call biginteger function*/ result = DatumGetInt32(bipickfun(num1, num2)); } else { /* handle NAN in cmp_numerics */ result = cmp_numerics(num1, num2) < 0; } PG_FREE_IF_COPY(num1, 0); PG_FREE_IF_COPY(num2, 1); PG_RETURN_BOOL(result); } /* * @Description: Numeric compare function, if num1 less than or equal to num2 * then return true, else return false. * * @IN PG_FUNCTION_ARGS: Numeric data. * @return: Datum - the result of (num1 <= num2). */ Datum numeric_le(PG_FUNCTION_ARGS) { Numeric num1 = PG_GETARG_NUMERIC(0); Numeric num2 = PG_GETARG_NUMERIC(1); bool result = false; if (NUMERIC_IS_BI(num1) && NUMERIC_IS_BI(num2)) { /*call biginteger function*/ result = DatumGetInt32(bipickfun(num1, num2)); } else { /* handle NAN in cmp_numerics */ result = cmp_numerics(num1, num2) <= 0; } PG_FREE_IF_COPY(num1, 0); PG_FREE_IF_COPY(num2, 1); PG_RETURN_BOOL(result); } int cmp_numerics(Numeric num1, Numeric num2) { int result; /* compare numeric data, convert big integer to numeric */ Numeric leftarg = NUMERIC_IS_BI(num1) ? makeNumericNormal(num1) : num1; Numeric rightarg = NUMERIC_IS_BI(num2) ? makeNumericNormal(num2) : num2; /* * We consider all NANs to be equal and larger than any non-NAN. This is * somewhat arbitrary; the important thing is to have a consistent sort * order. */ if (NUMERIC_IS_NAN(leftarg)) { if (NUMERIC_IS_NAN(rightarg)) result = 0; /* NAN = NAN */ else result = 1; /* NAN > non-NAN */ } else if (NUMERIC_IS_NAN(rightarg)) { result = -1; /* non-NAN < NAN */ } else { result = cmp_var_common(NUMERIC_DIGITS(leftarg), NUMERIC_NDIGITS(leftarg), NUMERIC_WEIGHT(leftarg), NUMERIC_SIGN(leftarg), NUMERIC_DIGITS(rightarg), NUMERIC_NDIGITS(rightarg), NUMERIC_WEIGHT(rightarg), NUMERIC_SIGN(rightarg)); } /* Free the template malloc space */ if (leftarg != num1) pfree_ext(leftarg); if (rightarg != num2) pfree_ext(rightarg); return 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 = NULL; /* If it's NaN, don't try to hash the rest of the fields */ if (NUMERIC_IS_NAN(key)) PG_RETURN_UINT32(0); /* * Convert int64/128 to Numeric * create function hash_bi() for hash_agg and hash_join */ if (NUMERIC_IS_BI(key)) key = makeNumericNormal(key); 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; (unsigned int)(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) == (unsigned int)(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 */ if ((unsigned int)(start_offset + end_offset) >= NUMERIC_NDIGITS(key)) { ereport(ERROR, (errcode(ERRCODE_INVALID_ATTRIBUTE), errmsg("there should be at least one non-zero digit."))); } /* * 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 ^ (uint32)weight; /* free memory if allocated by the toaster */ PG_FREE_IF_COPY(key, 0); 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); NumericVar arg1; NumericVar arg2; NumericVar result; Numeric res; uint16 num1Flags = NUMERIC_NB_FLAGBITS(num1); uint16 num2Flags = NUMERIC_NB_FLAGBITS(num2); if (NUMERIC_FLAG_IS_NANORBI(num1Flags) || NUMERIC_FLAG_IS_NANORBI(num2Flags)) { if (NUMERIC_FLAG_IS_BI(num1Flags) && NUMERIC_FLAG_IS_BI(num2Flags)) { // call biginteger function return bipickfun(num1, num2); } else if (NUMERIC_FLAG_IS_NAN(num1Flags) || NUMERIC_FLAG_IS_NAN(num2Flags)) { // handle NAN PG_RETURN_NUMERIC(make_result(&const_nan)); } else if (NUMERIC_FLAG_IS_BI(num1Flags)) { // num1 is int64/128, num2 is numeric, turn num1 to numeric num1 = makeNumericNormal(num1); } else { // num1 is numeric, num2 is int64/128, turn num2 to numeric num2 = makeNumericNormal(num2); } } /* * 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(&result); free_var(&result); PG_RETURN_NUMERIC(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); NumericVar arg1; NumericVar arg2; NumericVar result; Numeric res; uint16 num1Flags = NUMERIC_NB_FLAGBITS(num1); uint16 num2Flags = NUMERIC_NB_FLAGBITS(num2); if (NUMERIC_FLAG_IS_NANORBI(num1Flags) || NUMERIC_FLAG_IS_NANORBI(num2Flags)) { if (NUMERIC_FLAG_IS_BI(num1Flags) && NUMERIC_FLAG_IS_BI(num2Flags)) { // call biginteger function return bipickfun(num1, num2); } else if (NUMERIC_FLAG_IS_NAN(num1Flags) || NUMERIC_FLAG_IS_NAN(num2Flags)) { // handle NAN PG_RETURN_NUMERIC(make_result(&const_nan)); } else if (NUMERIC_FLAG_IS_BI(num1Flags)) { // num1 is int64/128, num2 is numeric, turn num1 to numeric num1 = makeNumericNormal(num1); } else { // num1 is numeric, num2 is int64/128, turn num2 to numeric num2 = makeNumericNormal(num2); } } /* * 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(&result); free_var(&result); PG_RETURN_NUMERIC(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); NumericVar arg1; NumericVar arg2; NumericVar result; Numeric res; uint16 num1Flags = NUMERIC_NB_FLAGBITS(num1); uint16 num2Flags = NUMERIC_NB_FLAGBITS(num2); if (NUMERIC_FLAG_IS_NANORBI(num1Flags) || NUMERIC_FLAG_IS_NANORBI(num2Flags)) { if (NUMERIC_FLAG_IS_BI(num1Flags) && NUMERIC_FLAG_IS_BI(num2Flags)) { // call biginteger function return bipickfun(num1, num2); } else if (NUMERIC_FLAG_IS_NAN(num1Flags) || NUMERIC_FLAG_IS_NAN(num2Flags)) { // handle NAN PG_RETURN_NUMERIC(make_result(&const_nan)); } else if (NUMERIC_FLAG_IS_BI(num1Flags)) { // num1 is int64/128, num2 is numeric, turn num1 to numeric num1 = makeNumericNormal(num1); } else { // num1 is numeric, num2 is int64/128, turn num2 to numeric num2 = makeNumericNormal(num2); } } /* * 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)). */ init_var_from_num(num1, &arg1); init_var_from_num(num2, &arg2); init_var(&result); mul_var(&arg1, &arg2, &result, arg1.dscale + arg2.dscale); res = make_result(&result); free_var(&result); PG_RETURN_NUMERIC(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); NumericVar arg1; NumericVar arg2; NumericVar result; Numeric res; int rscale; uint16 num1Flags = NUMERIC_NB_FLAGBITS(num1); uint16 num2Flags = NUMERIC_NB_FLAGBITS(num2); if (NUMERIC_FLAG_IS_NANORBI(num1Flags) || NUMERIC_FLAG_IS_NANORBI(num2Flags)) { if (NUMERIC_FLAG_IS_BI(num1Flags) && NUMERIC_FLAG_IS_BI(num2Flags)) { // call biginteger function return bipickfun(num1, num2); } else if (NUMERIC_FLAG_IS_NAN(num1Flags) || NUMERIC_FLAG_IS_NAN(num2Flags)) { // handle NAN PG_RETURN_NUMERIC(make_result(&const_nan)); } else if (NUMERIC_FLAG_IS_BI(num1Flags)) { // num1 is int64/128, num2 is numeric, turn num1 to numeric num1 = makeNumericNormal(num1); } else { // num1 is numeric, num2 is int64/128, turn num2 to numeric num2 = makeNumericNormal(num2); } } /* * 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); /* * Do the divide and return the result */ div_var(&arg1, &arg2, &result, rscale, true); res = make_result(&result); free_var(&result); PG_RETURN_NUMERIC(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; uint16 num1Flags = NUMERIC_NB_FLAGBITS(num1); uint16 num2Flags = NUMERIC_NB_FLAGBITS(num2); if (NUMERIC_FLAG_IS_NANORBI(num1Flags) || NUMERIC_FLAG_IS_NANORBI(num2Flags)) { /* * Handle NaN */ if (NUMERIC_FLAG_IS_NAN(num1Flags) || NUMERIC_FLAG_IS_NAN(num2Flags)) { PG_RETURN_NUMERIC(make_result(&const_nan)); } /* * If num1/num2 is int64/int128, turn it to Numeric */ if (NUMERIC_FLAG_IS_BI(num1Flags)) { num1 = makeNumericNormal(num1); } if (NUMERIC_FLAG_IS_BI(num2Flags)) { num2 = makeNumericNormal(num2); } } /* * 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; NumericVar arg1; NumericVar arg2; NumericVar result; uint16 num1Flags = NUMERIC_NB_FLAGBITS(num1); uint16 num2Flags = NUMERIC_NB_FLAGBITS(num2); if (NUMERIC_FLAG_IS_NANORBI(num1Flags) || NUMERIC_FLAG_IS_NANORBI(num2Flags)) { /* * Handle NaN */ if (NUMERIC_FLAG_IS_NAN(num1Flags) || NUMERIC_FLAG_IS_NAN(num2Flags)) { PG_RETURN_NUMERIC(make_result(&const_nan)); } /* * If num1/num2 is int64/int128, turn it to Numeric */ if (NUMERIC_FLAG_IS_BI(num1Flags)) { num1 = makeNumericNormal(num1); } if (NUMERIC_FLAG_IS_BI(num2Flags)) { num2 = makeNumericNormal(num2); } } init_var_from_num(num1, &arg1); init_var_from_num(num2, &arg2); init_var(&result); // zero is allowed to be divisor if (0 == cmp_var(&arg2, &const_zero)) { free_var(&result); free_var(&arg2); free_var(&arg1); if (DB_IS_CMPT(PG_FORMAT)) { /* zero is not allowed to be divisor if compatible with PG */ ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); /* ensure compiler realizes we mustn't reach the division (gcc bug) */ PG_RETURN_NULL(); } PG_RETURN_NUMERIC(num1); } mod_var(&arg1, &arg2, &result); res = make_result(&result); free_var(&result); PG_RETURN_NUMERIC(res); } /* * numeric_inc() - * * Increment a number by one */ Datum numeric_inc(PG_FUNCTION_ARGS) { Numeric num = PG_GETARG_NUMERIC(0); NumericVar arg; Numeric res; uint16 numFlags = NUMERIC_NB_FLAGBITS(num); if (NUMERIC_FLAG_IS_NANORBI(numFlags)) { /* * Handle Big Integer */ if (NUMERIC_FLAG_IS_BI(numFlags)) { num = makeNumericNormal(num); } else { /* * Handle NaN */ PG_RETURN_NUMERIC(make_result(&const_nan)); } } /* * 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); /* * If num1/num2 is int64/int128, turn it to Numeric */ if (NUMERIC_IS_BI(num1)) num1 = makeNumericNormal(num1); if (NUMERIC_IS_BI(num2)) num2 = makeNumericNormal(num2); /* * 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); /* * If num1/num2 is int64/int128, turn it to Numeric */ if (NUMERIC_IS_BI(num1)) num1 = makeNumericNormal(num1); if (NUMERIC_IS_BI(num2)) num2 = makeNumericNormal(num2); /* * 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_fac() * * Compute factorial */ Datum numeric_fac(PG_FUNCTION_ARGS) { int64 num = PG_GETARG_INT64(0); Numeric res; NumericVar fact; NumericVar result; 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; uint16 numFlags = NUMERIC_NB_FLAGBITS(num); if (NUMERIC_FLAG_IS_NANORBI(numFlags)) { /* * Handle Big Integer */ if (NUMERIC_FLAG_IS_BI(numFlags)) { num = makeNumericNormal(num); } else { /* * Handle NaN */ PG_RETURN_NUMERIC(make_result(&const_nan)); } } /* * 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); quick_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; uint16 numFlags = NUMERIC_NB_FLAGBITS(num); if (NUMERIC_FLAG_IS_NANORBI(numFlags)) { /* * Handle Big Integer */ if (NUMERIC_FLAG_IS_BI(numFlags)) { num = makeNumericNormal(num); } else { /* * Handle NaN */ PG_RETURN_NUMERIC(make_result(&const_nan)); } } /* * 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; uint16 numFlags = NUMERIC_NB_FLAGBITS(num); if (NUMERIC_FLAG_IS_NANORBI(numFlags)) { /* * Handle Big Integer */ if (NUMERIC_FLAG_IS_BI(numFlags)) { num = makeNumericNormal(num); } else { /* * Handle NaN */ PG_RETURN_NUMERIC(make_result(&const_nan)); } } 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; uint16 num1Flags = NUMERIC_NB_FLAGBITS(num1); uint16 num2Flags = NUMERIC_NB_FLAGBITS(num2); if (NUMERIC_FLAG_IS_NANORBI(num1Flags) || NUMERIC_FLAG_IS_NANORBI(num2Flags)) { /* * Handle NaN */ if (NUMERIC_FLAG_IS_NAN(num1Flags) || NUMERIC_FLAG_IS_NAN(num2Flags)) { PG_RETURN_NUMERIC(make_result(&const_nan)); } /* * If num1/num2 is int64/int128, turn it to Numeric */ if (NUMERIC_FLAG_IS_BI(num1Flags)) { num1 = makeNumericNormal(num1); } if (NUMERIC_FLAG_IS_BI(num2Flags)) { num2 = makeNumericNormal(num2); } } /* * 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 b to the power of x */ 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 arg2_trunc; NumericVar result; uint16 num1Flags = NUMERIC_NB_FLAGBITS(num1); uint16 num2Flags = NUMERIC_NB_FLAGBITS(num2); if (NUMERIC_FLAG_IS_NANORBI(num1Flags) || NUMERIC_FLAG_IS_NANORBI(num2Flags)) { /* * Handle NaN */ if (NUMERIC_FLAG_IS_NAN(num1Flags) || NUMERIC_FLAG_IS_NAN(num2Flags)) { PG_RETURN_NUMERIC(make_result(&const_nan)); } /* * If num1/num2 is int64/int128, turn it to Numeric */ if (NUMERIC_FLAG_IS_BI(num1Flags)) { num1 = makeNumericNormal(num1); } if (NUMERIC_FLAG_IS_BI(num2Flags)) { num2 = makeNumericNormal(num2); } } /* * Initialize things */ init_var(&arg2_trunc); init_var(&result); init_var_from_num(num1, &arg1); init_var_from_num(num2, &arg2); set_var_from_var(&arg2, &arg2_trunc); trunc_var(&arg2_trunc, 0); /* * 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. */ if (cmp_var(&arg1, &const_zero) == 0 && cmp_var(&arg2, &const_zero) < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION), errmsg("zero raised to a negative power is undefined"))); if (cmp_var(&arg1, &const_zero) < 0 && cmp_var(&arg2, &arg2_trunc) != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION), errmsg("a negative number raised to a non-integer power yields a complex result"))); /* * 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); free_var(&arg2_trunc); PG_RETURN_NUMERIC(res); } /* ---------------------------------------------------------------------- * * Type conversion functions * * ---------------------------------------------------------------------- */ Datum int4_numeric(PG_FUNCTION_ARGS) { int32 val = PG_GETARG_INT32(0); Numeric res; NumericVar result; init_var(&result); int64_to_numericvar((int64)val, &result); res = make_result(&result); free_var(&result); PG_RETURN_NUMERIC(res); } Datum numeric_int4(PG_FUNCTION_ARGS) { Numeric num = PG_GETARG_NUMERIC(0); NumericVar x; int32 result; uint16 numFlags = NUMERIC_NB_FLAGBITS(num); if (NUMERIC_FLAG_IS_NANORBI(numFlags)) { /* Handle Big Integer */ if (NUMERIC_FLAG_IS_BI(numFlags)) num = makeNumericNormal(num); /* XXX would it be better to return NULL? */ else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert NaN to integer"))); } /* Convert to variable format, then convert to int4 */ init_var_from_num(num, &x); result = numericvar_to_int32(&x, fcinfo->can_ignore); PG_RETURN_INT32(result); } /* * Given a NumericVar, convert it to an int32. If the NumericVar * exceeds the range of an int32, raise the appropriate error via * ereport(). The input NumericVar is *not* free'd. */ static int32 numericvar_to_int32(const NumericVar* var, bool can_ignore) { int32 result; int64 val; if (!numericvar_to_int64(var, &val, can_ignore)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); /* return INT32_MAX/INT32_MIN if SQL can ignore overflowing */ if (can_ignore && (val > INT_MAX || val < INT_MIN)) { ereport(WARNING, (errmsg("integer out of range"))); return val > INT_MAX ? INT_MAX : INT_MIN; } /* Down-convert to int4 */ result = (int32)val; /* Test for overflow by reverse-conversion. */ if ((int64)result != val) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); return result; } Datum int8_numeric(PG_FUNCTION_ARGS) { int64 val = PG_GETARG_INT64(0); Numeric res; NumericVar result; init_var(&result); int64_to_numericvar(val, &result); res = make_result(&result); free_var(&result); PG_RETURN_NUMERIC(res); } Datum numeric_int8(PG_FUNCTION_ARGS) { Numeric num = PG_GETARG_NUMERIC(0); NumericVar x; int64 result; uint16 numFlags = NUMERIC_NB_FLAGBITS(num); if (NUMERIC_FLAG_IS_NANORBI(numFlags)) { /* Handle Big Integer */ if (NUMERIC_FLAG_IS_BI(numFlags)) num = makeNumericNormal(num); /* XXX would it be better to return NULL? */ else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert NaN to bigint"))); } /* Convert to variable format and thence to int8 */ init_var_from_num(num, &x); if (!numericvar_to_int64(&x, &result, fcinfo->can_ignore)) 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); Numeric res; NumericVar result; init_var(&result); int64_to_numericvar((int64)val, &result); res = make_result(&result); free_var(&result); PG_RETURN_NUMERIC(res); } Datum numeric_int2(PG_FUNCTION_ARGS) { Numeric num = PG_GETARG_NUMERIC(0); NumericVar x; int64 val; int16 result; uint16 numFlags = NUMERIC_NB_FLAGBITS(num); if (NUMERIC_FLAG_IS_NANORBI(numFlags)) { /* Handle Big Integer */ if (NUMERIC_FLAG_IS_BI(numFlags)) num = makeNumericNormal(num); /* XXX would it be better to return NULL? */ else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert NaN to smallint"))); } /* Convert to variable format and thence to int8 */ init_var_from_num(num, &x); if (!numericvar_to_int64(&x, &val, fcinfo->can_ignore)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("smallint out of range"))); /* return INT16_MAX/INT16_MIN if SQL can ignore overflowing */ if (fcinfo->can_ignore && (val > SHRT_MAX || val < SHRT_MIN)) { ereport(WARNING, (errmsg("smallint out of range"))); PG_RETURN_INT16(val > SHRT_MAX ? SHRT_MAX : SHRT_MIN); } /* Down-convert to int2 */ result = (int16)val; /* Test for overflow by reverse-conversion. */ if ((int64)result != val) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("smallint out of range"))); PG_RETURN_INT16(result); } // sql compatible : sybase data type tinyint Datum int1_numeric(PG_FUNCTION_ARGS) { uint8 val = PG_GETARG_UINT8(0); Numeric res; NumericVar result; init_var(&result); int64_to_numericvar((int64)val, &result); res = make_result(&result); free_var(&result); PG_RETURN_NUMERIC(res); } Datum numeric_int1(PG_FUNCTION_ARGS) { Numeric num = PG_GETARG_NUMERIC(0); NumericVar x; int64 val; uint8 result; uint16 numFlags = NUMERIC_NB_FLAGBITS(num); if (NUMERIC_FLAG_IS_NANORBI(numFlags)) { /* Handle Big Integer */ if (NUMERIC_FLAG_IS_BI(numFlags)) num = makeNumericNormal(num); /* XXX would it be better to return NULL? */ else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert NaN to tinyint"))); } /* Convert to variable format and thence to uint8 */ init_var_from_num(num, &x); if (x.sign == NUMERIC_NEG && !fcinfo->can_ignore) { ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("tinyint out of range"))); } if (!numericvar_to_int64(&x, &val, fcinfo->can_ignore)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("tinyint out of range"))); /* return UINT8_MAX/UINT8_MIN if SQL can ignore overflowing */ if (fcinfo->can_ignore && (val > UCHAR_MAX || val < 0)) { ereport(WARNING, (errmsg("tinyint out of range"))); PG_RETURN_UINT8(val > UCHAR_MAX ? UCHAR_MAX : 0); } /* Down-convert to int1 */ result = (uint8)val; /* Test for overflow by reverse-conversion. */ if ((int64)result != val) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("tinyint out of range"))); PG_RETURN_UINT8(result); } Datum float8_numeric(PG_FUNCTION_ARGS) { float8 val = PG_GETARG_FLOAT8(0); Numeric res; NumericVar result; char buf[DBL_DIG + 100]; errno_t rc; if (isnan(val)) PG_RETURN_NUMERIC(make_result(&const_nan)); if (isinf(val)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert infinity to numeric"))); rc = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%.*g", DBL_DIG, val); securec_check_ss(rc, "\0", "\0"); 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 = NULL; Datum result; uint16 numFlags = NUMERIC_NB_FLAGBITS(num); if (NUMERIC_FLAG_IS_NANORBI(numFlags)) { /* If num is int64/int128, turn it to Numeric */ if (NUMERIC_FLAG_IS_BI(numFlags)) { num = makeNumericNormal(num); } else { /* Handle NaN */ PG_RETURN_FLOAT8(get_float8_nan()); } } tmp = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(num))); result = DirectFunctionCall1(float8in, CStringGetDatum(tmp)); pfree_ext(tmp); PG_RETURN_DATUM(result); } /* Convert numeric to float8; if out of range, return +/- HUGE_VAL */ Datum numeric_float8_no_overflow(PG_FUNCTION_ARGS) { Numeric num = PG_GETARG_NUMERIC(0); double val; uint16 numFlags = NUMERIC_NB_FLAGBITS(num); if (NUMERIC_FLAG_IS_NANORBI(numFlags)) { /* If num is int64/int128, turn it to Numeric */ if (NUMERIC_FLAG_IS_BI(numFlags)) { num = makeNumericNormal(num); } else { /* Handle NaN */ PG_RETURN_FLOAT8(get_float8_nan()); } } val = numeric_to_double_no_overflow(num); 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]; errno_t rc; if (isnan(val)) PG_RETURN_NUMERIC(make_result(&const_nan)); if (isinf(val)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert infinity to numeric"))); rc = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%.*g", FLT_DIG, val); securec_check_ss(rc, "\0", "\0"); 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 = NULL; Datum result; uint16 numFlags = NUMERIC_NB_FLAGBITS(num); if (NUMERIC_FLAG_IS_NANORBI(numFlags)) { /* If num is int64/int128, turn it to Numeric */ if (NUMERIC_FLAG_IS_BI(numFlags)) { num = makeNumericNormal(num); } else { /* Handle NaN */ PG_RETURN_FLOAT4(get_float4_nan()); } } tmp = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(num))); if (fcinfo->can_ignore) { result = DirectFunctionCall1Coll(float4in, InvalidOid, CStringGetDatum(tmp), true); } else { result = DirectFunctionCall1(float4in, CStringGetDatum(tmp)); } pfree_ext(tmp); PG_RETURN_DATUM(result); } /* ---------------------------------------------------------------------- * * Aggregate functions * * The transition datatype for all these aggregates is a 3-element array * of Numeric, holding the values N, sum(X), sum(X*X) in that order. * * We represent N as a numeric mainly to avoid having to build a special * datatype; it's unlikely it'd overflow an int4, but ... * * ---------------------------------------------------------------------- */ static ArrayType* do_numeric_accum(ArrayType* transarray, Numeric newval) { Datum* transdatums = NULL; int ndatums; Datum N, sumX, sumX2; ArrayType* result = NULL; /* We assume the input is array of numeric */ deconstruct_array(transarray, NUMERICOID, -1, false, 'i', &transdatums, NULL, &ndatums); if (ndatums != 3) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("expected 3-element numeric array"))); N = transdatums[0]; sumX = transdatums[1]; sumX2 = transdatums[2]; N = DirectFunctionCall1(numeric_inc, N); sumX = DirectFunctionCall2(numeric_add, sumX, NumericGetDatum(newval)); sumX2 = DirectFunctionCall2( numeric_add, sumX2, DirectFunctionCall2(numeric_mul, NumericGetDatum(newval), NumericGetDatum(newval))); transdatums[0] = N; transdatums[1] = sumX; transdatums[2] = sumX2; result = construct_array(transdatums, 3, NUMERICOID, -1, false, 'i'); return result; } /* * Improve avg performance by not caclulating sum(X*X). */ static ArrayType* do_numeric_avg_accum(ArrayType* transarray, Numeric newval) { Datum* transdatums = NULL; int ndatums; Datum N, sumX; ArrayType* result = NULL; /* We assume the input is array of numeric */ deconstruct_array(transarray, NUMERICOID, -1, false, 'i', &transdatums, NULL, &ndatums); if (ndatums != 2) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("expected 2-element numeric array"))); N = transdatums[0]; sumX = transdatums[1]; N = DirectFunctionCall1(numeric_inc, N); sumX = DirectFunctionCall2(numeric_add, sumX, NumericGetDatum(newval)); transdatums[0] = N; transdatums[1] = sumX; result = construct_array(transdatums, 2, NUMERICOID, -1, false, 'i'); return result; } Datum numeric_accum(PG_FUNCTION_ARGS) { ArrayType* transarray = PG_GETARG_ARRAYTYPE_P(0); Numeric newval = PG_GETARG_NUMERIC(1); PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval)); } /* * Optimized case for average of numeric. */ Datum numeric_avg_accum(PG_FUNCTION_ARGS) { ArrayType* transarray = PG_GETARG_ARRAYTYPE_P(0); Numeric newval = PG_GETARG_NUMERIC(1); PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval)); } /* * Integer data types all use Numeric accumulators to share code and * avoid risk of overflow. For int2 and int4 inputs, Numeric accumulation * is overkill for the N and sum(X) values, but definitely not overkill * for the sum(X*X) value. Hence, we use int2_accum and int4_accum only * for stddev/variance --- there are faster special-purpose accumulator * routines for SUM and AVG of these datatypes. */ Datum int2_accum(PG_FUNCTION_ARGS) { ArrayType* transarray = PG_GETARG_ARRAYTYPE_P(0); Datum newval2 = PG_GETARG_DATUM(1); Numeric newval; newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, newval2)); PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval)); } Datum int4_accum(PG_FUNCTION_ARGS) { ArrayType* transarray = PG_GETARG_ARRAYTYPE_P(0); Datum newval4 = PG_GETARG_DATUM(1); Numeric newval; newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, newval4)); PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval)); } Datum int8_accum(PG_FUNCTION_ARGS) { ArrayType* transarray = PG_GETARG_ARRAYTYPE_P(0); Datum newval8 = PG_GETARG_DATUM(1); Numeric newval; newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8)); PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval)); } /* * Optimized case for average of int8. */ Datum int8_avg_accum(PG_FUNCTION_ARGS) { ArrayType* transarray = PG_GETARG_ARRAYTYPE_P(0); Datum newval8 = PG_GETARG_DATUM(1); Numeric newval; newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8)); PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval)); } Datum numeric_avg(PG_FUNCTION_ARGS) { ArrayType* transarray = PG_GETARG_ARRAYTYPE_P(0); Datum* transdatums = NULL; int ndatums; Numeric N, sumX; /* We assume the input is array of numeric */ deconstruct_array(transarray, NUMERICOID, -1, false, 'i', &transdatums, NULL, &ndatums); if (ndatums != 2) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("expected 2-element numeric array"))); N = DatumGetNumeric(transdatums[0]); sumX = DatumGetNumeric(transdatums[1]); /* SQL92 defines AVG of no values to be NULL */ /* N is zero iff no digits (cf. numeric_uminus) */ if (NUMERIC_NDIGITS(N) == 0) PG_RETURN_NULL(); PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, NumericGetDatum(sumX), NumericGetDatum(N))); } /* * Workhorse routine for the standard deviance and variance * aggregates. 'transarray' is the aggregate's transition * array. '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(ArrayType* transarray, bool variance, bool sample, bool* is_null) { Datum* transdatums = NULL; int ndatums; Numeric N, sumX, sumX2, res; NumericVar vN, vsumX, vsumX2, vNminus1; NumericVar* comp = NULL; int rscale; *is_null = false; /* We assume the input is array of numeric */ deconstruct_array(transarray, NUMERICOID, -1, false, 'i', &transdatums, NULL, &ndatums); if (ndatums != 3) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("expected 3-element numeric array"))); N = DatumGetNumeric(transdatums[0]); sumX = DatumGetNumeric(transdatums[1]); sumX2 = DatumGetNumeric(transdatums[2]); if (NUMERIC_IS_NAN(N) || NUMERIC_IS_NAN(sumX) || NUMERIC_IS_NAN(sumX2)) { return make_result(&const_nan); } /* * Handle Big Integer */ if (NUMERIC_IS_BI(N)) { N = makeNumericNormal(N); } init_var_from_num(N, &vN); /* * Sample stddev and variance are undefined when N <= 1; population stddev * is undefined when N == 0. Return NULL in either case. */ if (sample) comp = &const_one; else comp = &const_zero; if (cmp_var(&vN, comp) <= 0) { *is_null = true; return NULL; } init_var(&vNminus1); sub_var(&vN, &const_one, &vNminus1); /* * Handle Big Integer */ if (NUMERIC_IS_BI(sumX)) sumX = makeNumericNormal(sumX); if (NUMERIC_IS_BI(sumX2)) sumX2 = makeNumericNormal(sumX2); init_var_from_num(sumX, &vsumX); init_var_from_num(sumX2, &vsumX2); /* 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) { Numeric res; bool is_null = false; res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0), true, true, &is_null); if (is_null) PG_RETURN_NULL(); else PG_RETURN_NUMERIC(res); } Datum numeric_stddev_samp(PG_FUNCTION_ARGS) { Numeric res; bool is_null = false; res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0), false, true, &is_null); if (is_null) PG_RETURN_NULL(); else PG_RETURN_NUMERIC(res); } Datum numeric_var_pop(PG_FUNCTION_ARGS) { Numeric res; bool is_null = false; res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0), true, false, &is_null); if (is_null) PG_RETURN_NULL(); else PG_RETURN_NUMERIC(res); } Datum numeric_stddev_pop(PG_FUNCTION_ARGS) { Numeric res; bool is_null = false; res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0), false, false, &is_null); if (is_null) PG_RETURN_NULL(); else PG_RETURN_NUMERIC(res); } /* * 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 SQL92 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. */ 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); } } Datum int8_sum(PG_FUNCTION_ARGS) { Numeric oldsum; Datum 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 = DirectFunctionCall1(int8_numeric, PG_GETARG_DATUM(1)); PG_RETURN_DATUM(newval); } /* * 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. */ newval = DirectFunctionCall1(int8_numeric, PG_GETARG_DATUM(1)); PG_RETURN_DATUM(DirectFunctionCall2(numeric_add, NumericGetDatum(oldsum), newval)); } #ifdef PGXC /* * similar to int8_sum, except that the result is casted into int8 */ Datum int8_sum_to_int8(PG_FUNCTION_ARGS) { Datum result_num; Datum numeric_arg; /* if both arguments are null, the result is null */ if (PG_ARGISNULL(0) && PG_ARGISNULL(1)) PG_RETURN_NULL(); /* if either of them is null, the other is the result */ if (PG_ARGISNULL(0)) PG_RETURN_DATUM(PG_GETARG_DATUM(1)); if (PG_ARGISNULL(1)) PG_RETURN_DATUM(PG_GETARG_DATUM(0)); /* * convert the first argument to numeric (second one is converted into * numeric) * add both the arguments using int8_sum * convert the result into int8 using numeric_int8 */ numeric_arg = DirectFunctionCall1(int8_numeric, PG_GETARG_DATUM(0)); result_num = DirectFunctionCall2(int8_sum, numeric_arg, PG_GETARG_DATUM(1)); PG_RETURN_DATUM(DirectFunctionCall1(numeric_int8, result_num)); } #endif /* * Routines for avg(int2) and avg(int4). The transition datatype * is a two-element int8 array, holding count and sum. */ typedef struct Int8TransTypeData { int64 count; int64 sum; } Int8TransTypeData; Datum int1_avg_accum(PG_FUNCTION_ARGS) { ArrayType* transarray = NULL; uint8 newval = PG_GETARG_UINT8(1); Int8TransTypeData* transdata = NULL; /* * 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)) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("expected 2-element int8 array"))); transdata = (Int8TransTypeData*)ARR_DATA_PTR(transarray); transdata->count++; transdata->sum += newval; PG_RETURN_ARRAYTYPE_P(transarray); } Datum int2_avg_accum(PG_FUNCTION_ARGS) { ArrayType* transarray = NULL; int16 newval = PG_GETARG_INT16(1); Int8TransTypeData* transdata = NULL; /* * 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)) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("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 = NULL; int32 newval = PG_GETARG_INT32(1); Int8TransTypeData* transdata = NULL; /* * 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)) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("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 = NULL; Datum countd, sumd; if (ARR_HASNULL(transarray) || ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData)) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("expected 2-element int8 array"))); transdata = (Int8TransTypeData*)ARR_DATA_PTR(transarray); /* SQL92 defines AVG of no values to be NULL */ if (transdata->count == 0) PG_RETURN_NULL(); countd = DirectFunctionCall1(int8_numeric, Int64GetDatumFast(transdata->count)); sumd = DirectFunctionCall1(int8_numeric, Int64GetDatumFast(transdata->sum)); PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, sumd, countd)); } /* ---------------------------------------------------------------------- * * 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; 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; 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 NaNs --- callers must eliminate * the possibility of NaN first. (make_result() is an exception.) * * ---------------------------------------------------------------------- */ /* * 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); init_alloc_var(var, ndigits); } /* * zero_var() - * * Set a variable to ZERO. * Note: its dscale is not touched. */ static void zero_var(NumericVar* var) { digitbuf_free(var); quick_init_var(var); 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, and it doesn't * accept "NaN" either. It returns the end+1 position 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 = NULL; int sign = NUMERIC_POS; int dweight = -1; int ddigits; int dscale = 0; int weight; int ndigits; int offset; NumericDigit* digits = NULL; /* * 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; default: break; } if (*cp == '.') { have_dp = TRUE; cp++; } if (!isdigit((unsigned char)*cp) && u_sess->attr.attr_sql.sql_compatibility == B_FORMAT) { char* cp = (char*)palloc0(sizeof(char)); return cp; } if (!isdigit((unsigned char)*cp)) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type numeric: \"%s\"", str))); decdigits = (unsigned char*)palloc(strlen(cp) + DEC_DIGITS * 2); /* leading padding for digit alignment later */ errno_t rc = memset_s(decdigits, strlen(cp) + DEC_DIGITS * 2, 0, DEC_DIGITS); securec_check_c(rc, "", ""); 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 numeric: \"%s\"", str))); have_dp = TRUE; cp++; } else break; } ddigits = i - DEC_DIGITS; /* trailing padding for digit alignment later */ rc = memset_s(decdigits + i, DEC_DIGITS - 1, 0, DEC_DIGITS - 1); securec_check(rc, "\0", "\0"); /* Handle exponent, if any */ if (*cp == 'e' || *cp == 'E') { long exponent; char* endptr = NULL; cp++; exponent = strtol(cp, &endptr, 10); if (endptr == cp) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type numeric: \"%s\"", str))); cp = endptr; if (exponent > NUMERIC_MAX_PRECISION || exponent < -NUMERIC_MAX_PRECISION) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type numeric: \"%s\"", str))); 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_ext(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) { Assert(!NUMERIC_IS_BI(num)); int ndigits = NUMERIC_NDIGITS(num); alloc_var(dest, ndigits); dest->weight = NUMERIC_WEIGHT(num); dest->sign = NUMERIC_SIGN(num); dest->dscale = NUMERIC_DSCALE(num); if (ndigits > 0) { errno_t rc = memcpy_s(dest->digits, ndigits * sizeof(NumericDigit), NUMERIC_DIGITS(num), ndigits * sizeof(NumericDigit)); securec_check(rc, "\0", "\0"); } } /* * 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. */ void init_var_from_num(Numeric num, NumericVar* dest) { Assert(!NUMERIC_IS_BI(num)); 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 = dest->ndb; } /* * set_var_from_var() - * * Copy one variable into another */ static void set_var_from_var(const NumericVar* value, NumericVar* dest) { NumericDigit* newbuf = NULL; errno_t rc = 0; newbuf = digitbuf_alloc(value->ndigits + 1); newbuf[0] = 0; /* spare digit for rounding */ if (value->ndigits > 0) { rc = memcpy_s( newbuf + 1, value->ndigits * sizeof(NumericDigit), value->digits, value->ndigits * sizeof(NumericDigit)); securec_check(rc, "\0", "\0"); } digitbuf_free(dest); rc = memmove_s(dest, sizeof(NumericVar), value, sizeof(NumericVar)); securec_check(rc, "\0", "\0"); dest->buf = newbuf; dest->digits = newbuf + 1; } /* * init_var_from_var() - * * init one variable from another - they must NOT be the same variable */ static void init_var_from_var(const NumericVar *value, NumericVar *dest) { init_alloc_var(dest, value->ndigits); dest->weight = value->weight; dest->sign = value->sign; dest->dscale = value->dscale; errno_t rc = memcpy_s(dest->digits, value->ndigits * sizeof(NumericDigit), value->digits, value->ndigits * sizeof(NumericDigit)); securec_check(rc, "\0", "\0"); } static void remove_tail_zero(char *ascii) { if (!HIDE_TAILING_ZERO || ascii == NULL) { return; } int len = 0; bool is_decimal = false; while (ascii[len] != '\0') { if (ascii[len] == '.') { is_decimal = true; } len++; } if (!is_decimal) { return; } len--; while (ascii[len] == '0') { ascii[len] = '\0'; len--; } if (ascii[len] == '.') { ascii[len] = '\0'; len--; } if (len == -1) { len++; ascii[len] = '0'; len++; ascii[len] = '\0'; } return; } /* * get_str_from_var() - * * Convert a var to text representation (guts of numeric_out). * CAUTION: var's contents may be modified by rounding! * Returns a palloc'd string. */ static char* get_str_from_var(NumericVar* var) { int dscale; char* str = NULL; char* cp = NULL; char* endcp = NULL; 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 = (char*)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; if (DISPLAY_LEADING_ZERO) { *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 |= (uint32)(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'; remove_tail_zero(str); 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. * * CAUTION: var's contents may be modified by rounding! * * Returns a palloc'd string. */ static char* get_str_from_var_sci(NumericVar* var, int rscale) { int32 exponent; NumericVar denominator; NumericVar significand; int denom_scale; size_t len; char* str = NULL; char* sig_out = NULL; errno_t ret = EOK; 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; } /* * The denominator is set to 10 raised to the power of the exponent. * * We then divide var by the denominator to get the significand, rounding * to rscale decimal digits in the process. */ if (exponent < 0) denom_scale = -exponent; else denom_scale = 0; init_var(&denominator); init_var(&significand); power_var_int(&const_ten, exponent, &denominator, denom_scale); div_var(var, &denominator, &significand, rscale, true); sig_out = get_str_from_var(&significand); free_var(&denominator); free_var(&significand); /* * 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; const size_t slen = len + 1; str = (char*)palloc(slen); ret = snprintf_s(str, slen, len, "%se%+03d", sig_out, exponent); securec_check_ss(ret, "", ""); pfree_ext(sig_out); return str; } /* * make_result() - * * Create the packed db numeric format in palloc()'d memory from * a variable. */ Numeric make_result(NumericVar* var) { Numeric result; NumericDigit* digits = var->digits; int weight = var->weight; int sign = var->sign; int n; Size len; if (sign == NUMERIC_NAN) { result = (Numeric)palloc(NUMERIC_HDRSZ_SHORT); SET_VARSIZE(result, NUMERIC_HDRSZ_SHORT); result->choice.n_header = NUMERIC_NAN; /* 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; } MemCpy(NUMERIC_DIGITS(result), digits, n * sizeof(NumericDigit)); Assert(NUMERIC_NDIGITS(result) == (unsigned int)(n)); /* Check for overflow of int16 fields */ if (NUMERIC_WEIGHT(result) != weight || NUMERIC_DSCALE(result) != var->dscale) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("value overflows numeric format"))); dump_numeric("make_result()", result); return result; } Numeric makeNumeric(NumericVar* var) { return make_result(var); } /* * apply_typmod() - * * Do bounds checking and rounding according to the attributes * typmod field. */ static void apply_typmod(NumericVar* var, int32 typmod) { int precision; int scale; int maxdigits; int ddigits; int i; /* Do nothing if we have a default typmod (-1) */ if (typmod < (int32)(VARHDRSZ)) return; typmod -= VARHDRSZ; precision = (int32)(((uint32)(typmod) >> 16) & 0xffff); scale = (int32)(((uint32)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; } } } /* * Convert numeric to int8, rounding if needed. * * Note: param can_ignore controls the function raising ERROR or WARNING. TRUE means overflowing will report WARNING * and set result to INT64_MAX/INT64_MIN instead. FALSE make it raise ERROR directly. FALSE DEFAULTED. It should be only * used for controls of keyword IGNORE. * * If overflow, return false (no error is raised). Return true if okay. */ bool numericvar_to_int64(const NumericVar* var, int64* result, bool can_ignore) { NumericDigit* digits = NULL; int ndigits; int weight; int i; int64 val; bool neg = false; 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); if (can_ignore) { *result = neg ? LONG_MIN : LONG_MAX; ereport(WARNING, (errmsg("value out of range"))); return true; } return false; } if (i < ndigits) { if (unlikely(pg_sub_s64_overflow(val, digits[i], &val))) { free_var(&rounded); if (can_ignore) { *result = neg ? LONG_MIN : LONG_MAX; ereport(WARNING, (errmsg("value out of range"))); return true; } 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. */ void int64_to_numericvar(int64 val, NumericVar* var) { uint64 uval, newuval; NumericDigit* ptr = NULL; 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 float8; if out of range, return +/- HUGE_VAL */ static double numeric_to_double_no_overflow(Numeric num) { char* tmp = NULL; double val; char* endptr = NULL; tmp = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(num))); /* 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 double precision: \"%s\"", tmp))); } pfree_ext(tmp); return val; } /* As above, but work from a NumericVar */ static double numericvar_to_double_no_overflow(NumericVar* var) { char* tmp = NULL; double val; char* endptr = NULL; 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 double precision: \"%s\"", tmp))); } pfree_ext(tmp); return val; } /* * cmp_var() - * * Compare two values on variable level. We assume zeroes have been * truncated to no digits. */ static int cmp_var(NumericVar* var1, 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. */ void add_var(NumericVar* var1, NumericVar* var2, NumericVar* result) { /* * Decide on the signs of the two variables what to do */ if (var1->sign == NUMERIC_POS) { if (var2->sign == NUMERIC_POS) { /* * Both are positive result = +(ABS(var1) + ABS(var2)) */ add_abs(var1, var2, result); result->sign = NUMERIC_POS; } else { /* * var1 is positive, var2 is negative Must compare absolute values */ switch (cmp_abs(var1, var2)) { case 0: /* ---------- * ABS(var1) == ABS(var2) * result = ZERO * ---------- */ 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; default: 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; default: break; } } else { /* ---------- * Both are negative * result = -(ABS(var1) + ABS(var2)) * ---------- */ add_abs(var1, var2, result); result->sign = NUMERIC_NEG; } } } /* * sub_var() - * * Full version of sub functionality on variable level (handling signs). * result might point to one of the operands too without danger. */ static void sub_var(NumericVar* var1, NumericVar* var2, NumericVar* result) { /* * Decide on the signs of the two variables what to do */ if (var1->sign == NUMERIC_POS) { if (var2->sign == NUMERIC_NEG) { /* ---------- * var1 is positive, var2 is negative * result = +(ABS(var1) + ABS(var2)) * ---------- */ add_abs(var1, var2, result); result->sign = NUMERIC_POS; } else { /* ---------- * Both are positive * Must compare absolute values * ---------- */ switch (cmp_abs(var1, var2)) { case 0: /* ---------- * ABS(var1) == ABS(var2) * result = ZERO * ---------- */ 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; default: 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; default: 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(NumericVar* var1, NumericVar* var2, NumericVar* result, int rscale) { int res_ndigits; int res_sign; int res_weight; int maxdigits; int* dig = NULL; int carry; int maxdig; int newdig; int var1ndigits; int var2ndigits; NumericDigit* var1digits = NULL; NumericDigit* var2digits = NULL; NumericDigit* res_digits = NULL; 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) { 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 - 1. */ for (i2 = Min(var2ndigits - 1, res_ndigits - i1 - 3), i = i1 + i2 + 2; i2 >= 0; i2--) dig[i--] += 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_ext(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(NumericVar* var1, 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 = NULL; NumericDigit* divisor = NULL; NumericDigit* res_digits = NULL; 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); errno_t rc = memcpy_s(dividend + 1, var1ndigits * sizeof(NumericDigit), var1->digits, var1ndigits * sizeof(NumericDigit)); securec_check(rc, "\0", "\0"); rc = memcpy_s(divisor + 1, var2ndigits * sizeof(NumericDigit), var2->digits, var2ndigits * sizeof(NumericDigit)); securec_check(rc, "\0", "\0"); /* * 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_ext(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. */ static void div_var_fast(NumericVar* var1, NumericVar* var2, NumericVar* result, int rscale, bool round) { int div_ndigits; int res_sign; int res_weight; int* div = NULL; int qdigit; int carry; int maxdiv; int newdig; NumericDigit* res_digits = NULL; 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; int tdiv[NUMERIC_LOCAL_NDIG]; /* * 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; /* Must be at least var1ndigits, too, to simplify data-loading loop */ if (div_ndigits < var1ndigits) div_ndigits = var1ndigits; /* * 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. */ i = (div_ndigits + 1) * sizeof(int); if (div_ndigits > NUMERIC_LOCAL_NMAX) { div = (int *) palloc0(i); } else { errno_t rc = memset_s(tdiv, i, 0, i); securec_check(rc, "\0", "\0"); div = tdiv; } for (i = 0; i < var1ndigits; 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. */ 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). */ 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 */ carry = 0; for (i = 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. */ maxdiv = Abs(newdig) / (NBASE - 1); maxdiv = Max(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 */ 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. We don't need to worry about * overflow here since this should nearly cancel with the subtraction * of the divisor. */ 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; /* * 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, 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); if (div != tdiv) { pfree_ext(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(NumericVar* var1, 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 * openGauss 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(NumericVar* var1, 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); } /* * ceil_var() - * * Return the smallest integer greater than or equal to the argument * on variable level */ static void ceil_var(NumericVar* var, NumericVar* result) { NumericVar tmp; init_var(&tmp); set_var_from_var(var, &tmp); 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(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); } /* * sqrt_var() - * * Compute the square root of x using Newton's algorithm */ static void sqrt_var(NumericVar* arg, NumericVar* result, int rscale) { NumericVar tmp_arg; NumericVar tmp_val; NumericVar last_val; int local_rscale; int stat; local_rscale = rscale + 8; 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"))); /* Copy arg in case it is the same var as result */ init_var_from_var(arg, &tmp_arg); /* * Initialize the result to the first guess */ alloc_var(result, 1); result->digits[0] = tmp_arg.digits[0] / 2; if (result->digits[0] == 0) result->digits[0] = 1; result->weight = tmp_arg.weight / 2; result->sign = NUMERIC_POS; init_var_from_var(result, &last_val); quick_init_var(&tmp_val); for (;;) { div_var_fast(&tmp_arg, result, &tmp_val, local_rscale, true); add_var(result, &tmp_val, result); mul_var(result, &const_zero_point_five, result, local_rscale); if (cmp_var(&last_val, result) == 0) break; set_var_from_var(result, &last_val); } free_var(&last_val); free_var(&tmp_val); free_var(&tmp_arg); /* Round to requested precision */ round_var(result, rscale); } /* * exp_var() - * * Raise e to the power of x, computed to rscale fractional digits */ static void exp_var(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 */ if (Abs(val) >= NUMERIC_MAX_RESULT_SCALE * 3) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("value overflows numeric format"))); /* 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. */ static int estimate_ln_dweight(NumericVar* var) { int ln_dweight; 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(NumericVar* arg, NumericVar* result, int rscale) { NumericVar x; NumericVar xx; NumericVar ni; NumericVar elem; NumericVar fact; 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); init_var_from_var(arg, &x); init_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 have 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). */ while (cmp_var(&x, &const_zero_point_nine) <= 0) { local_rscale = rscale - x.weight * DEC_DIGITS / 2 + 8; local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE); sqrt_var(&x, &x, local_rscale); mul_var(&fact, &const_two, &fact, 0); } while (cmp_var(&x, &const_one_point_one) >= 0) { local_rscale = rscale - x.weight * DEC_DIGITS / 2 + 8; local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE); sqrt_var(&x, &x, local_rscale); mul_var(&fact, &const_two, &fact, 0); } /* * 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. */ local_rscale = rscale + 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(NumericVar* base, 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(NumericVar* base, NumericVar* exp, NumericVar* result) { NumericVar ln_base; NumericVar ln_num; int ln_dweight; int rscale; 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)) { int expval = (int)expval64; /* Test for overflow by reverse-conversion. */ if ((int64)expval == expval64) { /* 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, expval, 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(&ln_base); init_var(&ln_num); /* ---------- * 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); local_rscale = 8 - ln_dweight; local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE); local_rscale = Min(local_rscale, NUMERIC_MAX_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 test with fuzz factor */ if (Abs(val) > NUMERIC_MAX_RESULT_SCALE * 3.01) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("value overflows numeric format"))); 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); /* set the scale for the real exp * ln(base) calculation */ local_rscale = rscale + (int)val - 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); free_var(&ln_num); free_var(&ln_base); } /* * power_var_int() - * * Raise base to the power of exp, where exp is an integer. */ static void power_var_int(NumericVar* base, int exp, NumericVar* result, int rscale) { double f; int p; int i; int sig_digits; unsigned int mask; bool neg = false; 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. * http://en.wikipedia.org/wiki/Exponentiation#Zero_to_the_zero_pow * er */ 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(Abs(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); } } /* ---------------------------------------------------------------------- * * Following are the lowest level functions that operate unsigned * on the variable level * * ---------------------------------------------------------------------- */ /* ---------- * cmp_abs() - * * Compare the absolute values of var1 and var2 * Returns: -1 for ABS(var1) < ABS(var2) * 0 for ABS(var1) == ABS(var2) * 1 for ABS(var1) > ABS(var2) * ---------- */ static int cmp_abs(NumericVar* var1, NumericVar* var2) { 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(NumericVar* var1, NumericVar* var2, NumericVar* result) { NumericDigit* res_buf = NULL; NumericDigit* res_digits = NULL; 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); 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(NumericVar* var1, NumericVar* var2, NumericVar* result) { NumericDigit* res_buf = NULL; NumericDigit* res_digits = NULL; 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); 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; } #ifdef PGXC Datum numeric_collect(PG_FUNCTION_ARGS) { ArrayType* collectarray = PG_GETARG_ARRAYTYPE_P(0); ArrayType* transarray = PG_GETARG_ARRAYTYPE_P(1); Datum* collectdatums = NULL; Datum* transdatums = NULL; int ndatums; Datum N, sumX, sumX2; /* We assume the input is array of numeric */ deconstruct_array(collectarray, NUMERICOID, -1, false, 'i', &collectdatums, NULL, &ndatums); if (ndatums != 3) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("expected 3-element numeric array"))); N = collectdatums[0]; sumX = collectdatums[1]; sumX2 = collectdatums[2]; /* We assume the input is array of numeric */ deconstruct_array(transarray, NUMERICOID, -1, false, 'i', &transdatums, NULL, &ndatums); if (ndatums != 3) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("expected 3-element numeric array"))); N = DirectFunctionCall2(numeric_add, N, transdatums[0]); sumX = DirectFunctionCall2(numeric_add, sumX, transdatums[1]); sumX2 = DirectFunctionCall2(numeric_add, sumX2, transdatums[2]); collectdatums[0] = N; collectdatums[1] = sumX; collectdatums[2] = sumX2; PG_RETURN_ARRAYTYPE_P(construct_array(collectdatums, 3, NUMERICOID, -1, false, 'i')); } Datum numeric_avg_collect(PG_FUNCTION_ARGS) { ArrayType* collectarray = PG_GETARG_ARRAYTYPE_P(0); ArrayType* transarray = PG_GETARG_ARRAYTYPE_P(1); Datum* collectdatums = NULL; Datum* transdatums = NULL; int ndatums; Datum N, sumX; /* We assume the input is array of numeric */ deconstruct_array(collectarray, NUMERICOID, -1, false, 'i', &collectdatums, NULL, &ndatums); if (ndatums != 2) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("expected 2-element numeric array"))); N = collectdatums[0]; sumX = collectdatums[1]; /* We assume the input is array of numeric */ deconstruct_array(transarray, NUMERICOID, -1, false, 'i', &transdatums, NULL, &ndatums); if (ndatums != 2) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("expected 2-element numeric array"))); N = DirectFunctionCall2(numeric_add, N, transdatums[0]); sumX = DirectFunctionCall2(numeric_add, sumX, transdatums[1]); collectdatums[0] = N; collectdatums[1] = sumX; PG_RETURN_ARRAYTYPE_P(construct_array(collectdatums, 2, NUMERICOID, -1, false, 'i')); } Datum int8_avg_collect(PG_FUNCTION_ARGS) { ArrayType* collectarray = NULL; ArrayType* transarray = PG_GETARG_ARRAYTYPE_P(1); Int8TransTypeData* collectdata = NULL; Int8TransTypeData* transdata = NULL; /* * If we're invoked by nodeAgg, 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 (fcinfo->context && (IsA(fcinfo->context, AggState) || IsA(fcinfo->context, WindowAggState))) collectarray = PG_GETARG_ARRAYTYPE_P(0); else collectarray = PG_GETARG_ARRAYTYPE_P_COPY(0); if (ARR_HASNULL(collectarray) || ARR_SIZE(collectarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData)) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("expected 2-element int8 array"))); collectdata = (Int8TransTypeData*)ARR_DATA_PTR(collectarray); if (ARR_HASNULL(transarray) || ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData)) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("expected 2-element int8 array"))); transdata = (Int8TransTypeData*)ARR_DATA_PTR(transarray); collectdata->count += transdata->count; collectdata->sum += transdata->sum; PG_RETURN_ARRAYTYPE_P(collectarray); } #endif /* Convert numeric to interval with format*/ Datum numtodsinterval(PG_FUNCTION_ARGS) { Datum num = PG_GETARG_DATUM(0); Datum fmt = PG_GETARG_DATUM(1); Oid collation = PG_GET_COLLATION(); Datum result; errno_t errorno = 0; int str_len; char* buf = NULL; char* cp = NULL; CHECK_RETNULL_INIT(); StringInfoData str; initStringInfo(&str); appendStringInfoString(&str, DatumGetCString(CHECK_RETNULL_CALL1(numeric_out, collation, num))); appendStringInfoString(&str, " "); appendStringInfoString(&str, TextDatumGetCString(fmt)); cp = str.data; if (*cp == '.' || (*cp == '-' && *(cp + 1) == '.')) { str_len = str.len + 2; buf = (char*)palloc0(str_len); if (*cp == '.') { errorno = snprintf_s(buf, str_len, str_len - 1, "0.%s", cp + 1); } else { errorno = snprintf_s(buf, str_len, str_len - 1, "-0.%s", cp + 2); } securec_check_ss(errorno, "\0", "\0"); resetStringInfo(&str); appendStringInfoString(&str, buf); pfree_ext(buf); } result = CHECK_RETNULL_CALL3( interval_in, collation, CStringGetDatum(str.data), ObjectIdGetDatum(InvalidOid), Int32GetDatum(-1)); pfree_ext(str.data); CHECK_RETNULL_RETURN_DATUM(result); } /* Convert numeric to interval */ Datum numeric_interval(PG_FUNCTION_ARGS) { Datum num = PG_GETARG_DATUM(0); const char* fmt = "day"; Oid collation = PG_GET_COLLATION(); Datum result; StringInfoData str; errno_t errorno = 0; int str_len; char* buf = NULL; char* cp = NULL; CHECK_RETNULL_INIT(); initStringInfo(&str); appendStringInfoString(&str, DatumGetCString(CHECK_RETNULL_CALL1(numeric_out, collation, num))); appendStringInfoString(&str, " "); appendStringInfoString(&str, fmt); cp = str.data; if (*cp == '.' || (*cp == '-' && *(cp + 1) == '.')) { str_len = str.len + 2; buf = (char*)palloc0(str_len); if (*cp == '.') { errorno = snprintf_s(buf, str_len, str_len - 1, "0.%s", cp + 1); } else { errorno = snprintf_s(buf, str_len, str_len - 1, "-0.%s", cp + 2); } securec_check_ss(errorno, "\0", "\0"); resetStringInfo(&str); appendStringInfoString(&str, buf); pfree_ext(buf); } result = CHECK_RETNULL_CALL3( interval_in, collation, CStringGetDatum(str.data), ObjectIdGetDatum(InvalidOid), Int32GetDatum(-1)); pfree_ext(str.data); CHECK_RETNULL_RETURN_DATUM(result); } ScalarVector* vnumeric_sum(PG_FUNCTION_ARGS) { ScalarVector* pVector = (ScalarVector*)PG_GETARG_DATUM(0); int idx = PG_GETARG_DATUM(1); hashCell** loc = (hashCell**)PG_GETARG_DATUM(2); MemoryContext context = (MemoryContext)PG_GETARG_DATUM(3); hashCell* cell = NULL; ScalarValue* pVal = pVector->m_vals; uint8* flag = pVector->m_flag; int nrows = pVector->m_rows; Datum args[2]; Datum result; FunctionCallInfoData finfo; bictl ctl; Numeric leftarg, rightarg; // left-hand and right-hand operand of addition uint16 num1Flags, num2Flags; // numeric flags of num1 and num2 int arg1, arg2, i; finfo.arg = &args[0]; ctl.context = context; for (i = 0; i < nrows; i++) { cell = loc[i]; if (cell && IS_NULL(flag[i]) == false) // only do when not null { if (NOT_NULL(cell->m_val[idx].flag)) { leftarg = (Numeric)(cell->m_val[idx].val); rightarg = DatumGetBINumeric(pVal[i]); num1Flags = NUMERIC_NB_FLAGBITS(leftarg); num2Flags = NUMERIC_NB_FLAGBITS(rightarg); if (likely(NUMERIC_FLAG_IS_BI(num1Flags) && NUMERIC_FLAG_IS_BI(num2Flags))) { arg1 = NUMERIC_FLAG_IS_BI128(num1Flags); arg2 = NUMERIC_FLAG_IS_BI128(num2Flags); ctl.store_pos = cell->m_val[idx].val; // call big integer fast add function (BiAggFunMatrix[BI_AGG_ADD][arg1][arg2])(leftarg, rightarg, &ctl); // ctl.store_pos may be pointed to new address. cell->m_val[idx].val = ctl.store_pos; } else { // call numeric_add args[0] = NumericGetDatum(leftarg); args[1] = NumericGetDatum(rightarg); result = numeric_add(&finfo); cell->m_val[idx].val = replaceVariable(context, cell->m_val[idx].val, result); } SET_NOTNULL(cell->m_val[idx].flag); } else { /* make sure cell->m_val[idx].val is 4 bytes header */ leftarg = DatumGetBINumeric(pVal[i]); cell->m_val[idx].val = addVariable(context, NumericGetDatum(leftarg)); SET_NOTNULL(cell->m_val[idx].flag); } } } return NULL; } /* * @Description : sum(numeric) function. * * @in m_loc : location in hash table. * @in pVal : vector to be calculated. * @out m_data[idx] : value of sum(numeric) for each group. * @out m_data[idx + 1] : number of sum(numeric) for each group. */ ScalarVector* vsnumeric_sum(PG_FUNCTION_ARGS) { ScalarVector* pVector = (ScalarVector*)PG_GETARG_DATUM(0); int idx = (int)PG_GETARG_DATUM(1); uint32* loc = (uint32*)PG_GETARG_DATUM(2); SonicDatumArray** sdata = (SonicDatumArray**)PG_GETARG_DATUM(3); SonicEncodingDatumArray* data = (SonicEncodingDatumArray*)sdata[idx]; ScalarValue* pVal = pVector->m_vals; uint8* flag = pVector->m_flag; int nrows = pVector->m_rows; Datum args[2]; Datum result; FunctionCallInfoData finfo; Datum* leftdata = NULL; uint8 leftflag; bictl ctl; /* left-hand and right-hand operand of addition */ Numeric leftarg, rightarg; /* numeric flags of num1 and num2 */ uint16 num1Flags, num2Flags; int arg1, arg2, i; int arrIndx, atomIndx; finfo.arg = &args[0]; ctl.context = data->m_cxt; for (i = 0; i < nrows; i++) { /* only consider not null numeric value */ if ((loc[i] != 0) && NOT_NULL(flag[i])) { /* get atom location in *data* array */ arrIndx = getArrayIndx(loc[i], data->m_nbit); atomIndx = getArrayLoc(loc[i], data->m_atomSize - 1); /* get previous sum result flag */ leftflag = data->getNthNullFlag(arrIndx, atomIndx); if (NOT_NULL(leftflag)) { /* previous sum result(leftdata) */ leftdata = &((Datum*)data->m_arr[arrIndx]->data)[atomIndx]; /* updata previous sum result based on the given pVal[i] */ leftarg = DatumGetBINumeric(leftdata[0]); rightarg = DatumGetBINumeric(pVal[i]); num1Flags = NUMERIC_NB_FLAGBITS(leftarg); num2Flags = NUMERIC_NB_FLAGBITS(rightarg); if (likely(NUMERIC_FLAG_IS_BI(num1Flags) && NUMERIC_FLAG_IS_BI(num2Flags))) { arg1 = NUMERIC_FLAG_IS_BI128(num1Flags); arg2 = NUMERIC_FLAG_IS_BI128(num2Flags); ctl.store_pos = leftdata[0]; /* call big integer fast add function */ (BiAggFunMatrix[BI_AGG_ADD][arg1][arg2])(leftarg, rightarg, &ctl); leftdata[0] = ctl.store_pos; } else { /* call numeric_add */ args[0] = NumericGetDatum(leftarg); args[1] = NumericGetDatum(rightarg); result = numeric_add(&finfo); /* update sum result */ leftdata[0] = data->replaceVariable(leftdata[0], result); } } else { leftarg = DatumGetBINumeric(pVal[i]); /* initialize sum result */ data->setValue(NumericGetDatum(leftarg), false, arrIndx, atomIndx); } } } return NULL; } ScalarVector* vnumeric_abs(PG_FUNCTION_ARGS) { ScalarValue* parg1 = PG_GETARG_VECVAL(0); ScalarVector* pvector1 = PG_GETARG_VECTOR(0); int32 nvalues = PG_GETARG_INT32(1); ScalarValue* presult = PG_GETARG_VECVAL(2); ScalarVector* presultVector = PG_GETARG_VECTOR(2); uint8* pflagsRes = (uint8*)(presultVector->m_flag); bool* pselection = PG_GETARG_SELECTION(3); Datum args; int i; FunctionCallInfoData finfo; finfo.arg = &args; if (pselection != NULL) { for (i = 0; i < nvalues; i++) { if (pvector1->IsNull(i)) { SET_NULL(pflagsRes[i]); continue; } if (pselection[i]) { args = ScalarVector::Decode(parg1[i]); presult[i] = numeric_abs(&finfo); SET_NOTNULL(pflagsRes[i]); } } } else { for (i = 0; i < nvalues; i++) { if (pvector1->IsNull(i)) { SET_NULL(pflagsRes[i]); continue; } args = ScalarVector::Decode(parg1[i]); presult[i] = numeric_abs(&finfo); SET_NOTNULL(pflagsRes[i]); } } PG_GETARG_VECTOR(2)->m_rows = nvalues; return PG_GETARG_VECTOR(2); } ScalarVector* vnumeric_fac(PG_FUNCTION_ARGS) { ScalarVector* pvector1 = PG_GETARG_VECTOR(0); ScalarValue* pVal = pvector1->m_vals; int32 nvalues = PG_GETARG_INT32(1); ScalarValue* presult = PG_GETARG_VECVAL(2); ScalarVector* presultVector = PG_GETARG_VECTOR(2); uint8* pflagsRes = (uint8*)(presultVector->m_flag); bool* pselection = PG_GETARG_SELECTION(3); Datum args; int i; FunctionCallInfoData finfo; finfo.arg = &args; if (pselection != NULL) { for (i = 0; i < nvalues; i++) { if (pvector1->IsNull(i)) { SET_NULL(pflagsRes[i]); continue; } if (pselection[i]) { args = pVal[i]; presult[i] = numeric_fac(&finfo); SET_NOTNULL(pflagsRes[i]); } } } else { for (i = 0; i < nvalues; i++) { if (pvector1->IsNull(i)) { SET_NULL(pflagsRes[i]); continue; } args = pVal[i]; presult[i] = numeric_fac(&finfo); SET_NOTNULL(pflagsRes[i]); } } PG_GETARG_VECTOR(2)->m_rows = nvalues; return PG_GETARG_VECTOR(2); } /* * The internal realization of function vnumeric_ne. */ template static void vnumeric_ne_internal(ScalarVector* arg1, uint8* pflags1, ScalarVector* arg2, uint8* pflags2, ScalarVector* vresult, uint8* pflagRes, Numeric num, NumericDigit* vardigits, int varndigits, int varweight, int varsign, bool is_nan, int idx) { int result; if (BOTH_NOT_NULL(pflags1[idx], pflags2[idx])) { Numeric num1 = m_const1 ? num : DatumGetNumeric(arg1->m_vals[idx]); Numeric num2 = m_const2 ? num : DatumGetNumeric(arg2->m_vals[idx]); bool is_nan1 = m_const1 ? is_nan : NUMERIC_IS_NAN(num1); bool is_nan2 = m_const2 ? is_nan : NUMERIC_IS_NAN(num2); if (is_nan1) { result = is_nan2 ? 0 : 1; } else if (is_nan2) { result = -1; } else { NumericDigit* vardigits1 = m_const1 ? vardigits : NUMERIC_DIGITS(num1); NumericDigit* vardigits2 = m_const2 ? vardigits : NUMERIC_DIGITS(num2); int varndigits1 = m_const1 ? varndigits : NUMERIC_NDIGITS(num1); int varndigits2 = m_const2 ? varndigits : NUMERIC_NDIGITS(num2); int varweight1 = m_const1 ? varweight : NUMERIC_WEIGHT(num1); int varweight2 = m_const2 ? varweight : NUMERIC_WEIGHT(num2); int varsign1 = m_const1 ? varsign : NUMERIC_SIGN(num1); int varsign2 = m_const2 ? varsign : NUMERIC_SIGN(num2); result = cmp_var_common( vardigits1, varndigits1, varweight1, varsign1, vardigits2, varndigits2, varweight2, varsign2); } vresult->m_vals[idx] = BoolGetDatum(result); SET_NOTNULL(pflagRes[idx]); } else SET_NULL(pflagRes[idx]); } ScalarVector* vnumeric_ne(PG_FUNCTION_ARGS) { ScalarVector* arg1 = PG_GETARG_VECTOR(0); ScalarVector* arg2 = PG_GETARG_VECTOR(1); uint8* pflags1 = arg1->m_flag; uint8* pflags2 = arg2->m_flag; int32 nvalues = PG_GETARG_INT32(2); ScalarVector* vresult = PG_GETARG_VECTOR(3); uint8* pflagRes = (uint8*)(vresult->m_flag); bool* pselection = PG_GETARG_SELECTION(4); int k; Numeric num = NULL; NumericDigit* vardigits = NULL; int varndigits = 0; int varweight = 0; int varsign = 0; bool is_nan = 0; /* * Since if both arg1->m_const and arg2->m_const are true, * we could never enter here. In the following, we only * need to consider there cases. When one of the vector * is a const vector, some parameters that we need in * cmp_var_common could be predefined. */ if (arg1->m_const && NOT_NULL(pflags1[0])) { num = DatumGetNumeric(arg1->m_vals[0]); vardigits = NUMERIC_DIGITS(num); varndigits = NUMERIC_NDIGITS(num); varweight = NUMERIC_WEIGHT(num); varsign = NUMERIC_SIGN(num); is_nan = NUMERIC_IS_NAN(num) ? true : false; } else if (arg2->m_const && NOT_NULL(pflags2[0])) { num = DatumGetNumeric(arg2->m_vals[0]); vardigits = NUMERIC_DIGITS(num); varndigits = NUMERIC_NDIGITS(num); varweight = NUMERIC_WEIGHT(num); varsign = NUMERIC_SIGN(num); is_nan = NUMERIC_IS_NAN(num) ? true : false; } if (pselection != NULL) { for (k = 0; k < nvalues; k++) { if (pselection[k]) { if (!arg1->m_const && arg2->m_const) vnumeric_ne_internal(arg1, pflags1, arg2, pflags2, vresult, pflagRes, num, vardigits, varndigits, varweight, varsign, is_nan, k); else if (arg1->m_const && !arg2->m_const) vnumeric_ne_internal(arg1, pflags1, arg2, pflags2, vresult, pflagRes, num, vardigits, varndigits, varweight, varsign, is_nan, k); else vnumeric_ne_internal(arg1, pflags1, arg2, pflags2, vresult, pflagRes, num, vardigits, varndigits, varweight, varsign, is_nan, k); } } } else { for (k = 0; k < nvalues; k++) { if (!arg1->m_const && arg2->m_const) vnumeric_ne_internal(arg1, pflags1, arg2, pflags2, vresult, pflagRes, num, vardigits, varndigits, varweight, varsign, is_nan, k); else if (arg1->m_const && !arg2->m_const) vnumeric_ne_internal(arg1, pflags1, arg2, pflags2, vresult, pflagRes, num, vardigits, varndigits, varweight, varsign, is_nan, k); else vnumeric_ne_internal(arg1, pflags1, arg2, pflags2, vresult, pflagRes, num, vardigits, varndigits, varweight, varsign, is_nan, k); } } PG_GETARG_VECTOR(3)->m_rows = nvalues; return PG_GETARG_VECTOR(3); } ScalarVector* vinterval_sum(PG_FUNCTION_ARGS) { ScalarVector* pVector = (ScalarVector*)PG_GETARG_DATUM(0); int idx = PG_GETARG_DATUM(1); hashCell** loc = (hashCell**)PG_GETARG_DATUM(2); MemoryContext context = (MemoryContext)PG_GETARG_DATUM(3); hashCell* cell = NULL; int i; ScalarValue* pVal = pVector->m_vals; uint8* flag = pVector->m_flag; int nrows = pVector->m_rows; Datum args[2]; Datum result; FunctionCallInfoData finfo; finfo.arg = &args[0]; for (i = 0; i < nrows; i++) { cell = loc[i]; if (cell && IS_NULL(flag[i]) == false) // only do when not null { if (IS_NULL(cell->m_val[idx].flag)) { cell->m_val[idx].val = addVariable(context, pVal[i]); SET_NOTNULL(cell->m_val[idx].flag); } else { args[0] = PointerGetDatum((char*)cell->m_val[idx].val + VARHDRSZ_SHORT); args[1] = PointerGetDatum((char*)pVal[i] + VARHDRSZ_SHORT); result = interval_pl(&finfo); cell->m_val[idx].val = replaceVariable( context, cell->m_val[idx].val, ScalarVector::DatumToScalar(result, INTERVALOID, false)); } } } return NULL; } ScalarVector* vcash_sum(PG_FUNCTION_ARGS) { ScalarVector* pVector = (ScalarVector*)PG_GETARG_DATUM(0); int idx = PG_GETARG_DATUM(1); hashCell** loc = (hashCell**)PG_GETARG_DATUM(2); hashCell* cell = NULL; int i; ScalarValue* pVal = pVector->m_vals; uint8* flag = pVector->m_flag; int nrows = pVector->m_rows; Datum args[2]; Datum result; for (i = 0; i < nrows; i++) { cell = loc[i]; if (cell && IS_NULL(flag[i]) == false) // only do when not null { if (IS_NULL(cell->m_val[idx].flag)) { cell->m_val[idx].val = pVal[i]; SET_NOTNULL(cell->m_val[idx].flag); } else { args[0] = cell->m_val[idx].val; args[1] = pVal[i]; result = args[0] + args[1]; cell->m_val[idx].val = result; } } } return NULL; } ScalarVector* vintervalpl(PG_FUNCTION_ARGS) { ScalarValue* parg1 = PG_GETARG_VECVAL(0); ScalarValue* parg2 = PG_GETARG_VECVAL(1); int32 nvalues = PG_GETARG_INT32(2); ScalarValue* presult = PG_GETARG_VECVAL(3); bool* pselection = PG_GETARG_SELECTION(4); uint8* pflags1 = (uint8*)(PG_GETARG_VECTOR(0)->m_flag); uint8* pflags2 = (uint8*)(PG_GETARG_VECTOR(1)->m_flag); uint8* pflagsRes = (uint8*)(PG_GETARG_VECTOR(3)->m_flag); int i; Datum args[2]; FunctionCallInfoData finfo; Datum result; finfo.arg = &args[0]; if (likely(pselection == NULL)) { for (i = 0; i < nvalues; i++) { if (BOTH_NOT_NULL(pflags1[i], pflags2[i])) { args[0] = PointerGetDatum((char*)parg1[i] + VARHDRSZ_SHORT); args[1] = PointerGetDatum((char*)parg2[i] + VARHDRSZ_SHORT); result = interval_pl(&finfo); presult[i] = ScalarVector::DatumToScalar(result, INTERVALOID, false); SET_NOTNULL(pflagsRes[i]); } else SET_NULL(pflagsRes[i]); } } else { for (i = 0; i < nvalues; i++) { if (pselection[i]) { if (BOTH_NOT_NULL(pflags1[i], pflags2[i])) { args[0] = PointerGetDatum((char*)parg1[i] + VARHDRSZ_SHORT); args[1] = PointerGetDatum((char*)parg2[i] + VARHDRSZ_SHORT); result = interval_pl(&finfo); presult[i] = ScalarVector::DatumToScalar(result, INTERVALOID, false); SET_NOTNULL(pflagsRes[i]); } else SET_NULL(pflagsRes[i]); } } } PG_GETARG_VECTOR(3)->m_rows = nvalues; PG_GETARG_VECTOR(3)->m_desc.typeId = INTERVALOID; return PG_GETARG_VECTOR(3); } Datum numeric_text(PG_FUNCTION_ARGS) { Numeric num = PG_GETARG_NUMERIC(0); char* tmp = NULL; Datum result; /* Handle Big Integer */ if (NUMERIC_IS_BI(num)) { num = makeNumericNormal(num); } tmp = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(num))); result = DirectFunctionCall1(textin, CStringGetDatum(tmp)); pfree_ext(tmp); PG_RETURN_DATUM(result); } Datum bpchar_numeric(PG_FUNCTION_ARGS) { Datum bpcharValue = PG_GETARG_DATUM(0); char* tmp = NULL; Datum result; tmp = DatumGetCString(DirectFunctionCall1(bpcharout, bpcharValue)); result = DirectFunctionCall3(numeric_in, CStringGetDatum(tmp), ObjectIdGetDatum(0), Int32GetDatum(-1)); pfree_ext(tmp); PG_RETURN_DATUM(result); } Datum varchar_numeric(PG_FUNCTION_ARGS) { Datum txt = PG_GETARG_DATUM(0); char* tmp = NULL; Datum result; tmp = DatumGetCString(DirectFunctionCall1(varcharout, txt)); result = DirectFunctionCall3(numeric_in, CStringGetDatum(tmp), ObjectIdGetDatum(0), Int32GetDatum(-1)); pfree_ext(tmp); PG_RETURN_DATUM(result); } Datum numeric_varchar(PG_FUNCTION_ARGS) { Numeric num = PG_GETARG_NUMERIC(0); char* tmp = NULL; Datum result; /* Handle Big Integer */ if (NUMERIC_IS_BI(num)) { num = makeNumericNormal(num); } tmp = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(num))); result = DirectFunctionCall3(varcharin, CStringGetDatum(tmp), ObjectIdGetDatum(0), Int32GetDatum(-1)); pfree_ext(tmp); PG_RETURN_DATUM(result); } /* * @Description: numeric convert to bpchar. * @in arg1 - numeric convert to bpchar. * @return bpchar type string. */ Datum numeric_bpchar(PG_FUNCTION_ARGS) { Numeric arg1 = PG_GETARG_NUMERIC(0); char* tmp = NULL; Datum result; /* Handle Big Integer */ if (NUMERIC_IS_BI(arg1)) { arg1 = makeNumericNormal(arg1); } tmp = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(arg1))); result = DirectFunctionCall3(bpcharin, CStringGetDatum(tmp), ObjectIdGetDatum(0), Int32GetDatum(-1)); pfree_ext(tmp); PG_RETURN_DATUM(result); } Datum text_float4(PG_FUNCTION_ARGS) { Datum txt = PG_GETARG_DATUM(0); char* tmp = NULL; Datum result; tmp = DatumGetCString(DirectFunctionCall1(textout, txt)); result = DirectFunctionCall1(float4in, CStringGetDatum(tmp)); pfree_ext(tmp); PG_RETURN_DATUM(result); } Datum text_float8(PG_FUNCTION_ARGS) { Datum txt = PG_GETARG_DATUM(0); char* tmp = NULL; Datum result; tmp = DatumGetCString(DirectFunctionCall1(textout, txt)); result = DirectFunctionCall1(float8in, CStringGetDatum(tmp)); pfree_ext(tmp); PG_RETURN_DATUM(result); } Datum text_numeric(PG_FUNCTION_ARGS) { Datum txt = PG_GETARG_DATUM(0); char* tmp = NULL; Datum result; tmp = DatumGetCString(DirectFunctionCall1(textout, txt)); result = DirectFunctionCall3(numeric_in, CStringGetDatum(tmp), ObjectIdGetDatum(0), Int32GetDatum(-1)); pfree_ext(tmp); PG_RETURN_DATUM(result); } Datum bpchar_float4(PG_FUNCTION_ARGS) { Datum bpcharValue = PG_GETARG_DATUM(0); char* tmp = NULL; Datum result; tmp = DatumGetCString(DirectFunctionCall1(bpcharout, bpcharValue)); result = DirectFunctionCall1(float4in, CStringGetDatum(tmp)); pfree_ext(tmp); PG_RETURN_DATUM(result); } Datum bpchar_float8(PG_FUNCTION_ARGS) { Datum bpcharValue = PG_GETARG_DATUM(0); char* tmp = NULL; Datum result; tmp = DatumGetCString(DirectFunctionCall1(bpcharout, bpcharValue)); result = DirectFunctionCall1(float8in, CStringGetDatum(tmp)); pfree_ext(tmp); PG_RETURN_DATUM(result); } Datum varchar_float4(PG_FUNCTION_ARGS) { Datum varcharValue = PG_GETARG_DATUM(0); char* tmp = NULL; Datum result; tmp = DatumGetCString(DirectFunctionCall1(varcharout, varcharValue)); result = DirectFunctionCall1(float4in, CStringGetDatum(tmp)); pfree_ext(tmp); PG_RETURN_DATUM(result); } Datum varchar_float8(PG_FUNCTION_ARGS) { Datum varcharValue = PG_GETARG_DATUM(0); char* tmp = NULL; Datum result; tmp = DatumGetCString(DirectFunctionCall1(varcharout, varcharValue)); result = DirectFunctionCall1(float8in, CStringGetDatum(tmp)); pfree_ext(tmp); PG_RETURN_DATUM(result); } // // Numeric Compression Codes Area // // ascale: adjusted scale /// we need to do unit testing for some *static* functions, /// so we redefine *static* if *ENABLE_UT* is defined. /// at the end of this file we will restore it. #ifdef ENABLE_UT #define static #endif #if DEC_DIGITS == 4 /// we know that it's failed to convert a numeric value to int64, /// whose digits number is equal to or greater than 6. #define NUMERIC_NDIGITS_UPLIMITED (6) #define NUMERIC_NDIGITS_INT128_UPLIMITED (11) /// infor about holding int64 min value. /// 1. how many digits at least; /// 2. its tailing data; #define INT64_MIN_VAL_NDIGITS (5) #define INT64_MIN_VALUE_LAST (5808) #define INT64_MAX_VALUE_FIRST (922) /// max buffer size for holding an valid int64 numeric. #define MAX_NUMERIC_BUFFER_SIZE (NUMERIC_HDRSZ_SHORT + INT64_MIN_VAL_NDIGITS * sizeof(NumericDigit)) /// the same to MAX_NUMERIC_BUFFER_SIZE but for 1 varlena head #define MAX_1HEAD_NUMERIC_BUFFER_SIZE (VARHDRSZ_SHORT + sizeof(uint16) + INT64_MIN_VAL_NDIGITS * sizeof(NumericDigit)) #endif // how many tailing zeros within each of 0~9999 static char number_of_tail_zeros[10000] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; const int16 INT16_MIN_VALUE = INT16_MIN; // equal to 0x8000 const int16 INT16_MAX_VALUE = INT16_MAX; // equal to 0x7fff const int32 INT32_MIN_VALUE = INT32_MIN; // equal to 0x80000000 const int32 INT32_MAX_VALUE = INT32_MAX; // equal to 0x7fffffff const int64 INT64_MIN_VALUE = INT64_MIN; // equal to 8000000000000000 const int64 INT64_MAX_VALUE = INT64_MAX; // equal to 7FFFFFFFFFFFFFFF #ifdef ENABLE_UT /// the same to dump_numeric() /// - Dump a value in the db storage format /// into a file for debugging. void test_dump_numeric_to_file(_out_ FILE* fd, _in_ const char* str, _in_ Numeric num) { fprintf(fd, "%s: NUMERIC weight=%d dscale=%d ", str, NUMERIC_WEIGHT(num), NUMERIC_DSCALE(num)); int sign = NUMERIC_SIGN(num); if (NUMERIC_POS == sign) fprintf(fd, "POS"); else if (NUMERIC_NEG == sign) fprintf(fd, "NEG"); else if (NUMERIC_NAN == sign) fprintf(fd, "NaN"); else fprintf(fd, "SIGN=0x%x", sign); NumericDigit* digits = NUMERIC_DIGITS(num); int ndigits = NUMERIC_NDIGITS(num); for (int k = 0; k < ndigits; k++) fprintf(fd, " %0*d", DEC_DIGITS, digits[k]); fprintf(fd, "\n"); } // check the display scales are the same. bool test_numeric_dscale_equal(_in_ Numeric v1, _in_ Numeric v2) { return (NUMERIC_DSCALE(v1) == NUMERIC_DSCALE(v2)); } void test_dump_compressed_numeric_to_file(_out_ FILE* fd, _in_ const char* str, _in_ int64 v, _in_ char ascale) { fprintf(fd, "%s: ", str); if (convert_short_numeric_to_int32(v, ascale)) fprintf(fd, "[INT32]%ld ", v); else fprintf(fd, "[INT64]%ld ", v); fprintf(fd, "[ascale]%d", ascale); fprintf(fd, "\n"); } #endif /// one member of make_numeric() family. /// make numeric result for the min-int64 value. /// and return the bytes size used. static inline int make_short_numeric_of_int64_minval(_out_ Numeric result, _in_ int dscale, _in_ int weight) { const int len = MAX_NUMERIC_BUFFER_SIZE; NumericDigit* digits = result->choice.n_short.n_data; SET_VARSIZE(result, len); result->choice.n_short.n_header = (NUMERIC_SHORT | NUMERIC_SHORT_SIGN_MASK) // sign info | (dscale << NUMERIC_SHORT_DSCALE_SHIFT) // display info | (weight < 0 ? NUMERIC_SHORT_WEIGHT_SIGN_MASK : 0) // weight info | (weight & NUMERIC_SHORT_WEIGHT_MASK); /// see *INT64_MIN_VALUE* value. digits[0] = 922; digits[1] = 3372; digits[2] = 368; digits[3] = 5477; digits[4] = INT64_MIN_VALUE_LAST; return len; } /// one member of make_numeric() family. /// this is for numeric 0. static inline int make_short_numeric_of_zero(Numeric result, int typmod) { /// set display scale if typmod is given, otherwise is 0 at default. int dscale = (typmod >= (int32)(VARHDRSZ)) ? ((typmod - VARHDRSZ) & 0xffff) : 0; SET_VARSIZE(result, NUMERIC_HDRSZ_SHORT); // length info result->choice.n_short.n_header = NUMERIC_SHORT // sign is NUMERIC_POS | (dscale << NUMERIC_SHORT_DSCALE_SHIFT) // dscale info // weight is 0 ; return (int)NUMERIC_HDRSZ_SHORT; } /// the same to make_result() but only for short numeric. /// and the bytes size will be returned also. static inline int make_short_numeric( Numeric result, NumericDigit* digits, int ndigits, int sign, int weight, int dscale, bool is_int128 = false) { Assert(sign != NUMERIC_NAN); Assert(NUMERIC_CAN_BE_SHORT(dscale, weight)); // check the leading and tailing zeros have been stripped. Assert(ndigits > 0 && ndigits <= NUMERIC_NDIGITS_INT128_UPLIMITED); Assert(digits[0] != 0); const int totalSize = (NUMERIC_HDRSZ_SHORT + ndigits * sizeof(NumericDigit)); // step 1: set length info SET_VARSIZE(result, totalSize); // step 2: set head info, including sign, weight and dscale. result->choice.n_short.n_header = ((sign == NUMERIC_NEG) ? (NUMERIC_SHORT | NUMERIC_SHORT_SIGN_MASK) : NUMERIC_SHORT) | (dscale << NUMERIC_SHORT_DSCALE_SHIFT) | (weight < 0 ? NUMERIC_SHORT_WEIGHT_SIGN_MASK : 0) | (((uint32)weight) & NUMERIC_SHORT_WEIGHT_MASK); // step 3: build the data info NumericDigit* src = digits; NumericDigit* dest = result->choice.n_short.n_data; switch (ndigits) { case 11: *dest++ = *src++; /* fall through */ case 10: *dest++ = *src++; /* fall through */ case 9: *dest++ = *src++; /* fall through */ case 8: *dest++ = *src++; /* fall through */ case 7: *dest++ = *src++; /* fall through */ case 6: *dest++ = *src++; /* fall through */ case 5: *dest++ = *src++; /* fall through */ case 4: *dest++ = *src++; /* fall through */ case 3: *dest++ = *src++; /* fall through */ case 2: *dest++ = *src++; /* fall through */ case 1: *dest++ = *src++; /* fall through */ default: // do nothing if 0 break; } // Check for overflow of int16 fields Assert(NUMERIC_NDIGITS(result) == (unsigned int)(ndigits)); Assert(weight == NUMERIC_WEIGHT(result)); Assert(dscale == NUMERIC_DSCALE(result)); return totalSize; } static inline int get_weight_from_ascale(int ndigits, int ascale) { return (ndigits - ascale - 1); } static int get_dscale_from_typmod(int typmod, int ascale, int last_item) { if (typmod >= (int32) (VARHDRSZ)) { return (int32) ((uint32) (typmod - VARHDRSZ) & 0xffff); } /* * If typmod is not given, we may restore the wrong dscale. * example: 1.000 its dscale is 3, but we cannot get it from {value=1, ascale=0} */ if (ascale <= 0) { return 0; } Assert(4 == DEC_DIGITS); Assert(last_item > 0 && last_item < 10000); return ((uint32)((uint32)ascale << 2) - number_of_tail_zeros[last_item]); } /* * @Description: get numeric whole scale * @IN numVar: numeric * @Return: numeric scale */ static inline int get_whole_scale(const NumericVar& numVar) { int whole_scale = 0; if (numVar.weight >= 0 && numVar.ndigits > 0) { whole_scale = numVar.weight * DEC_DIGITS + numVar.dscale; int first_digit = numVar.digits[0]; if (first_digit >= 1000) { whole_scale = whole_scale + 4; } else if (first_digit >= 100) { whole_scale = whole_scale + 3; } else if (first_digit >= 10) { whole_scale = whole_scale + 2; } else { whole_scale = whole_scale + 1; } } else { whole_scale = numVar.dscale; } return whole_scale; } /// make a copy with 4B varlena header. static inline Numeric numeric_copy(Numeric shortNum, char* outBuf) { Assert(VARATT_IS_SHORT(shortNum)); const int diff = VARHDRSZ - VARHDRSZ_SHORT; int len = VARSIZE_SHORT(shortNum); // Notice: include two parts: // 1. numeric header, uint16; // 2. numeric digits, int16[]; // so that we also can handle 0 numeric. int nShortDigtis = (len - VARHDRSZ_SHORT) / sizeof(uint16); Assert(nShortDigtis >= 1 && nShortDigtis <= NUMERIC_NDIGITS_UPLIMITED); char* buf = outBuf; uint16* dest = (uint16*)(buf + VARHDRSZ); uint16* src = (uint16*)((char*)shortNum + VARHDRSZ_SHORT); // set varlena header size. SET_VARSIZE(buf, len + diff); // copy all the data, including flags and digits. switch (nShortDigtis) { case 6: *dest++ = *src++; /* fall through */ case 5: *dest++ = *src++; /* fall through */ case 4: *dest++ = *src++; /* fall through */ case 3: *dest++ = *src++; /* fall through */ case 2: *dest++ = *src++; /* fall through */ case 1: default: *dest++ = *src++; break; } return (Numeric)buf; } #define update_result(__result, __digits, __weight) \ do { \ (__result) *= NBASE; \ (__result) += (__digits)[(__weight)]; \ } while (0) #define encode_digits(__result, __digits, __ndigits) \ do { \ switch (__ndigits) { \ case 6: { \ (__result) = (__digits)[0]; \ update_result(__result, __digits, 1); \ update_result(__result, __digits, 2); \ update_result(__result, __digits, 3); \ update_result(__result, __digits, 4); \ update_result(__result, __digits, 5); \ break; \ } \ case 5: { \ (__result) = (__digits)[0]; \ update_result(__result, __digits, 1); \ update_result(__result, __digits, 2); \ update_result(__result, __digits, 3); \ update_result(__result, __digits, 4); \ break; \ } \ case 4: { \ (__result) = (__digits)[0]; \ update_result(__result, __digits, 1); \ update_result(__result, __digits, 2); \ update_result(__result, __digits, 3); \ break; \ } \ case 3: { \ (__result) = (__digits)[0]; \ update_result(__result, __digits, 1); \ update_result(__result, __digits, 2); \ break; \ } \ case 2: { \ (__result) = (__digits)[0]; \ update_result(__result, __digits, 1); \ break; \ } \ case 1: { \ (__result) = (__digits)[0]; \ break; \ } \ default: { \ Assert(0 == (__ndigits)); \ (__result) = 0; \ break; \ } \ } \ } while (0) /* * @Description: encoding numeric to dscale int64 * @IN batchValues: array of numeric values * @IN batchNulls: array of nulls values * @IN batchRows: number of array * @OUT outInt: array of converted int64 * @OUT outDscales: array of dscale * @OUT outSuccess: array of success convert * @Return: number of success convert */ template int batch_convert_short_numeric_to_dscale_int64_T(_in_ Datum* batchValues, _in_ char* batchNulls, _in_ int batchRows, _out_ int64* outInt, _out_ char* outDscales, _out_ bool* outSuccess, _out_ int* outNullCount) { Assert(4 == DEC_DIGITS); if (t_thrd.mem_cxt.batch_encode_numeric_mem_cxt == NULL) { t_thrd.mem_cxt.batch_encode_numeric_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt, "BATCH ENCODE NUMERIC CNXT", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); } MemoryContext oldMemCnxt = MemoryContextSwitchTo(t_thrd.mem_cxt.batch_encode_numeric_mem_cxt); NumericVar numVar; Numeric num = NULL; char* outBuffer = (char*)palloc(MAX_NUMERIC_BUFFER_SIZE * batchRows); char* tmpOutBuf = outBuffer; char* notNullDscales = outDscales; int batchCnt = 0; int dscale = 0; for (int i = 0; i < batchRows; ++i) { if (hasNull && bitmap_been_set(batchNulls, i)) { // treat NULL as failed case. *outSuccess++ = false; (*outNullCount)++; continue; } num = (Numeric)DatumGetPointer(batchValues[i]); // make sure that every Numeric in *batch* is with 4 byte var-header. // if it's with 1 byte var-header, we have to do a copy // which is with 4 bytes var-header. if (unlikely(VARATT_IS_SHORT(num))) { // this short numeric is out of range. if (unlikely(VARSIZE_SHORT(num) > MAX_1HEAD_NUMERIC_BUFFER_SIZE)) { *outSuccess++ = false; *notNullDscales++ = FAILED_DSCALE_ENCODING; continue; } num = numeric_copy(num, tmpOutBuf); tmpOutBuf += MAX_NUMERIC_BUFFER_SIZE; } if (NUMERIC_IS_BI64(num)) { // numeric with bigint64 *outSuccess++ = true; *outInt++ = NUMERIC_64VALUE(num); dscale = NUMERIC_BI_SCALE(num); Assert(dscale < MAXINT64DIGIT && dscale >= 0); *notNullDscales++ = (char)dscale; ++batchCnt; } else if (NUMERIC_IS_BI128(num)) { // numeric with bigint128 *outSuccess++ = false; *notNullDscales++ = FAILED_DSCALE_ENCODING; } else if (NUMERIC_IS_NAN(num)) { // NAN *outSuccess++ = false; *notNullDscales++ = FAILED_DSCALE_ENCODING; } else { // numeric init_var_from_num(num, &numVar); // whole scale int whole_scale = get_whole_scale(numVar); if (likely(CAN_CONVERT_BI64(whole_scale))) { // convert to BI64 *outSuccess++ = true; *outInt++ = convert_short_numeric_to_int64_byscale(num, numVar.dscale); Assert(numVar.dscale < MAXINT64DIGIT && numVar.dscale >= 0); *notNullDscales++ = (char)numVar.dscale; ++batchCnt; } else { // can't not convert to BI64 *outSuccess++ = false; *notNullDscales++ = FAILED_DSCALE_ENCODING; } } } (void)MemoryContextSwitchTo(oldMemCnxt); // reset this memory context after numeric encoding MemoryContextReset(t_thrd.mem_cxt.batch_encode_numeric_mem_cxt); return batchCnt; } int batch_convert_short_numeric_to_int64(_in_ Datum* batchValues, _in_ char* batchNulls, _in_ int batchRows, _in_ bool hasNull, _out_ int64* outInt, _out_ char* outAscales, _out_ bool* outSuccess, _out_ int* outNullCount) { if (hasNull) return batch_convert_short_numeric_to_dscale_int64_T( batchValues, batchNulls, batchRows, outInt, outAscales, outSuccess, outNullCount); else return batch_convert_short_numeric_to_dscale_int64_T( batchValues, batchNulls, batchRows, outInt, outAscales, outSuccess, outNullCount); } bool convert_short_numeric_to_int64(char* inBuf, int64* v, char* ascale) { Datum values[1] = {PointerGetDatum(inBuf)}; char nulls[1] = {0}; bool successful = false; int outNullCount = 0; int nSuccess = batch_convert_short_numeric_to_dscale_int64_T(values, nulls, 1, v, ascale, &successful, &outNullCount); if (!(successful && (nSuccess == 1)) && !(!successful && (nSuccess == 0))) { Assert(0); } return successful; } /// this function is called after *convert_short_numeric_to_int64* works. /// both value and its adjusted scale are checked. bool convert_short_numeric_to_int32(_in_ int64 v, _in_ char ascale) { return ( (ascale >= INT32_MIN_ASCALE && ascale <= INT32_MAX_ASCALE) && (v >= INT32_MIN_VALUE && v <= INT32_MAX_VALUE)); } static int16* get_digits_from_int64(_out_ int& sign, _in_ int16* digits_buf, _in_ int128 v) { int128 multiple = 0; int16* digits_ptr = digits_buf + NUMERIC_NDIGITS_UPLIMITED; if (v < 0) { sign = NUMERIC_NEG; v = -v; } // step 1: compute digits_buf from int64 value. do { multiple = v / NBASE; *(--digits_ptr) = v - multiple * NBASE; v = multiple; } while (v); return digits_ptr; } int convert_int64_to_short_numeric(_out_ char* outBuf, _in_ int64 v, _in_ char ascale, _in_ int32 typmod) { int sign = NUMERIC_POS; int16 digits_buf[NUMERIC_NDIGITS_UPLIMITED]; Numeric n = (Numeric)outBuf; if (0 == v) { return make_short_numeric_of_zero(n, typmod); } if (unlikely(INT64_MIN_VALUE == v)) { // overflow happens when run "v = -v", // so we simply hard coding this situation. return make_short_numeric_of_int64_minval(n, get_dscale_from_typmod(typmod, ascale, INT64_MIN_VALUE_LAST), get_weight_from_ascale(INT64_MIN_VAL_NDIGITS, ascale)); } int16* digits_ptr = get_digits_from_int64(sign, digits_buf, v); // step 2: restore Numeric storage format. int ndigits = digits_buf + NUMERIC_NDIGITS_UPLIMITED - digits_ptr; Assert(digits_ptr[ndigits - 1] != 0); return make_short_numeric(n, digits_ptr, ndigits, sign, // compute weight from int64 ndignit and ascale. get_weight_from_ascale(ndigits, ascale), // compute the display scale. get_dscale_from_typmod(typmod, ascale, digits_buf[NUMERIC_NDIGITS_UPLIMITED - 1])); } /* * convert functions between int32|int64|int128 and numeric * convert numeric to int32, int64 or int128 * convert int32, int64, int128 to numeric */ int convert_int64_to_short_numeric_byscale(_out_ char* outBuf, _in_ int128 v, _in_ int32 typmod, _in_ int32 vscale) { Numeric n = (Numeric)outBuf; if (0 == v) { return make_short_numeric_of_zero(n, typmod); } int sign = NUMERIC_POS; int16 digits_buf[NUMERIC_NDIGITS_UPLIMITED]; int scale = (int32)((uint32)(typmod - VARHDRSZ) & 0xffff); int scaleDiff = NUMERIC_SCALE_ADJUST(vscale) * DEC_DIGITS - vscale; Assert(scaleDiff >= 0 && scaleDiff <= MAXINT64DIGIT); v = v * ScaleMultipler[scaleDiff]; int16* digits_ptr = get_digits_from_int64(sign, digits_buf, v); int ndigits = digits_buf + NUMERIC_NDIGITS_UPLIMITED - digits_ptr; int16* p = digits_buf + NUMERIC_NDIGITS_UPLIMITED - 1; int real_ndigits = ndigits; while (0 == *p) { --real_ndigits; --p; } Assert(real_ndigits > 0 && real_ndigits <= NUMERIC_NDIGITS_UPLIMITED); return make_short_numeric(n, digits_ptr, real_ndigits, sign, // compute weight from int64 ndignit and ascale. get_weight_from_ascale(ndigits, NUMERIC_SCALE_ADJUST(vscale)), scale); } int64 convert_short_numeric_to_int64_byscale(_in_ Numeric n, _in_ int scale) { int128 result = 0; int weight = NUMERIC_WEIGHT(n); int ndigits = SHORT_NUMERIC_NDIGITS(n); int ascale = (ndigits > 0) ? (ndigits - weight - 1) : 0; int scaleDiff = scale - ascale * DEC_DIGITS; NumericDigit* digits = SHORT_NUMERIC_DIGITS(n); encode_digits(result, digits, ndigits); /* adjust scale */ result = (scaleDiff > 0) ? (result * ScaleMultipler[scaleDiff]) : (result / ScaleMultipler[-scaleDiff]); /* get the result by sign */ result = (NUMERIC_POS == NUMERIC_SIGN(n)) ? result : -result; Assert(INT128_INT64_EQ(result)); return (int64)result; } /* * n: ndigits * w: weight * a: ascale = n - (w + 1) * d: dscale (display scale) * a <= 0 means no scale part in NumericShort; * w < 0 means just scale part in NumericShort; * * ... w=1 w=0 w=-1 ... * ... xxxx xxxx . xxxx ... * ... a=-1 a=0 a=1 ... * * convert NumericShort to int128: * step 1: get all valid digitals to result; if d%4 != 0, just get valid * digitals from the last item in digits[]. such as digits[-1] = 6800, * d%4=2, so just 68 is needed. * * step 2: get diff_scale as four cases below: * +--------------------------------------+---------------------------------+ * | a <= 0 | | * +--------------------------------------+ | * | a > 0 and (d%4) == 0 | diff_scale = d - a*4 | * +--------------------------------------+ | * | a > 0 and (d+4)/4 > a | | * +--------------------------------------+---------------------------------+ * | a > 0 and (d%4) != 0 and (d+4)/4==a | diff_scale = d - (a-1)*4 - d%4 | * +------------------------------------------------------------------------+ * NOTE: * case 4 : (a-1)*4+d%4 means the number of the valid digitals in scale part * example: * case 1: 1.0 * case 2: dec(4,4) 1.1230, dec(4,8) 1.1230 * case 3: dec(6,9) 1.1230 * case 4: dec(6,3) 1.1230 * * setp 3: * result *= S_INT128_POWER[diff_scale] * *============================================================================== * * @Description: convert PG Numeric format to the data type of int128 * * @IN value: the source data with PG Numeric format * @IN value: display scale * @OUT value: result, the result of the data type of int128. * @return: None */ void convert_short_numeric_to_int128_byscale(_in_ Numeric n, _in_ int dscale, _out_ int128& result) { bool special_do = false; int ndigits = SHORT_NUMERIC_NDIGITS(n); /* ndigits is 0, result is 0, return directly */ result = 0; if (0 == ndigits) { return; } int remainder = dscale % DEC_DIGITS; int weight = NUMERIC_WEIGHT(n); int ascale = ndigits - (weight + 1); int end_index = ndigits; int diff_scale = 0; NumericDigit* digits = SHORT_NUMERIC_DIGITS(n); Assert(ndigits >= 0); if (ascale > 0 && remainder != 0 && (dscale / DEC_DIGITS + 1) == ascale) { special_do = true; --end_index; } /* step1. get all valid digitals to result */ for (int i = 0; i < end_index; i++) result += (digits[i] * getScaleMultiplier((end_index - 1 - i) * DEC_DIGITS)); if (special_do) { result = (result * getScaleMultiplier(remainder)) + (digits[ndigits - 1] / getScaleMultiplier(DEC_DIGITS - remainder)); /* step2. get diff_scale by dscale and ascale */ diff_scale = dscale - (ascale - 1) * 4 - dscale % 4; } else { /* step2. get diff_scale by dscale and ascale */ diff_scale = dscale - ascale * 4; } /* step3. adjust result by diff_scale */ result *= getScaleMultiplier(diff_scale); result = (NUMERIC_POS == NUMERIC_SIGN(n)) ? result : -result; } /* * vscale is from orc file, and dscale is from gaussdb */ int convert_int128_to_short_numeric_byscale(_out_ char* outBuf, _in_ int128 v, _in_ int32 typmod, _in_ int32 vscale) { int16 digits_buf[NUMERIC_NDIGITS_INT128_UPLIMITED]; int16* digits_ptr = NULL; int16 tmp; int sign; int remainder; int scale; int ndigits; int weight; int128 multiple; Assert(vscale >= 0); if (0 == v) return make_short_numeric_of_zero((Numeric)outBuf, typmod); sign = NUMERIC_POS; if (v < 0) { v = -v; sign = NUMERIC_NEG; } digits_ptr = digits_buf + NUMERIC_NDIGITS_INT128_UPLIMITED; remainder = vscale % DEC_DIGITS; if (remainder > 0) { tmp = (int16)(v % ScaleMultipler[remainder]); v /= ScaleMultipler[remainder]; *(--digits_ptr) = tmp * ScaleMultipler[DEC_DIGITS - remainder]; } multiple = 0; while (v) { multiple = v / NBASE; *(--digits_ptr) = v - multiple * NBASE; v = multiple; }; scale = (int32)((uint32)(typmod - VARHDRSZ) & 0xffff); ndigits = digits_buf + NUMERIC_NDIGITS_INT128_UPLIMITED - digits_ptr; weight = get_weight_from_ascale(ndigits, NUMERIC_SCALE_ADJUST(vscale)); int16* p = digits_buf + NUMERIC_NDIGITS_INT128_UPLIMITED - 1; int real_ndigits = ndigits; while (0 == *p) { --real_ndigits; --p; } return make_short_numeric((Numeric)outBuf, digits_ptr, real_ndigits, sign, weight, scale, true); } /* * @Description: This function try to convert numeric to big interger * format. return the formated value or the original one. * * @IN value: input numeric value. * @return: Numeric - Datum points to fast numeric format */ Datum try_convert_numeric_normal_to_fast(Datum value, ScalarVector *arr) { Numeric val = DatumGetNumeric(value); if (NUMERIC_IS_NANORBI(val) || u_sess->attr.attr_sql.enable_fast_numeric == false) return NumericGetDatum(val); NumericVar numVar; init_var_from_num(val, &numVar); int whole_scale = get_whole_scale(numVar); // should be ( whole_scale <= MAXINT64DIGIT) if (CAN_CONVERT_BI64(whole_scale)) { int64 result = convert_short_numeric_to_int64_byscale(val, numVar.dscale); return makeNumeric64(result, numVar.dscale, arr); } else if (CAN_CONVERT_BI128(whole_scale)) { int128 result = 0; convert_short_numeric_to_int128_byscale(val, numVar.dscale, result); return makeNumeric128(result, numVar.dscale); } else return NumericGetDatum(val); } /* * @Description: This function convert big integer64 to * short numeric * * @IN data: value of bi64 * @IN scale: scale of bi64 * @return: Numeric - the result of numeric type */ Numeric convert_int64_to_numeric(int64 data, uint8 scale) { Assert(scale <= MAXINT64DIGIT); uint64 uval = 0; uint64 newuval = 0; Size len = 0; int tmp_loc = 0; NumericVar var; int rc; /* step 1: get the absolute value of int64 data */ if (data < 0) { var.sign = NUMERIC_NEG; /* (-1 * data) maybe out of int64 bound, turn to uint64 */ uval = -data; } else { var.sign = NUMERIC_POS; uval = data; } var.dscale = scale; var.ndigits = 0; var.weight = 0; /* data equals to 0, return here */ if (uval == 0) { len = NUMERIC_HDRSZ_SHORT; Numeric result = (Numeric)palloc(len); SET_VARSIZE(result, len); result->choice.n_short.n_header = (uint16)((NUMERIC_SHORT) | (((uint32)var.dscale) << NUMERIC_SHORT_DSCALE_SHIFT)); return result; } /* step 2: split source int64 data into pre_data and post_data by decimal point * pre_data stores the data before decimal point. */ uint64 pre_data = uval / getScaleMultiplier(scale); /* pre_data stores the data after decimal point. */ uint64 post_data = uval % getScaleMultiplier(scale); /* int64 can require at most 19 decimal digits; * add one for safety, buf1 stores pre_data, * buf2 stores post_data. */ NumericDigit buf1[20 / DEC_DIGITS]; NumericDigit buf2[20 / DEC_DIGITS]; NumericDigit* ptr1 = buf1 + 5; NumericDigit* ptr2 = buf2; int pre_digits = 0; int post_digits = 0; /* step 3: calculate pre_data and store result in buf1 * pre_data == 0, skip this */ if (pre_data != 0) { do { ptr1--; pre_digits++; newuval = pre_data / NBASE; *ptr1 = pre_data - newuval * NBASE; pre_data = newuval; } while (pre_data); var.weight = pre_digits - 1; } /* step 4: calculate pre_data and store result in buf2 * post_data == 0, skip this */ if (post_data != 0) { int result_scale = (int)scale; while (post_data && result_scale >= DEC_DIGITS) { post_digits++; result_scale = result_scale - DEC_DIGITS; *ptr2 = post_data / ScaleMultipler[result_scale]; post_data = post_data % ScaleMultipler[result_scale]; ptr2++; } if (post_data) { Assert(result_scale < DEC_DIGITS); post_digits++; *ptr2 = post_data * ScaleMultipler[DEC_DIGITS - result_scale]; ptr2++; } } /* step5: make numeric result */ Numeric result = NULL; if (pre_digits) { /* pre_digits != 0 && post_digits != 0 * Example: 900000000.0001 */ if (post_digits) { var.ndigits = pre_digits + post_digits; len = NUMERIC_HDRSZ_SHORT + var.ndigits * sizeof(NumericDigit); result = (Numeric)palloc(len); SET_VARSIZE(result, len); rc = memcpy_s(result->choice.n_short.n_data, pre_digits * sizeof(NumericDigit), ptr1, pre_digits * sizeof(NumericDigit)); securec_check(rc, "\0", "\0"); rc = memcpy_s(result->choice.n_short.n_data + pre_digits, post_digits * sizeof(NumericDigit), buf2, post_digits * sizeof(NumericDigit)); securec_check(rc, "\0", "\0"); } else { /* pre_digits != 0 && post_digits == 0 * Example: 9000000.000 */ for (tmp_loc = 0; tmp_loc < pre_digits;) { if (buf1[4 - tmp_loc] == 0) { tmp_loc++; } else { break; } } pre_digits = pre_digits - tmp_loc; var.ndigits = pre_digits; len = NUMERIC_HDRSZ_SHORT + var.ndigits * sizeof(NumericDigit); result = (Numeric)palloc(len); SET_VARSIZE(result, len); rc = memcpy_s(result->choice.n_short.n_data, pre_digits * sizeof(NumericDigit), ptr1, pre_digits * sizeof(NumericDigit)); securec_check(rc, "\0", "\0"); } } else { /* pre_digits == 0 && post_digits != 0 * Example: 0.0000001 */ Assert(post_digits <= 5); var.weight = 0; for (tmp_loc = 0; tmp_loc < post_digits; tmp_loc++) { var.weight--; if (buf2[tmp_loc] != 0) break; } post_digits = post_digits - tmp_loc; var.ndigits = post_digits; len = NUMERIC_HDRSZ_SHORT + var.ndigits * sizeof(NumericDigit); result = (Numeric)palloc(len); SET_VARSIZE(result, len); rc = memcpy_s(result->choice.n_short.n_data, post_digits * sizeof(NumericDigit), buf2 + tmp_loc, post_digits * sizeof(NumericDigit)); securec_check(rc, "\0", "\0"); } result->choice.n_short.n_header = (var.sign == NUMERIC_NEG ? (NUMERIC_SHORT | NUMERIC_SHORT_SIGN_MASK) : NUMERIC_SHORT) | ((uint32)(var.dscale) << NUMERIC_SHORT_DSCALE_SHIFT) | (var.weight < 0 ? NUMERIC_SHORT_WEIGHT_SIGN_MASK : 0) | ((uint32)(var.weight) & NUMERIC_SHORT_WEIGHT_MASK); /* Check for overflow of int64 fields */ Assert(NUMERIC_NDIGITS(result) == (unsigned int)(pre_digits + post_digits)); Assert(var.weight == NUMERIC_WEIGHT(result)); Assert(var.dscale == NUMERIC_DSCALE(result)); return result; } static inline uint16 GetNHeader(NumericVar var) { return (var.sign == NUMERIC_NEG ? (NUMERIC_SHORT | NUMERIC_SHORT_SIGN_MASK) : NUMERIC_SHORT) | ((uint32)var.dscale << NUMERIC_SHORT_DSCALE_SHIFT) | (var.weight < 0 ? NUMERIC_SHORT_WEIGHT_SIGN_MASK : 0) | ((uint32)var.weight & NUMERIC_SHORT_WEIGHT_MASK); } /* * @Description: This function convert big integer128 to * short numeric * * @IN data: value of bi128 * @IN scale: scale of bi128 * @return: Numeric - the result of numeric type */ Numeric convert_int128_to_numeric(int128 data, int scale) { Assert(scale <= MAXINT128DIGIT); uint128 uval = 0; uint128 newuval = 0; NumericVar var; Size len = 0; int tmp_loc = 0; int rc = 0; /* step 1: get the absolute value of int128 data */ if (data < 0) { var.sign = NUMERIC_NEG; /* (-1 * data) maybe out of int128 bound, turn to uint128 */ uval = -data; } else { var.sign = NUMERIC_POS; uval = data; } var.dscale = scale; var.ndigits = 0; var.weight = 0; /* data equals to 0, return here */ if (uval == 0) { len = NUMERIC_HDRSZ_SHORT; Numeric result = (Numeric)palloc(len); SET_VARSIZE(result, len); result->choice.n_short.n_header = (uint16)((NUMERIC_SHORT) | (((uint32)(var.dscale)) << NUMERIC_SHORT_DSCALE_SHIFT)); return result; } /* step 2: split source int128 data into pre_data and post_data by decimal point * pre_data stores the data before decimal point. */ uint128 pre_data = uval / getScaleMultiplier(scale); /* pre_data stores the data after decimal point. */ uint128 post_data = uval % getScaleMultiplier(scale); /* int128 can require at most 38 decimal digits; * add two for safety, buf1 stores pre_data, * buf2 stores post_data */ NumericDigit buf1[40 / DEC_DIGITS]; NumericDigit buf2[40 / DEC_DIGITS]; NumericDigit* ptr1 = buf1 + 10; NumericDigit* ptr2 = buf2; int pre_digits = 0; int post_digits = 0; /* step 3: calculate pre_data and store result in buf1 * pre_data == 0, skip this */ if (pre_data != 0) { do { ptr1--; pre_digits++; newuval = pre_data / NBASE; *ptr1 = pre_data - newuval * NBASE; pre_data = newuval; } while (pre_data); var.weight = pre_digits - 1; } /* step 4: calculate pre_data and store result in buf2 * post_data == 0, skip this */ if (post_data != 0) { int result_scale = (int)scale; while (post_data && result_scale >= DEC_DIGITS) { post_digits++; *ptr2 = post_data / getScaleMultiplier(result_scale - DEC_DIGITS); post_data = post_data % getScaleMultiplier(result_scale - DEC_DIGITS); result_scale = result_scale - DEC_DIGITS; ptr2++; } if (post_data) { Assert(result_scale < DEC_DIGITS); post_digits++; *ptr2 = post_data * ScaleMultipler[DEC_DIGITS - result_scale]; ptr2++; } } /* step5: make numeric result */ Numeric result = NULL; if (pre_digits) { /* pre_digits != 0 && post_digits != 0 * Example: 9000000.00001 */ if (post_digits) { var.ndigits = pre_digits + post_digits; len = NUMERIC_HDRSZ_SHORT + var.ndigits * sizeof(NumericDigit); result = (Numeric)palloc(len); SET_VARSIZE(result, len); rc = memcpy_s(result->choice.n_short.n_data, pre_digits * sizeof(NumericDigit), ptr1, pre_digits * sizeof(NumericDigit)); securec_check(rc, "\0", "\0"); rc = memcpy_s(result->choice.n_short.n_data + pre_digits, post_digits * sizeof(NumericDigit), buf2, post_digits * sizeof(NumericDigit)); securec_check(rc, "\0", "\0"); } else { /* pre_digits != 0 && post_digits == 0 * Example: 9000000.000 */ for (tmp_loc = 0; tmp_loc < pre_digits;) { if (buf1[9 - tmp_loc] == 0) { tmp_loc++; } else { break; } } pre_digits = pre_digits - tmp_loc; var.ndigits = pre_digits; len = NUMERIC_HDRSZ_SHORT + var.ndigits * sizeof(NumericDigit); result = (Numeric)palloc(len); SET_VARSIZE(result, len); rc = memcpy_s(result->choice.n_short.n_data, pre_digits * sizeof(NumericDigit), ptr1, pre_digits * sizeof(NumericDigit)); securec_check(rc, "\0", "\0"); } } else { /* pre_digits == 0 && post_digits != 0 * Example: 0.000001 */ Assert(post_digits <= 10); var.weight = 0; for (tmp_loc = 0; tmp_loc < post_digits; tmp_loc++) { var.weight--; if (buf2[tmp_loc] != 0) break; } post_digits = post_digits - tmp_loc; var.ndigits = post_digits; len = NUMERIC_HDRSZ_SHORT + var.ndigits * sizeof(NumericDigit); result = (Numeric)palloc(len); SET_VARSIZE(result, len); rc = memcpy_s(result->choice.n_short.n_data, post_digits * sizeof(NumericDigit), buf2 + tmp_loc, post_digits * sizeof(NumericDigit)); securec_check(rc, "\0", "\0"); } result->choice.n_short.n_header = GetNHeader(var); /* Check for overflow of int64 fields */ Assert(NUMERIC_NDIGITS(result) == (unsigned int)(pre_digits + post_digits)); Assert(var.weight == NUMERIC_WEIGHT(result)); Assert(var.dscale == NUMERIC_DSCALE(result)); return result; } /* * @Description: This function is used to calculate the byte size of the numeric type data, * but the result may not be the actual storage size. Due to business requirements, * management space and data compression are not considered, * so the calculation is defined as follows: * i.the sign bit is not considered * i.the leading zeros in the integer part and the trailing zeros in the fractional part are removed * i.each 4-digit decimal number occupies 2 bytes,and the integer part and the fractional part are calculated * respectively i.less than 4 decimal numbers occupy 2 bytes * * @IN num: value of numeric * @return result: the byte size of the numeric type data */ int32 get_ndigit_from_numeric(Numeric num) { int32 result = 0; char* string_from_numeric = NULL; int32 string_length_from_numeric = 0; int32 i = 0; int32 width_of_integer = 0; int32 width_of_decimal = 0; bool integer_flag = false; bool decimal_flag = false; int32 point_position = -1; if (NUMERIC_IS_NAN(num)) { result = NUMERIC_NAN_DATALENGTH; } else { /* Handle Big Integer */ if (NUMERIC_IS_BI(num)) { num = makeNumericNormal(num); } string_from_numeric = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(num))); string_length_from_numeric = strlen(string_from_numeric); for (i = 0; i < string_length_from_numeric; i++) { if (string_from_numeric[i] == '.') { point_position = i; break; } } if (-1 == point_position) { for (i = 0; i < string_length_from_numeric; i++) { if (string_from_numeric[i] == '+' || string_from_numeric[i] == '-') { continue; } if ('0' == string_from_numeric[i] && false == integer_flag) { continue; } else { integer_flag = true; width_of_integer++; } } } else { for (i = 0; i < point_position; i++) { if (string_from_numeric[i] == '+' || string_from_numeric[i] == '-') { continue; } if (string_from_numeric[i] == '0' && integer_flag == false) { continue; } else { integer_flag = true; width_of_integer++; } } for (i = string_length_from_numeric - 1; i > point_position; i--) { if (string_from_numeric[i] == '0' && decimal_flag == false) { continue; } else { decimal_flag = true; width_of_decimal++; } } } if (0 == width_of_integer && 0 == width_of_decimal) { result = NUMERIC_ZERO_DATALENGTH; } else { result = (width_of_integer + 3) / 4 * 2 + (width_of_decimal + 3) / 4 * 2; } pfree_ext(string_from_numeric); } return result; } /* * Convert int16 value to numeric. */ void int128_to_numericvar(int128 val, NumericVar* var) { uint128 uval, newuval; NumericDigit* ptr = NULL; 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; } /* * Convert numeric to int16, 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 = NULL; int ndigits; int weight; int i; int128 val; bool neg = false; 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 INT128_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_s128_overflow(val, NBASE, &val))) { free_var(&rounded); return false; } if (i < ndigits) { if (unlikely(pg_sub_s128_overflow(val, digits[i], &val))) { free_var(&rounded); return false; } } } free_var(&rounded); if (!neg) { if (unlikely(val == PG_INT128_MIN)) return false; val = -val; } *result = val; return true; } Datum int16_numeric(PG_FUNCTION_ARGS) { int128 val = PG_GETARG_INT128(0); Numeric res; NumericVar result; init_var(&result); int128_to_numericvar(val, &result); res = make_result(&result); free_var(&result); PG_RETURN_NUMERIC(res); } int128 numeric_int16_internal(Numeric num) { int128 result = 0; NumericVar x; uint16 numFlags = NUMERIC_NB_FLAGBITS(num); if (NUMERIC_FLAG_IS_NANORBI(numFlags)) { /* Handle Big Integer */ if (NUMERIC_FLAG_IS_BI(numFlags)) num = makeNumericNormal(num); /* XXX would it be better to return NULL? */ else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert NaN to int128"))); } /* Convert to variable format and thence to int8 */ init_var_from_num(num, &x); if (!numericvar_to_int128(&x, &result)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("int128 out of range"))); return result; } Datum numeric_int16(PG_FUNCTION_ARGS) { Numeric num = PG_GETARG_NUMERIC(0); int128 result = numeric_int16_internal(num); PG_RETURN_INT128(result); } Datum numeric_bool(PG_FUNCTION_ARGS) { Numeric num = PG_GETARG_NUMERIC(0); bool result = false; char* tmp = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(num))); if (strcmp(tmp, "0") != 0) { result = true; } pfree_ext(tmp); PG_RETURN_BOOL(result); } Datum bool_numeric(PG_FUNCTION_ARGS) { int val = 1; if (PG_GETARG_BOOL(0) == false) { val = 0; } Numeric res; NumericVar result; init_var(&result); int64_to_numericvar((int64)val, &result); res = make_result(&result); free_var(&result); PG_RETURN_NUMERIC(res); }