Conversation

GeneralK1ng

What

This PR adds the SQLite URI parameter immutable=1 when opening a database in read-only mode in Lib/dbm/sqlite3.py. This explicitly informs SQLite that the database is read-only and avoids attempts to create or write to auxiliary WAL/SHM files.

Why

Without this flag, opening a read-only SQLite database may fail with errors such as "unable to open database file" when the WAL or SHM files cannot be created due to filesystem permissions or other restrictions. This is especially common when the database file is read-only and the environment prevents creation of additional files.

Adding immutable=1 allows SQLite to operate correctly without needing to create WAL/SHM files, thus preventing these errors.

Where

The change is made in the _Database.__init__ constructor in Lib/dbm/sqlite3.py. The URI used to open the database connection is appended with &immutable=1 only if the flag is "ro" (read-only).

Related issue

#135386


This is my first contribution to CPython. I appreciate the opportunity and look forward to feedback from the community. Thank you!

@python-cla-bot

All commit authors signed the Contributor License Agreement.

CLA signed

@bedevere-app

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test case. You can refer to the devguide for how to do that.

@GeneralK1ngGeneralK1ng force-pushed the fix-issue-135386 branch 2 times, most recently from 707f4a3 to 779faf2 Compare June 17, 2025 04:53
@ZeroIntensity

No need to force-push, we squash at the end.

@GeneralK1ng

No need to force-push, we squash at the end.

Got it, thanks for the reminder!

@ZeroIntensity

FYI, tests are failing on Windows.

@GeneralK1ng

FYI, tests are failing on Windows.

Thanks for the suggestion. I've skipped the test on Windows using @unittest.skipIf(sys.platform.startswith("win"), ...) because of platform differences in file permissions and locking behavior that caused the test to fail in CI. Should be good now!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

I've skipped the test on Windows

I think we should be testing this on all platforms if possible. Maybe we can figure out why the test is failing on Windows and adapt it?

Comment on lines +62 to +66
if flag == "ro":
# Add immutable=1 to allow read-only SQLite access even if wal/shm missing
uri = f"{uri}?mode={flag}&immutable=1"
else:
uri = f"{uri}?mode={flag}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if flag == "ro":
# Add immutable=1 to allow read-only SQLite access even if wal/shm missing
uri = f"{uri}?mode={flag}&immutable=1"
else:
uri = f"{uri}?mode={flag}"
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"

# This is an optimization only; it's ok if it fails.
with suppress(sqlite3.OperationalError):
self._cx.execute("PRAGMA journal_mode = wal")
if not self._readonly:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self._readonly is not used anywhere else, is it? You could just use flag != "ro" directly:

Suggested change
if not self._readonly:
if flag != "ro":

Sign up for free to join this conversation on . Already have an account? Sign in to comment
None yet

Successfully merging this pull request may close these issues.