See -hackers thread for discussion.

Updated version of num508, against current CVS HEAD.

-- 
  Simon Riggs             
  EnterpriseDB   http://www.enterprisedb.com

Index: src/backend/utils/adt/numeric.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/adt/numeric.c,v
retrieving revision 1.105
diff -c -r1.105 numeric.c
*** src/backend/utils/adt/numeric.c	15 Jun 2007 20:56:50 -0000	1.105
--- src/backend/utils/adt/numeric.c	17 Jun 2007 20:30:43 -0000
***************
*** 85,93 ****
  #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,
--- 85,113 ----
  #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,
***************
*** 131,138 ****
  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 */
--- 151,158 ----
  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 */
***************
*** 200,206 ****
  {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};
--- 220,226 ----
  {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};
***************
*** 381,386 ****
--- 401,412 ----
   *
   * 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)
***************
*** 407,426 ****
  	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")));
--- 433,469 ----
  	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")));
***************
*** 437,442 ****
--- 480,487 ----
  
  /*
   *		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)
***************
*** 451,456 ****
--- 496,506 ----
  
  	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));
***************
*** 514,526 ****
  	 * 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(VARSIZE(num));
  		memcpy(new, num, VARSIZE(num));
! 		new->n_sign_dscale = NUMERIC_SIGN(new) |
! 			((uint16) scale & NUMERIC_DSCALE_MASK);
  		PG_RETURN_NUMERIC(new);
  	}
  
--- 564,575 ----
  	 * 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(VARSIZE(num));
  		memcpy(new, num, VARSIZE(num));
! 		new->n_dscale8bits = (uint8) scale;
  		PG_RETURN_NUMERIC(new);
  	}
  
***************
*** 613,618 ****
--- 662,668 ----
  {
  	Numeric		num = PG_GETARG_NUMERIC(0);
  	Numeric		res;
+ 	NumericDigit *resdigits;
  
  	/*
  	 * Handle NaN
***************
*** 625,632 ****
  	 */
  	res = (Numeric) palloc(VARSIZE(num));
  	memcpy(res, num, VARSIZE(num));
! 
! 	res->n_sign_dscale = NUMERIC_POS | NUMERIC_DSCALE(num);
  
  	PG_RETURN_NUMERIC(res);
  }
--- 675,682 ----
  	 */
  	res = (Numeric) palloc(VARSIZE(num));
  	memcpy(res, num, VARSIZE(num));
! 	resdigits = (NumericDigit *) res->n_data;
! 	resdigits[0] &= NUMERIC_ABS_MASK;
  
  	PG_RETURN_NUMERIC(res);
  }
***************
*** 637,642 ****
--- 687,693 ----
  {
  	Numeric		num = PG_GETARG_NUMERIC(0);
  	Numeric		res;
+ 	NumericDigit   *resdigits;
  
  	/*
  	 * Handle NaN
***************
*** 649,667 ****
  	 */
  	res = (Numeric) palloc(VARSIZE(num));
  	memcpy(res, num, VARSIZE(num));
! 
  	/*
! 	 * 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 (VARSIZE(num) != 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);
--- 700,723 ----
  	 */
  	res = (Numeric) palloc(VARSIZE(num));
  	memcpy(res, num, VARSIZE(num));
! 	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);
***************
*** 692,697 ****
--- 748,754 ----
  	Numeric		num = PG_GETARG_NUMERIC(0);
  	Numeric		res;
  	NumericVar	result;
+ 	NumericDigit *digits = (NumericDigit *) num->n_data;
  
  	/*
  	 * Handle NaN
***************
*** 701,721 ****
  
  	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 (VARSIZE(num) == 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);
--- 758,774 ----
  
  	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);
***************
*** 1123,1128 ****
--- 1176,1183 ----
  cmp_numerics(Numeric num1, Numeric num2)
  {
  	int			result;
+ 	NumericDigit *digits1 = (NumericDigit *) num1->n_data;
+ 	NumericDigit *digits2 = (NumericDigit *) num2->n_data;
  
  	/*
  	 * We consider all NANs to be equal and larger than any non-NAN. This is
***************
*** 1140,1151 ****
  	{
  		result = -1;			/* non-NAN < NAN */
  	}
  	else
  	{
  		result = cmp_var_common(NUMERIC_DIGITS(num1), NUMERIC_NDIGITS(num1),
! 								num1->n_weight, NUMERIC_SIGN(num1),
! 								NUMERIC_DIGITS(num2), NUMERIC_NDIGITS(num2),
! 								num2->n_weight, NUMERIC_SIGN(num2));
  	}
  
  	return result;
--- 1195,1225 ----
  	{
  		result = -1;			/* non-NAN < NAN */
  	}
+ 	else if (!NUMERIC_HAS_DIGITS(num1))
+ 	{
+ 		if (!NUMERIC_HAS_DIGITS(num2))
+ 			result = 0;			/* 0 = 0 */
+ 		else
+ 		{
+ 			if ((digits2[0] & NUMERIC_SIGN_MASK) == NUMERIC_POS)
+ 				result = -1;
+ 			else
+ 				result = 1;
+ 		}
+ 	}
+ 	else if (!NUMERIC_HAS_DIGITS(num2))
+ 	{
+ 		if ((digits1[0] & NUMERIC_SIGN_MASK) == NUMERIC_POS)
+ 			result = 1;
+ 		else
+ 			result = -1;
+ 	}
  	else
  	{
  		result = cmp_var_common(NUMERIC_DIGITS(num1), NUMERIC_NDIGITS(num1),
! 					num1->n_weight, digits1[0] & NUMERIC_SIGN_MASK,
! 					NUMERIC_DIGITS(num2), NUMERIC_NDIGITS(num2),
! 					num2->n_weight, digits2[0] & NUMERIC_SIGN_MASK);
  	}
  
  	return result;
***************
*** 2330,2335 ****
--- 2404,2410 ----
  	int			ndatums;
  	Numeric		N,
  				sumX;
+ 	NumericDigit *digits;
  
  	/* We assume the input is array of numeric */
  	deconstruct_array(transarray,
***************
*** 2340,2348 ****
  	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 (VARSIZE(N) == NUMERIC_HDRSZ)
  		PG_RETURN_NULL();
  
  	PG_RETURN_DATUM(DirectFunctionCall2(numeric_div,
--- 2415,2426 ----
  	N = DatumGetNumeric(transdatums[0]);
  	sumX = DatumGetNumeric(transdatums[1]);
  
+ 	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,
***************
*** 2772,2784 ****
  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, num->n_weight, NUMERIC_DSCALE(num));
! 	switch (NUMERIC_SIGN(num))
  	{
  		case NUMERIC_POS:
  			printf("POS");
--- 2850,2870 ----
  dump_numeric(const char *str, Numeric num)
  {
  	NumericDigit *digits = NUMERIC_DIGITS(num);
+ 	int			scale;
  	int			ndigits;
  	int			i;
  
  	ndigits = NUMERIC_NDIGITS(num);
  
! 	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");
***************
*** 2786,2805 ****
  		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
   */
--- 2872,2888 ----
  		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
   */
***************
*** 2809,2814 ****
--- 2892,2901 ----
  	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:
***************
*** 2817,2827 ****
  		case NUMERIC_NEG:
  			printf("NEG");
  			break;
- 		case NUMERIC_NAN:
- 			printf("NaN");
- 			break;
  		default:
- 			printf("SIGN=0x%x", var->sign);
  			break;
  	}
  
--- 2904,2910 ----
***************
*** 2871,2877 ****
  	digitbuf_free(var->buf);
  	var->buf = NULL;
  	var->digits = NULL;
! 	var->sign = NUMERIC_NAN;
  }
  
  
--- 2954,2961 ----
  	digitbuf_free(var->buf);
  	var->buf = NULL;
  	var->digits = NULL;
!     var->weight = NUMERIC_NAN;
! 	var->sign = NUMERIC_POS;
  }
  
  
***************
*** 3079,3089 ****
  
  	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));
  }
  
  
--- 3163,3184 ----
  
  	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;
  }
  
  
***************
*** 3268,3281 ****
  	int			sign = var->sign;
  	int			n;
  	Size		len;
  
! 	if (sign == NUMERIC_NAN)
  	{
  		result = (Numeric) palloc(NUMERIC_HDRSZ);
  
  		SET_VARSIZE(result, NUMERIC_HDRSZ);
! 		result->n_weight = 0;
! 		result->n_sign_dscale = NUMERIC_NAN;
  
  		dump_numeric("make_result()", result);
  		return result;
--- 3363,3377 ----
  	int			sign = var->sign;
  	int			n;
  	Size		len;
+ 	bool		setfirst = false;
  
! 	if (weight == NUMERIC_NAN)
  	{
  		result = (Numeric) palloc(NUMERIC_HDRSZ);
  
  		SET_VARSIZE(result, NUMERIC_HDRSZ);
! 		result->n_weight = NUMERIC_NAN;
! 		result->n_dscale8bits = 0;
  
  		dump_numeric("make_result()", result);
  		return result;
***************
*** 3294,3304 ****
  	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 */
--- 3390,3404 ----
  	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 */
***************
*** 3306,3318 ****
  	result = (Numeric) palloc(len);
  	SET_VARSIZE(result, 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")));
--- 3406,3433 ----
  	result = (Numeric) palloc(len);
  	SET_VARSIZE(result, 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")));
***************
*** 3337,3342 ****
--- 3452,3468 ----
  	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;
***************
*** 3349,3354 ****
--- 3475,3481 ----
  	/* 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
Index: src/include/utils/numeric.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/utils/numeric.h,v
retrieving revision 1.24
diff -c -r1.24 numeric.h
*** src/include/utils/numeric.h	27 Feb 2007 23:48:10 -0000	1.24
--- src/include/utils/numeric.h	17 Jun 2007 20:30:43 -0000
***************
*** 17,26 ****
  #include "fmgr.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
--- 17,25 ----
  #include "fmgr.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
***************
*** 41,55 ****
  /*
   * 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)
  
  
  /*
--- 40,66 ----
  /*
   * 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)	(VARSIZE(n) != NUMERIC_HDRSZ)
  
  
  /*
***************
*** 63,76 ****
  typedef struct NumericData
  {
  	int32		vl_len_;		/* varlena header (do not touch directly!) */
! 	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	(VARHDRSZ + sizeof(int16) + sizeof(uint16))
  
  
  /*
--- 74,87 ----
  typedef struct NumericData
  {
  	int32		vl_len_;		/* varlena header (do not touch directly!) */
! 	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	(VARHDRSZ + sizeof(int8) + sizeof(uint8))
  
  
  /*
---------------------------(end of broadcast)---------------------------
TIP 4: Have you searched our list archives?

               http://archives.postgresql.org

Reply via email to