diff --git a/src/odbc/mdbodbc.h b/src/odbc/mdbodbc.h index 6752389..48ff145 100644 --- a/src/odbc/mdbodbc.h +++ b/src/odbc/mdbodbc.h @@ -78,17 +78,9 @@ struct _sql_bind_info { struct _sql_bind_info *next; }; -typedef SQLRETURN (*_mdb_odbc_sql_data_callback) - (SQLHSTMT hstmt, - SQLUSMALLINT icol, - SQLSMALLINT fCType, - SQLPOINTER rgbValue, - SQLLEN cbValueMax, - SQLLEN *pcbValue); - -SQLRETURN _mdb_SQLFetch( - SQLHSTMT hstmt, - _mdb_odbc_sql_data_callback DataCallback); +size_t _mdb_odbc_ascii2unicode(struct _hdbc* dbc, + const char *_in, size_t _in_len, + SQLWCHAR *_out, size_t _out_count); #ifdef __cplusplus } diff --git a/src/odbc/odbc.c b/src/odbc/odbc.c index fe55a60..439fb94 100644 --- a/src/odbc/odbc.c +++ b/src/odbc/odbc.c @@ -116,6 +116,27 @@ static SQLRETURN do_connect ( return SQL_ERROR; } +size_t _mdb_odbc_ascii2unicode(struct _hdbc* dbc, const char *_in, size_t _in_len, SQLWCHAR *_out, size_t _out_count){ + wchar_t *w = malloc(_out_count * sizeof(wchar_t)); + size_t count = 0, i; +#if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) || defined(WINDOWS) + count = _mbstowcs_l(w, _in, _out_count, dbc->locale); +#elif defined(HAVE_MBSTOWCS_L) + count = mbstowcs_l(w, _in, _out_count, dbc->locale); +#else + locale_t oldlocale = uselocale(dbc->locale); + count = mbstowcs(w, _in, _out_count); + uselocale(oldlocale); +#endif + for (i=0; ibind_head = NULL; } -SQLRETURN _mdb_SQLFetch( - SQLHSTMT hstmt, - _mdb_odbc_sql_data_callback DataCallback) +SQLRETURN SQLFetch( + SQLHSTMT hstmt) { struct _hstmt *stmt = (struct _hstmt *) hstmt; struct _sql_bind_info *cur = stmt->bind_head; + TRACE("SQLFetch"); if ( stmt->sql->limit >= 0 && stmt->rows_affected == stmt->sql->limit ) { return SQL_NO_DATA_FOUND; @@ -850,7 +871,7 @@ SQLRETURN _mdb_SQLFetch( while (cur && (final_retval == SQL_SUCCESS || final_retval == SQL_SUCCESS_WITH_INFO)) { /* log error ? */ SQLLEN lenbind = 0; - SQLRETURN this_retval = DataCallback(hstmt, cur->column_number, cur->column_bindtype, + SQLRETURN this_retval = SQLGetData(hstmt, cur->column_number, cur->column_bindtype, cur->varaddr, cur->column_bindlen, &lenbind); if (cur->column_lenbind) *(cur->column_lenbind) = lenbind; @@ -866,12 +887,6 @@ SQLRETURN _mdb_SQLFetch( } } -SQLRETURN SQL_API SQLFetch( - SQLHSTMT hstmt) { - TRACE("SQLFetch"); - return _mdb_SQLFetch(hstmt, SQLGetData); -} - SQLRETURN SQL_API SQLFreeConnect( SQLHDBC hdbc) { @@ -1225,13 +1240,15 @@ SQLRETURN SQL_API SQLGetData( if (col->col_type == MDB_BOOL) { // bool cannot be null if (fCType == SQL_C_CHAR) { - if ( col->cur_value_len ) - ((char *)rgbValue)[0] = '0'; - else - ((char *)rgbValue)[0] = '1'; - ((char *)rgbValue)[1] = '\0'; + ((SQLCHAR *)rgbValue)[0] = col->cur_value_len ? '0' : '1'; + ((SQLCHAR *)rgbValue)[1] = '\0'; if (pcbValue) *pcbValue = sizeof(SQLCHAR); + } else if (fCType == SQL_C_WCHAR) { + ((SQLWCHAR *)rgbValue)[0] = col->cur_value_len ? '0' : '1'; + ((SQLWCHAR *)rgbValue)[1] = '\0'; + if (pcbValue) + *pcbValue = sizeof(SQLWCHAR); } else { *(BOOL*)rgbValue = col->cur_value_len ? 0 : 1; if (pcbValue) @@ -1265,7 +1282,7 @@ SQLRETURN SQL_API SQLGetData( found_bound_type: if (fCType==SQL_C_DEFAULT) fCType = _odbc_get_client_type(col); - if (fCType == SQL_C_CHAR) + if (fCType == SQL_C_CHAR || fCType == SQL_C_WCHAR) goto to_c_char; switch(col->col_type) { case MDB_BYTE: @@ -1425,7 +1442,7 @@ SQLRETURN SQL_API SQLGetData( free(stmt->ole_str); stmt->ole_str = NULL; break; - default: /* FIXME here we assume fCType == SQL_C_CHAR */ + default: /* FIXME here we assume fCType == SQL_C_CHAR || fCType == SQL_C_WCHAR */ to_c_char: { if (cbValueMax < 0) { @@ -1433,6 +1450,7 @@ SQLRETURN SQL_API SQLGetData( return SQL_ERROR; } char *str = NULL; + SQLWCHAR *wstr = NULL; if (col->col_type == MDB_NUMERIC) { str = mdb_numeric_to_string(mdb, col->cur_value_start, col->col_scale, col->col_prec); @@ -1441,37 +1459,47 @@ SQLRETURN SQL_API SQLGetData( col->cur_value_start, col->col_type, col->cur_value_len); } size_t len = strlen(str); + size_t charsize = 1; + if (fCType == SQL_C_WCHAR) { + wstr = calloc(len+1, charsize = sizeof(SQLWCHAR)); + len = _mdb_odbc_ascii2unicode(((struct _hstmt *)hstmt)->hdbc, str, len, wstr, len+1); + } if (stmt->pos >= len) { - free(str); - str = NULL; + free(wstr); wstr = NULL; + free(str); str = NULL; return SQL_NO_DATA; } if (pcbValue) { - *pcbValue = len - stmt->pos; + *pcbValue = (len - stmt->pos) * charsize; } if (cbValueMax == 0) { - free(str); - str = NULL; + free(wstr); wstr = NULL; + free(str); str = NULL; return SQL_SUCCESS_WITH_INFO; } - const int totalSizeRemaining = len - stmt->pos; - const int partsRemain = cbValueMax - 1 < totalSizeRemaining; - const int sizeToReadThisPart = partsRemain ? cbValueMax - 1 : totalSizeRemaining; - memcpy(rgbValue, str + stmt->pos, sizeToReadThisPart); + const int totalCharactersRemaining = len - stmt->pos; + const int partsRemain = cbValueMax/charsize - 1 < totalCharactersRemaining; + const int charactersToReadThisPart = partsRemain ? cbValueMax/charsize - 1 : totalCharactersRemaining; - ((char *)rgbValue)[sizeToReadThisPart] = '\0'; + if (wstr) { + memcpy(rgbValue, wstr + stmt->pos, charactersToReadThisPart * sizeof(SQLWCHAR)); + ((SQLWCHAR *)rgbValue)[charactersToReadThisPart] = '\0'; + } else { + memcpy(rgbValue, str + stmt->pos, charactersToReadThisPart); + ((SQLCHAR *)rgbValue)[charactersToReadThisPart] = '\0'; + } + + free(wstr); wstr = NULL; + free(str); str = NULL; if (partsRemain) { - stmt->pos += cbValueMax - 1; - free(str); str = NULL; + stmt->pos += charactersToReadThisPart; strcpy(stmt->sqlState, "01004"); // truncated return SQL_SUCCESS_WITH_INFO; } stmt->pos = len; - free(str); - str = NULL; break; } } diff --git a/src/odbc/odbcw.c b/src/odbc/odbcw.c index 2fa9911..d7fd226 100644 --- a/src/odbc/odbcw.c +++ b/src/odbc/odbcw.c @@ -16,6 +16,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +/* For a full list of functions that could be implemented, see: + * https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/unicode-function-arguments?view=sql-server-ver15 */ + #define SQL_NOUNICODEMAP #define UNICODE @@ -61,27 +64,6 @@ static size_t unicode2ascii(struct _hdbc* dbc, const SQLWCHAR *_in, size_t _in_c return count; } -static size_t ascii2unicode(struct _hdbc* dbc, const char *_in, size_t _in_len, SQLWCHAR *_out, size_t _out_count){ - wchar_t *w = malloc(_out_count * sizeof(wchar_t)); - size_t count = 0, i; -#if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) || defined(WINDOWS) - count = _mbstowcs_l(w, _in, _out_count, dbc->locale); -#elif defined(HAVE_MBSTOWCS_L) - count = mbstowcs_l(w, _in, _out_count, dbc->locale); -#else - locale_t oldlocale = uselocale(dbc->locale); - count = mbstowcs(w, _in, _out_count); - uselocale(oldlocale); -#endif - for (i=0; ihdbc, (char*)tmp, l, szColName, cbColNameMax); + *pcbColName = _mdb_odbc_ascii2unicode(((struct _hstmt*)hstmt)->hdbc, (char*)tmp, l, szColName, cbColNameMax); free(tmp); return ret; } @@ -183,7 +165,7 @@ SQLRETURN SQL_API SQLColAttributesW( size_t l=cbDescMax*4+1; SQLCHAR *tmp=calloc(l,1); SQLRETURN ret=SQLColAttributes(hstmt,icol,fDescType,tmp,l,(SQLSMALLINT*)&l,pfDesc); - *pcbDesc = ascii2unicode(((struct _hstmt *)hstmt)->hdbc, (char*)tmp, l, (SQLWCHAR*)rgbDesc, cbDescMax); + *pcbDesc = _mdb_odbc_ascii2unicode(((struct _hstmt *)hstmt)->hdbc, (char*)tmp, l, (SQLWCHAR*)rgbDesc, cbDescMax); free(tmp); return ret; } @@ -210,8 +192,8 @@ SQLRETURN SQL_API SQLErrorW( if (result == SQL_SUCCESS) { struct _hdbc *dbc = hstmt ? ((struct _hstmt *)hstmt)->hdbc : hdbc; size_t pcb; - ascii2unicode(dbc, (char*)szSqlState8, sizeof(szSqlState8), szSqlState, sizeof(szSqlState8)); - pcb = ascii2unicode(dbc, (char*)szErrorMsg8, pcbErrorMsg8, szErrorMsg, cbErrorMsgMax); + _mdb_odbc_ascii2unicode(dbc, (char*)szSqlState8, sizeof(szSqlState8), szSqlState, sizeof(szSqlState8)); + pcb = _mdb_odbc_ascii2unicode(dbc, (char*)szErrorMsg8, pcbErrorMsg8, szErrorMsg, cbErrorMsgMax); if (pcbErrorMsg) *pcbErrorMsg = pcb; } return result; @@ -259,31 +241,6 @@ SQLRETURN SQL_API SQLColumnsW( } } -SQLRETURN SQL_API SQLGetDataW( - SQLHSTMT hstmt, - SQLUSMALLINT icol, - SQLSMALLINT fCType, - SQLPOINTER rgbValue, - SQLLEN cbValueMax, - SQLLEN *pcbValue) -{ - if (fCType != SQL_C_CHAR) - return SQLGetData(hstmt, icol, fCType, rgbValue, cbValueMax, pcbValue); - - size_t l=cbValueMax*4+1; - SQLCHAR *tmp=calloc(l,1); - SQLRETURN ret = SQLGetData(hstmt, icol, fCType, tmp, l, (SQLLEN*)&l); - *pcbValue = ascii2unicode(((struct _hstmt *)hstmt)->hdbc, (char*)tmp, l, (SQLWCHAR*)rgbValue, cbValueMax); - free(tmp); - return ret; -} - -SQLRETURN SQL_API SQLFetchW( - SQLHSTMT hstmt) { - TRACE("SQLFetchW"); - return _mdb_SQLFetch(hstmt, SQLGetDataW); -} - SQLRETURN SQL_API SQLGetInfoW( SQLHDBC hdbc, SQLUSMALLINT fInfoType, @@ -299,7 +256,7 @@ SQLRETURN SQL_API SQLGetInfoW( size_t l=cbInfoValueMax*4+1; SQLCHAR *tmp=calloc(l,1); SQLRETURN ret = SQLGetInfo(hdbc, fInfoType, tmp, l, (SQLSMALLINT*)&l); - size_t pcb = ascii2unicode((struct _hdbc *)hdbc, (char*)tmp, l, (SQLWCHAR*)rgbInfoValue, cbInfoValueMax); + size_t pcb = _mdb_odbc_ascii2unicode((struct _hdbc *)hdbc, (char*)tmp, l, (SQLWCHAR*)rgbInfoValue, cbInfoValueMax); if(pcbInfoValue)*pcbInfoValue=pcb; free(tmp); return ret;