sqlx_mysql/options/mod.rs
1use std::path::{Path, PathBuf};
2
3mod connect;
4mod parse;
5mod ssl_mode;
6
7use crate::{connection::LogSettings, net::tls::CertificateInput};
8pub use ssl_mode::MySqlSslMode;
9
10/// Options and flags which can be used to configure a MySQL connection.
11///
12/// A value of `MySqlConnectOptions` can be parsed from a connection URL,
13/// as described by [MySQL](https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-jdbc-url-format.html).
14///
15/// The generic format of the connection URL:
16///
17/// ```text
18/// mysql://[host][/database][?properties]
19/// ```
20///
21/// This type also implements [`FromStr`][std::str::FromStr] so you can parse it from a string
22/// containing a connection URL and then further adjust options if necessary (see example below).
23///
24/// ## Properties
25///
26/// |Parameter|Default|Description|
27/// |---------|-------|-----------|
28/// | `ssl-mode` | `PREFERRED` | Determines whether or with what priority a secure SSL TCP/IP connection will be negotiated. See [`MySqlSslMode`]. |
29/// | `ssl-ca` | `None` | Sets the name of a file containing a list of trusted SSL Certificate Authorities. |
30/// | `statement-cache-capacity` | `100` | The maximum number of prepared statements stored in the cache. Set to `0` to disable. |
31/// | `socket` | `None` | Path to the unix domain socket, which will be used instead of TCP if set. |
32///
33/// # Example
34///
35/// ```rust,no_run
36/// # async fn example() -> sqlx::Result<()> {
37/// use sqlx::{Connection, ConnectOptions};
38/// use sqlx::mysql::{MySqlConnectOptions, MySqlConnection, MySqlPool, MySqlSslMode};
39///
40/// // URL connection string
41/// let conn = MySqlConnection::connect("mysql://root:password@localhost/db").await?;
42///
43/// // Manually-constructed options
44/// let conn = MySqlConnectOptions::new()
45/// .host("localhost")
46/// .username("root")
47/// .password("password")
48/// .database("db")
49/// .connect().await?;
50///
51/// // Modifying options parsed from a string
52/// let mut opts: MySqlConnectOptions = "mysql://root:password@localhost/db".parse()?;
53///
54/// // Change the log verbosity level for queries.
55/// // Information about SQL queries is logged at `DEBUG` level by default.
56/// opts = opts.log_statements(log::LevelFilter::Trace);
57///
58/// let pool = MySqlPool::connect_with(opts).await?;
59/// # Ok(())
60/// # }
61/// ```
62#[derive(Debug, Clone)]
63pub struct MySqlConnectOptions {
64 pub(crate) host: String,
65 pub(crate) port: u16,
66 pub(crate) socket: Option<PathBuf>,
67 pub(crate) username: String,
68 pub(crate) password: Option<String>,
69 pub(crate) database: Option<String>,
70 pub(crate) ssl_mode: MySqlSslMode,
71 pub(crate) ssl_ca: Option<CertificateInput>,
72 pub(crate) ssl_client_cert: Option<CertificateInput>,
73 pub(crate) ssl_client_key: Option<CertificateInput>,
74 pub(crate) statement_cache_capacity: usize,
75 pub(crate) charset: String,
76 pub(crate) collation: Option<String>,
77 pub(crate) log_settings: LogSettings,
78 pub(crate) pipes_as_concat: bool,
79 pub(crate) enable_cleartext_plugin: bool,
80 pub(crate) no_engine_substitution: bool,
81 pub(crate) timezone: Option<String>,
82 pub(crate) set_names: bool,
83}
84
85impl Default for MySqlConnectOptions {
86 fn default() -> Self {
87 Self::new()
88 }
89}
90
91impl MySqlConnectOptions {
92 /// Creates a new, default set of options ready for configuration
93 pub fn new() -> Self {
94 Self {
95 port: 3306,
96 host: String::from("localhost"),
97 socket: None,
98 username: String::from("root"),
99 password: None,
100 database: None,
101 charset: String::from("utf8mb4"),
102 collation: None,
103 ssl_mode: MySqlSslMode::Preferred,
104 ssl_ca: None,
105 ssl_client_cert: None,
106 ssl_client_key: None,
107 statement_cache_capacity: 100,
108 log_settings: Default::default(),
109 pipes_as_concat: true,
110 enable_cleartext_plugin: false,
111 no_engine_substitution: true,
112 timezone: Some(String::from("+00:00")),
113 set_names: true,
114 }
115 }
116
117 /// Sets the name of the host to connect to.
118 ///
119 /// The default behavior when the host is not specified,
120 /// is to connect to localhost.
121 pub fn host(mut self, host: &str) -> Self {
122 host.clone_into(&mut self.host);
123 self
124 }
125
126 /// Sets the port to connect to at the server host.
127 ///
128 /// The default port for MySQL is `3306`.
129 pub fn port(mut self, port: u16) -> Self {
130 self.port = port;
131 self
132 }
133
134 /// Pass a path to a Unix socket. This changes the connection stream from
135 /// TCP to UDS.
136 ///
137 /// By default set to `None`.
138 pub fn socket(mut self, path: impl AsRef<Path>) -> Self {
139 self.socket = Some(path.as_ref().to_path_buf());
140 self
141 }
142
143 /// Sets the username to connect as.
144 pub fn username(mut self, username: &str) -> Self {
145 username.clone_into(&mut self.username);
146 self
147 }
148
149 /// Sets the password to connect with.
150 pub fn password(mut self, password: &str) -> Self {
151 self.password = Some(password.to_owned());
152 self
153 }
154
155 /// Sets the database name.
156 pub fn database(mut self, database: &str) -> Self {
157 self.database = Some(database.to_owned());
158 self
159 }
160
161 /// Sets whether or with what priority a secure SSL TCP/IP connection will be negotiated
162 /// with the server.
163 ///
164 /// By default, the SSL mode is [`Preferred`](MySqlSslMode::Preferred), and the client will
165 /// first attempt an SSL connection but fallback to a non-SSL connection on failure.
166 ///
167 /// # Example
168 ///
169 /// ```rust
170 /// # use sqlx_mysql::{MySqlSslMode, MySqlConnectOptions};
171 /// let options = MySqlConnectOptions::new()
172 /// .ssl_mode(MySqlSslMode::Required);
173 /// ```
174 pub fn ssl_mode(mut self, mode: MySqlSslMode) -> Self {
175 self.ssl_mode = mode;
176 self
177 }
178
179 /// Sets the name of a file containing a list of trusted SSL Certificate Authorities.
180 ///
181 /// # Example
182 ///
183 /// ```rust
184 /// # use sqlx_mysql::{MySqlSslMode, MySqlConnectOptions};
185 /// let options = MySqlConnectOptions::new()
186 /// .ssl_mode(MySqlSslMode::VerifyCa)
187 /// .ssl_ca("path/to/ca.crt");
188 /// ```
189 pub fn ssl_ca(mut self, file_name: impl AsRef<Path>) -> Self {
190 self.ssl_ca = Some(CertificateInput::File(file_name.as_ref().to_owned()));
191 self
192 }
193
194 /// Sets PEM encoded list of trusted SSL Certificate Authorities.
195 ///
196 /// # Example
197 ///
198 /// ```rust
199 /// # use sqlx_mysql::{MySqlSslMode, MySqlConnectOptions};
200 /// let options = MySqlConnectOptions::new()
201 /// .ssl_mode(MySqlSslMode::VerifyCa)
202 /// .ssl_ca_from_pem(vec![]);
203 /// ```
204 pub fn ssl_ca_from_pem(mut self, pem_certificate: Vec<u8>) -> Self {
205 self.ssl_ca = Some(CertificateInput::Inline(pem_certificate));
206 self
207 }
208
209 /// Sets the name of a file containing SSL client certificate.
210 ///
211 /// # Example
212 ///
213 /// ```rust
214 /// # use sqlx_mysql::{MySqlSslMode, MySqlConnectOptions};
215 /// let options = MySqlConnectOptions::new()
216 /// .ssl_mode(MySqlSslMode::VerifyCa)
217 /// .ssl_client_cert("path/to/client.crt");
218 /// ```
219 pub fn ssl_client_cert(mut self, cert: impl AsRef<Path>) -> Self {
220 self.ssl_client_cert = Some(CertificateInput::File(cert.as_ref().to_path_buf()));
221 self
222 }
223
224 /// Sets the SSL client certificate as a PEM-encoded byte slice.
225 ///
226 /// This should be an ASCII-encoded blob that starts with `-----BEGIN CERTIFICATE-----`.
227 ///
228 /// # Example
229 /// Note: embedding SSL certificates and keys in the binary is not advised.
230 /// This is for illustration purposes only.
231 ///
232 /// ```rust
233 /// # use sqlx_mysql::{MySqlSslMode, MySqlConnectOptions};
234 ///
235 /// const CERT: &[u8] = b"\
236 /// -----BEGIN CERTIFICATE-----
237 /// <Certificate data here.>
238 /// -----END CERTIFICATE-----";
239 ///
240 /// let options = MySqlConnectOptions::new()
241 /// .ssl_mode(MySqlSslMode::VerifyCa)
242 /// .ssl_client_cert_from_pem(CERT);
243 /// ```
244 pub fn ssl_client_cert_from_pem(mut self, cert: impl AsRef<[u8]>) -> Self {
245 self.ssl_client_cert = Some(CertificateInput::Inline(cert.as_ref().to_vec()));
246 self
247 }
248
249 /// Sets the name of a file containing SSL client key.
250 ///
251 /// # Example
252 ///
253 /// ```rust
254 /// # use sqlx_mysql::{MySqlSslMode, MySqlConnectOptions};
255 /// let options = MySqlConnectOptions::new()
256 /// .ssl_mode(MySqlSslMode::VerifyCa)
257 /// .ssl_client_key("path/to/client.key");
258 /// ```
259 pub fn ssl_client_key(mut self, key: impl AsRef<Path>) -> Self {
260 self.ssl_client_key = Some(CertificateInput::File(key.as_ref().to_path_buf()));
261 self
262 }
263
264 /// Sets the SSL client key as a PEM-encoded byte slice.
265 ///
266 /// This should be an ASCII-encoded blob that starts with `-----BEGIN PRIVATE KEY-----`.
267 ///
268 /// # Example
269 /// Note: embedding SSL certificates and keys in the binary is not advised.
270 /// This is for illustration purposes only.
271 ///
272 /// ```rust
273 /// # use sqlx_mysql::{MySqlSslMode, MySqlConnectOptions};
274 ///
275 /// const KEY: &[u8] = b"\
276 /// -----BEGIN PRIVATE KEY-----
277 /// <Private key data here.>
278 /// -----END PRIVATE KEY-----";
279 ///
280 /// let options = MySqlConnectOptions::new()
281 /// .ssl_mode(MySqlSslMode::VerifyCa)
282 /// .ssl_client_key_from_pem(KEY);
283 /// ```
284 pub fn ssl_client_key_from_pem(mut self, key: impl AsRef<[u8]>) -> Self {
285 self.ssl_client_key = Some(CertificateInput::Inline(key.as_ref().to_vec()));
286 self
287 }
288
289 /// Sets the capacity of the connection's statement cache in a number of stored
290 /// distinct statements. Caching is handled using LRU, meaning when the
291 /// amount of queries hits the defined limit, the oldest statement will get
292 /// dropped.
293 ///
294 /// The default cache capacity is 100 statements.
295 pub fn statement_cache_capacity(mut self, capacity: usize) -> Self {
296 self.statement_cache_capacity = capacity;
297 self
298 }
299
300 /// Sets the character set for the connection.
301 ///
302 /// The default character set is `utf8mb4`. This is supported from MySQL 5.5.3.
303 /// If you need to connect to an older version, we recommend you to change this to `utf8`.
304 ///
305 /// Implies [`.set_names(true)`][Self::set_names()].
306 pub fn charset(mut self, charset: &str) -> Self {
307 self.set_names = true;
308 charset.clone_into(&mut self.charset);
309 self
310 }
311
312 /// Sets the collation for the connection.
313 ///
314 /// The default collation is derived on the server from the `charset`, if set.
315 /// Normally, you should only have to set the `charset`.
316 ///
317 /// If setting this, it is recommended to also set [`charset`][Self::charset()].
318 ///
319 /// Implies [`.set_names(true)`][Self::set_names()].
320 pub fn collation(mut self, collation: &str) -> Self {
321 self.set_names = true;
322 self.collation = Some(collation.to_owned());
323 self
324 }
325
326 /// Sets the flag that enables or disables the `PIPES_AS_CONCAT` connection setting
327 ///
328 /// The default value is set to true, but some MySql databases such as PlanetScale
329 /// error out with this connection setting so it needs to be set false in such
330 /// cases.
331 pub fn pipes_as_concat(mut self, flag_val: bool) -> Self {
332 self.pipes_as_concat = flag_val;
333 self
334 }
335
336 /// Enables mysql_clear_password plugin support.
337 ///
338 /// Security Note:
339 /// Sending passwords as cleartext may be a security problem in some
340 /// configurations. Without additional defensive configuration like
341 /// ssl-mode=VERIFY_IDENTITY, an attacker can compromise a router
342 /// and trick the application into divulging its credentials.
343 ///
344 /// It is strongly recommended to set `.ssl_mode` to `Required`,
345 /// `VerifyCa`, or `VerifyIdentity` when enabling cleartext plugin.
346 pub fn enable_cleartext_plugin(mut self, flag_val: bool) -> Self {
347 self.enable_cleartext_plugin = flag_val;
348 self
349 }
350
351 #[deprecated = "renamed to .no_engine_substitution()"]
352 pub fn no_engine_subsitution(self, flag_val: bool) -> Self {
353 self.no_engine_substitution(flag_val)
354 }
355
356 /// Flag that enables or disables the `NO_ENGINE_SUBSTITUTION` sql_mode setting after
357 /// connection.
358 ///
359 /// If not set, if the available storage engine specified by a `CREATE TABLE` is not available,
360 /// a warning is given and the default storage engine is used instead.
361 ///
362 /// By default, this is `true` (`NO_ENGINE_SUBSTITUTION` is passed, forbidding engine
363 /// substitution).
364 ///
365 /// <https://mariadb.com/kb/en/sql-mode/>
366 pub fn no_engine_substitution(mut self, flag_val: bool) -> Self {
367 self.no_engine_substitution = flag_val;
368 self
369 }
370
371 /// If `Some`, sets the `time_zone` option to the given string after connecting to the database.
372 ///
373 /// If `None`, no `time_zone` parameter is sent; the server timezone will be used instead.
374 ///
375 /// Defaults to `Some(String::from("+00:00"))` to ensure all timestamps are in UTC.
376 ///
377 /// ### Warning
378 /// Changing this setting from its default will apply an unexpected skew to any
379 /// `time::OffsetDateTime` or `chrono::DateTime<Utc>` value, whether passed as a parameter or
380 /// decoded as a result. `TIMESTAMP` values are not encoded with their UTC offset in the MySQL
381 /// protocol, so encoding and decoding of these types assumes the server timezone is *always*
382 /// UTC.
383 ///
384 /// If you are changing this option, ensure your application only uses
385 /// `time::PrimitiveDateTime` or `chrono::NaiveDateTime` and that it does not assume these
386 /// timestamps can be placed on a real timeline without applying the proper offset.
387 pub fn timezone(mut self, value: impl Into<Option<String>>) -> Self {
388 self.timezone = value.into();
389 self
390 }
391
392 /// If enabled, [`.charset()`] and [`.collation()`] are set with the appropriate command.
393 ///
394 /// If only `.charset()`
395 ///
396 /// This ensures the connection uses the specified character set and collation.
397 ///
398 /// Enabled by default.
399 ///
400 /// ### Warning
401 /// If this is disabled and the default charset is not binary-compatible with UTF-8, query
402 /// strings, column names and string values will likely not decode (or encode) correctly, which
403 /// may result in unexpected errors or garbage outputs at runtime.
404 ///
405 /// For proper functioning, you *must* ensure the server is using a binary-compatible charset,
406 /// such as ASCII or Latin-1 (ISO 8859-1), and that you do not pass any strings containing
407 /// codepoints not supported by said charset.
408 ///
409 /// Instead of disabling this, you may also consider setting [`.charset()`] to a charset that
410 /// is supported by your MySQL or MariaDB server version and compatible with UTF-8.
411 ///
412 /// [`.charset`]: Self::charset()
413 pub fn set_names(mut self, flag_val: bool) -> Self {
414 self.set_names = flag_val;
415 self
416 }
417}
418
419impl MySqlConnectOptions {
420 /// Get the current host.
421 ///
422 /// # Example
423 ///
424 /// ```rust
425 /// # use sqlx_mysql::MySqlConnectOptions;
426 /// let options = MySqlConnectOptions::new()
427 /// .host("127.0.0.1");
428 /// assert_eq!(options.get_host(), "127.0.0.1");
429 /// ```
430 pub fn get_host(&self) -> &str {
431 &self.host
432 }
433
434 /// Get the server's port.
435 ///
436 /// # Example
437 ///
438 /// ```rust
439 /// # use sqlx_mysql::MySqlConnectOptions;
440 /// let options = MySqlConnectOptions::new()
441 /// .port(6543);
442 /// assert_eq!(options.get_port(), 6543);
443 /// ```
444 pub fn get_port(&self) -> u16 {
445 self.port
446 }
447
448 /// Get the socket path.
449 ///
450 /// # Example
451 ///
452 /// ```rust
453 /// # use sqlx_mysql::MySqlConnectOptions;
454 /// let options = MySqlConnectOptions::new()
455 /// .socket("/tmp");
456 /// assert!(options.get_socket().is_some());
457 /// ```
458 pub fn get_socket(&self) -> Option<&PathBuf> {
459 self.socket.as_ref()
460 }
461
462 /// Get the current username.
463 ///
464 /// # Example
465 ///
466 /// ```rust
467 /// # use sqlx_mysql::MySqlConnectOptions;
468 /// let options = MySqlConnectOptions::new()
469 /// .username("foo");
470 /// assert_eq!(options.get_username(), "foo");
471 /// ```
472 pub fn get_username(&self) -> &str {
473 &self.username
474 }
475
476 /// Get the current database name.
477 ///
478 /// # Example
479 ///
480 /// ```rust
481 /// # use sqlx_mysql::MySqlConnectOptions;
482 /// let options = MySqlConnectOptions::new()
483 /// .database("postgres");
484 /// assert!(options.get_database().is_some());
485 /// ```
486 pub fn get_database(&self) -> Option<&str> {
487 self.database.as_deref()
488 }
489
490 /// Get the SSL mode.
491 ///
492 /// # Example
493 ///
494 /// ```rust
495 /// # use sqlx_mysql::{MySqlConnectOptions, MySqlSslMode};
496 /// let options = MySqlConnectOptions::new();
497 /// assert!(matches!(options.get_ssl_mode(), MySqlSslMode::Preferred));
498 /// ```
499 pub fn get_ssl_mode(&self) -> MySqlSslMode {
500 self.ssl_mode
501 }
502
503 /// Get the server charset.
504 ///
505 /// # Example
506 ///
507 /// ```rust
508 /// # use sqlx_mysql::MySqlConnectOptions;
509 /// let options = MySqlConnectOptions::new();
510 /// assert_eq!(options.get_charset(), "utf8mb4");
511 /// ```
512 pub fn get_charset(&self) -> &str {
513 &self.charset
514 }
515
516 /// Get the server collation.
517 ///
518 /// # Example
519 ///
520 /// ```rust
521 /// # use sqlx_mysql::MySqlConnectOptions;
522 /// let options = MySqlConnectOptions::new()
523 /// .collation("collation");
524 /// assert!(options.get_collation().is_some());
525 /// ```
526 pub fn get_collation(&self) -> Option<&str> {
527 self.collation.as_deref()
528 }
529}