SQLite Forum

Any reason "PRAGMA schema_version;" might return "attempt to write a readonly database"
Login

Any reason "PRAGMA schema_version;" might return "attempt to write a readonly database"

(1) By Simon Willison (simonw) on 2023-04-11 23:37:03 [source]

I have a user who reported the following error running my Datasette application on their own server - they got a "attempt to write a readonly database" error, which appears to have been triggered by running the read operation "PRAGMA schema_version" to read the current version of the schema.

Here's the full log:

File "/usr/local/lib/python3.11/site-packages/datasette/views/base.py", line 89, in dispatch_request
await self.ds.refresh_schemas()
File "/usr/local/lib/python3.11/site-packages/datasette/app.py", line 371, in refresh_schemas
await self._refresh_schemas()
File "/usr/local/lib/python3.11/site-packages/datasette/app.py", line 386, in _refresh_schemas
schema_version = (await db.execute("PRAGMA schema_version")).first()[0]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/datasette/database.py", line 267, in execute
results = await self.execute_fn(sql_operation_in_thread)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/datasette/database.py", line 213, in execute_fn
return await asyncio.get_event_loop().run_in_executor(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/datasette/database.py", line 211, in in_thread
return fn(conn)
^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/datasette/database.py", line 237, in sql_operation_in_thread
cursor.execute(sql, params if params is not None else {})
sqlite3.OperationalError: attempt to write a readonly database

Since "PRAGMA schema_version" is a read and not a write operation I'm surprised to see this error message. Any ideas what could be going wrong here?

(2.1) By Simon Willison (simonw) on 2023-04-12 01:56:25 edited from 2.0 in reply to 1 [link] [source]

If it turns out I can't rely on PRAGMA schema_version to always give me an indication if a schema has changed without risk of it throwing this error in some environments, I'm going to need an alternative schema-change-detection mechanism.

Top contender at the moment is this:

schema = db.execute(
    "select group_concat(sql) from sqlite_master"
).fetchall()[0]
hash = hashlib.md5(schema).hexdigest()

Then compare that hash with the previous value to see if it's changed.

I ran a micro-benchmark (actually got ChatGPT to run a micro-benchmark) on this and found that the performance isn't an order of magnitude worse. Does anyone else have a better alternative?

Here's my write-up of the benchmark: https://simonwillison.net/2023/Apr/12/code-interpreter/

(3) By mgr (mgrmgr) on 2023-04-12 14:22:48 in reply to 1 [link] [source]

Did you try a select from the table valued function pragma_schema_version instead? Like

select * from pragma_schema_version;

Maybe the "traditional style call"

PRAGMA schema_version;
sometimes checks for writing, as it can be used like PRAGMA schema_version=xxx according to pragma_schema_version

... untested, just an idea ...

(4) By Richard Hipp (drh) on 2023-04-12 14:39:30 in reply to 1 [link] [source]

My guess: There is a hot journal that needs to be rolled back. Write privilege is needed to roll back the hot journal before the read operation can proceed.