diff --git a/datasette/utils/sql_analysis.py b/datasette/utils/sql_analysis.py index 22bb55c4..6325e890 100644 --- a/datasette/utils/sql_analysis.py +++ b/datasette/utils/sql_analysis.py @@ -1,3 +1,4 @@ +import sys from dataclasses import dataclass from typing import Literal @@ -196,6 +197,16 @@ def _allow_authorizer_action(*args): return sqlite3.SQLITE_OK +def _disable_authorizer(conn): + # Python 3.11 added support for unregistering an authorizer using None. + # On Python 3.10, None is installed as the callback instead, and the next + # statement fails with "not authorized" when sqlite3 tries to call it. + if sys.version_info >= (3, 11): + conn.set_authorizer(None) + else: + conn.set_authorizer(_allow_authorizer_action) + + def analyze_sql_tables( conn, sql: str, @@ -484,7 +495,7 @@ def analyze_sql_tables( conn, key.table, schema=key.sqlite_schema ) finally: - conn.set_authorizer(None) + _disable_authorizer(conn) has_schema_operation = any( key.target_type in {"table", "index", "view", "trigger", "virtual-table"} diff --git a/tests/test_utils_sql_analysis.py b/tests/test_utils_sql_analysis.py index a6f95e5b..363814d7 100644 --- a/tests/test_utils_sql_analysis.py +++ b/tests/test_utils_sql_analysis.py @@ -439,7 +439,7 @@ def test_analyze_attached_database_tables(conn): } -def test_analyze_clears_authorizer_on_error(): +def test_analyze_disables_authorizer_on_error(): class FakeConnection: def __init__(self): self.authorizers = [] @@ -455,4 +455,5 @@ def test_analyze_clears_authorizer_on_error(): with pytest.raises(sqlite3.OperationalError): analyze_sql_tables(conn, "bad SQL") - assert conn.authorizers[-1] is None + final_authorizer = conn.authorizers[-1] + assert final_authorizer is None or final_authorizer() == sqlite3.SQLITE_OK