7 个版本

0.7.4 2024年3月15日
0.7.3 2024年1月2日
0.7.2 2023年11月20日
0.7.1-alpha-42023年10月26日
0.7.1-alpha-32023年9月12日

#1487 in 数据库接口

MIT/Apache

320KB
7.5K SLoC

Crates.io Docs.rs

sqlx-exasol

用于与 Rust sqlx 框架一起使用的 Exasol 数据库驱动程序,基于 Exasol Websocket API
Py-Exasol 启发,并基于(现已存档的)rust-exasol 同步驱动程序。

MSRV: 1.74

注意

由于软件包的版本与它所基于的 sqlx 版本相似,因此管理依赖项更加简单。

请使用 sqlxsqlx-exasolCargo.toml 中的固定版本,以避免问题,例如

sqlx = "=0.7.4"
sqlx-exasol = "=0.7.4"

软件包功能标志

  • etl - 启用使用 ETL 作业(无需 TLS 加密)。
  • etl_native_tls - 启用 etl 功能并通过 native-tls 添加 TLS 加密1
  • etl_rustls - 启用 etl 功能并通过 rustls 添加 TLS 加密1
  • compression - 启用压缩支持(适用于连接和 ETL 作业)
  • uuid - 启用对 uuid 软件包的支持
  • chrono - 启用对 chrono 软件包类型的支持
  • rust_decimal - 启用对 rust_decimal 类型的支持
  • migrate - 启用使用迁移和测试(就像在其他 sqlx 驱动程序中一样)。

与原生 sqlx 驱动程序的比较

由于驱动程序通过 sqlx 使用,并且它实现了那里的接口,因此它可以执行 sqlx 随附的所有驱动程序的功能,但有几点需要注意

  • 限制

    • 没有编译时查询检查支持2
    • 没有 sqlx-cli 支持2
    • 没有锁定迁移支持3
    • 没有列可空性检查4
    • 除了迁移之外,每个语句只允许一个查询(包括在 fixtures 中)5
  • 新增功能

    • 由于 Exasol 数据库的列式特性,查询中支持类似数组的参数绑定
    • 通过 HTTP 传输以 CSV 格式执行高效且可并行化的 ETL 导入/导出作业

连接字符串

连接字符串应为一个具有 exa:// 方案的 URL,例如:exa://sys:exasol@localhost:8563

示例

使用驱动进行常规数据库交互

use std::env;
use sqlx_exasol::*;

let pool = ExaPool::connect(&env::var("DATABASE_URL").unwrap()).await?;
let mut con = pool.acquire().await?;

sqlx::query("CREATE SCHEMA RUST_DOC_TEST")
    .execute(&mut *con)
    .await?;

类似数组的参数绑定,还包括 ExaIter 适配器。需要注意的是,参数集必须长度相同,否则会抛出错误

use std::{collections::HashSet, env};
use sqlx_exasol::*;

let pool = ExaPool::connect(&env::var("DATABASE_URL").unwrap()).await?;
let mut con = pool.acquire().await?;

let params1 = vec![1, 2, 3];
let params2 = HashSet::from([1, 2, 3]);

sqlx::query("INSERT INTO MY_TABLE VALUES (?, ?)")
    .bind(&params1)
    .bind(ExaIter::from(&params2))
    .execute(&mut *con)
    .await?;

导出 - 导入 ETL 数据管道。

use std::env;
use futures_util::{
    future::{try_join, try_join3, try_join_all},
    AsyncReadExt, AsyncWriteExt, TryFutureExt,
};
use sqlx_exasol::{etl::*, *};

async fn pipe(mut reader: ExaExport, mut writer: ExaImport) -> anyhow::Result<()> {
    let mut buf = vec![0; 5120].into_boxed_slice();
    let mut read = 1;

    while read > 0 {
        // Readers return EOF when there's no more data.
        read = reader.read(&mut buf).await?;
        // Write data to Exasol
        writer.write_all(&buf[..read]).await?;
    }

    // Writes, unlike readers, MUST be closed to signal we won't send more data to Exasol
    writer.close().await?;
    Ok(())
}

let pool = ExaPool::connect(&env::var("DATABASE_URL").unwrap()).await?;
let mut con1 = pool.acquire().await?;
let mut con2 = pool.acquire().await?;

// Build EXPORT job
let (export_fut, readers) = ExportBuilder::new(ExportSource::Table("TEST_ETL"))
    .build(&mut con1)
    .await?;

// Build IMPORT job
let (import_fut, writers) = ImportBuilder::new("TEST_ETL").build(&mut con2).await?;

// Use readers and writers in some futures
let transport_futs = std::iter::zip(readers, writers).map(|(r, w)| pipe(r, w));

// Execute the EXPORT and IMPORT query futures along with the worker futures
let (export_res, import_res, _) = try_join3(
    export_fut.map_err(From::from),
    import_fut.map_err(From::from),
    try_join_all(transport_futs),
)
.await?;

assert_eq!(export_res.rows_affected(), import_res.rows_affected());

许可证

根据您的选择,许可方式为以下之一

贡献

除非明确说明,否则对本存储库的贡献将被视为 MIT 和 Apache 2.0 双许可。
遇到的问题/错误可以在这里打开 这里

脚注

1:遗憾的是,没有自动根据包的依赖项功能标志选择功能标志的方法,因此必须手动选择 TLS 后端。虽然您可以使用,例如 native-tlssqlx 以及 rustls 与 Exasol ETL 作业,但最好避免编译两个不同的 TLS 后端。因此,请考虑以一致的方式选择 sqlxsqlx-exasol 功能标志。

2:为编译时查询检查和 sqlx-cli 工具提供动力的 sqlx API 不可公开。即使它是公开的,也由于代码中处理编译时驱动决策逻辑的部分是硬编码的,因此包含在 sqlx 中的驱动也是硬编码的。
根据我所了解的,主要问题是目前在 Rust 中没有简单的方法来定义插件系统,因此才会进行硬编码。

3:Exasol 没有建议或数据库锁,简单的、非嵌套的事务显然不足以定义一种机制,以便并发迁移不会冲突。当迁移顺序执行或不对相同的数据库对象进行操作时,这不会造成问题。

4:Exasol 不提供有关列是否可空的任何信息,因此驱动程序必须在实际尝试之前才能隐式决定某个数据库列是否可以接受 NULL 值。

5:我甚至不知道这一点(因为我从未想过要这样做),但是 sqlx 允许在一个语句中运行多个查询。由于基于该驱动程序的 WebSocket API 的限制,sqlx-exasol 只能一次运行一个查询。
这仅在迁移中通过一种相当有限、复杂且可能昂贵的解决方案来绕过,该解决方案尝试通过 ; 来分割查询,这根本不适用于运行时查询。

依赖关系

~13–27MB
~463K SLoC