8000 Guard against bad "dscale" values in numeric_recv(). · tvims81/postgres@0927bf8 · GitHub
  • [go: up one dir, main page]

    Skip to content

    Commit 0927bf8

    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 df761e3 commit 0927bf8

    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
    @@ -717,6 +717,8 @@ numeric_recv(PG_FUNCTION_ARGS)
    717717
    alloc_var(&value, len);
    718718

    719719
    value.weight = (int16) pq_getmsgint(buf, sizeof(int16));
    720+
    /* we allow any int16 for weight --- OK? */
    721+
    720722
    value.sign = (uint16) pq_getmsgint(buf, sizeof(uint16));
    721723
    if (!(value.sign == NUMERIC_POS ||
    722724
    value.sign == NUMERIC_NEG ||
    @@ -726,6 +728,11 @@ numeric_recv(PG_FUNCTION_ARGS)
    726728
    errmsg("invalid sign in external \"numeric\" value")));
    727729

    728730
    value.dscale = (uint16) pq_getmsgint(buf, sizeof(uint16));
    731+
    if ((value.dscale & NUMERIC_DSCALE_MASK) != value.dscale)
    732+
    ereport(ERROR,
    733+
    (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
    734+
    errmsg("invalid scale in external \"numeric\" value")));
    735+
    729736
    for (i = 0; i < len; i++)
    730737
    {
    731738
    NumericDigit d = pq_getmsgint(buf, sizeof(NumericDigit));
    @@ -737,6 +744,14 @@ numeric_recv(PG_FUNCTION_ARGS)
    737744
    value.digits[i] = d;
    738745
    }
    739746

    747+
    /*
    748+
    * If the given dscale would hide any digits, truncate those digits away.
    749+
    * We could alternatively throw an error, but that would take a bunch of
    750+
    * extra code (about as much as trunc_var involves), and it might cause
    751+
    * client compatibility issues.
    752+
    */
    753+
    trunc_var(&value, value.dscale);
    754+
    740755
    apply_typmod(&value, typmod);
    741756

    742757
    res = make_result(&value);

    0 commit comments

    Comments
     (0)
    0