@@ -29,8 +29,8 @@ use typed_builder::TypedBuilder;
2929use uuid:: Uuid ;
3030
3131use crate :: spec:: {
32- FormatVersion , Schema , Snapshot , SnapshotReference , SortOrder , TableMetadataBuilder ,
33- UnboundPartitionSpec , ViewRepresentations ,
32+ FormatVersion , Schema , SchemaId , Snapshot , SnapshotReference , SortOrder , TableMetadata ,
33+ TableMetadataBuilder , UnboundPartitionSpec , ViewRepresentations ,
3434} ;
3535use crate :: table:: Table ;
3636use crate :: { Error , ErrorKind , Result } ;
@@ -312,29 +312,29 @@ pub enum TableRequirement {
312312 LastAssignedFieldIdMatch {
313313 /// The last assigned field id of the table to assert.
314314 #[ serde( rename = "last-assigned-field-id" ) ]
315- last_assigned_field_id : i64 ,
315+ last_assigned_field_id : i32 ,
316316 } ,
317317 /// The table's current schema id must match the requirement.
318318 #[ serde( rename = "assert-current-schema-id" ) ]
319319 CurrentSchemaIdMatch {
320320 /// Current schema id of the table to assert.
321321 #[ serde( rename = "current-schema-id" ) ]
322- current_schema_id : i64 ,
322+ current_schema_id : SchemaId ,
323323 } ,
324324 /// The table's last assigned partition id must match the
325325 /// requirement.
326326 #[ serde( rename = "assert-last-assigned-partition-id" ) ]
327327 LastAssignedPartitionIdMatch {
328328 /// Last assigned partition id of the table to assert.
329329 #[ serde( rename = "last-assigned-partition-id" ) ]
330- last_assigned_partition_id : i64 ,
330+ last_assigned_partition_id : i32 ,
331331 } ,
332332 /// The table's default spec id must match the requirement.
333333 #[ serde( rename = "assert-default-spec-id" ) ]
334334 DefaultSpecIdMatch {
335335 /// Default spec id of the table to assert.
336336 #[ serde( rename = "default-spec-id" ) ]
337- default_spec_id : i64 ,
337+ default_spec_id : i32 ,
338338 } ,
339339 /// The table's default sort order id must match the requirement.
340340 #[ serde( rename = "assert-default-sort-order-id" ) ]
@@ -453,6 +453,140 @@ impl TableUpdate {
453453 }
454454}
455455
456+ impl TableRequirement {
457+ /// Check that the requirement is met by the table metadata.
458+ /// If the requirement is not met, an appropriate error is returned.
459+ ///
460+ /// Provide metadata as `None` if the table does not exist.
461+ pub fn check ( & self , metadata : Option < & TableMetadata > ) -> Result < ( ) > {
462+ if let Some ( metadata) = metadata {
463+ match self {
464+ TableRequirement :: NotExist => {
465+ return Err ( Error :: new (
466+ ErrorKind :: DataInvalid ,
467+ format ! (
468+ "Requirement failed: Table with id {} already exists" ,
469+ metadata. uuid( )
470+ ) ,
471+ ) ) ;
472+ }
473+ TableRequirement :: UuidMatch { uuid } => {
474+ if & metadata. uuid ( ) != uuid {
475+ return Err ( Error :: new (
476+ ErrorKind :: DataInvalid ,
477+ "Requirement failed: Table UUID does not match" ,
478+ )
479+ . with_context ( "expected" , * uuid)
480+ . with_context ( "found" , metadata. uuid ( ) ) ) ;
481+ }
482+ }
483+ TableRequirement :: CurrentSchemaIdMatch { current_schema_id } => {
484+ // ToDo: Harmonize the types of current_schema_id
485+ if metadata. current_schema_id != * current_schema_id {
486+ return Err ( Error :: new (
487+ ErrorKind :: DataInvalid ,
488+ "Requirement failed: Current schema id does not match" ,
489+ )
490+ . with_context ( "expected" , current_schema_id. to_string ( ) )
491+ . with_context ( "found" , metadata. current_schema_id . to_string ( ) ) ) ;
492+ }
493+ }
494+ TableRequirement :: DefaultSortOrderIdMatch {
495+ default_sort_order_id,
496+ } => {
497+ if metadata. default_sort_order ( ) . order_id != * default_sort_order_id {
498+ return Err ( Error :: new (
499+ ErrorKind :: DataInvalid ,
500+ "Requirement failed: Default sort order id does not match" ,
501+ )
502+ . with_context ( "expected" , default_sort_order_id. to_string ( ) )
503+ . with_context (
504+ "found" ,
505+ metadata. default_sort_order ( ) . order_id . to_string ( ) ,
506+ ) ) ;
507+ }
508+ }
509+ TableRequirement :: RefSnapshotIdMatch { r#ref, snapshot_id } => {
510+ let snapshot_ref = metadata. snapshot_for_ref ( r#ref) ;
511+ if let Some ( snapshot_id) = snapshot_id {
512+ let snapshot_ref = snapshot_ref. ok_or ( Error :: new (
513+ ErrorKind :: DataInvalid ,
514+ format ! ( "Requirement failed: Branch or tag `{}` not found" , r#ref) ,
515+ ) ) ?;
516+ if snapshot_ref. snapshot_id ( ) != * snapshot_id {
517+ return Err ( Error :: new (
518+ ErrorKind :: DataInvalid ,
519+ format ! (
520+ "Requirement failed: Branch or tag `{}`'s snapshot has changed" ,
521+ r#ref
522+ ) ,
523+ )
524+ . with_context ( "expected" , snapshot_id. to_string ( ) )
525+ . with_context ( "found" , snapshot_ref. snapshot_id ( ) . to_string ( ) ) ) ;
526+ }
527+ } else if snapshot_ref. is_some ( ) {
528+ // a null snapshot ID means the ref should not exist already
529+ return Err ( Error :: new (
530+ ErrorKind :: DataInvalid ,
531+ format ! (
532+ "Requirement failed: Branch or tag `{}` already exists" ,
533+ r#ref
534+ ) ,
535+ ) ) ;
536+ }
537+ }
538+ TableRequirement :: DefaultSpecIdMatch { default_spec_id } => {
539+ // ToDo: Harmonize the types of default_spec_id
540+ if metadata. default_partition_spec_id ( ) != * default_spec_id {
541+ return Err ( Error :: new (
542+ ErrorKind :: DataInvalid ,
543+ "Requirement failed: Default partition spec id does not match" ,
544+ )
545+ . with_context ( "expected" , default_spec_id. to_string ( ) )
546+ . with_context ( "found" , metadata. default_partition_spec_id ( ) . to_string ( ) ) ) ;
547+ }
548+ }
549+ TableRequirement :: LastAssignedPartitionIdMatch {
550+ last_assigned_partition_id,
551+ } => {
552+ if metadata. last_partition_id != * last_assigned_partition_id {
553+ return Err ( Error :: new (
554+ ErrorKind :: DataInvalid ,
555+ "Requirement failed: Last assigned partition id does not match" ,
556+ )
557+ . with_context ( "expected" , last_assigned_partition_id. to_string ( ) )
558+ . with_context ( "found" , metadata. last_partition_id . to_string ( ) ) ) ;
559+ }
560+ }
561+ TableRequirement :: LastAssignedFieldIdMatch {
562+ last_assigned_field_id,
563+ } => {
564+ if & metadata. last_column_id != last_assigned_field_id {
565+ return Err ( Error :: new (
566+ ErrorKind :: DataInvalid ,
567+ "Requirement failed: Last assigned field id does not match" ,
568+ )
569+ . with_context ( "expected" , last_assigned_field_id. to_string ( ) )
570+ . with_context ( "found" , metadata. last_column_id . to_string ( ) ) ) ;
571+ }
572+ }
573+ } ;
574+ } else {
575+ match self {
576+ TableRequirement :: NotExist => { }
577+ _ => {
578+ return Err ( Error :: new (
579+ ErrorKind :: DataInvalid ,
580+ "Requirement failed: Table does not exist" ,
581+ ) ) ;
582+ }
583+ }
584+ }
585+
586+ Ok ( ( ) )
587+ }
588+ }
589+
456590pub ( super ) mod _serde {
457591 use serde:: { Deserialize as _, Deserializer } ;
458592
@@ -549,7 +683,7 @@ mod tests {
549683 use crate :: spec:: {
550684 FormatVersion , NestedField , NullOrder , Operation , PrimitiveType , Schema , Snapshot ,
551685 SnapshotReference , SnapshotRetention , SortDirection , SortField , SortOrder , Summary ,
552- TableMetadataBuilder , Transform , Type , UnboundPartitionSpec ,
686+ TableMetadata , TableMetadataBuilder , Transform , Type , UnboundPartitionSpec ,
553687 } ;
554688 use crate :: { NamespaceIdent , TableCreation , TableIdent , TableRequirement , TableUpdate } ;
555689
@@ -593,6 +727,167 @@ mod tests {
593727 ) ;
594728 }
595729
730+ fn metadata ( ) -> TableMetadata {
731+ let tbl_creation = TableCreation :: builder ( )
732+ . name ( "table" . to_string ( ) )
733+ . location ( "/path/to/table" . to_string ( ) )
734+ . schema ( Schema :: builder ( ) . build ( ) . unwrap ( ) )
735+ . build ( ) ;
736+
737+ TableMetadataBuilder :: from_table_creation ( tbl_creation)
738+ . unwrap ( )
739+ . assign_uuid ( uuid:: Uuid :: nil ( ) )
740+ . unwrap ( )
741+ . build ( )
742+ . unwrap ( )
743+ }
744+
745+ #[ test]
746+ fn test_check_requirement_not_exist ( ) {
747+ let metadata = metadata ( ) ;
748+ let requirement = TableRequirement :: NotExist ;
749+
750+ assert ! ( requirement. check( Some ( & metadata) ) . is_err( ) ) ;
751+ assert ! ( requirement. check( None ) . is_ok( ) ) ;
752+ }
753+
754+ #[ test]
755+ fn test_check_table_uuid ( ) {
756+ let metadata = metadata ( ) ;
757+
758+ let requirement = TableRequirement :: UuidMatch {
759+ uuid : uuid:: Uuid :: now_v7 ( ) ,
760+ } ;
761+ assert ! ( requirement. check( Some ( & metadata) ) . is_err( ) ) ;
762+
763+ let requirement = TableRequirement :: UuidMatch {
764+ uuid : uuid:: Uuid :: nil ( ) ,
765+ } ;
766+ assert ! ( requirement. check( Some ( & metadata) ) . is_ok( ) ) ;
767+ }
768+
769+ #[ test]
770+ fn test_check_ref_snapshot_id ( ) {
771+ let metadata = metadata ( ) ;
772+
773+ // Ref does not exist but should
774+ let requirement = TableRequirement :: RefSnapshotIdMatch {
775+ r#ref : "my_branch" . to_string ( ) ,
776+ snapshot_id : Some ( 1 ) ,
777+ } ;
778+ assert ! ( requirement. check( Some ( & metadata) ) . is_err( ) ) ;
779+
780+ // Ref does not exist and should not
781+ let requirement = TableRequirement :: RefSnapshotIdMatch {
782+ r#ref : "my_branch" . to_string ( ) ,
783+ snapshot_id : None ,
784+ } ;
785+ assert ! ( requirement. check( Some ( & metadata) ) . is_ok( ) ) ;
786+
787+ // Add snapshot
788+ let record = r#"
789+ {
790+ "snapshot-id": 3051729675574597004,
791+ "sequence-number": 10,
792+ "timestamp-ms": 1515100955770,
793+ "summary": {
794+ "operation": "append"
795+ },
796+ "manifest-list": "s3://b/wh/.../s1.avro",
797+ "schema-id": 0
798+ }
799+ "# ;
800+
801+ let snapshot = serde_json:: from_str :: < Snapshot > ( record) . unwrap ( ) ;
802+ let mut metadata = metadata;
803+ metadata. append_snapshot ( snapshot) ;
804+
805+ // Ref exists and should matches
806+ let requirement = TableRequirement :: RefSnapshotIdMatch {
807+ r#ref : "main" . to_string ( ) ,
808+ snapshot_id : Some ( 3051729675574597004 ) ,
809+ } ;
810+ assert ! ( requirement. check( Some ( & metadata) ) . is_ok( ) ) ;
811+
812+ // Ref exists but does not match
813+ let requirement = TableRequirement :: RefSnapshotIdMatch {
814+ r#ref : "main" . to_string ( ) ,
815+ snapshot_id : Some ( 1 ) ,
816+ } ;
817+ assert ! ( requirement. check( Some ( & metadata) ) . is_err( ) ) ;
818+ }
819+
820+ #[ test]
821+ fn test_check_last_assigned_field_id ( ) {
822+ let metadata = metadata ( ) ;
823+
824+ let requirement = TableRequirement :: LastAssignedFieldIdMatch {
825+ last_assigned_field_id : 1 ,
826+ } ;
827+ assert ! ( requirement. check( Some ( & metadata) ) . is_err( ) ) ;
828+
829+ let requirement = TableRequirement :: LastAssignedFieldIdMatch {
830+ last_assigned_field_id : 0 ,
831+ } ;
832+ assert ! ( requirement. check( Some ( & metadata) ) . is_ok( ) ) ;
833+ }
834+
835+ #[ test]
836+ fn test_check_current_schema_id ( ) {
837+ let metadata = metadata ( ) ;
838+
839+ let requirement = TableRequirement :: CurrentSchemaIdMatch {
840+ current_schema_id : 1 ,
841+ } ;
842+ assert ! ( requirement. check( Some ( & metadata) ) . is_err( ) ) ;
843+
844+ let requirement = TableRequirement :: CurrentSchemaIdMatch {
845+ current_schema_id : 0 ,
846+ } ;
847+ assert ! ( requirement. check( Some ( & metadata) ) . is_ok( ) ) ;
848+ }
849+
850+ #[ test]
851+ fn test_check_last_assigned_partition_id ( ) {
852+ let metadata = metadata ( ) ;
853+
854+ let requirement = TableRequirement :: LastAssignedPartitionIdMatch {
855+ last_assigned_partition_id : 1 ,
856+ } ;
857+ assert ! ( requirement. check( Some ( & metadata) ) . is_err( ) ) ;
858+
859+ let requirement = TableRequirement :: LastAssignedPartitionIdMatch {
860+ last_assigned_partition_id : 0 ,
861+ } ;
862+ assert ! ( requirement. check( Some ( & metadata) ) . is_ok( ) ) ;
863+ }
864+
865+ #[ test]
866+ fn test_check_default_spec_id ( ) {
867+ let metadata = metadata ( ) ;
868+
869+ let requirement = TableRequirement :: DefaultSpecIdMatch { default_spec_id : 1 } ;
870+ assert ! ( requirement. check( Some ( & metadata) ) . is_err( ) ) ;
871+
872+ let requirement = TableRequirement :: DefaultSpecIdMatch { default_spec_id : 0 } ;
873+ assert ! ( requirement. check( Some ( & metadata) ) . is_ok( ) ) ;
874+ }
875+
876+ #[ test]
877+ fn test_check_default_sort_order_id ( ) {
878+ let metadata = metadata ( ) ;
879+
880+ let requirement = TableRequirement :: DefaultSortOrderIdMatch {
881+ default_sort_order_id : 1 ,
882+ } ;
883+ assert ! ( requirement. check( Some ( & metadata) ) . is_err( ) ) ;
884+
885+ let requirement = TableRequirement :: DefaultSortOrderIdMatch {
886+ default_sort_order_id : 0 ,
887+ } ;
888+ assert ! ( requirement. check( Some ( & metadata) ) . is_ok( ) ) ;
889+ }
890+
596891 #[ test]
597892 fn test_table_uuid ( ) {
598893 test_serde_json (
0 commit comments