1mod additions;
2mod bind;
3mod column;
4pub mod condition;
5use std::fmt::Debug;
6
7#[cfg(any(feature = "postgres", feature = "sqlite"))]
8use crate::driver::Driver;
9use crate::format_alised_col_name;
10pub use additions::JoinSpec;
11pub use additions::JoinType;
12pub use additions::OrderBySpec;
13pub use bind::BindValue;
14pub use column::Column;
15pub use condition::Condition;
16use sqlx::QueryBuilder;
17
18pub fn with_quotes(s: &str) -> String {
21 format!("\"{}\"", s)
24}
25
26pub struct QB<T> {
28 pub base: TableInfo,
30
31 pub eager: Vec<JoinSpec>,
33 pub batch: Vec<JoinSpec>,
35
36 pub filters: Vec<Condition>,
38 pub order_by: Vec<OrderBySpec>,
39
40 pub limit: Option<i32>,
41 pub offset: Option<i32>,
42
43 _marker: std::marker::PhantomData<T>,
44}
45#[derive(Clone, Debug)]
46pub struct TableInfo {
48 pub name: &'static str,
50 pub alias: String,
52 pub columns: Vec<&'static str>,
54}
55
56impl<T> QB<T> {
57 pub fn new(base: TableInfo) -> QB<T> {
58 QB {
59 base,
60 eager: Vec::new(),
61 order_by: Vec::new(),
62 batch: Vec::new(),
63 filters: Vec::new(),
64 _marker: std::marker::PhantomData,
65 limit: None,
66 offset: None,
67 }
68 }
69
70 pub fn filter(mut self, cond: Condition) -> Self {
71 self.filters.push(cond);
72 self
73 }
74
75 fn apply_projections(&self, builder: &mut QueryBuilder<'static, Driver>) {
76 let mut projections = Vec::new();
77
78 for col in &self.base.columns {
79 let field = format!("{}.{}", self.base.alias, col);
80 let as_field = format_alised_col_name(&self.base.alias, col);
81 projections.push(format!("{} AS {}", field, as_field));
82 }
83
84 for join in &self.eager {
85 for col in &join.foreign_table.columns {
86 let field = format!("{}.{}", join.foreign_table.alias, col);
87 let as_field = format_alised_col_name(&join.foreign_table.alias, col);
88 projections.push(format!("{} AS {}", field, as_field));
89 }
90 }
91
92 builder.push(projections.join(", "));
93
94 builder.push(" ");
95 }
96
97 fn apply_from_clause(&self, builder: &mut QueryBuilder<'static, Driver>) {
98 builder.push(format!(
99 "FROM {} AS {}",
100 with_quotes(self.base.name),
101 self.base.alias
102 ));
103
104 builder.push(" ");
105 }
106
107 fn apply_joins(&self, builder: &mut QueryBuilder<'static, Driver>) {
108 let mut joins = String::new();
109
110 for join in &self.eager {
111 let other_table = format!(
112 "{} AS {}",
113 with_quotes(join.foreign_table.name),
114 join.foreign_table.alias
115 );
116
117 let jt = match join.join_type {
118 JoinType::Inner => "INNER JOIN",
119 JoinType::Left => "LEFT JOIN",
120 };
121
122 let on_base = format!("{}.{}", self.base.alias, join.on.0);
123 let on_other = format!("{}.{}", join.foreign_table.alias, join.on.1);
124
125 joins.push_str(&format!(
126 " {} {} ON {} = {}",
127 jt, other_table, on_base, on_other
128 ));
129 }
130
131 builder.push(joins);
132 }
133
134 fn apply_limit<'args>(&self, builder: &mut QueryBuilder<'args, Driver>) {
135 if let Some(l) = self.limit {
136 builder.push(" LIMIT ");
137 builder.push_bind(l);
138 }
139 }
140
141 fn apply_offset<'args>(&self, builder: &mut QueryBuilder<'args, Driver>) {
142 if let Some(o) = self.offset {
143 #[cfg(feature = "sqlite")]
144 if let None = self.limit {
145 builder.push(" LIMIT ");
146 builder.push_bind(-1);
147 }
148 builder.push(" OFFSET ");
149 builder.push_bind(o);
150 }
151 }
152
153 fn apply_filters(&self, builder: &mut QueryBuilder<'static, Driver>) {
154 if !self.filters.is_empty() {
155 builder.push(" WHERE ");
156
157 for (i, cond) in self.filters.iter().enumerate() {
158 if i > 0 {
159 builder.push(" AND ");
160 }
161
162 let mut parts = cond.sql.split('?');
163 if let Some(first) = parts.next() {
164 builder.push(first);
165 }
166
167 for (val, part) in cond.values.iter().zip(parts) {
168 val.bind(builder);
169 builder.push(part);
170 }
171 }
172 }
173 }
174
175 fn apply_order_by(&self, builder: &mut QueryBuilder<'static, Driver>) {
176 if self.order_by.is_empty() {
177 return;
178 }
179
180 builder.push(" ORDER BY ");
181
182 for (i, spec) in self.order_by.iter().enumerate() {
183 if i > 0 {
184 builder.push(", ");
185 }
186 builder.push(format!("{} {}", spec.column, spec.order));
187 }
188 }
189
190 pub fn build_query(&self) -> QueryBuilder<'static, Driver> {
191 let mut builder = QueryBuilder::new("SELECT ");
192
193 self.apply_projections(&mut builder);
194 self.apply_from_clause(&mut builder);
195 self.apply_joins(&mut builder);
196 self.apply_filters(&mut builder);
197 self.apply_order_by(&mut builder);
198 self.apply_limit(&mut builder);
199 self.apply_offset(&mut builder);
200
201 builder
202 }
203
204 pub fn to_sql(&self) -> String {
205 self.build_query().sql().to_string()
206 }
207}