diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 2d50ed60c..7d7ff2f63 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -9,7 +9,16 @@ jobs: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 - uses: dtolnay/rust-toolchain@nightly - - run: cargo r -F codegen-docs > options.md + - run: cargo r -F codegen-docs - uses: stefanzweifel/git-auto-commit-action@v5 with: push_options: '--force' + codegen-check: + name: codegen-check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: Swatinem/rust-cache@v2 + - uses: dtolnay/rust-toolchain@nightly + - run: cargo r --bin sqruff -F codegen-docs + - run: git diff --quiet || exit 1 \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 22bb68f75..fe4f54cd7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -923,6 +923,15 @@ dependencies = [ "libc", ] +[[package]] +name = "minijinja" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e136ef580d7955019ab0a407b68d77c292a9976907e217900f3f76bc8f6dc1a4" +dependencies = [ + "serde", +] + [[package]] name = "miniz_oxide" version = "0.7.3" @@ -1518,6 +1527,8 @@ dependencies = [ "clap", "clap-markdown", "console", + "minijinja", + "serde", "sqruff-lib", "sqruff-lsp", "tikv-jemallocator", diff --git a/README.md b/README.md index 6905656df..941566de7 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,12 @@ To get help on the available commands and options, run the following command: sqruff --help ``` +For all the details on the CLI commands and options, see the [CLI documentation](./docs/cli.md). + +### Configuration + +For all the details on the rules options, see the [configuration documentation](./docs/rules.md). + ## Community Join the Quary community on [Slack](https://join.slack.com/t/quarylabs/shared_invite/zt-2dlbfnztw-dMLXJVL38NcbhqRuM5gUcw) to ask questions, suggest features, or share your projects. Also feel free to raise any issues in the repository. diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index c521b57c2..558845843 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -17,14 +17,18 @@ harness = false [features] jemalloc = ["jemallocator"] -codegen-docs = ["clap-markdown"] +codegen-docs = ["clap-markdown", "minijinja", "serde"] [dependencies] sqruff-lib = { version = "0.7.0", path = "../lib" } lsp = { version = "*", package = "sqruff-lsp", path = "../lsp" } clap = { version = "4", features = ["derive"] } console = "0.15.8" + +# Codegen dependencies clap-markdown = { version = "0.1.4", optional = true } +minijinja= { version="2.0.2" , optional = true } +serde = { version = "1.0.203", features = ["derive"], optional = true } [target.'cfg(not(target_env = "msvc"))'.dependencies] jemallocator = { version = "0.5", package = "tikv-jemallocator", optional = true } diff --git a/crates/cli/src/docs.rs b/crates/cli/src/docs.rs new file mode 100644 index 000000000..2c639fcd0 --- /dev/null +++ b/crates/cli/src/docs.rs @@ -0,0 +1,54 @@ +use std::io::Write; +use std::path::Path; + +use minijinja::{context, Environment}; +use serde::Serialize; +use sqruff_lib::core::rules::base::ErasedRule; +use sqruff_lib::rules::rules; + +use crate::commands::Cli; + +#[cfg(feature = "codegen-docs")] +pub(crate) fn codegen_docs() { + // CLI Docs + let markdown: String = clap_markdown::help_markdown::(); + let file_cli = std::fs::File::create("docs/cli.md").unwrap(); + let mut writer = std::io::BufWriter::new(file_cli); + writer.write_all(markdown.as_bytes()).unwrap(); + + // Rules Docs + let mut env = Environment::new(); + let crate_dir = env!("CARGO_MANIFEST_DIR"); + let template_path = + Path::new(crate_dir).join("src").join("docs").join("generate_rule_docs_template.md"); + let template = std::fs::read_to_string(template_path).expect("Failed to read template file"); + env.add_template("rules", &template).unwrap(); + + let tmpl = env.get_template("rules").unwrap(); + let rules = rules(); + let rules = rules.into_iter().map(Rule::from).collect::>(); + let file_rules = std::fs::File::create("docs/rules.md").unwrap(); + let mut writer = std::io::BufWriter::new(file_rules); + writer.write_all(tmpl.render(context!(rules => rules)).unwrap().as_bytes()).unwrap(); +} + +#[derive(Debug, Clone, Serialize)] +struct Rule { + pub name: &'static str, + pub code: &'static str, + pub description: &'static str, + pub fixable: bool, + pub long_description: Option<&'static str>, +} + +impl From for Rule { + fn from(value: ErasedRule) -> Self { + Rule { + name: value.name(), + code: value.code(), + fixable: value.is_fix_compatible(), + description: value.description(), + long_description: value.long_description(), + } + } +} diff --git a/crates/cli/src/docs/generate_rule_docs_template.md b/crates/cli/src/docs/generate_rule_docs_template.md new file mode 100644 index 000000000..482f9da05 --- /dev/null +++ b/crates/cli/src/docs/generate_rule_docs_template.md @@ -0,0 +1,22 @@ +# Rules + +The following rules are available in this create. This list is generated from the `rules` module in the source code and can be turned on or off and configured in the config file. + +## Rule Index + +| Rule Code | Rule Name | +|-----------|-----------|{% for rule in rules %} +| {{ rule.code }} | [{{ rule.name }}](#{{ rule.name }}) |{% endfor %} + +## Rule Details +{% for rule in rules %} +### {{ rule.name }} + +{{ rule.description }} + +**Code:** {{ rule.code }} + +**Fixable:** {% if rule.fixable %}Yes{% else %}No{% endif %} + +{% if rule.long_description %}{{ rule.long_description }}{% endif %} +{% endfor %} diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index a34b3097f..c23e8461f 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -5,18 +5,17 @@ use sqruff_lib::core::config::FluffConfig; use sqruff_lib::core::linter::linter::Linter; use crate::commands::{Cli, Commands}; +#[cfg(feature = "codegen-docs")] +use crate::docs::codegen_docs; + mod commands; +#[cfg(feature = "codegen-docs")] +mod docs; #[cfg(all(feature = "jemalloc", not(target_env = "msvc")))] #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; -#[cfg(feature = "codegen-docs")] -fn codegen_docs() { - let markdown: String = clap_markdown::help_markdown::(); - println!("{markdown}"); -} - #[cfg_attr(feature = "codegen-docs", allow(unreachable_code))] fn main() { #[cfg(feature = "codegen-docs")] diff --git a/crates/lib/src/core/rules/base.rs b/crates/lib/src/core/rules/base.rs index f6a2f4355..4e85ec391 100644 --- a/crates/lib/src/core/rules/base.rs +++ b/crates/lib/src/core/rules/base.rs @@ -248,6 +248,10 @@ pub trait Rule: CloneRule + dyn_clone::DynClone + Debug + 'static + Send + Sync fn name(&self) -> &'static str; + fn long_description(&self) -> Option<&'static str> { + None + } + fn config_ref(&self) -> &'static str { self.name() } diff --git a/crates/lib/src/rules/aliasing/AL01.rs b/crates/lib/src/rules/aliasing/AL01.rs index a3c366c8d..f64dfd73f 100644 --- a/crates/lib/src/rules/aliasing/AL01.rs +++ b/crates/lib/src/rules/aliasing/AL01.rs @@ -132,6 +132,31 @@ impl Rule for RuleAL01 { fn crawl_behaviour(&self) -> Crawler { SegmentSeekerCrawler::new(["alias_expression"].into()).into() } + + fn long_description(&self) -> Option<&'static str> { + r#" +**Anti-pattern** + +In this example, the alias `voo` is implicit. + +```sql +SELECT + voo.a +FROM foo voo +``` + +**Best practice** + +Add `AS` to make the alias explicit. + +```sql +SELECT + voo.a +FROM foo AS voo +``` +"# + .into() + } } #[cfg(test)] diff --git a/crates/lib/src/rules/aliasing/AL02.rs b/crates/lib/src/rules/aliasing/AL02.rs index e4c6fd63d..ed0ffbf12 100644 --- a/crates/lib/src/rules/aliasing/AL02.rs +++ b/crates/lib/src/rules/aliasing/AL02.rs @@ -34,6 +34,31 @@ impl Rule for RuleAL02 { "aliasing.column" } + fn long_description(&self) -> Option<&'static str> { + r#" +**Anti-pattern** + +In this example, the alias for column `a` is implicit. + +```sql +SELECT + a alias_col +FROM foo +``` + +**Best practice** + +Add the `AS` keyword to make the alias explicit. + +```sql +SELECT + a AS alias_col +FROM foo +``` +"# + .into() + } + fn description(&self) -> &'static str { "Implicit/explicit aliasing of columns." } diff --git a/crates/lib/src/rules/aliasing/AL03.rs b/crates/lib/src/rules/aliasing/AL03.rs index 9bdb0b179..35c191a9a 100644 --- a/crates/lib/src/rules/aliasing/AL03.rs +++ b/crates/lib/src/rules/aliasing/AL03.rs @@ -20,6 +20,33 @@ impl Rule for RuleAL03 { "aliasing.expression" } + fn long_description(&self) -> Option<&'static str> { + r#" +**Anti-pattern** + +In this example, there is no alias for both sums. + +```sql +SELECT + sum(a), + sum(b) +FROM foo +``` + +**Best practice** + +Add aliases. + +```sql +SELECT + sum(a) AS a_sum, + sum(b) AS b_sum +FROM foo +``` +"# + .into() + } + fn description(&self) -> &'static str { "Column expression without alias. Use explicit `AS` clause." } diff --git a/crates/lib/src/rules/aliasing/AL04.rs b/crates/lib/src/rules/aliasing/AL04.rs index 317a874a0..9dde1065d 100644 --- a/crates/lib/src/rules/aliasing/AL04.rs +++ b/crates/lib/src/rules/aliasing/AL04.rs @@ -20,6 +20,53 @@ impl Rule for RuleAL04 { "aliasing.unique.table" } + fn long_description(&self) -> Option<&'static str> { + r#" +**Anti-pattern** + +In this example, the alias t is reused for two different tables: + +```sql +SELECT + t.a, + t.b +FROM foo AS t, bar AS t + +-- This can also happen when using schemas where the +-- implicit alias is the table name: + +SELECT + a, + b +FROM + 2020.foo, + 2021.foo +``` + +**Best practice** + +Make all tables have a unique alias. + +```sql +SELECT + f.a, + b.b +FROM foo AS f, bar AS b + +-- Also use explicit aliases when referencing two tables +-- with the same name from two different schemas. + +SELECT + f1.a, + f2.b +FROM + 2020.foo AS f1, + 2021.foo AS f2 +``` +"# + .into() + } + fn description(&self) -> &'static str { "Table aliases should be unique within each clause." } diff --git a/crates/lib/src/rules/aliasing/AL05.rs b/crates/lib/src/rules/aliasing/AL05.rs index 41c055738..d874a9f80 100644 --- a/crates/lib/src/rules/aliasing/AL05.rs +++ b/crates/lib/src/rules/aliasing/AL05.rs @@ -33,12 +33,38 @@ impl Rule for RuleAL05 { "aliasing.unused" } - fn description(&self) -> &'static str { - "Tables should not be aliased if that alias is not used." + fn long_description(&self) -> Option<&'static str> { + r#" +**Anti-pattern** + +In this example, alias `zoo` is not used. + +```sql +SELECT + a +FROM foo AS zoo +``` + +**Best practice** + +Use the alias or remove it. An unused alias makes code harder to read without changing any functionality. + +```sql +SELECT + zoo.a +FROM foo AS zoo + +-- Alternatively... + +SELECT + a +FROM foo +``` +"#.into() } - fn is_fix_compatible(&self) -> bool { - true + fn description(&self) -> &'static str { + "Tables should not be aliased if that alias is not used." } fn eval(&self, context: RuleContext) -> Vec { @@ -72,6 +98,10 @@ impl Rule for RuleAL05 { violations } + fn is_fix_compatible(&self) -> bool { + true + } + fn crawl_behaviour(&self) -> Crawler { SegmentSeekerCrawler::new(["select_statement"].into()).into() } diff --git a/crates/lib/src/rules/aliasing/AL06.rs b/crates/lib/src/rules/aliasing/AL06.rs index cd4953866..5c77285a4 100644 --- a/crates/lib/src/rules/aliasing/AL06.rs +++ b/crates/lib/src/rules/aliasing/AL06.rs @@ -99,6 +99,42 @@ impl Rule for RuleAL06 { fn crawl_behaviour(&self) -> Crawler { SegmentSeekerCrawler::new(["select_statement"].into()).into() } + + fn long_description(&self) -> Option<&'static str> { + r#" +**Anti-pattern** + +In this example, alias `o` is used for the orders table. + +```sql +SELECT + SUM(o.amount) as order_amount, +FROM orders as o +``` + +**Best practice** + +Avoid aliases. Avoid short aliases when aliases are necessary. + +See also: Rule_AL07. + +```sql +SELECT + SUM(orders.amount) as order_amount, +FROM orders + +SELECT + replacement_orders.amount, + previous_orders.amount +FROM + orders AS replacement_orders +JOIN + orders AS previous_orders + ON replacement_orders.id = previous_orders.replacement_id +``` +"# + .into() + } } #[cfg(test)] diff --git a/crates/lib/src/rules/aliasing/AL07.rs b/crates/lib/src/rules/aliasing/AL07.rs index fd48575ea..fd4c9465b 100644 --- a/crates/lib/src/rules/aliasing/AL07.rs +++ b/crates/lib/src/rules/aliasing/AL07.rs @@ -184,17 +184,51 @@ impl Rule for RuleAL07 { fn load_from_config(&self, config: &AHashMap) -> ErasedRule { RuleAL07 { force_enable: config["force_enable"].as_bool().unwrap() }.erased() } - fn name(&self) -> &'static str { "aliasing.forbid" } - fn description(&self) -> &'static str { - "Avoid table aliases in from clauses and join conditions." + fn long_description(&self) -> Option<&'static str> { + r#" +**Anti-pattern** + +In this example, alias o is used for the orders table, and c is used for customers table. + +```sql +SELECT + COUNT(o.customer_id) as order_amount, + c.name +FROM orders as o +JOIN customers as c on o.id = c.user_id +``` + +**Best practice** + +Avoid aliases. + +```sql +SELECT + COUNT(orders.customer_id) as order_amount, + customers.name +FROM orders +JOIN customers on orders.id = customers.user_id + +-- Self-join will not raise issue + +SELECT + table1.a, + table_alias.b, +FROM + table1 + LEFT JOIN table1 AS table_alias ON + table1.foreign_key = table_alias.foreign_key +``` +"# + .into() } - fn is_fix_compatible(&self) -> bool { - true + fn description(&self) -> &'static str { + "Avoid table aliases in from clauses and join conditions." } fn eval(&self, context: RuleContext) -> Vec { @@ -246,6 +280,10 @@ impl Rule for RuleAL07 { ) } + fn is_fix_compatible(&self) -> bool { + true + } + fn crawl_behaviour(&self) -> Crawler { SegmentSeekerCrawler::new(["select_statement"].into()).into() } diff --git a/crates/lib/src/rules/aliasing/AL08.rs b/crates/lib/src/rules/aliasing/AL08.rs index a517bc5cf..2b248653f 100644 --- a/crates/lib/src/rules/aliasing/AL08.rs +++ b/crates/lib/src/rules/aliasing/AL08.rs @@ -24,6 +24,45 @@ impl Rule for RuleAL08 { "Column aliases should be unique within each clause." } + fn long_description(&self) -> Option<&'static str> { + r#" +**Anti-pattern** + +In this example, alias o is used for the orders table, and c is used for customers table. + +```sql +SELECT + COUNT(o.customer_id) as order_amount, + c.name +FROM orders as o +JOIN customers as c on o.id = c.user_id +``` + +**Best practice** + +Avoid aliases. + +```sql +SELECT + COUNT(orders.customer_id) as order_amount, + customers.name +FROM orders +JOIN customers on orders.id = customers.user_id + +-- Self-join will not raise issue + +SELECT + table1.a, + table_alias.b, +FROM + table1 + LEFT JOIN table1 AS table_alias ON + table1.foreign_key = table_alias.foreign_key +``` +"# + .into() + } + fn eval(&self, context: RuleContext) -> Vec { let mut used_aliases = AHashMap::new(); let mut violations = Vec::new(); diff --git a/options.md b/docs/cli.md similarity index 99% rename from options.md rename to docs/cli.md index 0cbbd1ed4..3684afc52 100644 --- a/options.md +++ b/docs/cli.md @@ -80,4 +80,3 @@ Run an LSP server This document was generated automatically by clap-markdown. - diff --git a/docs/rules.md b/docs/rules.md new file mode 100644 index 000000000..49bb9d4ec --- /dev/null +++ b/docs/rules.md @@ -0,0 +1,657 @@ +# Rules + +The following rules are available in this create. This list is generated from the `rules` module in the source code and can be turned on or off and configured in the config file. + +## Rule Index + +| Rule Code | Rule Name | +|-----------|-----------| +| AL01 | [aliasing.table](#aliasing.table) | +| AL02 | [aliasing.column](#aliasing.column) | +| AL03 | [aliasing.expression](#aliasing.expression) | +| AL04 | [aliasing.unique.table](#aliasing.unique.table) | +| AL05 | [aliasing.unused](#aliasing.unused) | +| AL06 | [aliasing.lenght](#aliasing.lenght) | +| AL07 | [aliasing.forbid](#aliasing.forbid) | +| AL08 | [layout.cte_newline](#layout.cte_newline) | +| AL09 | [aliasing.self_alias.column](#aliasing.self_alias.column) | +| AM01 | [ambiguous.distinct](#ambiguous.distinct) | +| AM02 | [ambiguous.union](#ambiguous.union) | +| AM06 | [ambiguous.column_references](#ambiguous.column_references) | +| CP01 | [capitalisation.keywords](#capitalisation.keywords) | +| CP02 | [capitalisation.identifiers](#capitalisation.identifiers) | +| CP03 | [capitalisation.functions](#capitalisation.functions) | +| CP04 | [capitalisation.literals](#capitalisation.literals) | +| CP05 | [capitalisation.types](#capitalisation.types) | +| CV02 | [convention.coalesce](#convention.coalesce) | +| CV03 | [convention.select_trailing_comma](#convention.select_trailing_comma) | +| CV04 | [convention.count_rows](#convention.count_rows) | +| LT01 | [layout.spacing](#layout.spacing) | +| LT02 | [layout.indent](#layout.indent) | +| LT03 | [layout.operators](#layout.operators) | +| LT04 | [layout.commas](#layout.commas) | +| LT05 | [layout.long_lines](#layout.long_lines) | +| LT06 | [layout.functions](#layout.functions) | +| LT07 | [layout.cte_bracket](#layout.cte_bracket) | +| LT08 | [layout.cte_newline](#layout.cte_newline) | +| LT09 | [layout.select_targets](#layout.select_targets) | +| LT10 | [layout.select_modifiers](#layout.select_modifiers) | +| LT11 | [layout.set_operators](#layout.set_operators) | +| LT12 | [layout.end_of_file](#layout.end_of_file) | +| RF01 | [references.from](#references.from) | +| RF03 | [references.consistent](#references.consistent) | +| ST01 | [structure.else_null](#structure.else_null) | +| ST02 | [structure.simple_case](#structure.simple_case) | +| ST03 | [structure.unused_cte](#structure.unused_cte) | +| ST08 | [structure.distinct](#structure.distinct) | + +## Rule Details + +### aliasing.table + +Implicit/explicit aliasing of table. + +**Code:** AL01 + +**Fixable:** Yes + + +**Anti-pattern** + +In this example, the alias `voo` is implicit. + +```sql +SELECT + voo.a +FROM foo voo +``` + +**Best practice** + +Add `AS` to make the alias explicit. + +```sql +SELECT + voo.a +FROM foo AS voo +``` + + +### aliasing.column + +Implicit/explicit aliasing of columns. + +**Code:** AL02 + +**Fixable:** No + + +**Anti-pattern** + +In this example, the alias for column `a` is implicit. + +```sql +SELECT + a alias_col +FROM foo +``` + +**Best practice** + +Add the `AS` keyword to make the alias explicit. + +```sql +SELECT + a AS alias_col +FROM foo +``` + + +### aliasing.expression + +Column expression without alias. Use explicit `AS` clause. + +**Code:** AL03 + +**Fixable:** No + + +**Anti-pattern** + +In this example, there is no alias for both sums. + +```sql +SELECT + sum(a), + sum(b) +FROM foo +``` + +**Best practice** + +Add aliases. + +```sql +SELECT + sum(a) AS a_sum, + sum(b) AS b_sum +FROM foo +``` + + +### aliasing.unique.table + +Table aliases should be unique within each clause. + +**Code:** AL04 + +**Fixable:** No + + +**Anti-pattern** + +In this example, the alias t is reused for two different tables: + +```sql +SELECT + t.a, + t.b +FROM foo AS t, bar AS t + +-- This can also happen when using schemas where the +-- implicit alias is the table name: + +SELECT + a, + b +FROM + 2020.foo, + 2021.foo +``` + +**Best practice** + +Make all tables have a unique alias. + +```sql +SELECT + f.a, + b.b +FROM foo AS f, bar AS b + +-- Also use explicit aliases when referencing two tables +-- with the same name from two different schemas. + +SELECT + f1.a, + f2.b +FROM + 2020.foo AS f1, + 2021.foo AS f2 +``` + + +### aliasing.unused + +Tables should not be aliased if that alias is not used. + +**Code:** AL05 + +**Fixable:** Yes + + +**Anti-pattern** + +In this example, alias `zoo` is not used. + +```sql +SELECT + a +FROM foo AS zoo +``` + +**Best practice** + +Use the alias or remove it. An unused alias makes code harder to read without changing any functionality. + +```sql +SELECT + zoo.a +FROM foo AS zoo + +-- Alternatively... + +SELECT + a +FROM foo +``` + + +### aliasing.lenght + +Identify aliases in from clause and join conditions + +**Code:** AL06 + +**Fixable:** No + + +**Anti-pattern** + +In this example, alias `o` is used for the orders table. + +```sql +SELECT + SUM(o.amount) as order_amount, +FROM orders as o +``` + +**Best practice** + +Avoid aliases. Avoid short aliases when aliases are necessary. + +See also: Rule_AL07. + +```sql +SELECT + SUM(orders.amount) as order_amount, +FROM orders + +SELECT + replacement_orders.amount, + previous_orders.amount +FROM + orders AS replacement_orders +JOIN + orders AS previous_orders + ON replacement_orders.id = previous_orders.replacement_id +``` + + +### aliasing.forbid + +Avoid table aliases in from clauses and join conditions. + +**Code:** AL07 + +**Fixable:** Yes + + +**Anti-pattern** + +In this example, alias o is used for the orders table, and c is used for customers table. + +```sql +SELECT + COUNT(o.customer_id) as order_amount, + c.name +FROM orders as o +JOIN customers as c on o.id = c.user_id +``` + +**Best practice** + +Avoid aliases. + +```sql +SELECT + COUNT(orders.customer_id) as order_amount, + customers.name +FROM orders +JOIN customers on orders.id = customers.user_id + +-- Self-join will not raise issue + +SELECT + table1.a, + table_alias.b, +FROM + table1 + LEFT JOIN table1 AS table_alias ON + table1.foreign_key = table_alias.foreign_key +``` + + +### layout.cte_newline + +Column aliases should be unique within each clause. + +**Code:** AL08 + +**Fixable:** No + + +**Anti-pattern** + +In this example, alias o is used for the orders table, and c is used for customers table. + +```sql +SELECT + COUNT(o.customer_id) as order_amount, + c.name +FROM orders as o +JOIN customers as c on o.id = c.user_id +``` + +**Best practice** + +Avoid aliases. + +```sql +SELECT + COUNT(orders.customer_id) as order_amount, + customers.name +FROM orders +JOIN customers on orders.id = customers.user_id + +-- Self-join will not raise issue + +SELECT + table1.a, + table_alias.b, +FROM + table1 + LEFT JOIN table1 AS table_alias ON + table1.foreign_key = table_alias.foreign_key +``` + + +### aliasing.self_alias.column + +Find self-aliased columns and fix them + +**Code:** AL09 + +**Fixable:** No + + + +### ambiguous.distinct + +Ambiguous use of 'DISTINCT' in a 'SELECT' statement with 'GROUP BY'. + +**Code:** AM01 + +**Fixable:** No + + + +### ambiguous.union + +Look for UNION keyword not immediately followed by DISTINCT or ALL + +**Code:** AM02 + +**Fixable:** Yes + + + +### ambiguous.column_references + +Inconsistent column references in 'GROUP BY/ORDER BY' clauses. + +**Code:** AM06 + +**Fixable:** No + + + +### capitalisation.keywords + +Inconsistent capitalisation of keywords. + +**Code:** CP01 + +**Fixable:** Yes + + + +### capitalisation.identifiers + +Inconsistent capitalisation of unquoted identifiers. + +**Code:** CP02 + +**Fixable:** Yes + + + +### capitalisation.functions + +Inconsistent capitalisation of function names. + +**Code:** CP03 + +**Fixable:** Yes + + + +### capitalisation.literals + +Inconsistent capitalisation of boolean/null literal. + +**Code:** CP04 + +**Fixable:** Yes + + + +### capitalisation.types + +Inconsistent capitalisation of datatypes. + +**Code:** CP05 + +**Fixable:** Yes + + + +### convention.coalesce + +Use 'COALESCE' instead of 'IFNULL' or 'NVL'. + +**Code:** CV02 + +**Fixable:** No + + + +### convention.select_trailing_comma + +Trailing commas within select clause + +**Code:** CV03 + +**Fixable:** No + + + +### convention.count_rows + +Use consistent syntax to express "count number of rows". + +**Code:** CV04 + +**Fixable:** No + + + +### layout.spacing + +Inappropriate Spacing. + +**Code:** LT01 + +**Fixable:** No + + + +### layout.indent + +Incorrect Indentation. + +**Code:** LT02 + +**Fixable:** Yes + + + +### layout.operators + +Operators should follow a standard for being before/after newlines. + +**Code:** LT03 + +**Fixable:** Yes + + + +### layout.commas + +Leading/Trailing comma enforcement. + +**Code:** LT04 + +**Fixable:** Yes + + + +### layout.long_lines + +Line is too long. + +**Code:** LT05 + +**Fixable:** Yes + + + +### layout.functions + +Function name not immediately followed by parenthesis. + +**Code:** LT06 + +**Fixable:** Yes + + + +### layout.cte_bracket + +'WITH' clause closing bracket should be on a new line. + +**Code:** LT07 + +**Fixable:** No + + + +### layout.cte_newline + +Blank line expected but not found after CTE closing bracket. + +**Code:** LT08 + +**Fixable:** Yes + + + +### layout.select_targets + +Select targets should be on a new line unless there is only one select target. + +**Code:** LT09 + +**Fixable:** Yes + + + +### layout.select_modifiers + +'SELECT' modifiers (e.g. 'DISTINCT') must be on the same line as 'SELECT'. + +**Code:** LT10 + +**Fixable:** Yes + + + +### layout.set_operators + +Set operators should be surrounded by newlines. + +**Code:** LT11 + +**Fixable:** Yes + + + +### layout.end_of_file + +Files must end with a single trailing newline. + +**Code:** LT12 + +**Fixable:** Yes + + + +### references.from + +References cannot reference objects not present in 'FROM' clause. + +**Code:** RF01 + +**Fixable:** No + + + +### references.consistent + +References should be consistent in statements with a single table. + +**Code:** RF03 + +**Fixable:** Yes + + + +### structure.else_null + +Do not specify 'else null' in a case when statement (redundant). + +**Code:** ST01 + +**Fixable:** No + + + +### structure.simple_case + +Unnecessary 'CASE' statement. + +**Code:** ST02 + +**Fixable:** No + + + +### structure.unused_cte + +Query defines a CTE (common-table expression) but does not use it. + +**Code:** ST03 + +**Fixable:** No + + + +### structure.distinct + +Looking for DISTINCT before a bracket + +**Code:** ST08 + +**Fixable:** No + + diff --git a/editors/code/README.md b/editors/code/README.md index 6905656df..941566de7 100644 --- a/editors/code/README.md +++ b/editors/code/README.md @@ -92,6 +92,12 @@ To get help on the available commands and options, run the following command: sqruff --help ``` +For all the details on the CLI commands and options, see the [CLI documentation](./docs/cli.md). + +### Configuration + +For all the details on the rules options, see the [configuration documentation](./docs/rules.md). + ## Community Join the Quary community on [Slack](https://join.slack.com/t/quarylabs/shared_invite/zt-2dlbfnztw-dMLXJVL38NcbhqRuM5gUcw) to ask questions, suggest features, or share your projects. Also feel free to raise any issues in the repository.