squawk-linter 2.50.0

Linter for Postgres migrations & SQL
Documentation
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);
    }
}