1515
1616#[ cfg( not( feature = "std" ) ) ]
1717use alloc:: { boxed:: Box , string:: String , vec:: Vec } ;
18- use core:: fmt;
18+ use core:: fmt:: { self , Write } ;
1919
2020#[ cfg( feature = "serde" ) ]
2121use serde:: { Deserialize , Serialize } ;
@@ -397,12 +397,68 @@ impl fmt::Display for AlterColumnOperation {
397397#[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
398398#[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
399399pub 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
401412 Unique {
413+ /// Constraint name.
414+ ///
415+ /// Can be not the same as `index_name`
402416 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.
403426 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 > ,
406462 characteristics : Option < ConstraintCharacteristics > ,
407463 } ,
408464 /// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY (<columns>)
@@ -472,22 +528,51 @@ impl fmt::Display for TableConstraint {
472528 match self {
473529 TableConstraint :: Unique {
474530 name,
531+ index_name,
532+ index_type_display,
533+ index_type,
475534 columns,
476- is_primary ,
535+ index_options ,
477536 characteristics,
478537 } => {
479538 write ! (
480539 f,
481- "{}{} ({})" ,
540+ "{}UNIQUE{index_type_display:>}{} {} ({})" ,
482541 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) ,
485545 ) ?;
486546
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, " " ) ) ?;
489573 }
490574
575+ write ! ( f, "{}" , display_option_spaced( characteristics) ) ?;
491576 Ok ( ( ) )
492577 }
493578 TableConstraint :: ForeignKey {
@@ -550,9 +635,7 @@ impl fmt::Display for TableConstraint {
550635 write ! ( f, "SPATIAL" ) ?;
551636 }
552637
553- if !matches ! ( index_type_display, KeyOrIndexDisplay :: None ) {
554- write ! ( f, " {index_type_display}" ) ?;
555- }
638+ write ! ( f, "{index_type_display:>}" ) ?;
556639
557640 if let Some ( name) = opt_index_name {
558641 write ! ( f, " {name}" ) ?;
@@ -585,8 +668,20 @@ pub enum KeyOrIndexDisplay {
585668 Index ,
586669}
587670
671+ impl KeyOrIndexDisplay {
672+ pub fn is_none ( self ) -> bool {
673+ matches ! ( self , Self :: None )
674+ }
675+ }
676+
588677impl fmt:: Display for KeyOrIndexDisplay {
589678 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+
590685 match self {
591686 KeyOrIndexDisplay :: None => {
592687 write ! ( f, "" )
@@ -626,6 +721,30 @@ impl fmt::Display for IndexType {
626721 }
627722 }
628723}
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+
629748#[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
630749#[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
631750#[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
@@ -909,6 +1028,7 @@ pub enum GeneratedExpressionMode {
9091028 Stored ,
9101029}
9111030
1031+ #[ must_use]
9121032fn display_constraint_name ( name : & ' _ Option < Ident > ) -> impl fmt:: Display + ' _ {
9131033 struct ConstraintName < ' a > ( & ' a Option < Ident > ) ;
9141034 impl < ' a > fmt:: Display for ConstraintName < ' a > {
@@ -922,6 +1042,36 @@ fn display_constraint_name(name: &'_ Option<Ident>) -> impl fmt::Display + '_ {
9221042 ConstraintName ( name)
9231043}
9241044
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+
9251075/// `<constraint_characteristics> = [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] [ ENFORCED | NOT ENFORCED ]`
9261076///
9271077/// Used in UNIQUE and foreign key constraints. The individual settings may occur in any order.
0 commit comments