2 个版本

0.1.1 2022 年 11 月 17 日
0.1.0 2022 年 11 月 17 日

#2699数据库接口


lunatic-db 中使用

MIT/Apache

345KB
6.5K SLoC

Chat Crates.io

mysql

此 crate 提供

  • 纯 Rust 的 MySql 数据库驱动程序;
  • 连接池。

特性

  • 支持 macOS、Windows 和 Linux;
  • 通过 nativetlsrustls 支持 TLS(请参阅 SSL 支持 部分);
  • 支持 MySql 文本协议,即支持简单文本查询和文本结果集;
  • 支持 MySql 二进制协议,即支持预编译语句和二进制结果集;
  • 支持多结果集;
  • 支持预编译语句的命名参数(请参阅 命名参数 部分);
  • 可选的每连接预编译语句缓存(请参阅 语句缓存 部分);
  • 缓冲池(请参阅 缓冲池 部分);
  • 支持大于 2^24 的 MySql 数据包;
  • 支持 Unix 套接字和 Windows 命名管道;
  • 支持自定义 LOCAL INFILE 处理程序;
  • 支持 MySql 协议压缩;
  • 支持身份验证插件
    • mysql_native_password - 用于 Mysql v8 之前的版本;
    • caching_sha2_password - 用于 Mysql v8 及更高版本。

安装

将 crate 的所需版本放入您的 dependencies 部分的 Cargo.toml 文件中

[dependencies]
mysql = "*"

示例

use mysql::*;
use mysql::prelude::*;

#[derive(Debug, PartialEq, Eq)]
struct Payment {
    customer_id: i32,
    amount: i32,
    account_name: Option<String>,
}


fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    let url = "mysql://root:password@localhost:3307/db_name";
    # Opts::try_from(url)?;
    # let url = get_opts();
    let pool = Pool::new(url)?;

    let mut conn = pool.get_conn()?;

    // Let's create a table for payments.
    conn.query_drop(
        r"CREATE TEMPORARY TABLE payment (
            customer_id int not null,
            amount int not null,
            account_name text
        )")?;

    let payments = vec![
        Payment { customer_id: 1, amount: 2, account_name: None },
        Payment { customer_id: 3, amount: 4, account_name: Some("foo".into()) },
        Payment { customer_id: 5, amount: 6, account_name: None },
        Payment { customer_id: 7, amount: 8, account_name: None },
        Payment { customer_id: 9, amount: 10, account_name: Some("bar".into()) },
    ];

    // Now let's insert payments to the database
    conn.exec_batch(
        r"INSERT INTO payment (customer_id, amount, account_name)
          VALUES (:customer_id, :amount, :account_name)",
        payments.iter().map(|p| params! {
            "customer_id" => p.customer_id,
            "amount" => p.amount,
            "account_name" => &p.account_name,
        })
    )?;

    // Let's select payments from database. Type inference should do the trick here.
    let selected_payments = conn
        .query_map(
            "SELECT customer_id, amount, account_name from payment",
            |(customer_id, amount, account_name)| {
                Payment { customer_id, amount, account_name }
            },
        )?;

    // Let's make sure, that `payments` equals to `selected_payments`.
    // Mysql gives no guaranties on order of returned rows
    // without `ORDER BY`, so assume we are lucky.
    assert_eq!(payments, selected_payments);
    println!("Yay!");

    Ok(())
}

crate 特性

  • crate 的特性

    • buffer-pool(默认启用) - 启用缓冲池(请参阅 缓冲池 部分)
  • 默认启用的外部特性

    • 对于 flate2 crate(请查阅 flate2 crate 文档了解可用的特性)

      • flate2/zlib(必需) - 默认选择 zlib 后端。
    • 对于 mysql_common crate(请查阅 mysql_common crate 文档了解可用的特性)

      • mysql_common/bigdecimal03 – 默认启用 bigdecimal03
      • mysql_common/rust_decimal – 默认启用 rust_decimal
      • mysql_common/time03 – 默认启用 time03
      • mysql_common/uuid – 默认启用 uuid
      • mysql_common/frunk – 默认启用 frunk

请注意,如果您正在使用 default-features = false,则需要重新启用外部功能。

[dependencies]
# Lets say that we want to use the `rustls-tls` feature:
mysql = { version = "*", default-features = false, features = ["rustls-tls", "buffer-pool"] }
# Previous line disables default mysql features,
# so now we have to choose the flate2 backend (this is necessary),
# as well as the desired set of mysql_common features:
flate2 = { version = "*", default-features = false, features = ["zlib"] }
mysql_common = { version = "*", default-features = false, features = ["bigdecimal03", "time03", "uuid"]}

API 文档

请参阅crate 文档

基本结构

Opts

此结构包含服务器主机名、客户端用户名/密码以及其他控制客户端行为的设置。

基于 URL 的连接字符串

请注意,您可以使用基于 URL 的连接字符串作为 Opts 实例的源。URL 架构必须是 mysql。主机、端口、凭据以及查询参数应按照 RFC 3986 规范提供。

示例

let _ = Opts::from_url("mysql://127.0.0.1/some_db")?;
let _ = Opts::from_url("mysql://[::1]/some_db")?;
let _ = Opts::from_url("mysql://user:pass%[email protected]:3307/some_db?")?;

支持的 URL 参数(有关每个字段的含义,请参阅创建 API 文档中 Opts 结构的文档)

  • prefer_socket: true | false - 定义 Opts 结构中相同字段的值;
  • tcp_keepalive_time_ms: u32 - 定义 Opts 结构中 tcp_keepalive_time 字段的值(以毫秒为单位);
  • tcp_keepalive_probe_interval_secs: u32 - 定义 tcp_keepalive_probe_interval_secs 字段的值;
  • tcp_keepalive_probe_count: u32 - 定义 tcp_keepalive_probe_count 字段的值;
  • tcp_connect_timeout_ms: u64 - 定义 tcp_connect_timeout 字段的值(以毫秒为单位);
  • tcp_user_timeout_ms - 定义 tcp_user_timeout 字段的值(以毫秒为单位);
  • stmt_cache_size: u32 - 定义 Opts 结构中相同字段的值;
  • compress - 定义 Opts 结构中相同字段的值。支持值包括
    • true - 启用默认压缩级别的压缩;
    • fast - 启用“快速”压缩级别的压缩;
    • best - 启用“最佳”压缩级别的压缩;
    • 1..9 - 启用给定压缩级别的压缩。
  • socket - UNIX 上的套接字路径或在 Windows 上的管道名称。

OptsBuilder

它是 Opts 结构的方便构建器。它定义了 Opts 结构字段的设置器。

let opts = OptsBuilder::new()
    .user(Some("foo"))
    .db_name(Some("bar"));
let _ = Conn::new(opts)?;

Conn

此结构表示一个活动的 MySql 连接。它还包含语句缓存和最后一个结果集的元数据。

Conn 的析构函数将优雅地将其从服务器断开连接。

事务

它是 START TRANSACTION 开始并在 COMMITROLLBACK 结束的例程的简单包装。

use mysql::*;
use mysql::prelude::*;

let pool = Pool::new(get_opts())?;
let mut conn = pool.get_conn()?;

let mut tx = conn.start_transaction(TxOpts::default())?;
tx.query_drop("CREATE TEMPORARY TABLE tmp (TEXT a)")?;
tx.exec_drop("INSERT INTO tmp (a) VALUES (?)", ("foo",))?;
let val: Option<String> = tx.query_first("SELECT a from tmp")?;
assert_eq!(val.unwrap(), "foo");
// Note, that transaction will be rolled back implicitly on Drop, if not committed.
tx.rollback();

let val: Option<String> = conn.query_first("SELECT a from tmp")?;
assert_eq!(val, None);

它是连接池的引用,可以克隆并在线程之间共享。

use mysql::*;
use mysql::prelude::*;

use std::thread::spawn;

let pool = Pool::new(get_opts())?;

let handles = (0..4).map(|i| {
    spawn({
        let pool = pool.clone();
        move || {
            let mut conn = pool.get_conn()?;
            conn.exec_first::<u32, _, _>("SELECT ? * 10", (i,))
                .map(Option::unwrap)
        }
    })
});

let result: Result<Vec<u32>> = handles.map(|handle| handle.join().unwrap()).collect();

assert_eq!(result.unwrap(), vec![0, 10, 20, 30]);

语句

实际上,声明(Statement)只是一个标识符与声明元数据的结合,即关于其参数和列的信息。在内部,Statement结构还持有支持命名参数所需的其他数据(见下文)。

use mysql::*;
use mysql::prelude::*;

let pool = Pool::new(get_opts())?;
let mut conn = pool.get_conn()?;

let stmt = conn.prep("DO ?")?;

// The prepared statement will return no columns.
assert!(stmt.columns().is_empty());

// The prepared statement have one parameter.
let param = stmt.params().get(0).unwrap();
assert_eq!(param.schema_str(), "");
assert_eq!(param.table_str(), "");
assert_eq!(param.name_str(), "?");

值(Value)

此枚举表示MySQL单元格的原始值。库提供了通过下文描述的FromValue特质在不同rust类型之间的转换。

FromValue特质

此特质由mysql_common创建重新导出。请参阅其crate文档以获取支持的转换列表。

特质提供两种转换风格

  • from_value(Value) -> T - 方便,但可能会引发恐慌的转换。

    注意,对于Value的任何变体,都存在一个类型,该类型完全覆盖其域,即对于Value的任何变体,都存在T: FromValue,使得from_value永远不会引发恐慌。这意味着,如果您的数据库模式是已知的,那么您可以使用from_value来编写应用程序,而无需担心运行时恐慌。

  • from_value_opt(Value) -> Option<T> - 非恐慌,但不太方便的转换。

    此函数对于在源数据库模式未知的情况下测试转换非常有用。

use mysql::*;
use mysql::prelude::*;

let via_test_protocol: u32 = from_value(Value::Bytes(b"65536".to_vec()));
let via_bin_protocol: u32 = from_value(Value::UInt(65536));
assert_eq!(via_test_protocol, via_bin_protocol);

let unknown_val = // ...

// Maybe it is a float?
let unknown_val = match from_value_opt::<f64>(unknown_val) {
    Ok(float) => {
        println!("A float value: {}", float);
        return Ok(());
    }
    Err(FromValueError(unknown_val)) => unknown_val,
};

// Or a string?
let unknown_val = match from_value_opt::<String>(unknown_val) {
    Ok(string) => {
        println!("A string value: {}", string);
        return Ok(());
    }
    Err(FromValueError(unknown_val)) => unknown_val,
};

// Screw this, I'll simply match on it
match unknown_val {
    val @ Value::NULL => {
        println!("An empty value: {:?}", from_value::<Option<u8>>(val))
    },
    val @ Value::Bytes(..) => {
        // It's non-utf8 bytes, since we already tried to convert it to String
        println!("Bytes: {:?}", from_value::<Vec<u8>>(val))
    }
    val @ Value::Int(..) => {
        println!("A signed integer: {}", from_value::<i64>(val))
    }
    val @ Value::UInt(..) => {
        println!("An unsigned integer: {}", from_value::<u64>(val))
    }
    Value::Float(..) => unreachable!("already tried"),
    val @ Value::Double(..) => {
        println!("A double precision float value: {}", from_value::<f64>(val))
    }
    val @ Value::Date(..) => {
        use time::PrimitiveDateTime;
        println!("A date value: {}", from_value::<PrimitiveDateTime>(val))
    }
    val @ Value::Time(..) => {
        use std::time::Duration;
        println!("A time value: {:?}", from_value::<Duration>(val))
    }
}

行(Row)

在内部,RowValue的向量,它还允许通过列名/偏移量进行索引,并存储行元数据。库提供了通过下文描述的FromRow特质在Row和Rust类型序列之间的转换。

FromRow特质

此特质由mysql_common创建重新导出。请参阅其crate文档以获取支持的转换列表。

此转换基于FromValue,因此具有两种类似的风格

  • from_row(Row) -> T - 与from_value相同,但用于行;
  • from_row_opt(Row) -> Option<T> - 与from_value_opt相同,但用于行。

Queryable特质提供了基于此特质的查询结果行的隐式转换。

use mysql::*;
use mysql::prelude::*;

let mut conn = Conn::new(get_opts())?;

// Single-column row can be converted to a singular value:
let val: Option<String> = conn.query_first("SELECT 'foo'")?;
assert_eq!(val.unwrap(), "foo");

// Example of a mutli-column row conversion to an inferred type:
let row = conn.query_first("SELECT 255, 256")?;
assert_eq!(row, Some((255u8, 256u16)));

// The FromRow trait does not support to-tuple conversion for rows with more than 12 columns,
// but you can do this by hand using row indexing or `Row::take` method:
let row: Row = conn.exec_first("select 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12", ())?.unwrap();
for i in 0..row.len() {
    assert_eq!(row[i], Value::Int(i as i64));
}

// Another way to handle wide rows is to use HList (requires `mysql_common/frunk` feature)
use frunk::{HList, hlist, hlist_pat};
let query = "select 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15";
type RowType = HList!(u8, u16, u32, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8);
let first_three_columns = conn.query_map(query, |row: RowType| {
    // do something with the row (see the `frunk` crate documentation)
    let hlist_pat![c1, c2, c3, ...] = row;
    (c1, c2, c3)
});
assert_eq!(first_three_columns.unwrap(), vec![(0_u8, 1_u16, 2_u32)]);

// Some unknown row
let row: Row = conn.query_first(
    // ...
    # "SELECT 255, Null",
)?.unwrap();

for column in row.columns_ref() {
    // Cells in a row can be indexed by numeric index or by column name
    let column_value = &row[column.name_str().as_ref()];

    println!(
        "Column {} of type {:?} with value {:?}",
        column.name_str(),
        column.column_type(),
        column_value,
    );
}

参数(Params)

表示预处理语句的参数,但此类型不会直接出现在您的代码中,因为二进制协议API会请求T: Into<Params>,其中Into<Params>被实现

  • 对于Into<Value>类型的元组,最多可达12个参数;

    注意:单元素元组需要额外的逗号,例如("foo",)

  • 对于当您的语句包含超过12个参数的情况,可以使用 IntoIterator<Item: Into<Value>>

  • 对于命名参数表示(下面将描述的 params! 宏的值)。

use mysql::*;
use mysql::prelude::*;

let mut conn = Conn::new(get_opts())?;

// Singular tuple requires extra comma:
let row: Option<u8> = conn.exec_first("SELECT ?", (0,))?;
assert_eq!(row.unwrap(), 0);

// More than 12 parameters:
let row: Option<u8> = conn.exec_first(
    "SELECT CONVERT(? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ?, UNSIGNED)",
    (0..16).collect::<Vec<_>>(),
)?;
assert_eq!(row.unwrap(), 120);

注意:请参考 mysql_common crate 文档,了解实现 Into<Value> 的类型列表。

SerializedDeserialized

用于在需要为JSON单元格提供值或需要将JSON单元格解析为结构体时的情况的包装结构。

use mysql::*;
use mysql::prelude::*;

/// Serializable structure.
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Example {
    foo: u32,
}

// Value::from for Serialized will emit json string.
let value = Value::from(Serialized(Example { foo: 42 }));
assert_eq!(value, Value::Bytes(br#"{"foo":42}"#.to_vec()));

// from_value for Deserialized will parse json string.
let structure: Deserialized<Example> = from_value(value);
assert_eq!(structure, Deserialized(Example { foo: 42 }));

QueryResult

这是一个支持多结果集的查询结果行的迭代器。它旨在在需要结果集迭代期间完全控制的情况下使用。对于其他情况,Queryable 提供了一组方法,这些方法将立即消耗第一个结果集并丢弃所有其他内容。

此迭代器是惰性的,因此它不会在您迭代之前从服务器读取结果。MySql协议是严格顺序的,所以 Conn 将在结果完全消耗之前可变借用(请参阅 QueryResult::iter 文档)。

use mysql::*;
use mysql::prelude::*;

let mut conn = Conn::new(get_opts())?;

// This query will emit two result sets.
let mut result = conn.query_iter("SELECT 1, 2; SELECT 3, 3.14;")?;

let mut sets = 0;
while let Some(result_set) = result.iter() {
    sets += 1;

    println!("Result set columns: {:?}", result_set.columns());
    println!(
        "Result set meta: {}, {:?}, {} {}",
        result_set.affected_rows(),
        result_set.last_insert_id(),
        result_set.warnings(),
        result_set.info_str(),
    );

    for row in result_set {
        match sets {
            1 => {
                // First result set will contain two numbers.
                assert_eq!((1_u8, 2_u8), from_row(row?));
            }
            2 => {
                // Second result set will contain a number and a float.
                assert_eq!((3_u8, 3.14), from_row(row?));
            }
            _ => unreachable!(),
        }
    }
}

assert_eq!(sets, 2);

文本协议

MySql文本协议实现在 Queryable::query* 方法集中。当您的查询没有参数时很有用。

注意:服务器将文本协议结果集中的所有值编码为字符串,因此 from_value 转换可能会导致额外的解析成本。

示例

let pool = Pool::new(get_opts())?;
let val = pool.get_conn()?.query_first("SELECT POW(2, 16)")?;

// Text protocol returns bytes even though the result of POW
// is actually a floating point number.
assert_eq!(val, Some(Value::Bytes("65536".as_bytes().to_vec())));

TextQuery 特性。

TextQuery 特性从查询的角度覆盖了 Queryable::query* 方法的集合,即 TextQuery 是在某些合适连接给出时可以执行的操作。合适的连接有

  • &
  • Conn
  • PooledConn
  • &mutConn
  • &mutPooledConn
  • &mut事务

此特性的独特之处在于,您可以放弃连接并因此生成满足 'staticQueryResult

use mysql::*;
use mysql::prelude::*;

fn iter(pool: &Pool) -> Result<impl Iterator<Item=Result<u32>>> {
    let result = "SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3".run(pool)?;
    Ok(result.map(|row| row.map(from_row)))
}

let pool = Pool::new(get_opts())?;

let it = iter(&pool)?;

assert_eq!(it.collect::<Result<Vec<u32>>>()?, vec![1, 2, 3]);

二进制协议和预编译语句。

MySql二进制协议实现在 prepclose 和在 Queryable 特性上定义的 exec* 方法集中。预编译语句是将Rust值传递给MySql服务器的唯一方法。MySql使用 ? 符号作为参数占位符,并且只能在预期单个MySql值的位置使用参数。例如

let pool = Pool::new(get_opts())?;
let val = pool.get_conn()?.exec_first("SELECT POW(?, ?)", (2, 16))?;

assert_eq!(val, Some(Value::Double(65536.0)));

语句

在MySql中,每个预编译语句都属于特定的连接,并且不能在其他连接上执行。这样做会导致错误。驱动程序不会以任何方式将语句与其连接绑定,但可以通过查看包含在 Statement 结构中的连接ID来观察连接ID。

let pool = Pool::new(get_opts())?;

let mut conn_1 = pool.get_conn()?;
let mut conn_2 = pool.get_conn()?;

let stmt_1 = conn_1.prep("SELECT ?")?;

// stmt_1 is for the conn_1, ..
assert!(stmt_1.connection_id() == conn_1.connection_id());
assert!(stmt_1.connection_id() != conn_2.connection_id());

// .. so stmt_1 will execute only on conn_1
assert!(conn_1.exec_drop(&stmt_1, ("foo",)).is_ok());
assert!(conn_2.exec_drop(&stmt_1, ("foo",)).is_err());

语句缓存

Conn 将在客户端管理预编译语句的缓存,因此后续使用相同语句的调用不会导致客户端-服务器往返。每个连接的缓存大小由 stmt_cache_size 字段确定,该字段位于 Opts 结构中。超出此边界的语句将按 LRU 顺序关闭。

如果 stmt_cache_size 为零,则完全禁用语句缓存。

注意事项

  • 禁用语句缓存意味着您必须自己使用 Conn::close 关闭语句,否则它们会耗尽服务器限制/资源;

  • 您应该注意 MySql 服务器的 max_prepared_stmt_count 选项。如果活动连接数乘以 stmt_cache_size 的值大于该值,则在准备另一个语句时可能会收到错误。

命名参数

MySql 本身不支持命名参数,因此在客户端实现。应使用 :name 作为命名参数的占位符语法。命名参数采用以下命名约定

  • 参数名称必须以 _a..z 开始
  • 参数名称可以继续使用 _a..z0..9

命名参数可以在语句中重复,例如 SELECT :foo, :foo 将需要一个重复的单个命名参数 foo,该参数将在执行语句时重复在相应的位置。

应使用 params! 宏来构建执行参数。

注意:在单个语句中不能混合位置参数和命名参数。

示例

let pool = Pool::new(get_opts())?;

let mut conn = pool.get_conn()?;
let stmt = conn.prep("SELECT :foo, :bar, :foo")?;

let foo = 42;

let val_13 = conn.exec_first(&stmt, params! { "foo" => 13, "bar" => foo })?.unwrap();
// Short syntax is available when param name is the same as variable name:
let val_42 = conn.exec_first(&stmt, params! { foo, "bar" => 13 })?.unwrap();

assert_eq!((foo, 13, foo), val_42);
assert_eq!((13, foo, 13), val_13);

缓冲池

Crate 使用全局无锁缓冲池用于 IO 和数据序列化/反序列化,这有助于避免基本场景的分配。您可以使用以下环境变量来控制其特性

  • RUST_MYSQL_BUFFER_POOL_CAP(默认为 128)- 控制池容量。如果池已满,则丢弃的缓冲区将被立即重新分配。将其设置为 0 以在运行时禁用池。

  • RUST_MYSQL_BUFFER_SIZE_CAP(默认为 4MiB)- 控制存储在池中的缓冲区的最大容量。当缓冲区返回到池时,丢弃的缓冲区容量将缩小到该值。

要完全禁用池(例如,您正在使用 jemalloc),请从默认 crate 特性集中删除 buffer-pool 特性(请参阅 Crate Features 部分)。

BinQueryBatchQuery 特性。

BinQueryBatchQuery 特性涵盖了从查询的角度来看 Queryable::exec* 方法的集合,即 BinQuery 是在提供适当的连接时可以执行的操作(请参阅 TextQuery 部分以获取适合连接的列表)。

TextQuery 一样,您可以放弃连接并获取满足 'staticQueryResult

BinQuery 用于预处理语句,预处理语句需要一组参数,因此 BinQueryQueryWithParams 结构体实现,可以通过 WithParams 特性来获取。

示例

use mysql::*;
use mysql::prelude::*;

let pool = Pool::new(get_opts())?;

let result: Option<(u8, u8, u8)> = "SELECT ?, ?, ?"
    .with((1, 2, 3)) // <- WithParams::with will construct an instance of QueryWithParams
    .first(&pool)?;  // <- QueryWithParams is executed on the given pool

assert_eq!(result.unwrap(), (1, 2, 3));

BatchQuery 特性是批量执行语句的辅助工具。它在 QueryWithParams 上实现,其中参数是一个参数迭代器。

use mysql::*;
use mysql::prelude::*;

let pool = Pool::new(get_opts())?;
let mut conn = pool.get_conn()?;

"CREATE TEMPORARY TABLE batch (x INT)".run(&mut conn)?;
"INSERT INTO batch (x) VALUES (?)"
    .with((0..3).map(|x| (x,))) // <- QueryWithParams constructed with an iterator
    .batch(&mut conn)?;         // <- batch execution is preformed here

let result: Vec<u8> = "SELECT x FROM batch".fetch(conn)?;

assert_eq!(result, vec![0, 1, 2]);

Queryable

Queryable 特性定义了 ConnPooledConnTransaction 的通用方法。基本方法包括

  • query_iter - 执行文本查询并获取 QueryResult 的基本方法;
  • prep - 准备语句的基本方法;
  • exec_iter - 执行语句并获取 QueryResult 的基本方法;
  • close - 关闭语句的基本方法;

该特性还定义了一组基于基本方法的辅助方法。这些方法将仅消耗第一个结果集,其他结果集将被丢弃

  • {query|exec} - 将结果收集到 Vec<T: FromRow> 中;
  • {query|exec}_first - 获取第一个 T: FromRow,如果有的话;
  • {query|exec}_map - 将每个 T: FromRow 映射到某个 U
  • {query|exec}_fold - 将 T: FromRow 的集合折叠到单个值;
  • {query|exec}_drop - 立即丢弃结果。

该特性还定义了 exec_batch 函数,它是批量执行语句的辅助工具。

SSL 支持

SSL 支持有两种形式

  1. 基于 native-tls – 这是默认选项,通常无需考虑陷阱(参见 native-tls crate 功能)。

  2. 基于 rustls – 使用 Rust 编写的 TLS 后端。请使用 rustls-tls crate 功能来启用它(参见 Crate Features 部分)。

    关于 rustls 的几点注意事项

    • 如果您尝试通过 IP 地址连接到服务器,它将失败,需要主机名;
    • 它很可能在 Windows 上不起作用,至少在使用默认服务器证书的情况下,这些证书由 MySql 安装程序生成。

变更日志

在此处提供 这里

许可证

根据以下其中之一许可

任选其一。

贡献

除非您明确说明,否则根据Apache-2.0许可协议定义的,您有意提交以包含在工作中的任何贡献,将如上双许可,不附加任何额外条款或条件。

依赖项

~12–17MB
~335K SLoC