Skip to content

Commit 97f3800

Browse files
committed
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.
1 parent ba19b23 commit 97f3800

File tree

1 file changed

+25
-6
lines changed

1 file changed

+25
-6
lines changed

src/backend/utils/adt/numeric.c

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* Copyright (c) 1998-2010, PostgreSQL Global Development Group
1515
*
1616
* IDENTIFICATION
17-
* $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.125 2010/08/03 23:09:29 rhaas Exp $
17+
* $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.126 2010/08/04 17:33:09 rhaas Exp $
1818
*
1919
*-------------------------------------------------------------------------
2020
*/
@@ -565,18 +565,37 @@ numeric_is_nan(Numeric num)
565565
* Maximum size of a numeric with given typmod, or -1 if unlimited/unknown.
566566
*/
567567
int32
568-
numeric_maximum_size(int32 typemod)
568+
numeric_maximum_size(int32 typmod)
569569
{
570570
int precision;
571+
int numeric_digits;
571572

572-
if (typemod <= VARHDRSZ)
573+
if (typmod < (int32) (VARHDRSZ))
573574
return -1;
574575

575576
/* precision (ie, max # of digits) is in upper bits of typmod */
576-
precision = ((typemod - VARHDRSZ) >> 16) & 0xffff;
577+
precision = ((typmod - VARHDRSZ) >> 16) & 0xffff;
578+
579+
/*
580+
* This formula computes the maximum number of NumericDigits we could
581+
* need in order to store the specified number of decimal digits.
582+
* Because the weight is stored as a number of NumericDigits rather
583+
* than a number of decimal digits, it's possible that the first
584+
* NumericDigit will contain only a single decimal digit. Thus, the
585+
* first two decimal digits can require two NumericDigits to store,
586+
* but it isn't until we reach DEC_DIGITS + 2 decimal digits that we
587+
* potentially need a third NumericDigit.
588+
*/
589+
numeric_digits = (precision + 2 * (DEC_DIGITS - 1)) / DEC_DIGITS;
577590

578-
/* Numeric stores 2 decimal digits/byte, plus header */
579-
return (precision + 1) / 2 + NUMERIC_HDRSZ;
591+
/*
592+
* In most cases, the size of a numeric will be smaller than the value
593+
* computed below, because the varlena header will typically get toasted
594+
* down to a single byte before being stored on disk, and it may also
595+
* be possible to use a short numeric header. But our job here is to
596+
* compute the worst case.
597+
*/
598+
return NUMERIC_HDRSZ + (numeric_digits * sizeof(NumericDigit));
580599
}
581600

582601
/*

0 commit comments

Comments
 (0)