Skip to content

Commit

Permalink
Merge branch 'main' into r7l208/sparksql-3484
Browse files Browse the repository at this point in the history
  • Loading branch information
R7L208 committed Jul 1, 2022
2 parents 0ff4dd7 + 81f75dc commit ed01196
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 20 deletions.
46 changes: 39 additions & 7 deletions src/sqlfluff/rules/L025.py
Expand Up @@ -4,6 +4,7 @@
from typing import cast, List, Set

from sqlfluff.core.dialects.base import Dialect
from sqlfluff.core.parser.segments import BaseSegment
from sqlfluff.core.rules.analysis.select import get_select_statement_info
from sqlfluff.core.rules.analysis.select_crawler import (
Query as SelectCrawlerQuery,
Expand Down Expand Up @@ -62,6 +63,10 @@ class Rule_L025(BaseRule):
"""

groups = ("all", "core")
_dialects_requiring_alias_for_values_clause = [
"snowflake",
"tsql",
]

def _eval(self, context: RuleContext) -> EvalResultType:
violations: List[LintResult] = []
Expand All @@ -81,19 +86,46 @@ def _eval(self, context: RuleContext) -> EvalResultType:
alias: AliasInfo
for alias in query.aliases:

# Skip alias for values clauses
if alias.from_expression_element:
table_expression = alias.from_expression_element.get_child(
"table_expression"
)
if table_expression and table_expression.get_child("values_clause"):
continue
# Skip alias if it's required (some dialects require aliases for
# VALUES clauses).
if alias.from_expression_element and self.is_alias_required(
alias.from_expression_element, context.dialect.name
):
continue

if alias.aliased and alias.ref_str not in query.tbl_refs:
# Unused alias. Report and fix.
violations.append(self._report_unused_alias(alias))
return violations or None

@classmethod
def is_alias_required(
cls, from_expression_element: BaseSegment, dialect_name: str
) -> bool:
"""Given an alias, is it REQUIRED to be present?
Aliases are required in SOME, but not all dialects when there's a VALUES
clause.
"""
# Look for a table_expression (i.e. VALUES clause) as a descendant of
# the FROM expression, potentially nested inside brackets. The reason we
# allow nesting in brackets is that in some dialects (e.g. TSQL), this
# is actually *required* in order for SQL Server to parse it.
for segment in from_expression_element.iter_segments(expanding=("bracketed",)):
if segment.is_type("table_expression"):
# Found a table expression. Does it have a VALUES clause?
if not segment.get_child("values_clause"):
# No VALUES clause, thus the alias is definitely not required.
return False
else:
# Is this a dialect that requires VALUE clauses to be aliased?
return (
dialect_name in cls._dialects_requiring_alias_for_values_clause
)

# This should never happen. Return False just to be safe.
return False # pragma: no cover

@classmethod
def _analyze_table_aliases(cls, query: L025Query, dialect: Dialect):
# Get table aliases defined in query.
Expand Down
41 changes: 28 additions & 13 deletions test/fixtures/rules/std_rule_cases/L025.yml
Expand Up @@ -228,6 +228,34 @@ test_fail_sparksql_values_clause:
core:
dialect: sparksql

test_pass_snowflake_values:
# Tests a fix for issue 3301.
pass_str: |
SELECT
thing_1
, thing_2
FROM VALUES
( 'foo', 'bar')
, ( 'foo', 'bar')
my_table_alias(thing_1, thing_2)
configs:
core:
dialect: snowflake

test_pass_tsql_values_clause_in_parentheses:
# Tests a fix for issue 3522. In tsql, the parentheses surrouding "values" are
# required (otherwise syntax error). SQLFluff was incorrectly complaining that
# the alias 't' was unused.
pass_str: |
SELECT *
FROM (VALUES
('a1', 'b1'),
('a2', 'b2'),
('a3', 'b3')) t(a,b)
configs:
core:
dialect: tsql

test_pass_join_on_expression_in_parentheses:
pass_str: |
SELECT table1.c1
Expand Down Expand Up @@ -284,16 +312,3 @@ test_fail_snowflake_flatten_function:
configs:
core:
dialect: snowflake


test_pass_snowflake_values:
# Tests a fix for issue 3301.
pass_str: |
SELECT
thing_1
, thing_2
FROM VALUES
( 'foo', 'bar')
, ( 'foo', 'bar')
my_table_alias(thing_1, thing_2)
;

0 comments on commit ed01196

Please sign in to comment.