use lance_core::{Error, Result};
use lance_io::object_store::ObjectStore;
use super::FRAG_REUSE_INDEX_NAME;
use crate::feature_flags::FLAG_FRAGMENT_REUSE_INDEX;
use crate::format::{IndexMetadata, Manifest};
use crate::io::commit::ManifestLocation;
use crate::io::manifest::read_manifest_indexes;
pub fn is_tagged(index: &IndexMetadata) -> bool {
index.name == FRAG_REUSE_INDEX_NAME && index.index_version != 0
}
pub fn uses_tagged_fri(manifest: &Manifest, existing_fri_entry: Option<&IndexMetadata>) -> bool {
manifest.reader_feature_flags & FLAG_FRAGMENT_REUSE_INDEX != 0
|| existing_fri_entry.is_some_and(is_tagged)
}
pub fn validate_flags(manifest: &Manifest, indices: &[IndexMetadata]) -> Result<()> {
if indices.iter().any(is_tagged)
&& (manifest.reader_feature_flags & manifest.writer_feature_flags)
& FLAG_FRAGMENT_REUSE_INDEX
== 0
{
return Err(Error::corrupt_file_named(
"manifest",
"tagged FRI metadata requires both reader and writer feature flags",
));
}
Ok(())
}
pub async fn ensure_clone_supported(
store: &ObjectStore,
location: &ManifestLocation,
manifest: &Manifest,
) -> Result<()> {
if manifest.reader_feature_flags & FLAG_FRAGMENT_REUSE_INDEX == 0 {
return Ok(());
}
let indices = read_manifest_indexes(store, location, manifest).await?;
if indices.iter().any(is_tagged) {
return Err(Error::not_supported(
"Cloning tagged FRI requires row-map reference relocation. Please upgrade to a version supporting FRI clone",
));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use uuid::Uuid;
fn entry(name: &str, index_version: i32) -> IndexMetadata {
IndexMetadata {
uuid: Uuid::nil(),
fields: vec![],
covering_fields: vec![],
name: name.into(),
dataset_version: 1,
fragment_bitmap: None,
index_details: None,
index_version,
created_at: None,
base_id: None,
files: None,
}
}
#[test]
fn tagged_requires_the_reserved_name_and_a_nonzero_version() {
assert!(!is_tagged(&entry(FRAG_REUSE_INDEX_NAME, 0)));
assert!(is_tagged(&entry(FRAG_REUSE_INDEX_NAME, 1)));
assert!(is_tagged(&entry(FRAG_REUSE_INDEX_NAME, 2)));
assert!(!is_tagged(&entry("user_idx", 0)));
assert!(!is_tagged(&entry("user_idx", 1)));
}
#[test]
fn sticky_flag_or_tagged_entry_makes_the_table_tagged() {
let mut flagged = crate::transaction::test_support::sample_manifest();
flagged.reader_feature_flags |= FLAG_FRAGMENT_REUSE_INDEX;
let unflagged = crate::transaction::test_support::sample_manifest();
assert!(uses_tagged_fri(&flagged, None));
assert!(uses_tagged_fri(
&flagged,
Some(&entry(FRAG_REUSE_INDEX_NAME, 0))
));
assert!(uses_tagged_fri(
&unflagged,
Some(&entry(FRAG_REUSE_INDEX_NAME, 1))
));
assert!(!uses_tagged_fri(
&unflagged,
Some(&entry(FRAG_REUSE_INDEX_NAME, 0))
));
assert!(!uses_tagged_fri(&unflagged, None));
}
}