11use crate :: connection:: ConnectOptions ;
22use crate :: error:: Error ;
33use crate :: executor:: Executor ;
4+ use crate :: net:: Socket ;
45use crate :: { MySqlConnectOptions , MySqlConnection } ;
56use futures_core:: future:: BoxFuture ;
67use log:: LevelFilter ;
78use sqlx_core:: Url ;
89use std:: time:: Duration ;
910
11+ impl MySqlConnectOptions {
12+ /// Establish a fully initialized connection over a pre-connected socket.
13+ ///
14+ /// This performs the MySQL handshake, authentication, and post-connect
15+ /// initialization (`SET NAMES`, `sql_mode`, `time_zone`) over the
16+ /// provided socket.
17+ ///
18+ /// The socket must already be connected to a MySQL-compatible server.
19+ /// This enables custom transports such as in-memory pipes, simulation
20+ /// frameworks (e.g. turmoil), SSH tunnels, or SOCKS proxies.
21+ ///
22+ /// # Example
23+ ///
24+ /// ```rust,no_run
25+ /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
26+ /// use sqlx::mysql::MySqlConnectOptions;
27+ ///
28+ /// let options = MySqlConnectOptions::new()
29+ /// .username("root")
30+ /// .database("mydb");
31+ ///
32+ /// let stream = tokio::net::TcpStream::connect("127.0.0.1:3306").await?;
33+ /// let conn = options.connect_with_socket(stream).await?;
34+ /// # Ok(())
35+ /// # }
36+ /// ```
37+ pub async fn connect_with_socket < S : Socket > (
38+ & self ,
39+ socket : S ,
40+ ) -> Result < MySqlConnection , Error > {
41+ let mut conn = MySqlConnection :: connect_with_socket ( self , socket) . await ?;
42+ self . after_connect ( & mut conn) . await ?;
43+ Ok ( conn)
44+ }
45+
46+ /// Post-connection initialization shared between `connect()` and
47+ /// `connect_with_socket()`.
48+ ///
49+ /// After the connection is established, we initialize by configuring a few
50+ /// connection parameters:
51+ ///
52+ /// - <https://mariadb.com/kb/en/sql-mode/>
53+ ///
54+ /// - `PIPES_AS_CONCAT` - Allows using the pipe character (ASCII 124) as string concatenation
55+ /// operator. This means that "A" || "B" can be used in place of CONCAT("A", "B").
56+ ///
57+ /// - `NO_ENGINE_SUBSTITUTION` - If not set, if the available storage engine specified by a
58+ /// CREATE TABLE is not available, a warning is given and the default storage engine is used
59+ /// instead.
60+ ///
61+ /// - `NO_ZERO_DATE` - Don't allow '0000-00-00'. This is invalid in Rust.
62+ ///
63+ /// - `NO_ZERO_IN_DATE` - Don't allow 'YYYY-00-00'. This is invalid in Rust.
64+ ///
65+ /// Setting the time zone allows us to assume that the output from a TIMESTAMP field is UTC.
66+ ///
67+ /// - <https://mathiasbynens.be/notes/mysql-utf8mb4>
68+ async fn after_connect ( & self , conn : & mut MySqlConnection ) -> Result < ( ) , Error > {
69+ let mut sql_mode = Vec :: new ( ) ;
70+ if self . pipes_as_concat {
71+ sql_mode. push ( r#"PIPES_AS_CONCAT"# ) ;
72+ }
73+ if self . no_engine_substitution {
74+ sql_mode. push ( r#"NO_ENGINE_SUBSTITUTION"# ) ;
75+ }
76+
77+ let mut options = Vec :: new ( ) ;
78+ if !sql_mode. is_empty ( ) {
79+ options. push ( format ! (
80+ r#"sql_mode=(SELECT CONCAT(@@sql_mode, ',{}'))"# ,
81+ sql_mode. join( "," )
82+ ) ) ;
83+ }
84+ if let Some ( timezone) = & self . timezone {
85+ options. push ( format ! ( r#"time_zone='{}'"# , timezone) ) ;
86+ }
87+ if self . set_names {
88+ options. push ( format ! (
89+ r#"NAMES {} COLLATE {}"# ,
90+ conn. inner. stream. charset. as_str( ) ,
91+ conn. inner. stream. collation. as_str( )
92+ ) )
93+ }
94+
95+ if !options. is_empty ( ) {
96+ conn. execute ( & * format ! ( r#"SET {};"# , options. join( "," ) ) )
97+ . await ?;
98+ }
99+
100+ Ok ( ( ) )
101+ }
102+ }
103+
10104impl ConnectOptions for MySqlConnectOptions {
11105 type Connection = MySqlConnection ;
12106
@@ -24,63 +118,7 @@ impl ConnectOptions for MySqlConnectOptions {
24118 {
25119 Box :: pin ( async move {
26120 let mut conn = MySqlConnection :: establish ( self ) . await ?;
27-
28- // After the connection is established, we initialize by configuring a few
29- // connection parameters
30-
31- // https://mariadb.com/kb/en/sql-mode/
32-
33- // PIPES_AS_CONCAT - Allows using the pipe character (ASCII 124) as string concatenation operator.
34- // This means that "A" || "B" can be used in place of CONCAT("A", "B").
35-
36- // NO_ENGINE_SUBSTITUTION - If not set, if the available storage engine specified by a CREATE TABLE is
37- // not available, a warning is given and the default storage
38- // engine is used instead.
39-
40- // NO_ZERO_DATE - Don't allow '0000-00-00'. This is invalid in Rust.
41-
42- // NO_ZERO_IN_DATE - Don't allow 'YYYY-00-00'. This is invalid in Rust.
43-
44- // --
45-
46- // Setting the time zone allows us to assume that the output
47- // from a TIMESTAMP field is UTC
48-
49- // --
50-
51- // https://mathiasbynens.be/notes/mysql-utf8mb4
52-
53- let mut sql_mode = Vec :: new ( ) ;
54- if self . pipes_as_concat {
55- sql_mode. push ( r#"PIPES_AS_CONCAT"# ) ;
56- }
57- if self . no_engine_substitution {
58- sql_mode. push ( r#"NO_ENGINE_SUBSTITUTION"# ) ;
59- }
60-
61- let mut options = Vec :: new ( ) ;
62- if !sql_mode. is_empty ( ) {
63- options. push ( format ! (
64- r#"sql_mode=(SELECT CONCAT(@@sql_mode, ',{}'))"# ,
65- sql_mode. join( "," )
66- ) ) ;
67- }
68- if let Some ( timezone) = & self . timezone {
69- options. push ( format ! ( r#"time_zone='{}'"# , timezone) ) ;
70- }
71- if self . set_names {
72- options. push ( format ! (
73- r#"NAMES {} COLLATE {}"# ,
74- conn. inner. stream. charset. as_str( ) ,
75- conn. inner. stream. collation. as_str( )
76- ) )
77- }
78-
79- if !options. is_empty ( ) {
80- conn. execute ( & * format ! ( r#"SET {};"# , options. join( "," ) ) )
81- . await ?;
82- }
83-
121+ self . after_connect ( & mut conn) . await ?;
84122 Ok ( conn)
85123 } )
86124 }
0 commit comments