Skip to content

Commit 0a3f969

Browse files
pagination integration test
1 parent f648733 commit 0a3f969

File tree

6 files changed

+324
-0
lines changed

6 files changed

+324
-0
lines changed

tests/integration/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
add_subdirectory(basic_example_it)
2+
add_subdirectory(pagination_it)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
add_ydb_test(NAME pagination_it
2+
SOURCES
3+
main.cpp
4+
pagination_data.cpp
5+
pagination.cpp
6+
pagination.h
7+
LINK_LIBRARIES
8+
yutil
9+
library-getopt
10+
YDB-CPP-SDK::Table
11+
GTest::gtest_main
12+
public-lib-json_value
13+
LABELS
14+
integration
15+
)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include "pagination.h"
2+
3+
#include <src/library/getopt/last_getopt.h>
4+
#include <ydb-cpp-sdk/json_value/ydb_json_value.h>
5+
6+
#include <gtest/gtest.h>
7+
8+
using namespace NLastGetopt;
9+
using namespace NYdb;
10+
11+
const uint32_t MaxPages = 10;
12+
13+
void StopHandler(int) {
14+
exit(1);
15+
}
16+
17+
RunArgs getRunArgs() {
18+
TOpts opts = TOpts::Default();
19+
20+
std::string database = std::getenv( "DATABASE" );
21+
std::string endpoint = std::getenv( "ENDPOINT" );
22+
std::string path;
23+
24+
if (path.empty()) {
25+
path = database;
26+
}
27+
28+
auto driverConfig = TDriverConfig()
29+
.SetEndpoint(endpoint)
30+
.SetDatabase(database)
31+
.SetAuthToken(std::getenv("YDB_TOKEN") ? std::getenv("YDB_TOKEN") : "");
32+
TDriver driver(driverConfig);
33+
34+
return {driver, path};
35+
}
36+
37+
TEST(Integration, Pagination) {
38+
39+
std::vector<std::string> mapPagToJson = {
40+
"{\"address\":\"Кирова, 6\",\"city\":\"Кирс\",\"number\":3}\n{\"address\":\"Урицкого, 21\",\"city\":\"Котельнич\",\"number\":1}\n{\"address\":\"Октябрьская, 109\",\"city\":\"Котельнич\",\"number\":2}\n",
41+
"{\"address\":\"Советская, 153\",\"city\":\"Котельнич\",\"number\":3}\n{\"address\":\"Школьная, 2\",\"city\":\"Котельнич\",\"number\":5}\n{\"address\":\"Октябрьская, 91\",\"city\":\"Котельнич\",\"number\":15}\n",
42+
"{\"address\":\"Коммуны, 4\",\"city\":\"Нолинск\",\"number\":1}\n{\"address\":\"Федосеева, 2Б\",\"city\":\"Нолинск\",\"number\":2}\n{\"address\":\"Ст.Халтурина, 2\",\"city\":\"Орлов\",\"number\":1}\n",
43+
"{\"address\":\"Свободы, 4\",\"city\":\"Орлов\",\"number\":2}\n{\"address\":\"Гоголя, 25\",\"city\":\"Яранск\",\"number\":1}\n{\"address\":\"Кирова, 18\",\"city\":\"Яранск\",\"number\":2}\n",
44+
"{\"address\":\"Некрасова, 59\",\"city\":\"Яранск\",\"number\":3}\n",
45+
""
46+
};
47+
48+
auto [driver, path] = getRunArgs();
49+
50+
TTableClient client(driver);
51+
52+
try {
53+
CreateTable(client, path);
54+
55+
ThrowOnError(client.RetryOperationSync([path](TSession session) {
56+
return FillTableDataTransaction(session, path);
57+
}));
58+
59+
uint64_t limit = 3;
60+
std::string lastCity;
61+
uint32_t lastNumber = 0;
62+
uint32_t page = 0;
63+
std::string currentResultSet = " ";
64+
65+
std::cout << "> Pagination, Limit=" << limit << std::endl;
66+
67+
// show first MaxPages=10 pages:
68+
while (!currentResultSet.empty() && page <= MaxPages) {
69+
++page;
70+
std::cout << "> Page " << page << ":" << std::endl;
71+
currentResultSet = SelectPaging(client, path, limit, lastCity, lastNumber);
72+
ASSERT_EQ(mapPagToJson[page - 1], currentResultSet);
73+
}
74+
}
75+
catch (const TYdbErrorException& e) {
76+
std::cerr << "Execution failed due to fatal error:" << std::endl;
77+
PrintStatus(e.Status);
78+
FAIL();
79+
}
80+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
#include "pagination.h"
2+
3+
#include <ydb-cpp-sdk/util/string/cast.h>
4+
#include <ydb-cpp-sdk/json_value/ydb_json_value.h>
5+
6+
#include <filesystem>
7+
8+
void ThrowOnError(const TStatus& status) {
9+
if (!status.IsSuccess()) {
10+
throw TYdbErrorException(status) << status;
11+
}
12+
}
13+
14+
void PrintStatus(const TStatus& status) {
15+
std::cerr << "Status: " << ToString(status.GetStatus()) << std::endl;
16+
status.GetIssues().PrintTo(std::cerr);
17+
}
18+
19+
static std::string JoinPath(const std::string& basePath, const std::string& path) {
20+
if (basePath.empty()) {
21+
return path;
22+
}
23+
24+
std::filesystem::path prefixPathSplit(basePath);
25+
prefixPathSplit /= path;
26+
27+
return prefixPathSplit;
28+
}
29+
30+
//! Creates sample table with CrateTable API.
31+
void CreateTable(TTableClient client, const std::string& path) {
32+
ThrowOnError(client.RetryOperationSync([path](TSession session) {
33+
auto schoolsDesc = TTableBuilder()
34+
.AddNullableColumn("city", EPrimitiveType::Utf8)
35+
.AddNullableColumn("number", EPrimitiveType::Uint32)
36+
.AddNullableColumn("address", EPrimitiveType::Utf8)
37+
.SetPrimaryKeyColumns({ "city", "number" })
38+
.Build();
39+
40+
return session.CreateTable(JoinPath(path, "schools"), std::move(schoolsDesc)).GetValueSync();
41+
}));
42+
}
43+
44+
//! Fills sample tables with data in single parameterized data query.
45+
TStatus FillTableDataTransaction(TSession& session, const std::string& path) {
46+
auto query = std::format(R"(
47+
--!syntax_v1
48+
PRAGMA TablePathPrefix("{}");
49+
50+
DECLARE $schoolsData AS List<Struct<
51+
city: Utf8,
52+
number: Uint32,
53+
address: Utf8>>;
54+
55+
REPLACE INTO schools
56+
SELECT
57+
city,
58+
number,
59+
address
60+
FROM AS_TABLE($schoolsData);
61+
)", path);
62+
63+
auto params = GetTablesDataParams();
64+
65+
return session.ExecuteDataQuery(
66+
query,
67+
TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx(),
68+
std::move(params)).GetValueSync();
69+
}
70+
71+
//! Shows usage of query paging.
72+
static TStatus SelectPagingTransaction(TSession& session, const std::string& path,
73+
uint64_t pageLimit, const std::string& lastCity, uint32_t lastNumber, std::optional<TResultSet>& resultSet)
74+
{
75+
auto query = std::format(R"(
76+
--!syntax_v1
77+
PRAGMA TablePathPrefix("{}");
78+
79+
DECLARE $limit AS Uint64;
80+
DECLARE $lastCity AS Utf8;
81+
DECLARE $lastNumber AS Uint32;
82+
83+
$part1 = (
84+
SELECT * FROM schools
85+
WHERE city = $lastCity AND number > $lastNumber
86+
ORDER BY city, number LIMIT $limit
87+
);
88+
89+
$part2 = (
90+
SELECT * FROM schools
91+
WHERE city > $lastCity
92+
ORDER BY city, number LIMIT $limit
93+
);
94+
95+
$union = (
96+
SELECT * FROM $part1
97+
UNION ALL
98+
SELECT * FROM $part2
99+
);
100+
101+
SELECT * FROM $union
102+
ORDER BY city, number LIMIT $limit;
103+
)", path);
104+
105+
auto params = session.GetParamsBuilder()
106+
.AddParam("$limit")
107+
.Uint64(pageLimit)
108+
.Build()
109+
.AddParam("$lastCity")
110+
.Utf8(lastCity)
111+
.Build()
112+
.AddParam("$lastNumber")
113+
.Uint32(lastNumber)
114+
.Build()
115+
.Build();
116+
117+
auto result = session.ExecuteDataQuery(
118+
query,
119+
TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx(),
120+
std::move(params)).GetValueSync();
121+
122+
if (result.IsSuccess()) {
123+
resultSet = result.GetResultSet(0);
124+
}
125+
126+
return result;
127+
}
128+
129+
std::string SelectPaging(TTableClient client, const std::string& path, uint64_t pageLimit, std::string& lastCity, uint32_t& lastNumber) {
130+
std::optional<TResultSet> resultSet;
131+
ThrowOnError(client.RetryOperationSync([path, pageLimit, &lastCity, lastNumber, &resultSet](TSession session) {
132+
return SelectPagingTransaction(session, path, pageLimit, lastCity, lastNumber, resultSet);
133+
}));
134+
135+
TResultSetParser parser(*resultSet);
136+
137+
if (!parser.TryNextRow()) {
138+
return "";
139+
}
140+
do {
141+
lastCity = parser.ColumnParser("city").GetOptionalUtf8().value();
142+
lastNumber = parser.ColumnParser("number").GetOptionalUint32().value();
143+
std::cout << lastCity << ", Школа №" << lastNumber << ", Адрес: " << ToString(parser.ColumnParser("address").GetOptionalUtf8()) << std::endl;
144+
} while (parser.TryNextRow());
145+
return FormatResultSetJson(resultSet.value(), EBinaryStringEncoding::Unicode);;
146+
}
147+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#pragma once
2+
3+
#include <ydb-cpp-sdk/client/driver/driver.h>
4+
#include <ydb-cpp-sdk/client/table/table.h>
5+
6+
NYdb::TParams GetTablesDataParams();
7+
8+
using namespace NYdb;
9+
using namespace NYdb::NTable;
10+
11+
struct RunArgs {
12+
TDriver driver;
13+
std::string path;
14+
};
15+
16+
class TYdbErrorException : public yexception {
17+
public:
18+
TYdbErrorException(const TStatus& status)
19+
: Status(status) {}
20+
21+
TStatus Status;
22+
};
23+
24+
void CreateTable(TTableClient client, const std::string& path);
25+
void ThrowOnError(const TStatus& status);
26+
TStatus FillTableDataTransaction(TSession& session, const std::string& path);
27+
std::string SelectPaging(TTableClient client, const std::string& path, uint64_t pageLimit, std::string& lastCity, uint32_t& lastNumber);
28+
void PrintStatus(const TStatus& status);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#include "pagination.h"
2+
3+
using namespace NYdb;
4+
using namespace NYdb::NTable;
5+
6+
struct TSchool {
7+
std::string City;
8+
uint32_t Number;
9+
std::string Address;
10+
11+
TSchool(const std::string& city, uint32_t number, const std::string& address)
12+
: City(city)
13+
, Number(number)
14+
, Address(address) {}
15+
};
16+
17+
TParams GetTablesDataParams() {
18+
std::vector<TSchool> schoolsData = {
19+
TSchool("Орлов", 1, "Ст.Халтурина, 2"),
20+
TSchool("Орлов", 2, "Свободы, 4"),
21+
TSchool("Яранск", 1, "Гоголя, 25"),
22+
TSchool("Яранск", 2, "Кирова, 18"),
23+
TSchool("Яранск", 3, "Некрасова, 59"),
24+
TSchool("Кирс", 3, "Кирова, 6"),
25+
TSchool("Нолинск", 1, "Коммуны, 4"),
26+
TSchool("Нолинск", 2, "Федосеева, 2Б"),
27+
TSchool("Котельнич", 1, "Урицкого, 21"),
28+
TSchool("Котельнич", 2, "Октябрьская, 109"),
29+
TSchool("Котельнич", 3, "Советская, 153"),
30+
TSchool("Котельнич", 5, "Школьная, 2"),
31+
TSchool("Котельнич", 15, "Октябрьская, 91")
32+
};
33+
34+
TParamsBuilder paramsBuilder;
35+
36+
auto& Param = paramsBuilder.AddParam("$schoolsData");
37+
Param.BeginList();
38+
for (auto& school : schoolsData) {
39+
Param.AddListItem()
40+
.BeginStruct()
41+
.AddMember("city")
42+
.Utf8(school.City)
43+
.AddMember("number")
44+
.Uint32(school.Number)
45+
.AddMember("address")
46+
.Utf8(school.Address)
47+
.EndStruct();
48+
}
49+
Param.EndList();
50+
Param.Build();
51+
52+
return paramsBuilder.Build();
53+
}

0 commit comments

Comments
 (0)