1use crate::types::DataValue;
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12pub struct TableSchema {
13 pub name: String,
15 pub columns: Vec<ColumnDefinition>,
17 pub indexes: Vec<IndexDefinition>,
19 pub constraints: Vec<ConstraintDefinition>,
21 pub options: TableOptions,
23 pub version: u32,
25 pub created_at: Option<chrono::DateTime<chrono::Utc>>,
27 pub updated_at: Option<chrono::DateTime<chrono::Utc>>,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
33pub struct ColumnDefinition {
34 pub name: String,
36 pub column_type: ColumnType,
38 pub nullable: bool,
40 pub default_value: Option<String>,
42 pub primary_key: bool,
44 pub auto_increment: bool,
46 pub unique: bool,
48 pub comment: Option<String>,
50 pub length: Option<u32>,
52 pub precision: Option<u32>,
54 pub scale: Option<u32>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
60pub enum ColumnType {
61 Integer,
63 BigInteger,
65 SmallInteger,
67 Float,
69 Double,
71 Decimal { precision: u32, scale: u32 },
73 String { length: Option<u32> },
75 Text,
77 LongText,
79 Boolean,
81 Date,
83 Time,
85 DateTime,
87 Timestamp,
89 Binary { length: Option<u32> },
91 Blob,
93 Json,
95 Uuid,
97 Enum { values: Vec<String> },
99 Custom { type_name: String },
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
105pub struct IndexDefinition {
106 pub name: String,
108 pub index_type: IndexType,
110 pub columns: Vec<String>,
112 pub unique: bool,
114 pub options: HashMap<String, String>,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
120pub enum IndexType {
121 BTree,
123 Hash,
125 FullText,
127 Spatial,
129 Custom(String),
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
135pub struct ConstraintDefinition {
136 pub name: String,
138 pub constraint_type: ConstraintType,
140 pub columns: Vec<String>,
142 pub reference_table: Option<String>,
144 pub reference_columns: Option<Vec<String>>,
146 pub on_delete: Option<ReferentialAction>,
148 pub on_update: Option<ReferentialAction>,
150 pub check_condition: Option<String>,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
156pub enum ConstraintType {
157 PrimaryKey,
159 ForeignKey,
161 Unique,
163 Check,
165 NotNull,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
171pub enum ReferentialAction {
172 Cascade,
174 SetNull,
176 SetDefault,
178 Restrict,
180 NoAction,
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
186pub struct TableOptions {
187 pub engine: Option<String>,
189 pub charset: Option<String>,
191 pub collation: Option<String>,
193 pub comment: Option<String>,
195 pub auto_increment: Option<u64>,
197 pub row_format: Option<String>,
199 pub extra_options: HashMap<String, String>,
201}
202
203impl Default for TableOptions {
204 fn default() -> Self {
205 Self {
206 engine: None,
207 charset: Some("utf8mb4".to_string()),
208 collation: Some("utf8mb4_unicode_ci".to_string()),
209 comment: None,
210 auto_increment: None,
211 row_format: None,
212 extra_options: HashMap::new(),
213 }
214 }
215}
216
217impl TableSchema {
218 pub fn new(name: String) -> Self {
220 Self {
221 name,
222 columns: Vec::new(),
223 indexes: Vec::new(),
224 constraints: Vec::new(),
225 options: TableOptions::default(),
226 version: 1,
227 created_at: Some(chrono::Utc::now()),
228 updated_at: Some(chrono::Utc::now()),
229 }
230 }
231
232 pub fn add_column(mut self, column: ColumnDefinition) -> Self {
234 self.columns.push(column);
235 self.updated_at = Some(chrono::Utc::now());
236 self
237 }
238
239 pub fn add_index(mut self, index: IndexDefinition) -> Self {
241 self.indexes.push(index);
242 self.updated_at = Some(chrono::Utc::now());
243 self
244 }
245
246 pub fn add_constraint(mut self, constraint: ConstraintDefinition) -> Self {
248 self.constraints.push(constraint);
249 self.updated_at = Some(chrono::Utc::now());
250 self
251 }
252
253 pub fn with_options(mut self, options: TableOptions) -> Self {
255 self.options = options;
256 self.updated_at = Some(chrono::Utc::now());
257 self
258 }
259
260 pub fn get_primary_key_columns(&self) -> Vec<&ColumnDefinition> {
262 self.columns.iter().filter(|col| col.primary_key).collect()
263 }
264
265 pub fn has_column(&self, column_name: &str) -> bool {
267 self.columns.iter().any(|col| col.name == column_name)
268 }
269
270 pub fn get_column(&self, column_name: &str) -> Option<&ColumnDefinition> {
272 self.columns.iter().find(|col| col.name == column_name)
273 }
274
275 pub fn has_index(&self, index_name: &str) -> bool {
277 self.indexes.iter().any(|idx| idx.name == index_name)
278 }
279
280 pub fn get_index(&self, index_name: &str) -> Option<&IndexDefinition> {
282 self.indexes.iter().find(|idx| idx.name == index_name)
283 }
284
285 pub fn validate(&self) -> Result<(), String> {
287 if self.columns.is_empty() {
289 return Err("表必须至少有一个列".to_string());
290 }
291
292 let mut column_names = std::collections::HashSet::new();
294 for column in &self.columns {
295 if !column_names.insert(&column.name) {
296 return Err(format!("列名 '{}' 重复", column.name));
297 }
298 }
299
300 let mut index_names = std::collections::HashSet::new();
302 for index in &self.indexes {
303 if !index_names.insert(&index.name) {
304 return Err(format!("索引名 '{}' 重复", index.name));
305 }
306
307 for column_name in &index.columns {
309 if !self.has_column(column_name) {
310 return Err(format!(
311 "索引 '{}' 引用的列 '{}' 不存在",
312 index.name, column_name
313 ));
314 }
315 }
316 }
317
318 let mut constraint_names = std::collections::HashSet::new();
320 for constraint in &self.constraints {
321 if !constraint_names.insert(&constraint.name) {
322 return Err(format!("约束名 '{}' 重复", constraint.name));
323 }
324
325 for column_name in &constraint.columns {
327 if !self.has_column(column_name) {
328 return Err(format!(
329 "约束 '{}' 引用的列 '{}' 不存在",
330 constraint.name, column_name
331 ));
332 }
333 }
334 }
335
336 Ok(())
337 }
338
339 pub fn infer_from_data(table_name: String, data: &HashMap<String, DataValue>) -> Self {
341 let mut columns = Vec::new();
342 let mut has_id_field = false;
343
344 for (field_name, field_value) in data {
346 if field_name == "id" {
347 has_id_field = true;
348 let column_type = match field_value {
350 DataValue::Null => ColumnType::BigInteger, _ => Self::infer_column_type(field_value),
352 };
353 columns.push(ColumnDefinition {
354 name: field_name.clone(),
355 column_type,
356 nullable: false, default_value: None,
358 primary_key: true,
359 auto_increment: matches!(field_value, DataValue::Int(_) | DataValue::Null), unique: true,
361 comment: Some("主键ID".to_string()),
362 length: None,
363 precision: None,
364 scale: None,
365 });
366 } else {
367 let column_type = Self::infer_column_type(field_value);
368 columns.push(ColumnDefinition {
369 name: field_name.clone(),
370 column_type,
371 nullable: matches!(field_value, DataValue::Null),
372 default_value: None,
373 primary_key: false,
374 auto_increment: false,
375 unique: false,
376 comment: None,
377 length: None,
378 precision: None,
379 scale: None,
380 });
381 }
382 }
383
384 if !has_id_field {
386 columns.insert(
387 0,
388 ColumnDefinition {
389 name: "id".to_string(),
390 column_type: ColumnType::BigInteger,
391 nullable: false,
392 default_value: None,
393 primary_key: true,
394 auto_increment: true,
395 unique: true,
396 comment: Some("主键ID".to_string()),
397 length: None,
398 precision: None,
399 scale: None,
400 },
401 );
402 }
403
404 Self {
405 name: table_name,
406 columns,
407 indexes: Vec::new(),
408 constraints: Vec::new(),
409 options: TableOptions::default(),
410 version: 1,
411 created_at: Some(chrono::Utc::now()),
412 updated_at: Some(chrono::Utc::now()),
413 }
414 }
415
416 fn infer_column_type(value: &DataValue) -> ColumnType {
418 match value {
419 DataValue::Null => ColumnType::String { length: Some(255) }, DataValue::Bool(_) => ColumnType::Boolean,
421 DataValue::Int(_) => ColumnType::BigInteger,
422 DataValue::UInt(_) => ColumnType::BigInteger,
423 DataValue::Float(_) => ColumnType::Double,
424 DataValue::String(s) => {
425 if s.len() > 65535 {
427 ColumnType::LongText
428 } else if s.len() > 255 {
429 ColumnType::Text
430 } else {
431 ColumnType::String { length: Some(255) }
432 }
433 }
434 DataValue::Bytes(_) => ColumnType::Blob,
435 DataValue::DateTime(_) => ColumnType::DateTime,
436 DataValue::DateTimeUTC(_) => ColumnType::DateTime,
437 DataValue::Uuid(_) => ColumnType::Uuid,
438 DataValue::Json(_) => ColumnType::Json,
439 DataValue::Array(_) => ColumnType::Json, DataValue::Object(_) => ColumnType::Json, }
442 }
443}
444
445impl ColumnDefinition {
446 pub fn new(name: String, column_type: ColumnType) -> Self {
448 Self {
449 name,
450 column_type,
451 nullable: true,
452 default_value: None,
453 primary_key: false,
454 auto_increment: false,
455 unique: false,
456 comment: None,
457 length: None,
458 precision: None,
459 scale: None,
460 }
461 }
462
463 pub fn primary_key(mut self) -> Self {
465 self.primary_key = true;
466 self.nullable = false;
467 self
468 }
469
470 pub fn not_null(mut self) -> Self {
472 self.nullable = false;
473 self
474 }
475
476 pub fn unique(mut self) -> Self {
478 self.unique = true;
479 self
480 }
481
482 pub fn auto_increment(mut self) -> Self {
484 self.auto_increment = true;
485 self.nullable = false;
486 self
487 }
488
489 pub fn default_value<T: ToString>(mut self, value: T) -> Self {
491 self.default_value = Some(value.to_string());
492 self
493 }
494
495 pub fn comment<T: ToString>(mut self, comment: T) -> Self {
497 self.comment = Some(comment.to_string());
498 self
499 }
500}
501
502impl IndexDefinition {
503 pub fn new(name: String, columns: Vec<String>) -> Self {
505 Self {
506 name,
507 index_type: IndexType::BTree,
508 columns,
509 unique: false,
510 options: HashMap::new(),
511 }
512 }
513
514 pub fn unique(mut self) -> Self {
516 self.unique = true;
517 self
518 }
519
520 pub fn index_type(mut self, index_type: IndexType) -> Self {
522 self.index_type = index_type;
523 self
524 }
525
526 pub fn option<K: ToString, V: ToString>(mut self, key: K, value: V) -> Self {
528 self.options.insert(key.to_string(), value.to_string());
529 self
530 }
531}
532
533impl ConstraintDefinition {
534 pub fn primary_key(name: String, columns: Vec<String>) -> Self {
536 Self {
537 name,
538 constraint_type: ConstraintType::PrimaryKey,
539 columns,
540 reference_table: None,
541 reference_columns: None,
542 on_delete: None,
543 on_update: None,
544 check_condition: None,
545 }
546 }
547
548 pub fn foreign_key(
550 name: String,
551 columns: Vec<String>,
552 reference_table: String,
553 reference_columns: Vec<String>,
554 ) -> Self {
555 Self {
556 name,
557 constraint_type: ConstraintType::ForeignKey,
558 columns,
559 reference_table: Some(reference_table),
560 reference_columns: Some(reference_columns),
561 on_delete: Some(ReferentialAction::Restrict),
562 on_update: Some(ReferentialAction::Restrict),
563 check_condition: None,
564 }
565 }
566
567 pub fn unique(name: String, columns: Vec<String>) -> Self {
569 Self {
570 name,
571 constraint_type: ConstraintType::Unique,
572 columns,
573 reference_table: None,
574 reference_columns: None,
575 on_delete: None,
576 on_update: None,
577 check_condition: None,
578 }
579 }
580
581 pub fn check(name: String, condition: String) -> Self {
583 Self {
584 name,
585 constraint_type: ConstraintType::Check,
586 columns: Vec::new(),
587 reference_table: None,
588 reference_columns: None,
589 on_delete: None,
590 on_update: None,
591 check_condition: Some(condition),
592 }
593 }
594
595 pub fn on_delete(mut self, action: ReferentialAction) -> Self {
597 self.on_delete = Some(action);
598 self
599 }
600
601 pub fn on_update(mut self, action: ReferentialAction) -> Self {
603 self.on_update = Some(action);
604 self
605 }
606}