use rowan::TextRange;
use squawk_syntax::{
Parse, SourceFile,
ast::{self, AstNode},
};
use crate::{Linter, Rule, Violation};
pub(crate) fn ban_create_domain_with_constraint(ctx: &mut Linter, parse: &Parse<SourceFile>) {
let file = parse.tree();
for stmt in file.stmts() {
if let ast::Stmt::CreateDomain(domain) = stmt {
let range =
domain
.constraints()
.map(|c| c.syntax().text_range())
.fold(None, |prev, cur| match prev {
None => Some(cur),
Some(prev) => {
let new_start = prev.start().min(cur.start());
let new_end = prev.end().max(cur.end());
Some(TextRange::new(new_start, new_end))
}
});
if let Some(range) = range {
ctx.report(Violation::for_range(
Rule::BanCreateDomainWithConstraint,
"Domains with constraints have poor support for online migrations. Use table and column constraints instead.".into(),
range,
))
}
}
}
}
#[cfg(test)]
mod test {
use insta::assert_snapshot;
use crate::Rule;
use crate::test_utils::{lint_errors, lint_ok};
#[test]
fn err() {
let sql = r#"
CREATE DOMAIN domain_name_3 AS NUMERIC(15,5) CHECK (value > 0);
"#;
assert_snapshot!(lint_errors(sql, Rule::BanCreateDomainWithConstraint));
}
#[test]
fn err_with_multiple_constraints() {
let sql = r#"
create domain d as t check (value > 0) not null;
"#;
assert_snapshot!(lint_errors(sql, Rule::BanCreateDomainWithConstraint));
}
#[test]
fn ok() {
let sql = r#"
CREATE DOMAIN domain_name_1 AS TEXT;
CREATE DOMAIN domain_name_2 AS CHARACTER VARYING;
"#;
lint_ok(sql, Rule::BanCreateDomainWithConstraint);
}
}