sea_schema/mysql/parser/
column.rs

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        // order does not matter
349        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}