Adds a per-request cache for permission check results, plus wiring that
resolves action permissions in bulk before plugin hooks need them:
- New _permission_check_cache contextvar, set to a fresh dict for each
request by DatasetteRouter and reset when the request ends. Keys
include the full serialized actor, so actors differing in any field
(e.g. token restrictions) never share entries. SkipPermissions mode
bypasses the cache entirely.
- datasette.allowed_many() now consults the cache and stores its
results there, so repeated datasette.allowed() checks within one
request resolve without further SQL.
- Table pages resolve all registered table-level actions against the
current table and all database-level actions against its database
(database pages likewise) in batched queries before invoking the
table_actions/database_actions plugin hooks - allowed() calls made
inside those hooks are then served from the cache with no plugin
changes required. Actions with no permission rules from any plugin
are resolved to False without touching the database.
Benchmarks (benchmarks/) with a simulated 12-plugin ecosystem making
18 checks per table page show 34 -> 13 internal-DB queries per page;
with 2ms-per-query internal DB latency (modelling Datasette Cloud)
table page time drops from 77.9ms to 27.6ms - the caching layer
accounts for ~91% of that improvement over allowed_many() alone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review of the generated ?_extra= documentation found several extras
with no example output or with examples that needed explanation:
- extras: now shows an abbreviated example of the toggle list and has
a clearer description (which also improves the live API output)
- set_column_type_ui: example of the shape seen with set-column-type
permission, plus a note that it is null otherwise
- column_types: live example generated from a table with an assigned
column type instead of an empty {}
- metadata: live table example now demonstrates a table description
and column descriptions; row and query examples gained explanatory
notes
- expandable_columns, foreign_key_tables, facets_timed_out, next_url,
renderers: notes explaining the shape of their output
Also added docs_note cross-references to the relevant documentation:
facets, pagination, render_cell and register_output_renderer plugin
hooks, column type configuration and API, metadata, custom templates,
permissions and foreign key label expansion. foreign_key_tables is
now flagged as potentially executing additional queries.
https://claude.ai/code/session_01EfjBe6E817m9XNFW7EX3Vm
Co-authored-by: Claude <noreply@anthropic.com>
These three extras return values that exist for the HTML templates -
a Filters instance, an async function and markupsafe/sqlite3.Row data
- so requesting them on a .json page returned a 500 serialization
error, while the generated documentation and ?_extra=extras both
advertised them as API surface. They are now public=False: ignored
like any unknown name on JSON requests, omitted from the docs and the
extras list, and still resolved for the HTML view via the new
include_internal flag on ExtraRegistry.resolve().
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Add web UI to edit and delete stored queries
Stored query pages now offer Edit and Delete actions in the query
actions menu, gated by the update-query and delete-query permissions.
- New QueryEditView (GET/POST at /<db>/<query>/-/edit) renders a
pre-filled form for editing a query's title, description, SQL and
privacy, reusing the create-query analysis UI. Changing the SQL still
requires execute-sql; metadata-only edits do not.
- QueryDeleteView gains a GET confirmation page and HTML form POST that
redirects to the query list, while keeping the existing JSON API.
- New default query_actions hook adds the Edit/Delete links for stored
(non-config, non-trusted) queries the actor is allowed to manage.
Permission semantics (already enforced by default_query_permissions_sql)
are surfaced in the UI: owners can always edit/delete their queries;
non-private queries can be edited/deleted by any actor with the relevant
permission; private queries remain owner-only.
Shared the create-query form styles into _query_form_styles.html so the
edit form can reuse them.
Animated demo: https://github.com/simonw/datasette/pull/2764#issuecomment-4655694668Closes#2760https://claude.ai/code/session_019GU9g3pZAERukLKYNa4uAL
* Fix for execute write returning, closes#2762
* Fix stored write returning rowcount message
* Add configurable execute_write returning limit
* Return rows/truncated from execute query if it used RETURNING
* INSERT ... RETURNING shows rows in /-/execute-write
* Skip RETURNING tests if SQLite version does not support it
Screenshot: https://github.com/simonw/datasette/issues/2762#issuecomment-4588111545
Expand the unreleased changelog with the deny-by-default operation analysis model, SQL function handling, and the VACUUM and virtual/shadow table restrictions for user-supplied write SQL.
Clarify the /-/execute-write JSON API documentation with the same restrictions and DDL permission requirements.