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
79pub(crate) fn encode_page_encoding(encoding: &PageEncoding) -> Result<Vec<u8>> {
81 let envelope = match encoding {
82 PageEncoding::Legacy(layout) => Any::from_msg(layout)?,
83 PageEncoding::Structural(layout) => {
84 let layout = lance_encoding::format::pb21::PageLayout::try_from(layout.clone())?;
85 Any::from_msg(&layout)?
86 }
87 PageEncoding::Planar(layout) => Any::from_msg(layout)?,
88 };
89 Ok(envelope.encode_to_vec())
90}
91
92fn column_info_to_metadata(column: &ColumnInfo) -> Result<pbfile::ColumnMetadata> {
93 let pages = column
94 .page_infos
95 .iter()
96 .map(|page| {
97 let encoding = encode_page_encoding(&page.encoding)?;
98 let (buffer_offsets, buffer_sizes) =
99 page.buffer_offsets_and_sizes.iter().copied().unzip();
100 Ok(pbfile::column_metadata::Page {
101 buffer_offsets,
102 buffer_sizes,
103 encoding: Some(pbfile::Encoding {
104 location: Some(pbfile::encoding::Location::Direct(pbfile::DirectEncoding {
105 encoding,
106 })),
107 }),
108 length: page.num_rows,
109 priority: page.priority,
110 })
111 })
112 .collect::<Result<Vec<_>>>()?;
113 let (buffer_offsets, buffer_sizes) = column.buffer_offsets_and_sizes.iter().copied().unzip();
114 let encoding = Any::from_msg(&column.encoding)?.encode_to_vec();
115 Ok(pbfile::ColumnMetadata {
116 pages,
117 buffer_offsets,
118 buffer_sizes,
119 encoding: Some(pbfile::Encoding {
120 location: Some(pbfile::encoding::Location::Direct(pbfile::DirectEncoding {
121 encoding,
122 })),
123 }),
124 })
125}
126
127impl From<versions::v2_0::Writer> for FileWriter {
128 fn from(writer: versions::v2_0::Writer) -> Self {
129 Self::V2_0(Box::new(writer))
130 }
131}
132
133impl From<versions::v2_1::Writer> for FileWriter {
134 fn from(writer: versions::v2_1::Writer) -> Self {
135 Self::V2_1(Box::new(writer))
136 }
137}
138
139impl From<versions::v2_2::Writer> for FileWriter {
140 fn from(writer: versions::v2_2::Writer) -> Self {
141 Self::V2_2(Box::new(writer))
142 }
143}
144
145impl From<versions::v2_3::Writer> for FileWriter {
146 fn from(writer: versions::v2_3::Writer) -> Self {
147 Self::V2_3(Box::new(writer))
148 }
149}
150
151impl FileWriter {
152 pub fn with_page_metadata_spill(self, object_store: Arc<ObjectStore>, path: Path) -> Self {
154 match self {
155 Self::V2_0(writer) => Self::V2_0(Box::new(
156 (*writer).with_page_metadata_spill(object_store, path),
157 )),
158 Self::V2_1(writer) => Self::V2_1(Box::new(
159 (*writer).with_page_metadata_spill(object_store, path),
160 )),
161 Self::V2_2(writer) => Self::V2_2(Box::new(
162 (*writer).with_page_metadata_spill(object_store, path),
163 )),
164 Self::V2_3(writer) => Self::V2_3(Box::new(
165 (*writer).with_page_metadata_spill(object_store, path),
166 )),
167 }
168 }
169
170 pub async fn write_batches(
172 &mut self,
173 batches: impl Iterator<Item = &RecordBatch>,
174 ) -> Result<()> {
175 for batch in batches {
176 self.write_batch(batch).await?;
177 }
178 Ok(())
179 }
180
181 pub async fn write_batch(&mut self, batch: &RecordBatch) -> Result<()> {
183 match self {
184 Self::V2_0(writer) => writer.write_batch(batch).await,
185 Self::V2_1(writer) => writer.write_batch(batch).await,
186 Self::V2_2(writer) => writer.write_batch(batch).await,
187 Self::V2_3(writer) => writer.write_batch(batch).await,
188 }
189 }
190
191 pub async fn write_column(&mut self, column_index: usize, array: ArrayRef) -> Result<()> {
193 match self {
194 Self::V2_0(writer) => writer.write_column(column_index, array).await,
195 Self::V2_1(writer) => writer.write_column(column_index, array).await,
196 Self::V2_2(writer) => writer.write_column(column_index, array).await,
197 Self::V2_3(writer) => writer.write_column(column_index, array).await,
198 }
199 }
200
201 pub async fn write_external_buffer(&mut self, bytes: &[u8]) -> Result<(u64, u64)> {
203 match self {
204 Self::V2_0(writer) => writer.write_external_buffer(bytes).await,
205 Self::V2_1(writer) => writer.write_external_buffer(bytes).await,
206 Self::V2_2(writer) => writer.write_external_buffer(bytes).await,
207 Self::V2_3(writer) => writer.write_external_buffer(bytes).await,
208 }
209 }
210
211 pub fn add_schema_metadata(&mut self, key: impl Into<String>, value: impl Into<String>) {
213 let key = key.into();
214 let value = value.into();
215 match self {
216 Self::V2_0(writer) => writer.add_schema_metadata(key, value),
217 Self::V2_1(writer) => writer.add_schema_metadata(key, value),
218 Self::V2_2(writer) => writer.add_schema_metadata(key, value),
219 Self::V2_3(writer) => writer.add_schema_metadata(key, value),
220 }
221 }
222
223 pub fn initialize_with_external_columns(
225 &mut self,
226 schema: Schema,
227 columns: &[Arc<ColumnInfo>],
228 rows_written: u64,
229 ) -> Result<()> {
230 let column_metadata = columns
231 .iter()
232 .map(|column| column_info_to_metadata(column))
233 .collect::<Result<Vec<_>>>()?;
234 match self {
235 Self::V2_0(writer) => {
236 writer.initialize_with_external_metadata(schema, column_metadata, rows_written)
237 }
238 Self::V2_1(writer) => {
239 writer.initialize_with_external_metadata(schema, column_metadata, rows_written)
240 }
241 Self::V2_2(writer) => {
242 writer.initialize_with_external_metadata(schema, column_metadata, rows_written)
243 }
244 Self::V2_3(writer) => {
245 writer.initialize_with_external_metadata(schema, column_metadata, rows_written)
246 }
247 }
248 Ok(())
249 }
250
251 pub async fn add_global_buffer(&mut self, buffer: Bytes) -> Result<u32> {
253 match self {
254 Self::V2_0(writer) => writer.add_global_buffer(buffer).await,
255 Self::V2_1(writer) => writer.add_global_buffer(buffer).await,
256 Self::V2_2(writer) => writer.add_global_buffer(buffer).await,
257 Self::V2_3(writer) => writer.add_global_buffer(buffer).await,
258 }
259 }
260
261 pub async fn finish(&mut self) -> Result<FileWriteSummary> {
263 match self {
264 Self::V2_0(writer) => writer.finish().await,
265 Self::V2_1(writer) => writer.finish().await,
266 Self::V2_2(writer) => writer.finish().await,
267 Self::V2_3(writer) => writer.finish().await,
268 }
269 }
270
271 pub async fn abort(&mut self) {
273 match self {
274 Self::V2_0(writer) => writer.abort().await,
275 Self::V2_1(writer) => writer.abort().await,
276 Self::V2_2(writer) => writer.abort().await,
277 Self::V2_3(writer) => writer.abort().await,
278 }
279 }
280
281 pub async fn tell(&mut self) -> Result<u64> {
283 match self {
284 Self::V2_0(writer) => writer.tell().await,
285 Self::V2_1(writer) => writer.tell().await,
286 Self::V2_2(writer) => writer.tell().await,
287 Self::V2_3(writer) => writer.tell().await,
288 }
289 }
290
291 pub fn field_id_to_column_indices(&self) -> &[(u32, u32)] {
293 match self {
294 Self::V2_0(writer) => writer.field_id_to_column_indices(),
295 Self::V2_1(writer) => writer.field_id_to_column_indices(),
296 Self::V2_2(writer) => writer.field_id_to_column_indices(),
297 Self::V2_3(writer) => writer.field_id_to_column_indices(),
298 }
299 }
300}
301
302#[cfg(test)]
303#[path = "writer_tests.rs"]
304mod writer_tests;