From de55a76d402a6326c60a5f4cd1a03c7476613f0b Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Mon, 25 May 2026 12:33:57 -0700 Subject: [PATCH] Fix 500 error when accessing query page without ?sql= parameter (#2744) Closes #2743 --- datasette/templates/query.html | 4 ++-- datasette/views/database.py | 43 ++++++++++++++++++---------------- docs/changelog.rst | 7 ++++++ tests/plugins/my_plugin.py | 4 ++-- tests/test_html.py | 16 +++++++++++++ 5 files changed, 50 insertions(+), 24 deletions(-) diff --git a/datasette/templates/query.html b/datasette/templates/query.html index 8b405da5..5f85ac6b 100644 --- a/datasette/templates/query.html +++ b/datasette/templates/query.html @@ -46,14 +46,14 @@ {% if not hide_sql %} {% if editable and allow_execute_sql %}

+ >{% if query and query.sql %}{{ query.sql }}{% elif tables %}select * from {{ tables[0].name|escape_sqlite }}{% endif %}

{% else %}
{% if query %}{{ query.sql }}{% endif %}
{% endif %} {% else %} {% if not canned_query %} {% endif %} {% endif %} diff --git a/datasette/views/database.py b/datasette/views/database.py index 0cf93832..8e4ea85a 100644 --- a/datasette/views/database.py +++ b/datasette/views/database.py @@ -577,7 +577,7 @@ class QueryView(View): named_parameters = [] if canned_query and canned_query.get("params"): named_parameters = canned_query["params"] - if not named_parameters: + if not named_parameters and sql: named_parameters = derive_named_parameters(sql) named_parameter_values = { named_parameter: params.get(named_parameter) or "" @@ -602,7 +602,7 @@ class QueryView(View): params_for_query = params - if not canned_query_write: + if sql and not canned_query_write: try: if not canned_query: # For regular queries we only allow SELECT, plus other rules @@ -646,6 +646,8 @@ class QueryView(View): # Handle formats from plugins if format_ == "csv": + if not sql: + raise DatasetteError("?sql= is required", status=400) async def fetch_data_for_csv(request, _next=None): results = await db.execute(sql, params, truncate=True) @@ -771,25 +773,26 @@ class QueryView(View): # - No magic parameters, so no :_ in the SQL string edit_sql_url = None is_validated_sql = False - try: - validate_sql_select(sql) - is_validated_sql = True - except InvalidSql: - pass - if allow_execute_sql and is_validated_sql and ":_" not in sql: - edit_sql_url = ( - datasette.urls.database(database) - + "/-/query" - + "?" - + urlencode( - { - **{ - "sql": sql, - }, - **named_parameter_values, - } + if sql: + try: + validate_sql_select(sql) + is_validated_sql = True + except InvalidSql: + pass + if allow_execute_sql and is_validated_sql and ":_" not in sql: + edit_sql_url = ( + datasette.urls.database(database) + + "/-/query" + + "?" + + urlencode( + { + **{ + "sql": sql, + }, + **named_parameter_values, + } + ) ) - ) async def query_actions(): query_actions = [] diff --git a/docs/changelog.rst b/docs/changelog.rst index 329b4769..dfb2a736 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,13 @@ Changelog ========= +.. _v1_0_unreleased: + +Unreleased +---------- + +- Fixed a bug where visiting ``//-/query`` without a ``?sql=`` parameter returned a 500 error. (:issue:`2743`) + .. _v1_0_a30: 1.0a30 (2026-05-24) diff --git a/tests/plugins/my_plugin.py b/tests/plugins/my_plugin.py index 4e401c07..f682e8b9 100644 --- a/tests/plugins/my_plugin.py +++ b/tests/plugins/my_plugin.py @@ -387,8 +387,8 @@ def view_actions(datasette, database, view, actor): @hookimpl def query_actions(datasette, database, query_name, sql): - # Don't explain an explain - if sql.lower().startswith("explain"): + # Don't explain an explain (or a missing query) + if not sql or sql.lower().startswith("explain"): return return [ { diff --git a/tests/test_html.py b/tests/test_html.py index efc1040d..d20796c9 100644 --- a/tests/test_html.py +++ b/tests/test_html.py @@ -241,6 +241,22 @@ def test_query_page_truncates(): ] +@pytest.mark.asyncio +async def test_query_page_with_no_sql(ds_client): + # https://github.com/simonw/datasette/issues/2743 + response = await ds_client.get("/fixtures/-/query") + assert response.status_code == 200 + assert '