Skip to main content

diesel/mysql/
connection.rs

1use crate::mysql::Mysql;
2use crate::mysql_like::MysqlLikeConnection;
3
4/// A connection to a MySQL database. Connection URLs should be in the form
5/// `mysql://[user[:password]@]host/database_name[?unix_socket=socket-path&ssl_mode=SSL_MODE*&ssl_ca=/etc/ssl/certs/ca-certificates.crt&ssl_cert=/etc/ssl/certs/client-cert.crt&ssl_key=/etc/ssl/certs/client-key.crt]`
6///
7///* `host` can be an IP address or a hostname. If it is set to `localhost`, a connection
8///  will be attempted through the socket at `/tmp/mysql.sock`. If you want to connect to
9///  a local server via TCP (e.g. docker containers), use `0.0.0.0` or `127.0.0.1` instead.
10/// * `unix_socket` expects the path to the unix socket
11/// * `ssl_ca` accepts a path to the system's certificate roots
12/// * `ssl_cert` accepts a path to the client's certificate file
13/// * `ssl_key` accepts a path to the client's private key file
14/// * `ssl_mode` expects a value defined for MySQL client command option `--ssl-mode`
15///   See <https://dev.mysql.com/doc/refman/5.7/en/connection-options.html#option_general_ssl-mode>
16///
17/// Errors carry the SQLSTATE the server or client library reports in
18/// [`DatabaseErrorInformation::sqlstate`](crate::result::DatabaseErrorInformation::sqlstate),
19/// including custom codes raised with
20/// [`SIGNAL`](https://dev.mysql.com/doc/refman/8.4/en/signal.html). The server
21/// reports `HY000` for errors without a specific SQLSTATE, as described at
22/// <https://dev.mysql.com/doc/refman/8.4/en/error-message-elements.html>.
23/// [`sql_state`](crate::mysql_like::sql_state) names the codes.
24///
25/// # Supported loading model implementations
26///
27/// * [`DefaultLoadingMode`](crate::connection::DefaultLoadingMode)
28///
29/// As `MysqlConnection` only supports a single loading mode implementation
30/// it is **not required** to explicitly specify a loading mode
31/// when calling [`RunQueryDsl::load_iter()`](crate::query_dsl::RunQueryDsl::load_iter)
32/// or [`LoadConnection::load`](crate::connection::LoadConnection::load)
33///
34/// ## DefaultLoadingMode
35///
36/// `MysqlConnection` only supports a single loading mode, which loads
37/// values row by row from the result set.
38///
39/// ```rust,dejadoc
40/// # include!("../doctest_setup.rs");
41/// #
42/// # fn main() {
43/// #     run_test().unwrap();
44/// # }
45/// #
46/// # fn run_test() -> QueryResult<()> {
47/// #     use schema::users;
48/// #     let connection = &mut establish_connection();
49/// use diesel::connection::DefaultLoadingMode;
50/// {
51///     // scope to restrict the lifetime of the iterator
52///     let iter1 = users::table.load_iter::<(i32, String), DefaultLoadingMode>(connection)?;
53///
54///     for r in iter1 {
55///         let (id, name) = r?;
56///         println!("Id: {} Name: {}", id, name);
57///     }
58/// }
59///
60/// // works without specifying the loading mode
61/// let iter2 = users::table.load_iter::<(i32, String), _>(connection)?;
62///
63/// for r in iter2 {
64///     let (id, name) = r?;
65///     println!("Id: {} Name: {}", id, name);
66/// }
67/// #   Ok(())
68/// # }
69/// ```
70///
71/// This mode does **not support** creating
72/// multiple iterators using the same connection.
73///
74/// ```compile_fail,dejadoc
75/// # include!("../../doctest_setup.rs");
76/// #
77/// # fn main() {
78/// #     run_test().unwrap();
79/// # }
80/// #
81/// # fn run_test() -> QueryResult<()> {
82/// #     use schema::users;
83/// #     let connection = &mut establish_connection();
84/// use diesel::connection::DefaultLoadingMode;
85///
86/// let iter1 = users::table.load_iter::<(i32, String), DefaultLoadingMode>(connection)?;
87/// let iter2 = users::table.load_iter::<(i32, String), DefaultLoadingMode>(connection)?;
88///
89/// for r in iter1 {
90///     let (id, name) = r?;
91///     println!("Id: {} Name: {}", id, name);
92/// }
93///
94/// for r in iter2 {
95///     let (id, name) = r?;
96///     println!("Id: {} Name: {}", id, name);
97/// }
98/// #   Ok(())
99/// # }
100/// ```
101pub type MysqlConnection = MysqlLikeConnection<Mysql>;
102
103#[cfg(test)]
104mod tests {
105    extern crate dotenvy;
106
107    use super::*;
108    use crate::connection::Connection;
109    use crate::connection::SimpleConnection;
110    use crate::query_dsl::RunQueryDsl;
111    use std::env;
112
113    fn connection() -> MysqlConnection {
114        dotenvy::dotenv().ok();
115        let database_url = env::var("MYSQL_UNIT_TEST_DATABASE_URL")
116            .or_else(|_| env::var("MYSQL_DATABASE_URL"))
117            .or_else(|_| env::var("DATABASE_URL"))
118            .expect("DATABASE_URL must be set in order to run unit tests");
119        MysqlConnection::establish(&database_url).unwrap()
120    }
121
122    #[diesel_test_helper::test]
123    fn batch_execute_handles_single_queries_with_results() {
124        let connection = &mut connection();
125        assert!(connection.batch_execute("SELECT 1").is_ok());
126        assert!(connection.batch_execute("SELECT 1").is_ok());
127    }
128
129    #[diesel_test_helper::test]
130    fn batch_execute_handles_multi_queries_with_results() {
131        let connection = &mut connection();
132        let query = "SELECT 1; SELECT 2; SELECT 3;";
133        assert!(connection.batch_execute(query).is_ok());
134        assert!(connection.batch_execute(query).is_ok());
135    }
136
137    #[diesel_test_helper::test]
138    fn execute_handles_queries_which_return_results() {
139        let connection = &mut connection();
140        assert!(crate::sql_query("SELECT 1").execute(connection).is_ok());
141        assert!(crate::sql_query("SELECT 1").execute(connection).is_ok());
142    }
143
144    #[diesel_test_helper::test]
145    fn check_client_found_rows_flag() {
146        let conn = &mut crate::test_helpers::connection();
147        crate::sql_query("DROP TABLE IF EXISTS update_test CASCADE")
148            .execute(conn)
149            .unwrap();
150
151        crate::sql_query("CREATE TABLE update_test(id INTEGER PRIMARY KEY, num INTEGER NOT NULL)")
152            .execute(conn)
153            .unwrap();
154
155        crate::sql_query("INSERT INTO update_test(id, num) VALUES (1, 5)")
156            .execute(conn)
157            .unwrap();
158
159        let output = crate::sql_query("UPDATE update_test SET num = 5 WHERE id = 1")
160            .execute(conn)
161            .unwrap();
162
163        assert_eq!(output, 1);
164    }
165
166    #[crate::declare_sql_function]
167    extern "SQL" {
168        fn version() -> crate::sql_types::Text;
169    }
170
171    #[diesel_test_helper::test]
172    fn version_matches_the_server_reported_version() {
173        let connection = &mut connection();
174        let expected = connection.version().unwrap().to_string();
175        let reported = crate::select(version())
176            .get_result::<String>(connection)
177            .unwrap();
178        assert!(
179            reported == expected || reported.starts_with(&format!("{expected}-")),
180            "`{reported}` does not start with `{expected}`"
181        );
182    }
183}