From e925fd912a5d45f952ab044447c844ce174f84f7 Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Sat, 18 May 2024 19:18:41 +0200 Subject: [PATCH] Allow raw identifiers for SqlIdentifier (column-name) This allows using the `r#identifier` syntax for SqlIdentifier (column-name), which is used in derive macros. Previously the derive macros would panic when encountering such an identifier: `"r#identifier"` is not a valid identifier --- diesel_derives/src/attrs.rs | 6 +- diesel_derives/tests/as_changeset.rs | 288 +++++++++++++++++++--- diesel_derives/tests/helpers.rs | 32 ++- diesel_derives/tests/insertable.rs | 135 +++++++--- diesel_derives/tests/queryable.rs | 32 ++- diesel_derives/tests/queryable_by_name.rs | 74 +++++- diesel_derives/tests/schema.rs | 2 + diesel_derives/tests/selectable.rs | 49 +++- 8 files changed, 515 insertions(+), 103 deletions(-) diff --git a/diesel_derives/src/attrs.rs b/diesel_derives/src/attrs.rs index 0433f8a61ecb..2a3f5e0d9333 100644 --- a/diesel_derives/src/attrs.rs +++ b/diesel_derives/src/attrs.rs @@ -75,7 +75,11 @@ impl SqlIdentifier { impl ToTokens for SqlIdentifier { fn to_tokens(&self, tokens: &mut TokenStream) { - Ident::new(&self.field_name, self.span).to_tokens(tokens) + if self.field_name.starts_with("r#") { + Ident::new_raw(&self.field_name[2..], self.span).to_tokens(tokens) + } else { + Ident::new(&self.field_name, self.span).to_tokens(tokens) + } } } diff --git a/diesel_derives/tests/as_changeset.rs b/diesel_derives/tests/as_changeset.rs index b4d7391a363f..639ef100b080 100644 --- a/diesel_derives/tests/as_changeset.rs +++ b/diesel_derives/tests/as_changeset.rs @@ -10,6 +10,7 @@ fn named_ref_struct() { struct User { name: String, hair_color: String, + r#type: String, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -18,13 +19,24 @@ fn named_ref_struct() { .set(&User { name: String::from("Jim"), hair_color: String::from("blue"), + r#type: String::from("super"), }) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("Jim"), Some(String::from("blue"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("Jim"), + Some(String::from("blue")), + Some(String::from("super")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -36,6 +48,7 @@ fn named_struct() { struct User { name: String, hair_color: String, + r#type: String, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -44,13 +57,24 @@ fn named_struct() { .set(User { name: String::from("Jim"), hair_color: String::from("blue"), + r#type: String::from("super"), }) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("Jim"), Some(String::from("blue"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("Jim"), + Some(String::from("blue")), + Some(String::from("super")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -63,6 +87,7 @@ fn with_explicit_table_name() { struct UserForm { name: String, hair_color: String, + r#type: String, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -71,13 +96,24 @@ fn with_explicit_table_name() { .set(&UserForm { name: String::from("Jim"), hair_color: String::from("blue"), + r#type: String::from("super"), }) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("Jim"), Some(String::from("blue"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("Jim"), + Some(String::from("blue")), + Some(String::from("super")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -90,6 +126,7 @@ fn with_path_in_table_name() { struct UserForm { name: String, hair_color: String, + r#type: String, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -98,13 +135,24 @@ fn with_path_in_table_name() { .set(&UserForm { name: String::from("Jim"), hair_color: String::from("blue"), + r#type: String::from("super"), }) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("Jim"), Some(String::from("blue"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("Jim"), + Some(String::from("blue")), + Some(String::from("super")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -117,6 +165,7 @@ fn with_lifetime() { struct UserForm<'a> { name: &'a str, hair_color: &'a str, + r#type: &'a str, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -125,13 +174,24 @@ fn with_lifetime() { .set(&UserForm { name: "Jim", hair_color: "blue", + r#type: "super", }) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("Jim"), Some(String::from("blue"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("Jim"), + Some(String::from("blue")), + Some(String::from("super")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -141,9 +201,10 @@ fn with_lifetime() { fn with_multiple_lifetimes() { #[derive(AsChangeset)] #[diesel(table_name = users)] - struct UserForm<'a, 'b> { + struct UserForm<'a, 'b, 'c> { name: &'a str, hair_color: &'b str, + r#type: &'c str, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -152,13 +213,24 @@ fn with_multiple_lifetimes() { .set(&UserForm { name: "Jim", hair_color: "blue", + r#type: "super", }) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("Jim"), Some(String::from("blue"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("Jim"), + Some(String::from("blue")), + Some(String::from("super")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -168,9 +240,10 @@ fn with_multiple_lifetimes() { fn with_lifetime_constraints() { #[derive(AsChangeset)] #[diesel(table_name = users)] - struct UserForm<'a, 'b: 'a> { + struct UserForm<'a, 'b: 'a, 'c: 'b> { name: &'a str, hair_color: &'b str, + r#type: &'c str, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -179,13 +252,24 @@ fn with_lifetime_constraints() { .set(&UserForm { name: "Jim", hair_color: "blue", + r#type: "super", }) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("Jim"), Some(String::from("blue"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("Jim"), + Some(String::from("blue")), + Some(String::from("super")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -200,6 +284,8 @@ fn with_explicit_column_names() { nombre: &'a str, #[diesel(column_name = hair_color)] color_de_pelo: &'a str, + #[diesel(column_name = r#type)] + tipe: &'a str, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -208,13 +294,24 @@ fn with_explicit_column_names() { .set(&UserForm { nombre: "Jim", color_de_pelo: "blue", + tipe: "super", }) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("Jim"), Some(String::from("blue"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("Jim"), + Some(String::from("blue")), + Some(String::from("super")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("super")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -248,6 +345,8 @@ fn with_serialize_as() { name: String, #[diesel(serialize_as = UppercaseString)] hair_color: Option, + #[diesel(serialize_as = UppercaseString)] + r#type: Option, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -256,13 +355,24 @@ fn with_serialize_as() { .set(User { name: String::from("Jim"), hair_color: Some(String::from("blue")), + r#type: Some(String::from("super")), }) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("JIM"), Some(String::from("BLUE"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("JIM"), + Some(String::from("BLUE")), + Some(String::from("SUPER")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -275,18 +385,29 @@ fn tuple_struct() { struct UserForm<'a>( #[diesel(column_name = name)] &'a str, #[diesel(column_name = hair_color)] &'a str, + #[diesel(column_name = r#type)] &'a str, ); let connection = &mut connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) - .set(&UserForm("Jim", "blue")) + .set(&UserForm("Jim", "blue", "super")) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("Jim"), Some(String::from("blue"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("Jim"), + Some(String::from("blue")), + Some(String::from("super")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -308,8 +429,18 @@ fn struct_containing_single_field() { .unwrap(); let expected = vec![ - (1, String::from("Jim"), Some(String::from("black"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("Jim"), + Some(String::from("black")), + Some(String::from("regular")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -329,8 +460,18 @@ fn tuple_struct_containing_single_field() { .unwrap(); let expected = vec![ - (1, String::from("Jim"), Some(String::from("black"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("Jim"), + Some(String::from("black")), + Some(String::from("regular")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -345,6 +486,7 @@ fn primary_key_is_not_updated() { id: i32, name: &'a str, hair_color: &'a str, + r#type: &'a str, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -354,13 +496,24 @@ fn primary_key_is_not_updated() { id: 3, name: "Jim", hair_color: "blue", + r#type: "super", }) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("Jim"), Some(String::from("blue"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("Jim"), + Some(String::from("blue")), + Some(String::from("super")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("super")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -375,6 +528,7 @@ fn primary_key_is_based_on_column_name() { _id: i32, name: &'a str, hair_color: &'a str, + r#type: &'a str, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -384,13 +538,24 @@ fn primary_key_is_based_on_column_name() { _id: 3, name: "Jim", hair_color: "blue", + r#type: "super", }) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("Jim"), Some(String::from("blue"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("Jim"), + Some(String::from("blue")), + Some(String::from("super")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -405,6 +570,7 @@ fn primary_key_is_not_updated_with_custom_pk() { #[allow(dead_code)] name: &'a str, hair_color: &'a str, + r#type: &'a str, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -413,13 +579,24 @@ fn primary_key_is_not_updated_with_custom_pk() { .set(&UserForm { name: "Jim", hair_color: "blue", + r#type: "super", }) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("Sean"), Some(String::from("blue"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("Sean"), + Some(String::from("blue")), + Some(String::from("super")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -435,6 +612,7 @@ fn primary_key_is_not_updated_with_custom_composite_pk() { id: i32, name: &'a str, hair_color: &'a str, + r#type: &'a str, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -444,13 +622,24 @@ fn primary_key_is_not_updated_with_custom_composite_pk() { id: 3, name: "Jim", hair_color: "blue", + r#type: "super", }) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("Sean"), Some(String::from("blue"))), - (2, String::from("Tess"), Some(String::from("brown"))), + ( + 1, + String::from("Sean"), + Some(String::from("blue")), + Some(String::from("super")), + ), + ( + 2, + String::from("Tess"), + Some(String::from("brown")), + Some(String::from("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -463,6 +652,7 @@ fn option_fields_are_skipped() { struct UserForm<'a> { name: &'a str, hair_color: Option<&'a str>, + r#type: Option<&'a str>, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -471,6 +661,7 @@ fn option_fields_are_skipped() { .set(&UserForm { name: "Jim", hair_color: Some("blue"), + r#type: Some("super"), }) .execute(connection) .unwrap(); @@ -478,13 +669,24 @@ fn option_fields_are_skipped() { .set(&UserForm { name: "Ruby", hair_color: None, + r#type: None, }) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("Jim"), Some(String::from("blue"))), - (2, String::from("Ruby"), Some(String::from("brown"))), + ( + 1, + String::from("Jim"), + Some(String::from("blue")), + Some(String::from("super")), + ), + ( + 2, + String::from("Ruby"), + Some(String::from("brown")), + Some(String::from("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -499,6 +701,7 @@ fn option_fields_are_assigned_null_when_specified() { #[diesel(treat_none_as_null = false)] name: Option<&'a str>, hair_color: Option<&'a str>, + r#type: Option<&'a str>, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -507,6 +710,7 @@ fn option_fields_are_assigned_null_when_specified() { .set(&UserForm { name: None, hair_color: Some("blue"), + r#type: None, }) .execute(connection) .unwrap(); @@ -514,13 +718,19 @@ fn option_fields_are_assigned_null_when_specified() { .set(&UserForm { name: Some("Ruby"), hair_color: None, + r#type: None, }) .execute(connection) .unwrap(); let expected = vec![ - (1, String::from("Sean"), Some(String::from("blue"))), - (2, String::from("Ruby"), None), + ( + 1, + String::from("Sean"), + Some(String::from("blue")), + Some(String::from("super")), + ), + (2, String::from("Ruby"), None, Some(String::from("admin"))), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); diff --git a/diesel_derives/tests/helpers.rs b/diesel_derives/tests/helpers.rs index 79715735f8c7..8376cb9b35c8 100644 --- a/diesel_derives/tests/helpers.rs +++ b/diesel_derives/tests/helpers.rs @@ -13,13 +13,15 @@ cfg_if! { sql_query("CREATE TABLE users (\ id INTEGER PRIMARY KEY AUTOINCREMENT, \ name VARCHAR NOT NULL, \ - hair_color VARCHAR DEFAULT 'Green')") + hair_color VARCHAR DEFAULT 'Green', + type VARCHAR DEFAULT 'regular')") .execute(&mut conn) .unwrap(); sql_query("CREATE TABLE users_ (\ id INTEGER PRIMARY KEY AUTOINCREMENT, \ name VARCHAR NOT NULL, \ - hair_color VARCHAR DEFAULT 'Green')") + hair_color VARCHAR DEFAULT 'Green', + type VARCHAR DEFAULT 'regular')") .execute(&mut conn) .unwrap(); conn @@ -40,13 +42,15 @@ cfg_if! { sql_query("CREATE TABLE users (\ id SERIAL PRIMARY KEY, \ name VARCHAR NOT NULL, \ - hair_color VARCHAR DEFAULT 'Green')") + hair_color VARCHAR DEFAULT 'Green', + type VARCHAR DEFAULT 'regular')") .execute(&mut conn) .unwrap(); sql_query("CREATE TABLE users_ (\ id SERIAL PRIMARY KEY, \ name VARCHAR NOT NULL, \ - hair_color VARCHAR DEFAULT 'Green')") + hair_color VARCHAR DEFAULT 'Green', + type VARCHAR DEFAULT 'regular')") .execute(&mut conn) .unwrap(); conn @@ -66,13 +70,15 @@ cfg_if! { sql_query("CREATE TABLE users (\ id INTEGER PRIMARY KEY AUTO_INCREMENT, \ name TEXT NOT NULL, \ - hair_color VARCHAR(255) DEFAULT 'Green')") + hair_color VARCHAR(255) DEFAULT 'Green', + type VARCHAR DEFAULT 'regular')") .execute(&mut conn) .unwrap(); sql_query("CREATE TABLE users_ (\ id INTEGER PRIMARY KEY AUTO_INCREMENT, \ name TEXT NOT NULL, \ - hair_color VARCHAR(255) DEFAULT 'Green')") + hair_color VARCHAR(255) DEFAULT 'Green', + type VARCHAR DEFAULT 'regular')") .execute(&mut conn) .unwrap(); conn.begin_test_transaction().unwrap(); @@ -94,8 +100,18 @@ pub fn connection_with_sean_and_tess_in_users_table() -> TestConnection { let mut connection = connection(); diesel::insert_into(users) .values(&vec![ - (id.eq(1), name.eq("Sean"), hair_color.eq("black")), - (id.eq(2), name.eq("Tess"), hair_color.eq("brown")), + ( + id.eq(1), + name.eq("Sean"), + hair_color.eq("black"), + r#type.eq("regular"), + ), + ( + id.eq(2), + name.eq("Tess"), + hair_color.eq("brown"), + r#type.eq("admin"), + ), ]) .execute(&mut connection) .unwrap(); diff --git a/diesel_derives/tests/insertable.rs b/diesel_derives/tests/insertable.rs index d6eaeef83ac2..20d8004c9a3e 100644 --- a/diesel_derives/tests/insertable.rs +++ b/diesel_derives/tests/insertable.rs @@ -9,12 +9,14 @@ fn simple_struct_definition() { struct NewUser { name: String, hair_color: String, + r#type: String, } let conn = &mut connection(); let new_user = NewUser { name: "Sean".into(), hair_color: "Black".into(), + r#type: "regular".into(), }; insert_into(users::table) .values(new_user) @@ -22,9 +24,13 @@ fn simple_struct_definition() { .unwrap(); let saved = users::table - .select((users::name, users::hair_color)) - .load::<(String, Option)>(conn); - let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; + .select((users::name, users::hair_color, users::r#type)) + .load::<(String, Option, Option)>(conn); + let expected = vec![( + "Sean".to_string(), + Some("Black".to_string()), + Some("regular".to_string()), + )]; assert_eq!(Ok(expected), saved); } @@ -34,12 +40,14 @@ fn with_implicit_table_name() { struct User { name: String, hair_color: String, + r#type: String, } let conn = &mut connection(); let new_user = User { name: "Sean".into(), hair_color: "Black".into(), + r#type: "regular".into(), }; insert_into(users::table) .values(new_user) @@ -47,9 +55,13 @@ fn with_implicit_table_name() { .unwrap(); let saved = users::table - .select((users::name, users::hair_color)) - .load::<(String, Option)>(conn); - let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; + .select((users::name, users::hair_color, users::r#type)) + .load::<(String, Option, Option)>(conn); + let expected = vec![( + "Sean".to_string(), + Some("Black".to_string()), + Some("regular".to_string()), + )]; assert_eq!(Ok(expected), saved); } @@ -60,12 +72,14 @@ fn with_path_in_table_name() { struct NewUser { name: String, hair_color: String, + r#type: String, } let conn = &mut connection(); let new_user = NewUser { name: "Sean".into(), hair_color: "Black".into(), + r#type: "regular".into(), }; insert_into(users::table) .values(new_user) @@ -73,9 +87,13 @@ fn with_path_in_table_name() { .unwrap(); let saved = users::table - .select((users::name, users::hair_color)) - .load::<(String, Option)>(conn); - let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; + .select((users::name, users::hair_color, users::r#type)) + .load::<(String, Option, Option)>(conn); + let expected = vec![( + "Sean".to_string(), + Some("Black".to_string()), + Some("regular".to_string()), + )]; assert_eq!(Ok(expected), saved); } @@ -86,12 +104,14 @@ fn simple_reference_definition() { struct NewUser { name: String, hair_color: String, + r#type: String, } let conn = &mut connection(); let new_user = NewUser { name: "Sean".into(), hair_color: "Black".into(), + r#type: "regular".into(), }; insert_into(users::table) .values(&new_user) @@ -99,9 +119,13 @@ fn simple_reference_definition() { .unwrap(); let saved = users::table - .select((users::name, users::hair_color)) - .load::<(String, Option)>(conn); - let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; + .select((users::name, users::hair_color, users::r#type)) + .load::<(String, Option, Option)>(conn); + let expected = vec![( + "Sean".to_string(), + Some("Black".to_string()), + Some("regular".to_string()), + )]; assert_eq!(Ok(expected), saved); } @@ -113,12 +137,14 @@ fn multiple_tables() { struct NewUser { name: String, hair_color: String, + r#type: String, } let conn = &mut connection(); let new_user = NewUser { name: "Sean".into(), hair_color: "Black".into(), + r#type: "regular".into(), }; insert_into(users::table) .values(new_user.clone()) @@ -126,9 +152,13 @@ fn multiple_tables() { .unwrap(); let saved = users::table - .select((users::name, users::hair_color)) - .load::<(String, Option)>(conn); - let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; + .select((users::name, users::hair_color, users::r#type)) + .load::<(String, Option, Option)>(conn); + let expected = vec![( + "Sean".to_string(), + Some("Black".to_string()), + Some("regular".to_string()), + )]; assert_eq!(Ok(expected.clone()), saved); insert_into(users_::table) @@ -137,8 +167,8 @@ fn multiple_tables() { .unwrap(); let saved = users_::table - .select((users_::name, users_::hair_color)) - .load::<(String, Option)>(conn); + .select((users_::name, users_::hair_color, users_::r#type)) + .load::<(String, Option, Option)>(conn); assert_eq!(Ok(expected), saved); } @@ -151,12 +181,12 @@ macro_rules! test_struct_definition { $struct_def let conn = &mut connection(); - let new_user = NewUser { name: "Sean".into(), hair_color: None }; + let new_user = NewUser { name: "Sean".into(), hair_color: None, r#type: Some("regular".into()) }; insert_into(users::table).values(&new_user).execute(conn).unwrap(); - let saved = users::table.select((users::name, users::hair_color)) - .load::<(String, Option)>(conn); - let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; + let saved = users::table.select((users::name, users::hair_color, users::r#type)) + .load::<(String, Option, Option)>(conn); + let expected = vec![("Sean".to_string(), Some("Green".to_string()), Some("regular".to_string()))]; assert_eq!(Ok(expected), saved); } } @@ -167,6 +197,7 @@ test_struct_definition! { struct NewUser { name: String, hair_color: Option, + r#type: Option, } } @@ -175,6 +206,7 @@ test_struct_definition! { pub struct NewUser { name: String, hair_color: Option, + r#type: Option, } } @@ -183,6 +215,7 @@ test_struct_definition! { pub struct NewUser { pub name: String, hair_color: Option, + r#type: Option, } } @@ -191,6 +224,7 @@ test_struct_definition! { pub struct NewUser { name: String, pub hair_color: Option, + r#type: Option, } } @@ -199,6 +233,7 @@ test_struct_definition! { struct NewUser<'a> { name: &'a str, hair_color: Option<&'a str>, + r#type: Option<&'a str>, } } @@ -210,12 +245,14 @@ fn named_struct_with_renamed_field() { #[diesel(column_name = name)] my_name: String, hair_color: String, + r#type: String, } let conn = &mut connection(); let new_user = NewUser { my_name: "Sean".into(), hair_color: "Black".into(), + r#type: "regular".into(), }; insert_into(users::table) .values(&new_user) @@ -223,9 +260,9 @@ fn named_struct_with_renamed_field() { .unwrap(); let saved = users::table - .select((users::name, users::hair_color)) - .load::<(String, Option)>(conn); - let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; + .select((users::name, users::hair_color, users::r#type)) + .load::<(String, Option, Option)>(conn); + let expected = vec![("Sean".to_string(), Some("Black".to_string()), Some("regular".to_string()))]; assert_eq!(Ok(expected), saved); } @@ -238,12 +275,15 @@ fn named_struct_with_renamed_option_field() { my_name: String, #[diesel(column_name = hair_color)] my_hair_color: Option, + #[diesel(column_name = r#type)] + my_type: String, } let conn = &mut connection(); let new_user = NewUser { my_name: "Sean".into(), my_hair_color: None, + my_type: "regular".into(), }; insert_into(users::table) .values(&new_user) @@ -251,9 +291,13 @@ fn named_struct_with_renamed_option_field() { .unwrap(); let saved = users::table - .select((users::name, users::hair_color)) - .load::<(String, Option)>(conn); - let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; + .select((users::name, users::hair_color, users::r#type)) + .load::<(String, Option, Option)>(conn); + let expected = vec![( + "Sean".to_string(), + Some("Green".to_string()), + Some("regular".to_string()), + )]; assert_eq!(Ok(expected), saved); } @@ -264,19 +308,24 @@ fn tuple_struct() { struct NewUser<'a>( #[diesel(column_name = name)] &'a str, #[diesel(column_name = hair_color)] Option<&'a str>, + #[diesel(column_name = r#type)] Option<&'a str>, ); let conn = &mut connection(); - let new_user = NewUser("Sean", None); + let new_user = NewUser("Sean", None, Some("regular")); insert_into(users::table) .values(&new_user) .execute(conn) .unwrap(); let saved = users::table - .select((users::name, users::hair_color)) - .load::<(String, Option)>(conn); - let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; + .select((users::name, users::hair_color, users::r#type)) + .load::<(String, Option, Option)>(conn); + let expected = vec![( + "Sean".to_string(), + Some("Green".to_string()), + Some("regular".to_string()), + )]; assert_eq!(Ok(expected), saved); } @@ -287,14 +336,17 @@ fn named_struct_with_unusual_reference_type() { struct NewUser<'a> { name: &'a String, hair_color: Option<&'a String>, + r#type: Option<&'a String>, } let conn = &mut connection(); let sean = "Sean".to_string(); let black = "Black".to_string(); + let regular = "regular".to_string(); let new_user = NewUser { name: &sean, hair_color: Some(&black), + r#type: Some(®ular), }; insert_into(users::table) .values(&new_user) @@ -302,9 +354,9 @@ fn named_struct_with_unusual_reference_type() { .unwrap(); let saved = users::table - .select((users::name, users::hair_color)) + .select((users::name, users::hair_color, users::r#type)) .load(conn); - let expected = vec![(sean.clone(), Some(black.clone()))]; + let expected = vec![(sean.clone(), Some(black.clone()), Some(regular.clone()))]; assert_eq!(Ok(expected), saved); } @@ -348,24 +400,26 @@ fn insertable_with_slice_of_borrowed() { fn embedded_struct() { #[derive(Insertable)] #[diesel(table_name = users)] - struct NameAndHairColor<'a> { + struct UserAttributes<'a> { name: &'a str, hair_color: &'a str, + r#type: &'a str, } #[derive(Insertable)] struct User<'a> { id: i32, #[diesel(embed)] - name_and_hair_color: NameAndHairColor<'a>, + attributes: UserAttributes<'a>, } let conn = &mut connection(); let new_user = User { id: 1, - name_and_hair_color: NameAndHairColor { + attributes: UserAttributes { name: "Sean", hair_color: "Black", + r#type: "regular", }, }; insert_into(users::table) @@ -373,7 +427,12 @@ fn embedded_struct() { .execute(conn) .unwrap(); - let saved = users::table.load::<(i32, String, Option)>(conn); - let expected = vec![(1, "Sean".to_string(), Some("Black".to_string()))]; + let saved = users::table.load::<(i32, String, Option, Option)>(conn); + let expected = vec![( + 1, + "Sean".to_string(), + Some("Black".to_string()), + Some("regular".to_string()), + )]; assert_eq!(Ok(expected), saved); } diff --git a/diesel_derives/tests/queryable.rs b/diesel_derives/tests/queryable.rs index 0b29a131f407..f2b11713b992 100644 --- a/diesel_derives/tests/queryable.rs +++ b/diesel_derives/tests/queryable.rs @@ -30,6 +30,26 @@ fn tuple_struct() { assert_eq!(Ok(MyStruct(1, 2)), data); } +#[test] +fn raw_ident_struct() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable)] + struct MyStruct { + r#foo: i32, + #[diesel(column_name = bar)] + r#struct: i32, + } + + let conn = &mut connection(); + let data = select(sql::<(Integer, Integer)>("1, 2")).get_result(conn); + assert_eq!( + Ok(MyStruct { + foo: 1, + r#struct: 2 + }), + data + ); +} + #[test] fn tuple_struct_without_column_name_annotations() { #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable)] @@ -48,17 +68,21 @@ fn multiple_tables() { struct NewUser { name: String, hair_color: String, + r#type: String, } let conn = &mut connection(); - let data = select(sql::<(diesel::sql_types::Text, diesel::sql_types::Text)>( - "'red', 'red'", - )) + let data = select(sql::<( + diesel::sql_types::Text, + diesel::sql_types::Text, + diesel::sql_types::Text, + )>("'red', 'red', 'red'")) .get_result(conn); assert_eq!( Ok(NewUser { name: "red".to_string(), - hair_color: "red".to_string() + hair_color: "red".to_string(), + r#type: "red".to_string(), }), data ); diff --git a/diesel_derives/tests/queryable_by_name.rs b/diesel_derives/tests/queryable_by_name.rs index c64d77779f21..0a8f575b918c 100644 --- a/diesel_derives/tests/queryable_by_name.rs +++ b/diesel_derives/tests/queryable_by_name.rs @@ -8,6 +8,7 @@ table! { my_structs (foo) { foo -> Integer, bar -> Integer, + r#type -> Integer, } } @@ -18,11 +19,19 @@ fn named_struct_definition() { struct MyStruct { foo: i32, bar: i32, + r#type: i32, } let conn = &mut connection(); - let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(conn); - assert_eq!(Ok(MyStruct { foo: 1, bar: 2 }), data); + let data = sql_query("SELECT 1 AS foo, 2 AS bar, 3 AS type").get_result(conn); + assert_eq!( + Ok(MyStruct { + foo: 1, + bar: 2, + r#type: 3 + }), + data + ); } #[test] @@ -32,11 +41,12 @@ fn tuple_struct() { struct MyStruct( #[diesel(column_name = foo)] i32, #[diesel(column_name = bar)] i32, + #[diesel(column_name = r#type)] i32, ); let conn = &mut connection(); - let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(conn); - assert_eq!(Ok(MyStruct(1, 2)), data); + let data = sql_query("SELECT 1 AS foo, 2 AS bar, 3 AS type").get_result(conn); + assert_eq!(Ok(MyStruct(1, 2, 3)), data); } #[test] @@ -46,11 +56,19 @@ fn struct_with_path_in_name() { struct MyStruct { foo: i32, bar: i32, + r#type: i32, } let conn = &mut connection(); - let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(conn); - assert_eq!(Ok(MyStruct { foo: 1, bar: 2 }), data); + let data = sql_query("SELECT 1 AS foo, 2 AS bar, 3 AS type").get_result(conn); + assert_eq!( + Ok(MyStruct { + foo: 1, + bar: 2, + r#type: 3 + }), + data + ); } // FIXME: Test usage with renamed columns @@ -63,11 +81,20 @@ fn struct_with_no_table() { foo: i32, #[diesel(sql_type = Integer)] bar: i32, + #[diesel(sql_type = Integer)] + r#type: i32, } let conn = &mut connection(); - let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(conn); - assert_eq!(Ok(MyStructNamedSoYouCantInferIt { foo: 1, bar: 2 }), data); + let data = sql_query("SELECT 1 AS foo, 2 AS bar, 3 AS type").get_result(conn); + assert_eq!( + Ok(MyStructNamedSoYouCantInferIt { + foo: 1, + bar: 2, + r#type: 3 + }), + data + ); } #[test] @@ -97,6 +124,8 @@ fn embedded_struct() { foo: i32, #[diesel(embed)] b: B, + #[diesel(embed)] + t: T, } #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] @@ -105,12 +134,19 @@ fn embedded_struct() { bar: i32, } + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + #[diesel(table_name = my_structs)] + struct T { + r#type: i32, + } + let conn = &mut connection(); - let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(conn); + let data = sql_query("SELECT 1 AS foo, 2 AS bar, 3 AS type").get_result(conn); assert_eq!( Ok(A { foo: 1, b: B { bar: 2 }, + t: T { r#type: 3 }, }), data ); @@ -124,6 +160,8 @@ fn embedded_option() { foo: i32, #[diesel(embed)] b: Option, + #[diesel(embed)] + t: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] @@ -132,15 +170,29 @@ fn embedded_option() { bar: i32, } + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + #[diesel(table_name = my_structs)] + struct T { + r#type: i32, + } + let conn = &mut connection(); let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(conn); assert_eq!( Ok(A { foo: 1, b: Some(B { bar: 2 }), + t: Some(T { r#type: 3 }), + }), + data + ); + let data = sql_query("SELECT 1 AS foo, NULL AS bar, NULL as type").get_result(conn); + assert_eq!( + Ok(A { + foo: 1, + b: None, + t: None }), data ); - let data = sql_query("SELECT 1 AS foo, NULL AS bar").get_result(conn); - assert_eq!(Ok(A { foo: 1, b: None }), data); } diff --git a/diesel_derives/tests/schema.rs b/diesel_derives/tests/schema.rs index b0fabd7a956f..2c499f951f1a 100644 --- a/diesel_derives/tests/schema.rs +++ b/diesel_derives/tests/schema.rs @@ -3,6 +3,7 @@ table! { id -> Integer, name -> Text, hair_color -> Nullable, + r#type -> Nullable, } } @@ -11,5 +12,6 @@ table! { id -> Integer, name -> Text, hair_color -> Nullable, + r#type -> Nullable, } } diff --git a/diesel_derives/tests/selectable.rs b/diesel_derives/tests/selectable.rs index 240fdccaa482..0615efcc7e7b 100644 --- a/diesel_derives/tests/selectable.rs +++ b/diesel_derives/tests/selectable.rs @@ -10,6 +10,7 @@ table! { my_structs (foo) { foo -> Integer, bar -> Integer, + r#type -> Integer, } } @@ -20,6 +21,7 @@ fn named_struct_definition() { struct MyStruct { foo: i32, bar: i32, + r#type: i32, } let conn = &mut connection(); @@ -36,6 +38,7 @@ fn tuple_struct() { struct MyStruct( #[diesel(column_name = foo)] i32, #[diesel(column_name = bar)] i32, + #[diesel(column_name = r#type)] i32, ); let conn = &mut connection(); @@ -51,10 +54,12 @@ fn tuple_struct() { fn embedded_struct() { #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable, Selectable)] #[diesel(table_name = my_structs)] - struct A { + struct A { foo: i32, #[diesel(embed)] b: B, + #[diesel(embed)] + t: T, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable, Selectable)] @@ -63,9 +68,15 @@ fn embedded_struct() { bar: i32, } + #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable, Selectable)] + #[diesel(table_name = my_structs)] + struct T { + r#type: i32, + } + let conn = &mut connection(); let data = my_structs::table - .select(A::::as_select()) + .select(A::::as_select()) .get_result(conn); assert!(data.is_err()); } @@ -78,6 +89,8 @@ fn embedded_option() { foo: i32, #[diesel(embed)] b: Option, + #[diesel(embed)] + t: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable, Selectable)] @@ -86,6 +99,12 @@ fn embedded_option() { bar: i32, } + #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable, Selectable)] + #[diesel(table_name = my_structs)] + struct T { + r#type: i32, + } + let conn = &mut connection(); let data = my_structs::table.select(A::as_select()).get_result(conn); assert!(data.is_err()); @@ -97,6 +116,7 @@ fn embedded_option_with_nullable_field() { my_structs (foo) { foo -> Integer, bar -> Nullable, + r#type -> Nullable, } } @@ -106,6 +126,8 @@ fn embedded_option_with_nullable_field() { foo: i32, #[diesel(embed)] b: Option, + #[diesel(embed)] + t: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable, Selectable)] @@ -114,6 +136,12 @@ fn embedded_option_with_nullable_field() { bar: Option, } + #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable, Selectable)] + #[diesel(table_name = my_structs)] + struct T { + r#type: Option, + } + let conn = &mut connection(); let data = my_structs::table.select(A::as_select()).get_result(conn); assert!(data.is_err()); @@ -126,6 +154,8 @@ fn manually_specified_expression() { foo -> Integer, bar -> Nullable, some_int -> Nullable, + r#type -> Nullable, + another_int -> Nullable, } } @@ -143,6 +173,16 @@ fn manually_specified_expression() { select_expression_type = dsl::IsNotNull, )] bar_is_set: bool, + #[diesel( + select_expression = (my_structs::r#type.is_not_null(), my_structs::another_int), + select_expression_type = (dsl::IsNotNull, my_structs::another_int), + )] + type_is_set_and_another_int: (bool, Option), + #[diesel( + select_expression = my_structs::r#type.is_not_null(), + select_expression_type = dsl::IsNotNull, + )] + type_is_set: bool, } let conn = &mut connection(); @@ -157,6 +197,7 @@ fn check_for_backend_with_deserialize_as() { tests { id -> Integer, name -> Text, + r#type -> Text, } } @@ -174,6 +215,8 @@ fn check_for_backend_with_deserialize_as() { id: i32, #[diesel(deserialize_as = String)] name: MyString, + #[diesel(deserialize_as = String)] + r#type: MyString, } } @@ -185,6 +228,7 @@ fn check_with_lifetime_and_type_param() { test { id -> Integer, name -> Text, + r#type -> Text, } } @@ -194,6 +238,7 @@ fn check_with_lifetime_and_type_param() { pub struct Account<'n0> { id: i32, name: Cow<'n0, str>, + r#type: Cow<'n0, str>, } #[derive(Queryable, Selectable)]