This is an automated email from the ASF dual-hosted git repository. maximebeauchemin pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-superset.git
The following commit(s) were added to refs/heads/master by this push: new 25c599d Escaping the user's SQL in the explore view (#3186) 25c599d is described below commit 25c599d0400cf320ce5accf50ed88e76ccff5980 Author: Maxime Beauchemin <maximebeauche...@gmail.com> AuthorDate: Thu Jul 27 09:47:31 2017 -0700 Escaping the user's SQL in the explore view (#3186) * Escaping the user's SQL in the explore view When executing SQL from SQL Lab, we use a lower level API to the database which doesn't require escaping the SQL. When going through the explore view, the stack chain leading to the same method may need escaping depending on how the DBAPI driver is written, and that is the case for Presto (and perhaps other drivers). * Using regex to avoid doubling doubles --- superset/connectors/sqla/models.py | 16 ++++++++++------ superset/db_engine_specs.py | 17 +++++++++-------- superset/sql_lab.py | 1 - 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/superset/connectors/sqla/models.py b/superset/connectors/sqla/models.py index 147c667..0d06bfb 100644 --- a/superset/connectors/sqla/models.py +++ b/superset/connectors/sqla/models.py @@ -285,10 +285,12 @@ class SqlaTable(Model, BaseDatasource): """ cols = {col.column_name: col for col in self.columns} target_col = cols[column_name] + tp = self.get_template_processor() + db_engine_spec = self.database.db_engine_spec qry = ( select([target_col.sqla_col]) - .select_from(self.get_from_clause()) + .select_from(self.get_from_clause(tp, db_engine_spec)) .distinct(column_name) ) if limit: @@ -322,7 +324,6 @@ class SqlaTable(Model, BaseDatasource): ) logging.info(sql) sql = sqlparse.format(sql, reindent=True) - sql = self.database.db_engine_spec.sql_preprocessor(sql) return sql def get_sqla_table(self): @@ -331,12 +332,14 @@ class SqlaTable(Model, BaseDatasource): tbl.schema = self.schema return tbl - def get_from_clause(self, template_processor=None): + def get_from_clause(self, template_processor=None, db_engine_spec=None): # Supporting arbitrary SQL statements in place of tables if self.sql: from_sql = self.sql if template_processor: from_sql = template_processor.process_template(from_sql) + if db_engine_spec: + from_sql = db_engine_spec.escape_sql(from_sql) return TextAsFrom(sa.text(from_sql), []).alias('expr_qry') return self.get_sqla_table() @@ -367,13 +370,14 @@ class SqlaTable(Model, BaseDatasource): 'form_data': form_data, } template_processor = self.get_template_processor(**template_kwargs) + db_engine_spec = self.database.db_engine_spec # For backward compatibility if granularity not in self.dttm_cols: granularity = self.main_dttm_col # Database spec supports join-free timeslot grouping - time_groupby_inline = self.database.db_engine_spec.time_groupby_inline + time_groupby_inline = db_engine_spec.time_groupby_inline cols = {col.column_name: col for col in self.columns} metrics_dict = {m.metric_name: m for m in self.metrics} @@ -428,7 +432,7 @@ class SqlaTable(Model, BaseDatasource): groupby_exprs += [timestamp] # Use main dttm column to support index with secondary dttm columns - if self.database.db_engine_spec.time_secondary_columns and \ + if db_engine_spec.time_secondary_columns and \ self.main_dttm_col in self.dttm_cols and \ self.main_dttm_col != dttm_col.column_name: time_filters.append(cols[self.main_dttm_col]. @@ -438,7 +442,7 @@ class SqlaTable(Model, BaseDatasource): select_exprs += metrics_exprs qry = sa.select(select_exprs) - tbl = self.get_from_clause(template_processor) + tbl = self.get_from_clause(template_processor, db_engine_spec) if not columns: qry = qry.group_by(*groupby_exprs) diff --git a/superset/db_engine_specs.py b/superset/db_engine_specs.py index b460226..d08f2a8 100644 --- a/superset/db_engine_specs.py +++ b/superset/db_engine_specs.py @@ -74,6 +74,11 @@ class BaseEngineSpec(object): return {} @classmethod + def escape_sql(cls, sql): + """Escapes the raw SQL""" + return sql + + @classmethod def convert_dttm(cls, target_type, dttm): return "'{}'".format(dttm.strftime('%Y-%m-%d %H:%M:%S')) @@ -140,14 +145,6 @@ class BaseEngineSpec(object): return uri @classmethod - def sql_preprocessor(cls, sql): - """If the SQL needs to be altered prior to running it - - For example Presto needs to double `%` characters - """ - return sql - - @classmethod def patch(cls): pass @@ -400,6 +397,10 @@ class PrestoEngineSpec(BaseEngineSpec): return uri @classmethod + def escape_sql(cls, sql): + return re.sub(r'%%|%', "%%", sql) + + @classmethod def convert_dttm(cls, target_type, dttm): tt = target_type.upper() if tt == 'DATE': diff --git a/superset/sql_lab.py b/superset/sql_lab.py index 4b0bd86..638b29a 100644 --- a/superset/sql_lab.py +++ b/superset/sql_lab.py @@ -154,7 +154,6 @@ def execute_sql(ctask, query_id, return_results=True, store_results=False): template_processor = get_template_processor( database=database, query=query) executed_sql = template_processor.process_template(executed_sql) - executed_sql = db_engine_spec.sql_preprocessor(executed_sql) except Exception as e: logging.exception(e) msg = "Template rendering failed: " + utils.error_msg_from_exception(e) -- To stop receiving notification emails like this one, please contact ['"comm...@superset.apache.org" <comm...@superset.apache.org>'].