@@ -4104,6 +4104,115 @@ def test_validate_sql_endpoint_failure(self, get_validator_by_name):
41044104 assert rv .status_code == 422
41054105 assert "Kaboom!" in response ["errors" ][0 ]["message" ]
41064106
4107+ @mock .patch .dict (
4108+ "superset.config.SQL_VALIDATORS_BY_ENGINE" ,
4109+ SQL_VALIDATORS_BY_ENGINE ,
4110+ clear = True ,
4111+ )
4112+ def test_validate_sql_with_jinja_templates (self ):
4113+ """
4114+ Database API: validate SQL with Jinja templates
4115+ """
4116+ request_payload = {
4117+ "sql" : (
4118+ "SELECT *\n FROM birth_names\n WHERE 1=1\n "
4119+ "{% if city_filter is defined %}\n "
4120+ " AND city = '{{ city_filter }}'\n {% endif %}\n "
4121+ "LIMIT {{ limit | default(100) }}"
4122+ ),
4123+ "schema" : None ,
4124+ "template_params" : {},
4125+ }
4126+
4127+ example_db = get_example_database ()
4128+ if example_db .backend not in ("presto" , "postgresql" ):
4129+ pytest .skip ("Only presto and PG are implemented" )
4130+
4131+ self .login (ADMIN_USERNAME )
4132+ uri = f"api/v1/database/{ example_db .id } /validate_sql/"
4133+ rv = self .client .post (uri , json = request_payload )
4134+ response = json .loads (rv .data .decode ("utf-8" ))
4135+ assert rv .status_code == 200
4136+ # Template was successfully rendered and validated
4137+ # so a valid query returns an empty result list
4138+ result = response ["result" ]
4139+ assert isinstance (result , list )
4140+ assert len (result ) == 0
4141+
4142+ @mock .patch .dict (
4143+ "superset.config.SQL_VALIDATORS_BY_ENGINE" ,
4144+ SQL_VALIDATORS_BY_ENGINE ,
4145+ clear = True ,
4146+ )
4147+ def test_validate_sql_with_jinja_templates_and_params (self ):
4148+ """
4149+ Database API: validate SQL with Jinja templates and parameters
4150+ """
4151+ request_payload = {
4152+ "sql" : (
4153+ "SELECT *\n FROM birth_names\n WHERE 1=1\n "
4154+ "{% if city_filter is defined %}\n "
4155+ " AND city = '{{ city_filter }}'\n "
4156+ "{% endif %}\n LIMIT {{ limit }}"
4157+ ),
4158+ "schema" : None ,
4159+ "template_params" : {"city_filter" : "New York" , "limit" : 50 },
4160+ }
4161+
4162+ example_db = get_example_database ()
4163+ if example_db .backend not in ("presto" , "postgresql" ):
4164+ pytest .skip ("Only presto and PG are implemented" )
4165+
4166+ self .login (ADMIN_USERNAME )
4167+ uri = f"api/v1/database/{ example_db .id } /validate_sql/"
4168+ rv = self .client .post (uri , json = request_payload )
4169+ response = json .loads (rv .data .decode ("utf-8" ))
4170+ assert rv .status_code == 200
4171+ # Template was successfully rendered with parameters and validated
4172+ # so a valid query returns an empty result list
4173+ result = response ["result" ]
4174+ assert isinstance (result , list )
4175+ assert len (result ) == 0
4176+
4177+ @mock .patch .dict (
4178+ "superset.config.SQL_VALIDATORS_BY_ENGINE" ,
4179+ SQL_VALIDATORS_BY_ENGINE ,
4180+ clear = True ,
4181+ )
4182+ def test_validate_sql_with_jinja_invalid_sql_after_render (self ):
4183+ """
4184+ Database API: validate SQL with Jinja templates that renders to invalid SQL
4185+
4186+ This test ensures that SQL validation errors are not hidden by template
4187+ processing. The template should render successfully, but the resulting SQL
4188+ should fail syntax validation.
4189+ """
4190+ request_payload = {
4191+ "sql" : (
4192+ "SELECT *\n FROM birth_names\n "
4193+ "{% if add_invalid_clause %}\n "
4194+ "WHERE\n "
4195+ "{% endif %}"
4196+ ),
4197+ "schema" : None ,
4198+ "template_params" : {"add_invalid_clause" : True },
4199+ }
4200+
4201+ example_db = get_example_database ()
4202+ if example_db .backend not in ("presto" , "postgresql" ):
4203+ pytest .skip ("Only presto and PG are implemented" )
4204+
4205+ self .login (ADMIN_USERNAME )
4206+ uri = f"api/v1/database/{ example_db .id } /validate_sql/"
4207+ rv = self .client .post (uri , json = request_payload )
4208+ response = json .loads (rv .data .decode ("utf-8" ))
4209+ assert rv .status_code == 200
4210+ # The template should render successfully, but SQL validation
4211+ # should catch the syntax error (WHERE clause with no condition)
4212+ result = response ["result" ]
4213+ assert isinstance (result , list )
4214+ assert len (result ) > 0
4215+
41074216 def test_get_databases_with_extra_filters (self ):
41084217 """
41094218 API: Test get database with extra query filter.
0 commit comments