use snafu::location;
use crate::format::Manifest;
use lance_core::{Error, Result};
pub const FLAG_DELETION_FILES: u64 = 1;
pub const FLAG_STABLE_ROW_IDS: u64 = 2;
pub const FLAG_USE_V2_FORMAT_DEPRECATED: u64 = 4;
pub const FLAG_TABLE_CONFIG: u64 = 8;
pub const FLAG_BASE_PATHS: u64 = 16;
pub const FLAG_UNKNOWN: u64 = 32;
pub fn apply_feature_flags(manifest: &mut Manifest, enable_stable_row_id: bool) -> Result<()> {
manifest.reader_feature_flags = 0;
manifest.writer_feature_flags = 0;
let has_deletion_files = manifest
.fragments
.iter()
.any(|frag| frag.deletion_file.is_some());
if has_deletion_files {
manifest.reader_feature_flags |= FLAG_DELETION_FILES;
manifest.writer_feature_flags |= FLAG_DELETION_FILES;
}
let has_row_ids = manifest
.fragments
.iter()
.any(|frag| frag.row_id_meta.is_some());
if has_row_ids || enable_stable_row_id {
if !manifest
.fragments
.iter()
.all(|frag| frag.row_id_meta.is_some())
{
return Err(Error::invalid_input(
"All fragments must have row ids",
location!(),
));
}
manifest.reader_feature_flags |= FLAG_STABLE_ROW_IDS;
manifest.writer_feature_flags |= FLAG_STABLE_ROW_IDS;
}
if !manifest.config.is_empty() {
manifest.writer_feature_flags |= FLAG_TABLE_CONFIG;
}
if !manifest.base_paths.is_empty() {
manifest.reader_feature_flags |= FLAG_BASE_PATHS;
manifest.writer_feature_flags |= FLAG_BASE_PATHS;
}
Ok(())
}
pub fn can_read_dataset(reader_flags: u64) -> bool {
reader_flags < FLAG_UNKNOWN
}
pub fn can_write_dataset(writer_flags: u64) -> bool {
writer_flags < FLAG_UNKNOWN
}
pub fn has_deprecated_v2_feature_flag(writer_flags: u64) -> bool {
writer_flags & FLAG_USE_V2_FORMAT_DEPRECATED != 0
}
#[cfg(test)]
mod tests {
use super::*;
use crate::format::BasePath;
#[test]
fn test_read_check() {
assert!(can_read_dataset(0));
assert!(can_read_dataset(super::FLAG_DELETION_FILES));
assert!(can_read_dataset(super::FLAG_STABLE_ROW_IDS));
assert!(can_read_dataset(super::FLAG_USE_V2_FORMAT_DEPRECATED));
assert!(can_read_dataset(super::FLAG_TABLE_CONFIG));
assert!(can_read_dataset(super::FLAG_BASE_PATHS));
assert!(can_read_dataset(
super::FLAG_DELETION_FILES
| super::FLAG_STABLE_ROW_IDS
| super::FLAG_USE_V2_FORMAT_DEPRECATED
));
assert!(!can_read_dataset(super::FLAG_UNKNOWN));
}
#[test]
fn test_write_check() {
assert!(can_write_dataset(0));
assert!(can_write_dataset(super::FLAG_DELETION_FILES));
assert!(can_write_dataset(super::FLAG_STABLE_ROW_IDS));
assert!(can_write_dataset(super::FLAG_USE_V2_FORMAT_DEPRECATED));
assert!(can_write_dataset(super::FLAG_TABLE_CONFIG));
assert!(can_write_dataset(super::FLAG_BASE_PATHS));
assert!(can_write_dataset(
super::FLAG_DELETION_FILES
| super::FLAG_STABLE_ROW_IDS
| super::FLAG_USE_V2_FORMAT_DEPRECATED
| super::FLAG_TABLE_CONFIG
| super::FLAG_BASE_PATHS
));
assert!(!can_write_dataset(super::FLAG_UNKNOWN));
}
#[test]
fn test_base_paths_feature_flags() {
use crate::format::{DataStorageFormat, Manifest};
use arrow_schema::{Field as ArrowField, Schema as ArrowSchema};
use lance_core::datatypes::Schema;
use std::collections::HashMap;
use std::sync::Arc;
let arrow_schema = ArrowSchema::new(vec![ArrowField::new(
"test_field",
arrow_schema::DataType::Int64,
false,
)]);
let schema = Schema::try_from(&arrow_schema).unwrap();
let mut normal_manifest = Manifest::new(
schema.clone(),
Arc::new(vec![]),
DataStorageFormat::default(),
None,
HashMap::new(), );
apply_feature_flags(&mut normal_manifest, false).unwrap();
assert_eq!(normal_manifest.reader_feature_flags & FLAG_BASE_PATHS, 0);
assert_eq!(normal_manifest.writer_feature_flags & FLAG_BASE_PATHS, 0);
let mut base_paths: HashMap<u32, BasePath> = HashMap::new();
base_paths.insert(
1,
BasePath::new(
1,
"file:///path/to/original".to_string(),
Some("test_ref".to_string()),
true,
),
);
let mut multi_base_manifest = Manifest::new(
schema,
Arc::new(vec![]),
DataStorageFormat::default(),
None,
base_paths,
);
apply_feature_flags(&mut multi_base_manifest, false).unwrap();
assert_ne!(
multi_base_manifest.reader_feature_flags & FLAG_BASE_PATHS,
0
);
assert_ne!(
multi_base_manifest.writer_feature_flags & FLAG_BASE_PATHS,
0
);
}
}