EOS source code analysis 1-write data to Multi-Index table, explain emplace function in detail

multi_index is the database interface of EOS, through which data can be added, deleted, modified and checked. These functions are as follows:

  • emplace (increase)
  • erase(删)
  • modify
  • get/find (check)

Today I mainly talk about the emplace function

emplace

Add a new object (row) to the table

const_iterator emplace( unit64_t payer, Lambda&& constructor )
  • parameter

payer: the account that pays for the storage used by the new object;

constructor: The lambda function allows the newly created object to be initialized in place.

  • return value

Returns a primary key iterator of the newly created object

  • Precondition

Payer is a valid account authorized by the current Action, allowing payment for the use of storage

  • Operation result

A new object with a unique primary key is created in the multi-index table;

This object will be serialized and then written to the table;

If the table does not exist, create the table.

The secondary index is updated to refer to the newly added object;

If the secondary index tables do not exist, create them.

The payer pays for the storage used to create the new object;

If the multi-index table and the secondary index table need to be created, the payer pays for the creation of the table.

  • abnormal

When the current receiver (code parameter of multi_index) is not the owner of the table, an exception is thrown.

 

 

Use of emplace

Here is an example from the previous article:

//添加联系人
//@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;
    });
}

For more detailed content, refer to this article: Examples of Smart Contracts Using Databases .

 

 

emplace source code

//函数模板
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};
}

 

The key code is in this line:

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

 

This is a function defined in 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);

 

Its implementation is also in wasm_interface.cpp , defined in the database_api class:

 

It calls the apply_context::db_store_i64 function, which is a series of operations on the database. The source code is as follows:

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 );
}

 

Special thanks: @松果 for your support

Guess you like

Origin blog.csdn.net/weixin_39842528/article/details/90608503