From 49238b428a10b586699aec977120ff903bb7a78d Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 6 Feb 2018 14:00:52 -0500 Subject: [PATCH] implement generic index framework --- libraries/chain/chain_controller.cpp | 1 + .../include/eosio/chain/apply_context.hpp | 243 ++++++++++++++---- .../contracts/contract_table_objects.hpp | 51 +++- libraries/chain/include/eosio/chain/types.hpp | 2 + libraries/chain/wasm_interface.cpp | 30 +++ 5 files changed, 273 insertions(+), 54 deletions(-) diff --git a/libraries/chain/chain_controller.cpp b/libraries/chain/chain_controller.cpp index a45e7ce8e..5c7de657d 100644 --- a/libraries/chain/chain_controller.cpp +++ b/libraries/chain/chain_controller.cpp @@ -1047,6 +1047,7 @@ void chain_controller::_initialize_indexes() { _db.add_index(); _db.add_index(); _db.add_index(); + _db.add_index(); _db.add_index(); diff --git a/libraries/chain/include/eosio/chain/apply_context.hpp b/libraries/chain/include/eosio/chain/apply_context.hpp index fc3a58add..75aa98792 100644 --- a/libraries/chain/include/eosio/chain/apply_context.hpp +++ b/libraries/chain/include/eosio/chain/apply_context.hpp @@ -19,13 +19,196 @@ using contracts::key_value_object; class chain_controller; class apply_context { + private: + template + class iterator_cache { + public: + typedef contracts::table_id_object table_id_object; + + iterator_cache(){ + _iterator_to_object.reserve(32); + } + + void cache_table( const table_id_object& tobj ) { + _table_cache[tobj.id] = &tobj; + } + + const table_id_object& get_table( table_id_object::id_type i ) { + auto itr = _table_cache.find(i); + FC_ASSERT( itr != _table_cache.end(), "an invariant was broken, table should be in cache" ); + return *_table_cache[i]; + } + + const T& get( int iterator ) { + FC_ASSERT( iterator >= 0, "invalid iterator" ); + FC_ASSERT( iterator < _iterator_to_object.size(), "iterator out of range" ); + auto result = _iterator_to_object[iterator]; + FC_ASSERT( result, "reference of deleted object" ); + return *result; + } + + void remove( int iterator, const T& obj ) { + _iterator_to_object[iterator] = nullptr; + _object_to_iterator.erase( &obj ); + } + + int add( const T& obj ) { + auto itr = _object_to_iterator.find( &obj ); + if( itr != _object_to_iterator.end() ) + return itr->second; + + _iterator_to_object.push_back( &obj ); + _object_to_iterator[&obj] = _iterator_to_object.size() - 1; + + return _iterator_to_object.size() - 1; + } + + private: + map _table_cache; + vector _iterator_to_object; + map _object_to_iterator; + }; public: + template + class generic_index + { + public: + typedef typename ObjectType::secondary_key_type secondary_key_type; + generic_index( apply_context& c ):context(c){} + + int store( uint64_t scope, uint64_t table, const account_name& payer, + uint64_t id, const secondary_key_type& value ) + { + FC_ASSERT( payer != account_name(), "must specify a valid account to pay for new record" ); + + context.require_write_lock( scope ); + + const auto& tab = context.find_or_create_table( scope, context.receiver, table ); + + const auto& obj = context.mutable_db.create( [&]( auto& o ){ + o.t_id = tab.id; + o.primary_key = id; + o.secondary_key = value; + o.payer = payer; + }); + + context.mutable_db.modify( tab, [&]( auto& t ) { + ++t.count; + }); + + context.update_db_usage( payer, sizeof(secondary_key_type)+200 ); + + itr_cache.cache_table( tab ); + return itr_cache.add( obj ); + } + + void remove( int iterator ) { + const auto& obj = itr_cache.get( iterator ); + context.update_db_usage( obj.payer, -( sizeof(secondary_key_type)+200 ) ); + + const auto& table_obj = itr_cache.get_table( obj.t_id ); + context.require_write_lock( table_obj.scope ); + + context.mutable_db.modify( table_obj, [&]( auto& t ) { + --t.count; + }); + context.mutable_db.remove( obj ); + + itr_cache.remove( iterator, obj ); + } + + void update( int iterator, account_name payer, const secondary_key_type& secondary ) { + const auto& obj = itr_cache.get( iterator ); + + if( payer == account_name() ) payer = obj.payer; + + if( obj.payer != payer ) { + context.update_db_usage( obj.payer, -(sizeof(secondary_key_type)+200) ); + context.update_db_usage( payer, +(sizeof(secondary_key_type)+200) ); + } + + context.mutable_db.modify( obj, [&]( auto& o ) { + o.secondary_key = secondary; + o.payer = payer; + }); + } + + int find_secondary( uint64_t code, uint64_t scope, uint64_t table, const secondary_key_type& secondary ) { + auto tab = context.find_table( scope, context.receiver, table ); + if( !tab ) return -1; + + const auto* obj = context.db.find( boost::make_tuple( tab->id, secondary ) ); + if( !obj ) return -1; + + itr_cache.cache_table( *tab ); + return itr_cache.add( *obj ); + } + + int lowerbound_secondary( uint64_t code, uint64_t scope, uint64_t table, const secondary_key_type& secondary ) { + auto tab = context.find_table( scope, context.receiver, table ); + if( !tab ) return -1; + + const auto& idx = context.db.get_index< chainbase::get_index_type::type, contracts::by_secondary >(); + auto itr = idx.lower_bound( boost::make_tuple( tab->id, secondary ) ); + if( itr == idx.end() ) return -1; + if( itr->t_id != tab.id ) return -1; + + itr_cache.cache_table( *tab ); + return itr_cache.add( *itr ); + } + + int upperbound_secondary( uint64_t code, uint64_t scope, uint64_t table, const secondary_key_type& secondary ) { + auto tab = context.find_table( scope, context.receiver, table ); + if( !tab ) return -1; + + const auto& idx = context.db.get_index< chainbase::get_index_type::type, contracts::by_secondary >(); + auto itr = idx.upper_bound( boost::make_tuple( tab->id, secondary ) ); + if( itr == idx.end() ) return -1; + if( itr->t_id != tab.id ) return -1; + + itr_cache.cache_table( *tab ); + return itr_cache.add( *itr ); + } + + int find_primary( uint64_t code, uint64_t scope, uint64_t table, uint64_t primary ) { + auto tab = context.find_table( scope, context.receiver, table ); + if( !tab ) return -1; + + const auto* obj = context.db.find( boost::make_tuple( tab->id, primary ) ); + if( !obj ) return -1; + + itr_cache.cache_table( *tab ); + return itr_cache.add( *obj ); + } + + void get( int iterator, uint64_t& primary, secondary_key_type& secondary ) { + const auto& obj = itr_cache.get( iterator ); + primary = obj.primary_key; + secondary = obj.secondary_key; + } + + private: + apply_context& context; + iterator_cache itr_cache; + }; + + + + apply_context(chain_controller& con, chainbase::database& db, const action& a, const transaction_metadata& trx_meta, uint32_t checktime_limit) - :controller(con), db(db), act(a), mutable_controller(con), - mutable_db(db), used_authorizations(act.authorization.size(), false), - trx_meta(trx_meta), _checktime_limit(checktime_limit) {} + :controller(con), + db(db), + act(a), + mutable_controller(con), + mutable_db(db), + used_authorizations(act.authorization.size(), false), + trx_meta(trx_meta), + idx64(*this), + idx128(*this), + _checktime_limit(checktime_limit) + {} void exec(); @@ -200,6 +383,8 @@ class apply_context { const auto& tab = find_or_create_table( scope, receiver, table ); auto tableid = tab.id; + FC_ASSERT( payer != account_name(), "must specify a valid account to pay for new record" ); + const auto& obj = mutable_db.create( [&]( auto& o ) { o.t_id = tableid; o.primary_key = id; @@ -324,59 +509,13 @@ class apply_context { return keyval_cache.add( *itr ); } - private: - template - class iterator_cache { - public: - iterator_cache(){ - _iterator_to_object.reserve(32); - } - void cache_table( const table_id_object& tobj ) { - _table_cache[tobj.id] = &tobj; - } - - const table_id_object& get_table( table_id_object::id_type i ) { - auto itr = _table_cache.find(i); - FC_ASSERT( itr != _table_cache.end(), "an invariant was broken, table should be in cache" ); - return *_table_cache[i]; - } - - const T& get( int iterator ) { - FC_ASSERT( iterator >= 0, "invalid iterator" ); - FC_ASSERT( iterator < _iterator_to_object.size(), "iterator out of range" ); - auto result = _iterator_to_object[iterator]; - FC_ASSERT( result, "reference of deleted object" ); - return *result; - } - - void remove( int iterator, const T& obj ) { - _iterator_to_object[iterator] = nullptr; - _object_to_iterator.erase( &obj ); - } - - int add( const T& obj ) { - auto itr = _object_to_iterator.find( &obj ); - if( itr != _object_to_iterator.end() ) - return itr->second; - - _iterator_to_object.push_back( &obj ); - _object_to_iterator[&obj] = _iterator_to_object.size() - 1; - - return _iterator_to_object.size() - 1; - } - - private: - map _table_cache; - vector _iterator_to_object; - map _object_to_iterator; - }; + generic_index idx64; + generic_index idx128; + private: iterator_cache keyval_cache; - - - void append_results(apply_results &&other) { fc::move_append(results.applied_actions, move(other.applied_actions)); fc::move_append(results.generated_transactions, move(other.generated_transactions)); diff --git a/libraries/chain/include/eosio/chain/contracts/contract_table_objects.hpp b/libraries/chain/include/eosio/chain/contracts/contract_table_objects.hpp index 93a2f6816..46f3c3c67 100644 --- a/libraries/chain/include/eosio/chain/contracts/contract_table_objects.hpp +++ b/libraries/chain/include/eosio/chain/contracts/contract_table_objects.hpp @@ -76,6 +76,52 @@ namespace eosio { namespace chain { namespace contracts { > >; + struct by_primary; + struct by_secondary; + + template + struct secondary_index + { + struct index_object : public chainbase::object { + OBJECT_CTOR(index_object) + typedef SecondaryKey secondary_key_type; + + typename chainbase::object::id_type id; + table_id t_id; + uint64_t primary_key; + SecondaryKey secondary_key; + account_name payer; + }; + + + typedef chainbase::shared_multi_index_container< + index_object, + indexed_by< + ordered_unique, member>, + ordered_unique, + composite_key< index_object, + member, + member + >, + composite_key_compare< std::less, std::less > + >, + ordered_unique, + composite_key< index_object, + member, + member + > + > + > + > index_index; + }; + + typedef secondary_index::index_object index64_object; + typedef secondary_index::index_index index64_index; + + typedef secondary_index::index_object index128_object; + typedef secondary_index::index_index index128_index; + + /* struct index64_object : public chainbase::object { OBJECT_CTOR(index64_object) @@ -86,10 +132,9 @@ namespace eosio { namespace chain { namespace contracts { table_id t_id; uint64_t primary_key; uint64_t secondary_key; + account_name payer; }; - struct by_primary; - struct by_secondary; using index64_index = chainbase::shared_multi_index_container< index64_object, indexed_by< @@ -109,6 +154,7 @@ namespace eosio { namespace chain { namespace contracts { > > >; + */ @@ -295,6 +341,7 @@ CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::key64x64_value_object, eosio:: CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::key64x64x64_value_object, eosio::chain::contracts::key64x64x64_value_index) CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::index64_object, eosio::chain::contracts::index64_index) +CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::index128_object, eosio::chain::contracts::index128_index) FC_REFLECT(eosio::chain::contracts::table_id_object, (id)(code)(scope)(table) ) FC_REFLECT(eosio::chain::contracts::key_value_object, (id)(t_id)(primary_key)(value)(payer) ) diff --git a/libraries/chain/include/eosio/chain/types.hpp b/libraries/chain/include/eosio/chain/types.hpp index f0c96b20a..e1f40cb31 100644 --- a/libraries/chain/include/eosio/chain/types.hpp +++ b/libraries/chain/include/eosio/chain/types.hpp @@ -122,6 +122,7 @@ namespace eosio { namespace chain { key128x128_value_object_type, key64x64_value_object_type, index64_object_type, + index128_object_type, action_permission_object_type, global_property_object_type, dynamic_global_property_object_type, @@ -176,6 +177,7 @@ FC_REFLECT_ENUM(eosio::chain::object_type, (key128x128_value_object_type) (key64x64_value_object_type) (index64_object_type) + (index128_object_type) (action_permission_object_type) (global_property_object_type) (dynamic_global_property_object_type) diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index f78db6844..cd2163ad7 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -708,6 +708,27 @@ class database_api : public context_aware_api { int db_upperbound_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id ) { return context.db_lowerbound_i64( code, scope, table, id ); } + + int db_idx64_store( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, const uint64_t& secondary ) { + return context.idx64.store( scope, table, payer, id, secondary ); + } + void db_idx64_update( int iterator, uint64_t payer, const uint64_t& secondary ) { + return context.idx64.update( iterator, payer, secondary ); + } + void db_idx64_remove( int iterator ) { + return context.idx64.remove( iterator ); + } + + + int db_idx128_store( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, const uint128_t& secondary ) { + return context.idx128.store( scope, table, payer, id, secondary ); + } + void db_idx128_update( int iterator, uint64_t payer, const uint128_t& secondary ) { + return context.idx128.update( iterator, payer, secondary ); + } + void db_idx128_remove( int iterator ) { + return context.idx128.remove( iterator ); + } }; @@ -944,6 +965,15 @@ REGISTER_INTRINSICS( database_api, (db_next_i64, int(int)) (db_find_i64, int(int64_t,int64_t,int64_t,int64_t)) (db_lowerbound_i64, int(int64_t,int64_t,int64_t,int64_t)) + + (db_idx64_store, int(int64_t,int64_t,int64_t,int64_t,int)) + (db_idx64_remove, void(int)) + (db_idx64_update, void(int,int64_t,int)) + + + (db_idx128_store, int(int64_t,int64_t,int64_t,int64_t,int)) + (db_idx128_remove, void(int)) + (db_idx128_update, void(int,int64_t,int)) ) REGISTER_INTRINSICS(crypto_api, -- GitLab