summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorErlend Egeberg Aasland <erlend.aasland@innova.no>2021-08-30 18:32:21 (GMT)
committerGitHub <noreply@github.com>2021-08-30 18:32:21 (GMT)
commit86d8b465231473f850cc5e906013ba8581ddb503 (patch)
tree38821067898cf1fb5fec7b0102e08776ce9df3fe /Modules
parentf62763d26755260c31c717fb396550e00eb6b2a0 (diff)
downloadcpython-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.c75
-rw-r--r--Modules/_sqlite/module.h2
-rw-r--r--Modules/_sqlite/util.c105
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;
}