1use std::sync::Arc;
21
22use crate::arrow::ArrowReaderBuilder;
23use crate::inspect::MetadataTable;
24use crate::io::FileIO;
25use crate::io::object_cache::ObjectCache;
26use crate::scan::TableScanBuilder;
27use crate::spec::{SchemaRef, TableMetadata, TableMetadataRef};
28use crate::{Error, ErrorKind, Result, TableIdent};
29
30pub struct TableBuilder {
32 file_io: Option<FileIO>,
33 metadata_location: Option<String>,
34 metadata: Option<TableMetadataRef>,
35 identifier: Option<TableIdent>,
36 readonly: bool,
37 disable_cache: bool,
38 cache_size_bytes: Option<u64>,
39}
40
41impl TableBuilder {
42 pub(crate) fn new() -> Self {
43 Self {
44 file_io: None,
45 metadata_location: None,
46 metadata: None,
47 identifier: None,
48 readonly: false,
49 disable_cache: false,
50 cache_size_bytes: None,
51 }
52 }
53
54 pub fn file_io(mut self, file_io: FileIO) -> Self {
56 self.file_io = Some(file_io);
57 self
58 }
59
60 pub fn metadata_location<T: Into<String>>(mut self, metadata_location: T) -> Self {
62 self.metadata_location = Some(metadata_location.into());
63 self
64 }
65
66 pub fn metadata<T: Into<TableMetadataRef>>(mut self, metadata: T) -> Self {
68 self.metadata = Some(metadata.into());
69 self
70 }
71
72 pub fn identifier(mut self, identifier: TableIdent) -> Self {
74 self.identifier = Some(identifier);
75 self
76 }
77
78 pub fn readonly(mut self, readonly: bool) -> Self {
80 self.readonly = readonly;
81 self
82 }
83
84 pub fn disable_cache(mut self) -> Self {
88 self.disable_cache = true;
89 self
90 }
91
92 pub fn cache_size_bytes(mut self, cache_size_bytes: u64) -> Self {
94 self.cache_size_bytes = Some(cache_size_bytes);
95 self
96 }
97
98 pub fn build(self) -> Result<Table> {
100 let Self {
101 file_io,
102 metadata_location,
103 metadata,
104 identifier,
105 readonly,
106 disable_cache,
107 cache_size_bytes,
108 } = self;
109
110 let Some(file_io) = file_io else {
111 return Err(Error::new(
112 ErrorKind::DataInvalid,
113 "FileIO must be provided with TableBuilder.file_io()",
114 ));
115 };
116
117 let Some(metadata) = metadata else {
118 return Err(Error::new(
119 ErrorKind::DataInvalid,
120 "TableMetadataRef must be provided with TableBuilder.metadata()",
121 ));
122 };
123
124 let Some(identifier) = identifier else {
125 return Err(Error::new(
126 ErrorKind::DataInvalid,
127 "TableIdent must be provided with TableBuilder.identifier()",
128 ));
129 };
130
131 let object_cache = if disable_cache {
132 Arc::new(ObjectCache::with_disabled_cache(file_io.clone()))
133 } else if let Some(cache_size_bytes) = cache_size_bytes {
134 Arc::new(ObjectCache::new_with_capacity(
135 file_io.clone(),
136 cache_size_bytes,
137 ))
138 } else {
139 Arc::new(ObjectCache::new(file_io.clone()))
140 };
141
142 Ok(Table {
143 file_io,
144 metadata_location,
145 metadata,
146 identifier,
147 readonly,
148 object_cache,
149 })
150 }
151}
152
153#[derive(Debug, Clone)]
155pub struct Table {
156 file_io: FileIO,
157 metadata_location: Option<String>,
158 metadata: TableMetadataRef,
159 identifier: TableIdent,
160 readonly: bool,
161 object_cache: Arc<ObjectCache>,
162}
163
164impl Table {
165 pub(crate) fn with_metadata(mut self, metadata: TableMetadataRef) -> Self {
167 self.metadata = metadata;
168 self
169 }
170
171 pub(crate) fn with_metadata_location(mut self, metadata_location: String) -> Self {
173 self.metadata_location = Some(metadata_location);
174 self
175 }
176
177 pub fn builder() -> TableBuilder {
179 TableBuilder::new()
180 }
181
182 pub fn identifier(&self) -> &TableIdent {
184 &self.identifier
185 }
186 pub fn metadata(&self) -> &TableMetadata {
188 &self.metadata
189 }
190
191 pub fn metadata_ref(&self) -> TableMetadataRef {
193 self.metadata.clone()
194 }
195
196 pub fn metadata_location(&self) -> Option<&str> {
198 self.metadata_location.as_deref()
199 }
200
201 pub fn metadata_location_result(&self) -> Result<&str> {
203 self.metadata_location.as_deref().ok_or(Error::new(
204 ErrorKind::DataInvalid,
205 format!(
206 "Metadata location does not exist for table: {}",
207 self.identifier
208 ),
209 ))
210 }
211
212 pub fn file_io(&self) -> &FileIO {
214 &self.file_io
215 }
216
217 pub(crate) fn object_cache(&self) -> Arc<ObjectCache> {
219 self.object_cache.clone()
220 }
221
222 pub fn scan(&self) -> TableScanBuilder<'_> {
224 TableScanBuilder::new(self)
225 }
226
227 pub fn inspect(&self) -> MetadataTable<'_> {
230 MetadataTable::new(self)
231 }
232
233 pub fn readonly(&self) -> bool {
235 self.readonly
236 }
237
238 pub fn current_schema_ref(&self) -> SchemaRef {
240 self.metadata.current_schema().clone()
241 }
242
243 pub fn reader_builder(&self) -> ArrowReaderBuilder {
245 ArrowReaderBuilder::new(self.file_io.clone())
246 }
247}
248
249#[derive(Debug, Clone)]
276pub struct StaticTable(Table);
277
278impl StaticTable {
279 pub async fn from_metadata(
281 metadata: TableMetadata,
282 table_ident: TableIdent,
283 file_io: FileIO,
284 ) -> Result<Self> {
285 let table = Table::builder()
286 .metadata(metadata)
287 .identifier(table_ident)
288 .file_io(file_io.clone())
289 .readonly(true)
290 .build();
291
292 Ok(Self(table?))
293 }
294 pub async fn from_metadata_file(
296 metadata_location: &str,
297 table_ident: TableIdent,
298 file_io: FileIO,
299 ) -> Result<Self> {
300 let metadata = TableMetadata::read_from(&file_io, metadata_location).await?;
301
302 let table = Table::builder()
303 .metadata(metadata)
304 .metadata_location(metadata_location)
305 .identifier(table_ident)
306 .file_io(file_io.clone())
307 .readonly(true)
308 .build();
309
310 Ok(Self(table?))
311 }
312
313 pub fn scan(&self) -> TableScanBuilder<'_> {
315 self.0.scan()
316 }
317
318 pub fn metadata(&self) -> TableMetadataRef {
320 self.0.metadata_ref()
321 }
322
323 pub fn into_table(self) -> Table {
327 self.0
328 }
329
330 pub fn reader_builder(&self) -> ArrowReaderBuilder {
332 ArrowReaderBuilder::new(self.0.file_io.clone())
333 }
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339
340 #[tokio::test]
341 async fn test_static_table_from_file() {
342 let metadata_file_name = "TableMetadataV2Valid.json";
343 let metadata_file_path = format!(
344 "{}/testdata/table_metadata/{}",
345 env!("CARGO_MANIFEST_DIR"),
346 metadata_file_name
347 );
348 let file_io = FileIO::from_path(&metadata_file_path)
349 .unwrap()
350 .build()
351 .unwrap();
352 let static_identifier = TableIdent::from_strs(["static_ns", "static_table"]).unwrap();
353 let static_table =
354 StaticTable::from_metadata_file(&metadata_file_path, static_identifier, file_io)
355 .await
356 .unwrap();
357 let snapshot_id = static_table
358 .metadata()
359 .current_snapshot()
360 .unwrap()
361 .snapshot_id();
362 assert_eq!(
363 snapshot_id, 3055729675574597004,
364 "snapshot id from metadata don't match"
365 );
366 }
367
368 #[tokio::test]
369 async fn test_static_into_table() {
370 let metadata_file_name = "TableMetadataV2Valid.json";
371 let metadata_file_path = format!(
372 "{}/testdata/table_metadata/{}",
373 env!("CARGO_MANIFEST_DIR"),
374 metadata_file_name
375 );
376 let file_io = FileIO::from_path(&metadata_file_path)
377 .unwrap()
378 .build()
379 .unwrap();
380 let static_identifier = TableIdent::from_strs(["static_ns", "static_table"]).unwrap();
381 let static_table =
382 StaticTable::from_metadata_file(&metadata_file_path, static_identifier, file_io)
383 .await
384 .unwrap();
385 let table = static_table.into_table();
386 assert!(table.readonly());
387 assert_eq!(table.identifier.name(), "static_table");
388 assert_eq!(
389 table.metadata_location(),
390 Some(metadata_file_path).as_deref()
391 );
392 }
393
394 #[tokio::test]
395 async fn test_table_readonly_flag() {
396 let metadata_file_name = "TableMetadataV2Valid.json";
397 let metadata_file_path = format!(
398 "{}/testdata/table_metadata/{}",
399 env!("CARGO_MANIFEST_DIR"),
400 metadata_file_name
401 );
402 let file_io = FileIO::from_path(&metadata_file_path)
403 .unwrap()
404 .build()
405 .unwrap();
406 let metadata_file = file_io.new_input(metadata_file_path).unwrap();
407 let metadata_file_content = metadata_file.read().await.unwrap();
408 let table_metadata =
409 serde_json::from_slice::<TableMetadata>(&metadata_file_content).unwrap();
410 let static_identifier = TableIdent::from_strs(["ns", "table"]).unwrap();
411 let table = Table::builder()
412 .metadata(table_metadata)
413 .identifier(static_identifier)
414 .file_io(file_io.clone())
415 .build()
416 .unwrap();
417 assert!(!table.readonly());
418 assert_eq!(table.identifier.name(), "table");
419 }
420}