rigsql_rules/rigsql/
rg05.rs1use rigsql_core::{Segment, SegmentType};
2
3use crate::rule::{CrawlType, Rule, RuleContext, RuleGroup};
4use crate::violation::LintViolation;
5
6#[derive(Debug, Default)]
11pub struct RuleRG05;
12
13impl Rule for RuleRG05 {
14 fn code(&self) -> &'static str {
15 "RG05"
16 }
17 fn name(&self) -> &'static str {
18 "rigsql.subquery_alias"
19 }
20 fn description(&self) -> &'static str {
21 "Subqueries in FROM clause should have an alias."
22 }
23 fn explanation(&self) -> &'static str {
24 "When a subquery is used as a table source in a FROM or JOIN clause, \
25 it must be given an explicit alias. Without an alias, columns from the \
26 subquery cannot be referenced clearly in the outer query."
27 }
28 fn groups(&self) -> &[RuleGroup] {
29 &[RuleGroup::Aliasing]
30 }
31 fn is_fixable(&self) -> bool {
32 false
33 }
34
35 fn crawl_type(&self) -> CrawlType {
36 CrawlType::Segment(vec![SegmentType::FromClause, SegmentType::JoinClause])
37 }
38
39 fn eval(&self, ctx: &RuleContext) -> Vec<LintViolation> {
40 let mut violations = Vec::new();
41 check_subqueries_have_alias(ctx.segment, &mut violations, self.code());
42 violations
43 }
44}
45
46fn check_subqueries_have_alias(
47 segment: &Segment,
48 violations: &mut Vec<LintViolation>,
49 code: &'static str,
50) {
51 let children = segment.children();
52
53 for child in children {
54 let st = child.segment_type();
55
56 if st == SegmentType::Subquery {
58 violations.push(LintViolation::new(
59 code,
60 "Subquery in FROM/JOIN clause should have an alias.",
61 child.span(),
62 ));
63 continue;
64 }
65
66 if st == SegmentType::AliasExpression {
68 continue;
69 }
70
71 check_subqueries_have_alias(child, violations, code);
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use crate::test_utils::lint_sql;
80
81 #[test]
82 fn test_rg05_flags_bare_subquery() {
83 let violations = lint_sql("SELECT * FROM (SELECT 1)", RuleRG05);
84 assert_eq!(violations.len(), 1);
85 }
86
87 #[test]
88 fn test_rg05_accepts_aliased_subquery() {
89 let violations = lint_sql("SELECT * FROM (SELECT 1) AS sub", RuleRG05);
90 assert_eq!(violations.len(), 0);
91 }
92}