Skip to content

Commit 6e29ca7

Browse files
authored
feat (static table): implement a read-only table struct loaded from metadata (#259)
* fixing some broken branch * adding readonly property to Table, and setting readonly value on StaticTable
1 parent 3d08f04 commit 6e29ca7

File tree

1 file changed

+165
-0
lines changed

1 file changed

+165
-0
lines changed

crates/iceberg/src/table.rs

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
use crate::io::FileIO;
2020
use crate::scan::TableScanBuilder;
2121
use crate::spec::{TableMetadata, TableMetadataRef};
22+
use crate::Result;
2223
use crate::TableIdent;
24+
use futures::AsyncReadExt;
2325
use typed_builder::TypedBuilder;
2426

2527
/// Table represents a table in the catalog.
@@ -31,6 +33,8 @@ pub struct Table {
3133
#[builder(setter(into))]
3234
metadata: TableMetadataRef,
3335
identifier: TableIdent,
36+
#[builder(default = false)]
37+
readonly: bool,
3438
}
3539

3640
impl Table {
@@ -62,4 +66,165 @@ impl Table {
6266
pub fn scan(&self) -> TableScanBuilder<'_> {
6367
TableScanBuilder::new(self)
6468
}
69+
70+
/// Returns the flag indicating whether the `Table` is readonly or not
71+
pub fn readonly(&self) -> bool {
72+
self.readonly
73+
}
74+
}
75+
76+
/// `StaticTable` is a read-only table struct that can be created from a metadata file or from `TableMetaData` without a catalog.
77+
/// It can only be used to read metadata and for table scan.
78+
/// # Examples
79+
///
80+
/// ```rust, no_run
81+
/// # use iceberg::io::FileIO;
82+
/// # use iceberg::table::StaticTable;
83+
/// # use iceberg::TableIdent;
84+
/// # async fn example() {
85+
/// let metadata_file_location = "s3://bucket_name/path/to/metadata.json";
86+
/// let file_io = FileIO::from_path(&metadata_file_location).unwrap().build().unwrap();
87+
/// let static_identifier = TableIdent::from_strs(["static_ns", "static_table"]).unwrap();
88+
/// let static_table = StaticTable::from_metadata_file(&metadata_file_location, static_identifier, file_io).await.unwrap();
89+
/// let snapshot_id = static_table
90+
/// .metadata()
91+
/// .current_snapshot()
92+
/// .unwrap()
93+
/// .snapshot_id();
94+
/// # }
95+
/// ```
96+
pub struct StaticTable(Table);
97+
98+
impl StaticTable {
99+
/// Creates a static table from a given `TableMetadata` and `FileIO`
100+
pub async fn from_metadata(
101+
metadata: TableMetadata,
102+
table_ident: TableIdent,
103+
file_io: FileIO,
104+
) -> Result<Self> {
105+
let table = Table::builder()
106+
.metadata(metadata)
107+
.identifier(table_ident)
108+
.file_io(file_io)
109+
.readonly(true)
110+
.build();
111+
112+
Ok(Self(table))
113+
}
114+
/// Creates a static table directly from metadata file and `FileIO`
115+
pub async fn from_metadata_file(
116+
metadata_file_path: &str,
117+
table_ident: TableIdent,
118+
file_io: FileIO,
119+
) -> Result<Self> {
120+
let metadata_file = file_io.new_input(metadata_file_path)?;
121+
let mut metadata_file_reader = metadata_file.reader().await?;
122+
let mut metadata_file_content = String::new();
123+
metadata_file_reader
124+
.read_to_string(&mut metadata_file_content)
125+
.await?;
126+
let table_metadata = serde_json::from_str::<TableMetadata>(&metadata_file_content)?;
127+
Self::from_metadata(table_metadata, table_ident, file_io).await
128+
}
129+
130+
/// Create a TableScanBuilder for the static table.
131+
pub fn scan(&self) -> TableScanBuilder<'_> {
132+
self.0.scan()
133+
}
134+
135+
/// Get TableMetadataRef for the static table
136+
pub fn metadata(&self) -> TableMetadataRef {
137+
self.0.metadata_ref()
138+
}
139+
140+
/// Consumes the `StaticTable` and return it as a `Table`
141+
/// Please use this method carefully as the Table it returns remains detached from a catalog
142+
/// and can't be used to perform modifications on the table.
143+
pub fn into_table(self) -> Table {
144+
self.0
145+
}
146+
}
147+
148+
#[cfg(test)]
149+
mod tests {
150+
use super::*;
151+
#[tokio::test]
152+
async fn test_static_table_from_file() {
153+
let metadata_file_name = "TableMetadataV2Valid.json";
154+
let metadata_file_path = format!(
155+
"{}/testdata/table_metadata/{}",
156+
env!("CARGO_MANIFEST_DIR"),
157+
metadata_file_name
158+
);
159+
let file_io = FileIO::from_path(&metadata_file_path)
160+
.unwrap()
161+
.build()
162+
.unwrap();
163+
let static_identifier = TableIdent::from_strs(["static_ns", "static_table"]).unwrap();
164+
let static_table =
165+
StaticTable::from_metadata_file(&metadata_file_path, static_identifier, file_io)
166+
.await
167+
.unwrap();
168+
let snapshot_id = static_table
169+
.metadata()
170+
.current_snapshot()
171+
.unwrap()
172+
.snapshot_id();
173+
assert_eq!(
174+
snapshot_id, 3055729675574597004,
175+
"snapshot id from metadata don't match"
176+
);
177+
}
178+
179+
#[tokio::test]
180+
async fn test_static_into_table() {
181+
let metadata_file_name = "TableMetadataV2Valid.json";
182+
let metadata_file_path = format!(
183+
"{}/testdata/table_metadata/{}",
184+
env!("CARGO_MANIFEST_DIR"),
185+
metadata_file_name
186+
);
187+
let file_io = FileIO::from_path(&metadata_file_path)
188+
.unwrap()
189+
.build()
190+
.unwrap();
191+
let static_identifier = TableIdent::from_strs(["static_ns", "static_table"]).unwrap();
192+
let static_table =
193+
StaticTable::from_metadata_file(&metadata_file_path, static_identifier, file_io)
194+
.await
195+
.unwrap();
196+
let table = static_table.into_table();
197+
assert!(table.readonly());
198+
assert_eq!(table.identifier.name(), "static_table");
199+
}
200+
201+
#[tokio::test]
202+
async fn test_table_readonly_flag() {
203+
let metadata_file_name = "TableMetadataV2Valid.json";
204+
let metadata_file_path = format!(
205+
"{}/testdata/table_metadata/{}",
206+
env!("CARGO_MANIFEST_DIR"),
207+
metadata_file_name
208+
);
209+
let file_io = FileIO::from_path(&metadata_file_path)
210+
.unwrap()
211+
.build()
212+
.unwrap();
213+
let metadata_file = file_io.new_input(metadata_file_path).unwrap();
214+
let mut metadata_file_reader = metadata_file.reader().await.unwrap();
215+
let mut metadata_file_content = String::new();
216+
metadata_file_reader
217+
.read_to_string(&mut metadata_file_content)
218+
.await
219+
.unwrap();
220+
let table_metadata = serde_json::from_str::<TableMetadata>(&metadata_file_content).unwrap();
221+
let static_identifier = TableIdent::from_strs(["ns", "table"]).unwrap();
222+
let table = Table::builder()
223+
.metadata(table_metadata)
224+
.identifier(static_identifier)
225+
.file_io(file_io)
226+
.build();
227+
assert!(!table.readonly());
228+
assert_eq!(table.identifier.name(), "table");
229+
}
65230
}

0 commit comments

Comments
 (0)