有些时候,由于前期考虑不周,或者后期设计升级,导致合约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 合约账户
最后再删除 合约内旧表及对象 就完成了此次合约表升级过程.