Skip to content

Commit 27acaf1

Browse files
authored
gh-139327: consolidate sqlite3_finalize and sqlite3_reset usages (GH-139329)
1 parent 4126d9f commit 27acaf1

File tree

5 files changed

+113
-16
lines changed

5 files changed

+113
-16
lines changed

Modules/_sqlite/blob.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,9 @@ static void
118118
blob_seterror(pysqlite_Blob *self, int rc)
119119
{
120120
assert(self->connection != NULL);
121-
set_error_from_db(self->connection->state, self->connection->db);
121+
assert(rc != SQLITE_OK);
122+
set_error_from_code(self->connection->state, rc);
123+
assert(PyErr_Occurred());
122124
}
123125

124126
static PyObject *

Modules/_sqlite/cursor.c

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,41 @@ check_cursor_locked(pysqlite_Cursor *cur)
5858
return 1;
5959
}
6060

61+
static pysqlite_state *
62+
get_module_state_by_cursor(pysqlite_Cursor *cursor)
63+
{
64+
if (cursor->connection != NULL && cursor->connection->state != NULL) {
65+
return cursor->connection->state;
66+
}
67+
return pysqlite_get_state_by_type(Py_TYPE(cursor));
68+
}
69+
70+
static void
71+
cursor_sqlite3_internal_error(pysqlite_Cursor *cursor,
72+
const char *error_message,
73+
int chain_exceptions)
74+
{
75+
pysqlite_state *state = get_module_state_by_cursor(cursor);
76+
if (chain_exceptions) {
77+
assert(PyErr_Occurred());
78+
PyObject *exc = PyErr_GetRaisedException();
79+
PyErr_SetString(state->InternalError, error_message);
80+
_PyErr_ChainExceptions1(exc);
81+
}
82+
else {
83+
assert(!PyErr_Occurred());
84+
PyErr_SetString(state->InternalError, error_message);
85+
}
86+
}
87+
88+
static void
89+
cursor_cannot_reset_stmt_error(pysqlite_Cursor *cursor, int chain_exceptions)
90+
{
91+
cursor_sqlite3_internal_error(cursor,
92+
"cannot reset statement",
93+
chain_exceptions);
94+
}
95+
6196
/*[clinic input]
6297
module _sqlite3
6398
class _sqlite3.Cursor "pysqlite_Cursor *" "clinic_state()->CursorType"
@@ -173,8 +208,12 @@ cursor_clear(PyObject *op)
173208
Py_CLEAR(self->row_factory);
174209
if (self->statement) {
175210
/* Reset the statement if the user has not closed the cursor */
176-
stmt_reset(self->statement);
211+
int rc = stmt_reset(self->statement);
177212
Py_CLEAR(self->statement);
213+
if (rc != SQLITE_OK) {
214+
cursor_cannot_reset_stmt_error(self, 0);
215+
PyErr_FormatUnraisable("Exception ignored in cursor_clear()");
216+
}
178217
}
179218

180219
return 0;
@@ -837,7 +876,9 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
837876

838877
if (self->statement) {
839878
// Reset pending statements on this cursor.
840-
(void)stmt_reset(self->statement);
879+
if (stmt_reset(self->statement) != SQLITE_OK) {
880+
goto reset_failure;
881+
}
841882
}
842883

843884
PyObject *stmt = get_statement_from_cache(self, operation);
@@ -861,7 +902,9 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
861902
}
862903
}
863904

864-
(void)stmt_reset(self->statement);
905+
if (stmt_reset(self->statement) != SQLITE_OK) {
906+
goto reset_failure;
907+
}
865908
self->rowcount = self->statement->is_dml ? 0L : -1L;
866909

867910
/* We start a transaction implicitly before a DML statement.
@@ -943,7 +986,9 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
943986
if (self->statement->is_dml) {
944987
self->rowcount += (long)sqlite3_changes(self->connection->db);
945988
}
946-
stmt_reset(self->statement);
989+
if (stmt_reset(self->statement) != SQLITE_OK) {
990+
goto reset_failure;
991+
}
947992
}
948993
Py_XDECREF(parameters);
949994
}
@@ -968,8 +1013,15 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
9681013

9691014
if (PyErr_Occurred()) {
9701015
if (self->statement) {
971-
(void)stmt_reset(self->statement);
1016+
sqlite3 *db = sqlite3_db_handle(self->statement->st);
1017+
int sqlite3_state = sqlite3_errcode(db);
1018+
// stmt_reset() may return a previously set exception,
1019+
// either triggered because of Python or sqlite3.
1020+
rc = stmt_reset(self->statement);
9721021
Py_CLEAR(self->statement);
1022+
if (sqlite3_state == SQLITE_OK && rc != SQLITE_OK) {
1023+
cursor_cannot_reset_stmt_error(self, 1);
1024+
}
9731025
}
9741026
self->rowcount = -1L;
9751027
return NULL;
@@ -978,6 +1030,20 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
9781030
Py_CLEAR(self->statement);
9791031
}
9801032
return Py_NewRef((PyObject *)self);
1033+
1034+
reset_failure:
1035+
/* suite to execute when stmt_reset() failed and no exception is set */
1036+
assert(!PyErr_Occurred());
1037+
1038+
Py_XDECREF(parameters);
1039+
Py_XDECREF(parameters_iter);
1040+
Py_XDECREF(parameters_list);
1041+
1042+
self->locked = 0;
1043+
self->rowcount = -1L;
1044+
Py_CLEAR(self->statement);
1045+
cursor_cannot_reset_stmt_error(self, 0);
1046+
return NULL;
9811047
}
9821048

9831049
/*[clinic input]
@@ -1120,14 +1186,20 @@ pysqlite_cursor_iternext(PyObject *op)
11201186
if (self->statement->is_dml) {
11211187
self->rowcount = (long)sqlite3_changes(self->connection->db);
11221188
}
1123-
(void)stmt_reset(self->statement);
1189+
rc = stmt_reset(self->statement);
11241190
Py_CLEAR(self->statement);
1191+
if (rc != SQLITE_OK) {
1192+
goto reset_failure;
1193+
}
11251194
}
11261195
else if (rc != SQLITE_ROW) {
1127-
set_error_from_db(self->connection->state, self->connection->db);
1128-
(void)stmt_reset(self->statement);
1196+
rc = set_error_from_db(self->connection->state, self->connection->db);
1197+
int reset_rc = stmt_reset(self->statement);
11291198
Py_CLEAR(self->statement);
11301199
Py_DECREF(row);
1200+
if (rc == SQLITE_OK && reset_rc != SQLITE_OK) {
1201+
goto reset_failure;
1202+
}
11311203
return NULL;
11321204
}
11331205
if (!Py_IsNone(self->row_factory)) {
@@ -1137,6 +1209,10 @@ pysqlite_cursor_iternext(PyObject *op)
11371209
Py_SETREF(row, new_row);
11381210
}
11391211
return row;
1212+
1213+
reset_failure:
1214+
cursor_cannot_reset_stmt_error(self, 0);
1215+
return NULL;
11401216
}
11411217

11421218
/*[clinic input]
@@ -1291,8 +1367,15 @@ pysqlite_cursor_close_impl(pysqlite_Cursor *self)
12911367
}
12921368

12931369
if (self->statement) {
1294-
(void)stmt_reset(self->statement);
1370+
int rc = stmt_reset(self->statement);
1371+
// Force self->statement to be NULL even if stmt_reset() may have
1372+
// failed to avoid a possible double-free if someone calls close()
1373+
// twice as a leak here would be better than a double-free.
12951374
Py_CLEAR(self->statement);
1375+
if (rc != SQLITE_OK) {
1376+
cursor_cannot_reset_stmt_error(self, 0);
1377+
return NULL;
1378+
}
12961379
}
12971380

12981381
self->closed = 1;

Modules/_sqlite/statement.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,12 @@ pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql)
103103
return self;
104104

105105
error:
106-
(void)sqlite3_finalize(stmt);
106+
assert(PyErr_Occurred());
107+
if (sqlite3_finalize(stmt) != SQLITE_OK) {
108+
PyObject *exc = PyErr_GetRaisedException();
109+
PyErr_SetString(connection->InternalError, "cannot finalize statement");
110+
_PyErr_ChainExceptions1(exc);
111+
}
107112
return NULL;
108113
}
109114

@@ -114,10 +119,16 @@ stmt_dealloc(PyObject *op)
114119
PyTypeObject *tp = Py_TYPE(self);
115120
PyObject_GC_UnTrack(op);
116121
if (self->st) {
122+
int rc;
117123
Py_BEGIN_ALLOW_THREADS
118-
sqlite3_finalize(self->st);
124+
rc = sqlite3_finalize(self->st);
119125
Py_END_ALLOW_THREADS
120-
self->st = 0;
126+
self->st = NULL;
127+
if (rc != SQLITE_OK) {
128+
pysqlite_state *state = PyType_GetModuleState(Py_TYPE(op));
129+
PyErr_SetString(state->InternalError, "cannot finalize statement");
130+
PyErr_FormatUnraisable("Exception ignored in stmt_dealloc()");
131+
}
121132
}
122133
tp->tp_free(self);
123134
Py_DECREF(tp);

Modules/_sqlite/util.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,21 +135,22 @@ set_error_from_code(pysqlite_state *state, int code)
135135
/**
136136
* Checks the SQLite error code and sets the appropriate DB-API exception.
137137
*/
138-
void
138+
int
139139
set_error_from_db(pysqlite_state *state, sqlite3 *db)
140140
{
141141
int errorcode = sqlite3_errcode(db);
142142
PyObject *exc_class = get_exception_class(state, errorcode);
143143
if (exc_class == NULL) {
144144
// No new exception need be raised.
145-
return;
145+
return SQLITE_OK;
146146
}
147147

148148
/* Create and set the exception. */
149149
int extended_errcode = sqlite3_extended_errcode(db);
150150
// sqlite3_errmsg() always returns an UTF-8 encoded message
151151
const char *errmsg = sqlite3_errmsg(db);
152152
raise_exception(exc_class, extended_errcode, errmsg);
153+
return errorcode;
153154
}
154155

155156
#ifdef WORDS_BIGENDIAN

Modules/_sqlite/util.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
/**
3232
* Checks the SQLite error code and sets the appropriate DB-API exception.
3333
*/
34-
void set_error_from_db(pysqlite_state *state, sqlite3 *db);
34+
int set_error_from_db(pysqlite_state *state, sqlite3 *db);
3535
void set_error_from_code(pysqlite_state *state, int code);
3636

3737
sqlite_int64 _pysqlite_long_as_int64(PyObject * value);

0 commit comments

Comments
 (0)