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}