1use std::sync::Arc;
5
6use arrow_array::{ArrayRef, RecordBatch};
7use bytes::Bytes;
8use lance_core::{Error, Result, datatypes::Schema};
9use lance_encoding::decoder::{ColumnInfo, PageEncoding};
10use lance_io::object_store::ObjectStore;
11use object_store::path::Path;
12use prost::Message;
13use prost_types::Any;
14
15use crate::{format::pbfile, versions};
16
17mod field_types;
18pub(crate) mod structural;
19
20pub(crate) use field_types::ExpectedTypes;
21pub use field_types::FieldTypeMismatch;
22
23pub(crate) const PAGE_BUFFER_ALIGNMENT: usize = 64;
25pub(crate) const ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES: &str = "LANCE_FILE_WRITER_MAX_PAGE_BYTES";
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct FileWriteSummary {
30 pub num_rows: u64,
32 pub size_bytes: u64,
34}
35
36#[derive(Debug, Clone, Default)]
41pub struct FileWriterOptions {
42 pub data_cache_bytes: Option<u64>,
47 pub max_page_bytes: Option<u64>,
49 pub keep_original_array: Option<bool>,
54}
55
56impl FileWriterOptions {
57 pub(crate) fn validate(&self) -> Result<()> {
58 if self.max_page_bytes == Some(0) {
59 return Err(Error::invalid_input(
60 "max_page_bytes must be greater than 0, got 0",
61 ));
62 }
63 Ok(())
64 }
65}
66
67pub enum FileWriter {
73 V2_0(Box<versions::v2_0::Writer>),
74 V2_1(Box<versions::v2_1::Writer>),
75 V2_2(Box<versions::v2_2::Writer>),
76 V2_3(Box<versions::v2_3::Writer>),
77}
78
79fn column_info_to_metadata(column: &ColumnInfo) -> Result<pbfile::ColumnMetadata> {
80 let pages = column
81 .page_infos
82 .iter()
83 .map(|page| {
84 let encoding = match &page.encoding {
85 PageEncoding::Legacy(encoding) => Any::from_msg(encoding)?.encode_to_vec(),
86 PageEncoding::Structural(encoding) => Any::from_msg(encoding)?.encode_to_vec(),
87 };
88 let (buffer_offsets, buffer_sizes) =
89 page.buffer_offsets_and_sizes.iter().copied().unzip();
90 Ok(pbfile::column_metadata::Page {
91 buffer_offsets,
92 buffer_sizes,
93 encoding: Some(pbfile::Encoding {
94 location: Some(pbfile::encoding::Location::Direct(pbfile::DirectEncoding {
95 encoding,
96 })),
97 }),
98 length: page.num_rows,
99 priority: page.priority,
100 })
101 })
102 .collect::<Result<Vec<_>>>()?;
103 let (buffer_offsets, buffer_sizes) = column.buffer_offsets_and_sizes.iter().copied().unzip();
104 let encoding = Any::from_msg(&column.encoding)?.encode_to_vec();
105 Ok(pbfile::ColumnMetadata {
106 pages,
107 buffer_offsets,
108 buffer_sizes,
109 encoding: Some(pbfile::Encoding {
110 location: Some(pbfile::encoding::Location::Direct(pbfile::DirectEncoding {
111 encoding,
112 })),
113 }),
114 })
115}
116
117impl From<versions::v2_0::Writer> for FileWriter {
118 fn from(writer: versions::v2_0::Writer) -> Self {
119 Self::V2_0(Box::new(writer))
120 }
121}
122
123impl From<versions::v2_1::Writer> for FileWriter {
124 fn from(writer: versions::v2_1::Writer) -> Self {
125 Self::V2_1(Box::new(writer))
126 }
127}
128
129impl From<versions::v2_2::Writer> for FileWriter {
130 fn from(writer: versions::v2_2::Writer) -> Self {
131 Self::V2_2(Box::new(writer))
132 }
133}
134
135impl From<versions::v2_3::Writer> for FileWriter {
136 fn from(writer: versions::v2_3::Writer) -> Self {
137 Self::V2_3(Box::new(writer))
138 }
139}
140
141impl FileWriter {
142 pub fn with_page_metadata_spill(self, object_store: Arc<ObjectStore>, path: Path) -> Self {
144 match self {
145 Self::V2_0(writer) => Self::V2_0(Box::new(
146 (*writer).with_page_metadata_spill(object_store, path),
147 )),
148 Self::V2_1(writer) => Self::V2_1(Box::new(
149 (*writer).with_page_metadata_spill(object_store, path),
150 )),
151 Self::V2_2(writer) => Self::V2_2(Box::new(
152 (*writer).with_page_metadata_spill(object_store, path),
153 )),
154 Self::V2_3(writer) => Self::V2_3(Box::new(
155 (*writer).with_page_metadata_spill(object_store, path),
156 )),
157 }
158 }
159
160 pub async fn write_batches(
162 &mut self,
163 batches: impl Iterator<Item = &RecordBatch>,
164 ) -> Result<()> {
165 for batch in batches {
166 self.write_batch(batch).await?;
167 }
168 Ok(())
169 }
170
171 pub async fn write_batch(&mut self, batch: &RecordBatch) -> Result<()> {
173 match self {
174 Self::V2_0(writer) => writer.write_batch(batch).await,
175 Self::V2_1(writer) => writer.write_batch(batch).await,
176 Self::V2_2(writer) => writer.write_batch(batch).await,
177 Self::V2_3(writer) => writer.write_batch(batch).await,
178 }
179 }
180
181 pub async fn write_column(&mut self, column_index: usize, array: ArrayRef) -> Result<()> {
183 match self {
184 Self::V2_0(writer) => writer.write_column(column_index, array).await,
185 Self::V2_1(writer) => writer.write_column(column_index, array).await,
186 Self::V2_2(writer) => writer.write_column(column_index, array).await,
187 Self::V2_3(writer) => writer.write_column(column_index, array).await,
188 }
189 }
190
191 pub async fn write_external_buffer(&mut self, bytes: &[u8]) -> Result<(u64, u64)> {
193 match self {
194 Self::V2_0(writer) => writer.write_external_buffer(bytes).await,
195 Self::V2_1(writer) => writer.write_external_buffer(bytes).await,
196 Self::V2_2(writer) => writer.write_external_buffer(bytes).await,
197 Self::V2_3(writer) => writer.write_external_buffer(bytes).await,
198 }
199 }
200
201 pub fn add_schema_metadata(&mut self, key: impl Into<String>, value: impl Into<String>) {
203 let key = key.into();
204 let value = value.into();
205 match self {
206 Self::V2_0(writer) => writer.add_schema_metadata(key, value),
207 Self::V2_1(writer) => writer.add_schema_metadata(key, value),
208 Self::V2_2(writer) => writer.add_schema_metadata(key, value),
209 Self::V2_3(writer) => writer.add_schema_metadata(key, value),
210 }
211 }
212
213 pub fn initialize_with_external_columns(
215 &mut self,
216 schema: Schema,
217 columns: &[Arc<ColumnInfo>],
218 rows_written: u64,
219 ) -> Result<()> {
220 let column_metadata = columns
221 .iter()
222 .map(|column| column_info_to_metadata(column))
223 .collect::<Result<Vec<_>>>()?;
224 match self {
225 Self::V2_0(writer) => {
226 writer.initialize_with_external_metadata(schema, column_metadata, rows_written)
227 }
228 Self::V2_1(writer) => {
229 writer.initialize_with_external_metadata(schema, column_metadata, rows_written)
230 }
231 Self::V2_2(writer) => {
232 writer.initialize_with_external_metadata(schema, column_metadata, rows_written)
233 }
234 Self::V2_3(writer) => {
235 writer.initialize_with_external_metadata(schema, column_metadata, rows_written)
236 }
237 }
238 Ok(())
239 }
240
241 pub async fn add_global_buffer(&mut self, buffer: Bytes) -> Result<u32> {
243 match self {
244 Self::V2_0(writer) => writer.add_global_buffer(buffer).await,
245 Self::V2_1(writer) => writer.add_global_buffer(buffer).await,
246 Self::V2_2(writer) => writer.add_global_buffer(buffer).await,
247 Self::V2_3(writer) => writer.add_global_buffer(buffer).await,
248 }
249 }
250
251 pub async fn finish(&mut self) -> Result<FileWriteSummary> {
253 match self {
254 Self::V2_0(writer) => writer.finish().await,
255 Self::V2_1(writer) => writer.finish().await,
256 Self::V2_2(writer) => writer.finish().await,
257 Self::V2_3(writer) => writer.finish().await,
258 }
259 }
260
261 pub async fn abort(&mut self) {
263 match self {
264 Self::V2_0(writer) => writer.abort().await,
265 Self::V2_1(writer) => writer.abort().await,
266 Self::V2_2(writer) => writer.abort().await,
267 Self::V2_3(writer) => writer.abort().await,
268 }
269 }
270
271 pub async fn tell(&mut self) -> Result<u64> {
273 match self {
274 Self::V2_0(writer) => writer.tell().await,
275 Self::V2_1(writer) => writer.tell().await,
276 Self::V2_2(writer) => writer.tell().await,
277 Self::V2_3(writer) => writer.tell().await,
278 }
279 }
280
281 pub fn field_id_to_column_indices(&self) -> &[(u32, u32)] {
283 match self {
284 Self::V2_0(writer) => writer.field_id_to_column_indices(),
285 Self::V2_1(writer) => writer.field_id_to_column_indices(),
286 Self::V2_2(writer) => writer.field_id_to_column_indices(),
287 Self::V2_3(writer) => writer.field_id_to_column_indices(),
288 }
289 }
290}
291
292#[cfg(test)]
293#[path = "writer_tests.rs"]
294mod writer_tests;