diff --git a/superset/connectors/sqla/models.py b/superset/connectors/sqla/models.py index 09d9a65a049c..52451a296931 100644 --- a/superset/connectors/sqla/models.py +++ b/superset/connectors/sqla/models.py @@ -1097,8 +1097,8 @@ def get_sqla_col( if template_processor: try: expression = template_processor.process_template(expression) - except SupersetSyntaxErrorException as ex: - msg = str(ex) + except (TemplateError, SupersetSyntaxErrorException) as ex: + msg = getattr(ex, "message", str(ex)) raise QueryObjectValidationError( _( "Error in jinja expression in column expression: %(msg)s", @@ -1144,8 +1144,8 @@ def get_timestamp_expression( if template_processor: try: expression = template_processor.process_template(expression) - except SupersetSyntaxErrorException as ex: - msg = str(ex) + except (TemplateError, SupersetSyntaxErrorException) as ex: + msg = getattr(ex, "message", str(ex)) raise QueryObjectValidationError( _( "Error in jinja expression in datetime column: %(msg)s", @@ -1239,8 +1239,8 @@ def get_sqla_col( if template_processor: try: expression = template_processor.process_template(expression) - except SupersetSyntaxErrorException as ex: - msg = str(ex) + except (TemplateError, SupersetSyntaxErrorException) as ex: + msg = getattr(ex, "message", str(ex)) raise QueryObjectValidationError( _( "Error in jinja expression in metric expression: %(msg)s", @@ -1769,11 +1769,11 @@ def _render_adhoc_expression_for_metadata_lookup( return sql_expression try: return template_processor.process_template(sql_expression) - except SupersetSyntaxErrorException as ex: + except (TemplateError, SupersetSyntaxErrorException) as ex: raise QueryObjectValidationError( _( "Error in jinja expression in adhoc column: %(msg)s", - msg=str(ex), + msg=getattr(ex, "message", str(ex)), ) ) from ex diff --git a/tests/unit_tests/connectors/sqla/models_test.py b/tests/unit_tests/connectors/sqla/models_test.py index 6543c69388cd..9540293c6729 100644 --- a/tests/unit_tests/connectors/sqla/models_test.py +++ b/tests/unit_tests/connectors/sqla/models_test.py @@ -33,10 +33,12 @@ from superset.daos.exceptions import DatasourceNotFound from superset.exceptions import ( OAuth2RedirectError, + QueryObjectValidationError, SupersetDisallowedSQLFunctionException, SupersetDisallowedSQLTableException, SupersetSecurityException, ) +from superset.jinja_context import get_template_processor from superset.models.core import Database from superset.sql.parse import Table from superset.superset_typing import QueryObjectDict @@ -1264,3 +1266,32 @@ def test_validate_stored_expression_rejects_subquery_around_jinja( None, "(SELECT password FROM ab_user LIMIT 1) {# x #}", ) + + +def test_get_sqla_col_wraps_raw_jinja_undefined_error() -> None: + """ + A raw ``jinja2.exceptions.UndefinedError`` raised while rendering a + column's Jinja expression (e.g. an arithmetic operation on an undefined + variable) must surface as a ``QueryObjectValidationError``, not + propagate as an unhandled 500. + """ + database = Database(id=1, database_name="my_database", sqlalchemy_uri="sqlite://") + table = SqlaTable( + id=1, + table_name="test_table", + database=database, + schema="my_schema", + sql=None, + columns=[ + TableColumn( + column_name="my_column", + type="VARCHAR", + expression="{{ nonexistent_var + 1 }}", + ) + ], + ) + column = table.columns[0] + processor = get_template_processor(database=database, table=table) + + with pytest.raises(QueryObjectValidationError): + column.get_sqla_col(template_processor=processor)