diff -r 08c215115842 Doc/includes/sqlite3/complete_statement.py --- a/Doc/includes/sqlite3/complete_statement.py Sun Dec 30 06:29:49 2012 -0800 +++ b/Doc/includes/sqlite3/complete_statement.py Sun Dec 30 18:24:38 2012 +0000 @@ -24,7 +24,9 @@ if buffer.lstrip().upper().startswith("SELECT"): print(cur.fetchall()) except sqlite3.Error as e: - print("An error occurred:", e.args[0]) + code = sqlite3.errorcode.get(e.sqlite_errorcode) + msg = e.args[0] + print("Error {} occurred: {}".format(code, msg)) buffer = "" con.close() diff -r 08c215115842 Doc/library/sqlite3.rst --- a/Doc/library/sqlite3.rst Sun Dec 30 06:29:49 2012 -0800 +++ b/Doc/library/sqlite3.rst Sun Dec 30 18:24:38 2012 +0000 @@ -213,6 +213,43 @@ again. +.. data:: errorcode + + Dictionary mapping SQLite error code values to their name in the `SQLite API + `_. For instance, + ``sqlite3.errorcode[sqlite3.SQLITE_OK]`` maps to ``'SQLITE_OK'``. Similar + to :data:`errno.errorcode`. + + The SQLite symbolic error codes are available as + ``sqlite3.errorcode.values()`` and as module attributes (e.g., + ``sqlite3.SQLITE_OK``, ``sqlite3.SQLITE_BUSY``, etc). + + .. note:: + + The set of symbolic error codes this module presents depends on the + SQLite library Python was built against: if the run-time SQLite library + is newer than the one Python was built against, SQLite calls might return + integer error codes this module doesn't know the symbolic names for. + +.. versionadded:: 3.4 + + +.. exception:: Error + + Raised to signal an error from the underlying SQLite library. The + following subtypes are available: + + .. literalinclude:: ../../Modules/_sqlite/exception_hierarchy.txt + + .. attribute:: sqlite_errorcode + + The numeric error code from the `SQLite API + `_. + See :data:`sqlite3.errorcode`. + + .. versionadded:: 3.4 + + .. _sqlite3-connection-objects: Connection Objects diff -r 08c215115842 Lib/sqlite3/test/dbapi.py --- a/Lib/sqlite3/test/dbapi.py Sun Dec 30 06:29:49 2012 -0800 +++ b/Lib/sqlite3/test/dbapi.py Sun Dec 30 18:24:38 2012 +0000 @@ -83,6 +83,20 @@ sqlite.DatabaseError), "NotSupportedError is not a subclass of DatabaseError") + def CheckErrorCodeMapping(self): + for errcode in {'SQLITE_OK', 'SQLITE_ROW', 'SQLITE_DONE', + 'SQLITE_PERM'}: + self.assertIsInstance(getattr(sqlite, errcode), int) + self.assertEqual(sqlite.errorcode[getattr(sqlite, errcode)], + errcode) + + def CheckErrorCodeOnExceptions(self): + with self.assertRaises(sqlite.Error) as cm: + db = sqlite.connect('/no/such/file/exists') + e = cm.exception + self.assertEqual(e.sqlite_errorcode, sqlite.SQLITE_CANTOPEN) + self.assertRegex(str(e), r"open|file") + class ConnectionTests(unittest.TestCase): def setUp(self): diff -r 08c215115842 Modules/_sqlite/exception_hierarchy.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_sqlite/exception_hierarchy.txt Sun Dec 30 18:24:38 2012 +0000 @@ -0,0 +1,11 @@ +builtins.Exception + +-- Warning + +-- Error + +-- InterfaceError + +-- DatabaseError + +-- InternalError + +-- OperationalError + +-- ProgrammingError + +-- IntegrityError + +-- DataError + +-- NotSupportedError diff -r 08c215115842 Modules/_sqlite/module.c --- a/Modules/_sqlite/module.c Sun Dec 30 06:29:49 2012 -0800 +++ b/Modules/_sqlite/module.c Sun Dec 30 18:24:38 2012 +0000 @@ -253,8 +253,9 @@ }; struct _IntConstantPair { - char* constant_name; + const char* constant_name; int constant_value; + int is_error_code; }; typedef struct _IntConstantPair IntConstantPair; @@ -263,9 +264,52 @@ {"PARSE_DECLTYPES", PARSE_DECLTYPES}, {"PARSE_COLNAMES", PARSE_COLNAMES}, - {"SQLITE_OK", SQLITE_OK}, + /* sqlite API error codes */ + {"SQLITE_OK", SQLITE_OK, 1}, + {"SQLITE_ERROR", SQLITE_ERROR, 1}, + {"SQLITE_INTERNAL", SQLITE_INTERNAL, 1}, + {"SQLITE_PERM", SQLITE_PERM, 1}, + {"SQLITE_ABORT", SQLITE_ABORT, 1}, + {"SQLITE_BUSY", SQLITE_BUSY, 1}, + {"SQLITE_LOCKED", SQLITE_LOCKED, 1}, + {"SQLITE_NOMEM", SQLITE_NOMEM, 1}, + {"SQLITE_READONLY", SQLITE_READONLY, 1}, + {"SQLITE_INTERRUPT", SQLITE_INTERRUPT, 1}, + {"SQLITE_IOERR", SQLITE_IOERR, 1}, + {"SQLITE_CORRUPT", SQLITE_CORRUPT, 1}, + {"SQLITE_NOTFOUND", SQLITE_NOTFOUND, 1}, + {"SQLITE_FULL", SQLITE_FULL, 1}, + {"SQLITE_CANTOPEN", SQLITE_CANTOPEN, 1}, + {"SQLITE_PROTOCOL", SQLITE_PROTOCOL, 1}, + {"SQLITE_EMPTY", SQLITE_EMPTY, 1}, + {"SQLITE_SCHEMA", SQLITE_SCHEMA, 1}, + {"SQLITE_TOOBIG", SQLITE_TOOBIG, 1}, + {"SQLITE_CONSTRAINT", SQLITE_CONSTRAINT, 1}, + {"SQLITE_MISMATCH", SQLITE_MISMATCH, 1}, + {"SQLITE_MISUSE", SQLITE_MISUSE, 1}, +#ifdef SQLITE_NOLFS + {"SQLITE_NOLFS", SQLITE_NOLFS, 1}, +#endif +#ifdef SQLITE_AUTH + {"SQLITE_AUTH", SQLITE_AUTH, 1}, +#endif +#ifdef SQLITE_FORMAT + {"SQLITE_FORMAT", SQLITE_FORMAT, 1}, +#endif +#ifdef SQLITE_RANGE + {"SQLITE_RANGE", SQLITE_RANGE, 1}, +#endif +#ifdef SQLITE_NOTADB + {"SQLITE_NOTADB", SQLITE_NOTADB, 1}, +#endif + {"SQLITE_DONE", SQLITE_DONE, 1}, + {"SQLITE_ROW", SQLITE_ROW, 1}, + + /* enumerated return values for sqlite3_set_authorizer() callback */ {"SQLITE_DENY", SQLITE_DENY}, {"SQLITE_IGNORE", SQLITE_IGNORE}, + + /* enumerated values for sqlite3_set_authorizer() callback */ {"SQLITE_CREATE_INDEX", SQLITE_CREATE_INDEX}, {"SQLITE_CREATE_TABLE", SQLITE_CREATE_TABLE}, {"SQLITE_CREATE_TEMP_INDEX", SQLITE_CREATE_TEMP_INDEX}, @@ -316,7 +360,7 @@ PyMODINIT_FUNC PyInit__sqlite3(void) { - PyObject *module, *dict; + PyObject *module, *dict, *errorcode; PyObject *tmp_obj; int i; @@ -415,14 +459,32 @@ Py_INCREF((PyObject*)&PyUnicode_Type); PyDict_SetItemString(dict, "OptimizedUnicode", (PyObject*)&PyUnicode_Type); + /* Prepare 'errorcode'. */ + errorcode = PyDict_New(); + if (!errorcode || PyDict_SetItemString(dict, "errorcode", errorcode) < 0) + goto error; + /* Set integer constants */ for (i = 0; _int_constants[i].constant_name != 0; i++) { + int sawerror; + PyObject *name = PyUnicode_FromString(_int_constants[i].constant_name); tmp_obj = PyLong_FromLong(_int_constants[i].constant_value); - if (!tmp_obj) { + if (!tmp_obj || !name) { + Py_XDECREF(name); + Py_XDECREF(tmp_obj); goto error; } - PyDict_SetItemString(dict, _int_constants[i].constant_name, tmp_obj); + + sawerror = PyDict_SetItem(dict, name, tmp_obj) < 0; + if (!sawerror) + if (_int_constants[i].is_error_code) + sawerror = PyDict_SetItem(errorcode, tmp_obj, name) < 0; + Py_DECREF(tmp_obj); + Py_DECREF(name); + + if (sawerror) + goto error; } if (!(tmp_obj = PyUnicode_FromString(PYSQLITE_VERSION))) { diff -r 08c215115842 Modules/_sqlite/util.c --- a/Modules/_sqlite/util.c Sun Dec 30 06:29:49 2012 -0800 +++ b/Modules/_sqlite/util.c Sun Dec 30 18:24:38 2012 +0000 @@ -47,6 +47,7 @@ */ int _pysqlite_seterror(sqlite3* db, sqlite3_stmt* st) { + PyObject *exc_class; int errorcode; /* SQLite often doesn't report anything useful, unless you reset the statement first */ @@ -60,14 +61,14 @@ { case SQLITE_OK: PyErr_Clear(); - break; + return errorcode; case SQLITE_INTERNAL: case SQLITE_NOTFOUND: - PyErr_SetString(pysqlite_InternalError, sqlite3_errmsg(db)); + exc_class = pysqlite_InternalError; break; case SQLITE_NOMEM: (void)PyErr_NoMemory(); - break; + return errorcode; case SQLITE_ERROR: case SQLITE_PERM: case SQLITE_ABORT: @@ -81,26 +82,51 @@ case SQLITE_PROTOCOL: case SQLITE_EMPTY: case SQLITE_SCHEMA: - PyErr_SetString(pysqlite_OperationalError, sqlite3_errmsg(db)); + exc_class = pysqlite_OperationalError; break; case SQLITE_CORRUPT: - PyErr_SetString(pysqlite_DatabaseError, sqlite3_errmsg(db)); + exc_class = pysqlite_DatabaseError; break; case SQLITE_TOOBIG: - PyErr_SetString(pysqlite_DataError, sqlite3_errmsg(db)); + exc_class = pysqlite_DataError; break; case SQLITE_CONSTRAINT: case SQLITE_MISMATCH: - PyErr_SetString(pysqlite_IntegrityError, sqlite3_errmsg(db)); + exc_class = pysqlite_IntegrityError; break; case SQLITE_MISUSE: - PyErr_SetString(pysqlite_ProgrammingError, sqlite3_errmsg(db)); + exc_class = pysqlite_ProgrammingError; break; default: - PyErr_SetString(pysqlite_DatabaseError, sqlite3_errmsg(db)); + exc_class = pysqlite_DatabaseError; break; } + /* Create and set the exception. */ + { + const char *msg; + PyObject *exc = NULL; + PyObject *args = NULL; + + msg = sqlite3_errmsg(db); + + args = Py_BuildValue("(si)", msg, errorcode); + if (!args) goto error; + + exc = PyObject_Call(exc_class, args, NULL); + if (!exc) goto error; + + if (PyObject_SetAttrString(exc, "sqlite_errorcode", + PyTuple_GET_ITEM(args, 1)) < 0) + goto error; + + PyErr_SetObject((PyObject *) Py_TYPE(exc), exc); + + error: + Py_XDECREF(args); + Py_XDECREF(exc); + } + return errorcode; }