Attempt to make the ODBC driver thread-safe

I'm not sure if this is a complete solution - some of the global state
has been moved to thread-local storage. So passing an ODBC handle across
threads may have unexpected results. But at least it's not global state.

Each ODBC handle now has its own iconv_t object.

See #23
This commit is contained in:
Evan Miller
2020-08-19 15:35:02 -04:00
parent 1ad0dd2d8b
commit 9020ca9a1e
5 changed files with 29 additions and 47 deletions

View File

@@ -121,7 +121,7 @@ typedef struct GOptionContext {
/* string functions */ /* string functions */
void *g_memdup(const void *src, size_t len); void *g_memdup(const void *src, size_t len);
int g_str_equal(const void *str1, const void *str2); int g_str_equal(const void *str1, const void *str2);
char **g_strsplit(const char *haystack, const char *needle, int something); char **g_strsplit(const char *haystack, const char *needle, int max_tokens);
void g_strfreev(char **dir); void g_strfreev(char **dir);
char *g_strconcat(const char *first, ...); char *g_strconcat(const char *first, ...);
char *g_strdup(const char *src); char *g_strdup(const char *src);

View File

@@ -22,7 +22,8 @@ int g_str_equal(const void *str1, const void *str2) {
return strcmp(str1, str2) == 0; return strcmp(str1, str2) == 0;
} }
char **g_strsplit(const char *haystack, const char *needle, int something) { // max_tokens not yet implemented
char **g_strsplit(const char *haystack, const char *needle, int max_tokens) {
char **ret = NULL; char **ret = NULL;
char *found = NULL; char *found = NULL;
size_t components = 2; // last component + terminating NULL size_t components = 2; // last component + terminating NULL

View File

@@ -43,9 +43,6 @@
#define FILENAME_MAX 512 #define FILENAME_MAX 512
#endif #endif
#define max_line 256
static char line[max_line];
static guint HashFunction (gconstpointer key); static guint HashFunction (gconstpointer key);
static void visit (gpointer key, gpointer value, gpointer user_data); static void visit (gpointer key, gpointer value, gpointer user_data);
@@ -224,17 +221,12 @@ gchar* ExtractDSN (ConnectParams* params, const gchar* connectString)
q++; q++;
while (isspace(*q)) while (isspace(*q))
q++; q++;
/*
* Copy the DSN value to a buffer
*/
s = line;
while (*q && *q != ';')
*s++ = *q++;
*s = '\0';
/* /*
* Save it as a string in the params object * Save it as a string in the params object
*/ */
params->dsnName = g_string_assign (params->dsnName, line); char **components = g_strsplit(q, ";", 2);
params->dsnName = g_string_assign(params->dsnName, components[0]);
g_strfreev(components);
return params->dsnName->str; return params->dsnName->str;
} }
@@ -261,17 +253,12 @@ gchar* ExtractDBQ (ConnectParams* params, const gchar* connectString)
q++; q++;
while (isspace(*q)) while (isspace(*q))
q++; q++;
/*
* Copy the DSN value to a buffer
*/
s = line;
while (*q && *q != ';')
*s++ = *q++;
*s = '\0';
/* /*
* Save it as a string in the params object * Save it as a string in the params object
*/ */
params->dsnName = g_string_assign (params->dsnName, line); char **components = g_strsplit(q, ";", 2);
params->dsnName = g_string_assign(params->dsnName, components[0]);
g_strfreev(components);
return params->dsnName->str; return params->dsnName->str;
} }

View File

@@ -43,6 +43,10 @@ struct _hdbc {
MdbSQL *sqlconn; MdbSQL *sqlconn;
ConnectParams* params; ConnectParams* params;
GPtrArray *statements; GPtrArray *statements;
#ifdef ENABLE_ODBC_W
iconv_t iconv_in;
iconv_t iconv_out;
#endif
}; };
struct _hstmt { struct _hstmt {
MdbSQL *sql; MdbSQL *sql;

View File

@@ -31,10 +31,6 @@
//#define TRACE(x) fprintf(stderr,"Function %s\n", x); //#define TRACE(x) fprintf(stderr,"Function %s\n", x);
#define TRACE(x) #define TRACE(x)
#ifdef ENABLE_ODBC_W
static iconv_t iconv_in,iconv_out;
#endif //ENABLE_ODBC_W
static SQLSMALLINT _odbc_get_client_type(MdbColumn *col); static SQLSMALLINT _odbc_get_client_type(MdbColumn *col);
static const char * _odbc_get_client_type_name(MdbColumn *col); static const char * _odbc_get_client_type_name(MdbColumn *col);
static int _odbc_fix_literals(struct _hstmt *stmt); static int _odbc_fix_literals(struct _hstmt *stmt);
@@ -50,8 +46,8 @@ static void unbind_columns (struct _hstmt*);
#define MIN(a,b) (a>b ? b : a) #define MIN(a,b) (a>b ? b : a)
#endif #endif
#define _MAX_ERROR_LEN 255 #define _MAX_ERROR_LEN 255
static char lastError[_MAX_ERROR_LEN+1]; static __thread char lastError[_MAX_ERROR_LEN+1];
static char sqlState[6]; static __thread char sqlState[6];
typedef struct { typedef struct {
SQLCHAR *type_name; SQLCHAR *type_name;
@@ -94,11 +90,9 @@ TypeInfo type_info[] = {
#define MAX_TYPE_INFO 11 #define MAX_TYPE_INFO 11
#ifdef ENABLE_ODBC_W #ifdef ENABLE_ODBC_W
void my_fini(void); static void _init_iconv(struct _hdbc* dbc) {
MDB_CONSTRUCTOR(my_init)
{ {
TRACE("my_init"); TRACE("_init_iconv");
int endian = 1; int endian = 1;
const char* wcharset; const char* wcharset;
if (sizeof(SQLWCHAR) == 2) if (sizeof(SQLWCHAR) == 2)
@@ -113,26 +107,16 @@ MDB_CONSTRUCTOR(my_init)
wcharset = "UCS-4BE"; wcharset = "UCS-4BE";
else else
fprintf(stderr, "Unsupported SQLWCHAR width %zd\n", sizeof(SQLWCHAR)); fprintf(stderr, "Unsupported SQLWCHAR width %zd\n", sizeof(SQLWCHAR));
//fprintf(stderr,"charset %s\n", wcharset);
//fprintf(stderr, "SQLWCHAR width %d\n", sizeof(SQLWCHAR));
/*
#if __SIZEOF_WCHAR_T__ == 4 || __WCHAR_MAX__ > 0x10000
#define WCHAR_CHARSET "UCS-4LE"
#else
#define WCHAR_CHARSET "UCS-2LE"
#endif
*/
iconv_out = iconv_open(wcharset, "UTF-8");
iconv_in = iconv_open("UTF-8", wcharset);
atexit(my_fini); dbc->iconv_out = iconv_open(wcharset, "UTF-8");
dbc->iconv_in = iconv_open("UTF-8", wcharset);
} }
void my_fini() static void _free_iconv(struct _hdbc *dbc)
{ {
TRACE("my_fini"); TRACE("_free_iconv");
if(iconv_out != (iconv_t)-1)iconv_close(iconv_out); if(dbc->iconv_out != (iconv_t)-1)iconv_close(dbc->iconv_out);
if(iconv_in != (iconv_t)-1)iconv_close(iconv_in); if(dbc->iconv_in != (iconv_t)-1)iconv_close(dbc->iconv_in);
} }
static int unicode2ascii(char *_in, size_t *_lin, char *_out, size_t *_lout){ static int unicode2ascii(char *_in, size_t *_lin, char *_out, size_t *_lout){
@@ -500,6 +484,9 @@ struct _hdbc* dbc;
dbc->params = NewConnectParams (); dbc->params = NewConnectParams ();
dbc->statements = g_ptr_array_new(); dbc->statements = g_ptr_array_new();
dbc->sqlconn = mdb_sql_init(); dbc->sqlconn = mdb_sql_init();
#ifdef ENABLE_ODBC_W
_init_iconv(dbc);
#endif
*phdbc=dbc; *phdbc=dbc;
return SQL_SUCCESS; return SQL_SUCCESS;
@@ -1115,6 +1102,9 @@ SQLRETURN SQL_API SQLFreeConnect(
FreeConnectParams(dbc->params); FreeConnectParams(dbc->params);
g_ptr_array_free(dbc->statements, TRUE); g_ptr_array_free(dbc->statements, TRUE);
mdb_sql_exit(dbc->sqlconn); mdb_sql_exit(dbc->sqlconn);
#ifdef ENABLE_ODBC_W
_free_iconv(dbc);
#endif
g_free(dbc); g_free(dbc);
return SQL_SUCCESS; return SQL_SUCCESS;