Skip to content

Commit bbbdbc3

Browse files
committed
gh-131918: Add _ThreadLocalSqliteConnection in dbm.sqlite
1 parent 39fa19a commit bbbdbc3

1 file changed

Lines changed: 33 additions & 7 deletions

File tree

Lib/dbm/sqlite3.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from pathlib import Path
44
from contextlib import suppress, closing
55
from collections.abc import MutableMapping
6+
from threading import current_thread
7+
from weakref import ref
68

79
BUILD_TABLE = """
810
CREATE TABLE IF NOT EXISTS Dict (
@@ -32,6 +34,34 @@ def _normalize_uri(path):
3234
uri = uri.replace("//", "/")
3335
return uri
3436

37+
class _ThreadLocalSqliteConnection:
38+
def __init__(self, uri: str):
39+
self._uri = uri
40+
self._conn = {}
41+
42+
def conn(self):
43+
thread = current_thread()
44+
idt = id(thread)
45+
def thread_deleted(_, idt=idt):
46+
# When the thread is deleted, remove the local dict.
47+
# Note that this is suboptimal if the thread object gets
48+
# caught in a reference loop. We would like to be called
49+
# as soon as the OS-level thread ends instead.
50+
if self._conn is not None:
51+
dct = self._conn.dicts.pop(idt)
52+
wrthread = ref(thread, thread_deleted)
53+
try:
54+
conn = sqlite3.connect(self._uri, autocommit=True, uri=True)
55+
self._conn[id(thread)] = conn
56+
return conn
57+
except sqlite3.Error as exc:
58+
raise error(str(exc))
59+
60+
def close(self):
61+
for t, conn in self._conn.items():
62+
conn.close()
63+
del self._conn[t]
64+
3565

3666
class _Database(MutableMapping):
3767

@@ -59,15 +89,11 @@ def __init__(self, path, /, *, flag, mode):
5989
# We use the URI format when opening the database.
6090
uri = _normalize_uri(path)
6191
uri = f"{uri}?mode={flag}"
62-
63-
try:
64-
self._cx = sqlite3.connect(uri, autocommit=True, uri=True)
65-
except sqlite3.Error as exc:
66-
raise error(str(exc))
92+
self._cx = _ThreadLocalSqliteConnection(uri)
6793

6894
# This is an optimization only; it's ok if it fails.
6995
with suppress(sqlite3.OperationalError):
70-
self._cx.execute("PRAGMA journal_mode = wal")
96+
self._cx.conn().execute("PRAGMA journal_mode = wal")
7197

7298
if flag == "rwc":
7399
self._execute(BUILD_TABLE)
@@ -76,7 +102,7 @@ def _execute(self, *args, **kwargs):
76102
if not self._cx:
77103
raise error(_ERR_CLOSED)
78104
try:
79-
return closing(self._cx.execute(*args, **kwargs))
105+
return closing(self._cx.conn().execute(*args, **kwargs))
80106
except sqlite3.Error as exc:
81107
raise error(str(exc))
82108

0 commit comments

Comments
 (0)