The last time I asked about a deficiency in the contributed libraries I received several replies which indicated that patches are welcome from the user community. Taking this feedback at face value, I have attached a patch for the odbc module, which I believe brings it a step closer to full support for Unicode values.
The file is from ActivePython-2.2.1-222/src/PyWin32/win32/src in the source distribution. The patch is in universal diff output format. I have attempted to emulate the coding style of the original, overlong lines and all, though I had some difficulty in determining consistent patterns in the conventions used. There are still some warts in this module which need to be addressed, but I believe my changes are a step in the right direction and don't break anything that was working properly before. Some of the remaining deficiences include the inability to handle SQL NULL values properly, and a limit on the length of data which can be retrieved (hardcoded to 65536 bytes). I didn't want to proceed further until I got some feedback confirming that there's interest and that such patches will eventually be incorporated into the module, and until what I have gets some additional testing beyond what I've been able to do myself. All my changes are bracketed by a preprocessor test for a version of Python which understands Unicode (#ifdef Py_USING_UNICODE). Cheers. -- Bob Kline mailto:bkline@;rksystems.com http://www.rksystems.com
--- odbc.cpp-original Mon Apr 15 15:44:36 2002 +++ odbc.cpp Sun Oct 20 15:37:16 2002 @@ -70,7 +70,11 @@ long len; long sqlBytesAvailable; bool bPutData; +#ifdef Py_USING_UNICODE + char bind_area[Py_UNICODE_SIZE]; +#else char bind_area[1]; +#endif } InputBinding; typedef struct @@ -529,6 +533,14 @@ { return Py_BuildValue("s", v); } + +#ifdef Py_USING_UNICODE +static PyObject *wstringCopy(const void *v, int) +{ + return Py_BuildValue("u", v); +} +#endif + static PyObject *longCopy(const void *v, int sz) { return PyInt_FromLong(*(unsigned long *)v); @@ -881,6 +893,51 @@ return 1; } +#ifdef Py_USING_UNICODE +static int ibindWString(cursorObject *cur, int column, PyObject *item) +{ + const Py_UNICODE *val = PyUnicode_AsUnicode(item); + int len = (int)wcslen(val); + + InputBinding *ib = initInputBinding(cur, len * Py_UNICODE_SIZE); + if (!ib) + return 0; + + wcscpy((wchar_t*)ib->bind_area, val); + int sqlType = SQL_WVARCHAR; + if (len > 255) + { + ib->sqlBytesAvailable = SQL_LEN_DATA_AT_EXEC(ib->len); + sqlType = SQL_WLONGVARCHAR; + ib->bPutData = true; + } + else + { + ib->sqlBytesAvailable = ib->len; + ib->bPutData = false; + } + + RETCODE rc = SQLBindParameter( + cur->hstmt, + column, + SQL_PARAM_INPUT, + SQL_C_WCHAR, + sqlType, + len, + 0, + ib->bind_area, + 0, + &NTS); + if (unsuccessful(rc)) + { + cursorError(cur, "input-binding"); + return 0; + } + + return 1; +} +#endif + static int rewriteQuery ( char *out, @@ -936,6 +993,12 @@ { rv = ibindString(cur, iCol, item); } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(item)) + { + rv = ibindWString(cur, iCol, item); + } +#endif else if (item==Py_None) { rv = ibindNull(cur, iCol); @@ -967,6 +1030,10 @@ { case SQL_CHAR: case SQL_VARCHAR: +#ifdef Py_USING_UNICODE + case SQL_WCHAR: + case SQL_WVARCHAR: +#endif case SQL_DATE: case SQL_TIMESTAMP: case SQL_BIT: @@ -987,6 +1054,9 @@ return(max(2*collen, (int)strlen(colname))); case SQL_LONGVARBINARY: case SQL_LONGVARCHAR: +#ifdef Py_USING_UNICODE + case SQL_WLONGVARCHAR: +#endif default: return (0); } @@ -1092,6 +1162,17 @@ bindOutputVar(cur, stringCopy, SQL_C_CHAR, cur->max_width, pos, true); typeOf = DbiString; break; +#ifdef Py_USING_UNICODE + case SQL_WCHAR: + bindOutputVar(cur, wstringCopy, SQL_C_WCHAR, +vsize*Py_UNICODE_SIZE+Py_UNICODE_SIZE, pos, false); + typeOf = DbiString; + break; + case SQL_WVARCHAR: + case SQL_WLONGVARCHAR: + bindOutputVar(cur, wstringCopy, SQL_C_WCHAR, cur->max_width, +pos, true); + typeOf = DbiString; + break; +#endif default: bindOutputVar(cur, stringCopy, SQL_C_CHAR, vsize+1, pos, false); typeOf = DbiString; @@ -1214,8 +1295,11 @@ { temp = PySequence_GetItem(inputvars, 0); // Strings don't count as a list in this case. - if (PySequence_Check(temp) && !PyString_Check(temp)) - { + if (PySequence_Check(temp) && !PyString_Check(temp) +#ifdef Py_USING_UNICODE + && !PyUnicode_Check(temp) +#endif + ) { rows = inputvars; inputvars = NULL; } @@ -1431,6 +1515,12 @@ // (silly, silly) cbRead = ob->vsize - 1; } +#ifdef Py_USING_UNICODE + else if (ob->vtype == SQL_C_CHAR) + { + cbRead = ob->vsize - Py_UNICODE_SIZE; + } +#endif else { cbRead = ob->vsize;