Skip to content

Commit 78fb56f

Browse files
committed
Make it possible to use copy_from/copy_to with dynamic tables
Previously, copy_from and copy_to would only be possible with tables known at compile time. It is now possible to also use them with tables that are only known at runtime. That is achieved by changing the signature of `CopyTarget::walk_target` to take a `self` argument.
1 parent f583fff commit 78fb56f

File tree

3 files changed

+35
-22
lines changed

3 files changed

+35
-22
lines changed

diesel/src/pg/query_builder/copy/copy_from.rs

+22-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::borrow::Cow;
22
use std::marker::PhantomData;
3+
use std::rc::Rc;
34

45
use byteorder::NetworkEndian;
56
use byteorder::WriteBytesExt;
@@ -87,7 +88,7 @@ impl CopyFromOptions {
8788
pub struct CopyFrom<S, F> {
8889
options: CopyFromOptions,
8990
copy_callback: F,
90-
p: PhantomData<S>,
91+
target: S,
9192
}
9293

9394
pub(crate) struct InternalCopyFromQuery<S, T> {
@@ -163,7 +164,7 @@ where
163164
&'b self,
164165
pass: crate::query_builder::AstPass<'_, 'b, Pg>,
165166
) -> crate::QueryResult<()> {
166-
S::walk_target(pass)
167+
self.target.walk_target(pass)
167168
}
168169
}
169170

@@ -269,12 +270,16 @@ macro_rules! impl_copy_from_insertable_helper_for_values_clause {
269270
diesel_derives::__diesel_for_each_tuple!(impl_copy_from_insertable_helper_for_values_clause);
270271

271272
#[derive(Debug)]
272-
pub struct InsertableWrapper<I>(Option<I>);
273+
pub struct InsertableWrapper<I, T> {
274+
table: Rc<T>,
275+
insertable: Option<I>,
276+
}
273277

274-
impl<I, T, V, QId, const STATIC_QUERY_ID: bool> CopyFromExpression<T> for InsertableWrapper<I>
278+
impl<I, T, V, QId, const STATIC_QUERY_ID: bool> CopyFromExpression<T> for InsertableWrapper<I, T>
275279
where
276280
I: Insertable<T, Values = BatchInsert<Vec<V>, T, QId, STATIC_QUERY_ID>>,
277281
V: CopyFromInsertableHelper,
282+
T: CopyTarget,
278283
{
279284
type Error = crate::result::Error;
280285

@@ -298,7 +303,7 @@ where
298303
// this skips reallocating
299304
let mut buffer = Vec::<u8>::new();
300305
let values = self
301-
.0
306+
.insertable
302307
.take()
303308
.expect("We only call this callback once")
304309
.values();
@@ -354,7 +359,7 @@ where
354359
&'b self,
355360
pass: crate::query_builder::AstPass<'_, 'b, Pg>,
356361
) -> crate::QueryResult<()> {
357-
<V as CopyFromInsertableHelper>::Target::walk_target(pass)
362+
self.table.walk_target(pass)
358363
}
359364
}
360365

@@ -372,7 +377,7 @@ where
372377
#[must_use = "`COPY FROM` statements are only executed when calling `.execute()`."]
373378
#[cfg(feature = "postgres_backend")]
374379
pub struct CopyFromQuery<T, Action> {
375-
table: T,
380+
table: Rc<T>,
376381
action: Action,
377382
}
378383

@@ -386,15 +391,15 @@ where
386391
/// `action` expects a callback which accepts a [`std::io::Write`] argument. The necessary format
387392
/// accepted by this writer sink depends on the options provided via the `with_*` methods
388393
#[allow(clippy::wrong_self_convention)] // the sql struct is named that way
389-
pub fn from_raw_data<F, C, E>(self, _target: C, action: F) -> CopyFromQuery<T, CopyFrom<C, F>>
394+
pub fn from_raw_data<F, C, E>(self, target: C, action: F) -> CopyFromQuery<T, CopyFrom<C, F>>
390395
where
391396
C: CopyTarget<Table = T>,
392397
F: Fn(&mut dyn std::io::Write) -> Result<(), E>,
393398
{
394399
CopyFromQuery {
395400
table: self.table,
396401
action: CopyFrom {
397-
p: PhantomData,
402+
target,
398403
options: Default::default(),
399404
copy_callback: action,
400405
},
@@ -411,13 +416,16 @@ where
411416
/// This uses the binary format. It internally configures the correct
412417
/// set of settings and does not allow to set other options
413418
#[allow(clippy::wrong_self_convention)] // the sql struct is named that way
414-
pub fn from_insertable<I>(self, insertable: I) -> CopyFromQuery<T, InsertableWrapper<I>>
419+
pub fn from_insertable<I>(self, insertable: I) -> CopyFromQuery<T, InsertableWrapper<I, T>>
415420
where
416-
InsertableWrapper<I>: CopyFromExpression<T>,
421+
InsertableWrapper<I, T>: CopyFromExpression<T>,
417422
{
418423
CopyFromQuery {
419-
table: self.table,
420-
action: InsertableWrapper(Some(insertable)),
424+
table: self.table.clone(),
425+
action: InsertableWrapper {
426+
table: self.table,
427+
insertable: Some(insertable),
428+
},
421429
}
422430
}
423431
}
@@ -650,7 +658,7 @@ where
650658
T: Table,
651659
{
652660
CopyFromQuery {
653-
table,
661+
table: Rc::new(table),
654662
action: NotSet,
655663
}
656664
}

diesel/src/pg/query_builder/copy/copy_to.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::io::BufRead;
2-
use std::marker::PhantomData;
32

43
use super::CommonOptions;
54
use super::CopyFormat;
@@ -57,7 +56,7 @@ impl QueryFragment<Pg> for CopyToOptions {
5756
#[derive(Debug)]
5857
pub struct CopyToCommand<S> {
5958
options: CopyToOptions,
60-
p: PhantomData<S>,
59+
target: S,
6160
}
6261

6362
impl<S> QueryId for CopyToCommand<S>
@@ -79,7 +78,7 @@ where
7978
) -> crate::QueryResult<()> {
8079
pass.unsafe_to_cache_prepared();
8180
pass.push_sql("COPY ");
82-
S::walk_target(pass.reborrow())?;
81+
self.target.walk_target(pass.reborrow())?;
8382
pass.push_sql(" TO STDOUT");
8483
self.options.walk_ast(pass.reborrow())?;
8584
Ok(())
@@ -301,7 +300,7 @@ where
301300
let io_result_mapper = |e| crate::result::Error::DeserializationError(Box::new(e));
302301

303302
let command = CopyToCommand {
304-
p: PhantomData::<U::SelectExpression>,
303+
target: self.target,
305304
options: CopyToOptions {
306305
header: None,
307306
common: CommonOptions {
@@ -410,7 +409,7 @@ where
410409
{
411410
let q = O::setup_options(self);
412411
let command = CopyToCommand {
413-
p: PhantomData::<T>,
412+
target: q.target,
414413
options: q.options,
415414
};
416415
ExecuteCopyToConnection::execute(conn, command)

diesel/src/pg/query_builder/copy/mod.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,10 @@ pub trait CopyTarget {
117117
type SqlType: SqlType;
118118

119119
#[doc(hidden)]
120-
fn walk_target(pass: crate::query_builder::AstPass<'_, '_, Pg>) -> crate::QueryResult<()>;
120+
fn walk_target(
121+
&self,
122+
pass: crate::query_builder::AstPass<'_, '_, Pg>,
123+
) -> crate::QueryResult<()>;
121124
}
122125

123126
impl<T> CopyTarget for T
@@ -130,7 +133,10 @@ where
130133
type Table = Self;
131134
type SqlType = T::SqlType;
132135

133-
fn walk_target(mut pass: crate::query_builder::AstPass<'_, '_, Pg>) -> crate::QueryResult<()> {
136+
fn walk_target(
137+
&self,
138+
mut pass: crate::query_builder::AstPass<'_, '_, Pg>,
139+
) -> crate::QueryResult<()> {
134140
T::STATIC_COMPONENT.walk_ast(pass.reborrow())?;
135141
pass.push_sql("(");
136142
T::all_columns().walk_ast(pass.reborrow())?;
@@ -157,7 +163,7 @@ macro_rules! copy_target_for_columns {
157163
type Table = T;
158164
type SqlType = crate::dsl::SqlTypeOf<Self>;
159165

160-
fn walk_target(
166+
fn walk_target(&self,
161167
mut pass: crate::query_builder::AstPass<'_, '_, Pg>,
162168
) -> crate::QueryResult<()> {
163169
T::STATIC_COMPONENT.walk_ast(pass.reborrow())?;

0 commit comments

Comments
 (0)