diesel/pg/query_builder/copy/
mod.rs

1use crate::pg::Pg;
2use crate::query_builder::nodes::StaticQueryFragment;
3use crate::query_builder::ColumnList;
4use crate::query_builder::QueryFragment;
5use crate::sql_types::SqlType;
6use crate::Expression;
7use crate::{Column, Table};
8
9pub(crate) mod copy_from;
10pub(crate) mod copy_to;
11
12#[cfg(feature = "postgres")]
13pub(crate) use self::copy_from::{CopyFromExpression, InternalCopyFromQuery};
14#[cfg(feature = "postgres")]
15pub(crate) use self::copy_to::CopyToCommand;
16
17pub use self::copy_from::{CopyFromQuery, CopyHeader, ExecuteCopyFromDsl};
18pub use self::copy_to::CopyToQuery;
19
20const COPY_MAGIC_HEADER: [u8; 11] = [
21    0x50, 0x47, 0x43, 0x4F, 0x50, 0x59, 0x0A, 0xFF, 0x0D, 0x0A, 0x00,
22];
23
24/// Describes the format used by `COPY FROM` or `COPY TO`
25/// statements
26///
27/// See [the postgresql documentation](https://www.postgresql.org/docs/current/sql-copy.html)
28/// for details about the different formats
29#[derive(#[automatically_derived]
impl ::core::default::Default for CopyFormat {
    #[inline]
    fn default() -> CopyFormat { Self::Text }
}Default, #[automatically_derived]
impl ::core::fmt::Debug for CopyFormat {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                CopyFormat::Text => "Text",
                CopyFormat::Csv => "Csv",
                CopyFormat::Binary => "Binary",
            })
    }
}Debug, #[automatically_derived]
impl ::core::marker::Copy for CopyFormat { }Copy, #[automatically_derived]
impl ::core::clone::Clone for CopyFormat {
    #[inline]
    fn clone(&self) -> CopyFormat { *self }
}Clone)]
30pub enum CopyFormat {
31    /// The postgresql text format
32    ///
33    /// This format is the default if no format is explicitly set
34    #[default]
35    Text,
36    /// Represents the data as comma separated values (CSV)
37    Csv,
38    /// The postgresql binary format
39    Binary,
40}
41
42impl CopyFormat {
43    fn to_sql_format(self) -> &'static str {
44        match self {
45            CopyFormat::Text => "text",
46            CopyFormat::Csv => "csv",
47            CopyFormat::Binary => "binary",
48        }
49    }
50}
51
52#[derive(#[automatically_derived]
impl ::core::default::Default for CommonOptions {
    #[inline]
    fn default() -> CommonOptions {
        CommonOptions {
            format: ::core::default::Default::default(),
            freeze: ::core::default::Default::default(),
            delimiter: ::core::default::Default::default(),
            null: ::core::default::Default::default(),
            quote: ::core::default::Default::default(),
            escape: ::core::default::Default::default(),
        }
    }
}Default, #[automatically_derived]
impl ::core::fmt::Debug for CommonOptions {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        let names: &'static _ =
            &["format", "freeze", "delimiter", "null", "quote", "escape"];
        let values: &[&dyn ::core::fmt::Debug] =
            &[&self.format, &self.freeze, &self.delimiter, &self.null,
                        &self.quote, &&self.escape];
        ::core::fmt::Formatter::debug_struct_fields_finish(f, "CommonOptions",
            names, values)
    }
}Debug)]
53struct CommonOptions {
54    format: Option<CopyFormat>,
55    freeze: Option<bool>,
56    delimiter: Option<char>,
57    null: Option<String>,
58    quote: Option<char>,
59    escape: Option<char>,
60}
61
62impl CommonOptions {
63    fn any_set(&self) -> bool {
64        self.format.is_some()
65            || self.freeze.is_some()
66            || self.delimiter.is_some()
67            || self.null.is_some()
68            || self.quote.is_some()
69            || self.escape.is_some()
70    }
71
72    fn walk_ast<'b>(
73        &'b self,
74        mut pass: crate::query_builder::AstPass<'_, 'b, Pg>,
75        comma: &mut &'static str,
76    ) {
77        if let Some(format) = self.format {
78            pass.push_sql(comma);
79            *comma = ", ";
80            pass.push_sql("FORMAT ");
81            pass.push_sql(format.to_sql_format());
82        }
83        if let Some(freeze) = self.freeze {
84            pass.push_sql(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{1}FREEZE {0}", freeze as u8,
                comma))
    })format!("{comma}FREEZE {}", freeze as u8));
85            *comma = ", ";
86        }
87        if let Some(delimiter) = self.delimiter {
88            pass.push_sql(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}DELIMITER \'{1}\'", comma,
                delimiter))
    })format!("{comma}DELIMITER '{delimiter}'"));
89            *comma = ", ";
90        }
91        if let Some(ref null) = self.null {
92            pass.push_sql(comma);
93            *comma = ", ";
94            pass.push_sql("NULL '");
95            // we cannot use binds here :(
96            pass.push_sql(null);
97            pass.push_sql("'");
98        }
99        if let Some(quote) = self.quote {
100            pass.push_sql(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}QUOTE \'{1}\'", comma, quote))
    })format!("{comma}QUOTE '{quote}'"));
101            *comma = ", ";
102        }
103        if let Some(escape) = self.escape {
104            pass.push_sql(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}ESCAPE \'{1}\'", comma, escape))
    })format!("{comma}ESCAPE '{escape}'"));
105            *comma = ", ";
106        }
107    }
108}
109
110/// A expression that could be used as target/source for `COPY FROM` and `COPY TO` commands
111///
112/// This trait is implemented for any table type and for tuples of columns from the same table
113pub trait CopyTarget {
114    /// The table targeted by the command
115    type Table: Table;
116    /// The sql side type of the target expression
117    type SqlType: SqlType;
118
119    #[doc(hidden)]
120    fn walk_target(pass: crate::query_builder::AstPass<'_, '_, Pg>) -> crate::QueryResult<()>;
121}
122
123impl<T> CopyTarget for T
124where
125    T: Table + StaticQueryFragment,
126    T::SqlType: SqlType,
127    T::AllColumns: ColumnList,
128    T::Component: QueryFragment<Pg>,
129{
130    type Table = Self;
131    type SqlType = T::SqlType;
132
133    fn walk_target(mut pass: crate::query_builder::AstPass<'_, '_, Pg>) -> crate::QueryResult<()> {
134        T::STATIC_COMPONENT.walk_ast(pass.reborrow())?;
135        pass.push_sql("(");
136        T::all_columns().walk_ast(pass.reborrow())?;
137        pass.push_sql(")");
138        Ok(())
139    }
140}
141
142macro_rules! copy_target_for_columns {
143    ($(
144        $Tuple:tt {
145            $(($idx:tt) -> $T:ident, $ST:ident, $TT:ident,)+
146        }
147    )+) => {
148        $(
149            impl<T, $($ST,)*> CopyTarget for ($($ST,)*)
150            where
151                $($ST: Column<Table = T> + Default,)*
152                ($(<$ST as Expression>::SqlType,)*): SqlType,
153                T: Table + StaticQueryFragment,
154                T::Component: QueryFragment<Pg>,
155                Self: ColumnList,
156            {
157                type Table = T;
158                type SqlType = crate::dsl::SqlTypeOf<Self>;
159
160                fn walk_target(
161                    mut pass: crate::query_builder::AstPass<'_, '_, Pg>,
162                ) -> crate::QueryResult<()> {
163                    T::STATIC_COMPONENT.walk_ast(pass.reborrow())?;
164                    pass.push_sql("(");
165                    <Self as ColumnList>::walk_ast(&($($ST::default(),)*), pass.reborrow())?;
166                    pass.push_sql(")");
167                    Ok(())
168                }
169            }
170        )*
171    }
172}
173
174impl<T, ST, ST1, ST2, ST3, ST4, ST5, ST6, ST7, ST8, ST9, ST10, ST11, ST12,
    ST13, ST14, ST15> CopyTarget for
    (ST, ST1, ST2, ST3, ST4, ST5, ST6, ST7, ST8, ST9, ST10, ST11, ST12, ST13,
    ST14, ST15) where ST: Column<Table = T> + Default,
    ST1: Column<Table = T> + Default, ST2: Column<Table = T> + Default,
    ST3: Column<Table = T> + Default, ST4: Column<Table = T> + Default,
    ST5: Column<Table = T> + Default, ST6: Column<Table = T> + Default,
    ST7: Column<Table = T> + Default, ST8: Column<Table = T> + Default,
    ST9: Column<Table = T> + Default, ST10: Column<Table = T> + Default,
    ST11: Column<Table = T> + Default, ST12: Column<Table = T> + Default,
    ST13: Column<Table = T> + Default, ST14: Column<Table = T> + Default,
    ST15: Column<Table = T> + Default,
    (<ST as Expression>::SqlType, <ST1 as Expression>::SqlType,
    <ST2 as Expression>::SqlType, <ST3 as Expression>::SqlType,
    <ST4 as Expression>::SqlType, <ST5 as Expression>::SqlType,
    <ST6 as Expression>::SqlType, <ST7 as Expression>::SqlType,
    <ST8 as Expression>::SqlType, <ST9 as Expression>::SqlType,
    <ST10 as Expression>::SqlType, <ST11 as Expression>::SqlType,
    <ST12 as Expression>::SqlType, <ST13 as Expression>::SqlType,
    <ST14 as Expression>::SqlType, <ST15 as Expression>::SqlType): SqlType,
    T: Table + StaticQueryFragment, T::Component: QueryFragment<Pg>,
    Self: ColumnList {
    type Table = T;
    type SqlType = crate::dsl::SqlTypeOf<Self>;
    fn walk_target(mut pass: crate::query_builder::AstPass<'_, '_, Pg>)
        -> crate::QueryResult<()> {
        T::STATIC_COMPONENT.walk_ast(pass.reborrow())?;
        pass.push_sql("(");
        <Self as
                    ColumnList>::walk_ast(&(ST::default(), ST1::default(),
                        ST2::default(), ST3::default(), ST4::default(),
                        ST5::default(), ST6::default(), ST7::default(),
                        ST8::default(), ST9::default(), ST10::default(),
                        ST11::default(), ST12::default(), ST13::default(),
                        ST14::default(), ST15::default()), pass.reborrow())?;
        pass.push_sql(")");
        Ok(())
    }
}diesel_derives::__diesel_for_each_tuple!(copy_target_for_columns);