Понимание ошибки привязки признаков в Diesel

Я хочу написать функцию, которая будет вставлять тип в базу данных, где параметр подключения к базе данных является универсальным, чтобы он мог работать на нескольких бэкэндах.

Я придумал следующую функцию для вставки объекта с использованием общего соединения:

pub fn create_label<C>(connection: &C, label: &model::Label)
where
    C: Connection,
    C::Backend: diesel::backend::Backend,
    C::Backend: diesel::backend::SupportsDefaultKeyword,
{
    diesel::insert(&label)
        .into(schema::label::table)
        .execute(connection);
}

Если я не включу ограничение SupportsDefaultKeyword, функция не скомпилируется. При вызове его с SqliteConnection в качестве параметра подключения я получаю следующую ошибку:

database::create_label(&db_conn, &label); ^^^^^^^^^^^^^^^^^^^^^^ the trait 'diesel::backend::SupportsDefaultKeyword' is not implemented for 'diesel::sqlite::Sqlite'

Это означало бы, что вставка данных с SqliteConnection не работает. Очевидно, что это не так, и, кроме того, изменение create_label таким образом, что требуется SqliteConnection, работает нормально.

pub fn create_label(connection: &SqliteConnection, label: &model::Label) {
    diesel::insert(&label)
        .into(schema::label::table)
        .execute(connection);
}

Почему общая функция требует ограничения SupportsDefaultKeyword, а функция, принимающая SqliteConnection, нет?

Вот минимальный пример, иллюстрирующий проблему. Согласно комментариям, строка 60 main.rs не будет компилироваться с ошибкой сверху, тогда как строка 61 компилируется:

#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;

mod schema {
    table! {
        labels {
            id -> Integer,
            name -> VarChar,
        }
    }
}

mod model {
    use schema::labels;

    #[derive(Debug, Identifiable, Insertable)]
    #[table_name = "labels"]
    pub struct Label {
        pub id: i32,
        pub name: String,
    }
}

use diesel::ExecuteDsl;
use diesel::Connection;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;

pub fn create_label<C>(connection: &C, label: &model::Label)
where
    C: Connection,
    C::Backend: diesel::backend::Backend,
    C::Backend: diesel::backend::SupportsDefaultKeyword,
{
    diesel::insert(label)
        .into(schema::labels::table)
        .execute(connection)
        .expect("nope");
}

pub fn create_label_sqlite(connection: &SqliteConnection, label: &model::Label) {
    diesel::insert(label)
        .into(schema::labels::table)
        .execute(connection)
        .expect("nope");
}

pub fn establish_connection() -> SqliteConnection {
    let url = "test.db";
    SqliteConnection::establish(&url).expect(&format!("Error connecting to {}", url))
}

fn main() {
    let label = model::Label {
        id: 1,
        name: String::from("test"),
    };
    let conn = establish_connection();

    create_label(&conn, &label); /* Does not compile */
    create_label_sqlite(&conn, &label); /*Compiles */
}
[dependencies]
diesel = { version = "0.16.0", features = ["sqlite"] }
diesel_codegen = "0.16.0"

person Tim    schedule 19.11.2017    source источник


Ответы (1)


Функция Diesel execute имеет несколько конкретные реализации. Здесь важны два:

impl<'a, T, U, Op, Ret, Conn, DB> ExecuteDsl<Conn, DB> for BatchInsertStatement<T, &'a [U], Op, Ret> 
where
    Conn: Connection<Backend = DB>,
    DB: Backend + SupportsDefaultKeyword,
    InsertStatement<T, &'a [U], Op, Ret>: ExecuteDsl<Conn>, 
impl<'a, T, U, Op, Ret> ExecuteDsl<SqliteConnection> for BatchInsertStatement<T, &'a [U], Op, Ret> 
where
    InsertStatement<T, &'a U, Op, Ret>: ExecuteDsl<SqliteConnection>,
    T: Copy,
    Op: Copy,
    Ret: Copy, 

Как видно из этих двух, реализация SQLite имеет особый характер. Я недостаточно разбираюсь в деталях Diesel, чтобы понять, почему, но я предполагаю, что в SQLite отсутствует ключевое слово по умолчанию.

Вместо этого вы можете переформулировать требования для любого соединения, которое работает с этим конкретным оператором:

use diesel::query_builder::insert_statement::InsertStatement;

pub fn create_label<C>(connection: &C, label: &model::Label)
where
    C: Connection,
    for<'a> InsertStatement<schema::labels::table, &'a model::Label>: ExecuteDsl<C>,
{
    diesel::insert(label)
        .into(schema::labels::table)
        .execute(connection)
        .expect("nope");
}
person Shepmaster    schedule 20.11.2017