mirror of
https://github.com/apache/superset.git
synced 2026-04-30 21:44:40 +00:00
Compare commits
1 Commits
upgrade-sq
...
issue-3607
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59ddd52789 |
@@ -337,16 +337,32 @@ class SqlLabRestApi(BaseSupersetApi):
|
|||||||
params = kwargs["rison"]
|
params = kwargs["rison"]
|
||||||
key = params.get("key")
|
key = params.get("key")
|
||||||
rows = params.get("rows")
|
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
|
# Using pessimistic json serialization since some database drivers can return
|
||||||
# unserializeable types at times
|
# unserializeable types at times
|
||||||
payload = json.dumps(
|
try:
|
||||||
result,
|
payload = json.dumps(
|
||||||
default=json.pessimistic_json_iso_dttm_ser,
|
result,
|
||||||
ignore_nan=True,
|
default=json.pessimistic_json_iso_dttm_ser,
|
||||||
)
|
ignore_nan=True,
|
||||||
return json_success(payload, 200)
|
)
|
||||||
|
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",))
|
@expose("/execute/", methods=("POST",))
|
||||||
@protect()
|
@protect()
|
||||||
@@ -410,8 +426,11 @@ class SqlLabRestApi(BaseSupersetApi):
|
|||||||
if command_result["status"] == SqlJsonExecutionStatus.QUERY_IS_RUNNING
|
if command_result["status"] == SqlJsonExecutionStatus.QUERY_IS_RUNNING
|
||||||
else 200
|
else 200
|
||||||
)
|
)
|
||||||
# return the execution result without special encoding
|
# Return the execution result without special encoding
|
||||||
return json_success(command_result["payload"], response_status)
|
# 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:
|
except SqlLabException as ex:
|
||||||
payload = {"errors": [ex.to_dict()]}
|
payload = {"errors": [ex.to_dict()]}
|
||||||
|
|
||||||
|
|||||||
@@ -126,6 +126,41 @@ class TestSqlLab(SupersetTestCase):
|
|||||||
"engine_name": engine_name,
|
"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")
|
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
|
||||||
def test_sql_json_dml_disallowed(self):
|
def test_sql_json_dml_disallowed(self):
|
||||||
self.login(ADMIN_USERNAME)
|
self.login(ADMIN_USERNAME)
|
||||||
|
|||||||
Reference in New Issue
Block a user