From 209bdee0e8d1b578c48fe6dbc2e51739dc1d1f0a Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Tue, 18 Feb 2025 10:23:23 -0800 Subject: [PATCH] Don't run prepare_connection() on internal database, closes #2468 --- datasette/app.py | 9 ++++++--- docs/plugin_hooks.rst | 2 ++ tests/test_plugins.py | 5 +++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/datasette/app.py b/datasette/app.py index d1d6c345..bf6cc03f 100644 --- a/datasette/app.py +++ b/datasette/app.py @@ -116,6 +116,8 @@ app_root = Path(__file__).parent.parent # https://github.com/simonw/datasette/issues/283#issuecomment-781591015 SQLITE_LIMIT_ATTACHED = 10 +INTERNAL_DB_NAME = "__INTERNAL__" + Setting = collections.namedtuple("Setting", ("name", "default", "help")) SETTINGS = ( Setting("default_page_size", 100, "Default page size for the table view"), @@ -328,7 +330,7 @@ class Datasette: self._internal_database = Database(self, memory_name=secrets.token_hex()) else: self._internal_database = Database(self, path=internal, mode="rwc") - self._internal_database.name = "__INTERNAL__" + self._internal_database.name = INTERNAL_DB_NAME self.cache_headers = cache_headers self.cors = cors @@ -878,7 +880,7 @@ class Datasette: def _prepare_connection(self, conn, database): conn.row_factory = sqlite3.Row conn.text_factory = lambda x: str(x, "utf-8", "replace") - if self.sqlite_extensions: + if self.sqlite_extensions and database != INTERNAL_DB_NAME: conn.enable_load_extension(True) for extension in self.sqlite_extensions: # "extension" is either a string path to the extension @@ -891,7 +893,8 @@ class Datasette: if self.setting("cache_size_kb"): conn.execute(f"PRAGMA cache_size=-{self.setting('cache_size_kb')}") # pylint: disable=no-member - pm.hook.prepare_connection(conn=conn, database=database, datasette=self) + if database != INTERNAL_DB_NAME: + pm.hook.prepare_connection(conn=conn, database=database, datasette=self) # If self.crossdb and this is _memory, connect the first SQLITE_LIMIT_ATTACHED databases if self.crossdb and database == "_memory": count = 0 diff --git a/docs/plugin_hooks.rst b/docs/plugin_hooks.rst index 0d25fbfc..84db9818 100644 --- a/docs/plugin_hooks.rst +++ b/docs/plugin_hooks.rst @@ -57,6 +57,8 @@ arguments and can be called like this:: select random_integer(1, 10); +``prepare_connection()`` hooks are not called for Datasette's :ref:`internal database `. + Examples: `datasette-jellyfish `__, `datasette-jq `__, `datasette-haversine `__, `datasette-rure `__ .. _plugin_hook_prepare_jinja2_environment: diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 8883c87a..33cacbcd 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -59,6 +59,11 @@ async def test_hook_plugin_prepare_connection_arguments(ds_client): "database=fixtures, datasette.plugin_config(\"name-of-plugin\")={'depth': 'root'}" ] == response.json() + # Function should not be available on the internal database + db = ds_client.ds.get_internal_database() + with pytest.raises(sqlite3.OperationalError): + await db.execute("select prepare_connection_args()") + @pytest.mark.asyncio @pytest.mark.parametrize(