LCOV - code coverage report
Current view: top level - irohad/ametsuchi/impl - postgres_query_executor.cpp (source / functions) Hit Total Coverage
Test: cleared_cor.info Lines: 449 484 92.8 %
Date: 2019-03-07 14:46:43 Functions: 169 172 98.3 %

          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_query_executor.hpp"
       7             : 
       8             : #include <boost-tuple.h>
       9             : #include <soci/boost-tuple.h>
      10             : #include <soci/postgresql/soci-postgresql.h>
      11             : #include <boost/algorithm/string.hpp>
      12             : #include <boost/format.hpp>
      13             : #include <boost/range/adaptor/filtered.hpp>
      14             : #include <boost/range/adaptor/transformed.hpp>
      15             : #include <boost/range/algorithm/for_each.hpp>
      16             : #include <boost/range/algorithm/transform.hpp>
      17             : #include <boost/range/irange.hpp>
      18             : 
      19             : #include "ametsuchi/impl/soci_utils.hpp"
      20             : #include "common/byteutils.hpp"
      21             : #include "cryptography/public_key.hpp"
      22             : #include "interfaces/queries/blocks_query.hpp"
      23             : #include "interfaces/queries/get_account.hpp"
      24             : #include "interfaces/queries/get_account_asset_transactions.hpp"
      25             : #include "interfaces/queries/get_account_assets.hpp"
      26             : #include "interfaces/queries/get_account_detail.hpp"
      27             : #include "interfaces/queries/get_account_transactions.hpp"
      28             : #include "interfaces/queries/get_asset_info.hpp"
      29             : #include "interfaces/queries/get_block.hpp"
      30             : #include "interfaces/queries/get_pending_transactions.hpp"
      31             : #include "interfaces/queries/get_role_permissions.hpp"
      32             : #include "interfaces/queries/get_roles.hpp"
      33             : #include "interfaces/queries/get_signatories.hpp"
      34             : #include "interfaces/queries/get_transactions.hpp"
      35             : #include "interfaces/queries/query.hpp"
      36             : #include "interfaces/queries/tx_pagination_meta.hpp"
      37             : #include "logger/logger.hpp"
      38             : #include "logger/logger_manager.hpp"
      39             : 
      40             : using namespace shared_model::interface::permissions;
      41             : 
      42             : namespace {
      43             : 
      44             :   using namespace iroha;
      45             : 
      46             :   shared_model::interface::types::DomainIdType getDomainFromName(
      47             :       const shared_model::interface::types::AccountIdType &account_id) {
      48             :     // TODO 03.10.18 andrei: IR-1728 Move getDomainFromName to shared_model
      49         250 :     std::vector<std::string> res;
      50         250 :     boost::split(res, account_id, boost::is_any_of("@"));
      51         250 :     return res.at(1);
      52         250 :   }
      53             : 
      54             :   std::string getAccountRolePermissionCheckSql(
      55             :       shared_model::interface::permissions::Role permission,
      56             :       const std::string &account_alias = "role_account_id") {
      57             :     const auto perm_str =
      58          87 :         shared_model::interface::RolePermissionSet({permission}).toBitstring();
      59          87 :     const auto bits = shared_model::interface::RolePermissionSet::size();
      60             :     // TODO 14.09.18 andrei: IR-1708 Load SQL from separate files
      61          91 :     std::string query = (boost::format(R"(
      62             :           SELECT (COALESCE(bit_or(rp.permission), '0'::bit(%1%))
      63             :           & '%2%') = '%2%' AS perm FROM role_has_permissions AS rp
      64             :               JOIN account_has_roles AS ar on ar.role_id = rp.role_id
      65             :               WHERE ar.account_id = :%3%)")
      66          88 :                          % bits % perm_str % account_alias)
      67          89 :                             .str();
      68          91 :     return query;
      69          91 :   }
      70             : 
      71             :   /**
      72             :    * Generate an SQL subquery which checks if creator has corresponding
      73             :    * permissions for target account
      74             :    * It verifies individual, domain, and global permissions, and returns true if
      75             :    * any of listed permissions is present
      76             :    */
      77             :   auto hasQueryPermission(
      78             :       const shared_model::interface::types::AccountIdType &creator,
      79             :       const shared_model::interface::types::AccountIdType &target_account,
      80             :       Role indiv_permission_id,
      81             :       Role all_permission_id,
      82             :       Role domain_permission_id) {
      83         125 :     const auto bits = shared_model::interface::RolePermissionSet::size();
      84             :     const auto perm_str =
      85         125 :         shared_model::interface::RolePermissionSet({indiv_permission_id})
      86         125 :             .toBitstring();
      87             :     const auto all_perm_str =
      88         125 :         shared_model::interface::RolePermissionSet({all_permission_id})
      89         125 :             .toBitstring();
      90             :     const auto domain_perm_str =
      91         125 :         shared_model::interface::RolePermissionSet({domain_permission_id})
      92         125 :             .toBitstring();
      93             : 
      94         125 :     boost::format cmd(R"(
      95             :     WITH
      96             :         has_indiv_perm AS (
      97             :           SELECT (COALESCE(bit_or(rp.permission), '0'::bit(%1%))
      98             :           & '%3%') = '%3%' FROM role_has_permissions AS rp
      99             :               JOIN account_has_roles AS ar on ar.role_id = rp.role_id
     100             :               WHERE ar.account_id = '%2%'
     101             :         ),
     102             :         has_all_perm AS (
     103             :           SELECT (COALESCE(bit_or(rp.permission), '0'::bit(%1%))
     104             :           & '%4%') = '%4%' FROM role_has_permissions AS rp
     105             :               JOIN account_has_roles AS ar on ar.role_id = rp.role_id
     106             :               WHERE ar.account_id = '%2%'
     107             :         ),
     108             :         has_domain_perm AS (
     109             :           SELECT (COALESCE(bit_or(rp.permission), '0'::bit(%1%))
     110             :           & '%5%') = '%5%' FROM role_has_permissions AS rp
     111             :               JOIN account_has_roles AS ar on ar.role_id = rp.role_id
     112             :               WHERE ar.account_id = '%2%'
     113             :         )
     114             :     SELECT ('%2%' = '%6%' AND (SELECT * FROM has_indiv_perm))
     115             :         OR (SELECT * FROM has_all_perm)
     116             :         OR ('%7%' = '%8%' AND (SELECT * FROM has_domain_perm)) AS perm
     117             :     )");
     118             : 
     119         125 :     return (cmd % bits % creator % perm_str % all_perm_str % domain_perm_str
     120         125 :             % target_account % getDomainFromName(creator)
     121         125 :             % getDomainFromName(target_account))
     122         125 :         .str();
     123         125 :   }
     124             : 
     125             :   /// Query result is a tuple of optionals, since there could be no entry
     126             :   template <typename... Value>
     127             :   using QueryType = boost::tuple<boost::optional<Value>...>;
     128             : 
     129             :   /**
     130             :    * Create an error response in case user does not have permissions to perform
     131             :    * a query
     132             :    * @tparam Roles - type of roles
     133             :    * @param roles, which user lacks
     134             :    * @return lambda returning the error response itself
     135             :    */
     136             :   template <typename... Roles>
     137             :   auto notEnoughPermissionsResponse(
     138             :       std::shared_ptr<shared_model::interface::PermissionToString>
     139             :           perm_converter,
     140             :       Roles... roles) {
     141             :     return [perm_converter, roles...] {
     142          43 :       std::string error = "user must have at least one of the permissions: ";
     143         172 :       for (auto role : {roles...}) {
     144         129 :         error += perm_converter->toString(role) + ", ";
     145             :       }
     146          43 :       return error;
     147          43 :     };
     148             :   }
     149             : 
     150             : }  // namespace
     151             : 
     152             : namespace iroha {
     153             :   namespace ametsuchi {
     154             : 
     155             :     template <typename RangeGen, typename Pred>
     156             :     std::vector<std::unique_ptr<shared_model::interface::Transaction>>
     157             :     PostgresQueryExecutorVisitor::getTransactionsFromBlock(uint64_t block_id,
     158             :                                                            RangeGen &&range_gen,
     159             :                                                            Pred &&pred) {
     160          27 :       std::vector<std::unique_ptr<shared_model::interface::Transaction>> result;
     161          27 :       auto serialized_block = block_store_.get(block_id);
     162          31 :       if (not serialized_block) {
     163           0 :         log_->error("Failed to retrieve block with id {}", block_id);
     164           0 :         return result;
     165             :       }
     166             :       auto deserialized_block =
     167          31 :           converter_->deserialize(bytesToString(*serialized_block));
     168             :       // boost::get of pointer returns pointer to requested type, or nullptr
     169          31 :       if (auto e =
     170          31 :               boost::get<expected::Error<std::string>>(&deserialized_block)) {
     171           0 :         log_->error(e->error);
     172           0 :         return result;
     173             :       }
     174             : 
     175          31 :       auto &block =
     176          31 :           boost::get<
     177             :               expected::Value<std::unique_ptr<shared_model::interface::Block>>>(
     178          31 :               deserialized_block)
     179          31 :               .value;
     180             : 
     181          31 :       boost::transform(range_gen(boost::size(block->transactions()))
     182          31 :                            | boost::adaptors::transformed(
     183             :                                  [&block](auto i) -> decltype(auto) {
     184          66 :                                    return block->transactions()[i];
     185           0 :                                  })
     186          31 :                            | boost::adaptors::filtered(pred),
     187          31 :                        std::back_inserter(result),
     188             :                        [&](const auto &tx) { return clone(tx); });
     189             : 
     190          31 :       return result;
     191          31 :     }
     192             : 
     193             :     template <typename QueryTuple,
     194             :               typename PermissionTuple,
     195             :               typename QueryExecutor,
     196             :               typename ResponseCreator,
     197             :               typename PermissionsErrResponse>
     198             :     QueryExecutorResult PostgresQueryExecutorVisitor::executeQuery(
     199             :         QueryExecutor &&query_executor,
     200             :         ResponseCreator &&response_creator,
     201             :         PermissionsErrResponse &&perms_err_response) {
     202             :       using T = concat<QueryTuple, PermissionTuple>;
     203             :       try {
     204          34 :         soci::rowset<T> st = std::forward<QueryExecutor>(query_executor)();
     205          34 :         auto range = boost::make_iterator_range(st.begin(), st.end());
     206             : 
     207          30 :         return apply(
     208          34 :             viewPermissions<PermissionTuple>(range.front()),
     209             :             [this, range, &response_creator, &perms_err_response](
     210             :                 auto... perms) {
     211          34 :               bool temp[] = {not perms...};
     212             :               if (std::all_of(std::begin(temp), std::end(temp), [](auto b) {
     213          38 :                     return b;
     214             :                   })) {
     215             :                 // TODO [IR-1816] Akvinikym 03.12.18: replace magic number 2
     216             :                 // with a named constant
     217           8 :                 return this->logAndReturnErrorResponse(
     218             :                     QueryErrorType::kStatefulFailed,
     219           8 :                     std::forward<PermissionsErrResponse>(perms_err_response)(),
     220             :                     2);
     221             :               }
     222          33 :               auto query_range = range
     223             :                   | boost::adaptors::transformed([](auto &t) {
     224          89 :                                    return rebind(viewQuery<QueryTuple>(t));
     225           0 :                                  })
     226             :                   | boost::adaptors::filtered([](const auto &t) {
     227          45 :                                    return static_cast<bool>(t);
     228             :                                  })
     229             :                   | boost::adaptors::transformed([](auto t) { return *t; });
     230          33 :               return std::forward<ResponseCreator>(response_creator)(
     231          33 :                   query_range, perms...);
     232          34 :             });
     233          34 :       } catch (const std::exception &e) {
     234           0 :         return this->logAndReturnErrorResponse(
     235           0 :             QueryErrorType::kStatefulFailed, e.what(), 1);
     236           0 :       }
     237          34 :     }
     238             : 
     239             :     template <class Q>
     240             :     bool PostgresQueryExecutor::validateSignatures(const Q &query) {
     241             :       auto keys_range =
     242             :           query.signatures() | boost::adaptors::transformed([](const auto &s) {
     243         133 :             return s.publicKey().hex();
     244             :           });
     245             : 
     246         132 :       if (boost::size(keys_range) != 1) {
     247           0 :         return false;
     248             :       }
     249         132 :       std::string keys = *std::begin(keys_range);
     250             :       // not using bool since it is not supported by SOCI
     251         132 :       boost::optional<uint8_t> signatories_valid;
     252             : 
     253         132 :       auto qry = R"(
     254             :         SELECT count(public_key) = 1
     255             :         FROM account_has_signatory
     256             :         WHERE account_id = :account_id AND public_key = :pk
     257             :         )";
     258             : 
     259             :       try {
     260         132 :         *sql_ << qry, soci::into(signatories_valid),
     261         133 :             soci::use(query.creatorAccountId(), "account_id"),
     262         133 :             soci::use(keys, "pk");
     263         132 :       } catch (const std::exception &e) {
     264           0 :         log_->error(e.what());
     265           0 :         return false;
     266           0 :       }
     267             : 
     268         133 :       return signatories_valid and *signatories_valid;
     269         133 :     }
     270             : 
     271             :     PostgresQueryExecutor::PostgresQueryExecutor(
     272             :         std::unique_ptr<soci::session> sql,
     273             :         KeyValueStorage &block_store,
     274             :         std::shared_ptr<PendingTransactionStorage> pending_txs_storage,
     275             :         std::shared_ptr<shared_model::interface::BlockJsonConverter> converter,
     276             :         std::shared_ptr<shared_model::interface::QueryResponseFactory>
     277             :             response_factory,
     278             :         std::shared_ptr<shared_model::interface::PermissionToString>
     279             :             perm_converter,
     280             :         logger::LoggerManagerTreePtr log_manager)
     281         192 :         : sql_(std::move(sql)),
     282         192 :           block_store_(block_store),
     283         192 :           pending_txs_storage_(std::move(pending_txs_storage)),
     284         192 :           visitor_(*sql_,
     285         192 :                    block_store_,
     286         192 :                    pending_txs_storage_,
     287         192 :                    std::move(converter),
     288         192 :                    response_factory,
     289         192 :                    perm_converter,
     290         192 :                    log_manager->getChild("Visitor")->getLogger()),
     291         192 :           query_response_factory_{std::move(response_factory)},
     292         192 :           log_(log_manager->getLogger()) {}
     293             : 
     294             :     QueryExecutorResult PostgresQueryExecutor::validateAndExecute(
     295             :         const shared_model::interface::Query &query,
     296             :         const bool validate_signatories = true) {
     297         190 :       visitor_.setCreatorId(query.creatorAccountId());
     298         190 :       visitor_.setQueryHash(query.hash());
     299         190 :       if (validate_signatories and not validateSignatures(query)) {
     300             :         // TODO [IR-1816] Akvinikym 03.12.18: replace magic number 3
     301             :         // with a named constant
     302           3 :         return query_response_factory_->createErrorQueryResponse(
     303             :             shared_model::interface::QueryResponseFactory::ErrorQueryType::
     304             :                 kStatefulFailed,
     305           3 :             "query signatories did not pass validation",
     306             :             3,
     307           3 :             query.hash());
     308             :       }
     309         187 :       return boost::apply_visitor(visitor_, query.get());
     310         190 :     }
     311             : 
     312             :     bool PostgresQueryExecutor::validate(
     313             :         const shared_model::interface::BlocksQuery &query,
     314             :         const bool validate_signatories = true) {
     315           2 :       if (validate_signatories and not validateSignatures(query)) {
     316           0 :         log_->error("query signatories did not pass validation");
     317           0 :         return false;
     318             :       }
     319           2 :       if (not visitor_.hasAccountRolePermission(Role::kGetBlocks,
     320           2 :                                                 query.creatorAccountId())) {
     321           1 :         log_->error("query creator does not have enough permissions");
     322           1 :         return false;
     323             :       }
     324             : 
     325           1 :       return true;
     326           2 :     }
     327             : 
     328             :     bool PostgresQueryExecutorVisitor::hasAccountRolePermission(
     329             :         shared_model::interface::permissions::Role permission,
     330             :         const std::string &account_id) const {
     331             :       using T = boost::tuple<int>;
     332           5 :       boost::format cmd(R"(%s)");
     333             :       try {
     334           5 :         soci::rowset<T> st =
     335           5 :             (sql_.prepare
     336           5 :                  << (cmd % getAccountRolePermissionCheckSql(permission)).str(),
     337           5 :              soci::use(account_id, "role_account_id"));
     338           5 :         return st.begin()->get<0>();
     339           5 :       } catch (const std::exception &e) {
     340           0 :         log_->error("Failed to validate query: {}", e.what());
     341           0 :         return false;
     342           0 :       }
     343           5 :     }
     344             : 
     345             :     PostgresQueryExecutorVisitor::PostgresQueryExecutorVisitor(
     346             :         soci::session &sql,
     347             :         KeyValueStorage &block_store,
     348             :         std::shared_ptr<PendingTransactionStorage> pending_txs_storage,
     349             :         std::shared_ptr<shared_model::interface::BlockJsonConverter> converter,
     350             :         std::shared_ptr<shared_model::interface::QueryResponseFactory>
     351             :             response_factory,
     352             :         std::shared_ptr<shared_model::interface::PermissionToString>
     353             :             perm_converter,
     354             :         logger::LoggerPtr log)
     355         192 :         : sql_(sql),
     356         192 :           block_store_(block_store),
     357         192 :           pending_txs_storage_(std::move(pending_txs_storage)),
     358         192 :           converter_(std::move(converter)),
     359         192 :           query_response_factory_{std::move(response_factory)},
     360         192 :           perm_converter_(std::move(perm_converter)),
     361         192 :           log_(std::move(log)) {}
     362             : 
     363             :     void PostgresQueryExecutorVisitor::setCreatorId(
     364             :         const shared_model::interface::types::AccountIdType &creator_id) {
     365         190 :       creator_id_ = creator_id;
     366         190 :     }
     367             : 
     368             :     void PostgresQueryExecutorVisitor::setQueryHash(
     369             :         const shared_model::interface::types::HashType &query_hash) {
     370         190 :       query_hash_ = query_hash;
     371         190 :     }
     372             : 
     373             :     std::unique_ptr<shared_model::interface::QueryResponse>
     374             :     PostgresQueryExecutorVisitor::logAndReturnErrorResponse(
     375             :         QueryErrorType error_type,
     376             :         QueryErrorMessageType error_body,
     377             :         QueryErrorCodeType error_code) const {
     378          64 :       std::string error;
     379          64 :       switch (error_type) {
     380             :         case QueryErrorType::kNoAccount:
     381           1 :           error = "could find account with such id: " + error_body;
     382           1 :           break;
     383             :         case QueryErrorType::kNoSignatories:
     384           1 :           error = "no signatories found in account with such id: " + error_body;
     385           1 :           break;
     386             :         case QueryErrorType::kNoAccountDetail:
     387           1 :           error = "no details in account with such id: " + error_body;
     388           1 :           break;
     389             :         case QueryErrorType::kNoRoles:
     390           1 :           error =
     391           1 :               "no role with such name in account with such id: " + error_body;
     392           1 :           break;
     393             :         case QueryErrorType::kNoAsset:
     394           2 :           error =
     395           2 :               "no asset with such name in account with such id: " + error_body;
     396           2 :           break;
     397             :           // other errors are either handled by generic response or do not
     398             :           // appear yet
     399             :         default:
     400          58 :           error = "failed to execute query: " + error_body;
     401          58 :           break;
     402             :       }
     403             : 
     404          64 :       log_->error("{}", error);
     405          64 :       return query_response_factory_->createErrorQueryResponse(
     406          64 :           error_type, error, error_code, query_hash_);
     407          64 :     }
     408             : 
     409             :     template <typename Query,
     410             :               typename QueryChecker,
     411             :               typename QueryApplier,
     412             :               typename... Permissions>
     413             :     QueryExecutorResult PostgresQueryExecutorVisitor::executeTransactionsQuery(
     414             :         const Query &q,
     415             :         QueryChecker &&qry_checker,
     416             :         const std::string &related_txs,
     417             :         QueryApplier applier,
     418             :         Permissions... perms) {
     419             :       using QueryTuple = QueryType<shared_model::interface::types::HeightType,
     420             :                                    uint64_t,
     421             :                                    uint64_t>;
     422             :       using PermissionTuple = boost::tuple<int>;
     423          23 :       const auto &pagination_info = q.paginationMeta();
     424          23 :       auto first_hash = pagination_info.firstTxHash();
     425             :       // retrieve one extra transaction to populate next_hash
     426          23 :       auto query_size = pagination_info.pageSize() + 1u;
     427             : 
     428          23 :       auto base = boost::format(R"(WITH has_perms AS (%s),
     429             :       my_txs AS (%s),
     430             :       first_hash AS (%s),
     431             :       total_size AS (
     432             :         SELECT COUNT(*) FROM my_txs
     433             :       ),
     434             :       t AS (
     435             :         SELECT my_txs.height, my_txs.index
     436             :         FROM my_txs JOIN
     437             :         first_hash ON my_txs.height > first_hash.height
     438             :         OR (my_txs.height = first_hash.height AND
     439             :             my_txs.index >= first_hash.index)
     440             :         LIMIT :page_size
     441             :       )
     442             :       SELECT height, index, count, perm FROM t
     443             :       RIGHT OUTER JOIN has_perms ON TRUE
     444             :       JOIN total_size ON TRUE
     445             :       )");
     446             : 
     447             :       // select tx with specified hash
     448          23 :       auto first_by_hash = R"(SELECT height, index FROM position_by_hash
     449             :       WHERE hash = :hash LIMIT 1)";
     450             : 
     451             :       // select first ever tx
     452          23 :       auto first_tx = R"(SELECT height, index FROM position_by_hash
     453             :       ORDER BY height, index ASC LIMIT 1)";
     454             : 
     455          23 :       auto cmd = base % hasQueryPermission(creator_id_, q.accountId(), perms...)
     456          23 :           % related_txs;
     457          23 :       if (first_hash) {
     458           2 :         cmd = base % first_by_hash;
     459           2 :       } else {
     460          21 :         cmd = base % first_tx;
     461             :       }
     462             : 
     463          23 :       auto query = cmd.str();
     464             : 
     465          23 :       return executeQuery<QueryTuple, PermissionTuple>(
     466          23 :           applier(query),
     467             :           [&](auto range, auto &) {
     468          16 :             uint64_t total_size = 0;
     469          16 :             if (not boost::empty(range)) {
     470          12 :               total_size = boost::get<2>(*range.begin());
     471          12 :             }
     472          16 :             std::map<uint64_t, std::vector<uint64_t>> index;
     473             :             // unpack results to get map from block height to index of tx in
     474             :             // a block
     475             :             boost::for_each(range, [&index](auto t) {
     476             :               apply(t, [&index](auto &height, auto &idx, auto &) {
     477          33 :                 index[height].push_back(idx);
     478          33 :               });
     479          33 :             });
     480             : 
     481             :             std::vector<std::unique_ptr<shared_model::interface::Transaction>>
     482          16 :                 response_txs;
     483             :             // get transactions corresponding to indexes
     484          42 :             for (auto &block : index) {
     485          27 :               auto txs = this->getTransactionsFromBlock(
     486          27 :                   block.first,
     487             :                   [&block](auto) { return block.second; },
     488             :                   [](auto &) { return true; });
     489          27 :               std::move(
     490          27 :                   txs.begin(), txs.end(), std::back_inserter(response_txs));
     491          27 :             }
     492             : 
     493          16 :             if (response_txs.empty()) {
     494           4 :               if (first_hash) {
     495             :                 // if 0 transactions are returned, and there is a specified
     496             :                 // paging hash, we assume it's invalid, since query with valid
     497             :                 // hash is guaranteed to return at least one transaction
     498           1 :                 auto error = (boost::format("invalid pagination hash: %s")
     499           1 :                               % first_hash->hex())
     500           1 :                                  .str();
     501           1 :                 return this->logAndReturnErrorResponse(
     502           1 :                     QueryErrorType::kStatefulFailed, error, 4);
     503           1 :               }
     504             :               // if paging hash is not specified, we should check, why 0
     505             :               // transactions are returned - it can be because there are
     506             :               // actually no transactions for this query or some of the
     507             :               // parameters were wrong
     508           3 :               if (auto query_incorrect =
     509          23 :                       std::forward<QueryChecker>(qry_checker)(q)) {
     510           2 :                 return this->logAndReturnErrorResponse(
     511             :                     QueryErrorType::kStatefulFailed,
     512           2 :                     query_incorrect.error_message,
     513           2 :                     query_incorrect.error_code);
     514             :               }
     515           1 :             }
     516             : 
     517             :             // if the number of returned transactions is equal to the
     518             :             // page size + 1, it means that the last transaction is the
     519             :             // first one in the next page and we need to return it as
     520             :             // the next hash
     521          13 :             if (response_txs.size() == query_size) {
     522           1 :               auto next_hash = response_txs.back()->hash();
     523           1 :               response_txs.pop_back();
     524           1 :               return query_response_factory_->createTransactionsPageResponse(
     525           1 :                   std::move(response_txs), next_hash, total_size, query_hash_);
     526           1 :             }
     527             : 
     528          12 :             return query_response_factory_->createTransactionsPageResponse(
     529          12 :                 std::move(response_txs), total_size, query_hash_);
     530          16 :           },
     531          23 :           notEnoughPermissionsResponse(perm_converter_, perms...));
     532          23 :     }
     533             : 
     534             :     QueryExecutorResult PostgresQueryExecutorVisitor::operator()(
     535             :         const shared_model::interface::GetAccount &q) {
     536             :       using QueryTuple =
     537             :           QueryType<shared_model::interface::types::AccountIdType,
     538             :                     shared_model::interface::types::DomainIdType,
     539             :                     shared_model::interface::types::QuorumType,
     540             :                     shared_model::interface::types::DetailType,
     541             :                     std::string>;
     542             :       using PermissionTuple = boost::tuple<int>;
     543             : 
     544          19 :       auto cmd = (boost::format(R"(WITH has_perms AS (%s),
     545             :       t AS (
     546             :           SELECT a.account_id, a.domain_id, a.quorum, a.data, ARRAY_AGG(ar.role_id) AS roles
     547             :           FROM account AS a, account_has_roles AS ar
     548             :           WHERE a.account_id = :target_account_id
     549             :           AND ar.account_id = a.account_id
     550             :           GROUP BY a.account_id
     551             :       )
     552             :       SELECT account_id, domain_id, quorum, data, roles, perm
     553             :       FROM t RIGHT OUTER JOIN has_perms AS p ON TRUE
     554             :       )")
     555          19 :                   % hasQueryPermission(creator_id_,
     556          19 :                                        q.accountId(),
     557             :                                        Role::kGetMyAccount,
     558             :                                        Role::kGetAllAccounts,
     559             :                                        Role::kGetDomainAccounts))
     560          19 :                      .str();
     561             : 
     562             :       auto query_apply = [this](auto &account_id,
     563             :                                 auto &domain_id,
     564             :                                 auto &quorum,
     565             :                                 auto &data,
     566             :                                 auto &roles_str) {
     567          10 :         std::vector<shared_model::interface::types::RoleIdType> roles;
     568          10 :         auto roles_str_no_brackets = roles_str.substr(1, roles_str.size() - 2);
     569          10 :         boost::split(
     570             :             roles, roles_str_no_brackets, [](char c) { return c == ','; });
     571          10 :         return query_response_factory_->createAccountResponse(
     572          10 :             account_id, domain_id, quorum, data, std::move(roles), query_hash_);
     573          10 :       };
     574             : 
     575          19 :       return executeQuery<QueryTuple, PermissionTuple>(
     576             :           [&] {
     577          19 :             return (sql_.prepare << cmd,
     578          19 :                     soci::use(q.accountId(), "target_account_id"));
     579           0 :           },
     580             :           [this, &q, &query_apply](auto range, auto &) {
     581          11 :             if (range.empty()) {
     582           1 :               return this->logAndReturnErrorResponse(
     583           1 :                   QueryErrorType::kNoAccount, q.accountId(), 0);
     584             :             }
     585             : 
     586          10 :             return apply(range.front(), query_apply);
     587          11 :           },
     588          19 :           notEnoughPermissionsResponse(perm_converter_,
     589             :                                        Role::kGetMyAccount,
     590             :                                        Role::kGetAllAccounts,
     591             :                                        Role::kGetDomainAccounts));
     592          19 :     }
     593             : 
     594             :     QueryExecutorResult PostgresQueryExecutorVisitor::operator()(
     595             :         const shared_model::interface::GetBlock &q) {
     596           3 :       if (not hasAccountRolePermission(Role::kGetBlocks, creator_id_)) {
     597             :         // no permission
     598           1 :         return query_response_factory_->createErrorQueryResponse(
     599             :             shared_model::interface::QueryResponseFactory::ErrorQueryType::
     600             :                 kStatefulFailed,
     601           1 :             notEnoughPermissionsResponse(perm_converter_, Role::kGetBlocks)(),
     602             :             2,
     603           1 :             query_hash_);
     604             :       }
     605             : 
     606           2 :       auto ledger_height = block_store_.last_id();
     607           2 :       if (q.height() > ledger_height) {
     608             :         // invalid height
     609           1 :         return logAndReturnErrorResponse(
     610             :             QueryErrorType::kStatefulFailed,
     611           1 :             "requested height (" + std::to_string(q.height())
     612           1 :                 + ") is greater than the ledger's one ("
     613           1 :                 + std::to_string(ledger_height) + ")",
     614             :             3);
     615             :       }
     616             : 
     617             :       auto block_deserialization_msg = [height = q.height()] {
     618           1 :         return "could not retrieve block with given height: "
     619           1 :             + std::to_string(height);
     620           0 :       };
     621           1 :       auto serialized_block = block_store_.get(q.height());
     622           1 :       if (not serialized_block) {
     623             :         // for some reason, block with such height was not retrieved
     624           0 :         return logAndReturnErrorResponse(
     625           0 :             QueryErrorType::kStatefulFailed, block_deserialization_msg(), 1);
     626             :       }
     627             : 
     628           1 :       return converter_->deserialize(bytesToString(*serialized_block))
     629           1 :           .match(
     630             :               [this](iroha::expected::Value<
     631             :                      std::unique_ptr<shared_model::interface::Block>> &block) {
     632           1 :                 return this->query_response_factory_->createBlockResponse(
     633           1 :                     std::move(block.value), query_hash_);
     634           0 :               },
     635             :               [this, err_msg = block_deserialization_msg()](const auto &err) {
     636             :                 auto extended_error =
     637           0 :                     err_msg + ", because it was not deserialized: " + err.error;
     638           0 :                 return this->logAndReturnErrorResponse(
     639             :                     QueryErrorType::kStatefulFailed,
     640           0 :                     std::move(extended_error),
     641             :                     1);
     642           0 :               });
     643           3 :     }
     644             : 
     645             :     QueryExecutorResult PostgresQueryExecutorVisitor::operator()(
     646             :         const shared_model::interface::GetSignatories &q) {
     647             :       using QueryTuple = QueryType<std::string>;
     648             :       using PermissionTuple = boost::tuple<int>;
     649             : 
     650          20 :       auto cmd = (boost::format(R"(WITH has_perms AS (%s),
     651             :       t AS (
     652             :           SELECT public_key FROM account_has_signatory
     653             :           WHERE account_id = :account_id
     654             :       )
     655             :       SELECT public_key, perm FROM t
     656             :       RIGHT OUTER JOIN has_perms ON TRUE
     657             :       )")
     658          20 :                   % hasQueryPermission(creator_id_,
     659          20 :                                        q.accountId(),
     660             :                                        Role::kGetMySignatories,
     661             :                                        Role::kGetAllSignatories,
     662             :                                        Role::kGetDomainSignatories))
     663          20 :                      .str();
     664             : 
     665          20 :       return executeQuery<QueryTuple, PermissionTuple>(
     666             :           [&] { return (sql_.prepare << cmd, soci::use(q.accountId())); },
     667             :           [this, &q](auto range, auto &) {
     668          13 :             if (range.empty()) {
     669           1 :               return this->logAndReturnErrorResponse(
     670           1 :                   QueryErrorType::kNoSignatories, q.accountId(), 0);
     671             :             }
     672             : 
     673          12 :             auto pubkeys = boost::copy_range<
     674             :                 std::vector<shared_model::interface::types::PubkeyType>>(
     675             :                 range | boost::adaptors::transformed([](auto t) {
     676             :                   return apply(t, [&](auto &public_key) {
     677          44 :                     return shared_model::interface::types::PubkeyType{
     678          44 :                         shared_model::crypto::Blob::fromHexString(public_key)};
     679           0 :                   });
     680             :                 }));
     681             : 
     682          12 :             return query_response_factory_->createSignatoriesResponse(
     683          12 :                 pubkeys, query_hash_);
     684          13 :           },
     685          20 :           notEnoughPermissionsResponse(perm_converter_,
     686             :                                        Role::kGetMySignatories,
     687             :                                        Role::kGetAllSignatories,
     688             :                                        Role::kGetDomainSignatories));
     689          20 :     }
     690             : 
     691             :     QueryExecutorResult PostgresQueryExecutorVisitor::operator()(
     692             :         const shared_model::interface::GetAccountTransactions &q) {
     693          22 :       std::string related_txs = R"(SELECT DISTINCT height, index
     694             :       FROM index_by_creator_height
     695             :       WHERE creator_id = :account_id
     696             :       ORDER BY height, index ASC)";
     697             : 
     698          22 :       const auto &pagination_info = q.paginationMeta();
     699          22 :       auto first_hash = pagination_info.firstTxHash();
     700             :       // retrieve one extra transaction to populate next_hash
     701          22 :       auto query_size = pagination_info.pageSize() + 1u;
     702             : 
     703             :       auto apply_query = [&](const auto &query) {
     704             :         return [&] {
     705          22 :           if (first_hash) {
     706          22 :             return (sql_.prepare << query,
     707          22 :                     soci::use(q.accountId()),
     708           2 :                     soci::use(first_hash->hex()),
     709          22 :                     soci::use(query_size));
     710             :           } else {
     711          20 :             return (sql_.prepare << query,
     712          20 :                     soci::use(q.accountId()),
     713          20 :                     soci::use(query_size));
     714             :           }
     715          22 :         };
     716             :       };
     717             : 
     718             :       auto check_query = [this](const auto &q) {
     719           2 :         if (this->existsInDb<int>(
     720           2 :                 "account", "account_id", "quorum", q.accountId())) {
     721           1 :           return QueryFallbackCheckResult{};
     722             :         }
     723           1 :         return QueryFallbackCheckResult{
     724           1 :             5, "no account with such id found: " + q.accountId()};
     725           2 :       };
     726             : 
     727          22 :       return executeTransactionsQuery(q,
     728          22 :                                       std::move(check_query),
     729             :                                       related_txs,
     730          22 :                                       apply_query,
     731             :                                       Role::kGetMyAccTxs,
     732             :                                       Role::kGetAllAccTxs,
     733             :                                       Role::kGetDomainAccTxs);
     734          22 :     }
     735             : 
     736             :     QueryExecutorResult PostgresQueryExecutorVisitor::operator()(
     737             :         const shared_model::interface::GetTransactions &q) {
     738             :       auto escape = [](auto &hash) { return "'" + hash.hex() + "'"; };
     739          33 :       std::string hash_str = std::accumulate(
     740          33 :           std::next(q.transactionHashes().begin()),
     741          33 :           q.transactionHashes().end(),
     742          33 :           escape(q.transactionHashes().front()),
     743             :           [&escape](auto &acc, auto &val) { return acc + "," + escape(val); });
     744             : 
     745             :       using QueryTuple =
     746             :           QueryType<shared_model::interface::types::HeightType, std::string>;
     747             :       using PermissionTuple = boost::tuple<int, int>;
     748             : 
     749             :       auto cmd =
     750          34 :           (boost::format(R"(WITH has_my_perm AS (%s),
     751             :       has_all_perm AS (%s),
     752             :       t AS (
     753             :           SELECT height, hash FROM position_by_hash WHERE hash IN (%s)
     754             :       )
     755             :       SELECT height, hash, has_my_perm.perm, has_all_perm.perm FROM t
     756             :       RIGHT OUTER JOIN has_my_perm ON TRUE
     757             :       RIGHT OUTER JOIN has_all_perm ON TRUE
     758          33 :       )") % getAccountRolePermissionCheckSql(Role::kGetMyTxs, "account_id")
     759          34 :            % getAccountRolePermissionCheckSql(Role::kGetAllTxs, "account_id")
     760          33 :            % hash_str)
     761          33 :               .str();
     762             : 
     763          34 :       return executeQuery<QueryTuple, PermissionTuple>(
     764             :           [&] {
     765          34 :             return (sql_.prepare << cmd, soci::use(creator_id_, "account_id"));
     766           0 :           },
     767             :           [&](auto range, auto &my_perm, auto &all_perm) {
     768          34 :             if (boost::size(range) != q.transactionHashes().size()) {
     769             :               // TODO [IR-1816] Akvinikym 03.12.18: replace magic number 4
     770             :               // with a named constant
     771             :               // at least one of the hashes in the query was invalid -
     772             :               // nonexistent or permissions were missed
     773           2 :               return this->logAndReturnErrorResponse(
     774             :                   QueryErrorType::kStatefulFailed,
     775           2 :                   "At least one of the supplied hashes is incorrect",
     776             :                   4);
     777             :             }
     778          31 :             std::map<uint64_t, std::unordered_set<std::string>> index;
     779             :             boost::for_each(range, [&index](auto t) {
     780             :               apply(t, [&index](auto &height, auto &hash) {
     781          31 :                 index[height].insert(hash);
     782          31 :               });
     783          31 :             });
     784             : 
     785             :             std::vector<std::unique_ptr<shared_model::interface::Transaction>>
     786          31 :                 response_txs;
     787          62 :             for (auto &block : index) {
     788          20 :               auto txs = this->getTransactionsFromBlock(
     789          20 :                   block.first,
     790             :                   [](auto size) {
     791          31 :                     return boost::irange(static_cast<decltype(size)>(0), size);
     792             :                   },
     793             :                   [&](auto &tx) {
     794          32 :                     return block.second.count(tx.hash().hex()) > 0
     795          32 :                         and (all_perm
     796          31 :                              or (my_perm
     797          27 :                                  and tx.creatorAccountId() == creator_id_));
     798             :                   });
     799          31 :               std::move(
     800          31 :                   txs.begin(), txs.end(), std::back_inserter(response_txs));
     801          31 :             }
     802             : 
     803          31 :             return query_response_factory_->createTransactionsResponse(
     804          24 :                 std::move(response_txs), query_hash_);
     805          33 :           },
     806          34 :           notEnoughPermissionsResponse(
     807          34 :               perm_converter_, Role::kGetMyTxs, Role::kGetAllTxs));
     808          34 :     }
     809             : 
     810             :     QueryExecutorResult PostgresQueryExecutorVisitor::operator()(
     811             :         const shared_model::interface::GetAccountAssetTransactions &q) {
     812          23 :       std::string related_txs = R"(SELECT DISTINCT height, index
     813             :           FROM position_by_account_asset
     814             :           WHERE account_id = :account_id
     815             :           AND asset_id = :asset_id
     816             :           ORDER BY height, index ASC)";
     817             : 
     818          23 :       const auto &pagination_info = q.paginationMeta();
     819          23 :       auto first_hash = pagination_info.firstTxHash();
     820             :       // retrieve one extra transaction to populate next_hash
     821          23 :       auto query_size = pagination_info.pageSize() + 1u;
     822             : 
     823             :       auto apply_query = [&](const auto &query) {
     824             :         return [&] {
     825          23 :           if (first_hash) {
     826          23 :             return (sql_.prepare << query,
     827          23 :                     soci::use(q.accountId()),
     828           2 :                     soci::use(q.assetId()),
     829           2 :                     soci::use(first_hash->hex()),
     830          23 :                     soci::use(query_size));
     831             :           } else {
     832          21 :             return (sql_.prepare << query,
     833          21 :                     soci::use(q.accountId()),
     834          21 :                     soci::use(q.assetId()),
     835          21 :                     soci::use(query_size));
     836             :           }
     837          23 :         };
     838             :       };
     839             : 
     840             :       auto check_query = [this](const auto &q) {
     841           3 :         if (not this->existsInDb<int>(
     842           3 :                 "account", "account_id", "quorum", q.accountId())) {
     843           1 :           return QueryFallbackCheckResult{
     844           1 :               5, "no account with such id found: " + q.accountId()};
     845             :         }
     846           2 :         if (not this->existsInDb<int>(
     847           2 :                 "asset", "asset_id", "precision", q.assetId())) {
     848           1 :           return QueryFallbackCheckResult{
     849           1 :               6, "no asset with such id found: " + q.assetId()};
     850             :         }
     851             : 
     852           1 :         return QueryFallbackCheckResult{};
     853           3 :       };
     854             : 
     855          23 :       return executeTransactionsQuery(q,
     856          23 :                                       std::move(check_query),
     857             :                                       related_txs,
     858          23 :                                       apply_query,
     859             :                                       Role::kGetMyAccAstTxs,
     860             :                                       Role::kGetAllAccAstTxs,
     861             :                                       Role::kGetDomainAccAstTxs);
     862          23 :     }
     863             : 
     864             :     QueryExecutorResult PostgresQueryExecutorVisitor::operator()(
     865             :         const shared_model::interface::GetAccountAssets &q) {
     866             :       using QueryTuple =
     867             :           QueryType<shared_model::interface::types::AccountIdType,
     868             :                     shared_model::interface::types::AssetIdType,
     869             :                     std::string>;
     870             :       using PermissionTuple = boost::tuple<int>;
     871             : 
     872          19 :       auto cmd = (boost::format(R"(WITH has_perms AS (%s),
     873             :       t AS (
     874             :           SELECT * FROM account_has_asset
     875             :           WHERE account_id = :account_id
     876             :       )
     877             :       SELECT account_id, asset_id, amount, perm FROM t
     878             :       RIGHT OUTER JOIN has_perms ON TRUE
     879             :       )")
     880          19 :                   % hasQueryPermission(creator_id_,
     881          19 :                                        q.accountId(),
     882             :                                        Role::kGetMyAccAst,
     883             :                                        Role::kGetAllAccAst,
     884             :                                        Role::kGetDomainAccAst))
     885          19 :                      .str();
     886             : 
     887          19 :       return executeQuery<QueryTuple, PermissionTuple>(
     888             :           [&] { return (sql_.prepare << cmd, soci::use(q.accountId())); },
     889             :           [&](auto range, auto &) {
     890             :             std::vector<
     891             :                 std::tuple<shared_model::interface::types::AccountIdType,
     892             :                            shared_model::interface::types::AssetIdType,
     893             :                            shared_model::interface::Amount>>
     894          12 :                 assets;
     895             :             boost::for_each(range, [&assets](auto t) {
     896          17 :               apply(t,
     897             :                     [&assets](auto &account_id, auto &asset_id, auto &amount) {
     898          17 :                       assets.push_back(std::make_tuple(
     899          17 :                           std::move(account_id),
     900          17 :                           std::move(asset_id),
     901          17 :                           shared_model::interface::Amount(amount)));
     902          17 :                     });
     903          17 :             });
     904          12 :             return query_response_factory_->createAccountAssetResponse(
     905          12 :                 assets, query_hash_);
     906          12 :           },
     907          19 :           notEnoughPermissionsResponse(perm_converter_,
     908             :                                        Role::kGetMyAccAst,
     909             :                                        Role::kGetAllAccAst,
     910             :                                        Role::kGetDomainAccAst));
     911          19 :     }
     912             : 
     913             :     QueryExecutorResult PostgresQueryExecutorVisitor::operator()(
     914             :         const shared_model::interface::GetAccountDetail &q) {
     915             :       using QueryTuple = QueryType<shared_model::interface::types::DetailType>;
     916             :       using PermissionTuple = boost::tuple<int>;
     917             : 
     918          22 :       std::string query_detail;
     919          22 :       if (q.key() and q.writer()) {
     920           1 :         auto filled_json = (boost::format("{\"%s\", \"%s\"}") % q.writer().get()
     921           1 :                             % q.key().get());
     922           1 :         query_detail = (boost::format(R"(SELECT json_build_object('%s'::text,
     923             :             json_build_object('%s'::text, (SELECT data #>> '%s'
     924             :             FROM account WHERE account_id = :account_id))) AS json)")
     925           1 :                         % q.writer().get() % q.key().get() % filled_json)
     926           1 :                            .str();
     927          21 :       } else if (q.key() and not q.writer()) {
     928           1 :         query_detail =
     929           1 :             (boost::format(
     930             :                  R"(SELECT json_object_agg(key, value) AS json FROM (SELECT
     931             :             json_build_object(kv.key, json_build_object('%1%'::text,
     932             :             kv.value -> '%1%')) FROM jsonb_each((SELECT data FROM account
     933             :             WHERE account_id = :account_id)) kv WHERE kv.value ? '%1%') AS
     934             :             jsons, json_each(json_build_object))")
     935           1 :              % q.key().get())
     936           1 :                 .str();
     937          20 :       } else if (not q.key() and q.writer()) {
     938           1 :         query_detail = (boost::format(R"(SELECT json_build_object('%1%'::text,
     939             :           (SELECT data -> '%1%' FROM account WHERE account_id =
     940             :            :account_id)) AS json)")
     941           1 :                         % q.writer().get())
     942           1 :                            .str();
     943           1 :       } else {
     944          19 :         query_detail = (boost::format(R"(SELECT data#>>'{}' AS json FROM account
     945             :             WHERE account_id = :account_id)"))
     946          19 :                            .str();
     947             :       }
     948          22 :       auto cmd = (boost::format(R"(WITH has_perms AS (%s),
     949             :       detail AS (%s)
     950             :       SELECT json, perm FROM detail
     951             :       RIGHT OUTER JOIN has_perms ON TRUE
     952             :       )")
     953          22 :                   % hasQueryPermission(creator_id_,
     954          22 :                                        q.accountId(),
     955             :                                        Role::kGetMyAccDetail,
     956             :                                        Role::kGetAllAccDetail,
     957             :                                        Role::kGetDomainAccDetail)
     958          22 :                   % query_detail)
     959          22 :                      .str();
     960             : 
     961          22 :       return executeQuery<QueryTuple, PermissionTuple>(
     962             :           [&] {
     963          22 :             return (sql_.prepare << cmd,
     964          22 :                     soci::use(q.accountId(), "account_id"));
     965           0 :           },
     966             :           [this, &q](auto range, auto &) {
     967          15 :             if (range.empty()) {
     968           1 :               return this->logAndReturnErrorResponse(
     969           1 :                   QueryErrorType::kNoAccountDetail, q.accountId(), 0);
     970             :             }
     971             : 
     972             :             return apply(range.front(), [this](auto &json) {
     973          14 :               return query_response_factory_->createAccountDetailResponse(
     974          14 :                   json, query_hash_);
     975           0 :             });
     976          15 :           },
     977          22 :           notEnoughPermissionsResponse(perm_converter_,
     978             :                                        Role::kGetMyAccDetail,
     979             :                                        Role::kGetAllAccDetail,
     980             :                                        Role::kGetDomainAccDetail));
     981          22 :     }
     982             : 
     983             :     QueryExecutorResult PostgresQueryExecutorVisitor::operator()(
     984             :         const shared_model::interface::GetRoles &q) {
     985             :       using QueryTuple = QueryType<shared_model::interface::types::RoleIdType>;
     986             :       using PermissionTuple = boost::tuple<int>;
     987             : 
     988           8 :       auto cmd = (boost::format(
     989             :                       R"(WITH has_perms AS (%s)
     990             :       SELECT role_id, perm FROM role
     991             :       RIGHT OUTER JOIN has_perms ON TRUE
     992           8 :       )") % getAccountRolePermissionCheckSql(Role::kGetRoles))
     993           8 :                      .str();
     994             : 
     995           8 :       return executeQuery<QueryTuple, PermissionTuple>(
     996             :           [&] {
     997           8 :             return (sql_.prepare << cmd,
     998           8 :                     soci::use(creator_id_, "role_account_id"));
     999           0 :           },
    1000             :           [&](auto range, auto &) {
    1001           6 :             auto roles = boost::copy_range<
    1002             :                 std::vector<shared_model::interface::types::RoleIdType>>(
    1003             :                 range | boost::adaptors::transformed([](auto t) {
    1004             :                   return apply(t, [](auto &role_id) { return role_id; });
    1005             :                 }));
    1006             : 
    1007           6 :             return query_response_factory_->createRolesResponse(roles,
    1008           6 :                                                                 query_hash_);
    1009           6 :           },
    1010           8 :           notEnoughPermissionsResponse(perm_converter_, Role::kGetRoles));
    1011           8 :     }
    1012             : 
    1013             :     QueryExecutorResult PostgresQueryExecutorVisitor::operator()(
    1014             :         const shared_model::interface::GetRolePermissions &q) {
    1015             :       using QueryTuple = QueryType<std::string>;
    1016             :       using PermissionTuple = boost::tuple<int>;
    1017             : 
    1018           5 :       auto cmd = (boost::format(
    1019             :                       R"(WITH has_perms AS (%s),
    1020             :       perms AS (SELECT permission FROM role_has_permissions
    1021             :                 WHERE role_id = :role_name)
    1022             :       SELECT permission, perm FROM perms
    1023             :       RIGHT OUTER JOIN has_perms ON TRUE
    1024           5 :       )") % getAccountRolePermissionCheckSql(Role::kGetRoles))
    1025           5 :                      .str();
    1026             : 
    1027           5 :       return executeQuery<QueryTuple, PermissionTuple>(
    1028             :           [&] {
    1029           5 :             return (sql_.prepare << cmd,
    1030           5 :                     soci::use(creator_id_, "role_account_id"),
    1031           5 :                     soci::use(q.roleId(), "role_name"));
    1032           0 :           },
    1033             :           [this, &q](auto range, auto &) {
    1034           3 :             if (range.empty()) {
    1035           1 :               return this->logAndReturnErrorResponse(
    1036             :                   QueryErrorType::kNoRoles,
    1037           1 :                   "{" + q.roleId() + ", " + creator_id_ + "}",
    1038             :                   0);
    1039             :             }
    1040             : 
    1041             :             return apply(range.front(), [this](auto &permission) {
    1042           2 :               return query_response_factory_->createRolePermissionsResponse(
    1043           2 :                   shared_model::interface::RolePermissionSet(permission),
    1044           2 :                   query_hash_);
    1045             :             });
    1046           3 :           },
    1047           5 :           notEnoughPermissionsResponse(perm_converter_, Role::kGetRoles));
    1048           5 :     }
    1049             : 
    1050             :     QueryExecutorResult PostgresQueryExecutorVisitor::operator()(
    1051             :         const shared_model::interface::GetAssetInfo &q) {
    1052             :       using QueryTuple =
    1053             :           QueryType<shared_model::interface::types::DomainIdType, uint32_t>;
    1054             :       using PermissionTuple = boost::tuple<int>;
    1055             : 
    1056           5 :       auto cmd = (boost::format(
    1057             :                       R"(WITH has_perms AS (%s),
    1058             :       perms AS (SELECT domain_id, precision FROM asset
    1059             :                 WHERE asset_id = :asset_id)
    1060             :       SELECT domain_id, precision, perm FROM perms
    1061             :       RIGHT OUTER JOIN has_perms ON TRUE
    1062           5 :       )") % getAccountRolePermissionCheckSql(Role::kReadAssets))
    1063           5 :                      .str();
    1064             : 
    1065           5 :       return executeQuery<QueryTuple, PermissionTuple>(
    1066             :           [&] {
    1067           5 :             return (sql_.prepare << cmd,
    1068           5 :                     soci::use(creator_id_, "role_account_id"),
    1069           5 :                     soci::use(q.assetId(), "asset_id"));
    1070           0 :           },
    1071             :           [this, &q](auto range, auto &) {
    1072           3 :             if (range.empty()) {
    1073           2 :               return this->logAndReturnErrorResponse(
    1074             :                   QueryErrorType::kNoAsset,
    1075           2 :                   "{" + q.assetId() + ", " + creator_id_ + "}",
    1076             :                   0);
    1077             :             }
    1078             : 
    1079           1 :             return apply(range.front(),
    1080             :                          [this, &q](auto &domain_id, auto &precision) {
    1081           1 :                            return query_response_factory_->createAssetResponse(
    1082           1 :                                q.assetId(), domain_id, precision, query_hash_);
    1083           0 :                          });
    1084           3 :           },
    1085           5 :           notEnoughPermissionsResponse(perm_converter_, Role::kReadAssets));
    1086           5 :     }
    1087             : 
    1088             :     QueryExecutorResult PostgresQueryExecutorVisitor::operator()(
    1089             :         const shared_model::interface::GetPendingTransactions &q) {
    1090             :       std::vector<std::unique_ptr<shared_model::interface::Transaction>>
    1091           7 :           response_txs;
    1092             :       auto interface_txs =
    1093           7 :           pending_txs_storage_->getPendingTransactions(creator_id_);
    1094           7 :       response_txs.reserve(interface_txs.size());
    1095             : 
    1096           7 :       std::transform(interface_txs.begin(),
    1097           7 :                      interface_txs.end(),
    1098           7 :                      std::back_inserter(response_txs),
    1099             :                      [](auto &tx) { return clone(*tx); });
    1100           7 :       return query_response_factory_->createTransactionsResponse(
    1101           7 :           std::move(response_txs), query_hash_);
    1102           7 :     }
    1103             : 
    1104             :     template <typename ReturnValueType>
    1105             :     bool PostgresQueryExecutorVisitor::existsInDb(
    1106             :         const std::string &table_name,
    1107             :         const std::string &key_name,
    1108             :         const std::string &value_name,
    1109             :         const std::string &value) const {
    1110           7 :       auto cmd = (boost::format(R"(SELECT %s
    1111             :                                    FROM %s
    1112             :                                    WHERE %s = '%s'
    1113             :                                    LIMIT 1)")
    1114           7 :                   % value_name % table_name % key_name % value)
    1115           7 :                      .str();
    1116           7 :       soci::rowset<ReturnValueType> result = this->sql_.prepare << cmd;
    1117           7 :       return result.begin() != result.end();
    1118           7 :     }
    1119             : 
    1120             :   }  // namespace ametsuchi
    1121             : }  // namespace iroha

Generated by: LCOV version 1.13