Kea 1.5.0
cql_connection.cc
Go to the documentation of this file.
1// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
2// Copyright (C) 2015-2018 Deutsche Telekom AG.
3//
4// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
5// Andrei Pavel <andrei.pavel@qualitance.com>
6//
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License at
10//
11// http://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS,
15// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16// See the License for the specific language governing permissions and
17// limitations under the License.
18
19#include <config.h>
20
21#include <cql/cql_connection.h>
22#include <cql/cql_exchange.h>
24#include <database/db_log.h>
25
26#include <string>
27
28namespace isc {
29namespace db {
30
32 : DatabaseConnection(parameters), statements_(), cluster_(NULL),
33 session_(NULL), consistency_(CASS_CONSISTENCY_QUORUM), schema_meta_(NULL),
34 keyspace_meta_(NULL), force_consistency_(true) {
35}
36
38 // Free up the prepared statements, ignoring errors. Session and connection
39 // resources are deallocated.
40 CassError rc = CASS_OK;
41 std::string error;
42
43 // Let's free the prepared statements.
45 CqlTaggedStatement statement = s.second;
46 if (statement.prepared_statement_) {
47 cass_prepared_free(statement.prepared_statement_);
48 }
49 }
50
51 // If there's a session, tear it down and free the resources.
52 if (session_) {
53 cass_schema_meta_free(schema_meta_);
54 CassFuture* close_future = cass_session_close(session_);
55 cass_future_wait(close_future);
56 error = checkFutureError(
57 "CqlConnection::~CqlConnection(): cass_sesssion_close() != CASS_OK",
58 close_future);
59 rc = cass_future_error_code(close_future);
60 cass_future_free(close_future);
61 cass_session_free(session_);
62 session_ = NULL;
63 }
64
65 // Free the cluster if there's one.
66 if (cluster_) {
67 cass_cluster_free(cluster_);
68 cluster_ = NULL;
69 }
70
71 if (rc != CASS_OK) {
72 // We're closing the connection anyway. Let's not throw at this stage.
74 }
75}
76
77void
79 CassError rc;
80 // Set up the values of the parameters
81 const char* contact_points = "127.0.0.1";
82 std::string scontact_points;
83 try {
84 scontact_points = getParameter("contact-points");
85 contact_points = scontact_points.c_str();
86 } catch (...) {
87 // No host. Fine, we'll use "127.0.0.1".
88 }
89
90 const char* port = NULL;
91 std::string sport;
92 try {
93 sport = getParameter("port");
94 port = sport.c_str();
95 } catch (...) {
96 // No port. Fine, we'll use the default "9042".
97 }
98
99 const char* user = NULL;
100 std::string suser;
101 try {
102 suser = getParameter("user");
103 user = suser.c_str();
104 } catch (...) {
105 // No user. Fine, we'll use NULL.
106 }
107
108 const char* password = NULL;
109 std::string spassword;
110 try {
111 spassword = getParameter("password");
112 password = spassword.c_str();
113 } catch (...) {
114 // No password. Fine, we'll use NULL.
115 }
116
117 const char* keyspace = "keatest";
118 std::string skeyspace;
119 try {
120 skeyspace = getParameter("keyspace");
121 keyspace = skeyspace.c_str();
122 } catch (...) {
123 // No keyspace name. Fine, we'll use "keatest".
124 }
125
126 const char* reconnect_wait_time = NULL;
127 std::string sreconnect_wait_time;
128 try {
129 sreconnect_wait_time = getParameter("reconnect-wait-time");
130 reconnect_wait_time = sreconnect_wait_time.c_str();
131 } catch (...) {
132 // No reconnect wait time. Fine, we'll use the default "2000".
133 }
134
135 const char* connect_timeout = NULL;
136 std::string sconnect_timeout;
137 try {
138 sconnect_timeout = getParameter("connect-timeout");
139 connect_timeout = sconnect_timeout.c_str();
140 } catch (...) {
141 // No connect timeout. Fine, we'll use the default "5000".
142 }
143
144 const char* request_timeout = NULL;
145 std::string srequest_timeout;
146 try {
147 srequest_timeout = getParameter("request-timeout");
148 request_timeout = srequest_timeout.c_str();
149 } catch (...) {
150 // No request timeout. Fine, we'll use the default "12000".
151 }
152
153 const char* tcp_keepalive = NULL;
154 std::string stcp_keepalive;
155 try {
156 stcp_keepalive = getParameter("tcp-keepalive");
157 tcp_keepalive = stcp_keepalive.c_str();
158 } catch (...) {
159 // No tcp-keepalive. Fine, we'll not use TCP keepalive.
160 }
161
162 std::string stcp_nodelay;
163 try {
164 stcp_nodelay = getParameter("tcp-nodelay");
165 } catch (...) {
166 // No tcp-nodelay. Fine, we'll use the default false.
167 }
168
169 cluster_ = cass_cluster_new();
170 cass_cluster_set_contact_points(cluster_, contact_points);
171
172 if (user && password) {
173 cass_cluster_set_credentials(cluster_, user, password);
174 }
175
176 if (port) {
177 int32_t port_number;
178 try {
179 port_number = boost::lexical_cast<int32_t>(port);
180 if (port_number < 1 || port_number > 65535) {
182 "CqlConnection::openDatabase(): "
183 "port outside of range, expected "
184 "1-65535, instead got "
185 << port);
186 }
187 } catch (const boost::bad_lexical_cast& ex) {
189 "CqlConnection::openDatabase(): invalid "
190 "port, expected castable to int, instead got "
191 "\"" << port
192 << "\", " << ex.what());
193 }
194 cass_cluster_set_port(cluster_, port_number);
195 }
196
197 if (reconnect_wait_time) {
198 int32_t reconnect_wait_time_number;
199 try {
200 reconnect_wait_time_number =
201 boost::lexical_cast<int32_t>(reconnect_wait_time);
202 if (reconnect_wait_time_number < 0) {
204 "CqlConnection::openDatabase(): invalid reconnect "
205 "wait time, expected positive number, instead got "
206 << reconnect_wait_time);
207 }
208 } catch (const boost::bad_lexical_cast& ex) {
210 "CqlConnection::openDatabase(): "
211 "invalid reconnect wait time, expected "
212 "castable to int, instead got \""
213 << reconnect_wait_time << "\", " << ex.what());
214 }
215 cass_cluster_set_reconnect_wait_time(cluster_,
216 reconnect_wait_time_number);
217 }
218
219 if (connect_timeout) {
220 int32_t connect_timeout_number;
221 try {
222 connect_timeout_number =
223 boost::lexical_cast<int32_t>(connect_timeout);
224 if (connect_timeout_number < 0) {
226 "CqlConnection::openDatabase(): "
227 "invalid connect timeout, expected "
228 "positive number, instead got "
229 << connect_timeout);
230 }
231 } catch (const boost::bad_lexical_cast& ex) {
233 "CqlConnection::openDatabase(): invalid connect timeout, "
234 "expected castable to int, instead got \""
235 << connect_timeout << "\", " << ex.what());
236 }
237 cass_cluster_set_connect_timeout(cluster_, connect_timeout_number);
238 }
239
240 if (request_timeout) {
241 int32_t request_timeout_number;
242 try {
243 request_timeout_number =
244 boost::lexical_cast<int32_t>(request_timeout);
245 if (request_timeout_number < 0) {
247 "CqlConnection::openDatabase(): "
248 "invalid request timeout, expected "
249 "positive number, instead got "
250 << request_timeout);
251 }
252 } catch (const boost::bad_lexical_cast& ex) {
254 "CqlConnection::openDatabase(): invalid request timeout, "
255 "expected castable to int, instead got \""
256 << request_timeout << "\", " << ex.what());
257 }
258 cass_cluster_set_request_timeout(cluster_, request_timeout_number);
259 }
260
261 if (tcp_keepalive) {
262 int32_t tcp_keepalive_number;
263 try {
264 tcp_keepalive_number = boost::lexical_cast<int32_t>(tcp_keepalive);
265 if (tcp_keepalive_number < 0) {
267 "CqlConnection::openDatabase(): "
268 "invalid TCP keepalive, expected "
269 "positive number, instead got "
270 << tcp_keepalive);
271 }
272 } catch (const boost::bad_lexical_cast& ex) {
274 "CqlConnection::openDatabase(): invalid TCP keepalive, "
275 "expected castable to int, instead got \""
276 << tcp_keepalive << "\", " << ex.what());
277 }
278 cass_cluster_set_tcp_keepalive(cluster_, cass_true,
279 tcp_keepalive_number);
280 }
281
282 if (stcp_nodelay == "true") {
283 cass_cluster_set_tcp_nodelay(cluster_, cass_true);
284 }
285
286 session_ = cass_session_new();
287
288 CassFuture* connect_future =
289 cass_session_connect_keyspace(session_, cluster_, keyspace);
290 cass_future_wait(connect_future);
291 const std::string error =
292 checkFutureError("CqlConnection::openDatabase(): "
293 "cass_session_connect_keyspace() != CASS_OK",
294 connect_future);
295 rc = cass_future_error_code(connect_future);
296 cass_future_free(connect_future);
297 if (rc != CASS_OK) {
298 cass_session_free(session_);
299 session_ = NULL;
300 cass_cluster_free(cluster_);
301 cluster_ = NULL;
302 isc_throw(DbOpenError, error);
303 }
304
305 // Get keyspace meta.
306 schema_meta_ = cass_session_get_schema_meta(session_);
307 keyspace_meta_ = cass_schema_meta_keyspace_by_name(schema_meta_, keyspace);
308 if (!keyspace_meta_) {
309 isc_throw(DbOpenError, "CqlConnection::openDatabase(): "
310 "!cass_schema_meta_keyspace_by_name()");
311 }
312}
313
314void
316 CassError rc = CASS_OK;
317 for (StatementMapEntry it : statements) {
318 CqlTaggedStatement& tagged_statement = it.second;
319 if (statements_.find(tagged_statement.name_) != statements_.end()) {
321 "CqlConnection::prepareStatements(): "
322 "duplicate statement with name "
323 << tagged_statement.name_);
324 }
325
326 CassFuture* future =
327 cass_session_prepare(session_, tagged_statement.text_);
328 cass_future_wait(future);
329 const std::string error =
330 checkFutureError("CqlConnection::prepareStatements():"
331 " cass_session_prepare() != CASS_OK",
332 future, tagged_statement.name_);
333 rc = cass_future_error_code(future);
334 if (rc != CASS_OK) {
335 cass_future_free(future);
337 }
338
339 tagged_statement.prepared_statement_ = cass_future_get_prepared(future);
340 statements_.insert(it);
341 cass_future_free(future);
342 }
343}
344
345void
346CqlConnection::setConsistency(bool force, CassConsistency consistency) {
347 force_consistency_ = force;
348 consistency_ = consistency;
349}
350
351void
354}
355
356void
359}
360
361void
364}
365
366const std::string
367CqlConnection::checkFutureError(const std::string& what,
368 CassFuture* future,
369 StatementTag statement_tag /* = NULL */) {
370 CassError cass_error = cass_future_error_code(future);
371 const char* error_message;
372 size_t error_message_size;
373 cass_future_error_message(future, &error_message, &error_message_size);
374
375 std::stringstream stream;
376 if (statement_tag && std::strlen(statement_tag) > 0) {
377 // future is from cass_session_execute() call.
378 stream << "Statement ";
379 stream << statement_tag;
380 } else {
381 // future is from cass_session_*() call.
382 stream << "Session action ";
383 }
384 if (cass_error == CASS_OK) {
385 stream << " executed succesfully.";
386 } else {
387 stream << " failed, Kea error: " << what
388 << ", Cassandra error code: " << cass_error_desc(cass_error)
389 << ", Cassandra future error: " << error_message;
390 }
391 return stream.str();
392}
393
394} // namespace dhcp
395} // namespace isc
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
virtual ~CqlConnection()
Destructor.
StatementMap statements_
Pointer to external array of tagged statements containing statement name, array of names of bind para...
CqlConnection(const ParameterMap &parameters)
Constructor.
virtual void commit()
Commit Transactions.
void startTransaction()
Start transaction.
virtual void rollback()
Rollback Transactions.
CassSession * session_
CQL session handle.
bool force_consistency_
CQL consistency enabled.
const CassKeyspaceMeta * keyspace_meta_
Keyspace meta information, used for UDTs.
CassCluster * cluster_
CQL connection handle.
void openDatabase()
Open database.
CassConsistency consistency_
CQL consistency.
const CassSchemaMeta * schema_meta_
static const std::string checkFutureError(const std::string &what, CassFuture *future, StatementTag statement_tag=NULL)
Check for errors.
void prepareStatements(StatementMap &statements)
Prepare statements.
void setConsistency(bool force, CassConsistency consistency)
Set consistency.
Common database connection class.
std::string getParameter(const std::string &name) const
Returns value of a connection parameter.
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
Exception thrown on failure to open database.
Exception thrown on failure to execute a database function.
We want to reuse the database backend connection and exchange code for other uses,...
#define DB_LOG_ERROR(MESSAGE)
Definition: db_log.h:137
#define DB_LOG_DEBUG(LEVEL, MESSAGE)
Macros.
Definition: db_log.h:115
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
const int DB_DBG_TRACE_DETAIL
Database logging levels.
Definition: db_log.h:40
char const *const StatementTag
Statement index representing the statement name.
std::pair< StatementTag, CqlTaggedStatement > StatementMapEntry
A type for a single entry on the statements map.
@ CQL_CONNECTION_COMMIT
Definition: db_log.h:67
@ CQL_CONNECTION_BEGIN_TRANSACTION
Definition: db_log.h:66
@ CQL_DEALLOC_ERROR
Definition: db_log.h:65
@ CQL_CONNECTION_ROLLBACK
Definition: db_log.h:68
std::unordered_map< StatementTag, CqlTaggedStatement, StatementTagHash, StatementTagEqual > StatementMap
A container for all statements.
Defines the logger used by the top-level component of kea-dhcp-ddns.
Defines a single statement or query.
StatementTag name_
Short description of the query.
char const *const text_
Text representation of the actual query.
const CassPrepared * prepared_statement_
Internal Cassandra object representing the prepared statement.