# 合约Table迁移

在更新合约过程中,如果对Table数据结构进行了变动,会造成旧结构中的数据,无法修改和访问。为了合理使用智能合约持久化存储表,可以按照以下步骤,对合约升级过程中的Table变动进行修改。

# 1、创建新Table

新创建的Table类型,需要兼容旧的Table类型。首先在合约中添加新的Table类型,并保留旧的Table类型,例如:

class movetable : public contract
{
  public:
    movetable(uint64_t id)
        : contract(id),accounts(_self,_self),accountnews(_self,_self){}

    //@abi action
    void setstat(bool states){
        ...
        auto itor = stats.find(0);
        if(itor == stats.end()){
            stats.emplace(_self,[&](auto &o) {
                o.id = stats.available_primary_key();;
                o.isable = states;
            });
        }else{
            stats.modify(_self,itor,[&](auto &o) {
                o.id = stats.available_primary_key();;
                o.isable = states;
            });
        }
        ...
    }
    //@abi action
    void add(std::string name){
        accounts.emplace(_self, [&](auto &o) {
            o.id = accounts.available_primary_key();;
            o.name = name;
        });
    }
  private:
    //@abi table stat i64
    struct stat {
        uint64_t id;
        uint64_t isable;
        uint64_t primary_key() const { return id; }
        GRAPHENE_SERIALIZE(stat, (id)(isable))
    };
    typedef graphene::multi_index<N(stat), stat> stat_index;
    stat_index stats;

    // 旧的Table类型
    //@abi table account i64
    struct account {
        uint64_t id;
        std::string name;
        uint64_t primary_key() const { return id; }
        GRAPHENE_SERIALIZE(account, (id)(name))
    };
    typedef graphene::multi_index<N(account), account> account_index;
    account_index accounts;

    // 新的Table类型
    //@abi table accountnew i64
    struct accountnew {
        uint64_t id;
        std::string name;
        uint64_t age;
        uint64_t primary_key() const { return id; }
        GRAPHENE_SERIALIZE(accountnew, (id)(name)(age))
    };
    typedef graphene::multi_index<N(accountnew), accountnew> accountnew_index;
    accountnew_index accountnews;
};

# 2、将旧Table中数据迁移至新Table

迁移数据之前,需要先设置参数isable为FALSE,禁止向旧的Table中插入数据,然后增加move函数,将旧Table中的数据拷贝到新Table中,示例代码如下:

class movetable : public contract
{
  public:
    movetable(uint64_t id)
        : contract(id),accounts(_self,_self),accountnews(_self,_self){}
    ...
    //@abi action
    void add(std::string name){
        auto itor = stats.find(0);
        graphene_assert(itor->isable, "now can't add data to table");
        accounts.emplace(_self, [&](auto &o) {
            o.id = accounts.available_primary_key();
            o.name = name;
        });
    }
    //@abi action
    void move(){
        auto itor = stats.find(0);
        stats.modify(_self,itor, [&](auto &o) {
            o.isable = FALSE;
        });
        for(auto i =accounts.begin(); i!=accounts.end(); i++){
            accountnews.emplace(_self, [&](auto &o) {
                o.id = i->id;
                o.name = i->name;
                o.age = 0;
            });
        }
    }
  private:
    ...
    accountnew_index accountnews;
};

# 3、删除旧Table数据

在数据迁移完成之后,需要先校验新Table中数据是否与旧Table中数据一致。如一致,则删除旧Table数据,新数据插入到新Table中。修改合约状态stat为TRUE,修改add接口,并添加delete接口,示例代码如下:

class movetable : public contract
{
  public:
    movetable(uint64_t id)
        : contract(id),accounts(_self,_self),accountnews(_self,_self){}
    ...
    //@abi action
    void add(std::string name){
        auto itor = stats.find(0);
        graphene_assert(itor->isable, "now can't add data to table");
        accountnews.emplace(_self, [&](auto &o) {
            o.id = accountnews.available_primary_key();
            o.name = name;
        });
    }
    ...
    //@abi action
    void delete(){
        for(auto i =accounts.begin(); i!=accounts.end(); i++){
            accounts.erase(i);
        }
    }
  private:
    ...
    accountnew_index accountnews;
};