ODBC: Support SQL_C_WCHAR target type. Fixes

Also remove SQLFetchW and SQLGetDataW since they are not documented
in any other ODBC drivers.
This commit is contained in:
Evan Miller 2021-09-07 09:38:17 -04:00
parent 30d55a87ef
commit 4369721363
3 changed files with 70 additions and 93 deletions

View File

@ -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
}

View File

@ -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; i<count; i++) {
_out[i] = (SQLWCHAR)w[i];
}
free(w);
if (count < _out_count)
_out[count] = '\0';
return count;
}
SQLRETURN SQL_API SQLDriverConnect(
SQLHDBC hdbc,
SQLHWND hwnd,
@ -835,12 +856,12 @@ unbind_columns(struct _hstmt *stmt)
stmt->bind_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;
}
}

View File

@ -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; i<count; i++) {
_out[i] = (SQLWCHAR)w[i];
}
free(w);
if (count < _out_count)
_out[count] = '\0';
return count;
}
static int sqlwlen(SQLWCHAR *p){
int r=0;
for(;*p;r++)
@ -161,7 +143,7 @@ SQLRETURN SQL_API SQLDescribeColW(
size_t l=cbColNameMax*4+1;
SQLCHAR *tmp=calloc(l,1);
SQLRETURN ret = SQLDescribeCol(hstmt, icol, tmp, l, (SQLSMALLINT*)&l, pfSqlType, pcbColDef, pibScale, pfNullable);
*pcbColName = ascii2unicode(((struct _hstmt*)hstmt)->hdbc, (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;