diff --git a/.typos.toml b/.typos.toml index 80503a2a6eca..a630e24b5532 100644 --- a/.typos.toml +++ b/.typos.toml @@ -20,7 +20,9 @@ extend-ignore-re = [ "cannot find value `titel` in module `posts`", "cannot find type `titel` in module `posts`", "[0-9]+[[:space]]+|[[:space:]]+titel: String", - "big_sur" + "big_sur", + # That's Spanish for "type" (used in a unit-test) + "tipe", ] [type.md] diff --git a/diesel_compile_tests/tests/fail/derive/bad_column_name.rs b/diesel_compile_tests/tests/fail/derive/bad_column_name.rs index ebfe47acb416..d031154d772c 100644 --- a/diesel_compile_tests/tests/fail/derive/bad_column_name.rs +++ b/diesel_compile_tests/tests/fail/derive/bad_column_name.rs @@ -5,8 +5,8 @@ table! { users { id -> Integer, name -> Text, - #[sql_name = "type"] - tpe -> Text, + #[sql_name = "spa ce"] + space -> Text, } } @@ -35,16 +35,16 @@ struct User3 { #[derive(Insertable)] #[diesel(table_name = users)] struct User4 { - #[diesel(column_name = "type")] - ty: String, + #[diesel(column_name = "spa ce")] + space: String, } #[derive(AsChangeset)] #[diesel(table_name = users)] struct User5 { - #[diesel(column_name = "type")] - ty: String, + #[diesel(column_name = "spa ce")] + space: String, } fn main() {} diff --git a/diesel_compile_tests/tests/fail/derive/bad_column_name.stderr b/diesel_compile_tests/tests/fail/derive/bad_column_name.stderr index 47e7baef6d0a..8707c402391c 100644 --- a/diesel_compile_tests/tests/fail/derive/bad_column_name.stderr +++ b/diesel_compile_tests/tests/fail/derive/bad_column_name.stderr @@ -17,14 +17,14 @@ error: expected string literal 30 | #[diesel(column_name = true)] | ^^^^ -error: Expected valid identifier, found `type`. Diesel automatically renames invalid identifiers, perhaps you meant to write `type_`? +error: Expected valid identifier, found `spa ce`. Diesel does not support column names with whitespaces yet --> tests/fail/derive/bad_column_name.rs:38:28 | -38 | #[diesel(column_name = "type")] - | ^^^^^^ +38 | #[diesel(column_name = "spa ce")] + | ^^^^^^^^ -error: Expected valid identifier, found `type`. Diesel automatically renames invalid identifiers, perhaps you meant to write `type_`? +error: Expected valid identifier, found `spa ce`. Diesel does not support column names with whitespaces yet --> tests/fail/derive/bad_column_name.rs:46:28 | -46 | #[diesel(column_name = "type")] - | ^^^^^^ +46 | #[diesel(column_name = "spa ce")] + | ^^^^^^^^ diff --git a/diesel_derives/src/as_changeset.rs b/diesel_derives/src/as_changeset.rs index b4382cc5a428..6755ea1b74d7 100644 --- a/diesel_derives/src/as_changeset.rs +++ b/diesel_derives/src/as_changeset.rs @@ -157,8 +157,7 @@ fn field_changeset_ty( lifetime: Option, treat_none_as_null: bool, ) -> Result { - let column_name = field.column_name()?; - column_name.valid_ident()?; + let column_name = field.column_name()?.to_ident()?; if !treat_none_as_null && is_option_ty(&field.ty) { let field_ty = inner_of_option_ty(&field.ty); Ok( @@ -177,8 +176,7 @@ fn field_changeset_expr( treat_none_as_null: bool, ) -> Result { let field_name = &field.name; - let column_name = field.column_name()?; - column_name.valid_ident()?; + let column_name = field.column_name()?.to_ident()?; if !treat_none_as_null && is_option_ty(&field.ty) { if lifetime.is_some() { Ok(quote!(self.#field_name.as_ref().map(|x| #table_name::#column_name.eq(x)))) @@ -196,8 +194,7 @@ fn field_changeset_ty_serialize_as( ty: &Type, treat_none_as_null: bool, ) -> Result { - let column_name = field.column_name()?; - column_name.valid_ident()?; + let column_name = field.column_name()?.to_ident()?; if !treat_none_as_null && is_option_ty(&field.ty) { let inner_ty = inner_of_option_ty(ty); Ok(quote!(std::option::Option>)) @@ -213,8 +210,7 @@ fn field_changeset_expr_serialize_as( treat_none_as_null: bool, ) -> Result { let field_name = &field.name; - let column_name = field.column_name()?; - column_name.valid_ident()?; + let column_name = field.column_name()?.to_ident()?; let column: Expr = parse_quote!(#table_name::#column_name); if !treat_none_as_null && is_option_ty(&field.ty) { Ok(quote!(self.#field_name.map(|x| #column.eq(::std::convert::Into::<#ty>::into(x))))) diff --git a/diesel_derives/src/attrs.rs b/diesel_derives/src/attrs.rs index 0433f8a61ecb..e196ba6f74ed 100644 --- a/diesel_derives/src/attrs.rs +++ b/diesel_derives/src/attrs.rs @@ -56,9 +56,21 @@ impl SqlIdentifier { self.span } - pub fn valid_ident(&self) -> Result<()> { - if syn::parse_str::(&self.field_name).is_err() { - Err(syn::Error::new( + pub fn to_ident(&self) -> Result { + match syn::parse_str::(&format!("r#{}", self.field_name)) { + Ok(mut ident) => { + ident.set_span(self.span); + Ok(ident) + } + Err(_e) if self.field_name.contains(' ') => Err(syn::Error::new( + self.span(), + format!( + "Expected valid identifier, found `{0}`. \ + Diesel does not support column names with whitespaces yet", + self.field_name + ), + )), + Err(_e) => Err(syn::Error::new( self.span(), format!( "Expected valid identifier, found `{0}`. \ @@ -66,22 +78,28 @@ impl SqlIdentifier { perhaps you meant to write `{0}_`?", self.field_name ), - )) - } else { - Ok(()) + )), } } } 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) + } } } impl Display for SqlIdentifier { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&self.field_name) + let mut start = 0; + if self.field_name.starts_with("r#") { + start = 2; + } + f.write_str(&self.field_name[start..]) } } @@ -93,6 +111,8 @@ impl PartialEq for SqlIdentifier { impl From<&'_ Ident> for SqlIdentifier { fn from(ident: &'_ Ident) -> Self { + use syn::ext::IdentExt; + let ident = ident.unraw(); Self { span: ident.span(), field_name: ident.to_string(), diff --git a/diesel_derives/src/insertable.rs b/diesel_derives/src/insertable.rs index 5581efa46739..fe81b192bfc1 100644 --- a/diesel_derives/src/insertable.rs +++ b/diesel_derives/src/insertable.rs @@ -194,8 +194,7 @@ fn field_ty_serialize_as( ty: &Type, treat_none_as_default_value: bool, ) -> Result { - let column_name = field.column_name()?; - column_name.valid_ident()?; + let column_name = field.column_name()?.to_ident()?; let span = field.span; if treat_none_as_default_value { let inner_ty = inner_of_option_ty(ty); @@ -223,8 +222,7 @@ fn field_expr_serialize_as( treat_none_as_default_value: bool, ) -> Result { let field_name = &field.name; - let column_name = field.column_name()?; - column_name.valid_ident()?; + let column_name = field.column_name()?.to_ident()?; let column = quote!(#table_name::#column_name); if treat_none_as_default_value { if is_option_ty(ty) { @@ -245,8 +243,7 @@ fn field_ty( lifetime: Option, treat_none_as_default_value: bool, ) -> Result { - let column_name = field.column_name()?; - column_name.valid_ident()?; + let column_name = field.column_name()?.to_ident()?; let span = field.span; if treat_none_as_default_value { let inner_ty = inner_of_option_ty(&field.ty); @@ -276,8 +273,7 @@ fn field_expr( treat_none_as_default_value: bool, ) -> Result { let field_name = &field.name; - let column_name = field.column_name()?; - column_name.valid_ident()?; + let column_name = field.column_name()?.to_ident()?; let column: Expr = parse_quote!(#table_name::#column_name); if treat_none_as_default_value { diff --git a/diesel_derives/src/queryable_by_name.rs b/diesel_derives/src/queryable_by_name.rs index 62853bd5123f..16763059a70b 100644 --- a/diesel_derives/src/queryable_by_name.rs +++ b/diesel_derives/src/queryable_by_name.rs @@ -120,7 +120,7 @@ fn sql_type(field: &Field, model: &Model) -> Result { match field.sql_type { Some(AttributeSpanWrapper { item: ref st, .. }) => Ok(st.clone()), None => { - let column_name = field.column_name()?; + let column_name = field.column_name()?.to_ident()?; Ok(parse_quote!(diesel::dsl::SqlTypeOf<#table_name::#column_name>)) } } diff --git a/diesel_derives/src/selectable.rs b/diesel_derives/src/selectable.rs index 5d2684bd8027..7f85863b1121 100644 --- a/diesel_derives/src/selectable.rs +++ b/diesel_derives/src/selectable.rs @@ -151,7 +151,7 @@ fn field_column_ty( Ok(quote!(<#embed_ty as Selectable<__DB>>::SelectExpression)) } else { let table_name = &model.table_names()[0]; - let column_name = field.column_name()?; + let column_name = field.column_name()?.to_ident()?; Ok(quote!(#table_name::#column_name)) } } @@ -165,7 +165,7 @@ fn field_column_inst(field: &Field, model: &Model) -> Result { Ok(quote!(<#embed_ty as Selectable<__DB>>::construct_selection())) } else { let table_name = &model.table_names()[0]; - let column_name = field.column_name()?; + let column_name = field.column_name()?.to_ident()?; Ok(quote!(#table_name::#column_name)) } } diff --git a/diesel_derives/tests/as_changeset.rs b/diesel_derives/tests/as_changeset.rs index b4d7391a363f..544a64cec0aa 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 = "type")] + tipe: &'a str, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -208,13 +294,66 @@ 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("admin")), + ), + ]; + let actual = users::table.order(users::id).load(connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn with_explicit_column_names_raw_type() { + #[derive(AsChangeset)] + #[diesel(table_name = users)] + struct UserForm<'a> { + #[diesel(column_name = name)] + 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(); + + update(users::table.find(1)) + .set(&UserForm { + nombre: "Jim", + color_de_pelo: "blue", + tipe: "super", + }) + .execute(connection) + .unwrap(); + + let expected = vec![ + ( + 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); @@ -248,6 +387,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 +397,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 +427,64 @@ fn tuple_struct() { struct UserForm<'a>( #[diesel(column_name = name)] &'a str, #[diesel(column_name = hair_color)] &'a str, + #[diesel(column_name = "type")] &'a str, + ); + + let connection = &mut connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm("Jim", "blue", "super")) + .execute(connection) + .unwrap(); + + let expected = vec![ + ( + 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); +} + +#[test] +fn tuple_struct_raw_type() { + #[derive(AsChangeset)] + #[diesel(table_name = users)] + 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 +506,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 +537,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 +563,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 +573,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("admin")), + ), ]; let actual = users::table.order(users::id).load(connection); assert_eq!(Ok(expected), actual); @@ -375,6 +605,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 +615,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 +647,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 +656,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 +689,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 +699,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 +729,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 +738,7 @@ fn option_fields_are_skipped() { .set(&UserForm { name: "Jim", hair_color: Some("blue"), + r#type: Some("super"), }) .execute(connection) .unwrap(); @@ -478,13 +746,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 +778,8 @@ fn option_fields_are_assigned_null_when_specified() { #[diesel(treat_none_as_null = false)] name: Option<&'a str>, hair_color: Option<&'a str>, + #[diesel(treat_none_as_null = false)] + r#type: Option<&'a str>, } let connection = &mut connection_with_sean_and_tess_in_users_table(); @@ -507,6 +788,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 +796,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("regular")), + ), + (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..12d97710d85b 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(255) 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(255) 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 65947dfe5cdb..0c48eae171d5 100644 --- a/diesel_derives/tests/insertable.rs +++ b/diesel_derives/tests/insertable.rs @@ -10,12 +10,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) @@ -23,9 +25,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); } @@ -35,12 +41,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) @@ -48,9 +56,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); } @@ -61,12 +73,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) @@ -74,9 +88,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); } @@ -87,12 +105,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) @@ -100,9 +120,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); } @@ -114,12 +138,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()) @@ -127,9 +153,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) @@ -138,8 +168,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); } @@ -152,12 +182,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); } } @@ -168,6 +198,7 @@ test_struct_definition! { struct NewUser { name: String, hair_color: Option, + r#type: Option, } } @@ -176,6 +207,7 @@ test_struct_definition! { pub struct NewUser { name: String, hair_color: Option, + r#type: Option, } } @@ -184,6 +216,7 @@ test_struct_definition! { pub struct NewUser { pub name: String, hair_color: Option, + r#type: Option, } } @@ -192,6 +225,7 @@ test_struct_definition! { pub struct NewUser { name: String, pub hair_color: Option, + r#type: Option, } } @@ -200,6 +234,7 @@ test_struct_definition! { struct NewUser<'a> { name: &'a str, hair_color: Option<&'a str>, + r#type: Option<&'a str>, } } @@ -211,12 +246,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) @@ -224,9 +261,13 @@ 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); } @@ -239,12 +280,15 @@ fn named_struct_with_renamed_option_field() { my_name: String, #[diesel(column_name = hair_color)] my_hair_color: Option, + #[diesel(column_name = "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) @@ -252,9 +296,48 @@ 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); +} + +#[test] +fn named_struct_with_renamed_option_field_raw_type() { + #[derive(Insertable)] + #[diesel(table_name = users)] + struct NewUser { + #[diesel(column_name = name)] + 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) + .execute(conn) + .unwrap(); + + 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); } @@ -265,19 +348,52 @@ fn tuple_struct() { struct NewUser<'a>( #[diesel(column_name = name)] &'a str, #[diesel(column_name = hair_color)] Option<&'a str>, + #[diesel(column_name = "type")] Option<&'a str>, + ); + + let conn = &mut connection(); + 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, 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); +} + +#[test] +fn tuple_struct_raw_type() { + #[derive(Insertable)] + #[diesel(table_name = users)] + 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); } @@ -288,14 +404,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) @@ -303,9 +422,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); } @@ -349,24 +468,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) @@ -374,8 +495,13 @@ 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); } @@ -435,7 +561,9 @@ fn serialize_as_with_option() { .execute(conn) .unwrap(); - let saved = users::table.load::<(i32, String, Option)>(conn); + let saved = users::table + .select((users::id, users::name, users::hair_color)) + .load::<(i32, String, Option)>(conn); let expected = vec![(1, "Sean".to_string(), Some("Black".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..d865ebba8804 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,27 @@ fn tuple_struct() { struct MyStruct( #[diesel(column_name = foo)] i32, #[diesel(column_name = bar)] i32, + #[diesel(column_name = "type")] i32, + ); + + let conn = &mut connection(); + 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] +fn tuple_struct_raw_type() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + #[diesel(table_name = my_structs)] + 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 +71,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 +96,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 +139,8 @@ fn embedded_struct() { foo: i32, #[diesel(embed)] b: B, + #[diesel(embed)] + t: T, } #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] @@ -105,12 +149,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 +175,8 @@ fn embedded_option() { foo: i32, #[diesel(embed)] b: Option, + #[diesel(embed)] + t: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] @@ -132,15 +185,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); + let data = sql_query("SELECT 1 AS foo, 2 AS bar, 3 AS type").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..b7864e13cb86 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,24 @@ fn tuple_struct() { struct MyStruct( #[diesel(column_name = foo)] i32, #[diesel(column_name = bar)] i32, + #[diesel(column_name = "type")] i32, + ); + + let conn = &mut connection(); + let data = my_structs::table + .select(MyStruct::as_select()) + .get_result(conn); + assert!(data.is_err()); +} + +#[test] +fn tuple_struct_raw_type() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable, Selectable)] + #[diesel(table_name = my_structs)] + struct MyStruct( + #[diesel(column_name = foo)] i32, + #[diesel(column_name = bar)] i32, + #[diesel(column_name = r#type)] i32, ); let conn = &mut connection(); @@ -51,10 +71,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 +85,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 +106,8 @@ fn embedded_option() { foo: i32, #[diesel(embed)] b: Option, + #[diesel(embed)] + t: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable, Selectable)] @@ -86,6 +116,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 +133,7 @@ fn embedded_option_with_nullable_field() { my_structs (foo) { foo -> Integer, bar -> Nullable, + r#type -> Nullable, } } @@ -106,6 +143,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 +153,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 +171,8 @@ fn manually_specified_expression() { foo -> Integer, bar -> Nullable, some_int -> Nullable, + r#type -> Nullable, + another_int -> Nullable, } } @@ -143,6 +190,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 +214,7 @@ fn check_for_backend_with_deserialize_as() { tests { id -> Integer, name -> Text, + r#type -> Text, } } @@ -174,6 +232,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 +245,7 @@ fn check_with_lifetime_and_type_param() { test { id -> Integer, name -> Text, + r#type -> Text, } } @@ -194,6 +255,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)] diff --git a/diesel_table_macro_syntax/src/lib.rs b/diesel_table_macro_syntax/src/lib.rs index c1f8cf2b6869..dbbd9d2f437f 100644 --- a/diesel_table_macro_syntax/src/lib.rs +++ b/diesel_table_macro_syntax/src/lib.rs @@ -121,8 +121,17 @@ fn get_sql_name( syn::Lit::Str(lit_str) => Some(lit_str), _ => None, })? { - None => fallback_ident.to_string(), - Some(str_lit) => str_lit.value(), + None => { + use syn::ext::IdentExt; + fallback_ident.unraw().to_string() + } + Some(str_lit) => { + let mut str_lit = str_lit.value(); + if str_lit.starts_with("r#") { + str_lit.drain(..2); + } + str_lit + } }, ) }