1use crate::mysql::def::*;
2use crate::mysql::query::ColumnQueryResult;
3use crate::{parser::Parser, Name};
4use sea_query::{EscapeBuilder, MysqlQueryBuilder};
5
6impl ColumnQueryResult {
7 pub fn parse(self, system: &SystemInfo) -> ColumnInfo {
8 parse_column_query_result(self, system)
9 }
10}
11
12pub fn parse_column_query_result(result: ColumnQueryResult, system: &SystemInfo) -> ColumnInfo {
13 let col_type = parse_column_type(&mut Parser::new(&result.column_type));
14 let default = parse_column_default(&col_type, result.column_default, &result.extra, system);
15 ColumnInfo {
16 name: result.column_name,
17 col_type,
18 null: parse_column_null(&result.is_nullable),
19 key: parse_column_key(&result.column_key),
20 default,
21 extra: parse_column_extra(&mut Parser::new(&result.extra)),
22 expression: match result.generation_expression {
23 Some(generation_expression) => parse_generation_expression(generation_expression),
24 None => None,
25 },
26 comment: result.column_comment,
27 }
28}
29
30pub fn parse_column_type(parser: &mut Parser) -> ColumnType {
31 let mut type_name = "";
32 if parser.curr().is_none() {
33 return Type::Unknown(type_name.to_owned());
34 }
35 if let Some(word) = parser.next_if_unquoted_any() {
36 type_name = word.as_str();
37 }
38 let ctype = parse_type_name(type_name);
39 if ctype.is_numeric() {
40 parse_numeric_attributes(parser, ctype)
41 } else if ctype.is_time() {
42 parse_time_attributes(parser, ctype)
43 } else if ctype.is_string() {
44 parse_string_attributes(parser, ctype)
45 } else if ctype.is_free_size_blob() {
46 parse_blob_attributes(parser, ctype)
47 } else if ctype.is_enum() {
48 parse_enum_definition(parser, ctype)
49 } else if ctype.is_set() {
50 parse_set_definition(parser, ctype)
51 } else if ctype.is_geometry() {
52 parse_geometry_attributes(parser, ctype)
53 } else {
54 ctype
55 }
56}
57
58pub fn parse_type_name(type_name: &str) -> Type {
59 match type_name.to_lowercase().as_str() {
60 "serial" => Type::Serial,
61 "bit" => Type::Bit(NumericAttr::default()),
62 "tinyint" => Type::TinyInt(NumericAttr::default()),
63 "bool" => Type::Bool,
64 "smallint" => Type::SmallInt(NumericAttr::default()),
65 "mediumint" => Type::MediumInt(NumericAttr::default()),
66 "int" => Type::Int(NumericAttr::default()),
67 "integer" => Type::Int(NumericAttr::default()),
68 "bigint" => Type::BigInt(NumericAttr::default()),
69 "decimal" => Type::Decimal(NumericAttr::default()),
70 "dec" => Type::Decimal(NumericAttr::default()),
71 "fixed" => Type::Decimal(NumericAttr::default()),
72 "float" => Type::Float(NumericAttr::default()),
73 "double" => Type::Double(NumericAttr::default()),
74 "date" => Type::Date,
75 "time" => Type::Time(TimeAttr::default()),
76 "datetime" => Type::DateTime(TimeAttr::default()),
77 "timestamp" => Type::Timestamp(TimeAttr::default()),
78 "year" => Type::Year,
79 "char" => Type::Char(StringAttr::default()),
80 "nchar" => Type::NChar(StringAttr::default()),
81 "varchar" => Type::Varchar(StringAttr::default()),
82 "nvarchar" => Type::NVarchar(StringAttr::default()),
83 "binary" => Type::Binary(StringAttr::default()),
84 "varbinary" => Type::Varbinary(StringAttr::default()),
85 "text" => Type::Text(StringAttr::default()),
86 "tinytext" => Type::TinyText(StringAttr::default()),
87 "mediumtext" => Type::MediumText(StringAttr::default()),
88 "longtext" => Type::LongText(StringAttr::default()),
89 "blob" => Type::Blob(BlobAttr::default()),
90 "tinyblob" => Type::TinyBlob,
91 "mediumblob" => Type::MediumBlob,
92 "longblob" => Type::LongBlob,
93 "enum" => Type::Enum(EnumDef::default()),
94 "set" => Type::Set(SetDef::default()),
95 "geometry" => Type::Geometry(GeometryAttr::default()),
96 "point" => Type::Point(GeometryAttr::default()),
97 "linestring" => Type::LineString(GeometryAttr::default()),
98 "polygon" => Type::Polygon(GeometryAttr::default()),
99 "multipoint" => Type::MultiPoint(GeometryAttr::default()),
100 "multilinestring" => Type::MultiLineString(GeometryAttr::default()),
101 "multipolygon" => Type::MultiPolygon(GeometryAttr::default()),
102 "geometrycollection" => Type::GeometryCollection(GeometryAttr::default()),
103 "json" => Type::Json,
104 _ => Type::Unknown(type_name.to_owned()),
105 }
106}
107
108fn parse_numeric_attributes(parser: &mut Parser, mut ctype: ColumnType) -> ColumnType {
109 if parser.next_if_punctuation("(") {
110 if let Some(word) = parser.next_if_unquoted_any() {
111 if let Ok(number) = word.as_str().parse::<u32>() {
112 ctype.get_numeric_attr_mut().maximum = Some(number);
113 }
114 }
115
116 if parser.next_if_punctuation(",") {
117 if let Some(word) = parser.next_if_unquoted_any() {
118 if let Ok(number) = word.as_str().parse::<u32>() {
119 ctype.get_numeric_attr_mut().decimal = Some(number);
120 }
121 }
122 }
123
124 parser.next_if_punctuation(")");
125 }
126
127 if parser.next_if_unquoted("unsigned") {
128 ctype.get_numeric_attr_mut().unsigned = Some(true);
129 }
130
131 if parser.next_if_unquoted("zerofill") {
132 ctype.get_numeric_attr_mut().zero_fill = Some(true);
133 }
134
135 ctype
136}
137
138fn parse_time_attributes(parser: &mut Parser, mut ctype: ColumnType) -> ColumnType {
139 if parser.next_if_punctuation("(") {
140 if let Some(word) = parser.next_if_unquoted_any() {
141 if let Ok(number) = word.as_str().parse::<u32>() {
142 ctype.get_time_attr_mut().fractional = Some(number);
143 }
144 }
145 parser.next_if_punctuation(")");
146 }
147
148 ctype
149}
150
151fn parse_string_attributes(parser: &mut Parser, mut ctype: ColumnType) -> ColumnType {
152 if parser.next_if_punctuation("(") {
153 if let Some(word) = parser.next_if_unquoted_any() {
154 if let Ok(number) = word.as_str().parse::<u32>() {
155 ctype.get_string_attr_mut().length = Some(number);
156 }
157 }
158 parser.next_if_punctuation(")");
159 }
160
161 parse_charset_collate(parser, ctype.get_string_attr_mut());
162
163 ctype
164}
165
166fn parse_charset_collate(parser: &mut Parser, str_attr: &mut StringAttr) {
167 if parser.next_if_unquoted("character") && parser.next_if_unquoted("set") {
168 if let Some(word) = parser.next_if_unquoted_any() {
169 str_attr.charset = CharSet::from_str(word.as_str());
170 }
171 }
172
173 if parser.next_if_unquoted("collate") {
174 if let Some(word) = parser.next_if_unquoted_any() {
175 str_attr.collation = Collation::from_str(word.as_str());
176 }
177 }
178}
179
180fn parse_blob_attributes(parser: &mut Parser, mut ctype: ColumnType) -> ColumnType {
181 if parser.next_if_punctuation("(") {
182 if let Some(word) = parser.next_if_unquoted_any() {
183 if let Ok(number) = word.as_str().parse::<u32>() {
184 ctype.get_blob_attr_mut().length = Some(number);
185 }
186 }
187 parser.next_if_punctuation(")");
188 }
189
190 ctype
191}
192
193fn parse_enum_definition(parser: &mut Parser, mut ctype: ColumnType) -> ColumnType {
194 if parser.next_if_punctuation("(") {
195 while parser.curr().is_some() {
196 if let Some(word) = parser.next_if_quoted_any() {
197 ctype
198 .get_enum_def_mut()
199 .values
200 .push(MysqlQueryBuilder.unescape_string(word.unquote().unwrap().as_str()));
201 parser.next_if_punctuation(",");
202 } else if parser.curr_is_unquoted() {
203 todo!("there can actually be numeric enum values but is very confusing");
204 }
205 if parser.next_if_punctuation(")") {
206 break;
207 }
208 }
209 }
210
211 parse_charset_collate(parser, &mut ctype.get_enum_def_mut().attr);
212
213 ctype
214}
215
216fn parse_set_definition(parser: &mut Parser, mut ctype: ColumnType) -> ColumnType {
217 if parser.next_if_punctuation("(") {
218 while parser.curr().is_some() {
219 if let Some(word) = parser.next_if_quoted_any() {
220 ctype
221 .get_set_def_mut()
222 .members
223 .push(MysqlQueryBuilder.unescape_string(word.unquote().unwrap().as_str()));
224 parser.next_if_punctuation(",");
225 } else if parser.curr_is_unquoted() {
226 todo!("there can actually be numeric set values but is very confusing");
227 }
228 if parser.next_if_punctuation(")") {
229 break;
230 }
231 }
232 }
233
234 parse_charset_collate(parser, &mut ctype.get_set_def_mut().attr);
235
236 ctype
237}
238
239fn parse_geometry_attributes(parser: &mut Parser, mut ctype: ColumnType) -> ColumnType {
240 if parser.next_if_unquoted("srid") {
241 if let Some(word) = parser.next_if_unquoted_any() {
242 if let Ok(number) = word.as_str().parse::<u32>() {
243 ctype.get_geometry_attr_mut().srid = Some(number);
244 }
245 }
246 parser.next_if_punctuation(")");
247 }
248
249 ctype
250}
251
252pub fn parse_column_null(string: &str) -> bool {
253 matches!(string.to_uppercase().as_str(), "YES")
254}
255
256pub fn parse_column_key(string: &str) -> ColumnKey {
257 match string.to_uppercase().as_str() {
258 "PRI" => ColumnKey::Primary,
259 "UNI" => ColumnKey::Unique,
260 "MUL" => ColumnKey::Multiple,
261 _ => ColumnKey::NotKey,
262 }
263}
264
265pub fn parse_column_default(
266 col_type: &Type,
267 default: Option<String>,
268 extra: &str,
269 system: &SystemInfo,
270) -> Option<ColumnDefault> {
271 match default {
272 Some(default) => {
273 if !default.is_empty() {
274 let default_value = if system.is_mysql() && system.version >= 80000 {
275 parse_mysql_8_default(default, extra)
276 } else if system.is_maria_db() && system.version >= 100207 {
277 parse_mariadb_10_default(default)
278 } else {
279 parse_mysql_5_default(default, col_type)
280 };
281 Some(default_value)
282 } else {
283 None
284 }
285 }
286 None => None,
287 }
288}
289
290pub fn parse_mysql_5_default(default: String, col_type: &Type) -> ColumnDefault {
291 let is_date_time = matches!(col_type, Type::DateTime(_) | Type::Timestamp(_));
292 if is_date_time && default == "CURRENT_TIMESTAMP" {
293 ColumnDefault::CurrentTimestamp
294 } else if let Ok(int) = default.parse() {
295 ColumnDefault::Int(int)
296 } else if let Ok(real) = default.parse() {
297 ColumnDefault::Real(real)
298 } else {
299 ColumnDefault::String(default)
300 }
301}
302
303pub fn parse_mysql_8_default(default: String, extra: &str) -> ColumnDefault {
304 let is_expression = extra.contains("DEFAULT_GENERATED");
305 if is_expression && default == "CURRENT_TIMESTAMP" {
306 ColumnDefault::CurrentTimestamp
307 } else if is_expression && default == "NULL" {
308 ColumnDefault::Null
309 } else if let Ok(int) = default.parse() {
310 ColumnDefault::Int(int)
311 } else if let Ok(real) = default.parse() {
312 ColumnDefault::Real(real)
313 } else if is_expression {
314 ColumnDefault::CustomExpr(default)
315 } else {
316 ColumnDefault::String(default)
317 }
318}
319
320pub fn parse_mariadb_10_default(default: String) -> ColumnDefault {
321 if default.starts_with('\'') && default.ends_with('\'') {
322 ColumnDefault::String(default[1..(default.len() - 1)].into())
323 } else if let Ok(int) = default.parse() {
324 ColumnDefault::Int(int)
325 } else if let Ok(real) = default.parse() {
326 ColumnDefault::Real(real)
327 } else if default == "current_timestamp()" {
328 ColumnDefault::CurrentTimestamp
329 } else if default == "NULL" {
330 ColumnDefault::Null
331 } else {
332 ColumnDefault::CustomExpr(default)
333 }
334}
335
336pub fn parse_generation_expression(string: String) -> Option<ColumnExpression> {
337 if string.is_empty() {
338 None
339 } else {
340 Some(ColumnExpression { expr: string })
341 }
342}
343
344pub fn parse_column_extra(parser: &mut Parser) -> ColumnExtra {
345 let mut extra = ColumnExtra::default();
346
347 while parser.curr().is_some() {
348 if parser.next_if_unquoted("on") {
350 if parser.next_if_unquoted("update") && parser.next_if_unquoted("current_timestamp") {
351 extra.on_update_current_timestamp = true;
352 }
353 } else if parser.next_if_unquoted("auto_increment") {
354 extra.auto_increment = true;
355 } else if parser.next_if_unquoted("default_generated") {
356 extra.default_generated = true;
357 } else if parser.next_if_unquoted("stored") || parser.next_if_unquoted("virtual") {
358 if parser.next_if_unquoted("generated") {
359 extra.generated = true;
360 }
361 } else {
362 parser.next();
363 }
364 }
365
366 extra
367}
368
369#[cfg(test)]
370mod tests {
371 use super::*;
372
373 #[test]
374 fn test_0() {
375 assert_eq!(
376 parse_column_extra(&mut Parser::new("")),
377 ColumnExtra {
378 auto_increment: false,
379 on_update_current_timestamp: false,
380 generated: false,
381 default_generated: false,
382 }
383 );
384 }
385
386 #[test]
387 fn test_0b() {
388 assert_eq!(
389 parse_column_extra(&mut Parser::new("NOTHING matters")),
390 ColumnExtra {
391 auto_increment: false,
392 on_update_current_timestamp: false,
393 generated: false,
394 default_generated: false,
395 }
396 );
397 }
398
399 #[test]
400 fn test_1() {
401 assert_eq!(
402 parse_column_extra(&mut Parser::new("DEFAULT_GENERATED")),
403 ColumnExtra {
404 auto_increment: false,
405 on_update_current_timestamp: false,
406 generated: false,
407 default_generated: true,
408 }
409 );
410 }
411
412 #[test]
413 fn test_1b() {
414 assert_eq!(
415 parse_column_extra(&mut Parser::new("DEFAULT_GENERATED garbage")),
416 ColumnExtra {
417 auto_increment: false,
418 on_update_current_timestamp: false,
419 generated: false,
420 default_generated: true,
421 }
422 );
423 }
424
425 #[test]
426 fn test_1c() {
427 assert_eq!(
428 parse_column_extra(&mut Parser::new("garbage DEFAULT_GENERATED")),
429 ColumnExtra {
430 auto_increment: false,
431 on_update_current_timestamp: false,
432 generated: false,
433 default_generated: true,
434 }
435 );
436 }
437
438 #[test]
439 fn test_2() {
440 assert_eq!(
441 parse_column_extra(&mut Parser::new(
442 "DEFAULT_GENERATED on update CURRENT_TIMESTAMP"
443 )),
444 ColumnExtra {
445 auto_increment: false,
446 on_update_current_timestamp: true,
447 generated: false,
448 default_generated: true,
449 }
450 );
451 }
452
453 #[test]
454 fn test_3() {
455 assert_eq!(
456 parse_column_type(&mut Parser::new("smallint unsigned")),
457 ColumnType::SmallInt(NumericAttr {
458 maximum: None,
459 decimal: None,
460 unsigned: Some(true),
461 zero_fill: None,
462 })
463 );
464 }
465
466 #[test]
467 fn test_4() {
468 assert_eq!(
469 parse_column_type(&mut Parser::new("smallint unsigned zerofill")),
470 ColumnType::SmallInt(NumericAttr {
471 maximum: None,
472 decimal: None,
473 unsigned: Some(true),
474 zero_fill: Some(true),
475 })
476 );
477 }
478
479 #[test]
480 fn test_5() {
481 assert_eq!(
482 parse_column_type(&mut Parser::new("decimal(4,2)")),
483 ColumnType::Decimal(NumericAttr {
484 maximum: Some(4),
485 decimal: Some(2),
486 unsigned: None,
487 zero_fill: None,
488 })
489 );
490 }
491
492 #[test]
493 fn test_6() {
494 assert_eq!(
495 parse_column_type(&mut Parser::new("decimal(18,4) zerofill")),
496 ColumnType::Decimal(NumericAttr {
497 maximum: Some(18),
498 decimal: Some(4),
499 unsigned: None,
500 zero_fill: Some(true),
501 })
502 );
503 }
504
505 #[test]
506 fn test_7() {
507 assert_eq!(
508 parse_column_type(&mut Parser::new("decimal(18,4) unsigned")),
509 ColumnType::Decimal(NumericAttr {
510 maximum: Some(18),
511 decimal: Some(4),
512 unsigned: Some(true),
513 zero_fill: None,
514 })
515 );
516 }
517
518 #[test]
519 fn test_8() {
520 assert_eq!(
521 parse_column_type(&mut Parser::new("decimal(18,4) unsigned zerofill")),
522 ColumnType::Decimal(NumericAttr {
523 maximum: Some(18),
524 decimal: Some(4),
525 unsigned: Some(true),
526 zero_fill: Some(true),
527 })
528 );
529 }
530
531 #[test]
532 fn test_9() {
533 assert_eq!(
534 parse_column_type(&mut Parser::new("smallint(8) unsigned zerofill")),
535 ColumnType::SmallInt(NumericAttr {
536 maximum: Some(8),
537 decimal: None,
538 unsigned: Some(true),
539 zero_fill: Some(true),
540 })
541 );
542 }
543
544 #[test]
545 fn test_10() {
546 assert_eq!(
547 parse_column_type(&mut Parser::new("DATETIME")),
548 ColumnType::DateTime(TimeAttr { fractional: None })
549 );
550 }
551
552 #[test]
553 fn test_11() {
554 assert_eq!(
555 parse_column_type(&mut Parser::new("DATETIME(6)")),
556 ColumnType::DateTime(TimeAttr {
557 fractional: Some(6),
558 })
559 );
560 }
561
562 #[test]
563 fn test_12() {
564 assert_eq!(
565 parse_column_type(&mut Parser::new("TIMESTAMP(0)")),
566 ColumnType::Timestamp(TimeAttr {
567 fractional: Some(0),
568 })
569 );
570 }
571
572 #[test]
573 fn test_13() {
574 assert_eq!(
575 parse_column_type(&mut Parser::new("varchar(20)")),
576 ColumnType::Varchar(StringAttr {
577 length: Some(20),
578 charset: None,
579 collation: None,
580 })
581 );
582 }
583
584 #[test]
585 fn test_14() {
586 assert_eq!(
587 parse_column_type(&mut Parser::new("TEXT")),
588 ColumnType::Text(StringAttr {
589 length: None,
590 charset: None,
591 collation: None,
592 })
593 );
594 }
595
596 #[test]
597 fn test_15() {
598 assert_eq!(
599 parse_column_type(&mut Parser::new(
600 "TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin"
601 )),
602 ColumnType::Text(StringAttr {
603 length: None,
604 charset: Some(CharSet::Utf8Mb4),
605 collation: Some(Collation::Utf8Mb4Bin),
606 })
607 );
608 }
609
610 #[test]
611 fn test_16() {
612 assert_eq!(
613 parse_column_type(&mut Parser::new("TEXT CHARACTER SET latin1")),
614 ColumnType::Text(StringAttr {
615 length: None,
616 charset: Some(CharSet::Latin1),
617 collation: None,
618 })
619 );
620 }
621
622 #[test]
623 fn test_17() {
624 assert_eq!(
625 parse_column_type(&mut Parser::new("BLOB")),
626 ColumnType::Blob(BlobAttr { length: None })
627 );
628 }
629
630 #[test]
631 fn test_18() {
632 assert_eq!(
633 parse_column_type(&mut Parser::new("BLOB(256)")),
634 ColumnType::Blob(BlobAttr { length: Some(256) })
635 );
636 }
637
638 #[test]
639 fn test_19() {
640 assert_eq!(
641 parse_column_type(&mut Parser::new("enum('G','PG','PG-13','R','NC-17')")),
642 ColumnType::Enum(EnumDef {
643 values: vec![
644 "G".to_owned(),
645 "PG".to_owned(),
646 "PG-13".to_owned(),
647 "R".to_owned(),
648 "NC-17".to_owned(),
649 ],
650 attr: StringAttr {
651 length: None,
652 charset: None,
653 collation: None,
654 }
655 })
656 );
657 }
658
659 #[test]
660 fn test_20() {
661 assert_eq!(
662 parse_column_type(&mut Parser::new(
663 "set('Trailers','Commentaries','Deleted Scenes','Behind the Scenes')"
664 )),
665 ColumnType::Set(SetDef {
666 members: vec![
667 "Trailers".to_owned(),
668 "Commentaries".to_owned(),
669 "Deleted Scenes".to_owned(),
670 "Behind the Scenes".to_owned(),
671 ],
672 attr: StringAttr {
673 length: None,
674 charset: None,
675 collation: None,
676 }
677 })
678 );
679 }
680
681 #[test]
682 fn test_21() {
683 assert_eq!(
684 parse_column_type(&mut Parser::new("GEOMETRY")),
685 ColumnType::Geometry(GeometryAttr { srid: None })
686 );
687 }
688
689 #[test]
690 fn test_22() {
691 assert_eq!(
692 parse_column_type(&mut Parser::new("GEOMETRY SRID 4326")),
693 ColumnType::Geometry(GeometryAttr { srid: Some(4326) })
694 );
695 }
696
697 #[test]
698 fn test_23() {
699 assert_eq!(parse_column_key("pri"), ColumnKey::Primary);
700 assert_eq!(parse_column_key("uni"), ColumnKey::Unique);
701 assert_eq!(parse_column_key("mul"), ColumnKey::Multiple);
702 assert_eq!(parse_column_key(""), ColumnKey::NotKey);
703 }
704
705 #[test]
706 fn test_24() {
707 assert!(parse_column_null("yes"));
708 assert!(!parse_column_null("no"));
709 }
710}