EOS源码分析1 - 写入数据到Multi-Index表,详解emplace函数

multi_index是EOS的数据库接口,通过它可以实现对数据的增删改查,就是下面这些函数:

  • emplace(增)
  • erase(删)
  • modify(改)
  • get/find(查)

今天主要讲emplace函数

emplace

添加一个新对象(row)到表中

const_iterator emplace( unit64_t payer, Lambda&& constructor )
  • 参数

payer:为新对象使用的存储付费的账户;

constructor:lambda函数,可以让新创建的对象就地初始化。

  • 返回值

返回一个当前新创建的对象的主键迭代器

  • 前置条件

payer是被当前Action授权的有效账户,允许为使用存储付费

  • 操作结果

带有唯一主键的新对象在multi-index表中被创建;

这个对象会被序列化,然后写入表中;

如果表不存在,则创建表。

二级索引被更新,用以引用新添加的对象;

如果二级索引表不存在,则创建它们。

payer为创建新对象所使用的存储付费;

如果multi-index表和二级索引表需要被创建,则payer为表的创建付费。

  • 异常

当前接收者(multi_index的code参数)不是表的拥有者时,抛出异常。

 

 

emplace的使用

这里列一个之前文章的例子:

//添加联系人
//@abi action
void add(const account_name account, const string& name, uint64_t phone) {

    //获取授权,如果没有授权,Action调用会中止,事务会回滚
    require_auth(account);

    //address_index是自己定义的eosio::multi_index

    //实例化address数据表(multi_index),参数用于建立对表的访问权限
    address_index addresses(_self, _self);

    //multi_index的find函数通过主键(primary_key)查询数据,返回迭代器itr
    //auto关键字会自动匹配类型
    auto itr = addresses.find(account);
    //如果判断条件不成立,则终止执行并打印错误信息
    eosio_assert(itr == addresses.end(), "Address for account already exists");

    //添加数据
    //使用存储需要付费,第一个参数account是付费的账户
    addresses.emplace(account, [&](auto& address){
        address.account = account;
        address.name = name;
        address.phone = phone;
    });
}

更详细的内容,参考这篇文章:使用数据库的智能合约实例

 

 

emplace源码

//函数模板
template<typename Lambda>
const_iterator emplace( uint64_t payer, Lambda&& constructor ) {
    using namespace _multi_index_detail;

    //合约的拥有者才能添加数据
    eosio_assert( _code == current_receiver(), "cannot create objects in table of another contract" );

    //make_unique是C++14新特性,这里可以理解为创建对象
    auto itm = std::make_unique<item>( this, [&]( auto& i ){
        T& obj = static_cast<T&>(i);
        constructor( obj );

        size_t size = pack_size( obj );

        void* buffer = max_stack_buffer_size < size ? malloc(size) : alloca(size);

        datastream<char*> ds( (char*)buffer, size );
        ds << obj;

        //获取主键
        auto pk = obj.primary_key();

        //关键代码,存储数据
        i.__primary_itr = db_store_i64( _scope, TableName, payer, pk, buffer, size );

        if ( max_stack_buffer_size < size ) {
           free(buffer);
        }

        if( pk >= _next_primary_key )
           _next_primary_key = (pk >= no_available_primary_key) ? no_available_primary_key : (pk + 1);

        hana::for_each( _indices, [&]( auto& idx ) {
           typedef typename decltype(+hana::at_c<0>(idx))::type index_type;

           i.__iters[index_type::number()] = secondary_index_db_functions<typename index_type::secondary_key_type>::db_idx_store( _scope, index_type::name(), payer, obj.primary_key(), index_type::extract_secondary_key(obj) );
        });
    });

    const item* ptr = itm.get();
    auto pk   = itm->primary_key();
    auto pitr = itm->__primary_itr;

    _items_vector.emplace_back( std::move(itm), pk, pitr );

    return {this, ptr};
}

 

关键代码在这一行:

i.__primary_itr = db_store_i64( _scope, TableName, payer, pk, buffer, size );

 

这是一个定义在eosiolib/db.h中的函数:

int32_t db_store_i64(account_name scope, table_name table, account_name payer, uint64_t id,  const void* data, uint32_t len);

 

它的实现同样在wasm_interface.cpp中,定义在database_api类:

 

它调用了apply_context::db_store_i64函数,这个函数是一系列对数据库的操作,源码如下:

int apply_context::db_store_i64( uint64_t code, uint64_t scope, uint64_t table, const account_name& payer, uint64_t id, const char* buffer, size_t buffer_size ) {
    //通过表名在数据库中查找表,如果不存在,则创建
    const auto& tab = find_or_create_table( code, scope, table, payer );
    auto tableid = tab.id;

    FC_ASSERT( payer != account_name(), "must specify a valid account to pay for new record" );

    //创建数据
    const auto& obj = db.create<key_value_object>( [&]( auto& o ) {
        o.t_id        = tableid;
         o.primary_key = id;
        o.value.resize( buffer_size );
        o.payer       = payer;
        memcpy( o.value.data(), buffer, buffer_size );
    });

    //修改count
    db.modify( tab, [&]( auto& t ) {
        ++t.count;
    });

    int64_t billable_size = (int64_t)(buffer_size + config::billable_size_v<key_value_object>);
    //更新数据库使用状态
    update_db_usage( payer, billable_size);

    keyval_cache.cache_table( tab );
    return keyval_cache.add( obj );
}

 

特别鸣谢:@松果  的支持

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北纬32.6

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值