lance_file/versions/v2_3/
writer.rs1use std::sync::Arc;
5
6use arrow_array::{ArrayRef, RecordBatch};
7use bytes::{BufMut, Bytes};
8use lance_core::{Result, datatypes::Schema};
9use lance_encoding::{
10 compression_config::CompressionParams,
11 encoder::{BatchEncoder, EncodedBatch},
12};
13use lance_io::{object_store::ObjectStore, traits::Writer as ObjectWriter};
14use object_store::path::Path;
15use tokio::io::AsyncWriteExt;
16
17use crate::{
18 format::{MAGIC, pbfile},
19 writer::{
20 FileWriteSummary, FileWriterOptions,
21 structural::{EncodedBatchBody, EncodingPipeline, StructuralFileSink, encode_batch_body},
22 },
23};
24
25use super::encoding_strategy;
26
27pub struct Writer {
33 sink: StructuralFileSink,
34 encoding: EncodingPipeline,
35 compression: CompressionParams,
36}
37
38impl Writer {
39 pub fn try_new(
41 object_writer: Box<dyn ObjectWriter>,
42 schema: Schema,
43 options: FileWriterOptions,
44 ) -> Result<Self> {
45 Self::try_new_with_compression(object_writer, schema, options, Default::default())
46 }
47
48 pub fn try_new_with_compression(
50 object_writer: Box<dyn ObjectWriter>,
51 schema: Schema,
52 options: FileWriterOptions,
53 compression: CompressionParams,
54 ) -> Result<Self> {
55 let mut writer = Self::new_lazy_with_compression(object_writer, options, compression);
56 writer.initialize(schema)?;
57 Ok(writer)
58 }
59
60 pub fn new_lazy(object_writer: Box<dyn ObjectWriter>, options: FileWriterOptions) -> Self {
62 Self::new_lazy_with_compression(object_writer, options, Default::default())
63 }
64
65 pub fn new_lazy_with_compression(
67 object_writer: Box<dyn ObjectWriter>,
68 options: FileWriterOptions,
69 compression: CompressionParams,
70 ) -> Self {
71 Self {
72 sink: StructuralFileSink::new(object_writer),
73 encoding: EncodingPipeline::new(options),
74 compression,
75 }
76 }
77
78 fn initialize(&mut self, schema: Schema) -> Result<()> {
79 let encoding_options = self.encoding.encoding_options(&schema);
80 schema.validate()?;
81 let strategy = encoding_strategy(self.compression.clone());
82 let encoder = BatchEncoder::try_new(&schema, strategy.as_ref(), &encoding_options)?;
83 self.encoding.initialize(schema, encoder, &mut self.sink);
84 Ok(())
85 }
86
87 fn ensure_initialized(&mut self, batch: &RecordBatch) -> Result<()> {
88 if !self.encoding.is_initialized() {
89 self.initialize(Schema::try_from(batch.schema().as_ref())?)?;
90 }
91 Ok(())
92 }
93
94 pub fn with_page_metadata_spill(mut self, object_store: Arc<ObjectStore>, path: Path) -> Self {
96 self.sink.with_page_metadata_spill(object_store, path);
97 self
98 }
99
100 pub async fn write_batches(
102 &mut self,
103 batches: impl Iterator<Item = &RecordBatch>,
104 ) -> Result<()> {
105 for batch in batches {
106 self.write_batch(batch).await?;
107 }
108 Ok(())
109 }
110
111 pub async fn write_batch(&mut self, batch: &RecordBatch) -> Result<()> {
113 self.ensure_initialized(batch)?;
114 self.encoding.write_batch(batch, &mut self.sink).await
115 }
116
117 pub async fn write_column(&mut self, column_index: usize, array: ArrayRef) -> Result<()> {
119 self.encoding
120 .write_column(column_index, array, &mut self.sink)
121 .await
122 }
123
124 pub async fn write_external_buffer(&mut self, bytes: &[u8]) -> Result<(u64, u64)> {
126 self.sink.write_external_buffer(bytes).await
127 }
128
129 pub fn add_schema_metadata(&mut self, key: impl Into<String>, value: impl Into<String>) {
131 self.encoding.add_schema_metadata(key, value);
132 }
133
134 pub fn initialize_with_external_metadata(
136 &mut self,
137 schema: Schema,
138 column_metadata: Vec<pbfile::ColumnMetadata>,
139 rows_written: u64,
140 ) {
141 self.encoding
142 .initialize_with_external_metadata(schema, rows_written);
143 self.sink.initialize_with_external_metadata(column_metadata);
144 }
145
146 pub async fn add_global_buffer(&mut self, buffer: Bytes) -> Result<u32> {
148 self.sink.add_global_buffer(buffer).await
149 }
150
151 pub async fn finish(&mut self) -> Result<FileWriteSummary> {
153 self.encoding.flush(&mut self.sink).await?;
155 self.encoding.finish_encoders(&mut self.sink).await?;
156
157 let descriptor = self.encoding.make_file_descriptor()?;
158 let global_buffer_offsets = self.sink.write_global_buffers(descriptor).await?;
159 let num_global_buffers = global_buffer_offsets.len() as u32;
160
161 let column_metadata_start = self.sink.tell().await?;
162 let column_metadata_offsets = self.sink.write_column_metadatas().await?;
163 let column_metadata_offsets_start = self
164 .sink
165 .write_offset_table(&column_metadata_offsets)
166 .await?;
167 let global_buffer_offsets_start =
168 self.sink.write_offset_table(&global_buffer_offsets).await?;
169 let num_columns = self.sink.num_columns();
170
171 let output = self.sink.output_mut();
172 output.write_u64_le(column_metadata_start).await?;
173 output.write_u64_le(column_metadata_offsets_start).await?;
174 output.write_u64_le(global_buffer_offsets_start).await?;
175 output.write_u32_le(num_global_buffers).await?;
176 output.write_u32_le(num_columns).await?;
177 output.write_u16_le(2).await?;
178 output.write_u16_le(3).await?;
179 output.write_all(MAGIC).await?;
180
181 Ok(FileWriteSummary {
182 num_rows: self.encoding.rows_written(),
183 size_bytes: self.sink.shutdown().await?,
184 })
185 }
186
187 pub async fn abort(&mut self) {
189 }
191
192 pub async fn tell(&mut self) -> Result<u64> {
194 self.sink.tell().await
195 }
196
197 pub fn field_id_to_column_indices(&self) -> &[(u32, u32)] {
199 self.encoding.field_id_to_column_indices()
200 }
201}
202
203pub fn concat_lance_footer(batch: &EncodedBatch, write_schema: bool) -> Result<Bytes> {
205 let EncodedBatchBody {
206 mut data,
207 column_metadata_start,
208 column_metadata_offsets_start,
209 global_buffer_offsets_start,
210 num_global_buffers,
211 num_columns,
212 } = encode_batch_body(batch, write_schema)?;
213
214 data.put_u64_le(column_metadata_start);
215 data.put_u64_le(column_metadata_offsets_start);
216 data.put_u64_le(global_buffer_offsets_start);
217 data.put_u32_le(num_global_buffers);
218 data.put_u32_le(num_columns);
219 data.put_u16_le(2);
220 data.put_u16_le(3);
221 data.extend_from_slice(MAGIC);
222 Ok(data.freeze())
223}