Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Structure and Ambiguous rule bundles #4444

Merged
merged 11 commits into from
Mar 1, 2023
8 changes: 4 additions & 4 deletions docs/source/gettingstarted.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ file.

$ sqlfluff lint test.sql --dialect ansi
== [test.sql] FAIL
L: 1 | P: 1 | L034 | Select wildcards then simple targets before calculations
L: 1 | P: 1 | ST06 | Select wildcards then simple targets before calculations
| and aggregates.
L: 1 | P: 1 | L036 | Select targets should be on a new line unless there is
| only one select target.
Expand Down Expand Up @@ -125,7 +125,7 @@ error (violation of *L006*) no longer shows up.

$ sqlfluff lint test.sql --dialect ansi
== [test.sql] FAIL
L: 1 | P: 1 | L034 | Select wildcards then simple targets before calculations
L: 1 | P: 1 | ST06 | Select wildcards then simple targets before calculations
| and aggregates.
L: 1 | P: 1 | L036 | Select targets should be on a new line unless there is
| only one select target.
Expand Down Expand Up @@ -190,7 +190,7 @@ specifying :code:`--rules`.
$ sqlfluff fix test.sql --dialect ansi
==== finding violations ====
== [test.sql] FAIL
L: 1 | P: 1 | L034 | Select wildcards then simple targets before calculations
L: 1 | P: 1 | ST06 | Select wildcards then simple targets before calculations
| and aggregates.
L: 1 | P: 1 | L036 | Select targets should be on a new line unless there is
| only one select target.
Expand Down Expand Up @@ -250,7 +250,7 @@ Then rerun the same command as before.

.. code-block:: text

$ sqlfluff fix test.sql --rules L003,L009,CP01,L034,L036,L039
$ sqlfluff fix test.sql --rules L003,L009,CP01,ST06,L036,L039

Then examine the file again, and you'll notice that the
file has been fixed accordingly.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-- L034 should ignore this as one of the select targets uses a macro
-- ST06 should ignore this as one of the select targets uses a macro

select
{{ dbt_utils.surrogate_key(['spots', 'moos']) }} as spot_moo_id,
Expand Down
8 changes: 4 additions & 4 deletions plugins/sqlfluff-templater-dbt/test/rules_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"rule,path,violations",
[
# Group By
("L021", "models/my_new_project/select_distinct_group_by.sql", [(1, 8)]),
("AM01", "models/my_new_project/select_distinct_group_by.sql", [(1, 8)]),
# Multiple trailing newline
("L009", "models/my_new_project/multiple_trailing_newline.sql", [(3, 1)]),
],
Expand Down Expand Up @@ -65,9 +65,9 @@ def test__rules__fix_utf8(project_dir): # noqa


def test__rules__order_by(project_dir): # noqa
"""Verify that rule L037 works with dbt."""
rule = "L037"
path = "models/my_new_project/L037_test.sql"
"""Verify that rule AM03 works with dbt."""
rule = "AM03"
path = "models/my_new_project/AM03_test.sql"
lntr = Linter(
config=FluffConfig(configs=DBT_FLUFF_CONFIG, overrides=dict(rules=rule))
)
Expand Down
2 changes: 1 addition & 1 deletion plugins/sqlfluff-templater-dbt/test/templater_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ def test__templater_dbt_skips_file(
"use_var.sql",
"incremental.sql",
"single_trailing_newline.sql",
"L034_test.sql",
"ST06_test.sql",
],
)
def test__dbt_templated_models_do_not_raise_lint_error(
Expand Down
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ sqlfluff =
# to a templater plugin.
sqlfluff_rules_capitalisation = sqlfluff.rules.capitalisation
sqlfluff_rules_aliasing = sqlfluff.rules.aliasing
sqlfluff_rules_ambiguous = sqlfluff.rules.ambiguous
sqlfluff_rules_structure = sqlfluff.rules.structure
sqlfluff_rules_jinja = sqlfluff.rules.jinja
sqlfluff_rules_tsql = sqlfluff.rules.tsql

Expand Down
4 changes: 2 additions & 2 deletions src/sqlfluff/cli/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,8 @@ def get_linter_and_formatter(
context_settings={"help_option_names": ["-h", "--help"]},
epilog="""\b\bExamples:\n
sqlfluff lint --dialect postgres .\n
sqlfluff lint --dialect postgres --rules L042 .\n
sqlfluff fix --dialect sqlite --rules L041,L042 src/queries\n
sqlfluff lint --dialect postgres --rules ST05 .\n
sqlfluff fix --dialect sqlite --rules L041,ST05 src/queries\n
sqlfluff parse --dialect sqlite --templater jinja src/queries/common.sql
""",
)
Expand Down
6 changes: 3 additions & 3 deletions src/sqlfluff/core/default_config.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ wildcard_policy = single
# Trailing commas
select_clause_trailing_comma = forbid

[sqlfluff:rules:L042]
[sqlfluff:rules:ST05]
# By default, allow subqueries in from clauses, but not join clauses
forbid_subquery_in = join

Expand All @@ -268,7 +268,7 @@ forbid_subquery_in = join
prefer_count_1 = False
prefer_count_0 = False

[sqlfluff:rules:L051]
[sqlfluff:rules:AM05]
# Fully qualify JOIN clause
fully_qualify_join_types = inner

Expand All @@ -277,7 +277,7 @@ fully_qualify_join_types = inner
multiline_newline = False
require_final_semicolon = False

[sqlfluff:rules:L054]
[sqlfluff:rules:AM06]
# GROUP BY/ORDER BY column references
group_by_and_order_by_style = consistent

Expand Down
10 changes: 7 additions & 3 deletions src/sqlfluff/core/rules/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1241,10 +1241,14 @@ def _expand_rule_refs(
# Is it a direct reference?
if r in reference_map:
expanded_rule_set.update(reference_map[r])
# Otherwise treat as a glob expression on codes.
# Otherwise treat as a glob expression on all references.
# NOTE: We expand _all_ references (i.e. groups, aliases, names
# AND codes) so that we preserve the most backward compatibility
# with existing references to legacy codes in config files.
else:
expanded_rule_set.update(fnmatch.filter(self._register.keys(), r))

matched_refs = fnmatch.filter(reference_map.keys(), r)
for matched in matched_refs:
expanded_rule_set.update(reference_map[matched])
return expanded_rule_set

def rule_reference_map(self) -> Dict[str, Set[str]]:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""Implementation of Rule L021."""
from typing import Optional
"""Implementation of Rule AM01."""
from typing import Optional, Tuple

from sqlfluff.core.rules import BaseRule, LintResult, RuleContext
from sqlfluff.core.rules.crawlers import SegmentSeekerCrawler
from sqlfluff.utils.functional import sp, FunctionalContext


class Rule_L021(BaseRule):
class Rule_AM01(BaseRule):
"""Ambiguous use of ``DISTINCT`` in a ``SELECT`` statement with ``GROUP BY``.

When using ``GROUP BY`` a `DISTINCT`` clause should not be necessary as every
Expand Down Expand Up @@ -34,7 +34,9 @@ class Rule_L021(BaseRule):
FROM foo
"""

groups = ("all", "core")
name = "ambiguous.distinct"
aliases = ("L021",)
groups: Tuple[str, ...] = ("all", "core", "ambiguous")
crawl_behaviour = SegmentSeekerCrawler({"select_statement"})

def _eval(self, context: RuleContext) -> Optional[LintResult]:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Implementation of Rule L033."""
"""Implementation of Rule AM02."""
from typing import Tuple
from sqlfluff.core.parser import (
WhitespaceSegment,
KeywordSegment,
Expand All @@ -8,7 +9,7 @@
from sqlfluff.core.rules.crawlers import SegmentSeekerCrawler


class Rule_L033(BaseRule):
class Rule_AM02(BaseRule):
"""``UNION [DISTINCT|ALL]`` is preferred over just ``UNION``.

.. note::
Expand Down Expand Up @@ -39,7 +40,9 @@ class Rule_L033(BaseRule):

"""

groups = ("all", "core")
name = "ambiguous.union"
aliases = ("L033",)
groups: Tuple[str, ...] = ("all", "core", "ambiguous")
crawl_behaviour = SegmentSeekerCrawler({"set_operator"})

def _eval(self, context: RuleContext) -> LintResult:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Implementation of Rule L037."""
"""Implementation of Rule AM03."""

from typing import NamedTuple, Optional, List
from typing import NamedTuple, Optional, List, Tuple

from sqlfluff.core.parser import WhitespaceSegment, KeywordSegment

Expand All @@ -10,13 +10,13 @@


class OrderByColumnInfo(NamedTuple):
"""For L037, segment that ends an ORDER BY column and any order provided."""
"""For AM03, segment that ends an ORDER BY column and any order provided."""

column_reference: BaseSegment
order: Optional[str] # One of 'ASC'/'DESC'/None


class Rule_L037(BaseRule):
class Rule_AM03(BaseRule):
"""Ambiguous ordering directions for columns in order by clause.

**Anti-pattern**
Expand All @@ -41,7 +41,9 @@ class Rule_L037(BaseRule):
ORDER BY a ASC, b DESC
"""

groups = ("all",)
name = "ambiguous.order_by"
aliases = ("L037",)
groups: Tuple[str, ...] = ("all", "ambiguous")
crawl_behaviour = SegmentSeekerCrawler({"orderby_clause"})
is_fix_compatible = True

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Implementation of Rule L044."""
from typing import Optional
"""Implementation of Rule AM04."""
from typing import Optional, Tuple

from sqlfluff.utils.analysis.select_crawler import Query, SelectCrawler
from sqlfluff.core.parser import BaseSegment
Expand All @@ -18,7 +18,7 @@ def __init__(self, anchor: BaseSegment):
self.anchor: BaseSegment = anchor


class Rule_L044(BaseRule):
class Rule_AM04(BaseRule):
"""Query produces an unknown number of result columns.

**Anti-pattern**
Expand Down Expand Up @@ -66,7 +66,9 @@ class Rule_L044(BaseRule):

"""

groups = ("all",)
name = "ambiguous.column_count"
aliases = ("L044",)
groups: Tuple[str, ...] = ("all", "ambiguous")
crawl_behaviour = SegmentSeekerCrawler(set(_START_TYPES))

def _handle_alias(self, selectable, alias_info, query):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""Implementation of Rule L051."""
from typing import Optional
"""Implementation of Rule AM05."""
from typing import Optional, Tuple
from sqlfluff.core.parser.segments.raw import KeywordSegment, WhitespaceSegment

from sqlfluff.core.rules import BaseRule, LintResult, LintFix, RuleContext
from sqlfluff.core.rules.crawlers import SegmentSeekerCrawler


class Rule_L051(BaseRule):
class Rule_AM05(BaseRule):
"""Join clauses should be fully qualified.

By default this rule is configured to enforce fully qualified ``INNER JOIN``
Expand All @@ -15,7 +15,7 @@ class Rule_L051(BaseRule):

**Anti-pattern**

A join is specified without expliciting the **kind** of join.
A join is used without specifying the **kind** of join.

.. code-block:: sql
:force:
Expand All @@ -38,7 +38,9 @@ class Rule_L051(BaseRule):
INNER JOIN baz;
"""

groups = ("all",)
name = "ambiguous.join"
aliases = ("L051",)
groups: Tuple[str, ...] = ("all", "ambiguous")
config_keywords = ["fully_qualify_join_types"]
crawl_behaviour = SegmentSeekerCrawler({"join_clause"})
is_fix_compatible = True
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""Implementation of Rule L054."""
from typing import Optional, List
"""Implementation of Rule AM06."""
from typing import Optional, List, Tuple

from sqlfluff.core.rules import BaseRule, LintResult, RuleContext
from sqlfluff.core.rules.crawlers import SegmentSeekerCrawler
from sqlfluff.utils.functional import sp, FunctionalContext


class Rule_L054(BaseRule):
class Rule_AM06(BaseRule):
"""Inconsistent column references in ``GROUP BY/ORDER BY`` clauses.

.. note::
Expand Down Expand Up @@ -80,7 +80,9 @@ class Rule_L054(BaseRule):
1, 2;
"""

groups = ("all", "core")
name = "ambiguous.column_references"
aliases = ("L054",)
groups: Tuple[str, ...] = ("all", "core", "ambiguous")
config_keywords = ["group_by_and_order_by_style"]
crawl_behaviour = SegmentSeekerCrawler({"groupby_clause", "orderby_clause"})
_ignore_types: List[str] = ["withingroup_clause", "window_specification"]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""Implementation of Rule L068."""
from typing import Optional
"""Implementation of Rule AM07."""
from typing import Optional, Tuple

from sqlfluff.utils.analysis.select_crawler import Query, SelectCrawler, WildcardInfo
from sqlfluff.core.rules import BaseRule, LintResult, RuleContext
from sqlfluff.core.rules.crawlers import SegmentSeekerCrawler


class Rule_L068(BaseRule):
class Rule_AM07(BaseRule):
"""Queries within set query produce different numbers of columns.

**Anti-pattern**
Expand Down Expand Up @@ -50,7 +50,9 @@ class Rule_L068(BaseRule):
FROM t
"""

groups = ("all",)
name = "ambiguous.set_columns"
aliases = ("L068",)
groups: Tuple[str, ...] = ("all", "ambiguous")
crawl_behaviour = SegmentSeekerCrawler({"set_expression"}, provide_raw_stack=True)

def __handle_alias_case(
Expand Down
20 changes: 20 additions & 0 deletions src/sqlfluff/rules/ambiguous/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""The ambiguous plugin bundle.

NOTE: Yes the title of this bundle is ...ambiguous. 😁
"""

from sqlfluff.core.plugin import hookimpl

from sqlfluff.rules.ambiguous.AM01 import Rule_AM01
from sqlfluff.rules.ambiguous.AM02 import Rule_AM02
from sqlfluff.rules.ambiguous.AM03 import Rule_AM03
from sqlfluff.rules.ambiguous.AM04 import Rule_AM04
from sqlfluff.rules.ambiguous.AM05 import Rule_AM05
from sqlfluff.rules.ambiguous.AM06 import Rule_AM06
from sqlfluff.rules.ambiguous.AM07 import Rule_AM07


@hookimpl
def get_rules():
"""Get plugin rules."""
return [Rule_AM01, Rule_AM02, Rule_AM03, Rule_AM04, Rule_AM05, Rule_AM06, Rule_AM07]
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""Implementation of Rule L035."""
from typing import Optional
"""Implementation of Rule ST01."""
from typing import Optional, Tuple

from sqlfluff.core.rules import BaseRule, LintFix, LintResult, RuleContext
from sqlfluff.core.rules.crawlers import SegmentSeekerCrawler
from sqlfluff.utils.functional import sp, FunctionalContext


class Rule_L035(BaseRule):
class Rule_ST01(BaseRule):
"""Do not specify ``else null`` in a case when statement (redundant).

**Anti-pattern**
Expand Down Expand Up @@ -35,7 +35,9 @@ class Rule_L035(BaseRule):
from x
"""

groups = ("all",)
name = "structure.else_null"
aliases = ("L035",)
groups: Tuple[str, ...] = ("all", "structure")
crawl_behaviour = SegmentSeekerCrawler({"case_expression"})
is_fix_compatible = True

Expand Down