lance_file/versions/v2_3/
mod.rs1use std::{
7 collections::BTreeMap,
8 sync::{
9 Arc,
10 atomic::{AtomicBool, Ordering},
11 },
12};
13
14use bytes::Bytes;
15use lance_core::{
16 Error, Result,
17 datatypes::{Field, Schema},
18};
19use lance_encoding::{
20 compression_config::CompressionParams,
21 encoder::{
22 ColumnIndexSequence, EncodedBatch, FieldEncoder, FieldEncodingContext,
23 FieldEncodingStrategy,
24 structural::{
25 PrimitiveFieldEncoding, PrimitivePageEncoding, try_create_list, try_create_map,
26 try_create_struct, try_create_structural_blob, try_create_structural_fixed_size_list,
27 },
28 },
29};
30use lance_io::traits::Writer as ObjectWriter;
31
32use crate::{
33 reader::{ReadProjection, structural},
34 writer::FileWriterOptions,
35};
36
37mod compression;
38mod reader;
39mod writer;
40
41pub(crate) use reader::{
42 decode_column_metadata, finish_metadata, finish_metadata_index, validate_global_buffers,
43};
44pub use reader::{
45 projection_from_column_names, projection_from_field_ids, projection_from_whole_schema,
46};
47
48pub(crate) fn read_projection() -> Arc<dyn ReadProjection> {
49 structural::read_projection(reader::decode_column)
50}
51pub use writer::Writer;
52
53static WARNED_ON_UNSTABLE_FORMAT: AtomicBool = AtomicBool::new(false);
54
55pub fn physical_column_count(field: &Field) -> usize {
57 structural::physical_column_count(field)
58}
59
60pub fn data_file_columns(schema: &Schema) -> (Vec<i32>, Vec<i32>) {
62 structural::data_file_columns(schema)
63}
64
65pub(super) fn field_id_to_column_index(schema: &Schema) -> BTreeMap<u32, u32> {
66 structural::field_id_to_column_index(schema)
67}
68
69#[derive(Debug)]
70struct FieldStrategy {
71 primitive: PrimitiveFieldEncoding,
72}
73
74impl FieldEncodingStrategy for FieldStrategy {
75 fn create_field_encoder(
76 &self,
77 field: &Field,
78 column_index: &mut ColumnIndexSequence,
79 context: &FieldEncodingContext<'_>,
80 ) -> Result<Box<dyn FieldEncoder>> {
81 if field.is_blob()
82 && matches!(
83 field.data_type(),
84 arrow_schema::DataType::Binary | arrow_schema::DataType::LargeBinary
85 )
86 {
87 return Err(Error::invalid_input(
88 "Lance 2.3 does not write Blob v1 pages; use a Blob v2 descriptor struct",
89 ));
90 }
91 if let Some(encoder) =
92 try_create_structural_blob(&self.primitive, field, column_index, context)?
93 {
94 return Ok(encoder);
95 }
96 if field.is_blob() {
97 return Err(Error::invalid_input_source(
98 format!(
99 "Blob encoding is not available for field '{}' with data type {}",
100 field.name,
101 field.data_type()
102 )
103 .into(),
104 ));
105 }
106 if let Some(encoder) = try_create_map(field, column_index, context)? {
107 return Ok(encoder);
108 }
109 if let Some(encoder) = try_create_structural_fixed_size_list(field, column_index, context)?
110 {
111 return Ok(encoder);
112 }
113 if let Some(encoder) = self.primitive.try_create(field, column_index, context)? {
114 return Ok(encoder);
115 }
116 if let Some(encoder) = try_create_list(field, column_index, context)? {
117 return Ok(encoder);
118 }
119 if let Some(encoder) = try_create_struct(field, column_index, context)? {
120 return Ok(encoder);
121 }
122 Err(Error::not_supported_source(
123 format!(
124 "Lance v2.3 has no field encoding for '{}' with data type {}",
125 field.name,
126 field.data_type()
127 )
128 .into(),
129 ))
130 }
131}
132
133pub fn encoding_strategy(params: CompressionParams) -> Arc<dyn FieldEncodingStrategy> {
135 let compression = Arc::new(compression::Strategy::new(params));
136 Arc::new(FieldStrategy {
137 primitive: PrimitiveFieldEncoding::new([PrimitivePageEncoding::planar(compression)]),
138 })
139}
140
141fn warn_unstable_format() {
142 if WARNED_ON_UNSTABLE_FORMAT
143 .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
144 .is_ok()
145 {
146 log::warn!(
147 "You have requested an unstable format version. Files written with this format version may not be readable in the future! This is a development feature and should only be used for experimentation and never for production data."
148 );
149 }
150}
151
152pub fn create_writer(
154 object_writer: Box<dyn ObjectWriter>,
155 schema: Schema,
156 options: FileWriterOptions,
157) -> Result<Writer> {
158 warn_unstable_format();
159 Writer::try_new(object_writer, schema, options)
160}
161
162pub fn create_writer_with_compression(
164 object_writer: Box<dyn ObjectWriter>,
165 schema: Schema,
166 options: FileWriterOptions,
167 compression: CompressionParams,
168) -> Result<Writer> {
169 warn_unstable_format();
170 Writer::try_new_with_compression(object_writer, schema, options, compression)
171}
172
173pub fn create_lazy_writer(
175 object_writer: Box<dyn ObjectWriter>,
176 options: FileWriterOptions,
177) -> Writer {
178 warn_unstable_format();
179 Writer::new_lazy(object_writer, options)
180}
181
182pub fn create_lazy_writer_with_compression(
184 object_writer: Box<dyn ObjectWriter>,
185 options: FileWriterOptions,
186 compression: CompressionParams,
187) -> Writer {
188 warn_unstable_format();
189 Writer::new_lazy_with_compression(object_writer, options, compression)
190}
191
192pub fn encode_self_described_batch(batch: &EncodedBatch) -> Result<Bytes> {
194 writer::concat_lance_footer(batch, true)
195}
196
197pub fn encode_mini_batch(batch: &EncodedBatch) -> Result<Bytes> {
199 writer::concat_lance_footer(batch, false)
200}