def _handle_alias(self, alias_info, dialect, queries): select_info_target = SelectCrawler.get( alias_info.from_expression_element, queries, dialect) if isinstance(select_info_target, str): # It's an alias to an external table whose # number of columns could vary without our # knowledge. Thus, warn. self.logger.debug( f"Query target {select_info_target} is external. Generating warning." ) raise RuleFailure() else: # Handle nested SELECT. self._analyze_result_columns(select_info_target, dialect, queries)
def _analyze_result_columns( self, select_info_list: List[SelectCrawler], dialect: Dialect, queries: Dict[str, List[SelectCrawler]], ): """Given info on a list of SELECTs, determine whether to warn.""" # Recursively walk from the given query (select_info_list) to any # wildcard columns in the select targets. If every wildcard evdentually # resolves to a query without wildcards, all is well. Otherwise, warn. for select_info in select_info_list: self.logger.debug( f"Analyzing query: {select_info.select_statement.raw}") for wildcard in select_info.get_wildcard_info(): if wildcard.tables: for wildcard_table in wildcard.tables: self.logger.debug( f"Wildcard: {wildcard.segment.raw} has target {wildcard_table}" ) # Is it an alias? alias_info = select_info.find_alias(wildcard_table) if alias_info: # Found the alias matching the wildcard. Recurse, # analyzing the query associated with that alias. self._handle_alias(alias_info, dialect, queries) else: # Not an alias. Is it a CTE? if wildcard_table in queries: # Wildcard refers to a CTE. Analyze it. self._analyze_result_columns( queries.pop(wildcard_table), dialect, queries) else: # Not CTE, not table alias. Presumably an # external table. Warn. self.logger.debug( f"Query target {wildcard_table} is external. Generating warning." ) raise RuleFailure() else: # No table was specified with the wildcard. Assume we're # querying from a nested select in FROM. select_info_target = SelectCrawler.get( select_info.select_statement, queries, dialect) assert isinstance(select_info_target, list) self._analyze_result_columns( select_info_target, dialect, queries, )
def _analyze_result_columns(self, query: Query): """Given info on a list of SELECTs, determine whether to warn.""" # Recursively walk from the given query (select_info_list) to any # wildcard columns in the select targets. If every wildcard evdentually # resolves to a query without wildcards, all is well. Otherwise, warn. if not query.selectables: return for selectable in query.selectables: self.logger.debug(f"Analyzing query: {selectable.selectable.raw}") for wildcard in selectable.get_wildcard_info(): if wildcard.tables: for wildcard_table in wildcard.tables: self.logger.debug( f"Wildcard: {wildcard.segment.raw} has target " "{wildcard_table}") # Is it an alias? alias_info = selectable.find_alias(wildcard_table) if alias_info: # Found the alias matching the wildcard. Recurse, # analyzing the query associated with that alias. self._handle_alias(selectable, alias_info, query) else: # Not an alias. Is it a CTE? cte = query.lookup_cte(wildcard_table) if cte: # Wildcard refers to a CTE. Analyze it. self._analyze_result_columns(cte) else: # Not CTE, not table alias. Presumably an # external table. Warn. self.logger.debug( f"Query target {wildcard_table} is external. " "Generating warning.") raise RuleFailure(selectable.selectable) else: # No table was specified with the wildcard. Assume we're # querying from a nested select in FROM. query_list = SelectCrawler.get( query, query.selectables[0].selectable) for o in query_list: if isinstance(o, Query): self._analyze_result_columns(o) return self.logger.debug( f'Query target "{query.selectables[0].selectable.raw}" has no ' "targets. Generating warning.") raise RuleFailure(query.selectables[0].selectable)