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 operators.
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum UnOper {
157    Not,
158}
159
160/// Binary operators.
161///
162/// If something is not supported here, you can use [`BinOper::Custom`].
163#[derive(Debug, Clone, Copy, PartialEq, Eq)]
164pub enum BinOper {
165    And,
166    Or,
167    Like,
168    NotLike,
169    Is,
170    IsNot,
171    In,
172    NotIn,
173    Between,
174    NotBetween,
175    Equal,
176    NotEqual,
177    SmallerThan,
178    GreaterThan,
179    SmallerThanOrEqual,
180    GreaterThanOrEqual,
181    Add,
182    Sub,
183    Mul,
184    Div,
185    Mod,
186    BitAnd,
187    BitOr,
188    LShift,
189    RShift,
190    As,
191    Escape,
192    Custom(&'static str),
193    #[cfg(feature = "backend-postgres")]
194    PgOperator(PgBinOper),
195    #[cfg(feature = "backend-sqlite")]
196    SqliteOperator(SqliteBinOper),
197}
198
199/// Logical chain operator: conjunction or disjunction.
200#[derive(Debug, Clone, PartialEq)]
201pub enum LogicalChainOper {
202    And(SimpleExpr),
203    Or(SimpleExpr),
204}
205
206/// Join types
207#[derive(Debug, Clone, Copy, PartialEq, Eq)]
208pub enum JoinType {
209    Join,
210    CrossJoin,
211    InnerJoin,
212    LeftJoin,
213    RightJoin,
214    FullOuterJoin,
215}
216
217/// Nulls order
218#[derive(Debug, Clone, Copy, PartialEq, Eq)]
219pub enum NullOrdering {
220    First,
221    Last,
222}
223
224/// Order expression
225#[derive(Debug, Clone, PartialEq)]
226pub struct OrderExpr {
227    pub(crate) expr: SimpleExpr,
228    pub(crate) order: Order,
229    pub(crate) nulls: Option<NullOrdering>,
230}
231
232/// Join on types
233#[derive(Debug, Clone, PartialEq)]
234pub enum JoinOn {
235    Condition(Box<ConditionHolder>),
236    Columns(Vec<SimpleExpr>),
237}
238
239/// Ordering options
240#[derive(Debug, Clone, PartialEq)]
241pub enum Order {
242    Asc,
243    Desc,
244    Field(Values),
245}
246
247/// Helper for create name alias
248#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
249pub struct Alias(String);
250
251/// Null Alias
252#[derive(Default, Debug, Copy, Clone)]
253pub struct NullAlias;
254
255/// Asterisk ("*")
256///
257/// Express the asterisk without table prefix.
258///
259/// # Examples
260///
261/// ```
262/// use sea_query::{tests_cfg::*, *};
263///
264/// let query = Query::select()
265///     .column(Asterisk)
266///     .from(Char::Table)
267///     .to_owned();
268///
269/// assert_eq!(
270///     query.to_string(MysqlQueryBuilder),
271///     r#"SELECT * FROM `character`"#
272/// );
273/// assert_eq!(
274///     query.to_string(PostgresQueryBuilder),
275///     r#"SELECT * FROM "character""#
276/// );
277/// assert_eq!(
278///     query.to_string(SqliteQueryBuilder),
279///     r#"SELECT * FROM "character""#
280/// );
281/// ```
282///
283/// Express the asterisk with table prefix.
284///
285/// Examples
286///
287/// ```
288/// use sea_query::{tests_cfg::*, *};
289///
290/// let query = Query::select()
291///     .column((Char::Table, Asterisk))
292///     .from(Char::Table)
293///     .to_owned();
294///
295/// assert_eq!(
296///     query.to_string(MysqlQueryBuilder),
297///     r#"SELECT `character`.* FROM `character`"#
298/// );
299/// assert_eq!(
300///     query.to_string(PostgresQueryBuilder),
301///     r#"SELECT "character".* FROM "character""#
302/// );
303/// assert_eq!(
304///     query.to_string(SqliteQueryBuilder),
305///     r#"SELECT "character".* FROM "character""#
306/// );
307/// ```
308#[derive(Default, Debug, Clone, Copy)]
309pub struct Asterisk;
310
311/// Known SQL keywords that can be used as expressions.
312///
313/// If something is not supported here, you can use [`Keyword::Custom`].
314#[derive(Debug, Clone, PartialEq)]
315pub enum Keyword {
316    Null,
317    CurrentDate,
318    CurrentTime,
319    CurrentTimestamp,
320    Custom(DynIden),
321}
322
323/// Like Expression
324#[derive(Debug, Clone)]
325pub struct LikeExpr {
326    pub(crate) pattern: String,
327    pub(crate) escape: Option<char>,
328}
329
330pub trait IntoLikeExpr {
331    fn into_like_expr(self) -> LikeExpr;
332}
333
334/// SubQuery operators
335#[derive(Debug, Copy, Clone, PartialEq)]
336pub enum SubQueryOper {
337    Exists,
338    Any,
339    Some,
340    All,
341}
342
343// Impl begins
344
345impl Quote {
346    pub fn new(c: u8) -> Self {
347        Self(c, c)
348    }
349
350    pub fn left(&self) -> char {
351        char::from(self.0)
352    }
353
354    pub fn right(&self) -> char {
355        char::from(self.1)
356    }
357}
358
359impl From<char> for Quote {
360    fn from(c: char) -> Self {
361        (c as u8).into()
362    }
363}
364
365impl From<(char, char)> for Quote {
366    fn from((l, r): (char, char)) -> Self {
367        (l as u8, r as u8).into()
368    }
369}
370
371impl From<u8> for Quote {
372    fn from(u8: u8) -> Self {
373        Quote::new(u8)
374    }
375}
376
377impl From<(u8, u8)> for Quote {
378    fn from((l, r): (u8, u8)) -> Self {
379        Quote(l, r)
380    }
381}
382
383impl<T: 'static> IntoIden for T
384where
385    T: Iden,
386{
387    fn into_iden(self) -> DynIden {
388        SeaRc::new(self)
389    }
390}
391
392impl IntoIden for DynIden {
393    fn into_iden(self) -> DynIden {
394        self
395    }
396}
397
398impl<I> IdenList for I
399where
400    I: IntoIden,
401{
402    type IntoIter = std::iter::Once<DynIden>;
403
404    fn into_iter(self) -> Self::IntoIter {
405        std::iter::once(self.into_iden())
406    }
407}
408
409impl<A, B> IdenList for (A, B)
410where
411    A: IntoIden,
412    B: IntoIden,
413{
414    type IntoIter = std::array::IntoIter<DynIden, 2>;
415
416    fn into_iter(self) -> Self::IntoIter {
417        [self.0.into_iden(), self.1.into_iden()].into_iter()
418    }
419}
420
421impl<A, B, C> IdenList for (A, B, C)
422where
423    A: IntoIden,
424    B: IntoIden,
425    C: IntoIden,
426{
427    type IntoIter = std::array::IntoIter<DynIden, 3>;
428
429    fn into_iter(self) -> Self::IntoIter {
430        [self.0.into_iden(), self.1.into_iden(), self.2.into_iden()].into_iter()
431    }
432}
433
434impl IntoColumnRef for ColumnRef {
435    fn into_column_ref(self) -> ColumnRef {
436        self
437    }
438}
439
440impl<T: 'static> IntoColumnRef for T
441where
442    T: IntoIden,
443{
444    fn into_column_ref(self) -> ColumnRef {
445        ColumnRef::Column(self.into_iden())
446    }
447}
448
449impl IntoColumnRef for Asterisk {
450    fn into_column_ref(self) -> ColumnRef {
451        ColumnRef::Asterisk
452    }
453}
454
455impl<S: 'static, T: 'static> IntoColumnRef for (S, T)
456where
457    S: IntoIden,
458    T: IntoIden,
459{
460    fn into_column_ref(self) -> ColumnRef {
461        ColumnRef::TableColumn(self.0.into_iden(), self.1.into_iden())
462    }
463}
464
465impl<T: 'static> IntoColumnRef for (T, Asterisk)
466where
467    T: IntoIden,
468{
469    fn into_column_ref(self) -> ColumnRef {
470        ColumnRef::TableAsterisk(self.0.into_iden())
471    }
472}
473
474impl<S: 'static, T: 'static, U: 'static> IntoColumnRef for (S, T, U)
475where
476    S: IntoIden,
477    T: IntoIden,
478    U: IntoIden,
479{
480    fn into_column_ref(self) -> ColumnRef {
481        ColumnRef::SchemaTableColumn(self.0.into_iden(), self.1.into_iden(), self.2.into_iden())
482    }
483}
484
485impl IntoTableRef for TableRef {
486    fn into_table_ref(self) -> TableRef {
487        self
488    }
489}
490
491impl<T: 'static> IntoTableRef for T
492where
493    T: IntoIden,
494{
495    fn into_table_ref(self) -> TableRef {
496        TableRef::Table(self.into_iden())
497    }
498}
499
500impl<S: 'static, T: 'static> IntoTableRef for (S, T)
501where
502    S: IntoIden,
503    T: IntoIden,
504{
505    fn into_table_ref(self) -> TableRef {
506        TableRef::SchemaTable(self.0.into_iden(), self.1.into_iden())
507    }
508}
509
510impl<S: 'static, T: 'static, U: 'static> IntoTableRef for (S, T, U)
511where
512    S: IntoIden,
513    T: IntoIden,
514    U: IntoIden,
515{
516    fn into_table_ref(self) -> TableRef {
517        TableRef::DatabaseSchemaTable(self.0.into_iden(), self.1.into_iden(), self.2.into_iden())
518    }
519}
520
521impl TableRef {
522    /// Add or replace the current alias
523    pub fn alias<A>(self, alias: A) -> Self
524    where
525        A: IntoIden,
526    {
527        match self {
528            Self::Table(table) => Self::TableAlias(table, alias.into_iden()),
529            Self::TableAlias(table, _) => Self::TableAlias(table, alias.into_iden()),
530            Self::SchemaTable(schema, table) => {
531                Self::SchemaTableAlias(schema, table, alias.into_iden())
532            }
533            Self::DatabaseSchemaTable(database, schema, table) => {
534                Self::DatabaseSchemaTableAlias(database, schema, table, alias.into_iden())
535            }
536            Self::SchemaTableAlias(schema, table, _) => {
537                Self::SchemaTableAlias(schema, table, alias.into_iden())
538            }
539            Self::DatabaseSchemaTableAlias(database, schema, table, _) => {
540                Self::DatabaseSchemaTableAlias(database, schema, table, alias.into_iden())
541            }
542            Self::SubQuery(statement, _) => Self::SubQuery(statement, alias.into_iden()),
543            Self::ValuesList(values, _) => Self::ValuesList(values, alias.into_iden()),
544            Self::FunctionCall(func, _) => Self::FunctionCall(func, alias.into_iden()),
545        }
546    }
547}
548
549impl Alias {
550    pub fn new<T>(n: T) -> Self
551    where
552        T: Into<String>,
553    {
554        Self(n.into())
555    }
556}
557
558impl Iden for Alias {
559    fn unquoted(&self, s: &mut dyn fmt::Write) {
560        write!(s, "{}", self.0).unwrap();
561    }
562}
563
564impl NullAlias {
565    pub fn new() -> Self {
566        Self
567    }
568}
569
570impl Iden for NullAlias {
571    fn unquoted(&self, _s: &mut dyn fmt::Write) {}
572}
573
574impl LikeExpr {
575    pub fn new<T>(pattern: T) -> Self
576    where
577        T: Into<String>,
578    {
579        Self {
580            pattern: pattern.into(),
581            escape: None,
582        }
583    }
584
585    #[deprecated(since = "0.29.0", note = "Please use the [`LikeExpr::new`] method")]
586    pub fn str<T>(pattern: T) -> Self
587    where
588        T: Into<String>,
589    {
590        Self {
591            pattern: pattern.into(),
592            escape: None,
593        }
594    }
595
596    pub fn escape(self, c: char) -> Self {
597        Self {
598            pattern: self.pattern,
599            escape: Some(c),
600        }
601    }
602}
603
604impl IntoLikeExpr for LikeExpr {
605    fn into_like_expr(self) -> LikeExpr {
606        self
607    }
608}
609
610impl<T> IntoLikeExpr for T
611where
612    T: Into<String>,
613{
614    fn into_like_expr(self) -> LikeExpr {
615        LikeExpr::new(self)
616    }
617}
618
619#[cfg(test)]
620mod tests {
621    pub use crate::{tests_cfg::*, *};
622    use pretty_assertions::assert_eq;
623    pub use Character as CharReexport;
624
625    #[test]
626    fn test_identifier() {
627        let query = Query::select()
628            .column(Alias::new("hello-World_"))
629            .to_owned();
630
631        #[cfg(feature = "backend-mysql")]
632        assert_eq!(query.to_string(MysqlQueryBuilder), r"SELECT `hello-World_`");
633        #[cfg(feature = "backend-postgres")]
634        assert_eq!(
635            query.to_string(PostgresQueryBuilder),
636            r#"SELECT "hello-World_""#
637        );
638        #[cfg(feature = "backend-sqlite")]
639        assert_eq!(
640            query.to_string(SqliteQueryBuilder),
641            r#"SELECT "hello-World_""#
642        );
643    }
644
645    #[test]
646    fn test_quoted_identifier_1() {
647        let query = Query::select().column(Alias::new("hel`lo")).to_owned();
648
649        #[cfg(feature = "backend-mysql")]
650        assert_eq!(query.to_string(MysqlQueryBuilder), r"SELECT `hel``lo`");
651        #[cfg(feature = "backend-sqlite")]
652        assert_eq!(query.to_string(SqliteQueryBuilder), r#"SELECT "hel`lo""#);
653
654        let query = Query::select().column(Alias::new("hel\"lo")).to_owned();
655
656        #[cfg(feature = "backend-postgres")]
657        assert_eq!(query.to_string(PostgresQueryBuilder), r#"SELECT "hel""lo""#);
658    }
659
660    #[test]
661    fn test_quoted_identifier_2() {
662        let query = Query::select().column(Alias::new("hel``lo")).to_owned();
663
664        #[cfg(feature = "backend-mysql")]
665        assert_eq!(query.to_string(MysqlQueryBuilder), r"SELECT `hel````lo`");
666        #[cfg(feature = "backend-sqlite")]
667        assert_eq!(query.to_string(SqliteQueryBuilder), r#"SELECT "hel``lo""#);
668
669        let query = Query::select().column(Alias::new("hel\"\"lo")).to_owned();
670
671        #[cfg(feature = "backend-postgres")]
672        assert_eq!(
673            query.to_string(PostgresQueryBuilder),
674            r#"SELECT "hel""""lo""#
675        );
676    }
677
678    #[test]
679    fn test_cmp_identifier() {
680        type CharLocal = Character;
681
682        assert_eq!(
683            ColumnRef::Column(Character::Id.into_iden()),
684            ColumnRef::Column(Character::Id.into_iden())
685        );
686        assert_eq!(
687            ColumnRef::Column(Character::Id.into_iden()),
688            ColumnRef::Column(Char::Id.into_iden())
689        );
690        assert_eq!(
691            ColumnRef::Column(Character::Id.into_iden()),
692            ColumnRef::Column(CharLocal::Id.into_iden())
693        );
694        assert_eq!(
695            ColumnRef::Column(Character::Id.into_iden()),
696            ColumnRef::Column(CharReexport::Id.into_iden())
697        );
698        assert_eq!(
699            ColumnRef::Column(Alias::new("id").into_iden()),
700            ColumnRef::Column(Alias::new("id").into_iden())
701        );
702        assert_ne!(
703            ColumnRef::Column(Alias::new("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(Alias::new("id").into_iden())
709        );
710        assert_ne!(
711            ColumnRef::Column(Character::Id.into_iden()),
712            ColumnRef::Column(Character::Table.into_iden())
713        );
714        assert_ne!(
715            ColumnRef::Column(Character::Id.into_iden()),
716            ColumnRef::Column(Font::Id.into_iden())
717        );
718    }
719}