1use crate::model::{ColumnDef, TableDef};
6
7pub fn quote_ident(name: &str) -> String {
11 format!("\"{}\"", name.replace('"', "\"\""))
12}
13
14pub fn column_sql(c: &ColumnDef, auto_pk: bool) -> String {
19 let mut parts = vec![quote_ident(&c.name), c.ty.sql().to_string()];
20 if c.primary_key && auto_pk {
21 parts.push("PRIMARY KEY AUTOINCREMENT".into());
22 }
23 if !c.nullable {
24 parts.push("NOT NULL".into());
25 }
26 if c.unique {
27 parts.push("UNIQUE".into());
28 }
29 if let Some(d) = &c.default {
30 parts.push(format!("DEFAULT {}", d.sql()));
31 }
32 if let Some(values) = &c.check_in
33 && !values.is_empty()
34 {
35 let list = values
36 .iter()
37 .map(|v| format!("'{}'", v.replace('\'', "''")))
38 .collect::<Vec<_>>()
39 .join(", ");
40 parts.push(format!("CHECK ({} IN ({list}))", quote_ident(&c.name)));
41 }
42 parts.join(" ")
43}
44
45pub fn create_table_sql(t: &TableDef) -> String {
46 let auto = t.auto_id();
47 let mut lines: Vec<String> = t.columns.iter().map(|c| column_sql(c, auto)).collect();
48
49 if !auto {
50 let pk: Vec<String> = t
51 .primary_key()
52 .iter()
53 .map(|c| quote_ident(&c.name))
54 .collect();
55 if !pk.is_empty() {
56 lines.push(format!("PRIMARY KEY ({})", pk.join(", ")));
57 }
58 }
59 for c in &t.columns {
60 if let Some(fk) = &c.references {
61 let mut line = format!(
62 "FOREIGN KEY ({}) REFERENCES {}({})",
63 quote_ident(&c.name),
64 quote_ident(&fk.table),
65 quote_ident(&fk.column)
66 );
67 if let Some(od) = fk.on_delete {
68 line.push_str(&format!(" ON DELETE {}", od.sql()));
69 }
70 lines.push(line);
71 }
72 }
73
74 format!(
75 "CREATE TABLE {} (\n {}\n);",
76 quote_ident(&t.name),
77 lines.join(",\n ")
78 )
79}
80
81pub fn index_name(table: &str, column: &str) -> String {
82 format!("idx_{table}_{column}")
83}
84
85pub fn index_sqls(t: &TableDef) -> Vec<String> {
87 t.columns
88 .iter()
89 .filter(|c| c.index)
90 .map(|c| {
91 format!(
92 "CREATE INDEX {} ON {} ({});",
93 quote_ident(&index_name(&t.name, &c.name)),
94 quote_ident(&t.name),
95 quote_ident(&c.name)
96 )
97 })
98 .collect()
99}
100
101pub fn composite_index_name(table: &str, columns: &[String]) -> String {
102 format!("idx_{table}_{}", columns.join("_"))
103}
104
105pub fn composite_index_sqls(t: &TableDef) -> Vec<String> {
109 let mut out: Vec<String> = t
110 .composite_uniques
111 .iter()
112 .map(|cols| composite_index_sql(&t.name, cols, true))
113 .collect();
114 out.extend(
115 t.composite_indexes
116 .iter()
117 .map(|cols| composite_index_sql(&t.name, cols, false)),
118 );
119 out
120}
121
122fn composite_index_sql(table: &str, columns: &[String], unique: bool) -> String {
123 format!(
124 "CREATE {}INDEX {} ON {} ({});",
125 if unique { "UNIQUE " } else { "" },
126 quote_ident(&composite_index_name(table, columns)),
127 quote_ident(table),
128 columns
129 .iter()
130 .map(|c| quote_ident(c))
131 .collect::<Vec<_>>()
132 .join(", "),
133 )
134}