mirror of
https://github.com/apache/superset.git
synced 2026-07-12 01:35:36 +00:00
165 lines
5.2 KiB
Python
165 lines
5.2 KiB
Python
# Licensed to the Apache Software Foundation (ASF) under one
|
|
# or more contributor license agreements. See the NOTICE file
|
|
# distributed with this work for additional information
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
# to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance
|
|
# with the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing,
|
|
# software distributed under the License is distributed on an
|
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
# KIND, either express or implied. See the License for the
|
|
# specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
"""
|
|
Regression tests for https://github.com/apache/superset/issues/32879.
|
|
|
|
Physical columns whose names contain multiple consecutive spaces
|
|
(e.g. ``Eladó 1``) used to be routed through the sqlparse-based
|
|
``validate_adhoc_subquery`` as raw, unquoted SQL, which misparsed the
|
|
name and rejected the query with "Custom SQL fields cannot contain
|
|
sub-queries.". The SIP-117 migration to the sqlglot-based parser and the
|
|
column-resolution logic in ``get_sqla_query`` (registered columns are
|
|
rendered as properly quoted SQLAlchemy columns instead of raw adhoc SQL)
|
|
fixed this; these tests pin the fixed behavior so it cannot regress.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
import pytest
|
|
from pytest_mock import MockerFixture
|
|
from sqlalchemy.orm.session import Session
|
|
|
|
if TYPE_CHECKING:
|
|
from superset.models.core import Database
|
|
|
|
MULTI_SPACE_COLUMN = "Eladó 1"
|
|
|
|
|
|
@pytest.fixture
|
|
def database(session: Session) -> Database:
|
|
from superset.connectors.sqla.models import SqlaTable
|
|
from superset.models.core import Database
|
|
|
|
SqlaTable.metadata.create_all(session.get_bind())
|
|
return Database(database_name="db", sqlalchemy_uri="sqlite://")
|
|
|
|
|
|
def _compile(database: Database, sqla_query: object) -> str:
|
|
with database.get_sqla_engine() as engine:
|
|
return str(
|
|
sqla_query.compile( # type: ignore[attr-defined]
|
|
dialect=engine.dialect,
|
|
compile_kwargs={"literal_binds": True},
|
|
)
|
|
)
|
|
|
|
|
|
def test_get_sqla_query_groupby_column_with_multiple_spaces(
|
|
database: Database,
|
|
) -> None:
|
|
"""
|
|
A registered physical column with multiple consecutive spaces in its
|
|
name can be used as a chart dimension (the exact scenario from
|
|
issue #32879) without tripping the adhoc-subquery validation.
|
|
"""
|
|
from superset.connectors.sqla.models import SqlaTable, TableColumn
|
|
|
|
table = SqlaTable(
|
|
database=database,
|
|
schema=None,
|
|
table_name="t",
|
|
columns=[
|
|
TableColumn(column_name="id", type="INTEGER"),
|
|
TableColumn(column_name=MULTI_SPACE_COLUMN, type="TEXT"),
|
|
],
|
|
)
|
|
|
|
# Must not raise SupersetSecurityException ("Custom SQL fields cannot
|
|
# contain sub-queries.") as it did on 4.1.x.
|
|
sqla_query = table.get_sqla_query(
|
|
groupby=[MULTI_SPACE_COLUMN],
|
|
metrics=[],
|
|
is_timeseries=False,
|
|
row_limit=100,
|
|
)
|
|
|
|
sql = _compile(database, sqla_query.sqla_query)
|
|
# The column is emitted as a single quoted identifier with both spaces
|
|
# preserved, so the database resolves the real column.
|
|
assert f'"{MULTI_SPACE_COLUMN}"' in sql
|
|
assert "GROUP BY" in sql.upper()
|
|
|
|
|
|
def test_get_sqla_query_select_column_with_multiple_spaces(
|
|
database: Database,
|
|
) -> None:
|
|
"""
|
|
The raw-columns path (drill to detail / samples) also handles a
|
|
multi-space physical column without raising.
|
|
"""
|
|
from superset.connectors.sqla.models import SqlaTable, TableColumn
|
|
|
|
table = SqlaTable(
|
|
database=database,
|
|
schema=None,
|
|
table_name="t",
|
|
columns=[
|
|
TableColumn(column_name="id", type="INTEGER"),
|
|
TableColumn(column_name=MULTI_SPACE_COLUMN, type="TEXT"),
|
|
],
|
|
)
|
|
|
|
sqla_query = table.get_sqla_query(
|
|
columns=["id", MULTI_SPACE_COLUMN],
|
|
metrics=None,
|
|
is_timeseries=False,
|
|
row_limit=100,
|
|
)
|
|
|
|
sql = _compile(database, sqla_query.sqla_query)
|
|
assert f'"{MULTI_SPACE_COLUMN}"' in sql
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"engine,expression",
|
|
[
|
|
("sqlite", f'"{MULTI_SPACE_COLUMN}"'),
|
|
("postgresql", f'"{MULTI_SPACE_COLUMN}"'),
|
|
("mssql", f"[{MULTI_SPACE_COLUMN}]"),
|
|
],
|
|
)
|
|
def test_validate_adhoc_subquery_accepts_quoted_multi_space_identifier(
|
|
mocker: MockerFixture,
|
|
engine: str,
|
|
expression: str,
|
|
) -> None:
|
|
"""
|
|
The sqlglot-based ``validate_adhoc_subquery`` accepts a quoted
|
|
identifier containing multiple consecutive spaces: it is not a
|
|
sub-query, so no SupersetSecurityException is raised and the SQL is
|
|
returned unchanged.
|
|
"""
|
|
from superset.models.helpers import validate_adhoc_subquery
|
|
|
|
# Only consulted for RLS when a subquery is detected, which must not
|
|
# happen here.
|
|
database = mocker.MagicMock()
|
|
|
|
assert (
|
|
validate_adhoc_subquery(
|
|
expression,
|
|
database,
|
|
None,
|
|
"",
|
|
engine,
|
|
)
|
|
== expression
|
|
)
|