1use llkv_plan::{
9 AlterTablePlan, CreateIndexPlan, CreateTablePlan, CreateViewPlan, DropIndexPlan, DropTablePlan,
10 DropViewPlan, RenameTablePlan,
11};
12use llkv_result::Result;
13use sqlparser::ast::{CreateTable, ObjectName, ObjectNamePart, TableConstraint};
14use std::collections::HashSet;
15
16pub trait CatalogDdl {
24 type CreateTableOutput;
26 type DropTableOutput;
28 type RenameTableOutput;
30 type AlterTableOutput;
32 type CreateIndexOutput;
34 type DropIndexOutput;
36
37 fn create_table(&self, plan: CreateTablePlan) -> Result<Self::CreateTableOutput>;
39
40 fn drop_table(&self, plan: DropTablePlan) -> Result<Self::DropTableOutput>;
42
43 fn create_view(&self, plan: CreateViewPlan) -> Result<()>;
45
46 fn drop_view(&self, plan: DropViewPlan) -> Result<()>;
48
49 fn rename_table(&self, plan: RenameTablePlan) -> Result<Self::RenameTableOutput>;
51
52 fn alter_table(&self, plan: AlterTablePlan) -> Result<Self::AlterTableOutput>;
54
55 fn create_index(&self, plan: CreateIndexPlan) -> Result<Self::CreateIndexOutput>;
57
58 fn drop_index(&self, plan: DropIndexPlan) -> Result<Self::DropIndexOutput>;
61}
62
63pub trait ObjectNameExt {
65 fn canonical_ident(&self) -> Option<String>;
67}
68
69impl ObjectNameExt for ObjectName {
70 fn canonical_ident(&self) -> Option<String> {
71 self.0.last().and_then(|part| match part {
72 ObjectNamePart::Identifier(ident) => Some(ident.value.to_ascii_uppercase()),
73 ObjectNamePart::Function(_) => None,
74 })
75 }
76}
77
78pub trait TableConstraintExt {
80 fn normalize(self) -> TableConstraint;
81}
82
83impl TableConstraintExt for TableConstraint {
84 fn normalize(self) -> TableConstraint {
85 match self {
86 TableConstraint::ForeignKey {
87 mut name,
88 mut index_name,
89 columns,
90 foreign_table,
91 referred_columns,
92 on_delete,
93 on_update,
94 characteristics,
95 } => {
96 if name.is_none() {
97 name = index_name.clone();
98 }
99 index_name = None;
100 TableConstraint::ForeignKey {
101 name,
102 index_name,
103 columns,
104 foreign_table,
105 referred_columns,
106 on_delete,
107 on_update,
108 characteristics,
109 }
110 }
111 TableConstraint::PrimaryKey {
112 name,
113 index_name: _,
114 index_type,
115 columns,
116 index_options,
117 characteristics,
118 } => TableConstraint::PrimaryKey {
119 name,
120 index_name: None,
121 index_type,
122 columns,
123 index_options,
124 characteristics,
125 },
126 TableConstraint::Unique {
127 name,
128 index_name: _,
129 index_type,
130 columns,
131 index_options,
132 nulls_distinct,
133 characteristics,
134 index_type_display,
135 } => TableConstraint::Unique {
136 name,
137 index_name: None,
138 index_type,
139 columns,
140 index_options,
141 nulls_distinct,
142 characteristics,
143 index_type_display,
144 },
145 other => other,
146 }
147 }
148}
149
150pub trait OrderCreateTablesExt {
152 fn order_by_foreign_keys(self) -> Vec<CreateTable>;
153}
154
155impl OrderCreateTablesExt for Vec<CreateTable> {
156 fn order_by_foreign_keys(mut self) -> Vec<CreateTable> {
157 let mut ordered = Vec::with_capacity(self.len());
158 if self.is_empty() {
159 return ordered;
160 }
161
162 let table_names: HashSet<String> = self
163 .iter()
164 .filter_map(|table| table.name.canonical_ident())
165 .collect();
166
167 let mut resolved: HashSet<String> = HashSet::new();
168
169 while !self.is_empty() {
170 let mut progress = false;
171 let current_round = std::mem::take(&mut self);
172 let mut next_round = Vec::new();
173
174 for table in current_round.into_iter() {
175 let table_name = table.name.canonical_ident().unwrap_or_default();
176 let deps = collect_foreign_key_dependencies(&table);
177 let deps_satisfied = deps.into_iter().all(|dep| {
178 dep.is_empty()
179 || dep == table_name
180 || !table_names.contains(&dep)
181 || resolved.contains(&dep)
182 });
183
184 if deps_satisfied {
185 resolved.insert(table_name);
186 ordered.push(table);
187 progress = true;
188 } else {
189 next_round.push(table);
190 }
191 }
192
193 if !progress {
194 ordered.append(&mut next_round);
195 break;
196 }
197
198 self = next_round;
199 }
200
201 ordered
202 }
203}
204
205fn collect_foreign_key_dependencies(table: &CreateTable) -> Vec<String> {
206 let mut deps = Vec::new();
207 for constraint in &table.constraints {
208 if let TableConstraint::ForeignKey { foreign_table, .. } = constraint
209 && let Some(name) = foreign_table.canonical_ident()
210 {
211 deps.push(name);
212 }
213 }
214 deps
215}