Index: src/backend/utils/adt/numeric.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/utils/adt/numeric.c,v retrieving revision 1.86 diff -c -r1.86 numeric.c *** src/backend/utils/adt/numeric.c 15 Oct 2005 02:49:29 -0000 1.86 --- src/backend/utils/adt/numeric.c 16 Nov 2005 17:00:51 -0000 *************** *** 84,92 **** #define MUL_GUARD_DIGITS 2 /* these are measured in NBASE digits */ #define DIV_GUARD_DIGITS 4 ! typedef int16 NumericDigit; #endif /* ---------- * The value represented by a NumericVar is determined by the sign, weight, --- 84,112 ---- #define MUL_GUARD_DIGITS 2 /* these are measured in NBASE digits */ #define DIV_GUARD_DIGITS 4 ! typedef uint16 NumericDigit; #endif + /* --------- + * The storage format for NUMERIC is + * + * numeric header + * int32 varlen is the standard variable length header + * weight 8 bits in int8 so +/-127; -128 is reserved for NUMERIC_NAN + * scale 9 bits + * first 8 bits in uint8 + * 9th bit is the high order bit of first digit + * sign is the second highest bit of first digit + * + * numeric digits + * an array of NumericDigits, each element storing NBASE + * digits. All trailing and leading zeros are not stored, + * apart from when the value is Zero AND the scale > 255 + * in which case we store a single zero digit, with the + * sign set to NUMERIC_POS so the actual stored value + * is equal to NUMERIC_DSCALE9_1 + *---------- + */ /* ---------- * The value represented by a NumericVar is determined by the sign, weight, *************** *** 130,137 **** typedef struct NumericVar { int ndigits; /* # of digits in digits[] - can be 0! */ ! int weight; /* weight of first digit */ ! int sign; /* NUMERIC_POS, NUMERIC_NEG, or NUMERIC_NAN */ int dscale; /* display scale */ NumericDigit *buf; /* start of palloc'd space for digits[] */ NumericDigit *digits; /* base-NBASE digits */ --- 150,157 ---- typedef struct NumericVar { int ndigits; /* # of digits in digits[] - can be 0! */ ! int weight; /* weight of first digit, or NUMERIC_NAN */ ! int sign; /* NUMERIC_POS, NUMERIC_NEG */ int dscale; /* display scale */ NumericDigit *buf; /* start of palloc'd space for digits[] */ NumericDigit *digits; /* base-NBASE digits */ *************** *** 199,205 **** {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}; --- 219,225 ---- {2, 0, NUMERIC_POS, 1, NULL, const_one_point_one_data}; static NumericVar const_nan = ! {0, NUMERIC_NAN, 0, 0, NULL, NULL}; #if DEC_DIGITS == 4 static const int round_powers[4] = {0, 1000, 100, 10}; *************** *** 368,373 **** --- 388,399 ---- * * External format is a sequence of int16's: * ndigits, weight, sign, dscale, NumericDigits. + * + * Note that the internal format is now different to the external format + * for the representation of NaN. In the external format, a value of + * NUMERIC_NAN_EXTERNAL in the sign field indicates NaN, which is converted + * into a NUMERIC_NAN in the weight field for the internal storage format and + * var formats. Sending data reverses this. */ Datum numeric_recv(PG_FUNCTION_ARGS) *************** *** 394,413 **** 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"))); --- 420,456 ---- alloc_var(&value, len); value.weight = (int16) pq_getmsgint(buf, sizeof(int16)); + if (!(value.weight > NUMERIC_NAN || + value.weight < NUMERIC_WEIGHT_MAX)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), + errmsg("invalid weight in external \"numeric\" value"))); + value.sign = (uint16) pq_getmsgint(buf, sizeof(uint16)); if (!(value.sign == NUMERIC_POS || value.sign == NUMERIC_NEG || ! value.sign == NUMERIC_NAN_EXTERNAL)) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("invalid sign in external \"numeric\" value"))); + if (value.sign == NUMERIC_NAN_EXTERNAL) + { + value.sign = NUMERIC_POS; + value.weight = NUMERIC_NAN; + } value.dscale = (uint16) pq_getmsgint(buf, sizeof(uint16)); + if (!(value.dscale > 0 || + value.dscale <= NUMERIC_MAX_PRECISION)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), + errmsg("invalid scale in external \"numeric\" value"))); + for (i = 0; i < len; i++) { NumericDigit d = pq_getmsgint(buf, sizeof(NumericDigit)); ! if (d >= NBASE) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("invalid digit in external \"numeric\" value"))); *************** *** 424,429 **** --- 467,474 ---- /* * numeric_send - converts numeric to binary format + * + * See comment for numeric_recv to explain conversion to NUMERIC_NAN_EXTERNAL */ Datum numeric_send(PG_FUNCTION_ARGS) *************** *** 438,443 **** --- 483,493 ---- pq_begintypsend(&buf); + if (x.weight == NUMERIC_NAN) + { + x.weight = NUMERIC_POS; + x.sign = NUMERIC_NAN_EXTERNAL; + } pq_sendint(&buf, x.ndigits, sizeof(int16)); pq_sendint(&buf, x.weight, sizeof(int16)); pq_sendint(&buf, x.sign, sizeof(int16)); *************** *** 501,513 **** * rounding could be necessary, just make a copy of the input and modify * its scale fields. (Note we assume the existing dscale is honest...) */ ! ddigits = (num->n_weight + 1) * DEC_DIGITS; ! if (ddigits <= maxdigits && scale >= NUMERIC_DSCALE(num)) { new = (Numeric) palloc(num->varlen); memcpy(new, num, num->varlen); ! new->n_sign_dscale = NUMERIC_SIGN(new) | ! ((uint16) scale & NUMERIC_DSCALE_MASK); PG_RETURN_NUMERIC(new); } --- 551,563 ---- * rounding could be necessary, just make a copy of the input and modify * its scale fields. (Note we assume the existing dscale is honest...) */ ! ddigits = (NUMERIC_WEIGHT(num) + 1) * DEC_DIGITS; ! if (ddigits <= maxdigits && scale >= num->n_dscale8bits && scale < 256) { new = (Numeric) palloc(num->varlen); memcpy(new, num, num->varlen); ! new->n_dscale8bits = (uint8) scale; ! PG_RETURN_NUMERIC(new); } *************** *** 539,544 **** --- 589,595 ---- { Numeric num = PG_GETARG_NUMERIC(0); Numeric res; + NumericDigit *resdigits; /* * Handle NaN *************** *** 551,558 **** */ res = (Numeric) palloc(num->varlen); memcpy(res, num, num->varlen); - res->n_sign_dscale = NUMERIC_POS | NUMERIC_DSCALE(num); PG_RETURN_NUMERIC(res); } --- 602,611 ---- */ res = (Numeric) palloc(num->varlen); memcpy(res, num, num->varlen); + resdigits = (NumericDigit *) res->n_data; + + resdigits[0] &= NUMERIC_ABS_MASK; PG_RETURN_NUMERIC(res); } *************** *** 563,568 **** --- 616,622 ---- { Numeric num = PG_GETARG_NUMERIC(0); Numeric res; + NumericDigit *resdigits; /* * Handle NaN *************** *** 574,593 **** * Do it the easy way directly on the packed format */ res = (Numeric) palloc(num->varlen); memcpy(res, num, num->varlen); /* ! * 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 (num->varlen != NUMERIC_HDRSZ) ! { ! /* Else, flip the sign */ ! if (NUMERIC_SIGN(num) == NUMERIC_POS) ! res->n_sign_dscale = NUMERIC_NEG | NUMERIC_DSCALE(num); else ! res->n_sign_dscale = NUMERIC_POS | NUMERIC_DSCALE(num); } PG_RETURN_NUMERIC(res); --- 628,654 ---- * Do it the easy way directly on the packed format */ res = (Numeric) palloc(num->varlen); + memcpy(res, num, num->varlen); + resdigits = (NumericDigit *) res->n_data; /* ! * Do nothing to a zero. ! */ ! if (NUMERIC_HAS_DIGITS(res)) ! { ! /* flip the sign */ ! if ((resdigits[0] & NUMERIC_SIGN_MASK) == NUMERIC_POS) ! { ! /* ! * unless the first digit is a zero with scale > 255 ! * in which case we should maintain the sign as NUMERIC_POS ! */ ! if (resdigits[0] != NUMERIC_DSCALE9_MASK) ! resdigits[0] |= NUMERIC_NEG; ! } else ! resdigits[0] &= NUMERIC_ABS_MASK; } PG_RETURN_NUMERIC(res); *************** *** 618,623 **** --- 679,685 ---- Numeric num = PG_GETARG_NUMERIC(0); Numeric res; NumericVar result; + NumericDigit *digits = (NumericDigit *) num->n_data; /* * Handle NaN *************** *** 627,647 **** 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 (num->varlen == NUMERIC_HDRSZ) ! set_var_from_var(&const_zero, &result); ! else { /* * And if there are some, we return a copy of ONE with the sign of our * argument */ set_var_from_var(&const_one, &result); ! result.sign = NUMERIC_SIGN(num); } res = make_result(&result); free_var(&result); --- 689,705 ---- init_var(&result); ! if (NUMERIC_HAS_DIGITS(num)) { /* * 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 = (digits[0] & NUMERIC_SIGN_MASK); } + else + set_var_from_var(&const_zero, &result); res = make_result(&result); free_var(&result); *************** *** 2157,2162 **** --- 2215,2221 ---- int ndatums; Numeric N, sumX; + NumericDigit *digits; /* We assume the input is array of numeric */ deconstruct_array(transarray, *************** *** 2168,2178 **** sumX = DatumGetNumeric(transdatums[1]); /* ignore sumX2 */ /* SQL92 defines AVG of no values to be NULL */ /* N is zero iff no digits (cf. numeric_uminus) */ ! if (N->varlen == NUMERIC_HDRSZ) PG_RETURN_NULL(); PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, NumericGetDatum(sumX), NumericGetDatum(N))); --- 2227,2241 ---- sumX = DatumGetNumeric(transdatums[1]); /* ignore sumX2 */ + digits = (NumericDigit *) N->n_data; + /* SQL92 defines AVG of no values to be NULL */ /* N is zero iff no digits (cf. numeric_uminus) */ ! /* Check to see whether we are zero, but with scale > 255 */ ! if ((!NUMERIC_HAS_DIGITS(N)) | (digits[0] == NUMERIC_DSCALE9_MASK)) PG_RETURN_NULL(); + PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, NumericGetDatum(sumX), NumericGetDatum(N))); *************** *** 2589,2601 **** dump_numeric(const char *str, Numeric num) { NumericDigit *digits = (NumericDigit *) num->n_data; int ndigits; int i; ndigits = (num->varlen - NUMERIC_HDRSZ) / sizeof(NumericDigit); ! printf("%s: NUMERIC w=%d d=%d ", str, num->n_weight, NUMERIC_DSCALE(num)); ! switch (NUMERIC_SIGN(num)) { case NUMERIC_POS: printf("POS"); --- 2652,2672 ---- dump_numeric(const char *str, Numeric num) { NumericDigit *digits = (NumericDigit *) num->n_data; + int scale; int ndigits; int i; ndigits = (num->varlen - NUMERIC_HDRSZ) / sizeof(NumericDigit); ! scale = num->n_dscale8bits; ! if ((digits[0] & NUMERIC_DSCALE9_MASK) == NUMERIC_DSCALE9_1) ! scale += 256; ! ! printf("%s: NUMERIC w=%d d=%u ", str, NUMERIC_WEIGHT(num), scale); ! if (NUMERIC_IS_NAN(num)) ! printf("NaN "); ! ! switch (digits[0] & NUMERIC_SIGN_MASK) { case NUMERIC_POS: printf("POS"); *************** *** 2603,2622 **** 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 */ --- 2674,2690 ---- case NUMERIC_NEG: printf("NEG"); break; default: break; } for (i = 0; i < ndigits; i++) ! printf(" %0*d(%X) ", DEC_DIGITS, digits[i] & NUMERIC_DIGIT_MASK, ! digits[i]); ! printf("\n"); } /* * dump_var() - Dump a value in the variable format for debugging */ *************** *** 2626,2631 **** --- 2694,2703 ---- int i; printf("%s: VAR w=%d d=%d ", str, var->weight, var->dscale); + + if (var->weight == NUMERIC_NAN) + printf("NaN"); + switch (var->sign) { case NUMERIC_POS: *************** *** 2634,2644 **** case NUMERIC_NEG: printf("NEG"); break; - case NUMERIC_NAN: - printf("NaN"); - break; default: - printf("SIGN=0x%x", var->sign); break; } --- 2706,2712 ---- *************** *** 2688,2694 **** digitbuf_free(var->buf); var->buf = NULL; var->digits = NULL; ! var->sign = NUMERIC_NAN; } --- 2756,2763 ---- digitbuf_free(var->buf); var->buf = NULL; var->digits = NULL; ! var->weight = NUMERIC_NAN; ! var->sign = NUMERIC_POS; } *************** *** 2896,2906 **** alloc_var(dest, ndigits); ! dest->weight = num->n_weight; ! dest->sign = NUMERIC_SIGN(num); ! dest->dscale = NUMERIC_DSCALE(num); ! memcpy(dest->digits, num->n_data, ndigits * sizeof(NumericDigit)); } --- 2965,2986 ---- alloc_var(dest, ndigits); ! dest->weight = NUMERIC_WEIGHT(num); ! ! dest->dscale = num->n_dscale8bits; ! if (ndigits > 0) ! { ! memcpy(dest->digits, num->n_data, ndigits * sizeof(NumericDigit)); ! if ((dest->digits[0] & NUMERIC_DSCALE9_MASK) == NUMERIC_DSCALE9_1) ! dest->dscale += 256; ! ! dest->sign = dest->digits[0] & NUMERIC_SIGN_MASK; ! ! dest->digits[0] = dest->digits[0] & NUMERIC_DIGIT_MASK; ! } ! else ! dest->sign = NUMERIC_POS; } *************** *** 3082,3098 **** 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); result->varlen = NUMERIC_HDRSZ; ! result->n_weight = 0; ! result->n_sign_dscale = NUMERIC_NAN; dump_numeric("make_result()", result); return result; --- 3162,3179 ---- Numeric result; NumericDigit *digits = var->digits; int weight = var->weight; ! int sign = var->sign; int n; Size len; + bool setfirst = false; ! if (weight == NUMERIC_NAN) { result = (Numeric) palloc(NUMERIC_HDRSZ); result->varlen = NUMERIC_HDRSZ; ! result->n_weight = NUMERIC_NAN; ! result->n_dscale8bits = 0; dump_numeric("make_result()", result); return result; *************** *** 3111,3135 **** 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 */ len = NUMERIC_HDRSZ + n * sizeof(NumericDigit); result = (Numeric) palloc(len); result->varlen = len; result->n_weight = weight; ! result->n_sign_dscale = sign | (var->dscale & NUMERIC_DSCALE_MASK); memcpy(result->n_data, digits, n * sizeof(NumericDigit)); ! /* Check for overflow of int16 fields */ if (result->n_weight != weight || ! NUMERIC_DSCALE(result) != var->dscale) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("value overflows numeric format"))); --- 3192,3236 ---- while (n > 0 && digits[n - 1] == 0) n--; ! /* ! * Zero is stored as no digits, unless we have a very large scale, ! * in which case we store a single zero digit with its dscale9 bit set ! * If we pass this test, digits[0] is already known as zero from above ! */ ! if (n == 0 && var->dscale > 255) ! { ! n = 1; ! setfirst = true; ! } /* Build the result */ len = NUMERIC_HDRSZ + n * sizeof(NumericDigit); result = (Numeric) palloc(len); result->varlen = len; result->n_weight = weight; ! ! if (var->dscale > 255) ! { ! result->n_dscale8bits = (uint8) (var->dscale - 256); ! /* We will always have a digits[0] if scale > 255 */ ! if (setfirst) ! digits[0] = 0; ! digits[0] |= NUMERIC_DSCALE9_1 | sign; ! } ! else ! { ! result->n_dscale8bits = (uint8) var->dscale; ! if (n > 0) ! digits[0] |= NUMERIC_DSCALE9_0 | sign; ! } memcpy(result->n_data, digits, n * sizeof(NumericDigit)); ! ! /* Check for overflow or underflow */ if (result->n_weight != weight || ! var->dscale < 0 || ! var->dscale > NUMERIC_MAX_PRECISION) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("value overflows numeric format"))); *************** *** 3154,3159 **** --- 3255,3271 ---- int ddigits; int i; + /* + * If we get here, var cannot be NUMERIC_NAN. Since we store NUMERIC_NAN + * in the weight field, then weight must be checked to be within + * bounds so that we do not overflow the weight to become NUMERIC_NAN + */ + if (var->weight <= NUMERIC_NAN) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value overflows numeric format"))); + + /* Do nothing if we have a default typmod (-1) */ if (typmod < (int32) (VARHDRSZ)) return; *************** *** 3166,3171 **** --- 3278,3284 ---- /* 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 *************** *** 3396,3401 **** --- 3509,3515 ---- return 1; return -1; } + if (var2->ndigits == 0) { if (var1->sign == NUMERIC_POS) Index: src/include/utils/numeric.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/utils/numeric.h,v retrieving revision 1.20 diff -c -r1.20 numeric.h *** src/include/utils/numeric.h 1 Jan 2005 05:43:09 -0000 1.20 --- src/include/utils/numeric.h 16 Nov 2005 17:00:53 -0000 *************** *** 15,24 **** #define _PG_NUMERIC_H_ /* ! * Hardcoded precision limit - arbitrary, but must be small enough that ! * dscale values will fit in 14 bits. */ ! #define NUMERIC_MAX_PRECISION 1000 /* * Internal limits on the scales chosen for calculation results --- 15,23 ---- #define _PG_NUMERIC_H_ /* ! * Hardcoded precision limit - maximum that can fit in Numeric storage */ ! #define NUMERIC_MAX_PRECISION 508 /* * Internal limits on the scales chosen for calculation results *************** *** 39,53 **** /* * Sign values and macros to deal with packing/unpacking n_sign_dscale */ ! #define NUMERIC_SIGN_MASK 0xC000 ! #define NUMERIC_POS 0x0000 ! #define NUMERIC_NEG 0x4000 ! #define NUMERIC_NAN 0xC000 ! #define NUMERIC_DSCALE_MASK 0x3FFF ! #define NUMERIC_SIGN(n) ((n)->n_sign_dscale & NUMERIC_SIGN_MASK) ! #define NUMERIC_DSCALE(n) ((n)->n_sign_dscale & NUMERIC_DSCALE_MASK) ! #define NUMERIC_IS_NAN(n) (NUMERIC_SIGN(n) != NUMERIC_POS && \ ! NUMERIC_SIGN(n) != NUMERIC_NEG) /* --- 38,64 ---- /* * Sign values and macros to deal with packing/unpacking n_sign_dscale */ ! #define NUMERIC_SIGN_MASK 0x4000 ! #define NUMERIC_POS 0x0000 ! #define NUMERIC_NEG 0x4000 ! ! #define NUMERIC_ABS_MASK 0xBFFF ! ! #define NUMERIC_DSCALE9_MASK 0x8000 ! #define NUMERIC_DSCALE9_1 0x8000 ! #define NUMERIC_DSCALE9_0 0x0000 ! ! #define NUMERIC_DIGIT_MASK 0x3FFF ! ! #define NUMERIC_NAN -128 ! /* See numeric.c for explanation of these two values */ ! #define NUMERIC_NAN_EXTERNAL 0xC000 ! ! #define NUMERIC_WEIGHT(n) ((n)->n_weight) ! #define NUMERIC_WEIGHT_MAX 127 ! ! #define NUMERIC_IS_NAN(n) ((n)->n_weight == NUMERIC_NAN) ! #define NUMERIC_HAS_DIGITS(n) ((n)->varlen != NUMERIC_HDRSZ) /* *************** *** 61,75 **** typedef struct NumericData { int32 varlen; /* Variable size (std varlena header) */ ! int16 n_weight; /* Weight of 1st digit */ ! uint16 n_sign_dscale; /* Sign + display scale */ char n_data[1]; /* Digits (really array of NumericDigit) */ } NumericData; typedef NumericData *Numeric; ! #define NUMERIC_HDRSZ (sizeof(int32) + sizeof(int16) + sizeof(uint16)) ! /* * fmgr interface macros --- 72,85 ---- typedef struct NumericData { int32 varlen; /* Variable size (std varlena header) */ ! int8 n_weight; /* Weight of 1st digit */ ! uint8 n_dscale8bits; /* First 8 bits of display scale */ char n_data[1]; /* Digits (really array of NumericDigit) */ } NumericData; typedef NumericData *Numeric; ! #define NUMERIC_HDRSZ (sizeof(int32) + sizeof(int8) + sizeof(uint8)) /* * fmgr interface macros