You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Rust Diesel向SQLite批量插入并返回自增ID时类型检查失败

问题描述

尝试使用最新版SQLite实现以下流程:插入ID列为空的行以自动生成自增ID,并通过INSERT语句的RETURNING子句返回这些自动生成的ID。SQLite 3.35+已支持RETURNING子句,Diesel也可通过returning_clauses_for_sqlite_3_35特性启用该支持,理论上该流程可行。

编写的代码如下:

Cargo.toml

[dependencies]
diesel = { version = "2.1.3", features = ["sqlite", "returning_clauses_for_sqlite_3_35"] }

main.rs

use diesel::prelude::*;
use diesel::sql_query;

table! {
    test_table (internal_id) {
        internal_id -> BigInt,
        content -> Text,
    }
}

#[derive(Debug, PartialEq, Clone, Identifiable, Selectable, Queryable, Insertable)]
#[diesel(primary_key(internal_id))]
#[diesel(table_name = test_table)]
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
pub struct TestTableRow {
    #[diesel(deserialize_as = i64)]
    pub internal_id: Option<i64>,
    pub content: String,
}

fn main() {
    let rows = vec![
        TestTableRow { internal_id: None, content: "Hello!".to_owned() },
        TestTableRow { internal_id: None, content: "World!".to_owned() },
    ];

    let mut conn = SqliteConnection::establish(":memory:").unwrap();
    sql_query(r"
        CREATE TABLE test_table(
            internal_id INTEGER PRIMARY KEY AUTOINCREMENT,
            content     TEXT NOT NULL
        ) STRICT;
    ").execute(&mut conn).unwrap();
    let internal_ids: Vec<i64> = diesel::insert_into(test_table::table)
        .values(&rows)
        .returning(test_table::columns::internal_id)
        .get_results(&mut conn)
        .unwrap();

    assert_eq!(internal_ids, vec![1, 2]);
}

但这段代码无法通过类型检查,错误信息如下:

error[E0277]: the trait bound `BatchInsert<Vec<diesel::query_builder::insert_statement::ValuesClause<(DefaultableColumnInsertValue<ColumnInsertValue<columns::internal_id, expression::bound::Bound<diesel::sql_types::BigInt, &i64>>>, DefaultableColumnInsertValue<ColumnInsertValue<columns::content, expression::bound::Bound<diesel::sql_types::Text, &String>>>, test_table::table>>, test_table::table, (), false>: QueryFragment<Sqlite, sqlite::backend::SqliteBatchInsert>` is not satisfied
    --> src/main.rs:37:22
     |
37   |         .get_results(&mut conn)
     |          ----------- ^^^^^^^^^ the trait `QueryFragment<Sqlite, sqlite::backend::SqliteBatchInsert>` is not implemented for `BatchInsert<Vec<diesel::query_builder::insert_statement::ValuesClause<(DefaultableColumnInsertValue<ColumnInsertValue<columns::internal_id, expression::bound::Bound<diesel::sql_types::BigInt, &i64>>>, DefaultableColumnInsertValue<ColumnInsertValue<columns::content, expression::bound::Bound<diesel::sql_types::Text, &String>>>, test_table::table>>, test_table::table, (), false>`
     |          |
     |          required by a bound introduced by this call
     |
     = help: the following other types implement trait `QueryFragment<DB, SP>`:
               <BatchInsert<Vec<diesel::query_builder::insert_statement::ValuesClause<V, Tab>>, Tab, QId, HAS_STATIC_QUERY_ID> as QueryFragment<DB, PostgresLikeBatchInsertSupport>>
               <BatchInsert<V, Tab, QId, HAS_STATIC_QUERY_ID> as QueryFragment<DB>>
     = note: required for `BatchInsert<Vec<ValuesClause<(DefaultableColumnInsertValue<...>, ...), ...>>, ..., ..., false>` to implement `QueryFragment<Sqlite>`
     = note: the full type name has been written to '/Users/fs/code/rust-diesel-issue/target/debug/deps/rust_diesel_issue-c1c37efb3df7ba71.long-type-1487086329238728488.txt'
     = note: 1 redundant requirement hidden
     = note: required for `InsertStatement<table, BatchInsert<Vec<ValuesClause<(..., ...), ...>>, ..., ..., false>, ..., ...>` to implement `QueryFragment<Sqlite>`
     = note: the full type name has been written to '/Users/fs/code/rust-diesel-issue/target/debug/deps/rust_diesel_issue-c1c37efb3df7ba71.long-type-8243152461613863375.txt'
     = note: required for `InsertStatement<table, BatchInsert<Vec<ValuesClause<(..., ...), ...>>, ..., ..., false>, ..., ...>` to implement `LoadQuery<'_, diesel::SqliteConnection, _>`
     = note: the full type name has been written to '/Users/fs/code/rust-diesel-issue/target/debug/deps/rust_diesel_issue-c1c37efb3df7ba71.long-type-8243152461613863375.txt'
note: required by a bound in `get_results`
    --> /Users/fs/.cargo/registry/src/index.crates.io-6f17d22bba15001f/diesel-2.1.4/src/query_dsl/mod.rs:1739:15
     |
1737 |     fn get_results<'query, U>(self, conn: &mut Conn) -> QueryResult<Vec<U>>
     |        ----------- required by a bound in this associated function
1738 |     where
1739 |         Self: LoadQuery<'query, Conn, U>,
     |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::get_results`

仅插入单行数据时,代码可正常编译运行:

let internal_ids: Vec<i64> = diesel::insert_into(test_table::table)
    .values(&rows[0])                               // <--- 此处修改!
    .returning(test_table::columns::internal_id)
    .get_results(&mut conn)
    .unwrap();
原因与解决方法

问题原因

Diesel的BatchInsert组件目前仅为PostgreSQL等兼容Postgres批量插入语法的数据库实现了QueryFragment<DB, PostgresLikeBatchInsertSupport> trait,对SQLite的批量插入+RETURNING子句组合没有提供支持,因此批量插入时会触发类型检查错误。而单行插入不受此限制,因为Diesel对SQLite的单行INSERT+RETURNING支持是完整的。

解决方案

有两种可行的解决方式:

方式一:循环插入单行

对每个待插入的行单独执行插入操作,收集返回的自增ID。这种方式实现简单,无需额外处理参数,适合数据量不大的场景:

let mut internal_ids = Vec::new();
for row in rows {
    let id: i64 = diesel::insert_into(test_table::table)
        .values(row)
        .returning(test_table::columns::internal_id)
        .get_result(&mut conn)
        .unwrap();
    internal_ids.push(id);
}

方式二:使用原始SQL执行批量插入

直接构造支持RETURNING子句的批量INSERT原始SQL,通过Diesel的sql_query执行并绑定参数,既保证性能又避免SQL注入风险:

// 构造批量插入的占位符
let placeholders = rows.iter()
    .map(|_| "(NULL, ?)")
    .collect::<Vec<_>>()
    .join(", ");
// 拼接完整SQL语句
let sql = format!(
    "INSERT INTO test_table (internal_id, content) VALUES {} RETURNING internal_id",
    placeholders
);
// 提取需要绑定的content参数
let contents: Vec<&String> = rows.iter().map(|r| &r.content).collect();
// 执行查询并获取返回的ID
let internal_ids: Vec<i64> = sql_query(&sql)
    .bind::<Text, _>(contents)
    .get_results(&mut conn)
    .unwrap();

内容的提问来源于stack exchange,提问作者Alex Abdugafarov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 13:34:51