Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ parse and warn, helps with copy pasting examples

support Trino, BigQuery, Aurora DSLQ, etc.

### Check `format()` calls

https://www.postgresql.org/docs/18/functions-string.html#FUNCTIONS-STRING-FORMAT

```sql
SELECT format('Hello %s', 'World');
-- ok

SELECT format('Hello %s %s', 'World');
-- error ^^
```

### Formatter

```shell
Expand Down Expand Up @@ -387,6 +399,24 @@ select count(*) from t where t.name in ('1 month', '2 month')
-- type checks!
```

### Rule: no-unncesssary-parens

```sql
select (((x * 2)));
-- becomes
select x * 2;
```

```sql
-- ok, indexes on expressions require extra parens
create index foo on t((1));

-- ok
select (x * 2) + 4;
```

related: https://eslint.style/rules/no-extra-parens

### Rule: dialect: now() to dest

should support various fixes so people can write in one dialect of SQL and have it easily convert to the other one
Expand Down
10 changes: 10 additions & 0 deletions crates/squawk/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,14 @@ fail_on_violations = true
fs::write(&squawk_toml, file).expect("Unable to write file");
assert_debug_snapshot!(Config::parse(Some(squawk_toml.path().to_path_buf())));
}
#[test]
fn load_excluded_rules_with_alias() {
let squawk_toml = NamedTempFile::new().expect("generate tempFile");
let file = r#"
excluded_rules = ["prefer-timestamp-tz", "prefer-timestamptz"]

"#;
fs::write(&squawk_toml, file).expect("Unable to write file");
assert_debug_snapshot!(Config::parse(Some(squawk_toml.path().to_path_buf())));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
source: crates/squawk/src/config.rs
expression: "Config::parse(Some(squawk_toml.path().to_path_buf()))"
---
Ok(
Some(
Config {
excluded_paths: [],
excluded_rules: [
PreferTimestampTz,
PreferTimestampTz,
],
pg_version: None,
assume_in_transaction: None,
upload_to_github: UploadToGitHubConfig {
fail_on_violations: None,
},
},
),
)
35 changes: 33 additions & 2 deletions crates/squawk_linter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ use rules::require_concurrent_index_deletion;
use rules::transaction_nesting;
// xtask:new-rule:rule-import

#[derive(Debug, PartialEq, Clone, Copy, Serialize, Hash, Eq, Deserialize, Sequence)]
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Hash, Eq, Sequence)]
pub enum Rule {
#[serde(rename = "require-concurrent-index-creation")]
RequireConcurrentIndexCreation,
Expand Down Expand Up @@ -176,7 +176,7 @@ impl std::error::Error for UnknownRuleName {}
impl std::str::FromStr for Rule {
type Err = UnknownRuleName;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_plain::from_str(s).map_err(|_| UnknownRuleName { val: s.to_string() })
Rule::try_from(s).map_err(|_| UnknownRuleName { val: s.to_string() })
}
}

Expand Down Expand Up @@ -220,6 +220,16 @@ impl fmt::Display for Rule {
}
}

impl<'de> Deserialize<'de> for Rule {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Fix {
pub title: String,
Expand Down Expand Up @@ -470,3 +480,24 @@ impl Linter {
}
}
}

#[cfg(test)]
mod tests {
use insta::assert_debug_snapshot;

use super::*;

#[test]
fn prefer_timestamp_aliases() {
let rule1: Rule = "prefer-timestamp-tz".parse().unwrap();
let rule2: Rule = "prefer-timestamptz".parse().unwrap();
assert_eq!(rule1, rule2);
assert_debug_snapshot!(rule1, @"PreferTimestampTz");
}

#[test]
fn invalid_rule_name() {
let result: Result<Rule, _> = "invalid-rule-name".parse();
assert!(result.is_err());
}
}
Loading