Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 321 lines (252 sloc) 8.95 kb
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
1
2 #include "pyodbc.h"
3 #include "errors.h"
4 #include "pyodbcmodule.h"
5
6 // Exceptions
7
8 struct SqlStateMapping
9 {
10 char* prefix;
3c7a9e7 @mkleehammer Cleaned up compiler warnings
authored
11 size_t prefix_len;
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
12 PyObject** pexc_class; // Note: Double indirection (pexc_class) necessary because the pointer values are not
13 // initialized during startup
14 };
15
16 static const struct SqlStateMapping sql_state_mapping[] =
17 {
18 { "0A000", 5, &NotSupportedError },
19 { "40002", 5, &IntegrityError },
20 { "22", 2, &DataError },
21 { "23", 2, &IntegrityError },
22 { "24", 2, &ProgrammingError },
23 { "25", 2, &ProgrammingError },
24 { "42", 2, &ProgrammingError },
25 { "HYT00", 5, &OperationalError },
26 { "HYT01", 5, &OperationalError },
27 };
28
29
b2dfac9 @mkleehammer Issue 178: Fixed compilation issues with 4.6.2
authored
30 static PyObject* ExceptionFromSqlState(const char* sqlstate)
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
31 {
32 // Returns the appropriate Python exception class given a SQLSTATE value.
33
34 if (sqlstate && *sqlstate)
35 {
36 for (size_t i = 0; i < _countof(sql_state_mapping); i++)
37 if (memcmp(sqlstate, sql_state_mapping[i].prefix, sql_state_mapping[i].prefix_len) == 0)
38 return *sql_state_mapping[i].pexc_class;
39 }
40
41 return Error;
42 }
43
b2dfac9 @mkleehammer Issue 178: Fixed compilation issues with 4.6.2
authored
44
45 PyObject* RaiseErrorV(const char* sqlstate, PyObject* exc_class, const char* format, ...)
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
46 {
47 PyObject *pAttrs = 0, *pError = 0;
48
49 if (!sqlstate || !*sqlstate)
50 sqlstate = "HY000";
51
52 if (!exc_class)
53 exc_class = ExceptionFromSqlState(sqlstate);
54
55 // Note: Don't use any native strprintf routines. With Py_ssize_t, we need "%zd", but VC .NET doesn't support it.
56 // PyString_FromFormatV already takes this into account.
57
58 va_list marker;
59 va_start(marker, format);
60 PyObject* pMsg = PyString_FromFormatV(format, marker);
61 va_end(marker);
62 if (!pMsg)
63 {
64 PyErr_NoMemory();
65 return 0;
66 }
67
68 // Create an exception with a 'sqlstate' attribute (set to None if we don't have one) whose 'args' attribute is a
69 // tuple containing the message and sqlstate value. The 'sqlstate' attribute ensures it is easy to access in
70 // Python (and more understandable to the reader than ex.args[1]), but putting it in the args ensures it shows up
71 // in logs because of the default repr/str.
72
73 pAttrs = Py_BuildValue("(Os)", pMsg, sqlstate);
74 if (pAttrs)
75 {
76 pError = PyEval_CallObject(exc_class, pAttrs);
77 if (pError)
78 RaiseErrorFromException(pError);
79 }
b2dfac9 @mkleehammer Issue 178: Fixed compilation issues with 4.6.2
authored
80
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
81 Py_DECREF(pMsg);
82 Py_XDECREF(pAttrs);
83 Py_XDECREF(pError);
84
85 return 0;
86 }
87
b2dfac9 @mkleehammer Issue 178: Fixed compilation issues with 4.6.2
authored
88
c278e74 @mkleehammer Single source base for Python 2 and 3
authored
89 #if PY_MAJOR_VERSION < 3
90 #define PyString_CompareWithASCIIString(lhs, rhs) _strcmpi(PyString_AS_STRING(lhs), rhs)
91 #else
92 #define PyString_CompareWithASCIIString PyUnicode_CompareWithASCIIString
93 #endif
94
b2dfac9 @mkleehammer Issue 178: Fixed compilation issues with 4.6.2
authored
95
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
96 bool HasSqlState(PyObject* ex, const char* szSqlState)
97 {
98 // Returns true if `ex` is an exception and has the given SQLSTATE. It is safe to pass 0 for ex.
99
100 bool has = false;
101
102 if (ex)
103 {
104 PyObject* args = PyObject_GetAttrString(ex, "args");
105 if (args != 0)
106 {
107 PyObject* s = PySequence_GetItem(args, 1);
108 if (s != 0 && PyString_Check(s))
109 {
c278e74 @mkleehammer Single source base for Python 2 and 3
authored
110 // const char* sz = PyString_AsString(s);
111 // if (sz && _strcmpi(sz, szSqlState) == 0)
112 // has = true;
113 has = (PyString_CompareWithASCIIString(s, szSqlState) == 0);
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
114 }
115 Py_XDECREF(s);
116 Py_DECREF(args);
117 }
118 }
119
120 return has;
121 }
122
123
124 static PyObject* GetError(const char* sqlstate, PyObject* exc_class, PyObject* pMsg)
125 {
126 // pMsg
127 // The error message. This function takes ownership of this object, so we'll free it if we fail to create an
128 // error.
129
130 PyObject *pSqlState=0, *pAttrs=0, *pError=0;
131
132 if (!sqlstate || !*sqlstate)
133 sqlstate = "HY000";
134
135 if (!exc_class)
136 exc_class = ExceptionFromSqlState(sqlstate);
137
138 pAttrs = PyTuple_New(2);
139 if (!pAttrs)
140 {
141 Py_DECREF(pMsg);
142 return 0;
143 }
b2dfac9 @mkleehammer Issue 178: Fixed compilation issues with 4.6.2
authored
144
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
145 PyTuple_SetItem(pAttrs, 1, pMsg); // pAttrs now owns the pMsg reference; steals a reference, does not increment
146
147 pSqlState = PyString_FromString(sqlstate);
148 if (!pSqlState)
149 {
150 Py_DECREF(pAttrs);
151 return 0;
152 }
b2dfac9 @mkleehammer Issue 178: Fixed compilation issues with 4.6.2
authored
153
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
154 PyTuple_SetItem(pAttrs, 0, pSqlState); // pAttrs now owns the pSqlState reference
155
156 pError = PyEval_CallObject(exc_class, pAttrs); // pError will incref pAttrs
157
158 Py_XDECREF(pAttrs);
159
160 return pError;
161 }
162
b2dfac9 @mkleehammer Issue 178: Fixed compilation issues with 4.6.2
authored
163
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
164 static const char* DEFAULT_ERROR = "The driver did not supply an error!";
165
166 PyObject* RaiseErrorFromHandle(const char* szFunction, HDBC hdbc, HSTMT hstmt)
167 {
168 // The exception is "set" in the interpreter. This function returns 0 so this can be used in a return statement.
169
170 PyObject* pError = GetErrorFromHandle(szFunction, hdbc, hstmt);
171
172 if (pError)
173 {
174 RaiseErrorFromException(pError);
175 Py_DECREF(pError);
176 }
b2dfac9 @mkleehammer Issue 178: Fixed compilation issues with 4.6.2
authored
177
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
178 return 0;
179 }
180
b2dfac9 @mkleehammer Issue 178: Fixed compilation issues with 4.6.2
authored
181
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
182 PyObject* GetErrorFromHandle(const char* szFunction, HDBC hdbc, HSTMT hstmt)
183 {
691b6ba @mkleehammer Reworked Unicode; changed version format; more Py_ssize_t
authored
184 TRACE("In RaiseError(%s)!\n", szFunction);
185
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
186 // Creates and returns an exception from ODBC error information.
b2dfac9 @mkleehammer Issue 178: Fixed compilation issues with 4.6.2
authored
187 //
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
188 // ODBC can generate a chain of errors which we concatenate into one error message. We use the SQLSTATE from the
189 // first message, which seems to be the most detailed, to determine the class of exception.
190 //
191 // If the function fails, for example, if it runs out of memory, zero is returned.
192 //
193 // szFunction
194 // The name of the function that failed. Python generates a useful stack trace, but we often don't know where in
195 // the C++ code we failed.
196
197 SQLSMALLINT nHandleType;
198 SQLHANDLE h;
199
200 char sqlstate[6] = "";
201 SQLINTEGER nNativeError;
202 SQLSMALLINT cchMsg;
203
204 char sqlstateT[6];
205 char szMsg[1024];
206
207 PyObject* pMsg = 0;
208 PyObject* pMsgPart = 0;
209
210 if (hstmt != SQL_NULL_HANDLE)
211 {
212 nHandleType = SQL_HANDLE_STMT;
213 h = hstmt;
214 }
215 else if (hdbc != SQL_NULL_HANDLE)
216 {
217 nHandleType = SQL_HANDLE_DBC;
218 h = hdbc;
219 }
220 else
221 {
222 nHandleType = SQL_HANDLE_ENV;
223 h = henv;
224 }
225
226 // unixODBC + PostgreSQL driver 07.01.0003 (Fedora 8 binaries from RPMs) crash if you call SQLGetDiagRec more
227 // than once. I hate to do this, but I'm going to only call it once for non-Windows platforms for now...
228
229 SQLSMALLINT iRecord = 1;
230
231 for (;;)
232 {
233 szMsg[0] = 0;
234 sqlstateT[0] = 0;
235 nNativeError = 0;
236 cchMsg = 0;
237
238 SQLRETURN ret;
239 Py_BEGIN_ALLOW_THREADS
240 ret = SQLGetDiagRec(nHandleType, h, iRecord, (SQLCHAR*)sqlstateT, &nNativeError, (SQLCHAR*)szMsg, (short)(_countof(szMsg)-1), &cchMsg);
241 Py_END_ALLOW_THREADS
242 if (!SQL_SUCCEEDED(ret))
243 break;
244
245 // Not always NULL terminated (MS Access)
246 sqlstateT[5] = 0;
247
248 if (cchMsg != 0)
249 {
250 if (iRecord == 1)
251 {
252 // This is the first error message, so save the SQLSTATE for determining the exception class and append
253 // the calling function name.
254
255 memcpy(sqlstate, sqlstateT, sizeof(sqlstate[0]) * _countof(sqlstate));
b2dfac9 @mkleehammer Issue 178: Fixed compilation issues with 4.6.2
authored
256
f85004f UCS4 fixes; printf fixes;
Michael Kleehammer authored
257 pMsg = PyString_FromFormat("[%s] %s (%ld) (%s)", sqlstateT, szMsg, (long)nNativeError, szFunction);
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
258 if (pMsg == 0)
259 return 0;
260 }
261 else
262 {
263 // This is not the first error message, so append to the existing one.
f85004f UCS4 fixes; printf fixes;
Michael Kleehammer authored
264 pMsgPart = PyString_FromFormat("; [%s] %s (%ld)", sqlstateT, szMsg, (long)nNativeError);
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
265 if (pMsgPart == 0)
266 {
267 Py_XDECREF(pMsg);
268 return 0;
269 }
270 PyString_ConcatAndDel(&pMsg, pMsgPart);
271 }
272 }
273
274 iRecord++;
275
276 #ifndef _MSC_VER
277 // See non-Windows comment above
278 break;
279 #endif
280 }
281
282 if (pMsg == 0)
283 {
284 // This only happens using unixODBC. (Haven't tried iODBC yet.) Either the driver or the driver manager is
285 // buggy and has signaled a fault without recording error information.
286 sqlstate[0] = '\0';
287 pMsg = PyString_FromString(DEFAULT_ERROR);
288 if (pMsg == 0)
289 {
290 PyErr_NoMemory();
291 return 0;
292 }
293 }
294
295 return GetError(sqlstate, 0, pMsg);
296 }
297
b2dfac9 @mkleehammer Issue 178: Fixed compilation issues with 4.6.2
authored
298
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
299 static bool GetSqlState(HSTMT hstmt, char* szSqlState)
300 {
301 SQLCHAR szMsg[300];
302 SQLSMALLINT cbMsg = (SQLSMALLINT)(_countof(szMsg) - 1);
303 SQLINTEGER nNative;
304 SQLSMALLINT cchMsg;
305 SQLRETURN ret;
306
307 Py_BEGIN_ALLOW_THREADS
308 ret = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1, (SQLCHAR*)szSqlState, &nNative, szMsg, cbMsg, &cchMsg);
309 Py_END_ALLOW_THREADS
310 return SQL_SUCCEEDED(ret);
311 }
312
b2dfac9 @mkleehammer Issue 178: Fixed compilation issues with 4.6.2
authored
313
4ba7b00 @mkleehammer Applied patch from patrik.simons which adds a timeout attribute.
authored
314 bool HasSqlState(HSTMT hstmt, const char* szSqlState)
315 {
316 char szActual[6];
317 if (!GetSqlState(hstmt, szActual))
318 return false;
319 return memcmp(szActual, szSqlState, 5) == 0;
320 }
Something went wrong with that request. Please try again.