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;