aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/numeric.c
diff options
context:
space:
mode:
authorRobert Haas <rhaas@postgresql.org>2010-08-04 17:33:09 +0000
committerRobert Haas <rhaas@postgresql.org>2010-08-04 17:33:09 +0000
commit97f38001acc61449f7ac09c539ccc29e40fecd26 (patch)
treee7725279b11cab95942b94d328adc16473bb7ce9 /src/backend/utils/adt/numeric.c
parentba19b236769c9dcb5782c804276abd0db667fe61 (diff)
downloadpostgresql-97f38001acc61449f7ac09c539ccc29e40fecd26.tar.gz
postgresql-97f38001acc61449f7ac09c539ccc29e40fecd26.zip
Fix numeric_maximum_size() calculation.
The old computation can sometimes underestimate the necessary space by 2 bytes; however we're not back-patching this, because this result isn't used for anything critical. Per discussion with Tom Lane, make the typmod test in this function match the ones in numeric() and apply_typmod() exactly.
Diffstat (limited to 'src/backend/utils/adt/numeric.c')
-rw-r--r--src/backend/utils/adt/numeric.c31
1 files changed, 25 insertions, 6 deletions
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 8398bb90e4f..0b2a1b16737 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -14,7 +14,7 @@
* Copyright (c) 1998-2010, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.125 2010/08/03 23:09:29 rhaas Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.126 2010/08/04 17:33:09 rhaas Exp $
*
*-------------------------------------------------------------------------
*/
@@ -565,18 +565,37 @@ numeric_is_nan(Numeric num)
* Maximum size of a numeric with given typmod, or -1 if unlimited/unknown.
*/
int32
-numeric_maximum_size(int32 typemod)
+numeric_maximum_size(int32 typmod)
{
int precision;
+ int numeric_digits;
- if (typemod <= VARHDRSZ)
+ if (typmod < (int32) (VARHDRSZ))
return -1;
/* precision (ie, max # of digits) is in upper bits of typmod */
- precision = ((typemod - VARHDRSZ) >> 16) & 0xffff;
+ precision = ((typmod - VARHDRSZ) >> 16) & 0xffff;
+
+ /*
+ * This formula computes the maximum number of NumericDigits we could
+ * need in order to store the specified number of decimal digits.
+ * Because the weight is stored as a number of NumericDigits rather
+ * than a number of decimal digits, it's possible that the first
+ * NumericDigit will contain only a single decimal digit. Thus, the
+ * first two decimal digits can require two NumericDigits to store,
+ * but it isn't until we reach DEC_DIGITS + 2 decimal digits that we
+ * potentially need a third NumericDigit.
+ */
+ numeric_digits = (precision + 2 * (DEC_DIGITS - 1)) / DEC_DIGITS;
- /* Numeric stores 2 decimal digits/byte, plus header */
- return (precision + 1) / 2 + NUMERIC_HDRSZ;
+ /*
+ * 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));
}
/*