def _determine_opening_parenthesis_type(self, token: SQLToken): """ Determines the type of left parenthesis in query """ if token.previous_token.normalized in SUBQUERY_PRECEDING_KEYWORDS: # inside subquery / derived table token.is_subquery_start = True self._subquery_level += 1 token.subquery_level = self._subquery_level elif token.previous_token.normalized in KEYWORDS_BEFORE_COLUMNS.union({","}): # we are in columns and in a column subquery definition token.is_column_definition_start = True elif ( token.previous_token.is_as_keyword and token.last_keyword_normalized != "WINDOW" ): # window clause also contains AS keyword, but it is not a query token.is_with_query_start = True elif ( token.last_keyword_normalized == "TABLE" and token.find_nearest_token("(") is EmptyToken ): token.is_create_table_columns_declaration_start = True elif token.previous_token.normalized == "OVER": token.is_partition_clause_start = True else: # nested function token.is_nested_function_start = True self._nested_level += 1 self._is_in_nested_function = True self._open_parentheses.append(token) self._parenthesis_level += 1
def _add_to_columns_aliases_subsection(self, token: SQLToken): """ Add alias to the section in which it appears in query """ keyword = token.last_keyword_normalized alias = token.left_expanded if ( token.last_keyword_normalized in ["FROM", "WITH"] and token.find_nearest_token("(").is_with_columns_start ): keyword = "SELECT" section = COLUMNS_SECTIONS[keyword] self._columns_aliases_dict = self._columns_aliases_dict or dict() self._columns_aliases_dict.setdefault(section, UniqueList()).append(alias)
def _resolve_function_alias(self, token: SQLToken) -> Union[str, List[str]]: # it can be one function or a chain of functions # like: sum(a) + sum(b) as alias # or operation on columns like: col1 + col2 as alias start_token = token.find_nearest_token( [",", "SELECT"], value_attribute="normalized" ) while start_token.is_in_nested_function: start_token = start_token.find_nearest_token( [",", "SELECT"], value_attribute="normalized" ) return self._find_all_columns_between_tokens( start_token=start_token, end_token=token )
def _handle_with_name_save(token: SQLToken, with_names: List[str]) -> None: if token.is_right_parenthesis: # inside columns of with statement # like: with (col1, col2) as (subquery) token.is_with_columns_end = True token.is_nested_function_end = False start_token = token.find_nearest_token("(") start_token.is_with_columns_start = True start_token.is_nested_function_start = False prev_token = start_token.previous_token prev_token.token_type = TokenType.WITH_NAME with_names.append(prev_token.value) else: token.token_type = TokenType.WITH_NAME with_names.append(token.value)
def _find_column_for_with_column_alias(self, token: SQLToken) -> str: start_token = token.find_nearest_token( True, direction="right", value_attribute="is_with_query_start" ) if start_token not in self._with_columns_candidates: end_token = start_token.find_nearest_token( True, direction="right", value_attribute="is_with_query_end" ) columns = self._find_all_columns_between_tokens( start_token=start_token, end_token=end_token ) self._with_columns_candidates[start_token] = columns if isinstance(self._with_columns_candidates[start_token], list): alias_of = self._with_columns_candidates[start_token].pop(0) else: alias_of = self._with_columns_candidates[start_token] return alias_of
def _resolve_subquery_alias(self, token: SQLToken) -> Union[str, List[str]]: # nested subquery like select a, (select a as b from x) as column start_token = token.find_nearest_token( True, value_attribute="is_column_definition_start" ) if start_token.next_token.normalized == "SELECT": # we have a subquery alias_token = start_token.next_token.find_nearest_token( self._aliases_to_check, direction="right", value_attribute="value", ) return self._resolve_alias_to_column(alias_token) # chain of functions or redundant parenthesis return self._find_all_columns_between_tokens( start_token=start_token, end_token=token )