Skip to content
52 changes: 35 additions & 17 deletions Doc/library/sqlite3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -159,23 +159,41 @@ Module functions and constants

.. data:: threadsafety

Integer constant required by the DB-API, stating the level of thread safety
the :mod:`sqlite3` module supports. Currently hard-coded to ``1``, meaning
*"Threads may share the module, but not connections."* However, this may not
always be true. You can check the underlying SQLite library's compile-time
threaded mode using the following query::

import sqlite3
con = sqlite3.connect(":memory:")
con.execute("""
select * from pragma_compile_options
where compile_options like 'THREADSAFE=%'
""").fetchall()

Note that the `SQLITE_THREADSAFE levels
<https://sqlite.org/compile.html#threadsafe>`_ do not match the DB-API 2.0
``threadsafety`` levels.

Integer constant required by the DB-API 2.0, stating the level of thread
safety the :mod:`sqlite3` module supports. This attribute is set based on
the default `threading mode <https://sqlite.org/threadsafe.html>`_ the
underlying SQLite library is compiled with. The SQLite threading modes are:

1. **Single-thread**: In this mode, all mutexes are disabled and SQLite is
unsafe to use in more than a single thread at once.
2. **Multi-thread**: In this mode, SQLite can be safely used by multiple
threads provided that no single database connection is used
simultaneously in two or more threads.
3. **Serialized**: In serialized mode, SQLite can be safely used by
multiple threads with no restriction.

The mappings from SQLite threading modes to DB-API 2.0 threadsafety levels
are as follows:

+------------------+-----------------+----------------------+-------------------------------+
| SQLite threading | `threadsafety`_ | `SQLITE_THREADSAFE`_ | DB-API 2.0 meaning |
| mode | | | |
+==================+=================+======================+===============================+
| single-thread | 0 | 0 | Threads may not share the |
| | | | module |
+------------------+-----------------+----------------------+-------------------------------+
| multi-thread | 1 | 2 | Threads may share the module, |
| | | | but not connections |
+------------------+-----------------+----------------------+-------------------------------+
| serialized | 3 | 1 | Threads may share the module, |
| | | | connections and cursors |
+------------------+-----------------+----------------------+-------------------------------+

.. _threadsafety: https://www.python.org/dev/peps/pep-0249/#threadsafety
.. _SQLITE_THREADSAFE: https://sqlite.org/compile.html#threadsafe

.. versionchanged:: 3.11
Set *threadsafety* dynamically instead of hard-coding it to ``1``.

.. data:: PARSE_DECLTYPES

Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.11.rst
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@ sqlite3
setting and getting SQLite limits by connection basis.
(Contributed by Erlend E. Aasland in :issue:`45243`.)

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


threading
---------
Expand Down
2 changes: 0 additions & 2 deletions Lib/sqlite3/dbapi2.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@

paramstyle = "qmark"

threadsafety = 1

apilevel = "2.0"

Date = datetime.date
Expand Down
5 changes: 3 additions & 2 deletions Lib/test/test_sqlite3/test_dbapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ def test_api_level(self):
"apilevel is %s, should be 2.0" % sqlite.apilevel)

def test_thread_safety(self):
self.assertEqual(sqlite.threadsafety, 1,
"threadsafety is %d, should be 1" % sqlite.threadsafety)
self.assertIn(sqlite.threadsafety, {0, 1, 3},
"threadsafety is %d, should be 0, 1 or 3" %
sqlite.threadsafety)

def test_param_style(self):
self.assertEqual(sqlite.paramstyle, "qmark",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:mod:`sqlite3` now sets :attr:`sqlite3.threadsafety` based on the default
threading mode the underlying SQLite library has been compiled with. Patch by
Erlend E. Aasland.
30 changes: 30 additions & 0 deletions Modules/_sqlite/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,28 @@ add_integer_constants(PyObject *module) {
return 0;
}

/* Convert SQLite default threading mode (as set by the compile-time constant
* SQLITE_THREADSAFE) to the corresponding DB-API 2.0 (PEP 249) threadsafety
* level. */
static int
get_threadsafety(pysqlite_state *state)
{
int mode = sqlite3_threadsafe();
switch (mode) {
case 0: // Single-thread mode; threads may not share the module.
return 0;
case 1: // Serialized mode; threads may share the module,
return 3; // connections, and cursors.
case 2: // Multi-thread mode; threads may share the module, but not
return 1; // connections.
default:
PyErr_Format(state->InterfaceError,
"Unable to interpret SQLite threadsafety mode. Got %d, "
"expected 0, 1, or 2", mode);
return -1;
}
}

static int
module_traverse(PyObject *module, visitproc visit, void *arg)
{
Expand Down Expand Up @@ -564,6 +586,14 @@ module_exec(PyObject *module)
goto error;
}

int threadsafety = get_threadsafety(state);
if (threadsafety < 0) {
goto error;
}
if (PyModule_AddIntConstant(module, "threadsafety", threadsafety) < 0) {
goto error;
}

/* initialize microprotocols layer */
if (pysqlite_microprotocols_init(module) < 0) {
goto error;
Expand Down