Skip to content

Commit

Permalink
feat: add RF02 rule
Browse files Browse the repository at this point in the history
  • Loading branch information
gvozdvmozgu authored and benfdking committed Jul 11, 2024
1 parent 2ab693b commit d9e0774
Show file tree
Hide file tree
Showing 9 changed files with 747 additions and 45 deletions.
5 changes: 4 additions & 1 deletion crates/lib/src/core/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,10 @@ impl Value {
pub fn as_array(&self) -> Option<Vec<Value>> {
match self {
Self::Array(v) => Some(v.clone()),
v @ Self::String(_) => Some(vec![v.clone()]),
Self::String(q) => {
let xs = q.split(',').map(|it| Value::String(it.into())).collect_vec();
Some(xs)
}
_ => None,
}
}
Expand Down
11 changes: 9 additions & 2 deletions crates/lib/src/core/parser/segments/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::core::parser::markers::PositionMarker;
use crate::core::parser::segments::fix::{AnchorEditInfo, FixPatch, SourceFix};
use crate::core::rules::base::{EditType, LintFix};
use crate::core::templaters::base::TemplatedFile;
use crate::dialects::ansi::ObjectReferenceSegment;
use crate::dialects::ansi::{ObjectReferenceKind, ObjectReferenceSegment};
use crate::helpers::ToErasedSegment;

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -123,7 +123,14 @@ impl ErasedSegment {
}

pub fn reference(&self) -> ObjectReferenceSegment {
ObjectReferenceSegment(self.clone())
ObjectReferenceSegment(
self.clone(),
match self.get_type() {
"table_reference" => ObjectReferenceKind::Table,
"wildcard_identifier" => ObjectReferenceKind::WildcardIdentifier,
_ => ObjectReferenceKind::Object,
},
)
}

pub fn recursive_crawl_all(&self, reverse: bool) -> Vec<ErasedSegment> {
Expand Down
91 changes: 83 additions & 8 deletions crates/lib/src/dialects/ansi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::fmt::Debug;
use std::sync::{Arc, OnceLock};

use ahash::AHashSet;
use itertools::Itertools;
use smol_str::SmolStr;
use itertools::{enumerate, Itertools};
use smol_str::{SmolStr, ToSmolStr};
use uuid::Uuid;

use super::ansi_keywords::{ANSI_RESERVED_KEYWORDS, ANSI_UNRESERVED_KEYWORDS};
Expand Down Expand Up @@ -6048,7 +6048,14 @@ pub struct ObjectReferencePart {
}

#[derive(Clone)]
pub struct ObjectReferenceSegment(pub ErasedSegment);
pub struct ObjectReferenceSegment(pub ErasedSegment, pub ObjectReferenceKind);

#[derive(Clone)]
pub enum ObjectReferenceKind {
Object,
Table,
WildcardIdentifier,
}

impl ObjectReferenceSegment {
pub fn is_qualified(&self) -> bool {
Expand Down Expand Up @@ -6116,13 +6123,81 @@ impl ObjectReferenceSegment {
}

pub fn iter_raw_references(&self) -> Vec<ObjectReferencePart> {
let mut acc = Vec::new();
match self.1 {
ObjectReferenceKind::Object => {
let mut acc = Vec::new();

for elem in self.0.recursive_crawl(&["identifier", "naked_identifier"], true, None, true) {
acc.extend(self.iter_reference_parts(elem));
}
for elem in
self.0.recursive_crawl(&["identifier", "naked_identifier"], true, None, true)
{
acc.extend(self.iter_reference_parts(elem));
}

acc
acc
}
ObjectReferenceKind::Table => {
let mut acc = Vec::new();
let mut parts = Vec::new();
let mut elems_for_parts = Vec::new();

let mut flush =
|parts: &mut Vec<SmolStr>, elems_for_parts: &mut Vec<ErasedSegment>| {
acc.push(ObjectReferencePart {
part: std::mem::take(parts).iter().join(""),
segments: std::mem::take(elems_for_parts),
});
};

for elem in self.0.recursive_crawl(
&["identifier", "naked_identifier", "literal", "dash", "dot", "star"],
true,
None,
true,
) {
if !elem.is_type("dot") {
if elem.is_type("identifier") || elem.is_type("naked_identifier") {
let elem_raw = elem.raw();
let elem_subparts = elem_raw.split(".").collect_vec();
let elem_subparts_count = elem_subparts.len();

for (idx, part) in enumerate(elem_subparts) {
parts.push(part.to_smolstr());
elems_for_parts.push(elem.clone());

if idx != elem_subparts_count - 1 {
flush(&mut parts, &mut elems_for_parts);
}
}
} else {
parts.push(elem.raw().to_smolstr());
elems_for_parts.push(elem);
}
} else {
flush(&mut parts, &mut elems_for_parts);
}
}

if !parts.is_empty() {
flush(&mut parts, &mut elems_for_parts);
}

acc
}
ObjectReferenceKind::WildcardIdentifier => {
let mut acc = Vec::new();

for elem in self.0.recursive_crawl(
&["identifier", "star", "naked_identifier"],
true,
None,
true,
) {
acc.extend(self.iter_reference_parts(elem));
}

acc
}
}
}

fn iter_reference_parts(&self, elem: ErasedSegment) -> Vec<ObjectReferencePart> {
Expand Down
2 changes: 1 addition & 1 deletion crates/lib/src/rules/aliasing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub fn rules() -> Vec<ErasedRule> {
AL01::RuleAL01::default().erased(),
AL02::RuleAL02::default().erased(),
AL03::RuleAL03.erased(),
AL04::RuleAL04.erased(),
AL04::RuleAL04::default().erased(),
AL05::RuleAL05.erased(),
AL06::RuleAL06::default().erased(),
AL07::RuleAL07::default().erased(),
Expand Down
87 changes: 56 additions & 31 deletions crates/lib/src/rules/aliasing/AL04.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
use std::fmt::Debug;

use ahash::{AHashMap, AHashSet};
use smol_str::SmolStr;

use crate::core::config::Value;
use crate::core::dialects::common::AliasInfo;
use crate::core::dialects::common::{AliasInfo, ColumnAliasInfo};
use crate::core::rules::base::{Erased, ErasedRule, LintResult, Rule, RuleGroups};
use crate::core::rules::context::RuleContext;
use crate::core::rules::crawlers::{Crawler, SegmentSeekerCrawler};
use crate::dialects::ansi::ObjectReferenceSegment;
use crate::helpers::IndexSet;
use crate::utils::analysis::select::get_select_statement_info;

#[derive(Debug, Clone, Default)]
pub struct RuleAL04;
#[derive(Debug, Clone)]
pub struct RuleAL04<T = ()> {
pub(crate) lint_references_and_aliases: fn(
Vec<AliasInfo>,
Vec<SmolStr>,
Vec<ObjectReferenceSegment>,
Vec<ColumnAliasInfo>,
Vec<SmolStr>,
&T,
) -> Vec<LintResult>,
pub(crate) context: T,
}

impl Rule for RuleAL04 {
impl Default for RuleAL04 {
fn default() -> Self {
RuleAL04 { lint_references_and_aliases: Self::lint_references_and_aliases, context: () }
}
}

impl<T: Clone + Debug + Send + Sync + 'static> Rule for RuleAL04<T> {
fn load_from_config(&self, _config: &AHashMap<String, Value>) -> Result<ErasedRule, String> {
Ok(RuleAL04.erased())
Ok(RuleAL04::default().erased())
}

fn name(&self) -> &'static str {
Expand Down Expand Up @@ -84,7 +104,14 @@ FROM
let _parent_select =
context.parent_stack.iter().rev().find(|seg| seg.is_type("select_statement"));

self.lint_references_and_aliases(select_info.table_aliases).unwrap_or_default()
(self.lint_references_and_aliases)(
select_info.table_aliases,
select_info.standalone_aliases,
select_info.reference_buffer,
select_info.col_aliases,
select_info.using_cols,
&self.context,
)
}

fn crawl_behaviour(&self) -> Crawler {
Expand All @@ -94,9 +121,13 @@ FROM

impl RuleAL04 {
pub fn lint_references_and_aliases(
&self,
table_aliases: Vec<AliasInfo>,
) -> Option<Vec<LintResult>> {
_: Vec<SmolStr>,
_: Vec<ObjectReferenceSegment>,
_: Vec<ColumnAliasInfo>,
_: Vec<SmolStr>,
_: &(),
) -> Vec<LintResult> {
let mut duplicates = IndexSet::default();
let mut seen: AHashSet<_> = AHashSet::new();

Expand All @@ -106,28 +137,22 @@ impl RuleAL04 {
}
}

if duplicates.is_empty() {
None
} else {
Some(
duplicates
.into_iter()
.map(|alias| {
LintResult::new(
alias.segment.clone(),
Vec::new(),
None,
format!(
"Duplicate table alias '{}'. Table aliases should be unique.",
alias.ref_str
)
.into(),
None,
)
})
.collect(),
)
}
duplicates
.into_iter()
.map(|alias| {
LintResult::new(
alias.segment.clone(),
Vec::new(),
None,
format!(
"Duplicate table alias '{}'. Table aliases should be unique.",
alias.ref_str
)
.into(),
None,
)
})
.collect()
}
}

Expand All @@ -138,7 +163,7 @@ mod tests {
use crate::rules::aliasing::AL04::RuleAL04;

fn rules() -> Vec<ErasedRule> {
vec![RuleAL04.erased()]
vec![RuleAL04::default().erased()]
}

#[test]
Expand Down
2 changes: 2 additions & 0 deletions crates/lib/src/rules/references.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::core::rules::base::ErasedRule;

pub mod RF01;
mod RF02;
pub mod RF03;
pub mod RF04;
pub mod RF05;
Expand All @@ -11,6 +12,7 @@ pub fn rules() -> Vec<ErasedRule> {

vec![
RF01::RuleRF01.erased(),
RF02::RuleRF02::default().erased(),
RF03::RuleRF03::default().erased(),
RF04::RuleRF04::default().erased(),
RF05::RuleRF05::default().erased(),
Expand Down
5 changes: 3 additions & 2 deletions crates/lib/src/rules/references/RF01.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ impl RuleRF01 {
}

if let Some(object_reference) = &alias.object_reference {
let references = ObjectReferenceSegment(object_reference.clone())
let references = object_reference
.reference()
.iter_raw_references()
.into_iter()
.map(|it| it.part.into())
Expand Down Expand Up @@ -217,7 +218,7 @@ FROM foo
let dml_target_table = if !context.segment.is_type("select_statement") {
let refs = context.segment.recursive_crawl(&["table_reference"], true, None, true);
if let Some(reference) = refs.first() {
let reference = ObjectReferenceSegment(reference.clone());
let reference = reference.reference();

tmp = reference
.iter_raw_references()
Expand Down
Loading

0 comments on commit d9e0774

Please sign in to comment.