-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Postgres enums and query_as! #1004
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I was running into something similar. I found that this worked. sqlx::query_as!(ServiceStatus, r#"SELECT ip, state as "state: _" FROM service_statuses"#)
.fetch_all(&pool)
.await
.expect("Failed to execute query"); or you can make the type explicit. sqlx::query_as!(ServiceStatus, r#"SELECT ip, state as "state: ServiceState" FROM service_statuses"#)
.fetch_all(&pool)
.await
.expect("Failed to execute query"); https://docs.rs/sqlx/0.4.2/sqlx/macro.query.html#force-a-differentcustom-type |
@ZacharyLeBlanc, thanks, this works! Do you happen to know whether this is runtime or compile-time check? |
I just picked up sqlx for a side project so I'm no expert but reading the docs it looks like this would be a runtime error. If there is a better solution I would love to hear it as I was running into this as well. I just found that this worked for me. |
This disables the compile-time type check for the specific column. Checking custom types for compatibility at compile time is not currently supported. |
I'm also interested in how one can insert UDT with an update on the conflict, like so: INSERT INTO service_statuses (ip, state) VALUES ($1, $2)
ON CONFLICT (ip) DO UPDATE SET state = $1 The compiler says that I need to cast value, but I'm not sure how to do it: |
INSERT INTO service_statuses (ip, state) VALUES ($1, $2)
ON CONFLICT (ip) DO UPDATE SET state = $1 Are you sure that's right? You are setting |
Sorry, it's a typo. Actual message: |
Ah you need https://docs.rs/sqlx/0.4.2/sqlx/macro.query.html#type-overrides-bind-parameters-postgres-only query!(
r#"
INSERT INTO service_statuses (ip, state) VALUES ($1, $2)
ON CONFLICT (ip) DO UPDATE SET state = $1
"#,
one as _,
two as _,
) It is a similar concept but in the inverse direction for parameters. |
Yep, that works, appreciate it! Is there any issue about compatibility for UDT at compile time I can follow? |
I'm getting this error from |
I'm running into the same issue except with an
My query code: sqlx::query!(
r#"
INSERT INTO tweets
(id, created_at,
tweet_id, tweet_created_at, tweet_text, tweet_url,
replied_to_tweet_id, quoted_tweet_id, tweet_class,
like_count, quote_count, reply_count, retweet_count, total_retweet_count, popularity_count,
user_id)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
"#,
Uuid::new_v4(),
Utc::now(),
tweet_id,
tweet_created_at,
tweet["text"].as_str(),
tweet_url,
replied_to_tweet_id,
quoted_tweet_id,
tweet_class as TweetClass, // also tried "as _" - neither works
tweet_metrics.like_count,
tweet_metrics.quote_count,
tweet_metrics.reply_count,
tweet_metrics.retweet_count,
tweet_metrics.total_retweet_count,
tweet_metrics.popularity_count,
author.id,
)
.execute(pool)
.await
.unwrap(); TweetClass (defined in the same file): #[allow(non_camel_case_types)]
#[derive(Debug, sqlx::Type)]
pub enum TweetClass {
normal,
rt_original,
helper,
} What am I missing? |
@ilmoi Your enum definition is likely wrong. What you need: // assuming your postgres type name is in snake_case like the variants seem to be
#[derive(Debug, sqlx::Type)]
#[sqlx(type_name = "tweet_class", rename_all = "snake_case")]
pub enum TweetClass {
Normal,
RtOriginal,
Helper
} |
I'm having a similar issue but with an array of enums, trying to insert in bulk using sqlx::query!(
r#"
INSERT INTO "Expansion"(region)
SELECT * FROM UNNEST($1::"Region"[])
"#,
®ion[..] as _,
)
.execute(&ctx.db)
.await?; where Region is pub enum Region {
#[sqlx(rename = "intl")]
International,
#[sqlx(rename = "jp")]
Japan,
}
How can we bulk insert enums? Thanks! |
As of the latest release, to make arrays of custom types work you just have to implement PgHasArrayType for the enum, returning |
Is there a reason why |
I didn't try implementing that. It would probably only make sense when a |
Hi!, I had the exact problem and this worked, but my question is why the underscore? is this because of postgres or sqlx? |
It's Postgres' default naming convention for array types. |
Are these solutions documented? I always have trouble finding documentation for the enum handling, and I need it rarely enough (so far) that I haven't memorized it. |
@LukeMathWalker where is this documented in Postgres so I can understand that convention better? |
Yes, it is documented here:
|
I'm trying to go with this approach and use the #[derive(Debug, sqlx::Type)]
#[sqlx(type_name = "user_type", rename_all = "snake_case")]
pub enum UserType {
Viewer,
Editor,
Admin,
}
impl PgHasArrayType for UserType {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::with_name("_user_type")
}
}
#[derive(Debug, sqlx::Type)]
pub struct User {
pub username: String,
pub user_type: UserType,
} And this is the corresponding SQL: create type user_type as enum ('viewer', 'editor', 'admin');
create table users (
user_id serial not null primary key,
username text not null,
user_type user_type not null
); However, when I try to query it like this, #[tokio::main]
async fn main() {
dotenv().ok();
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL not set");
let pool = PgPool::connect(&database_url)
.await
.expect("Could not connect to DATABASE_URL");
let users = sqlx::query!(r#"select user_id, username, user_type from users;"#)
.fetch_all(&pool)
.await
.unwrap();
println!("{:?}", users);
} I get Is there something else I have to do here? |
I'd love some clarity on this. I wish the reason I can't turn |
any updates on this for how you guys handled it? |
I've just been using the non-macro forms of |
Well that works but it kinda bugs me that macro's are giving me error. Feels like it's a wrong practice. |
It's not ideal but it is what it is. |
Running into this issue as well with For example: #[derive(Debug, sqlx::Type)]
#[sqlx(type_name = "job_status", rename_all = "lowercase")]
pub enum JobStatus {
Queued,
Running,
Completed,
} But when I want to I tried to override "just that one" column with something like: SELECT *, status AS "status: JobStatus" FROM job_history But it gives me that Is there a way to annotate this on the type being Ex: use crate::enums::JobStatus;
pub struct JobHistory {
pub id: Uuid,
// ...
#[sqlx(as_type=JobStatus)]
pub status: JobStatus
} I admit that I'm relatively new to Rust and so I am not sure of the macro limitations and nuance that goes into making this work. It does feel like magic at times, but things like this become head scratchers for mapping. For the time being, I'm also going with the non-macro versions of |
@ewrogers |
Yeah that works if I just need the two properties, but if I need "everything" I can't use |
Using |
That's a fair point, and I admit that I'm "getting back into SQL" after recent years of Mongo/DynamoDB land so there's a good chance I'm re-learning best practices there. Though is it a matter of "we don't support this because it's not a good idea and you shouldn't do it" (opinionated), or simply "we haven't found a way to support it yet"? I'm not arguing either side, just more or less to manage expectations of people who run into this. |
Unfortunately, it's not supported yet. Personally, I'd love to skip casting each enum column in the future. |
Any workaround for inserts? |
|
Just wanted to add for reference how I got enum of custom type + query_as! working. #[derive(Debug, Deserialize, Serialize, ToSchema, sqlx::Type)]
pub enum LightCondition {
DirectSunlight,
BrightIndirectSunlight,
MediumIndirectSunlight,
LowIndirectSunlight
}
impl PgHasArrayType for LightCondition {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::with_name("_light_condition")
}
}
// SQL query in query_as!
SELECT s.light_conditions AS "light_conditions: _" FROM species s The custom type was created as follows.
The light_conditions column was created as follows (it is an array of enums).
|
@alexanderameye Would you please edit your comment to show the schema for your column This part may have gotten lost in the discussion above: if a column type isn't an array, then |
Also had the same issue using #[derive(sqlx::Type, Serialize, Deserialize, Debug)]
#[sqlx(type_name = "git_source", rename_all = "lowercase")]
pub enum GitSource {
Github,
Gitlab,
Bitbucket,
}
let project = Project {
id: Uuid::new_v4(),
git_source: GitSource::Github,
};
sqlx::query_as!(
Project,
r#"INSERT INTO projects (id, git_source)
VALUES ($1, $2)
RETURNING id, git_source AS "git_source!: GitSource"#",
project.id,
project.git_source as GitSource,
).fetch_one(pool).await; Hope this helps anyone wanting to do inserts. |
let updated_user = sqlx::query_as!(
UserModel,
r#"UPDATE users SET role=$1 as "role:UserRole",updated_at=$2 WHERE id=$3 RETURNING id,name,email,verified,created_at,password,updated_at,role as "role: UserRole""#,
role.into(),
now,
user_id,
)
.fetch_optional(&self.pool)
.await?; does it possible to UPDATE role ? in my example syntax error. i've tryed without i opened issue here |
YES! r#"UPDATE users SET role=($1::text)::user_role, updated_at=$2 WHERE id=$3 RETURNING id, name, email, verified, created_at, password, updated_at, role as "role:UserRole""#, |
Polluting the SQL with weirdly named columns isn't ideal, as this might cause issues if you want to reuse the same SQL files for other stuff. What stops the macro from type-checking these fields without further annotation, considering the type of the field in the rust struct has a |
I have the following enum and struct:
Corresponding type and table:
I'm trying to fetch all rows like so:
But compiler fails with the following message:
Am I doing something wrong?
I've tried to search through issues first and got an impression that there are no compile-time checks for this now, is that right?
The text was updated successfully, but these errors were encountered: