Skip to content

Commit

Permalink
pythongh-129928: Rework sqlite3 error helpers
Browse files Browse the repository at this point in the history
Add a private API for raising DB-API compatible exceptions based on the
result code of SQLite C APIs. Some APIs do not store the error indicator
on the database pointer, so we need to be able to deduce the DB-API
compatible exception directly from the error code.

- rename _pysqlite_seterror() as set_error_from_db()
- introduce set_error_from_code()
  • Loading branch information
erlend-aasland committed Feb 10, 2025
1 parent 7e6ee50 commit bd2a2a9
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 30 deletions.
2 changes: 1 addition & 1 deletion Modules/_sqlite/blob.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ static void
blob_seterror(pysqlite_Blob *self, int rc)
{
assert(self->connection != NULL);
_pysqlite_seterror(self->connection->state, self->connection->db);
set_error_from_db(self->connection->state, self->connection->db);
}

static PyObject *
Expand Down
32 changes: 19 additions & 13 deletions Modules/_sqlite/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ connection_exec_stmt(pysqlite_Connection *self, const char *sql)
Py_END_ALLOW_THREADS

if (rc != SQLITE_OK) {
(void)_pysqlite_seterror(self->state, self->db);
set_error_from_db(self->state, self->db);
return -1;
}
return 0;
Expand Down Expand Up @@ -274,7 +274,7 @@ pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database,

pysqlite_state *state = pysqlite_get_state_by_type(Py_TYPE(self));
if (rc != SQLITE_OK) {
_pysqlite_seterror(state, db);
set_error_from_db(state, db);
goto error;
}

Expand Down Expand Up @@ -607,11 +607,11 @@ blobopen_impl(pysqlite_Connection *self, const char *table, const char *col,
Py_END_ALLOW_THREADS

if (rc == SQLITE_MISUSE) {
PyErr_Format(self->state->InterfaceError, sqlite3_errstr(rc));
set_error_from_code(self->state, rc);
return NULL;
}
else if (rc != SQLITE_OK) {
_pysqlite_seterror(self->state, self->db);
set_error_from_db(self->state, self->db);
return NULL;
}

Expand Down Expand Up @@ -1307,6 +1307,12 @@ create_window_function_impl(pysqlite_Connection *self, PyTypeObject *cls,
"SQLite 3.25.0 or higher");
return NULL;
}
int limit = sqlite3_limit(self->db, SQLITE_LIMIT_FUNCTION_ARG, -1);
if (num_params < -1 || num_params > limit) {
return PyErr_Format(self->ProgrammingError,
"'num_params' must be between -1 and %d, not %d",
limit, num_params);
}

if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
return NULL;
Expand All @@ -1333,9 +1339,9 @@ create_window_function_impl(pysqlite_Connection *self, PyTypeObject *cls,
}

if (rc != SQLITE_OK) {
// Errors are not set on the database connection, so we cannot
// use _pysqlite_seterror().
PyErr_SetString(self->ProgrammingError, sqlite3_errstr(rc));
/* Errors are not set on the database connection; use result code
* instead. */
set_error_from_code(self->state, rc);
return NULL;
}
Py_RETURN_NONE;
Expand Down Expand Up @@ -2090,7 +2096,7 @@ pysqlite_connection_backup_impl(pysqlite_Connection *self,
Py_END_ALLOW_THREADS

if (bck_handle == NULL) {
_pysqlite_seterror(self->state, bck_conn);
set_error_from_db(self->state, bck_conn);
return NULL;
}

Expand Down Expand Up @@ -2128,7 +2134,7 @@ pysqlite_connection_backup_impl(pysqlite_Connection *self,
Py_END_ALLOW_THREADS

if (rc != SQLITE_OK) {
_pysqlite_seterror(self->state, bck_conn);
set_error_from_db(self->state, bck_conn);
return NULL;
}

Expand Down Expand Up @@ -2186,7 +2192,7 @@ pysqlite_connection_create_collation_impl(pysqlite_Connection *self,
if (callable != Py_None) {
free_callback_context(ctx);
}
_pysqlite_seterror(self->state, self->db);
set_error_from_db(self->state, self->db);
return NULL;
}

Expand Down Expand Up @@ -2304,7 +2310,7 @@ deserialize_impl(pysqlite_Connection *self, Py_buffer *data,
Py_END_ALLOW_THREADS

if (rc != SQLITE_OK) {
(void)_pysqlite_seterror(self->state, self->db);
set_error_from_db(self->state, self->db);
return NULL;
}
Py_RETURN_NONE;
Expand Down Expand Up @@ -2499,7 +2505,7 @@ setconfig_impl(pysqlite_Connection *self, int op, int enable)
int actual;
int rc = sqlite3_db_config(self->db, op, enable, &actual);
if (rc != SQLITE_OK) {
(void)_pysqlite_seterror(self->state, self->db);
set_error_from_db(self->state, self->db);
return NULL;
}
if (enable != actual) {
Expand Down Expand Up @@ -2534,7 +2540,7 @@ getconfig_impl(pysqlite_Connection *self, int op)
int current;
int rc = sqlite3_db_config(self->db, op, -1, &current);
if (rc != SQLITE_OK) {
(void)_pysqlite_seterror(self->state, self->db);
set_error_from_db(self->state, self->db);
return -1;
}
return current;
Expand Down
13 changes: 6 additions & 7 deletions Modules/_sqlite/cursor.c
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ begin_transaction(pysqlite_Connection *self)
Py_END_ALLOW_THREADS

if (rc != SQLITE_OK) {
(void)_pysqlite_seterror(self->state, self->db);
set_error_from_db(self->state, self->db);
return -1;
}

Expand Down Expand Up @@ -715,7 +715,7 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self,
if (rc != SQLITE_OK) {
PyObject *exc = PyErr_GetRaisedException();
sqlite3 *db = sqlite3_db_handle(self->st);
_pysqlite_seterror(state, db);
set_error_from_db(state, db);
_PyErr_ChainExceptions1(exc);
return;
}
Expand Down Expand Up @@ -764,7 +764,7 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self,
if (rc != SQLITE_OK) {
PyObject *exc = PyErr_GetRaisedException();
sqlite3 *db = sqlite3_db_handle(self->st);
_pysqlite_seterror(state, db);
set_error_from_db(state, db);
_PyErr_ChainExceptions1(exc);
return;
}
Expand Down Expand Up @@ -896,7 +896,7 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
PyErr_Clear();
}
}
_pysqlite_seterror(state, self->connection->db);
set_error_from_db(state, self->connection->db);
goto error;
}

Expand Down Expand Up @@ -1087,7 +1087,7 @@ pysqlite_cursor_executescript_impl(pysqlite_Cursor *self,
return Py_NewRef((PyObject *)self);

error:
_pysqlite_seterror(self->connection->state, db);
set_error_from_db(self->connection->state, db);
return NULL;
}

Expand Down Expand Up @@ -1122,8 +1122,7 @@ pysqlite_cursor_iternext(PyObject *op)
Py_CLEAR(self->statement);
}
else if (rc != SQLITE_ROW) {
(void)_pysqlite_seterror(self->connection->state,
self->connection->db);
set_error_from_db(self->connection->state, self->connection->db);
(void)stmt_reset(self->statement);
Py_CLEAR(self->statement);
Py_DECREF(row);
Expand Down
2 changes: 1 addition & 1 deletion Modules/_sqlite/statement.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql)
Py_END_ALLOW_THREADS

if (rc != SQLITE_OK) {
_pysqlite_seterror(state, db);
set_error_from_db(state, db);
return NULL;
}

Expand Down
24 changes: 18 additions & 6 deletions Modules/_sqlite/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,26 +118,38 @@ raise_exception(PyObject *type, int errcode, const char *errmsg)
Py_XDECREF(exc);
}

void
set_error_from_code(pysqlite_state *state, int code)
{
PyObject *exc_class = get_exception_class(state, code);
if (exc_class == NULL) {
// No new exception need be raised.
return;
}

const char *errmsg = sqlite3_errstr(code);
assert(errmsg != NULL);
raise_exception(exc_class, code, errmsg);
}

/**
* 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)
void
set_error_from_db(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;
// No new exception need be raised.
return;
}

/* Create and set the exception. */
int extended_errcode = sqlite3_extended_errcode(db);
// sqlite3_errmsg() always returns an UTF-8 encoded message
const char *errmsg = sqlite3_errmsg(db);
raise_exception(exc_class, extended_errcode, errmsg);
return extended_errcode;
}

#ifdef WORDS_BIGENDIAN
Expand Down
4 changes: 2 additions & 2 deletions Modules/_sqlite/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@

/**
* 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);
void set_error_from_db(pysqlite_state *state, sqlite3 *db);
void set_error_from_code(pysqlite_state *state, int code);

sqlite_int64 _pysqlite_long_as_int64(PyObject * value);

Expand Down

0 comments on commit bd2a2a9

Please sign in to comment.