This repository has been archived by the owner on Apr 17, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 297
GetAccountTransactions Pagination #1903
Merged
nickaleks
merged 19 commits into
trunk/tx-queries-pagination
from
feature/get_transactions_pagination
Dec 6, 2018
Merged
Changes from 17 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
2af0860
add comments to query
nickaleks daf31c7
add position in a block to hash index
nickaleks 4153473
add basic pagination to getaccounttxs
nickaleks 42540a8
unpaginated query
nickaleks 1bb1f45
add parametrization to query
nickaleks f070a99
add additional pagination tests
nickaleks 9cf0f6f
fix tests
nickaleks e78d1aa
add next_tx_hash
nickaleks 24dce38
add total transactions
nickaleks ea96e1f
add several tests
nickaleks c7b4bec
refactor and add test for empty case
nickaleks 7bc61c7
remove redundat tests
nickaleks 3b29fb7
Merge branch 'trunk/tx-queries-pagination' of https://github.com/hype…
nickaleks 486ff6e
fix missing include in query test
nickaleks e0d1dda
refactor tests
nickaleks 81a1fd4
fix gcc compilation
nickaleks ee26504
simplify sql query
nickaleks 1f17fdd
fix review issues
nickaleks e8c3811
Merge branch 'trunk/tx-queries-pagination' of https://github.com/hype…
nickaleks File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,7 @@ | |
#include "interfaces/queries/get_signatories.hpp" | ||
#include "interfaces/queries/get_transactions.hpp" | ||
#include "interfaces/queries/query.hpp" | ||
#include "interfaces/queries/tx_pagination_meta.hpp" | ||
|
||
using namespace shared_model::interface::permissions; | ||
|
||
|
@@ -449,41 +450,100 @@ namespace iroha { | |
|
||
QueryExecutorResult PostgresQueryExecutorVisitor::operator()( | ||
const shared_model::interface::GetAccountTransactions &q) { | ||
using QueryTuple = | ||
QueryType<shared_model::interface::types::HeightType, uint64_t>; | ||
using QueryTuple = QueryType<shared_model::interface::types::HeightType, | ||
uint64_t, | ||
uint64_t>; | ||
using PermissionTuple = boost::tuple<int>; | ||
|
||
auto cmd = (boost::format(R"(WITH has_perms AS (%s), | ||
auto &pagination_info = q.paginationMeta(); | ||
auto first_hash = pagination_info.firstTxHash(); | ||
// retrieve one extra transaction to populate next_hash | ||
auto query_size = pagination_info.pageSize() + 1u; | ||
|
||
auto base = boost::format(R"(WITH has_perms AS (%s), | ||
first_hash AS (%s), | ||
previous_txes AS ( | ||
SELECT position_by_hash.height, position_by_hash.index | ||
FROM position_by_hash JOIN first_hash | ||
ON position_by_hash.height > first_hash.height | ||
OR (position_by_hash.height = first_hash.height AND | ||
position_by_hash.index >= first_hash.index) | ||
), | ||
my_txs AS ( | ||
SELECT DISTINCT height, index | ||
FROM index_by_creator_height | ||
WHERE creator_id = :account_id | ||
ORDER BY height, index ASC | ||
), | ||
total_size AS ( | ||
SELECT COUNT(*) FROM my_txs | ||
), | ||
t AS ( | ||
SELECT DISTINCT has.height, index | ||
FROM height_by_account_set AS has | ||
JOIN index_by_creator_height AS ich ON has.height = ich.height | ||
AND has.account_id = ich.creator_id | ||
WHERE account_id = :account_id | ||
ORDER BY has.height, index ASC | ||
SELECT my_txs.height, my_txs.index | ||
FROM my_txs | ||
JOIN previous_txes ON my_txs.height = previous_txes.height | ||
AND my_txs.index = previous_txes.index | ||
LIMIT :page_size | ||
) | ||
SELECT height, index, perm FROM t | ||
SELECT height, index, count, perm FROM t | ||
RIGHT OUTER JOIN has_perms ON TRUE | ||
)") | ||
% hasQueryPermission(creator_id_, | ||
q.accountId(), | ||
Role::kGetMyAccTxs, | ||
Role::kGetAllAccTxs, | ||
Role::kGetDomainAccTxs)) | ||
.str(); | ||
JOIN total_size ON TRUE | ||
)"); | ||
|
||
// select tx with specified hash | ||
auto first_by_hash = R"(SELECT height, index FROM position_by_hash | ||
WHERE hash = :hash LIMIT 1)"; | ||
|
||
// select first ever tx | ||
auto first_tx = R"(SELECT height, index FROM position_by_hash | ||
ORDER BY height, index ASC LIMIT 1)"; | ||
|
||
auto cmd = boost::format(base | ||
% hasQueryPermission(creator_id_, | ||
q.accountId(), | ||
Role::kGetMyAccTxs, | ||
Role::kGetAllAccTxs, | ||
Role::kGetDomainAccTxs)); | ||
if (first_hash) { | ||
cmd = base % first_by_hash; | ||
} else { | ||
cmd = base % first_tx; | ||
} | ||
|
||
auto query = cmd.str(); | ||
|
||
return executeQuery<QueryTuple, PermissionTuple>( | ||
[&] { return (sql_.prepare << cmd, soci::use(q.accountId())); }, | ||
[&] { | ||
if (first_hash) { | ||
return (sql_.prepare << query, | ||
soci::use(first_hash->hex()), | ||
soci::use(q.accountId()), | ||
soci::use(query_size)); | ||
} else { | ||
return (sql_.prepare << query, | ||
soci::use(q.accountId()), | ||
soci::use(query_size)); | ||
} | ||
}, | ||
[&](auto range, auto &) { | ||
uint64_t total_size = 0; | ||
if (not boost::empty(range)) { | ||
total_size = boost::get<2>(*range.begin()); | ||
} | ||
std::map<uint64_t, std::vector<uint64_t>> index; | ||
// unpack results to get map from block height to index of tx in | ||
// a block | ||
boost::for_each(range, [&index](auto t) { | ||
apply(t, [&index](auto &height, auto &idx) { | ||
index[height].push_back(idx); | ||
}); | ||
apply( | ||
t, | ||
[&index](auto &height, auto &idx, auto &count) { | ||
index[height].push_back(idx); | ||
}); | ||
}); | ||
|
||
std::vector<std::unique_ptr<shared_model::interface::Transaction>> | ||
response_txs; | ||
// get transactions corresponding to indexes | ||
for (auto &block : index) { | ||
auto txs = this->getTransactionsFromBlock( | ||
block.first, | ||
|
@@ -492,9 +552,27 @@ namespace iroha { | |
std::move( | ||
txs.begin(), txs.end(), std::back_inserter(response_txs)); | ||
} | ||
// If 0 transactions are returned, we assume that hash is invalid. | ||
// Since query with valid hash is guaranteed to return at least one | ||
// transaction | ||
if (first_hash and response_txs.empty()) { | ||
auto error = (boost::format("invalid pagination hash: %s") | ||
% first_hash->hex()) | ||
.str(); | ||
return this->logAndReturnErrorResponse( | ||
QueryErrorType::kStatefulFailed, error); | ||
} | ||
|
||
return query_response_factory_->createTransactionsResponse( | ||
std::move(response_txs), query_hash_); | ||
// next transaction exists | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not very clear comment, please expand it a little |
||
if (response_txs.size() == query_size) { | ||
auto next_hash = response_txs.back()->hash(); | ||
response_txs.pop_back(); | ||
return query_response_factory_->createTransactionsPageResponse( | ||
std::move(response_txs), next_hash, total_size, query_hash_); | ||
} | ||
|
||
return query_response_factory_->createTransactionsPageResponse( | ||
std::move(response_txs), total_size, query_hash_); | ||
}, | ||
notEnoughPermissionsResponse(perm_converter_, | ||
Role::kGetMyAccTxs, | ||
|
@@ -518,7 +596,7 @@ namespace iroha { | |
auto cmd = (boost::format(R"(WITH has_my_perm AS (%s), | ||
has_all_perm AS (%s), | ||
t AS ( | ||
SELECT height, hash FROM height_by_hash WHERE hash IN (%s) | ||
SELECT height, hash FROM position_by_hash WHERE hash IN (%s) | ||
) | ||
SELECT height, hash, has_my_perm.perm, has_all_perm.perm FROM t | ||
RIGHT OUTER JOIN has_my_perm ON TRUE | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -534,7 +534,7 @@ DELETE FROM domain; | |
DELETE FROM signatory; | ||
DELETE FROM peer; | ||
DELETE FROM role; | ||
DELETE FROM height_by_hash; | ||
DELETE FROM position_by_hash; | ||
DELETE FROM height_by_account_set; | ||
DELETE FROM index_by_creator_height; | ||
DELETE FROM index_by_id_height_asset; | ||
|
@@ -606,9 +606,10 @@ CREATE TABLE IF NOT EXISTS account_has_grantable_permissions ( | |
+ R"() NOT NULL, | ||
PRIMARY KEY (permittee_account_id, account_id) | ||
); | ||
CREATE TABLE IF NOT EXISTS height_by_hash ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We still have that table in ametsuchi fixture. Replace it there as well |
||
CREATE TABLE IF NOT EXISTS position_by_hash ( | ||
hash varchar, | ||
height text | ||
height text, | ||
index text | ||
); | ||
CREATE TABLE IF NOT EXISTS height_by_account_set ( | ||
account_id text, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If
paginationMeta()
returns constref, it should beconst auto &pagination_info