1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
|
// Copyright (C) 2016-2017 Deutsche Telekom AG.
//
// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
// Andrei Pavel <andrei.pavel@qualitance.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CQL_EXCHANGE_H
#define CQL_EXCHANGE_H
#include <dhcpsrv/cql_connection.h>
#include <dhcpsrv/sql_common.h>
#include <boost/any.hpp> // for boost::any
#include <string>
#include <typeinfo> // for std::type_info
#include <unordered_map> // for std::unordered_map
#include <utility>
#include <vector>
namespace isc {
namespace dhcp {
/// @brief Host identifier converted to Cassandra data type
typedef std::vector<cass_byte_t> CassBlob;
/// @brief Forward declaration to @ref CqlExchange
class CqlExchange;
/// @brief Structure used to bind C++ input values to dynamic CQL parameters
///
/// The structure contains a vector which stores the input values. The vector is
/// passed directly into the CQL execute call. Note that the data values are
/// stored as pointers. These pointers need to be valid for the duration of the
/// CQL statement execution. In other words, populating them with pointers that
/// go out of scope before the statement is executed results in undefined
/// behaviour.
class AnyArray : public std::vector<boost::any> {
public:
/// @brief Add a value at the end of the vector.
void add(const boost::any& value);
/// @brief Remove the void pointer to the data value from a specified
/// position inside the vector.
void remove(const size_t& index);
};
// @brief Representation of a Cassandra User Defined Type
class Udt : public AnyArray {
public:
/// @brief Parameterized constructor
Udt(const CqlConnection& connection, const std::string& name);
/// @brief Destructor
~Udt();
/// @brief Frees the underlying container.
void freeUserType();
/// @brief Creates the underlying container.
void newUserType();
/// @brief Connection to the Cassandra database
const CqlConnection& connection_;
/// @brief Name of the UDT in the schema: CREATE TYPE ___ { ... }
const std::string name_;
/// @brief Internal Cassandra driver object representing a Cassandra data
/// type
const CassDataType* cass_data_type_;
/// @brief Internal Cassandra driver object representing a user defined type
CassUserType* cass_user_type_;
};
/// @brief Defines an array of arbitrary objects (used by Cassandra backend)
typedef AnyArray AnyCollection;
/// @brief Binds a C++ object to a Cassandra statement's parameter. Used in all
/// statements.
/// @param value the value to be set or retreived
/// @param index offset of the value being processed
/// @param statement pointer to the parent statement being used
typedef CassError (*CqlBindFunction)(const boost::any& value,
const size_t& index,
CassStatement* statement);
/// @brief Sets a member in a UDT. Used in INSERT & UPDATE statements.
/// @param value the value to be set or retreived
/// @param index offset of the value being processed
/// @param cass_user_type pointer to the user type that uses this member
typedef CassError (*CqlUdtSetFunction)(const boost::any& value,
const size_t& index,
CassUserType* cass_user_type);
/// @brief Sets an item in a collection. Used in INSERT & UPDATE statements.
/// @param value pointer to a value to be inserted or updated
/// @param collection pointer to collection to be inserted or updated
typedef CassError (*CqlCollectionAppendFunction)(const boost::any& value,
CassCollection* collection);
/// @brief Converts a single Cassandra column value to a C++ object. Used in
/// SELECT statements.
///
/// @param data the result will be stored here (this pointer will be updated)
/// @param value this value will be converted
typedef CassError (*CqlGetFunction)(const boost::any& data,
const CassValue* value);
/// @brief Wrapper over the bind and get functions that interface with Cassandra
struct CqlFunction {
/// @brief Binds a C++ object to a Cassandra statement's parameter. Used in
/// all statements.
CqlBindFunction cqlBindFunction_;
/// @brief Sets a member in a UDT. Used in INSERT & UPDATE statements.
CqlUdtSetFunction cqlUdtSetFunction_;
/// @brief Sets an item in a collection. Used in INSERT & UPDATE statements.
CqlCollectionAppendFunction cqlCollectionAppendFunction_;
/// @brief Converts a single Cassandra column value to a C++ object. Used in
/// SELECT statements.
CqlGetFunction cqlGetFunction_;
};
/// @brief Cassandra Exchange
///
/// Used to convert between Cassandra CQL and C++ data types. A different
/// exchange is made for every distinct set of columns. Multiple tables may use
/// the same exchange if they have the same columns.
class CqlExchange : public virtual SqlExchange {
public:
/// @brief Constructor
///
/// Empty body. Derived constructors specify table columns.
CqlExchange();
/// @brief Destructor
virtual ~CqlExchange();
/// @name Time conversion:
/// @{
/// @brief Converts time to Cassandra format
///
/// @param cltt timestamp of last client transmission time to be converted
/// @param valid_lifetime lifetime of a lease
/// @param expire expiration time (result will be stored here)
static void convertToDatabaseTime(const time_t& cltt,
const uint32_t& valid_lifetime,
cass_int64_t& expire);
/// @brief Converts time from Cassandra format
/// @param expire expiration time in Cassandra format
/// @param valid_lifetime lifetime of a lease
/// @param cltt client last transmission time (result will be stored here)
static void convertFromDatabaseTime(const cass_int64_t& expire,
const cass_int64_t& valid_lifetime,
time_t& cltt);
/// @}
/// @brief Create BIND array to receive C++ data.
///
/// Used in executeSelect() to retrieve from database
///
/// @param data array of bound objects representing data to be retrieved
/// @param statement_tag prepared statement being executed; defaults to an
/// invalid index
virtual void createBindForSelect(AnyArray& data,
StatementTag statement_tag = NULL) = 0;
/// @brief Executes SELECT statements.
///
/// @param connection connection used to communicate with the Cassandra
/// database
/// @param where_values array of bound objects used to filter the results
/// @param statement_tag prepared statement being executed
/// @param single true if a single row should be returned; by default,
/// multiple rows are allowed
///
/// @return collection of boost::any objects
///
/// @throw DbOperationError
/// @throw MultipleRecords
AnyArray executeSelect(const CqlConnection& connection,
const AnyArray& where_values,
StatementTag statement_tag,
const bool& single = false);
/// @brief Executes INSERT, UPDATE or DELETE statements.
///
/// @param connection connection used to communicate with the Cassandra
/// database
/// @param assigned_values array of bound objects to be used when inserting
/// values
/// @param statement_tag prepared statement being executed
/// applied, could be different for commit than it is for rollback
void executeMutation(const CqlConnection& connection,
const AnyArray& assigned_values,
StatementTag statement_tag);
/// @brief Check if CQL statement has been applied.
///
/// @param future structure used to wait on statement executions
/// @param row_count number of rows returned
/// @param column_count number of columns queried
///
/// On insert, a false [applied] means there is a duplicate entry with the
/// same priumary key.
///
/// @return true if statement has been succesfully applied, false otherwise
bool statementApplied(CassFuture* future, size_t* row_count = NULL,
size_t* column_count = NULL);
/// @brief Copy received data into the derived class' object.
///
/// Copies information about the entity to be retrieved into a holistic
/// object. Called in @ref executeSelect(). Not implemented for base class
/// CqlExchange. To be implemented in derived classes.
///
/// @return a pointer to the object retrieved.
virtual boost::any retrieve() = 0;
};
/// @brief Exchange used to retrieve schema version from the keyspace.
class CqlVersionExchange : public virtual CqlExchange {
public:
/// @brief Constructor
///
/// Specifies table columns.
CqlVersionExchange();
/// @brief Destructor
virtual ~CqlVersionExchange();
/// @brief Create BIND array to receive C++ data.
///
/// Used in executeSelect() to retrieve from database
///
/// @param data array of bound objects representing data to be retrieved
/// @param statement_tag prepared statement being executed; defaults to an
/// invalid index
virtual void
createBindForSelect(AnyArray& data,
StatementTag statement_tag = NULL) override;
/// @brief Standalone method used to retrieve schema version
///
/// @param connection array of bound objects representing data to be
/// retrieved
///
/// @return version of schema specified in the prepared statement in the
/// @ref CqlConnection parameter
virtual VersionPair retrieveVersion(const CqlConnection& connection);
/// @brief Copy received data into the <version,minor> pair.
///
/// Copies information about the version to be retrieved into a pair. Called
/// in executeSelect().
///
/// @return a pointer to the object retrieved.
virtual boost::any retrieve() override;
/// @brief Statement tags definitions
/// @{
static constexpr StatementTag GET_VERSION = "GET_VERSION";
/// @}
/// @brief Cassandra statements
static StatementMap tagged_statements_;
private:
/// @brief Major version
cass_int32_t version_;
/// @brief Minor version
cass_int32_t minor_;
/// @brief Pair containing major and minor version
VersionPair pair_;
};
/// @brief Common operations in Cassandra exchanges
class CqlCommon {
public:
/// @brief Assigns values to every column of an INSERT or an UPDATE statement.
///
/// Calls cqlBindFunction_() for every column with its respective type.
///
/// @param data array containing column values to be passed to the statement
/// being executed
/// @param statement internal Cassandra object representing the statement
/// being executed
static void bindData(const AnyArray& data, CassStatement* statement);
/// @brief Retrieves data returned by Cassandra.
///
/// Calls cqlGetFunction_() for every column with its respective type.
///
/// @param row internal Cassandra object containing data returned by
/// Cassandra
/// @param data array containing objects to be populated with results
static void getData(const CassRow* row, AnyArray& data);
};
/// @brief Determine exchange type based on boost::any type.
ExchangeDataType
exchangeType(const boost::any& object);
/// @brief Determine exchange type based on CassValueType.
ExchangeDataType
exchangeType(const CassValueType& type);
} // namespace dhcp
} // namespace isc
#endif // CQL_EXCHANGE_H
|