Skip to content

Commit 3db4ed2

Browse files
authored
Merge pull request #4030 from z33ky/raw-sqlidentifier
Allow raw identifiers for SqlIdentifier (column-name)
2 parents ee44189 + 5a044a6 commit 3db4ed2

16 files changed

+756
-146
lines changed

.typos.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ extend-ignore-re = [
2020
"cannot find value `titel` in module `posts`",
2121
"cannot find type `titel` in module `posts`",
2222
"[0-9]+[[:space]]+|[[:space:]]+titel: String",
23-
"big_sur"
23+
"big_sur",
24+
# That's Spanish for "type" (used in a unit-test)
25+
"tipe",
2426
]
2527

2628
[type.md]

diesel_compile_tests/tests/fail/derive/bad_column_name.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ table! {
55
users {
66
id -> Integer,
77
name -> Text,
8-
#[sql_name = "type"]
9-
tpe -> Text,
8+
#[sql_name = "spa ce"]
9+
space -> Text,
1010
}
1111
}
1212

@@ -35,16 +35,16 @@ struct User3 {
3535
#[derive(Insertable)]
3636
#[diesel(table_name = users)]
3737
struct User4 {
38-
#[diesel(column_name = "type")]
39-
ty: String,
38+
#[diesel(column_name = "spa ce")]
39+
space: String,
4040
}
4141

4242

4343
#[derive(AsChangeset)]
4444
#[diesel(table_name = users)]
4545
struct User5 {
46-
#[diesel(column_name = "type")]
47-
ty: String,
46+
#[diesel(column_name = "spa ce")]
47+
space: String,
4848
}
4949

5050
fn main() {}

diesel_compile_tests/tests/fail/derive/bad_column_name.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ error: expected string literal
1717
30 | #[diesel(column_name = true)]
1818
| ^^^^
1919

20-
error: Expected valid identifier, found `type`. Diesel automatically renames invalid identifiers, perhaps you meant to write `type_`?
20+
error: Expected valid identifier, found `spa ce`. Diesel does not support column names with whitespaces yet
2121
--> tests/fail/derive/bad_column_name.rs:38:28
2222
|
23-
38 | #[diesel(column_name = "type")]
24-
| ^^^^^^
23+
38 | #[diesel(column_name = "spa ce")]
24+
| ^^^^^^^^
2525

26-
error: Expected valid identifier, found `type`. Diesel automatically renames invalid identifiers, perhaps you meant to write `type_`?
26+
error: Expected valid identifier, found `spa ce`. Diesel does not support column names with whitespaces yet
2727
--> tests/fail/derive/bad_column_name.rs:46:28
2828
|
29-
46 | #[diesel(column_name = "type")]
30-
| ^^^^^^
29+
46 | #[diesel(column_name = "spa ce")]
30+
| ^^^^^^^^

diesel_derives/src/as_changeset.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,7 @@ fn field_changeset_ty(
157157
lifetime: Option<TokenStream>,
158158
treat_none_as_null: bool,
159159
) -> Result<TokenStream> {
160-
let column_name = field.column_name()?;
161-
column_name.valid_ident()?;
160+
let column_name = field.column_name()?.to_ident()?;
162161
if !treat_none_as_null && is_option_ty(&field.ty) {
163162
let field_ty = inner_of_option_ty(&field.ty);
164163
Ok(
@@ -177,8 +176,7 @@ fn field_changeset_expr(
177176
treat_none_as_null: bool,
178177
) -> Result<TokenStream> {
179178
let field_name = &field.name;
180-
let column_name = field.column_name()?;
181-
column_name.valid_ident()?;
179+
let column_name = field.column_name()?.to_ident()?;
182180
if !treat_none_as_null && is_option_ty(&field.ty) {
183181
if lifetime.is_some() {
184182
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(
196194
ty: &Type,
197195
treat_none_as_null: bool,
198196
) -> Result<TokenStream> {
199-
let column_name = field.column_name()?;
200-
column_name.valid_ident()?;
197+
let column_name = field.column_name()?.to_ident()?;
201198
if !treat_none_as_null && is_option_ty(&field.ty) {
202199
let inner_ty = inner_of_option_ty(ty);
203200
Ok(quote!(std::option::Option<diesel::dsl::Eq<#table_name::#column_name, #inner_ty>>))
@@ -213,8 +210,7 @@ fn field_changeset_expr_serialize_as(
213210
treat_none_as_null: bool,
214211
) -> Result<TokenStream> {
215212
let field_name = &field.name;
216-
let column_name = field.column_name()?;
217-
column_name.valid_ident()?;
213+
let column_name = field.column_name()?.to_ident()?;
218214
let column: Expr = parse_quote!(#table_name::#column_name);
219215
if !treat_none_as_null && is_option_ty(&field.ty) {
220216
Ok(quote!(self.#field_name.map(|x| #column.eq(::std::convert::Into::<#ty>::into(x)))))

diesel_derives/src/attrs.rs

+28-8
Original file line numberDiff line numberDiff line change
@@ -56,32 +56,50 @@ impl SqlIdentifier {
5656
self.span
5757
}
5858

59-
pub fn valid_ident(&self) -> Result<()> {
60-
if syn::parse_str::<Ident>(&self.field_name).is_err() {
61-
Err(syn::Error::new(
59+
pub fn to_ident(&self) -> Result<Ident> {
60+
match syn::parse_str::<Ident>(&format!("r#{}", self.field_name)) {
61+
Ok(mut ident) => {
62+
ident.set_span(self.span);
63+
Ok(ident)
64+
}
65+
Err(_e) if self.field_name.contains(' ') => Err(syn::Error::new(
66+
self.span(),
67+
format!(
68+
"Expected valid identifier, found `{0}`. \
69+
Diesel does not support column names with whitespaces yet",
70+
self.field_name
71+
),
72+
)),
73+
Err(_e) => Err(syn::Error::new(
6274
self.span(),
6375
format!(
6476
"Expected valid identifier, found `{0}`. \
6577
Diesel automatically renames invalid identifiers, \
6678
perhaps you meant to write `{0}_`?",
6779
self.field_name
6880
),
69-
))
70-
} else {
71-
Ok(())
81+
)),
7282
}
7383
}
7484
}
7585

7686
impl ToTokens for SqlIdentifier {
7787
fn to_tokens(&self, tokens: &mut TokenStream) {
78-
Ident::new(&self.field_name, self.span).to_tokens(tokens)
88+
if self.field_name.starts_with("r#") {
89+
Ident::new_raw(&self.field_name[2..], self.span).to_tokens(tokens)
90+
} else {
91+
Ident::new(&self.field_name, self.span).to_tokens(tokens)
92+
}
7993
}
8094
}
8195

8296
impl Display for SqlIdentifier {
8397
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
84-
f.write_str(&self.field_name)
98+
let mut start = 0;
99+
if self.field_name.starts_with("r#") {
100+
start = 2;
101+
}
102+
f.write_str(&self.field_name[start..])
85103
}
86104
}
87105

@@ -93,6 +111,8 @@ impl PartialEq<Ident> for SqlIdentifier {
93111

94112
impl From<&'_ Ident> for SqlIdentifier {
95113
fn from(ident: &'_ Ident) -> Self {
114+
use syn::ext::IdentExt;
115+
let ident = ident.unraw();
96116
Self {
97117
span: ident.span(),
98118
field_name: ident.to_string(),

diesel_derives/src/insertable.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,7 @@ fn field_ty_serialize_as(
194194
ty: &Type,
195195
treat_none_as_default_value: bool,
196196
) -> Result<TokenStream> {
197-
let column_name = field.column_name()?;
198-
column_name.valid_ident()?;
197+
let column_name = field.column_name()?.to_ident()?;
199198
let span = field.span;
200199
if treat_none_as_default_value {
201200
let inner_ty = inner_of_option_ty(ty);
@@ -223,8 +222,7 @@ fn field_expr_serialize_as(
223222
treat_none_as_default_value: bool,
224223
) -> Result<TokenStream> {
225224
let field_name = &field.name;
226-
let column_name = field.column_name()?;
227-
column_name.valid_ident()?;
225+
let column_name = field.column_name()?.to_ident()?;
228226
let column = quote!(#table_name::#column_name);
229227
if treat_none_as_default_value {
230228
if is_option_ty(ty) {
@@ -245,8 +243,7 @@ fn field_ty(
245243
lifetime: Option<TokenStream>,
246244
treat_none_as_default_value: bool,
247245
) -> Result<TokenStream> {
248-
let column_name = field.column_name()?;
249-
column_name.valid_ident()?;
246+
let column_name = field.column_name()?.to_ident()?;
250247
let span = field.span;
251248
if treat_none_as_default_value {
252249
let inner_ty = inner_of_option_ty(&field.ty);
@@ -276,8 +273,7 @@ fn field_expr(
276273
treat_none_as_default_value: bool,
277274
) -> Result<TokenStream> {
278275
let field_name = &field.name;
279-
let column_name = field.column_name()?;
280-
column_name.valid_ident()?;
276+
let column_name = field.column_name()?.to_ident()?;
281277

282278
let column: Expr = parse_quote!(#table_name::#column_name);
283279
if treat_none_as_default_value {

diesel_derives/src/queryable_by_name.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ fn sql_type(field: &Field, model: &Model) -> Result<Type> {
120120
match field.sql_type {
121121
Some(AttributeSpanWrapper { item: ref st, .. }) => Ok(st.clone()),
122122
None => {
123-
let column_name = field.column_name()?;
123+
let column_name = field.column_name()?.to_ident()?;
124124
Ok(parse_quote!(diesel::dsl::SqlTypeOf<#table_name::#column_name>))
125125
}
126126
}

diesel_derives/src/selectable.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ fn field_column_ty(
151151
Ok(quote!(<#embed_ty as Selectable<__DB>>::SelectExpression))
152152
} else {
153153
let table_name = &model.table_names()[0];
154-
let column_name = field.column_name()?;
154+
let column_name = field.column_name()?.to_ident()?;
155155
Ok(quote!(#table_name::#column_name))
156156
}
157157
}
@@ -165,7 +165,7 @@ fn field_column_inst(field: &Field, model: &Model) -> Result<TokenStream> {
165165
Ok(quote!(<#embed_ty as Selectable<__DB>>::construct_selection()))
166166
} else {
167167
let table_name = &model.table_names()[0];
168-
let column_name = field.column_name()?;
168+
let column_name = field.column_name()?.to_ident()?;
169169
Ok(quote!(#table_name::#column_name))
170170
}
171171
}

0 commit comments

Comments
 (0)