mirror of
https://github.com/apache/superset.git
synced 2026-04-19 08:04:53 +00:00
fix: boolean type into SQL 'in' operator (#16107)
* fix: boolean type into SQL 'in' operator * fix ut * fix ut again * update url * remove blank line
This commit is contained in:
@@ -112,7 +112,7 @@ const ColumnButtonWrapper = styled.div`
|
||||
const checkboxGenerator = (d, onChange) => (
|
||||
<CheckboxControl value={d} onChange={onChange} />
|
||||
);
|
||||
const DATA_TYPES = ['STRING', 'NUMERIC', 'DATETIME'];
|
||||
const DATA_TYPES = ['STRING', 'NUMERIC', 'DATETIME', 'BOOLEAN'];
|
||||
|
||||
const DATASOURCE_TYPES_ARR = [
|
||||
{ key: 'physical', label: t('Physical (table or view)') },
|
||||
|
||||
@@ -374,6 +374,8 @@ class BaseDatasource(
|
||||
return None
|
||||
if value == "<empty string>":
|
||||
return ""
|
||||
if target_column_type == utils.GenericDataType.BOOLEAN:
|
||||
return utils.cast_to_boolean(value)
|
||||
return value
|
||||
|
||||
if isinstance(values, (list, tuple)):
|
||||
|
||||
@@ -423,6 +423,35 @@ def cast_to_num(value: Optional[Union[float, int, str]]) -> Optional[Union[float
|
||||
return None
|
||||
|
||||
|
||||
def cast_to_boolean(value: Any) -> bool:
|
||||
"""Casts a value to an int/float
|
||||
|
||||
>>> cast_to_boolean(1)
|
||||
True
|
||||
>>> cast_to_boolean(0)
|
||||
False
|
||||
>>> cast_to_boolean(0.5)
|
||||
True
|
||||
>>> cast_to_boolean('true')
|
||||
True
|
||||
>>> cast_to_boolean('false')
|
||||
False
|
||||
>>> cast_to_boolean('False')
|
||||
False
|
||||
>>> cast_to_boolean(None)
|
||||
False
|
||||
|
||||
:param value: value to be converted to boolean representation
|
||||
:returns: value cast to `bool`. when value is 'true' or value that are not 0
|
||||
converte into True
|
||||
"""
|
||||
if isinstance(value, (int, float)):
|
||||
return value != 0
|
||||
if isinstance(value, str):
|
||||
return value.strip().lower() == "true"
|
||||
return False
|
||||
|
||||
|
||||
def list_minus(l: List[Any], minus: List[Any]) -> List[Any]:
|
||||
"""Returns l without what is in minus
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ from typing import Any, Dict, NamedTuple, List, Pattern, Tuple, Union
|
||||
from unittest.mock import patch
|
||||
import pytest
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from superset import db
|
||||
from superset.connectors.sqla.models import SqlaTable, TableColumn
|
||||
from superset.db_engine_specs.bigquery import BigQueryEngineSpec
|
||||
@@ -264,6 +266,43 @@ class TestDatabaseModel(SupersetTestCase):
|
||||
else:
|
||||
self.assertIn(filter_.expected, sql)
|
||||
|
||||
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
|
||||
def test_boolean_type_where_operators(self):
|
||||
table = self.get_table(name="birth_names")
|
||||
db.session.add(
|
||||
TableColumn(
|
||||
column_name="boolean_gender",
|
||||
expression="case when gender = 'boy' then True else False end",
|
||||
type="BOOLEAN",
|
||||
table=table,
|
||||
)
|
||||
)
|
||||
query_obj = {
|
||||
"granularity": None,
|
||||
"from_dttm": None,
|
||||
"to_dttm": None,
|
||||
"groupby": ["boolean_gender"],
|
||||
"metrics": ["count"],
|
||||
"is_timeseries": False,
|
||||
"filter": [
|
||||
{
|
||||
"col": "boolean_gender",
|
||||
"op": FilterOperator.IN,
|
||||
"val": ["true", "false"],
|
||||
}
|
||||
],
|
||||
"extras": {},
|
||||
}
|
||||
sqla_query = table.get_sqla_query(**query_obj)
|
||||
sql = table.database.compile_sqla_query(sqla_query.sqla_query)
|
||||
dialect = table.database.get_dialect()
|
||||
operand = "(true, false)"
|
||||
# override native_boolean=False behavior in MySQLCompiler
|
||||
# https://github.com/sqlalchemy/sqlalchemy/blob/master/lib/sqlalchemy/dialects/mysql/base.py
|
||||
if not dialect.supports_native_boolean and dialect.name != "mysql":
|
||||
operand = "(1, 0)"
|
||||
self.assertIn(f"IN {operand}", sql)
|
||||
|
||||
def test_incorrect_jinja_syntax_raises_correct_exception(self):
|
||||
query_obj = {
|
||||
"granularity": None,
|
||||
|
||||
Reference in New Issue
Block a user