ferriorm_runtime/
query.rs1use crate::client::DatabaseClient;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum ParamStyle {
14 Dollar,
16 QuestionMark,
18}
19
20impl ParamStyle {
21 #[must_use]
23 pub fn from_client(client: &DatabaseClient) -> Self {
24 match client {
25 #[cfg(feature = "postgres")]
26 DatabaseClient::Postgres(_) => Self::Dollar,
27 #[cfg(feature = "sqlite")]
28 DatabaseClient::Sqlite(_) => Self::QuestionMark,
29 }
30 }
31}
32
33#[derive(Debug)]
42pub struct SqlBuilder {
43 sql: String,
44 param_count: usize,
45 style: ParamStyle,
46}
47
48impl SqlBuilder {
49 #[must_use]
51 pub fn new(style: ParamStyle) -> Self {
52 Self {
53 sql: String::with_capacity(256),
54 param_count: 0,
55 style,
56 }
57 }
58
59 #[must_use]
61 pub fn for_client(client: &DatabaseClient) -> Self {
62 Self::new(ParamStyle::from_client(client))
63 }
64
65 pub fn push(&mut self, sql: &str) {
67 self.sql.push_str(sql);
68 }
69
70 pub fn push_char(&mut self, c: char) {
72 self.sql.push(c);
73 }
74
75 pub fn push_param(&mut self) -> usize {
78 self.param_count += 1;
79 match self.style {
80 ParamStyle::Dollar => {
81 self.sql.push('$');
82 self.sql.push_str(&self.param_count.to_string());
83 }
84 ParamStyle::QuestionMark => {
85 self.sql.push('?');
86 }
87 }
88 self.param_count
89 }
90
91 pub fn push_identifier(&mut self, name: &str) {
93 self.sql.push('"');
94 for c in name.chars() {
96 if c == '"' {
97 self.sql.push('"');
98 }
99 self.sql.push(c);
100 }
101 self.sql.push('"');
102 }
103
104 #[must_use]
106 pub fn param_count(&self) -> usize {
107 self.param_count
108 }
109
110 #[must_use]
112 pub fn style(&self) -> ParamStyle {
113 self.style
114 }
115
116 #[must_use]
118 pub fn build(self) -> String {
119 self.sql
120 }
121
122 #[must_use]
124 pub fn sql(&self) -> &str {
125 &self.sql
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn test_postgres_params() {
135 let mut b = SqlBuilder::new(ParamStyle::Dollar);
136 b.push("SELECT * FROM ");
137 b.push_identifier("users");
138 b.push(" WHERE ");
139 b.push_identifier("email");
140 b.push(" = ");
141 b.push_param();
142 b.push(" AND ");
143 b.push_identifier("age");
144 b.push(" > ");
145 b.push_param();
146
147 assert_eq!(
148 b.build(),
149 r#"SELECT * FROM "users" WHERE "email" = $1 AND "age" > $2"#
150 );
151 }
152
153 #[test]
154 fn test_sqlite_params() {
155 let mut b = SqlBuilder::new(ParamStyle::QuestionMark);
156 b.push("SELECT * FROM ");
157 b.push_identifier("users");
158 b.push(" WHERE ");
159 b.push_identifier("email");
160 b.push(" = ");
161 b.push_param();
162
163 assert_eq!(b.build(), r#"SELECT * FROM "users" WHERE "email" = ?"#);
164 }
165}