mirror of
https://github.com/python/cpython.git
synced 2025-11-24 12:20:42 +00:00
gh-135386: Fix "unable to open database file" errors on readonly DB (GH-135566)
Add immutable=1 flag for read-only SQLite access to avoid WAL/SHM errors on readonly DB. Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
f27af8ba8e
commit
c0ae92b7c0
3 changed files with 56 additions and 5 deletions
|
|
@ -60,12 +60,16 @@ class _Database(MutableMapping):
|
|||
# We use the URI format when opening the database.
|
||||
uri = _normalize_uri(path)
|
||||
uri = f"{uri}?mode={flag}"
|
||||
if flag == "ro":
|
||||
# Add immutable=1 to allow read-only SQLite access even if wal/shm missing
|
||||
uri += "&immutable=1"
|
||||
|
||||
try:
|
||||
self._cx = sqlite3.connect(uri, autocommit=True, uri=True)
|
||||
except sqlite3.Error as exc:
|
||||
raise error(str(exc))
|
||||
|
||||
if flag != "ro":
|
||||
# This is an optimization only; it's ok if it fails.
|
||||
with suppress(sqlite3.OperationalError):
|
||||
self._cx.execute("PRAGMA journal_mode = wal")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import os
|
||||
import stat
|
||||
import sys
|
||||
import unittest
|
||||
from contextlib import closing
|
||||
|
|
@ -90,6 +92,49 @@ class ReadOnly(_SQLiteDbmTests):
|
|||
self.assertEqual([k for k in self.db], [b"key1", b"key2"])
|
||||
|
||||
|
||||
class ReadOnlyFilesystem(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.test_dir = os_helper.TESTFN
|
||||
self.addCleanup(os_helper.rmtree, self.test_dir)
|
||||
os.mkdir(self.test_dir)
|
||||
self.db_path = os.path.join(self.test_dir, "test.db")
|
||||
|
||||
db = dbm_sqlite3.open(self.db_path, "c")
|
||||
db[b"key"] = b"value"
|
||||
db.close()
|
||||
|
||||
def test_readonly_file_read(self):
|
||||
os.chmod(self.db_path, stat.S_IREAD)
|
||||
with dbm_sqlite3.open(self.db_path, "r") as db:
|
||||
self.assertEqual(db[b"key"], b"value")
|
||||
|
||||
def test_readonly_file_write(self):
|
||||
os.chmod(self.db_path, stat.S_IREAD)
|
||||
with dbm_sqlite3.open(self.db_path, "w") as db:
|
||||
with self.assertRaises(dbm_sqlite3.error):
|
||||
db[b"newkey"] = b"newvalue"
|
||||
|
||||
def test_readonly_dir_read(self):
|
||||
os.chmod(self.test_dir, stat.S_IREAD | stat.S_IEXEC)
|
||||
with dbm_sqlite3.open(self.db_path, "r") as db:
|
||||
self.assertEqual(db[b"key"], b"value")
|
||||
|
||||
def test_readonly_dir_write(self):
|
||||
os.chmod(self.test_dir, stat.S_IREAD | stat.S_IEXEC)
|
||||
with dbm_sqlite3.open(self.db_path, "w") as db:
|
||||
try:
|
||||
db[b"newkey"] = b"newvalue"
|
||||
modified = True # on Windows and macOS
|
||||
except dbm_sqlite3.error:
|
||||
modified = False
|
||||
with dbm_sqlite3.open(self.db_path, "r") as db:
|
||||
if modified:
|
||||
self.assertEqual(db[b"newkey"], b"newvalue")
|
||||
else:
|
||||
self.assertNotIn(b"newkey", db)
|
||||
|
||||
|
||||
class ReadWrite(_SQLiteDbmTests):
|
||||
|
||||
def setUp(self):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
Fix opening a :mod:`dbm.sqlite3` database for reading from read-only file
|
||||
or directory.
|
||||
Loading…
Add table
Add a link
Reference in a new issue