mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Fix numeric_mul() overflow due to too many digits after decimal point.
This fixes an overflow error when using the numeric * operator if the result has more than 16383 digits after the decimal point by rounding the result. Overflow errors should only occur if the result has too many digits *before* the decimal point. Discussion: https://postgr.es/m/CAEZATCUmeFWCrq2dNzZpRj5+6LfN85jYiDoqm+ucSXhb9U2TbA@mail.gmail.com
This commit is contained in:
		@@ -233,6 +233,7 @@ struct NumericData
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define NUMERIC_DSCALE_MASK			0x3FFF
 | 
					#define NUMERIC_DSCALE_MASK			0x3FFF
 | 
				
			||||||
 | 
					#define NUMERIC_DSCALE_MAX			NUMERIC_DSCALE_MASK
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define NUMERIC_SIGN(n) \
 | 
					#define NUMERIC_SIGN(n) \
 | 
				
			||||||
	(NUMERIC_IS_SHORT(n) ? \
 | 
						(NUMERIC_IS_SHORT(n) ? \
 | 
				
			||||||
@@ -2955,7 +2956,11 @@ numeric_mul_opt_error(Numeric num1, Numeric num2, bool *have_error)
 | 
				
			|||||||
	 * Unlike add_var() and sub_var(), mul_var() will round its result. In the
 | 
						 * 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,
 | 
						 * case of numeric_mul(), which is invoked for the * operator on numerics,
 | 
				
			||||||
	 * we request exact representation for the product (rscale = sum(dscale of
 | 
						 * we request exact representation for the product (rscale = sum(dscale of
 | 
				
			||||||
	 * arg1, dscale of arg2)).
 | 
						 * arg1, dscale of arg2)).  If the exact result has more digits after the
 | 
				
			||||||
 | 
						 * decimal point than can be stored in a numeric, we round it.  Rounding
 | 
				
			||||||
 | 
						 * after computing the exact result ensures that the final result is
 | 
				
			||||||
 | 
						 * correctly rounded (rounding in mul_var() using a truncated product
 | 
				
			||||||
 | 
						 * would not guarantee this).
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	init_var_from_num(num1, &arg1);
 | 
						init_var_from_num(num1, &arg1);
 | 
				
			||||||
	init_var_from_num(num2, &arg2);
 | 
						init_var_from_num(num2, &arg2);
 | 
				
			||||||
@@ -2963,6 +2968,9 @@ numeric_mul_opt_error(Numeric num1, Numeric num2, bool *have_error)
 | 
				
			|||||||
	init_var(&result);
 | 
						init_var(&result);
 | 
				
			||||||
	mul_var(&arg1, &arg2, &result, arg1.dscale + arg2.dscale);
 | 
						mul_var(&arg1, &arg2, &result, arg1.dscale + arg2.dscale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (result.dscale > NUMERIC_DSCALE_MAX)
 | 
				
			||||||
 | 
							round_var(&result, NUMERIC_DSCALE_MAX);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res = make_result_opt_error(&result, have_error);
 | 
						res = make_result_opt_error(&result, have_error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	free_var(&result);
 | 
						free_var(&result);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2145,6 +2145,12 @@ select 4769999999999999999999999999999999999999999999999999999999999999999999999
 | 
				
			|||||||
 47699999999999999999999999999999999999999999999999999999999999999999999999999999999999985230000000000000000000000000000000000000000000000000000000000000000000000000000000000001
 | 
					 47699999999999999999999999999999999999999999999999999999999999999999999999999999999999985230000000000000000000000000000000000000000000000000000000000000000000000000000000000001
 | 
				
			||||||
(1 row)
 | 
					(1 row)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					select trim_scale((0.1 - 2e-16383) * (0.1 - 3e-16383));
 | 
				
			||||||
 | 
					 trim_scale 
 | 
				
			||||||
 | 
					------------
 | 
				
			||||||
 | 
					       0.01
 | 
				
			||||||
 | 
					(1 row)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
-- Test some corner cases for division
 | 
					-- Test some corner cases for division
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1044,6 +1044,8 @@ select 4770999999999999999999999999999999999999999999999999999999999999999999999
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
select 4769999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999;
 | 
					select 4769999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					select trim_scale((0.1 - 2e-16383) * (0.1 - 3e-16383));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
-- Test some corner cases for division
 | 
					-- Test some corner cases for division
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user