1use arrow_array::{RecordBatch, RecordBatchReader};
8use arrow_schema::DataType;
9use byteorder::{ByteOrder, LittleEndian};
10use chrono::{Duration, prelude::*};
11use deepsize::DeepSizeOf;
12use futures::future::BoxFuture;
13use futures::stream::{self, BoxStream, StreamExt, TryStreamExt};
14use futures::{FutureExt, Stream};
15
16use crate::dataset::metadata::UpdateFieldMetadataBuilder;
17use crate::dataset::transaction::translate_schema_metadata_updates;
18use crate::index::DatasetIndexExt;
19use crate::session::caches::{DSMetadataCache, ManifestKey, TransactionKey};
20use crate::session::index_caches::DSIndexCache;
21use itertools::Itertools;
22use lance_core::ROW_ADDR;
23use lance_core::datatypes::{OnMissing, OnTypeMismatch, Projectable, Projection};
24use lance_core::traits::DatasetTakeRows;
25use lance_core::utils::address::RowAddress;
26use lance_core::utils::tracing::{
27 DATASET_CLEANING_EVENT, DATASET_DELETING_EVENT, DATASET_DROPPING_COLUMN_EVENT,
28 TRACE_DATASET_EVENTS,
29};
30use lance_datafusion::projection::ProjectionPlan;
31use lance_file::datatypes::populate_schema_dictionary;
32use lance_file::reader::{FileReader, FileReaderOptions};
33use lance_file::version::LanceFileVersion;
34use lance_index::{IndexType, progress::IndexBuildProgress};
35use lance_io::object_store::{
36 LanceNamespaceStorageOptionsProvider, ObjectStore, ObjectStoreParams, StorageOptions,
37 StorageOptionsAccessor, StorageOptionsProvider,
38};
39use lance_io::scheduler::{ScanScheduler, SchedulerConfig};
40use lance_io::utils::{
41 CachedFileSize, read_last_block, read_message, read_metadata_offset, read_struct,
42};
43use lance_namespace::LanceNamespace;
44use lance_table::format::{
45 DataFile, DataStorageFormat, DeletionFile, Fragment, IndexMetadata, Manifest, RowIdMeta, pb,
46};
47use lance_table::io::commit::{
48 CommitConfig, CommitError, CommitHandler, CommitLock, ManifestLocation, ManifestNamingScheme,
49 VERSIONS_DIR, external_manifest::ExternalManifestCommitHandler, migrate_scheme_to_v2,
50 write_manifest_file_to_path,
51};
52
53use crate::io::commit::namespace_manifest::LanceNamespaceExternalManifestStore;
54use lance_table::io::manifest::{read_manifest, read_manifest_indexes};
55use object_store::path::Path;
56use prost::Message;
57use roaring::RoaringBitmap;
58use rowids::get_row_id_index;
59use serde::{Deserialize, Serialize};
60use std::borrow::Cow;
61use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
62use std::fmt::Debug;
63use std::num::NonZero;
64use std::ops::Range;
65use std::pin::Pin;
66use std::sync::Arc;
67use tracing::{info, instrument};
68
69pub(crate) mod blob;
70mod branch_location;
71pub mod builder;
72pub mod cleanup;
73pub mod delta;
74pub mod fragment;
75mod hash_joiner;
76pub mod index;
77pub mod mem_wal;
78mod metadata;
79pub mod optimize;
80pub mod progress;
81pub mod refs;
82pub(crate) mod rowids;
83pub mod scanner;
84mod schema_evolution;
85pub mod sql;
86pub mod statistics;
87mod take;
88pub mod transaction;
89pub mod udtf;
90pub mod updater;
91mod utils;
92pub mod write;
93
94pub(crate) use take::row_offsets_to_row_addresses;
95
96use self::builder::DatasetBuilder;
97use self::cleanup::RemovalStats;
98use self::fragment::FileFragment;
99use self::refs::Refs;
100use self::scanner::{DatasetRecordBatchStream, Scanner};
101use self::transaction::{Operation, Transaction, TransactionBuilder, UpdateMapEntry};
102use self::write::write_fragments_internal;
103use crate::dataset::branch_location::BranchLocation;
104use crate::dataset::cleanup::{CleanupPolicy, CleanupPolicyBuilder};
105use crate::dataset::refs::{BranchContents, BranchIdentifier, Branches, Tags};
106use crate::dataset::sql::SqlQueryBuilder;
107use crate::datatypes::Schema;
108use crate::index::retain_supported_indices;
109use crate::io::commit::{
110 commit_detached_transaction, commit_new_dataset, commit_transaction,
111 detect_overlapping_fragments,
112};
113use crate::session::Session;
114use crate::utils::temporal::{SystemTime, timestamp_to_nanos, utc_now};
115use crate::{Error, Result};
116pub use blob::{BlobFile, ReadBlob, ReadBlobsBuilder, ReadBlobsStream};
117use hash_joiner::HashJoiner;
118pub use lance_core::ROW_ID;
119use lance_core::box_error;
120use lance_index::scalar::lance_format::LanceIndexStore;
121use lance_namespace::models::{DeclareTableRequest, DescribeTableRequest};
122use lance_table::feature_flags::{apply_feature_flags, can_read_dataset};
123use lance_table::io::deletion::{DELETIONS_DIR, relative_deletion_file_path};
124pub use schema_evolution::{
125 BatchInfo, BatchUDF, ColumnAlteration, NewColumnTransform, UDFCheckpointStore,
126};
127pub use take::TakeBuilder;
128pub use write::merge_insert::{
129 MergeInsertBuilder, MergeInsertJob, MergeStats, UncommittedMergeInsert, WhenMatched,
130 WhenNotMatched, WhenNotMatchedBySource,
131};
132
133use crate::dataset::index::LanceIndexStoreExt;
134pub use write::update::{UpdateBuilder, UpdateJob};
135#[allow(deprecated)]
136pub use write::{
137 AutoCleanupParams, CommitBuilder, DeleteBuilder, DeleteResult, ExternalBlobMode, InsertBuilder,
138 WriteDestination, WriteMode, WriteParams, WriteProgressFn, WriteStats, write_fragments,
139};
140
141pub(crate) const INDICES_DIR: &str = "_indices";
142pub(crate) const DATA_DIR: &str = "data";
143pub(crate) const TRANSACTIONS_DIR: &str = "_transactions";
144
145pub const DEFAULT_INDEX_CACHE_SIZE: usize = 6 * 1024 * 1024 * 1024;
148pub const DEFAULT_METADATA_CACHE_SIZE: usize = 1024 * 1024 * 1024;
152
153#[derive(Clone)]
155pub struct Dataset {
156 pub object_store: Arc<ObjectStore>,
157 pub(crate) commit_handler: Arc<dyn CommitHandler>,
158 uri: String,
163 pub(crate) base: Path,
164 pub manifest: Arc<Manifest>,
165 pub(crate) manifest_location: ManifestLocation,
168 pub(crate) session: Arc<Session>,
169 pub refs: Refs,
170
171 pub(crate) fragment_bitmap: Arc<RoaringBitmap>,
173
174 pub(crate) index_cache: Arc<DSIndexCache>,
176 pub(crate) metadata_cache: Arc<DSMetadataCache>,
177
178 pub(crate) file_reader_options: Option<FileReaderOptions>,
180
181 pub(crate) store_params: Option<Box<ObjectStoreParams>>,
184 pub(crate) base_store_params: Option<Arc<HashMap<String, ObjectStoreParams>>>,
186}
187
188impl std::fmt::Debug for Dataset {
189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190 f.debug_struct("Dataset")
191 .field("uri", &self.uri)
192 .field("base", &self.base)
193 .field("version", &self.manifest.version)
194 .field("cache_num_items", &self.session.approx_num_items())
195 .field("base_store_params", &self.base_store_params.is_some())
196 .finish()
197 }
198}
199
200#[derive(Deserialize, Serialize, Debug)]
202pub struct Version {
203 pub version: u64,
205
206 pub timestamp: DateTime<Utc>,
208
209 pub metadata: BTreeMap<String, String>,
211}
212
213impl From<&Manifest> for Version {
215 fn from(m: &Manifest) -> Self {
216 Self {
217 version: m.version,
218 timestamp: m.timestamp(),
219 metadata: m.summary().into(),
220 }
221 }
222}
223
224#[derive(Clone, Debug)]
226pub struct ReadParams {
227 pub index_cache_size_bytes: usize,
230
231 pub metadata_cache_size_bytes: usize,
234
235 pub session: Option<Arc<Session>>,
239
240 pub store_options: Option<ObjectStoreParams>,
241
242 pub commit_handler: Option<Arc<dyn CommitHandler>>,
259
260 pub file_reader_options: Option<FileReaderOptions>,
267}
268
269impl ReadParams {
270 #[deprecated(
272 since = "0.30.0",
273 note = "Use `index_cache_size_bytes` instead, which accepts a size in bytes."
274 )]
275 pub fn index_cache_size(&mut self, cache_size: usize) -> &mut Self {
276 let assumed_entry_size = 20 * 1024 * 1024; self.index_cache_size_bytes = cache_size * assumed_entry_size;
278 self
279 }
280
281 pub fn index_cache_size_bytes(&mut self, cache_size: usize) -> &mut Self {
282 self.index_cache_size_bytes = cache_size;
283 self
284 }
285
286 #[deprecated(
288 since = "0.30.0",
289 note = "Use `metadata_cache_size_bytes` instead, which accepts a size in bytes."
290 )]
291 pub fn metadata_cache_size(&mut self, cache_size: usize) -> &mut Self {
292 let assumed_entry_size = 10 * 1024 * 1024; self.metadata_cache_size_bytes = cache_size * assumed_entry_size;
294 self
295 }
296
297 pub fn metadata_cache_size_bytes(&mut self, cache_size: usize) -> &mut Self {
299 self.metadata_cache_size_bytes = cache_size;
300 self
301 }
302
303 pub fn session(&mut self, session: Arc<Session>) -> &mut Self {
305 self.session = Some(session);
306 self
307 }
308
309 pub fn set_commit_lock<T: CommitLock + Send + Sync + 'static>(&mut self, lock: Arc<T>) {
311 self.commit_handler = Some(Arc::new(lock));
312 }
313
314 pub fn file_reader_options(&mut self, options: FileReaderOptions) -> &mut Self {
316 self.file_reader_options = Some(options);
317 self
318 }
319}
320
321impl Default for ReadParams {
322 fn default() -> Self {
323 Self {
324 index_cache_size_bytes: DEFAULT_INDEX_CACHE_SIZE,
325 metadata_cache_size_bytes: DEFAULT_METADATA_CACHE_SIZE,
326 session: None,
327 store_options: None,
328 commit_handler: None,
329 file_reader_options: None,
330 }
331 }
332}
333
334#[derive(Debug, Clone)]
335pub enum ProjectionRequest {
336 Schema(Arc<Schema>),
337 Sql(Vec<(String, String)>),
338}
339
340impl ProjectionRequest {
341 pub fn from_columns(
342 columns: impl IntoIterator<Item = impl AsRef<str>>,
343 dataset_schema: &Schema,
344 ) -> Self {
345 let columns = columns
346 .into_iter()
347 .map(|s| s.as_ref().to_string())
348 .collect::<Vec<_>>();
349
350 let schema = dataset_schema
351 .project_preserve_system_columns(&columns)
352 .unwrap();
353 Self::Schema(Arc::new(schema))
354 }
355
356 pub fn from_schema(schema: Schema) -> Self {
357 Self::Schema(Arc::new(schema))
358 }
359
360 pub fn from_sql(
366 columns: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
367 ) -> Self {
368 Self::Sql(
369 columns
370 .into_iter()
371 .map(|(a, b)| (a.into(), b.into()))
372 .collect(),
373 )
374 }
375
376 pub fn into_projection_plan(self, dataset: Arc<Dataset>) -> Result<ProjectionPlan> {
377 match self {
378 Self::Schema(schema) => {
379 let system_columns_present = schema
382 .fields
383 .iter()
384 .any(|f| lance_core::is_system_column(&f.name));
385
386 if system_columns_present {
387 ProjectionPlan::from_schema(dataset, schema.as_ref())
390 } else {
391 let projection = dataset.schema().project_by_schema(
393 schema.as_ref(),
394 OnMissing::Error,
395 OnTypeMismatch::Error,
396 )?;
397 ProjectionPlan::from_schema(dataset, &projection)
398 }
399 }
400 Self::Sql(columns) => ProjectionPlan::from_expressions(dataset, &columns),
401 }
402 }
403}
404
405impl From<Arc<Schema>> for ProjectionRequest {
406 fn from(schema: Arc<Schema>) -> Self {
407 Self::Schema(schema)
408 }
409}
410
411impl From<Schema> for ProjectionRequest {
412 fn from(schema: Schema) -> Self {
413 Self::from(Arc::new(schema))
414 }
415}
416
417impl Dataset {
418 #[instrument]
422 pub async fn open(uri: &str) -> Result<Self> {
423 DatasetBuilder::from_uri(uri).load().await
424 }
425
426 pub async fn checkout_version(&self, version: impl Into<refs::Ref>) -> Result<Self> {
428 let reference: refs::Ref = version.into();
429 match reference {
430 refs::Ref::Version(branch, version_number) => {
431 self.checkout_by_ref(version_number, branch.as_deref())
432 .await
433 }
434 refs::Ref::VersionNumber(version_number) => {
435 self.checkout_by_ref(Some(version_number), self.manifest.branch.as_deref())
436 .await
437 }
438 refs::Ref::Tag(tag_name) => {
439 let tag_contents = self.tags().get(tag_name.as_str()).await?;
440 self.checkout_by_ref(Some(tag_contents.version), tag_contents.branch.as_deref())
441 .await
442 }
443 }
444 }
445
446 pub fn tags(&self) -> Tags<'_> {
447 self.refs.tags()
448 }
449
450 pub fn branches(&self) -> Branches<'_> {
451 self.refs.branches()
452 }
453
454 pub async fn checkout_latest(&mut self) -> Result<()> {
456 let (manifest, manifest_location) = self.latest_manifest().await?;
457 self.manifest = manifest;
458 self.manifest_location = manifest_location;
459 self.fragment_bitmap = Arc::new(
460 self.manifest
461 .fragments
462 .iter()
463 .map(|f| f.id as u32)
464 .collect(),
465 );
466 Ok(())
467 }
468
469 pub async fn checkout_branch(&self, branch: &str) -> Result<Self> {
471 self.checkout_by_ref(None, Some(branch)).await
472 }
473
474 pub async fn create_branch(
490 &mut self,
491 branch: &str,
492 version: impl Into<refs::Ref>,
493 store_params: Option<ObjectStoreParams>,
494 ) -> Result<Self> {
495 let (source_branch, version_number) = self.resolve_reference(version.into()).await?;
496 let branch_location = self.branch_location().find_branch(Some(branch))?;
497 let clone_op = Operation::Clone {
498 is_shallow: true,
499 ref_name: source_branch.clone(),
500 ref_version: version_number,
501 ref_path: String::from(self.uri()),
502 branch_name: Some(branch.to_string()),
503 };
504 let transaction = Transaction::new(version_number, clone_op, None);
505
506 let builder = CommitBuilder::new(WriteDestination::Uri(branch_location.uri.as_str()))
507 .with_store_params(store_params.unwrap_or_default())
508 .with_object_store(Arc::new(self.object_store().clone()))
509 .with_commit_handler(self.commit_handler.clone())
510 .with_storage_format(self.manifest.data_storage_format.lance_file_version()?);
511 let dataset = builder.execute(transaction).await?;
512
513 self.branches()
515 .create(branch, version_number, source_branch.as_deref())
516 .await?;
517 Ok(dataset)
518 }
519
520 pub async fn delete_branch(&mut self, branch: &str) -> Result<()> {
521 self.branches().delete(branch, false).await
522 }
523
524 pub async fn force_delete_branch(&mut self, branch: &str) -> Result<()> {
527 self.branches().delete(branch, true).await
528 }
529
530 pub async fn list_branches(&self) -> Result<HashMap<String, BranchContents>> {
531 self.branches().list().await
532 }
533
534 fn already_checked_out(&self, location: &ManifestLocation, branch_name: Option<&str>) -> bool {
535 self.manifest.branch.as_deref() == branch_name
538 && self.manifest.version == location.version
539 && self.manifest_location.naming_scheme == location.naming_scheme
540 && location.e_tag.as_ref().is_some_and(|e_tag| {
541 self.manifest_location
542 .e_tag
543 .as_ref()
544 .is_some_and(|current_e_tag| e_tag == current_e_tag)
545 })
546 }
547
548 async fn checkout_by_ref(
549 &self,
550 version_number: Option<u64>,
551 branch: Option<&str>,
552 ) -> Result<Self> {
553 let new_location = self.branch_location().find_branch(branch)?;
554
555 let manifest_location = if let Some(version_number) = version_number {
556 self.commit_handler
557 .resolve_version_location(
558 &new_location.path,
559 version_number,
560 &self.object_store.inner,
561 )
562 .await?
563 } else {
564 self.commit_handler
565 .resolve_latest_location(&new_location.path, &self.object_store)
566 .await?
567 };
568
569 if self.already_checked_out(&manifest_location, branch) {
570 return Ok(self.clone());
571 }
572
573 let manifest = Self::load_manifest(
574 self.object_store.as_ref(),
575 &manifest_location,
576 &new_location.uri,
577 self.session.as_ref(),
578 )
579 .await?;
580 Self::checkout_manifest(
581 self.object_store.clone(),
582 new_location.path,
583 new_location.uri,
584 Arc::new(manifest),
585 manifest_location,
586 self.session.clone(),
587 self.commit_handler.clone(),
588 self.file_reader_options.clone(),
589 self.store_params.as_deref().cloned(),
590 self.base_store_params.clone(),
591 )
592 }
593
594 pub(crate) async fn load_manifest(
595 object_store: &ObjectStore,
596 manifest_location: &ManifestLocation,
597 uri: &str,
598 session: &Session,
599 ) -> Result<Manifest> {
600 let object_reader = if let Some(size) = manifest_location.size {
601 object_store
602 .open_with_size(&manifest_location.path, size as usize)
603 .await
604 } else {
605 object_store.open(&manifest_location.path).await
606 };
607 let object_reader = object_reader.map_err(|e| match &e {
608 Error::NotFound { uri, .. } => Error::dataset_not_found(uri.clone(), box_error(e)),
609 _ => e,
610 })?;
611
612 let last_block =
613 read_last_block(object_reader.as_ref())
614 .await
615 .map_err(|err| match err {
616 object_store::Error::NotFound { path, source } => {
617 Error::dataset_not_found(path, source)
618 }
619 _ => Error::io_source(err.into()),
620 })?;
621 let offset = read_metadata_offset(&last_block)?;
622
623 let manifest_size = object_reader.size().await?;
625 let mut manifest = if manifest_size - offset <= last_block.len() {
626 let manifest_len = manifest_size - offset;
627 let offset_in_block = last_block.len() - manifest_len;
628 let message_len =
629 LittleEndian::read_u32(&last_block[offset_in_block..offset_in_block + 4]) as usize;
630 let message_data = &last_block[offset_in_block + 4..offset_in_block + 4 + message_len];
631 Manifest::try_from(lance_table::format::pb::Manifest::decode(message_data)?)
632 } else {
633 read_struct(object_reader.as_ref(), offset).await
634 }?;
635
636 if !can_read_dataset(manifest.reader_feature_flags) {
637 let message = format!(
638 "This dataset cannot be read by this version of Lance. \
639 Please upgrade Lance to read this dataset.\n Flags: {}",
640 manifest.reader_feature_flags
641 );
642 return Err(Error::not_supported_source(message.into()));
643 }
644
645 if let Some(index_offset) = manifest.index_section
648 && manifest_size - index_offset <= last_block.len()
649 {
650 let offset_in_block = last_block.len() - (manifest_size - index_offset);
651 let message_len =
652 LittleEndian::read_u32(&last_block[offset_in_block..offset_in_block + 4]) as usize;
653 let message_data = &last_block[offset_in_block + 4..offset_in_block + 4 + message_len];
654 let section = lance_table::format::pb::IndexSection::decode(message_data)?;
655 let mut indices: Vec<IndexMetadata> = section
656 .indices
657 .into_iter()
658 .map(IndexMetadata::try_from)
659 .collect::<Result<Vec<_>>>()?;
660 retain_supported_indices(&mut indices);
661 let ds_index_cache = session.index_cache.for_dataset(uri);
662 let metadata_key = crate::session::index_caches::IndexMetadataKey {
663 version: manifest_location.version,
664 };
665 ds_index_cache
666 .insert_with_key(&metadata_key, Arc::new(indices))
667 .await;
668 }
669
670 if let Some(transaction_offset) = manifest.transaction_section
673 && manifest_size - transaction_offset <= last_block.len()
674 {
675 let offset_in_block = last_block.len() - (manifest_size - transaction_offset);
676 let message_len =
677 LittleEndian::read_u32(&last_block[offset_in_block..offset_in_block + 4]) as usize;
678 let message_data = &last_block[offset_in_block + 4..offset_in_block + 4 + message_len];
679 let transaction: Transaction =
680 lance_table::format::pb::Transaction::decode(message_data)?.try_into()?;
681
682 let metadata_cache = session.metadata_cache.for_dataset(uri);
683 let metadata_key = TransactionKey {
684 version: manifest_location.version,
685 };
686 metadata_cache
687 .insert_with_key(&metadata_key, Arc::new(transaction))
688 .await;
689 }
690
691 if manifest.should_use_legacy_format() {
692 populate_schema_dictionary(&mut manifest.schema, object_reader.as_ref()).await?;
693 }
694
695 Ok(manifest)
696 }
697
698 #[allow(clippy::too_many_arguments)]
699 fn checkout_manifest(
700 object_store: Arc<ObjectStore>,
701 base_path: Path,
702 uri: String,
703 manifest: Arc<Manifest>,
704 manifest_location: ManifestLocation,
705 session: Arc<Session>,
706 commit_handler: Arc<dyn CommitHandler>,
707 file_reader_options: Option<FileReaderOptions>,
708 store_params: Option<ObjectStoreParams>,
709 base_store_params: Option<Arc<HashMap<String, ObjectStoreParams>>>,
710 ) -> Result<Self> {
711 let refs = Refs::new(
712 object_store.clone(),
713 commit_handler.clone(),
714 BranchLocation {
715 path: base_path.clone(),
716 uri: uri.clone(),
717 branch: manifest.branch.clone(),
718 },
719 );
720 let metadata_cache = Arc::new(session.metadata_cache.for_dataset(&uri));
721 let index_cache = Arc::new(session.index_cache.for_dataset(&uri));
722 let fragment_bitmap = Arc::new(manifest.fragments.iter().map(|f| f.id as u32).collect());
723 Ok(Self {
724 object_store,
725 base: base_path,
726 uri,
727 manifest,
728 manifest_location,
729 commit_handler,
730 session,
731 refs,
732 fragment_bitmap,
733 metadata_cache,
734 index_cache,
735 file_reader_options,
736 store_params: store_params.map(Box::new),
737 base_store_params,
738 })
739 }
740
741 pub async fn write(
749 batches: impl RecordBatchReader + Send + 'static,
750 dest: impl Into<WriteDestination<'_>>,
751 params: Option<WriteParams>,
752 ) -> Result<Self> {
753 let mut builder = InsertBuilder::new(dest);
754 if let Some(params) = ¶ms {
755 builder = builder.with_params(params);
756 }
757 Box::pin(builder.execute_stream(Box::new(batches) as Box<dyn RecordBatchReader + Send>))
758 .await
759 }
760
761 pub async fn write_into_namespace(
773 batches: impl RecordBatchReader + Send + 'static,
774 namespace_client: Arc<dyn LanceNamespace>,
775 table_id: Vec<String>,
776 mut params: Option<WriteParams>,
777 ) -> Result<Self> {
778 let mut write_params = params.take().unwrap_or_default();
779
780 match write_params.mode {
781 WriteMode::Create => {
782 let declare_request = DeclareTableRequest {
783 id: Some(table_id.clone()),
784 ..Default::default()
785 };
786 let response = namespace_client
787 .declare_table(declare_request)
788 .await
789 .map_err(|e| Error::namespace_source(Box::new(e)))?;
790
791 let uri = response.location.ok_or_else(|| {
792 Error::namespace_source(Box::new(std::io::Error::other(
793 "Table location not found in declare_table response",
794 )))
795 })?;
796
797 if response.managed_versioning == Some(true) {
799 let external_store = LanceNamespaceExternalManifestStore::new(
800 namespace_client.clone(),
801 table_id.clone(),
802 );
803 let commit_handler: Arc<dyn CommitHandler> =
804 Arc::new(ExternalManifestCommitHandler {
805 external_manifest_store: Arc::new(external_store),
806 });
807 write_params.commit_handler = Some(commit_handler);
808 }
809
810 if let Some(namespace_storage_options) = response.storage_options {
812 let provider: Arc<dyn StorageOptionsProvider> = Arc::new(
813 LanceNamespaceStorageOptionsProvider::new(namespace_client, table_id),
814 );
815
816 let mut merged_options = write_params
818 .store_params
819 .as_ref()
820 .and_then(|p| p.storage_options().cloned())
821 .unwrap_or_default();
822 merged_options.extend(namespace_storage_options);
823
824 let accessor = Arc::new(StorageOptionsAccessor::with_initial_and_provider(
825 merged_options,
826 provider,
827 ));
828
829 let existing_params = write_params.store_params.take().unwrap_or_default();
830 write_params.store_params = Some(ObjectStoreParams {
831 storage_options_accessor: Some(accessor),
832 ..existing_params
833 });
834 }
835
836 Self::write(batches, uri.as_str(), Some(write_params)).await
837 }
838 WriteMode::Append | WriteMode::Overwrite => {
839 let request = DescribeTableRequest {
840 id: Some(table_id.clone()),
841 ..Default::default()
842 };
843 let response = namespace_client
844 .describe_table(request)
845 .await
846 .map_err(|e| Error::namespace_source(Box::new(e)))?;
847
848 let uri = response.location.ok_or_else(|| {
849 Error::namespace_source(Box::new(std::io::Error::other(
850 "Table location not found in describe_table response",
851 )))
852 })?;
853
854 if response.managed_versioning == Some(true) {
856 let external_store = LanceNamespaceExternalManifestStore::new(
857 namespace_client.clone(),
858 table_id.clone(),
859 );
860 let commit_handler: Arc<dyn CommitHandler> =
861 Arc::new(ExternalManifestCommitHandler {
862 external_manifest_store: Arc::new(external_store),
863 });
864 write_params.commit_handler = Some(commit_handler);
865 }
866
867 if let Some(namespace_storage_options) = response.storage_options {
869 let provider: Arc<dyn StorageOptionsProvider> =
870 Arc::new(LanceNamespaceStorageOptionsProvider::new(
871 namespace_client.clone(),
872 table_id.clone(),
873 ));
874
875 let mut merged_options = write_params
877 .store_params
878 .as_ref()
879 .and_then(|p| p.storage_options().cloned())
880 .unwrap_or_default();
881 merged_options.extend(namespace_storage_options);
882
883 let accessor = Arc::new(StorageOptionsAccessor::with_initial_and_provider(
884 merged_options,
885 provider,
886 ));
887
888 let existing_params = write_params.store_params.take().unwrap_or_default();
889 write_params.store_params = Some(ObjectStoreParams {
890 storage_options_accessor: Some(accessor),
891 ..existing_params
892 });
893 }
894
895 let mut builder = DatasetBuilder::from_uri(uri.as_str());
899 if let Some(ref store_params) = write_params.store_params
900 && let Some(accessor) = &store_params.storage_options_accessor
901 {
902 builder = builder.with_storage_options_accessor(accessor.clone());
903 }
904 let dataset = Arc::new(builder.load().await?);
905
906 Self::write(batches, dataset, Some(write_params)).await
907 }
908 }
909 }
910
911 pub async fn append(
915 &mut self,
916 batches: impl RecordBatchReader + Send + 'static,
917 params: Option<WriteParams>,
918 ) -> Result<()> {
919 let write_params = WriteParams {
920 mode: WriteMode::Append,
921 ..params.unwrap_or_default()
922 };
923
924 let new_dataset = InsertBuilder::new(WriteDestination::Dataset(Arc::new(self.clone())))
925 .with_params(&write_params)
926 .execute_stream(Box::new(batches) as Box<dyn RecordBatchReader + Send>)
927 .await?;
928
929 *self = new_dataset;
930
931 Ok(())
932 }
933
934 pub fn uri(&self) -> &str {
936 &self.uri
937 }
938
939 pub fn branch_location(&self) -> BranchLocation {
940 BranchLocation {
941 path: self.base.clone(),
942 uri: self.uri.clone(),
943 branch: self.manifest.branch.clone(),
944 }
945 }
946
947 pub async fn branch_identifier(&self) -> Result<BranchIdentifier> {
948 self.refs
949 .branches()
950 .get_identifier(self.manifest.branch.as_deref())
951 .await
952 }
953
954 pub fn manifest(&self) -> &Manifest {
956 &self.manifest
957 }
958
959 pub fn manifest_location(&self) -> &ManifestLocation {
960 &self.manifest_location
961 }
962
963 pub fn delta(&self) -> delta::DatasetDeltaBuilder {
978 delta::DatasetDeltaBuilder::new(self.clone())
979 }
980
981 pub(crate) fn is_legacy_storage(&self) -> bool {
983 self.manifest
984 .data_storage_format
985 .lance_file_version()
986 .unwrap()
987 == LanceFileVersion::Legacy
988 }
989
990 pub async fn latest_manifest(&self) -> Result<(Arc<Manifest>, ManifestLocation)> {
991 let location = self
992 .commit_handler
993 .resolve_latest_location(&self.base, &self.object_store)
994 .await?;
995
996 let manifest_key = ManifestKey {
998 version: location.version,
999 e_tag: location.e_tag.as_deref(),
1000 };
1001 let cached_manifest = self.metadata_cache.get_with_key(&manifest_key).await;
1002 if let Some(cached_manifest) = cached_manifest {
1003 return Ok((cached_manifest, location));
1004 }
1005
1006 if self.already_checked_out(&location, self.manifest.branch.as_deref()) {
1007 return Ok((self.manifest.clone(), self.manifest_location.clone()));
1008 }
1009 let mut manifest = read_manifest(&self.object_store, &location.path, location.size).await?;
1010 if manifest.schema.has_dictionary_types() && manifest.should_use_legacy_format() {
1011 let reader = if let Some(size) = location.size {
1012 self.object_store
1013 .open_with_size(&location.path, size as usize)
1014 .await?
1015 } else {
1016 self.object_store.open(&location.path).await?
1017 };
1018 populate_schema_dictionary(&mut manifest.schema, reader.as_ref()).await?;
1019 }
1020 let manifest_arc = Arc::new(manifest);
1021 self.metadata_cache
1022 .insert_with_key(&manifest_key, manifest_arc.clone())
1023 .await;
1024 Ok((manifest_arc, location))
1025 }
1026
1027 pub async fn read_transaction(&self) -> Result<Option<Transaction>> {
1032 let transaction_key = TransactionKey {
1033 version: self.manifest.version,
1034 };
1035 if let Some(transaction) = self.metadata_cache.get_with_key(&transaction_key).await {
1036 return Ok(Some((*transaction).clone()));
1037 }
1038
1039 let transaction = if let Some(pos) = self.manifest.transaction_section {
1041 let reader = if let Some(size) = self.manifest_location.size {
1042 self.object_store
1043 .open_with_size(&self.manifest_location.path, size as usize)
1044 .await?
1045 } else {
1046 self.object_store.open(&self.manifest_location.path).await?
1047 };
1048
1049 let tx: pb::Transaction = read_message(reader.as_ref(), pos).await?;
1050 Transaction::try_from(tx).map(Some)?
1051 } else if let Some(path) = &self.manifest.transaction_file {
1052 let path = self.transactions_dir().child(path.as_str());
1054 let data = self.object_store.inner.get(&path).await?.bytes().await?;
1055 let transaction = lance_table::format::pb::Transaction::decode(data)?;
1056 Transaction::try_from(transaction).map(Some)?
1057 } else {
1058 None
1059 };
1060
1061 if let Some(tx) = transaction.as_ref() {
1062 self.metadata_cache
1063 .insert_with_key(&transaction_key, Arc::new(tx.clone()))
1064 .await;
1065 }
1066 Ok(transaction)
1067 }
1068
1069 pub async fn read_transaction_by_version(&self, version: u64) -> Result<Option<Transaction>> {
1074 let dataset_version = self.checkout_version(version).await?;
1075 dataset_version.read_transaction().await
1076 }
1077
1078 pub async fn get_transactions(
1093 &self,
1094 recent_transactions: usize,
1095 ) -> Result<Vec<Option<Transaction>>> {
1096 let mut transactions = vec![];
1097 let mut dataset = self.clone();
1098
1099 loop {
1100 let transaction = dataset.read_transaction().await?;
1101 transactions.push(transaction);
1102
1103 if transactions.len() >= recent_transactions {
1104 break;
1105 } else {
1106 match dataset
1107 .checkout_version(dataset.version().version - 1)
1108 .await
1109 {
1110 Ok(ds) => dataset = ds,
1111 Err(Error::DatasetNotFound { .. }) => break,
1112 Err(err) => return Err(err),
1113 }
1114 }
1115 }
1116
1117 Ok(transactions)
1118 }
1119
1120 pub async fn restore(&mut self) -> Result<()> {
1122 let (latest_manifest, _) = self.latest_manifest().await?;
1123 let latest_version = latest_manifest.version;
1124
1125 let transaction = Transaction::new(
1126 latest_version,
1127 Operation::Restore {
1128 version: self.manifest.version,
1129 },
1130 None,
1131 );
1132
1133 self.apply_commit(transaction, &Default::default(), &Default::default())
1134 .await?;
1135
1136 Ok(())
1137 }
1138
1139 #[instrument(level = "debug", skip(self))]
1160 pub fn cleanup_old_versions(
1161 &self,
1162 older_than: Duration,
1163 delete_unverified: Option<bool>,
1164 error_if_tagged_old_versions: Option<bool>,
1165 ) -> BoxFuture<'_, Result<RemovalStats>> {
1166 let mut builder = CleanupPolicyBuilder::default();
1167 builder = builder.before_timestamp(utc_now() - older_than);
1168 if let Some(v) = delete_unverified {
1169 builder = builder.delete_unverified(v);
1170 }
1171 if let Some(v) = error_if_tagged_old_versions {
1172 builder = builder.error_if_tagged_old_versions(v);
1173 }
1174
1175 self.cleanup_with_policy(builder.build())
1176 }
1177
1178 #[instrument(level = "debug", skip(self))]
1194 pub fn cleanup_with_policy(
1195 &self,
1196 policy: CleanupPolicy,
1197 ) -> BoxFuture<'_, Result<RemovalStats>> {
1198 info!(target: TRACE_DATASET_EVENTS, event=DATASET_CLEANING_EVENT, uri=&self.uri);
1199 cleanup::cleanup_old_versions(self, policy).boxed()
1200 }
1201
1202 #[allow(clippy::too_many_arguments)]
1203 async fn do_commit(
1204 base_uri: WriteDestination<'_>,
1205 operation: Operation,
1206 read_version: Option<u64>,
1207 store_params: Option<ObjectStoreParams>,
1208 commit_handler: Option<Arc<dyn CommitHandler>>,
1209 session: Arc<Session>,
1210 enable_v2_manifest_paths: bool,
1211 detached: bool,
1212 ) -> Result<Self> {
1213 let read_version = read_version.map_or_else(
1214 || match operation {
1215 Operation::Overwrite { .. } | Operation::Restore { .. } => Ok(0),
1216 _ => Err(Error::invalid_input(
1217 "read_version must be specified for this operation",
1218 )),
1219 },
1220 Ok,
1221 )?;
1222
1223 let transaction = Transaction::new(read_version, operation, None);
1224
1225 let mut builder = CommitBuilder::new(base_uri)
1226 .enable_v2_manifest_paths(enable_v2_manifest_paths)
1227 .with_session(session)
1228 .with_detached(detached);
1229
1230 if let Some(store_params) = store_params {
1231 builder = builder.with_store_params(store_params);
1232 }
1233
1234 if let Some(commit_handler) = commit_handler {
1235 builder = builder.with_commit_handler(commit_handler);
1236 }
1237
1238 builder.execute(transaction).await
1239 }
1240
1241 pub async fn commit(
1276 dest: impl Into<WriteDestination<'_>>,
1277 operation: Operation,
1278 read_version: Option<u64>,
1279 store_params: Option<ObjectStoreParams>,
1280 commit_handler: Option<Arc<dyn CommitHandler>>,
1281 session: Arc<Session>,
1282 enable_v2_manifest_paths: bool,
1283 ) -> Result<Self> {
1284 Self::do_commit(
1285 dest.into(),
1286 operation,
1287 read_version,
1288 store_params,
1289 commit_handler,
1290 session,
1291 enable_v2_manifest_paths,
1292 false,
1293 )
1294 .await
1295 }
1296
1297 pub async fn commit_detached(
1306 dest: impl Into<WriteDestination<'_>>,
1307 operation: Operation,
1308 read_version: Option<u64>,
1309 store_params: Option<ObjectStoreParams>,
1310 commit_handler: Option<Arc<dyn CommitHandler>>,
1311 session: Arc<Session>,
1312 enable_v2_manifest_paths: bool,
1313 ) -> Result<Self> {
1314 Self::do_commit(
1315 dest.into(),
1316 operation,
1317 read_version,
1318 store_params,
1319 commit_handler,
1320 session,
1321 enable_v2_manifest_paths,
1322 true,
1323 )
1324 .await
1325 }
1326
1327 pub(crate) async fn apply_commit(
1328 &mut self,
1329 transaction: Transaction,
1330 write_config: &ManifestWriteConfig,
1331 commit_config: &CommitConfig,
1332 ) -> Result<()> {
1333 let (manifest, manifest_location) = commit_transaction(
1334 self,
1335 self.object_store(),
1336 self.commit_handler.as_ref(),
1337 &transaction,
1338 write_config,
1339 commit_config,
1340 self.manifest_location.naming_scheme,
1341 None,
1342 )
1343 .await?;
1344
1345 self.manifest = Arc::new(manifest);
1346 self.manifest_location = manifest_location;
1347 self.fragment_bitmap = Arc::new(
1348 self.manifest
1349 .fragments
1350 .iter()
1351 .map(|f| f.id as u32)
1352 .collect(),
1353 );
1354
1355 Ok(())
1356 }
1357
1358 pub fn scan(&self) -> Scanner {
1360 Scanner::new(Arc::new(self.clone()))
1361 }
1362
1363 #[instrument(skip_all)]
1367 pub async fn count_rows(&self, filter: Option<String>) -> Result<usize> {
1368 if let Some(filter) = filter {
1370 let mut scanner = self.scan();
1371 scanner.filter(&filter)?;
1372 Ok(scanner
1373 .project::<String>(&[])?
1374 .with_row_id() .count_rows()
1376 .await? as usize)
1377 } else {
1378 self.count_all_rows().await
1379 }
1380 }
1381
1382 pub(crate) async fn count_all_rows(&self) -> Result<usize> {
1383 let cnts = stream::iter(self.get_fragments())
1384 .map(|f| async move { f.count_rows(None).await })
1385 .buffer_unordered(16)
1386 .try_collect::<Vec<_>>()
1387 .await?;
1388 Ok(cnts.iter().sum())
1389 }
1390
1391 #[instrument(skip_all, fields(num_rows=row_indices.len()))]
1393 pub async fn take(
1394 &self,
1395 row_indices: &[u64],
1396 projection: impl Into<ProjectionRequest>,
1397 ) -> Result<RecordBatch> {
1398 take::take(self, row_indices, projection.into()).await
1399 }
1400
1401 pub async fn take_rows(
1436 &self,
1437 row_ids: &[u64],
1438 projection: impl Into<ProjectionRequest>,
1439 ) -> Result<RecordBatch> {
1440 Arc::new(self.clone())
1441 .take_builder(row_ids, projection)?
1442 .execute()
1443 .await
1444 }
1445
1446 pub fn take_builder(
1447 self: &Arc<Self>,
1448 row_ids: &[u64],
1449 projection: impl Into<ProjectionRequest>,
1450 ) -> Result<TakeBuilder> {
1451 TakeBuilder::try_new_from_ids(self.clone(), row_ids.to_vec(), projection.into())
1452 }
1453
1454 pub async fn take_blobs(
1456 self: &Arc<Self>,
1457 row_ids: &[u64],
1458 column: impl AsRef<str>,
1459 ) -> Result<Vec<BlobFile>> {
1460 blob::take_blobs(self, row_ids, column.as_ref()).await
1461 }
1462
1463 pub async fn take_blobs_by_addresses(
1471 self: &Arc<Self>,
1472 row_addrs: &[u64],
1473 column: impl AsRef<str>,
1474 ) -> Result<Vec<BlobFile>> {
1475 blob::take_blobs_by_addresses(self, row_addrs, column.as_ref()).await
1476 }
1477
1478 pub async fn take_blobs_by_indices(
1480 self: &Arc<Self>,
1481 row_indices: &[u64],
1482 column: impl AsRef<str>,
1483 ) -> Result<Vec<BlobFile>> {
1484 let fragments = self.get_fragments();
1485 let row_addrs = row_offsets_to_row_addresses(&fragments, row_indices).await?;
1486 blob::take_blobs_by_addresses(self, &row_addrs, column.as_ref()).await
1487 }
1488
1489 pub fn read_blobs(self: &Arc<Self>, column: impl AsRef<str>) -> Result<ReadBlobsBuilder> {
1512 let column = column.as_ref();
1513 let blob_field_id = blob::validate_blob_column(self, column)?;
1514 Ok(ReadBlobsBuilder::new(
1515 self.clone(),
1516 column.to_string(),
1517 blob_field_id,
1518 ))
1519 }
1520
1521 pub fn take_scan(
1525 &self,
1526 row_ranges: Pin<Box<dyn Stream<Item = Result<Range<u64>>> + Send>>,
1527 projection: Arc<Schema>,
1528 batch_readahead: usize,
1529 ) -> DatasetRecordBatchStream {
1530 take::take_scan(self, row_ranges, projection, batch_readahead)
1531 }
1532
1533 pub async fn sample(
1541 &self,
1542 n: usize,
1543 projection: &Schema,
1544 fragment_ids: Option<&[u32]>,
1545 ) -> Result<RecordBatch> {
1546 use rand::seq::IteratorRandom;
1547
1548 match fragment_ids {
1549 None => {
1550 let num_rows = self.count_rows(None).await?;
1551 let mut ids = (0..num_rows as u64).choose_multiple(&mut rand::rng(), n);
1552 ids.sort_unstable();
1553 self.take(&ids, projection.clone()).await
1554 }
1555 Some(fragment_ids) => {
1556 if fragment_ids.is_empty() {
1557 return Err(Error::invalid_input(
1558 "Dataset::sample does not accept an empty fragment_ids list".to_string(),
1559 ));
1560 }
1561
1562 let selected_fragment_ids = fragment_ids.iter().copied().collect::<BTreeSet<_>>();
1563 let selected_fragments = self
1564 .get_fragments()
1565 .into_iter()
1566 .filter(|fragment| selected_fragment_ids.contains(&(fragment.id() as u32)))
1567 .collect::<Vec<_>>();
1568
1569 if selected_fragments.len() != selected_fragment_ids.len() {
1570 let present_fragment_ids = selected_fragments
1571 .iter()
1572 .map(|fragment| fragment.id() as u32)
1573 .collect::<HashSet<_>>();
1574 let missing_fragment_ids = selected_fragment_ids
1575 .into_iter()
1576 .filter(|fragment_id| !present_fragment_ids.contains(fragment_id))
1577 .collect::<Vec<_>>();
1578 return Err(Error::invalid_input(format!(
1579 "Dataset::sample received fragment ids that are not part of the current dataset version: {missing_fragment_ids:?}",
1580 )));
1581 }
1582
1583 let num_rows = stream::iter(selected_fragments.iter().cloned())
1584 .map(|fragment| async move { fragment.count_rows(None).await })
1585 .buffer_unordered(16)
1586 .try_fold(0_u64, |acc, rows| async move { Ok(acc + rows as u64) })
1587 .await?;
1588
1589 let mut offsets = (0..num_rows).choose_multiple(&mut rand::rng(), n);
1590 offsets.sort_unstable();
1591
1592 let row_addrs = row_offsets_to_row_addresses(&selected_fragments, &offsets).await?;
1593 let dataset = Arc::new(self.clone());
1594 let projection = Arc::new(
1595 ProjectionRequest::from(projection.clone())
1596 .into_projection_plan(dataset.clone())?,
1597 );
1598 TakeBuilder::try_new_from_addresses(dataset, row_addrs, projection)?
1599 .execute()
1600 .await
1601 }
1602 }
1603 }
1604
1605 pub async fn delete(&mut self, predicate: &str) -> Result<write::delete::DeleteResult> {
1607 info!(target: TRACE_DATASET_EVENTS, event=DATASET_DELETING_EVENT, uri = &self.uri, predicate=predicate);
1608 write::delete::delete(self, predicate).await
1609 }
1610
1611 pub async fn truncate_table(&mut self) -> Result<()> {
1613 self.delete("true").await.map(|_| ())
1614 }
1615
1616 pub async fn add_bases(
1632 self: &Arc<Self>,
1633 new_bases: Vec<lance_table::format::BasePath>,
1634 transaction_properties: Option<HashMap<String, String>>,
1635 ) -> Result<Self> {
1636 let operation = Operation::UpdateBases { new_bases };
1637
1638 let transaction = TransactionBuilder::new(self.manifest.version, operation)
1639 .transaction_properties(transaction_properties.map(Arc::new))
1640 .build();
1641
1642 let new_dataset = CommitBuilder::new(self.clone())
1643 .execute(transaction)
1644 .await?;
1645
1646 Ok(new_dataset)
1647 }
1648
1649 pub async fn count_deleted_rows(&self) -> Result<usize> {
1650 futures::stream::iter(self.get_fragments())
1651 .map(|f| async move { f.count_deletions().await })
1652 .buffer_unordered(self.object_store.io_parallelism())
1653 .try_fold(0, |acc, x| futures::future::ready(Ok(acc + x)))
1654 .await
1655 }
1656
1657 pub fn object_store(&self) -> &ObjectStore {
1658 &self.object_store
1659 }
1660
1661 pub fn with_object_store(
1667 &self,
1668 object_store: Arc<ObjectStore>,
1669 store_params: Option<ObjectStoreParams>,
1670 ) -> Self {
1671 let mut cloned = self.clone();
1672 cloned.object_store = object_store;
1673 if let Some(store_params) = store_params {
1674 cloned.store_params = Some(Box::new(store_params));
1675 }
1676 cloned
1677 }
1678
1679 fn store_params_for_base(
1680 &self,
1681 base_path: Option<&lance_table::format::BasePath>,
1682 ) -> ObjectStoreParams {
1683 base_path
1687 .and_then(|base_path| {
1688 self.base_store_params
1689 .as_ref()
1690 .and_then(|params| params.get(&base_path.path))
1691 })
1692 .cloned()
1693 .unwrap_or_else(|| self.store_params.as_deref().cloned().unwrap_or_default())
1694 }
1695
1696 #[deprecated(since = "0.25.0", note = "Use initial_storage_options() instead")]
1701 pub fn storage_options(&self) -> Option<&HashMap<String, String>> {
1702 self.initial_storage_options()
1703 }
1704
1705 pub fn initial_storage_options(&self) -> Option<&HashMap<String, String>> {
1709 self.store_params
1710 .as_ref()
1711 .and_then(|params| params.storage_options())
1712 }
1713
1714 pub fn storage_options_provider(
1716 &self,
1717 ) -> Option<Arc<dyn lance_io::object_store::StorageOptionsProvider>> {
1718 self.store_params
1719 .as_ref()
1720 .and_then(|params| params.storage_options_accessor.as_ref())
1721 .and_then(|accessor| accessor.provider().cloned())
1722 }
1723
1724 pub fn storage_options_accessor(&self) -> Option<Arc<StorageOptionsAccessor>> {
1730 self.store_params
1731 .as_ref()
1732 .and_then(|params| params.get_accessor())
1733 }
1734
1735 pub async fn latest_storage_options(&self) -> Result<Option<StorageOptions>> {
1748 if let Some(accessor) = self.storage_options_accessor() {
1750 let options = accessor.get_storage_options().await?;
1751 return Ok(Some(options));
1752 }
1753
1754 Ok(self.initial_storage_options().cloned().map(StorageOptions))
1756 }
1757
1758 pub fn data_dir(&self) -> Path {
1759 self.base.child(DATA_DIR)
1760 }
1761
1762 pub fn indices_dir(&self) -> Path {
1763 self.base.child(INDICES_DIR)
1764 }
1765
1766 pub fn transactions_dir(&self) -> Path {
1767 self.base.child(TRANSACTIONS_DIR)
1768 }
1769
1770 pub fn deletions_dir(&self) -> Path {
1771 self.base.child(DELETIONS_DIR)
1772 }
1773
1774 pub fn versions_dir(&self) -> Path {
1775 self.base.child(VERSIONS_DIR)
1776 }
1777
1778 pub(crate) fn data_file_dir(&self, data_file: &DataFile) -> Result<Path> {
1779 self.data_file_dir_for_base(data_file.base_id)
1780 }
1781
1782 pub async fn create_data_file(&self, path: &str, base_id: Option<u32>) -> Result<DataFile> {
1794 let data_dir = self.data_file_dir_for_base(base_id)?;
1795 let filepath = data_dir.child(path);
1796
1797 let file_size = self.object_store().size(&filepath).await?;
1799
1800 let scheduler = ScanScheduler::new(
1802 self.object_store.clone(),
1803 SchedulerConfig::new(2 * 1024 * 1024 * 1024),
1804 );
1805 let file = scheduler
1806 .open_file(&filepath, &CachedFileSize::new(file_size))
1807 .await?;
1808 let file_metadata = FileReader::read_all_metadata(&file).await?;
1809
1810 let file_version = LanceFileVersion::try_from_major_minor(
1811 file_metadata.major_version as u32,
1812 file_metadata.minor_version as u32,
1813 )?;
1814
1815 let column_names: Vec<&str> = file_metadata
1817 .file_schema
1818 .fields
1819 .iter()
1820 .map(|f| f.name.as_str())
1821 .collect();
1822
1823 let projected_ds_schema = self.schema().project(&column_names)?;
1825
1826 let is_structural = file_version >= LanceFileVersion::V2_1;
1828 let ds_fields: Vec<_> = projected_ds_schema.fields_pre_order().collect();
1829 let file_fields: Vec<_> = file_metadata.file_schema.fields_pre_order().collect();
1830
1831 if ds_fields.len() != file_fields.len() {
1832 return Err(Error::invalid_input(format!(
1833 "Schema mismatch: dataset projection has {} fields but file has {} fields",
1834 ds_fields.len(),
1835 file_fields.len()
1836 )));
1837 }
1838
1839 let mut fields = Vec::new();
1840 let mut column_indices = Vec::new();
1841 let mut curr_column_idx: i32 = 0;
1842 let mut packed_struct_fields_num: usize = 0;
1843
1844 for (ds_field, file_field) in ds_fields.iter().zip(file_fields.iter()) {
1845 if ds_field.name != file_field.name {
1846 return Err(Error::invalid_input(format!(
1847 "Schema mismatch: expected field '{}' but file has '{}'",
1848 ds_field.name, file_field.name
1849 )));
1850 }
1851
1852 if packed_struct_fields_num > 0 {
1853 packed_struct_fields_num -= 1;
1854 continue;
1855 }
1856
1857 if file_field.is_packed_struct() {
1858 fields.push(ds_field.id);
1859 column_indices.push(curr_column_idx);
1860 curr_column_idx += 1;
1861 packed_struct_fields_num = file_field.children.len();
1862 } else if file_field.children.is_empty() || !is_structural {
1863 fields.push(ds_field.id);
1864 column_indices.push(curr_column_idx);
1865 curr_column_idx += 1;
1866 }
1867 }
1868
1869 let file_size_nz = NonZero::new(file_size);
1870 Ok(DataFile::new(
1871 path,
1872 fields,
1873 column_indices,
1874 file_metadata.major_version as u32,
1875 file_metadata.minor_version as u32,
1876 file_size_nz,
1877 base_id,
1878 ))
1879 }
1880
1881 fn data_file_dir_for_base(&self, base_id: Option<u32>) -> Result<Path> {
1885 match base_id {
1886 Some(base_id) => {
1887 let base_path = self.manifest.base_paths.get(&base_id).ok_or_else(|| {
1888 Error::invalid_input(format!("base_path id {} not found", base_id))
1889 })?;
1890 let path = base_path.extract_path(self.session.store_registry())?;
1891 if base_path.is_dataset_root {
1892 Ok(path.child(DATA_DIR))
1893 } else {
1894 Ok(path)
1895 }
1896 }
1897 None => Ok(self.base.child(DATA_DIR)),
1898 }
1899 }
1900
1901 pub(crate) async fn object_store_for_base(&self, base_id: u32) -> Result<Arc<ObjectStore>> {
1903 let base_path = self.manifest.base_paths.get(&base_id).ok_or_else(|| {
1904 Error::invalid_input(format!("Dataset base path with ID {} not found", base_id))
1905 })?;
1906 let store_params = self.store_params_for_base(Some(base_path));
1907
1908 let (store, _) = ObjectStore::from_uri_and_params(
1909 self.session.store_registry(),
1910 &base_path.path,
1911 &store_params,
1912 )
1913 .await?;
1914
1915 Ok(store)
1916 }
1917
1918 pub(crate) fn dataset_dir_for_deletion(&self, deletion_file: &DeletionFile) -> Result<Path> {
1919 match deletion_file.base_id.as_ref() {
1920 Some(base_id) => {
1921 let base_paths = &self.manifest.base_paths;
1922 let base_path = base_paths.get(base_id).ok_or_else(|| {
1923 Error::invalid_input(format!(
1924 "base_path id {} not found for deletion_file {:?}",
1925 base_id, deletion_file
1926 ))
1927 })?;
1928
1929 if !base_path.is_dataset_root {
1930 return Err(Error::internal(format!(
1931 "base_path id {} is not a dataset root for deletion_file {:?}",
1932 base_id, deletion_file
1933 )));
1934 }
1935 base_path.extract_path(self.session.store_registry())
1936 }
1937 None => Ok(self.base.clone()),
1938 }
1939 }
1940
1941 pub(crate) fn indice_files_dir(&self, index: &IndexMetadata) -> Result<Path> {
1943 match index.base_id.as_ref() {
1944 Some(base_id) => {
1945 let base_paths = &self.manifest.base_paths;
1946 let base_path = base_paths.get(base_id).ok_or_else(|| {
1947 Error::invalid_input(format!(
1948 "base_path id {} not found for index {}",
1949 base_id, index.uuid
1950 ))
1951 })?;
1952 let path = base_path.extract_path(self.session.store_registry())?;
1953 if base_path.is_dataset_root {
1954 Ok(path.child(INDICES_DIR))
1955 } else {
1956 Ok(path)
1958 }
1959 }
1960 None => Ok(self.base.child(INDICES_DIR)),
1961 }
1962 }
1963
1964 pub fn session(&self) -> Arc<Session> {
1965 self.session.clone()
1966 }
1967
1968 pub fn version_id(&self) -> u64 {
1973 self.manifest.version
1974 }
1975
1976 pub fn version(&self) -> Version {
1981 Version::from(self.manifest.as_ref())
1982 }
1983
1984 pub async fn index_cache_entry_count(&self) -> usize {
1986 self.session.index_cache.size().await
1987 }
1988
1989 pub async fn index_cache_hit_rate(&self) -> f32 {
1991 let stats = self.session.index_cache_stats().await;
1992 stats.hit_ratio()
1993 }
1994
1995 pub fn cache_size_bytes(&self) -> u64 {
1996 self.session.deep_size_of() as u64
1997 }
1998
1999 pub async fn versions(&self) -> Result<Vec<Version>> {
2001 let mut versions: Vec<Version> = self
2002 .commit_handler
2003 .list_manifest_locations(&self.base, &self.object_store, false)
2004 .try_filter_map(|location| async move {
2005 match read_manifest(&self.object_store, &location.path, location.size).await {
2006 Ok(manifest) => Ok(Some(Version::from(&manifest))),
2007 Err(e) => Err(e),
2008 }
2009 })
2010 .try_collect()
2011 .await?;
2012
2013 versions.sort_by_key(|v| v.version);
2015
2016 Ok(versions)
2017 }
2018
2019 pub async fn list_detached_manifests(&self) -> Result<Vec<ManifestLocation>> {
2034 self.commit_handler
2035 .list_detached_manifest_locations(&self.base, &self.object_store)
2036 .try_collect()
2037 .await
2038 }
2039
2040 pub async fn latest_version_id(&self) -> Result<u64> {
2044 Ok(self
2045 .commit_handler
2046 .resolve_latest_location(&self.base, &self.object_store)
2047 .await?
2048 .version)
2049 }
2050
2051 pub fn count_fragments(&self) -> usize {
2052 self.manifest.fragments.len()
2053 }
2054
2055 pub fn schema(&self) -> &Schema {
2057 &self.manifest.schema
2058 }
2059
2060 pub fn empty_projection(self: &Arc<Self>) -> Projection {
2063 Projection::empty(self.clone())
2064 }
2065
2066 pub fn full_projection(self: &Arc<Self>) -> Projection {
2068 Projection::full(self.clone())
2069 }
2070
2071 pub fn get_fragments(&self) -> Vec<FileFragment> {
2073 let dataset = Arc::new(self.clone());
2074 self.manifest
2075 .fragments
2076 .iter()
2077 .map(|f| FileFragment::new(dataset.clone(), f.clone()))
2078 .collect()
2079 }
2080
2081 pub fn iter_fragments(&self) -> impl Iterator<Item = &Fragment> {
2083 self.manifest.fragments.iter()
2084 }
2085
2086 pub fn get_fragment(&self, fragment_id: usize) -> Option<FileFragment> {
2087 let dataset = Arc::new(self.clone());
2088 let fragment = self
2089 .manifest
2090 .fragments
2091 .iter()
2092 .find(|f| f.id == fragment_id as u64)?;
2093 Some(FileFragment::new(dataset, fragment.clone()))
2094 }
2095
2096 pub fn fragments(&self) -> &Arc<Vec<Fragment>> {
2097 &self.manifest.fragments
2098 }
2099
2100 pub fn get_frags_from_ordered_ids(&self, ordered_ids: &[u32]) -> Vec<Option<FileFragment>> {
2103 let mut fragments = Vec::with_capacity(ordered_ids.len());
2104 let mut id_iter = ordered_ids.iter();
2105 let mut id = id_iter.next();
2106 let mut last_id: i64 = -1;
2108 for frag in self.manifest.fragments.iter() {
2109 let mut the_id = if let Some(id) = id { *id } else { break };
2110 assert!(the_id as i64 > last_id);
2112 while the_id < frag.id as u32 {
2115 fragments.push(None);
2116 last_id = the_id as i64;
2117 id = id_iter.next();
2118 the_id = if let Some(id) = id { *id } else { break };
2119 }
2120
2121 if the_id == frag.id as u32 {
2122 fragments.push(Some(FileFragment::new(
2123 Arc::new(self.clone()),
2124 frag.clone(),
2125 )));
2126 last_id = the_id as i64;
2127 id = id_iter.next();
2128 }
2129 }
2130 fragments
2131 }
2132
2133 async fn filter_addr_or_ids(&self, addr_or_ids: &[u64], addrs: &[u64]) -> Result<Vec<u64>> {
2135 if addrs.is_empty() {
2136 return Ok(Vec::new());
2137 }
2138
2139 let mut perm = permutation::sort(addrs);
2140 let sorted_addrs = perm.apply_slice(addrs);
2143
2144 let referenced_frag_ids = sorted_addrs
2146 .iter()
2147 .map(|addr| RowAddress::from(*addr).fragment_id())
2148 .dedup()
2149 .collect::<Vec<_>>();
2150 let frags = self.get_frags_from_ordered_ids(&referenced_frag_ids);
2151 let dv_futs = frags
2152 .iter()
2153 .map(|frag| {
2154 if let Some(frag) = frag {
2155 frag.get_deletion_vector().boxed()
2156 } else {
2157 std::future::ready(Ok(None)).boxed()
2158 }
2159 })
2160 .collect::<Vec<_>>();
2161 let dvs = stream::iter(dv_futs)
2162 .buffered(self.object_store.io_parallelism())
2163 .try_collect::<Vec<_>>()
2164 .await?;
2165
2166 let mut filtered_sorted_addrs = Vec::with_capacity(sorted_addrs.len());
2169 let mut sorted_addr_iter = sorted_addrs.into_iter().map(RowAddress::from);
2170 let mut next_addr = sorted_addr_iter.next().unwrap();
2171 let mut exhausted = false;
2172
2173 for frag_dv in frags.iter().zip(dvs).zip(referenced_frag_ids.iter()) {
2174 let ((frag, dv), frag_id) = frag_dv;
2175 if frag.is_some() {
2176 if let Some(dv) = dv.as_ref() {
2178 for deleted in dv.to_sorted_iter() {
2180 while next_addr.fragment_id() == *frag_id
2181 && next_addr.row_offset() < deleted
2182 {
2183 filtered_sorted_addrs.push(Some(u64::from(next_addr)));
2184 if let Some(next) = sorted_addr_iter.next() {
2185 next_addr = next;
2186 } else {
2187 exhausted = true;
2188 break;
2189 }
2190 }
2191 if exhausted {
2192 break;
2193 }
2194 if next_addr.fragment_id() != *frag_id {
2195 break;
2196 }
2197 if next_addr.row_offset() == deleted {
2198 filtered_sorted_addrs.push(None);
2199 if let Some(next) = sorted_addr_iter.next() {
2200 next_addr = next;
2201 } else {
2202 exhausted = true;
2203 break;
2204 }
2205 }
2206 }
2207 }
2208 if exhausted {
2209 break;
2210 }
2211 while next_addr.fragment_id() == *frag_id {
2214 filtered_sorted_addrs.push(Some(u64::from(next_addr)));
2215 if let Some(next) = sorted_addr_iter.next() {
2216 next_addr = next;
2217 } else {
2218 break;
2219 }
2220 }
2221 } else {
2222 while next_addr.fragment_id() == *frag_id {
2224 filtered_sorted_addrs.push(None);
2225 if let Some(next) = sorted_addr_iter.next() {
2226 next_addr = next;
2227 } else {
2228 break;
2229 }
2230 }
2231 }
2232 }
2233
2234 perm.apply_inv_slice_in_place(&mut filtered_sorted_addrs);
2238 Ok(addr_or_ids
2239 .iter()
2240 .zip(filtered_sorted_addrs)
2241 .filter_map(|(addr_or_id, maybe_addr)| maybe_addr.map(|_| *addr_or_id))
2242 .collect())
2243 }
2244
2245 pub(crate) async fn filter_deleted_ids(&self, ids: &[u64]) -> Result<Vec<u64>> {
2246 let addresses = if let Some(row_id_index) = get_row_id_index(self).await? {
2247 let addresses = ids
2248 .iter()
2249 .filter_map(|id| row_id_index.get(*id).map(|address| address.into()))
2250 .collect::<Vec<_>>();
2251 Cow::Owned(addresses)
2252 } else {
2253 Cow::Borrowed(ids)
2254 };
2255
2256 self.filter_addr_or_ids(ids, &addresses).await
2257 }
2258
2259 pub async fn num_small_files(&self, max_rows_per_group: usize) -> usize {
2264 futures::stream::iter(self.get_fragments())
2265 .map(|f| async move { f.physical_rows().await })
2266 .buffered(self.object_store.io_parallelism())
2267 .try_filter(|row_count| futures::future::ready(*row_count < max_rows_per_group))
2268 .count()
2269 .await
2270 }
2271
2272 pub async fn validate(&self) -> Result<()> {
2273 let id_counts =
2275 self.manifest
2276 .fragments
2277 .iter()
2278 .map(|f| f.id)
2279 .fold(HashMap::new(), |mut acc, id| {
2280 *acc.entry(id).or_insert(0) += 1;
2281 acc
2282 });
2283 for (id, count) in id_counts {
2284 if count > 1 {
2285 return Err(Error::corrupt_file(
2286 self.base.clone(),
2287 format!(
2288 "Duplicate fragment id {} found in dataset {:?}",
2289 id, self.base
2290 ),
2291 ));
2292 }
2293 }
2294
2295 self.manifest
2297 .fragments
2298 .iter()
2299 .map(|f| f.id)
2300 .try_fold(0, |prev, id| {
2301 if id < prev {
2302 Err(Error::corrupt_file(self.base.clone(), format!(
2303 "Fragment ids are not sorted in increasing fragment-id order. Found {} after {} in dataset {:?}",
2304 id, prev, self.base
2305 )))
2306 } else {
2307 Ok(id)
2308 }
2309 })?;
2310
2311 futures::stream::iter(self.get_fragments())
2313 .map(|f| async move { f.validate().await })
2314 .buffer_unordered(self.object_store.io_parallelism())
2315 .try_collect::<Vec<()>>()
2316 .await?;
2317
2318 let indices = self.load_indices().await?;
2320 self.validate_indices(&indices)?;
2321
2322 Ok(())
2323 }
2324
2325 fn validate_indices(&self, indices: &[IndexMetadata]) -> Result<()> {
2326 let mut index_ids = HashSet::new();
2328 for index in indices.iter() {
2329 if !index_ids.insert(&index.uuid) {
2330 return Err(Error::corrupt_file(
2331 self.manifest_location.path.clone(),
2332 format!(
2333 "Duplicate index id {} found in dataset {:?}",
2334 &index.uuid, self.base
2335 ),
2336 ));
2337 }
2338 }
2339
2340 if let Err(err) = detect_overlapping_fragments(indices) {
2342 let mut message = "Overlapping fragments detected in dataset.".to_string();
2343 for (index_name, overlapping_frags) in err.bad_indices {
2344 message.push_str(&format!(
2345 "\nIndex {:?} has overlapping fragments: {:?}",
2346 index_name, overlapping_frags
2347 ));
2348 }
2349 return Err(Error::corrupt_file(
2350 self.manifest_location.path.clone(),
2351 message,
2352 ));
2353 };
2354
2355 Ok(())
2356 }
2357
2358 pub async fn migrate_manifest_paths_v2(&mut self) -> Result<()> {
2393 migrate_scheme_to_v2(self.object_store(), &self.base).await?;
2394 let latest_version = self.latest_version_id().await?;
2396 *self = self.checkout_version(latest_version).await?;
2397 Ok(())
2398 }
2399
2400 pub async fn shallow_clone(
2405 &mut self,
2406 target_path: &str,
2407 version: impl Into<refs::Ref>,
2408 store_params: Option<ObjectStoreParams>,
2409 ) -> Result<Self> {
2410 let (ref_name, version_number) = self.resolve_reference(version.into()).await?;
2411 let clone_op = Operation::Clone {
2412 is_shallow: true,
2413 ref_name,
2414 ref_version: version_number,
2415 ref_path: self.uri.clone(),
2416 branch_name: None,
2417 };
2418 let transaction = Transaction::new(version_number, clone_op, None);
2419
2420 let builder = CommitBuilder::new(WriteDestination::Uri(target_path))
2421 .with_store_params(
2422 store_params.unwrap_or(self.store_params.as_deref().cloned().unwrap_or_default()),
2423 )
2424 .with_object_store(Arc::new(self.object_store().clone()))
2425 .with_commit_handler(self.commit_handler.clone())
2426 .with_storage_format(self.manifest.data_storage_format.lance_file_version()?);
2427 builder.execute(transaction).await
2428 }
2429
2430 pub async fn deep_clone(
2440 &mut self,
2441 target_path: &str,
2442 version: impl Into<refs::Ref>,
2443 store_params: Option<ObjectStoreParams>,
2444 ) -> Result<Self> {
2445 use futures::StreamExt;
2446
2447 let src_ds = self.checkout_version(version).await?;
2449 let src_paths = src_ds.collect_paths().await?;
2450
2451 let (target_store, target_base) = ObjectStore::from_uri_and_params(
2453 self.session.store_registry(),
2454 target_path,
2455 &store_params.clone().unwrap_or_default(),
2456 )
2457 .await?;
2458
2459 if self
2461 .commit_handler
2462 .resolve_latest_location(&target_base, &target_store)
2463 .await
2464 .is_ok()
2465 {
2466 return Err(Error::dataset_already_exists(target_path.to_string()));
2467 }
2468
2469 let build_absolute_path = |relative_path: &str, base: &Path| -> Path {
2470 let mut path = base.clone();
2471 for seg in relative_path.split('/') {
2472 if !seg.is_empty() {
2473 path = path.child(seg);
2474 }
2475 }
2476 path
2477 };
2478
2479 let io_parallelism = self.object_store.io_parallelism();
2486 let copy_futures = src_paths
2487 .iter()
2488 .map(|(relative_path, base)| {
2489 let store = Arc::clone(&target_store);
2490 let src_path = build_absolute_path(relative_path, base);
2491 let target_path = build_absolute_path(relative_path, &target_base);
2492 async move { store.copy(&src_path, &target_path).await.map(|_| ()) }
2493 })
2494 .collect::<Vec<_>>();
2495
2496 futures::stream::iter(copy_futures)
2497 .buffer_unordered(io_parallelism)
2498 .collect::<Vec<_>>()
2499 .await
2500 .into_iter()
2501 .collect::<Result<Vec<_>>>()?;
2502
2503 let ref_name = src_ds.manifest.branch.clone();
2505 let ref_version = src_ds.manifest_location.version;
2506 let clone_op = Operation::Clone {
2507 is_shallow: false,
2508 ref_name,
2509 ref_version,
2510 ref_path: src_ds.uri().to_string(),
2511 branch_name: None,
2512 };
2513 let txn = Transaction::new(ref_version, clone_op, None);
2514 let builder = CommitBuilder::new(WriteDestination::Uri(target_path))
2515 .with_store_params(store_params.clone().unwrap_or_default())
2516 .with_object_store(target_store.clone())
2517 .with_commit_handler(self.commit_handler.clone())
2518 .with_storage_format(self.manifest.data_storage_format.lance_file_version()?);
2519 let new_ds = builder.execute(txn).await?;
2520 Ok(new_ds)
2521 }
2522
2523 async fn resolve_reference(&self, reference: refs::Ref) -> Result<(Option<String>, u64)> {
2524 match reference {
2525 refs::Ref::Version(branch, version_number) => {
2526 if let Some(version_number) = version_number {
2527 Ok((branch, version_number))
2528 } else {
2529 let branch_location = self.branch_location().find_branch(branch.as_deref())?;
2530 let version_number = self
2531 .commit_handler
2532 .resolve_latest_location(&branch_location.path, &self.object_store)
2533 .await?
2534 .version;
2535 Ok((branch, version_number))
2536 }
2537 }
2538 refs::Ref::VersionNumber(version_number) => {
2539 Ok((self.manifest.branch.clone(), version_number))
2540 }
2541 refs::Ref::Tag(tag_name) => {
2542 let tag_contents = self.tags().get(tag_name.as_str()).await?;
2543 Ok((tag_contents.branch, tag_contents.version))
2544 }
2545 }
2546 }
2547
2548 async fn collect_paths(&self) -> Result<Vec<(String, Path)>> {
2550 let mut file_paths: Vec<(String, Path)> = Vec::new();
2551 for fragment in self.manifest.fragments.iter() {
2552 if let Some(RowIdMeta::External(external_file)) = &fragment.row_id_meta {
2553 return Err(Error::internal(format!(
2554 "External row_id_meta is not supported yet. external file path: {}",
2555 external_file.path
2556 )));
2557 }
2558 for data_file in fragment.files.iter() {
2559 let base_root = if let Some(base_id) = data_file.base_id {
2560 let base_path =
2561 self.manifest.base_paths.get(&base_id).ok_or_else(|| {
2562 Error::internal(format!("base_id {} not found", base_id))
2563 })?;
2564 Path::parse(base_path.path.as_str())?
2565 } else {
2566 self.base.clone()
2567 };
2568 file_paths.push((
2569 format!("{}/{}", DATA_DIR, data_file.path.clone()),
2570 base_root,
2571 ));
2572 }
2573 if let Some(deletion_file) = &fragment.deletion_file {
2574 let base_root = if let Some(base_id) = deletion_file.base_id {
2575 let base_path =
2576 self.manifest.base_paths.get(&base_id).ok_or_else(|| {
2577 Error::internal(format!("base_id {} not found", base_id))
2578 })?;
2579 Path::parse(base_path.path.as_str())?
2580 } else {
2581 self.base.clone()
2582 };
2583 file_paths.push((
2584 relative_deletion_file_path(fragment.id, deletion_file),
2585 base_root,
2586 ));
2587 }
2588 }
2589
2590 let indices = read_manifest_indexes(
2591 self.object_store.as_ref(),
2592 &self.manifest_location,
2593 &self.manifest,
2594 )
2595 .await?;
2596
2597 for index in &indices {
2598 let base_root = if let Some(base_id) = index.base_id {
2599 let base_path = self
2600 .manifest
2601 .base_paths
2602 .get(&base_id)
2603 .ok_or_else(|| Error::internal(format!("base_id {} not found", base_id)))?;
2604 Path::parse(base_path.path.as_str())?
2605 } else {
2606 self.base.clone()
2607 };
2608 let index_root = base_root.child(INDICES_DIR).child(index.uuid.to_string());
2609 let mut stream = self.object_store.read_dir_all(&index_root, None);
2610 while let Some(meta) = stream.next().await.transpose()? {
2611 if let Some(filename) = meta.location.filename() {
2612 file_paths.push((
2613 format!("{}/{}/{}", INDICES_DIR, index.uuid, filename),
2614 base_root.clone(),
2615 ));
2616 }
2617 }
2618 }
2619 Ok(file_paths)
2620 }
2621
2622 pub fn sql(&self, sql: &str) -> SqlQueryBuilder {
2626 SqlQueryBuilder::new(self.clone(), sql)
2627 }
2628
2629 pub(crate) fn lance_supports_nulls(&self, datatype: &DataType) -> bool {
2631 match self
2632 .manifest()
2633 .data_storage_format
2634 .lance_file_version()
2635 .unwrap_or(LanceFileVersion::Legacy)
2636 .resolve()
2637 {
2638 LanceFileVersion::Legacy => matches!(
2639 datatype,
2640 DataType::Utf8
2641 | DataType::LargeUtf8
2642 | DataType::Binary
2643 | DataType::List(_)
2644 | DataType::FixedSizeBinary(_)
2645 | DataType::FixedSizeList(_, _)
2646 ),
2647 LanceFileVersion::V2_0 => !matches!(datatype, DataType::Struct(..)),
2648 _ => true,
2649 }
2650 }
2651}
2652
2653pub(crate) struct NewTransactionResult<'a> {
2654 pub dataset: BoxFuture<'a, Result<Dataset>>,
2655 pub new_transactions: BoxStream<'a, Result<(u64, Arc<Transaction>)>>,
2656}
2657
2658pub(crate) fn load_new_transactions(dataset: &Dataset) -> NewTransactionResult<'_> {
2659 let io_parallelism = dataset.object_store().io_parallelism();
2662 let latest_version = dataset.manifest.version;
2663 let locations = dataset
2664 .commit_handler
2665 .list_manifest_locations(&dataset.base, dataset.object_store(), true)
2666 .try_take_while(move |location| {
2667 futures::future::ready(Ok(location.version > latest_version))
2668 });
2669
2670 let (latest_tx, latest_rx) = tokio::sync::oneshot::channel();
2672 let mut latest_tx = Some(latest_tx);
2673
2674 let manifests = locations
2675 .map_ok(move |location| {
2676 let latest_tx = latest_tx.take();
2677 async move {
2678 let manifest_key = ManifestKey {
2679 version: location.version,
2680 e_tag: location.e_tag.as_deref(),
2681 };
2682 let manifest = if let Some(cached) =
2683 dataset.metadata_cache.get_with_key(&manifest_key).await
2684 {
2685 cached
2686 } else {
2687 let loaded = Arc::new(
2688 Dataset::load_manifest(
2689 dataset.object_store(),
2690 &location,
2691 &dataset.uri,
2692 dataset.session.as_ref(),
2693 )
2694 .await?,
2695 );
2696 dataset
2697 .metadata_cache
2698 .insert_with_key(&manifest_key, loaded.clone())
2699 .await;
2700 loaded
2701 };
2702
2703 if let Some(latest_tx) = latest_tx {
2704 let _ = latest_tx.send((manifest.clone(), location.clone()));
2706 }
2707
2708 Ok((manifest, location))
2709 }
2710 })
2711 .try_buffer_unordered(io_parallelism / 2);
2712 let transactions = manifests
2713 .map_ok(move |(manifest, location)| async move {
2714 let manifest_copy = manifest.clone();
2715 let tx_key = TransactionKey {
2716 version: manifest.version,
2717 };
2718 let transaction =
2719 if let Some(cached) = dataset.metadata_cache.get_with_key(&tx_key).await {
2720 cached
2721 } else {
2722 let dataset_version = Dataset::checkout_manifest(
2723 dataset.object_store.clone(),
2724 dataset.base.clone(),
2725 dataset.uri.clone(),
2726 manifest_copy.clone(),
2727 location,
2728 dataset.session(),
2729 dataset.commit_handler.clone(),
2730 dataset.file_reader_options.clone(),
2731 dataset.store_params.as_deref().cloned(),
2732 dataset.base_store_params.clone(),
2733 )?;
2734 let loaded =
2735 Arc::new(dataset_version.read_transaction().await?.ok_or_else(|| {
2736 Error::internal(format!(
2737 "Dataset version {} does not have a transaction file",
2738 manifest_copy.version
2739 ))
2740 })?);
2741 dataset
2742 .metadata_cache
2743 .insert_with_key(&tx_key, loaded.clone())
2744 .await;
2745 loaded
2746 };
2747 Ok((manifest.version, transaction))
2748 })
2749 .try_buffer_unordered(io_parallelism / 2);
2750
2751 let dataset = async move {
2752 if let Ok((latest_manifest, location)) = latest_rx.await {
2753 Dataset::checkout_manifest(
2755 dataset.object_store.clone(),
2756 dataset.base.clone(),
2757 dataset.uri.clone(),
2758 latest_manifest,
2759 location,
2760 dataset.session(),
2761 dataset.commit_handler.clone(),
2762 dataset.file_reader_options.clone(),
2763 dataset.store_params.as_deref().cloned(),
2764 dataset.base_store_params.clone(),
2765 )
2766 } else {
2767 Ok(dataset.clone())
2770 }
2771 }
2772 .boxed();
2773
2774 let new_transactions = transactions.boxed();
2775
2776 NewTransactionResult {
2777 dataset,
2778 new_transactions,
2779 }
2780}
2781
2782impl Dataset {
2799 pub async fn add_columns(
2801 &mut self,
2802 transforms: NewColumnTransform,
2803 read_columns: Option<Vec<String>>,
2804 batch_size: Option<u32>,
2805 ) -> Result<()> {
2806 schema_evolution::add_columns(self, transforms, read_columns, batch_size).await
2807 }
2808
2809 pub async fn alter_columns(&mut self, alterations: &[ColumnAlteration]) -> Result<()> {
2818 schema_evolution::alter_columns(self, alterations).await
2819 }
2820
2821 pub async fn drop_columns(&mut self, columns: &[&str]) -> Result<()> {
2828 info!(target: TRACE_DATASET_EVENTS, event=DATASET_DROPPING_COLUMN_EVENT, uri = &self.uri, columns = columns.join(","));
2829 schema_evolution::drop_columns(self, columns).await
2830 }
2831
2832 #[deprecated(since = "0.9.12", note = "Please use `drop_columns` instead.")]
2838 pub async fn drop(&mut self, columns: &[&str]) -> Result<()> {
2839 self.drop_columns(columns).await
2840 }
2841
2842 async fn merge_impl(
2843 &mut self,
2844 stream: Box<dyn RecordBatchReader + Send>,
2845 left_on: &str,
2846 right_on: &str,
2847 ) -> Result<()> {
2848 if self.schema().field(left_on).is_none() && left_on != ROW_ID && left_on != ROW_ADDR {
2850 return Err(Error::invalid_input(format!(
2851 "Column {} does not exist in the left side dataset",
2852 left_on
2853 )));
2854 };
2855 let right_schema = stream.schema();
2856 if right_schema.field_with_name(right_on).is_err() {
2857 return Err(Error::invalid_input(format!(
2858 "Column {} does not exist in the right side dataset",
2859 right_on
2860 )));
2861 };
2862 for field in right_schema.fields() {
2863 if field.name() == right_on {
2864 continue;
2867 }
2868 if self.schema().field(field.name()).is_some() {
2869 return Err(Error::invalid_input(format!(
2870 "Column {} exists in both sides of the dataset",
2871 field.name()
2872 )));
2873 }
2874 }
2875
2876 let joiner = Arc::new(HashJoiner::try_new(stream, right_on).await?);
2878 let mut new_schema: Schema = self.schema().merge(joiner.out_schema().as_ref())?;
2881 new_schema.set_field_id(Some(self.manifest.max_field_id()));
2882
2883 let updated_fragments: Vec<Fragment> = stream::iter(self.get_fragments())
2886 .then(|f| {
2887 let joiner = joiner.clone();
2888 async move { f.merge(left_on, &joiner).await.map(|f| f.metadata) }
2889 })
2890 .try_collect::<Vec<_>>()
2891 .await?;
2892
2893 let transaction = Transaction::new(
2894 self.manifest.version,
2895 Operation::Merge {
2896 fragments: updated_fragments,
2897 schema: new_schema,
2898 },
2899 None,
2900 );
2901
2902 self.apply_commit(transaction, &Default::default(), &Default::default())
2903 .await?;
2904
2905 Ok(())
2906 }
2907
2908 pub async fn merge(
2920 &mut self,
2921 stream: impl RecordBatchReader + Send + 'static,
2922 left_on: &str,
2923 right_on: &str,
2924 ) -> Result<()> {
2925 let stream = Box::new(stream);
2926 self.merge_impl(stream, left_on, right_on).await
2927 }
2928
2929 pub async fn merge_index_metadata(
2932 &self,
2933 index_uuid: &str,
2934 index_type: IndexType,
2935 batch_readhead: Option<usize>,
2936 progress: Arc<dyn IndexBuildProgress>,
2937 ) -> Result<()> {
2938 let store = LanceIndexStore::from_dataset_for_new(self, index_uuid)?;
2939 let index_dir = self.indices_dir().child(index_uuid);
2940 match index_type {
2941 IndexType::Inverted => {
2942 lance_index::scalar::inverted::builder::merge_index_files(
2944 self.object_store(),
2945 &index_dir,
2946 Arc::new(store),
2947 progress,
2948 )
2949 .await
2950 }
2951 IndexType::BTree => {
2952 lance_index::scalar::btree::merge_index_files(
2954 self.object_store(),
2955 &index_dir,
2956 Arc::new(store),
2957 batch_readhead,
2958 progress,
2959 )
2960 .await
2961 }
2962 IndexType::IvfFlat | IndexType::IvfPq | IndexType::IvfSq | IndexType::Vector => {
2963 Err(Error::invalid_input(
2964 "Vector distributed indexing no longer supports merge_index_metadata; \
2965 build segments, optionally merge groups with merge_existing_index_segments(...), \
2966 and commit with commit_existing_index_segments(...)"
2967 .to_string(),
2968 ))
2969 }
2970 _ => Err(Error::invalid_input_source(Box::new(std::io::Error::new(
2971 std::io::ErrorKind::InvalidInput,
2972 format!("Unsupported index type (patched): {}", index_type),
2973 )))),
2974 }
2975 }
2976}
2977
2978impl Dataset {
2991 pub fn metadata(&self) -> &HashMap<String, String> {
2993 &self.manifest.table_metadata
2994 }
2995
2996 pub fn config(&self) -> &HashMap<String, String> {
2998 &self.manifest.config
2999 }
3000
3001 #[deprecated(
3003 note = "Use the new update_config(values, replace) method - pass None values to delete keys"
3004 )]
3005 pub async fn delete_config_keys(&mut self, delete_keys: &[&str]) -> Result<()> {
3006 let updates = delete_keys.iter().map(|key| (*key, None));
3007 self.update_config(updates).await?;
3008 Ok(())
3009 }
3010
3011 pub fn update_metadata(
3038 &mut self,
3039 values: impl IntoIterator<Item = impl Into<UpdateMapEntry>>,
3040 ) -> metadata::UpdateMetadataBuilder<'_> {
3041 metadata::UpdateMetadataBuilder::new(self, values, metadata::MetadataType::TableMetadata)
3042 }
3043
3044 pub fn update_config(
3071 &mut self,
3072 values: impl IntoIterator<Item = impl Into<UpdateMapEntry>>,
3073 ) -> metadata::UpdateMetadataBuilder<'_> {
3074 metadata::UpdateMetadataBuilder::new(self, values, metadata::MetadataType::Config)
3075 }
3076
3077 pub fn update_schema_metadata(
3104 &mut self,
3105 values: impl IntoIterator<Item = impl Into<UpdateMapEntry>>,
3106 ) -> metadata::UpdateMetadataBuilder<'_> {
3107 metadata::UpdateMetadataBuilder::new(self, values, metadata::MetadataType::SchemaMetadata)
3108 }
3109
3110 #[deprecated(note = "Use the new update_schema_metadata(values).replace() instead")]
3112 pub async fn replace_schema_metadata(
3113 &mut self,
3114 new_values: impl IntoIterator<Item = (String, String)>,
3115 ) -> Result<()> {
3116 let new_values = new_values
3117 .into_iter()
3118 .map(|(k, v)| (k, Some(v)))
3119 .collect::<HashMap<_, _>>();
3120 self.update_schema_metadata(new_values).replace().await?;
3121 Ok(())
3122 }
3123
3124 pub fn update_field_metadata(&mut self) -> UpdateFieldMetadataBuilder<'_> {
3154 UpdateFieldMetadataBuilder::new(self)
3155 }
3156
3157 pub async fn replace_field_metadata(
3159 &mut self,
3160 new_values: impl IntoIterator<Item = (u32, HashMap<String, String>)>,
3161 ) -> Result<()> {
3162 let new_values = new_values.into_iter().collect::<HashMap<_, _>>();
3163 let field_metadata_updates = new_values
3164 .into_iter()
3165 .map(|(field_id, metadata)| {
3166 (
3167 field_id as i32,
3168 translate_schema_metadata_updates(&metadata),
3169 )
3170 })
3171 .collect();
3172 metadata::execute_metadata_update(
3173 self,
3174 Operation::UpdateConfig {
3175 config_updates: None,
3176 table_metadata_updates: None,
3177 schema_metadata_updates: None,
3178 field_metadata_updates,
3179 },
3180 )
3181 .await
3182 }
3183}
3184
3185#[async_trait::async_trait]
3186impl DatasetTakeRows for Dataset {
3187 fn schema(&self) -> &Schema {
3188 Self::schema(self)
3189 }
3190
3191 async fn take_rows(&self, row_ids: &[u64], projection: &Schema) -> Result<RecordBatch> {
3192 Self::take_rows(self, row_ids, projection.clone()).await
3193 }
3194}
3195
3196#[derive(Debug)]
3197pub(crate) struct ManifestWriteConfig {
3198 auto_set_feature_flags: bool, timestamp: Option<SystemTime>, use_stable_row_ids: bool, use_legacy_format: Option<bool>, storage_format: Option<DataStorageFormat>, disable_transaction_file: bool, }
3205
3206impl Default for ManifestWriteConfig {
3207 fn default() -> Self {
3208 Self {
3209 auto_set_feature_flags: true,
3210 timestamp: None,
3211 use_stable_row_ids: false,
3212 disable_transaction_file: false,
3213 use_legacy_format: None,
3214 storage_format: None,
3215 }
3216 }
3217}
3218
3219impl ManifestWriteConfig {
3220 pub fn disable_transaction_file(&self) -> bool {
3221 self.disable_transaction_file
3222 }
3223}
3224
3225#[allow(clippy::too_many_arguments)]
3227pub(crate) async fn write_manifest_file(
3228 object_store: &ObjectStore,
3229 commit_handler: &dyn CommitHandler,
3230 base_path: &Path,
3231 manifest: &mut Manifest,
3232 indices: Option<Vec<IndexMetadata>>,
3233 config: &ManifestWriteConfig,
3234 naming_scheme: ManifestNamingScheme,
3235 mut transaction: Option<&Transaction>,
3236) -> std::result::Result<ManifestLocation, CommitError> {
3237 if config.auto_set_feature_flags {
3238 apply_feature_flags(
3239 manifest,
3240 config.use_stable_row_ids,
3241 config.disable_transaction_file,
3242 )?;
3243 }
3244
3245 manifest.set_timestamp(timestamp_to_nanos(config.timestamp));
3246
3247 manifest.update_max_fragment_id();
3248
3249 commit_handler
3250 .commit(
3251 manifest,
3252 indices,
3253 base_path,
3254 object_store,
3255 write_manifest_file_to_path,
3256 naming_scheme,
3257 transaction.take().map(|tx| tx.into()),
3258 )
3259 .await
3260}
3261
3262impl Projectable for Dataset {
3263 fn schema(&self) -> &Schema {
3264 self.schema()
3265 }
3266}
3267
3268#[cfg(test)]
3269mod tests;