sqlmodel_core/
identifiers.rs1#[inline]
22pub fn quote_ident(name: &str) -> String {
23 format!("\"{}\"", name.replace('"', "\"\""))
24}
25
26#[inline]
40pub fn quote_ident_mysql(name: &str) -> String {
41 format!("`{}`", name.replace('`', "``"))
42}
43
44#[inline]
63pub fn sanitize_identifier(name: &str) -> String {
64 name.chars()
65 .filter(|c| c.is_ascii_alphanumeric() || *c == '_')
66 .collect()
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
76 fn test_quote_ident_simple() {
77 assert_eq!(quote_ident("users"), "\"users\"");
78 }
79
80 #[test]
81 fn test_quote_ident_empty() {
82 assert_eq!(quote_ident(""), "\"\"");
84 }
85
86 #[test]
87 fn test_quote_ident_embedded_double_quote() {
88 assert_eq!(quote_ident("user\"name"), "\"user\"\"name\"");
89 }
90
91 #[test]
92 fn test_quote_ident_multiple_quotes() {
93 assert_eq!(quote_ident("a\"b\"c"), "\"a\"\"b\"\"c\"");
94 }
95
96 #[test]
97 fn test_quote_ident_sql_keyword() {
98 assert_eq!(quote_ident("select"), "\"select\"");
100 assert_eq!(quote_ident("from"), "\"from\"");
101 assert_eq!(quote_ident("where"), "\"where\"");
102 }
103
104 #[test]
105 fn test_quote_ident_spaces() {
106 assert_eq!(quote_ident("first name"), "\"first name\"");
107 }
108
109 #[test]
110 fn test_quote_ident_unicode() {
111 assert_eq!(quote_ident("用户"), "\"用户\"");
112 assert_eq!(quote_ident("naïve"), "\"naïve\"");
113 }
114
115 #[test]
116 fn test_quote_ident_semicolon() {
117 assert_eq!(quote_ident("a;b"), "\"a;b\"");
119 }
120
121 #[test]
122 fn test_quote_ident_newline() {
123 assert_eq!(quote_ident("a\nb"), "\"a\nb\"");
125 }
126
127 #[test]
128 fn test_quote_ident_null_byte() {
129 assert_eq!(quote_ident("a\0b"), "\"a\0b\"");
131 }
132
133 #[test]
134 fn test_quote_ident_backslash() {
135 assert_eq!(quote_ident("a\\b"), "\"a\\b\"");
137 }
138
139 #[test]
140 fn test_quote_ident_only_quotes() {
141 assert_eq!(quote_ident("\"\""), "\"\"\"\"\"\"");
143 }
144
145 #[test]
146 fn test_quote_ident_sql_injection_attempt() {
147 let malicious = "users\"; DROP TABLE secrets; --";
149 let quoted = quote_ident(malicious);
150 assert_eq!(quoted, "\"users\"\"; DROP TABLE secrets; --\"");
151 }
153
154 #[test]
157 fn test_quote_ident_mysql_simple() {
158 assert_eq!(quote_ident_mysql("users"), "`users`");
159 }
160
161 #[test]
162 fn test_quote_ident_mysql_embedded_backtick() {
163 assert_eq!(quote_ident_mysql("user`name"), "`user``name`");
164 }
165
166 #[test]
167 fn test_quote_ident_mysql_keyword() {
168 assert_eq!(quote_ident_mysql("select"), "`select`");
169 }
170
171 #[test]
172 fn test_quote_ident_mysql_multiple_backticks() {
173 assert_eq!(quote_ident_mysql("a`b`c"), "`a``b``c`");
174 }
175
176 #[test]
177 fn test_quote_ident_mysql_empty() {
178 assert_eq!(quote_ident_mysql(""), "``");
179 }
180
181 #[test]
184 fn test_sanitize_simple() {
185 assert_eq!(sanitize_identifier("users"), "users");
186 }
187
188 #[test]
189 fn test_sanitize_strips_quotes() {
190 assert_eq!(sanitize_identifier("user\"name"), "username");
191 }
192
193 #[test]
194 fn test_sanitize_strips_semicolons() {
195 assert_eq!(sanitize_identifier("a;b"), "ab");
196 }
197
198 #[test]
199 fn test_sanitize_preserves_underscore() {
200 assert_eq!(sanitize_identifier("user_name"), "user_name");
201 }
202
203 #[test]
204 fn test_sanitize_strips_spaces() {
205 assert_eq!(sanitize_identifier("user name"), "username");
206 }
207
208 #[test]
209 fn test_sanitize_empty_input() {
210 assert_eq!(sanitize_identifier(""), "");
211 }
212
213 #[test]
214 fn test_sanitize_only_invalid_chars() {
215 assert_eq!(sanitize_identifier("!@#$%"), "");
216 }
217
218 #[test]
219 fn test_sanitize_sql_injection_attempt() {
220 assert_eq!(
221 sanitize_identifier("users; DROP TABLE secrets; --"),
222 "usersDROPTABLEsecrets"
223 );
224 }
225
226 #[test]
227 fn test_sanitize_unicode_stripped() {
228 assert_eq!(sanitize_identifier("用户"), "");
230 assert_eq!(sanitize_identifier("naïve"), "nave");
231 }
232
233 #[test]
234 fn test_sanitize_numbers_preserved() {
235 assert_eq!(sanitize_identifier("table123"), "table123");
236 assert_eq!(sanitize_identifier("123table"), "123table");
237 }
238}