Skip to content

Commit 40fafcc

Browse files
drewdzzzalyapunov
authored andcommitted
Misc: add an example of work with schemaless tuples
The new example `examples/Schemaless.cpp` shows how to work with tuples that have variable length and even different field types.
1 parent f6596e2 commit 40fafcc

File tree

3 files changed

+181
-0
lines changed

3 files changed

+181
-0
lines changed

CMakeLists.txt

+5
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,8 @@ TNTCXX_TEST(NAME SqlExample TYPE other
243243
SOURCES examples/Sql.cpp
244244
LIBRARIES ${COMMON_LIB}
245245
)
246+
247+
TNTCXX_TEST(NAME SchemalessExample TYPE other
248+
SOURCES examples/Schemaless.cpp
249+
LIBRARIES ${COMMON_LIB}
250+
)

examples/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ ADD_COMPILE_OPTIONS(-Wall -Wextra -Werror)
1010

1111
ADD_EXECUTABLE(Simple Simple.cpp)
1212
ADD_EXECUTABLE(Sql Sql.cpp)
13+
ADD_EXECUTABLE(Schemaless Schemaless.cpp)

examples/Schemaless.cpp

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* Copyright 2010-2023, Tarantool AUTHORS, please see AUTHORS file.
3+
*
4+
* Redistribution and use in source and binary forms, with or
5+
* without modification, are permitted provided that the following
6+
* conditions are met:
7+
*
8+
* 1. Redistributions of source code must retain the above
9+
* copyright notice, this list of conditions and the
10+
* following disclaimer.
11+
*
12+
* 2. Redistributions in binary form must reproduce the above
13+
* copyright notice, this list of conditions and the following
14+
* disclaimer in the documentation and/or other materials
15+
* provided with the distribution.
16+
*
17+
* THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
18+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21+
* <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22+
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25+
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28+
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29+
* SUCH DAMAGE.
30+
*/
31+
/**
32+
* To build this example see CMakeLists.txt or Makefile in current directory.
33+
* Prerequisites to run this test:
34+
* 1. Run Tarantool instance on localhost and set listening port 3301;
35+
* 2. Create space with id = 512 without format (primary key must index the first field)
36+
* 3. Grant read-write privileges for guest (or simply box.schema.user.grant('guest', 'super'))
37+
* 4. Compile and run ./Schemaless
38+
*/
39+
40+
#include "../src/Client/Connector.hpp"
41+
#include "../src/Buffer/Buffer.hpp"
42+
43+
#include "Reader.hpp"
44+
45+
const char *address = "127.0.0.1";
46+
int port = 3301;
47+
int WAIT_TIMEOUT = 1000; //milliseconds
48+
49+
using Buf_t = tnt::Buffer<16 * 1024>;
50+
#include "../src/Client/LibevNetProvider.hpp"
51+
using Net_t = LibevNetProvider<Buf_t, DefaultStream>;
52+
53+
/**
54+
* Field of schemaless tuple.
55+
*/
56+
using SchemalessField = std::variant<nullptr_t, int, std::string>;
57+
58+
/**
59+
* Schemaless tuple itself - dynamic array of fields.
60+
*/
61+
using SchemalessTuple = std::vector<SchemalessField>;
62+
63+
template <class BUFFER>
64+
std::vector<SchemalessTuple>
65+
decodeSchemalessTuple(Data<BUFFER> &data)
66+
{
67+
std::vector<SchemalessTuple> results;
68+
bool ok = data.decode(results);
69+
(void)ok;
70+
assert(ok);
71+
return results;
72+
}
73+
74+
template<class BUFFER>
75+
void
76+
printResponse(Response<BUFFER> &response)
77+
{
78+
std::cout << ">>> RESPONSE {" << std::endl;
79+
if (response.body.error_stack != std::nullopt) {
80+
Error err = (*response.body.error_stack)[0];
81+
std::cout << "RESPONSE ERROR: msg=" << err.msg <<
82+
" line=" << err.file << " file=" << err.file <<
83+
" errno=" << err.saved_errno <<
84+
" type=" << err.type_name <<
85+
" code=" << err.errcode << std::endl;
86+
}
87+
if (response.body.data != std::nullopt) {
88+
Data<BUFFER>& data = *response.body.data;
89+
std::vector<SchemalessTuple> tuples = decodeSchemalessTuple(data);
90+
if (tuples.empty()) {
91+
std::cout << "Empty result" << std::endl;
92+
return;
93+
}
94+
for (auto const& t : tuples) {
95+
for (auto const &field : t)
96+
std::visit([](auto &v){std::cout << v << " ";}, field);
97+
std::cout << std::endl;
98+
}
99+
}
100+
std::cout << "}" << std::endl;
101+
}
102+
103+
int
104+
main()
105+
{
106+
/*
107+
* Create default connector.
108+
*/
109+
Connector<Buf_t, Net_t> client;
110+
/*
111+
* Create single connection. Constructor takes only client reference.
112+
*/
113+
Connection<Buf_t, Net_t> conn(client);
114+
/*
115+
* Try to connect to given address:port. Current implementation is
116+
* exception free, so we rely only on return codes.
117+
*/
118+
int rc = client.connect(conn, {.address = address,
119+
.service = std::to_string(port),
120+
/*.user = ...,*/
121+
/*.passwd = ...,*/
122+
/* .transport = STREAM_SSL, */});
123+
if (rc != 0) {
124+
//assert(conn.getError().saved_errno != 0);
125+
std::cerr << conn.getError().msg << std::endl;
126+
return -1;
127+
}
128+
/*
129+
* Now let's execute several requests.
130+
* Note that any of :request() methods can't fail; they always
131+
* return request id - the future (number) which is used to get
132+
* response once it is received. Also note that at this step,
133+
* requests are encoded (into msgpack format) and saved into
134+
* output connection's buffer - they are ready to be sent.
135+
* But network communication itself will be done later.
136+
*/
137+
/*
138+
* Let's create a collection of tuples of different formats.
139+
*/
140+
std::vector<SchemalessTuple> tuples = {
141+
{1, "first"},
142+
{2, 5, nullptr, "second"},
143+
{3, nullptr, 1, 2, 3, 4, 5, 6},
144+
{4, "a", "bc", "def"},
145+
{5}
146+
};
147+
/* Insert the collection into our space. */
148+
uint32_t space_id = 512;
149+
std::vector<rid_t> futures;
150+
for (const auto &t : tuples) {
151+
rid_t f = conn.space[space_id].replace(t);
152+
futures.push_back(f);
153+
}
154+
/* Wait for all futures and then print each response. */
155+
client.waitAll(conn, futures);
156+
Response<Buf_t> response;
157+
for (const auto &f : futures) {
158+
assert(conn.futureIsReady(f));
159+
response = conn.getResponse(f);
160+
printResponse<Buf_t>(response);
161+
}
162+
163+
/* Let's select our tuples of different formats. */
164+
rid_t select = conn.space[space_id].select(std::make_tuple(0), 0, 100,
165+
0, IteratorType::GE);
166+
/* Wait for the future and then print the response. */
167+
client.wait(conn, select);
168+
assert(conn.futureIsReady(select));
169+
response = conn.getResponse(select);
170+
printResponse<Buf_t>(response);
171+
172+
/* Finally, user is responsible for closing connections. */
173+
client.close(conn);
174+
return 0;
175+
}

0 commit comments

Comments
 (0)