use crate::checks::Check;
use crate::violation::Violation;
use sqlparser::ast::Statement;
pub struct CreateExtensionCheck;
impl Check for CreateExtensionCheck {
fn check(&self, stmt: &Statement) -> Vec<Violation> {
let mut violations = vec![];
if let Statement::CreateExtension {
name,
if_not_exists,
..
} = stmt
{
let extension_name = name.to_string();
let if_not_exists_str = if *if_not_exists { "IF NOT EXISTS " } else { "" };
violations.push(Violation::new(
"CREATE EXTENSION",
format!(
"Creating extension '{extension}' in a migration requires superuser privileges, which application \
database users typically lack in production. Extensions are infrastructure concerns that should be \
managed outside application migrations.",
extension = extension_name
),
format!(
r#"Install the extension outside of migrations:
1. For local development, add to your database setup scripts:
CREATE EXTENSION {if_not_exists}{extension};
2. For production, use infrastructure automation (Ansible, Terraform, etc.):
- Include extension installation in database provisioning
- Grant appropriate privileges to superuser/admin role
- Run before deploying application migrations
3. Document required extensions in your project README
Note: Common extensions like pg_trgm, uuid-ossp, hstore, and postgis should be
installed by your DBA or infrastructure team before application deployment."#,
if_not_exists = if_not_exists_str,
extension = extension_name
),
));
}
violations
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{assert_allows, assert_detects_violation};
#[test]
fn test_detects_create_extension() {
assert_detects_violation!(
CreateExtensionCheck,
"CREATE EXTENSION pg_trgm;",
"CREATE EXTENSION"
);
}
#[test]
fn test_detects_create_extension_if_not_exists() {
assert_detects_violation!(
CreateExtensionCheck,
"CREATE EXTENSION IF NOT EXISTS uuid_ossp;",
"CREATE EXTENSION"
);
}
#[test]
fn test_ignores_other_create_statements() {
assert_allows!(
CreateExtensionCheck,
"CREATE TABLE users (id SERIAL PRIMARY KEY);"
);
}
#[test]
fn test_ignores_create_index() {
assert_allows!(
CreateExtensionCheck,
"CREATE INDEX idx_users_email ON users(email);"
);
}
}