Skip to content

Commit 12092b2

Browse files
OzanCanselakeles85
andauthored
row_iterator_sync have been implemented. [API-1762] (#1161)
* row_iterator_sync is added. --------- Co-authored-by: Ali Keles <[email protected]>
1 parent 49353ec commit 12092b2

File tree

6 files changed

+445
-2
lines changed

6 files changed

+445
-2
lines changed

Reference_Manual.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@
149149
* [7.11.8 Iterators](#7118-iterators)
150150
* [7.11.8.1 page_iterator](#71181-page_iterator)
151151
* [7.11.8.2 page_iterator_sync](#71182-page_iterator)
152+
* [7.11.8.3 row_iterator_sync](#71182-row_iterator)
152153
* [8. Development and Testing](#8-development-and-testing)
153154
* [8.1. Testing](#81-testing)
154155
* [9. Getting Help](#9-getting-help)
@@ -3690,6 +3691,8 @@ Exceptions which can be thrown by SQL API are stated at below:
36903691
- `sql_page::sql_row::get_object(std::string)` can throw `illegal_argument` exception if the column doesn't exist.
36913692
- `sql_result::page_iterator_sync::operator++()` can throw `no_such_element` exception if the fetch operation is timed out.
36923693
- `sql_result::page_iterator_sync::operator*()` can throw `no_such_element` exception if the iterator points to past-end element;
3694+
- `sql_result::row_iterator_sync::operator++()` can throw `no_such_element` exception if the fetch operation is timed out.
3695+
- `sql_result::row_iterator_sync::operator*()` can throw `no_such_element` exception if the iterator points to past-end element;
36933696

36943697
In addition, any method which returns `boost::future<T>` can throw an exception.
36953698
Unless otherwise is stated, `sql::hazelcast_sql_exception` is thrown.
@@ -3699,6 +3702,7 @@ There are several iterators in SQL Api to satisfy different use cases. They are
36993702

37003703
- `page_iterator` (Async page iterator)
37013704
- `page_iterator_sync` (Sync page iterator)
3705+
- `row_iterator_sync` (Sync row iterator)
37023706

37033707
Reminder 1, only one type of iterator can be used. So it is suggested to choose appropriate one to fetch `SELECT` query results.
37043708

@@ -3741,6 +3745,36 @@ std::vector<std::shared_ptr<sql_page>> pages;
37413745
copy(result->pbegin(), result->pend(), back_inserter(pages));
37423746
```
37433747
3748+
#### 7.11.8.3 row_iterator_sync
3749+
`row_iterator_sync` is the third iterator which wraps `page_iterator_sync` and allows user to iterate over rows rather than pages.
3750+
It is similar to native C++ iterators. So it can be used with `algorithm` header.
3751+
It supports `operator*()`, `operator->()`, `operator++()` operators. Post increment operator is marked as `delete`.
3752+
`timeout` can be set, it is used when fetching new page. It throws `exception::no_such_element` exception if the fetch operation is timed out.
3753+
`row_iterator_sync` is copyable but it is there only for convenience. Copy is shallow copy so copied instances should not be used simultaneously.
3754+
This iterator is acquired by `sql_result::begin()` and `sql_result::end()`. Hence, this iterator can be used in `range-for` loop. `timeout` is defaulted to `std::chrono::milliseconds{ -1 }` which means wait forever.
3755+
3756+
`range-for` loop example:
3757+
``` C++
3758+
auto result = client.get_sql().execute("SELECT * FROM integers").get();
3759+
3760+
for (const sql_page::sql_row& row : *result) {
3761+
std::cout << row.get_object<int>(0);
3762+
}
3763+
```
3764+
3765+
`algorithm` header example:
3766+
``` C++
3767+
auto result = client.get_sql().execute("SELECT * FROM integers").get();
3768+
3769+
std::vector<int> numbers;
3770+
3771+
transform(
3772+
begin(*result),
3773+
end(*result),
3774+
back_inserter(numbers),
3775+
[](const sql_page::sql_row& row) { return *row.get_object<int>(0); });
3776+
```
3777+
37443778
# 8. Development and Testing
37453779
37463780
Hazelcast C++ client is developed using C++. If you want to help with bug fixes, develop new features or

examples/sql/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ add_executable(sql_cancellation_example sql_cancellation_example.cpp)
1919
add_executable(sql_json_example sql_json_example.cpp)
2020
add_executable(sql_order_by_limit_offset sql_order_by_limit_offset.cpp)
2121
add_executable(sql_page_iterator_sync sql_page_iterator_sync.cpp)
22+
add_executable(sql_row_iterator_sync sql_row_iterator_sync.cpp)
2223
add_executable(sql_advanced_query_options sql_advanced_query_options.cpp)
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
2+
/*
3+
* Copyright (c) 2008-2023, Hazelcast, Inc. All Rights Reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#include <algorithm>
18+
#include <iterator>
19+
20+
#include <boost/algorithm/string.hpp>
21+
22+
#include <hazelcast/client/hazelcast_client.h>
23+
24+
using hazelcast::client::hazelcast_client;
25+
26+
void
27+
populate_map(hazelcast_client&);
28+
void
29+
create_mapping(hazelcast_client&);
30+
void
31+
for_loop(hazelcast_client&);
32+
void
33+
algorithm_copy(hazelcast_client&);
34+
void
35+
algorithm_filter(hazelcast_client&);
36+
void
37+
timeout(hazelcast_client&);
38+
39+
/**
40+
* Normally, cpp-client provides an async api for every features.
41+
* But for sake of convenience and similar usages with native C++ iterators
42+
* cpp-client provides sync row iterator. So `row_iterator_sync` is a blocking
43+
* iterator. It wraps the `page_iterator_sync`.
44+
* This example demonstrates how to use `row_iterator_sync` and what
45+
* the use cases are.
46+
*/
47+
int
48+
main()
49+
{
50+
auto hz = hazelcast::new_client().get();
51+
52+
// Preparation
53+
populate_map(hz);
54+
create_mapping(hz);
55+
56+
// Use cases, examples
57+
for_loop(hz);
58+
algorithm_copy(hz);
59+
algorithm_filter(hz);
60+
timeout(hz);
61+
62+
return 0;
63+
}
64+
65+
void
66+
populate_map(hazelcast_client& client)
67+
{
68+
// Populate a map before using it in sql.
69+
auto map = client.get_map("integers").get();
70+
71+
for (int i = 0; i < 100; ++i) {
72+
map->put(i, i).get();
73+
}
74+
}
75+
76+
void
77+
create_mapping(hazelcast_client& client)
78+
{
79+
// Create mapping for the integers.
80+
// This needs to be done only once per map.
81+
// It is required to use a map in SQL query.
82+
auto result = client.get_sql()
83+
.execute(R"(
84+
CREATE OR REPLACE MAPPING integers
85+
TYPE IMap
86+
OPTIONS (
87+
'keyFormat' = 'int',
88+
'valueFormat' = 'int'
89+
)
90+
)")
91+
.get();
92+
}
93+
94+
std::shared_ptr<hazelcast::client::sql::sql_result>
95+
select_numbers(hazelcast::client::hazelcast_client& client)
96+
{
97+
using namespace hazelcast::client::sql;
98+
99+
sql_statement statement(client, "SELECT * FROM integers");
100+
101+
// Set cursor buffer size to 5
102+
// So there will be 20 pages(100 / 5 = 20)
103+
statement.cursor_buffer_size(5);
104+
105+
return client.get_sql().execute(statement).get();
106+
}
107+
108+
void
109+
seperator(const std::string& text = std::string{})
110+
{
111+
std::string output(60, '=');
112+
boost::replace_first(output, std::string(text.size(), '='), text);
113+
114+
output = std::string(20, '=') + output;
115+
116+
std::cout << output << std::endl;
117+
}
118+
119+
void
120+
for_loop(hazelcast_client& client)
121+
{
122+
seperator("for_loop() - BEGIN");
123+
124+
auto result = select_numbers(client);
125+
126+
for (const auto& row : *result) {
127+
std::cout << row.get_object<int>(0);
128+
}
129+
130+
seperator("for_loop() - END");
131+
seperator();
132+
}
133+
134+
void
135+
algorithm_copy(hazelcast_client& client)
136+
{
137+
using hazelcast::client::sql::sql_page;
138+
139+
seperator("algorithm_copy() - BEGIN");
140+
141+
auto result = select_numbers(client);
142+
143+
std::vector<int> numbers;
144+
145+
transform(
146+
begin(*result),
147+
end(*result),
148+
back_inserter(numbers),
149+
[](const sql_page::sql_row& row) { return *row.get_object<int>(0); });
150+
151+
sort(begin(numbers), end(numbers));
152+
copy(begin(numbers),
153+
end(numbers),
154+
std::ostream_iterator<int>(std::cout, "\n"));
155+
156+
seperator("algorithm_copy - END");
157+
}
158+
159+
void
160+
algorithm_filter(hazelcast_client& client)
161+
{
162+
using hazelcast::client::sql::sql_page;
163+
164+
seperator("algorithm_filter - BEGIN");
165+
166+
auto result = select_numbers(client);
167+
168+
std::vector<std::shared_ptr<sql_page>> pages;
169+
170+
copy_if(result->pbegin(),
171+
result->pend(),
172+
back_inserter(pages),
173+
[](const std::shared_ptr<sql_page>& p) {
174+
// Filter out the pages which contains a number which is
175+
// divisable by 20
176+
return any_of(begin(p->rows()),
177+
end(p->rows()),
178+
[](const sql_page::sql_row& row) {
179+
return *row.get_object<int>(0) % 20 == 0;
180+
});
181+
});
182+
183+
for (const auto& page : pages) {
184+
for (const sql_page::sql_row& row : page->rows()) {
185+
std::cout << row.get_object<int>(0) << " ";
186+
}
187+
188+
std::cout << std::endl;
189+
}
190+
191+
seperator("algorithm_filter - END");
192+
}
193+
194+
void
195+
timeout(hazelcast_client& client)
196+
{
197+
seperator("timeout - BEGIN");
198+
199+
// `generate_stream(1)` generates a row per seconds, so it will guaranteed
200+
// that it will timeout
201+
auto result =
202+
client.get_sql().execute("SELECT * FROM TABLE(generate_stream(1))").get();
203+
204+
auto it = result->begin(std::chrono::milliseconds{ 1 });
205+
206+
try {
207+
++it;
208+
++it;
209+
} catch (hazelcast::client::exception::no_such_element& e) {
210+
std::cout << "Timedout" << std::endl;
211+
}
212+
213+
seperator("timeout - END");
214+
}

hazelcast/include/hazelcast/client/sql/sql_result.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,70 @@ class HAZELCAST_API sql_result : public std::enable_shared_from_this<sql_result>
180180
std::chrono::milliseconds timeout_;
181181
};
182182

183+
/**
184+
* Copy is allowed for convenience but it does shallow copy so it should be avoided.
185+
*/
186+
class HAZELCAST_API row_iterator_sync
187+
{
188+
public:
189+
using difference_type = void;
190+
using value_type = sql_page::sql_row;
191+
using pointer = sql_page::sql_row*;
192+
using reference = const sql_page::sql_row&;
193+
using iterator_category = std::input_iterator_tag;
194+
195+
/**
196+
* Sets timeout for page fetch operation.
197+
*/
198+
void set_timeout(std::chrono::milliseconds);
199+
200+
/**
201+
* Retrieves the timeout
202+
*/
203+
std::chrono::milliseconds timeout() const;
204+
205+
friend HAZELCAST_API bool operator==(const row_iterator_sync&,
206+
const row_iterator_sync&);
207+
friend HAZELCAST_API bool operator!=(const row_iterator_sync&,
208+
const row_iterator_sync&);
209+
210+
/**
211+
* Returns current row. It might block in case of the current page is empty.
212+
*
213+
* @throws exception::no_such_element if the iterator points to the past-end
214+
*/
215+
const sql_page::sql_row& operator*() const;
216+
217+
218+
/**
219+
* Returns current row. It might block in case of the current page is empty.
220+
*
221+
* @throws exception::no_such_element if the iterator points to the past-end
222+
*/
223+
const sql_page::sql_row* operator->() const;
224+
225+
/**
226+
* Post increment operator is deleted because copy is discouraged.
227+
*/
228+
row_iterator_sync operator++(int) = delete;
229+
230+
/**
231+
* Fetches next row in blocking manner. If page is already fetched, it doesn't block, otherwise it blocks.
232+
*
233+
* @throws exception::no_such_element if the iterator points to the past-end or operation is timedout.
234+
*/
235+
row_iterator_sync& operator++();
236+
237+
private:
238+
239+
friend class sql_result;
240+
row_iterator_sync(page_iterator_sync&&);
241+
row_iterator_sync() = default;
242+
243+
mutable page_iterator_sync iterator_;
244+
std::size_t row_idx_;
245+
};
246+
183247
/**
184248
* The destructor closes the result if it were open.
185249
*/
@@ -236,6 +300,9 @@ class HAZELCAST_API sql_result : public std::enable_shared_from_this<sql_result>
236300
page_iterator_sync pbegin(
237301
std::chrono::milliseconds timeout = std::chrono::milliseconds{ -1 });
238302
page_iterator_sync pend();
303+
row_iterator_sync begin(
304+
std::chrono::milliseconds timeout = std::chrono::milliseconds{ -1 });
305+
row_iterator_sync end();
239306

240307
private:
241308
friend class sql_service;

0 commit comments

Comments
 (0)