有些时候,由于前期考虑不周,或者后期设计升级,导致合约table 字段需要增加,或者类型需要更改,所以需要数据迁移,
下面举例我常用的升级方法
假设目前合约内有个table xxxinfo
struct [[eosio::table("xxxinfo"), eosio::contract("eosxxx.game")]] xxxinfo{
uint64_t id;
uint64_t test; // 为测试添加的字段
uint8_t test1; // 为测试添加的字段
auto primary_key() const { return id; }
};
typedef eosio::multi_index<"xxxinfo"_n, xxxinfo> xxxinfo_tables;
现在升级需要解决的问题是test 当初设计字段类型过大,导致ram 浪费,test1 选型过小,增加 test2字段{uint32_t}.
在合约中增加新的表结构xxxinfo1 及其对象,并修正上面问题
struct [[eosio::table("xxxinfo1"), eosio::contract("eosxxx.game")]] xxxinfo1{
uint64_t id;
uint32_t test; // 为测试添加的字段
uint16_t test1; // 为测试添加的字段
uint32_t test2; // 为测试添加的字段
auto primary_key() const { return id; }
};
typedef eosio::multi_index<"xxxinfo1"_n, xxxinfo1> xxxinfo1_tables;
此时合约内同时存在 xxxinfo1 和 xxxinfo1两张表.
增加 迁移执行的action 接口
//.h
ACTION migratexxx();
//.cpp
void migratexxx(){
xxxinfo1_tables xxxinfo1_table(_self, _self.value);
xxxinfo_tables xxxinfo_table(_self, _self.value);
auto itr = xxxinfo_table.begin();
while(itr != xxxinfo_table.end()){
xxxinfo1_table.emplace( _self, [&]( auto& h ) {
h.id = xxxinfo1_table.available_primary_key();
h.test = itr->test;
h.test1= itr->test1;
});
itr ++;
}
}
停止Dapp,避免迁移期间数据改变,然后执行action
cleos -u https://api.eoslaomao.com push action 合约账户 migratexxx '{}' -p 合约账户
如果数据较多,且数据是累计增长(不修改历史数据),可以分区间执行迁移,迁移过程中,可以不停止dapp,等迁移差不多追上旧表了,再暂停dapp,然后等数据全部迁移完.
修正合约中的新表为
struct [[eosio::table("xxxinfo1"), eosio::contract("eosxxx.game")]] xxxinfo{
uint64_t id;
uint32_t test; // 为测试添加的字段
uint16_t test1; // 为测试添加的字段
uint32_t test2; // 为测试添加的字段
auto primary_key() const { return id; }
};
typedef eosio::multi_index<"xxxinfo1"_n, xxxinfo> xxxinfo_tables;
将旧表修改为
struct [[eosio::table("xxxinfo"), eosio::contract("eosxxx.game")]] xxxinfo_bak{
uint64_t id;
uint64_t test; // 为测试添加的字段
uint8_t test1; // 为测试添加的字段
auto primary_key() const { return id; }
};
typedef eosio::multi_index<"xxxinfo"_n, xxxinfo_bak> xxxinfo_bak_tables;
修正前后端调用的table名,重新上线,并运行dapp, 建议等 运行一段时间,在删除旧表
增加清理旧表的action
//.h
ACTION clearxxxbak();
//.cpp
void clearxxxbak(){
xxxinfo_bak_tables xxxinfo_bak_table(_self, _self.value);
auto itr = xxxinfo_bak_table.begin();
while(itr != xxxinfo_bak_table.end()){
itr = xxxinfo_bak_table.erase(itr);
}
}
然后执行action
cleos -u https://api.eoslaomao.com push action 合约账户 clearxxxbak '{}' -p 合约账户
最后再删除 合约内旧表及对象 就完成了此次合约表升级过程.