Skip to content

Commit 66dfe92

Browse files
committed
Take the connection mutex around more SQLite getters
SQLite 3.53, which the Windows build bundles, also takes the connection mutex in sqlite3_limit(), sqlite3_get_autocommit(), sqlite3_changes(), sqlite3_total_changes() and sqlite3_errcode().
1 parent 876fd7a commit 66dfe92

6 files changed

Lines changed: 53 additions & 18 deletions

File tree

‎Lib/test/test_sqlite3/test_dbapi.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1833,6 +1833,9 @@ def collation(cx):
18331833
lambda cx: cx.set_trace_callback(None))
18341834
run("setconfig", sql, function,
18351835
lambda cx: cx.setconfig(sqlite3.SQLITE_DBCONFIG_ENABLE_FKEY))
1836+
run("getters", sql, function,
1837+
lambda cx: (cx.in_transaction, cx.total_changes,
1838+
cx.getlimit(sqlite3.SQLITE_LIMIT_LENGTH)))
18361839
print("done")
18371840
""")
18381841
try:

‎Modules/_sqlite/connection.c‎

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ connection_close(pysqlite_Connection *self)
454454

455455
int rc = 0;
456456
if (self->autocommit == AUTOCOMMIT_DISABLED &&
457-
!sqlite3_get_autocommit(self->db))
457+
!pysqlite_get_autocommit(self->db))
458458
{
459459
if (connection_exec_stmt(self, "ROLLBACK") < 0) {
460460
rc = -1;
@@ -759,7 +759,7 @@ pysqlite_connection_commit_impl(pysqlite_Connection *self)
759759
}
760760

761761
if (self->autocommit == AUTOCOMMIT_LEGACY) {
762-
if (!sqlite3_get_autocommit(self->db)) {
762+
if (!pysqlite_get_autocommit(self->db)) {
763763
if (connection_exec_stmt(self, "COMMIT") < 0) {
764764
return NULL;
765765
}
@@ -793,7 +793,7 @@ pysqlite_connection_rollback_impl(pysqlite_Connection *self)
793793
}
794794

795795
if (self->autocommit == AUTOCOMMIT_LEGACY) {
796-
if (!sqlite3_get_autocommit(self->db)) {
796+
if (!pysqlite_get_autocommit(self->db)) {
797797
if (connection_exec_stmt(self, "ROLLBACK") < 0) {
798798
return NULL;
799799
}
@@ -1184,7 +1184,7 @@ destructor_callback(void *ctx)
11841184
static int
11851185
check_num_params(pysqlite_Connection *self, const int n, const char *name)
11861186
{
1187-
int limit = sqlite3_limit(self->db, SQLITE_LIMIT_FUNCTION_ARG, -1);
1187+
int limit = pysqlite_limit(self->db, SQLITE_LIMIT_FUNCTION_ARG, -1);
11881188
assert(limit >= 0);
11891189
if (n < -1 || n > limit) {
11901190
PyErr_Format(self->ProgrammingError,
@@ -1838,7 +1838,10 @@ pysqlite_connection_get_total_changes(PyObject *op, void *Py_UNUSED(closure))
18381838
if (!pysqlite_check_connection(self)) {
18391839
return NULL;
18401840
}
1841-
return PyLong_FromLong(sqlite3_total_changes(self->db));
1841+
pysqlite_db_mutex_enter(self->db);
1842+
int changes = sqlite3_total_changes(self->db);
1843+
pysqlite_db_mutex_leave(self->db);
1844+
return PyLong_FromLong(changes);
18421845
}
18431846

18441847
static PyObject *
@@ -1848,7 +1851,7 @@ pysqlite_connection_get_in_transaction(PyObject *op, void *Py_UNUSED(closure))
18481851
if (!pysqlite_check_connection(self)) {
18491852
return NULL;
18501853
}
1851-
if (!sqlite3_get_autocommit(self->db)) {
1854+
if (!pysqlite_get_autocommit(self->db)) {
18521855
Py_RETURN_TRUE;
18531856
}
18541857
Py_RETURN_FALSE;
@@ -2508,7 +2511,7 @@ setlimit_impl(pysqlite_Connection *self, int category, int limit)
25082511
return NULL;
25092512
}
25102513

2511-
int old_limit = sqlite3_limit(self->db, category, limit);
2514+
int old_limit = pysqlite_limit(self->db, category, limit);
25122515
if (old_limit < 0) {
25132516
PyErr_SetString(self->ProgrammingError, "'category' is out of bounds");
25142517
return NULL;
@@ -2679,14 +2682,14 @@ set_autocommit(PyObject *op, PyObject *val, void *Py_UNUSED(closure))
26792682
return -1;
26802683
}
26812684
if (self->autocommit == AUTOCOMMIT_ENABLED) {
2682-
if (!sqlite3_get_autocommit(self->db)) {
2685+
if (!pysqlite_get_autocommit(self->db)) {
26832686
if (connection_exec_stmt(self, "COMMIT") < 0) {
26842687
return -1;
26852688
}
26862689
}
26872690
}
26882691
else if (self->autocommit == AUTOCOMMIT_DISABLED) {
2689-
if (sqlite3_get_autocommit(self->db)) {
2692+
if (pysqlite_get_autocommit(self->db)) {
26902693
if (connection_exec_stmt(self, "BEGIN") < 0) {
26912694
return -1;
26922695
}

‎Modules/_sqlite/cursor.c‎

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,7 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
937937
if (self->connection->autocommit == AUTOCOMMIT_LEGACY
938938
&& self->connection->isolation_level
939939
&& self->statement->is_dml
940-
&& sqlite3_get_autocommit(self->connection->db))
940+
&& pysqlite_get_autocommit(self->connection->db))
941941
{
942942
if (begin_transaction(self->connection) < 0) {
943943
goto error;
@@ -1025,7 +1025,9 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
10251025

10261026
if (rc == SQLITE_DONE) {
10271027
if (self->statement->is_dml) {
1028-
self->rowcount += (long)sqlite3_changes(self->connection->db);
1028+
pysqlite_db_mutex_enter(db);
1029+
self->rowcount += (long)sqlite3_changes(db);
1030+
pysqlite_db_mutex_leave(db);
10291031
}
10301032
if (stmt_reset(self->statement) != SQLITE_OK) {
10311033
goto reset_failure;
@@ -1059,7 +1061,9 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
10591061
if (PyErr_Occurred()) {
10601062
if (self->statement) {
10611063
sqlite3 *db = sqlite3_db_handle(self->statement->st);
1064+
pysqlite_db_mutex_enter(db);
10621065
int sqlite3_state = sqlite3_errcode(db);
1066+
pysqlite_db_mutex_leave(db);
10631067
// stmt_reset() may return a previously set exception,
10641068
// either triggered because of Python or sqlite3.
10651069
rc = stmt_reset(self->statement);
@@ -1146,8 +1150,8 @@ pysqlite_cursor_executescript_impl(pysqlite_Cursor *self,
11461150
}
11471151

11481152
size_t sql_len = strlen(sql_script);
1149-
int max_length = sqlite3_limit(self->connection->db,
1150-
SQLITE_LIMIT_SQL_LENGTH, -1);
1153+
int max_length = pysqlite_limit(self->connection->db,
1154+
SQLITE_LIMIT_SQL_LENGTH, -1);
11511155
if (sql_len > (unsigned)max_length) {
11521156
PyErr_SetString(self->connection->DataError,
11531157
"query string is too large");
@@ -1157,7 +1161,7 @@ pysqlite_cursor_executescript_impl(pysqlite_Cursor *self,
11571161
// Commit if needed
11581162
sqlite3 *db = self->connection->db;
11591163
if (self->connection->autocommit == AUTOCOMMIT_LEGACY
1160-
&& !sqlite3_get_autocommit(db))
1164+
&& !pysqlite_get_autocommit(db))
11611165
{
11621166
int rc = SQLITE_OK;
11631167

@@ -1229,7 +1233,10 @@ pysqlite_cursor_iternext(PyObject *op)
12291233
int rc = stmt_step(stmt);
12301234
if (rc == SQLITE_DONE) {
12311235
if (self->statement->is_dml) {
1232-
self->rowcount = (long)sqlite3_changes(self->connection->db);
1236+
sqlite3 *db = self->connection->db;
1237+
pysqlite_db_mutex_enter(db);
1238+
self->rowcount = (long)sqlite3_changes(db);
1239+
pysqlite_db_mutex_leave(db);
12331240
}
12341241
rc = stmt_reset(self->statement);
12351242
Py_CLEAR(self->statement);

‎Modules/_sqlite/statement.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql)
4949
}
5050

5151
sqlite3 *db = connection->db;
52-
int max_length = sqlite3_limit(db, SQLITE_LIMIT_SQL_LENGTH, -1);
52+
int max_length = pysqlite_limit(db, SQLITE_LIMIT_SQL_LENGTH, -1);
5353
if (size > max_length) {
5454
PyErr_SetString(connection->DataError,
5555
"query string is too large");

‎Modules/_sqlite/util.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,17 +141,18 @@ set_error_from_code(pysqlite_state *state, int code)
141141
int
142142
set_error_from_db(pysqlite_state *state, sqlite3 *db)
143143
{
144+
pysqlite_db_mutex_enter(db);
144145
int errorcode = sqlite3_errcode(db);
145146
PyObject *exc_class = get_exception_class(state, errorcode);
146147
if (exc_class == NULL) {
147148
// No new exception need be raised.
149+
pysqlite_db_mutex_leave(db);
148150
return SQLITE_OK;
149151
}
150152

151153
/* Create and set the exception. */
152154
int extended_errcode = sqlite3_extended_errcode(db);
153155
// sqlite3_errmsg() always returns an UTF-8 encoded message
154-
pysqlite_db_mutex_enter(db);
155156
PyObject *errmsg = PyUnicode_FromString(sqlite3_errmsg(db));
156157
pysqlite_db_mutex_leave(db);
157158
raise_exception(exc_class, extended_errcode, errmsg);

‎Modules/_sqlite/util.h‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ pysqlite_set_ref(PyObject *owner, PyObject **field, PyObject *value)
6666
* never block on the connection mutex while holding the GIL: enter the mutex
6767
* with these helpers around SQLite API calls that take it and are made with
6868
* the GIL held. The mutex is recursive, and it is NULL unless SQLite is in
69-
* serialized threading mode.
69+
* serialized threading mode. Newer SQLite versions take the mutex in more
70+
* calls, including getters such as sqlite3_get_autocommit() and
71+
* sqlite3_limit(), so assume that any call on a connection or a statement
72+
* takes it unless the SQLite documentation says otherwise.
7073
*
7174
* Do not call back into Python code while holding the mutex: it could wait
7275
* for another thread that is blocked on the same mutex. */
@@ -109,4 +112,22 @@ pysqlite_db_mutex_leave(sqlite3 *db)
109112
pysqlite_mutex_leave(sqlite3_db_mutex(db));
110113
}
111114

115+
static inline int
116+
pysqlite_get_autocommit(sqlite3 *db)
117+
{
118+
pysqlite_db_mutex_enter(db);
119+
int autocommit = sqlite3_get_autocommit(db);
120+
pysqlite_db_mutex_leave(db);
121+
return autocommit;
122+
}
123+
124+
static inline int
125+
pysqlite_limit(sqlite3 *db, int id, int new_value)
126+
{
127+
pysqlite_db_mutex_enter(db);
128+
int old_value = sqlite3_limit(db, id, new_value);
129+
pysqlite_db_mutex_leave(db);
130+
return old_value;
131+
}
132+
112133
#endif

0 commit comments

Comments
 (0)