Skip to content

Commit e824d3d

Browse files
gh-129928: Rework sqlite3 error helpers
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()
1 parent e6d299b commit e824d3d

File tree

7 files changed

+51
-33
lines changed

7 files changed

+51
-33
lines changed

Lib/test/test_sqlite3/test_dbapi.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -747,8 +747,9 @@ def test_wal_preservation(self):
747747

748748
def test_file_control_raises(self):
749749
with memory_database() as cx:
750-
with self.assertRaises(sqlite.ProgrammingError):
751-
cx.file_control(sqlite.SQLITE_FCNTL_PERSIST_WAL, 1)
750+
with self.assertRaises(sqlite.OperationalError):
751+
cx.file_control(sqlite.SQLITE_FCNTL_DATA_VERSION, 1,
752+
name="nosuchdatabase")
752753

753754

754755
class CursorTests(unittest.TestCase):

Modules/_sqlite/blob.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ static void
119119
blob_seterror(pysqlite_Blob *self, int rc)
120120
{
121121
assert(self->connection != NULL);
122-
_pysqlite_seterror(self->connection->state, self->connection->db);
122+
set_error_from_db(self->connection->state, self->connection->db);
123123
}
124124

125125
static PyObject *

Modules/_sqlite/connection.c

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ connection_exec_stmt(pysqlite_Connection *self, const char *sql)
188188
Py_END_ALLOW_THREADS
189189

190190
if (rc != SQLITE_OK) {
191-
(void)_pysqlite_seterror(self->state, self->db);
191+
set_error_from_db(self->state, self->db);
192192
return -1;
193193
}
194194
return 0;
@@ -274,7 +274,7 @@ pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database,
274274

275275
pysqlite_state *state = pysqlite_get_state_by_type(Py_TYPE(self));
276276
if (rc != SQLITE_OK) {
277-
_pysqlite_seterror(state, db);
277+
set_error_from_db(state, db);
278278
goto error;
279279
}
280280

@@ -607,11 +607,11 @@ blobopen_impl(pysqlite_Connection *self, const char *table, const char *col,
607607
Py_END_ALLOW_THREADS
608608

609609
if (rc == SQLITE_MISUSE) {
610-
PyErr_Format(self->state->InterfaceError, sqlite3_errstr(rc));
610+
set_error_from_code(self->state, rc);
611611
return NULL;
612612
}
613613
else if (rc != SQLITE_OK) {
614-
_pysqlite_seterror(self->state, self->db);
614+
set_error_from_db(self->state, self->db);
615615
return NULL;
616616
}
617617

@@ -1307,6 +1307,12 @@ create_window_function_impl(pysqlite_Connection *self, PyTypeObject *cls,
13071307
"SQLite 3.25.0 or higher");
13081308
return NULL;
13091309
}
1310+
int limit = sqlite3_limit(self->db, SQLITE_LIMIT_FUNCTION_ARG, -1);
1311+
if (num_params < -1 || num_params > limit) {
1312+
return PyErr_Format(self->ProgrammingError,
1313+
"'num_params' must be between -1 and %d, not %d",
1314+
limit, num_params);
1315+
}
13101316

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

13351341
if (rc != SQLITE_OK) {
1336-
// Errors are not set on the database connection, so we cannot
1337-
// use _pysqlite_seterror().
1338-
PyErr_SetString(self->ProgrammingError, sqlite3_errstr(rc));
1342+
/* Errors are not set on the database connection; use result code
1343+
* instead. */
1344+
set_error_from_code(self->state, rc);
13391345
return NULL;
13401346
}
13411347
Py_RETURN_NONE;
@@ -2090,7 +2096,7 @@ pysqlite_connection_backup_impl(pysqlite_Connection *self,
20902096
Py_END_ALLOW_THREADS
20912097

20922098
if (bck_handle == NULL) {
2093-
_pysqlite_seterror(self->state, bck_conn);
2099+
set_error_from_db(self->state, bck_conn);
20942100
return NULL;
20952101
}
20962102

@@ -2128,7 +2134,7 @@ pysqlite_connection_backup_impl(pysqlite_Connection *self,
21282134
Py_END_ALLOW_THREADS
21292135

21302136
if (rc != SQLITE_OK) {
2131-
_pysqlite_seterror(self->state, bck_conn);
2137+
set_error_from_db(self->state, bck_conn);
21322138
return NULL;
21332139
}
21342140

@@ -2186,7 +2192,7 @@ pysqlite_connection_create_collation_impl(pysqlite_Connection *self,
21862192
if (callable != Py_None) {
21872193
free_callback_context(ctx);
21882194
}
2189-
_pysqlite_seterror(self->state, self->db);
2195+
set_error_from_db(self->state, self->db);
21902196
return NULL;
21912197
}
21922198

@@ -2301,7 +2307,7 @@ pysqlite_connection_file_control_impl(pysqlite_Connection *self, int op,
23012307
Py_END_ALLOW_THREADS
23022308

23032309
if (rc != SQLITE_OK) {
2304-
PyErr_SetString(self->ProgrammingError, sqlite3_errstr(rc));
2310+
set_error_from_code(self->state, rc);
23052311
return NULL;
23062312
}
23072313

@@ -2420,7 +2426,7 @@ deserialize_impl(pysqlite_Connection *self, Py_buffer *data,
24202426
Py_END_ALLOW_THREADS
24212427

24222428
if (rc != SQLITE_OK) {
2423-
(void)_pysqlite_seterror(self->state, self->db);
2429+
set_error_from_db(self->state, self->db);
24242430
return NULL;
24252431
}
24262432
Py_RETURN_NONE;
@@ -2615,7 +2621,7 @@ setconfig_impl(pysqlite_Connection *self, int op, int enable)
26152621
int actual;
26162622
int rc = sqlite3_db_config(self->db, op, enable, &actual);
26172623
if (rc != SQLITE_OK) {
2618-
(void)_pysqlite_seterror(self->state, self->db);
2624+
set_error_from_db(self->state, self->db);
26192625
return NULL;
26202626
}
26212627
if (enable != actual) {
@@ -2650,7 +2656,7 @@ getconfig_impl(pysqlite_Connection *self, int op)
26502656
int current;
26512657
int rc = sqlite3_db_config(self->db, op, -1, &current);
26522658
if (rc != SQLITE_OK) {
2653-
(void)_pysqlite_seterror(self->state, self->db);
2659+
set_error_from_db(self->state, self->db);
26542660
return -1;
26552661
}
26562662
return current;

Modules/_sqlite/cursor.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ begin_transaction(pysqlite_Connection *self)
505505
Py_END_ALLOW_THREADS
506506

507507
if (rc != SQLITE_OK) {
508-
(void)_pysqlite_seterror(self->state, self->db);
508+
set_error_from_db(self->state, self->db);
509509
return -1;
510510
}
511511

@@ -715,7 +715,7 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self,
715715
if (rc != SQLITE_OK) {
716716
PyObject *exc = PyErr_GetRaisedException();
717717
sqlite3 *db = sqlite3_db_handle(self->st);
718-
_pysqlite_seterror(state, db);
718+
set_error_from_db(state, db);
719719
_PyErr_ChainExceptions1(exc);
720720
return;
721721
}
@@ -764,7 +764,7 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self,
764764
if (rc != SQLITE_OK) {
765765
PyObject *exc = PyErr_GetRaisedException();
766766
sqlite3 *db = sqlite3_db_handle(self->st);
767-
_pysqlite_seterror(state, db);
767+
set_error_from_db(state, db);
768768
_PyErr_ChainExceptions1(exc);
769769
return;
770770
}
@@ -896,7 +896,7 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
896896
PyErr_Clear();
897897
}
898898
}
899-
_pysqlite_seterror(state, self->connection->db);
899+
set_error_from_db(state, self->connection->db);
900900
goto error;
901901
}
902902

@@ -1087,7 +1087,7 @@ pysqlite_cursor_executescript_impl(pysqlite_Cursor *self,
10871087
return Py_NewRef((PyObject *)self);
10881088

10891089
error:
1090-
_pysqlite_seterror(self->connection->state, db);
1090+
set_error_from_db(self->connection->state, db);
10911091
return NULL;
10921092
}
10931093

@@ -1122,8 +1122,7 @@ pysqlite_cursor_iternext(PyObject *op)
11221122
Py_CLEAR(self->statement);
11231123
}
11241124
else if (rc != SQLITE_ROW) {
1125-
(void)_pysqlite_seterror(self->connection->state,
1126-
self->connection->db);
1125+
set_error_from_db(self->connection->state, self->connection->db);
11271126
(void)stmt_reset(self->statement);
11281127
Py_CLEAR(self->statement);
11291128
Py_DECREF(row);

Modules/_sqlite/statement.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql)
6262
Py_END_ALLOW_THREADS
6363

6464
if (rc != SQLITE_OK) {
65-
_pysqlite_seterror(state, db);
65+
set_error_from_db(state, db);
6666
return NULL;
6767
}
6868

Modules/_sqlite/util.c

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,26 +118,38 @@ raise_exception(PyObject *type, int errcode, const char *errmsg)
118118
Py_XDECREF(exc);
119119
}
120120

121+
void
122+
set_error_from_code(pysqlite_state *state, int code)
123+
{
124+
PyObject *exc_class = get_exception_class(state, code);
125+
if (exc_class == NULL) {
126+
// No new exception need be raised.
127+
return;
128+
}
129+
130+
const char *errmsg = sqlite3_errstr(code);
131+
assert(errmsg != NULL);
132+
raise_exception(exc_class, code, errmsg);
133+
}
134+
121135
/**
122136
* Checks the SQLite error code and sets the appropriate DB-API exception.
123-
* Returns the error code (0 means no error occurred).
124137
*/
125-
int
126-
_pysqlite_seterror(pysqlite_state *state, sqlite3 *db)
138+
void
139+
set_error_from_db(pysqlite_state *state, sqlite3 *db)
127140
{
128141
int errorcode = sqlite3_errcode(db);
129142
PyObject *exc_class = get_exception_class(state, errorcode);
130143
if (exc_class == NULL) {
131-
// No new exception need be raised; just pass the error code
132-
return errorcode;
144+
// No new exception need be raised.
145+
return;
133146
}
134147

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

143155
#ifdef WORDS_BIGENDIAN

Modules/_sqlite/util.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@
3030

3131
/**
3232
* Checks the SQLite error code and sets the appropriate DB-API exception.
33-
* Returns the error code (0 means no error occurred).
3433
*/
35-
int _pysqlite_seterror(pysqlite_state *state, sqlite3 *db);
34+
void set_error_from_db(pysqlite_state *state, sqlite3 *db);
35+
void set_error_from_code(pysqlite_state *state, int code);
3636

3737
sqlite_int64 _pysqlite_long_as_int64(PyObject * value);
3838

0 commit comments

Comments
 (0)