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