{"html_url": "https://github.com/simonw/datasette/issues/1151#issuecomment-767762551", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1151", "id": 767762551, "node_id": "MDEyOklzc3VlQ29tbWVudDc2Nzc2MjU1MQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2021-01-26T19:07:44Z", "updated_at": "2021-01-26T19:07:44Z", "author_association": "OWNER", "body": "Mentioned in https://simonwillison.net/2021/Jan/25/datasette/", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 770448622, "label": "Database class mechanism for cross-connection in-memory databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1151#issuecomment-747801751", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1151", "id": 747801751, "node_id": "MDEyOklzc3VlQ29tbWVudDc0NzgwMTc1MQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-18T01:03:39Z", "updated_at": "2020-12-18T01:03:39Z", "author_association": "OWNER", "body": "This feature is illustrated by the tests: https://github.com/simonw/datasette/blob/5e9895c67f08e9f42acedd3d6d29512ac446e15f/tests/test_internals_database.py#L469-L496\r\n\r\nI added new documentation for the `Datasette()` constructor here as well: https://docs.datasette.io/en/latest/internals.html#database-ds-path-none-is-mutable-false-is-memory-false-memory-name-none", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 770448622, "label": "Database class mechanism for cross-connection in-memory databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1151#issuecomment-747801084", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1151", "id": 747801084, "node_id": "MDEyOklzc3VlQ29tbWVudDc0NzgwMTA4NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-18T01:01:26Z", "updated_at": "2020-12-18T01:01:26Z", "author_association": "OWNER", "body": "I tested this with a one-off plugin and it worked!\r\n\r\n```python\r\nfrom datasette import hookimpl\r\nfrom datasette.database import Database\r\n\r\n\r\n@hookimpl\r\ndef startup(datasette):\r\n datasette.add_database(\"statistics\", Database(\r\n datasette,\r\n memory_name=\"statistics\"\r\n ))\r\n```\r\nThis created a `/statistics` database when I ran `datasette` - and if I installed https://github.com/simonw/datasette-write I could then create tables in it which persisted until I restarted the server.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 770448622, "label": "Database class mechanism for cross-connection in-memory databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1151#issuecomment-747784199", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1151", "id": 747784199, "node_id": "MDEyOklzc3VlQ29tbWVudDc0Nzc4NDE5OQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-18T00:09:36Z", "updated_at": "2020-12-18T00:09:36Z", "author_association": "OWNER", "body": "Is it possible to connect to a memory database in read-only mode?\r\n\r\n`file:foo?mode=memory&cache=shared&mode=ro` isn't valid because it features `mode=` more than once.\r\n\r\nhttps://stackoverflow.com/a/40548682 suggests using `PRAGMA query_only` on the connection instead.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 770448622, "label": "Database class mechanism for cross-connection in-memory databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1151#issuecomment-747775245", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1151", "id": 747775245, "node_id": "MDEyOklzc3VlQ29tbWVudDc0Nzc3NTI0NQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-17T23:43:41Z", "updated_at": "2020-12-17T23:56:27Z", "author_association": "OWNER", "body": "I'm going to add an argument to the `Database()` constructor which means \"connect to named in-memory database called X\".\r\n\r\n```python\r\ndb = Database(ds, memory_name=\"datasette\")\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 770448622, "label": "Database class mechanism for cross-connection in-memory databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1151#issuecomment-747779056", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1151", "id": 747779056, "node_id": "MDEyOklzc3VlQ29tbWVudDc0Nzc3OTA1Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-17T23:55:57Z", "updated_at": "2020-12-17T23:55:57Z", "author_association": "OWNER", "body": "Wait I do use it - if you run `datasette --memory` - which is useful for trying things out in SQL that doesn't need to run against a table.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 770448622, "label": "Database class mechanism for cross-connection in-memory databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1151#issuecomment-747775792", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1151", "id": 747775792, "node_id": "MDEyOklzc3VlQ29tbWVudDc0Nzc3NTc5Mg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-17T23:45:20Z", "updated_at": "2020-12-17T23:45:20Z", "author_association": "OWNER", "body": "Do I use the current `is_memory=` boolean anywhere at the moment?\r\n\r\nhttps://ripgrep.datasette.io/-/ripgrep?pattern=is_memory - doesn't look like it.\r\n\r\nI may remove that feature, since it's not actually useful, and replace it with a mechanism for creating shared named memory databases instead.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 770448622, "label": "Database class mechanism for cross-connection in-memory databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1151#issuecomment-747774855", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1151", "id": 747774855, "node_id": "MDEyOklzc3VlQ29tbWVudDc0Nzc3NDg1NQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-17T23:42:34Z", "updated_at": "2020-12-17T23:42:34Z", "author_association": "OWNER", "body": "This worked as a prototype:\r\n```diff\r\ndiff --git a/datasette/database.py b/datasette/database.py\r\nindex 412e0c5..a90e617 100644\r\n--- a/datasette/database.py\r\n+++ b/datasette/database.py\r\n@@ -24,11 +24,12 @@ connections = threading.local()\r\n \r\n \r\n class Database:\r\n- def __init__(self, ds, path=None, is_mutable=False, is_memory=False):\r\n+ def __init__(self, ds, path=None, is_mutable=False, is_memory=False, uri=None):\r\n self.ds = ds\r\n self.path = path\r\n self.is_mutable = is_mutable\r\n self.is_memory = is_memory\r\n+ self.uri = uri\r\n self.hash = None\r\n self.cached_size = None\r\n self.cached_table_counts = None\r\n@@ -46,6 +47,8 @@ class Database:\r\n }\r\n \r\n def connect(self, write=False):\r\n+ if self.uri:\r\n+ return sqlite3.connect(self.uri, uri=True, check_same_thread=False)\r\n if self.is_memory:\r\n return sqlite3.connect(\":memory:\")\r\n # mode=ro or immutable=1?\r\n```\r\nThen in `ipython`:\r\n```\r\nfrom datasette.app import Datasette\r\nfrom datasette.database import Database\r\nds = Datasette([])\r\ndb = Database(ds, uri=\"file:datasette?mode=memory&cache=shared\", is_memory=True)\r\nawait db.execute_write(\"create table foo (bar text)\")\r\nawait db.table_names()\r\n# Outputs [\"foo\"]\r\ndb2 = Database(ds, uri=\"file:datasette?mode=memory&cache=shared\", is_memory=True)\r\nawait db2.table_names()\r\n# Also outputs [\"foo\"]\r\n```\r\n\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 770448622, "label": "Database class mechanism for cross-connection in-memory databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1151#issuecomment-747770581", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1151", "id": 747770581, "node_id": "MDEyOklzc3VlQ29tbWVudDc0Nzc3MDU4MQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-17T23:31:18Z", "updated_at": "2020-12-17T23:32:07Z", "author_association": "OWNER", "body": "This works in `ipython`:\r\n```\r\nIn [1]: import sqlite3\r\n\r\nIn [2]: c1 = sqlite3.connect(\"file:datasette?mode=memory&cache=shared\", uri=True)\r\n\r\nIn [3]: c2 = sqlite3.connect(\"file:datasette?mode=memory&cache=shared\", uri=True)\r\n\r\nIn [4]: c1.executescript(\"CREATE TABLE hello (world TEXT)\")\r\nOut[4]: \r\n\r\nIn [5]: c1.execute(\"select * from sqlite_master\").fetchall()\r\nOut[5]: [('table', 'hello', 'hello', 2, 'CREATE TABLE hello (world TEXT)')]\r\n\r\nIn [6]: c2.execute(\"select * from sqlite_master\").fetchall()\r\nOut[6]: [('table', 'hello', 'hello', 2, 'CREATE TABLE hello (world TEXT)')]\r\n\r\nIn [7]: c3 = sqlite3.connect(\"file:datasette?mode=memory&cache=shared\", uri=True)\r\n\r\nIn [9]: c3.execute(\"select * from sqlite_master\").fetchall()\r\nOut[9]: [('table', 'hello', 'hello', 2, 'CREATE TABLE hello (world TEXT)')]\r\n\r\nIn [10]: c4 = sqlite3.connect(\"file:datasette?mode=memory\", uri=True)\r\n\r\nIn [11]: c4.execute(\"select * from sqlite_master\").fetchall()\r\nOut[11]: []\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 770448622, "label": "Database class mechanism for cross-connection in-memory databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1151#issuecomment-747770082", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1151", "id": 747770082, "node_id": "MDEyOklzc3VlQ29tbWVudDc0Nzc3MDA4Mg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-17T23:29:53Z", "updated_at": "2020-12-17T23:29:53Z", "author_association": "OWNER", "body": "I'm going to try with `file:datasette?mode=memory&cache=shared`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 770448622, "label": "Database class mechanism for cross-connection in-memory databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1151#issuecomment-747769830", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1151", "id": 747769830, "node_id": "MDEyOklzc3VlQ29tbWVudDc0Nzc2OTgzMA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-17T23:29:08Z", "updated_at": "2020-12-17T23:29:08Z", "author_association": "OWNER", "body": "https://sqlite.org/inmemorydb.html\r\n\r\n> The database ceases to exist as soon as the database connection is closed. Every :memory: database is distinct from every other. So, opening two database connections each with the filename \":memory:\" will create two independent in-memory databases.\r\n>\r\n> [...]\r\n>\r\n> The special `\":memory:\"` filename also works when using URI filenames. For example:\r\n>\r\n> rc = sqlite3_open(\"file::memory:\", &db);\r\n>\r\n> [...]\r\n>\r\n> However, the same in-memory database can be opened by two or more database connections as follows:\r\n>\r\n> rc = sqlite3_open(\"file::memory:?cache=shared\", &db);\r\n>\r\n> [...]\r\n> If two or more distinct but shareable in-memory databases are needed in a single process, then the mode=memory query parameter can be used with a URI filename to create a named in-memory database:\r\n>\r\n> rc = sqlite3_open(\"file:memdb1?mode=memory&cache=shared\", &db);\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 770448622, "label": "Database class mechanism for cross-connection in-memory databases"}, "performed_via_github_app": null}