Kea 1.5.0
cql_exchange.cc
Go to the documentation of this file.
1// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
2// Copyright (C) 2016-2017 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>
23#include <cql/sql_common.h>
25
26#include <boost/multi_index/hashed_index.hpp>
27#include <boost/multi_index/member.hpp>
28#include <boost/multi_index/sequenced_index.hpp>
29#include <boost/multi_index_container.hpp>
30#include <boost/noncopyable.hpp>
31#include <boost/shared_ptr.hpp>
32
33#include <fstream>
34#include <iostream>
35#include <map>
36#include <string>
37#include <utility>
38#include <vector>
39
40namespace isc {
41namespace db {
42
44#define KEA_CASS_CHECK(cass_error) \
45 { \
46 if (cass_error != CASS_OK) { \
47 return cass_error; \
48 } \
49 }
50
54public:
55 size_t operator()(const ExchangeDataType& key) const {
56 return std::hash<size_t>{}(static_cast<size_t>(key));
57 }
58};
59
61typedef std::unordered_map<ExchangeDataType, CqlFunction, ExchangeDataTypeHash>
64
72std::size_t
73hash_value(const CassValueType& key) {
74 return key;
75}
76
79
81typedef std::unordered_map<std::type_index, ExchangeDataType> AnyTypeMap;
82
83// Declare uint8_t as key here for compatibility with g++ version 5. Ideally,
84// it would be CassValueType
85typedef std::unordered_map<uint8_t, ExchangeDataType> CassTypeMap;
87
89static AnyTypeMap ANY_TYPE_MAP = {
90 {typeid(NULL), EXCHANGE_DATA_TYPE_NONE},
91 {typeid(cass_bool_t*), EXCHANGE_DATA_TYPE_BOOL},
92 {typeid(cass_int8_t*), EXCHANGE_DATA_TYPE_INT8},
93 {typeid(cass_int16_t*), EXCHANGE_DATA_TYPE_INT16},
94 {typeid(cass_int32_t*), EXCHANGE_DATA_TYPE_INT32},
95 {typeid(cass_int64_t*), EXCHANGE_DATA_TYPE_INT64},
96 {typeid(std::string*), EXCHANGE_DATA_TYPE_STRING},
98 {typeid(CassUuid*), EXCHANGE_DATA_TYPE_UUID},
99 {typeid(Udt*), EXCHANGE_DATA_TYPE_UDT}, // user data type
101
103static CassTypeMap CASS_TYPE_MAP = {
104 {CASS_VALUE_TYPE_CUSTOM, EXCHANGE_DATA_TYPE_UDT},
105 {CASS_VALUE_TYPE_ASCII, EXCHANGE_DATA_TYPE_STRING},
106 {CASS_VALUE_TYPE_BIGINT, EXCHANGE_DATA_TYPE_INT64},
107 {CASS_VALUE_TYPE_BLOB, EXCHANGE_DATA_TYPE_BYTES},
108 {CASS_VALUE_TYPE_BOOLEAN, EXCHANGE_DATA_TYPE_BOOL},
109 {CASS_VALUE_TYPE_COUNTER, EXCHANGE_DATA_TYPE_INT32},
110 {CASS_VALUE_TYPE_DECIMAL, EXCHANGE_DATA_TYPE_INT32},
111 {CASS_VALUE_TYPE_DOUBLE, EXCHANGE_DATA_TYPE_INT64},
112 {CASS_VALUE_TYPE_FLOAT, EXCHANGE_DATA_TYPE_INT32},
113 {CASS_VALUE_TYPE_INT, EXCHANGE_DATA_TYPE_INT32},
114 {CASS_VALUE_TYPE_TEXT, EXCHANGE_DATA_TYPE_STRING},
115 {CASS_VALUE_TYPE_TIMESTAMP, EXCHANGE_DATA_TYPE_INT64},
116 {CASS_VALUE_TYPE_UUID, EXCHANGE_DATA_TYPE_UUID},
117 {CASS_VALUE_TYPE_VARCHAR, EXCHANGE_DATA_TYPE_STRING},
118 {CASS_VALUE_TYPE_VARINT, EXCHANGE_DATA_TYPE_INT32},
119 {CASS_VALUE_TYPE_TIMEUUID, EXCHANGE_DATA_TYPE_INT64},
120 {CASS_VALUE_TYPE_INET, EXCHANGE_DATA_TYPE_NONE},
121 {CASS_VALUE_TYPE_DATE, EXCHANGE_DATA_TYPE_INT64},
122 {CASS_VALUE_TYPE_TIME, EXCHANGE_DATA_TYPE_INT64},
123 {CASS_VALUE_TYPE_SMALL_INT, EXCHANGE_DATA_TYPE_INT16},
124 {CASS_VALUE_TYPE_TINY_INT, EXCHANGE_DATA_TYPE_INT8},
125 {CASS_VALUE_TYPE_LIST, EXCHANGE_DATA_TYPE_COLLECTION},
126 {CASS_VALUE_TYPE_MAP, EXCHANGE_DATA_TYPE_COLLECTION},
127 {CASS_VALUE_TYPE_SET, EXCHANGE_DATA_TYPE_COLLECTION},
128 {CASS_VALUE_TYPE_UDT, EXCHANGE_DATA_TYPE_UDT},
129 {CASS_VALUE_TYPE_TUPLE, EXCHANGE_DATA_TYPE_UDT}};
130
133Udt::Udt(const CqlConnection& connection, const std::string& name)
134 : AnyArray(), connection_(connection), name_(name) {
135 // Create type.
136 cass_data_type_ = cass_keyspace_meta_user_type_by_name(
138 if (!cass_data_type_) {
140 "Udt::Udt(): UDT " << name_ << " does not exist ");
141 }
142 // Create container.
143 cass_user_type_ = cass_user_type_new_from_data_type(cass_data_type_);
144 if (!cass_user_type_) {
146 "Udt::Udt(): Type " << name_
147 << " is not a UDT as expected. ");
148 }
149}
150
153 //
154 // Bug: it seems that if there is no call to
155 // cass_user_type_set_*(cass_user_type_), then
156 // cass_user_type_free(cass_user_type_) might SIGSEGV, so we never
157 // free. Udt objects should have application scope though.
158 // cass_user_type_free(cass_user_type_);
159}
161
164void
165AnyArray::add(const boost::any& value) {
166 push_back(value);
167}
168
169void
170AnyArray::remove(const size_t& index) {
171 if (size() <= index) {
173 "AnyArray::remove(): index "
174 << index << " out of bounds: [0, " << (size() - 1)
175 << "]");
176 }
177 erase(begin() + index);
178}
180
184static CassError
185CqlBindNone(const boost::any& /* value */,
186 const size_t& index,
187 CassStatement* statement) {
188 return cass_statement_bind_null(statement, index);
189}
190
191static CassError
192CqlBindBool(const boost::any& value,
193 const size_t& index,
194 CassStatement* statement) {
195 return cass_statement_bind_bool(statement, index,
196 *boost::any_cast<cass_bool_t*>(value));
197}
198
199static CassError
200CqlBindInt8(const boost::any& value,
201 const size_t& index,
202 CassStatement* statement) {
203 return cass_statement_bind_int8(statement, index,
204 *boost::any_cast<cass_int8_t*>(value));
205}
206
207static CassError
208CqlBindInt16(const boost::any& value,
209 const size_t& index,
210 CassStatement* statement) {
211 return cass_statement_bind_int16(statement, index,
212 *boost::any_cast<cass_int16_t*>(value));
213}
214
215static CassError
216CqlBindInt32(const boost::any& value,
217 const size_t& index,
218 CassStatement* statement) {
219 return cass_statement_bind_int32(statement, index,
220 *boost::any_cast<cass_int32_t*>(value));
221}
222
223static CassError
224CqlBindInt64(const boost::any& value,
225 const size_t& index,
226 CassStatement* statement) {
227 return cass_statement_bind_int64(statement, index,
228 *boost::any_cast<cass_int64_t*>(value));
229}
230
231static CassError
232CqlBindString(const boost::any& value,
233 const size_t& index,
234 CassStatement* statement) {
235 return cass_statement_bind_string(
236 statement, index, boost::any_cast<std::string*>(value)->c_str());
237}
238
239static CassError
240CqlBindBytes(const boost::any& value,
241 const size_t& index,
242 CassStatement* statement) {
243 CassBlob* blob_value = boost::any_cast<CassBlob*>(value);
244 return cass_statement_bind_bytes(statement, index, blob_value->data(),
245 blob_value->size());
246}
247
248static CassError
249CqlBindUuid(const boost::any& value,
250 const size_t& index,
251 CassStatement* statement) {
252 return cass_statement_bind_uuid(statement, index,
253 *boost::any_cast<CassUuid*>(value));
254}
255
256static CassError
257CqlBindUdt(const boost::any& value,
258 const size_t& index,
259 CassStatement* statement) {
260 Udt* udt = boost::any_cast<Udt*>(value);
261
262 if (!udt) {
263 isc_throw(BadValue, "Invalid value specified, not an Udt object");
264 }
265
266 size_t i = 0u;
267
268 // Let's iterate over all elements in udt and check that we indeed
269 // can assign the set function for each specified type.
270 for (boost::any& element : *udt) {
271 try {
273 CQL_FUNCTIONS[exchangeType(element)].cqlUdtSetFunction_(
274 element, i, udt->cass_user_type_));
275 } catch (const boost::bad_any_cast& exception) {
276 isc_throw(DbOperationError,
277 "CqlCommon::udtSetData(): "
278 << exception.what() << " when binding parameter "
279 << " of type " << element.type().name()
280 << "in UDT with function CQL_FUNCTIONS["
281 << exchangeType(element) << "].cqlUdtSetFunction_");
282 }
283 ++i;
284 }
285
286 return cass_statement_bind_user_type(statement, index,
287 udt->cass_user_type_);
288}
289
290static CassError
291CqlBindCollection(const boost::any& value,
292 const size_t& index,
293 CassStatement* statement) {
294 AnyCollection* elements = boost::any_cast<AnyCollection*>(value);
295
296 CassCollection* collection =
297 cass_collection_new(CASS_COLLECTION_TYPE_SET, elements->size());
298
299 // Iterate over all elements and assign appropriate append function
300 // for each.
301 for (boost::any& element : *elements) {
302 ExchangeDataType type = exchangeType(element);
303 KEA_CASS_CHECK(CQL_FUNCTIONS[type].cqlCollectionAppendFunction_(
304 element, collection));
305 }
306
307 const CassError cass_error =
308 cass_statement_bind_collection(statement, index, collection);
309 cass_collection_free(collection);
310
311 return cass_error;
312}
314
318static CassError
319CqlUdtSetNone(const boost::any& /* udt_member */,
320 const size_t& position,
321 CassUserType* cass_user_type) {
322 return cass_user_type_set_null(cass_user_type, position);
323}
324
325static CassError
326CqlUdtSetBool(const boost::any& udt_member,
327 const size_t& position,
328 CassUserType* cass_user_type) {
329 return cass_user_type_set_bool(cass_user_type, position,
330 *boost::any_cast<cass_bool_t*>(udt_member));
331}
332
333static CassError
334CqlUdtSetInt8(const boost::any& udt_member,
335 const size_t& position,
336 CassUserType* cass_user_type) {
337 return cass_user_type_set_int8(cass_user_type, position,
338 *boost::any_cast<cass_int8_t*>(udt_member));
339}
340
341static CassError
342CqlUdtSetInt16(const boost::any& udt_member,
343 const size_t& position,
344 CassUserType* cass_user_type) {
345 return cass_user_type_set_int16(
346 cass_user_type, position, *boost::any_cast<cass_int16_t*>(udt_member));
347}
348
349static CassError
350CqlUdtSetInt32(const boost::any& udt_member,
351 const size_t& position,
352 CassUserType* cass_user_type) {
353 return cass_user_type_set_int32(
354 cass_user_type, position, *boost::any_cast<cass_int32_t*>(udt_member));
355}
356
357static CassError
358CqlUdtSetInt64(const boost::any& udt_member,
359 const size_t& position,
360 CassUserType* cass_user_type) {
361 return cass_user_type_set_int64(
362 cass_user_type, position, *boost::any_cast<cass_int64_t*>(udt_member));
363}
364
365static CassError
366CqlUdtSetString(const boost::any& udt_member,
367 const size_t& position,
368 CassUserType* cass_user_type) {
369 return cass_user_type_set_string(
370 cass_user_type, position,
371 boost::any_cast<std::string*>(udt_member)->c_str());
372}
373
374static CassError
375CqlUdtSetBytes(const boost::any& udt_member,
376 const size_t& position,
377 CassUserType* cass_user_type) {
378 CassBlob* blob_value = boost::any_cast<CassBlob*>(udt_member);
379 return cass_user_type_set_bytes(cass_user_type, position,
380 blob_value->data(), blob_value->size());
381}
382
383static CassError
384CqlUdtSetUuid(const boost::any& udt_member,
385 const size_t& position,
386 CassUserType* cass_user_type) {
387 return cass_user_type_set_uuid(cass_user_type, position,
388 *boost::any_cast<CassUuid*>(udt_member));
389}
390
391static CassError
392CqlUdtSetUdt(const boost::any& udt_member,
393 const size_t& position,
394 CassUserType* cass_user_type) {
395 return cass_user_type_set_user_type(
396 cass_user_type, position,
397 boost::any_cast<Udt*>(udt_member)->cass_user_type_);
398}
399
400static CassError
401CqlUdtSetCollection(const boost::any& udt_member,
402 const size_t& position,
403 CassUserType* cass_user_type) {
404 return cass_user_type_set_collection(
405 cass_user_type, position, boost::any_cast<CassCollection*>(udt_member));
406}
408
412static CassError
413CqlCollectionAppendNone(const boost::any& /* value */,
414 CassCollection* /* collection */) {
415 return CASS_OK;
416}
417
418static CassError
419CqlCollectionAppendBool(const boost::any& value, CassCollection* collection) {
420 return cass_collection_append_bool(collection,
421 *boost::any_cast<cass_bool_t*>(value));
422}
423
424static CassError
425CqlCollectionAppendInt8(const boost::any& value, CassCollection* collection) {
426 return cass_collection_append_int8(collection,
427 *boost::any_cast<cass_int8_t*>(value));
428}
429
430static CassError
431CqlCollectionAppendInt16(const boost::any& value, CassCollection* collection) {
432 return cass_collection_append_int16(collection,
433 *boost::any_cast<cass_int16_t*>(value));
434}
435
436static CassError
437CqlCollectionAppendInt32(const boost::any& value, CassCollection* collection) {
438 return cass_collection_append_int32(collection,
439 *boost::any_cast<cass_int32_t*>(value));
440}
441
442static CassError
443CqlCollectionAppendInt64(const boost::any& value, CassCollection* collection) {
444 return cass_collection_append_int64(collection,
445 *boost::any_cast<cass_int64_t*>(value));
446}
447
448static CassError
449CqlCollectionAppendString(const boost::any& value, CassCollection* collection) {
450 return cass_collection_append_string(
451 collection, boost::any_cast<std::string*>(value)->c_str());
452}
453
454static CassError
455CqlCollectionAppendBytes(const boost::any& value, CassCollection* collection) {
456 CassBlob* blob_value = boost::any_cast<CassBlob*>(value);
457 return cass_collection_append_bytes(collection, blob_value->data(),
458 blob_value->size());
459}
460
461static CassError
462CqlCollectionAppendUuid(const boost::any& value, CassCollection* collection) {
463 return cass_collection_append_uuid(collection,
464 *boost::any_cast<CassUuid*>(value));
465}
466
467static CassError
468CqlCollectionAppendUdt(const boost::any& value, CassCollection* collection) {
469 Udt* udt = boost::any_cast<Udt*>(value);
470 size_t i = 0u;
471 for (boost::any& element : *udt) {
472 KEA_CASS_CHECK(CQL_FUNCTIONS[exchangeType(element)].cqlUdtSetFunction_(
473 element, i, udt->cass_user_type_));
474 ++i;
475 }
476 return cass_collection_append_user_type(collection, udt->cass_user_type_);
477}
478
479static CassError
480CqlCollectionAppendCollection(const boost::any& value,
481 CassCollection* collection) {
482 return cass_collection_append_collection(
483 collection, boost::any_cast<CassCollection*>(value));
484}
485// @}
486
489static CassError
490CqlGetNone(const boost::any& /* data */, const CassValue* /* value */) {
491 return CASS_OK;
492}
493
494static CassError
495CqlGetBool(const boost::any& data, const CassValue* value) {
496 return cass_value_get_bool(value, boost::any_cast<cass_bool_t*>(data));
497}
498
499static CassError
500CqlGetInt8(const boost::any& data, const CassValue* value) {
501 return cass_value_get_int8(value, boost::any_cast<cass_int8_t*>(data));
502}
503
504static CassError
505CqlGetInt16(const boost::any& data, const CassValue* value) {
506 return cass_value_get_int16(value, boost::any_cast<cass_int16_t*>(data));
507}
508
509static CassError
510CqlGetInt32(const boost::any& data, const CassValue* value) {
511 return cass_value_get_int32(value, boost::any_cast<cass_int32_t*>(data));
512}
513
514static CassError
515CqlGetInt64(const boost::any& data, const CassValue* value) {
516 return cass_value_get_int64(value, boost::any_cast<cass_int64_t*>(data));
517}
518
519static CassError
520CqlGetString(const boost::any& data, const CassValue* value) {
521 char const* data_value;
522 size_t size_value;
523 CassError cass_error = cass_value_get_string(
524 value, static_cast<char const**>(&data_value), &size_value);
525 boost::any_cast<std::string*>(data)->assign(data_value,
526 data_value + size_value);
527 return cass_error;
528}
529
530static CassError
531CqlGetBytes(const boost::any& data, const CassValue* value) {
532 const cass_byte_t* data_value;
533 size_t size_value;
534 CassError cass_error = cass_value_get_bytes(
535 value, static_cast<const cass_byte_t**>(&data_value), &size_value);
536 boost::any_cast<CassBlob*>(data)->assign(data_value,
537 data_value + size_value);
538 return cass_error;
539}
540
541static CassError
542CqlGetUuid(const boost::any& data, const CassValue* value) {
543 return cass_value_get_uuid(value, boost::any_cast<CassUuid*>(data));
544}
545
546static CassError
547CqlGetUdt(const boost::any& data, const CassValue* value) {
548 Udt* udt = boost::any_cast<Udt*>(data);
549
550 CassIterator* fields = cass_iterator_fields_from_user_type(value);
551 if (!fields) {
552 isc_throw(DbOperationError, "CqlGetUdt(): column is not a UDT");
553 }
554 Udt::const_iterator it = udt->begin();
555 while (cass_iterator_next(fields)) {
556 const CassValue* field_value =
557 cass_iterator_get_user_type_field_value(fields);
558 if (cass_value_is_null(field_value)) {
559 isc_throw(DbOperationError,
560 "CqlGetUdt(): null value returned in UDT");
561 }
562 const CassValueType& type = cass_value_type(field_value);
563 KEA_CASS_CHECK(CQL_FUNCTIONS[exchangeType(type)].cqlGetFunction_(
564 *it, field_value));
565 ++it;
566 // If cqlGetFunction_() returns != CASS_OK, don't
567 // cass_iterator_free(items_iterator) because we're returning from this
568 // function and throwing from the callee.
569 }
570 cass_iterator_free(fields);
571 return CASS_OK;
572}
573
574static CassError
575CqlGetCollection(const boost::any& data, const CassValue* value) {
576 AnyCollection* collection = boost::any_cast<AnyCollection*>(data);
577 if (!collection) {
578 isc_throw(DbOperationError, "CqlGetCollection(): column is not a collection");
579 }
580
581 BOOST_ASSERT(collection->size() == 1);
582
585 boost::any underlying_object = *collection->begin();
586
587 collection->clear();
588
589 CassIterator* items = cass_iterator_from_collection(value);
590 if (!items) {
591 isc_throw(DbOperationError,
592 "CqlGetCollection(): column is not a collection");
593 }
594 while (cass_iterator_next(items)) {
595 const CassValue* item_value = cass_iterator_get_value(items);
596 if (cass_value_is_null(item_value)) {
597 isc_throw(DbOperationError,
598 "CqlGetCollection(): null value returned in collection");
599 }
600 const CassValueType& type = cass_value_type(item_value);
601
602 collection->push_back(underlying_object);
603 KEA_CASS_CHECK(CQL_FUNCTIONS[exchangeType(type)].cqlGetFunction_(
604 *collection->rbegin(), item_value));
605 // If cqlGetFunction_() returns != CASS_OK, don't call
606 // cass_iterator_free(items_iterator) because we're returning from this
607 // function and throwing from the callee.
608 }
609 cass_iterator_free(items);
610 return CASS_OK;
611}
613
617 {CqlBindNone, CqlUdtSetNone, CqlCollectionAppendNone, CqlGetNone}},
619 {CqlBindBool, CqlUdtSetBool, CqlCollectionAppendBool, CqlGetBool}},
621 {CqlBindInt8, CqlUdtSetInt8, CqlCollectionAppendInt8, CqlGetInt8}},
623 {CqlBindInt16, CqlUdtSetInt16, CqlCollectionAppendInt16, CqlGetInt16}},
625 {CqlBindInt32, CqlUdtSetInt32, CqlCollectionAppendInt32, CqlGetInt32}},
627 {CqlBindInt64, CqlUdtSetInt64, CqlCollectionAppendInt64, CqlGetInt64}},
629 {CqlBindString, CqlUdtSetString, CqlCollectionAppendString,
630 CqlGetString}},
632 {CqlBindBytes, CqlUdtSetBytes, CqlCollectionAppendBytes, CqlGetBytes}},
634 {CqlBindUuid, CqlUdtSetUuid, CqlCollectionAppendUuid, CqlGetUuid}},
636 {CqlBindUdt, CqlUdtSetUdt, CqlCollectionAppendUdt, CqlGetUdt}},
638 {CqlBindCollection, CqlUdtSetCollection, CqlCollectionAppendCollection,
639 CqlGetCollection}}};
640
642exchangeType(const boost::any& object) {
643 const std::type_index type = object.type();
644 AnyTypeMap::const_iterator exchange_type_it = ANY_TYPE_MAP.find(type);
645 if (exchange_type_it == ANY_TYPE_MAP.end()) {
647 "exchangeType(): boost::any type "
648 << type.name() << " does not map to any exchange type");
649 }
650 const ExchangeDataType exchange_type = exchange_type_it->second;
651 if (exchange_type >= CQL_FUNCTIONS.size()) {
653 "exchangeType(): index " << exchange_type << " out of bounds "
654 << 0 << " - "
655 << (CQL_FUNCTIONS.size() - 1));
656 }
657 return exchange_type;
658}
659
661exchangeType(const CassValueType& type) {
662 CassTypeMap::const_iterator exchange_type_it = CASS_TYPE_MAP.find(type);
663 if (exchange_type_it == CASS_TYPE_MAP.end()) {
665 "exchangeType(): Cassandra value type "
666 << type << " does not map to any exchange type");
667 }
668 const ExchangeDataType exchange_type = exchange_type_it->second;
669 if (exchange_type >= CQL_FUNCTIONS.size()) {
671 "exchangeType(): index " << exchange_type << " out of bounds "
672 << 0 << " - "
673 << CQL_FUNCTIONS.size() - 1);
674 }
675 return exchange_type;
676}
677
678void
679CqlCommon::bindData(const AnyArray& data, CassStatement* statement) {
680 size_t i = 0u;
681 for (const boost::any& element : data) {
682 CassError cass_error;
683 try {
684 cass_error = CQL_FUNCTIONS[exchangeType(element)].cqlBindFunction_(
685 element, i, statement);
686 } catch (const boost::bad_any_cast& exception) {
688 "CqlCommon::bindData(): "
689 << exception.what() << " when binding parameter " << i
690 << " which is of type " << element.type().name()
691 << " with function CQL_FUNCTIONS["
692 << exchangeType(element) << "].cqlBindFunction_()");
693 }
694 if (cass_error != CASS_OK) {
696 "CqlCommon::bindData(): unable to bind parameter "
697 << i << " which is of type " << element.type().name()
698 << " with function CQL_FUNCTIONS["
699 << exchangeType(element)
700 << "].cqlBindFunction_(), Cassandra error code: "
701 << cass_error_desc(cass_error));
702 }
703 ++i;
704 }
705}
706
707void
708CqlCommon::getData(const CassRow* row, AnyArray& data) {
709 size_t i = 0u;
710 for (boost::any& element : data) {
711 const CassValue* value = cass_row_get_column(row, i);
712 CassError cass_error;
713 try {
714 cass_error = CQL_FUNCTIONS[exchangeType(element)].cqlGetFunction_(
715 element, value);
716 } catch (const boost::bad_any_cast& exception) {
718 "CqlCommon::getData(): "
719 << exception.what() << " when retrieving parameter "
720 << i << " which is of type " << element.type().name()
721 << " with function CQL_FUNCTIONS["
722 << exchangeType(element) << "].cqlGetFunction_()");
723 }
724 if (cass_error != CASS_OK) {
725 isc_throw(
727 "CqlCommon::getData(): Cassandra error when retrieving column "
728 << i << ", Cassandra error code: "
729 << cass_error_desc(cass_error));
730 }
731 ++i;
732 }
733}
734
736}
737
739}
740
741void
743 const uint32_t& valid_lifetime,
744 cass_int64_t& expire) {
745 // Calculate expire time. Store it in the 64-bit value so as we can
746 // detect overflows.
747 cass_int64_t expire_time = static_cast<cass_int64_t>(cltt) +
748 static_cast<cass_int64_t>(valid_lifetime);
749
750 if (expire_time > DatabaseConnection::MAX_DB_TIME) {
752 "CqlExchange(): convertToDatabaseTime(): time value "
753 << expire_time << " is too large");
754 }
755
756 expire = expire_time;
757}
758
759void
760CqlExchange::convertFromDatabaseTime(const cass_int64_t& expire,
761 const cass_int64_t& valid_lifetime,
762 time_t& cltt) {
766
767 // Convert to local time
768 cltt = static_cast<time_t>(expire - valid_lifetime);
769}
770
772CqlExchange::executeSelect(const CqlConnection& connection, const AnyArray& data,
773 StatementTag statement_tag, const bool& single /* = false */) {
774 CassError rc;
775 CassStatement* statement = NULL;
776 CassFuture* future = NULL;
777 AnyArray local_data = data;
778
779 // Find the query statement first.
780 StatementMap::const_iterator it = connection.statements_.find(statement_tag);
781 if (it == connection.statements_.end()) {
783 "CqlExchange::executeSelect(): Statement "
784 << statement_tag << "has not been prepared.");
785 }
786
787 // Bind the data before the query is executed.
788 CqlTaggedStatement tagged_statement = it->second;
789 if (tagged_statement.is_raw_) {
790 // The entire query is the first element in data.
791 std::string* query = boost::any_cast<std::string*>(local_data.back());
792 local_data.pop_back();
793 statement = cass_statement_new(query->c_str(), local_data.size());
794 } else {
795 statement = cass_prepared_bind(tagged_statement.prepared_statement_);
796 if (!statement) {
798 "CqlExchange::executeSelect(): unable to bind statement "
799 << tagged_statement.name_);
800 }
801 }
802
803 // Set specific level of consistency if we're told to do so.
804 if (connection.force_consistency_) {
805 rc = cass_statement_set_consistency(statement, connection.consistency_);
806 if (rc != CASS_OK) {
807 cass_statement_free(statement);
809 "CqlExchange::executeSelect(): unable to set statement "
810 "consistency for statement "
811 << tagged_statement.name_
812 << ", Cassandra error code: " << cass_error_desc(rc));
813 }
814 }
815
816 CqlCommon::bindData(local_data, statement);
817
818 // Everything's ready. Call the actual statement.
819 future = cass_session_execute(connection.session_, statement);
820 if (!future) {
821 cass_statement_free(statement);
823 "CqlExchange::executeSelect(): no CassFuture for statement "
824 << tagged_statement.name_);
825 }
826
827 // Wait for the statement execution to complete.
828 cass_future_wait(future);
829 const std::string error = connection.checkFutureError(
830 "CqlExchange::executeSelect(): cass_session_execute() != CASS_OK",
831 future, statement_tag);
832 rc = cass_future_error_code(future);
833 if (rc != CASS_OK) {
834 cass_future_free(future);
835 cass_statement_free(statement);
837 }
838
839 // Get column values.
840 const CassResult* result_collection = cass_future_get_result(future);
841 if (single && cass_result_row_count(result_collection) > 1) {
842 cass_result_free(result_collection);
843 cass_future_free(future);
844 cass_statement_free(statement);
845 isc_throw(
847 "CqlExchange::executeSelect(): multiple records were found in "
848 "the database where only one was expected for statement "
849 << tagged_statement.name_);
850 }
851
852 // Get results.
853 AnyArray return_values;
854 AnyArray collection;
855 CassIterator* rows = cass_iterator_from_result(result_collection);
856 while (cass_iterator_next(rows)) {
857 const CassRow* row = cass_iterator_get_row(rows);
858 createBindForSelect(return_values, statement_tag);
859 CqlCommon::getData(row, return_values);
860 collection.add(retrieve());
861 }
862
863 // Free resources.
864 cass_iterator_free(rows);
865 cass_result_free(result_collection);
866 cass_future_free(future);
867 cass_statement_free(statement);
868
869 return collection;
870}
871
872void
874 StatementTag statement_tag) {
875 CassError rc;
876 CassStatement* statement = NULL;
877 CassFuture* future = NULL;
878
879 // Find the statement on a list of prepared statements.
880 StatementMap::const_iterator it =
881 connection.statements_.find(statement_tag);
882 if (it == connection.statements_.end()) {
883 isc_throw(DbOperationError, "CqlExchange::executeSelect(): Statement "
884 << statement_tag << "has not been prepared.");
885 }
886 // Bind the statement.
887 CqlTaggedStatement tagged_statement = it->second;
888 statement = cass_prepared_bind(tagged_statement.prepared_statement_);
889 if (!statement) {
891 "CqlExchange::executeMutation(): unable to bind statement "
892 << tagged_statement.name_);
893 }
894
895 // Set specific level of consistency, if told to do so.
896 if (connection.force_consistency_) {
897 rc = cass_statement_set_consistency(statement, connection.consistency_);
898 if (rc != CASS_OK) {
899 cass_statement_free(statement);
900 isc_throw(DbOperationError, "CqlExchange::executeMutation(): unable to set"
901 " statement consistency for statement " << tagged_statement.name_
902 << ", Cassandra error code: " << cass_error_desc(rc));
903 }
904 }
905
906 CqlCommon::bindData(data, statement);
907
908 future = cass_session_execute(connection.session_, statement);
909 if (!future) {
910 cass_statement_free(statement);
912 "CqlExchange::executeMutation(): unable to execute statement "
913 << tagged_statement.name_);
914 }
915 cass_future_wait(future);
916 const std::string error = connection.checkFutureError("CqlExchange::executeMutation():"
917 "cass_session_execute() != CASS_OK", future, statement_tag);
918 rc = cass_future_error_code(future);
919 if (rc != CASS_OK) {
920 cass_future_free(future);
921 cass_statement_free(statement);
923 }
924
925 // Check if statement has been applied.
926 bool applied = statementApplied(future);
927
928 // Free resources.
929 cass_future_free(future);
930 cass_statement_free(statement);
931
932 if (!applied) {
933 isc_throw(
935 "CqlExchange::executeMutation(): [applied] is false for statement "
936 << tagged_statement.name_);
937 }
938}
939
940bool
942 size_t* row_count,
943 size_t* column_count) {
944 const CassResult* result_collection = cass_future_get_result(future);
945 if (!result_collection) {
946 isc_throw(DbOperationError, "CqlExchange::statementApplied(): unable to get"
947 " results collection");
948 }
949 if (row_count) {
950 *row_count = cass_result_row_count(result_collection);
951 }
952 if (column_count) {
953 *column_count = cass_result_column_count(result_collection);
954 }
955 CassIterator* rows = cass_iterator_from_result(result_collection);
956 AnyArray data;
957 cass_bool_t applied = cass_true;
958 while (cass_iterator_next(rows)) {
959 const CassRow* row = cass_iterator_get_row(rows);
960 // [applied]: bool
961 data.add(&applied);
962 CqlCommon::getData(row, data);
963 }
964 cass_iterator_free(rows);
965 cass_result_free(result_collection);
966 return applied == cass_true;
967}
968
970
972 {GET_VERSION, {GET_VERSION, "SELECT version, minor FROM schema_version "}}
973};
974
976}
977
979}
980
981void
983 data.clear(); // Start with a fresh array.
984 data.add(&version_); // first column is a major version
985 data.add(&minor_); // second column is a minor version
986}
987
988boost::any
990 pair_ = VersionPair(version_, minor_);
991 return &pair_;
992}
993
996 // Run statement.
997 const AnyArray where_values;
998 AnyArray version_collection =
999 executeSelect(connection, where_values, GET_VERSION, true);
1000
1001 if (!version_collection.empty()) {
1002 return *boost::any_cast<VersionPair*>(*version_collection.begin());
1003 }
1004
1005 return VersionPair();
1006}
1007
1008} // namespace db
1009} // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Structure used to bind C++ input values to dynamic CQL parameters.
Definition: cql_exchange.h:50
void add(const boost::any &value)
Add a value at the end of the vector.
void remove(const size_t &index)
Remove the void pointer to the data value from a specified position inside the vector.
static void bindData(const AnyArray &data, CassStatement *statement)
Assigns values to every column of an INSERT or an UPDATE statement.
static void getData(const CassRow *row, AnyArray &data)
Retrieves data returned by Cassandra.
Common CQL connector pool.
StatementMap statements_
Pointer to external array of tagged statements containing statement name, array of names of bind para...
CassSession * session_
CQL session handle.
bool force_consistency_
CQL consistency enabled.
const CassKeyspaceMeta * keyspace_meta_
Keyspace meta information, used for UDTs.
CassConsistency consistency_
CQL consistency.
static const std::string checkFutureError(const std::string &what, CassFuture *future, StatementTag statement_tag=NULL)
Check for errors.
CqlExchange()
Constructor.
AnyArray executeSelect(const CqlConnection &connection, const AnyArray &where_values, StatementTag statement_tag, const bool &single=false)
Executes SELECT statements.
void executeMutation(const CqlConnection &connection, const AnyArray &assigned_values, StatementTag statement_tag)
Executes INSERT, UPDATE or DELETE statements.
virtual boost::any retrieve()=0
Copy received data into the derived class' object.
virtual ~CqlExchange()
Destructor.
virtual void createBindForSelect(AnyArray &data, StatementTag statement_tag=NULL)=0
Create BIND array to receive C++ data.
bool statementApplied(CassFuture *future, size_t *row_count=NULL, size_t *column_count=NULL)
Check if CQL statement has been applied.
static void convertFromDatabaseTime(const cass_int64_t &expire, const cass_int64_t &valid_lifetime, time_t &cltt)
Converts time from Cassandra format.
static void convertToDatabaseTime(const time_t &cltt, const uint32_t &valid_lifetime, cass_int64_t &expire)
virtual VersionPair retrieveVersion(const CqlConnection &connection)
Standalone method used to retrieve schema version.
static StatementMap tagged_statements_
Cassandra statements.
Definition: cql_exchange.h:280
virtual ~CqlVersionExchange()
Destructor.
static constexpr StatementTag GET_VERSION
Statement tags definitions.
Definition: cql_exchange.h:276
CqlVersionExchange()
Constructor.
virtual boost::any retrieve() override
Copy received data into the <version,minor> pair.
virtual void createBindForSelect(AnyArray &data, StatementTag statement_tag=NULL) override
Create BIND array to receive C++ data.
static const time_t MAX_DB_TIME
Defines maximum value for time that can be reliably stored.
Exception thrown on failure to execute a database function.
Multiple lease records found where one expected.
Definition: db_exceptions.h:28
Database statement not applied.
Definition: db_exceptions.h:20
Udt(const CqlConnection &connection, const std::string &name)
Parameterized constructor.
const CassDataType * cass_data_type_
Internal Cassandra driver object representing a Cassandra data type.
Definition: cql_exchange.h:83
const CqlConnection & connection_
Connection to the Cassandra database.
Definition: cql_exchange.h:76
CassUserType * cass_user_type_
Internal Cassandra driver object representing a user defined type.
Definition: cql_exchange.h:86
~Udt()
Destructor.
const std::string name_
Name of the UDT in the schema: CREATE TYPE ___ { ... }.
Definition: cql_exchange.h:79
#define KEA_CASS_CHECK(cass_error)
Macro to return directly from caller function.
Definition: cql_exchange.cc:44
const Name & name_
Definition: dns/message.cc:693
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
AnyArray AnyCollection
Defines an array of arbitrary objects (used by Cassandra backend)
Definition: cql_exchange.h:90
char const *const StatementTag
Statement index representing the statement name.
std::unordered_map< std::type_index, ExchangeDataType > AnyTypeMap
Map types used to determine exchange type.
Definition: cql_exchange.cc:81
CqlFunctionMap CQL_FUNCTIONS
Functions used to interface with the Cassandra C++ driver.
std::pair< uint32_t, uint32_t > VersionPair
Pair containing major and minor versions.
ExchangeDataType
Used to map server data types with internal backend storage data types.
Definition: sql_common.h:26
@ EXCHANGE_DATA_TYPE_INT64
Definition: sql_common.h:32
@ EXCHANGE_DATA_TYPE_STRING
Definition: sql_common.h:34
@ EXCHANGE_DATA_TYPE_UDT
User-Defined Type (used in Cassandra)
Definition: sql_common.h:37
@ EXCHANGE_DATA_TYPE_BOOL
Definition: sql_common.h:28
@ EXCHANGE_DATA_TYPE_NONE
Definition: sql_common.h:27
@ EXCHANGE_DATA_TYPE_INT16
Definition: sql_common.h:30
@ EXCHANGE_DATA_TYPE_BYTES
Definition: sql_common.h:35
@ EXCHANGE_DATA_TYPE_INT8
Definition: sql_common.h:29
@ EXCHANGE_DATA_TYPE_INT32
Definition: sql_common.h:31
@ EXCHANGE_DATA_TYPE_COLLECTION
Collection (used in Cassandra)
Definition: sql_common.h:38
@ EXCHANGE_DATA_TYPE_UUID
Definition: sql_common.h:36
std::unordered_map< uint8_t, ExchangeDataType > CassTypeMap
Definition: cql_exchange.cc:85
std::unordered_map< ExchangeDataType, CqlFunction, ExchangeDataTypeHash > CqlFunctionMap
Defines a type for storing aux. Cassandra functions.
Definition: cql_exchange.cc:62
std::size_t hash_value(const CassValueType &key)
hash function for CassTypeMap
Definition: cql_exchange.cc:73
std::vector< cass_byte_t > CassBlob
Host identifier converted to Cassandra data type.
Definition: cql_exchange.h:37
std::unordered_map< StatementTag, CqlTaggedStatement, StatementTagHash, StatementTagEqual > StatementMap
A container for all statements.
ExchangeDataType exchangeType(const boost::any &object)
Determine exchange type based on boost::any type.
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.
bool is_raw_
Should the statement be executed raw or with binds?
const CassPrepared * prepared_statement_
Internal Cassandra object representing the prepared statement.
a helper structure with a function call operator that returns key value in a format expected by std::...
Definition: cql_exchange.cc:53
size_t operator()(const ExchangeDataType &key) const
Definition: cql_exchange.cc:55