diff options
author | Erlend Egeberg Aasland <erlend.aasland@innova.no> | 2021-08-30 18:32:21 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-30 18:32:21 (GMT) |
commit | 86d8b465231473f850cc5e906013ba8581ddb503 (patch) | |
tree | 38821067898cf1fb5fec7b0102e08776ce9df3fe /Modules | |
parent | f62763d26755260c31c717fb396550e00eb6b2a0 (diff) | |
download | cpython-86d8b465231473f850cc5e906013ba8581ddb503.zip cpython-86d8b465231473f850cc5e906013ba8581ddb503.tar.gz cpython-86d8b465231473f850cc5e906013ba8581ddb503.tar.bz2 |
bpo-16379: expose SQLite error codes and error names in `sqlite3` (GH-27786)
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_sqlite/module.c | 75 | ||||
-rw-r--r-- | Modules/_sqlite/module.h | 2 | ||||
-rw-r--r-- | Modules/_sqlite/util.c | 105 |
3 files changed, 153 insertions, 29 deletions
diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 993e572..47b1f7a 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -282,12 +282,79 @@ static PyMethodDef module_methods[] = { {NULL, NULL} }; +/* SQLite API error codes */ +static const struct { + const char *name; + long value; +} error_codes[] = { +#define DECLARE_ERROR_CODE(code) {#code, code} + // Primary result code list + DECLARE_ERROR_CODE(SQLITE_ABORT), + DECLARE_ERROR_CODE(SQLITE_AUTH), + DECLARE_ERROR_CODE(SQLITE_BUSY), + DECLARE_ERROR_CODE(SQLITE_CANTOPEN), + DECLARE_ERROR_CODE(SQLITE_CONSTRAINT), + DECLARE_ERROR_CODE(SQLITE_CORRUPT), + DECLARE_ERROR_CODE(SQLITE_DONE), + DECLARE_ERROR_CODE(SQLITE_EMPTY), + DECLARE_ERROR_CODE(SQLITE_ERROR), + DECLARE_ERROR_CODE(SQLITE_FORMAT), + DECLARE_ERROR_CODE(SQLITE_FULL), + DECLARE_ERROR_CODE(SQLITE_INTERNAL), + DECLARE_ERROR_CODE(SQLITE_INTERRUPT), + DECLARE_ERROR_CODE(SQLITE_IOERR), + DECLARE_ERROR_CODE(SQLITE_LOCKED), + DECLARE_ERROR_CODE(SQLITE_MISMATCH), + DECLARE_ERROR_CODE(SQLITE_MISUSE), + DECLARE_ERROR_CODE(SQLITE_NOLFS), + DECLARE_ERROR_CODE(SQLITE_NOMEM), + DECLARE_ERROR_CODE(SQLITE_NOTADB), + DECLARE_ERROR_CODE(SQLITE_NOTFOUND), + DECLARE_ERROR_CODE(SQLITE_OK), + DECLARE_ERROR_CODE(SQLITE_PERM), + DECLARE_ERROR_CODE(SQLITE_PROTOCOL), + DECLARE_ERROR_CODE(SQLITE_READONLY), + DECLARE_ERROR_CODE(SQLITE_ROW), + DECLARE_ERROR_CODE(SQLITE_SCHEMA), + DECLARE_ERROR_CODE(SQLITE_TOOBIG), +#if SQLITE_VERSION_NUMBER >= 3007017 + DECLARE_ERROR_CODE(SQLITE_NOTICE), + DECLARE_ERROR_CODE(SQLITE_WARNING), +#endif +#undef DECLARE_ERROR_CODE + {NULL, 0}, +}; + +static int +add_error_constants(PyObject *module) +{ + for (int i = 0; error_codes[i].name != NULL; i++) { + const char *name = error_codes[i].name; + const long value = error_codes[i].value; + if (PyModule_AddIntConstant(module, name, value) < 0) { + return -1; + } + } + return 0; +} + +const char * +pysqlite_error_name(int rc) +{ + for (int i = 0; error_codes[i].name != NULL; i++) { + if (error_codes[i].value == rc) { + return error_codes[i].name; + } + } + // No error code matched. + return NULL; +} + static int add_integer_constants(PyObject *module) { int ret = 0; ret += PyModule_AddIntMacro(module, PARSE_DECLTYPES); ret += PyModule_AddIntMacro(module, PARSE_COLNAMES); - ret += PyModule_AddIntMacro(module, SQLITE_OK); ret += PyModule_AddIntMacro(module, SQLITE_DENY); ret += PyModule_AddIntMacro(module, SQLITE_IGNORE); ret += PyModule_AddIntMacro(module, SQLITE_CREATE_INDEX); @@ -325,7 +392,6 @@ static int add_integer_constants(PyObject *module) { #if SQLITE_VERSION_NUMBER >= 3008003 ret += PyModule_AddIntMacro(module, SQLITE_RECURSIVE); #endif - ret += PyModule_AddIntMacro(module, SQLITE_DONE); return ret; } @@ -406,6 +472,11 @@ PyMODINIT_FUNC PyInit__sqlite3(void) ADD_EXCEPTION(module, state, DataError, state->DatabaseError); ADD_EXCEPTION(module, state, NotSupportedError, state->DatabaseError); + /* Set error constants */ + if (add_error_constants(module) < 0) { + goto error; + } + /* Set integer constants */ if (add_integer_constants(module) < 0) { goto error; diff --git a/Modules/_sqlite/module.h b/Modules/_sqlite/module.h index a286739..c273c1f 100644 --- a/Modules/_sqlite/module.h +++ b/Modules/_sqlite/module.h @@ -81,6 +81,8 @@ pysqlite_get_state_by_type(PyTypeObject *Py_UNUSED(tp)) return &pysqlite_global_state; } +extern const char *pysqlite_error_name(int rc); + #define PARSE_DECLTYPES 1 #define PARSE_COLNAMES 2 #endif diff --git a/Modules/_sqlite/util.c b/Modules/_sqlite/util.c index 24cefc6..cfd189d 100644 --- a/Modules/_sqlite/util.c +++ b/Modules/_sqlite/util.c @@ -36,27 +36,19 @@ pysqlite_step(sqlite3_stmt *statement) return rc; } -/** - * Checks the SQLite error code and sets the appropriate DB-API exception. - * Returns the error code (0 means no error occurred). - */ -int -_pysqlite_seterror(pysqlite_state *state, sqlite3 *db) +// Returns non-NULL if a new exception should be raised +static PyObject * +get_exception_class(pysqlite_state *state, int errorcode) { - int errorcode = sqlite3_errcode(db); - - switch (errorcode) - { + switch (errorcode) { case SQLITE_OK: PyErr_Clear(); - break; + return NULL; case SQLITE_INTERNAL: case SQLITE_NOTFOUND: - PyErr_SetString(state->InternalError, sqlite3_errmsg(db)); - break; + return state->InternalError; case SQLITE_NOMEM: - (void)PyErr_NoMemory(); - break; + return PyErr_NoMemory(); case SQLITE_ERROR: case SQLITE_PERM: case SQLITE_ABORT: @@ -70,26 +62,85 @@ _pysqlite_seterror(pysqlite_state *state, sqlite3 *db) case SQLITE_PROTOCOL: case SQLITE_EMPTY: case SQLITE_SCHEMA: - PyErr_SetString(state->OperationalError, sqlite3_errmsg(db)); - break; + return state->OperationalError; case SQLITE_CORRUPT: - PyErr_SetString(state->DatabaseError, sqlite3_errmsg(db)); - break; + return state->DatabaseError; case SQLITE_TOOBIG: - PyErr_SetString(state->DataError, sqlite3_errmsg(db)); - break; + return state->DataError; case SQLITE_CONSTRAINT: case SQLITE_MISMATCH: - PyErr_SetString(state->IntegrityError, sqlite3_errmsg(db)); - break; + return state->IntegrityError; case SQLITE_MISUSE: - PyErr_SetString(state->ProgrammingError, sqlite3_errmsg(db)); - break; + return state->ProgrammingError; default: - PyErr_SetString(state->DatabaseError, sqlite3_errmsg(db)); - break; + return state->DatabaseError; + } +} + +static void +raise_exception(PyObject *type, int errcode, const char *errmsg) +{ + PyObject *exc = NULL; + PyObject *args[] = { PyUnicode_FromString(errmsg), }; + if (args[0] == NULL) { + goto exit; + } + exc = PyObject_Vectorcall(type, args, 1, NULL); + Py_DECREF(args[0]); + if (exc == NULL) { + goto exit; + } + + PyObject *code = PyLong_FromLong(errcode); + if (code == NULL) { + goto exit; + } + int rc = PyObject_SetAttrString(exc, "sqlite_errorcode", code); + Py_DECREF(code); + if (rc < 0) { + goto exit; + } + + const char *error_name = pysqlite_error_name(errcode); + PyObject *name; + if (error_name) { + name = PyUnicode_FromString(error_name); + } + else { + name = PyUnicode_InternFromString("unknown"); + } + if (name == NULL) { + goto exit; + } + rc = PyObject_SetAttrString(exc, "sqlite_errorname", name); + Py_DECREF(name); + if (rc < 0) { + goto exit; + } + + PyErr_SetObject(type, exc); + +exit: + Py_XDECREF(exc); +} + +/** + * Checks the SQLite error code and sets the appropriate DB-API exception. + * Returns the error code (0 means no error occurred). + */ +int +_pysqlite_seterror(pysqlite_state *state, sqlite3 *db) +{ + int errorcode = sqlite3_errcode(db); + PyObject *exc_class = get_exception_class(state, errorcode); + if (exc_class == NULL) { + // No new exception need be raised; just pass the error code + return errorcode; } + /* Create and set the exception. */ + const char *errmsg = sqlite3_errmsg(db); + raise_exception(exc_class, errorcode, errmsg); return errorcode; } |