diff --git a/.gitignore b/.gitignore index 65853b2..85b4ce9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # Ignore IDE files .vscode .idea -/.sqlfluff **/.DS_Store # Ignore Python cache and prebuilt things diff --git a/.sqlfluff b/.sqlfluff new file mode 100644 index 0000000..9c76e45 --- /dev/null +++ b/.sqlfluff @@ -0,0 +1,5 @@ +[sqlfluff] +dialect = snowflake + +[sqlfluff:layout:type:binary_operator] +line_position = trailing diff --git a/TODO.md b/TODO.md index 63d3d99..81a86ab 100644 --- a/TODO.md +++ b/TODO.md @@ -3,12 +3,20 @@ [ ] enumerate rules [ ] list all the rules we need to add to the plugin - [x] join and on in the same line -> disable default checks in the config + [x] join and on in the same line + [ ] disable default checks in the sqlfluff config + [ ] do not write 1 = 1 in where clause + [ ] subqueries + [ ] select * from final at the end of CTEs + [ ] name of object created in new indented line + [ ] the last ; should be in a separate line, when the last line of the statement is indented ... -[ ] setup the pipeline to turn this project into a PyPi (https://packaging.python.org/en/latest/tutorials/packaging-projects/) +[x] setup the pipeline to turn this project into a PyPI (https://packaging.python.org/en/latest/tutorials/packaging-projects/) + [ ] when first version is complete, release it to PyPI (not testpypi) + https://twine.readthedocs.io/en/latest/ -[ ] setup flake8 and mypy linting for python code +[x] setup flake8 and mypy linting for python code [ ] Curate and improve code samples, for the guideline .md and for testing [ ] Think better and complete examples @@ -16,3 +24,5 @@ [x] Check https://docs.sqlfluff.com/en/stable/gettingstarted.html#basic-usage * https://docs.sqlfluff.com/en/stable/configuration.html#default-configuration + +[ ] integrate the EasyQL plugin properly in the VS Code sqlfluff plugin diff --git a/src/sqlfluff_easy_ql/__init__.py b/src/sqlfluff_easy_ql/__init__.py index b22a706..d0edcae 100644 --- a/src/sqlfluff_easy_ql/__init__.py +++ b/src/sqlfluff_easy_ql/__init__.py @@ -20,10 +20,12 @@ def get_rules() -> List[Type[BaseRule]]: """ # i.e. we DO recommend importing here: from sqlfluff_easy_ql.rules import ( - Rule_EasyQL_L001, Rule_EasyQL_L002 + Rule_EasyQL_L001, + Rule_EasyQL_L002, + Rule_EasyQL_L003 ) # noqa: F811 - return [Rule_EasyQL_L001, Rule_EasyQL_L002] + return [Rule_EasyQL_L001, Rule_EasyQL_L002, Rule_EasyQL_L003] @hookimpl @@ -40,5 +42,4 @@ def get_configs_info() -> dict: """Get rule config validations and descriptions.""" return { "forbidden_columns": {"definition": "A list of column to forbid"}, - "join_on_same_line": {"definition": "Whether this rule is on or not"} } diff --git a/src/sqlfluff_easy_ql/easy_ql_default_config.cfg b/src/sqlfluff_easy_ql/easy_ql_default_config.cfg index ed7220c..5e90cb4 100644 --- a/src/sqlfluff_easy_ql/easy_ql_default_config.cfg +++ b/src/sqlfluff_easy_ql/easy_ql_default_config.cfg @@ -1,5 +1,5 @@ [sqlfluff:rules:EasyQL_L001] forbidden_columns = bar, baaz -[sqlfluff:rules:EasyQL_L002] -join_on_same_line = True +[sqlfluff:layout:type:binary_operator] +line_position = trailing diff --git a/src/sqlfluff_easy_ql/rules.py b/src/sqlfluff_easy_ql/rules.py index e8ab3f5..8e85fc2 100644 --- a/src/sqlfluff_easy_ql/rules.py +++ b/src/sqlfluff_easy_ql/rules.py @@ -7,9 +7,14 @@ BaseRule, LintResult, RuleContext, + LintResult, + LintFix ) +from sqlfluff.utils.functional import FunctionalContext, sp +from sqlfluff.core.parser import Sequence +from sqlfluff.core.rules.doc_decorators import document_fix_compatible from sqlfluff.core.rules.crawlers import SegmentSeekerCrawler - +from sqlfluff.core.rules.base import BaseRule, LintResult, RuleContext # These two decorators allow plugins # to be displayed in the sqlfluff docs @@ -74,9 +79,6 @@ class Rule_EasyQL_L002(BaseRule): def __init__(self, *args, **kwargs): """Overwrite __init__ to set config.""" super().__init__(*args, **kwargs) - with open("test_eval.txt", "a") as f: - f.write("logging init L2") - f.write("\n---\n") def _eval(self, context: RuleContext): """We should not JOIN .. ON differnet lines.""" @@ -90,3 +92,22 @@ def _eval(self, context: RuleContext): anchor=seg, description=desc ) + + +class Rule_EasyQL_L003(BaseRule): + """Prohibit the use of 'WHERE 1=1' in queries.""" + groups = ("all",) + crawl_behaviour = SegmentSeekerCrawler({"where_clause"}) + is_fix_compatible = False + + def _eval(self, context: RuleContext): + text_segments = ( + FunctionalContext(context) + .segment.children() + ) + + for idx, seg in enumerate(text_segments): + # Look for the pattern '1=1' + if "1=1" in seg.raw_upper.replace(" ", ""): + return LintResult(anchor=seg) + return None \ No newline at end of file diff --git a/test/rules/test_cases/Rule_EasyQL_L003.yml b/test/rules/test_cases/Rule_EasyQL_L003.yml new file mode 100644 index 0000000..83a2589 --- /dev/null +++ b/test/rules/test_cases/Rule_EasyQL_L003.yml @@ -0,0 +1,53 @@ +rule: EasyQL_L003 + +no_where_used: + pass_str: | + SELECT + a, + sum(b) + FROM tbl + +where_without_tautology: + pass_str: | + SELECT + a, + b, + c + FROM tbl + WHERE + a > b + +where_1_eq_1_v1: + fail_str: | + SELECT + bar, + baaz + FROM tbl + WHERE 1 = 1 + +where_1_eq_1_v1: + fail_str: | + SELECT + bar, + baaz + FROM tbl + WHERE 1=1 + AND bar=1 + +where_1_eq_1_v2: + fail_str: | + SELECT + bar, + baaz + FROM tbl + WHERE + 1 = 1 AND bar = 1 + +where_1_eq_1_v3: + fail_str: | + SELECT + bar, + baaz + FROM tbl + WHERE + 1 = 1