Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
fix definition of upper_bound in chain_plugin #6273; fix issues resul…
Browse files Browse the repository at this point in the history
…ting from upper_bound < lower_bound
  • Loading branch information
arhag committed Nov 9, 2018
1 parent 5f06702 commit 69e929e
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 53 deletions.
20 changes: 4 additions & 16 deletions libraries/chain/include/eosio/chain/contract_table_objects.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,33 +130,21 @@ namespace eosio { namespace chain {
typedef secondary_index<key256_t,index256_object_type>::index_object index256_object;
typedef secondary_index<key256_t,index256_object_type>::index_index index256_index;

struct soft_double_less {
bool operator()( const float64_t& lhs, const float64_t& rhs )const {
return f64_lt(lhs, rhs);
}
};

struct soft_long_double_less {
bool operator()( const float128_t lhs, const float128_t& rhs )const {
return f128_lt(lhs, rhs);
}
};

/**
* This index supports a deterministic software implementation of double as the secondary key.
*
* The software double implementation is using the Berkeley softfloat library (release 3).
*/
typedef secondary_index<float64_t,index_double_object_type,soft_double_less>::index_object index_double_object;
typedef secondary_index<float64_t,index_double_object_type,soft_double_less>::index_index index_double_index;
typedef secondary_index<float64_t,index_double_object_type>::index_object index_double_object;
typedef secondary_index<float64_t,index_double_object_type>::index_index index_double_index;

/**
* This index supports a deterministic software implementation of long double as the secondary key.
*
* The software long double implementation is using the Berkeley softfloat library (release 3).
*/
typedef secondary_index<float128_t,index_long_double_object_type,soft_long_double_less>::index_object index_long_double_object;
typedef secondary_index<float128_t,index_long_double_object_type,soft_long_double_less>::index_index index_long_double_index;
typedef secondary_index<float128_t,index_long_double_object_type>::index_object index_long_double_object;
typedef secondary_index<float128_t,index_long_double_object_type>::index_index index_long_double_index;

/**
* helper template to map from an index type to the best tag
Expand Down
9 changes: 8 additions & 1 deletion libraries/chain/include/eosio/chain/database_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@
#include <fc/io/raw.hpp>
#include <softfloat.hpp>

inline bool operator< (const float64_t& lhs, const float64_t& rhs) {
return f64_lt(lhs, rhs);
}

inline bool operator< (const float128_t& lhs, const float128_t& rhs) {
return f128_lt(lhs, rhs);
}

namespace eosio { namespace chain {

template<typename ...Indices>
Expand Down Expand Up @@ -216,4 +224,3 @@ DataStream& operator >> ( DataStream& ds, float128_t& v ) {
fc::raw::unpack(ds, *reinterpret_cast<eosio::chain::uint128_t*>(&v));
return ds;
}

28 changes: 17 additions & 11 deletions plugins/chain_plugin/chain_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1189,28 +1189,34 @@ read_only::get_table_rows_result read_only::get_table_rows( const read_only::get
}

read_only::get_table_by_scope_result read_only::get_table_by_scope( const read_only::get_table_by_scope_params& p )const {
read_only::get_table_by_scope_result result;
const auto& d = db.db();

const auto& idx = d.get_index<chain::table_id_multi_index, chain::by_code_scope_table>();
decltype(idx.lower_bound(boost::make_tuple(0, 0, 0))) lower;
decltype(idx.upper_bound(boost::make_tuple(0, 0, 0))) upper;
auto lower_bound_lookup_tuple = std::make_tuple( p.code.value, std::numeric_limits<uint64_t>::lowest(), p.table.value );
auto upper_bound_lookup_tuple = std::make_tuple( p.code.value, std::numeric_limits<uint64_t>::max(),
(p.table.empty() ? std::numeric_limits<uint64_t>::max() : p.table.value) );

if (p.lower_bound.size()) {
if( p.lower_bound.size() ) {
uint64_t scope = convert_to_type<uint64_t>(p.lower_bound, "lower_bound scope");
lower = idx.lower_bound( boost::make_tuple(p.code, scope, p.table));
} else {
lower = idx.lower_bound(boost::make_tuple(p.code, 0, p.table));
std::get<1>(lower_bound_lookup_tuple) = scope;
}
if (p.upper_bound.size()) {

if( p.upper_bound.size() ) {
uint64_t scope = convert_to_type<uint64_t>(p.upper_bound, "upper_bound scope");
upper = idx.lower_bound( boost::make_tuple(p.code, scope, 0));
} else {
upper = idx.lower_bound(boost::make_tuple((uint64_t)p.code + 1, 0, 0));
std::get<1>(upper_bound_lookup_tuple) = scope;
}

if( upper_bound_lookup_tuple < lower_bound_lookup_tuple )
return result;

auto lower = idx.lower_bound( lower_bound_lookup_tuple );
auto upper = idx.upper_bound( upper_bound_lookup_tuple );

auto end = fc::time_point::now() + fc::microseconds(1000 * 10); /// 10ms max time

unsigned int count = 0;
auto itr = lower;
read_only::get_table_by_scope_result result;
for (; itr != upper; ++itr) {
if (p.table && itr->table != p.table) {
if (fc::time_point::now() > end) {
Expand Down
133 changes: 108 additions & 25 deletions plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,75 @@ namespace eosio {
using chain::abi_def;
using chain::abi_serializer;

template<typename T>
struct secondary_key_traits {
using value_type = std::enable_if_t<std::is_integral<T>::value, T>;

static_assert( std::numeric_limits<uint128_t>::is_specialized, "value_type does not have specialized numeric_limits" );

static constexpr value_type true_lowest() { return std::numeric_limits<value_type>::lowest(); }
static constexpr value_type true_highest() { return std::numeric_limits<value_type>::max(); }
};

template<size_t N>
struct secondary_key_traits<std::array<uint128_t, N>> {
private:
static constexpr uint128_t max_uint128 = uint128_t(std::numeric_limits<uint64_t>::max()) << 64 | std::numeric_limits<uint64_t>::max();
static_assert( std::numeric_limits<uint128_t>::max() == max_uint128, "numeric_limits for uint128_t is not properly defined" );

public:
using value_type = std::array<uint128_t, N>;

static value_type true_lowest() {
value_type arr;
return arr;
}

static value_type true_highest() {
value_type arr;
for( auto& v : arr ) {
v = std::numeric_limits<uint128_t>::max();
}
return arr;
}
};

template<>
struct secondary_key_traits<float64_t> {
using value_type = float64_t;

static value_type true_lowest() {
float64_t f;
f.v = 0xff00000000000000ull;
return f; // -infinity?
}

static value_type true_highest() {
float64_t f;
f.v = 0x7f00000000000000ull;
return f; // +infinity?
}
};

template<>
struct secondary_key_traits<float128_t> {
using value_type = float128_t;

static value_type true_lowest() {
float128_t f;
f.v[0] = 0x0ull;
f.v[1] = 0xffff000000000000ull;
return f; // -infinity?
}

static value_type true_highest() {
float128_t f;
f.v[0] = 0x0ull;
f.v[1] = 0x7fff000000000000ull;
return f; // +infinity?
}
};

namespace chain_apis {
struct empty{};

Expand Down Expand Up @@ -398,34 +467,42 @@ class read_only {
const uint64_t table_with_index = get_table_index_name(p, primary);
const auto* t_id = d.find<chain::table_id_object, chain::by_code_scope_table>(boost::make_tuple(p.code, scope, p.table));
const auto* index_t_id = d.find<chain::table_id_object, chain::by_code_scope_table>(boost::make_tuple(p.code, scope, table_with_index));
if (t_id != nullptr && index_t_id != nullptr) {
if( t_id != nullptr && index_t_id != nullptr ) {
using secondary_key_type = std::result_of_t<decltype(conv)(SecKeyType)>;
static_assert( std::is_same<decltype(IndexType::value_type::secondary_key), secondary_key_type>::value, "Return type of conv does not match type of secondary key for IndexType" );

const auto& secidx = d.get_index<IndexType, chain::by_secondary>();
decltype(index_t_id->id) low_tid(index_t_id->id._id);
decltype(index_t_id->id) next_tid(index_t_id->id._id + 1);
auto lower = secidx.lower_bound(boost::make_tuple(low_tid));
auto upper = secidx.lower_bound(boost::make_tuple(next_tid));
auto lower_bound_lookup_tuple = std::make_tuple( index_t_id->id._id, secondary_key_traits<secondary_key_type>::true_lowest(), std::numeric_limits<uint64_t>::lowest() );
auto upper_bound_lookup_tuple = std::make_tuple( index_t_id->id._id, secondary_key_traits<secondary_key_type>::true_highest(), std::numeric_limits<uint64_t>::max() );

if (p.lower_bound.size()) {
if (p.key_type == "name") {
if( p.lower_bound.size() ) {
if( p.key_type == "name" ) {
name s(p.lower_bound);
SecKeyType lv = convert_to_type<SecKeyType>( s.to_string(), "lower_bound name" ); // avoids compiler error
lower = secidx.lower_bound( boost::make_tuple( low_tid, conv( lv )));
std::get<1>(lower_bound_lookup_tuple) = conv( lv );
} else {
SecKeyType lv = convert_to_type<SecKeyType>( p.lower_bound, "lower_bound" );
lower = secidx.lower_bound( boost::make_tuple( low_tid, conv( lv )));
std::get<1>(lower_bound_lookup_tuple) = conv( lv );
}
}
if (p.upper_bound.size()) {
if (p.key_type == "name") {

if( p.upper_bound.size() ) {
if( p.key_type == "name" ) {
name s(p.upper_bound);
SecKeyType uv = convert_to_type<SecKeyType>( s.to_string(), "upper_bound name" );
upper = secidx.lower_bound( boost::make_tuple( low_tid, conv( uv )));
std::get<1>(upper_bound_lookup_tuple) = conv( uv );
} else {
SecKeyType uv = convert_to_type<SecKeyType>( p.upper_bound, "upper_bound" );
upper = secidx.lower_bound( boost::make_tuple( low_tid, conv( uv )));
std::get<1>(upper_bound_lookup_tuple) = conv( uv );
}
}

if( upper_bound_lookup_tuple < lower_bound_lookup_tuple )
return result;

auto lower = secidx.lower_bound( lower_bound_lookup_tuple );
auto upper = secidx.upper_bound( upper_bound_lookup_tuple );

vector<char> data;

auto end = fc::time_point::now() + fc::microseconds(1000 * 10); /// 10ms max time
Expand Down Expand Up @@ -465,31 +542,37 @@ class read_only {
abi_serializer abis;
abis.set_abi(abi, abi_serializer_max_time);
const auto* t_id = d.find<chain::table_id_object, chain::by_code_scope_table>(boost::make_tuple(p.code, scope, p.table));
if (t_id != nullptr) {
if( t_id != nullptr ) {
const auto& idx = d.get_index<IndexType, chain::by_scope_primary>();
decltype(t_id->id) next_tid(t_id->id._id + 1);
auto lower = idx.lower_bound(boost::make_tuple(t_id->id));
auto upper = idx.lower_bound(boost::make_tuple(next_tid));
auto lower_bound_lookup_tuple = std::make_tuple( t_id->id, std::numeric_limits<uint64_t>::lowest() );
auto upper_bound_lookup_tuple = std::make_tuple( t_id->id, std::numeric_limits<uint64_t>::max() );

if (p.lower_bound.size()) {
if (p.key_type == "name") {
if( p.lower_bound.size() ) {
if( p.key_type == "name" ) {
name s(p.lower_bound);
lower = idx.lower_bound( boost::make_tuple( t_id->id, s.value ));
std::get<1>(lower_bound_lookup_tuple) = s.value;
} else {
auto lv = convert_to_type<typename IndexType::value_type::key_type>( p.lower_bound, "lower_bound" );
lower = idx.lower_bound( boost::make_tuple( t_id->id, lv ));
std::get<1>(lower_bound_lookup_tuple) = lv;
}
}
if (p.upper_bound.size()) {
if (p.key_type == "name") {

if( p.upper_bound.size() ) {
if( p.key_type == "name" ) {
name s(p.upper_bound);
upper = idx.lower_bound( boost::make_tuple( t_id->id, s.value ));
std::get<1>(upper_bound_lookup_tuple) = s.value;
} else {
auto uv = convert_to_type<typename IndexType::value_type::key_type>( p.upper_bound, "upper_bound" );
upper = idx.lower_bound( boost::make_tuple( t_id->id, uv ));
std::get<1>(upper_bound_lookup_tuple) = uv;
}
}

if( upper_bound_lookup_tuple < lower_bound_lookup_tuple )
return result;

auto lower = idx.lower_bound( lower_bound_lookup_tuple );
auto upper = idx.upper_bound( upper_bound_lookup_tuple );

vector<char> data;

auto end = fc::time_point::now() + fc::microseconds(1000 * 10); /// 10ms max time
Expand Down

0 comments on commit 69e929e

Please sign in to comment.