Rust Diesel:SQLite 插入返回多个 id

问题描述 投票:0回答:1

我正在尝试使用最新的 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();

我做错了什么?

sqlite rust rust-diesel sql-returning
1个回答
0
投票

这是柴油如何处理插入语句的一个不幸的边缘情况。值得注意的是,如果结构中存在

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

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

© www.soinside.com 2019 - 2024. All rights reserved.