1919use crate :: io:: FileIO ;
2020use crate :: scan:: TableScanBuilder ;
2121use crate :: spec:: { TableMetadata , TableMetadataRef } ;
22+ use crate :: Result ;
2223use crate :: TableIdent ;
24+ use futures:: AsyncReadExt ;
2325use 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
3640impl 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