Skip to main content

pg2sqlite_core/pg/
normalize.rs

1/// Schema filtering and identifier normalization for parsed PG DDL.
2use crate::ir::SchemaModel;
3
4/// Options for schema normalization.
5pub struct NormalizeOptions {
6    /// Schema to include (default: "public").
7    pub schema: Option<String>,
8    /// If true, include all schemas (bypass schema filtering).
9    pub include_all_schemas: bool,
10}
11
12impl Default for NormalizeOptions {
13    fn default() -> Self {
14        Self {
15            schema: Some("public".to_string()),
16            include_all_schemas: false,
17        }
18    }
19}
20
21/// Filter and normalize the schema model based on options.
22pub fn normalize(model: &mut SchemaModel, opts: &NormalizeOptions) {
23    if opts.include_all_schemas {
24        return;
25    }
26
27    let target_schema = opts.schema.as_deref().unwrap_or("public");
28
29    // Filter tables by schema
30    model.tables.retain(|t| {
31        match &t.name.schema {
32            Some(s) => s.normalized == target_schema,
33            None => true, // Unqualified names are assumed to be in the target schema
34        }
35    });
36
37    // Filter indexes by table schema
38    model.indexes.retain(|idx| match &idx.table.schema {
39        Some(s) => s.normalized == target_schema,
40        None => true,
41    });
42
43    // Filter sequences by schema
44    model.sequences.retain(|seq| match &seq.name.schema {
45        Some(s) => s.normalized == target_schema,
46        None => true,
47    });
48
49    // Filter enums by schema
50    model.enums.retain(|e| match &e.name.schema {
51        Some(s) => s.normalized == target_schema,
52        None => true,
53    });
54
55    // Filter domains by schema
56    model.domains.retain(|d| match &d.name.schema {
57        Some(s) => s.normalized == target_schema,
58        None => true,
59    });
60
61    // Filter alter constraints by table schema
62    model.alter_constraints.retain(|ac| match &ac.table.schema {
63        Some(s) => s.normalized == target_schema,
64        None => true,
65    });
66
67    // Filter identity columns by table schema
68    model.identity_columns.retain(|ic| match &ic.table.schema {
69        Some(s) => s.normalized == target_schema,
70        None => true,
71    });
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    use crate::pg::parser;
78
79    #[test]
80    fn test_normalize_filters_schema() {
81        let sql = r#"
82            CREATE TABLE public.users (id INTEGER);
83            CREATE TABLE other.accounts (id INTEGER);
84        "#;
85        let (mut model, _) = parser::parse(sql);
86        assert_eq!(model.tables.len(), 2);
87
88        normalize(&mut model, &NormalizeOptions::default());
89        assert_eq!(model.tables.len(), 1);
90        assert_eq!(model.tables[0].name.name.normalized, "users");
91    }
92
93    #[test]
94    fn test_normalize_include_all_schemas() {
95        let sql = r#"
96            CREATE TABLE public.users (id INTEGER);
97            CREATE TABLE other.accounts (id INTEGER);
98        "#;
99        let (mut model, _) = parser::parse(sql);
100        normalize(
101            &mut model,
102            &NormalizeOptions {
103                schema: None,
104                include_all_schemas: true,
105            },
106        );
107        assert_eq!(model.tables.len(), 2);
108    }
109
110    #[test]
111    fn test_normalize_unqualified_passes() {
112        let sql = "CREATE TABLE users (id INTEGER);";
113        let (mut model, _) = parser::parse(sql);
114        normalize(&mut model, &NormalizeOptions::default());
115        assert_eq!(model.tables.len(), 1);
116    }
117
118    #[test]
119    fn test_normalize_custom_schema() {
120        let sql = r#"
121            CREATE TABLE myschema.users (id INTEGER);
122            CREATE TABLE public.accounts (id INTEGER);
123        "#;
124        let (mut model, _) = parser::parse(sql);
125        normalize(
126            &mut model,
127            &NormalizeOptions {
128                schema: Some("myschema".to_string()),
129                include_all_schemas: false,
130            },
131        );
132        assert_eq!(model.tables.len(), 1);
133        assert_eq!(model.tables[0].name.name.normalized, "users");
134    }
135}