fire_postgres/query/
whr.rs

1/// Possible operators
2/// - = | != | < | <= | > | >= | LIKE | IN
3/// - AND | OR
4/// ## Example
5/// ```
6/// use fire_postgres::whr;
7/// let a = &"val".to_string();
8/// let b = &"val2".to_string();
9/// let c: &Option<String> = &None;
10/// let query = whr!(a AND "b" != b OR c);
11///
12/// assert_eq!(r#""a" = $1 AND "b" != $2 OR "c" IS NULL"#, query.sql().to_string().trim());
13/// ```
14#[macro_export]
15macro_rules! whr {
16	($($tt:tt)*) => ({
17		#[allow(unused_mut)]
18		let mut params = vec![];
19		let mut sql = $crate::query::SqlBuilder::new();
20		$crate::whr_comp!(sql, params, $($tt)*);
21		$crate::query::Query::new(sql, params)
22	})
23}
24
25#[macro_export]
26macro_rules! whr_comp {
27	($s:ident, $p:ident,) => ();
28	($s:ident, $p:ident, LIMIT $value:tt $($tt:tt)*) => ({
29		let v: usize = $value;
30		$s.space(format!("LIMIT {}", v));
31		$crate::whr_comp!($s, $p, $($tt)*);
32	});
33	// value should not be a user input
34	($s:ident, $p:ident, ORDER $value:tt ASC $($tt:tt)*) => ({
35		$s.space(format!("ORDER BY \"{}\" ASC", $value));
36		$crate::whr_comp!($s, $p, $($tt)*);
37	});
38	// value should not be a user input
39	($s:ident, $p:ident, ORDER $value:tt DESC $($tt:tt)*) => ({
40		$s.space(format!("ORDER BY \"{}\" DESC", $value));
41		$crate::whr_comp!($s, $p, $($tt)*);
42	});
43	($s:ident, $p:ident, $id:ident $($tt:tt)*) => (
44		$crate::short_whr_comp!($s, $p, stringify!($id), "=", $id, $($tt)*);
45	);
46	($s:ident, $p:ident, $name:tt = $value:tt $($tt:tt)*) => (
47		$crate::short_whr_comp!($s, $p, $name, "=", $value, $($tt)*);
48	);
49	($s:ident, $p:ident, $name:tt != $value:tt $($tt:tt)*) => (
50		$crate::short_whr_comp!($s, $p, $name, "!=", $value, $($tt)*);
51	);
52	($s:ident, $p:ident, $name:tt < $value:tt $($tt:tt)*) => (
53		$crate::short_whr_comp!($s, $p, $name, "<", $value, $($tt)*);
54	);
55	($s:ident, $p:ident, $name:tt <= $value:tt $($tt:tt)*) => (
56		$crate::short_whr_comp!($s, $p, $name, "<=", $value, $($tt)*);
57	);
58	($s:ident, $p:ident, $name:tt > $value:tt $($tt:tt)*) => (
59		$crate::short_whr_comp!($s, $p, $name, ">", $value, $($tt)*);
60	);
61	($s:ident, $p:ident, $name:tt >= $value:tt $($tt:tt)*) => (
62		$crate::short_whr_comp!($s, $p, $name, ">=", $value, $($tt)*);
63	);
64	($s:ident, $p:ident, $name:tt LIKE $value:tt $($tt:tt)*) => (
65		$crate::short_whr_comp!($s, $p, $name, "LIKE", $value, $($tt)*);
66	);
67	($s:ident, $p:ident, $name:tt ~ $value:tt $($tt:tt)*) => (
68		$crate::short_whr_comp!($s, $p, $name, "LIKE", format!("%{}%", $value), $($tt)*);
69	);
70	($s:ident, $p:ident, $name:tt ~= $value:tt $($tt:tt)*) => (
71		$crate::short_whr_comp!($s, $p, $name, "LIKE", format!("%{}", $value), $($tt)*);
72	);
73	($s:ident, $p:ident, $name:tt =~ $value:tt $($tt:tt)*) => (
74		$crate::short_whr_comp!($s, $p, $name, "LIKE", format!("{}%", $value), $($tt)*);
75	);
76	($s:ident, $p:ident, $name:tt IN $value:tt $($tt:tt)*) => (
77		$crate::short_whr_in_comp!($s, $p, $name, $value, $($tt)*);
78	);
79}
80
81#[macro_export]
82macro_rules! short_whr_comp {
83	($s:ident, $p:ident, $name:expr, $symb:expr, $value:expr, $($tt:tt)*) => (
84		let param = $crate::query::Param::new($name, $value);
85
86		let mut cont = true;
87
88		// todo: can this become a noop
89		if param.maybe_null() && param.data().is_null() {
90			match $symb {
91				"=" => {
92					$s.space_after(format!("\"{}\" IS NULL", $name));
93					cont = false;
94				},
95				"!=" => {
96					$s.space_after(format!("\"{}\" IS NOT NULL", $name));
97					cont = false;
98				},
99				_ => {}
100			}
101		}
102
103		if cont {
104			$s.space_after(format!("\"{}\" {}", $name, $symb));
105			$s.param();
106			$p.push(param);
107		}
108
109		$crate::whr_log!($s, $p, $($tt)*);
110	);
111	// LIMIT
112}
113
114#[macro_export]
115macro_rules! short_whr_in_comp {
116	($s:ident, $p:ident, $name:expr, $value:expr, $($tt:tt)*) => (
117		if $value.iter().len() > 0 {
118			$s.no_space(format!("\"{}\" IN (", $name));
119			let end = $value.iter().len() - 1;
120			for (i, v) in $value.iter().enumerate() {
121				$s.param();
122				$p.push($crate::query::Param::new($name, v));
123				if i != end {
124					$s.space_after(",");
125				}
126			}
127			$s.no_space(")");
128		} else {
129			$s.space_after(format!("\"{}\" IS NULL", $name));
130		}
131		$crate::whr_log!($s, $p, $($tt)*);
132	);
133}
134
135#[macro_export]
136macro_rules! whr_log {
137	($s:ident, $p:ident, AND $($tt:tt)+) => (
138		$s.space("AND");
139		$crate::whr_comp!($s, $p, $($tt)+);
140	);
141	($s:ident, $p:ident, OR $($tt:tt)+) => (
142		$s.space("OR");
143		$crate::whr_comp!($s, $p, $($tt)+);
144	);
145	($s:ident, $p:ident, LIMIT $($tt:tt)+) => (
146		$crate::whr_comp!($s, $p, LIMIT $($tt)+);
147	);
148	($s:ident, $p:ident, ORDER $($tt:tt)+) => (
149		$crate::whr_comp!($s, $p, ORDER $($tt)+);
150	);
151	($s:ident, $p:ident,) => ();
152}
153
154#[cfg(test)]
155mod tests {
156	use crate::UniqueId;
157
158	#[test]
159	fn test_limit() {
160		let id = &UniqueId::new();
161		let limit = 10;
162		let query = whr!(id LIMIT limit);
163		assert_eq!(query.sql.to_string().trim(), "\"id\" = $1 LIMIT 10");
164	}
165
166	#[test]
167	fn test_order() {
168		let id = &UniqueId::new();
169		let limit = 10;
170		let query = whr!(id ORDER "id" ASC LIMIT limit);
171		assert_eq!(
172			query.sql.to_string().trim(),
173			"\"id\" = $1 ORDER BY \"id\" ASC  LIMIT 10"
174		);
175		let query = whr!(ORDER "id" DESC);
176		assert_eq!(query.sql.to_string().trim(), "ORDER BY \"id\" DESC");
177	}
178}