github
html_url | issue_url | id | node_id | user | created_at | updated_at | author_association | body | reactions | issue | performed_via_github_app |
---|---|---|---|---|---|---|---|---|---|---|---|
https://github.com/simonw/datasette/issues/1555#issuecomment-997320824 | https://api.github.com/repos/simonw/datasette/issues/1555 | 997320824 | IC_kwDOBm6k_c47ceh4 | 9599 | 2021-12-19T02:59:57Z | 2021-12-19T03:00:44Z | OWNER | To list all indexes: https://latest.datasette.io/fixtures?sql=SELECT%0D%0A++sqlite_master.name%2C%0D%0A++index_list.*%0D%0AFROM%0D%0A++sqlite_master%2C%0D%0A++pragma_index_list%28sqlite_master.name%29+AS+index_list%0D%0AWHERE%0D%0A++sqlite_master.type+%3D+%27table%27 ```sql SELECT sqlite_master.name, index_list.* FROM sqlite_master, pragma_index_list(sqlite_master.name) AS index_list WHERE sqlite_master.type = 'table' ``` Foreign keys: https://latest.datasette.io/fixtures?sql=SELECT%0D%0A++sqlite_master.name%2C%0D%0A++foreign_key_list.*%0D%0AFROM%0D%0A++sqlite_master%2C%0D%0A++pragma_foreign_key_list%28sqlite_master.name%29+AS+foreign_key_list%0D%0AWHERE%0D%0A++sqlite_master.type+%3D+%27table%27 ```sql SELECT sqlite_master.name, foreign_key_list.* FROM sqlite_master, pragma_foreign_key_list(sqlite_master.name) AS foreign_key_list WHERE sqlite_master.type = 'table' ``` | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1079149656 | |
https://github.com/simonw/datasette/issues/1555#issuecomment-997321115 | https://api.github.com/repos/simonw/datasette/issues/1555 | 997321115 | IC_kwDOBm6k_c47cemb | 9599 | 2021-12-19T03:03:12Z | 2021-12-19T03:03:12Z | OWNER | Table columns is a bit harder, because `table_xinfo` is only in SQLite 3.26.0 or higher: https://github.com/simonw/datasette/blob/d637ed46762fdbbd8e32b86f258cd9a53c1cfdc7/datasette/utils/__init__.py#L565-L581 So if that function is available: https://latest.datasette.io/fixtures?sql=SELECT%0D%0A++sqlite_master.name%2C%0D%0A++table_xinfo.*%0D%0AFROM%0D%0A++sqlite_master%2C%0D%0A++pragma_table_xinfo%28sqlite_master.name%29+AS+table_xinfo%0D%0AWHERE%0D%0A++sqlite_master.type+%3D+%27table%27 ```sql SELECT sqlite_master.name, table_xinfo.* FROM sqlite_master, pragma_table_xinfo(sqlite_master.name) AS table_xinfo WHERE sqlite_master.type = 'table' ``` And otherwise, using `table_info`: https://latest.datasette.io/fixtures?sql=SELECT%0D%0A++sqlite_master.name%2C%0D%0A++table_info.*%2C%0D%0A++0+as+hidden%0D%0AFROM%0D%0A++sqlite_master%2C%0D%0A++pragma_table_info%28sqlite_master.name%29+AS+table_info%0D%0AWHERE%0D%0A++sqlite_master.type+%3D+%27table%27 ```sql SELECT sqlite_master.name, table_info.*, 0 as hidden FROM sqlite_master, pragma_table_info(sqlite_master.name) AS table_info WHERE sqlite_master.type = 'table' ``` | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1079149656 | |
https://github.com/simonw/datasette/issues/1555#issuecomment-997321217 | https://api.github.com/repos/simonw/datasette/issues/1555 | 997321217 | IC_kwDOBm6k_c47ceoB | 9599 | 2021-12-19T03:04:16Z | 2021-12-19T03:04:16Z | OWNER | One thing to watch out for though, from https://sqlite.org/pragma.html#pragfunc > The table-valued functions for PRAGMA feature was added in SQLite version 3.16.0 (2017-01-02). Prior versions of SQLite cannot use this feature. | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1079149656 | |
https://github.com/simonw/datasette/issues/1555#issuecomment-997321327 | https://api.github.com/repos/simonw/datasette/issues/1555 | 997321327 | IC_kwDOBm6k_c47cepv | 9599 | 2021-12-19T03:05:39Z | 2021-12-19T03:05:44Z | OWNER | This caught me out once before in: - https://github.com/simonw/datasette/issues/1276 Turns out Glitch was running SQLite 3.11.0 from 2016-02-15. | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1079149656 | |
https://github.com/simonw/datasette/issues/1555#issuecomment-997321477 | https://api.github.com/repos/simonw/datasette/issues/1555 | 997321477 | IC_kwDOBm6k_c47cesF | 9599 | 2021-12-19T03:07:33Z | 2021-12-19T03:07:33Z | OWNER | If I want to continue supporting SQLite prior to 3.16.0 (2017-01-02) I'll need this optimization to only kick in with versions that support table-valued PRAGMA functions, while keeping the old `PRAGMA foreign_key_list(table)` stuff working for those older versions. That's feasible, but it's a bit more work - and I need to make sure I have robust testing in place for SQLite 3.15.0. | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1079149656 | |
https://github.com/simonw/datasette/issues/1555#issuecomment-997321653 | https://api.github.com/repos/simonw/datasette/issues/1555 | 997321653 | IC_kwDOBm6k_c47ceu1 | 9599 | 2021-12-19T03:09:43Z | 2021-12-19T03:09:43Z | OWNER | On that same documentation page I just spotted this: > This feature is experimental and is subject to change. Further documentation will become available if and when the table-valued functions for PRAGMAs feature becomes officially supported. This makes me nervous to rely on pragma function optimizations in Datasette itself. | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1079149656 | |
https://github.com/simonw/datasette/issues/1555#issuecomment-997321767 | https://api.github.com/repos/simonw/datasette/issues/1555 | 997321767 | IC_kwDOBm6k_c47cewn | 9599 | 2021-12-19T03:10:58Z | 2021-12-19T03:10:58Z | OWNER | I wonder how much overhead there is switching between the `async` event loop main code and the thread that runs the SQL queries. Would there be a performance boost if I gathered all of the column/index information in a single function run on the thread using `db.execute_fn()` I wonder? It would eliminate a bunch of switching between threads. Would be great to understand how much of an impact that would have. | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1079149656 | |
https://github.com/simonw/datasette/issues/1555#issuecomment-997324156 | https://api.github.com/repos/simonw/datasette/issues/1555 | 997324156 | IC_kwDOBm6k_c47cfV8 | 9599 | 2021-12-19T03:40:05Z | 2021-12-19T03:40:05Z | OWNER | Using the prototype of this: - https://github.com/simonw/datasette-pretty-traces/issues/5 I'm seeing about 180ms spent running all of these queries on startup! ![CleanShot 2021-12-18 at 19 38 37@2x](https://user-images.githubusercontent.com/9599/146663045-46bda669-90de-474f-8870-345182725dc1.png) | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1079149656 | |
https://github.com/simonw/datasette/issues/1555#issuecomment-997324666 | https://api.github.com/repos/simonw/datasette/issues/1555 | 997324666 | IC_kwDOBm6k_c47cfd6 | 9599 | 2021-12-19T03:47:51Z | 2021-12-19T03:48:09Z | OWNER | Here's a hacked together prototype of running all of that stuff inside a single function passed to `.execute_fn()`: ```diff diff --git a/datasette/utils/internal_db.py b/datasette/utils/internal_db.py index 95055d8..58f9982 100644 --- a/datasette/utils/internal_db.py +++ b/datasette/utils/internal_db.py @@ -1,4 +1,5 @@ import textwrap +from datasette.utils import table_column_details async def init_internal_db(db): @@ -70,49 +71,70 @@ async def populate_schema_tables(internal_db, db): "DELETE FROM tables WHERE database_name = ?", [database_name], block=True ) tables = (await db.execute("select * from sqlite_master WHERE type = 'table'")).rows - tables_to_insert = [] - columns_to_delete = [] - columns_to_insert = [] - foreign_keys_to_delete = [] - foreign_keys_to_insert = [] - indexes_to_delete = [] - indexes_to_insert = [] - for table in tables: - table_name = table["name"] - tables_to_insert.append( - (database_name, table_name, table["rootpage"], table["sql"]) - ) - columns_to_delete.append((database_name, table_name)) - columns = await db.table_column_details(table_name) - columns_to_insert.extend( - { - **{"database_name": database_name, "table_name": table_name}, - **column._asdict(), - } - for column in columns - ) - foreign_keys_to_delete.append((database_name, table_name)) - foreign_keys = ( - await db.execute(f"PRAGMA foreign_key_list([{table_name}])") - ).rows - foreign_keys_to_insert.extend( - { - **{"database_name": database_name, "table_name": table_name}, - **dict(foreign_key), - } - for foreign_key in foreign_keys - ) - indexes_to_delete.append((database_name, table_name)) - indexes = (await db.execute(f"PRAGMA index_list([{table_name}])")).rows - … | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1079149656 | |
https://github.com/simonw/datasette/issues/1555#issuecomment-997325189 | https://api.github.com/repos/simonw/datasette/issues/1555 | 997325189 | IC_kwDOBm6k_c47cfmF | 9599 | 2021-12-19T03:55:01Z | 2021-12-19T20:54:51Z | OWNER | It's a bit annoying that the queries no longer show up in the trace at all now, thanks to running in `.execute_fn()`. I wonder if there's something smart I can do about that - maybe have `trace()` record that function with a traceback even though it doesn't have the executed SQL string? 5fac26aa221a111d7633f2dd92014641f7c0ade9 has the same problem. | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1079149656 | |
https://github.com/simonw/datasette/issues/1555#issuecomment-997342494 | https://api.github.com/repos/simonw/datasette/issues/1555 | 997342494 | IC_kwDOBm6k_c47cj0e | 9599 | 2021-12-19T07:22:04Z | 2021-12-19T07:22:04Z | OWNER | Another option would be to provide an abstraction that makes it easier to run a group of SQL queries in the same thread at the same time, and have them traced correctly. | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1079149656 | |
https://github.com/simonw/datasette/issues/1566#issuecomment-997457790 | https://api.github.com/repos/simonw/datasette/issues/1566 | 997457790 | IC_kwDOBm6k_c47c_9- | 9599 | 2021-12-19T20:40:50Z | 2021-12-19T20:40:57Z | OWNER | Also release new version of `datasette-pretty-traces` with this feature: - https://github.com/simonw/datasette-pretty-traces/issues/7 | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1083669410 | |
https://github.com/simonw/datasette/issues/1555#issuecomment-997459637 | https://api.github.com/repos/simonw/datasette/issues/1555 | 997459637 | IC_kwDOBm6k_c47dAa1 | 9599 | 2021-12-19T20:53:46Z | 2021-12-19T20:53:46Z | OWNER | Using #1571 showed me that the `DELETE FROM columns/foreign_keys/indexes WHERE database_name = ? and table_name = ?` queries were running way more times than I expected. I came up with a new optimization that just does `DELETE FROM columns/foreign_keys/indexes WHERE database_name = ?` instead. | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1079149656 | |
https://github.com/simonw/datasette/issues/1555#issuecomment-997459958 | https://api.github.com/repos/simonw/datasette/issues/1555 | 997459958 | IC_kwDOBm6k_c47dAf2 | 9599 | 2021-12-19T20:55:59Z | 2021-12-19T20:55:59Z | OWNER | Closing this issue because I've optimized this a whole bunch, and it's definitely good enough for the moment. | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1079149656 | |
https://github.com/simonw/datasette/issues/1570#issuecomment-997460061 | https://api.github.com/repos/simonw/datasette/issues/1570 | 997460061 | IC_kwDOBm6k_c47dAhd | 9599 | 2021-12-19T20:56:54Z | 2021-12-19T20:56:54Z | OWNER | Documentation: https://docs.datasette.io/en/latest/internals.html#await-db-execute-write-sql-params-none-block-false | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1083921371 | |
https://github.com/simonw/datasette/issues/1547#issuecomment-997460731 | https://api.github.com/repos/simonw/datasette/issues/1547 | 997460731 | IC_kwDOBm6k_c47dAr7 | 9599 | 2021-12-19T21:02:15Z | 2021-12-19T21:02:15Z | OWNER | Yes, this is a bug. It looks like the problem is with the `if write:` branch in this code here: https://github.com/simonw/datasette/blob/5fac26aa221a111d7633f2dd92014641f7c0ade9/datasette/views/database.py#L252-L327 Is missing this bit of code: https://github.com/simonw/datasette/blob/5fac26aa221a111d7633f2dd92014641f7c0ade9/datasette/views/database.py#L343-L347 | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1076388044 | |
https://github.com/simonw/datasette/issues/1573#issuecomment-997462117 | https://api.github.com/repos/simonw/datasette/issues/1573 | 997462117 | IC_kwDOBm6k_c47dBBl | 9599 | 2021-12-19T21:13:13Z | 2021-12-19T21:13:13Z | OWNER | This might also be the impetus I need to bring the https://datasette.io/plugins/datasette-pretty-traces plugin into Datasette core itself. | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1084185188 | |
https://github.com/simonw/datasette/issues/1545#issuecomment-997462604 | https://api.github.com/repos/simonw/datasette/issues/1545 | 997462604 | IC_kwDOBm6k_c47dBJM | 9599 | 2021-12-19T21:17:08Z | 2021-12-19T21:17:08Z | OWNER | Here's the relevant code: https://github.com/simonw/datasette/blob/4094741c2881c2ada3f3f878b532fdaec7914953/datasette/app.py#L1204-L1219 It's using `route_path.split("/")` which should be OK because that's the incoming `request.path` path - which I would expect to use `/` even on Windows. Then it uses `os.path.join` which should do the right thing. I need to get myself a proper Windows development environment setup to investigate this one. | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1075893249 | |
https://github.com/simonw/datasette/issues/1566#issuecomment-997470633 | https://api.github.com/repos/simonw/datasette/issues/1566 | 997470633 | IC_kwDOBm6k_c47dDGp | 9599 | 2021-12-19T22:12:00Z | 2021-12-19T22:12:00Z | OWNER | Released another alpha, 0.60a1: https://github.com/simonw/datasette/releases/tag/0.60a1 | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1083669410 | |
https://github.com/simonw/datasette/issues/1547#issuecomment-997471672 | https://api.github.com/repos/simonw/datasette/issues/1547 | 997471672 | IC_kwDOBm6k_c47dDW4 | 9599 | 2021-12-19T22:18:26Z | 2021-12-19T22:18:26Z | OWNER | I released this [in an alpha](https://github.com/simonw/datasette/releases/tag/0.60a1), so you can try out this fix using: pip install datasette==0.60a1 | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1076388044 | |
https://github.com/simonw/datasette/issues/1518#issuecomment-997472214 | https://api.github.com/repos/simonw/datasette/issues/1518 | 997472214 | IC_kwDOBm6k_c47dDfW | 9599 | 2021-12-19T22:22:08Z | 2021-12-19T22:22:08Z | OWNER | I sketched out a chained SQL builder pattern that might be useful for further tidying up this code - though with the new plugin hook I'm less excited about it than I was: ```python class TableQuery: def __init__(self, table, columns, pks, is_view=False, prev=None): self.table = table self.columns = columns self.pks = pks self.is_view = is_view self.prev = prev # These can be changed for different instances in the chain: self._where_clauses = None self._order_by = None self._page_size = None self._offset = None self._select_columns = None self.select_all_columns = '*' self.select_specified_columns = '*' @property def where_clauses(self): wheres = [] current = self while current: if current._where_clauses is not None: wheres.extend(current._where_clauses) current = current.prev return list(reversed(wheres)) def where(self, where): new_cls = TableQuery(self.table, self.columns, self.pks, self.is_view, self) new_cls._where_clauses = [where] return new_cls @classmethod async def introspect(cls, db, table): return cls( table, columns = await db.table_columns(table), pks = await db.primary_keys(table), is_view = bool(await db.get_view_definition(table)) ) @property def sql_from(self): return f"from {self.table}{self.sql_where}" @property def sql_where(self): if not self.where_clauses: return "" else: return f" where {' and '.join(self.where_clauses)}" @property def sql_no_order_no_limit(self): return f"select {self.select_all_columns} from {self.table}{self.sql_where}" @property def sql(self): return f"select {self.select_specified_columns} from {se… | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1058072543 | |
https://github.com/simonw/datasette/issues/1565#issuecomment-997472370 | https://api.github.com/repos/simonw/datasette/issues/1565 | 997472370 | IC_kwDOBm6k_c47dDhy | 9599 | 2021-12-19T22:23:36Z | 2021-12-19T22:23:36Z | OWNER | This should also expose the JSON API endpoints used to execute SQL against this database. | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1083657868 | |
https://github.com/simonw/datasette/issues/1565#issuecomment-997472509 | https://api.github.com/repos/simonw/datasette/issues/1565 | 997472509 | IC_kwDOBm6k_c47dDj9 | 9599 | 2021-12-19T22:24:50Z | 2021-12-19T22:24:50Z | OWNER | ... huh, it could even expose a JavaScript function that can be called to execute a SQL query. ```javascript datasette.query("select * from blah").then(...) ``` Maybe it takes an optional second argument that specifies the database - defaulting to the one for the current page. | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1083657868 | |
https://github.com/simonw/datasette/issues/1565#issuecomment-997472639 | https://api.github.com/repos/simonw/datasette/issues/1565 | 997472639 | IC_kwDOBm6k_c47dDl_ | 9599 | 2021-12-19T22:25:50Z | 2021-12-19T22:25:50Z | OWNER | Or... ```javascript rows = await datasette.query`select * from searchable where id > ${id}`; ``` And it knows how to turn that into a parameterized call using tagged template literals. | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1083657868 | |
https://github.com/simonw/datasette/issues/1565#issuecomment-997473856 | https://api.github.com/repos/simonw/datasette/issues/1565 | 997473856 | IC_kwDOBm6k_c47dD5A | 9599 | 2021-12-19T22:35:20Z | 2021-12-19T22:35:20Z | OWNER | Quick prototype of that tagged template `query` function: ```javascript function query(pieces, ...parameters) { var qs = new URLSearchParams(); var sql = pieces[0]; parameters.forEach((param, i) => { sql += `:p${i}${pieces[i + 1]}`; qs.append(`p${i}`, param); }); qs.append("sql", sql); return qs.toString(); } var id = 4; console.log(query`select * from ids where id > ${id}`); ``` Outputs: ``` p0=4&sql=select+*+from+ids+where+id+%3E+%3Ap0 ``` | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1083657868 | |
https://github.com/simonw/datasette/issues/1565#issuecomment-997474022 | https://api.github.com/repos/simonw/datasette/issues/1565 | 997474022 | IC_kwDOBm6k_c47dD7m | 9599 | 2021-12-19T22:36:49Z | 2021-12-19T22:37:29Z | OWNER | No way with a tagged template literal to pass an extra database name argument, so instead I need a method that returns a callable that can be used for the tagged template literal for a specific database - or the default database. This could work (bit weird looking though): ```javascript var rows = await datasette.query("fixtures")`select * from foo`; ``` | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1083657868 | |
https://github.com/simonw/sqlite-utils/issues/356#issuecomment-997485361 | https://api.github.com/repos/simonw/sqlite-utils/issues/356 | 997485361 | IC_kwDOCGYnMM47dGsx | 9599 | 2021-12-19T23:45:30Z | 2021-12-19T23:45:30Z | OWNER | Really interesting example input for this: https://blog.timac.org/2021/1219-state-of-swift-and-swiftui-ios15/iOS13.txt - see https://blog.timac.org/2021/1219-state-of-swift-and-swiftui-ios15/ | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1077431957 | |
https://github.com/simonw/sqlite-utils/issues/356#issuecomment-997486156 | https://api.github.com/repos/simonw/sqlite-utils/issues/356 | 997486156 | IC_kwDOCGYnMM47dG5M | 9599 | 2021-12-19T23:51:02Z | 2021-12-19T23:51:02Z | OWNER | This is going to need a `--import` multi option too. | {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} | 1077431957 |