pyodbc 5.2.0__tar.gz → 5.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyodbc-5.4.0/PKG-INFO +58 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/pyproject.toml +3 -4
- {pyodbc-5.2.0 → pyodbc-5.4.0}/setup.py +8 -2
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/connection.cpp +197 -20
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/connection.h +15 -1
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/cursor.cpp +336 -11
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/cursor.h +12 -2
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/dbspecific.h +1 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/decimal.cpp +69 -9
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/decimal.h +2 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/errors.cpp +10 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/getdata.cpp +117 -5
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/params.cpp +293 -128
- pyodbc-5.4.0/src/pyodbc.egg-info/PKG-INFO +58 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/pyodbc.egg-info/SOURCES.txt +1 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/pyodbc.h +0 -3
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/pyodbc.pyi +145 -65
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/pyodbcmodule.cpp +121 -13
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/pyodbcmodule.h +2 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/row.cpp +1 -1
- {pyodbc-5.2.0 → pyodbc-5.4.0}/tests/conftest.py +2 -2
- {pyodbc-5.2.0 → pyodbc-5.4.0}/tests/mysql_test.py +52 -15
- {pyodbc-5.2.0 → pyodbc-5.4.0}/tests/postgresql_test.py +100 -16
- pyodbc-5.4.0/tests/sqlite_test.py +801 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/tests/sqlserver_test.py +408 -79
- pyodbc-5.2.0/PKG-INFO +0 -25
- pyodbc-5.2.0/src/pyodbc.egg-info/PKG-INFO +0 -25
- {pyodbc-5.2.0 → pyodbc-5.4.0}/LICENSE.txt +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/MANIFEST.in +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/README.md +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/setup.cfg +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/cnxninfo.cpp +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/cnxninfo.h +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/errors.h +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/getdata.h +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/params.h +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/pyodbc.egg-info/dependency_links.txt +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/pyodbc.egg-info/top_level.txt +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/pyodbcdbg.cpp +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/resource.h +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/row.h +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/textenc.cpp +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/textenc.h +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/src/wrapper.h +0 -0
- {pyodbc-5.2.0 → pyodbc-5.4.0}/tests/__init__.py +0 -0
pyodbc-5.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyodbc
|
|
3
|
+
Version: 5.4.0
|
|
4
|
+
Summary: DB API module for ODBC
|
|
5
|
+
Home-page: https://github.com/mkleehammer/pyodbc
|
|
6
|
+
Author-email: Michael Kleehammer <michael@kleehammer.com>
|
|
7
|
+
Maintainer: Michael Kleehammer
|
|
8
|
+
Maintainer-email: Michael Kleehammer <michael@kleehammer.com>
|
|
9
|
+
License-Expression: MIT-0
|
|
10
|
+
Project-URL: Homepage, https://github.com/mkleehammer/pyodbc
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Classifier: Operating System :: POSIX
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Topic :: Database
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE.txt
|
|
22
|
+
Dynamic: home-page
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
Dynamic: maintainer
|
|
25
|
+
Dynamic: requires-python
|
|
26
|
+
|
|
27
|
+
# pyodbc
|
|
28
|
+
|
|
29
|
+
[](https://ci.appveyor.com/project/mkleehammer/pyodbc)
|
|
30
|
+
[](https://github.com/mkleehammer/pyodbc/actions/workflows/ubuntu_build.yml)
|
|
31
|
+
[](https://pypi.org/project/pyodbc/)
|
|
32
|
+
|
|
33
|
+
pyodbc is an open source Python module that makes accessing ODBC databases simple. It
|
|
34
|
+
implements the [DB API 2.0](https://www.python.org/dev/peps/pep-0249) specification but is packed with even more Pythonic convenience.
|
|
35
|
+
|
|
36
|
+
The easiest way to install pyodbc is to use pip:
|
|
37
|
+
|
|
38
|
+
python -m pip install pyodbc
|
|
39
|
+
|
|
40
|
+
On Macs, you should probably install unixODBC first if you don't already have an ODBC
|
|
41
|
+
driver manager installed. For example, using the [homebrew](https://brew.sh/) package manager:
|
|
42
|
+
|
|
43
|
+
brew install unixodbc
|
|
44
|
+
python -m pip install pyodbc
|
|
45
|
+
|
|
46
|
+
Similarly, on Unix you should make sure you have an ODBC driver manager installed before
|
|
47
|
+
installing pyodbc. See the [docs](https://github.com/mkleehammer/pyodbc/wiki/Install)
|
|
48
|
+
for more information about how to do this on different Unix flavors. (On Windows, the
|
|
49
|
+
ODBC driver manager is built-in.)
|
|
50
|
+
|
|
51
|
+
Precompiled binary wheels are provided for multiple Python versions on most Windows, macOS,
|
|
52
|
+
and Linux platforms. On other platforms pyodbc will be built from the source code. Note,
|
|
53
|
+
pyodbc contains C++ extensions so you will need a suitable C++ compiler when building from
|
|
54
|
+
source. See the [docs](https://github.com/mkleehammer/pyodbc/wiki/Install) for details.
|
|
55
|
+
|
|
56
|
+
[Documentation](https://github.com/mkleehammer/pyodbc/wiki)
|
|
57
|
+
|
|
58
|
+
[Release Notes](https://github.com/mkleehammer/pyodbc/releases)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pyodbc"
|
|
3
|
-
version = "5.
|
|
3
|
+
version = "5.4.0"
|
|
4
4
|
|
|
5
|
-
requires-python = ">=3.
|
|
5
|
+
requires-python = ">=3.11"
|
|
6
6
|
# This is used by the GitHub action that builds release artifacts using cibuildwheel.
|
|
7
7
|
# cibuildwheel reads this directly:
|
|
8
8
|
#
|
|
@@ -10,7 +10,7 @@ requires-python = ">=3.8"
|
|
|
10
10
|
|
|
11
11
|
description = "DB API module for ODBC"
|
|
12
12
|
readme = "README.md"
|
|
13
|
-
license =
|
|
13
|
+
license = "MIT-0"
|
|
14
14
|
|
|
15
15
|
authors = [{name = "Michael Kleehammer", email="michael@kleehammer.com"}]
|
|
16
16
|
|
|
@@ -22,7 +22,6 @@ maintainers = [{name = "Michael Kleehammer", email="michael@kleehammer.com"}]
|
|
|
22
22
|
classifiers=['Development Status :: 5 - Production/Stable',
|
|
23
23
|
'Intended Audience :: Developers',
|
|
24
24
|
'Intended Audience :: System Administrators',
|
|
25
|
-
'License :: OSI Approved :: MIT No Attribution License (MIT-0)',
|
|
26
25
|
'Operating System :: Microsoft :: Windows',
|
|
27
26
|
'Operating System :: POSIX',
|
|
28
27
|
'Programming Language :: Python',
|
|
@@ -56,11 +56,11 @@ def main():
|
|
|
56
56
|
package_dir={'': 'src'},
|
|
57
57
|
package_data={'': ['pyodbc.pyi']}, # places pyodbc.pyi alongside pyodbc.{platform}.{pyd|so} in site-packages
|
|
58
58
|
license='MIT-0',
|
|
59
|
-
python_requires='>=3.
|
|
59
|
+
python_requires='>=3.11',
|
|
60
60
|
classifiers=['Development Status :: 5 - Production/Stable',
|
|
61
61
|
'Intended Audience :: Developers',
|
|
62
62
|
'Intended Audience :: System Administrators',
|
|
63
|
-
'License :: OSI Approved :: MIT
|
|
63
|
+
'License :: OSI Approved :: MIT-0',
|
|
64
64
|
'Operating System :: Microsoft :: Windows',
|
|
65
65
|
'Operating System :: POSIX',
|
|
66
66
|
'Programming Language :: Python',
|
|
@@ -116,6 +116,7 @@ def get_compiler_settings():
|
|
|
116
116
|
|
|
117
117
|
settings['libraries'].append('odbc32')
|
|
118
118
|
settings['libraries'].append('advapi32')
|
|
119
|
+
settings['libraries'].append('user32')
|
|
119
120
|
|
|
120
121
|
elif os.environ.get("OS", '').lower().startswith('windows'):
|
|
121
122
|
# Windows Cygwin (posix on windows)
|
|
@@ -147,10 +148,15 @@ def get_compiler_settings():
|
|
|
147
148
|
'/opt/homebrew/include',
|
|
148
149
|
expanduser('~/homebrew/include'),
|
|
149
150
|
]
|
|
151
|
+
_HOMEBREW_PREFIX = os.environ.get('HOMEBREW_PREFIX')
|
|
152
|
+
if _HOMEBREW_PREFIX is not None:
|
|
153
|
+
dirs.insert(0, os.path.join(_HOMEBREW_PREFIX, 'include'))
|
|
150
154
|
settings['include_dirs'].extend(dir for dir in dirs if isdir(dir))
|
|
151
155
|
# unixODBC make/install places libodbc.dylib in /usr/local/lib/ by default
|
|
152
156
|
# ( also OS/X since El Capitan prevents /usr/lib from being accessed )
|
|
153
157
|
settings['library_dirs'] = ['/usr/local/lib', '/opt/homebrew/lib']
|
|
158
|
+
if _HOMEBREW_PREFIX is not None:
|
|
159
|
+
settings['library_dirs'].insert(1, os.path.join(_HOMEBREW_PREFIX, 'lib'))
|
|
154
160
|
else:
|
|
155
161
|
# Other posix-like: Linux, Solaris, etc.
|
|
156
162
|
|
|
@@ -59,7 +59,7 @@ static char* StrDup(const char* text) {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
static bool Connect(PyObject* pConnectString, HDBC hdbc, long timeout, PyObject* encoding)
|
|
62
|
+
static bool Connect(PyObject* pConnectString, HDBC hdbc, long timeout, PyObject* encoding, SQLUSMALLINT driver_completion)
|
|
63
63
|
{
|
|
64
64
|
assert(PyUnicode_Check(pConnectString));
|
|
65
65
|
|
|
@@ -88,25 +88,42 @@ static bool Connect(PyObject* pConnectString, HDBC hdbc, long timeout, PyObject*
|
|
|
88
88
|
if (!cstring.isValid())
|
|
89
89
|
return false;
|
|
90
90
|
|
|
91
|
+
SQLHWND hwnd = 0;
|
|
92
|
+
#ifdef _WIN32
|
|
93
|
+
if (driver_completion != SQL_DRIVER_NOPROMPT)
|
|
94
|
+
{
|
|
95
|
+
hwnd = GetDesktopWindow();
|
|
96
|
+
if (!hwnd)
|
|
97
|
+
{
|
|
98
|
+
PyErr_SetString(OperationalError, "Failed to get desktop window handle");
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
#endif
|
|
103
|
+
|
|
91
104
|
Py_BEGIN_ALLOW_THREADS
|
|
92
|
-
ret = SQLDriverConnectW(hdbc,
|
|
105
|
+
ret = SQLDriverConnectW(hdbc, hwnd, cstring, SQL_NTS, 0, 0, 0, driver_completion);
|
|
93
106
|
Py_END_ALLOW_THREADS
|
|
94
107
|
if (SQL_SUCCEEDED(ret))
|
|
95
108
|
return true;
|
|
96
109
|
|
|
110
|
+
if (ret == SQL_NO_DATA)
|
|
111
|
+
{
|
|
112
|
+
PyErr_SetString(OperationalError, "User cancelled connection request");
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
|
|
97
116
|
RaiseErrorFromHandle(0, "SQLDriverConnect", hdbc, SQL_NULL_HANDLE);
|
|
98
117
|
|
|
99
118
|
return false;
|
|
100
119
|
}
|
|
101
120
|
|
|
102
|
-
static bool ApplyPreconnAttrs(HDBC hdbc, SQLINTEGER ikey, PyObject *value, char *strencoding)
|
|
121
|
+
static bool ApplyPreconnAttrs(HDBC hdbc, SQLINTEGER ikey, PyObject *value, char *strencoding, PyObject *keepalives)
|
|
103
122
|
{
|
|
104
123
|
SQLRETURN ret;
|
|
105
124
|
SQLPOINTER ivalue = 0;
|
|
106
125
|
SQLINTEGER vallen = 0;
|
|
107
126
|
|
|
108
|
-
SQLWChar sqlchar;
|
|
109
|
-
|
|
110
127
|
if (PyLong_Check(value))
|
|
111
128
|
{
|
|
112
129
|
if (_PyLong_Sign(value) >= 0)
|
|
@@ -121,19 +138,42 @@ static bool ApplyPreconnAttrs(HDBC hdbc, SQLINTEGER ikey, PyObject *value, char
|
|
|
121
138
|
}
|
|
122
139
|
else if (PyByteArray_Check(value))
|
|
123
140
|
{
|
|
124
|
-
|
|
141
|
+
// A bytearray is mutable: PyByteArray_AsString returns a pointer into its internal
|
|
142
|
+
// storage, which a concurrent resize (.extend(), +=, slice assignment) reallocates,
|
|
143
|
+
// invalidating the pointer we hand to the driver. Some drivers read the buffer late
|
|
144
|
+
// (during SQLDriverConnectW, after the GIL has been released), so snapshot the contents
|
|
145
|
+
// into an immutable bytes and keep that alive instead -- matching the bytes/str paths
|
|
146
|
+
// and closing the race for the bytearray case.
|
|
147
|
+
// https://github.com/mkleehammer/pyodbc/issues/1497
|
|
148
|
+
Object po(PyBytes_FromObject(value));
|
|
149
|
+
if (!po)
|
|
150
|
+
return false; // OOM
|
|
151
|
+
ivalue = (SQLPOINTER)PyBytes_AsString(po);
|
|
125
152
|
vallen = SQL_IS_POINTER;
|
|
153
|
+
// PyList_Append increments the refcount, so the list owns the snapshot for as long as
|
|
154
|
+
// the driver needs it; po releases our own reference when it goes out of scope.
|
|
155
|
+
if (PyList_Append(keepalives, po)) // OOM
|
|
156
|
+
return false;
|
|
126
157
|
}
|
|
127
158
|
else if (PyBytes_Check(value))
|
|
128
159
|
{
|
|
129
|
-
|
|
160
|
+
// Keep the value alive beyond this function invocation's lifetime.
|
|
161
|
+
if (PyList_Append(keepalives, value)) // OOM
|
|
162
|
+
return false;
|
|
163
|
+
ivalue = (SQLPOINTER)PyBytes_AsString(value);
|
|
130
164
|
vallen = SQL_IS_POINTER;
|
|
131
165
|
}
|
|
132
166
|
else if (PyUnicode_Check(value))
|
|
133
167
|
{
|
|
134
|
-
|
|
135
|
-
|
|
168
|
+
Object po(PyUnicode_AsEncodedString(value, strencoding ? strencoding : "utf-16le", "strict"));
|
|
169
|
+
if (!po)
|
|
170
|
+
return false; // OOM
|
|
171
|
+
ivalue = (SQLPOINTER)PyBytes_AsString(po);
|
|
136
172
|
vallen = SQL_NTS;
|
|
173
|
+
// PyList_Append increments the refcount, so the list owns the encoded buffer for as long
|
|
174
|
+
// as the driver needs it; po releases our own reference when it goes out of scope.
|
|
175
|
+
if (PyList_Append(keepalives, po)) // OOM
|
|
176
|
+
return false;
|
|
137
177
|
}
|
|
138
178
|
else if (PySequence_Check(value))
|
|
139
179
|
{
|
|
@@ -142,7 +182,7 @@ static bool ApplyPreconnAttrs(HDBC hdbc, SQLINTEGER ikey, PyObject *value, char
|
|
|
142
182
|
for (Py_ssize_t i = 0; i < len; i++)
|
|
143
183
|
{
|
|
144
184
|
Object v(PySequence_GetItem(value, i));
|
|
145
|
-
if (!ApplyPreconnAttrs(hdbc, ikey, v.Get(), strencoding))
|
|
185
|
+
if (!ApplyPreconnAttrs(hdbc, ikey, v.Get(), strencoding, keepalives))
|
|
146
186
|
return false;
|
|
147
187
|
}
|
|
148
188
|
return true;
|
|
@@ -170,7 +210,7 @@ static bool ApplyPreconnAttrs(HDBC hdbc, SQLINTEGER ikey, PyObject *value, char
|
|
|
170
210
|
}
|
|
171
211
|
|
|
172
212
|
PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, long timeout, bool fReadOnly,
|
|
173
|
-
PyObject* attrs_before, PyObject* encoding)
|
|
213
|
+
PyObject* attrs_before, PyObject* encoding, SQLUSMALLINT driver_completion)
|
|
174
214
|
{
|
|
175
215
|
//
|
|
176
216
|
// Allocate HDBC and connect
|
|
@@ -189,8 +229,18 @@ PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, long timeou
|
|
|
189
229
|
// Attributes that must be set before connecting.
|
|
190
230
|
//
|
|
191
231
|
|
|
232
|
+
PyObject* preconn_keepalives = 0;
|
|
192
233
|
if (attrs_before)
|
|
193
234
|
{
|
|
235
|
+
// We have evidence that some drivers hold on to preconnection values longer after the
|
|
236
|
+
// call to SQLSetConnectAttrW has returned, so we're keeping those values alive to avoid
|
|
237
|
+
// a crash.
|
|
238
|
+
// https://github.com/mkleehammer/pyodbc/issues/1469
|
|
239
|
+
// https://github.com/microsoft/msphpsql/issues/1594
|
|
240
|
+
preconn_keepalives = PyList_New(0);
|
|
241
|
+
if (!preconn_keepalives)
|
|
242
|
+
return 0;
|
|
243
|
+
|
|
194
244
|
Py_ssize_t pos = 0;
|
|
195
245
|
PyObject* key = 0;
|
|
196
246
|
PyObject* value = 0;
|
|
@@ -206,22 +256,28 @@ PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, long timeou
|
|
|
206
256
|
|
|
207
257
|
if (PyLong_Check(key))
|
|
208
258
|
ikey = (int)PyLong_AsLong(key);
|
|
209
|
-
if (!ApplyPreconnAttrs(hdbc, ikey, value, strencoding))
|
|
259
|
+
if (!ApplyPreconnAttrs(hdbc, ikey, value, strencoding, preconn_keepalives))
|
|
210
260
|
{
|
|
261
|
+
Py_DECREF(preconn_keepalives);
|
|
211
262
|
return 0;
|
|
212
263
|
}
|
|
213
264
|
}
|
|
214
265
|
}
|
|
215
266
|
|
|
216
|
-
if (!Connect(pConnectString, hdbc, timeout, encoding))
|
|
267
|
+
if (!Connect(pConnectString, hdbc, timeout, encoding, driver_completion))
|
|
217
268
|
{
|
|
218
269
|
// Connect has already set an exception.
|
|
219
270
|
Py_BEGIN_ALLOW_THREADS
|
|
220
271
|
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
|
|
221
272
|
Py_END_ALLOW_THREADS
|
|
273
|
+
Py_XDECREF(preconn_keepalives);
|
|
222
274
|
return 0;
|
|
223
275
|
}
|
|
224
276
|
|
|
277
|
+
// The current evidence indicates that the bug in Microsoft's driver isn't as bad as it
|
|
278
|
+
// could be, so we don't have to keep these objects alive until the connection is closed.
|
|
279
|
+
Py_XDECREF(preconn_keepalives);
|
|
280
|
+
|
|
225
281
|
//
|
|
226
282
|
// Connected, so allocate the Connection object.
|
|
227
283
|
//
|
|
@@ -250,8 +306,11 @@ PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, long timeou
|
|
|
250
306
|
cnxn->maxwrite = 0;
|
|
251
307
|
cnxn->timeout = 0;
|
|
252
308
|
cnxn->map_sqltype_to_converter = 0;
|
|
309
|
+
cnxn->readvar_initsize = 4096;
|
|
310
|
+
cnxn->compat_diagrec_byte_length = false;
|
|
253
311
|
|
|
254
312
|
cnxn->attrs_before = attrs_before_o.Detach();
|
|
313
|
+
cnxn->fetch_decimal_as_string = false;
|
|
255
314
|
|
|
256
315
|
// This is an inefficient default, but should work all the time. When we are offered
|
|
257
316
|
// single-byte text we don't actually know what the encoding is. For example, with SQL
|
|
@@ -351,25 +410,44 @@ static char set_attr_doc[] =
|
|
|
351
410
|
"attr_id\n"
|
|
352
411
|
" The attribute id (integer) to set. These are ODBC or driver constants.\n\n"
|
|
353
412
|
"value\n"
|
|
354
|
-
" An integer value
|
|
355
|
-
"At this time, only integer values are supported and are always passed as SQLUINTEGER.";
|
|
413
|
+
" An integer or string value.";
|
|
356
414
|
|
|
357
415
|
static PyObject* Connection_set_attr(PyObject* self, PyObject* args)
|
|
358
416
|
{
|
|
359
417
|
int id;
|
|
360
|
-
|
|
361
|
-
if (!PyArg_ParseTuple(args, "
|
|
418
|
+
PyObject* value;
|
|
419
|
+
if (!PyArg_ParseTuple(args, "iO", &id, &value))
|
|
362
420
|
return 0;
|
|
363
421
|
|
|
364
422
|
Connection* cnxn = (Connection*)self;
|
|
365
423
|
|
|
366
424
|
SQLRETURN ret;
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
425
|
+
SQLWChar sqlchar; // declared here so buffer stays alive across the call
|
|
426
|
+
|
|
427
|
+
if (PyLong_Check(value))
|
|
428
|
+
{
|
|
429
|
+
long ival = PyLong_AsLong(value);
|
|
430
|
+
Py_BEGIN_ALLOW_THREADS
|
|
431
|
+
ret = SQLSetConnectAttrW(cnxn->hdbc, id, (SQLPOINTER)(intptr_t)ival, SQL_IS_INTEGER);
|
|
432
|
+
Py_END_ALLOW_THREADS
|
|
433
|
+
}
|
|
434
|
+
else if (PyUnicode_Check(value))
|
|
435
|
+
{
|
|
436
|
+
sqlchar.set(value, "utf-16le");
|
|
437
|
+
Py_BEGIN_ALLOW_THREADS
|
|
438
|
+
ret = SQLSetConnectAttrW(cnxn->hdbc, id, sqlchar.get(), SQL_NTS);
|
|
439
|
+
Py_END_ALLOW_THREADS
|
|
440
|
+
}
|
|
441
|
+
else
|
|
442
|
+
{
|
|
443
|
+
PyErr_Format(PyExc_TypeError, "set_attr value must be a string or integer, not '%s'",
|
|
444
|
+
Py_TYPE(value)->tp_name);
|
|
445
|
+
return 0;
|
|
446
|
+
}
|
|
370
447
|
|
|
371
448
|
if (!SQL_SUCCEEDED(ret))
|
|
372
449
|
return RaiseErrorFromHandle(cnxn, "SQLSetConnectAttr", cnxn->hdbc, SQL_NULL_HANDLE);
|
|
450
|
+
|
|
373
451
|
Py_RETURN_NONE;
|
|
374
452
|
}
|
|
375
453
|
|
|
@@ -875,6 +953,21 @@ static PyObject* Connection_getclosed(PyObject* self, void* closure)
|
|
|
875
953
|
Py_RETURN_FALSE;
|
|
876
954
|
}
|
|
877
955
|
|
|
956
|
+
static PyObject* Connection_gethdbc(PyObject* self, void* closure)
|
|
957
|
+
{
|
|
958
|
+
UNUSED(closure);
|
|
959
|
+
Connection* cnxn;
|
|
960
|
+
|
|
961
|
+
if (!self || !Connection_Check(self))
|
|
962
|
+
{
|
|
963
|
+
PyErr_SetString(PyExc_TypeError, "Connection object required");
|
|
964
|
+
return nullptr;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
cnxn = (Connection*)self;
|
|
968
|
+
|
|
969
|
+
return MakeVoidPointerFromHandle(cnxn->hdbc);
|
|
970
|
+
}
|
|
878
971
|
|
|
879
972
|
static PyObject* Connection_getsearchescape(PyObject* self, void* closure)
|
|
880
973
|
{
|
|
@@ -942,6 +1035,34 @@ static int Connection_setmaxwrite(PyObject* self, PyObject* value, void* closure
|
|
|
942
1035
|
return 0;
|
|
943
1036
|
}
|
|
944
1037
|
|
|
1038
|
+
static PyObject* Connection_getreadvarinitsize(PyObject* self, void* closure)
|
|
1039
|
+
{
|
|
1040
|
+
UNUSED(closure);
|
|
1041
|
+
|
|
1042
|
+
Connection* cnxn = Connection_Validate(self);
|
|
1043
|
+
if (!cnxn)
|
|
1044
|
+
return 0;
|
|
1045
|
+
return PyLong_FromSsize_t(cnxn->readvar_initsize);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
static int Connection_setreadvarinitsize(PyObject* self, PyObject* value, void* closure)
|
|
1049
|
+
{
|
|
1050
|
+
UNUSED(closure);
|
|
1051
|
+
|
|
1052
|
+
Connection* cnxn = Connection_Validate(self);
|
|
1053
|
+
if (!cnxn)
|
|
1054
|
+
return -1;
|
|
1055
|
+
Py_ssize_t v = PyLong_AsSsize_t(value);
|
|
1056
|
+
if (v == -1 && PyErr_Occurred())
|
|
1057
|
+
return -1;
|
|
1058
|
+
if (v < 0)
|
|
1059
|
+
{
|
|
1060
|
+
PyErr_SetString(PyExc_TypeError, "Cannot set readvar_initsize to a negative value.");
|
|
1061
|
+
return -1;
|
|
1062
|
+
}
|
|
1063
|
+
cnxn->readvar_initsize = v;
|
|
1064
|
+
return 0;
|
|
1065
|
+
}
|
|
945
1066
|
|
|
946
1067
|
static PyObject* Connection_gettimeout(PyObject* self, void* closure)
|
|
947
1068
|
{
|
|
@@ -991,6 +1112,53 @@ static int Connection_settimeout(PyObject* self, PyObject* value, void* closure)
|
|
|
991
1112
|
return 0;
|
|
992
1113
|
}
|
|
993
1114
|
|
|
1115
|
+
static PyObject* Connection_getfetchdecimalasstring(PyObject* self, void* closure)
|
|
1116
|
+
{
|
|
1117
|
+
UNUSED(closure);
|
|
1118
|
+
|
|
1119
|
+
Connection* cnxn = Connection_Validate(self);
|
|
1120
|
+
if (!cnxn)
|
|
1121
|
+
return 0;
|
|
1122
|
+
|
|
1123
|
+
PyObject* result = cnxn->fetch_decimal_as_string ? Py_True : Py_False;
|
|
1124
|
+
Py_INCREF(result);
|
|
1125
|
+
return result;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
static int Connection_setfetchdecimalasstring(PyObject* self, PyObject* value, void* closure)
|
|
1129
|
+
{
|
|
1130
|
+
UNUSED(closure);
|
|
1131
|
+
|
|
1132
|
+
Connection* cnxn = Connection_Validate(self);
|
|
1133
|
+
if (!cnxn)
|
|
1134
|
+
return -1;
|
|
1135
|
+
|
|
1136
|
+
if (value == 0)
|
|
1137
|
+
{
|
|
1138
|
+
PyErr_SetString(PyExc_TypeError, "Cannot delete the fetch_decimal_as_string attribute.");
|
|
1139
|
+
return -1;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
cnxn->fetch_decimal_as_string = PyObject_IsTrue(value);
|
|
1143
|
+
return 0;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
static PyObject* Connection_getcompat_diagrec_byte_length(PyObject* self, void* closure)
|
|
1147
|
+
{
|
|
1148
|
+
return PyBool_FromLong(((Connection*)self)->compat_diagrec_byte_length);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
static int Connection_setcompat_diagrec_byte_length(PyObject* self, PyObject* value, void* closure)
|
|
1152
|
+
{
|
|
1153
|
+
if (!PyBool_Check(value))
|
|
1154
|
+
{
|
|
1155
|
+
PyErr_SetString(PyExc_TypeError, "compat_diagrec_byte_length must be a bool");
|
|
1156
|
+
return -1;
|
|
1157
|
+
}
|
|
1158
|
+
((Connection*)self)->compat_diagrec_byte_length = (value == Py_True);
|
|
1159
|
+
return 0;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
994
1162
|
static bool _remove_converter(PyObject* self, SQLSMALLINT sqltype)
|
|
995
1163
|
{
|
|
996
1164
|
Connection* cnxn = (Connection*)self;
|
|
@@ -1386,6 +1554,7 @@ static struct PyMethodDef Connection_methods[] =
|
|
|
1386
1554
|
static PyGetSetDef Connection_getseters[] = {
|
|
1387
1555
|
{ "closed", (getter)Connection_getclosed, 0,
|
|
1388
1556
|
"Returns True if the connection is closed; False otherwise.", 0},
|
|
1557
|
+
{ "hdbc", (getter)Connection_gethdbc, 0, "ODBC connection handle.", 0 },
|
|
1389
1558
|
{ "searchescape", (getter)Connection_getsearchescape, 0,
|
|
1390
1559
|
"The ODBC search pattern escape character, as returned by\n"
|
|
1391
1560
|
"SQLGetInfo(SQL_SEARCH_PATTERN_ESCAPE). These are driver specific.", 0 },
|
|
@@ -1394,6 +1563,14 @@ static PyGetSetDef Connection_getseters[] = {
|
|
|
1394
1563
|
{ "timeout", Connection_gettimeout, Connection_settimeout,
|
|
1395
1564
|
"The timeout in seconds, zero means no timeout.", 0 },
|
|
1396
1565
|
{ "maxwrite", Connection_getmaxwrite, Connection_setmaxwrite, "The maximum bytes to write before using SQLPutData.", 0 },
|
|
1566
|
+
{ "readvar_initsize", Connection_getreadvarinitsize, Connection_setreadvarinitsize,
|
|
1567
|
+
"The initial buffer size in bytes for reading values from variable-length columns.", 0 },
|
|
1568
|
+
{ "fetch_decimal_as_string", Connection_getfetchdecimalasstring, Connection_setfetchdecimalasstring,
|
|
1569
|
+
"If True, DECIMAL and NUMERIC values are fetched as strings using the legacy\n"
|
|
1570
|
+
"locale-aware path. If False (the default), values are fetched using a binary\n"
|
|
1571
|
+
"representation that is not affected by the locale.", 0 },
|
|
1572
|
+
{ "compat_diagrec_byte_length", Connection_getcompat_diagrec_byte_length, Connection_setcompat_diagrec_byte_length,
|
|
1573
|
+
"If True, the driver reports byte length instead of character length in SQLGetDiagRecW().", 0 },
|
|
1397
1574
|
{ 0 }
|
|
1398
1575
|
};
|
|
1399
1576
|
|
|
@@ -39,9 +39,18 @@ struct Connection
|
|
|
39
39
|
// to insert NULLs into binary columns.
|
|
40
40
|
bool supports_describeparam;
|
|
41
41
|
|
|
42
|
+
// Set to true if the driver doesn't handle SQL_NUMERIC_STRUCT properly.
|
|
43
|
+
bool fetch_decimal_as_string;
|
|
44
|
+
|
|
42
45
|
// The column size of datetime columns, obtained from SQLGetInfo(), used to determine the datetime precision.
|
|
43
46
|
int datetime_precision;
|
|
44
47
|
|
|
48
|
+
// Initial buffer allocation size for ReadVarColumn
|
|
49
|
+
// Any integer greater than 0 overrides the default value of 4096
|
|
50
|
+
// < 1 means use columnSize * cbElement + cbNullTeminator from the column descriptor,
|
|
51
|
+
// up to a ceiling of 32 MB. See https://github.com/mkleehammer/pyodbc/issues/1071.
|
|
52
|
+
Py_ssize_t readvar_initsize;
|
|
53
|
+
|
|
45
54
|
// The connection timeout in seconds.
|
|
46
55
|
long timeout;
|
|
47
56
|
|
|
@@ -87,6 +96,11 @@ struct Connection
|
|
|
87
96
|
|
|
88
97
|
bool need_long_data_len;
|
|
89
98
|
|
|
99
|
+
// Flag for drivers which report the number of bytes in the returned diagnostic message
|
|
100
|
+
// from a call to SQLGetDiagRecW() rather than the number of characters, as required
|
|
101
|
+
// by ODBC. See https://github.com/mkleehammer/pyodbc/issues/489.
|
|
102
|
+
bool compat_diagrec_byte_length;
|
|
103
|
+
|
|
90
104
|
PyObject* map_sqltype_to_converter;
|
|
91
105
|
// If converters are defined, this will be a dictionary mapping from the SQLTYPE cast to an
|
|
92
106
|
// int (because types can be negative) to the converter function.
|
|
@@ -104,7 +118,7 @@ struct Connection
|
|
|
104
118
|
* exception is set and zero is returned.
|
|
105
119
|
*/
|
|
106
120
|
PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, long timeout, bool fReadOnly,
|
|
107
|
-
PyObject* attrs_before, PyObject* encoding);
|
|
121
|
+
PyObject* attrs_before, PyObject* encoding, SQLUSMALLINT driver_completion);
|
|
108
122
|
|
|
109
123
|
/*
|
|
110
124
|
* Used by the Cursor to implement commit and rollback.
|