sqlite - 使用rust 柴油 : SQLite INSERT RETURNING multiple ids

标签 sqlite rust rust-diesel sql-returning

我正在尝试使用最新的 SQLite 实现一个相当简单的工作流程:插入带有空 ID 列的行,以便自动生成它,并从 INSERT 返回这些自动生成的 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])                               // <--- Here!
        .returning(test_table::columns::internal_id)
        .get_results(&mut conn)
        .unwrap();

我做错了什么?

最佳答案

这是diesel如何处理插入语句的一个不幸的边缘情况。值得注意的是,如果结构中存在 None 值,diesel 支持插入默认值。在 postgresqldiesel 上使用 DEFAULT value 关键字。 Sqlite不支持该关键字,因此diesel需要模拟它。如果值为 None,则只需跳过相关行即可完成此操作,但这仅适用于单行。这就是为什么单个插入查询可以工作,而批量插入查询无法编译的原因。 现在这里甚至还有另一件事: .execute(&conn) (即不返回 id)也适用于批量插入。这是因为diesel通过在内部一一插入所有元素来模拟批量插入。不幸的是,返回变体是不可能的,因为这会在柴油机内部特征设置中遇到冲突的特征实现。

可以说,这些都没有在柴油文档中得到很好的记录,因此提交一份改进相关文档的 PR 肯定会很棒。

关于sqlite - 使用rust 柴油 : SQLite INSERT RETURNING multiple ids,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77487976/

相关文章:

entity-framework - EF7 RC2 LINQ选择不包括新添加的记录

c++ - 有没有更清洁的方法来做到这一点? (在 Qt C++ 中准备 SQL 查询)

windows - 由于 SSL 连接错误,Cargo 无法下载文件

rust - Cargo init 像 cargo new 一样创建新目录

mysql - 如何从帮助程序方法动态返回Diesel相等表达式?

java - sqlite 安卓 : table not found

sql - 在 sqlite SQL 语句中结合 order by 子句使用限制

pattern-matching - 从 Options 和 Results 中移除 unwrap 调用而不引入双重缩进的匹配语句

rust - 如何为Struct实现 `std::iter::FromIterator<_>'?

rust - 了解 Diesel 中的特征边界误差