From a50e28377bcf37121b55c2de70d95a5386c478f8 Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Wed, 23 Jun 2021 14:56:40 +0200 Subject: [PATCH] bpo-42064: Move `sqlite3` exceptions to global state, part 1 of 2 (GH-26745) Also adds a test to verify the (borrowed) exceptions in `sqlite3.Connection`. --- Lib/sqlite3/test/dbapi.py | 18 +++++++++++++++ Modules/_sqlite/connection.c | 11 +++++---- Modules/_sqlite/cursor.c | 6 +++-- Modules/_sqlite/module.c | 34 +++++++++++++++------------- Modules/_sqlite/module.h | 10 ++++---- Modules/_sqlite/statement.c | 18 +++++++++------ Modules/_sqlite/util.c | 7 +++--- Tools/c-analyzer/cpython/ignored.tsv | 10 -------- 8 files changed, 66 insertions(+), 48 deletions(-) diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py index c9bcac9bb83..7e44cac76f5 100644 --- a/Lib/sqlite3/test/dbapi.py +++ b/Lib/sqlite3/test/dbapi.py @@ -208,6 +208,24 @@ def test_in_transaction_ro(self): with self.assertRaises(AttributeError): self.cx.in_transaction = True + def test_connection_exceptions(self): + exceptions = [ + "DataError", + "DatabaseError", + "Error", + "IntegrityError", + "InterfaceError", + "NotSupportedError", + "OperationalError", + "ProgrammingError", + "Warning", + ] + for exc in exceptions: + with self.subTest(exc=exc): + self.assertTrue(hasattr(self.cx, exc)) + self.assertIs(getattr(sqlite, exc), getattr(self.cx, exc)) + + class OpenTests(unittest.TestCase): _sql = "create table test(id integer)" diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 11656b8a900..3e12679cd14 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -182,14 +182,15 @@ pysqlite_connection_init_impl(pysqlite_Connection *self, return -1; } - self->Warning = pysqlite_Warning; - self->Error = pysqlite_Error; - self->InterfaceError = pysqlite_InterfaceError; - self->DatabaseError = pysqlite_DatabaseError; + pysqlite_state *state = pysqlite_get_state(NULL); + self->Warning = state->Warning; + self->Error = state->Error; + self->InterfaceError = state->InterfaceError; + self->DatabaseError = state->DatabaseError; self->DataError = pysqlite_DataError; self->OperationalError = pysqlite_OperationalError; self->IntegrityError = pysqlite_IntegrityError; - self->InternalError = pysqlite_InternalError; + self->InternalError = state->InternalError; self->ProgrammingError = pysqlite_ProgrammingError; self->NotSupportedError = pysqlite_NotSupportedError; diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index 8e7d65faa28..451742b3ecd 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -272,7 +272,8 @@ _pysqlite_fetch_one_row(pysqlite_Cursor* self) PyObject* error_msg; if (self->reset) { - PyErr_SetString(pysqlite_InterfaceError, errmsg_fetch_across_rollback); + PyObject *exc = self->connection->InterfaceError; + PyErr_SetString(exc, errmsg_fetch_across_rollback); return NULL; } @@ -822,7 +823,8 @@ pysqlite_cursor_iternext(pysqlite_Cursor *self) } if (self->reset) { - PyErr_SetString(pysqlite_InterfaceError, errmsg_fetch_across_rollback); + PyObject *exc = self->connection->InterfaceError; + PyErr_SetString(exc, errmsg_fetch_across_rollback); return NULL; } diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 6adadf69216..2607c6ee3b4 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -43,11 +43,6 @@ module _sqlite3 /* static objects at module-level */ -PyObject *pysqlite_Error = NULL; -PyObject *pysqlite_Warning = NULL; -PyObject *pysqlite_InterfaceError = NULL; -PyObject *pysqlite_DatabaseError = NULL; -PyObject *pysqlite_InternalError = NULL; PyObject *pysqlite_OperationalError = NULL; PyObject *pysqlite_ProgrammingError = NULL; PyObject *pysqlite_IntegrityError = NULL; @@ -409,20 +404,27 @@ PyMODINIT_FUNC PyInit__sqlite3(void) ADD_TYPE(module, state->RowType); /*** Create DB-API Exception hierarchy */ - ADD_EXCEPTION(module, "Error", pysqlite_Error, PyExc_Exception); - ADD_EXCEPTION(module, "Warning", pysqlite_Warning, PyExc_Exception); + ADD_EXCEPTION(module, "Error", state->Error, PyExc_Exception); + ADD_EXCEPTION(module, "Warning", state->Warning, PyExc_Exception); /* Error subclasses */ - ADD_EXCEPTION(module, "InterfaceError", pysqlite_InterfaceError, pysqlite_Error); - ADD_EXCEPTION(module, "DatabaseError", pysqlite_DatabaseError, pysqlite_Error); + ADD_EXCEPTION(module, "InterfaceError", state->InterfaceError, + state->Error); + ADD_EXCEPTION(module, "DatabaseError", state->DatabaseError, state->Error); - /* pysqlite_DatabaseError subclasses */ - ADD_EXCEPTION(module, "InternalError", pysqlite_InternalError, pysqlite_DatabaseError); - ADD_EXCEPTION(module, "OperationalError", pysqlite_OperationalError, pysqlite_DatabaseError); - ADD_EXCEPTION(module, "ProgrammingError", pysqlite_ProgrammingError, pysqlite_DatabaseError); - ADD_EXCEPTION(module, "IntegrityError", pysqlite_IntegrityError, pysqlite_DatabaseError); - ADD_EXCEPTION(module, "DataError", pysqlite_DataError, pysqlite_DatabaseError); - ADD_EXCEPTION(module, "NotSupportedError", pysqlite_NotSupportedError, pysqlite_DatabaseError); + /* DatabaseError subclasses */ + ADD_EXCEPTION(module, "InternalError", state->InternalError, + state->DatabaseError); + ADD_EXCEPTION(module, "OperationalError", pysqlite_OperationalError, + state->DatabaseError); + ADD_EXCEPTION(module, "ProgrammingError", pysqlite_ProgrammingError, + state->DatabaseError); + ADD_EXCEPTION(module, "IntegrityError", pysqlite_IntegrityError, + state->DatabaseError); + ADD_EXCEPTION(module, "DataError", pysqlite_DataError, + state->DatabaseError); + ADD_EXCEPTION(module, "NotSupportedError", pysqlite_NotSupportedError, + state->DatabaseError); /* Set integer constants */ if (add_integer_constants(module) < 0) { diff --git a/Modules/_sqlite/module.h b/Modules/_sqlite/module.h index 3f29035b5fe..a745096a8ec 100644 --- a/Modules/_sqlite/module.h +++ b/Modules/_sqlite/module.h @@ -30,6 +30,11 @@ #define MODULE_NAME "sqlite3" typedef struct { + PyObject *DatabaseError; + PyObject *Error; + PyObject *InterfaceError; + PyObject *InternalError; + PyObject *Warning; PyObject *lru_cache; PyTypeObject *ConnectionType; PyTypeObject *CursorType; @@ -46,11 +51,6 @@ pysqlite_get_state(PyObject *Py_UNUSED(module)) return &pysqlite_global_state; } -extern PyObject* pysqlite_Error; -extern PyObject* pysqlite_Warning; -extern PyObject* pysqlite_InterfaceError; -extern PyObject* pysqlite_DatabaseError; -extern PyObject* pysqlite_InternalError; extern PyObject* pysqlite_OperationalError; extern PyObject* pysqlite_ProgrammingError; extern PyObject* pysqlite_IntegrityError; diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c index fcf13809763..035023dc009 100644 --- a/Modules/_sqlite/statement.c +++ b/Modules/_sqlite/statement.c @@ -51,12 +51,13 @@ typedef enum { pysqlite_Statement * pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql) { + pysqlite_state *state = pysqlite_get_state(NULL); assert(PyUnicode_Check(sql)); Py_ssize_t size; const char *sql_cstr = PyUnicode_AsUTF8AndSize(sql, &size); if (sql_cstr == NULL) { - PyErr_Format(pysqlite_Warning, - "SQL is of wrong type ('%s'). Must be string.", + PyObject *exc = connection->Warning; + PyErr_Format(exc, "SQL is of wrong type ('%s'). Must be string.", Py_TYPE(sql)->tp_name); return NULL; } @@ -86,8 +87,8 @@ pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql) } if (pysqlite_check_remaining_sql(tail)) { - PyErr_SetString(pysqlite_Warning, - "You can only execute one statement at a time."); + PyObject *exc = connection->Warning; + PyErr_SetString(exc, "You can only execute one statement at a time."); goto error; } @@ -110,7 +111,6 @@ pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql) break; } - pysqlite_state *state = pysqlite_get_state(NULL); pysqlite_Statement *self = PyObject_GC_New(pysqlite_Statement, state->StatementType); if (self == NULL) { @@ -288,7 +288,9 @@ void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* para if (rc != SQLITE_OK) { if (!PyErr_Occurred()) { - PyErr_Format(pysqlite_InterfaceError, "Error binding parameter %d - probably unsupported type.", i); + PyErr_Format(state->InterfaceError, + "Error binding parameter %d - " + "probably unsupported type.", i); } return; } @@ -342,7 +344,9 @@ void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* para if (rc != SQLITE_OK) { if (!PyErr_Occurred()) { - PyErr_Format(pysqlite_InterfaceError, "Error binding parameter :%s - probably unsupported type.", binding_name); + PyErr_Format(state->InterfaceError, + "Error binding parameter :%s - " + "probably unsupported type.", binding_name); } return; } diff --git a/Modules/_sqlite/util.c b/Modules/_sqlite/util.c index 2de819e9e85..2684773b6d3 100644 --- a/Modules/_sqlite/util.c +++ b/Modules/_sqlite/util.c @@ -43,6 +43,7 @@ pysqlite_step(sqlite3_stmt *statement) int _pysqlite_seterror(sqlite3 *db) { + pysqlite_state *state = pysqlite_get_state(NULL); int errorcode = sqlite3_errcode(db); switch (errorcode) @@ -52,7 +53,7 @@ _pysqlite_seterror(sqlite3 *db) break; case SQLITE_INTERNAL: case SQLITE_NOTFOUND: - PyErr_SetString(pysqlite_InternalError, sqlite3_errmsg(db)); + PyErr_SetString(state->InternalError, sqlite3_errmsg(db)); break; case SQLITE_NOMEM: (void)PyErr_NoMemory(); @@ -73,7 +74,7 @@ _pysqlite_seterror(sqlite3 *db) PyErr_SetString(pysqlite_OperationalError, sqlite3_errmsg(db)); break; case SQLITE_CORRUPT: - PyErr_SetString(pysqlite_DatabaseError, sqlite3_errmsg(db)); + PyErr_SetString(state->DatabaseError, sqlite3_errmsg(db)); break; case SQLITE_TOOBIG: PyErr_SetString(pysqlite_DataError, sqlite3_errmsg(db)); @@ -86,7 +87,7 @@ _pysqlite_seterror(sqlite3 *db) PyErr_SetString(pysqlite_ProgrammingError, sqlite3_errmsg(db)); break; default: - PyErr_SetString(pysqlite_DatabaseError, sqlite3_errmsg(db)); + PyErr_SetString(state->DatabaseError, sqlite3_errmsg(db)); break; } diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index d8407ed3745..b002703b352 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -49,11 +49,6 @@ Modules/_io/_iomodule.h - _PyIO_str_write - Modules/_io/_iomodule.h - _PyIO_empty_str - Modules/_io/_iomodule.h - _PyIO_empty_bytes - Modules/_multiprocessing/multiprocessing.h - _PyMp_SemLockType - -Modules/_sqlite/module.c - pysqlite_Error - -Modules/_sqlite/module.c - pysqlite_Warning - -Modules/_sqlite/module.c - pysqlite_InterfaceError - -Modules/_sqlite/module.c - pysqlite_DatabaseError - -Modules/_sqlite/module.c - pysqlite_InternalError - Modules/_sqlite/module.c - pysqlite_OperationalError - Modules/_sqlite/module.c - pysqlite_ProgrammingError - Modules/_sqlite/module.c - pysqlite_IntegrityError - @@ -2371,11 +2366,6 @@ Modules/_ctypes/_ctypes.c - PyExc_ArgError - Modules/_cursesmodule.c - PyCursesError - Modules/_decimal/_decimal.c - DecimalException - Modules/_queuemodule.c - EmptyError - -Modules/_sqlite/module.h - pysqlite_Error - -Modules/_sqlite/module.h - pysqlite_Warning - -Modules/_sqlite/module.h - pysqlite_InterfaceError - -Modules/_sqlite/module.h - pysqlite_DatabaseError - -Modules/_sqlite/module.h - pysqlite_InternalError - Modules/_sqlite/module.h - pysqlite_OperationalError - Modules/_sqlite/module.h - pysqlite_ProgrammingError - Modules/_sqlite/module.h - pysqlite_IntegrityError -