sea_query/
types.rs

1//! Base types used throughout sea-query.
2
3use crate::{expr::*, query::*, FunctionCall, ValueTuple, Values};
4use std::{fmt, mem, ops};
5
6#[cfg(feature = "backend-postgres")]
7use crate::extension::postgres::PgBinOper;
8#[cfg(feature = "backend-sqlite")]
9use crate::extension::sqlite::SqliteBinOper;
10#[cfg(not(feature = "thread-safe"))]
11pub use std::rc::Rc as RcOrArc;
12#[cfg(feature = "thread-safe")]
13pub use std::sync::Arc as RcOrArc;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct Quote(pub(crate) u8, pub(crate) u8);
17
18macro_rules! iden_trait {
19    ($($bounds:ident),*) => {
20        /// Identifier
21        pub trait Iden where $(Self: $bounds),* {
22            fn prepare(&self, s: &mut dyn fmt::Write, q: Quote) {
23                write!(s, "{}{}{}", q.left(), self.quoted(q), q.right()).unwrap();
24            }
25
26            fn quoted(&self, q: Quote) -> String {
27                let byte = [q.1];
28                let qq: &str = std::str::from_utf8(&byte).unwrap();
29                self.to_string().replace(qq, qq.repeat(2).as_str())
30            }
31
32            fn to_string(&self) -> String {
33                let mut s = String::new();
34                self.unquoted(&mut s);
35                s
36            }
37
38            fn unquoted(&self, s: &mut dyn fmt::Write);
39        }
40
41        /// Identifier
42        pub trait IdenStatic: Iden + Copy + 'static {
43            fn as_str(&self) -> &'static str;
44        }
45    };
46}
47
48#[cfg(feature = "thread-safe")]
49iden_trait!(Send, Sync);
50#[cfg(not(feature = "thread-safe"))]
51iden_trait!();
52
53pub type DynIden = SeaRc<dyn Iden>;
54
55#[derive(Debug)]
56#[repr(transparent)]
57pub struct SeaRc<I>(pub(crate) RcOrArc<I>)
58where
59    I: ?Sized;
60
61impl ops::Deref for SeaRc<dyn Iden> {
62    type Target = dyn Iden;
63
64    fn deref(&self) -> &Self::Target {
65        ops::Deref::deref(&self.0)
66    }
67}
68
69impl Clone for SeaRc<dyn Iden> {
70    fn clone(&self) -> SeaRc<dyn Iden> {
71        SeaRc(RcOrArc::clone(&self.0))
72    }
73}
74
75impl PartialEq for SeaRc<dyn Iden> {
76    fn eq(&self, other: &Self) -> bool {
77        let (self_vtable, other_vtable) = unsafe {
78            let (_, self_vtable) = mem::transmute::<&dyn Iden, (usize, usize)>(&*self.0);
79            let (_, other_vtable) = mem::transmute::<&dyn Iden, (usize, usize)>(&*other.0);
80            (self_vtable, other_vtable)
81        };
82        self_vtable == other_vtable && self.to_string() == other.to_string()
83    }
84}
85
86impl SeaRc<dyn Iden> {
87    pub fn new<I>(i: I) -> SeaRc<dyn Iden>
88    where
89        I: Iden + 'static,
90    {
91        SeaRc(RcOrArc::new(i))
92    }
93}
94
95pub trait IntoIden {
96    fn into_iden(self) -> DynIden;
97}
98
99pub trait IdenList {
100    type IntoIter: Iterator<Item = DynIden>;
101
102    fn into_iter(self) -> Self::IntoIter;
103}
104
105impl fmt::Debug for dyn Iden {
106    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
107        self.unquoted(formatter);
108        Ok(())
109    }
110}
111
112/// Column references
113#[derive(Debug, Clone, PartialEq)]
114pub enum ColumnRef {
115    Column(DynIden),
116    TableColumn(DynIden, DynIden),
117    SchemaTableColumn(DynIden, DynIden, DynIden),
118    Asterisk,
119    TableAsterisk(DynIden),
120}
121
122pub trait IntoColumnRef {
123    fn into_column_ref(self) -> ColumnRef;
124}
125
126/// Table references
127#[allow(clippy::large_enum_variant)]
128#[derive(Debug, Clone, PartialEq)]
129pub enum TableRef {
130    /// Table identifier without any schema / database prefix
131    Table(DynIden),
132    /// Table identifier with schema prefix
133    SchemaTable(DynIden, DynIden),
134    /// Table identifier with database and schema prefix
135    DatabaseSchemaTable(DynIden, DynIden, DynIden),
136    /// Table identifier with alias
137    TableAlias(DynIden, DynIden),
138    /// Table identifier with schema prefix and alias
139    SchemaTableAlias(DynIden, DynIden, DynIden),
140    /// Table identifier with database and schema prefix and alias
141    DatabaseSchemaTableAlias(DynIden, DynIden, DynIden, DynIden),
142    /// Subquery with alias
143    SubQuery(SelectStatement, DynIden),
144    /// Values list with alias
145    ValuesList(Vec<ValueTuple>, DynIden),
146    /// Function call with alias
147    FunctionCall(FunctionCall, DynIden),
148}
149
150pub trait IntoTableRef {
151    fn into_table_ref(self) -> TableRef;
152}
153
154/// Unary operator
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum UnOper {
157    Not,
158}
159
160/// Binary operator
161#[derive(Debug, Clone, Copy, PartialEq, Eq)]
162pub enum BinOper {
163    And,
164    Or,
165    Like,
166    NotLike,
167    Is,
168    IsNot,
169    In,
170    NotIn,
171    Between,
172    NotBetween,
173    Equal,
174    NotEqual,
175    SmallerThan,
176    GreaterThan,
177    SmallerThanOrEqual,
178    GreaterThanOrEqual,
179    Add,
180    Sub,
181    Mul,
182    Div,
183    Mod,
184    BitAnd,
185    BitOr,
186    LShift,
187    RShift,
188    As,
189    Escape,
190    Custom(&'static str),
191    #[cfg(feature = "backend-postgres")]
192    PgOperator(PgBinOper),
193    #[cfg(feature = "backend-sqlite")]
194    SqliteOperator(SqliteBinOper),
195}
196
197/// Logical chain operator
198#[derive(Debug, Clone, PartialEq)]
199pub enum LogicalChainOper {
200    And(SimpleExpr),
201    Or(SimpleExpr),
202}
203
204/// Join types
205#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206pub enum JoinType {
207    Join,
208    CrossJoin,
209    InnerJoin,
210    LeftJoin,
211    RightJoin,
212    FullOuterJoin,
213}
214
215/// Nulls order
216#[derive(Debug, Clone, Copy, PartialEq, Eq)]
217pub enum NullOrdering {
218    First,
219    Last,
220}
221
222/// Order expression
223#[derive(Debug, Clone, PartialEq)]
224pub struct OrderExpr {
225    pub(crate) expr: SimpleExpr,
226    pub(crate) order: Order,
227    pub(crate) nulls: Option<NullOrdering>,
228}
229
230/// Join on types
231#[derive(Debug, Clone, PartialEq)]
232pub enum JoinOn {
233    Condition(Box<ConditionHolder>),
234    Columns(Vec<SimpleExpr>),
235}
236
237/// Ordering options
238#[derive(Debug, Clone, PartialEq)]
239pub enum Order {
240    Asc,
241    Desc,
242    Field(Values),
243}
244
245/// Helper for create name alias
246#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
247pub struct Alias(String);
248
249/// Null Alias
250#[derive(Default, Debug, Copy, Clone)]
251pub struct NullAlias;
252
253/// Asterisk ("*")
254///
255/// Express the asterisk without table prefix.
256///
257/// # Examples
258///
259/// ```
260/// use sea_query::{tests_cfg::*, *};
261///
262/// let query = Query::select()
263///     .column(Asterisk)
264///     .from(Char::Table)
265///     .to_owned();
266///
267/// assert_eq!(
268///     query.to_string(MysqlQueryBuilder),
269///     r#"SELECT * FROM `character`"#
270/// );
271/// assert_eq!(
272///     query.to_string(PostgresQueryBuilder),
273///     r#"SELECT * FROM "character""#
274/// );
275/// assert_eq!(
276///     query.to_string(SqliteQueryBuilder),
277///     r#"SELECT * FROM "character""#
278/// );
279/// ```
280///
281/// Express the asterisk with table prefix.
282///
283/// Examples
284///
285/// ```
286/// use sea_query::{tests_cfg::*, *};
287///
288/// let query = Query::select()
289///     .column((Char::Table, Asterisk))
290///     .from(Char::Table)
291///     .to_owned();
292///
293/// assert_eq!(
294///     query.to_string(MysqlQueryBuilder),
295///     r#"SELECT `character`.* FROM `character`"#
296/// );
297/// assert_eq!(
298///     query.to_string(PostgresQueryBuilder),
299///     r#"SELECT "character".* FROM "character""#
300/// );
301/// assert_eq!(
302///     query.to_string(SqliteQueryBuilder),
303///     r#"SELECT "character".* FROM "character""#
304/// );
305/// ```
306#[derive(Default, Debug, Clone, Copy)]
307pub struct Asterisk;
308
309/// SQL Keywords
310#[derive(Debug, Clone, PartialEq)]
311pub enum Keyword {
312    Null,
313    CurrentDate,
314    CurrentTime,
315    CurrentTimestamp,
316    Custom(DynIden),
317}
318
319/// Like Expression
320#[derive(Debug, Clone)]
321pub struct LikeExpr {
322    pub(crate) pattern: String,
323    pub(crate) escape: Option<char>,
324}
325
326pub trait IntoLikeExpr {
327    fn into_like_expr(self) -> LikeExpr;
328}
329
330/// SubQuery operators
331#[derive(Debug, Copy, Clone, PartialEq)]
332pub enum SubQueryOper {
333    Exists,
334    Any,
335    Some,
336    All,
337}
338
339// Impl begins
340
341impl Quote {
342    pub fn new(c: u8) -> Self {
343        Self(c, c)
344    }
345
346    pub fn left(&self) -> char {
347        char::from(self.0)
348    }
349
350    pub fn right(&self) -> char {
351        char::from(self.1)
352    }
353}
354
355impl From<char> for Quote {
356    fn from(c: char) -> Self {
357        (c as u8).into()
358    }
359}
360
361impl From<(char, char)> for Quote {
362    fn from((l, r): (char, char)) -> Self {
363        (l as u8, r as u8).into()
364    }
365}
366
367impl From<u8> for Quote {
368    fn from(u8: u8) -> Self {
369        Quote::new(u8)
370    }
371}
372
373impl From<(u8, u8)> for Quote {
374    fn from((l, r): (u8, u8)) -> Self {
375        Quote(l, r)
376    }
377}
378
379impl<T: 'static> IntoIden for T
380where
381    T: Iden,
382{
383    fn into_iden(self) -> DynIden {
384        SeaRc::new(self)
385    }
386}
387
388impl IntoIden for DynIden {
389    fn into_iden(self) -> DynIden {
390        self
391    }
392}
393
394impl<I> IdenList for I
395where
396    I: IntoIden,
397{
398    type IntoIter = std::iter::Once<DynIden>;
399
400    fn into_iter(self) -> Self::IntoIter {
401        std::iter::once(self.into_iden())
402    }
403}
404
405impl<A, B> IdenList for (A, B)
406where
407    A: IntoIden,
408    B: IntoIden,
409{
410    type IntoIter = std::array::IntoIter<DynIden, 2>;
411
412    fn into_iter(self) -> Self::IntoIter {
413        [self.0.into_iden(), self.1.into_iden()].into_iter()
414    }
415}
416
417impl<A, B, C> IdenList for (A, B, C)
418where
419    A: IntoIden,
420    B: IntoIden,
421    C: IntoIden,
422{
423    type IntoIter = std::array::IntoIter<DynIden, 3>;
424
425    fn into_iter(self) -> Self::IntoIter {
426        [self.0.into_iden(), self.1.into_iden(), self.2.into_iden()].into_iter()
427    }
428}
429
430impl IntoColumnRef for ColumnRef {
431    fn into_column_ref(self) -> ColumnRef {
432        self
433    }
434}
435
436impl<T: 'static> IntoColumnRef for T
437where
438    T: IntoIden,
439{
440    fn into_column_ref(self) -> ColumnRef {
441        ColumnRef::Column(self.into_iden())
442    }
443}
444
445impl IntoColumnRef for Asterisk {
446    fn into_column_ref(self) -> ColumnRef {
447        ColumnRef::Asterisk
448    }
449}
450
451impl<S: 'static, T: 'static> IntoColumnRef for (S, T)
452where
453    S: IntoIden,
454    T: IntoIden,
455{
456    fn into_column_ref(self) -> ColumnRef {
457        ColumnRef::TableColumn(self.0.into_iden(), self.1.into_iden())
458    }
459}
460
461impl<T: 'static> IntoColumnRef for (T, Asterisk)
462where
463    T: IntoIden,
464{
465    fn into_column_ref(self) -> ColumnRef {
466        ColumnRef::TableAsterisk(self.0.into_iden())
467    }
468}
469
470impl<S: 'static, T: 'static, U: 'static> IntoColumnRef for (S, T, U)
471where
472    S: IntoIden,
473    T: IntoIden,
474    U: IntoIden,
475{
476    fn into_column_ref(self) -> ColumnRef {
477        ColumnRef::SchemaTableColumn(self.0.into_iden(), self.1.into_iden(), self.2.into_iden())
478    }
479}
480
481impl IntoTableRef for TableRef {
482    fn into_table_ref(self) -> TableRef {
483        self
484    }
485}
486
487impl<T: 'static> IntoTableRef for T
488where
489    T: IntoIden,
490{
491    fn into_table_ref(self) -> TableRef {
492        TableRef::Table(self.into_iden())
493    }
494}
495
496impl<S: 'static, T: 'static> IntoTableRef for (S, T)
497where
498    S: IntoIden,
499    T: IntoIden,
500{
501    fn into_table_ref(self) -> TableRef {
502        TableRef::SchemaTable(self.0.into_iden(), self.1.into_iden())
503    }
504}
505
506impl<S: 'static, T: 'static, U: 'static> IntoTableRef for (S, T, U)
507where
508    S: IntoIden,
509    T: IntoIden,
510    U: IntoIden,
511{
512    fn into_table_ref(self) -> TableRef {
513        TableRef::DatabaseSchemaTable(self.0.into_iden(), self.1.into_iden(), self.2.into_iden())
514    }
515}
516
517impl TableRef {
518    /// Add or replace the current alias
519    pub fn alias<A>(self, alias: A) -> Self
520    where
521        A: IntoIden,
522    {
523        match self {
524            Self::Table(table) => Self::TableAlias(table, alias.into_iden()),
525            Self::TableAlias(table, _) => Self::TableAlias(table, alias.into_iden()),
526            Self::SchemaTable(schema, table) => {
527                Self::SchemaTableAlias(schema, table, alias.into_iden())
528            }
529            Self::DatabaseSchemaTable(database, schema, table) => {
530                Self::DatabaseSchemaTableAlias(database, schema, table, alias.into_iden())
531            }
532            Self::SchemaTableAlias(schema, table, _) => {
533                Self::SchemaTableAlias(schema, table, alias.into_iden())
534            }
535            Self::DatabaseSchemaTableAlias(database, schema, table, _) => {
536                Self::DatabaseSchemaTableAlias(database, schema, table, alias.into_iden())
537            }
538            Self::SubQuery(statement, _) => Self::SubQuery(statement, alias.into_iden()),
539            Self::ValuesList(values, _) => Self::ValuesList(values, alias.into_iden()),
540            Self::FunctionCall(func, _) => Self::FunctionCall(func, alias.into_iden()),
541        }
542    }
543}
544
545impl Alias {
546    pub fn new<T>(n: T) -> Self
547    where
548        T: Into<String>,
549    {
550        Self(n.into())
551    }
552}
553
554impl Iden for Alias {
555    fn unquoted(&self, s: &mut dyn fmt::Write) {
556        write!(s, "{}", self.0).unwrap();
557    }
558}
559
560impl NullAlias {
561    pub fn new() -> Self {
562        Self
563    }
564}
565
566impl Iden for NullAlias {
567    fn unquoted(&self, _s: &mut dyn fmt::Write) {}
568}
569
570impl LikeExpr {
571    pub fn new<T>(pattern: T) -> Self
572    where
573        T: Into<String>,
574    {
575        Self {
576            pattern: pattern.into(),
577            escape: None,
578        }
579    }
580
581    #[deprecated(since = "0.29.0", note = "Please use the [`LikeExpr::new`] method")]
582    pub fn str<T>(pattern: T) -> Self
583    where
584        T: Into<String>,
585    {
586        Self {
587            pattern: pattern.into(),
588            escape: None,
589        }
590    }
591
592    pub fn escape(self, c: char) -> Self {
593        Self {
594            pattern: self.pattern,
595            escape: Some(c),
596        }
597    }
598}
599
600impl IntoLikeExpr for LikeExpr {
601    fn into_like_expr(self) -> LikeExpr {
602        self
603    }
604}
605
606impl<T> IntoLikeExpr for T
607where
608    T: Into<String>,
609{
610    fn into_like_expr(self) -> LikeExpr {
611        LikeExpr::new(self)
612    }
613}
614
615#[cfg(test)]
616mod tests {
617    pub use crate::{tests_cfg::*, *};
618    use pretty_assertions::assert_eq;
619    pub use Character as CharReexport;
620
621    #[test]
622    fn test_identifier() {
623        let query = Query::select()
624            .column(Alias::new("hello-World_"))
625            .to_owned();
626
627        #[cfg(feature = "backend-mysql")]
628        assert_eq!(query.to_string(MysqlQueryBuilder), r"SELECT `hello-World_`");
629        #[cfg(feature = "backend-postgres")]
630        assert_eq!(
631            query.to_string(PostgresQueryBuilder),
632            r#"SELECT "hello-World_""#
633        );
634        #[cfg(feature = "backend-sqlite")]
635        assert_eq!(
636            query.to_string(SqliteQueryBuilder),
637            r#"SELECT "hello-World_""#
638        );
639    }
640
641    #[test]
642    fn test_quoted_identifier_1() {
643        let query = Query::select().column(Alias::new("hel`lo")).to_owned();
644
645        #[cfg(feature = "backend-mysql")]
646        assert_eq!(query.to_string(MysqlQueryBuilder), r"SELECT `hel``lo`");
647        #[cfg(feature = "backend-sqlite")]
648        assert_eq!(query.to_string(SqliteQueryBuilder), r#"SELECT "hel`lo""#);
649
650        let query = Query::select().column(Alias::new("hel\"lo")).to_owned();
651
652        #[cfg(feature = "backend-postgres")]
653        assert_eq!(query.to_string(PostgresQueryBuilder), r#"SELECT "hel""lo""#);
654    }
655
656    #[test]
657    fn test_quoted_identifier_2() {
658        let query = Query::select().column(Alias::new("hel``lo")).to_owned();
659
660        #[cfg(feature = "backend-mysql")]
661        assert_eq!(query.to_string(MysqlQueryBuilder), r"SELECT `hel````lo`");
662        #[cfg(feature = "backend-sqlite")]
663        assert_eq!(query.to_string(SqliteQueryBuilder), r#"SELECT "hel``lo""#);
664
665        let query = Query::select().column(Alias::new("hel\"\"lo")).to_owned();
666
667        #[cfg(feature = "backend-postgres")]
668        assert_eq!(
669            query.to_string(PostgresQueryBuilder),
670            r#"SELECT "hel""""lo""#
671        );
672    }
673
674    #[test]
675    fn test_cmp_identifier() {
676        type CharLocal = Character;
677
678        assert_eq!(
679            ColumnRef::Column(Character::Id.into_iden()),
680            ColumnRef::Column(Character::Id.into_iden())
681        );
682        assert_eq!(
683            ColumnRef::Column(Character::Id.into_iden()),
684            ColumnRef::Column(Char::Id.into_iden())
685        );
686        assert_eq!(
687            ColumnRef::Column(Character::Id.into_iden()),
688            ColumnRef::Column(CharLocal::Id.into_iden())
689        );
690        assert_eq!(
691            ColumnRef::Column(Character::Id.into_iden()),
692            ColumnRef::Column(CharReexport::Id.into_iden())
693        );
694        assert_eq!(
695            ColumnRef::Column(Alias::new("id").into_iden()),
696            ColumnRef::Column(Alias::new("id").into_iden())
697        );
698        assert_ne!(
699            ColumnRef::Column(Alias::new("id").into_iden()),
700            ColumnRef::Column(Alias::new("id_").into_iden())
701        );
702        assert_ne!(
703            ColumnRef::Column(Character::Id.into_iden()),
704            ColumnRef::Column(Alias::new("id").into_iden())
705        );
706        assert_ne!(
707            ColumnRef::Column(Character::Id.into_iden()),
708            ColumnRef::Column(Character::Table.into_iden())
709        );
710        assert_ne!(
711            ColumnRef::Column(Character::Id.into_iden()),
712            ColumnRef::Column(Font::Id.into_iden())
713        );
714    }
715}