Skip to content
This repository
Browse code

Reworked Unicode; changed version format; more Py_ssize_t

Reworked Unicode support, properly differentiating between SQLWCHAR on the different platforms.
This should fix a lot of the OS/X problems and perhaps Linux UCS4 build problems.

Version format now includes the branch name if not 'master' or v<version>, allowing custom
builds to be identified, such as v2unicode-2.1.8-beta03.

Also tested with 64-bit Windows builds, so more Py_ssize_t warnings were found and corrected.

Created TRACE macro to replace the #ifdefs sprinkled through the code.
  • Loading branch information...
commit 691b6ba73a641823fe6fb1f9f83c49596834d0f7 1 parent fc71271
Michael Kleehammer authored
18 setup.py
@@ -42,12 +42,8 @@ def main():
42 42 extra_link_args = None
43 43
44 44 if os.name == 'nt':
45   - # if not '--compiler=mingw32' in sys.argv:
46   - # # Windows native
47   - # files.append(join('src', 'pyodbc.rc'))
48   - # extra_compile_args = ['/W4']
49 45 libraries.append('odbc32')
50   -
  46 + # extra_compile_args = ['/W4']
51 47 # extra_compile_args = ['/W4', '/Zi', '/Od']
52 48 # extra_link_args = ['/DEBUG']
53 49
@@ -69,7 +65,7 @@ def main():
69 65 # What is the proper way to detect iODBC, MyODBC, unixODBC, etc.?
70 66 libraries.append('odbc')
71 67
72   - macros = [('PYODBC_%s' % name, value) for name,value in zip(['MAJOR', 'MINOR', 'MICRO', 'BUILD'], version)]
  68 + macros = [ ('PYODBC_VERSION', version_str) ]
73 69
74 70 # This isn't the best or right way to do this, but I don't see how someone is supposed to sanely subclass the build
75 71 # command.
@@ -81,7 +77,7 @@ def main():
81 77
82 78 try:
83 79 sys.argv.remove('--trace')
84   - macros.append(('TRACE_ALL', 1))
  80 + macros.append(('PYODBC_TRACE', 1))
85 81 except ValueError:
86 82 pass
87 83
@@ -195,7 +191,13 @@ def _get_version_git():
195 191 if numbers[-1] != OFFICIAL_BUILD:
196 192 # This is a beta of the next micro release, so increment the micro number to reflect this.
197 193 numbers[-2] += 1
198   - name = '%s.%s.%s-beta%s' % tuple(numbers)
  194 + name = '%s.%s.%s-beta%02d' % tuple(numbers)
  195 +
  196 + n, result = getoutput('git branch')
  197 + branch = re.search(r'\* (\w+)', result).group(1)
  198 + if branch != 'master' and not re.match('^v\d+$', branch):
  199 + name = branch + '-' + name
  200 +
199 201 return name, numbers
200 202
201 203
9 src/cnxninfo.cpp
@@ -106,6 +106,7 @@ static PyObject* CnxnInfo_New(Connection* cnxn)
106 106
107 107 // These defaults are tiny, but are necessary for Access.
108 108 p->varchar_maxlength = 255;
  109 + p->wvarchar_maxlength = 255;
109 110 p->binary_maxlength = 510;
110 111
111 112 HSTMT hstmt = 0;
@@ -128,6 +129,14 @@ static PyObject* CnxnInfo_New(Connection* cnxn)
128 129 SQLFreeStmt(hstmt, SQL_CLOSE);
129 130 }
130 131
  132 + if (SQL_SUCCEEDED(SQLGetTypeInfo(hstmt, SQL_WVARCHAR)) && SQL_SUCCEEDED(SQLFetch(hstmt)))
  133 + {
  134 + if (SQL_SUCCEEDED(SQLGetData(hstmt, 3, SQL_INTEGER, &columnsize, sizeof(columnsize), 0)))
  135 + p->wvarchar_maxlength = (int)columnsize;
  136 +
  137 + SQLFreeStmt(hstmt, SQL_CLOSE);
  138 + }
  139 +
131 140 if (SQL_SUCCEEDED(SQLGetTypeInfo(hstmt, SQL_BINARY)) && SQL_SUCCEEDED(SQLFetch(hstmt)))
132 141 {
133 142 if (SQL_SUCCEEDED(SQLGetData(hstmt, 3, SQL_INTEGER, &columnsize, sizeof(columnsize), 0)))
2  src/cnxninfo.h
@@ -27,7 +27,9 @@ struct CnxnInfo
27 27 bool supports_describeparam;
28 28 int datetime_precision;
29 29
  30 + // These are from SQLGetTypeInfo.column_size, so the char ones are in characters, not bytes.
30 31 int varchar_maxlength;
  32 + int wvarchar_maxlength;
31 33 int binary_maxlength;
32 34 };
33 35
24 src/connection.cpp
@@ -75,14 +75,14 @@ static bool Connect(PyObject* pConnectString, HDBC hdbc, bool fAnsi)
75 75 if (PyUnicode_Check(pConnectString))
76 76 {
77 77 Py_UNICODE* p = PyUnicode_AS_UNICODE(pConnectString);
78   - for (int i = 0, c = PyUnicode_GET_SIZE(pConnectString); i <= c; i++)
79   - szConnectW[i] = (wchar_t)p[i];
  78 + for (Py_ssize_t i = 0, c = PyUnicode_GET_SIZE(pConnectString); i <= c; i++)
  79 + szConnectW[i] = (SQLWCHAR)p[i];
80 80 }
81 81 else
82 82 {
83 83 const char* p = PyString_AS_STRING(pConnectString);
84   - for (int i = 0, c = PyString_GET_SIZE(pConnectString); i <= c; i++)
85   - szConnectW[i] = (wchar_t)p[i];
  84 + for (Py_ssize_t i = 0, c = PyString_GET_SIZE(pConnectString); i <= c; i++)
  85 + szConnectW[i] = (SQLWCHAR)p[i];
86 86 }
87 87
88 88 Py_BEGIN_ALLOW_THREADS
@@ -107,7 +107,7 @@ static bool Connect(PyObject* pConnectString, HDBC hdbc, bool fAnsi)
107 107 if (PyUnicode_Check(pConnectString))
108 108 {
109 109 Py_UNICODE* p = PyUnicode_AS_UNICODE(pConnectString);
110   - for (int i = 0, c = PyUnicode_GET_SIZE(pConnectString); i <= c; i++)
  110 + for (Py_ssize_t i = 0, c = PyUnicode_GET_SIZE(pConnectString); i <= c; i++)
111 111 {
112 112 if (p[i] > 0xFF)
113 113 {
@@ -213,9 +213,7 @@ PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, bool fAnsi,
213 213 }
214 214 }
215 215
216   -#ifdef TRACE_ALL
217   - printf("cnxn.new cnxn=%p hdbc=%d\n", cnxn, cnxn->hdbc);
218   -#endif
  216 + TRACE("cnxn.new cnxn=%p hdbc=%d\n", cnxn, cnxn->hdbc);
219 217
220 218 //
221 219 // Gather connection-level information we'll need later.
@@ -235,6 +233,7 @@ PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, bool fAnsi,
235 233 cnxn->supports_describeparam = p->supports_describeparam;
236 234 cnxn->datetime_precision = p->datetime_precision;
237 235 cnxn->varchar_maxlength = p->varchar_maxlength;
  236 + cnxn->wvarchar_maxlength = p->wvarchar_maxlength;
238 237 cnxn->binary_maxlength = p->binary_maxlength;
239 238
240 239 return reinterpret_cast<PyObject*>(cnxn);
@@ -251,9 +250,8 @@ Connection_clear(Connection* cnxn)
251 250 {
252 251 // REVIEW: Release threads? (But make sure you zero out hdbc *first*!
253 252
254   -#ifdef TRACE_ALL
255   - printf("cnxn.clear cnxn=%p hdbc=%d\n", cnxn, cnxn->hdbc);
256   -#endif
  253 + TRACE("cnxn.clear cnxn=%p hdbc=%d\n", cnxn, cnxn->hdbc);
  254 +
257 255 Py_BEGIN_ALLOW_THREADS
258 256 if (cnxn->nAutoCommit == SQL_AUTOCOMMIT_OFF)
259 257 SQLEndTran(SQL_HANDLE_DBC, cnxn->hdbc, SQL_ROLLBACK);
@@ -571,9 +569,7 @@ Connection_endtrans(PyObject* self, PyObject* args, SQLSMALLINT type)
571 569 if (!cnxn)
572 570 return 0;
573 571
574   -#ifdef TRACE_ALL
575   - printf("%s: cnxn=%p hdbc=%d\n", (type == SQL_COMMIT) ? "commit" : "rollback", cnxn, cnxn->hdbc);
576   -#endif
  572 + TRACE("%s: cnxn=%p hdbc=%d\n", (type == SQL_COMMIT) ? "commit" : "rollback", cnxn, cnxn->hdbc);
577 573
578 574 SQLRETURN ret;
579 575 Py_BEGIN_ALLOW_THREADS
1  src/connection.h
@@ -49,6 +49,7 @@ struct Connection
49 49 // These are copied from cnxn info for performance and convenience.
50 50
51 51 int varchar_maxlength;
  52 + int wvarchar_maxlength;
52 53 int binary_maxlength;
53 54 };
54 55
59 src/cursor.cpp
@@ -23,6 +23,7 @@
23 23 #include "errors.h"
24 24 #include "getdata.h"
25 25 #include "dbspecific.h"
  26 +#include "sqlwchar.h"
26 27
27 28 enum
28 29 {
@@ -267,9 +268,8 @@ create_name_map(Cursor* cur, SQLSMALLINT field_count, bool lower)
267 268 goto done;
268 269 }
269 270
270   -#ifdef TRACE_ALL
271   - printf("Col %d: type=%d colsize=%d\n", (i+1), (int)nDataType, (int)nColSize);
272   -#endif
  271 + TRACE("Col %d: type=%d colsize=%d\n", (i+1), (int)nDataType, (int)nColSize);
  272 +
273 273 if (lower)
274 274 _strlwr((char*)name);
275 275
@@ -695,8 +695,8 @@ execute(Cursor* cur, PyObject* pSql, PyObject* params, bool skip_first)
695 695
696 696 while (ret == SQL_NEED_DATA)
697 697 {
698   - // We have bound a `buffer` object using SQL_DATA_AT_EXEC, so ODBC is asking us for the data now. We gave the
699   - // buffer pointer to ODBC in SQLBindParameter -- SQLParamData below gives the pointer back to us.
  698 + // We have bound a PyObject* using SQL_LEN_DATA_AT_EXEC, so ODBC is asking us for the data now. We gave the
  699 + // PyObject pointer to ODBC in SQLBindParameter -- SQLParamData below gives the pointer back to us.
700 700
701 701 szLastFunction = "SQLParamData";
702 702 PyObject* pParam;
@@ -704,6 +704,8 @@ execute(Cursor* cur, PyObject* pSql, PyObject* params, bool skip_first)
704 704 ret = SQLParamData(cur->hstmt, (SQLPOINTER*)&pParam);
705 705 Py_END_ALLOW_THREADS
706 706
  707 + TRACE("SQLParamData() --> %d\n", ret);
  708 +
707 709 if (ret == SQL_NEED_DATA)
708 710 {
709 711 szLastFunction = "SQLPutData";
@@ -718,22 +720,27 @@ execute(Cursor* cur, PyObject* pSql, PyObject* params, bool skip_first)
718 720 while (it.Next(pb, cb))
719 721 {
720 722 Py_BEGIN_ALLOW_THREADS
721   - SQLPutData(cur->hstmt, pb, cb);
  723 + ret = SQLPutData(cur->hstmt, pb, cb);
722 724 Py_END_ALLOW_THREADS
  725 + if (!SQL_SUCCEEDED(ret))
  726 + return RaiseErrorFromHandle("SQLPutData", cur->cnxn->hdbc, cur->hstmt);
723 727 }
724 728 }
725 729 else if (PyUnicode_Check(pParam))
726 730 {
727   - // REVIEW: This will fail if PyUnicode != wchar_t?
728   - Py_UNICODE* p = PyUnicode_AS_UNICODE(pParam);
729   - SQLLEN offset = 0;
730   - SQLLEN cb = (SQLLEN)PyUnicode_GET_SIZE(pParam);
731   - while (offset < cb)
  731 + SQLWChar wchar(pParam); // Will convert to SQLWCHAR if necessary.
  732 +
  733 + Py_ssize_t offset = 0; // in characters
  734 + Py_ssize_t length = wchar.size(); // in characters
  735 +
  736 + while (offset < length)
732 737 {
733   - SQLLEN remaining = min(cur->cnxn->varchar_maxlength, cb - offset);
  738 + SQLLEN remaining = min(cur->cnxn->varchar_maxlength, length - offset);
734 739 Py_BEGIN_ALLOW_THREADS
735   - SQLPutData(cur->hstmt, &p[offset], remaining * 2);
  740 + ret = SQLPutData(cur->hstmt, (SQLPOINTER)wchar[offset], remaining * sizeof(SQLWCHAR));
736 741 Py_END_ALLOW_THREADS
  742 + if (!SQL_SUCCEEDED(ret))
  743 + return RaiseErrorFromHandle("SQLPutData", cur->cnxn->hdbc, cur->hstmt);
737 744 offset += remaining;
738 745 }
739 746 }
@@ -745,12 +752,17 @@ execute(Cursor* cur, PyObject* pSql, PyObject* params, bool skip_first)
745 752 while (offset < cb)
746 753 {
747 754 SQLLEN remaining = min(cur->cnxn->varchar_maxlength, cb - offset);
  755 + TRACE("SQLPutData [%d] (%d) %s\n", offset, remaining, &p[offset]);
748 756 Py_BEGIN_ALLOW_THREADS
749   - SQLPutData(cur->hstmt, (SQLPOINTER)&p[offset], remaining);
  757 + ret = SQLPutData(cur->hstmt, (SQLPOINTER)&p[offset], remaining);
750 758 Py_END_ALLOW_THREADS
  759 + if (!SQL_SUCCEEDED(ret))
  760 + return RaiseErrorFromHandle("SQLPutData", cur->cnxn->hdbc, cur->hstmt);
751 761 offset += remaining;
752 762 }
753 763 }
  764 +
  765 + ret = SQL_NEED_DATA;
754 766 }
755 767 }
756 768
@@ -771,12 +783,12 @@ execute(Cursor* cur, PyObject* pSql, PyObject* params, bool skip_first)
771 783 Py_BEGIN_ALLOW_THREADS
772 784 ret = SQLRowCount(cur->hstmt, &cRows);
773 785 Py_END_ALLOW_THREADS
  786 + if (!SQL_SUCCEEDED(ret))
  787 + return RaiseErrorFromHandle("SQLRowCount", cur->cnxn->hdbc, cur->hstmt);
774 788
775 789 cur->rowcount = (int)cRows;
776 790
777   -#ifdef TRACE_ALL
778   - printf("SQLRowCount: %d\n", cRows);
779   -#endif
  791 + TRACE("SQLRowCount: %d\n", cRows);
780 792
781 793 SQLSMALLINT cCols = 0;
782 794 Py_BEGIN_ALLOW_THREADS
@@ -790,9 +802,7 @@ execute(Cursor* cur, PyObject* pSql, PyObject* params, bool skip_first)
790 802 return RaiseErrorFromHandle("SQLNumResultCols", cur->cnxn->hdbc, cur->hstmt);
791 803 }
792 804
793   -#ifdef TRACE_ALL
794   - printf("SQLNumResultCols: %d\n", cCols);
795   -#endif
  805 + TRACE("SQLNumResultCols: %d\n", cCols);
796 806
797 807 if (cur->cnxn->hdbc == SQL_NULL_HANDLE)
798 808 {
@@ -946,7 +956,7 @@ Cursor_fetch(Cursor* cur)
946 956 // exception is set and zero is returned. (To differentiate between the last two, use PyErr_Occurred.)
947 957
948 958 SQLRETURN ret = 0;
949   - int field_count, i;
  959 + Py_ssize_t field_count, i;
950 960 PyObject** apValues;
951 961
952 962 Py_BEGIN_ALLOW_THREADS
@@ -1624,9 +1634,6 @@ Cursor_nextset(PyObject* self, PyObject* args)
1624 1634
1625 1635 if (ret == SQL_NO_DATA)
1626 1636 {
1627   - //#ifdef TRACE_ALL
1628   - //printf("Cursor_nextset: SQL_NO_DATA\r\n");
1629   - //#endif
1630 1637 free_results(cur, FREE_STATEMENT);
1631 1638 Py_RETURN_FALSE;
1632 1639 }
@@ -2124,9 +2131,7 @@ Cursor_New(Connection* cnxn)
2124 2131 }
2125 2132 }
2126 2133
2127   -#ifdef TRACE_ALL
2128   - printf("cursor.new cnxn=%p hdbc=%d cursor=%p hstmt=%d\n", (Connection*)cur->cnxn, ((Connection*)cur->cnxn)->hdbc, cur, cur->hstmt);
2129   -#endif
  2134 + TRACE("cursor.new cnxn=%p hdbc=%d cursor=%p hstmt=%d\n", (Connection*)cur->cnxn, ((Connection*)cur->cnxn)->hdbc, cur, cur->hstmt);
2130 2135 }
2131 2136
2132 2137 return cur;
5 src/errors.cpp
@@ -171,9 +171,8 @@ PyObject* RaiseErrorFromHandle(const char* szFunction, HDBC hdbc, HSTMT hstmt)
171 171
172 172 PyObject* GetErrorFromHandle(const char* szFunction, HDBC hdbc, HSTMT hstmt)
173 173 {
174   -#ifdef TRACE_ALL
175   - printf("In RaiseError!\n");
176   -#endif
  174 + TRACE("In RaiseError(%s)!\n", szFunction);
  175 +
177 176 // Creates and returns an exception from ODBC error information.
178 177 //
179 178 // ODBC can generate a chain of errors which we concatenate into one error message. We use the SQLSTATE from the
58 src/getdata.cpp
@@ -8,6 +8,7 @@
8 8 #include "connection.h"
9 9 #include "errors.h"
10 10 #include "dbspecific.h"
  11 +#include "sqlwchar.h"
11 12
12 13 void GetData_init()
13 14 {
@@ -24,22 +25,24 @@ class DataBuffer
24 25 //
25 26 // 1) Binary, which is a simple array of 8-bit bytes.
26 27 // 2) ANSI text, which is an array of chars with a NULL terminator.
27   - // 3) Unicode text, which is an array of wchar_ts with a NULL terminator.
  28 + // 3) Unicode text, which is an array of SQLWCHARs with a NULL terminator.
28 29 //
29   - // When dealing with Unicode, there are two widths we have to be aware of: (1) wchar_t and (2) Py_UNICODE. If
  30 + // When dealing with Unicode, there are two widths we have to be aware of: (1) SQLWCHAR and (2) Py_UNICODE. If
30 31 // these are the same we can use a PyUnicode object so we don't have to allocate our own buffer and then the
31 32 // Unicode object. If they are not the same (e.g. OS/X where wchar_t-->4 Py_UNICODE-->2) then we need to maintain
32   - // our own buffer and pass it to the PyUnicode object later.
  33 + // our own buffer and pass it to the PyUnicode object later. Many Linux distros are now using UCS4, so Py_UNICODE
  34 + // will be larger than SQLWCHAR.
33 35 //
34 36 // To reduce heap fragmentation, we perform the initial read into an array on the stack since we don't know the
35   - // length of the data. If the data doesn't fit, this class then allocates new memory.
  37 + // length of the data. If the data doesn't fit, this class then allocates new memory. If the first read gives us
  38 + // the length, then we create a Python object of the right size and read into its memory.
36 39
37 40 private:
38 41 SQLSMALLINT dataType;
39 42
40 43 char* buffer;
41 44 Py_ssize_t bufferSize; // How big is the buffer.
42   - int bytesUsed; // How many elements have been read into the buffer?
  45 + int bytesUsed; // How many elements have been read into the buffer?
43 46
44 47 PyObject* bufferOwner; // If possible, we bind into a PyString or PyUnicode object.
45 48 int element_size; // How wide is each character: ASCII/ANSI -> 1, Unicode -> 2 or 4, binary -> 1
@@ -47,7 +50,7 @@ class DataBuffer
47 50 bool usingStack; // Is buffer pointing to the initial stack buffer?
48 51
49 52 public:
50   - int null_size; // How much room to add for null terminator: binary -> 0, other -> same as a element_size
  53 + int null_size; // How much room, in bytes, to add for null terminator: binary -> 0, other -> same as a element_size
51 54
52 55 DataBuffer(SQLSMALLINT dataType, char* stackBuffer, SQLLEN stackBufferSize)
53 56 {
@@ -56,7 +59,7 @@ class DataBuffer
56 59
57 60 this->dataType = dataType;
58 61
59   - element_size = (dataType == SQL_C_WCHAR) ? sizeof(wchar_t) : sizeof(char);
  62 + element_size = (dataType == SQL_C_WCHAR) ? sizeof(SQLWCHAR) : sizeof(char);
60 63 null_size = (dataType == SQL_C_BINARY) ? 0 : element_size;
61 64
62 65 buffer = stackBuffer;
@@ -98,11 +101,14 @@ class DataBuffer
98 101 void AddUsed(SQLLEN cbRead)
99 102 {
100 103 I(cbRead <= GetRemaining());
101   - bytesUsed += cbRead;
  104 + bytesUsed += (int)cbRead;
102 105 }
103 106
104 107 bool AllocateMore(SQLLEN cbAdd)
105 108 {
  109 + // cbAdd
  110 + // The number of bytes (cb --> count of bytes) to add.
  111 +
106 112 if (cbAdd == 0)
107 113 return true;
108 114
@@ -120,7 +126,7 @@ class DataBuffer
120 126 bufferOwner = PyString_FromStringAndSize(0, newSize);
121 127 buffer = bufferOwner ? PyString_AS_STRING(bufferOwner) : 0;
122 128 }
123   - else if (sizeof(wchar_t) == Py_UNICODE_SIZE)
  129 + else if (sizeof(SQLWCHAR) == Py_UNICODE_SIZE)
124 130 {
125 131 // Allocate directly into a Unicode object.
126 132 bufferOwner = PyUnicode_FromUnicode(0, newSize / element_size);
@@ -128,15 +134,15 @@ class DataBuffer
128 134 }
129 135 else
130 136 {
131   - // We're Unicode, but wchar_t and Py_UNICODE don't match, so maintain our own wchar_t buffer.
  137 + // We're Unicode, but SQLWCHAR and Py_UNICODE don't match, so maintain our own SQLWCHAR buffer.
132 138 buffer = (char*)malloc(newSize);
133 139 }
134 140
135   - usingStack = false;
136   -
137 141 if (buffer == 0)
138 142 return false;
139 143
  144 + usingStack = false;
  145 +
140 146 memcpy(buffer, stackBuffer, bufferSize);
141 147 bufferSize = newSize;
142 148 return true;
@@ -179,10 +185,10 @@ class DataBuffer
179 185 if (dataType == SQL_C_CHAR || dataType == SQL_C_BINARY)
180 186 return PyString_FromStringAndSize(buffer, bytesUsed);
181 187
182   - if (sizeof(wchar_t) == Py_UNICODE_SIZE)
  188 + if (sizeof(SQLWCHAR) == Py_UNICODE_SIZE)
183 189 return PyUnicode_FromUnicode((const Py_UNICODE*)buffer, bytesUsed / element_size);
184 190
185   - return PyUnicode_FromWideChar((const wchar_t*)buffer, bytesUsed / element_size);
  191 + return PyUnicode_FromSQLWCHAR((const SQLWCHAR*)buffer, bytesUsed / element_size);
186 192 }
187 193
188 194 if (PyString_CheckExact(bufferOwner))
@@ -205,8 +211,8 @@ class DataBuffer
205 211 return tmp;
206 212 }
207 213
208   - // We have allocated our own wchar_t buffer and must now copy it to a Unicode object.
209   - PyObject* result = PyUnicode_FromWideChar((const wchar_t*)buffer, bytesUsed / element_size);
  214 + // We have allocated our own SQLWCHAR buffer and must now copy it to a Unicode object.
  215 + PyObject* result = PyUnicode_FromSQLWCHAR((const SQLWCHAR*)buffer, bytesUsed / element_size);
210 216 if (result == 0)
211 217 return false;
212 218 free(buffer);
@@ -216,14 +222,14 @@ class DataBuffer
216 222 };
217 223
218 224 static PyObject*
219   -GetDataString(Cursor* cur, int iCol)
  225 +GetDataString(Cursor* cur, Py_ssize_t iCol)
220 226 {
221 227 // Returns a String or Unicode object for character and binary data.
222 228
223 229 // NULL terminator notes:
224 230 //
225 231 // * pinfo->column_size, from SQLDescribeCol, does not include a NULL terminator. For example, column_size for a
226   - // char(10) column would be 10. (Also, when dealing with wchar_t, it is the number of *characters*, not bytes.)
  232 + // char(10) column would be 10. (Also, when dealing with SQLWCHAR, it is the number of *characters*, not bytes.)
227 233 //
228 234 // * When passing a length to PyString_FromStringAndSize and similar Unicode functions, do not add the NULL
229 235 // terminator -- it will be added automatically. See objects/stringobject.c
@@ -361,7 +367,7 @@ GetDataBuffer(Cursor* cur, Py_ssize_t iCol)
361 367 }
362 368
363 369 static PyObject*
364   -GetDataDecimal(Cursor* cur, int iCol)
  370 +GetDataDecimal(Cursor* cur, Py_ssize_t iCol)
365 371 {
366 372 // The SQL_NUMERIC_STRUCT support is hopeless (SQL Server ignores scale on input parameters and output columns), so
367 373 // we'll rely on the Decimal's string parsing. Unfortunately, the Decimal author does not pay attention to the
@@ -397,7 +403,7 @@ GetDataDecimal(Cursor* cur, int iCol)
397 403 //
398 404 // Note: cbFetched does not include the NULL terminator.
399 405
400   - for (int i = cbFetched - 1; i >=0; i--)
  406 + for (int i = (int)(cbFetched - 1); i >=0; i--)
401 407 {
402 408 if (sz[i] == chGroupSeparator || sz[i] == '$' || sz[i] == chCurrencySymbol)
403 409 {
@@ -414,7 +420,7 @@ GetDataDecimal(Cursor* cur, int iCol)
414 420 }
415 421
416 422 static PyObject*
417   -GetDataBit(Cursor* cur, int iCol)
  423 +GetDataBit(Cursor* cur, Py_ssize_t iCol)
418 424 {
419 425 SQLCHAR ch;
420 426 SQLLEN cbFetched;
@@ -437,7 +443,7 @@ GetDataBit(Cursor* cur, int iCol)
437 443 }
438 444
439 445 static PyObject*
440   -GetDataLong(Cursor* cur, int iCol)
  446 +GetDataLong(Cursor* cur, Py_ssize_t iCol)
441 447 {
442 448 ColumnInfo* pinfo = &cur->colinfos[iCol];
443 449
@@ -463,7 +469,7 @@ GetDataLong(Cursor* cur, int iCol)
463 469 }
464 470
465 471 static PyObject*
466   -GetDataLongLong(Cursor* cur, int iCol)
  472 +GetDataLongLong(Cursor* cur, Py_ssize_t iCol)
467 473 {
468 474 ColumnInfo* pinfo = &cur->colinfos[iCol];
469 475
@@ -490,7 +496,7 @@ GetDataLongLong(Cursor* cur, int iCol)
490 496 }
491 497
492 498 static PyObject*
493   -GetDataDouble(Cursor* cur, int iCol)
  499 +GetDataDouble(Cursor* cur, Py_ssize_t iCol)
494 500 {
495 501 double value;
496 502 SQLLEN cbFetched = 0;
@@ -509,7 +515,7 @@ GetDataDouble(Cursor* cur, int iCol)
509 515 }
510 516
511 517 static PyObject*
512   -GetSqlServerTime(Cursor* cur, int iCol)
  518 +GetSqlServerTime(Cursor* cur, Py_ssize_t iCol)
513 519 {
514 520 SQL_SS_TIME2_STRUCT value;
515 521
@@ -530,7 +536,7 @@ GetSqlServerTime(Cursor* cur, int iCol)
530 536 }
531 537
532 538 static PyObject*
533   -GetDataTimestamp(Cursor* cur, int iCol)
  539 +GetDataTimestamp(Cursor* cur, Py_ssize_t iCol)
534 540 {
535 541 TIMESTAMP_STRUCT value;
536 542
92 src/params.cpp
@@ -8,15 +8,16 @@
8 8 #include "wrapper.h"
9 9 #include "errors.h"
10 10 #include "dbspecific.h"
  11 +#include "sqlwchar.h"
11 12
12 13 inline Connection* GetConnection(Cursor* cursor)
13 14 {
14 15 return (Connection*)cursor->cnxn;
15 16 }
16 17
17   -static int GetParamBufferSize(PyObject* param, Py_ssize_t iParam);
18   -static bool BindParam(Cursor* cur, int iParam, PyObject* param, byte** ppbParam);
19   -static SQLSMALLINT GetParamType(Cursor* cur, int iParam);
  18 +static Py_ssize_t GetParamBufferSize(Cursor* cur, PyObject* param, Py_ssize_t iParam);
  19 +static bool BindParam(Cursor* cur, Py_ssize_t iParam, PyObject* param, byte** ppbParam);
  20 +static SQLSMALLINT GetParamType(Cursor* cur, Py_ssize_t iParam);
20 21
21 22 static Py_ssize_t GetDecimalColumnSize(PyObject *p)
22 23 {
@@ -167,8 +168,9 @@ bool PrepareAndBind(Cursor* cur, PyObject* pSql, PyObject* original_params, bool
167 168 }
168 169 else
169 170 {
  171 + SQLWChar sql(pSql);
170 172 Py_BEGIN_ALLOW_THREADS
171   - ret = SQLPrepareW(cur->hstmt, (SQLWCHAR*)PyUnicode_AsUnicode(pSql), SQL_NTS);
  173 + ret = SQLPrepareW(cur->hstmt, sql, SQL_NTS);
172 174 if (SQL_SUCCEEDED(ret))
173 175 {
174 176 szErrorFunc = "SQLNumParams";
@@ -211,19 +213,21 @@ bool PrepareAndBind(Cursor* cur, PyObject* pSql, PyObject* original_params, bool
211 213 // we will bind directly into its memory. (We only use a vector so its destructor will free the memory.)
212 214 // We'll set aside one SQLLEN for each column to be used as the StrLen_or_IndPtr.
213 215
214   - int cb = 0;
  216 + Py_ssize_t cbParams = 0;
215 217
216 218 for (Py_ssize_t i = 0; i < cParams; i++)
217 219 {
218   - int cbT = GetParamBufferSize(params[i], i + 1) + sizeof(SQLLEN); // +1 to map to ODBC one-based index
  220 + Py_ssize_t cbT = GetParamBufferSize(cur, params[i], i + 1) + sizeof(SQLLEN); // +1 to map to ODBC one-based index
219 221
220 222 if (cbT < 0)
221 223 return 0;
222 224
223   - cb += cbT;
  225 + TRACE("* size(%d): %d\n", (i + 1), cbT);
  226 +
  227 + cbParams += cbT;
224 228 }
225 229
226   - cur->paramdata = reinterpret_cast<byte*>(malloc(cb));
  230 + cur->paramdata = reinterpret_cast<byte*>(malloc(cbParams));
227 231 if (cur->paramdata == 0)
228 232 {
229 233 PyErr_NoMemory();
@@ -250,18 +254,29 @@ bool PrepareAndBind(Cursor* cur, PyObject* pSql, PyObject* original_params, bool
250 254
251 255 for (Py_ssize_t i = 0; i < cParams; i++)
252 256 {
  257 + #ifdef PYODBC_TRACE
  258 + byte* pbBefore = pbParam;
  259 + #endif
  260 +
253 261 if (!BindParam(cur, i + 1, params[i], &pbParam))
254 262 {
255 263 free(cur->paramdata);
256 264 cur->paramdata = 0;
257 265 return false;
258 266 }
  267 +
  268 + #ifdef PYODBC_TRACE
  269 + TRACE("* used(%d): %d\n", (i + 1), (pbParam - pbBefore));
  270 + #endif
259 271 }
260 272
  273 + // We didn't use exactly the amount of memory allocated? GetParamBufferSize and BindParam are not in sync!
  274 + I(pbParam == cur->paramdata + cbParams);
  275 +
261 276 return true;
262 277 }
263 278
264   -static SQLSMALLINT GetParamType(Cursor* cur, int iParam)
  279 +static SQLSMALLINT GetParamType(Cursor* cur, Py_ssize_t iParam)
265 280 {
266 281 // Returns the ODBC type of the of given parameter.
267 282 //
@@ -312,21 +327,40 @@ static SQLSMALLINT GetParamType(Cursor* cur, int iParam)
312 327 }
313 328
314 329
315   -static int GetParamBufferSize(PyObject* param, Py_ssize_t iParam)
  330 +static Py_ssize_t GetParamBufferSize(Cursor* cur, PyObject* param, Py_ssize_t iParam)
316 331 {
317 332 // Returns the size in bytes needed to hold the parameter in a format for binding, used to allocate the parameter
318 333 // buffer. (The value is not passed to ODBC. Values passed to ODBC are in BindParam.)
319 334 //
320 335 // If we can bind directly into the Python object (e.g., using PyString_AsString), zero is returned since no extra
321   - // memory is required. If the data will be provided at execution time (e.g. SQL_DATA_AT_EXEC), zero is returned
  336 + // memory is required. If the data will be provided at execution time (e.g. SQL_LEN_DATA_AT_EXEC), zero is returned
322 337 // since the parameter value is not stored at all. If the data type is not recognized, -1 is returned.
323 338
324 339 if (param == Py_None)
325 340 return 0;
326 341
327   - if (PyString_Check(param) || PyUnicode_Check(param))
  342 + if (PyString_Check(param))
328 343 return 0;
329 344
  345 + if (PyUnicode_Check(param))
  346 + {
  347 + if (sizeof(SQLWCHAR) == Py_UNICODE_SIZE)
  348 + {
  349 + // We can bind directly into the parameter itself.
  350 + return 0;
  351 + }
  352 +
  353 + if (PyUnicode_GET_SIZE(param) > cur->cnxn->wvarchar_maxlength)
  354 + {
  355 + // This is too long to pass, so we'll use SQLPutData to pass in chunks. We'll need to store the pointer to
  356 + // the object.
  357 + return 0;
  358 + }
  359 +
  360 + // We are going to need to copy this buffer to convert from PyUNICODE to SQLWCHAR.
  361 + return PyUnicode_GET_SIZE(param) * sizeof(SQLWCHAR);
  362 + }
  363 +
330 364 if (param == Py_True || param == Py_False)
331 365 return 1;
332 366
@@ -345,7 +379,7 @@ static int GetParamBufferSize(PyObject* param, Py_ssize_t iParam)
345 379 if (PyBuffer_Check(param))
346 380 {
347 381 // If the buffer has a single segment, we can bind directly to it, so we need 0 bytes. Otherwise, we'll use
348   - // SQL_DATA_AT_EXEC, so we still need 0 bytes.
  382 + // SQL_LEN_DATA_AT_EXEC, so we still need 0 bytes.
349 383 return 0;
350 384 }
351 385
@@ -363,7 +397,6 @@ static int GetParamBufferSize(PyObject* param, Py_ssize_t iParam)
363 397 return -1;
364 398 }
365 399
366   -#ifdef TRACE_ALL
367 400 #define _MAKESTR(n) case n: return #n
368 401 static const char* SqlTypeName(SQLSMALLINT n)
369 402 {
@@ -371,6 +404,8 @@ static const char* SqlTypeName(SQLSMALLINT n)
371 404 {
372 405 _MAKESTR(SQL_UNKNOWN_TYPE);
373 406 _MAKESTR(SQL_CHAR);
  407 + _MAKESTR(SQL_VARCHAR);
  408 + _MAKESTR(SQL_LONGVARCHAR);
374 409 _MAKESTR(SQL_NUMERIC);
375 410 _MAKESTR(SQL_DECIMAL);
376 411 _MAKESTR(SQL_INTEGER);
@@ -379,12 +414,17 @@ static const char* SqlTypeName(SQLSMALLINT n)
379 414 _MAKESTR(SQL_REAL);
380 415 _MAKESTR(SQL_DOUBLE);
381 416 _MAKESTR(SQL_DATETIME);
382   - _MAKESTR(SQL_VARCHAR);
  417 + _MAKESTR(SQL_WCHAR);
  418 + _MAKESTR(SQL_WVARCHAR);
  419 + _MAKESTR(SQL_WLONGVARCHAR);
383 420 _MAKESTR(SQL_TYPE_DATE);
384 421 _MAKESTR(SQL_TYPE_TIME);
385 422 _MAKESTR(SQL_TYPE_TIMESTAMP);
386 423 _MAKESTR(SQL_SS_TIME2);
387 424 _MAKESTR(SQL_SS_XML);
  425 + _MAKESTR(SQL_BINARY);
  426 + _MAKESTR(SQL_VARBINARY);
  427 + _MAKESTR(SQL_LONGVARBINARY);
388 428 }
389 429 return "unknown";
390 430 }
@@ -394,6 +434,7 @@ static const char* CTypeName(SQLSMALLINT n)
394 434 switch (n)
395 435 {
396 436 _MAKESTR(SQL_C_CHAR);
  437 + _MAKESTR(SQL_C_WCHAR);
397 438 _MAKESTR(SQL_C_LONG);
398 439 _MAKESTR(SQL_C_SHORT);
399 440 _MAKESTR(SQL_C_FLOAT);
@@ -435,9 +476,7 @@ static const char* CTypeName(SQLSMALLINT n)
435 476 return "unknown";
436 477 }
437 478
438   -#endif
439   -
440   -static bool BindParam(Cursor* cur, int iParam, PyObject* param, byte** ppbParam)
  479 +static bool BindParam(Cursor* cur, Py_ssize_t iParam, PyObject* param, byte** ppbParam)
441 480 {
442 481 // Called to bind a single parameter.
443 482 //
@@ -502,8 +541,8 @@ static bool BindParam(Cursor* cur, int iParam, PyObject* param, byte** ppbParam)
502 541 }
503 542 else if (PyString_Check(param))
504 543 {
505   - char* pch = PyString_AS_STRING(param);
506   - int len = PyString_GET_SIZE(param);
  544 + char* pch = PyString_AS_STRING(param);
  545 + Py_ssize_t len = PyString_GET_SIZE(param);
507 546
508 547 if (len <= cur->cnxn->varchar_maxlength)
509 548 {
@@ -527,7 +566,7 @@ static bool BindParam(Cursor* cur, int iParam, PyObject* param, byte** ppbParam)
527 566 else if (PyUnicode_Check(param))
528 567 {
529 568 Py_UNICODE* pch = PyUnicode_AsUnicode(param);
530   - int len = PyUnicode_GET_SIZE(param);
  569 + Py_ssize_t len = PyUnicode_GET_SIZE(param);
531 570
532 571 if (len <= cur->cnxn->wvarchar_maxlength)
533 572 {
@@ -673,8 +712,8 @@ static bool BindParam(Cursor* cur, int iParam, PyObject* param, byte** ppbParam)
673 712 if (!str)
674 713 return false;
675 714
676   - char* pch = PyString_AS_STRING(str.Get());
677   - int len = PyString_GET_SIZE(str.Get());
  715 + char* pch = PyString_AS_STRING(str.Get());
  716 + Py_ssize_t len = PyString_GET_SIZE(str.Get());
678 717
679 718 *pcbValue = (SQLLEN)len;
680 719
@@ -699,7 +738,7 @@ static bool BindParam(Cursor* cur, int iParam, PyObject* param, byte** ppbParam)
699 738 else if (PyBuffer_Check(param))
700 739 {
701 740 const char* pb;
702   - int cb = PyBuffer_GetMemory(param, &pb);
  741 + Py_ssize_t cb = PyBuffer_GetMemory(param, &pb);
703 742
704 743 if (cb != -1 && cb <= cur->cnxn->binary_maxlength)
705 744 {
@@ -734,10 +773,7 @@ static bool BindParam(Cursor* cur, int iParam, PyObject* param, byte** ppbParam)
734 773 return false;
735 774 }
736 775
737   - #ifdef TRACE_ALL
738   - printf("BIND: param=%d fCType=%d (%s) fSqlType=%d (%s) cbColDef=%d DecimalDigits=%d cbValueMax=%d *pcb=%d\n", iParam,
739   - fCType, CTypeName(fCType), fSqlType, SqlTypeName(fSqlType), cbColDef, decimalDigits, cbValueMax, pcbValue ? *pcbValue : 0);
740   - #endif
  776 + TRACE("BIND: param=%d fCType=%d (%s) fSqlType=%d (%s) cbColDef=%d DecimalDigits=%d cbValueMax=%d *pcb=%d\n", iParam, fCType, CTypeName(fCType), fSqlType, SqlTypeName(fSqlType), cbColDef, decimalDigits, cbValueMax, pcbValue ? *pcbValue : 0);
741 777
742 778 SQLRETURN ret = -1;
743 779 Py_BEGIN_ALLOW_THREADS
14 src/pyodbc.h
@@ -113,13 +113,12 @@ inline void _strlwr(char* name)
113 113 }
114 114 #endif
115 115
  116 +#define STRINGIFY(x) #x
  117 +#define TOSTRING(x) STRINGIFY(x)
  118 +
116 119 // Building an actual debug version of Python is so much of a pain that it never happens. I'm providing release-build
117 120 // versions of assertions.
118 121
119   -// REVIEW: Put these into the setup script command line (or setup.cfg)
120   -// #define PYODBC_ASSERT 1
121   -// #define TRACE_ALL 1
122   -
123 122 #ifdef PYODBC_ASSERT
124 123 #ifdef _MSC_VER
125 124 #include <crtdbg.h>
@@ -139,4 +138,11 @@ inline void _strlwr(char* name)
139 138 #define N(expr)
140 139 #endif
141 140
  141 +#ifdef PYODBC_TRACE
  142 +void __cdecl DebugTrace(const char* szFmt, ...);
  143 +#else
  144 +inline void __cdecl DebugTrace(const char* szFmt, ...) { UNUSED(szFmt); }
  145 +#endif
  146 +#define TRACE DebugTrace
  147 +
142 148 #endif // pyodbc_h
12 src/pyodbcdbg.cpp
... ... @@ -0,0 +1,12 @@
  1 +
  2 +#include "pyodbc.h"
  3 +
  4 +#ifdef PYODBC_TRACE
  5 +void DebugTrace(const char* szFmt, ...)
  6 +{
  7 + va_list marker;
  8 + va_start(marker, szFmt);
  9 + vprintf(szFmt, marker);
  10 + va_end(marker);
  11 +}
  12 +#endif
28 src/pyodbcmodule.cpp
@@ -32,7 +32,7 @@ _typeobject* OurTimeType = 0;
32 32 PyObject* pModule = 0;
33 33
34 34 static char module_doc[] =
35   - "A DB API 2.0 module for ODBC databases.\n"
  35 + "A database module for accessing databases via ODBC.\n"
36 36 "\n"
37 37 "This module conforms to the DB API 2.0 specification while providing\n"
38 38 "non-standard convenience features. Only standard Python data types are used\n"
@@ -40,7 +40,11 @@ static char module_doc[] =
40 40 "\n"
41 41 "Static Variables:\n\n"
42 42 "version\n"
43   - " The module version string in the format major.minor.revision\n"
  43 + " The module version string. Official builds will have a version in the format\n"
  44 + " `major.minor.revision`, such as 2.1.7. Beta versions will have -beta appended,\n"
  45 + " such as 2.1.8-beta03. (This would be a build before the official 2.1.8 release.)\n"
  46 + " Some special test builds will have a test name (the git branch name) prepended,\n"
  47 + " such as fixissue90-2.1.8-beta03.\n"
44 48 "\n"
45 49 "apilevel\n"
46 50 " The string constant '2.0' indicating this module supports DB API level 2.0.\n"
@@ -50,6 +54,7 @@ static char module_doc[] =
50 54 " This can be changed any time and affects queries executed after the change.\n"
51 55 " The default is False. This can be useful when database columns have\n"
52 56 " inconsistent capitalization.\n"
  57 + "\n"
53 58 "pooling\n"
54 59 " A Boolean indicating whether connection pooling is enabled. This is a\n"
55 60 " global (HENV) setting, so it can only be modified before the first\n"
@@ -276,7 +281,7 @@ static PyObject* mod_connect(PyObject* self, PyObject* args, PyObject* kwargs)
276 281 int fAnsi = 0; // force ansi
277 282 int fUnicodeResults = 0;
278 283
279   - int size = args ? PyTuple_Size(args) : 0;
  284 + Py_ssize_t size = args ? PyTuple_Size(args) : 0;
280 285
281 286 if (size > 1)
282 287 {
@@ -879,17 +884,8 @@ initpyodbc()
879 884 if (!CreateExceptions())
880 885 return;
881 886
882   - // The 'build' version number is a beta identifier. For example, if it is 7, then we are on beta7 of the
883   - // (major,minor.micro) version. On Windows, we poke these values into the DLL's version resource, so when we make
884   - // an official build (which come *after* the betas), we set the BUILD to 9999 so installers will know that it
885   - // should replace any installed betas. However, we obviously don't want to see these.
886   -
887   - PyObject* pVersion;
888   - if (PYODBC_BUILD == 9999)
889   - pVersion = PyString_FromFormat("%d.%d.%d", PYODBC_MAJOR, PYODBC_MINOR, PYODBC_MICRO);
890   - else
891   - pVersion = PyString_FromFormat("%d.%d.%d-beta%d", PYODBC_MAJOR, PYODBC_MINOR, PYODBC_MICRO, PYODBC_BUILD);
892   - PyModule_AddObject(pModule, "version", pVersion);
  887 + const char* szVersion = TOSTRING(PYODBC_VERSION);
  888 + PyModule_AddStringConstant(pModule, "version", szVersion);
893 889
894 890 PyModule_AddIntConstant(pModule, "threadsafety", 1);
895 891 PyModule_AddStringConstant(pModule, "apilevel", "2.0");
@@ -950,7 +946,7 @@ static PyObject* MakeConnectionString(PyObject* existing, PyObject* parts)
950 946 // Creates a connection string from an optional existing connection string plus a dictionary of keyword value
951 947 // pairs. The keywords must be String objects and the values must be Unicode objects.
952 948
953   - int length = 0;
  949 + Py_ssize_t length = 0;
954 950 if (existing)
955 951 length = PyUnicode_GET_SIZE(existing) + 1; // + 1 to add a trailing
956 952
@@ -968,7 +964,7 @@ static PyObject* MakeConnectionString(PyObject* existing, PyObject* parts)
968 964 return 0;
969 965
970 966 Py_UNICODE* buffer = PyUnicode_AS_UNICODE(result);
971   - int offset = 0;
  967 + Py_ssize_t offset = 0;
972 968
973 969 if (existing)
974 970 {
2  src/row.cpp
@@ -39,7 +39,7 @@ struct Row
39 39 #define Row_CheckExact(op) ((op)->ob_type == &RowType)
40 40
41 41 void
42   -FreeRowValues(int cValues, PyObject** apValues)
  42 +FreeRowValues(Py_ssize_t cValues, PyObject** apValues)
43 43 {
44 44 if (apValues)
45 45 {
2  src/row.h
@@ -28,7 +28,7 @@ Row* Row_New(PyObject* description, PyObject* map_name_to_index, Py_ssize_t cVal
28 28 *
29 29 * apValues: The array of values. This can be NULL.
30 30 */
31   -void FreeRowValues(int cValues, PyObject** apValues);
  31 +void FreeRowValues(Py_ssize_t cValues, PyObject** apValues);
32 32
33 33 extern PyTypeObject RowType;
34 34 #define Row_Check(op) PyObject_TypeCheck(op, &RowType)
118 src/sqlwchar.cpp
... ... @@ -0,0 +1,118 @@
  1 +
  2 +#include "pyodbc.h"
  3 +#include "sqlwchar.h"
  4 +#include "wrapper.h"
  5 +
  6 +// We could eliminate a lot of trouble if we had a define for the size of a SQLWCHAR. Unfortunately, I can't think of
  7 +// a way to do this with the preprocessor. Python's setup.cfg files aren't really making it easy either. For now
  8 +// we'll use C code and revisit this later.
  9 +
  10 +SQLWChar::SQLWChar(PyObject* o)
  11 +{
  12 + // Converts from a Python Unicode string.
  13 +
  14 + pch = 0;
  15 + len = 0;
  16 + owns_memory = false;
  17 +
  18 + Convert(o);
  19 +}
  20 +
  21 +void SQLWChar::Free()
  22 +{
  23 + if (pch && owns_memory)
  24 + free(pch);
  25 + pch = 0;
  26 + len = 0;
  27 + owns_memory = false;
  28 +}
  29 +
  30 +bool SQLWChar::Convert(PyObject* o)
  31 +{
  32 + Free();
  33 +
  34 + if (!PyUnicode_Check(o))
  35 + {
  36 + PyErr_SetString(PyExc_TypeError, "Unicode required");
  37 + return false;
  38 + }
  39 +
  40 + Py_UNICODE* pU = (SQLWCHAR*)PyUnicode_AS_UNICODE(o);
  41 + Py_ssize_t lenT = PyUnicode_GET_SIZE(o);
  42 +
  43 + if (sizeof(SQLWCHAR) == Py_UNICODE_SIZE)
  44 + {
  45 + // The ideal case - SQLWCHAR and Py_UNICODE are the same, so we point into the Unicode object.
  46 +
  47 + pch = (SQLWCHAR*)pU;
  48 + len = lenT;
  49 + owns_memory = false;
  50 + return true;
  51 + }
  52 +
  53 + SQLWCHAR* pchT = (SQLWCHAR*)malloc(sizeof(SQLWCHAR) * (lenT + 1));
  54 + if (pchT == 0)
  55 + {
  56 + PyErr_NoMemory();
  57 + return false;
  58 + }
  59 +
  60 + if (!sqlwchar_copy(pchT, pU, lenT))
  61 + {
  62 + free(pchT);
  63 + return false;
  64 + }
  65 +
  66 + pch = pchT;
  67 + len = lenT;
  68 + owns_memory = true;
  69 + return true;
  70 +}
  71 +
  72 +
  73 +bool sqlwchar_copy(SQLWCHAR* pdest, const Py_UNICODE* psrc, Py_ssize_t len)
  74 +{
  75 + for (int i = 0; i <= len; i++) // ('<=' to include the NULL)
  76 + {
  77 + pdest[i] = (SQLWCHAR)psrc[i];
  78 + if ((Py_UNICODE)pdest[i] < psrc[i])
  79 + {
  80 + PyErr_Format(PyExc_ValueError, "Cannot convert from Unicode %zd to SQLWCHAR. Value is too large.", (Py_ssize_t)psrc[i]);
  81 + return false;
  82 + }
  83 + }
  84 + return true;
  85 +}
  86 +
  87 +
  88 +PyObject* PyUnicode_FromSQLWCHAR(const SQLWCHAR* sz, Py_ssize_t cch)
  89 +{
  90 + if (sizeof(SQLWCHAR) == Py_UNICODE_SIZE)
  91 + return PyUnicode_FromUnicode(sz, cch);
  92 +
  93 +#if HAVE_WCHAR_H
  94 + if (sizeof(wchar_t) == sizeof(SQLWCHAR))
  95 + {
  96 + // Python provides a function to map from wchar_t to Unicode. Since wchar_t and SQLWCHAR are the same size, we can
  97 + // use it.
  98 + return PyUnicode_FromWideChar((const wchar_t*)sz, cch);
  99 + }
  100 +#endif
  101 +
  102 + Object result(PyUnicode_FromUnicode(0, cch));
  103 + if (!result)
  104 + return 0;
  105 +
  106 + Py_UNICODE* pch = PyUnicode_AS_UNICODE(result.Get());
  107 + for (Py_ssize_t i = 0; i < cch; i++)
  108 + {
  109 + pch[i] = (Py_UNICODE)sz[i];
  110 + if ((SQLWCHAR)pch[i] != sz[i])
  111 + {
  112 + PyErr_Format(PyExc_ValueError, "Cannot convert from SQLWCHAR %zd to Unicode. Value is too large.", (Py_ssize_t)pch[i]);
  113 + return 0;
  114 + }
  115 + }
  116 +
  117 + return result.Detach();
  118 +}
54 src/sqlwchar.h
... ... @@ -0,0 +1,54 @@
  1 +
  2 +#ifndef _PYODBCSQLWCHAR_H
  3 +#define _PYODBCSQLWCHAR_H
  4 +
  5 +class SQLWChar
  6 +{
  7 +private:
  8 + SQLWCHAR* pch;
  9 + Py_ssize_t len;
  10 + bool owns_memory;
  11 +
  12 +public:
  13 + SQLWChar()
  14 + {
  15 + pch = 0;
  16 + len = 0;
  17 + owns_memory = false;
  18 + }
  19 +
  20 + SQLWChar(PyObject* o);
  21 +
  22 + bool Convert(PyObject* o);
  23 +
  24 + void Free();
  25 +
  26 + ~SQLWChar()
  27 + {
  28 + Free();
  29 + }
  30 +
  31 + operator SQLWCHAR*() { return pch; }
  32 + operator const SQLWCHAR*() const { return pch; }
  33 + operator bool() const { return pch != 0; }
  34 + Py_ssize_t size() const { return len; }
  35 +
  36 + SQLWCHAR* operator[] (Py_ssize_t i)
  37 + {
  38 + I(i <= len); // we'll allow access to the NULL?
  39 + return &pch[i];
  40 + }
  41 +
  42 + const SQLWCHAR* operator[] (Py_ssize_t i) const
  43 + {
  44 + I(i <= len); // we'll allow access to the NULL?
  45 + return &pch[i];
  46 + }
  47 +};
  48 +
  49 +PyObject* PyUnicode_FromSQLWCHAR(const SQLWCHAR* sz, Py_ssize_t cch);
  50 +
  51 +bool sqlwchar_copy(SQLWCHAR* pdest, const Py_UNICODE* psrc, Py_ssize_t len);
  52 +
  53 +
  54 +#endif // _PYODBCSQLWCHAR_H

0 comments on commit 691b6ba

Please sign in to comment.
Something went wrong with that request. Please try again.