Hello Noah,

On Mon, Sep 02, 2019 at 10:06:00AM -0700, Noah Misch wrote:

> > What are your
> > thoughts on this, particularly license-wise where to grab an
> > implementation from (BSD?)?
> BSD is a good bet.  More often than not, I use NetBSD as a source of library
> code like this.

I've finally had some time to look into this. The NetBSD as well as
glibc implementations of strtod() are huge. Duplicating them in DBD::Pg
just for this single call would be a nightmare IMO.

I did however stumble across strtod_l() which takes a locale as a third
argument. On OS X and the BSDs a NULL for this argument stands for the C
locale which would be exactly what we need here.

Unfortunately, glibc requires a valid locale_t pointer for the third
argument. This would lead to code like this:

diff --git a/dbdimp.c b/dbdimp.c
index c88321f..f261a86 100644
--- a/dbdimp.c
+++ b/dbdimp.c
@@ -3739,8 +3739,15 @@ AV * dbd_st_fetch (SV * sth, imp_sth_t * imp_sth)
                                                break;
                                        case PG_FLOAT4:
                                        case PG_FLOAT8:
-                                               sv_setnv(sv, strtod((char 
*)value, NULL));
-                                               break;
+                                               {
+                                                       /* use extended locale 
version of strtod with C
+                                                        * locale argument to 
force . as decimal point
+                                                        * character for perl 
versions < 5.19 */
+                                                       locale_t cloc = 
newlocale(LC_ALL_MASK, "C", NULL);
+                                                       sv_setnv(sv, 
strtod_l((char *)value, NULL, cloc));
+                                                       freelocale(cloc);
+                                                       break;
+                                               }
                                        default:
                                                sv_setpvn(sv, (char *)value, 
value_len);
                                        }

It should be possible to keep the C locale_t around in a static local or
global variable for performance. As further optimization we could check
the decimal point character of the current locale and just keep using
strtod() if it's the dot already. This is discussed in below lua thread
as well as here: https://github.com/nlohmann/json/issues/302. I'm not
quite sure which direction they went in the end.

I was not able to find any better overview on standards conformance and
availability than this discussion of the same problem by the lua folks:
http://lua-users.org/lists/lua-l/2016-04/msg00215.html. Seems to me this
could severely constrain portability of DBD::Pg. Since only old perl
versions are affected, we could use ifdefs to conditionalise it and
maybe adopt a best-effort approach.

And finally: Without _GNU_SOURCE defined, the above code still compiles
and links with glibc but returns 0 with my 1,1 test case. So we'd likely
need to do some more testing and sanity checks here.

What do you think?
-- 
Michael

Reply via email to