dataset_writer/
partitioned.rs1use std::collections::HashMap;
7use std::collections::hash_map::Entry;
8use std::ffi::OsString;
9use std::num::NonZeroU16;
10use std::path::PathBuf;
11
12use anyhow::{ensure, Context, Result};
13use rayon::prelude::*;
14
15use crate::TableWriter;
16
17pub type PartitionedTableWriter<PartitionWriter> = U16PartitionedTableWriter<PartitionWriter>;
19
20pub struct U16PartitionedTableWriter<PartitionWriter: TableWriter + Send> {
28 partition_writers: Vec<PartitionWriter>,
29}
30
31impl<PartitionWriter: TableWriter + Send> TableWriter
32 for U16PartitionedTableWriter<PartitionWriter>
33{
34 type Schema = (String, Option<NonZeroU16>, PartitionWriter::Schema);
36 type CloseResult = Vec<PartitionWriter::CloseResult>;
37 type Config = PartitionWriter::Config;
38
39 fn new(
40 mut path: PathBuf,
41 (partition_column, num_partitions, schema): Self::Schema,
42 config: Self::Config,
43 ) -> Result<Self> {
44 let thread_id = path.file_name().map(|p| p.to_owned());
47 ensure!(
48 path.pop(),
49 "Unexpected root path for partitioned writer: {}",
50 path.display()
51 );
52 let thread_id = thread_id.unwrap();
53 Ok(U16PartitionedTableWriter {
54 partition_writers: (0..num_partitions.map(NonZeroU16::get).unwrap_or(1))
55 .map(|partition_id| {
56 let partition_path = if num_partitions.is_some() {
57 path.join(format!("{}={}", partition_column, partition_id))
58 } else {
59 path.to_owned()
61 };
62 std::fs::create_dir_all(&partition_path).with_context(|| {
63 format!("Could not create {}", partition_path.display())
64 })?;
65 PartitionWriter::new(
66 partition_path.join(&thread_id),
67 schema.clone(),
68 config.clone(),
69 )
70 })
71 .collect::<Result<_>>()?,
72 })
73 }
74
75 fn flush(&mut self) -> Result<()> {
76 self.partition_writers
77 .par_iter_mut()
78 .try_for_each(|writer| writer.flush())
79 }
80
81 fn close(self) -> Result<Self::CloseResult> {
82 self.partition_writers
83 .into_par_iter()
84 .map(|writer| writer.close())
85 .collect()
86 }
87}
88
89impl<PartitionWriter: TableWriter + Send> U16PartitionedTableWriter<PartitionWriter> {
90 pub fn partitions(&mut self) -> &mut [PartitionWriter] {
91 &mut self.partition_writers
92 }
93}
94
95pub struct Utf8PartitionedTableWriter<PartitionWriter: TableWriter + Send> {
102 path: PathBuf,
103 partition_column: String,
104 schema: PartitionWriter::Schema,
105 config: PartitionWriter::Config,
106 thread_id: OsString,
107 partition_writers: HashMap<String, PartitionWriter>,
108}
109
110impl<PartitionWriter: TableWriter + Send> TableWriter
111 for Utf8PartitionedTableWriter<PartitionWriter>
112{
113 type Schema = (String, PartitionWriter::Schema);
115 type CloseResult = Vec<PartitionWriter::CloseResult>;
116 type Config = PartitionWriter::Config;
117
118 fn new(
119 mut path: PathBuf,
120 (partition_column, schema): Self::Schema,
121 config: Self::Config,
122 ) -> Result<Self> {
123 let thread_id = path.file_name().map(|p| p.to_owned());
126 ensure!(
127 path.pop(),
128 "Unexpected root path for partitioned writer: {}",
129 path.display()
130 );
131 let thread_id = thread_id.unwrap();
132 Ok(Utf8PartitionedTableWriter {
133 path,
134 partition_column,
135 schema,
136 config,
137 thread_id,
138 partition_writers: HashMap::new(),
139 })
140 }
141
142 fn flush(&mut self) -> Result<()> {
143 self.partition_writers
144 .par_iter_mut()
145 .try_for_each(|(_partitiong_key, writer)| writer.flush())
146 }
147
148 fn close(self) -> Result<Self::CloseResult> {
149 self.partition_writers
150 .into_par_iter()
151 .map(|(_partitiong_key, writer)| writer.close())
152 .collect()
153 }
154}
155
156impl<PartitionWriter: TableWriter + Send> Utf8PartitionedTableWriter<PartitionWriter> {
157 pub fn partition(&mut self, partition_key: String) -> Result<&mut PartitionWriter> {
158 match self.partition_writers.entry(partition_key) {
159 Entry::Occupied(entry) => Ok(entry.into_mut()),
160 Entry::Vacant(entry) => {
161 let partition_path = self
162 .path
163 .join(format!("{}={}", self.partition_column, entry.key()));
164 std::fs::create_dir_all(&partition_path)
165 .with_context(|| format!("Could not create {}", partition_path.display()))?;
166 Ok(entry.insert(PartitionWriter::new(
167 partition_path.join(&self.thread_id),
168 self.schema.clone(),
169 self.config.clone(),
170 )?))
171 }
172 }
173 }
174 pub fn partitions(&mut self) -> &mut HashMap<String, PartitionWriter> {
175 &mut self.partition_writers
176 }
177}