multi_index es la interfaz de la base de datos de EOS, a través de la cual se pueden agregar, eliminar, modificar y verificar datos. Estas funciones son las siguientes:
- emplace (aumento)
- borrar (删)
- modificar
- obtener / encontrar (comprobar)
Hoy hablo principalmente de la función de emplazamiento
emplazamiento
Agregar un nuevo objeto (fila) a la tabla
const_iterator emplace( unit64_t payer, Lambda&& constructor )
- parámetro
pagador: la cuenta que paga por el almacenamiento utilizado por el nuevo objeto;
constructor: la función lambda permite que el objeto recién creado se inicialice en su lugar.
- valor de retorno
Devuelve un iterador de clave principal del objeto recién creado
- Condición previa
El pagador es una cuenta válida autorizada por la Acción actual, que permite el pago por el uso del almacenamiento.
- Resultado de la operación
Se crea un nuevo objeto con una clave primaria única en la tabla de índices múltiples;
Este objeto se serializará y luego se escribirá en la tabla;
Si la tabla no existe, cree la tabla.
El índice secundario se actualiza para hacer referencia al objeto recién agregado;
Si las tablas de índice secundarias no existen, créalas.
El pagador paga por el almacenamiento utilizado para crear el nuevo objeto;
Si es necesario crear la tabla de índices múltiples y la tabla de índice secundaria, el pagador paga por la creación de la tabla.
- anormal
Cuando el receptor actual (parámetro de código de multi_index) no es el propietario de la tabla, se lanza una excepción.
Uso de emplazamiento
A continuación, se muestra un ejemplo del artículo anterior:
//添加联系人
//@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;
});
}
Para obtener contenido más detallado, consulte este artículo: Ejemplos de contratos inteligentes que utilizan bases de datos .
colocar código fuente
//函数模板
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};
}
El código clave está en esta línea:
i.__primary_itr = db_store_i64( _scope, TableName, payer, pk, buffer, size );
Esta es una función definida en 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);
Su implementación también está en wasm_interface.cpp , definido en la clase database_api :
Llama a la función apply_context :: db_store_i64 , que es una serie de operaciones en la base de datos. El código fuente es el siguiente:
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 );
}
Un agradecimiento especial: @ 松果 por su apoyo