Skip to content

Commit c273986

Browse files
author
Erlend Egeberg Aasland
authored
bpo-45613: Set sqlite3.threadsafety dynamically (GH-29227)
Use the compile-time selected default SQLite threaded mode to set the DB-API 2.0 attribute 'threadsafety' Mappings: - SQLITE_THREADSAFE=0 => threadsafety=0 - SQLITE_THREADSAFE=1 => threadsafety=3 - SQLITE_THREADSAFE=2 => threadsafety=1
1 parent 762173c commit c273986

File tree

6 files changed

+75
-21
lines changed

6 files changed

+75
-21
lines changed

Doc/library/sqlite3.rst

+35-17
Original file line numberDiff line numberDiff line change
@@ -159,23 +159,41 @@ Module functions and constants
159159

160160
.. data:: threadsafety
161161

162-
Integer constant required by the DB-API, stating the level of thread safety
163-
the :mod:`sqlite3` module supports. Currently hard-coded to ``1``, meaning
164-
*"Threads may share the module, but not connections."* However, this may not
165-
always be true. You can check the underlying SQLite library's compile-time
166-
threaded mode using the following query::
167-
168-
import sqlite3
169-
con = sqlite3.connect(":memory:")
170-
con.execute("""
171-
select * from pragma_compile_options
172-
where compile_options like 'THREADSAFE=%'
173-
""").fetchall()
174-
175-
Note that the `SQLITE_THREADSAFE levels
176-
<https://sqlite.org/compile.html#threadsafe>`_ do not match the DB-API 2.0
177-
``threadsafety`` levels.
178-
162+
Integer constant required by the DB-API 2.0, stating the level of thread
163+
safety the :mod:`sqlite3` module supports. This attribute is set based on
164+
the default `threading mode <https://sqlite.org/threadsafe.html>`_ the
165+
underlying SQLite library is compiled with. The SQLite threading modes are:
166+
167+
1. **Single-thread**: In this mode, all mutexes are disabled and SQLite is
168+
unsafe to use in more than a single thread at once.
169+
2. **Multi-thread**: In this mode, SQLite can be safely used by multiple
170+
threads provided that no single database connection is used
171+
simultaneously in two or more threads.
172+
3. **Serialized**: In serialized mode, SQLite can be safely used by
173+
multiple threads with no restriction.
174+
175+
The mappings from SQLite threading modes to DB-API 2.0 threadsafety levels
176+
are as follows:
177+
178+
+------------------+-----------------+----------------------+-------------------------------+
179+
| SQLite threading | `threadsafety`_ | `SQLITE_THREADSAFE`_ | DB-API 2.0 meaning |
180+
| mode | | | |
181+
+==================+=================+======================+===============================+
182+
| single-thread | 0 | 0 | Threads may not share the |
183+
| | | | module |
184+
+------------------+-----------------+----------------------+-------------------------------+
185+
| multi-thread | 1 | 2 | Threads may share the module, |
186+
| | | | but not connections |
187+
+------------------+-----------------+----------------------+-------------------------------+
188+
| serialized | 3 | 1 | Threads may share the module, |
189+
| | | | connections and cursors |
190+
+------------------+-----------------+----------------------+-------------------------------+
191+
192+
.. _threadsafety: https://www.python.org/dev/peps/pep-0249/#threadsafety
193+
.. _SQLITE_THREADSAFE: https://sqlite.org/compile.html#threadsafe
194+
195+
.. versionchanged:: 3.11
196+
Set *threadsafety* dynamically instead of hard-coding it to ``1``.
179197

180198
.. data:: PARSE_DECLTYPES
181199

Doc/whatsnew/3.11.rst

+4
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ sqlite3
254254
setting and getting SQLite limits by connection basis.
255255
(Contributed by Erlend E. Aasland in :issue:`45243`.)
256256

257+
* :mod:`sqlite3` now sets :attr:`sqlite3.threadsafety` based on the default
258+
threading mode the underlying SQLite library has been compiled with.
259+
(Contributed by Erlend E. Aasland in :issue:`45613`.)
260+
257261

258262
threading
259263
---------

Lib/sqlite3/dbapi2.py

-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828

2929
paramstyle = "qmark"
3030

31-
threadsafety = 1
32-
3331
apilevel = "2.0"
3432

3533
Date = datetime.date

Lib/test/test_sqlite3/test_dbapi.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ def test_api_level(self):
5454
"apilevel is %s, should be 2.0" % sqlite.apilevel)
5555

5656
def test_thread_safety(self):
57-
self.assertEqual(sqlite.threadsafety, 1,
58-
"threadsafety is %d, should be 1" % sqlite.threadsafety)
57+
self.assertIn(sqlite.threadsafety, {0, 1, 3},
58+
"threadsafety is %d, should be 0, 1 or 3" %
59+
sqlite.threadsafety)
5960

6061
def test_param_style(self):
6162
self.assertEqual(sqlite.paramstyle, "qmark",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:mod:`sqlite3` now sets :attr:`sqlite3.threadsafety` based on the default
2+
threading mode the underlying SQLite library has been compiled with. Patch by
3+
Erlend E. Aasland.

Modules/_sqlite/module.c

+30
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,28 @@ add_integer_constants(PyObject *module) {
540540
return 0;
541541
}
542542

543+
/* Convert SQLite default threading mode (as set by the compile-time constant
544+
* SQLITE_THREADSAFE) to the corresponding DB-API 2.0 (PEP 249) threadsafety
545+
* level. */
546+
static int
547+
get_threadsafety(pysqlite_state *state)
548+
{
549+
int mode = sqlite3_threadsafe();
550+
switch (mode) {
551+
case 0: // Single-thread mode; threads may not share the module.
552+
return 0;
553+
case 1: // Serialized mode; threads may share the module,
554+
return 3; // connections, and cursors.
555+
case 2: // Multi-thread mode; threads may share the module, but not
556+
return 1; // connections.
557+
default:
558+
PyErr_Format(state->InterfaceError,
559+
"Unable to interpret SQLite threadsafety mode. Got %d, "
560+
"expected 0, 1, or 2", mode);
561+
return -1;
562+
}
563+
}
564+
543565
static int
544566
module_traverse(PyObject *module, visitproc visit, void *arg)
545567
{
@@ -689,6 +711,14 @@ module_exec(PyObject *module)
689711
goto error;
690712
}
691713

714+
int threadsafety = get_threadsafety(state);
715+
if (threadsafety < 0) {
716+
goto error;
717+
}
718+
if (PyModule_AddIntConstant(module, "threadsafety", threadsafety) < 0) {
719+
goto error;
720+
}
721+
692722
/* initialize microprotocols layer */
693723
if (pysqlite_microprotocols_init(module) < 0) {
694724
goto error;

0 commit comments

Comments
 (0)