I am trying to get the average value of a column using rust diesel but am stuck with the type error.
Error:
the trait bound `f64: FromSql<Numeric, _>` is not satisfied
the following other types implement trait `FromSql<A, DB>`:
<f32 as FromSql<diesel::sql_types::Float, DB>>
<f64 as FromSql<Double, DB>>
<i16 as FromSql<SmallInt, DB>>
<i32 as FromSql<Integer, DB>>
<i64 as FromSql<BigInt, DB>>
<u32 as FromSql<Oid, Pg>>
required because of the requirements on the impl of `diesel::Queryable<Numeric, _>` for `f64`
Code:
let new_avg: Option<f64> = fruits
.select(avg(weight))
.filter(fruit_name.eq(&fruit_name))
.get_result::<Option<f64>>(&self.postgres.get().unwrap())
.unwrap();
The problem seems to be that you are trying to cast type Numeric from postgres to f64 in Rust, which does not have implementation.
I tried to reproduce your case so I created table like so:
CREATE TABLE fruits (
id SERIAL PRIMARY KEY,
value NUMERIC NOT NULL
)
for which diesel generated for me this in schemas:
table! {
fruits (id) {
id -> Int4,
value -> Numeric,
}
}
and in models I created Fruit:
#[derive(Queryable, Debug)]
pub struct Fruit {
pub id: i32,
pub value: f64
}
Right now when I try to run this:
let results = fruits.load::<Fruit>(&pg_connection).expect("");
I'm getting the same error as you which we can solve in few ways.
If you want to keep type f64 in Rust then you can change in table creation that value should have type DOUBLE PRECISION which after running diesel migration will generate Float8 type in schema which has it's implementation mentioned in error:
= help: the following implementations were found:
<f64 as FromSql<Double, DB>>
If you want to keep type Numeric in postgres table you can try using bigecimal::BigDecimal or diesel::pg::data_types::PgNumeric as type of value in Fruit struct since there is also implementation for casting Numeric to PgNumeric.
If you want to keep both you probably have to implement it on you own
Related
I have a seq 'user_tfa_info_seq' which I want to use in 'user_tfa_info' table in Gorm Model.
I used the following structure, but it does not work.
type UserTfaInfo struct{
ID uint `gorm:"primary_key;type:bigint(20) not null" sql:"nextval('user_tfa_info_seq')"`
}
I'm trying to use jOOQ's metadata API, and most columns behave the way I'd expect, but enum columns seem to be missing type and nullability information somehow.
For example, if I have a schema defined as:
CREATE TYPE public.my_enum AS ENUM (
'foo',
'bar',
'baz'
);
CREATE TABLE public.my_table (
id bigint NOT NULL,
created_at timestamp with time zone DEFAULT now() NOT NULL,
name text,
my_enum_column public.my_enum NOT NULL,
);
The following test passes:
// this is Kotlin, but hopefully pretty easy to decipher
test("something fishy going on here") {
val jooq = DSL.using(myDataSource, SQLDialect.POSTGRES)
val myTable = jooq.meta().tables.find { it.name == "my_table" }!!
// This looks right...
val createdAt = myTable.field("created_at")!!
createdAt.dataType.nullability() shouldBe Nullability.NOT_NULL
createdAt.dataType.typeName shouldBe "timestamp with time zone"
// ...but none of this seems right
val myEnumField = myTable.field("my_enum_column")!!
myEnumField.dataType.typeName shouldBe "other"
myEnumField.dataType.nullability() shouldBe Nullability.DEFAULT
myEnumField.dataType.castTypeName shouldBe "other"
myEnumField.type shouldBe Any::class.java
}
It's telling me that enum columns have Nullability.DEFAULT regardless of whether they are null or not null. For other types, Field.dataType.nullability will vary depending on whether the column is null or not null, as expected.
For any enum column, the type is Object (Any in Kotlin), and the dataType.typeName is "other". For non-enum columns, dataType.typeName gives me the correct SQL for the type.
I'm also using the jOOQ code generator, and it generates the correct types for enum columns. That is, it creates an enum class and uses that as the type for the corresponding fields, which are marked as not-nullable. The generated code for this field looks something like (reformatted to avoid long lines):
public final TableField<MyTableRecord, MyEnum> MY_ENUM_COLUMN =
createField(
DSL.name("my_enum_column"),
SQLDataType.VARCHAR
.nullable(false)
.asEnumDataType(com.example.schema.enums.MyEnum.class),
this,
""
)
So it appears that jOOQ's code generator has the type information, but how can I access the type information via the metadata API?
I'm using postgres:11-alpine and org.jooq:jooq:3.14.11.
Update 1
I tried testing this with org.jooq:jooq:3.16.10 and org.jooq:jooq:3.17.4. They seem to fix the nullability issue, but the datatype is still "other", and the type is still Object. So it appears the nullability issue was a bug in jOOQ. I'll file an issue about the type+datatype.
Update 2
This is looking like it may be a bug, so I've filed an issue.
I am store some tags as a jsonb datatype in PostgreSQL 13, this is the table DDL:
-- Drop table
-- DROP TABLE public.test;
CREATE TABLE public.test (
id int8 NOT NULL GENERATED ALWAYS AS IDENTITY,
tags jsonb NOT NULL,
CONSTRAINT test_pkey PRIMARY KEY (id)
);
now I want to do a query using jsonb in rust diesel diesel = { version = "1.4.7", features = ["postgres","serde_json"] }, this is the rust main.rs:
#[macro_use]
extern crate diesel;
use diesel::pg::types::sql_types::Jsonb;
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
use rocket::serde::Deserialize;
use rocket::serde::Serialize;
use rust_wheel::config::db::config::establish_connection;
pub mod model;
fn main() {
use crate::model::diesel::dict::dict_schema::test::dsl::*;
let connection = rust_wheel::config::db::config::establish_connection();
let predicate = crate::model::diesel::dict::dict_schema::test::tags.eq(serde_json::from_value("value".parse().unwrap()));
let db_admin_user = test.filter(&predicate)
.limit(1)
.load::<Test>(&connection)
.expect("query test failed");
}
#[derive(Queryable,Debug,Serialize,Deserialize,Default)]
pub struct Test {
pub id: i64,
pub tags: serde_json::Value,
}
and this is the schema file:
table! {
test (id) {
id -> Int8,
tags -> Jsonb,
}
}
when I compile the code, shows error like this:
error[E0277]: the trait bound `Result<_, serde_json::Error>: diesel::Expression` is not satisfied
--> src/main.rs:15:76
|
15 | let predicate = crate::model::diesel::dict::dict_schema::test::tags.eq(serde_json::from_value("value".parse().unwrap()));
| -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `diesel::Expression` is not implemented for `Result<_, serde_json::Error>`
| |
| required by a bound introduced by this call
|
= note: required because of the requirements on the impl of `AsExpression<diesel::sql_types::Jsonb>` for `Result<_, serde_json::Error>`
what should I do to query the record using jsonb data type?
I have the following simple (cut down for brevity) Postgres table:
create table users(
id uuid NOT NULL,
year_of_birth smallint NOT NULL
);
Within a test I have seeded data.
When I run the following SQL update to correct a year_of_birth the error implies that I'm not providing the necessary UUID correctly.
The Doobie SQL I run is:
val id: String = "6ee7a37c-6f58-4c14-a66c-c17083adff81"
val correctYear: Int = 1980
sql"update users set year_of_birth = $correctYear where id = $id".update.run
I have tried both with and without quotes around the given $id e.g. the other version is:
sql"update users set year_of_birth = $correctYear where id = '$id'".update.run
The error upon running the above is:
org.postgresql.util.PSQLException: ERROR: operator does not exist: uuid = character varying
Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
Both comments provided viable solutions.
a_horse_with_no_name suggested the use of cast which works though the SQL becomes no so nice when compared to the other solution.
AminMal suggested the use of available Doobie implicits which can handle a UUID within SQL and thus avoid a cast.
So I changed my code to the following:
import doobie.postgres.implicits._
val id: UUID = UUID.fromString("6ee7a37c-6f58-4c14-a66c-c17083adff81")
sql"update users set year_of_birth = $correctYear where id = $id".update.run
So I'd like to mark this question as resolved because of the comment provided by AminMal
I'm currently using the pg-promise library to insert multiple values into a database in the format:
const cs = new pgp.helpers.ColumnSet(['booking_id', {name:'timeslot', cast:'timestamp'}], {table: 'booking'});
// data input values:
const values = [];
bookings.forEach(slot => {
values.push({booking_id: booking_id, timeslot: slot});
});
Where I need timeslot to be a timestamp. However it comes into the API as value like
1515586500.0
Using the above cast property my query gets resolved like so
insert into "booking"("booking_id","timeslot") values(1,'1515586500.0'::timestamp)
however this throws an error of cannot cast type numeric to timestamp without time zone
If I use the to_timestamp function however this works how I need it to e.g
insert into "booking"("booking_id","timeslot") values(1,to_timestamp('1515586500.0'));
Is there any way I can get pg-promise to use to_timestamp rather than the ::timestamp notation?
Change the column definition to this one:
{
name: 'timeslot',
mod: ':raw',
init: c => pgp.as.format('to_timestamp($1)', c.value)
}
or
{
name: 'timeslot',
mod: ':raw',
init: c => pgp.as.format('to_timestamp(${value})', c)
}
...as per the Column type documentation.
Or you can use Custom Type Formatting on the type, to self-format automatically.
Also, you do not need to remap values to suit the ColumnSet object, you use ColumnSet object to fit the data instead. So if the value for column timeslot is in property slot, you just use prop: 'slot' within your column definition to change where the value is coming from.