From 512110458d6916efb7515770d6c4f4b7477b1d22 Mon Sep 17 00:00:00 2001 From: Thomas Munro Date: Thu, 16 Nov 2023 15:53:51 +1300 Subject: [PATCH] ecpg: Use thread-safe _l() functions if possible. In order to use snprintf("%g") and strtod() with the C locale in certain parts of the ECPG code, we call uselocale() around those sections. Two operating systems don't have uselocale(), but our fallbacks were non-ideal. For NetBSD we'd use setlocale(), which is not thread-safe and dangerous, and for Windows we'd either use setlocale() or a Windows API that is egregiously different and hard to maintain, depending on the compiler tool chain. We can remove all of that and use not-yet-standardized [v]s[n]printf_l() and strtod_l(), if available. Those two operating systems, along with the other BSDs and macOS have them. Glibc has strtod_l() but not yet the other. Discussion: https://postgr.es/m/CWZBBRR6YA8D.8EHMDRGLCKCD%40neon.tech --- configure | 13 +- configure.ac | 5 +- meson.build | 5 +- src/include/pg_config.h.in | 15 +- src/interfaces/ecpg/ecpglib/connect.c | 55 ++++---- src/interfaces/ecpg/ecpglib/data.c | 3 +- src/interfaces/ecpg/ecpglib/descriptor.c | 29 +--- src/interfaces/ecpg/ecpglib/ecpglib_extern.h | 11 -- src/interfaces/ecpg/ecpglib/execute.c | 54 +------- src/interfaces/ecpg/include/pgtypes.h | 6 + src/interfaces/ecpg/include/pgtypes_format.h | 26 ++++ src/interfaces/ecpg/pgtypeslib/common.c | 138 ++++++++++++++++++- src/interfaces/ecpg/pgtypeslib/exports.txt | 6 + src/interfaces/ecpg/pgtypeslib/interval.c | 5 +- src/interfaces/ecpg/pgtypeslib/numeric.c | 3 +- src/tools/msvc/Solution.pm | 5 +- 16 files changed, 233 insertions(+), 146 deletions(-) create mode 100644 src/interfaces/ecpg/include/pgtypes_format.h diff --git a/configure b/configure index c064115038..91c9865ae3 100755 --- a/configure +++ b/configure @@ -15539,7 +15539,7 @@ fi LIBS_including_readline="$LIBS" LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'` -for ac_func in backtrace_symbols copyfile getifaddrs getpeerucred inet_pton kqueue mbstowcs_l memset_s posix_fallocate ppoll pthread_is_threaded_np setproctitle setproctitle_fast strchrnul strsignal syncfs sync_file_range uselocale wcstombs_l +for ac_func in backtrace_symbols copyfile getifaddrs getpeerucred inet_pton kqueue mbstowcs_l memset_s posix_fallocate ppoll pthread_is_threaded_np setproctitle setproctitle_fast strchrnul strsignal strtod_l syncfs sync_file_range vsnprintf_l vsprintf_l wcstombs_l do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -16309,17 +16309,6 @@ fi # Win32 (really MinGW) support if test "$PORTNAME" = "win32"; then - for ac_func in _configthreadlocale -do : - ac_fn_c_check_func "$LINENO" "_configthreadlocale" "ac_cv_func__configthreadlocale" -if test "x$ac_cv_func__configthreadlocale" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE__CONFIGTHREADLOCALE 1 -_ACEOF - -fi -done - case " $LIBOBJS " in *" dirmod.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS dirmod.$ac_objext" diff --git a/configure.ac b/configure.ac index f220b379b3..67ecb83447 100644 --- a/configure.ac +++ b/configure.ac @@ -1782,9 +1782,11 @@ AC_CHECK_FUNCS(m4_normalize([ setproctitle_fast strchrnul strsignal + strtod_l syncfs sync_file_range - uselocale + vsnprintf_l + vsprintf_l wcstombs_l ])) @@ -1868,7 +1870,6 @@ fi # Win32 (really MinGW) support if test "$PORTNAME" = "win32"; then - AC_CHECK_FUNCS(_configthreadlocale) AC_LIBOBJ(dirmod) AC_LIBOBJ(kill) AC_LIBOBJ(open) diff --git a/meson.build b/meson.build index 286d7e4269..e24277b473 100644 --- a/meson.build +++ b/meson.build @@ -2417,7 +2417,6 @@ endif # XXX: Might be worth conditioning some checks on the OS, to avoid doing # unnecessary checks over and over, particularly on windows. func_checks = [ - ['_configthreadlocale', {'skip': host_system != 'windows'}], ['backtrace_symbols', {'dependencies': [execinfo_dep]}], ['clock_gettime', {'dependencies': [rt_dep], 'define': false}], ['copyfile'], @@ -2456,9 +2455,11 @@ func_checks = [ ['strlcpy'], ['strnlen'], ['strsignal'], + ['strtod_l'], ['sync_file_range'], ['syncfs'], - ['uselocale'], + ['vsnprintf_l'], + ['vsprintf_l'], ['wcstombs_l'], ] diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index d8a2985567..ea047e85cf 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -429,6 +429,9 @@ /* Define to 1 if you have the `strsignal' function. */ #undef HAVE_STRSIGNAL +/* Define to 1 if you have the `strtod_l' function. */ +#undef HAVE_STRTOD_L + /* Define to 1 if the system has the type `struct option'. */ #undef HAVE_STRUCT_OPTION @@ -495,9 +498,6 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H -/* Define to 1 if you have the `uselocale' function. */ -#undef HAVE_USELOCALE - /* Define to 1 if you have BSD UUID support. */ #undef HAVE_UUID_BSD @@ -516,6 +516,12 @@ /* Define to 1 if your compiler knows the visibility("hidden") attribute. */ #undef HAVE_VISIBILITY_ATTRIBUTE +/* Define to 1 if you have the `vsnprintf_l' function. */ +#undef HAVE_VSNPRINTF_L + +/* Define to 1 if you have the `vsprintf_l' function. */ +#undef HAVE_VSPRINTF_L + /* Define to 1 if you have the `wcstombs_l' function. */ #undef HAVE_WCSTOMBS_L @@ -561,9 +567,6 @@ /* Define to 1 if your compiler understands __builtin_unreachable. */ #undef HAVE__BUILTIN_UNREACHABLE -/* Define to 1 if you have the `_configthreadlocale' function. */ -#undef HAVE__CONFIGTHREADLOCALE - /* Define to 1 if you have __cpuid. */ #undef HAVE__CPUID diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c index 8afb1f0a26..cf2083713c 100644 --- a/src/interfaces/ecpg/ecpglib/connect.c +++ b/src/interfaces/ecpg/ecpglib/connect.c @@ -8,12 +8,9 @@ #include "ecpglib.h" #include "ecpglib_extern.h" #include "ecpgtype.h" +#include "pgtypes.h" #include "sqlca.h" -#ifdef HAVE_USELOCALE -locale_t ecpg_clocale = (locale_t) 0; -#endif - static pthread_mutex_t connections_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_key_t actual_connection_key; static pthread_once_t actual_connection_key_once = PTHREAD_ONCE_INIT; @@ -484,37 +481,31 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p pthread_mutex_lock(&connections_mutex); /* - * ... but first, make certain we have created ecpg_clocale. Rely on - * holding connections_mutex to ensure this is done by only one thread. + * ... but first, make certain pgtypes' locale is set up. Rely on holding + * connections_mutex to ensure this is done by only one thread. */ -#ifdef HAVE_USELOCALE - if (!ecpg_clocale) + if (PGTYPESinit() < 0) { - ecpg_clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0); - if (!ecpg_clocale) - { - pthread_mutex_unlock(&connections_mutex); - ecpg_raise(lineno, ECPG_OUT_OF_MEMORY, - ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL); - if (host) - ecpg_free(host); - if (port) - ecpg_free(port); - if (options) - ecpg_free(options); - if (realname) - ecpg_free(realname); - if (dbname) - ecpg_free(dbname); - if (conn_keywords) - ecpg_free(conn_keywords); - if (conn_values) - ecpg_free(conn_values); - free(this); - return false; - } + pthread_mutex_unlock(&connections_mutex); + ecpg_raise(lineno, ECPG_OUT_OF_MEMORY, + ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL); + if (host) + ecpg_free(host); + if (port) + ecpg_free(port); + if (options) + ecpg_free(options); + if (realname) + ecpg_free(realname); + if (dbname) + ecpg_free(dbname); + if (conn_keywords) + ecpg_free(conn_keywords); + if (conn_values) + ecpg_free(conn_values); + free(this); + return false; } -#endif if (connection_name != NULL) this->name = ecpg_strdup(connection_name, lineno); diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c index fa56276758..0f3ab36575 100644 --- a/src/interfaces/ecpg/ecpglib/data.c +++ b/src/interfaces/ecpg/ecpglib/data.c @@ -10,6 +10,7 @@ #include "ecpglib_extern.h" #include "ecpgtype.h" #include "pgtypes_date.h" +#include "pgtypes_format.h" #include "pgtypes_interval.h" #include "pgtypes_numeric.h" #include "pgtypes_timestamp.h" @@ -466,7 +467,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno, pval++; if (!check_special_value(pval, &dres, &scan_length)) - dres = strtod(pval, &scan_length); + dres = PGTYPESstrtod(pval, &scan_length); if (isarray && *scan_length == '"') scan_length++; diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c index ad279e245c..e7d4cdf971 100644 --- a/src/interfaces/ecpg/ecpglib/descriptor.c +++ b/src/interfaces/ecpg/ecpglib/descriptor.c @@ -12,6 +12,7 @@ #include "ecpglib.h" #include "ecpglib_extern.h" #include "ecpgtype.h" +#include "pgtypes_format.h" #include "sql3types.h" #include "sqlca.h" #include "sqlda.h" @@ -478,43 +479,21 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...) /* Make sure we do NOT honor the locale for numeric input */ /* since the database gives the standard decimal point */ /* (see comments in execute.c) */ -#ifdef HAVE_USELOCALE /* * To get here, the above PQnfields() test must have found nonzero * fields. One needs a connection to create such a descriptor. (EXEC * SQL SET DESCRIPTOR can populate the descriptor's "items", but it * can't change the descriptor's PQnfields().) Any successful - * connection initializes ecpg_clocale. + * connection calls PGTYPESinit(). */ - Assert(ecpg_clocale); - stmt.oldlocale = uselocale(ecpg_clocale); -#else -#ifdef HAVE__CONFIGTHREADLOCALE - stmt.oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); -#endif - stmt.oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno); - setlocale(LC_NUMERIC, "C"); -#endif + PGTYPESbegin_clocale(&stmt.oldlocale); /* desperate try to guess something sensible */ stmt.connection = ecpg_get_connection(NULL); ecpg_store_result(ECPGresult, index, &stmt, &data_var); -#ifdef HAVE_USELOCALE - if (stmt.oldlocale != (locale_t) 0) - uselocale(stmt.oldlocale); -#else - if (stmt.oldlocale) - { - setlocale(LC_NUMERIC, stmt.oldlocale); - ecpg_free(stmt.oldlocale); - } -#ifdef HAVE__CONFIGTHREADLOCALE - if (stmt.oldthreadlocale != -1) - (void) _configthreadlocale(stmt.oldthreadlocale); -#endif -#endif + PGTYPESend_clocale(stmt.oldlocale); } else if (data_var.ind_type != ECPGt_NO_INDICATOR && data_var.ind_pointer != NULL) diff --git a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h index 01b4309a71..ae4c49d87e 100644 --- a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h +++ b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h @@ -59,10 +59,6 @@ struct ECPGtype_information_cache enum ARRAY_TYPE isarray; }; -#ifdef HAVE_USELOCALE -extern locale_t ecpg_clocale; /* LC_NUMERIC=C */ -#endif - /* structure to store one statement */ struct statement { @@ -76,14 +72,7 @@ struct statement bool questionmarks; struct variable *inlist; struct variable *outlist; -#ifdef HAVE_USELOCALE locale_t oldlocale; -#else - char *oldlocale; -#ifdef HAVE__CONFIGTHREADLOCALE - int oldthreadlocale; -#endif -#endif int nparams; char **paramvalues; int *paramlengths; diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c index 04d0b40c53..a9afc88a61 100644 --- a/src/interfaces/ecpg/ecpglib/execute.c +++ b/src/interfaces/ecpg/ecpglib/execute.c @@ -24,6 +24,7 @@ #include "ecpglib_extern.h" #include "ecpgtype.h" #include "pgtypes_date.h" +#include "pgtypes_format.h" #include "pgtypes_interval.h" #include "pgtypes_numeric.h" #include "pgtypes_timestamp.h" @@ -101,9 +102,6 @@ free_statement(struct statement *stmt) free_variable(stmt->outlist); ecpg_free(stmt->command); ecpg_free(stmt->name); -#ifndef HAVE_USELOCALE - ecpg_free(stmt->oldlocale); -#endif ecpg_free(stmt); } @@ -465,7 +463,7 @@ sprintf_double_value(char *ptr, double value, const char *delim) sprintf(ptr, "%s%s", "Infinity", delim); } else - sprintf(ptr, "%.15g%s", value, delim); + PGTYPESsprintf(ptr, "%.15g%s", value, delim); } static void @@ -481,7 +479,7 @@ sprintf_float_value(char *ptr, float value, const char *delim) sprintf(ptr, "%s%s", "Infinity", delim); } else - sprintf(ptr, "%.15g%s", value, delim); + PGTYPESsprintf(ptr, "%.15g%s", value, delim); } static char * @@ -1975,37 +1973,13 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator, /* * Make sure we do NOT honor the locale for numeric input/output since the - * database wants the standard decimal point. If available, use - * uselocale() for this because it's thread-safe. Windows doesn't have - * that, but it usually does have _configthreadlocale(). In some versions - * of MinGW, _configthreadlocale() exists but always returns -1 --- so - * treat that situation as if the function doesn't exist. + * database wants the standard decimal point. */ -#ifdef HAVE_USELOCALE - - /* - * Since ecpg_init() succeeded, we have a connection. Any successful - * connection initializes ecpg_clocale. - */ - Assert(ecpg_clocale); - stmt->oldlocale = uselocale(ecpg_clocale); - if (stmt->oldlocale == (locale_t) 0) - { - ecpg_do_epilogue(stmt); - return false; - } -#else -#ifdef HAVE__CONFIGTHREADLOCALE - stmt->oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); -#endif - stmt->oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno); - if (stmt->oldlocale == NULL) + if (PGTYPESbegin_clocale(&stmt->oldlocale) < 0) { ecpg_do_epilogue(stmt); return false; } - setlocale(LC_NUMERIC, "C"); -#endif /* * If statement type is ECPGst_prepnormal we are supposed to prepare the @@ -2213,23 +2187,7 @@ ecpg_do_epilogue(struct statement *stmt) if (stmt == NULL) return; -#ifdef HAVE_USELOCALE - if (stmt->oldlocale != (locale_t) 0) - uselocale(stmt->oldlocale); -#else - if (stmt->oldlocale) - setlocale(LC_NUMERIC, stmt->oldlocale); -#ifdef HAVE__CONFIGTHREADLOCALE - - /* - * This is a bit trickier than it looks: if we failed partway through - * statement initialization, oldthreadlocale could still be 0. But that's - * okay because a call with 0 is defined to be a no-op. - */ - if (stmt->oldthreadlocale != -1) - (void) _configthreadlocale(stmt->oldthreadlocale); -#endif -#endif + PGTYPESend_clocale(stmt->oldlocale); free_statement(stmt); } diff --git a/src/interfaces/ecpg/include/pgtypes.h b/src/interfaces/ecpg/include/pgtypes.h index dbf759b45f..18251d74c9 100644 --- a/src/interfaces/ecpg/include/pgtypes.h +++ b/src/interfaces/ecpg/include/pgtypes.h @@ -10,6 +10,12 @@ extern "C" extern void PGTYPESchar_free(char *ptr); +/* + * Initialize. Not thread-safe and should only be called in one thread at a + * time, but idempotent. Returns 0 on success, -1 on failure and sets errno. + */ +extern int PGTYPESinit(void); + #ifdef __cplusplus } #endif diff --git a/src/interfaces/ecpg/include/pgtypes_format.h b/src/interfaces/ecpg/include/pgtypes_format.h new file mode 100644 index 0000000000..d6dd06d361 --- /dev/null +++ b/src/interfaces/ecpg/include/pgtypes_format.h @@ -0,0 +1,26 @@ +/* + * We would like to be able to convert between strings and doubles using the C + * locale. We can set the thread's current locale with uselocale() and use + * snprintf() and strtod() according to the POSIX standard, but some systems + * haven't implemented uselocale() yet. We can use snprintf_l() and + * strtod_l() on some systems, but POSIX hasn't standardized those yet. All + * systems we target can do one or the other, so provide a simple abstraction. + */ + +#ifndef PGTYPES_FORMAT_H +#define PGTYPES_FORMAT_H + +#if defined(LOCALE_T_IN_XLOCALE) +#include +#else +#include +#endif + +extern int PGTYPESbegin_clocale(locale_t *old_locale); +extern void PGTYPESend_clocale(locale_t old_locale); + +extern double PGTYPESstrtod(const char *str, char **endptr); +extern int PGTYPESsprintf(char *str, const char *format, ...) pg_attribute_printf(2, 3); +extern int PGTYPESsnprintf(char *str, size_t size, const char *format, ...) pg_attribute_printf(3, 4); + +#endif diff --git a/src/interfaces/ecpg/pgtypeslib/common.c b/src/interfaces/ecpg/pgtypeslib/common.c index 8972229ca2..37ba35bc7a 100644 --- a/src/interfaces/ecpg/pgtypeslib/common.c +++ b/src/interfaces/ecpg/pgtypeslib/common.c @@ -3,8 +3,142 @@ #include "postgres_fe.h" #include "pgtypes.h" +#include "pgtypes_format.h" #include "pgtypeslib_extern.h" +/* + * Use wcstombs_l's header as a clue about where to find the other extra _l + * functions. + */ +#if defined(LOCALE_T_IN_XLOCALE) || defined(WCSTOMBS_L_IN_XLOCALE) +#include +#else +#include +#endif + +#include +#include +#include + +static locale_t PGTYPESclocale = (locale_t) 0; + +int +PGTYPESinit(void) +{ + /* Already called? */ + if (PGTYPESclocale != (locale_t) 0) + return 0; + +#ifdef WIN32 + PGTYPESclocale = _create_locale(LC_ALL, "C"); +#else + PGTYPESclocale = newlocale(LC_ALL_MASK, "C", (locale_t) 0); +#endif + if (PGTYPESclocale == (locale_t) 0) + return -1; + return -0; +} + +/* + * If any of these _l() functions are missing, we need to mess with the + * thread-local locale. + */ +#if !defined(HAVE_STRTOD_L) || !defined(HAVE_VSPRINTF_L) || !defined(HAVE_VSNPRINTF_L) +#if defined(WIN32) +/* Windows has all of these in slightly scrambled form. */ +#else +/* At least one of these functions is missing. We'll do the save/restore. */ +#define PGTYPES_MUST_SAVE_RESTORE_THREAD_LOCALE +#endif +#endif + +/* + * Before running code that might do conversions, call this to change the + * thread's current locale on platforms that need it. This is done as a + * separate function rather than inside individual conversion wrappers for + * some batching effect, avoiding repeated save/restore. + * + * PGTYPESinit() must have been called, or this will have no effect. + */ +int +PGTYPESbegin_clocale(locale_t *old_locale) +{ +#ifdef PGTYPES_MUST_SAVE_RESTORE_THREAD_LOCALE + /* + * On this platform, at least one of the functions below expects us to + * have changed the thread's locale. The caller provides space for us to + * store the current locale, so we can change it back later. + */ + *old_locale = uselocale(PGTYPESclocale); + return (*old_locale == (locale_t) 0) ? -1 : 0; +#else + /* + * Dummy value. We have _l() variants of the functions we need, and we + * might not even have uselocale() on this platform. + */ + *old_locale = (locale_t) 0; + return 0; +#endif +} + +/* + * Restore the current thread's locale. Call with the value returned by + * PGTYPESbegin_clocale(). Does nothing on platforms with all the required + * _l() functions. + */ +void +PGTYPESend_clocale(locale_t old_locale) +{ +#ifdef PGTYPES_MUST_SAVE_RESTORE_THREAD_LOCALE + /* Put it back. */ + uselocale(old_locale); +#endif +} + +double +PGTYPESstrtod(const char *str, char **endptr) +{ +#if defined(WIN32) + return _strtod_l(str, endptr, PGTYPESclocale); +#elif defined(HAVE_STRTOD_L) + return strtod_l(str, endptr, PGTYPESclocale); +#else + return strtod(str, endptr); +#endif +} + +int +PGTYPESsprintf(char *str, const char *format, ...) +{ + va_list args; + + va_start(args, format); + +#if defined(WIN32) + return _vsprintf_l(str, format, PGTYPESclocale, args); +#elif defined(HAVE_VSPRINTF_L) + return vsprintf_l(str, PGTYPESclocale, format, args); +#else + return vsprintf(str, format, args); +#endif +} + +int +PGTYPESsnprintf(char *str, size_t size, const char *format, ...) +{ + va_list args; + + va_start(args, format); + +#if defined(WIN32) + return _vsnprintf_l(str, size, format, PGTYPESclocale, args); +#elif defined(HAVE_VSPRINTF_L) + return vsnprintf_l(str, size, PGTYPESclocale, format, args); +#else + return vsnprintf(str, size, format, args); +#endif +} + /* Return value is zero-filled. */ char * pgtypes_alloc(long size) @@ -81,8 +215,8 @@ pgtypes_fmt_replace(union un_fmt_comb replace_val, int replace_type, char **outp switch (replace_type) { case PGTYPES_TYPE_DOUBLE_NF: - i = snprintf(t, PGTYPES_FMT_NUM_MAX_DIGITS, - "%0.0g", replace_val.double_val); + i = PGTYPESsnprintf(t, PGTYPES_FMT_NUM_MAX_DIGITS, + "%0.0g", replace_val.double_val); break; case PGTYPES_TYPE_INT64: i = snprintf(t, PGTYPES_FMT_NUM_MAX_DIGITS, diff --git a/src/interfaces/ecpg/pgtypeslib/exports.txt b/src/interfaces/ecpg/pgtypeslib/exports.txt index 2d5ec17656..3fe02bc60a 100644 --- a/src/interfaces/ecpg/pgtypeslib/exports.txt +++ b/src/interfaces/ecpg/pgtypeslib/exports.txt @@ -46,3 +46,9 @@ PGTYPEStimestamp_sub 43 PGTYPEStimestamp_sub_interval 44 PGTYPEStimestamp_to_asc 45 PGTYPESchar_free 46 +PGTYPESinit 47 +PGTYPESsprintf 48 +PGTYPESsnprintf 49 +PGTYPESstrtod 50 +PGTYPESbegin_clocale 51 +PGTYPESend_clocale 52 diff --git a/src/interfaces/ecpg/pgtypeslib/interval.c b/src/interfaces/ecpg/pgtypeslib/interval.c index 936a688381..124ed5e1c2 100644 --- a/src/interfaces/ecpg/pgtypeslib/interval.c +++ b/src/interfaces/ecpg/pgtypeslib/interval.c @@ -13,6 +13,7 @@ #include "common/string.h" #include "dt.h" #include "pgtypes_error.h" +#include "pgtypes_format.h" #include "pgtypes_interval.h" #include "pgtypeslib_extern.h" @@ -60,7 +61,7 @@ ParseISO8601Number(const char *str, char **endptr, int *ipart, double *fpart) if (!(isdigit((unsigned char) *str) || *str == '-' || *str == '.')) return DTERR_BAD_FORMAT; errno = 0; - val = strtod(str, endptr); + val = PGTYPESstrtod(str, endptr); /* did we not see anything that looks like a double? */ if (*endptr == str || errno != 0) return DTERR_BAD_FORMAT; @@ -455,7 +456,7 @@ DecodeInterval(char **field, int *ftype, int nf, /* int range, */ else if (*cp == '.') { errno = 0; - fval = strtod(cp, &cp); + fval = PGTYPESstrtod(cp, &cp); if (*cp != '\0' || errno != 0) return DTERR_BAD_FORMAT; diff --git a/src/interfaces/ecpg/pgtypeslib/numeric.c b/src/interfaces/ecpg/pgtypeslib/numeric.c index 35e7b92da4..a8bc19cc8f 100644 --- a/src/interfaces/ecpg/pgtypeslib/numeric.c +++ b/src/interfaces/ecpg/pgtypeslib/numeric.c @@ -7,6 +7,7 @@ #include #include "pgtypes_error.h" +#include "pgtypes_format.h" #include "pgtypes_numeric.h" #include "pgtypeslib_extern.h" @@ -1414,7 +1415,7 @@ PGTYPESnumeric_from_double(double d, numeric *dst) numeric *tmp; int i; - if (sprintf(buffer, "%.*g", DBL_DIG, d) <= 0) + if (PGTYPESsprintf(buffer, "%.*g", DBL_DIG, d) <= 0) return -1; if ((tmp = PGTYPESnumeric_from_asc(buffer, NULL)) == NULL) diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm index 98a5b5d872..6c550f3d37 100644 --- a/src/tools/msvc/Solution.pm +++ b/src/tools/msvc/Solution.pm @@ -339,6 +339,7 @@ sub GenerateFiles HAVE_STRLCPY => undef, HAVE_STRNLEN => 1, HAVE_STRSIGNAL => undef, + HAVE_STRTOD_L => undef, HAVE_STRUCT_OPTION => undef, HAVE_STRUCT_SOCKADDR_SA_LEN => undef, HAVE_STRUCT_TM_TM_ZONE => undef, @@ -361,7 +362,6 @@ sub GenerateFiles HAVE_UINT8 => undef, HAVE_UNION_SEMUN => undef, HAVE_UNISTD_H => 1, - HAVE_USELOCALE => undef, HAVE_UUID_BSD => undef, HAVE_UUID_E2FS => undef, HAVE_UUID_OSSP => undef, @@ -369,6 +369,8 @@ sub GenerateFiles HAVE_UUID_UUID_H => undef, HAVE_WCSTOMBS_L => undef, HAVE_VISIBILITY_ATTRIBUTE => undef, + HAVE_VSNPRINTF_L => undef, + HAVE_VSPRINTF_L => undef, HAVE_X509_GET_SIGNATURE_INFO => undef, HAVE_X86_64_POPCNTQ => undef, HAVE__BOOL => undef, @@ -383,7 +385,6 @@ sub GenerateFiles HAVE__BUILTIN_POPCOUNT => undef, HAVE__BUILTIN_TYPES_COMPATIBLE_P => undef, HAVE__BUILTIN_UNREACHABLE => undef, - HAVE__CONFIGTHREADLOCALE => 1, HAVE__CPUID => 1, HAVE__GET_CPUID => undef, HAVE__STATIC_ASSERT => undef, -- 2.39.3 (Apple Git-145)