1use arrow_array::{RecordBatch, RecordBatchReader};
8use byteorder::{ByteOrder, LittleEndian};
9use chrono::{Duration, prelude::*};
10use futures::future::BoxFuture;
11use futures::stream::{self, BoxStream, StreamExt, TryStreamExt};
12use futures::{FutureExt, Stream};
13use lance_core::deepsize::DeepSizeOf;
14
15use crate::dataset::metadata::UpdateFieldMetadataBuilder;
16use crate::dataset::transaction::translate_schema_metadata_updates;
17use crate::index::DatasetIndexExt;
18use crate::session::caches::{DSMetadataCache, ManifestKey, TransactionKey};
19use crate::session::index_caches::DSIndexCache;
20use itertools::Itertools;
21use lance_core::ROW_ADDR;
22use lance_core::datatypes::{OnMissing, OnTypeMismatch, Projectable, Projection};
23use lance_core::traits::DatasetTakeRows;
24use lance_core::utils::address::RowAddress;
25use lance_core::utils::tracing::{
26 DATASET_DELETING_EVENT, DATASET_DROPPING_COLUMN_EVENT, TRACE_DATASET_EVENTS,
27};
28use lance_datafusion::projection::ProjectionPlan;
29use lance_file::reader::{FileReader, FileReaderOptions};
30use lance_file::versions as file_versions;
31use lance_index::{IndexType, progress::IndexBuildProgress};
32use lance_io::object_store::{
33 ChainedWrappingObjectStore, LanceNamespaceStorageOptionsProvider, ObjectStore,
34 ObjectStoreParams, StorageOptions, StorageOptionsAccessor, StorageOptionsProvider,
35 WrappingObjectStore,
36};
37use lance_io::scheduler::{ScanScheduler, SchedulerConfig};
38use lance_io::traits::{WriteExt, Writer};
39use lance_io::utils::{
40 CachedFileSize, read_last_block, read_message, read_metadata_offset, read_struct,
41};
42use lance_namespace::LanceNamespace;
43use lance_table::format::{
44 DataFile, DataStorageFormat, DeletionFile, Fragment, IndexMetadata, MAGIC, Manifest,
45 ManifestBuildConfig, RowIdMeta, pb, populate_manifest_schema_dictionaries,
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::ObjectStoreExt;
56use object_store::path::Path;
57use prost::Message;
58use roaring::RoaringBitmap;
59use rowids::get_row_id_index;
60use serde::{Deserialize, Serialize};
61use std::borrow::Cow;
62use std::collections::{BTreeMap, HashMap, HashSet};
63use std::fmt::Debug;
64use std::num::NonZero;
65use std::ops::Range;
66use std::pin::Pin;
67use std::sync::Arc;
68use tracing::{info, instrument};
69
70pub(crate) mod blob;
71pub(crate) mod branch_location;
72pub mod builder;
73pub mod cleanup;
74pub mod delta;
75pub mod files;
76pub mod fragment;
77mod hash_joiner;
78pub mod index;
79pub mod mem_wal;
80mod metadata;
81pub mod optimize;
82pub(crate) mod overlay;
83pub mod progress;
84pub mod refs;
85pub mod rowids;
86pub mod scanner;
87mod schema_evolution;
88pub mod sql;
89pub mod statistics;
90mod take;
91pub mod transaction {
106 pub use lance_table::transaction::{
107 DataOverlayGroup, DataReplacementGroup, Operation, ReadVersionState, RewriteGroup,
108 RewrittenIndex, Transaction, TransactionBuilder, UpdateMap, UpdateMapEntry, UpdateMode,
109 UpdatedFragmentOffsets, translate_config_updates, translate_schema_metadata_updates,
110 validate_operation,
111 };
112}
113pub mod udtf;
114pub mod updater;
115mod utils;
116pub(crate) mod versions;
117pub mod write;
118
119pub(crate) use take::row_offsets_to_row_addresses;
120
121use self::builder::DatasetBuilder;
122use self::cleanup::RemovalStats;
123use self::fragment::FileFragment;
124use self::refs::Refs;
125use self::scanner::{DatasetRecordBatchStream, Scanner};
126use self::statistics::DatasetStatistics;
127use self::transaction::{Operation, Transaction, TransactionBuilder, UpdateMapEntry};
128use self::write::{cleanup_data_fragments, write_fragments_internal};
129use crate::dataset::branch_location::BranchLocation;
130use crate::dataset::cleanup::{CleanupOperation, CleanupPolicy, CleanupPolicyBuilder};
131use crate::dataset::refs::{BranchContents, BranchIdentifier, Branches, Tags};
132use crate::dataset::sql::SqlQueryBuilder;
133use crate::datatypes::Schema;
134use crate::index::retain_supported_indices;
135use crate::io::commit::{
136 DEFAULT_COMMIT_RETRY_TIMEOUT, commit_detached_transaction, commit_new_dataset,
137 commit_transaction, detect_overlapping_fragments,
138};
139use crate::session::Session;
140use crate::utils::temporal::{SystemTime, timestamp_to_nanos, utc_now};
141use crate::{Error, Result};
142pub use blob::{
143 BlobFile, BlobRangeRequest, BlobReadRange, ReadBlob, ReadBlobRange, ReadBlobRangesBuilder,
144 ReadBlobRangesStream, ReadBlobsBuilder, ReadBlobsStream,
145};
146use hash_joiner::HashJoiner;
147pub use lance_core::ROW_ID;
148use lance_core::box_error;
149use lance_index::scalar::lance_format::LanceIndexStore;
150use lance_namespace::models::{DeclareTableRequest, DescribeTableRequest};
151use lance_table::feature_flags::{apply_feature_flags, can_read_dataset};
152use lance_table::io::deletion::{DELETIONS_DIR, relative_deletion_file_path};
153use lance_table::rowids::{RowIdSequence, write_row_ids};
154pub use schema_evolution::{
155 BatchInfo, BatchUDF, ColumnAlteration, NewColumnTransform, UDFCheckpointStore,
156};
157pub use take::TakeBuilder;
158use uuid::Uuid;
159pub use write::merge_insert::{
160 MergeInsertBuilder, MergeInsertJob, MergeInsertWriteMode, MergeStats, UncommittedMergeInsert,
161 WhenMatched, WhenNotMatched, WhenNotMatchedBySource,
162};
163
164use crate::dataset::index::LanceIndexStoreExt;
165pub use write::update::{UpdateBuilder, UpdateJob};
166#[allow(deprecated)]
167pub use write::{
168 AutoCleanupParams, CommitBuilder, DEFAULT_COMMIT_TIMEOUT, DeleteBuilder, DeleteResult,
169 ExternalBlobMode, InsertBuilder, UncommittedDelete, WriteDestination, WriteMode, WriteParams,
170 WriteProgressFn, WriteStats, write_fragments,
171};
172
173pub(crate) const INDICES_DIR: &str = "_indices";
174pub(crate) const DATA_DIR: &str = "data";
175pub(crate) const TRANSACTIONS_DIR: &str = "_transactions";
176
177pub const DEFAULT_INDEX_CACHE_SIZE: usize = 6 * 1024 * 1024 * 1024;
180pub const DEFAULT_METADATA_CACHE_SIZE: usize = 1024 * 1024 * 1024;
184
185#[derive(Clone)]
187pub struct Dataset {
188 pub(crate) object_store: Arc<ObjectStore>,
191 pub(crate) commit_handler: Arc<dyn CommitHandler>,
192 uri: String,
197 pub(crate) base: Path,
198 pub manifest: Arc<Manifest>,
199 pub(crate) manifest_location: ManifestLocation,
202 pub(crate) session: Arc<Session>,
203 pub refs: Refs,
204
205 pub(crate) fragment_bitmap: Arc<RoaringBitmap>,
207
208 pub(crate) index_cache: Arc<DSIndexCache>,
210 pub(crate) metadata_cache: Arc<DSMetadataCache>,
211
212 pub(crate) file_reader_options: Option<FileReaderOptions>,
214
215 pub(crate) store_params: Option<Box<ObjectStoreParams>>,
218 pub(crate) base_store_params: Option<Arc<HashMap<String, ObjectStoreParams>>>,
220 pub(crate) base_object_stores: BaseObjectStores,
223}
224
225pub(crate) type BaseObjectStores =
227 Arc<std::sync::Mutex<HashMap<u32, Arc<tokio::sync::OnceCell<Arc<ObjectStore>>>>>>;
228
229impl std::fmt::Debug for Dataset {
230 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231 f.debug_struct("Dataset")
232 .field("uri", &self.uri)
233 .field("base", &self.base)
234 .field("version", &self.manifest.version)
235 .field("cache_num_items", &self.session.approx_num_items())
236 .field("base_store_params", &self.base_store_params.is_some())
237 .finish()
238 }
239}
240
241#[derive(Deserialize, Serialize, Debug)]
243pub struct Version {
244 pub version: u64,
246
247 pub timestamp: DateTime<Utc>,
249
250 pub metadata: BTreeMap<String, String>,
252}
253
254#[non_exhaustive]
256#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
257pub struct VersionRef {
258 pub version: u64,
260}
261
262impl From<&Manifest> for Version {
264 fn from(m: &Manifest) -> Self {
265 Self {
266 version: m.version,
267 timestamp: m.timestamp(),
268 metadata: m.summary().into(),
269 }
270 }
271}
272
273#[derive(Debug, Clone)]
279pub struct VersionTransaction {
280 pub version: u64,
282
283 pub timestamp: DateTime<Utc>,
285
286 pub transaction: Option<Transaction>,
288}
289
290#[derive(Clone, Debug)]
292pub struct ReadParams {
293 pub index_cache_size_bytes: usize,
296
297 pub metadata_cache_size_bytes: usize,
300
301 pub session: Option<Arc<Session>>,
305
306 pub store_options: Option<ObjectStoreParams>,
307
308 pub commit_handler: Option<Arc<dyn CommitHandler>>,
325
326 pub file_reader_options: Option<FileReaderOptions>,
333}
334
335impl ReadParams {
336 #[deprecated(
338 since = "0.30.0",
339 note = "Use `index_cache_size_bytes` instead, which accepts a size in bytes."
340 )]
341 pub fn index_cache_size(&mut self, cache_size: usize) -> &mut Self {
342 let assumed_entry_size = 20 * 1024 * 1024; self.index_cache_size_bytes = cache_size * assumed_entry_size;
344 self
345 }
346
347 pub fn index_cache_size_bytes(&mut self, cache_size: usize) -> &mut Self {
348 self.index_cache_size_bytes = cache_size;
349 self
350 }
351
352 #[deprecated(
354 since = "0.30.0",
355 note = "Use `metadata_cache_size_bytes` instead, which accepts a size in bytes."
356 )]
357 pub fn metadata_cache_size(&mut self, cache_size: usize) -> &mut Self {
358 let assumed_entry_size = 10 * 1024 * 1024; self.metadata_cache_size_bytes = cache_size * assumed_entry_size;
360 self
361 }
362
363 pub fn metadata_cache_size_bytes(&mut self, cache_size: usize) -> &mut Self {
365 self.metadata_cache_size_bytes = cache_size;
366 self
367 }
368
369 pub fn session(&mut self, session: Arc<Session>) -> &mut Self {
371 self.session = Some(session);
372 self
373 }
374
375 pub fn set_commit_lock<T: CommitLock + Send + Sync + 'static>(&mut self, lock: Arc<T>) {
377 self.commit_handler = Some(Arc::new(lock));
378 }
379
380 pub fn file_reader_options(&mut self, options: FileReaderOptions) -> &mut Self {
382 self.file_reader_options = Some(options);
383 self
384 }
385}
386
387impl Default for ReadParams {
388 fn default() -> Self {
389 Self {
390 index_cache_size_bytes: DEFAULT_INDEX_CACHE_SIZE,
391 metadata_cache_size_bytes: DEFAULT_METADATA_CACHE_SIZE,
392 session: None,
393 store_options: None,
394 commit_handler: None,
395 file_reader_options: None,
396 }
397 }
398}
399
400#[derive(Debug, Clone)]
401pub enum ProjectionRequest {
402 Schema(Arc<Schema>),
403 Sql(Vec<(String, String)>),
404}
405
406impl ProjectionRequest {
407 pub fn from_columns(
408 columns: impl IntoIterator<Item = impl AsRef<str>>,
409 dataset_schema: &Schema,
410 ) -> Self {
411 let columns = columns
412 .into_iter()
413 .map(|s| s.as_ref().to_string())
414 .collect::<Vec<_>>();
415
416 let schema = dataset_schema
417 .project_preserve_system_columns(&columns)
418 .unwrap();
419 Self::Schema(Arc::new(schema))
420 }
421
422 pub fn from_schema(schema: Schema) -> Self {
423 Self::Schema(Arc::new(schema))
424 }
425
426 pub fn from_sql(
432 columns: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
433 ) -> Self {
434 Self::Sql(
435 columns
436 .into_iter()
437 .map(|(a, b)| (a.into(), b.into()))
438 .collect(),
439 )
440 }
441
442 pub fn into_projection_plan(self, dataset: Arc<Dataset>) -> Result<ProjectionPlan> {
443 match self {
444 Self::Schema(schema) => {
445 let system_columns_present = schema
448 .fields
449 .iter()
450 .any(|f| lance_core::is_system_column(&f.name));
451
452 if system_columns_present {
453 ProjectionPlan::from_schema(dataset, schema.as_ref())
456 } else {
457 let projection = dataset.schema().project_by_schema(
459 schema.as_ref(),
460 OnMissing::Error,
461 OnTypeMismatch::Error,
462 )?;
463 ProjectionPlan::from_schema(dataset, &projection)
464 }
465 }
466 Self::Sql(columns) => ProjectionPlan::from_expressions(dataset, &columns),
467 }
468 }
469}
470
471impl From<Arc<Schema>> for ProjectionRequest {
472 fn from(schema: Arc<Schema>) -> Self {
473 Self::Schema(schema)
474 }
475}
476
477impl From<Schema> for ProjectionRequest {
478 fn from(schema: Schema) -> Self {
479 Self::from(Arc::new(schema))
480 }
481}
482
483impl Dataset {
484 #[instrument]
488 pub async fn open(uri: &str) -> Result<Self> {
489 DatasetBuilder::from_uri(uri).load().await
490 }
491
492 pub async fn checkout_version(&self, version: impl Into<refs::Ref>) -> Result<Self> {
494 let reference: refs::Ref = version.into();
495 match reference {
496 refs::Ref::Version(branch, version_number) => {
497 self.checkout_by_ref(version_number, branch.as_deref())
498 .await
499 }
500 refs::Ref::VersionNumber(version_number) => {
501 self.checkout_by_ref(Some(version_number), self.manifest.branch.as_deref())
502 .await
503 }
504 refs::Ref::Tag(tag_name) => {
505 let tag_contents = self.tags().get(tag_name.as_str()).await?;
506 self.checkout_by_ref(Some(tag_contents.version), tag_contents.branch.as_deref())
507 .await
508 }
509 }
510 }
511
512 pub fn tags(&self) -> Tags<'_> {
513 self.refs.tags()
514 }
515
516 pub fn statistics(&self) -> DatasetStatistics<'_> {
519 DatasetStatistics::new(self)
520 }
521
522 pub fn branches(&self) -> Branches<'_> {
523 self.refs.branches()
524 }
525
526 pub async fn checkout_latest(&mut self) -> Result<()> {
528 let (manifest, manifest_location) = self.latest_manifest().await?;
529 self.set_manifest(manifest, manifest_location);
530 Ok(())
531 }
532
533 fn set_manifest(&mut self, manifest: Arc<Manifest>, manifest_location: ManifestLocation) {
536 if manifest.base_paths != self.manifest.base_paths {
537 self.base_object_stores = Default::default();
538 }
539 self.manifest = manifest;
540 self.manifest_location = manifest_location;
541 self.fragment_bitmap = Arc::new(
542 self.manifest
543 .fragments
544 .iter()
545 .map(|f| f.id as u32)
546 .collect(),
547 );
548 }
549
550 pub async fn checkout_branch(&self, branch: &str) -> Result<Self> {
552 self.checkout_by_ref(None, Some(branch)).await
553 }
554
555 pub async fn create_branch(
571 &mut self,
572 branch: &str,
573 version: impl Into<refs::Ref>,
574 store_params: Option<ObjectStoreParams>,
575 ) -> Result<Self> {
576 let (source_branch, version_number) = self.resolve_reference(version.into()).await?;
577 let branch_location = self.branch_location().find_branch(Some(branch))?;
578 let source_location = self
579 .branch_location()
580 .find_branch(source_branch.as_deref())?;
581 let clone_op = Operation::Clone {
582 is_shallow: true,
583 ref_name: source_branch.clone(),
584 ref_version: version_number,
585 ref_path: source_location.uri,
586 branch_name: Some(branch.to_string()),
587 };
588 let transaction = Transaction::new(version_number, clone_op, None);
589
590 let builder = CommitBuilder::new(WriteDestination::Uri(branch_location.uri.as_str()))
591 .with_store_params(
593 store_params.unwrap_or(self.store_params.as_deref().cloned().unwrap_or_default()),
594 )
595 .with_object_store(Arc::new(self.object_store.as_ref().clone()))
596 .with_commit_handler(self.commit_handler.clone())
597 .with_exact_storage_format(self.manifest.data_storage_format.lance_file_format());
598 let dataset = builder.execute(transaction).await?;
599
600 self.branches()
602 .create(branch, version_number, source_branch.as_deref())
603 .await?;
604 Ok(dataset)
605 }
606
607 pub async fn delete_branch(&mut self, branch: &str) -> Result<()> {
608 self.branches().delete(branch, false).await
609 }
610
611 pub async fn force_delete_branch(&mut self, branch: &str) -> Result<()> {
614 self.branches().delete(branch, true).await
615 }
616
617 pub async fn list_branches(&self) -> Result<HashMap<String, BranchContents>> {
618 self.branches().list().await
619 }
620
621 fn already_checked_out(&self, location: &ManifestLocation, branch_name: Option<&str>) -> bool {
622 self.manifest.branch.as_deref() == branch_name
627 && self.manifest.version == location.version
628 && self.manifest_location.naming_scheme == location.naming_scheme
629 && location.e_tag.as_ref().is_some_and(|e_tag| {
630 self.manifest_location
631 .e_tag
632 .as_ref()
633 .is_some_and(|current_e_tag| e_tag == current_e_tag)
634 })
635 }
636
637 async fn checkout_by_ref(
638 &self,
639 version_number: Option<u64>,
640 branch: Option<&str>,
641 ) -> Result<Self> {
642 if let Some(branch_name) = branch
646 && !Branches::is_main_branch(branch)
647 {
648 refs::check_valid_branch(branch_name)?;
649 }
650
651 let new_location = self.branch_location().find_branch(branch)?;
652
653 let manifest_location = if let Some(version_number) = version_number {
654 self.commit_handler
655 .resolve_version_location(
656 &new_location.path,
657 version_number,
658 &self.object_store.inner,
659 )
660 .await?
661 } else {
662 self.commit_handler
663 .resolve_latest_location(&new_location.path, &self.object_store)
664 .await?
665 };
666
667 if self.already_checked_out(&manifest_location, branch) {
668 return Ok(self.clone());
669 }
670
671 let manifest = Self::get_manifest(
672 self.object_store.as_ref(),
673 &manifest_location,
674 &new_location.uri,
675 self.session.as_ref(),
676 )
677 .await?;
678
679 let requested_branch = branch.and_then(refs::standardize_branch);
684 if manifest.branch.as_deref() != requested_branch.as_deref() {
685 return Err(Error::internal(format!(
686 "checkout of branch '{}' at version {} resolved a manifest belonging to branch '{}'",
687 refs::normalize_branch(branch),
688 manifest.version,
689 refs::normalize_branch(manifest.branch.as_deref()),
690 )));
691 }
692
693 Self::checkout_manifest(
694 self.object_store.clone(),
695 new_location.path,
696 new_location.uri,
697 manifest,
698 manifest_location,
699 self.session.clone(),
700 self.commit_handler.clone(),
701 self.file_reader_options.clone(),
702 self.store_params.as_deref().cloned(),
703 self.base_store_params.clone(),
704 )
705 }
706
707 pub(crate) async fn load_manifest(
708 object_store: &ObjectStore,
709 manifest_location: &ManifestLocation,
710 uri: &str,
711 session: &Session,
712 ) -> Result<Manifest> {
713 let object_reader = if let Some(size) = manifest_location.size {
714 object_store
715 .open_with_size(&manifest_location.path, size as usize)
716 .await
717 } else {
718 object_store.open(&manifest_location.path).await
719 };
720 let object_reader = object_reader.map_err(|e| match &e {
721 Error::NotFound { uri, .. } => Error::dataset_not_found(uri.clone(), box_error(e)),
722 _ => e,
723 })?;
724
725 let last_block =
726 read_last_block(object_reader.as_ref())
727 .await
728 .map_err(|err| match err {
729 object_store::Error::NotFound { path, source } => {
730 Error::dataset_not_found(path, source)
731 }
732 _ => Error::io_source(err.into()),
733 })?;
734
735 if manifest_location.size.is_some() && !last_block.ends_with(MAGIC) {
739 let manifest_location = ManifestLocation {
740 size: None,
741 ..manifest_location.clone()
742 };
743 return Box::pin(Self::load_manifest(
744 object_store,
745 &manifest_location,
746 uri,
747 session,
748 ))
749 .await;
750 }
751
752 let offset = read_metadata_offset(&last_block)?;
753
754 let manifest_size = object_reader.size().await?;
756 let mut manifest = if manifest_size - offset <= last_block.len() {
757 let manifest_len = manifest_size - offset;
758 let offset_in_block = last_block.len() - manifest_len;
759 let message_len =
760 LittleEndian::read_u32(&last_block[offset_in_block..offset_in_block + 4]) as usize;
761 let message_data = &last_block[offset_in_block + 4..offset_in_block + 4 + message_len];
762 Manifest::try_from(lance_table::format::pb::Manifest::decode(message_data)?)
763 } else {
764 read_struct(object_reader.as_ref(), offset).await
765 }?;
766
767 if !can_read_dataset(manifest.reader_feature_flags) {
768 let message = format!(
769 "This dataset cannot be read by this version of Lance. \
770 Please upgrade Lance to read this dataset.\n Flags: {}",
771 manifest.reader_feature_flags
772 );
773 return Err(Error::not_supported_source(message.into()));
774 }
775
776 if let Some(index_offset) = manifest.index_section
779 && manifest_size - index_offset <= last_block.len()
780 {
781 let offset_in_block = last_block.len() - (manifest_size - index_offset);
782 let message_len =
783 LittleEndian::read_u32(&last_block[offset_in_block..offset_in_block + 4]) as usize;
784 let message_data = &last_block[offset_in_block + 4..offset_in_block + 4 + message_len];
785 let section = lance_table::format::pb::IndexSection::decode(message_data)?;
786 let mut indices: Vec<IndexMetadata> = section
787 .indices
788 .into_iter()
789 .map(IndexMetadata::try_from)
790 .collect::<Result<Vec<_>>>()?;
791 retain_supported_indices(&mut indices);
792 let ds_index_cache = session.index_cache.for_dataset(uri);
793 let metadata_key = crate::session::index_caches::IndexMetadataKey {
794 version: manifest_location.version,
795 store_identity: &object_store.store_prefix,
796 };
797 ds_index_cache
798 .insert_with_key(&metadata_key, Arc::new(indices))
799 .await;
800 }
801
802 if let Some(transaction_offset) = manifest.transaction_section
805 && manifest_size - transaction_offset <= last_block.len()
806 {
807 let offset_in_block = last_block.len() - (manifest_size - transaction_offset);
808 let message_len =
809 LittleEndian::read_u32(&last_block[offset_in_block..offset_in_block + 4]) as usize;
810 let message_data = &last_block[offset_in_block + 4..offset_in_block + 4 + message_len];
811 let transaction: Transaction =
812 lance_table::format::pb::Transaction::decode(message_data)?.try_into()?;
813
814 let metadata_cache = session.metadata_cache.for_dataset(uri);
815 let metadata_key = TransactionKey {
816 version: manifest_location.version,
817 };
818 metadata_cache
819 .insert_with_key(&metadata_key, Arc::new(transaction))
820 .await;
821 }
822
823 populate_manifest_schema_dictionaries(&mut manifest, object_reader.as_ref()).await?;
824
825 Ok(manifest)
826 }
827
828 pub(crate) async fn get_manifest(
831 object_store: &ObjectStore,
832 manifest_location: &ManifestLocation,
833 uri: &str,
834 session: &Session,
835 ) -> Result<Arc<Manifest>> {
836 if manifest_location.size.is_none() {
837 return Ok(Arc::new(
838 Self::load_manifest(object_store, manifest_location, uri, session).await?,
839 ));
840 }
841 let metadata_cache = session.metadata_cache.for_dataset(uri);
842 let manifest_key = ManifestKey {
843 version: manifest_location.version,
844 e_tag: manifest_location.e_tag.as_deref(),
845 };
846 if let Some(cached) = metadata_cache.get_with_key(&manifest_key).await {
847 return Ok(cached);
848 }
849 let loaded =
850 Arc::new(Self::load_manifest(object_store, manifest_location, uri, session).await?);
851 metadata_cache
852 .insert_with_key(&manifest_key, loaded.clone())
853 .await;
854 Ok(loaded)
855 }
856
857 #[allow(clippy::too_many_arguments)]
858 fn checkout_manifest(
859 object_store: Arc<ObjectStore>,
860 base_path: Path,
861 uri: String,
862 manifest: Arc<Manifest>,
863 manifest_location: ManifestLocation,
864 session: Arc<Session>,
865 commit_handler: Arc<dyn CommitHandler>,
866 file_reader_options: Option<FileReaderOptions>,
867 store_params: Option<ObjectStoreParams>,
868 base_store_params: Option<Arc<HashMap<String, ObjectStoreParams>>>,
869 ) -> Result<Self> {
870 let refs = Refs::new(
871 object_store.clone(),
872 commit_handler.clone(),
873 BranchLocation {
874 path: base_path.clone(),
875 uri: uri.clone(),
876 branch: manifest.branch.clone(),
877 },
878 );
879 let metadata_cache = Arc::new(session.metadata_cache.for_dataset(&uri));
880 let index_cache = Arc::new(session.index_cache.for_dataset(&uri));
881 let fragment_bitmap = Arc::new(manifest.fragments.iter().map(|f| f.id as u32).collect());
882 write::log_unregistered_base_scoped_options(
883 store_params.as_ref(),
884 &manifest.base_paths,
885 log::Level::Debug,
886 );
887 Ok(Self {
888 object_store,
889 base: base_path,
890 uri,
891 manifest,
892 manifest_location,
893 commit_handler,
894 session,
895 refs,
896 fragment_bitmap,
897 metadata_cache,
898 index_cache,
899 file_reader_options,
900 store_params: store_params.map(Box::new),
901 base_store_params,
902 base_object_stores: Default::default(),
903 })
904 }
905
906 pub async fn write(
914 batches: impl RecordBatchReader + Send + 'static,
915 dest: impl Into<WriteDestination<'_>>,
916 params: Option<WriteParams>,
917 ) -> Result<Self> {
918 let mut builder = InsertBuilder::new(dest);
919 if let Some(params) = ¶ms {
920 builder = builder.with_params(params);
921 }
922 Box::pin(builder.execute_stream(Box::new(batches) as Box<dyn RecordBatchReader + Send>))
923 .await
924 }
925
926 pub async fn write_into_namespace(
938 batches: impl RecordBatchReader + Send + 'static,
939 namespace_client: Arc<dyn LanceNamespace>,
940 table_id: Vec<String>,
941 params: Option<WriteParams>,
942 ) -> Result<Self> {
943 Self::write_into_namespace_impl(batches, namespace_client, table_id, None, params).await
944 }
945
946 pub async fn write_into_namespace_on_branch(
952 batches: impl RecordBatchReader + Send + 'static,
953 namespace_client: Arc<dyn LanceNamespace>,
954 table_id: Vec<String>,
955 branch: &str,
956 params: Option<WriteParams>,
957 ) -> Result<Self> {
958 Self::write_into_namespace_impl(
959 batches,
960 namespace_client,
961 table_id,
962 Some(branch.to_string()),
963 params,
964 )
965 .await
966 }
967
968 async fn write_into_namespace_impl(
969 batches: impl RecordBatchReader + Send + 'static,
970 namespace_client: Arc<dyn LanceNamespace>,
971 table_id: Vec<String>,
972 branch: Option<String>,
973 mut params: Option<WriteParams>,
974 ) -> Result<Self> {
975 let mut write_params = params.take().unwrap_or_default();
976
977 match write_params.mode {
978 WriteMode::Create => {
979 if branch.is_some() {
980 return Err(Error::not_supported_source(
981 "cannot create a table on a branch; create on main first, then branch it"
982 .into(),
983 ));
984 }
985 let declare_request = DeclareTableRequest {
986 id: Some(table_id.clone()),
987 ..Default::default()
988 };
989 let response = namespace_client
990 .declare_table(declare_request)
991 .await
992 .map_err(|e| Error::namespace_source(Box::new(e)))?;
993
994 let uri = response.location.ok_or_else(|| {
995 Error::namespace_source(Box::new(std::io::Error::other(
996 "Table location not found in declare_table response",
997 )))
998 })?;
999
1000 if response.managed_versioning == Some(true) {
1002 let external_store = LanceNamespaceExternalManifestStore::for_table_uri(
1005 namespace_client.clone(),
1006 table_id.clone(),
1007 &uri,
1008 )?;
1009 let commit_handler: Arc<dyn CommitHandler> =
1010 Arc::new(ExternalManifestCommitHandler {
1011 external_manifest_store: Arc::new(external_store),
1012 });
1013 write_params.commit_handler = Some(commit_handler);
1014 }
1015
1016 if let Some(namespace_storage_options) = response.storage_options {
1018 let provider: Arc<dyn StorageOptionsProvider> = Arc::new(
1019 LanceNamespaceStorageOptionsProvider::new(namespace_client, table_id),
1020 );
1021
1022 let mut merged_options = write_params
1024 .store_params
1025 .as_ref()
1026 .and_then(|p| p.storage_options().cloned())
1027 .unwrap_or_default();
1028 merged_options.extend(namespace_storage_options);
1029
1030 let accessor = Arc::new(StorageOptionsAccessor::with_initial_and_provider(
1031 merged_options,
1032 provider,
1033 ));
1034
1035 let existing_params = write_params.store_params.take().unwrap_or_default();
1036 write_params.store_params = Some(ObjectStoreParams {
1037 storage_options_accessor: Some(accessor),
1038 ..existing_params
1039 });
1040 }
1041
1042 Self::write(batches, uri.as_str(), Some(write_params)).await
1043 }
1044 WriteMode::Append | WriteMode::Overwrite => {
1045 let request = DescribeTableRequest {
1046 id: Some(table_id.clone()),
1047 ..Default::default()
1048 };
1049 let response = namespace_client
1050 .describe_table(request)
1051 .await
1052 .map_err(|e| Error::namespace_source(Box::new(e)))?;
1053
1054 let uri = response.location.ok_or_else(|| {
1055 Error::namespace_source(Box::new(std::io::Error::other(
1056 "Table location not found in describe_table response",
1057 )))
1058 })?;
1059
1060 let commit_handler: Option<Arc<dyn CommitHandler>> =
1065 if response.managed_versioning == Some(true) {
1066 let external_store = LanceNamespaceExternalManifestStore::for_table_uri(
1069 namespace_client.clone(),
1070 table_id.clone(),
1071 uri.as_str(),
1072 )?;
1073 Some(Arc::new(ExternalManifestCommitHandler {
1074 external_manifest_store: Arc::new(external_store),
1075 }))
1076 } else {
1077 None
1078 };
1079
1080 if let Some(namespace_storage_options) = response.storage_options {
1082 let provider: Arc<dyn StorageOptionsProvider> =
1083 Arc::new(LanceNamespaceStorageOptionsProvider::new(
1084 namespace_client.clone(),
1085 table_id.clone(),
1086 ));
1087
1088 let mut merged_options = write_params
1090 .store_params
1091 .as_ref()
1092 .and_then(|p| p.storage_options().cloned())
1093 .unwrap_or_default();
1094 merged_options.extend(namespace_storage_options);
1095
1096 let accessor = Arc::new(StorageOptionsAccessor::with_initial_and_provider(
1097 merged_options,
1098 provider,
1099 ));
1100
1101 let existing_params = write_params.store_params.take().unwrap_or_default();
1102 write_params.store_params = Some(ObjectStoreParams {
1103 storage_options_accessor: Some(accessor),
1104 ..existing_params
1105 });
1106 }
1107
1108 let mut builder = DatasetBuilder::from_uri(uri.as_str());
1112 if let Some(ref store_params) = write_params.store_params
1113 && let Some(accessor) = &store_params.storage_options_accessor
1114 {
1115 builder = builder.with_storage_options_accessor(accessor.clone());
1116 }
1117 if let Some(commit_handler) = commit_handler {
1118 builder = builder.with_commit_handler(commit_handler);
1119 }
1120 if let Some(branch) = &branch {
1121 builder = builder.with_branch(branch, None);
1122 }
1123 let dataset = Arc::new(builder.load().await?);
1124
1125 Self::write(batches, dataset, Some(write_params)).await
1126 }
1127 }
1128 }
1129
1130 pub async fn append(
1134 &mut self,
1135 batches: impl RecordBatchReader + Send + 'static,
1136 params: Option<WriteParams>,
1137 ) -> Result<()> {
1138 let write_params = WriteParams {
1139 mode: WriteMode::Append,
1140 ..params.unwrap_or_default()
1141 };
1142
1143 let new_dataset = InsertBuilder::new(WriteDestination::Dataset(Arc::new(self.clone())))
1144 .with_params(&write_params)
1145 .execute_stream(Box::new(batches) as Box<dyn RecordBatchReader + Send>)
1146 .await?;
1147
1148 *self = new_dataset;
1149
1150 Ok(())
1151 }
1152
1153 pub fn uri(&self) -> &str {
1155 &self.uri
1156 }
1157
1158 pub fn branch_location(&self) -> BranchLocation {
1159 BranchLocation {
1160 path: self.base.clone(),
1161 uri: self.uri.clone(),
1162 branch: self.manifest.branch.clone(),
1163 }
1164 }
1165
1166 pub async fn branch_identifier(&self) -> Result<BranchIdentifier> {
1167 self.refs
1168 .branches()
1169 .get_identifier(self.manifest.branch.as_deref())
1170 .await
1171 }
1172
1173 pub fn manifest(&self) -> &Manifest {
1175 &self.manifest
1176 }
1177
1178 pub fn manifest_location(&self) -> &ManifestLocation {
1179 &self.manifest_location
1180 }
1181
1182 pub fn delta(&self) -> delta::DatasetDeltaBuilder {
1197 delta::DatasetDeltaBuilder::new(self.clone())
1198 }
1199
1200 pub async fn latest_manifest(&self) -> Result<(Arc<Manifest>, ManifestLocation)> {
1201 let location = self
1202 .commit_handler
1203 .resolve_latest_location(&self.base, &self.object_store)
1204 .await?;
1205
1206 let manifest_key = ManifestKey {
1208 version: location.version,
1209 e_tag: location.e_tag.as_deref(),
1210 };
1211 let cached_manifest = self.metadata_cache.get_with_key(&manifest_key).await;
1212 if let Some(cached_manifest) = cached_manifest {
1213 return Ok((cached_manifest, location));
1214 }
1215
1216 if self.already_checked_out(&location, self.manifest.branch.as_deref()) {
1217 return Ok((self.manifest.clone(), self.manifest_location.clone()));
1218 }
1219 let mut manifest = read_manifest(&self.object_store, &location.path, location.size).await?;
1220 if manifest.schema.has_dictionary_types() {
1221 let reader = if let Some(size) = location.size {
1222 self.object_store
1223 .open_with_size(&location.path, size as usize)
1224 .await?
1225 } else {
1226 self.object_store.open(&location.path).await?
1227 };
1228 populate_manifest_schema_dictionaries(&mut manifest, reader.as_ref()).await?;
1229 }
1230 let manifest_arc = Arc::new(manifest);
1231 self.metadata_cache
1232 .insert_with_key(&manifest_key, manifest_arc.clone())
1233 .await;
1234 Ok((manifest_arc, location))
1235 }
1236
1237 pub async fn read_transaction(&self) -> Result<Option<Transaction>> {
1242 let transaction_key = TransactionKey {
1243 version: self.manifest.version,
1244 };
1245 if let Some(transaction) = self.metadata_cache.get_with_key(&transaction_key).await {
1246 return Ok(Some((*transaction).clone()));
1247 }
1248
1249 let transaction = self
1250 .read_transaction_from_storage(&self.manifest, &self.manifest_location)
1251 .await?;
1252
1253 if let Some(tx) = transaction.as_ref() {
1254 self.metadata_cache
1255 .insert_with_key(&transaction_key, Arc::new(tx.clone()))
1256 .await;
1257 }
1258 Ok(transaction)
1259 }
1260
1261 async fn read_transaction_from_storage(
1264 &self,
1265 manifest: &Manifest,
1266 manifest_location: &ManifestLocation,
1267 ) -> Result<Option<Transaction>> {
1268 if let Some(pos) = manifest.transaction_section {
1270 let reader = match manifest_location.size {
1271 Some(size) => {
1272 self.object_store
1273 .open_with_size(&manifest_location.path, size as usize)
1274 .await?
1275 }
1276 None => self.object_store.open(&manifest_location.path).await?,
1277 };
1278
1279 let tx: pb::Transaction = match read_message(reader.as_ref(), pos).await {
1282 Err(e)
1283 if manifest_location.size.is_some()
1284 && e.to_string().contains("file size is too small") =>
1285 {
1286 let reader = self.object_store.open(&manifest_location.path).await?;
1287 read_message(reader.as_ref(), pos).await?
1288 }
1289 other => other?,
1290 };
1291 Transaction::try_from(tx).map(Some)
1292 } else if let Some(path) = &manifest.transaction_file {
1293 let path = self.transactions_dir().join(path.as_str());
1295 let data = self.object_store.inner.get(&path).await?.bytes().await?;
1296 let transaction = lance_table::format::pb::Transaction::decode(data)?;
1297 Transaction::try_from(transaction).map(Some)
1298 } else {
1299 Ok(None)
1300 }
1301 }
1302
1303 pub async fn read_version_transaction(&self, version: u64) -> Result<VersionTransaction> {
1326 let manifest_location = self
1328 .commit_handler
1329 .resolve_version_location(&self.base, version, &self.object_store.inner)
1330 .await?;
1331
1332 let manifest = read_manifest(
1334 &self.object_store,
1335 &manifest_location.path,
1336 manifest_location.size,
1337 )
1338 .await
1339 .map_err(|e| match &e {
1340 Error::NotFound { uri, .. } => Error::dataset_not_found(uri.clone(), box_error(e)),
1341 _ => e,
1342 })?;
1343
1344 if manifest.branch != self.manifest.branch {
1350 return Err(Error::internal(format!(
1351 "reading version {} on branch '{}' resolved a manifest belonging to branch '{}'",
1352 version,
1353 refs::normalize_branch(self.manifest.branch.as_deref()),
1354 refs::normalize_branch(manifest.branch.as_deref()),
1355 )));
1356 }
1357
1358 let transaction = self
1359 .read_transaction_from_storage(&manifest, &manifest_location)
1360 .await?;
1361
1362 Ok(VersionTransaction {
1363 version: manifest.version,
1364 timestamp: manifest.timestamp(),
1365 transaction,
1366 })
1367 }
1368
1369 pub async fn read_transaction_by_version(&self, version: u64) -> Result<Option<Transaction>> {
1388 Ok(self.read_version_transaction(version).await?.transaction)
1389 }
1390
1391 pub async fn get_transactions(
1406 &self,
1407 recent_transactions: usize,
1408 ) -> Result<Vec<Option<Transaction>>> {
1409 let mut transactions = vec![];
1410 let mut dataset = self.clone();
1411
1412 loop {
1413 let transaction = dataset.read_transaction().await?;
1414 transactions.push(transaction);
1415
1416 if transactions.len() >= recent_transactions {
1417 break;
1418 } else {
1419 match dataset
1420 .checkout_version(dataset.version().version - 1)
1421 .await
1422 {
1423 Ok(ds) => dataset = ds,
1424 Err(Error::DatasetNotFound { .. }) => break,
1425 Err(err) => return Err(err),
1426 }
1427 }
1428 }
1429
1430 Ok(transactions)
1431 }
1432
1433 pub async fn restore(&mut self) -> Result<()> {
1435 let (latest_manifest, _) = self.latest_manifest().await?;
1436 let latest_version = latest_manifest.version;
1437
1438 let transaction = Transaction::new(
1439 latest_version,
1440 Operation::Restore {
1441 version: self.manifest.version,
1442 },
1443 None,
1444 );
1445
1446 self.apply_commit(transaction, &Default::default(), &Default::default())
1447 .await?;
1448
1449 Ok(())
1450 }
1451
1452 #[instrument(level = "debug", skip(self))]
1473 pub fn cleanup_old_versions(
1474 &self,
1475 older_than: Duration,
1476 delete_unverified: Option<bool>,
1477 error_if_tagged_old_versions: Option<bool>,
1478 ) -> BoxFuture<'_, Result<RemovalStats>> {
1479 let mut builder = CleanupPolicyBuilder::default();
1480 builder = builder.before_timestamp(utc_now() - older_than);
1481 if let Some(v) = delete_unverified {
1482 builder = builder.delete_unverified(v);
1483 }
1484 if let Some(v) = error_if_tagged_old_versions {
1485 builder = builder.error_if_tagged_old_versions(v);
1486 }
1487
1488 self.cleanup_with_policy(builder.build())
1489 }
1490
1491 #[instrument(level = "debug", skip(self))]
1507 pub fn cleanup_with_policy(
1508 &self,
1509 policy: CleanupPolicy,
1510 ) -> BoxFuture<'_, Result<RemovalStats>> {
1511 async move { self.cleanup(policy).execute().await }.boxed()
1512 }
1513
1514 pub fn cleanup(&self, policy: CleanupPolicy) -> CleanupOperation<'_> {
1519 CleanupOperation::new(self, policy)
1520 }
1521
1522 #[allow(clippy::too_many_arguments)]
1523 async fn do_commit(
1524 base_uri: WriteDestination<'_>,
1525 operation: Operation,
1526 read_version: Option<u64>,
1527 store_params: Option<ObjectStoreParams>,
1528 commit_handler: Option<Arc<dyn CommitHandler>>,
1529 session: Arc<Session>,
1530 enable_v2_manifest_paths: bool,
1531 detached: bool,
1532 ) -> Result<Self> {
1533 let read_version = read_version.map_or_else(
1534 || match operation {
1535 Operation::Overwrite { .. } | Operation::Restore { .. } => Ok(0),
1536 _ => Err(Error::invalid_input(
1537 "read_version must be specified for this operation",
1538 )),
1539 },
1540 Ok,
1541 )?;
1542
1543 let transaction = Transaction::new(read_version, operation, None);
1544
1545 let mut builder = CommitBuilder::new(base_uri)
1546 .enable_v2_manifest_paths(enable_v2_manifest_paths)
1547 .with_session(session)
1548 .with_detached(detached);
1549
1550 if let Some(store_params) = store_params {
1551 builder = builder.with_store_params(store_params);
1552 }
1553
1554 if let Some(commit_handler) = commit_handler {
1555 builder = builder.with_commit_handler(commit_handler);
1556 }
1557
1558 builder.execute(transaction).await
1559 }
1560
1561 pub async fn commit(
1596 dest: impl Into<WriteDestination<'_>>,
1597 operation: Operation,
1598 read_version: Option<u64>,
1599 store_params: Option<ObjectStoreParams>,
1600 commit_handler: Option<Arc<dyn CommitHandler>>,
1601 session: Arc<Session>,
1602 enable_v2_manifest_paths: bool,
1603 ) -> Result<Self> {
1604 Self::do_commit(
1605 dest.into(),
1606 operation,
1607 read_version,
1608 store_params,
1609 commit_handler,
1610 session,
1611 enable_v2_manifest_paths,
1612 false,
1613 )
1614 .await
1615 }
1616
1617 pub async fn commit_detached(
1626 dest: impl Into<WriteDestination<'_>>,
1627 operation: Operation,
1628 read_version: Option<u64>,
1629 store_params: Option<ObjectStoreParams>,
1630 commit_handler: Option<Arc<dyn CommitHandler>>,
1631 session: Arc<Session>,
1632 enable_v2_manifest_paths: bool,
1633 ) -> Result<Self> {
1634 Self::do_commit(
1635 dest.into(),
1636 operation,
1637 read_version,
1638 store_params,
1639 commit_handler,
1640 session,
1641 enable_v2_manifest_paths,
1642 true,
1643 )
1644 .await
1645 }
1646
1647 pub(crate) async fn apply_commit(
1648 &mut self,
1649 transaction: Transaction,
1650 write_config: &ManifestWriteConfig,
1651 commit_config: &CommitConfig,
1652 ) -> Result<()> {
1653 let (manifest, manifest_location) = commit_transaction(
1654 self,
1655 self.object_store.as_ref(),
1656 self.commit_handler.as_ref(),
1657 &transaction,
1658 write_config,
1659 commit_config,
1660 DEFAULT_COMMIT_RETRY_TIMEOUT,
1661 self.manifest_location.naming_scheme,
1662 None,
1663 )
1664 .await?;
1665
1666 self.set_manifest(Arc::new(manifest), manifest_location);
1667
1668 Ok(())
1669 }
1670
1671 pub fn scan(&self) -> Scanner {
1673 Scanner::new(Arc::new(self.clone()))
1674 }
1675
1676 #[instrument(skip_all)]
1680 pub async fn count_rows(&self, filter: Option<String>) -> Result<usize> {
1681 if let Some(filter) = filter {
1683 let mut scanner = self.scan();
1684 scanner.filter(&filter)?;
1685 Ok(scanner
1686 .project::<String>(&[])?
1687 .with_row_id() .count_rows()
1689 .await? as usize)
1690 } else {
1691 self.count_all_rows().await
1692 }
1693 }
1694
1695 pub(crate) async fn count_all_rows(&self) -> Result<usize> {
1696 let cnts = stream::iter(self.get_fragments())
1697 .map(|f| async move { f.count_rows(None).await })
1698 .buffer_unordered(16)
1699 .try_collect::<Vec<_>>()
1700 .await?;
1701 Ok(cnts.iter().sum())
1702 }
1703
1704 #[instrument(skip_all, fields(num_rows=row_indices.len()))]
1706 pub async fn take(
1707 &self,
1708 row_indices: &[u64],
1709 projection: impl Into<ProjectionRequest>,
1710 ) -> Result<RecordBatch> {
1711 take::take(self, row_indices, projection.into()).await
1712 }
1713
1714 pub async fn take_rows(
1749 &self,
1750 row_ids: &[u64],
1751 projection: impl Into<ProjectionRequest>,
1752 ) -> Result<RecordBatch> {
1753 Arc::new(self.clone())
1754 .take_builder(row_ids, projection)?
1755 .execute()
1756 .await
1757 }
1758
1759 pub fn take_builder(
1760 self: &Arc<Self>,
1761 row_ids: &[u64],
1762 projection: impl Into<ProjectionRequest>,
1763 ) -> Result<TakeBuilder> {
1764 TakeBuilder::try_new_from_ids(self.clone(), row_ids.to_vec(), projection.into())
1765 }
1766
1767 pub async fn take_blobs(
1788 self: &Arc<Self>,
1789 row_ids: &[u64],
1790 column: impl AsRef<str>,
1791 ) -> Result<Vec<Option<BlobFile>>> {
1792 blob::take_blobs(self, row_ids, column.as_ref()).await
1793 }
1794
1795 pub async fn take_blobs_by_addresses(
1821 self: &Arc<Self>,
1822 row_addrs: &[u64],
1823 column: impl AsRef<str>,
1824 ) -> Result<Vec<Option<BlobFile>>> {
1825 blob::take_blobs_by_addresses(self, row_addrs, column.as_ref()).await
1826 }
1827
1828 pub async fn take_blobs_by_indices(
1848 self: &Arc<Self>,
1849 row_indices: &[u64],
1850 column: impl AsRef<str>,
1851 ) -> Result<Vec<Option<BlobFile>>> {
1852 let fragments = self.get_fragments();
1853 let row_addrs = row_offsets_to_row_addresses(&fragments, row_indices).await?;
1854 blob::take_blobs_by_addresses(self, &row_addrs, column.as_ref()).await
1855 }
1856
1857 pub fn read_blobs(self: &Arc<Self>, column: impl AsRef<str>) -> Result<ReadBlobsBuilder> {
1882 let column = column.as_ref();
1883 let blob_field_id = blob::validate_blob_column(self, column)?;
1884 Ok(ReadBlobsBuilder::new(
1885 self.clone(),
1886 column.to_string(),
1887 blob_field_id,
1888 ))
1889 }
1890
1891 pub fn read_blob_ranges(
1917 self: &Arc<Self>,
1918 column: impl AsRef<str>,
1919 ) -> Result<ReadBlobRangesBuilder> {
1920 Ok(ReadBlobRangesBuilder::new(self.read_blobs(column)?))
1921 }
1922
1923 pub fn take_scan(
1927 &self,
1928 row_ranges: Pin<Box<dyn Stream<Item = Result<Range<u64>>> + Send>>,
1929 projection: Arc<Schema>,
1930 batch_readahead: usize,
1931 ) -> DatasetRecordBatchStream {
1932 take::take_scan(self, row_ranges, projection, batch_readahead)
1933 }
1934
1935 pub async fn sample(
1943 &self,
1944 n: usize,
1945 projection: &Schema,
1946 fragment_ids: Option<&[u32]>,
1947 ) -> Result<RecordBatch> {
1948 use rand::seq::IteratorRandom;
1949
1950 match fragment_ids {
1951 None => {
1952 let num_rows = self.count_rows(None).await?;
1953 let mut ids = (0..num_rows as u64).choose_multiple(&mut rand::rng(), n);
1954 ids.sort_unstable();
1955 self.take(&ids, projection.clone()).await
1956 }
1957 Some(fragment_ids) => {
1958 if fragment_ids.is_empty() {
1959 return Err(Error::invalid_input(
1960 "Dataset::sample does not accept an empty fragment_ids list".to_string(),
1961 ));
1962 }
1963
1964 let selected_fragments = self.get_fragments_from_ids(fragment_ids)?;
1965
1966 let num_rows = stream::iter(selected_fragments.iter().cloned())
1967 .map(|fragment| async move { fragment.count_rows(None).await })
1968 .buffer_unordered(16)
1969 .try_fold(0_u64, |acc, rows| async move { Ok(acc + rows as u64) })
1970 .await?;
1971
1972 let mut offsets = (0..num_rows).choose_multiple(&mut rand::rng(), n);
1973 offsets.sort_unstable();
1974
1975 let row_addrs = row_offsets_to_row_addresses(&selected_fragments, &offsets).await?;
1976 let dataset = Arc::new(self.clone());
1977 let projection = Arc::new(
1978 ProjectionRequest::from(projection.clone())
1979 .into_projection_plan(dataset.clone())?,
1980 );
1981 TakeBuilder::try_new_from_addresses(dataset, row_addrs, projection)?
1982 .execute()
1983 .await
1984 }
1985 }
1986 }
1987
1988 pub async fn delete(&mut self, predicate: &str) -> Result<write::delete::DeleteResult> {
1990 info!(target: TRACE_DATASET_EVENTS, event=DATASET_DELETING_EVENT, uri = &self.uri, predicate=predicate);
1991 write::delete::delete(self, predicate).await
1992 }
1993
1994 pub async fn truncate_table(&mut self) -> Result<()> {
1996 self.delete("true").await.map(|_| ())
1997 }
1998
1999 pub async fn add_bases(
2015 self: &Arc<Self>,
2016 new_bases: Vec<lance_table::format::BasePath>,
2017 transaction_properties: Option<HashMap<String, String>>,
2018 ) -> Result<Self> {
2019 let operation = Operation::UpdateBases { new_bases };
2020
2021 let transaction = TransactionBuilder::new(self.manifest.version, operation)
2022 .transaction_properties(transaction_properties.map(Arc::new))
2023 .build();
2024
2025 let new_dataset = CommitBuilder::new(self.clone())
2026 .execute(transaction)
2027 .await?;
2028
2029 Ok(new_dataset)
2030 }
2031
2032 pub async fn count_deleted_rows(&self) -> Result<usize> {
2033 futures::stream::iter(self.get_fragments())
2034 .map(|f| async move { f.count_deletions().await })
2035 .buffer_unordered(self.object_store.io_parallelism())
2036 .try_fold(0, |acc, x| futures::future::ready(Ok(acc + x)))
2037 .await
2038 }
2039
2040 pub fn with_object_store(
2046 &self,
2047 object_store: Arc<ObjectStore>,
2048 store_params: Option<ObjectStoreParams>,
2049 ) -> Self {
2050 let mut cloned = self.clone();
2051 cloned.object_store = object_store;
2052 cloned.base_object_stores = Default::default();
2053 if let Some(store_params) = store_params {
2054 cloned.store_params = Some(Box::new(store_params));
2055 }
2056 cloned
2057 }
2058
2059 pub fn with_object_store_wrappers(
2065 &self,
2066 wrappers: impl IntoIterator<Item = Arc<dyn WrappingObjectStore>>,
2067 ) -> Self {
2068 let wrappers = wrappers.into_iter().collect::<Vec<_>>();
2069 if wrappers.is_empty() {
2070 return self.clone();
2071 }
2072
2073 let mut cloned = self.clone();
2074 cloned.base_object_stores = Default::default();
2078 let mut object_store = self.object_store.as_ref().clone();
2079 for wrapper in &wrappers {
2080 object_store.inner =
2081 wrapper.wrap(&object_store.store_prefix, object_store.inner.clone());
2082 }
2083 cloned.object_store = Arc::new(object_store);
2084 cloned.refs = Refs::new(
2085 cloned.object_store.clone(),
2086 cloned.commit_handler.clone(),
2087 cloned.branch_location(),
2088 );
2089
2090 let store_params = self.store_params.as_deref().cloned().unwrap_or_default();
2091 cloned.store_params = Some(Box::new(Self::append_object_store_wrappers(
2092 store_params,
2093 &wrappers,
2094 )));
2095 cloned.base_store_params = self.base_store_params.as_ref().map(|base_store_params| {
2096 Arc::new(
2097 base_store_params
2098 .iter()
2099 .map(|(base_path, store_params)| {
2100 (
2101 base_path.clone(),
2102 Self::append_object_store_wrappers(store_params.clone(), &wrappers),
2103 )
2104 })
2105 .collect(),
2106 )
2107 });
2108 cloned
2109 }
2110
2111 fn append_object_store_wrappers(
2112 mut store_params: ObjectStoreParams,
2113 wrappers: &[Arc<dyn WrappingObjectStore>],
2114 ) -> ObjectStoreParams {
2115 let mut all_wrappers = Vec::with_capacity(
2116 store_params.object_store_wrapper.as_ref().map_or(0, |_| 1) + wrappers.len(),
2117 );
2118 if let Some(wrapper) = store_params.object_store_wrapper.take() {
2119 all_wrappers.push(wrapper);
2120 }
2121 all_wrappers.extend(wrappers.iter().cloned());
2122 store_params.object_store_wrapper = match all_wrappers.len() {
2123 0 => None,
2124 1 => all_wrappers.pop(),
2125 _ => Some(Arc::new(ChainedWrappingObjectStore::new(all_wrappers))),
2126 };
2127 store_params
2128 }
2129
2130 pub(crate) fn store_params_for_base(
2131 &self,
2132 base_path: Option<&lance_table::format::BasePath>,
2133 ) -> ObjectStoreParams {
2134 if let Some(params) = base_path.and_then(|base_path| {
2139 self.base_store_params
2140 .as_ref()
2141 .and_then(|params| params.get(&base_path.path))
2142 }) {
2143 return params.clone();
2144 }
2145 let default_params = self.store_params.as_deref().cloned().unwrap_or_default();
2146 match default_params.scoped_to_base(base_path.map(|base_path| base_path.id)) {
2147 Cow::Owned(scoped_params) => scoped_params,
2148 Cow::Borrowed(_) => default_params,
2149 }
2150 }
2151
2152 #[deprecated(since = "0.25.0", note = "Use initial_storage_options() instead")]
2157 pub fn storage_options(&self) -> Option<&HashMap<String, String>> {
2158 self.initial_storage_options()
2159 }
2160
2161 pub fn initial_storage_options(&self) -> Option<&HashMap<String, String>> {
2165 self.store_params
2166 .as_ref()
2167 .and_then(|params| params.storage_options())
2168 }
2169
2170 pub fn storage_options_provider(
2172 &self,
2173 ) -> Option<Arc<dyn lance_io::object_store::StorageOptionsProvider>> {
2174 self.store_params
2175 .as_ref()
2176 .and_then(|params| params.storage_options_accessor.as_ref())
2177 .and_then(|accessor| accessor.provider().cloned())
2178 }
2179
2180 pub fn storage_options_accessor(&self) -> Option<Arc<StorageOptionsAccessor>> {
2186 self.store_params
2187 .as_ref()
2188 .and_then(|params| params.get_accessor())
2189 }
2190
2191 pub async fn latest_storage_options(&self) -> Result<Option<StorageOptions>> {
2204 if let Some(accessor) = self.storage_options_accessor() {
2206 let options = accessor.get_storage_options().await?;
2207 return Ok(Some(options));
2208 }
2209
2210 Ok(self.initial_storage_options().cloned().map(StorageOptions))
2212 }
2213
2214 pub fn data_dir(&self) -> Path {
2215 self.base.clone().join(DATA_DIR)
2216 }
2217
2218 pub fn indices_dir(&self) -> Path {
2219 self.base.clone().join(INDICES_DIR)
2220 }
2221
2222 pub fn transactions_dir(&self) -> Path {
2223 self.base.clone().join(TRANSACTIONS_DIR)
2224 }
2225
2226 pub fn deletions_dir(&self) -> Path {
2227 self.base.clone().join(DELETIONS_DIR)
2228 }
2229
2230 pub fn versions_dir(&self) -> Path {
2231 self.base.clone().join(VERSIONS_DIR)
2232 }
2233
2234 pub(crate) fn data_file_dir(&self, data_file: &DataFile) -> Result<Path> {
2235 self.data_file_dir_for_base(data_file.base_id)
2236 }
2237
2238 pub async fn create_data_file(&self, path: &str, base_id: Option<u32>) -> Result<DataFile> {
2250 let data_dir = self.data_file_dir_for_base(base_id)?;
2251 let filepath = data_dir.clone().join(path);
2252
2253 let object_store = self.object_store(base_id).await?;
2254
2255 let file_size = object_store.size(&filepath).await?;
2257
2258 let scheduler = ScanScheduler::new(
2260 object_store.clone(),
2261 SchedulerConfig::new(2 * 1024 * 1024 * 1024),
2262 );
2263 let file = scheduler
2264 .open_file(&filepath, &CachedFileSize::new(file_size))
2265 .await?;
2266 let file_metadata = FileReader::read_all_metadata(&file).await?;
2267
2268 let lance_file_format = file_metadata.version;
2269 let physical_columns = file_metadata.column_metadatas.len();
2270 let has_footer_orphans = file_metadata.file_schema.fields.len() > physical_columns;
2271 let dataset_schema = self.schema();
2272 let mut represented_columns = 0usize;
2273 let mut column_names = Vec::new();
2274 let mut consumed_top_level_fields = 0usize;
2275
2276 fn field_contains_blob(field: &lance_core::datatypes::Field) -> bool {
2277 field.is_blob() || field.children.iter().any(field_contains_blob)
2278 }
2279
2280 fn field_names_match(
2281 fields: &[lance_core::datatypes::Field],
2282 start: usize,
2283 expected: &arrow_schema::Fields,
2284 ) -> bool {
2285 fields
2286 .get(start..start + expected.len())
2287 .is_some_and(|candidate| {
2288 candidate
2289 .iter()
2290 .zip(expected.iter())
2291 .all(|(field, expected)| field.name == expected.name().as_str())
2292 })
2293 }
2294
2295 fn blob_descriptor_orphan_len(
2296 fields: &[lance_core::datatypes::Field],
2297 start: usize,
2298 ) -> usize {
2299 if field_names_match(fields, start, &lance_core::datatypes::BLOB_V2_DESC_FIELDS) {
2300 lance_core::datatypes::BLOB_V2_DESC_FIELDS.len()
2301 } else if field_names_match(fields, start, &lance_core::datatypes::BLOB_DESC_FIELDS) {
2302 lance_core::datatypes::BLOB_DESC_FIELDS.len()
2303 } else {
2304 0
2305 }
2306 }
2307
2308 fn validate_file_field_matches_dataset(
2309 dataset_field: &lance_core::datatypes::Field,
2310 file_field: &lance_core::datatypes::Field,
2311 path: &str,
2312 ) -> Result<()> {
2313 if dataset_field.name != file_field.name {
2314 return Err(Error::invalid_input(format!(
2315 "Schema mismatch: expected field '{}' but file has '{}'",
2316 path, file_field.name
2317 )));
2318 }
2319
2320 if dataset_field.is_blob() && file_field.is_blob() {
2321 return Ok(());
2322 }
2323
2324 if dataset_field.children.len() != file_field.children.len() {
2325 return Err(Error::invalid_input(format!(
2326 "Schema mismatch: field '{}' has {} children in dataset schema but {} children in file schema",
2327 path,
2328 dataset_field.children.len(),
2329 file_field.children.len()
2330 )));
2331 }
2332
2333 for (dataset_child, file_child) in
2334 dataset_field.children.iter().zip(&file_field.children)
2335 {
2336 let child_path = format!("{}.{}", path, dataset_child.name);
2337 validate_file_field_matches_dataset(dataset_child, file_child, &child_path)?;
2338 }
2339
2340 Ok(())
2341 }
2342
2343 let file_schema_fields = &file_metadata.file_schema.fields;
2344 let mut idx = 0usize;
2345 while represented_columns < physical_columns {
2346 let Some(field) = file_schema_fields.get(idx) else {
2347 return Err(Error::invalid_input(format!(
2348 "Schema mismatch: file schema ended after representing {} physical columns but file has {} columns",
2349 represented_columns, physical_columns
2350 )));
2351 };
2352
2353 let Some(dataset_field) = dataset_schema.field(&field.name) else {
2354 return Err(Error::invalid_input(format!(
2355 "Schema mismatch: file has extra field '{}'",
2356 field.name
2357 )));
2358 };
2359 validate_file_field_matches_dataset(dataset_field, field, &field.name)?;
2360
2361 represented_columns += file_versions::physical_column_count(lance_file_format, field);
2362 column_names.push(field.name.as_str());
2363 consumed_top_level_fields = idx + 1;
2364 idx += 1;
2365
2366 if has_footer_orphans && field_contains_blob(field) {
2367 loop {
2368 let skipped = blob_descriptor_orphan_len(file_schema_fields, idx);
2369 if skipped == 0 {
2370 break;
2371 }
2372 consumed_top_level_fields = idx + skipped;
2373 idx += skipped;
2374 }
2375 }
2376 }
2377
2378 if represented_columns != physical_columns {
2379 return Err(Error::invalid_input(format!(
2380 "Schema mismatch: file schema represents {} physical columns but file has {} columns",
2381 represented_columns, physical_columns
2382 )));
2383 }
2384
2385 if let Some(field) = file_schema_fields.get(consumed_top_level_fields) {
2386 return Err(Error::invalid_input(format!(
2387 "Schema mismatch: file has extra field '{}'",
2388 field.name
2389 )));
2390 }
2391
2392 let projected_ds_schema = self.schema().project(&column_names)?;
2393
2394 let (fields, column_indices) =
2395 file_versions::data_file_columns(lance_file_format, &projected_ds_schema);
2396 let represented_dataset_columns = column_indices
2397 .iter()
2398 .filter(|column_index| **column_index >= 0)
2399 .count();
2400
2401 if represented_dataset_columns != physical_columns {
2402 return Err(Error::invalid_input(format!(
2403 "Schema mismatch: dataset projection maps to {} physical columns but file has {} columns",
2404 represented_dataset_columns, physical_columns
2405 )));
2406 }
2407
2408 if fields.is_empty() && physical_columns > 0 {
2409 return Err(Error::invalid_input(
2410 "Schema mismatch: file has columns but none matched the dataset schema",
2411 ));
2412 }
2413
2414 let file_size_nz = NonZero::new(file_size);
2415 Ok(DataFile::new(
2416 path,
2417 fields,
2418 column_indices,
2419 lance_file_format,
2420 file_size_nz,
2421 base_id,
2422 ))
2423 }
2424
2425 pub(crate) fn data_file_dir_for_base(&self, base_id: Option<u32>) -> Result<Path> {
2429 match base_id {
2430 Some(base_id) => {
2431 let base_path = self.manifest.base_paths.get(&base_id).ok_or_else(|| {
2432 Error::invalid_input(format!("base_path id {} not found", base_id))
2433 })?;
2434 let path = base_path.extract_path(self.session.store_registry())?;
2435 if base_path.is_dataset_root {
2436 Ok(path.join(DATA_DIR))
2437 } else {
2438 Ok(path)
2439 }
2440 }
2441 None => Ok(self.base.clone().join(DATA_DIR)),
2442 }
2443 }
2444
2445 async fn base_object_store(&self, base_id: u32) -> Result<Arc<ObjectStore>> {
2446 let base_path = self.manifest.base_paths.get(&base_id).ok_or_else(|| {
2447 Error::invalid_input(format!("Dataset base path with ID {} not found", base_id))
2448 })?;
2449 let store_params = self.store_params_for_base(Some(base_path));
2450
2451 let cell = {
2452 let mut stores = self.base_object_stores.lock().unwrap();
2453 stores.entry(base_id).or_default().clone()
2454 };
2455 let store = cell
2456 .get_or_try_init(|| async {
2457 let (store, _) = if store_params.object_store_wrapper.is_some() {
2462 ObjectStore::from_uri_and_params_uncached(
2463 self.session.store_registry(),
2464 &base_path.path,
2465 &store_params,
2466 )
2467 .await?
2468 } else {
2469 ObjectStore::from_uri_and_params(
2470 self.session.store_registry(),
2471 &base_path.path,
2472 &store_params,
2473 )
2474 .await?
2475 };
2476 Ok::<_, Error>(store)
2477 })
2478 .await?;
2479 Ok(store.clone())
2480 }
2481
2482 pub async fn object_store(&self, base_id: Option<u32>) -> Result<Arc<ObjectStore>> {
2487 match base_id {
2488 Some(base_id) => self.base_object_store(base_id).await,
2489 None => Ok(self.object_store.clone()),
2490 }
2491 }
2492
2493 pub fn store_params(&self) -> Option<&ObjectStoreParams> {
2497 self.store_params.as_deref()
2498 }
2499
2500 pub(crate) async fn object_store_for_data_file(
2501 &self,
2502 data_file: &DataFile,
2503 ) -> Result<Arc<ObjectStore>> {
2504 self.object_store(data_file.base_id).await
2505 }
2506
2507 pub(crate) async fn object_store_for_deletion(
2508 &self,
2509 deletion_file: &DeletionFile,
2510 ) -> Result<Arc<ObjectStore>> {
2511 self.object_store(deletion_file.base_id).await
2512 }
2513
2514 pub(crate) async fn object_store_for_index(
2515 &self,
2516 index: &IndexMetadata,
2517 ) -> Result<Arc<ObjectStore>> {
2518 self.object_store(index.base_id).await
2519 }
2520
2521 pub(crate) fn dataset_dir_for_deletion(&self, deletion_file: &DeletionFile) -> Result<Path> {
2522 match deletion_file.base_id.as_ref() {
2523 Some(base_id) => {
2524 let base_paths = &self.manifest.base_paths;
2525 let base_path = base_paths.get(base_id).ok_or_else(|| {
2526 Error::invalid_input(format!(
2527 "base_path id {} not found for deletion_file {:?}",
2528 base_id, deletion_file
2529 ))
2530 })?;
2531
2532 if !base_path.is_dataset_root {
2533 return Err(Error::internal(format!(
2534 "base_path id {} is not a dataset root for deletion_file {:?}",
2535 base_id, deletion_file
2536 )));
2537 }
2538 base_path.extract_path(self.session.store_registry())
2539 }
2540 None => Ok(self.base.clone()),
2541 }
2542 }
2543
2544 pub(crate) fn indice_files_dir(&self, index: &IndexMetadata) -> Result<Path> {
2546 match index.base_id.as_ref() {
2547 Some(base_id) => {
2548 let base_paths = &self.manifest.base_paths;
2549 let base_path = base_paths.get(base_id).ok_or_else(|| {
2550 Error::invalid_input(format!(
2551 "base_path id {} not found for index {}",
2552 base_id, index.uuid
2553 ))
2554 })?;
2555 let path = base_path.extract_path(self.session.store_registry())?;
2556 if base_path.is_dataset_root {
2557 Ok(path.join(INDICES_DIR))
2558 } else {
2559 Ok(path)
2561 }
2562 }
2563 None => Ok(self.base.clone().join(INDICES_DIR)),
2564 }
2565 }
2566
2567 pub fn session(&self) -> Arc<Session> {
2568 self.session.clone()
2569 }
2570
2571 pub fn version_id(&self) -> u64 {
2576 self.manifest.version
2577 }
2578
2579 pub fn version(&self) -> Version {
2584 Version::from(self.manifest.as_ref())
2585 }
2586
2587 pub async fn index_cache_entry_count(&self) -> usize {
2589 self.session.index_cache.size().await
2590 }
2591
2592 pub async fn index_cache_hit_rate(&self) -> f32 {
2594 let stats = self.session.index_cache_stats().await;
2595 stats.hit_ratio()
2596 }
2597
2598 pub fn cache_size_bytes(&self) -> u64 {
2599 self.session.deep_size_of() as u64
2600 }
2601
2602 pub async fn versions(&self) -> Result<Vec<Version>> {
2604 let mut versions: Vec<Version> = self
2605 .commit_handler
2606 .list_manifest_locations(&self.base, &self.object_store, false)
2607 .try_filter_map(|location| async move {
2608 match read_manifest(&self.object_store, &location.path, location.size).await {
2609 Ok(manifest) => Ok(Some(Version::from(&manifest))),
2610 Err(e) => Err(e),
2611 }
2612 })
2613 .try_collect()
2614 .await?;
2615
2616 versions.sort_by_key(|v| v.version);
2618
2619 Ok(versions)
2620 }
2621
2622 pub async fn count_versions(&self) -> Result<u64> {
2627 self.commit_handler
2628 .list_manifest_locations(&self.base, &self.object_store, false)
2629 .try_fold(0_u64, |count, _| async move { Ok(count + 1) })
2630 .await
2631 }
2632
2633 pub async fn version_refs(&self) -> Result<Vec<VersionRef>> {
2641 let mut versions: Vec<_> = self
2642 .commit_handler
2643 .list_manifest_locations(&self.base, &self.object_store, false)
2644 .map_ok(|location| VersionRef {
2645 version: location.version,
2646 })
2647 .try_collect()
2648 .await?;
2649 versions.sort_unstable_by_key(|version| version.version);
2650 Ok(versions)
2651 }
2652
2653 pub async fn list_detached_manifests(&self) -> Result<Vec<ManifestLocation>> {
2668 self.commit_handler
2669 .list_detached_manifest_locations(&self.base, &self.object_store)
2670 .try_collect()
2671 .await
2672 }
2673
2674 pub async fn latest_version_id(&self) -> Result<u64> {
2678 Ok(self
2679 .commit_handler
2680 .resolve_latest_location(&self.base, &self.object_store)
2681 .await?
2682 .version)
2683 }
2684
2685 pub async fn is_stale(&self) -> Result<bool> {
2687 let latest_version = self.latest_version_id().await?;
2688 Ok(latest_version != self.manifest.version)
2689 }
2690
2691 #[doc(hidden)]
2698 pub async fn has_successor_version(&self) -> Result<bool> {
2699 let Some(next_version) = self.manifest.version.checked_add(1) else {
2700 return Ok(false);
2701 };
2702 if lance_table::format::is_detached_version(next_version) {
2703 return Ok(false);
2704 }
2705
2706 let exists = self
2707 .commit_handler
2708 .version_exists(
2709 &self.base,
2710 next_version,
2711 self.object_store.inner.as_ref(),
2712 self.manifest_location.naming_scheme,
2713 )
2714 .await?;
2715 Ok(exists)
2716 }
2717
2718 pub fn count_fragments(&self) -> usize {
2719 self.manifest.fragments.len()
2720 }
2721
2722 pub fn schema(&self) -> &Schema {
2724 &self.manifest.schema
2725 }
2726
2727 pub fn empty_projection(self: &Arc<Self>) -> Projection {
2730 Projection::empty(self.clone())
2731 }
2732
2733 pub fn full_projection(self: &Arc<Self>) -> Projection {
2735 Projection::full(self.clone())
2736 }
2737
2738 pub fn get_fragments(&self) -> Vec<FileFragment> {
2740 let dataset = Arc::new(self.clone());
2741 self.manifest
2742 .fragments
2743 .iter()
2744 .map(|f| FileFragment::new(dataset.clone(), f.clone()))
2745 .collect()
2746 }
2747
2748 pub fn iter_fragments(&self) -> impl Iterator<Item = &Fragment> {
2750 self.manifest.fragments.iter()
2751 }
2752
2753 pub fn get_fragment(&self, fragment_id: usize) -> Option<FileFragment> {
2754 let metadata = self.find_fragment(fragment_id as u64)?.clone();
2755 Some(FileFragment::new(Arc::new(self.clone()), metadata))
2756 }
2757
2758 pub fn fragments(&self) -> &Arc<Vec<Fragment>> {
2759 &self.manifest.fragments
2760 }
2761
2762 pub(crate) fn normalize_fragment_ids(fragment_ids: &[u32]) -> Vec<u32> {
2763 let mut ids = fragment_ids.to_vec();
2764 ids.sort_unstable();
2765 ids.dedup();
2766 ids
2767 }
2768
2769 pub(crate) fn get_fragments_from_ids(&self, fragment_ids: &[u32]) -> Result<Vec<FileFragment>> {
2770 let ordered_ids = Self::normalize_fragment_ids(fragment_ids);
2771 let fragments = self.get_frags_from_ordered_ids(&ordered_ids);
2772 if let Some(missing_id) = fragments
2773 .iter()
2774 .zip(ordered_ids.iter())
2775 .find_map(|(fragment, fragment_id)| fragment.is_none().then_some(*fragment_id))
2776 {
2777 return Err(Error::invalid_input(format!(
2778 "Unknown fragment id {missing_id} in fragment filter; not part of the current dataset version"
2779 )));
2780 }
2781
2782 Ok(fragments.into_iter().flatten().collect())
2783 }
2784
2785 pub(crate) fn get_existing_fragments_from_ids(
2786 &self,
2787 fragment_ids: &[u32],
2788 ) -> Vec<FileFragment> {
2789 let ordered_ids = Self::normalize_fragment_ids(fragment_ids);
2790 self.get_frags_from_ordered_ids(&ordered_ids)
2791 .into_iter()
2792 .flatten()
2793 .collect()
2794 }
2795
2796 pub(crate) fn get_fragment_metadata_from_ids(
2797 &self,
2798 fragment_ids: &[u32],
2799 ) -> Result<Vec<Fragment>> {
2800 Ok(self
2801 .get_fragments_from_ids(fragment_ids)?
2802 .into_iter()
2803 .map(|fragment| fragment.metadata().clone())
2804 .collect())
2805 }
2806
2807 pub(crate) fn get_existing_fragment_metadata_from_ids(
2808 &self,
2809 fragment_ids: &[u32],
2810 ) -> Vec<Fragment> {
2811 self.get_existing_fragments_from_ids(fragment_ids)
2812 .into_iter()
2813 .map(|fragment| fragment.metadata().clone())
2814 .collect()
2815 }
2816
2817 pub(crate) async fn count_rows_in_fragments(&self, fragment_ids: &[u32]) -> Result<usize> {
2818 let fragments = self.get_fragments_from_ids(fragment_ids)?;
2819 self.count_rows_in_resolved_fragments(fragments).await
2820 }
2821
2822 pub(crate) async fn count_rows_in_existing_fragments(
2823 &self,
2824 fragment_ids: &[u32],
2825 ) -> Result<usize> {
2826 let fragments = self.get_existing_fragments_from_ids(fragment_ids);
2827 self.count_rows_in_resolved_fragments(fragments).await
2828 }
2829
2830 async fn count_rows_in_resolved_fragments(
2831 &self,
2832 fragments: Vec<FileFragment>,
2833 ) -> Result<usize> {
2834 let counts = stream::iter(fragments)
2835 .map(|fragment| async move { fragment.count_rows(None).await })
2836 .buffer_unordered(16)
2837 .try_collect::<Vec<_>>()
2838 .await?;
2839 Ok(counts.iter().sum())
2840 }
2841
2842 pub fn get_frags_from_ordered_ids(&self, ordered_ids: &[u32]) -> Vec<Option<FileFragment>> {
2847 let dataset = Arc::new(self.clone());
2848 ordered_ids
2849 .iter()
2850 .map(|id| {
2851 if !self.fragment_bitmap.contains(*id) {
2852 return None;
2853 }
2854 let fragment_index = self.fragment_bitmap.rank(*id) as usize - 1;
2855 let fragment = self.manifest.fragments.get(fragment_index)?;
2856 debug_assert_eq!(
2857 fragment.id, *id as u64,
2858 "fragment_bitmap rank({id}) resolved to fragment {}, but fragment_bitmap and manifest.fragments are expected to stay in sync",
2859 fragment.id
2860 );
2861 Some(FileFragment::new(dataset.clone(), fragment.clone()))
2862 })
2863 .collect()
2864 }
2865
2866 fn find_fragment(&self, id: u64) -> Option<&Fragment> {
2876 if !u32::try_from(id).is_ok_and(|id| self.fragment_bitmap.contains(id)) {
2877 return None;
2878 }
2879 let fragments = self.manifest.fragments.as_slice();
2880 let index = fragments.partition_point(|fragment| fragment.id < id);
2881 match fragments.get(index) {
2882 Some(fragment) if fragment.id == id => Some(fragment),
2883 _ => fragments.iter().find(|fragment| fragment.id == id),
2884 }
2885 }
2886
2887 async fn filter_addr_or_ids(&self, addr_or_ids: &[u64], addrs: &[u64]) -> Result<Vec<u64>> {
2889 if addr_or_ids.len() != addrs.len() {
2892 return Err(Error::internal(format!(
2893 "filter_addr_or_ids: addr_or_ids has {} entries but addrs has {}",
2894 addr_or_ids.len(),
2895 addrs.len()
2896 )));
2897 }
2898 if addrs.is_empty() {
2899 return Ok(Vec::new());
2900 }
2901
2902 let mut perm = permutation::sort(addrs);
2903 let sorted_addrs = perm.apply_slice(addrs);
2906
2907 let referenced_frag_ids = sorted_addrs
2909 .iter()
2910 .map(|addr| RowAddress::from(*addr).fragment_id())
2911 .dedup()
2912 .collect::<Vec<_>>();
2913 let frags = self.get_frags_from_ordered_ids(&referenced_frag_ids);
2914 let dv_futs = frags
2915 .iter()
2916 .map(|frag| {
2917 if let Some(frag) = frag {
2918 frag.get_deletion_vector().boxed()
2919 } else {
2920 std::future::ready(Ok(None)).boxed()
2921 }
2922 })
2923 .collect::<Vec<_>>();
2924 let dvs = stream::iter(dv_futs)
2925 .buffered(self.object_store.io_parallelism())
2926 .try_collect::<Vec<_>>()
2927 .await?;
2928
2929 let mut filtered_sorted_addrs = Vec::with_capacity(sorted_addrs.len());
2932 let mut sorted_addr_iter = sorted_addrs.into_iter().map(RowAddress::from);
2933 let mut next_addr = sorted_addr_iter.next().unwrap();
2934 let mut exhausted = false;
2935
2936 for frag_dv in frags.iter().zip(dvs).zip(referenced_frag_ids.iter()) {
2937 let ((frag, dv), frag_id) = frag_dv;
2938 if frag.is_some() {
2939 if let Some(dv) = dv.as_ref() {
2941 for deleted in dv.to_sorted_iter() {
2943 while next_addr.fragment_id() == *frag_id
2944 && next_addr.row_offset() < deleted
2945 {
2946 filtered_sorted_addrs.push(Some(u64::from(next_addr)));
2947 if let Some(next) = sorted_addr_iter.next() {
2948 next_addr = next;
2949 } else {
2950 exhausted = true;
2951 break;
2952 }
2953 }
2954 if exhausted {
2955 break;
2956 }
2957 if next_addr.fragment_id() != *frag_id {
2958 break;
2959 }
2960 if next_addr.row_offset() == deleted {
2961 filtered_sorted_addrs.push(None);
2962 if let Some(next) = sorted_addr_iter.next() {
2963 next_addr = next;
2964 } else {
2965 exhausted = true;
2966 break;
2967 }
2968 }
2969 }
2970 }
2971 if exhausted {
2972 break;
2973 }
2974 while next_addr.fragment_id() == *frag_id {
2977 filtered_sorted_addrs.push(Some(u64::from(next_addr)));
2978 if let Some(next) = sorted_addr_iter.next() {
2979 next_addr = next;
2980 } else {
2981 break;
2982 }
2983 }
2984 } else {
2985 while next_addr.fragment_id() == *frag_id {
2987 filtered_sorted_addrs.push(None);
2988 if let Some(next) = sorted_addr_iter.next() {
2989 next_addr = next;
2990 } else {
2991 break;
2992 }
2993 }
2994 }
2995 }
2996
2997 perm.apply_inv_slice_in_place(&mut filtered_sorted_addrs);
3001 Ok(addr_or_ids
3002 .iter()
3003 .zip(filtered_sorted_addrs)
3004 .filter_map(|(addr_or_id, maybe_addr)| maybe_addr.map(|_| *addr_or_id))
3005 .collect())
3006 }
3007
3008 pub(crate) async fn filter_deleted_ids(&self, ids: &[u64]) -> Result<Vec<u64>> {
3009 let (ids, addresses) = if let Some(row_id_index) = get_row_id_index(self).await? {
3010 let mut live_ids = Vec::with_capacity(ids.len());
3014 let mut addresses = Vec::with_capacity(ids.len());
3015 for id in ids {
3016 if let Some(address) = row_id_index.get(*id) {
3017 live_ids.push(*id);
3018 addresses.push(u64::from(address));
3019 }
3020 }
3021 (Cow::Owned(live_ids), Cow::Owned(addresses))
3022 } else {
3023 (Cow::Borrowed(ids), Cow::Borrowed(ids))
3024 };
3025
3026 self.filter_addr_or_ids(&ids, &addresses).await
3027 }
3028
3029 pub async fn num_small_files(&self, max_rows_per_group: usize) -> usize {
3034 futures::stream::iter(self.get_fragments())
3035 .map(|f| async move { f.physical_rows().await })
3036 .buffered(self.object_store.io_parallelism())
3037 .try_filter(|row_count| futures::future::ready(*row_count < max_rows_per_group))
3038 .count()
3039 .await
3040 }
3041
3042 pub async fn validate(&self) -> Result<()> {
3043 let id_counts =
3045 self.manifest
3046 .fragments
3047 .iter()
3048 .map(|f| f.id)
3049 .fold(HashMap::new(), |mut acc, id| {
3050 *acc.entry(id).or_insert(0) += 1;
3051 acc
3052 });
3053 for (id, count) in id_counts {
3054 if count > 1 {
3055 return Err(Error::corrupt_file(
3056 self.base.clone(),
3057 format!(
3058 "Duplicate fragment id {} found in dataset {:?}",
3059 id, self.base
3060 ),
3061 ));
3062 }
3063 }
3064
3065 self.manifest
3067 .fragments
3068 .iter()
3069 .map(|f| f.id)
3070 .try_fold(0, |prev, id| {
3071 if id < prev {
3072 Err(Error::corrupt_file(self.base.clone(), format!(
3073 "Fragment ids are not sorted in increasing fragment-id order. Found {} after {} in dataset {:?}",
3074 id, prev, self.base
3075 )))
3076 } else {
3077 Ok(id)
3078 }
3079 })?;
3080
3081 futures::stream::iter(self.get_fragments())
3083 .map(|f| async move { f.validate().await })
3084 .buffer_unordered(self.object_store.io_parallelism())
3085 .try_collect::<Vec<()>>()
3086 .await?;
3087
3088 rowids::validate_stable_row_ids(self).await?;
3089
3090 let indices = self.load_indices().await?;
3092 self.validate_indices(&indices)?;
3093
3094 Ok(())
3095 }
3096
3097 fn validate_indices(&self, indices: &[IndexMetadata]) -> Result<()> {
3098 let mut index_ids = HashSet::new();
3100 for index in indices.iter() {
3101 if !index_ids.insert(&index.uuid) {
3102 return Err(Error::corrupt_file(
3103 self.manifest_location.path.clone(),
3104 format!(
3105 "Duplicate index id {} found in dataset {:?}",
3106 index.uuid, self.base
3107 ),
3108 ));
3109 }
3110 }
3111
3112 if let Err(err) = detect_overlapping_fragments(indices) {
3114 let mut message = "Overlapping fragments detected in dataset.".to_string();
3115 for (index_name, overlapping_frags) in err.bad_indices {
3116 message.push_str(&format!(
3117 "\nIndex {:?} has overlapping fragments: {:?}",
3118 index_name, overlapping_frags
3119 ));
3120 }
3121 return Err(Error::corrupt_file(
3122 self.manifest_location.path.clone(),
3123 message,
3124 ));
3125 };
3126
3127 Ok(())
3128 }
3129
3130 pub async fn migrate_manifest_paths_v2(&mut self) -> Result<()> {
3165 migrate_scheme_to_v2(self.object_store.as_ref(), &self.base).await?;
3166 let latest_version = self.latest_version_id().await?;
3168 *self = self.checkout_version(latest_version).await?;
3169 Ok(())
3170 }
3171
3172 fn assign_stable_row_ids_for_migration(fragments: &mut [Fragment], start: u64) -> Result<u64> {
3176 let mut next_row_id = start;
3177 for fragment in fragments.iter_mut() {
3178 let physical_rows = fragment.physical_rows.ok_or_else(|| {
3179 Error::internal(format!(
3180 "Fragment {} is missing physical_rows; cannot assign stable row IDs",
3181 fragment.id
3182 ))
3183 })? as u64;
3184 let end = next_row_id
3185 .checked_add(physical_rows)
3186 .ok_or_else(|| Error::internal("Row ID overflow during stable row ID migration"))?;
3187 let sequence = RowIdSequence::from(next_row_id..end);
3188 fragment.row_id_meta = Some(RowIdMeta::Inline(write_row_ids(&sequence).into()));
3189 next_row_id = end;
3190 }
3191 Ok(next_row_id)
3192 }
3193
3194 pub async fn migrate_to_stable_row_ids(&mut self) -> Result<()> {
3213 if self.manifest.uses_stable_row_ids() {
3214 return Ok(());
3215 }
3216
3217 let mut fragments = self.manifest.fragments.as_ref().clone();
3218 let next_row_id =
3222 Self::assign_stable_row_ids_for_migration(&mut fragments, self.manifest.next_row_id)?;
3223 let schema = self.manifest.schema.clone();
3224 let read_version = self.manifest.version;
3225
3226 let transaction = Transaction::new(
3227 read_version,
3228 Operation::Merge {
3229 fragments,
3230 schema,
3231 preserves_nullability: true,
3232 },
3233 None,
3234 );
3235
3236 let new_ds = CommitBuilder::new(Arc::new(self.clone()))
3237 .with_max_retries(0)
3238 .with_stable_row_id_migration_activation(next_row_id)
3239 .execute(transaction)
3240 .await?;
3241
3242 *self = new_ds;
3243 Ok(())
3244 }
3245
3246 pub async fn shallow_clone(
3251 &mut self,
3252 target_path: &str,
3253 version: impl Into<refs::Ref>,
3254 store_params: Option<ObjectStoreParams>,
3255 ) -> Result<Self> {
3256 let (ref_name, version_number) = self.resolve_reference(version.into()).await?;
3257 let source_location = self.branch_location().find_branch(ref_name.as_deref())?;
3258 let clone_op = Operation::Clone {
3259 is_shallow: true,
3260 ref_name,
3261 ref_version: version_number,
3262 ref_path: source_location.uri,
3263 branch_name: None,
3264 };
3265 let transaction = Transaction::new(version_number, clone_op, None);
3266
3267 let builder = CommitBuilder::new(WriteDestination::Uri(target_path))
3268 .with_store_params(
3269 store_params.unwrap_or(self.store_params.as_deref().cloned().unwrap_or_default()),
3270 )
3271 .with_object_store(Arc::new(self.object_store.as_ref().clone()))
3272 .with_commit_handler(self.commit_handler.clone())
3273 .with_exact_storage_format(self.manifest.data_storage_format.lance_file_format());
3274 builder.execute(transaction).await
3275 }
3276
3277 pub async fn deep_clone(
3297 &mut self,
3298 target_path: &str,
3299 version: impl Into<refs::Ref>,
3300 store_params: Option<ObjectStoreParams>,
3301 ) -> Result<Self> {
3302 use futures::StreamExt;
3303
3304 let src_ds = self.checkout_version(version).await?;
3306 let src_paths = src_ds.collect_paths().await?;
3307
3308 let (target_store, target_base) = ObjectStore::from_uri_and_params(
3310 self.session.store_registry(),
3311 target_path,
3312 &store_params.clone().unwrap_or_default(),
3313 )
3314 .await?;
3315
3316 if self
3318 .commit_handler
3319 .resolve_latest_location(&target_base, &target_store)
3320 .await
3321 .is_ok()
3322 {
3323 return Err(Error::dataset_already_exists(target_path.to_string()));
3324 }
3325
3326 let build_absolute_path = |relative_path: &str, base: &Path| -> Path {
3327 let mut path = base.clone();
3328 for seg in relative_path.split('/') {
3329 if !seg.is_empty() {
3330 path = path.clone().join(seg);
3331 }
3332 }
3333 path
3334 };
3335
3336 let same_store = src_ds.object_store.store_prefix == target_store.store_prefix;
3340
3341 let io_parallelism = self.object_store.io_parallelism();
3348 let copy_futures = src_paths
3349 .iter()
3350 .map(|(relative_path, base)| {
3351 let source_store = Arc::clone(&src_ds.object_store);
3352 let target_store = Arc::clone(&target_store);
3353 let src_path = build_absolute_path(relative_path, base);
3354 let target_path = build_absolute_path(relative_path, &target_base);
3355 async move {
3356 if same_store {
3357 target_store.copy(&src_path, &target_path).await?;
3358 } else {
3359 let reader = source_store.open(&src_path).await?;
3360 let mut writer = target_store.create(&target_path).await?;
3361 writer.copy_from_reader(reader.as_ref()).await?;
3362 writer.shutdown().await?;
3363 }
3364 Result::Ok(())
3365 }
3366 })
3367 .collect::<Vec<_>>();
3368
3369 futures::stream::iter(copy_futures)
3370 .buffer_unordered(io_parallelism)
3371 .collect::<Vec<_>>()
3372 .await
3373 .into_iter()
3374 .collect::<Result<Vec<_>>>()?;
3375
3376 let ref_name = src_ds.manifest.branch.clone();
3378 let ref_version = src_ds.manifest_location.version;
3379 let clone_op = Operation::Clone {
3380 is_shallow: false,
3381 ref_name,
3382 ref_version,
3383 ref_path: src_ds.uri().to_string(),
3384 branch_name: None,
3385 };
3386 let txn = Transaction::new(ref_version, clone_op, None);
3387 let builder = CommitBuilder::new(WriteDestination::Uri(target_path))
3388 .with_store_params(store_params.clone().unwrap_or_default())
3389 .with_object_store(target_store.clone())
3390 .with_source_store(src_ds.object_store.clone())
3391 .with_commit_handler(self.commit_handler.clone())
3392 .with_exact_storage_format(self.manifest.data_storage_format.lance_file_format());
3393 let new_ds = builder.execute(txn).await?;
3394 Ok(new_ds)
3395 }
3396
3397 async fn resolve_reference(&self, reference: refs::Ref) -> Result<(Option<String>, u64)> {
3398 match reference {
3399 refs::Ref::Version(branch, version_number) => {
3400 if let Some(version_number) = version_number {
3401 Ok((branch, version_number))
3402 } else {
3403 let branch_location = self.branch_location().find_branch(branch.as_deref())?;
3404 let version_number = self
3405 .commit_handler
3406 .resolve_latest_location(&branch_location.path, &self.object_store)
3407 .await?
3408 .version;
3409 Ok((branch, version_number))
3410 }
3411 }
3412 refs::Ref::VersionNumber(version_number) => {
3413 Ok((self.manifest.branch.clone(), version_number))
3414 }
3415 refs::Ref::Tag(tag_name) => {
3416 let tag_contents = self.tags().get(tag_name.as_str()).await?;
3417 Ok((tag_contents.branch, tag_contents.version))
3418 }
3419 }
3420 }
3421
3422 async fn collect_paths(&self) -> Result<Vec<(String, Path)>> {
3424 let mut file_paths: Vec<(String, Path)> = Vec::new();
3425 for fragment in self.manifest.fragments.iter() {
3426 if let Some(RowIdMeta::External(external_file)) = &fragment.row_id_meta {
3427 return Err(Error::internal(format!(
3428 "External row_id_meta is not supported yet. external file path: {}",
3429 external_file.path
3430 )));
3431 }
3432 for data_file in fragment.referenced_lance_files() {
3433 let base_root = if let Some(base_id) = data_file.base_id {
3434 let base_path =
3435 self.manifest.base_paths.get(&base_id).ok_or_else(|| {
3436 Error::internal(format!("base_id {} not found", base_id))
3437 })?;
3438 Path::parse(base_path.path.as_str())?
3439 } else {
3440 self.base.clone()
3441 };
3442 file_paths.push((
3443 format!("{}/{}", DATA_DIR, data_file.path.clone()),
3444 base_root,
3445 ));
3446 }
3447 if let Some(deletion_file) = &fragment.deletion_file {
3448 let base_root = if let Some(base_id) = deletion_file.base_id {
3449 let base_path =
3450 self.manifest.base_paths.get(&base_id).ok_or_else(|| {
3451 Error::internal(format!("base_id {} not found", base_id))
3452 })?;
3453 Path::parse(base_path.path.as_str())?
3454 } else {
3455 self.base.clone()
3456 };
3457 file_paths.push((
3458 relative_deletion_file_path(fragment.id, deletion_file),
3459 base_root,
3460 ));
3461 }
3462 }
3463
3464 let indices = read_manifest_indexes(
3465 self.object_store.as_ref(),
3466 &self.manifest_location,
3467 &self.manifest,
3468 )
3469 .await?;
3470
3471 for index in &indices {
3472 let base_root = if let Some(base_id) = index.base_id {
3473 let base_path = self
3474 .manifest
3475 .base_paths
3476 .get(&base_id)
3477 .ok_or_else(|| Error::internal(format!("base_id {} not found", base_id)))?;
3478 Path::parse(base_path.path.as_str())?
3479 } else {
3480 self.base.clone()
3481 };
3482 let index_root = base_root
3483 .clone()
3484 .join(INDICES_DIR)
3485 .join(index.uuid.to_string());
3486 let mut stream = self.object_store.read_dir_all(&index_root, None);
3487 while let Some(meta) = stream.next().await.transpose()? {
3488 if let Some(filename) = meta.location.filename() {
3489 file_paths.push((
3490 format!("{}/{}/{}", INDICES_DIR, index.uuid, filename),
3491 base_root.clone(),
3492 ));
3493 }
3494 }
3495 }
3496 Ok(file_paths)
3497 }
3498
3499 pub fn sql(&self, sql: &str) -> SqlQueryBuilder {
3503 SqlQueryBuilder::new(self.clone(), sql)
3504 }
3505}
3506
3507pub(crate) struct NewTransactionResult<'a> {
3508 pub dataset: BoxFuture<'a, Result<Dataset>>,
3509 pub new_transactions: BoxStream<'a, Result<(u64, Arc<Transaction>)>>,
3510}
3511
3512pub(crate) fn load_new_transactions(dataset: &Dataset) -> NewTransactionResult<'_> {
3513 let io_parallelism = dataset.object_store.as_ref().io_parallelism();
3517 let locations = dataset.commit_handler.list_manifest_locations_since(
3518 &dataset.base,
3519 dataset.object_store.as_ref(),
3520 dataset.manifest.version,
3521 );
3522
3523 let (latest_tx, latest_rx) = tokio::sync::oneshot::channel();
3525 let mut latest_tx = Some(latest_tx);
3526
3527 let manifests = locations
3528 .map_ok(move |location| {
3529 let latest_tx = latest_tx.take();
3530 async move {
3531 let manifest = Dataset::get_manifest(
3532 dataset.object_store.as_ref(),
3533 &location,
3534 &dataset.uri,
3535 dataset.session.as_ref(),
3536 )
3537 .await?;
3538
3539 if let Some(latest_tx) = latest_tx {
3540 let _ = latest_tx.send((manifest.clone(), location.clone()));
3542 }
3543
3544 Ok((manifest, location))
3545 }
3546 })
3547 .try_buffer_unordered(io_parallelism / 2);
3548 let transactions = manifests
3549 .map_ok(move |(manifest, location)| async move {
3550 let manifest_copy = manifest.clone();
3551 let tx_key = TransactionKey {
3552 version: manifest.version,
3553 };
3554 let transaction =
3555 if let Some(cached) = dataset.metadata_cache.get_with_key(&tx_key).await {
3556 cached
3557 } else {
3558 let dataset_version = Dataset::checkout_manifest(
3559 dataset.object_store.clone(),
3560 dataset.base.clone(),
3561 dataset.uri.clone(),
3562 manifest_copy.clone(),
3563 location,
3564 dataset.session(),
3565 dataset.commit_handler.clone(),
3566 dataset.file_reader_options.clone(),
3567 dataset.store_params.as_deref().cloned(),
3568 dataset.base_store_params.clone(),
3569 )?;
3570 let loaded =
3571 Arc::new(dataset_version.read_transaction().await?.ok_or_else(|| {
3572 Error::internal(format!(
3573 "Dataset version {} does not have a transaction file",
3574 manifest_copy.version
3575 ))
3576 })?);
3577 dataset
3578 .metadata_cache
3579 .insert_with_key(&tx_key, loaded.clone())
3580 .await;
3581 loaded
3582 };
3583 Ok((manifest.version, transaction))
3584 })
3585 .try_buffer_unordered(io_parallelism / 2);
3586
3587 let dataset = async move {
3588 if let Ok((latest_manifest, location)) = latest_rx.await {
3589 Dataset::checkout_manifest(
3591 dataset.object_store.clone(),
3592 dataset.base.clone(),
3593 dataset.uri.clone(),
3594 latest_manifest,
3595 location,
3596 dataset.session(),
3597 dataset.commit_handler.clone(),
3598 dataset.file_reader_options.clone(),
3599 dataset.store_params.as_deref().cloned(),
3600 dataset.base_store_params.clone(),
3601 )
3602 } else {
3603 Ok(dataset.clone())
3606 }
3607 }
3608 .boxed();
3609
3610 let new_transactions = transactions.boxed();
3611
3612 NewTransactionResult {
3613 dataset,
3614 new_transactions,
3615 }
3616}
3617
3618impl Dataset {
3635 pub async fn add_columns(
3637 &mut self,
3638 transforms: NewColumnTransform,
3639 read_columns: Option<Vec<String>>,
3640 batch_size: Option<u32>,
3641 ) -> Result<()> {
3642 schema_evolution::add_columns(self, transforms, read_columns, batch_size).await
3643 }
3644
3645 pub async fn alter_columns(&mut self, alterations: &[ColumnAlteration]) -> Result<()> {
3654 schema_evolution::alter_columns(self, alterations).await
3655 }
3656
3657 pub async fn drop_columns(&mut self, columns: &[&str]) -> Result<()> {
3664 info!(target: TRACE_DATASET_EVENTS, event=DATASET_DROPPING_COLUMN_EVENT, uri = &self.uri, columns = columns.join(","));
3665 schema_evolution::drop_columns(self, columns).await
3666 }
3667
3668 #[deprecated(since = "0.9.12", note = "Please use `drop_columns` instead.")]
3674 pub async fn drop(&mut self, columns: &[&str]) -> Result<()> {
3675 self.drop_columns(columns).await
3676 }
3677
3678 async fn merge_impl(
3679 &mut self,
3680 stream: Box<dyn RecordBatchReader + Send>,
3681 left_on: &str,
3682 right_on: &str,
3683 ) -> Result<()> {
3684 if self.schema().field(left_on).is_none() && left_on != ROW_ID && left_on != ROW_ADDR {
3686 return Err(Error::invalid_input(format!(
3687 "Column {} does not exist in the left side dataset",
3688 left_on
3689 )));
3690 };
3691 let right_schema = stream.schema();
3692 if right_schema.field_with_name(right_on).is_err() {
3693 return Err(Error::invalid_input(format!(
3694 "Column {} does not exist in the right side dataset",
3695 right_on
3696 )));
3697 };
3698 for field in right_schema.fields() {
3699 if field.name() == right_on {
3700 continue;
3703 }
3704 if self.schema().field(field.name()).is_some() {
3705 return Err(Error::invalid_input(format!(
3706 "Column {} exists in both sides of the dataset",
3707 field.name()
3708 )));
3709 }
3710 }
3711
3712 let joiner = Arc::new(HashJoiner::try_new(stream, right_on).await?);
3714 let mut new_schema: Schema = self.schema().merge(joiner.out_schema().as_ref())?;
3717 new_schema.set_field_id(Some(self.manifest.max_field_id()));
3718
3719 let updated_fragments: Vec<Fragment> = stream::iter(self.get_fragments())
3722 .then(|f| {
3723 let joiner = joiner.clone();
3724 async move { f.merge(left_on, &joiner).await.map(|f| f.metadata) }
3725 })
3726 .try_collect::<Vec<_>>()
3727 .await?;
3728
3729 let preserves_nullability =
3730 !schema_evolution::merge_introduces_required_field(self.schema(), &new_schema);
3731 let transaction = Transaction::new(
3732 self.manifest.version,
3733 Operation::Merge {
3734 fragments: updated_fragments,
3735 schema: new_schema,
3736 preserves_nullability,
3737 },
3738 None,
3739 );
3740
3741 self.apply_commit(transaction, &Default::default(), &Default::default())
3742 .await?;
3743
3744 Ok(())
3745 }
3746
3747 pub async fn merge(
3759 &mut self,
3760 stream: impl RecordBatchReader + Send + 'static,
3761 left_on: &str,
3762 right_on: &str,
3763 ) -> Result<()> {
3764 let stream = Box::new(stream);
3765 self.merge_impl(stream, left_on, right_on).await
3766 }
3767
3768 pub async fn merge_index_metadata(
3771 &self,
3772 index_uuid: &Uuid,
3773 index_type: IndexType,
3774 _batch_readhead: Option<usize>,
3775 progress: Arc<dyn IndexBuildProgress>,
3776 ) -> Result<()> {
3777 let store = LanceIndexStore::from_dataset_for_new(self, index_uuid)?;
3778 let index_dir = self.indices_dir().join(index_uuid.to_string());
3779 match index_type {
3780 IndexType::Inverted => {
3781 lance_index::scalar::inverted::builder::merge_index_files(
3783 self.object_store.as_ref(),
3784 &index_dir,
3785 Arc::new(store),
3786 progress,
3787 )
3788 .await
3789 }
3790 IndexType::BTree => {
3791 Err(Error::invalid_input(
3792 "BTree distributed indexing no longer supports merge_index_metadata; \
3793 build segments, optionally merge groups with merge_existing_index_segments(...), \
3794 and commit with commit_existing_index_segments(...)"
3795 .to_string(),
3796 ))
3797 }
3798 IndexType::Bitmap => {
3799 Err(Error::invalid_input(
3800 "Bitmap distributed indexing no longer supports merge_index_metadata; \
3801 build segments with create_index_uncommitted(...), merge them with \
3802 merge_existing_index_segments(...), and commit with \
3803 commit_existing_index_segments(...)"
3804 .to_string(),
3805 ))
3806 }
3807 IndexType::IvfFlat | IndexType::IvfPq | IndexType::IvfSq | IndexType::Vector => {
3808 Err(Error::invalid_input(
3809 "Vector distributed indexing no longer supports merge_index_metadata; \
3810 build segments, optionally merge groups with merge_existing_index_segments(...), \
3811 and commit with commit_existing_index_segments(...)"
3812 .to_string(),
3813 ))
3814 }
3815 _ => Err(Error::invalid_input_source(Box::new(std::io::Error::new(
3816 std::io::ErrorKind::InvalidInput,
3817 format!("Unsupported index type (patched): {}", index_type),
3818 )))),
3819 }
3820 }
3821}
3822
3823impl Dataset {
3836 pub fn metadata(&self) -> &HashMap<String, String> {
3838 &self.manifest.table_metadata
3839 }
3840
3841 pub fn config(&self) -> &HashMap<String, String> {
3843 &self.manifest.config
3844 }
3845
3846 #[deprecated(
3848 note = "Use the new update_config(values, replace) method - pass None values to delete keys"
3849 )]
3850 pub async fn delete_config_keys(&mut self, delete_keys: &[&str]) -> Result<()> {
3851 let updates = delete_keys.iter().map(|key| (*key, None));
3852 self.update_config(updates).await?;
3853 Ok(())
3854 }
3855
3856 pub fn update_metadata(
3883 &mut self,
3884 values: impl IntoIterator<Item = impl Into<UpdateMapEntry>>,
3885 ) -> metadata::UpdateMetadataBuilder<'_> {
3886 metadata::UpdateMetadataBuilder::new(self, values, metadata::MetadataType::TableMetadata)
3887 }
3888
3889 pub fn update_config(
3916 &mut self,
3917 values: impl IntoIterator<Item = impl Into<UpdateMapEntry>>,
3918 ) -> metadata::UpdateMetadataBuilder<'_> {
3919 metadata::UpdateMetadataBuilder::new(self, values, metadata::MetadataType::Config)
3920 }
3921
3922 pub fn update_schema_metadata(
3949 &mut self,
3950 values: impl IntoIterator<Item = impl Into<UpdateMapEntry>>,
3951 ) -> metadata::UpdateMetadataBuilder<'_> {
3952 metadata::UpdateMetadataBuilder::new(self, values, metadata::MetadataType::SchemaMetadata)
3953 }
3954
3955 #[deprecated(note = "Use the new update_schema_metadata(values).replace() instead")]
3957 pub async fn replace_schema_metadata(
3958 &mut self,
3959 new_values: impl IntoIterator<Item = (String, String)>,
3960 ) -> Result<()> {
3961 let new_values = new_values
3962 .into_iter()
3963 .map(|(k, v)| (k, Some(v)))
3964 .collect::<HashMap<_, _>>();
3965 self.update_schema_metadata(new_values).replace().await?;
3966 Ok(())
3967 }
3968
3969 pub fn update_field_metadata(&mut self) -> UpdateFieldMetadataBuilder<'_> {
3999 UpdateFieldMetadataBuilder::new(self)
4000 }
4001
4002 pub async fn replace_field_metadata(
4004 &mut self,
4005 new_values: impl IntoIterator<Item = (u32, HashMap<String, String>)>,
4006 ) -> Result<()> {
4007 let new_values = new_values.into_iter().collect::<HashMap<_, _>>();
4008 let field_metadata_updates = new_values
4009 .into_iter()
4010 .map(|(field_id, metadata)| {
4011 (
4012 field_id as i32,
4013 translate_schema_metadata_updates(&metadata),
4014 )
4015 })
4016 .collect();
4017 metadata::execute_metadata_update(
4018 self,
4019 Operation::UpdateConfig {
4020 config_updates: None,
4021 table_metadata_updates: None,
4022 schema_metadata_updates: None,
4023 field_metadata_updates,
4024 },
4025 )
4026 .await
4027 }
4028}
4029
4030#[async_trait::async_trait]
4031impl DatasetTakeRows for Dataset {
4032 fn schema(&self) -> &Schema {
4033 Self::schema(self)
4034 }
4035
4036 async fn take_rows(&self, row_ids: &[u64], projection: &Schema) -> Result<RecordBatch> {
4037 Self::take_rows(self, row_ids, projection.clone()).await
4038 }
4039}
4040
4041#[derive(Debug)]
4042pub(crate) struct ManifestWriteConfig {
4043 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, migration_next_row_id: Option<u64>, }
4054
4055impl Default for ManifestWriteConfig {
4056 fn default() -> Self {
4057 Self {
4058 auto_set_feature_flags: true,
4059 timestamp: None,
4060 use_stable_row_ids: false,
4061 disable_transaction_file: false,
4062 use_legacy_format: None,
4063 storage_format: None,
4064 migration_next_row_id: None,
4065 }
4066 }
4067}
4068
4069impl ManifestWriteConfig {
4070 pub fn disable_transaction_file(&self) -> bool {
4071 self.disable_transaction_file
4072 }
4073
4074 #[cfg(test)]
4075 pub(crate) fn with_transaction_file_disabled(mut self) -> Self {
4076 self.disable_transaction_file = true;
4077 self
4078 }
4079
4080 pub(crate) fn to_build_config(&self) -> ManifestBuildConfig {
4085 ManifestBuildConfig {
4086 auto_set_feature_flags: self.auto_set_feature_flags,
4087 timestamp_nanos: timestamp_to_nanos(self.timestamp),
4088 use_stable_row_ids: self.use_stable_row_ids,
4089 use_legacy_format: self.use_legacy_format,
4090 storage_format: self.storage_format.clone(),
4091 disable_transaction_file: self.disable_transaction_file,
4092 migration_next_row_id: self.migration_next_row_id,
4093 }
4094 }
4095}
4096
4097#[allow(clippy::too_many_arguments)]
4099pub(crate) async fn write_manifest_file(
4100 object_store: &ObjectStore,
4101 commit_handler: &dyn CommitHandler,
4102 base_path: &Path,
4103 manifest: &mut Manifest,
4104 indices: Option<Vec<IndexMetadata>>,
4105 config: &ManifestWriteConfig,
4106 naming_scheme: ManifestNamingScheme,
4107 transaction: Option<lance_table::format::Transaction>,
4108) -> std::result::Result<ManifestLocation, CommitError> {
4109 if config.auto_set_feature_flags {
4110 let use_stable_row_ids = config.use_stable_row_ids || manifest.uses_stable_row_ids();
4114 apply_feature_flags(
4115 manifest,
4116 use_stable_row_ids,
4117 config.disable_transaction_file,
4118 )?;
4119 }
4120
4121 manifest.set_timestamp(timestamp_to_nanos(config.timestamp));
4122
4123 manifest.update_max_fragment_id();
4124
4125 commit_handler
4126 .commit(
4127 manifest,
4128 indices,
4129 base_path,
4130 object_store,
4131 write_manifest_file_to_path,
4132 naming_scheme,
4133 transaction,
4134 )
4135 .await
4136}
4137
4138impl Projectable for Dataset {
4139 fn schema(&self) -> &Schema {
4140 self.schema()
4141 }
4142}
4143
4144#[cfg(test)]
4145mod tests;