This commit is contained in:
max 2026-10-06 21:01:00 +03:00 • committed by GitHub
commit 1cc6f3e0f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 30 additions and 42 deletions

View file

@ -1,6 +1,9 @@
import csv
import hashlib
import sys
import textwrap
import markupsafe
from datasette.utils import (
EscapeHtmlWriter,
@ -39,6 +42,29 @@ class DatasetteError(Exception):
self.plain_message = plain_message
def query_interrupted_error(ex):
"""The SQL time limit error page, shared by every view that runs user SQL."""
return DatasetteError(
textwrap.dedent(f"""
<p>SQL query took too long. The time limit is controlled by the
<a href="https://docs.datasette.io/en/stable/settings.html#sql-time-limit-ms">sql_time_limit_ms</a>
configuration option.</p>
<textarea style="width: 90%">{markupsafe.escape(ex.sql)}</textarea>
<script>
let ta = document.querySelector("textarea");
ta.style.height = ta.scrollHeight + "px";
</script>
""").strip(),
title="SQL Interrupted",
status=400,
message_is_html=True,
plain_message=(
"SQL query took too long. The time limit is"
" controlled by the sql_time_limit_ms setting."
),
)
class View:
async def head(self, request, datasette):
if not hasattr(self, "get"):

View file

@ -3,7 +3,6 @@ import hashlib
import itertools
import json
import os
import textwrap
from dataclasses import asdict, dataclass, field
from urllib.parse import parse_qsl, urlencode
@ -39,7 +38,7 @@ from datasette.utils.asgi import AsgiFileDownload, Forbidden, NotFound, Response
from datasette.write_sql import QueryWriteRejected
from . import Context
from .base import DatasetteError, View, stream_csv
from .base import DatasetteError, View, query_interrupted_error, stream_csv
from .query_helpers import (
_block_framing,
_ensure_stored_query_execution_permissions,
@ -819,25 +818,7 @@ class QueryView(View):
columns = results.columns
rows = results.rows
except QueryInterrupted as ex:
raise DatasetteError(
textwrap.dedent(f"""
<p>SQL query took too long. The time limit is controlled by the
<a href="https://docs.datasette.io/en/stable/settings.html#sql-time-limit-ms">sql_time_limit_ms</a>
configuration option.</p>
<textarea style="width: 90%">{markupsafe.escape(ex.sql)}</textarea>
<script>
let ta = document.querySelector("textarea");
ta.style.height = ta.scrollHeight + "px";
</script>
""").strip(),
title="SQL Interrupted",
status=400,
message_is_html=True,
plain_message=(
"SQL query took too long. The time limit is"
" controlled by the sql_time_limit_ms setting."
),
)
raise query_interrupted_error(ex)
except sqlite3.DatabaseError as ex:
query_error = str(ex)
results = None

View file

@ -1,6 +1,5 @@
import asyncio
import json
import textwrap
import time
import urllib.parse
from dataclasses import dataclass, field
@ -35,7 +34,7 @@ from datasette.utils.asgi import Forbidden, NotFound, PayloadTooLarge, Response
from datasette.utils.sqlite import check_structured_write_table
from . import Context, from_extra
from .base import BaseView, DatasetteError, stream_csv
from .base import BaseView, DatasetteError, query_interrupted_error, stream_csv
from .table import (
_table_page_data,
display_columns_and_rows,
@ -195,25 +194,7 @@ class RowView(BaseView):
else:
data, extra_template_data, templates = response_or_template_contexts
except QueryInterrupted as ex:
raise DatasetteError(
textwrap.dedent(f"""
<p>SQL query took too long. The time limit is controlled by the
<a href="https://docs.datasette.io/en/stable/settings.html#sql-time-limit-ms">sql_time_limit_ms</a>
configuration option.</p>
<textarea style="width: 90%">{markupsafe.escape(ex.sql)}</textarea>
<script>
let ta = document.querySelector("textarea");
ta.style.height = ta.scrollHeight + "px";
</script>
""").strip(),
title="SQL Interrupted",
status=400,
message_is_html=True,
plain_message=(
"SQL query took too long. The time limit is"
" controlled by the sql_time_limit_ms setting."
),
)
raise query_interrupted_error(ex)
except (sqlite3.OperationalError, InvalidSql) as e:
raise DatasetteError(str(e), title="Invalid SQL", status=400)
except DatasetteError: