use crate::ir::catalog::Catalog;
use crate::lint::finding::{Finding, Severity};
pub const RULE_ID: &str = "table-access-method-change";
pub fn check(source: &Catalog, target: &Catalog) -> Vec<Finding> {
let mut out = Vec::new();
for s in &source.tables {
if let Some(t) = target.tables.iter().find(|t| t.qname == s.qname)
&& s.access_method.is_some()
&& s.access_method != t.access_method
{
out.push(Finding {
severity: Severity::Warning,
rule: RULE_ID,
message: format!(
"table {} access method differs: live={:?}, source={:?} \
— pgevolve does not rewrite a table's access method; run \
ALTER TABLE … SET ACCESS METHOD manually (PG 15+) if intended",
s.qname,
t.access_method
.as_ref()
.map(crate::identifier::Identifier::as_str),
s.access_method
.as_ref()
.map(crate::identifier::Identifier::as_str),
),
location: None,
});
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ir::catalog::Catalog;
use crate::ir::table::Table;
use crate::lint::finding::Severity;
use crate::lint::test_helpers::{id, qn};
fn table(schema: &str, name: &str, am: Option<&str>) -> Table {
Table {
qname: qn(schema, name),
columns: vec![],
constraints: vec![],
partition_by: None,
partition_of: None,
comment: None,
owner: None,
grants: vec![],
rls_enabled: false,
rls_forced: false,
policies: vec![],
storage: crate::ir::reloptions::TableStorageOptions::default(),
access_method: am.map(id),
tablespace: None,
}
}
fn catalog_with(tables: Vec<Table>) -> Catalog {
let mut c = Catalog::empty();
c.tables = tables;
c
}
#[test]
fn differing_access_method_fires_one_finding() {
let source = catalog_with(vec![table("app", "events", Some("columnar"))]);
let target = catalog_with(vec![table("app", "events", None)]);
let findings = check(&source, &target);
assert_eq!(findings.len(), 1);
assert_eq!(findings[0].rule, RULE_ID);
assert_eq!(findings[0].severity, Severity::Warning);
assert!(
findings[0].message.contains("columnar"),
"message should mention the source access method: {}",
findings[0].message
);
}
#[test]
fn same_access_method_no_finding() {
let source = catalog_with(vec![table("app", "events", Some("columnar"))]);
let target = catalog_with(vec![table("app", "events", Some("columnar"))]);
assert!(check(&source, &target).is_empty());
}
#[test]
fn table_only_in_source_no_finding() {
let source = catalog_with(vec![table("app", "events", Some("columnar"))]);
let target = catalog_with(vec![]);
assert!(check(&source, &target).is_empty());
}
#[test]
fn source_access_method_none_no_finding() {
let source = catalog_with(vec![table("app", "events", None)]);
let target = catalog_with(vec![table("app", "events", Some("columnar"))]);
assert!(check(&source, &target).is_empty());
}
}