@@ -69,6 +69,8 @@ use databend_common_meta_app::schema::ListDroppedTableResp;
6969use databend_common_meta_app:: schema:: ListTableReq ;
7070use databend_common_meta_app:: schema:: RenameTableReply ;
7171use databend_common_meta_app:: schema:: RenameTableReq ;
72+ use databend_common_meta_app:: schema:: SwapTableReply ;
73+ use databend_common_meta_app:: schema:: SwapTableReq ;
7274use databend_common_meta_app:: schema:: TableCopiedFileNameIdent ;
7375use databend_common_meta_app:: schema:: TableId ;
7476use databend_common_meta_app:: schema:: TableIdHistoryIdent ;
@@ -712,6 +714,192 @@ where
712714 }
713715 }
714716
717+ #[ logcall:: logcall]
718+ #[ fastrace:: trace]
719+ async fn swap_table ( & self , req : SwapTableReq ) -> Result < SwapTableReply , KVAppError > {
720+ debug ! ( req : ? =( & req) ; "SchemaApi: {}" , func_name!( ) ) ;
721+
722+ let mut trials = txn_backoff ( None , func_name ! ( ) ) ;
723+ loop {
724+ trials. next ( ) . unwrap ( ) ?. await ;
725+
726+ // Get databases
727+ let tenant_dbname_left = req. origin_table . db_name_ident ( ) ;
728+
729+ let ( seq_db_id_left, db_meta_left) =
730+ get_db_or_err ( self , & tenant_dbname_left, "swap_table: tenant_dbname_left" ) . await ?;
731+
732+ let dbid_tbname_left = DBIdTableName {
733+ db_id : * seq_db_id_left. data ,
734+ table_name : req. origin_table . table_name . clone ( ) ,
735+ } ;
736+
737+ let ( tb_id_seq_left, table_id_left) = get_u64_value ( self , & dbid_tbname_left) . await ?;
738+ if req. if_exists && tb_id_seq_left == 0 {
739+ return Ok ( SwapTableReply { } ) ;
740+ }
741+ assert_table_exist (
742+ tb_id_seq_left,
743+ & req. origin_table ,
744+ "swap_table: origin_table" ,
745+ ) ?;
746+
747+ let dbid_tbname_right = DBIdTableName {
748+ db_id : * seq_db_id_left. data ,
749+ table_name : req. target_table_name . clone ( ) ,
750+ } ;
751+
752+ let ( tb_id_seq_right, table_id_right) = get_u64_value ( self , & dbid_tbname_right) . await ?;
753+ if req. if_exists && tb_id_seq_right == 0 {
754+ return Ok ( SwapTableReply { } ) ;
755+ }
756+ assert_table_exist (
757+ tb_id_seq_right,
758+ & TableNameIdent {
759+ tenant : req. origin_table . tenant . clone ( ) ,
760+ db_name : req. origin_table . db_name . clone ( ) ,
761+ table_name : req. target_table_name . clone ( ) ,
762+ } ,
763+ "swap_table: target_table" ,
764+ ) ?;
765+
766+ // Get table id lists
767+ let dbid_tbname_idlist_left = TableIdHistoryIdent {
768+ database_id : * seq_db_id_left. data ,
769+ table_name : req. origin_table . table_name . clone ( ) ,
770+ } ;
771+ let dbid_tbname_idlist_right = TableIdHistoryIdent {
772+ database_id : * seq_db_id_left. data ,
773+ table_name : req. target_table_name . clone ( ) ,
774+ } ;
775+
776+ let seq_table_history_left = self . get_pb ( & dbid_tbname_idlist_left) . await ?;
777+ let seq_table_history_right = self . get_pb ( & dbid_tbname_idlist_right) . await ?;
778+
779+ let tb_id_list_seq_left = seq_table_history_left. seq ( ) ;
780+ let tb_id_list_seq_right = seq_table_history_right. seq ( ) ;
781+
782+ let mut tb_id_list_left = seq_table_history_left
783+ . into_value ( )
784+ . unwrap_or_else ( || TableIdList :: new_with_ids ( [ table_id_left] ) ) ;
785+ let mut tb_id_list_right = seq_table_history_right
786+ . into_value ( )
787+ . unwrap_or_else ( || TableIdList :: new_with_ids ( [ table_id_right] ) ) ;
788+
789+ // Validate table IDs in history lists
790+ {
791+ let last_left = tb_id_list_left. last ( ) . copied ( ) ;
792+ if Some ( table_id_left) != last_left {
793+ let err_message = format ! (
794+ "swap_table {:?} but last table id conflict, id list last: {:?}, current: {}" ,
795+ req. origin_table, last_left, table_id_left
796+ ) ;
797+ error ! ( "{}" , err_message) ;
798+ return Err ( KVAppError :: AppError ( AppError :: UnknownTable (
799+ UnknownTable :: new ( & req. origin_table . table_name , err_message) ,
800+ ) ) ) ;
801+ }
802+
803+ let last_right = tb_id_list_right. last ( ) . copied ( ) ;
804+ if Some ( table_id_right) != last_right {
805+ let err_message = format ! (
806+ "swap_table {:?} but last table id conflict, id list last: {:?}, current: {}" ,
807+ req. target_table_name, last_right, table_id_right
808+ ) ;
809+ error ! ( "{}" , err_message) ;
810+ return Err ( KVAppError :: AppError ( AppError :: UnknownTable (
811+ UnknownTable :: new ( & req. target_table_name , err_message) ,
812+ ) ) ) ;
813+ }
814+ }
815+
816+ // Get table id to name mappings
817+ let table_id_to_name_key_left = TableIdToName {
818+ table_id : table_id_left,
819+ } ;
820+ let table_id_to_name_key_right = TableIdToName {
821+ table_id : table_id_right,
822+ } ;
823+ let table_id_to_name_seq_left = self . get_seq ( & table_id_to_name_key_left) . await ?;
824+ let table_id_to_name_seq_right = self . get_seq ( & table_id_to_name_key_right) . await ?;
825+
826+ // Prepare new mappings after swap
827+ let db_id_table_name_left = DBIdTableName {
828+ db_id : * seq_db_id_left. data ,
829+ table_name : req. origin_table . table_name . clone ( ) ,
830+ } ;
831+ let db_id_table_name_right = DBIdTableName {
832+ db_id : * seq_db_id_left. data ,
833+ table_name : req. target_table_name . clone ( ) ,
834+ } ;
835+
836+ {
837+ // Update history lists: remove current table IDs
838+ tb_id_list_left. pop ( ) ;
839+ tb_id_list_right. pop ( ) ;
840+ // Add swapped table IDs
841+ tb_id_list_left. append ( table_id_right) ;
842+ tb_id_list_right. append ( table_id_left) ;
843+
844+ let txn = TxnRequest :: new (
845+ vec ! [
846+ // Ensure databases haven't changed
847+ txn_cond_seq( & seq_db_id_left. data, Eq , db_meta_left. seq) ,
848+ // Ensure table name->table_id mappings haven't changed
849+ txn_cond_seq( & dbid_tbname_left, Eq , tb_id_seq_left) ,
850+ txn_cond_seq( & dbid_tbname_right, Eq , tb_id_seq_right) ,
851+ // Ensure table history lists haven't changed
852+ txn_cond_seq( & dbid_tbname_idlist_left, Eq , tb_id_list_seq_left) ,
853+ txn_cond_seq( & dbid_tbname_idlist_right, Eq , tb_id_list_seq_right) ,
854+ // Ensure table_id->name mappings haven't changed
855+ txn_cond_seq( & table_id_to_name_key_left, Eq , table_id_to_name_seq_left) ,
856+ txn_cond_seq( & table_id_to_name_key_right, Eq , table_id_to_name_seq_right) ,
857+ ] ,
858+ vec ! [
859+ // Swap table name->table_id mappings
860+ txn_op_put( & dbid_tbname_left, serialize_u64( table_id_right) ?) , /* origin_table_name -> target_table_id */
861+ txn_op_put( & dbid_tbname_right, serialize_u64( table_id_left) ?) , /* target_table_name -> origin_table_id */
862+ // Update database metadata sequences
863+ txn_op_put( & seq_db_id_left. data, serialize_struct( & * db_meta_left) ?) ,
864+ // Update table history lists
865+ txn_op_put(
866+ & dbid_tbname_idlist_left,
867+ serialize_struct( & tb_id_list_left) ?,
868+ ) ,
869+ txn_op_put(
870+ & dbid_tbname_idlist_right,
871+ serialize_struct( & tb_id_list_right) ?,
872+ ) ,
873+ // Update table_id->name mappings
874+ txn_op_put(
875+ & table_id_to_name_key_left,
876+ serialize_struct( & db_id_table_name_right) ?,
877+ ) , // origin_table_id -> target_table_name
878+ txn_op_put(
879+ & table_id_to_name_key_right,
880+ serialize_struct( & db_id_table_name_left) ?,
881+ ) , // target_table_id -> origin_table_name
882+ ] ,
883+ ) ;
884+
885+ let ( succ, _responses) = send_txn ( self , txn) . await ?;
886+
887+ debug ! (
888+ origin_table : ? =( & req. origin_table) ,
889+ target_table_name : ? =( & req. target_table_name) ,
890+ table_id_left : ? =( & table_id_left) ,
891+ table_id_right : ? =( & table_id_right) ,
892+ succ = succ;
893+ "swap_table"
894+ ) ;
895+
896+ if succ {
897+ return Ok ( SwapTableReply { } ) ;
898+ }
899+ }
900+ }
901+ }
902+
715903 #[ logcall:: logcall]
716904 #[ fastrace:: trace]
717905 async fn truncate_table (
0 commit comments