lance_namespace_impls/dir/
load.rs1use lance::dataset::Dataset;
5use lance::dataset::builder::DatasetBuilder;
6use lance_core::Result;
7use lance_namespace::LanceNamespace;
8use lance_namespace::error::NamespaceError;
9use lance_namespace::models::{DescribeTableRequest, DescribeTableResponse};
10use lance_namespace::schema::arrow_schema_to_json;
11
12use super::{DirectoryNamespace, manifest, manifest_feature_flags};
13
14impl DirectoryNamespace {
15 pub(super) async fn describe_table_impl(
19 &self,
20 request: DescribeTableRequest,
21 ) -> Result<DescribeTableResponse> {
22 let is_root_level = request.id.as_ref().is_some_and(|id| id.len() == 1);
23 let is_child_table = request.id.as_ref().is_some_and(|id| id.len() > 1);
24 let skip_manifest_for_root = self.dir_listing_enabled
25 && is_root_level
26 && !self.dir_listing_to_manifest_migration_enabled;
27 if is_child_table
35 || !self.dir_listing_enabled
36 || self.dir_listing_to_manifest_migration_enabled
37 {
38 self.ensure_read_manifest().await?;
39 }
40 if let Some(manifest_ns) = self.manifest_ns_for_read()
41 && !skip_manifest_for_root
42 {
43 match manifest_ns.describe_table(request.clone()).await {
44 Ok(mut response) => {
45 if let Some(ref table_uri) = response.table_uri {
46 let vend = request.vend_credentials.unwrap_or(true);
48 let identity = request.identity.as_deref();
49 response.storage_options = self
50 .get_storage_options_for_table(table_uri, vend, identity)
51 .await?;
52 }
53 if self.table_version_tracking_enabled {
55 response.managed_versioning = Some(true);
56 }
57 return Ok(response);
58 }
59 Err(e) if manifest_feature_flags::is_incompatible_manifest_error(&e) => {
60 return Err(e);
63 }
64 Err(e) if self.dir_listing_enabled && is_root_level => {
65 if !Self::is_manifest_table_absent_error(&e) {
69 return Err(Self::classify_storage_error(e));
70 }
71 }
72 Err(e) => return Err(e),
73 }
74 }
75 if is_child_table {
76 return Err(self.child_namespace_requires_manifest_error());
77 }
78
79 let table_name = Self::table_name_from_id(&request.id)?;
80 let table_id = Self::format_table_id_from_request(&request.id);
81 if !self.dir_listing_enabled {
82 return Err(NamespaceError::TableNotFound { message: table_id }.into());
83 }
84
85 let table_uri = self.table_full_uri(&table_name);
86
87 let status = self.check_table_status(&table_name).await?;
89
90 if !status.exists {
91 return Err(NamespaceError::TableNotFound {
92 message: table_id.clone(),
93 }
94 .into());
95 }
96
97 if status.is_deregistered {
98 return Err(NamespaceError::TableNotFound {
99 message: format!("Table is deregistered: {}", table_id),
100 }
101 .into());
102 }
103
104 let load_detailed_metadata = request.load_detailed_metadata.unwrap_or(false);
105 let should_check_declared =
106 load_detailed_metadata || request.check_declared.unwrap_or(false);
107 let vend_credentials = request.vend_credentials.unwrap_or(true);
109 let identity = request.identity.as_deref();
110 let is_only_declared = if should_check_declared {
111 if status.has_reserved_file {
112 Some(!self.table_has_actual_manifests(&table_name).await?)
113 } else {
114 Some(false)
115 }
116 } else {
117 None
118 };
119
120 if !load_detailed_metadata {
121 let storage_options = self
122 .get_storage_options_for_table(&table_uri, vend_credentials, identity)
123 .await?;
124 return Ok(DescribeTableResponse {
125 table: Some(table_name),
126 namespace: request.id.as_ref().map(|id| {
127 if id.len() > 1 {
128 id[..id.len() - 1].to_vec()
129 } else {
130 vec![]
131 }
132 }),
133 location: Some(table_uri.clone()),
134 table_uri: Some(table_uri),
135 storage_options,
136 is_only_declared,
137 managed_versioning: if self.table_version_tracking_enabled {
138 Some(true)
139 } else {
140 None
141 },
142 ..Default::default()
143 });
144 }
145
146 if is_only_declared == Some(true) {
147 let storage_options = self
148 .get_storage_options_for_table(&table_uri, vend_credentials, identity)
149 .await?;
150 return Ok(DescribeTableResponse {
151 table: Some(table_name),
152 namespace: request.id.as_ref().map(|id| {
153 if id.len() > 1 {
154 id[..id.len() - 1].to_vec()
155 } else {
156 vec![]
157 }
158 }),
159 location: Some(table_uri.clone()),
160 table_uri: Some(table_uri),
161 storage_options,
162 is_only_declared,
163 managed_versioning: if self.table_version_tracking_enabled {
164 Some(true)
165 } else {
166 None
167 },
168 ..Default::default()
169 });
170 }
171
172 let mut builder = DatasetBuilder::from_uri(&table_uri);
175 if let Some(opts) = &self.storage_options {
176 builder = builder.with_storage_options(opts.clone());
177 }
178 if let Some(sess) = &self.session {
179 builder = builder.with_session(sess.clone());
180 }
181 match builder.load().await {
182 Ok(mut dataset) => {
183 if let Some(requested_version) = request.version {
185 dataset = dataset
186 .checkout_version(requested_version as u64)
187 .await
188 .map_err(|e| {
189 let message = format!(
190 "Version {} not found for table '{}': {}",
191 requested_version, table_name, e
192 );
193 Self::map_open_error(
194 e,
195 NamespaceError::TableVersionNotFound { message },
196 )
197 })?;
198 }
199
200 let version_info = dataset.version();
201 let lance_schema = dataset.schema();
202 let arrow_schema: arrow_schema::Schema = lance_schema.into();
203 let json_schema = arrow_schema_to_json(&arrow_schema)?;
204 let storage_options = self
205 .get_storage_options_for_table(&table_uri, vend_credentials, identity)
206 .await?;
207
208 let metadata: std::collections::HashMap<String, String> =
210 version_info.metadata.into_iter().collect();
211
212 Ok(DescribeTableResponse {
213 table: Some(table_name),
214 namespace: request.id.as_ref().map(|id| {
215 if id.len() > 1 {
216 id[..id.len() - 1].to_vec()
217 } else {
218 vec![]
219 }
220 }),
221 version: Some(version_info.version as i64),
222 location: Some(table_uri.clone()),
223 table_uri: Some(table_uri),
224 schema: Some(Box::new(json_schema)),
225 storage_options,
226 metadata: Some(metadata),
227 is_only_declared,
228 managed_versioning: if self.table_version_tracking_enabled {
229 Some(true)
230 } else {
231 None
232 },
233 ..Default::default()
234 })
235 }
236 Err(err) => {
237 if manifest::ManifestNamespace::is_not_found_load_error(&err)
238 && is_only_declared == Some(true)
239 {
240 let storage_options = self
241 .get_storage_options_for_table(&table_uri, vend_credentials, identity)
242 .await?;
243 Ok(DescribeTableResponse {
244 table: Some(table_name),
245 namespace: request.id.as_ref().map(|id| {
246 if id.len() > 1 {
247 id[..id.len() - 1].to_vec()
248 } else {
249 vec![]
250 }
251 }),
252 location: Some(table_uri.clone()),
253 table_uri: Some(table_uri),
254 storage_options,
255 is_only_declared,
256 managed_versioning: if self.table_version_tracking_enabled {
257 Some(true)
258 } else {
259 None
260 },
261 ..Default::default()
262 })
263 } else {
264 Err(NamespaceError::Internal {
265 message: format!(
266 "Table directory exists but cannot load dataset {}: {:?}",
267 table_name, err
268 ),
269 }
270 .into())
271 }
272 }
273 }
274 }
275
276 pub(super) fn configured_builder(&self, table_uri: &str) -> DatasetBuilder {
279 let mut builder = DatasetBuilder::from_uri(table_uri);
280 if let Some(opts) = &self.storage_options {
281 builder = builder.with_storage_options(opts.clone());
282 }
283 if let Some(sess) = &self.session {
284 builder = builder.with_session(sess.clone());
285 }
286 builder
287 }
288
289 pub(super) async fn load_dataset(
290 &self,
291 table_uri: &str,
292 version: Option<i64>,
293 operation: &str,
294 ) -> Result<Dataset> {
295 self.load_branch_dataset(table_uri, None, version, operation)
296 .await
297 }
298
299 pub(super) async fn load_branch_dataset(
301 &self,
302 table_uri: &str,
303 branch: Option<&str>,
304 version: Option<i64>,
305 operation: &str,
306 ) -> Result<Dataset> {
307 if let Some(version) = version
308 && version < 0
309 {
310 return Err(NamespaceError::InvalidInput {
311 message: format!(
312 "Table version for {} must be non-negative, got {}",
313 operation, version
314 ),
315 }
316 .into());
317 }
318
319 let dataset = match Self::normalized_branch(branch)? {
320 Some(branch) => self.open_validated_branch(table_uri, branch).await?,
321 None => self
322 .configured_builder(table_uri)
323 .load()
324 .await
325 .map_err(|e| {
326 let message = format!(
327 "Failed to open table at '{}' for {}: {}",
328 table_uri, operation, e
329 );
330 Self::map_open_error(e, NamespaceError::TableNotFound { message })
331 })?,
332 };
333
334 if let Some(version) = version {
335 return dataset.checkout_version(version as u64).await.map_err(|e| {
336 let message = format!(
337 "Failed to checkout version {} for table at '{}' during {}: {}",
338 version, table_uri, operation, e
339 );
340 Self::map_open_error(e, NamespaceError::TableVersionNotFound { message })
341 });
342 }
343
344 Ok(dataset)
345 }
346
347 pub async fn migrate(&self) -> Result<usize> {
401 let Some(manifest_ns) = self.manifest_ns_for_write().await? else {
403 return Ok(0); };
405
406 let manifest_locations = manifest_ns.list_manifest_table_locations().await?;
408
409 let dir_tables = self
412 .filter_declared_tables(self.list_directory_tables().await?, false)
413 .await?;
414
415 let mut migrated_count = 0;
420 for table_name in dir_tables {
421 let dir_name = format!("{}.lance", table_name);
423 if !manifest_locations.contains(&dir_name) {
424 manifest_ns.register_table(&table_name, dir_name).await?;
425 migrated_count += 1;
426 }
427 }
428
429 Ok(migrated_count)
430 }
431}