Skip to main content

diesel/pg/query_builder/copy/
mod.rs

1use crate::Expression;
2use crate::pg::Pg;
3use crate::query_builder::ColumnList;
4use crate::query_builder::QueryFragment;
5use crate::query_builder::nodes::StaticQueryFragment;
6use crate::sql_types::SqlType;
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            // we need to take care for the quoting character here
89            let delimiter = if delimiter == '\'' {
90                "''".into()
91            } else {
92                String::from(delimiter)
93            };
94            pass.push_sql(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}DELIMITER \'{1}\'", comma,
                delimiter))
    })format!("{comma}DELIMITER '{delimiter}'"));
95            *comma = ", ";
96        }
97        if let Some(ref null) = self.null {
98            pass.push_sql(comma);
99            *comma = ", ";
100            // we cannot use binds here
101            // so we need to make sure quotes in
102            // the input are handled correctly
103            let null = null.replace('\'', "''");
104            pass.push_sql("NULL '");
105            pass.push_sql(&null);
106            pass.push_sql("'");
107        }
108        if let Some(quote) = self.quote {
109            // we need to take care for the quoting character here
110            let quote = if quote == '\'' {
111                "''".into()
112            } else {
113                String::from(quote)
114            };
115            pass.push_sql(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}QUOTE \'{1}\'", comma, quote))
    })format!("{comma}QUOTE '{quote}'"));
116            *comma = ", ";
117        }
118        if let Some(escape) = self.escape {
119            // we need to take care for the quoting character here
120            let escape = if escape == '\'' {
121                "''".into()
122            } else {
123                String::from(escape)
124            };
125            pass.push_sql(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}ESCAPE \'{1}\'", comma, escape))
    })format!("{comma}ESCAPE '{escape}'"));
126            *comma = ", ";
127        }
128    }
129}
130
131/// A expression that could be used as target/source for `COPY FROM` and `COPY TO` commands
132///
133/// This trait is implemented for any table type and for tuples of columns from the same table
134pub trait CopyTarget {
135    /// The table targeted by the command
136    type Table: Table;
137    /// The sql side type of the target expression
138    type SqlType: SqlType;
139
140    #[doc(hidden)]
141    fn walk_target(pass: crate::query_builder::AstPass<'_, '_, Pg>) -> crate::QueryResult<()>;
142}
143
144impl<T> CopyTarget for T
145where
146    T: Table + StaticQueryFragment,
147    T::SqlType: SqlType,
148    T::AllColumns: ColumnList,
149    T::Component: QueryFragment<Pg>,
150{
151    type Table = Self;
152    type SqlType = T::SqlType;
153
154    fn walk_target(mut pass: crate::query_builder::AstPass<'_, '_, Pg>) -> crate::QueryResult<()> {
155        T::STATIC_COMPONENT.walk_ast(pass.reborrow())?;
156        pass.push_sql("(");
157        T::all_columns().walk_ast(pass.reborrow())?;
158        pass.push_sql(")");
159        Ok(())
160    }
161}
162
163macro_rules! copy_target_for_columns {
164    ($(
165        $Tuple:tt {
166            $(($idx:tt) -> $T:ident, $ST:ident, $TT:ident,)+
167        }
168    )+) => {
169        $(
170            impl<T, $($ST,)*> CopyTarget for ($($ST,)*)
171            where
172                $($ST: Column<Table = T> + Default,)*
173                ($(<$ST as Expression>::SqlType,)*): SqlType,
174                T: Table + StaticQueryFragment,
175                T::Component: QueryFragment<Pg>,
176                Self: ColumnList,
177            {
178                type Table = T;
179                type SqlType = crate::dsl::SqlTypeOf<Self>;
180
181                fn walk_target(
182                    mut pass: crate::query_builder::AstPass<'_, '_, Pg>,
183                ) -> crate::QueryResult<()> {
184                    T::STATIC_COMPONENT.walk_ast(pass.reborrow())?;
185                    pass.push_sql("(");
186                    <Self as ColumnList>::walk_ast(&($($ST::default(),)*), pass.reborrow())?;
187                    pass.push_sql(")");
188                    Ok(())
189                }
190            }
191        )*
192    }
193}
194
195impl<T, ST, ST1, ST2, ST3, ST4, ST5, ST6, ST7, ST8, ST9, ST10, ST11, ST12,
    ST13, ST14, ST15, ST16, ST17, ST18, ST19, ST20, ST21, ST22, ST23, ST24,
    ST25, ST26, ST27, ST28, ST29, ST30, ST31> CopyTarget for
    (ST, ST1, ST2, ST3, ST4, ST5, ST6, ST7, ST8, ST9, ST10, ST11, ST12, ST13,
    ST14, ST15, ST16, ST17, ST18, ST19, ST20, ST21, ST22, ST23, ST24, ST25,
    ST26, ST27, ST28, ST29, ST30, ST31) 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, ST16: Column<Table = T> + Default,
    ST17: Column<Table = T> + Default, ST18: Column<Table = T> + Default,
    ST19: Column<Table = T> + Default, ST20: Column<Table = T> + Default,
    ST21: Column<Table = T> + Default, ST22: Column<Table = T> + Default,
    ST23: Column<Table = T> + Default, ST24: Column<Table = T> + Default,
    ST25: Column<Table = T> + Default, ST26: Column<Table = T> + Default,
    ST27: Column<Table = T> + Default, ST28: Column<Table = T> + Default,
    ST29: Column<Table = T> + Default, ST30: Column<Table = T> + Default,
    ST31: 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,
    <ST16 as Expression>::SqlType, <ST17 as Expression>::SqlType,
    <ST18 as Expression>::SqlType, <ST19 as Expression>::SqlType,
    <ST20 as Expression>::SqlType, <ST21 as Expression>::SqlType,
    <ST22 as Expression>::SqlType, <ST23 as Expression>::SqlType,
    <ST24 as Expression>::SqlType, <ST25 as Expression>::SqlType,
    <ST26 as Expression>::SqlType, <ST27 as Expression>::SqlType,
    <ST28 as Expression>::SqlType, <ST29 as Expression>::SqlType,
    <ST30 as Expression>::SqlType, <ST31 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(), ST16::default(),
                        ST17::default(), ST18::default(), ST19::default(),
                        ST20::default(), ST21::default(), ST22::default(),
                        ST23::default(), ST24::default(), ST25::default(),
                        ST26::default(), ST27::default(), ST28::default(),
                        ST29::default(), ST30::default(), ST31::default()),
                pass.reborrow())?;
        pass.push_sql(")");
        Ok(())
    }
}diesel_derives::__diesel_for_each_tuple!(copy_target_for_columns);