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 pub fn from_client(client: &DatabaseClient) -> Self {
22 match client {
23 #[cfg(feature = "postgres")]
24 DatabaseClient::Postgres(_) => Self::Dollar,
25 #[cfg(feature = "sqlite")]
26 DatabaseClient::Sqlite(_) => Self::QuestionMark,
27 }
28 }
29}
30
31#[derive(Debug)]
40pub struct SqlBuilder {
41 sql: String,
42 param_count: usize,
43 style: ParamStyle,
44}
45
46impl SqlBuilder {
47 pub fn new(style: ParamStyle) -> Self {
48 Self {
49 sql: String::with_capacity(256),
50 param_count: 0,
51 style,
52 }
53 }
54
55 pub fn for_client(client: &DatabaseClient) -> Self {
56 Self::new(ParamStyle::from_client(client))
57 }
58
59 pub fn push(&mut self, sql: &str) {
61 self.sql.push_str(sql);
62 }
63
64 pub fn push_char(&mut self, c: char) {
66 self.sql.push(c);
67 }
68
69 pub fn push_param(&mut self) -> usize {
72 self.param_count += 1;
73 match self.style {
74 ParamStyle::Dollar => {
75 self.sql.push('$');
76 self.sql.push_str(&self.param_count.to_string());
77 }
78 ParamStyle::QuestionMark => {
79 self.sql.push('?');
80 }
81 }
82 self.param_count
83 }
84
85 pub fn push_identifier(&mut self, name: &str) {
87 self.sql.push('"');
88 for c in name.chars() {
90 if c == '"' {
91 self.sql.push('"');
92 }
93 self.sql.push(c);
94 }
95 self.sql.push('"');
96 }
97
98 pub fn param_count(&self) -> usize {
100 self.param_count
101 }
102
103 pub fn style(&self) -> ParamStyle {
105 self.style
106 }
107
108 pub fn build(self) -> String {
110 self.sql
111 }
112
113 pub fn sql(&self) -> &str {
115 &self.sql
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_postgres_params() {
125 let mut b = SqlBuilder::new(ParamStyle::Dollar);
126 b.push("SELECT * FROM ");
127 b.push_identifier("users");
128 b.push(" WHERE ");
129 b.push_identifier("email");
130 b.push(" = ");
131 b.push_param();
132 b.push(" AND ");
133 b.push_identifier("age");
134 b.push(" > ");
135 b.push_param();
136
137 assert_eq!(
138 b.build(),
139 r#"SELECT * FROM "users" WHERE "email" = $1 AND "age" > $2"#
140 );
141 }
142
143 #[test]
144 fn test_sqlite_params() {
145 let mut b = SqlBuilder::new(ParamStyle::QuestionMark);
146 b.push("SELECT * FROM ");
147 b.push_identifier("users");
148 b.push(" WHERE ");
149 b.push_identifier("email");
150 b.push(" = ");
151 b.push_param();
152
153 assert_eq!(b.build(), r#"SELECT * FROM "users" WHERE "email" = ?"#);
154 }
155}