Search Postgresql Archives

Re: Strange results when casting string to double

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 





On 19.02.2022 20:34 Tom Lane wrote:

Per grep, there is no call of fesetround() in the Postgres source
tree.  I'm not sure offhand whether libc exposes any other APIs
that could change the rounding mode, but I am quite sure that we
wouldn't be intentionally changing it anywhere.

The OS would surely allow each process to have its own setting of the
rounding mode, so I doubt you can see it from outside.

Another point to keep in mind is that no matter how invasive that
import script might be, it's still hard to explain how it'd affect
the rounding mode in other backend processes.  You have to postulate
either that the rounding mode has been changed in the postmaster
process (and then inherited by session backends via fork()), or that
some code running at the time of child process creation changes the
mode, or that they replaced numeric_float8 with something else.

I think the only way that the postmaster's rounding mode could change
after postmaster start is the cosmic-ray hypothesis; while we do have
features that'd allow loading extra code into the postmaster, I'm
pretty sure they only take effect at postmaster start.  So even if
that import script tried to do that, it wouldn't have succeeded yet.

Of the other two hypotheses, "substitute numeric_float8" seems like
the most likely, especially given the other stuff you mentioned the
script doing.  Have you checked the relevant pg_cast entry to see
if it's been changed?  It'd also be interesting to see if the odd
rounding behavior happens in all databases of the cluster or just
one.

The script has finished!

After a restart of the database, everything works as expected again. Rounding as well as text/numeric to double precision works the same on all of my servers.

Prior to restarting, I've implemented my own Python based versions of both

int fegetround(void);

int fesetround(int rounding_mode integer);

Have a look a these:

CREATE OR REPLACE FUNCTION fegetround()
  RETURNS integer AS
$BODY$
    if 'fn.fegetround' in SD:
        return SD['fn.fegetround']()

    from ctypes import cdll
    from ctypes.util import find_library
    libm = cdll.LoadLibrary(find_library('m'))
    def fegetround():
        return libm.fegetround()

    SD['fn.fegetround'] = fegetround
    return SD['fn.fegetround']()
 $BODY$
  LANGUAGE plpython3u VOLATILE
  COST 100;

CREATE OR REPLACE FUNCTION fesetround(rounding_mode integer)
  RETURNS integer AS
$BODY$
    if 'fn.fesetround' in SD:
        return SD['fn.fesetround'](rounding_mode)

    from ctypes import cdll
    from ctypes.util import find_library
    libm = cdll.LoadLibrary(find_library('m'))
    def fesetround(rounding_mode):
        return libm.fesetround(rounding_mode)

    SD['fn.fesetround'] = fesetround
    return SD['fn.fesetround'](rounding_mode)
 $BODY$
  LANGUAGE plpython3u VOLATILE STRICT
  COST 100;

With those, I was able to proof, that actually the "wrong" rounding mode

FE_DOWNWARD (0x400)

was in effect for every new process/connection with all the described effects on casting from string or numeric to double precision:

SELECT 1.56::double precision
-> 1.55999999999999

Setting rounding mode to

FE_TONEAREST (0x0),

instantly lead back to the expected casting behavior:

SELECT 1.56::double precision
-> 1.56

Setting rounding mode after restarting the database is still possible, however, new sessions start off with the "correct" rounding mode FE_TONEAREST (0x0). So, the only thing that's really changed after the restart was, that the postmaster now has the "correct" rounding mode, which it promotes down when forking off child processes.

We'll likely never know, why ever the postmaster got tainted with that FE_DOWNWARD (0x400) rounding mode.

As Tom Lane said, no matter how aggressive the script could be, it can, if at all, only change its current session's rounding mode. So, maybe it actually was a random bit flip or a side effect caused by a quite rare error condition in postmaster.

Nearly the same is true for any core functions or casts hijacked by the script - these are only in effect for the database the script was ever connecting to. In my case, the script only used one database. However, the issue was present with any database.

Two official math functions to get and set the session's rounding mode provided by PostgreSQL could be a good add-on for any of the next versions of the database. Thinking about it again... maybe that's just too dangerous :-p

Finally, many thanks to all that supported me and came up with that many helpful ideas! :-)

Regards, Carsten





[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Postgresql Jobs]     [Postgresql Admin]     [Postgresql Performance]     [Linux Clusters]     [PHP Home]     [PHP on Windows]     [Kernel Newbies]     [PHP Classes]     [PHP Databases]     [Postgresql & PHP]     [Yosemite]

  Powered by Linux