qail/
sql_gen.rs

1//! SQL generation utilities for migrations
2
3use qail_core::prelude::*;
4
5/// Convert QailCmd to SQL string for preview.
6pub fn cmd_to_sql(cmd: &QailCmd) -> String {
7    // Generate SQL for migration DDL operations
8    match cmd.action {
9        Action::Make => {
10            // CREATE TABLE
11            let mut sql = format!("CREATE TABLE {} (", cmd.table);
12            let cols: Vec<String> = cmd.columns.iter().filter_map(|col| {
13                if let Expr::Def { name, data_type, constraints } = col {
14                    let mut col_def = format!("{} {}", name, data_type);
15                    for c in constraints {
16                        match c {
17                            Constraint::PrimaryKey => col_def.push_str(" PRIMARY KEY"),
18                            Constraint::Nullable => {},
19                            Constraint::Unique => col_def.push_str(" UNIQUE"),
20                            Constraint::Default(v) => col_def.push_str(&format!(" DEFAULT {}", v)),
21                            _ => {},
22                        }
23                    }
24                    Some(col_def)
25                } else {
26                    None
27                }
28            }).collect();
29            sql.push_str(&cols.join(", "));
30            sql.push(')');
31            sql
32        },
33        Action::Drop => {
34            format!("DROP TABLE IF EXISTS {}", cmd.table)
35        },
36        Action::Alter => {
37            // ADD COLUMN
38            if let Some(Expr::Def { name, data_type, constraints }) = cmd.columns.first() {
39                let mut sql = format!("ALTER TABLE {} ADD COLUMN {} {}", cmd.table, name, data_type);
40                for c in constraints {
41                    match c {
42                        Constraint::Nullable => {},
43                        Constraint::Unique => sql.push_str(" UNIQUE"),
44                        Constraint::Default(v) => sql.push_str(&format!(" DEFAULT {}", v)),
45                        _ => {},
46                    }
47                }
48                return sql;
49            }
50            format!("ALTER TABLE {} ADD COLUMN ...", cmd.table)
51        },
52        Action::AlterDrop => {
53            // DROP COLUMN
54            if let Some(Expr::Named(name)) = cmd.columns.first() {
55                return format!("ALTER TABLE {} DROP COLUMN {}", cmd.table, name);
56            }
57            if let Some(Expr::Def { name, .. }) = cmd.columns.first() {
58                return format!("ALTER TABLE {} DROP COLUMN {}", cmd.table, name);
59            }
60            format!("ALTER TABLE {} DROP COLUMN ...", cmd.table)
61        },
62        Action::Index => {
63            if let Some(ref idx) = cmd.index_def {
64                let unique = if idx.unique { "UNIQUE " } else { "" };
65                return format!("CREATE {}INDEX {} ON {} ({})", 
66                    unique, idx.name, cmd.table, idx.columns.join(", "));
67            }
68            format!("CREATE INDEX ON {} (...)", cmd.table)
69        },
70        Action::DropIndex => {
71            if let Some(ref idx) = cmd.index_def {
72                return format!("DROP INDEX IF EXISTS {}", idx.name);
73            }
74            "DROP INDEX ...".to_string()
75        },
76        Action::Mod => {
77            // RENAME COLUMN
78            format!("ALTER TABLE {} RENAME COLUMN ... TO ...", cmd.table)
79        },
80        _ => format!("-- Unsupported action: {:?}", cmd.action),
81    }
82}
83
84/// Generate rollback SQL for a command.
85pub fn generate_rollback_sql(cmd: &QailCmd) -> String {
86    match cmd.action {
87        Action::Make => {
88            format!("DROP TABLE IF EXISTS {}", cmd.table)
89        },
90        Action::Drop => {
91            format!("-- Cannot auto-rollback DROP TABLE {} (data lost)", cmd.table)
92        },
93        Action::Alter => {
94            // ADD COLUMN -> DROP COLUMN
95            if let Some(col) = cmd.columns.first() {
96                if let Expr::Def { name, .. } = col {
97                    return format!("ALTER TABLE {} DROP COLUMN {}", cmd.table, name);
98                }
99            }
100            format!("-- Cannot determine rollback for ALTER on {}", cmd.table)
101        },
102        Action::AlterDrop => {
103            // DROP COLUMN -> cannot easily reverse
104            format!("-- Cannot auto-rollback DROP COLUMN on {} (data lost)", cmd.table)
105        },
106        Action::Index => {
107            if let Some(ref idx) = cmd.index_def {
108                return format!("DROP INDEX IF EXISTS {}", idx.name);
109            }
110            "-- Cannot determine index name for rollback".to_string()
111        },
112        Action::DropIndex => {
113            format!("-- Cannot auto-rollback DROP INDEX (need original definition)")
114        },
115        Action::Mod => {
116            format!("-- RENAME operation: reverse manually")
117        },
118        _ => format!("-- No rollback for {:?}", cmd.action),
119    }
120}
121
122/// Generate DOWN SQL for a migration command.
123pub fn generate_down_sql(cmd: &QailCmd) -> String {
124    generate_rollback_sql(cmd)
125}