LCOV - code coverage report
Current view: top level - irohad/ametsuchi/impl - postgres_block_query.cpp (source / functions) Hit Total Coverage
Test: coverage_cleared.info Lines: 145 160 90.6 %
Date: 2018-12-05 17:11:35 Functions: 38 40 95.0 %

          Line data    Source code
       1             : /**
       2             :  * Copyright Soramitsu Co., Ltd. All Rights Reserved.
       3             :  * SPDX-License-Identifier: Apache-2.0
       4             :  */
       5             : 
       6             : #include "ametsuchi/impl/postgres_block_query.hpp"
       7             : 
       8             : #include <boost/format.hpp>
       9             : #include <boost/range/adaptor/transformed.hpp>
      10             : #include <boost/range/algorithm/for_each.hpp>
      11             : 
      12             : #include "ametsuchi/impl/soci_utils.hpp"
      13             : #include "common/byteutils.hpp"
      14             : 
      15             : namespace iroha {
      16             :   namespace ametsuchi {
      17             :     PostgresBlockQuery::PostgresBlockQuery(
      18             :         soci::session &sql,
      19             :         KeyValueStorage &file_store,
      20             :         std::shared_ptr<shared_model::interface::BlockJsonDeserializer>
      21             :             converter)
      22          44 :         : sql_(sql),
      23          44 :           block_store_(file_store),
      24          44 :           converter_(std::move(converter)),
      25          44 :           log_(logger::log("PostgresBlockQuery")) {}
      26             : 
      27             :     PostgresBlockQuery::PostgresBlockQuery(
      28             :         std::unique_ptr<soci::session> sql,
      29             :         KeyValueStorage &file_store,
      30             :         std::shared_ptr<shared_model::interface::BlockJsonDeserializer>
      31             :             converter)
      32        2450 :         : psql_(std::move(sql)),
      33        2450 :           sql_(*psql_),
      34        2450 :           block_store_(file_store),
      35        2450 :           converter_(std::move(converter)),
      36        2450 :           log_(logger::log("PostgresBlockQuery")) {}
      37             : 
      38             :     std::vector<BlockQuery::wBlock> PostgresBlockQuery::getBlocks(
      39             :         shared_model::interface::types::HeightType height, uint32_t count) {
      40         257 :       shared_model::interface::types::HeightType last_id =
      41         257 :           block_store_.last_id();
      42         257 :       auto to = std::min(last_id, height + count - 1);
      43         257 :       std::vector<BlockQuery::wBlock> result;
      44         257 :       if (height > to or count == 0) {
      45           2 :         return result;
      46             :       }
      47         516 :       for (auto i = height; i <= to; i++) {
      48         261 :         auto block = getBlock(i);
      49         261 :         block.match(
      50             :             [&result](
      51             :                 expected::Value<std::unique_ptr<shared_model::interface::Block>>
      52         258 :                     &v) { result.emplace_back(std::move(v.value)); },
      53             :             [this](const expected::Error<std::string> &e) {
      54           3 :               log_->error(e.error);
      55           3 :             });
      56         261 :       }
      57         255 :       return result;
      58         257 :     }
      59             : 
      60             :     std::vector<BlockQuery::wBlock> PostgresBlockQuery::getBlocksFrom(
      61             :         shared_model::interface::types::HeightType height) {
      62         247 :       return getBlocks(height, block_store_.last_id());
      63             :     }
      64             : 
      65             :     std::vector<BlockQuery::wBlock> PostgresBlockQuery::getTopBlocks(
      66             :         uint32_t count) {
      67           1 :       auto last_id = block_store_.last_id();
      68           1 :       count = std::min(count, last_id);
      69           1 :       return getBlocks(last_id - count + 1, count);
      70             :     }
      71             : 
      72             :     std::vector<shared_model::interface::types::HeightType>
      73             :     PostgresBlockQuery::getBlockIds(
      74             :         const shared_model::interface::types::AccountIdType &account_id) {
      75          21 :       std::vector<shared_model::interface::types::HeightType> result;
      76             :       soci::indicator ind;
      77          21 :       std::string row;
      78          21 :       soci::statement st =
      79          21 :           (sql_.prepare << "SELECT DISTINCT height FROM height_by_account_set "
      80          21 :                            "WHERE account_id = :id",
      81          21 :            soci::into(row, ind),
      82             :            soci::use(account_id));
      83          21 :       st.execute();
      84             : 
      85             :       processSoci(st, ind, row, [&result](std::string &r) {
      86          28 :         result.push_back(std::stoull(r));
      87          28 :       });
      88          21 :       return result;
      89          21 :     }
      90             : 
      91             :     boost::optional<shared_model::interface::types::HeightType>
      92             :     PostgresBlockQuery::getBlockId(const shared_model::crypto::Hash &hash) {
      93           9 :       boost::optional<uint64_t> blockId = boost::none;
      94           9 :       boost::optional<std::string> block_str;
      95           9 :       auto hash_str = hash.hex();
      96             : 
      97           9 :       sql_ << "SELECT height FROM height_by_hash WHERE hash = :hash",
      98           9 :           soci::into(block_str), soci::use(hash_str);
      99           9 :       if (block_str) {
     100           5 :         blockId = std::stoull(block_str.get());
     101           5 :       } else {
     102             :         log_->info("No block with transaction {}", hash.toString());
     103             :       }
     104             :       return blockId;
     105           9 :     }
     106             : 
     107             :     std::function<void(std::vector<std::string> &result)>
     108             :     PostgresBlockQuery::callback(std::vector<wTransaction> &blocks,
     109             :                                  uint64_t block_id) {
     110             :       return [this, &blocks, block_id](std::vector<std::string> &result) {
     111          28 :         auto block = getBlock(block_id);
     112             :         block.match(
     113             :             [&result, &blocks](
     114             :                 expected::Value<std::unique_ptr<shared_model::interface::Block>>
     115             :                     &v) {
     116          28 :               boost::for_each(
     117             :                   result | boost::adaptors::transformed([](const auto &x) {
     118          19 :                     std::istringstream iss(x);
     119             :                     size_t size;
     120          19 :                     iss >> size;
     121          19 :                     return size;
     122          19 :                   }),
     123             :                   [&](const auto &x) {
     124          28 :                     blocks.emplace_back(clone(v.value->transactions()[x]));
     125          19 :                   });
     126          28 :             },
     127             :             [this](const expected::Error<std::string> &e) {
     128           0 :               log_->error(e.error);
     129           0 :             });
     130          28 :       };
     131             :     }
     132             : 
     133             :     std::vector<BlockQuery::wTransaction>
     134             :     PostgresBlockQuery::getAccountTransactions(
     135             :         const shared_model::interface::types::AccountIdType &account_id) {
     136           8 :       std::vector<BlockQuery::wTransaction> result;
     137           8 :       auto block_ids = this->getBlockIds(account_id);
     138           8 :       if (block_ids.empty()) {
     139           2 :         return result;
     140             :       }
     141          13 :       for (const auto &block_id : block_ids) {
     142           7 :         std::vector<std::string> index;
     143             :         soci::indicator ind;
     144           7 :         std::string row;
     145           7 :         soci::statement st =
     146           7 :             (sql_.prepare
     147           7 :                  << "SELECT DISTINCT index FROM index_by_creator_height "
     148           7 :                     "WHERE creator_id = :id AND height = :height",
     149           7 :              soci::into(row, ind),
     150           7 :              soci::use(account_id),
     151           7 :              soci::use(block_id));
     152           7 :         st.execute();
     153             : 
     154           7 :         processSoci(
     155             :             st, ind, row, [&index](std::string &r) { index.push_back(r); });
     156           7 :         this->callback(result, block_id)(index);
     157           7 :       }
     158           6 :       return result;
     159           8 :     }
     160             : 
     161             :     std::vector<BlockQuery::wTransaction>
     162             :     PostgresBlockQuery::getAccountAssetTransactions(
     163             :         const shared_model::interface::types::AccountIdType &account_id,
     164             :         const shared_model::interface::types::AssetIdType &asset_id) {
     165          13 :       std::vector<BlockQuery::wTransaction> result;
     166          13 :       auto block_ids = this->getBlockIds(account_id);
     167          13 :       if (block_ids.empty()) {
     168           1 :         return result;
     169             :       }
     170             : 
     171          33 :       for (const auto &block_id : block_ids) {
     172          21 :         std::vector<std::string> index;
     173             :         soci::indicator ind;
     174          21 :         std::string row;
     175          21 :         soci::statement st =
     176          21 :             (sql_.prepare
     177          21 :                  << "SELECT DISTINCT index FROM index_by_id_height_asset "
     178             :                     "WHERE id = :id AND height = :height AND asset_id = "
     179          21 :                     ":asset_id",
     180          21 :              soci::into(row, ind),
     181          21 :              soci::use(account_id),
     182          21 :              soci::use(block_id),
     183          21 :              soci::use(asset_id));
     184          21 :         st.execute();
     185             : 
     186          21 :         processSoci(
     187             :             st, ind, row, [&index](std::string &r) { index.push_back(r); });
     188          21 :         this->callback(result, block_id)(index);
     189          21 :       }
     190          12 :       return result;
     191          13 :     }
     192             : 
     193             :     std::vector<boost::optional<BlockQuery::wTransaction>>
     194             :     PostgresBlockQuery::getTransactions(
     195             :         const std::vector<shared_model::crypto::Hash> &tx_hashes) {
     196           4 :       std::vector<boost::optional<BlockQuery::wTransaction>> result;
     197           4 :       std::for_each(tx_hashes.begin(),
     198           4 :                     tx_hashes.end(),
     199             :                     [this, &result](const auto &tx_hash) {
     200           6 :                       result.push_back(this->getTxByHashSync(tx_hash));
     201           6 :                     });
     202           4 :       return result;
     203           4 :     }
     204             : 
     205             :     boost::optional<BlockQuery::wTransaction>
     206             :     PostgresBlockQuery::getTxByHashSync(
     207             :         const shared_model::crypto::Hash &hash) {
     208           9 :       return getBlockId(hash) |
     209             :           [this](const auto &block_id) {
     210           5 :             auto result = this->getBlock(block_id);
     211           5 :             return result.match(
     212             :                 [](expected::Value<
     213             :                     std::unique_ptr<shared_model::interface::Block>> &v)
     214             :                     -> boost::optional<
     215             :                         std::unique_ptr<shared_model::interface::Block>> {
     216           5 :                   return std::move(v.value);
     217             :                 },
     218             :                 [this](const expected::Error<std::string> &e)
     219             :                     -> boost::optional<
     220             :                         std::unique_ptr<shared_model::interface::Block>> {
     221           0 :                   log_->error(e.error);
     222           0 :                   return boost::none;
     223             :                 });
     224           5 :           }
     225             :       | [&hash, this](const auto &block) {
     226           5 :           auto it = std::find_if(
     227           5 :               block->transactions().begin(),
     228           5 :               block->transactions().end(),
     229             :               [&hash](const auto &tx) { return tx.hash() == hash; });
     230           5 :           if (it != block->transactions().end()) {
     231           5 :             return boost::make_optional<PostgresBlockQuery::wTransaction>(
     232           5 :                 clone(*it));
     233             :           } else {
     234           0 :             log_->error("Failed to find transaction {} in block {}",
     235           0 :                         hash.hex(),
     236           0 :                         block->height());
     237             :             // return type specification for lambda breaks formatting.
     238             :             // That is why optional is constructed explicitly
     239           0 :             return boost::optional<PostgresBlockQuery::wTransaction>(
     240             :                 boost::none);
     241             :           }
     242           5 :         };
     243           0 :     }
     244             : 
     245             :     boost::optional<TxCacheStatusType> PostgresBlockQuery::checkTxPresence(
     246             :         const shared_model::crypto::Hash &hash) {
     247           6 :       int res = -1;
     248           6 :       const auto &hash_str = hash.hex();
     249             : 
     250             :       try {
     251           6 :         sql_ << "SELECT status FROM tx_status_by_hash WHERE hash = :hash",
     252           6 :             soci::into(res), soci::use(hash_str);
     253           6 :       } catch (const std::exception &e) {
     254           0 :         log_->error("Failed to execute query: {}", e.what());
     255           0 :         return boost::none;
     256           0 :       }
     257             : 
     258             :       // res > 0 => Committed
     259             :       // res == 0 => Rejected
     260             :       // res < 0 => Missing
     261           6 :       if (res > 0) {
     262           4 :         return boost::make_optional<TxCacheStatusType>(
     263           4 :             tx_cache_status_responses::Committed{hash});
     264           2 :       } else if (res == 0) {
     265           1 :         return boost::make_optional<TxCacheStatusType>(
     266           1 :             tx_cache_status_responses::Rejected{hash});
     267             :       }
     268           1 :       return boost::make_optional<TxCacheStatusType>(
     269           1 :           tx_cache_status_responses::Missing{hash});
     270           6 :     }
     271             : 
     272             :     uint32_t PostgresBlockQuery::getTopBlockHeight() {
     273         708 :       return block_store_.last_id();
     274             :     }
     275             : 
     276             :     expected::Result<BlockQuery::wBlock, std::string>
     277             :     PostgresBlockQuery::getTopBlock() {
     278        1494 :       return getBlock(block_store_.last_id())
     279        1494 :           .match(
     280             :               [](expected::Value<
     281             :                   std::unique_ptr<shared_model::interface::Block>> &v)
     282             :                   -> expected::Result<BlockQuery::wBlock, std::string> {
     283         976 :                 return expected::makeValue<BlockQuery::wBlock>(
     284         976 :                     std::move(v.value));
     285           0 :               },
     286             :               [](expected::Error<std::string> &e)
     287             :                   -> expected::Result<BlockQuery::wBlock, std::string> {
     288         518 :                 return expected::makeError(std::move(e.error));
     289           0 :               });
     290           0 :     }
     291             : 
     292             :     expected::Result<std::unique_ptr<shared_model::interface::Block>,
     293             :                      std::string>
     294             :     PostgresBlockQuery::getBlock(
     295             :         shared_model::interface::types::HeightType id) const {
     296        1788 :       auto serialized_block = block_store_.get(id);
     297        1788 :       if (not serialized_block) {
     298         519 :         auto error = boost::format("Failed to retrieve block with id %d") % id;
     299         519 :         return expected::makeError(error.str());
     300         519 :       }
     301        1269 :       return converter_->deserialize(bytesToString(*serialized_block));
     302        1788 :     }
     303             :   }  // namespace ametsuchi
     304             : }  // namespace iroha

Generated by: LCOV version 1.13