Skip to main content

diesel/pg/query_builder/copy/
copy_to.rs

1use core::marker::PhantomData;
2use std::io::BufRead;
3
4use super::CommonOptions;
5use super::CopyFormat;
6use super::CopyTarget;
7use crate::AppearsOnTable;
8use crate::Connection;
9use crate::Expression;
10use crate::QueryResult;
11use crate::Selectable;
12use crate::deserialize::FromSqlRow;
13use crate::pg::Pg;
14#[cfg(feature = "postgres")]
15use crate::pg::value::TypeOidLookup;
16use crate::query_builder::QueryFragment;
17use crate::query_builder::QueryId;
18use crate::row::Row;
19#[cfg(feature = "postgres")]
20use crate::row::{self, Field, PartialRow, RowIndex, RowSealed};
21
22#[derive(#[automatically_derived]impl ::core::default::Default for CopyToOptions {
    #[inline]
    fn default() -> Self {
        CopyToOptions {
            common: ::core::default::Default::default(),
            header: ::core::default::Default::default(),
        }
    }
}Default, #[automatically_derived]impl ::core::fmt::Debug for CopyToOptions {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "CopyToOptions common header", &self.common, &&self.header)
    }
}Debug)]
23pub struct CopyToOptions {
24    common: CommonOptions,
25    header: Option<bool>,
26}
27
28impl CopyToOptions {
29    fn any_set(&self) -> bool {
30        self.common.any_set() || self.header.is_some()
31    }
32}
33
34impl QueryFragment<Pg> for CopyToOptions {
35    fn walk_ast<'b>(
36        &'b self,
37        mut pass: crate::query_builder::AstPass<'_, 'b, Pg>,
38    ) -> crate::QueryResult<()> {
39        if self.any_set() {
40            let mut comma = "";
41            pass.push_sql(" WITH (");
42            self.common.walk_ast(pass.reborrow(), &mut comma);
43            if let Some(header_is_set) = self.header {
44                pass.push_sql(comma);
45                // commented out because rustc complains otherwise
46                //comma = ", ";
47                pass.push_sql("HEADER ");
48                pass.push_sql(if header_is_set { "1" } else { "0" });
49            }
50
51            pass.push_sql(")");
52        }
53        Ok(())
54    }
55}
56
57#[derive(#[automatically_derived]impl<S: ::core::fmt::Debug> ::core::fmt::Debug for
    CopyToCommand<S> {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "CopyToCommand options p", &self.options, &&self.p)
    }
}Debug)]
58pub struct CopyToCommand<S> {
59    options: CopyToOptions,
60    p: PhantomData<S>,
61}
62
63impl<S> QueryId for CopyToCommand<S>
64where
65    S: CopyTarget,
66{
67    type QueryId = ();
68
69    const HAS_STATIC_QUERY_ID: bool = false;
70}
71
72impl<S> QueryFragment<Pg> for CopyToCommand<S>
73where
74    S: CopyTarget,
75{
76    fn walk_ast<'b>(
77        &'b self,
78        mut pass: crate::query_builder::AstPass<'_, 'b, Pg>,
79    ) -> crate::QueryResult<()> {
80        pass.unsafe_to_cache_prepared();
81        pass.push_sql("COPY ");
82        S::walk_target(pass.reborrow())?;
83        pass.push_sql(" TO STDOUT");
84        self.options.walk_ast(pass.reborrow())?;
85        Ok(())
86    }
87}
88
89#[derive(#[automatically_derived]impl ::core::fmt::Debug for NotSet {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "NotSet")
    }
}Debug, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for NotSet {
}
#[automatically_derived]impl ::core::clone::Clone for NotSet {
    #[inline]
    #[rustc_trivial_field_reads]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::marker::Copy for NotSet { }Copy)]
90pub struct NotSet;
91
92pub trait CopyToMarker: Sized {
93    fn setup_options<T>(q: CopyToQuery<T, Self>) -> CopyToQuery<T, CopyToOptions>;
94}
95
96impl CopyToMarker for NotSet {
97    fn setup_options<T>(q: CopyToQuery<T, Self>) -> CopyToQuery<T, CopyToOptions> {
98        CopyToQuery {
99            target: q.target,
100            options: CopyToOptions::default(),
101        }
102    }
103}
104impl CopyToMarker for CopyToOptions {
105    fn setup_options<T>(q: CopyToQuery<T, Self>) -> CopyToQuery<T, CopyToOptions> {
106        q
107    }
108}
109/// The structure returned by [`copy_to`]
110///
111/// The [`load`] and the [`load_raw`] methods allow
112/// to receive the configured data from the database.
113/// If you don't have any special needs you should prefer using
114/// the more convenient `load` method.
115///
116/// The `with_*` methods allow to configure the settings used for the
117/// copy statement.
118///
119/// [`load`]: CopyToQuery::load
120/// [`load_raw`]: CopyToQuery::load_raw
121#[derive(#[automatically_derived]impl<T: ::core::fmt::Debug, O: ::core::fmt::Debug>
    ::core::fmt::Debug for CopyToQuery<T, O> {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "CopyToQuery target options", &self.target, &&self.options)
    }
}Debug)]
122#[must_use = "`COPY TO` statements are only executed when calling `.load()` or `load_raw()`."]
123#[cfg(feature = "postgres_backend")]
124pub struct CopyToQuery<T, O> {
125    target: T,
126    options: O,
127}
128
129#[cfg(feature = "postgres")]
130struct CopyRow<'a> {
131    buffers: Vec<Option<&'a [u8]>>,
132    result: &'a crate::pg::connection::PgResult,
133}
134
135#[cfg(feature = "postgres")]
136struct CopyField<'a> {
137    field: &'a Option<&'a [u8]>,
138    result: &'a crate::pg::connection::PgResult,
139    col_idx: usize,
140}
141
142#[cfg(feature = "postgres")]
143impl<'f> Field<'f, Pg> for CopyField<'f> {
144    fn field_name(&self) -> Option<&str> {
145        None
146    }
147
148    fn value(&self) -> Option<<Pg as crate::backend::Backend>::RawValue<'_>> {
149        let value = self.field.as_deref()?;
150        Some(crate::pg::PgValue::new_internal(value, self))
151    }
152}
153
154#[cfg(feature = "postgres")]
155impl TypeOidLookup for CopyField<'_> {
156    fn lookup(&self) -> core::num::NonZeroU32 {
157        self.result.column_type(self.col_idx)
158    }
159}
160
161#[cfg(feature = "postgres")]
162impl RowSealed for CopyRow<'_> {}
163
164#[cfg(feature = "postgres")]
165impl RowIndex<usize> for CopyRow<'_> {
166    fn idx(&self, idx: usize) -> Option<usize> {
167        if idx < self.field_count() {
168            Some(idx)
169        } else {
170            None
171        }
172    }
173}
174
175#[cfg(feature = "postgres")]
176impl<'a> RowIndex<&'a str> for CopyRow<'_> {
177    fn idx(&self, _idx: &'a str) -> Option<usize> {
178        None
179    }
180}
181
182#[cfg(feature = "postgres")]
183impl<'a> Row<'a, Pg> for CopyRow<'_> {
184    type Field<'f>
185        = CopyField<'f>
186    where
187        'a: 'f,
188        Self: 'f;
189
190    type InnerPartialRow = Self;
191
192    fn field_count(&self) -> usize {
193        self.buffers.len()
194    }
195
196    fn get<'b, I>(&'b self, idx: I) -> Option<Self::Field<'b>>
197    where
198        'a: 'b,
199        Self: RowIndex<I>,
200    {
201        let idx = self.idx(idx)?;
202        let buffer = self.buffers.get(idx)?;
203        Some(CopyField {
204            field: buffer,
205            result: self.result,
206            col_idx: idx,
207        })
208    }
209
210    fn partial_row(
211        &self,
212        range: core::ops::Range<usize>,
213    ) -> row::PartialRow<'_, Self::InnerPartialRow> {
214        PartialRow::new(self, range)
215    }
216}
217
218pub trait ExecuteCopyToConnection: Connection<Backend = Pg> {
219    type CopyToBuffer<'a>: BufRead;
220
221    fn make_row<'a, 'b>(
222        out: &'a Self::CopyToBuffer<'_>,
223        buffers: Vec<Option<&'a [u8]>>,
224    ) -> impl Row<'b, Pg> + 'a;
225
226    fn get_buffer<'a>(out: &'a Self::CopyToBuffer<'_>) -> &'a [u8];
227
228    fn execute<T>(&mut self, command: CopyToCommand<T>) -> QueryResult<Self::CopyToBuffer<'_>>
229    where
230        T: CopyTarget;
231}
232
233#[cfg(feature = "postgres")]
234impl ExecuteCopyToConnection for crate::PgConnection {
235    type CopyToBuffer<'a> = crate::pg::connection::copy::CopyToBuffer<'a>;
236
237    fn make_row<'a, 'b>(
238        out: &'a Self::CopyToBuffer<'_>,
239        buffers: Vec<Option<&'a [u8]>>,
240    ) -> impl Row<'b, Pg> + 'a {
241        CopyRow {
242            buffers,
243            result: out.get_result(),
244        }
245    }
246
247    fn get_buffer<'a>(out: &'a Self::CopyToBuffer<'_>) -> &'a [u8] {
248        out.data_slice()
249    }
250
251    fn execute<T>(&mut self, command: CopyToCommand<T>) -> QueryResult<Self::CopyToBuffer<'_>>
252    where
253        T: CopyTarget,
254    {
255        self.copy_to(command)
256    }
257}
258
259#[cfg(feature = "r2d2")]
260impl<C> ExecuteCopyToConnection for crate::r2d2::PooledConnection<crate::r2d2::ConnectionManager<C>>
261where
262    C: ExecuteCopyToConnection + crate::r2d2::R2D2Connection + 'static,
263{
264    type CopyToBuffer<'a> = C::CopyToBuffer<'a>;
265
266    fn make_row<'a, 'b>(
267        out: &'a Self::CopyToBuffer<'_>,
268        buffers: Vec<Option<&'a [u8]>>,
269    ) -> impl Row<'b, Pg> + 'a {
270        C::make_row(out, buffers)
271    }
272
273    fn get_buffer<'a>(out: &'a Self::CopyToBuffer<'_>) -> &'a [u8] {
274        C::get_buffer(out)
275    }
276
277    fn execute<T>(&mut self, command: CopyToCommand<T>) -> QueryResult<Self::CopyToBuffer<'_>>
278    where
279        T: CopyTarget,
280    {
281        C::execute(&mut **self, command)
282    }
283}
284
285impl<T> CopyToQuery<T, NotSet>
286where
287    T: CopyTarget,
288{
289    /// Copy data from the database by returning an iterator of deserialized data
290    ///
291    /// This function allows to easily load data from the database via a `COPY TO` statement.
292    /// It does **not** allow to configure any settings via the `with_*` method, as it internally
293    /// sets the required options itself. It will use the binary format to deserialize the result
294    /// into the specified type `U`. Column selection is performed via [`Selectable`].
295    pub fn load<U, C>(self, conn: &mut C) -> QueryResult<impl Iterator<Item = QueryResult<U>> + '_>
296    where
297        U: FromSqlRow<<U::SelectExpression as Expression>::SqlType, Pg> + Selectable<Pg>,
298        U::SelectExpression: AppearsOnTable<T::Table> + CopyTarget<Table = T::Table>,
299        C: ExecuteCopyToConnection,
300    {
301        let command = CopyToCommand {
302            p: PhantomData::<U::SelectExpression>,
303            options: CopyToOptions {
304                header: None,
305                common: CommonOptions {
306                    format: Some(CopyFormat::Binary),
307                    ..Default::default()
308                },
309            },
310        };
311        let out = ExecuteCopyToConnection::execute(conn, command)?;
312        decode_data::<U, C>(out)
313    }
314}
315
316fn decode_data<'a, U, C>(
317    mut out: C::CopyToBuffer<'a>,
318) -> QueryResult<impl Iterator<Item = QueryResult<U>> + 'a>
319where
320    U: FromSqlRow<<U::SelectExpression as Expression>::SqlType, Pg> + Selectable<Pg>,
321    C: ExecuteCopyToConnection + 'a,
322{
323    let io_result_mapper = |e| crate::result::Error::DeserializationError(Box::new(e));
324    // see https://www.postgresql.org/docs/current/sql-copy.html for
325    // a description of the binary format
326    //
327    // We don't write oids
328
329    let buffer = out.fill_buf().map_err(io_result_mapper)?;
330    expect_buffer_len(buffer, super::COPY_MAGIC_HEADER.len() + 8)?;
331    if buffer[..super::COPY_MAGIC_HEADER.len()] != super::COPY_MAGIC_HEADER {
332        return Err(crate::result::Error::DeserializationError(
333            "Unexpected protocol header".into(),
334        ));
335    }
336    // we care only about bit 16-31 here, so we can just skip the bytes in between
337    let flags_backward_incompatible = i16::from_be_bytes(
338        (&buffer[super::COPY_MAGIC_HEADER.len() + 2..super::COPY_MAGIC_HEADER.len() + 4])
339            .try_into()
340            .expect("Exactly 2 byte"),
341    );
342    if flags_backward_incompatible != 0 {
343        return Err(crate::result::Error::DeserializationError(
344            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Unexpected flag value: {0:x}",
                flags_backward_incompatible))
    })format!("Unexpected flag value: {flags_backward_incompatible:x}").into(),
345        ));
346    }
347    let header_size = usize::try_from(i32::from_be_bytes(
348        (&buffer[super::COPY_MAGIC_HEADER.len() + 4..super::COPY_MAGIC_HEADER.len() + 8])
349            .try_into()
350            .expect("Exactly 4 byte"),
351    ))
352    .map_err(|e| crate::result::Error::DeserializationError(Box::new(e)))?;
353    out.consume(super::COPY_MAGIC_HEADER.len() + 8 + header_size);
354    let mut len = None;
355    let mut finished = false;
356    Ok(core::iter::from_fn(move || {
357        if finished {
358            return None;
359        }
360        if let Some(len) = len {
361            out.consume(len);
362            if let Err(e) = out.fill_buf().map_err(io_result_mapper) {
363                finished = true;
364                return Some(Err(e));
365            }
366        }
367        let buffer = C::get_buffer(&out);
368        len = Some(buffer.len());
369        if let Err(e) = expect_buffer_len(buffer, 2) {
370            finished = true;
371            return Some(Err(e));
372        }
373        let tuple_count = i16::from_be_bytes((&buffer[..2]).try_into().expect("Exactly 2 bytes"));
374        if tuple_count > 0 {
375            let tuple_count = match usize::try_from(tuple_count) {
376                Ok(o) => o,
377                Err(e) => {
378                    return Some(Err(crate::result::Error::DeserializationError(Box::new(e))));
379                }
380            };
381            let mut buffers = Vec::with_capacity(tuple_count);
382            let mut offset = 2;
383            for _t in 0..tuple_count {
384                if let Err(e) = expect_buffer_len(buffer, offset + 4) {
385                    finished = true;
386                    return Some(Err(e));
387                }
388                let data_size = i32::from_be_bytes(
389                    (&buffer[offset..offset + 4])
390                        .try_into()
391                        .expect("Exactly 4 bytes"),
392                );
393                if data_size < 0 {
394                    offset += 4;
395                    buffers.push(None);
396                } else {
397                    match usize::try_from(data_size) {
398                        Ok(data_size) => {
399                            if let Err(e) = expect_buffer_len(buffer, offset + 4 + data_size) {
400                                finished = true;
401                                return Some(Err(e));
402                            }
403                            buffers.push(Some(&buffer[offset + 4..offset + 4 + data_size]));
404                            offset += data_size + 4;
405                        }
406                        Err(e) => {
407                            return Some(Err(crate::result::Error::DeserializationError(
408                                Box::new(e),
409                            )));
410                        }
411                    }
412                }
413            }
414
415            let row = C::make_row(&out, buffers);
416            Some(U::build_from_row(&row).map_err(crate::result::Error::DeserializationError))
417        } else {
418            out.consume(len.expect("It's always set here"));
419            finished = true;
420            if let Err(e) = out.fill_buf().map_err(io_result_mapper) {
421                return Some(Err(e));
422            }
423            None
424        }
425    }))
426}
427
428fn expect_buffer_len(buffer: &[u8], expected: usize) -> QueryResult<()> {
429    if buffer.len() < expected {
430        Err(crate::result::Error::DeserializationError(
431            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Expected at least {1} bytes but got {0} bytes",
                buffer.len(), expected))
    })format!(
432                "Expected at least {expected} bytes but got {} bytes",
433                buffer.len()
434            )
435            .into(),
436        ))
437    } else {
438        Ok(())
439    }
440}
441
442impl<T, O> CopyToQuery<T, O>
443where
444    O: CopyToMarker,
445    T: CopyTarget,
446{
447    /// Copy data from the database by directly accessing the provided response
448    ///
449    /// This function returns a type that implements [`std::io::BufRead`] which allows to directly read
450    /// the data as provided by the database. The exact format depends on what options are
451    /// set via the various `with_*` methods.
452    pub fn load_raw<C>(self, conn: &mut C) -> QueryResult<impl BufRead + '_>
453    where
454        C: ExecuteCopyToConnection,
455    {
456        let q = O::setup_options(self);
457        let command = CopyToCommand {
458            p: PhantomData::<T>,
459            options: q.options,
460        };
461        ExecuteCopyToConnection::execute(conn, command)
462    }
463
464    /// The format used for the copy statement
465    ///
466    /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html)
467    /// for more details.
468    pub fn with_format(self, format: CopyFormat) -> CopyToQuery<T, CopyToOptions> {
469        let mut out = O::setup_options(self);
470        out.options.common.format = Some(format);
471        out
472    }
473
474    /// Whether or not the `freeze` option is set
475    ///
476    /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html)
477    /// for more details.
478    pub fn with_freeze(self, freeze: bool) -> CopyToQuery<T, CopyToOptions> {
479        let mut out = O::setup_options(self);
480        out.options.common.freeze = Some(freeze);
481        out
482    }
483
484    /// Which delimiter should be used for textual output formats
485    ///
486    /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html)
487    /// for more details.
488    ///
489    /// Diesel will automatically escape the provided delimiter for the default
490    /// PostgreSQL setting `standard_conforming_strings=on`. If you use a non-standard
491    /// conforming setting you need to take care of escaping the value on your own
492    pub fn with_delimiter(self, delimiter: char) -> CopyToQuery<T, CopyToOptions> {
493        let mut out = O::setup_options(self);
494        out.options.common.delimiter = Some(delimiter);
495        out
496    }
497
498    /// Which string should be used in place of a `NULL` value
499    /// for textual output formats
500    ///
501    /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html)
502    /// for more details.
503    ///
504    /// Diesel will automatically escape the provided delimiter for the default
505    /// PostgreSQL setting `standard_conforming_strings=on`. If you use a non-standard
506    /// conforming setting you need to take care of escaping the value on your own
507    pub fn with_null(self, null: impl Into<String>) -> CopyToQuery<T, CopyToOptions> {
508        let mut out = O::setup_options(self);
509        out.options.common.null = Some(null.into());
510        out
511    }
512
513    /// Which quote character should be used for textual output formats
514    ///
515    /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html)
516    /// for more details.
517    ///
518    /// Diesel will automatically escape the provided delimiter for the default
519    /// PostgreSQL setting `standard_conforming_strings=on`. If you use a non-standard
520    /// conforming setting you need to take care of escaping the value on your own
521    pub fn with_quote(self, quote: char) -> CopyToQuery<T, CopyToOptions> {
522        let mut out = O::setup_options(self);
523        out.options.common.quote = Some(quote);
524        out
525    }
526
527    /// Which escape character should be used for textual output formats
528    ///
529    /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html)
530    /// for more details.
531    ///
532    /// Diesel will automatically escape the provided delimiter for the default
533    /// PostgreSQL setting `standard_conforming_strings=on`. If you use a non-standard
534    /// conforming setting you need to take care of escaping the value on your own
535    pub fn with_escape(self, escape: char) -> CopyToQuery<T, CopyToOptions> {
536        let mut out = O::setup_options(self);
537        out.options.common.escape = Some(escape);
538        out
539    }
540
541    /// Is a header provided as part of the textual input or not
542    ///
543    /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html)
544    /// for more details.
545    pub fn with_header(self, set: bool) -> CopyToQuery<T, CopyToOptions> {
546        let mut out = O::setup_options(self);
547        out.options.header = Some(set);
548        out
549    }
550}
551
552/// Creates a `COPY TO` statement
553///
554/// This function constructs a `COPY TO` statement which copies data
555/// from the database **to** a client side target. It's designed to move
556/// larger amounts of data out of the database.
557///
558/// This function accepts a target selection (table name or list of columns) as argument.
559///
560/// There are two ways to use a `COPY TO` statement with diesel:
561///
562/// * By using [`CopyToQuery::load`] directly to load the deserialized result
563///   directly into a specified type
564/// * By using the `with_*` methods to configure the format sent by the database
565///   and then by calling [`CopyToQuery::load_raw`] to receive the raw data
566///   sent by the database.
567///
568/// The first variant uses the `BINARY` format internally to receive
569/// the selected data efficiently. It automatically sets the right options
570/// and does not allow to change them via `with_*` methods.
571///
572/// The second variant allows you to control the behaviour of the
573/// generated `COPY TO` statement in detail. You can use the various
574/// `with_*` methods for that before issuing the statement via [`CopyToQuery::load_raw`].
575/// That method will return an type that implements [`std::io::BufRead`], which
576/// allows you to directly read the response from the database in the configured
577/// format.
578/// See [the postgresql documentation](https://www.postgresql.org/docs/current/sql-copy.html)
579/// for more details about the supported formats.
580///
581/// If you don't have any specific needs you should prefer using the more
582/// convenient first variant.
583///
584/// This functionality is postgresql specific.
585///
586/// # Examples
587///
588/// ## Via [`CopyToQuery::load()`]
589///
590/// ```rust
591/// # include!("../../../doctest_setup.rs");
592/// # use crate::schema::users;
593///
594/// #[derive(Queryable, Selectable, PartialEq, Debug)]
595/// #[diesel(table_name = users)]
596/// #[diesel(check_for_backend(diesel::pg::Pg))]
597/// struct User {
598///     name: String,
599/// }
600///
601/// # fn run_test() -> QueryResult<()> {
602/// # let connection = &mut establish_connection();
603/// let out = diesel::copy_to(users::table)
604///     .load::<User, _>(connection)?
605///     .collect::<Result<Vec<_>, _>>()?;
606///
607/// assert_eq!(
608///     out,
609///     vec![
610///         User {
611///             name: "Sean".into()
612///         },
613///         User {
614///             name: "Tess".into()
615///         }
616///     ]
617/// );
618/// # Ok(())
619/// # }
620/// # fn main() {
621/// #    run_test().unwrap();
622/// # }
623/// ```
624///
625/// ## Via [`CopyToQuery::load_raw()`]
626///
627/// ```rust
628/// # include!("../../../doctest_setup.rs");
629/// # fn run_test() -> QueryResult<()> {
630/// # use crate::schema::users;
631/// use diesel::pg::CopyFormat;
632/// use std::io::Read;
633/// # let connection = &mut establish_connection();
634///
635/// let mut copy = diesel::copy_to(users::table)
636///     .with_format(CopyFormat::Csv)
637///     .load_raw(connection)?;
638///
639/// let mut out = String::new();
640/// copy.read_to_string(&mut out).unwrap();
641/// assert_eq!(out, "1,Sean\n2,Tess\n");
642/// # Ok(())
643/// # }
644/// # fn main() {
645/// #    run_test().unwrap();
646/// # }
647/// ```
648#[cfg(feature = "postgres_backend")]
649pub fn copy_to<T>(target: T) -> CopyToQuery<T, NotSet>
650where
651    T: CopyTarget,
652{
653    CopyToQuery {
654        target,
655        options: NotSet,
656    }
657}
658
659#[cfg(test)]
660mod tests {
661
662    use super::*;
663    use crate::connection::Instrumentation;
664    use crate::connection::{AnsiTransactionManager, TransactionManager};
665    use crate::pg::query_builder::copy::COPY_MAGIC_HEADER;
666    use std::collections::VecDeque;
667    use std::io::Read;
668
669    struct Wrapper;
670
671    /// Mimics `CopyToBuffer`: hands out one `CopyData` message at a time
672    /// and only moves on to the next one once the current one is consumed
673    struct Messages {
674        messages: VecDeque<Vec<u8>>,
675        offset: usize,
676    }
677
678    impl Messages {
679        fn new(messages: Vec<Vec<u8>>) -> Self {
680            Self {
681                messages: messages.into(),
682                offset: 0,
683            }
684        }
685
686        fn data_slice(&self) -> &[u8] {
687            self.messages
688                .front()
689                .map(|m| &m[self.offset..])
690                .unwrap_or_default()
691        }
692    }
693
694    impl Read for Messages {
695        fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
696            let data = self.fill_buf()?;
697            let len = usize::min(buf.len(), data.len());
698            buf[..len].copy_from_slice(&data[..len]);
699            self.consume(len);
700            Ok(len)
701        }
702    }
703
704    impl BufRead for Messages {
705        fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
706            if self.data_slice().is_empty() && self.messages.pop_front().is_some() {
707                self.offset = 0;
708            }
709            Ok(self.data_slice())
710        }
711
712        fn consume(&mut self, amt: usize) {
713            let len = self.messages.front().map_or(0, Vec::len);
714            self.offset = usize::min(len, self.offset + amt);
715        }
716    }
717
718    impl crate::connection::SimpleConnection for Wrapper {
719        fn batch_execute(&mut self, _query: &str) -> QueryResult<()> {
720            unreachable!()
721        }
722    }
723
724    impl crate::connection::ConnectionSealed for Wrapper {}
725
726    impl crate::connection::Connection for Wrapper {
727        type Backend = crate::pg::Pg;
728
729        type TransactionManager = crate::connection::AnsiTransactionManager;
730
731        fn establish(_database_url: &str) -> crate::prelude::ConnectionResult<Self> {
732            unreachable!()
733        }
734
735        fn execute_returning_count<T>(&mut self, _source: &T) -> QueryResult<usize>
736        where
737            T: QueryFragment<Self::Backend> + QueryId,
738        {
739            unreachable!()
740        }
741
742        fn transaction_state(
743            &mut self,
744        ) -> &mut <AnsiTransactionManager as TransactionManager<Self>>::TransactionStateData
745        {
746            unreachable!()
747        }
748
749        fn instrumentation(&mut self) -> &mut dyn Instrumentation {
750            unreachable!()
751        }
752
753        fn set_instrumentation(
754            &mut self,
755            _instrumentation: impl crate::connection::Instrumentation,
756        ) {
757            unreachable!()
758        }
759
760        fn set_prepared_statement_cache_size(&mut self, _size: crate::connection::CacheSize) {
761            unreachable!()
762        }
763    }
764
765    impl ExecuteCopyToConnection for Wrapper {
766        type CopyToBuffer<'a> = Messages;
767
768        fn make_row<'a, 'b>(
769            _out: &'a Self::CopyToBuffer<'_>,
770            _buffers: Vec<Option<&'a [u8]>>,
771        ) -> impl Row<'b, Pg> + 'a {
772            // the tests below never decode a complete row
773            unreachable!() as CopyRow<'a>
774        }
775
776        fn get_buffer<'a>(out: &'a Self::CopyToBuffer<'_>) -> &'a [u8] {
777            out.data_slice()
778        }
779
780        fn execute<T>(&mut self, _command: CopyToCommand<T>) -> QueryResult<Self::CopyToBuffer<'_>>
781        where
782            T: CopyTarget,
783        {
784            unreachable!()
785        }
786    }
787
788    crate::table! {
789        users {
790            id -> Integer,
791            name -> Text,
792            hair_color -> Nullable<Text>,
793            age -> Nullable<Integer>,
794        }
795    }
796
797    #[derive(diesel::Queryable, diesel::Selectable, Debug)]
798    #[diesel(table_name = users)]
799    #[allow(dead_code, reason = "Only used as structure for the test")]
800    struct Dummy {
801        id: i32,
802        name: String,
803        hair_color: Option<String>,
804        age: Option<i32>,
805    }
806
807    fn decode(messages: Vec<Vec<u8>>) -> QueryResult<impl Iterator<Item = QueryResult<Dummy>>> {
808        decode_data::<Dummy, Wrapper>(Messages::new(messages))
809    }
810
811    fn tuple_count(n: i16) -> [u8; 2] {
812        n.to_be_bytes()
813    }
814
815    fn field_len(n: i32) -> [u8; 4] {
816        n.to_be_bytes()
817    }
818
819    #[track_caller]
820    fn first_row_error(first_message: &[&[u8]]) -> String {
821        let first_message = [
822            COPY_MAGIC_HEADER.as_slice(),
823            [0; 8].as_slice(),
824            &first_message.concat(),
825        ]
826        .concat();
827        let mut rows = decode(vec![first_message]).unwrap();
828        let error = match rows.next() {
829            Some(Err(e)) => e.to_string(),
830            Some(Ok(row)) => panic!("expected an error, got {row:?}"),
831            None => panic!("expected an error, got the end of the data"),
832        };
833        assert!(
834            rows.next().is_none(),
835            "the iterator must end after an error"
836        );
837        error
838    }
839
840    #[track_caller]
841    fn check_decode_error(input: Vec<Vec<u8>>, error: &str) {
842        let r = decode(input);
843        assert!(r.is_err());
844        let Err(e) = r else { unreachable!() };
845        assert_eq!(e.to_string(), error);
846    }
847
848    #[test]
849    fn check_empty_buffer_returns_error() {
850        check_decode_error(
851            vec![Vec::new()],
852            "Expected at least 19 bytes but got 0 bytes",
853        );
854    }
855
856    #[test]
857    fn check_header_without_flags_returns_error() {
858        check_decode_error(
859            vec![COPY_MAGIC_HEADER.to_vec()],
860            "Expected at least 19 bytes but got 11 bytes",
861        );
862    }
863
864    #[test]
865    fn check_header_without_extension_length_returns_error() {
866        check_decode_error(
867            vec![[COPY_MAGIC_HEADER.as_slice(), &[0; 7]].concat().to_vec()],
868            "Expected at least 19 bytes but got 18 bytes",
869        );
870    }
871
872    #[test]
873    fn check_missing_trailer_returns_error() {
874        // the data ends right after the header
875        assert_eq!(
876            first_row_error(&[]),
877            "Expected at least 2 bytes but got 0 bytes",
878        );
879    }
880
881    #[test]
882    fn check_truncated_tuple_count_returns_error() {
883        assert_eq!(
884            first_row_error(&[&[0]]),
885            "Expected at least 2 bytes but got 1 bytes"
886        );
887    }
888
889    #[test]
890    fn check_missing_field_length_returns_error() {
891        assert_eq!(
892            first_row_error(&[&tuple_count(4)]),
893            "Expected at least 6 bytes but got 2 bytes"
894        );
895    }
896
897    #[test]
898    fn check_truncated_field_length_returns_error() {
899        assert_eq!(
900            first_row_error(&[&tuple_count(4), &[0, 0]]),
901            "Expected at least 6 bytes but got 4 bytes"
902        );
903    }
904
905    #[test]
906    fn check_missing_field_length_after_value_returns_error() {
907        assert_eq!(
908            first_row_error(&[&tuple_count(4), &field_len(4), &1_i32.to_be_bytes()]),
909            "Expected at least 14 bytes but got 10 bytes"
910        );
911    }
912
913    #[test]
914    fn check_missing_field_length_after_null_returns_error() {
915        assert_eq!(
916            first_row_error(&[&tuple_count(4), &field_len(-1)]),
917            "Expected at least 10 bytes but got 6 bytes"
918        );
919    }
920
921    #[test]
922    fn check_missing_field_data_returns_error() {
923        assert_eq!(
924            first_row_error(&[&tuple_count(4), &field_len(4)]),
925            "Expected at least 10 bytes but got 6 bytes"
926        );
927    }
928
929    #[test]
930    fn check_truncated_field_data_returns_error() {
931        assert_eq!(
932            first_row_error(&[&tuple_count(4), &field_len(100), b"abc"]),
933            "Expected at least 106 bytes but got 9 bytes"
934        );
935    }
936
937    #[test]
938    fn check_truncated_later_field_data_returns_error() {
939        assert_eq!(
940            first_row_error(&[
941                &tuple_count(4),
942                &field_len(4),
943                &1_i32.to_be_bytes(),
944                &field_len(5),
945                b"Sean",
946            ]),
947            "Expected at least 19 bytes but got 18 bytes"
948        );
949    }
950
951    #[test]
952    fn check_field_length_i32_max_returns_error() {
953        assert_eq!(
954            first_row_error(&[&tuple_count(4), &field_len(i32::MAX), b"abc"]),
955            format!(
956                "Expected at least {} bytes but got 9 bytes",
957                6 + i32::MAX as usize
958            )
959        );
960    }
961}