1use crate::{baquote, brquote, dquote, quote};
2
3#[macro_export]
25macro_rules! name {
26 ( $n:expr ) => {
27 {
28 SqlName::new( $n ).safe()
29 }
30 };
31 ( $n:expr, $( $x:expr ),* ) => {
32 {
33 SqlName::new( $n )
34 $(
35 .add( $x )
36 )*
37 .safe()
38 }
39 };
40 ( $n:expr; $a:expr ) => {
41 {
42 SqlName::new( $n ).alias( $a ).safe()
43 }
44 };
45 ( $n:expr, $( $x:expr ),*; $a:expr ) => {
46 {
47 SqlName::new( $n )
48 $(
49 .add( $x )
50 )*
51 .alias( $a )
52 .safe()
53 }
54 };
55}
56
57#[macro_export]
80macro_rules! qname {
81 ( $n:expr ) => {
82 {
83 SqlName::new( $n ).quoted()
84 }
85 };
86 ( $n:expr, $( $x:expr ),* ) => {
87 {
88 SqlName::new( $n )
89 $(
90 .add( $x )
91 )*
92 .quoted()
93 }
94 };
95 ( $n:expr; $a:expr ) => {
96 {
97 SqlName::new( $n ).alias( $a ).quoted()
98 }
99 };
100 ( $n:expr, $( $x:expr ),*; $a:expr ) => {
101 {
102 SqlName::new( $n )
103 $(
104 .add( $x )
105 )*
106 .alias( $a )
107 .quoted()
108 }
109 };
110}
111
112#[macro_export]
135macro_rules! baname {
136 ( $n:expr ) => {
137 {
138 SqlName::new( $n ).baquoted()
139 }
140 };
141 ( $n:expr, $( $x:expr ),* ) => {
142 {
143 SqlName::new( $n )
144 $(
145 .add( $x )
146 )*
147 .baquoted()
148 }
149 };
150 ( $n:expr; $a:expr ) => {
151 {
152 SqlName::new( $n ).alias( $a ).baquoted()
153 }
154 };
155 ( $n:expr, $( $x:expr ),*; $a:expr ) => {
156 {
157 SqlName::new( $n )
158 $(
159 .add( $x )
160 )*
161 .alias( $a )
162 .baquoted()
163 }
164 };
165}
166
167#[macro_export]
190macro_rules! brname {
191 ( $n:expr ) => {
192 {
193 SqlName::new( $n ).brquoted()
194 }
195 };
196 ( $n:expr, $( $x:expr ),* ) => {
197 {
198 SqlName::new( $n )
199 $(
200 .add( $x )
201 )*
202 .brquoted()
203 }
204 };
205 ( $n:expr; $a:expr ) => {
206 {
207 SqlName::new( $n ).alias( $a ).brquoted()
208 }
209 };
210 ( $n:expr, $( $x:expr ),*; $a:expr ) => {
211 {
212 SqlName::new( $n )
213 $(
214 .add( $x )
215 )*
216 .alias( $a )
217 .brquoted()
218 }
219 };
220}
221
222#[macro_export]
245macro_rules! dname {
246 ( $n:expr ) => {
247 {
248 SqlName::new( $n ).dquoted()
249 }
250 };
251 ( $n:expr, $( $x:expr ),* ) => {
252 {
253 SqlName::new( $n )
254 $(
255 .add( $x )
256 )*
257 .dquoted()
258 }
259 };
260 ( $n:expr; $a:expr ) => {
261 {
262 SqlName::new( $n ).alias( $a ).dquoted()
263 }
264 };
265 ( $n:expr, $( $x:expr ),*; $a:expr ) => {
266 {
267 SqlName::new( $n )
268 $(
269 .add( $x )
270 )*
271 .alias( $a )
272 .dquoted()
273 }
274 };
275}
276
277#[derive(Clone)]
318pub struct SqlName {
319 parts: Vec<String>,
320 alias: Option<String>,
321}
322
323impl SqlName {
324 pub fn new<S: ToString>(name: S) -> Self {
326 Self {
327 parts: vec![name.to_string()],
328 alias: None,
329 }
330 }
331
332 pub fn add<S: ToString>(&mut self, name: S) -> &mut Self {
334 self.parts.push(name.to_string());
335 self
336 }
337
338 pub fn alias<S: ToString>(&mut self, alias: S) -> &mut Self {
340 self.alias = Some(alias.to_string());
341 self
342 }
343
344 pub fn safe(&self) -> String {
346 let safe_name = self.make_safe_parts().join(".");
347 self.join_with_alias(safe_name)
348 }
349
350 pub fn quoted(&self) -> String {
352 let safe_name = self
353 .parts
354 .iter()
355 .map(quote)
356 .collect::<Vec<String>>()
357 .join(".");
358 self.join_with_alias(safe_name)
359 }
360
361 pub fn baquoted(&self) -> String {
363 let safe_name = self
364 .parts
365 .iter()
366 .map(baquote)
367 .collect::<Vec<String>>()
368 .join(".");
369 self.join_with_alias(safe_name)
370 }
371
372 pub fn brquoted(&self) -> String {
374 let safe_name = self
375 .parts
376 .iter()
377 .map(brquote)
378 .collect::<Vec<String>>()
379 .join(".");
380 self.join_with_alias(safe_name)
381 }
382
383 pub fn dquoted(&self) -> String {
385 let safe_name = self
386 .parts
387 .iter()
388 .map(dquote)
389 .collect::<Vec<String>>()
390 .join(".");
391 self.join_with_alias(safe_name)
392 }
393
394 fn join_with_alias(&self, safe_name: String) -> String {
396 match &self.alias {
397 Some(alias) => {
398 let safe_alias = Self::make_safe_name(&alias);
399 format!("{} AS {}", safe_name, safe_alias)
400 }
401 None => safe_name,
402 }
403 }
404
405 fn make_safe_parts(&self) -> Vec<String> {
407 if self.all_is_safe() {
408 self.parts.clone()
409 } else {
410 self.parts.iter().map(baquote).collect()
411 }
412 }
413
414 fn make_safe_name(name: &str) -> String {
416 if Self::is_safe(&name) {
417 name.to_string()
418 } else {
419 baquote(name)
420 }
421 }
422
423 fn is_safe(name: &str) -> bool {
425 name.chars()
426 .all(|c| matches!(c, 'a'..='z' | '0'..='9' | '_'))
427 }
428
429 fn all_is_safe(&self) -> bool {
431 self.parts.iter().all(|name| Self::is_safe(&name))
432 }
433}
434
435#[cfg(test)]
436mod tests {
437 use super::*;
438 use anyhow::Result;
439
440 #[test]
441 fn test_simple_name() -> Result<()> {
442 let name = SqlName::new("safe_name").safe();
443 assert_eq!(&name, "safe_name");
444
445 let name = SqlName::new("safe_name").alias("sn").safe();
446 assert_eq!(&name, "safe_name AS sn");
447
448 let name = name!("safe_name");
449 assert_eq!(&name, "safe_name");
450
451 let name = name!("safe_name"; "sn");
452 assert_eq!(&name, "safe_name AS sn");
453
454 Ok(())
455 }
456
457 #[test]
458 fn test_spaced_name() -> Result<()> {
459 let name = SqlName::new("spaced name").safe();
460 assert_eq!(&name, "`spaced name`");
461
462 let name = SqlName::new("spaced name").alias("s n").safe();
463 assert_eq!(&name, "`spaced name` AS `s n`");
464
465 let name = name!("spaced name");
466 assert_eq!(&name, "`spaced name`");
467
468 let name = name!("spaced name"; "s n");
469 assert_eq!(&name, "`spaced name` AS `s n`");
470
471 Ok(())
472 }
473
474 #[test]
475 fn test_quoted_name() -> Result<()> {
476 let name = SqlName::new("some 'awesome' name").quoted();
477 assert_eq!(&name, "'some ''awesome'' name'");
478
479 let name = SqlName::new("some 'awesome' name")
480 .alias("awesome name")
481 .quoted();
482 assert_eq!(&name, "'some ''awesome'' name' AS `awesome name`");
483
484 let name = SqlName::new("some 'awesome' name")
485 .add("sub")
486 .alias("awesome name")
487 .quoted();
488 assert_eq!(&name, "'some ''awesome'' name'.'sub' AS `awesome name`");
489
490 let name = qname!("some 'awesome' name");
491 assert_eq!(&name, "'some ''awesome'' name'");
492
493 let name = qname!("some 'awesome' name"; "awesome name");
494 assert_eq!(&name, "'some ''awesome'' name' AS `awesome name`");
495
496 let name = qname!("some 'awesome' name", "sub"; "awesome name");
497 assert_eq!(&name, "'some ''awesome'' name'.'sub' AS `awesome name`");
498
499 Ok(())
500 }
501
502 #[test]
503 fn test_baquoted_name() -> Result<()> {
504 let name = SqlName::new("safe_name").baquoted();
505 assert_eq!(&name, "`safe_name`");
506
507 let name = SqlName::new("safe_name").alias("sn").baquoted();
508 assert_eq!(&name, "`safe_name` AS sn");
509
510 let name = SqlName::new("safe_name").add("sub").alias("sn").baquoted();
511 assert_eq!(&name, "`safe_name`.`sub` AS sn");
512
513 let name = baname!("safe_name");
514 assert_eq!(&name, "`safe_name`");
515
516 let name = baname!("safe_name"; "sn");
517 assert_eq!(&name, "`safe_name` AS sn");
518
519 let name = baname!("safe_name", "sub"; "sn");
520 assert_eq!(&name, "`safe_name`.`sub` AS sn");
521
522 Ok(())
523 }
524
525 #[test]
526 fn test_brquoted_name() -> Result<()> {
527 let name = SqlName::new("safe_name").brquoted();
528 assert_eq!(&name, "[safe_name]");
529
530 let name = SqlName::new("safe_name").alias("sn").brquoted();
531 assert_eq!(&name, "[safe_name] AS sn");
532
533 let name = SqlName::new("safe_name").add("sub").alias("sn").brquoted();
534 assert_eq!(&name, "[safe_name].[sub] AS sn");
535
536 let name = brname!("safe_name");
537 assert_eq!(&name, "[safe_name]");
538
539 let name = brname!("safe_name"; "sn");
540 assert_eq!(&name, "[safe_name] AS sn");
541
542 let name = brname!("safe_name", "sub"; "sn");
543 assert_eq!(&name, "[safe_name].[sub] AS sn");
544
545 Ok(())
546 }
547
548 #[test]
549 fn test_dquoted_name() -> Result<()> {
550 let name = SqlName::new("safe_name").dquoted();
551 assert_eq!(&name, "\"safe_name\"");
552
553 let name = SqlName::new("safe_name").alias("sn").dquoted();
554 assert_eq!(&name, "\"safe_name\" AS sn");
555
556 let name = SqlName::new("safe_name").add("sub").alias("sn").dquoted();
557 assert_eq!(&name, "\"safe_name\".\"sub\" AS sn");
558
559 let name = dname!("safe_name");
560 assert_eq!(&name, "\"safe_name\"");
561
562 let name = dname!("safe_name"; "sn");
563 assert_eq!(&name, "\"safe_name\" AS sn");
564
565 let name = dname!("safe_name", "sub"; "sn");
566 assert_eq!(&name, "\"safe_name\".\"sub\" AS sn");
567
568 Ok(())
569 }
570}