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