Skip to content

Commit 11ed09b

Browse files
committed
sqlite3 module expose sqlite error code and name in exceptions
1 parent 2c134c3 commit 11ed09b

File tree

6 files changed

+179
-16
lines changed

6 files changed

+179
-16
lines changed

Doc/includes/sqlite3/complete_statement.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@
2424
if buffer.lstrip().upper().startswith("SELECT"):
2525
print(cur.fetchall())
2626
except sqlite3.Error as e:
27-
print("An error occurred:", e.args[0])
27+
msg = str(e))
28+
error_code = e.sqlite_errorcode
29+
error_name = e.sqlite_name
30+
print(f"Error {error_name} [Errno {error_code}]: {msg}")
2831
buffer = ""
2932

3033
con.close()

Doc/library/sqlite3.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,26 @@ Module functions and constants
263263
disable the feature again.
264264

265265

266+
.. exception:: Error
267+
268+
Raised to signal an error from the underlying SQLite library.
269+
270+
.. attribute:: sqlite_errorcode
271+
272+
The numeric error code from the `SQLite API
273+
<http://www.sqlite.org/c3ref/c_abort.html>`_.
274+
275+
.. versionadded:: 3.7
276+
277+
.. attribute:: sqlite_errorname
278+
279+
The symbolic name of the numeric error code
280+
from the `SQLite API
281+
<http://www.sqlite.org/c3ref/c_abort.html>`_.
282+
283+
.. versionadded:: 3.7
284+
285+
266286
.. _sqlite3-connection-objects:
267287

268288
Connection Objects

Lib/sqlite3/test/dbapi.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ def CheckNotSupportedError(self):
8686
sqlite.DatabaseError),
8787
"NotSupportedError is not a subclass of DatabaseError")
8888

89+
def CheckErrorCodeOnException(self):
90+
with self.assertRaises(sqlite.Error) as cm:
91+
db = sqlite.connect('/no/such/file/exists')
92+
e = cm.exception
93+
self.assertEqual(e.sqlite_errorcode, sqlite.SQLITE_CANTOPEN)
94+
self.assertEqual(e.sqlite_errorname, "SQLITE_CANTOPEN")
95+
self.assertEqual(str(e), "unable to open database file")
96+
8997
class ConnectionTests(unittest.TestCase):
9098

9199
def setUp(self):

Modules/_sqlite/module.c

Lines changed: 90 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -264,13 +264,70 @@ struct _IntConstantPair {
264264

265265
typedef struct _IntConstantPair IntConstantPair;
266266

267+
/* sqlite API error codes */
268+
static const IntConstantPair _error_codes[] = {
269+
{"SQLITE_OK", SQLITE_OK},
270+
{"SQLITE_ERROR", SQLITE_ERROR},
271+
{"SQLITE_INTERNAL", SQLITE_INTERNAL},
272+
{"SQLITE_PERM", SQLITE_PERM},
273+
{"SQLITE_ABORT", SQLITE_ABORT},
274+
{"SQLITE_BUSY", SQLITE_BUSY},
275+
{"SQLITE_LOCKED", SQLITE_LOCKED},
276+
{"SQLITE_NOMEM", SQLITE_NOMEM},
277+
{"SQLITE_READONLY", SQLITE_READONLY},
278+
{"SQLITE_INTERRUPT", SQLITE_INTERRUPT},
279+
{"SQLITE_IOERR", SQLITE_IOERR},
280+
{"SQLITE_CORRUPT", SQLITE_CORRUPT},
281+
{"SQLITE_NOTFOUND", SQLITE_NOTFOUND},
282+
{"SQLITE_FULL", SQLITE_FULL},
283+
{"SQLITE_CANTOPEN", SQLITE_CANTOPEN},
284+
{"SQLITE_PROTOCOL", SQLITE_PROTOCOL},
285+
{"SQLITE_EMPTY", SQLITE_EMPTY},
286+
{"SQLITE_SCHEMA", SQLITE_SCHEMA},
287+
{"SQLITE_TOOBIG", SQLITE_TOOBIG},
288+
{"SQLITE_CONSTRAINT", SQLITE_CONSTRAINT},
289+
{"SQLITE_MISMATCH", SQLITE_MISMATCH},
290+
{"SQLITE_MISUSE", SQLITE_MISUSE},
291+
#ifdef SQLITE_NOLFS
292+
{"SQLITE_NOLFS", SQLITE_NOLFS},
293+
#endif
294+
#ifdef SQLITE_AUTH
295+
{"SQLITE_AUTH", SQLITE_AUTH},
296+
#endif
297+
#ifdef SQLITE_FORMAT
298+
{"SQLITE_FORMAT", SQLITE_FORMAT},
299+
#endif
300+
#ifdef SQLITE_RANGE
301+
{"SQLITE_RANGE", SQLITE_RANGE},
302+
#endif
303+
#ifdef SQLITE_NOTADB
304+
{"SQLITE_NOTADB", SQLITE_NOTADB},
305+
#endif
306+
{"SQLITE_DONE", SQLITE_DONE},
307+
{"SQLITE_ROW", SQLITE_ROW},
308+
{(char*)NULL, 0},
309+
{"SQLITE_UNKNOWN", -1}
310+
};
311+
312+
const char *sqlite3ErrName(int rc) {
313+
int i;
314+
for (i = 0; _error_codes[i].constant_name != 0; i++) {
315+
if (_error_codes[i].constant_value == rc)
316+
return _error_codes[i].constant_name;
317+
}
318+
// No error code matched.
319+
return _error_codes[i+1].constant_name;
320+
}
321+
267322
static const IntConstantPair _int_constants[] = {
268323
{"PARSE_DECLTYPES", PARSE_DECLTYPES},
269324
{"PARSE_COLNAMES", PARSE_COLNAMES},
270325

271-
{"SQLITE_OK", SQLITE_OK},
326+
/* enumerated return values for sqlite3_set_authorizer() callback */
272327
{"SQLITE_DENY", SQLITE_DENY},
273328
{"SQLITE_IGNORE", SQLITE_IGNORE},
329+
330+
/* enumerated values for sqlite3_set_authorizer() callback */
274331
{"SQLITE_CREATE_INDEX", SQLITE_CREATE_INDEX},
275332
{"SQLITE_CREATE_TABLE", SQLITE_CREATE_TABLE},
276333
{"SQLITE_CREATE_TEMP_INDEX", SQLITE_CREATE_TEMP_INDEX},
@@ -332,6 +389,29 @@ static struct PyModuleDef _sqlite3module = {
332389
NULL
333390
};
334391

392+
393+
static int add_to_dict(PyObject *dict, const char *key, int value)
394+
{
395+
int sawerror;
396+
PyObject *value_obj = PyLong_FromLong(value);
397+
PyObject *name = PyUnicode_FromString(key);
398+
399+
if (!value_obj || !name) {
400+
Py_XDECREF(name);
401+
Py_XDECREF(value_obj);
402+
return 1;
403+
}
404+
405+
sawerror = PyDict_SetItem(dict, name, value_obj) < 0;
406+
407+
Py_DECREF(value_obj);
408+
Py_DECREF(name);
409+
410+
if (sawerror)
411+
return 1;
412+
return 0;
413+
}
414+
335415
PyMODINIT_FUNC PyInit__sqlite3(void)
336416
{
337417
PyObject *module, *dict;
@@ -435,12 +515,16 @@ PyMODINIT_FUNC PyInit__sqlite3(void)
435515

436516
/* Set integer constants */
437517
for (i = 0; _int_constants[i].constant_name != NULL; i++) {
438-
tmp_obj = PyLong_FromLong(_int_constants[i].constant_value);
439-
if (!tmp_obj) {
518+
if (add_to_dict(dict, _int_constants[i].constant_name,
519+
_int_constants[i].constant_value) != 0)
520+
goto error;
521+
}
522+
523+
/* Set error constants */
524+
for (i = 0; _error_codes[i].constant_name != 0; i++) {
525+
if (add_to_dict(dict, _error_codes[i].constant_name,
526+
_error_codes[i].constant_value) != 0)
440527
goto error;
441-
}
442-
PyDict_SetItemString(dict, _int_constants[i].constant_name, tmp_obj);
443-
Py_DECREF(tmp_obj);
444528
}
445529

446530
if (!(tmp_obj = PyUnicode_FromString(PYSQLITE_VERSION))) {

Modules/_sqlite/module.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ extern PyObject* converters;
5151
extern int _enable_callback_tracebacks;
5252
extern int pysqlite_BaseTypeAdapted;
5353

54+
extern const char *sqlite3ErrName(int rc);
55+
5456
#define PARSE_DECLTYPES 1
5557
#define PARSE_COLNAMES 2
5658
#endif

Modules/_sqlite/util.c

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ int pysqlite_step(sqlite3_stmt* statement, pysqlite_Connection* connection)
4747
*/
4848
int _pysqlite_seterror(sqlite3* db, sqlite3_stmt* st)
4949
{
50+
PyObject *exc_class;
5051
int errorcode;
5152

5253
#if SQLITE_VERSION_NUMBER < 3003009
@@ -63,14 +64,14 @@ int _pysqlite_seterror(sqlite3* db, sqlite3_stmt* st)
6364
{
6465
case SQLITE_OK:
6566
PyErr_Clear();
66-
break;
67+
return errorcode;
6768
case SQLITE_INTERNAL:
6869
case SQLITE_NOTFOUND:
69-
PyErr_SetString(pysqlite_InternalError, sqlite3_errmsg(db));
70+
exc_class = pysqlite_InternalError;
7071
break;
7172
case SQLITE_NOMEM:
7273
(void)PyErr_NoMemory();
73-
break;
74+
return errorcode;
7475
case SQLITE_ERROR:
7576
case SQLITE_PERM:
7677
case SQLITE_ABORT:
@@ -84,26 +85,71 @@ int _pysqlite_seterror(sqlite3* db, sqlite3_stmt* st)
8485
case SQLITE_PROTOCOL:
8586
case SQLITE_EMPTY:
8687
case SQLITE_SCHEMA:
87-
PyErr_SetString(pysqlite_OperationalError, sqlite3_errmsg(db));
88+
exc_class = pysqlite_OperationalError;
8889
break;
8990
case SQLITE_CORRUPT:
90-
PyErr_SetString(pysqlite_DatabaseError, sqlite3_errmsg(db));
91+
exc_class = pysqlite_DatabaseError;
9192
break;
9293
case SQLITE_TOOBIG:
93-
PyErr_SetString(pysqlite_DataError, sqlite3_errmsg(db));
94+
exc_class = pysqlite_DataError;
9495
break;
9596
case SQLITE_CONSTRAINT:
9697
case SQLITE_MISMATCH:
97-
PyErr_SetString(pysqlite_IntegrityError, sqlite3_errmsg(db));
98+
exc_class = pysqlite_IntegrityError;
9899
break;
99100
case SQLITE_MISUSE:
100-
PyErr_SetString(pysqlite_ProgrammingError, sqlite3_errmsg(db));
101+
exc_class = pysqlite_ProgrammingError;
101102
break;
102103
default:
103-
PyErr_SetString(pysqlite_DatabaseError, sqlite3_errmsg(db));
104+
exc_class = pysqlite_DatabaseError;
104105
break;
105106
}
106107

108+
/* Create and set the exception. */
109+
{
110+
const char *error_msg;
111+
const char *error_name;
112+
PyObject *exc = NULL;
113+
PyObject *args = NULL;
114+
PyObject *py_code = NULL;
115+
PyObject *py_name = NULL;
116+
117+
error_name = sqlite3ErrName(errorcode);
118+
119+
error_msg = sqlite3_errmsg(db);
120+
121+
args = Py_BuildValue("(s)", error_msg);
122+
if (!args)
123+
goto error;
124+
125+
exc = PyObject_Call(exc_class, args, NULL);
126+
if (!exc)
127+
goto error;
128+
129+
py_code = Py_BuildValue("i", errorcode);
130+
if (!py_code)
131+
goto error;
132+
133+
if (PyObject_SetAttrString(exc, "sqlite_errorcode", py_code) < 0)
134+
goto error;
135+
136+
py_name = Py_BuildValue("s", error_name);
137+
if (!py_name)
138+
goto error;
139+
140+
if (PyObject_SetAttrString(exc, "sqlite_errorname", py_name) < 0)
141+
goto error;
142+
143+
PyErr_SetObject((PyObject *) Py_TYPE(exc), exc);
144+
145+
error:
146+
Py_XDECREF(py_code);
147+
Py_XDECREF(py_name);
148+
Py_XDECREF(args);
149+
Py_XDECREF(exc);
150+
}
151+
152+
107153
return errorcode;
108154
}
109155

0 commit comments

Comments
 (0)