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