1use crate::{error::FbError, ibase, SqlType};
4use regex::{Captures, Regex};
5use std::collections::HashMap;
6
7pub use SqlType::*;
8
9pub const MAX_TEXT_LENGTH: usize = 32767;
11
12impl SqlType {
13 pub fn sql_type_and_subtype(&self) -> (u32, u32) {
15 match self {
16 Text(s) => {
17 if s.len() > MAX_TEXT_LENGTH {
18 (ibase::SQL_BLOB + 1, 1)
19 } else {
20 (ibase::SQL_TEXT + 1, 0)
21 }
22 }
23 Integer(_) => (ibase::SQL_INT64 + 1, 0),
24 Int128(_) => (ibase::SQL_INT128 + 1, 0),
25 Floating(_) => (ibase::SQL_DOUBLE + 1, 0),
26 Timestamp(_) => (ibase::SQL_TIMESTAMP + 1, 0),
27 Null => (ibase::SQL_TEXT + 1, 0),
28 Binary(_) => (ibase::SQL_BLOB + 1, 0),
29 Boolean(_) => (ibase::SQL_BOOLEAN + 1, 0),
30 }
31 }
32}
33
34pub trait IntoParam {
36 fn into_param(self) -> SqlType;
37}
38
39impl IntoParam for Vec<u8> {
40 fn into_param(self) -> SqlType {
41 Binary(self)
42 }
43}
44
45impl IntoParam for String {
46 fn into_param(self) -> SqlType {
47 Text(self)
48 }
49}
50
51impl IntoParam for i64 {
52 fn into_param(self) -> SqlType {
53 Integer(self)
54 }
55}
56
57impl IntoParam for i128 {
58 fn into_param(self) -> SqlType {
59 Int128(self)
60 }
61}
62
63impl IntoParam for bool {
64 fn into_param(self) -> SqlType {
65 Boolean(self)
66 }
67}
68
69macro_rules! impl_param_int {
71 ( $( $t: ident ),+ ) => {
72 $(
73 impl IntoParam for $t {
74 fn into_param(self) -> SqlType {
75 (self as i64).into_param()
76 }
77 }
78 )+
79 };
80}
81
82impl_param_int!(i32, u32, i16, u16, i8, u8);
83
84impl IntoParam for f64 {
85 fn into_param(self) -> SqlType {
86 Floating(self)
87 }
88}
89
90impl IntoParam for f32 {
91 fn into_param(self) -> SqlType {
92 (self as f64).into_param()
93 }
94}
95
96impl<T> IntoParam for Option<T>
98where
99 T: IntoParam,
100{
101 fn into_param(self) -> SqlType {
102 if let Some(v) = self {
103 v.into_param()
104 } else {
105 Null
106 }
107 }
108}
109
110impl<T, B> IntoParam for &B
112where
113 B: ToOwned<Owned = T> + ?Sized,
114 T: core::borrow::Borrow<B> + IntoParam,
115{
116 fn into_param(self) -> SqlType {
117 self.to_owned().into_param()
118 }
119}
120
121impl<T> From<T> for SqlType
123where
124 T: IntoParam,
125{
126 fn from(param: T) -> Self {
127 param.into_param()
128 }
129}
130
131pub enum ParamsType {
133 Positional(Vec<SqlType>),
137
138 Named(HashMap<String, SqlType>),
157}
158
159impl ParamsType {
160 pub fn named(&self) -> bool {
161 match self {
162 ParamsType::Positional(_) => false,
163 ParamsType::Named(_) => true,
164 }
165 }
166}
167
168#[allow(clippy::wrong_self_convention)]
169pub trait IntoParams {
176 fn to_params(self) -> ParamsType;
177}
178
179impl IntoParams for ParamsType {
180 fn to_params(self) -> ParamsType {
181 self
182 }
183}
184
185impl IntoParams for Vec<SqlType> {
188 fn to_params(self) -> ParamsType {
189 ParamsType::Positional(self)
190 }
191}
192
193impl IntoParams for () {
195 fn to_params(self) -> ParamsType {
196 ParamsType::Positional(vec![])
197 }
198}
199
200macro_rules! impl_into_params {
202 ($([$t: ident, $v: ident]),+) => {
203 impl<$($t),+> IntoParams for ($($t,)+)
204 where
205 $( $t: IntoParam, )+
206 {
207 fn to_params(self) -> ParamsType {
208 let ( $($v,)+ ) = self;
209
210 ParamsType::Positional(vec![ $(
211 $v.into_param(),
212 )+ ])
213 }
214 }
215 };
216}
217
218macro_rules! impls_into_params {
220 ([$t: ident, $v: ident]) => {
221 impl_into_params!([$t, $v]);
222 };
223
224 ([$t: ident, $v: ident], $([$ts: ident, $vs: ident]),+ ) => {
225 impls_into_params!($([$ts, $vs]),+);
226
227 impl_into_params!([$t, $v], $([$ts, $vs]),+);
228 };
229}
230
231impls_into_params!(
232 [A, a],
233 [B, b],
234 [C, c],
235 [D, d],
236 [E, e],
237 [F, f],
238 [G, g],
239 [H, h],
240 [I, i],
241 [J, j],
242 [K, k],
243 [L, l],
244 [M, m],
245 [N, n],
246 [O, o]
247);
248
249pub struct NamedParams {
253 pub sql: String,
254 params_names: Vec<String>,
255}
256
257impl NamedParams {
258 pub fn parse(raw_sql: &str) -> Result<Self, FbError> {
261 let rparams = Regex::new(r#"('[^']*')|:\w+"#)
262 .map_err(|e| FbError::from(format!("Error on start the regex for named params: {}", e)))
263 .unwrap();
264
265 let mut params_names = vec![];
266 let sql = rparams
267 .replace_all(raw_sql, |caps: &Captures| match caps.get(1) {
268 Some(same) => same.as_str().to_string(),
269 None => "?".to_string(),
270 })
271 .to_string();
272
273 for params in rparams.captures_iter(raw_sql) {
274 for param in params
275 .iter()
276 .filter_map(|p| p.map(|m| m.as_str()))
277 .filter(|p| p.starts_with(':'))
278 {
279 params_names.push(param.replace(':', ""));
280 }
281 }
282
283 Ok(NamedParams { sql, params_names })
284 }
285
286 pub fn empty(raw_sql: &str) -> Self {
288 Self {
289 sql: raw_sql.to_string(),
290 params_names: Default::default(),
291 }
292 }
293
294 pub fn convert<P>(&self, params: P) -> Result<Vec<SqlType>, FbError>
297 where
298 P: IntoParams,
299 {
300 match params.to_params() {
301 ParamsType::Named(names) => {
302 let mut new_params = vec![];
303
304 for qname in &self.params_names {
305 if let Some(param) = names.get(qname) {
306 new_params.push(param.clone());
307 } else {
308 return Err(FbError::from(format!(
309 "Param :{} not found in the provided struct",
310 qname
311 )));
312 }
313 }
314
315 Ok(new_params)
316 }
317 ParamsType::Positional(p) => Ok(p),
318 }
319 }
320}