LCOV - code coverage report
Current view: top level - irohad/ametsuchi/impl - postgres_command_executor.cpp (source / functions) Hit Total Coverage
Test: cleared_cor.info Lines: 523 551 94.9 %
Date: 2019-03-07 14:46:43 Functions: 113 123 91.9 %

          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_command_executor.hpp"
       7             : 
       8             : #include <soci/postgresql/soci-postgresql.h>
       9             : #include <boost/algorithm/string.hpp>
      10             : #include <boost/format.hpp>
      11             : #include "ametsuchi/impl/soci_utils.hpp"
      12             : #include "cryptography/public_key.hpp"
      13             : #include "interfaces/commands/add_asset_quantity.hpp"
      14             : #include "interfaces/commands/add_peer.hpp"
      15             : #include "interfaces/commands/add_signatory.hpp"
      16             : #include "interfaces/commands/append_role.hpp"
      17             : #include "interfaces/commands/create_account.hpp"
      18             : #include "interfaces/commands/create_asset.hpp"
      19             : #include "interfaces/commands/create_domain.hpp"
      20             : #include "interfaces/commands/create_role.hpp"
      21             : #include "interfaces/commands/detach_role.hpp"
      22             : #include "interfaces/commands/grant_permission.hpp"
      23             : #include "interfaces/commands/remove_signatory.hpp"
      24             : #include "interfaces/commands/revoke_permission.hpp"
      25             : #include "interfaces/commands/set_account_detail.hpp"
      26             : #include "interfaces/commands/set_quorum.hpp"
      27             : #include "interfaces/commands/subtract_asset_quantity.hpp"
      28             : #include "interfaces/commands/transfer_asset.hpp"
      29             : #include "interfaces/common_objects/types.hpp"
      30             : #include "interfaces/permission_to_string.hpp"
      31             : #include "utils/string_builder.hpp"
      32             : 
      33             : namespace {
      34             :   struct PreparedStatement {
      35             :     std::string command_name;
      36             :     std::string command_base;
      37             :     std::vector<std::string> permission_checks;
      38             : 
      39             :     static const std::string validationPrefix;
      40             :     static const std::string noValidationPrefix;
      41             :   };
      42             : 
      43             :   const std::string PreparedStatement::validationPrefix = "WithValidation";
      44             :   const std::string PreparedStatement::noValidationPrefix = "WithOutValidation";
      45             : 
      46             :   // Transforms prepared statement into two strings:
      47             :   //    1. SQL query with validation
      48             :   //    2. SQL query without validation
      49             :   std::pair<std::string, std::string> compileStatement(
      50             :       const PreparedStatement &statement) {
      51             :     // Create query with validation
      52       47712 :     auto with_validation = boost::format(statement.command_base)
      53       47712 :         % (statement.command_name + PreparedStatement::validationPrefix);
      54             : 
      55             :     // append all necessary checks to the query
      56      199794 :     for (const auto &check : statement.permission_checks) {
      57      152082 :       with_validation = with_validation % check;
      58             :     }
      59             : 
      60             :     // Create query without validation
      61       47712 :     auto without_validation = boost::format(statement.command_base)
      62       47712 :         % (statement.command_name + PreparedStatement::noValidationPrefix);
      63             : 
      64             :     // since checks are not needed, append empty strings to their place
      65      199794 :     for (size_t i = 0; i < statement.permission_checks.size(); i++) {
      66      152082 :       without_validation = without_validation % "";
      67      152082 :     }
      68             : 
      69       47712 :     return {with_validation.str(), without_validation.str()};
      70       47712 :   }
      71             : 
      72             :   void prepareStatement(soci::session &sql,
      73             :                         const PreparedStatement &statement) {
      74       47712 :     auto queries = compileStatement(statement);
      75             : 
      76       47712 :     sql << queries.first;
      77       47712 :     sql << queries.second;
      78       47712 :   }
      79             : 
      80             :   template <typename QueryArgsCallable>
      81             :   iroha::expected::Error<iroha::ametsuchi::CommandError> makeCommandError(
      82             :       std::string &&command_name,
      83             :       const iroha::ametsuchi::CommandError::ErrorCodeType code,
      84             :       QueryArgsCallable &&query_args) noexcept {
      85          15 :     return iroha::expected::makeError(iroha::ametsuchi::CommandError{
      86          15 :         std::move(command_name), code, query_args()});
      87           0 :   }
      88             : 
      89             :   /// mapping between pairs of SQL error substrings and related fake error
      90             :   /// codes, which are indices in this collection
      91             :   const std::vector<std::tuple<std::string, std::string>> kSqlToFakeErrorCode =
      92         410 :       {std::make_tuple("Key (account_id)=", "is not present in table"),
      93          41 :        std::make_tuple("Key (permittee_account_id)", "is not present in table"),
      94          41 :        std::make_tuple("Key (role_id)=", "is not present in table"),
      95          41 :        std::make_tuple("Key (domain_id)=", "is not present in table"),
      96          41 :        std::make_tuple("Key (asset_id)=", "already exists"),
      97          41 :        std::make_tuple("Key (domain_id)=", "already exists"),
      98          41 :        std::make_tuple("Key (role_id)=", "already exists"),
      99          41 :        std::make_tuple("Key (account_id, public_key)=", "already exists"),
     100          41 :        std::make_tuple("Key (account_id)=", "already exists"),
     101          41 :        std::make_tuple("Key (default_role)=", "is not present in table")};
     102             : 
     103             :   /// mapping between command name, fake error code and related real error code
     104             :   const std::map<std::string, std::map<int, int>> kCmdNameToErrorCode{
     105          41 :       std::make_pair(
     106             :           "AddSignatory",
     107          41 :           std::map<int, int>{std::make_pair(0, 3), std::make_pair(7, 4)}),
     108          41 :       std::make_pair(
     109             :           "AppendRole",
     110          41 :           std::map<int, int>{std::make_pair(0, 3), std::make_pair(2, 4)}),
     111          41 :       std::make_pair(
     112             :           "DetachRole",
     113          41 :           std::map<int, int>{std::make_pair(0, 3), std::make_pair(2, 5)}),
     114          41 :       std::make_pair("RemoveSignatory",
     115          41 :                      std::map<int, int>{std::make_pair(0, 3)}),
     116          41 :       std::make_pair("SetAccountDetail",
     117          41 :                      std::map<int, int>{std::make_pair(0, 3)}),
     118          41 :       std::make_pair("SetQuorum", std::map<int, int>{std::make_pair(0, 3)}),
     119          41 :       std::make_pair("GrantPermission",
     120          41 :                      std::map<int, int>{std::make_pair(1, 3)}),
     121          41 :       std::make_pair("RevokePermission",
     122          41 :                      std::map<int, int>{std::make_pair(1, 3)}),
     123          41 :       std::make_pair(
     124             :           "CreateAccount",
     125          41 :           std::map<int, int>{std::make_pair(3, 3), std::make_pair(8, 4)}),
     126          41 :       std::make_pair(
     127             :           "CreateAsset",
     128          41 :           std::map<int, int>{std::make_pair(3, 3), std::make_pair(4, 4)}),
     129          41 :       std::make_pair(
     130             :           "CreateDomain",
     131          41 :           std::map<int, int>{std::make_pair(5, 3), std::make_pair(9, 4)}),
     132          41 :       std::make_pair("CreateRole", std::map<int, int>{std::make_pair(6, 3)}),
     133          41 :       std::make_pair("AddSignatory", std::map<int, int>{std::make_pair(7, 4)})};
     134             : 
     135             :   /**
     136             :    * Get a real error code based on the fake one and a command name
     137             :    * @param fake_error_code - inner error code to be translated into the user's
     138             :    * one
     139             :    * @param command_name of the failed command
     140             :    * @return real error code
     141             :    */
     142             :   boost::optional<iroha::ametsuchi::CommandError::ErrorCodeType>
     143             :   getRealErrorCode(size_t fake_error_code, const std::string &command_name) {
     144          18 :     auto fake_to_real_code = kCmdNameToErrorCode.find(command_name);
     145          18 :     if (fake_to_real_code == kCmdNameToErrorCode.end()) {
     146           0 :       return {};
     147             :     }
     148             : 
     149          18 :     auto real_code = fake_to_real_code->second.find(fake_error_code);
     150          18 :     if (real_code == fake_to_real_code->second.end()) {
     151           0 :       return {};
     152             :     }
     153             : 
     154          18 :     return real_code->second;
     155          18 :   }
     156             : 
     157             :   // TODO [IR-1830] Akvinikym 31.10.18: make benchmarks to compare exception
     158             :   // parsing vs nested queries
     159             :   /**
     160             :    * Get an error code from the text SQL error
     161             :    * @tparam QueryArgsCallable - type of callable to get query arguments
     162             :    * @param command_name - name of the failed command
     163             :    * @param error - string error, which SQL gave out
     164             :    * @param query_args - callable to get a string representation of query
     165             :    * arguments
     166             :    * @return command_error structure
     167             :    */
     168             :   template <typename QueryArgsCallable>
     169             :   iroha::ametsuchi::CommandResult getCommandError(
     170             :       std::string &&command_name,
     171             :       const std::string &error,
     172             :       QueryArgsCallable &&query_args) noexcept {
     173           5 :     std::string key, to_be_presented;
     174             :     bool errors_matched;
     175             : 
     176             :     // go through mapping of SQL errors and get index of the current error - it
     177             :     // is "fake" error code
     178          38 :     for (size_t fakeErrorCode = 0; fakeErrorCode < kSqlToFakeErrorCode.size();
     179          33 :          ++fakeErrorCode) {
     180          38 :       std::tie(key, to_be_presented) = kSqlToFakeErrorCode[fakeErrorCode];
     181          38 :       errors_matched = error.find(key) != std::string::npos
     182          38 :           and error.find(to_be_presented) != std::string::npos;
     183          38 :       if (errors_matched) {
     184           5 :         if (auto real_error_code =
     185           5 :                 getRealErrorCode(fakeErrorCode, command_name)) {
     186           5 :           return makeCommandError(std::move(command_name),
     187           5 :                                   *real_error_code,
     188           5 :                                   std::forward<QueryArgsCallable>(query_args));
     189             :         }
     190           0 :         break;
     191             :       }
     192          33 :     }
     193             :     // parsing is not successful, return the general error
     194           1 :     return makeCommandError(std::move(command_name),
     195             :                             1,
     196           1 :                             std::forward<QueryArgsCallable>(query_args));
     197           5 :   }
     198             : 
     199             :   /**
     200             :    * Executes sql query
     201             :    * Assumes that statement query returns 0 in case of success
     202             :    * or error code in case of failure
     203             :    * @tparam QueryArgsCallable - type of callable to get query arguments
     204             :    * @param sql - connection on which to execute statement
     205             :    * @param cmd - sql query to be executed
     206             :    * @param command_name - which command executes a query
     207             :    * @param query_args - callable to get a string representation of query
     208             :    * arguments
     209             :    * @return CommandResult with command name and error message
     210             :    */
     211             :   template <typename QueryArgsCallable>
     212             :   iroha::ametsuchi::CommandResult executeQuery(
     213             :       soci::session &sql,
     214             :       const std::string &cmd,
     215             :       std::string command_name,
     216             :       QueryArgsCallable &&query_args) noexcept {
     217             :     uint32_t result;
     218             :     try {
     219        1618 :       sql << cmd, soci::into(result);
     220        1616 :       if (result != 0) {
     221          15 :         return makeCommandError(std::move(command_name),
     222          15 :                                 result,
     223          15 :                                 std::forward<QueryArgsCallable>(query_args));
     224             :       }
     225        1614 :       return {};
     226           5 :     } catch (const std::exception &e) {
     227           5 :       return getCommandError(std::move(command_name),
     228           5 :                              e.what(),
     229           5 :                              std::forward<QueryArgsCallable>(query_args));
     230           5 :     }
     231        1618 :   }
     232             : 
     233             :   std::string checkAccountRolePermission(
     234             :       shared_model::interface::permissions::Role permission,
     235             :       const shared_model::interface::types::AccountIdType &account_id) {
     236             :     const auto perm_str =
     237       50694 :         shared_model::interface::RolePermissionSet({permission}).toBitstring();
     238       50694 :     const auto bits = shared_model::interface::RolePermissionSet::size();
     239       50694 :     std::string query = (boost::format(R"(
     240             :           SELECT COALESCE(bit_or(rp.permission), '0'::bit(%1%))
     241             :           & '%2%' = '%2%' FROM role_has_permissions AS rp
     242             :               JOIN account_has_roles AS ar on ar.role_id = rp.role_id
     243             :               WHERE ar.account_id = %3%)")
     244       50694 :                          % bits % perm_str % account_id)
     245       50694 :                             .str();
     246       50694 :     return query;
     247       50694 :   }
     248             : 
     249             :   std::string checkAccountGrantablePermission(
     250             :       shared_model::interface::permissions::Grantable permission,
     251             :       const shared_model::interface::types::AccountIdType &creator_id,
     252             :       const shared_model::interface::types::AccountIdType &account_id) {
     253             :     const auto perm_str =
     254       14910 :         shared_model::interface::GrantablePermissionSet({permission})
     255       14910 :             .toBitstring();
     256       14910 :     const auto bits = shared_model::interface::GrantablePermissionSet::size();
     257       14910 :     std::string query = (boost::format(R"(
     258             :           SELECT COALESCE(bit_or(permission), '0'::bit(%1%))
     259             :           & '%2%' = '%2%' FROM account_has_grantable_permissions
     260             :               WHERE account_id = %4% AND
     261             :               permittee_account_id = %3%
     262       14910 :           )") % bits % perm_str
     263       14910 :                          % creator_id % account_id)
     264       14910 :                             .str();
     265       14910 :     return query;
     266       14910 :   }
     267             : 
     268             :   std::string checkAccountDomainRoleOrGlobalRolePermission(
     269             :       shared_model::interface::permissions::Role global_permission,
     270             :       shared_model::interface::permissions::Role domain_permission,
     271             :       const shared_model::interface::types::AccountIdType &creator_id,
     272             :       const shared_model::interface::types::AssetIdType
     273             :           &id_with_target_domain) {
     274        5964 :     std::string query = (boost::format(R"(WITH
     275             :           has_global_role_perm AS (%1%),
     276             :           has_domain_role_perm AS (%2%)
     277             :           SELECT CASE
     278             :                            WHEN (SELECT * FROM has_global_role_perm) THEN true
     279             :                            WHEN ((split_part(%3%, '@', 2) = split_part(%4%, '#', 2))) THEN
     280             :                                CASE
     281             :                                    WHEN (SELECT * FROM has_domain_role_perm) THEN true
     282             :                                    ELSE false
     283             :                                 END
     284             :                            ELSE false END
     285        5964 :           )") % checkAccountRolePermission(global_permission, creator_id)
     286        5964 :                          % checkAccountRolePermission(domain_permission,
     287        5964 :                                                       creator_id)
     288        5964 :                          % creator_id % id_with_target_domain)
     289        5964 :                             .str();
     290        5964 :     return query;
     291        5964 :   }
     292             : 
     293             :   std::string checkAccountHasRoleOrGrantablePerm(
     294             :       shared_model::interface::permissions::Role role,
     295             :       shared_model::interface::permissions::Grantable grantable,
     296             :       const shared_model::interface::types::AccountIdType &creator_id,
     297             :       const shared_model::interface::types::AccountIdType &account_id) {
     298        8946 :     return (boost::format(R"(WITH
     299             :           has_role_perm AS (%s),
     300             :           has_grantable_perm AS (%s)
     301             :           SELECT CASE
     302             :                            WHEN (SELECT * FROM has_grantable_perm) THEN true
     303             :                            WHEN (%s = %s) THEN
     304             :                                CASE
     305             :                                    WHEN (SELECT * FROM has_role_perm) THEN true
     306             :                                    ELSE false
     307             :                                 END
     308             :                            ELSE false END
     309             :           )")
     310        8946 :             % checkAccountRolePermission(role, creator_id)
     311        8946 :             % checkAccountGrantablePermission(grantable, creator_id, account_id)
     312        8946 :             % creator_id % account_id)
     313        8946 :         .str();
     314           0 :   }
     315             : 
     316             :   template <typename Format>
     317             :   void appendCommandName(const std::string &name,
     318             :                          Format &cmd,
     319             :                          bool do_validation) {
     320        7038 :     auto command_name = name
     321        7038 :         + (do_validation ? PreparedStatement::validationPrefix
     322             :                          : PreparedStatement::noValidationPrefix);
     323        7038 :     cmd % command_name;
     324        7038 :   }
     325             : 
     326             :   /**
     327             :    * Get a pretty string builder initialized for query arguments append
     328             :    * @return string builder
     329             :    */
     330             :   shared_model::detail::PrettyStringBuilder getQueryArgsStringBuilder() {
     331         100 :     return shared_model::detail::PrettyStringBuilder().init("Query arguments");
     332           0 :   }
     333             : }  // namespace
     334             : 
     335             : namespace iroha {
     336             :   namespace ametsuchi {
     337             :     // TODO [IR-1830] Akvinikym 31.10.18: make benchmarks to compare exception
     338             :     // parsing vs nested queries
     339             :     const std::string PostgresCommandExecutor::addAssetQuantityBase = R"(
     340             :           PREPARE %s (text, text, int, text) AS
     341             :           WITH has_account AS (SELECT account_id FROM account
     342             :                                WHERE account_id = $1 LIMIT 1),
     343             :                has_asset AS (SELECT asset_id FROM asset
     344             :                              WHERE asset_id = $2 AND
     345             :                              precision >= $3 LIMIT 1),
     346             :                %s
     347             :                amount AS (SELECT amount FROM account_has_asset
     348             :                           WHERE asset_id = $2 AND
     349             :                           account_id = $1 LIMIT 1),
     350             :                new_value AS (SELECT $4::decimal +
     351             :                               (SELECT
     352             :                                   CASE WHEN EXISTS
     353             :                                       (SELECT amount FROM amount LIMIT 1) THEN
     354             :                                       (SELECT amount FROM amount LIMIT 1)
     355             :                                   ELSE 0::decimal
     356             :                               END) AS value
     357             :                           ),
     358             :                inserted AS
     359             :                (
     360             :                   INSERT INTO account_has_asset(account_id, asset_id, amount)
     361             :                   (
     362             :                       SELECT $1, $2, value FROM new_value
     363             :                       WHERE EXISTS (SELECT * FROM has_account LIMIT 1) AND
     364             :                         EXISTS (SELECT * FROM has_asset LIMIT 1) AND
     365             :                         EXISTS (SELECT value FROM new_value
     366             :                                 WHERE value < 2::decimal ^ (256 - $3)
     367             :                                 LIMIT 1)
     368             :                         %s
     369             :                   )
     370             :                   ON CONFLICT (account_id, asset_id) DO UPDATE
     371             :                   SET amount = EXCLUDED.amount
     372             :                   RETURNING (1)
     373             :                )
     374             :           SELECT CASE
     375             :               WHEN EXISTS (SELECT * FROM inserted LIMIT 1) THEN 0
     376             :               %s
     377             :               WHEN NOT EXISTS (SELECT * FROM has_asset LIMIT 1) THEN 3
     378             :               WHEN NOT EXISTS (SELECT value FROM new_value
     379             :                                WHERE value < 2::decimal ^ (256 - $3)
     380             :                                LIMIT 1) THEN 4
     381             :               ELSE 1
     382             :           END AS result;)";
     383             : 
     384             :     const std::string PostgresCommandExecutor::addPeerBase = R"(
     385             :           PREPARE %s (text, text, text) AS
     386             :           WITH
     387             :           %s
     388             :           inserted AS (
     389             :               INSERT INTO peer(public_key, address)
     390             :               (
     391             :                   SELECT $2, $3
     392             :                   %s
     393             :               ) RETURNING (1)
     394             :           )
     395             :           SELECT CASE WHEN EXISTS (SELECT * FROM inserted) THEN 0
     396             :               %s
     397             :               ELSE 1 END AS result)";
     398             : 
     399             :     const std::string PostgresCommandExecutor::addSignatoryBase = R"(
     400             :           PREPARE %s (text, text, text) AS
     401             :           WITH %s
     402             :           insert_signatory AS
     403             :           (
     404             :               INSERT INTO signatory(public_key)
     405             :               (SELECT $3 %s) ON CONFLICT DO NOTHING RETURNING (1)
     406             :           ),
     407             :           has_signatory AS (SELECT * FROM signatory WHERE public_key = $3),
     408             :           insert_account_signatory AS
     409             :           (
     410             :               INSERT INTO account_has_signatory(account_id, public_key)
     411             :               (
     412             :                   SELECT $2, $3 WHERE (EXISTS
     413             :                   (SELECT * FROM insert_signatory) OR
     414             :                   EXISTS (SELECT * FROM has_signatory))
     415             :                   %s
     416             :               )
     417             :               RETURNING (1)
     418             :           )
     419             :           SELECT CASE
     420             :               WHEN EXISTS (SELECT * FROM insert_account_signatory) THEN 0
     421             :               %s
     422             :               ELSE 1
     423             :           END AS RESULT;)";
     424             : 
     425             :     const std::string PostgresCommandExecutor::appendRoleBase = R"(
     426             :             PREPARE %s (text, text, text) AS
     427             :             WITH %s
     428             :             role_exists AS (SELECT * FROM role WHERE role_id = $3),
     429             :             inserted AS (
     430             :                 INSERT INTO account_has_roles(account_id, role_id)
     431             :                 (
     432             :                     SELECT $2, $3 %s) RETURNING (1)
     433             :             )
     434             :             SELECT CASE
     435             :                 WHEN EXISTS (SELECT * FROM inserted) THEN 0
     436             :                 WHEN NOT EXISTS (SELECT * FROM role_exists) THEN 4
     437             :                 %s
     438             :                 ELSE 1
     439             :             END AS result)";
     440             : 
     441             :     const std::string PostgresCommandExecutor::createAccountBase = R"(
     442             :           PREPARE %s (text, text, text, text) AS
     443             :           WITH get_domain_default_role AS (SELECT default_role FROM domain
     444             :                                            WHERE domain_id = $3),
     445             :           %s
     446             :           insert_signatory AS
     447             :           (
     448             :               INSERT INTO signatory(public_key)
     449             :               (
     450             :                   SELECT $4 WHERE EXISTS
     451             :                   (SELECT * FROM get_domain_default_role)
     452             :               ) ON CONFLICT DO NOTHING RETURNING (1)
     453             :           ),
     454             :           has_signatory AS (SELECT * FROM signatory WHERE public_key = $4),
     455             :           insert_account AS
     456             :           (
     457             :               INSERT INTO account(account_id, domain_id, quorum, data)
     458             :               (
     459             :                   SELECT $2, $3, 1, '{}' WHERE (EXISTS
     460             :                       (SELECT * FROM insert_signatory) OR EXISTS
     461             :                       (SELECT * FROM has_signatory)
     462             :                   ) AND EXISTS (SELECT * FROM get_domain_default_role)
     463             :                   %s
     464             :               ) RETURNING (1)
     465             :           ),
     466             :           insert_account_signatory AS
     467             :           (
     468             :               INSERT INTO account_has_signatory(account_id, public_key)
     469             :               (
     470             :                   SELECT $2, $4 WHERE
     471             :                      EXISTS (SELECT * FROM insert_account)
     472             :               )
     473             :               RETURNING (1)
     474             :           ),
     475             :           insert_account_role AS
     476             :           (
     477             :               INSERT INTO account_has_roles(account_id, role_id)
     478             :               (
     479             :                   SELECT $2, default_role FROM get_domain_default_role
     480             :                   WHERE EXISTS (SELECT * FROM get_domain_default_role)
     481             :                     AND EXISTS (SELECT * FROM insert_account_signatory)
     482             :               ) RETURNING (1)
     483             :           )
     484             :           SELECT CASE
     485             :               WHEN EXISTS (SELECT * FROM insert_account_role) THEN 0
     486             :               %s
     487             :               WHEN NOT EXISTS (SELECT * FROM get_domain_default_role) THEN 3
     488             :               ELSE 1
     489             :               END AS result)";
     490             : 
     491             :     const std::string PostgresCommandExecutor::createAssetBase = R"(
     492             :               PREPARE %s (text, text, text, int) AS
     493             :               WITH %s
     494             :               inserted AS
     495             :               (
     496             :                   INSERT INTO asset(asset_id, domain_id, precision, data)
     497             :                   (
     498             :                       SELECT $2, $3, $4, NULL
     499             :                       %s
     500             :                   ) RETURNING (1)
     501             :               )
     502             :               SELECT CASE WHEN EXISTS (SELECT * FROM inserted) THEN 0
     503             :               %s
     504             :               ELSE 1 END AS result)";
     505             : 
     506             :     const std::string PostgresCommandExecutor::createDomainBase = R"(
     507             :               PREPARE %s (text, text, text) AS
     508             :               WITH %s
     509             :               inserted AS
     510             :               (
     511             :                   INSERT INTO domain(domain_id, default_role)
     512             :                   (
     513             :                       SELECT $2, $3
     514             :                       %s
     515             :                   ) RETURNING (1)
     516             :               )
     517             :               SELECT CASE WHEN EXISTS (SELECT * FROM inserted) THEN 0
     518             :               %s
     519             :               ELSE 1 END AS result)";
     520             : 
     521             :     const std::string PostgresCommandExecutor::createRoleBase = R"(
     522             :           PREPARE %s (text, text, bit) AS
     523             :           WITH %s
     524             :           insert_role AS (INSERT INTO role(role_id)
     525             :                               (SELECT $2
     526             :                               %s) RETURNING (1)),
     527             :           insert_role_permissions AS
     528             :           (
     529             :               INSERT INTO role_has_permissions(role_id, permission)
     530             :               (
     531             :                   SELECT $2, $3 WHERE EXISTS
     532             :                       (SELECT * FROM insert_role)
     533             :               ) RETURNING (1)
     534             :           )
     535             :           SELECT CASE
     536             :               WHEN EXISTS (SELECT * FROM insert_role_permissions) THEN 0
     537             :               %s
     538             :               WHEN EXISTS (SELECT * FROM role WHERE role_id = $2) THEN 2
     539             :               ELSE 1
     540             :               END AS result)";
     541             : 
     542             :     const std::string PostgresCommandExecutor::detachRoleBase = R"(
     543             :             PREPARE %s (text, text, text) AS
     544             :             WITH %s
     545             :             deleted AS
     546             :             (
     547             :               DELETE FROM account_has_roles
     548             :               WHERE account_id=$2
     549             :               AND role_id=$3
     550             :               %s
     551             :               RETURNING (1)
     552             :             )
     553             :             SELECT CASE WHEN EXISTS (SELECT * FROM deleted) THEN 0
     554             :             WHEN NOT EXISTS (SELECT * FROM account
     555             :                              WHERE account_id = $2) THEN 3
     556             :             WHEN NOT EXISTS (SELECT * FROM role
     557             :                              WHERE role_id = $3) THEN 5
     558             :             WHEN NOT EXISTS (SELECT * FROM account_has_roles
     559             :                              WHERE account_id=$2 AND role_id=$3) THEN 4
     560             :             %s
     561             :             ELSE 1 END AS result)";
     562             : 
     563             :     const std::string PostgresCommandExecutor::grantPermissionBase = R"(
     564             :           PREPARE %s (text, text, bit, bit) AS
     565             :           WITH %s
     566             :             inserted AS (
     567             :               INSERT INTO account_has_grantable_permissions AS
     568             :               has_perm(permittee_account_id, account_id, permission)
     569             :               (SELECT $2, $1, $3 %s) ON CONFLICT
     570             :               (permittee_account_id, account_id)
     571             :               DO UPDATE SET permission=(SELECT has_perm.permission | $3
     572             :               WHERE (has_perm.permission & $3) <> $3)
     573             :               RETURNING (1)
     574             :             )
     575             :             SELECT CASE WHEN EXISTS (SELECT * FROM inserted) THEN 0
     576             :               %s
     577             :               ELSE 1 END AS result)";
     578             : 
     579             :     const std::string PostgresCommandExecutor::removeSignatoryBase = R"(
     580             :           PREPARE %s (text, text, text) AS
     581             :           WITH
     582             :           %s
     583             :           delete_account_signatory AS (DELETE FROM account_has_signatory
     584             :               WHERE account_id = $2
     585             :               AND public_key = $3
     586             :               %s
     587             :               RETURNING (1)),
     588             :           delete_signatory AS
     589             :           (
     590             :               DELETE FROM signatory WHERE public_key = $3 AND
     591             :                   NOT EXISTS (SELECT 1 FROM account_has_signatory
     592             :                               WHERE public_key = $3)
     593             :                   AND NOT EXISTS (SELECT 1 FROM peer WHERE public_key = $3)
     594             :               RETURNING (1)
     595             :           )
     596             :           SELECT CASE
     597             :               WHEN EXISTS (SELECT * FROM delete_account_signatory) THEN
     598             :               CASE
     599             :                   WHEN EXISTS (SELECT * FROM delete_signatory) THEN 0
     600             :                   WHEN EXISTS (SELECT 1 FROM account_has_signatory
     601             :                                WHERE public_key = $3) THEN 0
     602             :                   WHEN EXISTS (SELECT 1 FROM peer
     603             :                                WHERE public_key = $3) THEN 0
     604             :                   ELSE 1
     605             :               END
     606             :               %s
     607             :               ELSE 1
     608             :           END AS result)";
     609             : 
     610             :     const std::string PostgresCommandExecutor::revokePermissionBase = R"(
     611             :           PREPARE %s (text, text, bit, bit) AS
     612             :           WITH %s
     613             :               inserted AS (
     614             :                   UPDATE account_has_grantable_permissions as has_perm
     615             :                   SET permission=(SELECT has_perm.permission & $4
     616             :                   WHERE has_perm.permission & $3 = $3 AND
     617             :                   has_perm.permittee_account_id=$2 AND
     618             :                   has_perm.account_id=$1) WHERE
     619             :                   permittee_account_id=$2 AND
     620             :                   account_id=$1 %s
     621             :                 RETURNING (1)
     622             :               )
     623             :               SELECT CASE WHEN EXISTS (SELECT * FROM inserted) THEN 0
     624             :                   %s
     625             :                   ELSE 1 END AS result)";
     626             : 
     627             :     const std::string PostgresCommandExecutor::setAccountDetailBase = R"(
     628             :           PREPARE %s (text, text, text[], text[], text, text) AS
     629             :           WITH %s
     630             :               inserted AS
     631             :               (
     632             :                   UPDATE account SET data = jsonb_set(
     633             :                   CASE WHEN data ?$1 THEN data ELSE
     634             :                   jsonb_set(data, $3, $6::jsonb) END,
     635             :                   $4, $5::jsonb) WHERE account_id=$2 %s
     636             :                   RETURNING (1)
     637             :               )
     638             :               SELECT CASE WHEN EXISTS (SELECT * FROM inserted) THEN 0
     639             :                   %s
     640             :                   WHEN NOT EXISTS
     641             :                       (SELECT * FROM account WHERE account_id=$2) THEN 3
     642             :                   ELSE 1 END AS result)";
     643             : 
     644             :     const std::string PostgresCommandExecutor::setQuorumBase = R"(
     645             :           PREPARE %s (text, text, int) AS
     646             :           WITH
     647             :           %s
     648             :           %s
     649             :           updated AS (
     650             :               UPDATE account SET quorum=$3
     651             :               WHERE account_id=$2
     652             :               %s
     653             :               RETURNING (1)
     654             :           )
     655             :           SELECT CASE WHEN EXISTS (SELECT * FROM updated) THEN 0
     656             :               %s
     657             :               ELSE 1
     658             :           END AS result)";
     659             : 
     660             :     const std::string PostgresCommandExecutor::subtractAssetQuantityBase = R"(
     661             :           PREPARE %s (text, text, int, text) AS
     662             :           WITH %s
     663             :                has_account AS (SELECT account_id FROM account
     664             :                                WHERE account_id = $1 LIMIT 1),
     665             :                has_asset AS (SELECT asset_id FROM asset
     666             :                              WHERE asset_id = $2
     667             :                              AND precision >= $3 LIMIT 1),
     668             :                amount AS (SELECT amount FROM account_has_asset
     669             :                           WHERE asset_id = $2
     670             :                           AND account_id = $1 LIMIT 1),
     671             :                new_value AS (SELECT
     672             :                               (SELECT
     673             :                                   CASE WHEN EXISTS
     674             :                                       (SELECT amount FROM amount LIMIT 1)
     675             :                                       THEN (SELECT amount FROM amount LIMIT 1)
     676             :                                   ELSE 0::decimal
     677             :                               END) - $4::decimal AS value
     678             :                           ),
     679             :                inserted AS
     680             :                (
     681             :                   INSERT INTO account_has_asset(account_id, asset_id, amount)
     682             :                   (
     683             :                       SELECT $1, $2, value FROM new_value
     684             :                       WHERE EXISTS (SELECT * FROM has_account LIMIT 1) AND
     685             :                         EXISTS (SELECT * FROM has_asset LIMIT 1) AND
     686             :                         EXISTS (SELECT value FROM new_value WHERE value >= 0 LIMIT 1)
     687             :                         %s
     688             :                   )
     689             :                   ON CONFLICT (account_id, asset_id)
     690             :                   DO UPDATE SET amount = EXCLUDED.amount
     691             :                   RETURNING (1)
     692             :                )
     693             :           SELECT CASE
     694             :               WHEN EXISTS (SELECT * FROM inserted LIMIT 1) THEN 0
     695             :               %s
     696             :               WHEN NOT EXISTS (SELECT * FROM has_asset LIMIT 1) THEN 3
     697             :               WHEN NOT EXISTS
     698             :                   (SELECT value FROM new_value WHERE value >= 0 LIMIT 1) THEN 4
     699             :               ELSE 1
     700             :           END AS result)";
     701             : 
     702             :     const std::string PostgresCommandExecutor::transferAssetBase = R"(
     703             :           PREPARE %s (text, text, text, text, int, text) AS
     704             :           WITH
     705             :               %s
     706             :               has_src_account AS (SELECT account_id FROM account
     707             :                                    WHERE account_id = $2 LIMIT 1),
     708             :               has_dest_account AS (SELECT account_id FROM account
     709             :                                     WHERE account_id = $3
     710             :                                     LIMIT 1),
     711             :               has_asset AS (SELECT asset_id FROM asset
     712             :                              WHERE asset_id = $4 AND
     713             :                              precision >= $5 LIMIT 1),
     714             :               src_amount AS (SELECT amount FROM account_has_asset
     715             :                               WHERE asset_id = $4 AND
     716             :                               account_id = $2 LIMIT 1),
     717             :               dest_amount AS (SELECT amount FROM account_has_asset
     718             :                                WHERE asset_id = $4 AND
     719             :                                account_id = $3 LIMIT 1),
     720             :               new_src_value AS (SELECT
     721             :                               (SELECT
     722             :                                   CASE WHEN EXISTS
     723             :                                       (SELECT amount FROM src_amount LIMIT 1)
     724             :                                       THEN
     725             :                                       (SELECT amount FROM src_amount LIMIT 1)
     726             :                                   ELSE 0::decimal
     727             :                               END) - $6::decimal AS value
     728             :                           ),
     729             :               new_dest_value AS (SELECT
     730             :                               (SELECT $6::decimal +
     731             :                                   CASE WHEN EXISTS
     732             :                                       (SELECT amount FROM dest_amount LIMIT 1)
     733             :                                           THEN
     734             :                                       (SELECT amount FROM dest_amount LIMIT 1)
     735             :                                   ELSE 0::decimal
     736             :                               END) AS value
     737             :                           ),
     738             :               insert_src AS
     739             :               (
     740             :                   INSERT INTO account_has_asset(account_id, asset_id, amount)
     741             :                   (
     742             :                       SELECT $2, $4, value
     743             :                       FROM new_src_value
     744             :                       WHERE EXISTS (SELECT * FROM has_src_account LIMIT 1) AND
     745             :                         EXISTS (SELECT * FROM has_dest_account LIMIT 1) AND
     746             :                         EXISTS (SELECT * FROM has_asset LIMIT 1) AND
     747             :                         EXISTS (SELECT value FROM new_src_value
     748             :                                 WHERE value >= 0 LIMIT 1) %s
     749             :                   )
     750             :                   ON CONFLICT (account_id, asset_id)
     751             :                   DO UPDATE SET amount = EXCLUDED.amount
     752             :                   RETURNING (1)
     753             :               ),
     754             :               insert_dest AS
     755             :               (
     756             :                   INSERT INTO account_has_asset(account_id, asset_id, amount)
     757             :                   (
     758             :                       SELECT $3, $4, value
     759             :                       FROM new_dest_value
     760             :                       WHERE EXISTS (SELECT * FROM insert_src) AND
     761             :                         EXISTS (SELECT * FROM has_src_account LIMIT 1) AND
     762             :                         EXISTS (SELECT * FROM has_dest_account LIMIT 1) AND
     763             :                         EXISTS (SELECT * FROM has_asset LIMIT 1) AND
     764             :                         EXISTS (SELECT value FROM new_dest_value
     765             :                                 WHERE value < 2::decimal ^ (256 - $5)
     766             :                                 LIMIT 1) %s
     767             :                   )
     768             :                   ON CONFLICT (account_id, asset_id)
     769             :                   DO UPDATE SET amount = EXCLUDED.amount
     770             :                   RETURNING (1)
     771             :                )
     772             :           SELECT CASE
     773             :               WHEN EXISTS (SELECT * FROM insert_dest LIMIT 1) THEN 0
     774             :               %s
     775             :               WHEN NOT EXISTS (SELECT * FROM has_dest_account LIMIT 1) THEN 4
     776             :               WHEN NOT EXISTS (SELECT * FROM has_src_account LIMIT 1) THEN 3
     777             :               WHEN NOT EXISTS (SELECT * FROM has_asset LIMIT 1) THEN 5
     778             :               WHEN NOT EXISTS (SELECT value FROM new_src_value
     779             :                                WHERE value >= 0 LIMIT 1) THEN 6
     780             :               WHEN NOT EXISTS (SELECT value FROM new_dest_value
     781             :                                WHERE value < 2::decimal ^ (256 - $5)
     782             :                                LIMIT 1) THEN 7
     783             :               ELSE 1
     784             :           END AS result)";
     785             : 
     786             :     std::string CommandError::toString() const {
     787           0 :       return (boost::format("%s: %d with extra info '%s'") % command_name
     788           0 :               % error_code % error_extra)
     789           0 :           .str();
     790           0 :     }
     791             : 
     792             :     PostgresCommandExecutor::PostgresCommandExecutor(
     793             :         soci::session &sql,
     794             :         std::shared_ptr<shared_model::interface::PermissionToString>
     795             :             perm_converter)
     796        1409 :         : sql_(sql),
     797        1409 :           do_validation_(true),
     798        1409 :           perm_converter_{std::move(perm_converter)} {}
     799             : 
     800             :     void PostgresCommandExecutor::setCreatorAccountId(
     801             :         const shared_model::interface::types::AccountIdType
     802             :             &creator_account_id) {
     803        2340 :       creator_account_id_ = creator_account_id;
     804        2340 :     }
     805             : 
     806             :     void PostgresCommandExecutor::doValidation(bool do_validation) {
     807        2340 :       do_validation_ = do_validation;
     808        2340 :     }
     809             : 
     810             :     CommandResult PostgresCommandExecutor::operator()(
     811             :         const shared_model::interface::AddAssetQuantity &command) {
     812         156 :       auto &account_id = creator_account_id_;
     813         156 :       auto &asset_id = command.assetId();
     814         156 :       auto amount = command.amount().toStringRepr();
     815         156 :       int precision = command.amount().precision();
     816             : 
     817             :       // 14.09.2018 nickaleks: IR-1707 move common logic to separate function
     818         156 :       auto cmd = boost::format("EXECUTE %1% ('%2%', '%3%', %4%, '%5%')");
     819             : 
     820         156 :       appendCommandName("addAssetQuantity", cmd, do_validation_);
     821             : 
     822         156 :       cmd = (cmd % account_id % asset_id % precision % amount);
     823             : 
     824             :       auto str_args = [&account_id, &asset_id, &amount, precision] {
     825           7 :         return getQueryArgsStringBuilder()
     826           7 :             .append("account_id", account_id)
     827           7 :             .append("asset_id", asset_id)
     828           7 :             .append("amount", amount)
     829           7 :             .append("precision", std::to_string(precision))
     830           7 :             .finalize();
     831           0 :       };
     832             : 
     833         156 :       return executeQuery(
     834         156 :           sql_, cmd.str(), "AddAssetQuantity", std::move(str_args));
     835         156 :     }
     836             : 
     837             :     CommandResult PostgresCommandExecutor::operator()(
     838             :         const shared_model::interface::AddPeer &command) {
     839         517 :       auto &peer = command.peer();
     840             : 
     841         517 :       auto cmd = boost::format("EXECUTE %1% ('%2%', '%3%', '%4%')");
     842             : 
     843         517 :       appendCommandName("addPeer", cmd, do_validation_);
     844             : 
     845         517 :       cmd = (cmd % creator_account_id_ % peer.pubkey().hex() % peer.address());
     846             : 
     847             :       auto str_args = [&peer] {
     848           1 :         return getQueryArgsStringBuilder()
     849           1 :             .append("peer", peer.toString())
     850           1 :             .finalize();
     851           0 :       };
     852             : 
     853         517 :       return executeQuery(sql_, cmd.str(), "AddPeer", std::move(str_args));
     854         517 :     }
     855             : 
     856             :     CommandResult PostgresCommandExecutor::operator()(
     857             :         const shared_model::interface::AddSignatory &command) {
     858         114 :       auto &account_id = command.accountId();
     859         114 :       auto pubkey = command.pubkey().hex();
     860         114 :       auto cmd = boost::format("EXECUTE %1% ('%2%', '%3%', '%4%')");
     861             : 
     862         114 :       appendCommandName("addSignatory", cmd, do_validation_);
     863             : 
     864         114 :       cmd = (cmd % creator_account_id_ % account_id % pubkey);
     865             : 
     866             :       auto str_args = [&account_id, &pubkey] {
     867           5 :         return getQueryArgsStringBuilder()
     868           5 :             .append("account_id", account_id)
     869           5 :             .append("pubkey", pubkey)
     870           5 :             .finalize();
     871           0 :       };
     872             : 
     873         114 :       return executeQuery(sql_, cmd.str(), "AddSignatory", std::move(str_args));
     874         114 :     }
     875             : 
     876             :     CommandResult PostgresCommandExecutor::operator()(
     877             :         const shared_model::interface::AppendRole &command) {
     878         946 :       auto &account_id = command.accountId();
     879         946 :       auto &role_name = command.roleName();
     880         946 :       auto cmd = boost::format("EXECUTE %1% ('%2%', '%3%', '%4%')");
     881             : 
     882         946 :       appendCommandName("appendRole", cmd, do_validation_);
     883             : 
     884         946 :       cmd = (cmd % creator_account_id_ % account_id % role_name);
     885             : 
     886             :       auto str_args = [&account_id, &role_name] {
     887           4 :         return getQueryArgsStringBuilder()
     888           4 :             .append("account_id", account_id)
     889           4 :             .append("role_name", role_name)
     890           4 :             .finalize();
     891           0 :       };
     892             : 
     893         946 :       return executeQuery(sql_, cmd.str(), "AppendRole", std::move(str_args));
     894         946 :     }
     895             : 
     896             :     CommandResult PostgresCommandExecutor::operator()(
     897             :         const shared_model::interface::CreateAccount &command) {
     898        1174 :       auto &account_name = command.accountName();
     899        1174 :       auto &domain_id = command.domainId();
     900        1174 :       auto &pubkey = command.pubkey().hex();
     901             :       shared_model::interface::types::AccountIdType account_id =
     902        1174 :           account_name + "@" + domain_id;
     903             : 
     904        1174 :       auto cmd = boost::format("EXECUTE %1% ('%2%', '%3%', '%4%', '%5%')");
     905             : 
     906        1174 :       appendCommandName("createAccount", cmd, do_validation_);
     907             : 
     908        1174 :       cmd = (cmd % creator_account_id_ % account_id % domain_id % pubkey);
     909             : 
     910             :       auto str_args = [&account_id, &domain_id, &pubkey] {
     911           7 :         return getQueryArgsStringBuilder()
     912           7 :             .append("account_id", account_id)
     913           7 :             .append("domain_id", domain_id)
     914           7 :             .append("pubkey", pubkey)
     915           7 :             .finalize();
     916           0 :       };
     917             : 
     918        1174 :       return executeQuery(
     919        1174 :           sql_, cmd.str(), "CreateAccount", std::move(str_args));
     920        1174 :     }
     921             : 
     922             :     CommandResult PostgresCommandExecutor::operator()(
     923             :         const shared_model::interface::CreateAsset &command) {
     924         601 :       auto &domain_id = command.domainId();
     925         601 :       auto asset_id = command.assetName() + "#" + domain_id;
     926         601 :       int precision = command.precision();
     927         601 :       auto cmd = boost::format("EXECUTE %1% ('%2%', '%3%', '%4%', %5%)");
     928             : 
     929         601 :       appendCommandName("createAsset", cmd, do_validation_);
     930             : 
     931         601 :       cmd = (cmd % creator_account_id_ % asset_id % domain_id % precision);
     932             : 
     933             :       auto str_args = [&domain_id, &asset_id, precision] {
     934           7 :         return getQueryArgsStringBuilder()
     935           7 :             .append("domain_id", domain_id)
     936           7 :             .append("asset_id", asset_id)
     937           7 :             .append("precision", std::to_string(precision))
     938           7 :             .finalize();
     939           0 :       };
     940             : 
     941         601 :       return executeQuery(sql_, cmd.str(), "CreateAsset", std::move(str_args));
     942         601 :     }
     943             : 
     944             :     CommandResult PostgresCommandExecutor::operator()(
     945             :         const shared_model::interface::CreateDomain &command) {
     946         782 :       auto &domain_id = command.domainId();
     947         782 :       auto &default_role = command.userDefaultRole();
     948         782 :       auto cmd = boost::format("EXECUTE %1% ('%2%', '%3%', '%4%')");
     949             : 
     950         782 :       appendCommandName("createDomain", cmd, do_validation_);
     951             : 
     952         782 :       cmd = (cmd % creator_account_id_ % domain_id % default_role);
     953             : 
     954             :       auto str_args = [&domain_id, &default_role] {
     955           7 :         return getQueryArgsStringBuilder()
     956           7 :             .append("domain_id", domain_id)
     957           7 :             .append("default_role", default_role)
     958           7 :             .finalize();
     959           0 :       };
     960             : 
     961         782 :       return executeQuery(sql_, cmd.str(), "CreateDomain", std::move(str_args));
     962         782 :     }
     963             : 
     964             :     CommandResult PostgresCommandExecutor::operator()(
     965             :         const shared_model::interface::CreateRole &command) {
     966        1618 :       auto &role_id = command.roleName();
     967        1618 :       auto &permissions = command.rolePermissions();
     968        1618 :       auto perm_str = permissions.toBitstring();
     969        1618 :       auto cmd = boost::format("EXECUTE %1% ('%2%', '%3%', '%4%')");
     970             : 
     971        1618 :       appendCommandName("createRole", cmd, do_validation_);
     972             : 
     973        1618 :       cmd = (cmd % creator_account_id_ % role_id % perm_str);
     974             : 
     975             :       auto str_args = [&role_id, &perm_str] {
     976             :         // TODO [IR-1889] Akvinikym 21.11.18: integrate
     977             :         // PermissionSet::toString() instead of bit string, when it is created
     978           4 :         return getQueryArgsStringBuilder()
     979           4 :             .append("role_id", role_id)
     980           4 :             .append("perm_str", perm_str)
     981           4 :             .finalize();
     982           0 :       };
     983             : 
     984        1618 :       return executeQuery(sql_, cmd.str(), "CreateRole", std::move(str_args));
     985        1618 :     }
     986             : 
     987             :     CommandResult PostgresCommandExecutor::operator()(
     988             :         const shared_model::interface::DetachRole &command) {
     989         829 :       auto &account_id = command.accountId();
     990         829 :       auto &role_name = command.roleName();
     991         829 :       auto cmd = boost::format("EXECUTE %1% ('%2%', '%3%', '%4%')");
     992             : 
     993         829 :       appendCommandName("detachRole", cmd, do_validation_);
     994             : 
     995         829 :       cmd = (cmd % creator_account_id_ % account_id % role_name);
     996             : 
     997             :       auto str_args = [&account_id, &role_name] {
     998           4 :         return getQueryArgsStringBuilder()
     999           4 :             .append("account_id", account_id)
    1000           4 :             .append("role_name", role_name)
    1001           4 :             .finalize();
    1002           0 :       };
    1003             : 
    1004         829 :       return executeQuery(sql_, cmd.str(), "DetachRole", std::move(str_args));
    1005         829 :     }
    1006             : 
    1007             :     CommandResult PostgresCommandExecutor::operator()(
    1008             :         const shared_model::interface::GrantPermission &command) {
    1009          37 :       auto &permittee_account_id = command.accountId();
    1010          37 :       auto permission = command.permissionName();
    1011          37 :       auto perm = shared_model::interface::RolePermissionSet(
    1012          37 :                       {shared_model::interface::permissions::permissionFor(
    1013          37 :                           command.permissionName())})
    1014          37 :                       .toBitstring();
    1015             :       const auto perm_str =
    1016          37 :           shared_model::interface::GrantablePermissionSet({permission})
    1017          37 :               .toBitstring();
    1018          37 :       auto cmd = boost::format("EXECUTE %1% ('%2%', '%3%', '%4%', '%5%')");
    1019             : 
    1020          37 :       appendCommandName("grantPermission", cmd, do_validation_);
    1021             : 
    1022          37 :       cmd =
    1023          37 :           (cmd % creator_account_id_ % permittee_account_id % perm_str % perm);
    1024             : 
    1025             :       auto str_args = [&creator_account_id = creator_account_id_,
    1026          37 :                        &permittee_account_id,
    1027          37 :                        permission = perm_converter_->toString(permission)] {
    1028           9 :         return getQueryArgsStringBuilder()
    1029           9 :             .append("creator_account_id_", creator_account_id)
    1030           9 :             .append("permittee_account_id", permittee_account_id)
    1031           9 :             .append("permission", permission)
    1032           9 :             .finalize();
    1033           0 :       };
    1034             : 
    1035          37 :       return executeQuery(
    1036          37 :           sql_, cmd.str(), "GrantPermission", std::move(str_args));
    1037          37 :     }
    1038             : 
    1039             :     CommandResult PostgresCommandExecutor::operator()(
    1040             :         const shared_model::interface::RemoveSignatory &command) {
    1041          19 :       auto &account_id = command.accountId();
    1042          19 :       auto &pubkey = command.pubkey().hex();
    1043          19 :       auto cmd = boost::format("EXECUTE %1% ('%2%', '%3%', '%4%')");
    1044             : 
    1045          19 :       appendCommandName("removeSignatory", cmd, do_validation_);
    1046             : 
    1047          19 :       cmd = (cmd % creator_account_id_ % account_id % pubkey);
    1048             : 
    1049             :       auto str_args = [&account_id, &pubkey] {
    1050           9 :         return getQueryArgsStringBuilder()
    1051           9 :             .append("account_id", account_id)
    1052           9 :             .append("pubkey", pubkey)
    1053           9 :             .finalize();
    1054           0 :       };
    1055             : 
    1056          19 :       return executeQuery(
    1057          19 :           sql_, cmd.str(), "RemoveSignatory", std::move(str_args));
    1058          19 :     }
    1059             : 
    1060             :     CommandResult PostgresCommandExecutor::operator()(
    1061             :         const shared_model::interface::RevokePermission &command) {
    1062          12 :       auto &permittee_account_id = command.accountId();
    1063          12 :       auto permission = command.permissionName();
    1064             :       const auto without_perm_str =
    1065          12 :           shared_model::interface::GrantablePermissionSet()
    1066          12 :               .set()
    1067          12 :               .unset(permission)
    1068          12 :               .toBitstring();
    1069          12 :       const auto perms = shared_model::interface::GrantablePermissionSet()
    1070          12 :                              .set(permission)
    1071          12 :                              .toBitstring();
    1072             : 
    1073          12 :       auto cmd = boost::format("EXECUTE %1% ('%2%', '%3%', '%4%', '%5%')");
    1074             : 
    1075          12 :       appendCommandName("revokePermission", cmd, do_validation_);
    1076             : 
    1077          12 :       cmd = (cmd % creator_account_id_ % permittee_account_id % perms
    1078          12 :              % without_perm_str);
    1079             : 
    1080             :       auto str_args = [&creator_account_id = creator_account_id_,
    1081          12 :                        &permittee_account_id,
    1082          12 :                        permission = perm_converter_->toString(permission)] {
    1083           4 :         return getQueryArgsStringBuilder()
    1084           4 :             .append("creator_account_id_", creator_account_id)
    1085           4 :             .append("permittee_account_id", permittee_account_id)
    1086           4 :             .append("permission", permission)
    1087           4 :             .finalize();
    1088           0 :       };
    1089             : 
    1090          12 :       return executeQuery(
    1091          12 :           sql_, cmd.str(), "RevokePermission", std::move(str_args));
    1092          12 :     }
    1093             : 
    1094             :     CommandResult PostgresCommandExecutor::operator()(
    1095             :         const shared_model::interface::SetAccountDetail &command) {
    1096          99 :       auto &account_id = command.accountId();
    1097          99 :       auto &key = command.key();
    1098          99 :       auto &value = command.value();
    1099          99 :       if (creator_account_id_.empty()) {
    1100             :         // When creator is not known, it is genesis block
    1101           0 :         creator_account_id_ = "genesis";
    1102           0 :       }
    1103          99 :       std::string json = "{" + creator_account_id_ + "}";
    1104          99 :       std::string empty_json = "{}";
    1105          99 :       std::string filled_json = "{" + creator_account_id_ + ", " + key + "}";
    1106          99 :       std::string val = "\"" + value + "\"";
    1107             : 
    1108          99 :       auto cmd = boost::format(
    1109             :           "EXECUTE %1% ('%2%', '%3%', '%4%', '%5%', '%6%', '%7%')");
    1110             : 
    1111          99 :       appendCommandName("setAccountDetail", cmd, do_validation_);
    1112             : 
    1113          99 :       cmd = (cmd % creator_account_id_ % account_id % json % filled_json % val
    1114          99 :              % empty_json);
    1115             : 
    1116             :       auto str_args = [&account_id, &key, &value] {
    1117           5 :         return getQueryArgsStringBuilder()
    1118           5 :             .append("account_id", account_id)
    1119           5 :             .append("key", key)
    1120           5 :             .append("value", value)
    1121           5 :             .finalize();
    1122           0 :       };
    1123             : 
    1124          99 :       return executeQuery(
    1125          99 :           sql_, cmd.str(), "SetAccountDetail", std::move(str_args));
    1126          99 :     }
    1127             : 
    1128             :     CommandResult PostgresCommandExecutor::operator()(
    1129             :         const shared_model::interface::SetQuorum &command) {
    1130          18 :       auto &account_id = command.accountId();
    1131          18 :       int quorum = command.newQuorum();
    1132          18 :       auto cmd = boost::format("EXECUTE %1% ('%2%', '%3%', %4%)");
    1133             : 
    1134          18 :       appendCommandName("setQuorum", cmd, do_validation_);
    1135             : 
    1136          18 :       cmd = (cmd % creator_account_id_ % account_id % quorum);
    1137             : 
    1138             :       auto str_args = [&account_id, quorum] {
    1139           4 :         return getQueryArgsStringBuilder()
    1140           4 :             .append("account_id", account_id)
    1141           4 :             .append("quorum", std::to_string(quorum))
    1142           4 :             .finalize();
    1143           0 :       };
    1144             : 
    1145          18 :       return executeQuery(sql_, cmd.str(), "SetQuorum", std::move(str_args));
    1146          18 :     }
    1147             : 
    1148             :     CommandResult PostgresCommandExecutor::operator()(
    1149             :         const shared_model::interface::SubtractAssetQuantity &command) {
    1150          11 :       auto &asset_id = command.assetId();
    1151          11 :       auto amount = command.amount().toStringRepr();
    1152          11 :       uint32_t precision = command.amount().precision();
    1153          11 :       auto cmd = boost::format("EXECUTE %1% ('%2%', '%3%', %4%, '%5%')");
    1154             : 
    1155          11 :       appendCommandName("subtractAssetQuantity", cmd, do_validation_);
    1156             : 
    1157          11 :       cmd = (cmd % creator_account_id_ % asset_id % precision % amount);
    1158             : 
    1159             :       auto str_args = [&creator_account_id = creator_account_id_,
    1160          11 :                        &asset_id,
    1161             :                        &amount,
    1162          11 :                        precision] {
    1163           8 :         return getQueryArgsStringBuilder()
    1164           8 :             .append("creator_account_id", creator_account_id)
    1165           8 :             .append("asset_id", asset_id)
    1166           8 :             .append("amount", amount)
    1167           8 :             .append("precision", std::to_string(precision))
    1168           8 :             .finalize();
    1169           0 :       };
    1170             : 
    1171          11 :       return executeQuery(
    1172          11 :           sql_, cmd.str(), "SubtractAssetQuantity", std::move(str_args));
    1173          11 :     }
    1174             : 
    1175             :     CommandResult PostgresCommandExecutor::operator()(
    1176             :         const shared_model::interface::TransferAsset &command) {
    1177         105 :       auto &src_account_id = command.srcAccountId();
    1178         105 :       auto &dest_account_id = command.destAccountId();
    1179         105 :       auto &asset_id = command.assetId();
    1180         105 :       auto amount = command.amount().toStringRepr();
    1181         105 :       uint32_t precision = command.amount().precision();
    1182             :       auto cmd =
    1183         105 :           boost::format("EXECUTE %1% ('%2%', '%3%', '%4%', '%5%', %6%, '%7%')");
    1184             : 
    1185         105 :       appendCommandName("transferAsset", cmd, do_validation_);
    1186             : 
    1187         105 :       cmd = (cmd % creator_account_id_ % src_account_id % dest_account_id
    1188         105 :              % asset_id % precision % amount);
    1189             : 
    1190             :       auto str_args =
    1191             :           [&src_account_id, &dest_account_id, &asset_id, &amount, precision] {
    1192          15 :             return getQueryArgsStringBuilder()
    1193          15 :                 .append("src_account_id", src_account_id)
    1194          15 :                 .append("dest_account_id", dest_account_id)
    1195          15 :                 .append("asset_id", asset_id)
    1196          15 :                 .append("amount", amount)
    1197          15 :                 .append("precision", std::to_string(precision))
    1198          15 :                 .finalize();
    1199           0 :           };
    1200             : 
    1201         105 :       return executeQuery(
    1202         105 :           sql_, cmd.str(), "TransferAsset", std::move(str_args));
    1203         105 :     }
    1204             : 
    1205             :     void PostgresCommandExecutor::prepareStatements(soci::session &sql) {
    1206        2982 :       std::vector<PreparedStatement> statements;
    1207             : 
    1208        8946 :       statements.push_back(
    1209        2982 :           {"addAssetQuantity",
    1210        2982 :            addAssetQuantityBase,
    1211        2982 :            {(boost::format(R"(has_perm AS (%s),)")
    1212        2982 :              % checkAccountDomainRoleOrGlobalRolePermission(
    1213             :                    shared_model::interface::permissions::Role::kAddAssetQty,
    1214             :                    shared_model::interface::permissions::Role::
    1215             :                        kAddDomainAssetQty,
    1216        2982 :                    "$1",
    1217        2982 :                    "$2"))
    1218        2982 :                 .str(),
    1219        2982 :             "AND (SELECT * from has_perm)",
    1220        2982 :             "WHEN NOT (SELECT * from has_perm) THEN 2"}});
    1221             : 
    1222        8946 :       statements.push_back(
    1223        2982 :           {"addPeer",
    1224        2982 :            addPeerBase,
    1225        2982 :            {(boost::format(R"(has_perm AS (%s),)")
    1226        2982 :              % checkAccountRolePermission(
    1227        2982 :                    shared_model::interface::permissions::Role::kAddPeer, "$1"))
    1228        2982 :                 .str(),
    1229        2982 :             "WHERE (SELECT * FROM has_perm)",
    1230        2982 :             "WHEN NOT (SELECT * from has_perm) THEN 2"}});
    1231             : 
    1232       11928 :       statements.push_back(
    1233        2982 :           {"addSignatory",
    1234        2982 :            addSignatoryBase,
    1235        2982 :            {(boost::format(R"(
    1236             :                 has_perm AS (%s),)")
    1237        2982 :              % checkAccountHasRoleOrGrantablePerm(
    1238             :                    shared_model::interface::permissions::Role::kAddSignatory,
    1239             :                    shared_model::interface::permissions::Grantable::
    1240             :                        kAddMySignatory,
    1241        2982 :                    "$1",
    1242        2982 :                    "$2"))
    1243        2982 :                 .str(),
    1244        2982 :             " WHERE (SELECT * FROM has_perm)",
    1245        2982 :             " AND (SELECT * FROM has_perm)",
    1246        2982 :             "WHEN NOT (SELECT * from has_perm) THEN 2"}});
    1247             : 
    1248        2982 :       const auto bits = shared_model::interface::RolePermissionSet::size();
    1249        2982 :       const auto grantable_bits =
    1250        2982 :           shared_model::interface::GrantablePermissionSet::size();
    1251             : 
    1252        8946 :       statements.push_back(
    1253        2982 :           {"appendRole",
    1254        2982 :            appendRoleBase,
    1255        2982 :            {(boost::format(R"(
    1256             :             has_perm AS (%1%),
    1257             :             role_permissions AS (
    1258             :                 SELECT permission FROM role_has_permissions
    1259             :                 WHERE role_id = $3
    1260             :             ),
    1261             :             account_roles AS (
    1262             :                 SELECT role_id FROM account_has_roles WHERE account_id = $1
    1263             :             ),
    1264             :             account_has_role_permissions AS (
    1265             :                 SELECT COALESCE(bit_or(rp.permission), '0'::bit(%2%)) &
    1266             :                     (SELECT * FROM role_permissions) =
    1267             :                     (SELECT * FROM role_permissions)
    1268             :                 FROM role_has_permissions AS rp
    1269             :                 JOIN account_has_roles AS ar on ar.role_id = rp.role_id
    1270             :                 WHERE ar.account_id = $1
    1271             :             ),)")
    1272        2982 :              % checkAccountRolePermission(
    1273             :                    shared_model::interface::permissions::Role::kAppendRole,
    1274        2982 :                    "$1")
    1275        2982 :              % bits)
    1276        2982 :                 .str(),
    1277        2982 :             R"( WHERE
    1278             :                     EXISTS (SELECT * FROM account_roles) AND
    1279             :                     (SELECT * FROM account_has_role_permissions)
    1280             :                     AND (SELECT * FROM has_perm))",
    1281        2982 :             R"(
    1282             :                 WHEN NOT EXISTS (SELECT * FROM account_roles) THEN 2
    1283             :                 WHEN NOT (SELECT * FROM account_has_role_permissions) THEN 2
    1284             :                 WHEN NOT (SELECT * FROM has_perm) THEN 2)"}});
    1285             : 
    1286        8946 :       statements.push_back(
    1287        2982 :           {"createAccount",
    1288        2982 :            createAccountBase,
    1289        2982 :            {(boost::format(R"(
    1290             :            domain_role_permissions_bits AS (
    1291             :                  SELECT COALESCE(bit_or(rhp.permission), '0'::bit(%1%)) AS bits
    1292             :                  FROM role_has_permissions AS rhp
    1293             :                  WHERE rhp.role_id = (SELECT * FROM get_domain_default_role)),
    1294             :            account_permissions AS (
    1295             :                  SELECT COALESCE(bit_or(rhp.permission), '0'::bit(%1%)) AS perm
    1296             :                  FROM role_has_permissions AS rhp
    1297             :                  JOIN account_has_roles AS ar ON ar.role_id = rhp.role_id
    1298             :                  WHERE ar.account_id = $1
    1299             :            ),
    1300             :            creator_has_enough_permissions AS (
    1301             :                 SELECT ap.perm & dpb.bits = dpb.bits
    1302             :                 FROM account_permissions AS ap, domain_role_permissions_bits AS dpb
    1303             :            ),
    1304             :            has_perm AS (%2%),
    1305        2982 :           )") % bits
    1306        2982 :              % checkAccountRolePermission(
    1307             :                    shared_model::interface::permissions::Role::kCreateAccount,
    1308        2982 :                    "$1"))
    1309        2982 :                 .str(),
    1310        2982 :             R"(AND (SELECT * FROM has_perm)
    1311             :                AND (SELECT * FROM creator_has_enough_permissions))",
    1312        2982 :             R"(WHEN NOT (SELECT * FROM has_perm) THEN 2
    1313             :                WHEN NOT (SELECT * FROM creator_has_enough_permissions) THEN 2)"}});
    1314             : 
    1315        8946 :       statements.push_back(
    1316        2982 :           {"createAsset",
    1317        2982 :            createAssetBase,
    1318        2982 :            {(boost::format(R"(
    1319             :               has_perm AS (%s),)")
    1320        2982 :              % checkAccountRolePermission(
    1321             :                    shared_model::interface::permissions::Role::kCreateAsset,
    1322        2982 :                    "$1"))
    1323        2982 :                 .str(),
    1324        2982 :             R"(WHERE (SELECT * FROM has_perm))",
    1325        2982 :             R"(WHEN NOT (SELECT * FROM has_perm) THEN 2)"}});
    1326             : 
    1327        8946 :       statements.push_back(
    1328        2982 :           {"createDomain",
    1329        2982 :            createDomainBase,
    1330        2982 :            {(boost::format(R"(
    1331             :               has_perm AS (%s),)")
    1332        2982 :              % checkAccountRolePermission(
    1333             :                    shared_model::interface::permissions::Role::kCreateDomain,
    1334        2982 :                    "$1"))
    1335        2982 :                 .str(),
    1336        2982 :             R"(WHERE (SELECT * FROM has_perm))",
    1337        2982 :             R"(WHEN NOT (SELECT * FROM has_perm) THEN 2)"}});
    1338             : 
    1339        8946 :       statements.push_back(
    1340        2982 :           {"createRole",
    1341        2982 :            createRoleBase,
    1342        2982 :            {(boost::format(R"(
    1343             :           account_has_role_permissions AS (
    1344             :                 SELECT COALESCE(bit_or(rp.permission), '0'::bit(%s)) &
    1345             :                     $3 = $3
    1346             :                 FROM role_has_permissions AS rp
    1347             :                 JOIN account_has_roles AS ar on ar.role_id = rp.role_id
    1348             :                 WHERE ar.account_id = $1),
    1349             :           has_perm AS (%s),)")
    1350        2982 :              % bits
    1351        2982 :              % checkAccountRolePermission(
    1352             :                    shared_model::interface::permissions::Role::kCreateRole,
    1353        2982 :                    "$1"))
    1354        2982 :                 .str(),
    1355        2982 :             R"(WHERE (SELECT * FROM account_has_role_permissions)
    1356             :                           AND (SELECT * FROM has_perm))",
    1357        2982 :             R"(WHEN NOT (SELECT * FROM
    1358             :                                account_has_role_permissions) THEN 2
    1359             :                         WHEN NOT (SELECT * FROM has_perm) THEN 2)"}});
    1360             : 
    1361        8946 :       statements.push_back(
    1362        2982 :           {"detachRole",
    1363        2982 :            detachRoleBase,
    1364        2982 :            {(boost::format(R"(
    1365             :             has_perm AS (%s),)")
    1366        2982 :              % checkAccountRolePermission(
    1367             :                    shared_model::interface::permissions::Role::kDetachRole,
    1368        2982 :                    "$1"))
    1369        2982 :                 .str(),
    1370        2982 :             R"(AND (SELECT * FROM has_perm))",
    1371        2982 :             R"(WHEN NOT (SELECT * FROM has_perm) THEN 2)"}});
    1372             : 
    1373        8946 :       statements.push_back({"grantPermission",
    1374        2982 :                             grantPermissionBase,
    1375        2982 :                             {(boost::format(R"(
    1376             :             has_perm AS (SELECT COALESCE(bit_or(rp.permission), '0'::bit(%1%))
    1377             :           & $4 = $4 FROM role_has_permissions AS rp
    1378             :               JOIN account_has_roles AS ar on ar.role_id = rp.role_id
    1379             :               WHERE ar.account_id = $1),)")
    1380        2982 :                               % bits)
    1381        2982 :                                  .str(),
    1382        2982 :                              R"( WHERE (SELECT * FROM has_perm))",
    1383        2982 :                              R"(WHEN NOT (SELECT * FROM has_perm) THEN 2)"}});
    1384             : 
    1385        8946 :       statements.push_back(
    1386        2982 :           {"removeSignatory",
    1387        2982 :            removeSignatoryBase,
    1388        2982 :            {(boost::format(R"(
    1389             :           has_perm AS (%s),
    1390             :           get_account AS (
    1391             :               SELECT quorum FROM account WHERE account_id = $2 LIMIT 1
    1392             :            ),
    1393             :           get_signatories AS (
    1394             :               SELECT public_key FROM account_has_signatory
    1395             :               WHERE account_id = $2
    1396             :           ),
    1397             :           get_signatory AS (
    1398             :               SELECT * FROM get_signatories
    1399             :               WHERE public_key = $3
    1400             :           ),
    1401             :           check_account_signatories AS (
    1402             :               SELECT quorum FROM get_account
    1403             :               WHERE quorum < (SELECT COUNT(*) FROM get_signatories)
    1404             :           ),
    1405             :           )")
    1406        2982 :              % checkAccountHasRoleOrGrantablePerm(
    1407             :                    shared_model::interface::permissions::Role::kRemoveSignatory,
    1408             :                    shared_model::interface::permissions::Grantable::
    1409             :                        kRemoveMySignatory,
    1410        2982 :                    "$1",
    1411        2982 :                    "$2"))
    1412        2982 :                 .str(),
    1413        2982 :             R"(
    1414             :               AND (SELECT * FROM has_perm)
    1415             :               AND EXISTS (SELECT * FROM get_account)
    1416             :               AND EXISTS (SELECT * FROM get_signatories)
    1417             :               AND EXISTS (SELECT * FROM check_account_signatories)
    1418             :           )",
    1419        2982 :             R"(
    1420             :               WHEN NOT EXISTS (SELECT * FROM get_account) THEN 3
    1421             :               WHEN NOT (SELECT * FROM has_perm) THEN 2
    1422             :               WHEN NOT EXISTS (SELECT * FROM get_signatory) THEN 4
    1423             :               WHEN NOT EXISTS (SELECT * FROM check_account_signatories) THEN 5
    1424             :           )"}});
    1425             : 
    1426        8946 :       statements.push_back({"revokePermission",
    1427        2982 :                             revokePermissionBase,
    1428        2982 :                             {(boost::format(R"(
    1429             :             has_perm AS (SELECT COALESCE(bit_or(permission), '0'::bit(%1%))
    1430             :           & $3 = $3 FROM account_has_grantable_permissions
    1431             :               WHERE account_id = $1 AND
    1432             :               permittee_account_id = $2),)")
    1433        2982 :                               % grantable_bits)
    1434        2982 :                                  .str(),
    1435        2982 :                              R"( AND (SELECT * FROM has_perm))",
    1436        2982 :                              R"( WHEN NOT (SELECT * FROM has_perm) THEN 2 )"}});
    1437             : 
    1438        8946 :       statements.push_back(
    1439        2982 :           {"setAccountDetail",
    1440        2982 :            setAccountDetailBase,
    1441        2982 :            {(boost::format(R"(
    1442             :               has_role_perm AS (%s),
    1443             :               has_grantable_perm AS (%s),
    1444             :               has_perm AS (SELECT CASE
    1445             :                                WHEN (SELECT * FROM has_grantable_perm) THEN true
    1446             :                                WHEN ($1 = $2) THEN true
    1447             :                                WHEN (SELECT * FROM has_role_perm) THEN true
    1448             :                                ELSE false END
    1449             :               ),
    1450             :               )")
    1451        2982 :              % checkAccountRolePermission(
    1452        2982 :                    shared_model::interface::permissions::Role::kSetDetail, "$1")
    1453        2982 :              % checkAccountGrantablePermission(
    1454             :                    shared_model::interface::permissions::Grantable::
    1455             :                        kSetMyAccountDetail,
    1456        2982 :                    "$1",
    1457        2982 :                    "$2"))
    1458        2982 :                 .str(),
    1459        2982 :             R"( AND (SELECT * FROM has_perm))",
    1460        2982 :             R"( WHEN NOT (SELECT * FROM has_perm) THEN 2 )"}});
    1461             : 
    1462       11928 :       statements.push_back(
    1463        2982 :           {"setQuorum",
    1464        2982 :            setQuorumBase,
    1465        2982 :            {R"( get_signatories AS (
    1466             :                     SELECT public_key FROM account_has_signatory
    1467             :                     WHERE account_id = $2
    1468             :                 ),
    1469             :                 check_account_signatories AS (
    1470             :                     SELECT 1 FROM account
    1471             :                     WHERE $3 <= (SELECT COUNT(*) FROM get_signatories)
    1472             :                     AND account_id = $2
    1473             :                 ),)",
    1474        2982 :             (boost::format(R"(
    1475             :           has_perm AS (%s),)")
    1476        2982 :              % checkAccountHasRoleOrGrantablePerm(
    1477             :                    shared_model::interface::permissions::Role::kSetQuorum,
    1478             :                    shared_model::interface::permissions::Grantable::
    1479             :                        kSetMyQuorum,
    1480        2982 :                    "$1",
    1481        2982 :                    "$2"))
    1482        2982 :                 .str(),
    1483        2982 :             R"(AND EXISTS
    1484             :               (SELECT * FROM get_signatories)
    1485             :               AND EXISTS (SELECT * FROM check_account_signatories)
    1486             :               AND (SELECT * FROM has_perm))",
    1487        2982 :             R"(
    1488             :               WHEN NOT (SELECT * FROM has_perm) THEN 2
    1489             :               WHEN NOT EXISTS (SELECT * FROM get_signatories) THEN 4
    1490             :               WHEN NOT EXISTS (SELECT * FROM check_account_signatories) THEN 5
    1491             :               )"}});
    1492             : 
    1493        8946 :       statements.push_back({"subtractAssetQuantity",
    1494        2982 :                             subtractAssetQuantityBase,
    1495        2982 :                             {(boost::format(R"(
    1496             :                has_perm AS (%s),)")
    1497        2982 :                               % checkAccountDomainRoleOrGlobalRolePermission(
    1498             :                                     shared_model::interface::permissions::Role::
    1499             :                                         kSubtractAssetQty,
    1500             :                                     shared_model::interface::permissions::Role::
    1501             :                                         kSubtractDomainAssetQty,
    1502        2982 :                                     "$1",
    1503        2982 :                                     "$2"))
    1504        2982 :                                  .str(),
    1505        2982 :                              R"( AND (SELECT * FROM has_perm))",
    1506        2982 :                              R"( WHEN NOT (SELECT * FROM has_perm) THEN 2 )"}});
    1507             : 
    1508       11928 :       statements.push_back(
    1509        2982 :           {"transferAsset",
    1510        2982 :            transferAssetBase,
    1511        2982 :            {(boost::format(R"(
    1512             :               has_role_perm AS (%s),
    1513             :               has_grantable_perm AS (%s),
    1514             :               dest_can_receive AS (%s),
    1515             :               has_perm AS (SELECT
    1516             :                                CASE WHEN (SELECT * FROM dest_can_receive) THEN
    1517             :                                    CASE WHEN NOT ($1 = $2) THEN
    1518             :                                        CASE WHEN (SELECT * FROM has_grantable_perm)
    1519             :                                            THEN true
    1520             :                                        ELSE false END
    1521             :                                    ELSE
    1522             :                                         CASE WHEN (SELECT * FROM has_role_perm)
    1523             :                                             THEN true
    1524             :                                         ELSE false END
    1525             :                                    END
    1526             :                                ELSE false END
    1527             :               ),
    1528             :               )")
    1529        2982 :              % checkAccountRolePermission(
    1530        2982 :                    shared_model::interface::permissions::Role::kTransfer, "$1")
    1531        2982 :              % checkAccountGrantablePermission(
    1532             :                    shared_model::interface::permissions::Grantable::
    1533             :                        kTransferMyAssets,
    1534        2982 :                    "$1",
    1535        2982 :                    "$2")
    1536        2982 :              % checkAccountRolePermission(
    1537        2982 :                    shared_model::interface::permissions::Role::kReceive, "$3"))
    1538        2982 :                 .str(),
    1539        2982 :             R"( AND (SELECT * FROM has_perm))",
    1540        2982 :             R"( AND (SELECT * FROM has_perm))",
    1541        2982 :             R"( WHEN NOT (SELECT * FROM has_perm) THEN 2 )"}});
    1542             : 
    1543       50694 :       for (const auto &st : statements) {
    1544       47712 :         prepareStatement(sql, st);
    1545             :       }
    1546        2982 :     };
    1547             : 
    1548             :   }  // namespace ametsuchi
    1549             : }  // namespace iroha

Generated by: LCOV version 1.13