html_url,issue_url,id,node_id,user,user_label,created_at,updated_at,author_association,body,reactions,issue,issue_label,performed_via_github_app https://github.com/simonw/datasette/issues/1744#issuecomment-1129184908,https://api.github.com/repos/simonw/datasette/issues/1744,1129184908,IC_kwDOBm6k_c5DTf6M,9599,simonw,2022-05-17T18:25:57Z,2022-05-17T18:25:57Z,OWNER,"I knocked out a quick prototype of this and it worked! datasette ~/Library/Application\ Support/Google/Chrome/Default/History --nolock Here's the prototype diff: ```diff diff --git a/datasette/app.py b/datasette/app.py index b7b8437..f43700d 100644 --- a/datasette/app.py +++ b/datasette/app.py @@ -213,6 +213,7 @@ class Datasette: config_dir=None, pdb=False, crossdb=False, + nolock=False, ): assert config_dir is None or isinstance( config_dir, Path @@ -238,6 +239,7 @@ class Datasette: self.databases = collections.OrderedDict() self._refresh_schemas_lock = asyncio.Lock() self.crossdb = crossdb + self.nolock = nolock if memory or crossdb or not self.files: self.add_database(Database(self, is_memory=True), name=""_memory"") # memory_name is a random string so that each Datasette instance gets its own diff --git a/datasette/cli.py b/datasette/cli.py index 3c6e1b2..7e44665 100644 --- a/datasette/cli.py +++ b/datasette/cli.py @@ -452,6 +452,11 @@ def uninstall(packages, yes): is_flag=True, help=""Enable cross-database joins using the /_memory database"", ) +@click.option( + ""--nolock"", + is_flag=True, + help=""Ignore locking and open locked files in read-only mode"", +) @click.option( ""--ssl-keyfile"", help=""SSL key file"", @@ -486,6 +491,7 @@ def serve( open_browser, create, crossdb, + nolock, ssl_keyfile, ssl_certfile, return_instance=False, @@ -545,6 +551,7 @@ def serve( version_note=version_note, pdb=pdb, crossdb=crossdb, + nolock=nolock, ) # if files is a single directory, use that as config_dir= diff --git a/datasette/database.py b/datasette/database.py index 44d3266..fa55804 100644 --- a/datasette/database.py +++ b/datasette/database.py @@ -89,6 +89,8 @@ class Database: # mode=ro or immutable=1? if self.is_mutable: qs = ""?mode=ro"" + if self.ds.nolock: + qs += ""&nolock=1"" else: qs = ""?immutable=1"" assert not (write and not self.is_mutable) ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1239008850,`--nolock` feature for opening locked databases, https://github.com/simonw/datasette/issues/1744#issuecomment-1129185356,https://api.github.com/repos/simonw/datasette/issues/1744,1129185356,IC_kwDOBm6k_c5DTgBM,9599,simonw,2022-05-17T18:26:26Z,2022-05-17T18:26:26Z,OWNER,Not sure how to test this - I'd need to open my own lock against a database file somehow.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1239008850,`--nolock` feature for opening locked databases, https://github.com/simonw/datasette/issues/1744#issuecomment-1129187486,https://api.github.com/repos/simonw/datasette/issues/1744,1129187486,IC_kwDOBm6k_c5DTgie,9599,simonw,2022-05-17T18:28:49Z,2022-05-17T18:28:49Z,OWNER,I think I do that with `fcntl.flock()`: https://docs.python.org/3/library/fcntl.html#fcntl.flock,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1239008850,`--nolock` feature for opening locked databases, https://github.com/simonw/datasette/issues/1744#issuecomment-1129241873,https://api.github.com/repos/simonw/datasette/issues/1744,1129241873,IC_kwDOBm6k_c5DTt0R,9599,simonw,2022-05-17T19:33:16Z,2022-05-17T19:33:16Z,OWNER,"I'm going to skip adding a test for this - the test logic would have to be pretty convoluted to exercise it properly, and it's a pretty minor and low-risk feature in the scheme of things.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1239008850,`--nolock` feature for opening locked databases, https://github.com/simonw/datasette/issues/1744#issuecomment-1129241283,https://api.github.com/repos/simonw/datasette/issues/1744,1129241283,IC_kwDOBm6k_c5DTtrD,9599,simonw,2022-05-17T19:32:35Z,2022-05-17T19:32:35Z,OWNER,"I tried writing a test like this: ```python @pytest.mark.parametrize(""locked"", (True, False)) def test_locked_sqlite_db(tmp_path_factory, locked): dir = tmp_path_factory.mktemp(""test_locked_sqlite_db"") test_db = str(dir / ""test.db"") sqlite3.connect(test_db).execute(""create table t (id integer primary key)"") if locked: fp = open(test_db, ""w"") fcntl.lockf(fp.fileno(), fcntl.LOCK_EX) runner = CliRunner() result = runner.invoke( cli, [ ""serve"", ""--memory"", ""--get"", ""/test"", ], catch_exceptions=False, ) ``` But it didn't work, because the test runs in the same process - so taking an exclusive lock on that file didn't cause an error when the test later tried to access it via Datasette!","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1239008850,`--nolock` feature for opening locked databases, https://github.com/simonw/datasette/issues/1744#issuecomment-1129243427,https://api.github.com/repos/simonw/datasette/issues/1744,1129243427,IC_kwDOBm6k_c5DTuMj,9599,simonw,2022-05-17T19:35:02Z,2022-05-17T19:35:02Z,OWNER,"One thing to note is that the `datasette-copy-to-memory` plugin broke with a locked file, because it does this: https://github.com/simonw/datasette-copy-to-memory/blob/d541c18a78ae6f707a8f9b1e7fc4c020a9f68f2e/datasette_copy_to_memory/__init__.py#L27 ```python tmp.execute(""ATTACH DATABASE ? AS _copy_from"", [db.path]) ``` That would need to use a URI filename too for it to work with locked files.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1239008850,`--nolock` feature for opening locked databases, https://github.com/simonw/datasette/issues/1744#issuecomment-1129251699,https://api.github.com/repos/simonw/datasette/issues/1744,1129251699,IC_kwDOBm6k_c5DTwNz,9599,simonw,2022-05-17T19:44:47Z,2022-05-17T19:46:38Z,OWNER,Updated docs: https://docs.datasette.io/en/latest/getting_started.html#using-datasette-on-your-own-computer and https://docs.datasette.io/en/latest/cli-reference.html#datasette-serve-help,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1239008850,`--nolock` feature for opening locked databases, https://github.com/simonw/datasette/issues/1745#issuecomment-1129252603,https://api.github.com/repos/simonw/datasette/issues/1745,1129252603,IC_kwDOBm6k_c5DTwb7,9599,simonw,2022-05-17T19:45:51Z,2022-05-17T19:45:51Z,OWNER,Now documented here: https://docs.datasette.io/en/latest/contributing.html#running-cog,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1239080102,Documentation on running cog, https://github.com/simonw/sqlite-utils/issues/425#issuecomment-1129332959,https://api.github.com/repos/simonw/sqlite-utils/issues/425,1129332959,IC_kwDOCGYnMM5DUEDf,102771161,McEazy2700,2022-05-17T21:27:02Z,2022-05-17T21:27:02Z,NONE,"Hi, I'm trying to deploy my site using elasticbeanstalk and I keep getting this same error : deterministic=True requires SQLite 3.8.3 or higher I saw your previous solution that involves editing sqlite-utils/sqlite_utils/db.py file, but I'm curious as to how that will work in production.","{""total_count"": 5, ""+1"": 5, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1203842656,`sqlite3.NotSupportedError`: deterministic=True requires SQLite 3.8.3 or higher,