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#[derive(Default, Debug, Copy, Clone)]
30pub enum CopyFormat {
31 #[default]
35 Text,
36 Csv,
38 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(Default, 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(&format!("{comma}FREEZE {}", freeze as u8));
85 *comma = ", ";
86 }
87 if let Some(delimiter) = self.delimiter {
88 pass.push_sql(&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 pass.push_sql(null);
97 pass.push_sql("'");
98 }
99 if let Some(quote) = self.quote {
100 pass.push_sql(&format!("{comma}QUOTE '{quote}'"));
101 *comma = ", ";
102 }
103 if let Some(escape) = self.escape {
104 pass.push_sql(&format!("{comma}ESCAPE '{escape}'"));
105 *comma = ", ";
106 }
107 }
108}
109
110pub trait CopyTarget {
114 type Table: Table;
116 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
174diesel_derives::__diesel_for_each_tuple!(copy_target_for_columns);