drizzle_sqlite/
common.rs

1use drizzle_core::{SQL, SQLIndexInfo, SQLSchemaType, ToSQL, traits::SQLParam};
2
3use crate::traits::SQLiteTableInfo;
4
5/// The type of database object
6#[derive(Debug, Clone)]
7pub enum SQLiteSchemaType {
8    /// A regular table
9    Table(&'static dyn SQLiteTableInfo),
10    /// A view
11    View,
12    /// An index
13    Index(&'static dyn SQLIndexInfo),
14    /// A trigger
15    Trigger,
16}
17
18impl SQLSchemaType for SQLiteSchemaType {}
19
20//------------------------------------------------------------------------------
21// Number Type
22//------------------------------------------------------------------------------
23
24/// Numeric type that can be either an integer or a floating point value
25#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
26pub enum Number {
27    /// Integer value
28    Integer(i64),
29    /// Floating point value
30    Real(f64),
31}
32
33impl Default for Number {
34    fn default() -> Self {
35        Self::Integer(Default::default())
36    }
37}
38
39impl From<i64> for Number {
40    fn from(value: i64) -> Self {
41        Self::Integer(value)
42    }
43}
44
45impl From<f64> for Number {
46    fn from(value: f64) -> Self {
47        Self::Real(value)
48    }
49}
50
51// Note: Generic From implementation is removed to avoid conflicts.
52// The table macro will generate specific implementations using SQLiteEnumVisitor.
53#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
54pub enum JoinType {
55    #[default]
56    Join,
57    Inner,
58    Left,
59    Right,
60    Full,
61    Cross,
62}
63
64#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
65pub struct Join {
66    pub natural: bool,
67    pub join_type: JoinType,
68    pub outer: bool, // only meaningful for LEFT/RIGHT/FULL
69}
70
71impl Join {
72    pub fn new() -> Self {
73        Self::default()
74    }
75
76    pub fn natural(mut self) -> Self {
77        self.natural = true;
78        self
79    }
80
81    pub fn inner(mut self) -> Self {
82        self.join_type = JoinType::Inner;
83        self
84    }
85
86    pub fn left(mut self) -> Self {
87        self.join_type = JoinType::Left;
88        self
89    }
90
91    pub fn right(mut self) -> Self {
92        self.join_type = JoinType::Right;
93        self
94    }
95
96    pub fn full(mut self) -> Self {
97        self.join_type = JoinType::Full;
98        self
99    }
100
101    pub fn cross(mut self) -> Self {
102        self.join_type = JoinType::Cross;
103        self
104    }
105
106    pub fn outer(mut self) -> Self {
107        self.outer = true;
108        self
109    }
110}
111impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for Join {
112    fn to_sql(&self) -> SQL<'a, V> {
113        let mut parts = Vec::new();
114
115        if self.natural {
116            parts.push("NATURAL");
117        }
118
119        match self.join_type {
120            JoinType::Inner => parts.push("INNER"),
121            JoinType::Left => {
122                parts.push("LEFT");
123                if self.outer {
124                    parts.push("OUTER");
125                }
126            }
127            JoinType::Right => {
128                parts.push("RIGHT");
129                if self.outer {
130                    parts.push("OUTER");
131                }
132            }
133            JoinType::Full => {
134                parts.push("FULL");
135                if self.outer {
136                    parts.push("OUTER");
137                }
138            }
139            JoinType::Cross => parts.push("CROSS"),
140            JoinType::Join => {}
141        }
142
143        parts.push("JOIN");
144        SQL::raw(parts.join(" "))
145    }
146}
147
148//------------------------------------------------------------------------------
149// Tests
150//------------------------------------------------------------------------------
151
152#[cfg(any(feature = "turso", feature = "libsql", feature = "rusqlite"))]
153#[cfg(test)]
154mod tests {
155    use crate::common::{Join, JoinType, Number};
156    use crate::*;
157    use std::borrow::Cow;
158
159    #[test]
160    fn test_into_sqlite_value_impls() {
161        assert_eq!(
162            SQLiteValue::from("hello"),
163            SQLiteValue::Text(Cow::Borrowed("hello"))
164        );
165        assert_eq!(
166            SQLiteValue::from(String::from("world")),
167            SQLiteValue::Text(Cow::Owned("world".to_string()))
168        );
169        assert_eq!(SQLiteValue::from(42i64), SQLiteValue::Integer(42));
170        assert_eq!(SQLiteValue::from(123i32), SQLiteValue::Integer(123));
171        assert_eq!(SQLiteValue::from(3.14f64), SQLiteValue::Real(3.14));
172        assert_eq!(SQLiteValue::from(true), SQLiteValue::Integer(1));
173        assert_eq!(SQLiteValue::from(false), SQLiteValue::Integer(0));
174        let blob_vec: Vec<u8> = vec![1, 2, 3];
175        assert_eq!(
176            SQLiteValue::from(blob_vec.clone()),
177            SQLiteValue::Blob(Cow::Owned(blob_vec.clone()))
178        );
179        let blob_slice: &[u8] = &[4, 5, 6];
180        assert_eq!(
181            SQLiteValue::from(blob_slice),
182            SQLiteValue::Blob(Cow::Borrowed(blob_slice))
183        );
184        assert_eq!(SQLiteValue::from(Option::<String>::None), SQLiteValue::Null);
185        assert_eq!(
186            SQLiteValue::from(Some("optional")),
187            SQLiteValue::Text(Cow::Borrowed("optional"))
188        );
189    }
190
191    #[test]
192    fn test_number_enum() {
193        let int_num = Number::Integer(42);
194        let real_num = Number::Real(3.14);
195
196        assert_eq!(int_num, Number::from(42i64));
197        assert_eq!(real_num, Number::from(3.14f64));
198        assert_eq!(Number::default(), Number::Integer(0));
199    }
200
201    #[test]
202    fn test_join_type_and_join() {
203        let join = Join::new().inner().natural();
204        assert_eq!(join.join_type, JoinType::Inner);
205        assert_eq!(join.natural, true);
206        assert_eq!(join.outer, false);
207
208        let outer_join = Join::new().left().outer();
209        assert_eq!(outer_join.join_type, JoinType::Left);
210        assert_eq!(outer_join.outer, true);
211
212        let cross_join = Join::new().cross();
213        assert_eq!(cross_join.join_type, JoinType::Cross);
214    }
215
216    #[test]
217    fn test_join_to_sql() {
218        use drizzle_core::{SQL, ToSQL};
219
220        let inner_join = Join::new().inner();
221        let sql: SQL<SQLiteValue> = inner_join.to_sql();
222        assert_eq!(sql.sql(), "INNER JOIN");
223
224        let natural_left_outer = Join::new().natural().left().outer();
225        let sql: SQL<SQLiteValue> = natural_left_outer.to_sql();
226        assert_eq!(sql.sql(), "NATURAL LEFT OUTER JOIN");
227
228        let cross_join = Join::new().cross();
229        let sql: SQL<SQLiteValue> = cross_join.to_sql();
230        assert_eq!(sql.sql(), "CROSS JOIN");
231    }
232}