You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ma...@apache.org on 2025/11/13 18:47:59 UTC
(superset) branch issue-36072-sql-lab-where created (now 59ddd52789)
This is an automated email from the ASF dual-hosted git repository.
maximebeauchemin pushed a change to branch issue-36072-sql-lab-where
in repository https://gitbox.apache.org/repos/asf/superset.git
at 59ddd52789 fix(sqllab): Set explicit Content-Type headers to prevent HTTP 406 errors
This branch includes the following new commits:
new 59ddd52789 fix(sqllab): Set explicit Content-Type headers to prevent HTTP 406 errors
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(superset) 01/01: fix(sqllab): Set explicit Content-Type headers to prevent HTTP 406 errors
Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
maximebeauchemin pushed a commit to branch issue-36072-sql-lab-where
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 59ddd52789157eda60d5d5efe628a303d14adfdb
Author: Maxime Beauchemin <ma...@gmail.com>
AuthorDate: Thu Nov 13 10:47:44 2025 -0800
fix(sqllab): Set explicit Content-Type headers to prevent HTTP 406 errors
Fixes #36072 where SQL Lab queries with WHERE clauses failed with
"Database error: Not acceptable" in Superset v4.1+.
Root cause: Flask 2.3+ (upgraded in v4.1.0) has stricter content
negotiation that could return HTTP 406 when Content-Type headers
aren't explicitly set, particularly with ENABLE_PROXY_FIX or certain
Accept header configurations.
Changes:
- Add explicit Content-Type headers to /api/v1/sqllab/execute/ and
/api/v1/sqllab/results/ endpoints
- Improve error handling with try-except blocks for result fetching
and JSON serialization
- Add targeted integration test for WHERE clause queries
The fix ensures Flask 2.3+ doesn't attempt content negotiation that
could fail, while maintaining backward compatibility.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <no...@anthropic.com>
---
superset/sqllab/api.py | 37 +++++++++++++++++++++++++--------
tests/integration_tests/sqllab_tests.py | 35 +++++++++++++++++++++++++++++++
2 files changed, 63 insertions(+), 9 deletions(-)
diff --git a/superset/sqllab/api.py b/superset/sqllab/api.py
index 906dd72bca..f4ccce7c41 100644
--- a/superset/sqllab/api.py
+++ b/superset/sqllab/api.py
@@ -337,16 +337,32 @@ class SqlLabRestApi(BaseSupersetApi):
params = kwargs["rison"]
key = params.get("key")
rows = params.get("rows")
- result = SqlExecutionResultsCommand(key=key, rows=rows).run()
+
+ try:
+ result = SqlExecutionResultsCommand(key=key, rows=rows).run()
+ except Exception as ex:
+ logger.exception("Error fetching query results for key=%s", key)
+ return self.response_500(message=str(ex))
# Using pessimistic json serialization since some database drivers can return
# unserializeable types at times
- payload = json.dumps(
- result,
- default=json.pessimistic_json_iso_dttm_ser,
- ignore_nan=True,
- )
- return json_success(payload, 200)
+ try:
+ payload = json.dumps(
+ result,
+ default=json.pessimistic_json_iso_dttm_ser,
+ ignore_nan=True,
+ )
+ except Exception as ex:
+ logger.exception("Error serializing query results for key=%s", key)
+ return self.response_500(message="Unable to serialize query results")
+
+ # Use json_success with explicit Content-Type to ensure Flask 2.3+ correctly
+ # handles the response and doesn't trigger HTTP 406 errors due to content
+ # negotiation issues with Accept headers or proxy configurations
+ response = json_success(payload, 200)
+ # Explicitly set Content-Type as a safeguard against content negotiation issues
+ response.headers["Content-Type"] = "application/json; charset=utf-8"
+ return response
@expose("/execute/", methods=("POST",))
@protect()
@@ -410,8 +426,11 @@ class SqlLabRestApi(BaseSupersetApi):
if command_result["status"] == SqlJsonExecutionStatus.QUERY_IS_RUNNING
else 200
)
- # return the execution result without special encoding
- return json_success(command_result["payload"], response_status)
+ # Return the execution result without special encoding
+ # Set explicit Content-Type to prevent Flask 2.3+ content negotiation issues
+ response = json_success(command_result["payload"], response_status)
+ response.headers["Content-Type"] = "application/json; charset=utf-8"
+ return response
except SqlLabException as ex:
payload = {"errors": [ex.to_dict()]}
diff --git a/tests/integration_tests/sqllab_tests.py b/tests/integration_tests/sqllab_tests.py
index 99c347d95f..1a1c65e2e0 100644
--- a/tests/integration_tests/sqllab_tests.py
+++ b/tests/integration_tests/sqllab_tests.py
@@ -126,6 +126,41 @@ class TestSqlLab(SupersetTestCase):
"engine_name": engine_name,
}
+ @pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
+ def test_sql_json_where_clause_content_type(self):
+ """
+ Test that queries with WHERE clauses return proper Content-Type headers.
+
+ This test addresses issue #36072 where Flask 2.3+ content negotiation
+ could cause HTTP 406 errors for queries with WHERE clauses, particularly
+ when using ENABLE_PROXY_FIX or certain Accept header configurations.
+ """
+ self.login(ADMIN_USERNAME)
+
+ # Test query with WHERE clause
+ resp = self.client.post(
+ "/api/v1/sqllab/execute/",
+ json={
+ "database_id": self.get_database_by_name("examples").id,
+ "sql": "SELECT * FROM birth_names WHERE name = 'John' LIMIT 5",
+ "client_id": "test_where_1",
+ },
+ )
+
+ # Verify response is successful
+ assert resp.status_code in (200, 202), f"Expected 200/202, got {resp.status_code}"
+
+ # Verify Content-Type header is explicitly set to prevent 406 errors
+ assert "application/json" in resp.headers.get("Content-Type", "")
+
+ # Verify response body is valid JSON
+ data = resp.json
+ assert isinstance(data, dict)
+
+ # If query ran synchronously (200), verify it has data
+ if resp.status_code == 200:
+ assert "data" in data or "query_id" in data
+
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
def test_sql_json_dml_disallowed(self):
self.login(ADMIN_USERNAME)