1
0
mirror of https://github.com/postgres/postgres.git synced 2025-10-22 14:32:25 +03:00
Files
postgres/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
Peter Eisentraut 8e993bff53 Tidy up locale thread safety in ECPG library.
Remove setlocale() and _configthreadlocal() as fallback strategy on
systems that don't have uselocale(), where ECPG tries to control
LC_NUMERIC formatting on input and output of floating point numbers.  It
was probably broken on some systems (NetBSD), and the code was also
quite messy and complicated, with obsolete configure tests (Windows).
It was also arguably broken, or at least had unstated environmental
requirements, if pgtypeslib code was called directly.

Instead, introduce PG_C_LOCALE to refer to the "C" locale as a locale_t
value.  It maps to the special constant LC_C_LOCALE when defined by libc
(macOS, NetBSD), or otherwise uses a process-lifetime locale_t that is
allocated on first use, just as ECPG previously did itself.  The new
replacement might be more widely useful.  Then change the float parsing
and printing code to pass that to _l() functions where appropriate.

Unfortunately the portability of those functions is a bit complicated.
First, many obvious and useful _l() functions are missing from POSIX,
though most standard libraries define some of them anyway.  Second,
although the thread-safe save/restore technique can be used to replace
the missing ones, Windows and NetBSD refused to implement standard
uselocale().  They might have a point: "wide scope" uselocale() is hard
to combine with other code and error-prone, especially in library code.
Luckily they have the  _l() functions we want so far anyway.  So we have
to be prepared for both ways of doing things:

1.  In ECPG, use strtod_l() for parsing, and supply a port.h replacement
using uselocale() over a limited scope if missing.

2.  Inside our own snprintf.c, use three different approaches to format
floats.  For frontend code, call libc's snprintf_l(), or wrap libc's
snprintf() in uselocale() if it's missing.  For backend code, snprintf.c
can keep assuming that the global locale's LC_NUMERIC is "C" and call
libc's snprintf() without change, for now.

(It might eventually be possible to call our in-tree Ryū routines to
display floats in snprintf.c, given the C-locale-always remit of our
in-tree snprintf(), but this patch doesn't risk changing anything that
complicated.)

Author: Thomas Munro <thomas.munro@gmail.com>
Reviewed-by: Peter Eisentraut <peter@eisentraut.org>
Reviewed-by: Tristan Partin <tristan@partin.io>
Reviewed-by: Heikki Linnakangas <hlinnaka@iki.fi>
Discussion: https://postgr.es/m/CWZBBRR6YA8D.8EHMDRGLCKCD%40neon.tech
2025-03-28 16:18:36 +01:00

257 lines
7.7 KiB
C

/* src/interfaces/ecpg/ecpglib/ecpglib_extern.h */
#ifndef _ECPG_ECPGLIB_EXTERN_H
#define _ECPG_ECPGLIB_EXTERN_H
#include "ecpg_config.h"
#include "ecpgtype.h"
#include "libpq-fe.h"
#include "sqlca.h"
#include "sqlda-compat.h"
#include "sqlda-native.h"
#ifndef CHAR_BIT
#include <limits.h>
#endif
enum COMPAT_MODE
{
ECPG_COMPAT_PGSQL = 0, ECPG_COMPAT_INFORMIX, ECPG_COMPAT_INFORMIX_SE, ECPG_COMPAT_ORACLE
};
extern bool ecpg_internal_regression_mode;
#define INFORMIX_MODE(X) ((X) == ECPG_COMPAT_INFORMIX || (X) == ECPG_COMPAT_INFORMIX_SE)
#define ORACLE_MODE(X) ((X) == ECPG_COMPAT_ORACLE)
enum ARRAY_TYPE
{
ECPG_ARRAY_ERROR, ECPG_ARRAY_NOT_SET, ECPG_ARRAY_ARRAY, ECPG_ARRAY_VECTOR, ECPG_ARRAY_NONE
};
#define ECPG_IS_ARRAY(X) ((X) == ECPG_ARRAY_ARRAY || (X) == ECPG_ARRAY_VECTOR)
/* A generic varchar type. */
struct ECPGgeneric_varchar
{
int len;
char arr[FLEXIBLE_ARRAY_MEMBER];
};
/* A generic bytea type. */
struct ECPGgeneric_bytea
{
int len;
char arr[FLEXIBLE_ARRAY_MEMBER];
};
/*
* type information cache
*/
struct ECPGtype_information_cache
{
struct ECPGtype_information_cache *next;
int oid;
enum ARRAY_TYPE isarray;
};
/* structure to store one statement */
struct statement
{
int lineno;
char *command;
char *name;
struct connection *connection;
enum COMPAT_MODE compat;
bool force_indicator;
enum ECPG_statement_type statement_type;
bool questionmarks;
struct variable *inlist;
struct variable *outlist;
int nparams;
char **paramvalues;
int *paramlengths;
int *paramformats;
PGresult *results;
};
/* structure to store prepared statements for a connection */
struct prepared_statement
{
char *name;
bool prepared;
struct statement *stmt;
struct prepared_statement *next;
};
/* structure to store connections */
struct connection
{
char *name;
PGconn *connection;
bool autocommit;
struct ECPGtype_information_cache *cache_head;
struct prepared_statement *prep_stmts;
struct connection *next;
};
/* structure to store descriptors */
struct descriptor
{
char *name;
PGresult *result;
struct descriptor *next;
int count;
struct descriptor_item *items;
};
struct descriptor_item
{
int num;
char *data;
int indicator;
int length;
int precision;
int scale;
int type;
bool is_binary;
int data_len;
struct descriptor_item *next;
};
struct variable
{
enum ECPGttype type;
void *value;
void *pointer;
long varcharsize;
long arrsize;
long offset;
enum ECPGttype ind_type;
void *ind_value;
void *ind_pointer;
long ind_varcharsize;
long ind_arrsize;
long ind_offset;
struct variable *next;
};
struct var_list
{
int number;
void *pointer;
struct var_list *next;
};
extern struct var_list *ivlist;
/* Here are some methods used by the lib. */
bool ecpg_add_mem(void *ptr, int lineno);
bool ecpg_get_data(const PGresult *, int, int, int, enum ECPGttype type,
enum ECPGttype, char *, char *, long, long, long,
enum ARRAY_TYPE, enum COMPAT_MODE, bool);
void ecpg_pthreads_init(void);
struct connection *ecpg_get_connection(const char *connection_name);
char *ecpg_alloc(long size, int lineno);
char *ecpg_auto_alloc(long size, int lineno);
char *ecpg_realloc(void *ptr, long size, int lineno);
void ecpg_free(void *ptr);
bool ecpg_init(const struct connection *con,
const char *connection_name,
const int lineno);
char *ecpg_strdup(const char *string, int lineno);
const char *ecpg_type_name(enum ECPGttype typ);
int ecpg_dynamic_type(Oid type);
int sqlda_dynamic_type(Oid type, enum COMPAT_MODE compat);
void ecpg_clear_auto_mem(void);
struct descriptor *ecpg_find_desc(int line, const char *name);
struct prepared_statement *ecpg_find_prepared_statement(const char *name,
struct connection *con,
struct prepared_statement **prev_);
bool ecpg_store_result(const PGresult *results, int act_field,
const struct statement *stmt, struct variable *var);
bool ecpg_store_input(const int lineno, const bool force_indicator,
const struct variable *var,
char **tobeinserted_p, bool quote);
void ecpg_free_params(struct statement *stmt, bool print);
bool ecpg_do_prologue(int lineno, const int compat,
const int force_indicator, const char *connection_name,
const bool questionmarks, enum ECPG_statement_type statement_type,
const char *query, va_list args,
struct statement **stmt_out);
bool ecpg_build_params(struct statement *stmt);
bool ecpg_autostart_transaction(struct statement *stmt);
bool ecpg_execute(struct statement *stmt);
bool ecpg_process_output(struct statement *stmt, bool clear_result);
void ecpg_do_epilogue(struct statement *stmt);
bool ecpg_do(const int lineno, const int compat,
const int force_indicator, const char *connection_name,
const bool questionmarks, const int st, const char *query,
va_list args);
bool ecpg_check_PQresult(PGresult *results, int lineno,
PGconn *connection, enum COMPAT_MODE compat);
void ecpg_raise(int line, int code, const char *sqlstate, const char *str);
void ecpg_raise_backend(int line, PGresult *result, PGconn *conn, int compat);
char *ecpg_prepared(const char *name, struct connection *con);
bool ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection *con);
void ecpg_log(const char *format,...) pg_attribute_printf(1, 2);
bool ecpg_auto_prepare(int lineno, const char *connection_name,
const int compat, char **name, const char *query);
bool ecpg_register_prepared_stmt(struct statement *stmt);
void ecpg_init_sqlca(struct sqlca_t *sqlca);
struct sqlda_compat *ecpg_build_compat_sqlda(int line, PGresult *res, int row,
enum COMPAT_MODE compat);
void ecpg_set_compat_sqlda(int lineno, struct sqlda_compat **_sqlda,
const PGresult *res, int row,
enum COMPAT_MODE compat);
struct sqlda_struct *ecpg_build_native_sqlda(int line, PGresult *res, int row,
enum COMPAT_MODE compat);
void ecpg_set_native_sqlda(int lineno, struct sqlda_struct **_sqlda,
const PGresult *res, int row, enum COMPAT_MODE compat);
unsigned ecpg_hex_dec_len(unsigned srclen);
unsigned ecpg_hex_enc_len(unsigned srclen);
unsigned ecpg_hex_encode(const char *src, unsigned len, char *dst);
#ifdef ENABLE_NLS
extern char *ecpg_gettext(const char *msgid) pg_attribute_format_arg(1);
#else
#define ecpg_gettext(x) (x)
#endif
/* SQLSTATE values generated or processed by ecpglib (intentionally
* not exported -- users should refer to the codes directly) */
#define ECPG_SQLSTATE_NO_DATA "02000"
#define ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS "07001"
#define ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS "07002"
#define ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION "07006"
#define ECPG_SQLSTATE_INVALID_DESCRIPTOR_INDEX "07009"
#define ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION "08001"
#define ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST "08003"
#define ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN "08007"
#define ECPG_SQLSTATE_CARDINALITY_VIOLATION "21000"
#define ECPG_SQLSTATE_NULL_VALUE_NO_INDICATOR_PARAMETER "22002"
#define ECPG_SQLSTATE_ACTIVE_SQL_TRANSACTION "25001"
#define ECPG_SQLSTATE_NO_ACTIVE_SQL_TRANSACTION "25P01"
#define ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME "26000"
#define ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME "33000"
#define ECPG_SQLSTATE_INVALID_CURSOR_NAME "34000"
#define ECPG_SQLSTATE_SYNTAX_ERROR "42601"
#define ECPG_SQLSTATE_DATATYPE_MISMATCH "42804"
#define ECPG_SQLSTATE_DUPLICATE_CURSOR "42P03"
/* implementation-defined internal errors of ecpg */
#define ECPG_SQLSTATE_ECPG_INTERNAL_ERROR "YE000"
#define ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY "YE001"
#endif /* _ECPG_ECPGLIB_EXTERN_H */