mirror of
https://github.com/mdbtools/mdbtools.git
synced 2025-04-05 20:31:00 +08:00
ODBC: Support SQL_C_WCHAR target type. Fixes #347
Also remove SQLFetchW and SQLGetDataW since they are not documented in any other ODBC drivers.
This commit is contained in:
parent
30d55a87ef
commit
4369721363
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user