15
15
16
16
#[ cfg( not( feature = "std" ) ) ]
17
17
use alloc:: { boxed:: Box , string:: String , vec:: Vec } ;
18
- use core:: fmt;
18
+ use core:: fmt:: { self , Write } ;
19
19
20
20
#[ cfg( feature = "serde" ) ]
21
21
use serde:: { Deserialize , Serialize } ;
@@ -397,12 +397,68 @@ impl fmt::Display for AlterColumnOperation {
397
397
#[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
398
398
#[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
399
399
pub enum TableConstraint {
400
- /// `[ CONSTRAINT <name> ] { PRIMARY KEY | UNIQUE } (<columns>)`
400
+ /// MySQL [definition][1] for `UNIQUE` constraints statements:\
401
+ /// * `[CONSTRAINT [<name>]] UNIQUE <index_type_display> [<index_name>] [index_type] (<columns>) <index_options>`
402
+ ///
403
+ /// where:
404
+ /// * [index_type][2] is `USING {BTREE | HASH}`
405
+ /// * [index_options][3] is `{index_type | COMMENT 'string' | ... %currently unsupported stmts% } ...`
406
+ /// * [index_type_display][4] is `[INDEX | KEY]`
407
+ ///
408
+ /// [1]: https://dev.mysql.com/doc/refman/8.3/en/create-table.html
409
+ /// [2]: IndexType
410
+ /// [3]: IndexOption
411
+ /// [4]: KeyOrIndexDisplay
401
412
Unique {
413
+ /// Constraint name.
414
+ ///
415
+ /// Can be not the same as `index_name`
402
416
name : Option < Ident > ,
417
+ /// Index name
418
+ index_name : Option < Ident > ,
419
+ /// Whether the type is followed by the keyword `KEY`, `INDEX`, or no keyword at all.
420
+ index_type_display : KeyOrIndexDisplay ,
421
+ /// Optional `USING` of [index type][1] statement before columns.
422
+ ///
423
+ /// [1]: IndexType
424
+ index_type : Option < IndexType > ,
425
+ /// Identifiers of the columns that are unique.
403
426
columns : Vec < Ident > ,
404
- /// Whether this is a `PRIMARY KEY` or just a `UNIQUE` constraint
405
- is_primary : bool ,
427
+ index_options : Vec < IndexOption > ,
428
+ characteristics : Option < ConstraintCharacteristics > ,
429
+ } ,
430
+ /// MySQL [definition][1] for `PRIMARY KEY` constraints statements:\
431
+ /// * `[CONSTRAINT [<name>]] PRIMARY KEY [index_name] [index_type] (<columns>) <index_options>`
432
+ ///
433
+ /// Actually the specification have no `[index_name]` but the next query will complete successfully:
434
+ /// ```sql
435
+ /// CREATE TABLE unspec_table (
436
+ /// xid INT NOT NULL,
437
+ /// CONSTRAINT p_name PRIMARY KEY index_name USING BTREE (xid)
438
+ /// );
439
+ /// ```
440
+ ///
441
+ /// where:
442
+ /// * [index_type][2] is `USING {BTREE | HASH}`
443
+ /// * [index_options][3] is `{index_type | COMMENT 'string' | ... %currently unsupported stmts% } ...`
444
+ ///
445
+ /// [1]: https://dev.mysql.com/doc/refman/8.3/en/create-table.html
446
+ /// [2]: IndexType
447
+ /// [3]: IndexOption
448
+ PrimaryKey {
449
+ /// Constraint name.
450
+ ///
451
+ /// Can be not the same as `index_name`
452
+ name : Option < Ident > ,
453
+ /// Index name
454
+ index_name : Option < Ident > ,
455
+ /// Optional `USING` of [index type][1] statement before columns.
456
+ ///
457
+ /// [1]: IndexType
458
+ index_type : Option < IndexType > ,
459
+ /// Identifiers of the columns that form the primary key.
460
+ columns : Vec < Ident > ,
461
+ index_options : Vec < IndexOption > ,
406
462
characteristics : Option < ConstraintCharacteristics > ,
407
463
} ,
408
464
/// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY (<columns>)
@@ -472,22 +528,51 @@ impl fmt::Display for TableConstraint {
472
528
match self {
473
529
TableConstraint :: Unique {
474
530
name,
531
+ index_name,
532
+ index_type_display,
533
+ index_type,
475
534
columns,
476
- is_primary ,
535
+ index_options ,
477
536
characteristics,
478
537
} => {
479
538
write ! (
480
539
f,
481
- "{}{} ({})" ,
540
+ "{}UNIQUE{index_type_display:>}{} {} ({})" ,
482
541
display_constraint_name( name) ,
483
- if * is_primary { "PRIMARY KEY" } else { "UNIQUE" } ,
484
- display_comma_separated( columns)
542
+ display_option_spaced( index_name) ,
543
+ display_option( " USING " , "" , index_type) ,
544
+ display_comma_separated( columns) ,
485
545
) ?;
486
546
487
- if let Some ( characteristics) = characteristics {
488
- write ! ( f, " {}" , characteristics) ?;
547
+ if !index_options. is_empty ( ) {
548
+ write ! ( f, " {}" , display_separated( index_options, " " ) ) ?;
549
+ }
550
+
551
+ write ! ( f, "{}" , display_option_spaced( characteristics) ) ?;
552
+ Ok ( ( ) )
553
+ }
554
+ TableConstraint :: PrimaryKey {
555
+ name,
556
+ index_name,
557
+ index_type,
558
+ columns,
559
+ index_options,
560
+ characteristics,
561
+ } => {
562
+ write ! (
563
+ f,
564
+ "{}PRIMARY KEY{}{} ({})" ,
565
+ display_constraint_name( name) ,
566
+ display_option_spaced( index_name) ,
567
+ display_option( " USING " , "" , index_type) ,
568
+ display_comma_separated( columns) ,
569
+ ) ?;
570
+
571
+ if !index_options. is_empty ( ) {
572
+ write ! ( f, " {}" , display_separated( index_options, " " ) ) ?;
489
573
}
490
574
575
+ write ! ( f, "{}" , display_option_spaced( characteristics) ) ?;
491
576
Ok ( ( ) )
492
577
}
493
578
TableConstraint :: ForeignKey {
@@ -550,9 +635,7 @@ impl fmt::Display for TableConstraint {
550
635
write ! ( f, "SPATIAL" ) ?;
551
636
}
552
637
553
- if !matches ! ( index_type_display, KeyOrIndexDisplay :: None ) {
554
- write ! ( f, " {index_type_display}" ) ?;
555
- }
638
+ write ! ( f, "{index_type_display:>}" ) ?;
556
639
557
640
if let Some ( name) = opt_index_name {
558
641
write ! ( f, " {name}" ) ?;
@@ -585,8 +668,20 @@ pub enum KeyOrIndexDisplay {
585
668
Index ,
586
669
}
587
670
671
+ impl KeyOrIndexDisplay {
672
+ pub fn is_none ( self ) -> bool {
673
+ matches ! ( self , Self :: None )
674
+ }
675
+ }
676
+
588
677
impl fmt:: Display for KeyOrIndexDisplay {
589
678
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
679
+ let left_space = matches ! ( f. align( ) , Some ( fmt:: Alignment :: Right ) ) ;
680
+
681
+ if left_space && !self . is_none ( ) {
682
+ f. write_char ( ' ' ) ?
683
+ }
684
+
590
685
match self {
591
686
KeyOrIndexDisplay :: None => {
592
687
write ! ( f, "" )
@@ -626,6 +721,30 @@ impl fmt::Display for IndexType {
626
721
}
627
722
}
628
723
}
724
+
725
+ /// MySQLs index option.
726
+ ///
727
+ /// This structure used here [`MySQL` CREATE TABLE][1], [`MySQL` CREATE INDEX][2].
728
+ ///
729
+ /// [1]: https://dev.mysql.com/doc/refman/8.3/en/create-table.html
730
+ /// [2]: https://dev.mysql.com/doc/refman/8.3/en/create-index.html
731
+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
732
+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
733
+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
734
+ pub enum IndexOption {
735
+ Using ( IndexType ) ,
736
+ Comment ( String ) ,
737
+ }
738
+
739
+ impl fmt:: Display for IndexOption {
740
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
741
+ match self {
742
+ Self :: Using ( index_type) => write ! ( f, "USING {index_type}" ) ,
743
+ Self :: Comment ( s) => write ! ( f, "COMMENT '{s}'" ) ,
744
+ }
745
+ }
746
+ }
747
+
629
748
#[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
630
749
#[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
631
750
#[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
@@ -909,6 +1028,7 @@ pub enum GeneratedExpressionMode {
909
1028
Stored ,
910
1029
}
911
1030
1031
+ #[ must_use]
912
1032
fn display_constraint_name ( name : & ' _ Option < Ident > ) -> impl fmt:: Display + ' _ {
913
1033
struct ConstraintName < ' a > ( & ' a Option < Ident > ) ;
914
1034
impl < ' a > fmt:: Display for ConstraintName < ' a > {
@@ -922,6 +1042,36 @@ fn display_constraint_name(name: &'_ Option<Ident>) -> impl fmt::Display + '_ {
922
1042
ConstraintName ( name)
923
1043
}
924
1044
1045
+ /// If `option` is
1046
+ /// * `Some(inner)` => create display struct for `"{prefix}{inner}{postfix}"`
1047
+ /// * `_` => do nothing
1048
+ #[ must_use]
1049
+ fn display_option < ' a , T : fmt:: Display > (
1050
+ prefix : & ' a str ,
1051
+ postfix : & ' a str ,
1052
+ option : & ' a Option < T > ,
1053
+ ) -> impl fmt:: Display + ' a {
1054
+ struct OptionDisplay < ' a , T > ( & ' a str , & ' a str , & ' a Option < T > ) ;
1055
+ impl < ' a , T : fmt:: Display > fmt:: Display for OptionDisplay < ' a , T > {
1056
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
1057
+ if let Some ( inner) = self . 2 {
1058
+ let ( prefix, postfix) = ( self . 0 , self . 1 ) ;
1059
+ write ! ( f, "{prefix}{inner}{postfix}" ) ?;
1060
+ }
1061
+ Ok ( ( ) )
1062
+ }
1063
+ }
1064
+ OptionDisplay ( prefix, postfix, option)
1065
+ }
1066
+
1067
+ /// If `option` is
1068
+ /// * `Some(inner)` => create display struct for `" {inner}"`
1069
+ /// * `_` => do nothing
1070
+ #[ must_use]
1071
+ fn display_option_spaced < T : fmt:: Display > ( option : & Option < T > ) -> impl fmt:: Display + ' _ {
1072
+ display_option ( " " , "" , option)
1073
+ }
1074
+
925
1075
/// `<constraint_characteristics> = [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] [ ENFORCED | NOT ENFORCED ]`
926
1076
///
927
1077
/// Used in UNIQUE and foreign key constraints. The individual settings may occur in any order.
0 commit comments