Skip to content

Commit e640042

Browse files
committed
Guard against bad "dscale" values in numeric_recv().
We were not checking to see if the supplied dscale was valid for the given digit array when receiving binary-format numeric values. While dscale can validly be more than the number of nonzero fractional digits, it shouldn't be less; that case causes fractional digits to be hidden on display even though they're there and participate in arithmetic. Bug #12053 from Tommaso Sala indicates that there's at least one broken client library out there that sometimes supplies an incorrect dscale value, leading to strange behavior. This suggests that simply throwing an error might not be the best response; it would lead to failures in applications that might seem to be working fine today. What seems the least risky fix is to truncate away any digits that would be hidden by dscale. This preserves the existing behavior in terms of what will be printed for the transmitted value, while preventing subsequent arithmetic from producing results inconsistent with that. In passing, throw a specific error for the case of dscale being outside the range that will fit into a numeric's header. Before you got "value overflows numeric format", which is a bit misleading. Back-patch to all supported branches.
1 parent 7b46ef1 commit e640042

File tree

1 file changed

+15
-0
lines changed

1 file changed

+15
-0
lines changed

src/backend/utils/adt/numeric.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,8 @@ numeric_recv(PG_FUNCTION_ARGS)
656656
alloc_var(&value, len);
657657

658658
value.weight = (int16) pq_getmsgint(buf, sizeof(int16));
659+
/* we allow any int16 for weight --- OK? */
660+
659661
value.sign = (uint16) pq_getmsgint(buf, sizeof(uint16));
660662
if (!(value.sign == NUMERIC_POS ||
661663
value.sign == NUMERIC_NEG ||
@@ -665,6 +667,11 @@ numeric_recv(PG_FUNCTION_ARGS)
665667
errmsg("invalid sign in external \"numeric\" value")));
666668

667669
value.dscale = (uint16) pq_getmsgint(buf, sizeof(uint16));
670+
if ((value.dscale & NUMERIC_DSCALE_MASK) != value.dscale)
671+
ereport(ERROR,
672+
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
673+
errmsg("invalid scale in external \"numeric\" value")));
674+
668675
for (i = 0; i < len; i++)
669676
{
670677
NumericDigit d = pq_getmsgint(buf, sizeof(NumericDigit));
@@ -676,6 +683,14 @@ numeric_recv(PG_FUNCTION_ARGS)
676683
value.digits[i] = d;
677684
}
678685

686+
/*
687+
* If the given dscale would hide any digits, truncate those digits away.
688+
* We could alternatively throw an error, but that would take a bunch of
689+
* extra code (about as much as trunc_var involves), and it might cause
690+
* client compatibility issues.
691+
*/
692+
trunc_var(&value, value.dscale);
693+
679694
apply_typmod(&value, typmod);
680695

681696
res = make_result(&value);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy