use squawk_syntax::{
Parse, SourceFile,
ast::{self, AstNode},
};
use crate::{Linter, Rule, Violation};
pub(crate) fn adding_primary_key_constraint(ctx: &mut Linter, parse: &Parse<SourceFile>) {
let message = "Adding a primary key constraint requires an `ACCESS EXCLUSIVE` lock that will block all reads and writes to the table while the primary key index is built.";
let help = "Add the `PRIMARY KEY` constraint `USING` an index.";
let file = parse.tree();
for stmt in file.stmts() {
if let ast::Stmt::AlterTable(alter_table) = stmt {
for action in alter_table.actions() {
match action {
ast::AlterTableAction::AddConstraint(add_constraint) => {
if let Some(ast::Constraint::PrimaryKeyConstraint(primary_key_constraint)) =
add_constraint.constraint()
{
if primary_key_constraint.using_index().is_none() {
ctx.report(
Violation::for_node(
Rule::AddingSerialPrimaryKeyField,
message.to_string(),
primary_key_constraint.syntax(),
)
.help(help),
);
}
}
}
ast::AlterTableAction::AddColumn(add_column) => {
for constraint in add_column.constraints() {
if let ast::Constraint::PrimaryKeyConstraint(primary_key_constraint) =
constraint
{
if primary_key_constraint.using_index().is_none() {
ctx.report(
Violation::for_node(
Rule::AddingSerialPrimaryKeyField,
message.to_string(),
primary_key_constraint.syntax(),
)
.help(help),
);
}
}
}
}
_ => (),
}
}
}
}
}
#[cfg(test)]
mod test {
use insta::assert_snapshot;
use crate::Rule;
use crate::test_utils::{lint_errors, lint_ok};
#[test]
fn serial_primary_key() {
let sql = r#"
ALTER TABLE a ADD COLUMN b SERIAL PRIMARY KEY;
"#;
assert_snapshot!(lint_errors(sql, Rule::AddingSerialPrimaryKeyField));
}
#[test]
fn plain_primary_key() {
let sql = r#"
ALTER TABLE items ADD PRIMARY KEY (id);
"#;
assert_snapshot!(lint_errors(sql, Rule::AddingSerialPrimaryKeyField));
}
#[test]
fn okay_add_constraint() {
let sql = r#"
ALTER TABLE items ADD CONSTRAINT items_pk PRIMARY KEY USING INDEX items_pk;
"#;
lint_ok(sql, Rule::AddingSerialPrimaryKeyField);
}
}