I am trying to create custom types around H160 and U256 so that I can use them with Diesel.
Here is the code I used to create the custom types:
use diesel::deserialize::{self, FromSql};
use diesel::pg::Pg;
use diesel::serialize::{self, Output, ToSql};
use diesel::sql_types::*;
use diesel::{backend::Backend, expression::AsExpression};
use ethers::{
prelude::{Address as EthereumAddress, U256 as Eth256, *},
};
use serde::Serialize;
use std::io;
use std::io::Write;
table! {
ethbalances (id) {
id -> Int4,
account -> Varchar,
balance -> Int4,
last_updated -> Text,
holder -> Bool,
}
}
#[derive(AsExpression, FromSqlRow, Debug, Copy, Clone, Serialize)]
#[sql_type = "Varchar"]
pub struct Address {
value: EthereumAddress,
}
// Something to do with incompatible type
// https://stackoverflow.com/questions/62746540/diesel-with-custom-wrapper-types
// https://stackoverflow.com/questions/49092437/how-do-i-implement-queryable-and-insertable-for-custom-field-types-in-diesel
// https://stackoverflow.com/questions/65158596/rust-diesel-orm-queries
// https://stackoverflow.com/questions/47874398/how-do-i-combine-multiple-functions-using-diesel-into-one-through-abstraction
impl ToSql<VarChar, Pg> for Address {
fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
<VarChar as ToSql<VarChar, Pg>>::to_sql(&self.value, out)
}
}
impl<DB: Backend<RawValue = [u8]>> FromSql<Varchar, DB> for Address {
fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
<VarChar as FromSql<VarChar, Pg>>::from_sql(bytes).map(|value| Address { value })
}
}
#[derive(AsExpression, FromSqlRow, Debug, Copy, Clone, Serialize)]
#[sql_type = "Integer"]
pub struct U256 {
value: Eth256,
}
// Something to do with incompatible type
impl ToSql<Integer, Pg> for U256 {
fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
<Integer as ToSql<Integer, Pg>>::to_sql(&self.value, out)
}
}
impl ToSql<diesel::sql_types::Uuid, Pg> for PostId {
fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
<uuid::Uuid as ToSql<diesel::sql_types::Uuid, Pg>>::to_sql(&self.value, out)
}
}
impl<DB: Backend<RawValue = [u8]>> FromSql<Integer, DB> for U256 {
fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
<Integer as FromSql<Integer, Pg>>::from_sql(bytes).map(|value| U256 { value })
}
}
#[derive(Queryable, Insertable, Serialize)]
#[table_name = "ethbalances"]
pub struct ETHRioBalance {
id: i32,
account: Address,
balance: U256,
holder: bool,
}
Here is my up.sql file that generates the table!
macro
CREATE TABLE ethbalances (
id SERIAL PRIMARY KEY,
account NOT NULL,
balance INTEGER NOT NULL,
last_updated TEXT NOT NULL,
holder BOOLEAN NOT NUll
)
Unfortunately , I get the following error
error[E0277]: the trait bound `diesel::sql_types::Text: ToSql<diesel::sql_types::Text, Pg>` is not satisfied
--> src/schema.rs:48:9
|
48 | <VarChar as ToSql<VarChar, Pg>>::to_sql(&self.value, out)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ToSql<diesel::sql_types::Text, Pg>` is not implemented for `diesel::sql_types::Text`
error[E0308]: mismatched types
--> src/schema.rs:48:49
|
48 | <VarChar as ToSql<VarChar, Pg>>::to_sql(&self.value, out)
| ^^^^^^^^^^^ expected struct `diesel::sql_types::Text`, found struct `H160`
|
= note: expected reference `&diesel::sql_types::Text`
found reference `&H160`
error[E0277]: the trait bound `diesel::sql_types::Text: FromSql<diesel::sql_types::Text, Pg>` is not satisfied
--> src/schema.rs:54:29
|
54 | <VarChar as FromSql<VarChar, Pg>>::from_sql(bytes).map(|value| Address { value })
| ^^^^^^^ the trait `FromSql<diesel::sql_types::Text, Pg>` is not implemented for `diesel::sql_types::Text`
error[E0308]: mismatched types
--> src/schema.rs:54:82
|
54 | <VarChar as FromSql<VarChar, Pg>>::from_sql(bytes).map(|value| Address { value })
| ^^^^^ expected struct `H160`, found struct `diesel::sql_types::Text`
error[E0277]: the trait bound `diesel::sql_types::Integer: ToSql<diesel::sql_types::Integer, Pg>` is not satisfied
--> src/schema.rs:66:9
|
66 | <Integer as ToSql<Integer, Pg>>::to_sql(&self.value, out)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ToSql<diesel::sql_types::Integer, Pg>` is not implemented for `diesel::sql_types::Integer`
error[E0308]: mismatched types
--> src/schema.rs:66:49
|
66 | <Integer as ToSql<Integer, Pg>>::to_sql(&self.value, out)
| ^^^^^^^^^^^ expected struct `diesel::sql_types::Integer`, found struct `ethers::prelude::U256`
|
= note: expected reference `&diesel::sql_types:x:Integer`
found reference `ðers::prelude::U256`
error[E0277]: the trait bound `diesel::sql_types::Integer: FromSql<diesel::sql_types::Integer, Pg>` is not satisfied
--> src/schema.rs:78:29
|
78 | <Integer as FromSql<Integer, Pg>>::from_sql(bytes).map(|value| U256 { value })
| ^^^^^^^ the trait `FromSql<diesel::sql_types::Integer, Pg>` is not implemented for `diesel::sql_types::Integer`
error[E0308]: mismatched types
--> src/schema.rs:78:79
|
78 | <Integer as FromSql<Integer, Pg>>::from_sql(bytes).map(|value| U256 { value })
| ^^^^^ expected struct `ethers::prelude::U256`, found struct `diesel::sql_types::Integer`
I will appreciate any guidance on what I am doing wrong.
The easiest way to implement ToSql
and FromSql
for a custom type is to simply convert it into a diesel-known type and defer to its implementation. Here are working samples for ethers::Address
using String
as the intermediate type.
Using Diesel v1.4.8: heed the documentation for ToSql
and FromSql
for backend-specific details.
#[macro_use]
extern crate diesel;
use diesel::deserialize::{self, FromSql};
use diesel::pg::Pg;
use diesel::serialize::{self, Output, ToSql};
use diesel::sql_types::*;
use ethers::prelude::Address as EthereumAddress;
#[derive(AsExpression, FromSqlRow, Debug, Copy, Clone)]
#[sql_type = "VarChar"]
pub struct Address {
value: EthereumAddress,
}
impl ToSql<VarChar, Pg> for Address {
fn to_sql<W: std::io::Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
<String as ToSql<VarChar, Pg>>::to_sql(&self.value.to_string(), out)
}
}
impl FromSql<Varchar, Pg> for Address {
fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
<String as FromSql<VarChar, Pg>>::from_sql(bytes).map(|s| Address { value: s.parse().unwrap() })
}
}
Using Diesel v2.0.0: heed the documentation for ToSql
and FromSql
for backend-specific details.
use diesel::backend::RawValue;
use diesel::deserialize::{self, FromSql, FromSqlRow};
use diesel::expression::AsExpression;
use diesel::pg::Pg;
use diesel::serialize::{self, Output, ToSql};
use diesel::sql_types::*;
use ethers::prelude::Address as EthereumAddress;
#[derive(AsExpression, FromSqlRow, Debug, Copy, Clone)]
#[diesel(sql_type = VarChar)]
pub struct Address {
value: EthereumAddress,
}
impl ToSql<VarChar, Pg> for Address {
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
<String as ToSql<VarChar, Pg>>::to_sql(&self.value.to_string(), &mut out.reborrow())
}
}
impl FromSql<Varchar, Pg> for Address {
fn from_sql(bytes: RawValue<Pg>) -> deserialize::Result<Self> {
<String as FromSql<VarChar, Pg>>::from_sql(bytes).map(|s| Address { value: s.parse().unwrap() })
}
}
You can checkout the Diesel 2.0 migration guide for more details on changes and upgrading.