1use crate::{
4 data_order::{coordinate, DataOrder},
5 destinations::{Destination, DestinationPartition},
6 errors::{ConnectorXError, Result as CXResult},
7 sources::{PartitionParser, Source, SourcePartition},
8 sql::CXQuery,
9 typesystem::Transport,
10};
11use itertools::Itertools;
12use log::debug;
13use rayon::prelude::*;
14use std::marker::PhantomData;
15
16pub type Prepared<'w, S, D> = (
19 DataOrder,
20 Vec<<S as Source>::Partition>,
21 Vec<<D as Destination>::Partition<'w>>,
22 Vec<<S as Source>::TypeSystem>,
23 Vec<<D as Destination>::TypeSystem>,
24);
25
26pub struct Dispatcher<'a, S, D, TP> {
29 src: S,
30 dst: &'a mut D,
31 queries: Vec<CXQuery<String>>,
32 origin_query: Option<String>,
33 _phantom: PhantomData<TP>,
34}
35
36impl<'w, S, D, TP> Dispatcher<'w, S, D, TP>
37where
38 S: Source,
39 D: Destination,
40 TP: Transport<TSS = S::TypeSystem, TSD = D::TypeSystem, S = S, D = D>,
41{
42 pub fn new<Q>(src: S, dst: &'w mut D, queries: &[Q], origin_query: Option<String>) -> Self
44 where
45 for<'a> &'a Q: Into<CXQuery>,
46 {
47 Self {
48 src,
49 dst,
50 queries: queries.iter().map(Into::into).collect(),
51 origin_query,
52 _phantom: PhantomData,
53 }
54 }
55
56 pub fn set_pre_execution_queries(&mut self, pre_execution_queries: Option<&[String]>) {
57 self.src.set_pre_execution_queries(pre_execution_queries);
58 }
59
60 pub fn prepare(mut self) -> Result<Prepared<'w, S, D>, TP::Error> {
61 debug!("Prepare");
62 let dorder = coordinate(S::DATA_ORDERS, D::DATA_ORDERS)?;
63 self.src.set_data_order(dorder)?;
64 self.src.set_queries(self.queries.as_slice());
65 self.src.set_origin_query(self.origin_query);
66
67 debug!("Fetching metadata");
68 self.src.fetch_metadata()?;
69 let src_schema = self.src.schema();
70 let dst_schema = src_schema
71 .iter()
72 .map(|&s| TP::convert_typesystem(s))
73 .collect::<CXResult<Vec<_>>>()?;
74 let names = self.src.names();
75
76 let mut total_rows = if self.dst.needs_count() {
77 debug!("Try get row rounts for entire result");
79 self.src.result_rows()?
80 } else {
81 debug!("Do not need counts in advance");
82 Some(0)
83 };
84 let mut src_partitions: Vec<S::Partition> = self.src.partition()?;
85 if self.dst.needs_count() && total_rows.is_none() {
86 debug!("Manually count rows of each partitioned query and sum up");
87 src_partitions
89 .par_iter_mut()
90 .try_for_each(|partition| -> Result<(), S::Error> { partition.result_rows() })?;
91
92 let part_rows: Vec<usize> = src_partitions
94 .iter()
95 .map(|partition| partition.nrows())
96 .collect();
97 total_rows = Some(part_rows.iter().sum());
98 }
99 let total_rows = total_rows.ok_or_else(ConnectorXError::CountError)?;
100
101 debug!(
102 "Allocate destination memory: {}x{}",
103 total_rows,
104 src_schema.len()
105 );
106 self.dst.allocate(total_rows, &names, &dst_schema, dorder)?;
107
108 debug!("Create destination partition");
109 let dst_partitions = self.dst.partition(self.queries.len())?;
110
111 Ok((
112 dorder,
113 src_partitions,
114 dst_partitions,
115 src_schema,
116 dst_schema,
117 ))
118 }
119
120 pub fn run(self) -> Result<(), TP::Error> {
122 debug!("Run dispatcher");
123 let (dorder, src_partitions, dst_partitions, src_schema, dst_schema) = self.prepare()?;
124
125 #[cfg(all(not(feature = "branch"), not(feature = "fptr")))]
126 compile_error!("branch or fptr, pick one");
127
128 #[cfg(feature = "branch")]
129 let schemas: Vec<_> = src_schema
130 .iter()
131 .zip_eq(&dst_schema)
132 .map(|(&src_ty, &dst_ty)| (src_ty, dst_ty))
133 .collect();
134
135 debug!("Start writing");
136 dst_partitions
138 .into_par_iter()
139 .zip_eq(src_partitions)
140 .enumerate()
141 .try_for_each(|(i, (mut dst, mut src))| -> Result<(), TP::Error> {
142 #[cfg(feature = "fptr")]
143 let f: Vec<_> = src_schema
144 .iter()
145 .zip_eq(&dst_schema)
146 .map(|(&src_ty, &dst_ty)| TP::processor(src_ty, dst_ty))
147 .collect::<CXResult<Vec<_>>>()?;
148
149 let mut parser = src.parser()?;
150
151 match dorder {
152 DataOrder::RowMajor => loop {
153 let (n, is_last) = parser.fetch_next()?;
154 dst.aquire_row(n)?;
155 for _ in 0..n {
156 #[allow(clippy::needless_range_loop)]
157 for col in 0..dst.ncols() {
158 #[cfg(feature = "fptr")]
159 f[col](&mut parser, &mut dst)?;
160
161 #[cfg(feature = "branch")]
162 {
163 let (s1, s2) = schemas[col];
164 TP::process(s1, s2, &mut parser, &mut dst)?;
165 }
166 }
167 }
168 if is_last {
169 break;
170 }
171 },
172 DataOrder::ColumnMajor => loop {
173 let (n, is_last) = parser.fetch_next()?;
174 dst.aquire_row(n)?;
175 #[allow(clippy::needless_range_loop)]
176 for col in 0..dst.ncols() {
177 for _ in 0..n {
178 #[cfg(feature = "fptr")]
179 f[col](&mut parser, &mut dst)?;
180 #[cfg(feature = "branch")]
181 {
182 let (s1, s2) = schemas[col];
183 TP::process(s1, s2, &mut parser, &mut dst)?;
184 }
185 }
186 }
187 if is_last {
188 break;
189 }
190 },
191 }
192
193 debug!("Finalize partition {}", i);
194 dst.finalize()?;
195 debug!("Partition {} finished", i);
196 Ok(())
197 })?;
198
199 debug!("Writing finished");
200
201 Ok(())
202 }
203
204 pub fn get_meta(&mut self) -> Result<(), TP::Error> {
206 let dorder = coordinate(S::DATA_ORDERS, D::DATA_ORDERS)?;
207 self.src.set_data_order(dorder)?;
208 self.src.set_queries(self.queries.as_slice());
209 self.src.set_origin_query(self.origin_query.clone());
210 self.src.fetch_metadata()?;
211 let src_schema = self.src.schema();
212 let dst_schema = src_schema
213 .iter()
214 .map(|&s| TP::convert_typesystem(s))
215 .collect::<CXResult<Vec<_>>>()?;
216 let names = self.src.names();
217 self.dst.allocate(0, &names, &dst_schema, dorder)?;
218 Ok(())
219 }
220}