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> 44 #define KEA_CASS_CHECK(cass_error) \ 46 if (cass_error != CASS_OK) { \ 56 return std::hash<size_t>{}(static_cast<size_t>(key));
61 typedef std::unordered_map<ExchangeDataType, CqlFunction, ExchangeDataTypeHash>
81 typedef std::unordered_map<std::type_index, ExchangeDataType>
AnyTypeMap;
85 typedef std::unordered_map<uint8_t, ExchangeDataType>
CassTypeMap;
140 "Udt::Udt(): UDT " <<
name_ <<
" does not exist ");
146 "Udt::Udt(): Type " <<
name_ 147 <<
" is not a UDT as expected. ");
171 if (size() <= index) {
173 "AnyArray::remove(): index " 174 << index <<
" out of bounds: [0, " << (size() - 1)
177 erase(begin() + index);
185 CqlBindNone(
const boost::any& ,
187 CassStatement* statement) {
188 return cass_statement_bind_null(statement, index);
192 CqlBindBool(
const boost::any& value,
194 CassStatement* statement) {
195 return cass_statement_bind_bool(statement, index,
196 *boost::any_cast<cass_bool_t*>(value));
200 CqlBindInt8(
const boost::any& value,
202 CassStatement* statement) {
203 return cass_statement_bind_int8(statement, index,
204 *boost::any_cast<cass_int8_t*>(value));
208 CqlBindInt16(
const boost::any& value,
210 CassStatement* statement) {
211 return cass_statement_bind_int16(statement, index,
212 *boost::any_cast<cass_int16_t*>(value));
216 CqlBindInt32(
const boost::any& value,
218 CassStatement* statement) {
219 return cass_statement_bind_int32(statement, index,
220 *boost::any_cast<cass_int32_t*>(value));
224 CqlBindInt64(
const boost::any& value,
226 CassStatement* statement) {
227 return cass_statement_bind_int64(statement, index,
228 *boost::any_cast<cass_int64_t*>(value));
232 CqlBindString(
const boost::any& value,
234 CassStatement* statement) {
235 return cass_statement_bind_string(
236 statement, index, boost::any_cast<std::string*>(value)->c_str());
240 CqlBindBytes(
const boost::any& value,
242 CassStatement* statement) {
243 CassBlob* blob_value = boost::any_cast<CassBlob*>(value);
244 return cass_statement_bind_bytes(statement, index, blob_value->data(),
249 CqlBindUuid(
const boost::any& value,
251 CassStatement* statement) {
252 return cass_statement_bind_uuid(statement, index,
253 *boost::any_cast<CassUuid*>(value));
257 CqlBindUdt(
const boost::any& value,
259 CassStatement* statement) {
260 Udt* udt = boost::any_cast<Udt*>(value);
263 isc_throw(BadValue,
"Invalid value specified, not an Udt object");
270 for (boost::any& element : *udt) {
274 element, i, udt->cass_user_type_));
275 }
catch (
const boost::bad_any_cast& exception) {
277 "CqlCommon::udtSetData(): " 278 << exception.what() <<
" when binding parameter " 279 <<
" of type " << element.type().name()
280 <<
"in UDT with function CQL_FUNCTIONS[" 286 return cass_statement_bind_user_type(statement, index,
287 udt->cass_user_type_);
291 CqlBindCollection(
const boost::any& value,
293 CassStatement* statement) {
294 AnyCollection* elements = boost::any_cast<AnyCollection*>(value);
296 CassCollection* collection =
297 cass_collection_new(CASS_COLLECTION_TYPE_SET, elements->size());
301 for (boost::any& element : *elements) {
304 element, collection));
307 const CassError cass_error =
308 cass_statement_bind_collection(statement, index, collection);
309 cass_collection_free(collection);
319 CqlUdtSetNone(
const boost::any& ,
320 const size_t& position,
321 CassUserType* cass_user_type) {
322 return cass_user_type_set_null(cass_user_type, position);
326 CqlUdtSetBool(
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));
334 CqlUdtSetInt8(
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));
342 CqlUdtSetInt16(
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));
350 CqlUdtSetInt32(
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));
358 CqlUdtSetInt64(
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));
366 CqlUdtSetString(
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());
375 CqlUdtSetBytes(
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());
384 CqlUdtSetUuid(
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));
392 CqlUdtSetUdt(
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_);
401 CqlUdtSetCollection(
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));
413 CqlCollectionAppendNone(
const boost::any& ,
419 CqlCollectionAppendBool(
const boost::any& value, CassCollection* collection) {
420 return cass_collection_append_bool(collection,
421 *boost::any_cast<cass_bool_t*>(value));
425 CqlCollectionAppendInt8(
const boost::any& value, CassCollection* collection) {
426 return cass_collection_append_int8(collection,
427 *boost::any_cast<cass_int8_t*>(value));
431 CqlCollectionAppendInt16(
const boost::any& value, CassCollection* collection) {
432 return cass_collection_append_int16(collection,
433 *boost::any_cast<cass_int16_t*>(value));
437 CqlCollectionAppendInt32(
const boost::any& value, CassCollection* collection) {
438 return cass_collection_append_int32(collection,
439 *boost::any_cast<cass_int32_t*>(value));
443 CqlCollectionAppendInt64(
const boost::any& value, CassCollection* collection) {
444 return cass_collection_append_int64(collection,
445 *boost::any_cast<cass_int64_t*>(value));
449 CqlCollectionAppendString(
const boost::any& value, CassCollection* collection) {
450 return cass_collection_append_string(
451 collection, boost::any_cast<std::string*>(value)->c_str());
455 CqlCollectionAppendBytes(
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(),
462 CqlCollectionAppendUuid(
const boost::any& value, CassCollection* collection) {
463 return cass_collection_append_uuid(collection,
464 *boost::any_cast<CassUuid*>(value));
468 CqlCollectionAppendUdt(
const boost::any& value, CassCollection* collection) {
469 Udt* udt = boost::any_cast<Udt*>(value);
471 for (boost::any& element : *udt) {
473 element, i, udt->cass_user_type_));
476 return cass_collection_append_user_type(collection, udt->cass_user_type_);
480 CqlCollectionAppendCollection(
const boost::any& value,
481 CassCollection* collection) {
482 return cass_collection_append_collection(
483 collection, boost::any_cast<CassCollection*>(value));
490 CqlGetNone(
const boost::any& ,
const CassValue* ) {
495 CqlGetBool(
const boost::any& data,
const CassValue* value) {
496 return cass_value_get_bool(value, boost::any_cast<cass_bool_t*>(data));
500 CqlGetInt8(
const boost::any& data,
const CassValue* value) {
501 return cass_value_get_int8(value, boost::any_cast<cass_int8_t*>(data));
505 CqlGetInt16(
const boost::any& data,
const CassValue* value) {
506 return cass_value_get_int16(value, boost::any_cast<cass_int16_t*>(data));
510 CqlGetInt32(
const boost::any& data,
const CassValue* value) {
511 return cass_value_get_int32(value, boost::any_cast<cass_int32_t*>(data));
515 CqlGetInt64(
const boost::any& data,
const CassValue* value) {
516 return cass_value_get_int64(value, boost::any_cast<cass_int64_t*>(data));
520 CqlGetString(
const boost::any& data,
const CassValue* value) {
521 char const* data_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);
531 CqlGetBytes(
const boost::any& data,
const CassValue* value) {
532 const cass_byte_t* data_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);
542 CqlGetUuid(
const boost::any& data,
const CassValue* value) {
543 return cass_value_get_uuid(value, boost::any_cast<CassUuid*>(data));
547 CqlGetUdt(
const boost::any& data,
const CassValue* value) {
548 Udt* udt = boost::any_cast<Udt*>(data);
550 CassIterator* fields = cass_iterator_fields_from_user_type(value);
552 isc_throw(DbOperationError,
"CqlGetUdt(): column is not a UDT");
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)) {
560 "CqlGetUdt(): null value returned in UDT");
562 const CassValueType& type = cass_value_type(field_value);
570 cass_iterator_free(fields);
575 CqlGetCollection(
const boost::any& data,
const CassValue* value) {
576 AnyCollection* collection = boost::any_cast<AnyCollection*>(data);
578 isc_throw(DbOperationError,
"CqlGetCollection(): column is not a collection");
581 BOOST_ASSERT(collection->size() == 1);
585 boost::any underlying_object = *collection->begin();
589 CassIterator* items = cass_iterator_from_collection(value);
592 "CqlGetCollection(): column is not a collection");
594 while (cass_iterator_next(items)) {
595 const CassValue* item_value = cass_iterator_get_value(items);
596 if (cass_value_is_null(item_value)) {
598 "CqlGetCollection(): null value returned in collection");
600 const CassValueType& type = cass_value_type(item_value);
602 collection->push_back(underlying_object);
604 *collection->rbegin(), item_value));
609 cass_iterator_free(items);
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,
632 {CqlBindBytes, CqlUdtSetBytes, CqlCollectionAppendBytes, CqlGetBytes}},
634 {CqlBindUuid, CqlUdtSetUuid, CqlCollectionAppendUuid, CqlGetUuid}},
636 {CqlBindUdt, CqlUdtSetUdt, CqlCollectionAppendUdt, CqlGetUdt}},
638 {CqlBindCollection, CqlUdtSetCollection, CqlCollectionAppendCollection,
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");
653 "exchangeType(): index " << exchange_type <<
" out of bounds " 657 return exchange_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");
671 "exchangeType(): index " << exchange_type <<
" out of bounds " 675 return exchange_type;
681 for (
const boost::any& element : data) {
682 CassError cass_error;
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[" 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[" 700 <<
"].cqlBindFunction_(), Cassandra error code: " 701 << cass_error_desc(cass_error));
710 for (boost::any& element : data) {
711 const CassValue* value = cass_row_get_column(row, i);
712 CassError cass_error;
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[" 724 if (cass_error != CASS_OK) {
727 "CqlCommon::getData(): Cassandra error when retrieving column " 728 << i <<
", Cassandra error code: " 729 << cass_error_desc(cass_error));
743 const uint32_t& valid_lifetime,
744 cass_int64_t& expire) {
747 cass_int64_t expire_time = static_cast<cass_int64_t>(cltt) +
748 static_cast<cass_int64_t>(valid_lifetime);
752 "CqlExchange(): convertToDatabaseTime(): time value " 753 << expire_time <<
" is too large");
756 expire = expire_time;
761 const cass_int64_t& valid_lifetime,
768 cltt = static_cast<time_t>(expire - valid_lifetime);
775 CassStatement* statement = NULL;
776 CassFuture* future = NULL;
780 StatementMap::const_iterator it = connection.
statements_.find(statement_tag);
783 "CqlExchange::executeSelect(): Statement " 784 << statement_tag <<
"has not been prepared.");
789 if (tagged_statement.
is_raw_) {
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());
798 "CqlExchange::executeSelect(): unable to bind statement " 799 << tagged_statement.
name_);
805 rc = cass_statement_set_consistency(statement, connection.
consistency_);
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));
819 future = cass_session_execute(connection.
session_, statement);
821 cass_statement_free(statement);
823 "CqlExchange::executeSelect(): no CassFuture for statement " 824 << tagged_statement.
name_);
828 cass_future_wait(future);
830 "CqlExchange::executeSelect(): cass_session_execute() != CASS_OK",
831 future, statement_tag);
832 rc = cass_future_error_code(future);
834 cass_future_free(future);
835 cass_statement_free(statement);
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);
847 "CqlExchange::executeSelect(): multiple records were found in " 848 "the database where only one was expected for statement " 849 << tagged_statement.
name_);
855 CassIterator* rows = cass_iterator_from_result(result_collection);
856 while (cass_iterator_next(rows)) {
857 const CassRow* row = cass_iterator_get_row(rows);
864 cass_iterator_free(rows);
865 cass_result_free(result_collection);
866 cass_future_free(future);
867 cass_statement_free(statement);
876 CassStatement* statement = NULL;
877 CassFuture* future = NULL;
880 StatementMap::const_iterator it =
884 << statement_tag <<
"has not been prepared.");
891 "CqlExchange::executeMutation(): unable to bind statement " 892 << tagged_statement.
name_);
897 rc = cass_statement_set_consistency(statement, connection.
consistency_);
899 cass_statement_free(statement);
901 " statement consistency for statement " << tagged_statement.
name_ 902 <<
", Cassandra error code: " << cass_error_desc(rc));
908 future = cass_session_execute(connection.
session_, statement);
910 cass_statement_free(statement);
912 "CqlExchange::executeMutation(): unable to execute statement " 913 << tagged_statement.
name_);
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);
920 cass_future_free(future);
921 cass_statement_free(statement);
929 cass_future_free(future);
930 cass_statement_free(statement);
935 "CqlExchange::executeMutation(): [applied] is false for statement " 936 << tagged_statement.
name_);
943 size_t* column_count) {
944 const CassResult* result_collection = cass_future_get_result(future);
945 if (!result_collection) {
947 " results collection");
950 *row_count = cass_result_row_count(result_collection);
953 *column_count = cass_result_column_count(result_collection);
955 CassIterator* rows = cass_iterator_from_result(result_collection);
957 cass_bool_t applied = cass_true;
958 while (cass_iterator_next(rows)) {
959 const CassRow* row = cass_iterator_get_row(rows);
964 cass_iterator_free(rows);
965 cass_result_free(result_collection);
966 return applied == cass_true;
972 {GET_VERSION, {GET_VERSION,
"SELECT version, minor FROM schema_version "}}
1001 if (!version_collection.empty()) {
1002 return *boost::any_cast<VersionPair*>(*version_collection.begin());
Database statement not applied.
#define KEA_CASS_CHECK(cass_error)
Macro to return directly from caller function.
StatementMap statements_
Pointer to external array of tagged statements containing statement name, array of names of bind para...
void executeMutation(const CqlConnection &connection, const AnyArray &assigned_values, StatementTag statement_tag)
Executes INSERT, UPDATE or DELETE statements.
virtual void createBindForSelect(AnyArray &data, StatementTag statement_tag=NULL) override
Create BIND array to receive C++ data.
ExchangeDataType exchangeType(const boost::any &object)
Determine exchange type based on boost::any type.
const CassDataType * cass_data_type_
Internal Cassandra driver object representing a Cassandra data type.
Structure used to bind C++ input values to dynamic CQL parameters.
StatementTag name_
Short description of the query.
const CassPrepared * prepared_statement_
Internal Cassandra object representing the prepared statement.
std::unordered_map< std::type_index, ExchangeDataType > AnyTypeMap
Map types used to determine exchange type.
bool force_consistency_
CQL consistency enabled.
static constexpr StatementTag GET_VERSION
Statement tags definitions.
virtual ~CqlVersionExchange()
Destructor.
static void getData(const CassRow *row, AnyArray &data)
Retrieves data returned by Cassandra.
const CqlConnection & connection_
Connection to the Cassandra database.
std::unordered_map< ExchangeDataType, CqlFunction, ExchangeDataTypeHash > CqlFunctionMap
Defines a type for storing aux. Cassandra functions.
static const time_t MAX_DB_TIME
Defines maximum value for time that can be reliably stored.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
CqlFunctionMap CQL_FUNCTIONS
Functions used to interface with the Cassandra C++ driver.
char const *const StatementTag
Statement index representing the statement name.
const CassKeyspaceMeta * keyspace_meta_
Keyspace meta information, used for UDTs.
bool statementApplied(CassFuture *future, size_t *row_count=NULL, size_t *column_count=NULL)
Check if CQL statement has been applied.
Multiple lease records found where one expected.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
CassUserType * cass_user_type_
Internal Cassandra driver object representing a user defined type.
Defines a single statement or query.
void add(const boost::any &value)
Add a value at the end of the vector.
static void convertToDatabaseTime(const time_t &cltt, const uint32_t &valid_lifetime, cass_int64_t &expire)
CassConsistency consistency_
CQL consistency.
virtual boost::any retrieve() override
Copy received data into the <version,minor> pair.
std::vector< cass_byte_t > CassBlob
Host identifier converted to Cassandra data type.
CassSession * session_
CQL session handle.
size_t operator()(const ExchangeDataType &key) const
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.
Udt(const CqlConnection &connection, const std::string &name)
Parameterized constructor.
const std::string name_
Name of the UDT in the schema: CREATE TYPE ___ { ... }.
Collection (used in Cassandra)
Defines the logger used by the top-level component of kea-dhcp-ddns.
static void convertFromDatabaseTime(const cass_int64_t &expire, const cass_int64_t &valid_lifetime, time_t &cltt)
Converts time from Cassandra format.
std::unordered_map< uint8_t, ExchangeDataType > CassTypeMap
static void bindData(const AnyArray &data, CassStatement *statement)
Assigns values to every column of an INSERT or an UPDATE statement.
std::unordered_map< StatementTag, CqlTaggedStatement, StatementTagHash, StatementTagEqual > StatementMap
A container for all statements.
AnyArray executeSelect(const CqlConnection &connection, const AnyArray &where_values, StatementTag statement_tag, const bool &single=false)
Executes SELECT statements.
void remove(const size_t &index)
Remove the void pointer to the data value from a specified position inside the vector.
a helper structure with a function call operator that returns key value in a format expected by std::...
virtual boost::any retrieve()=0
Copy received data into the derived class' object.
virtual ~CqlExchange()
Destructor.
std::size_t hash_value(const CassValueType &key)
hash function for CassTypeMap
static StatementMap tagged_statements_
Cassandra statements.
Common CQL connector pool.
static const std::string checkFutureError(const std::string &what, CassFuture *future, StatementTag statement_tag=NULL)
Check for errors.
User-Defined Type (used in Cassandra)
virtual VersionPair retrieveVersion(const CqlConnection &connection)
Standalone method used to retrieve schema version.
Exception thrown on failure to execute a database function.
bool is_raw_
Should the statement be executed raw or with binds?
CqlExchange()
Constructor.
CqlVersionExchange()
Constructor.
AnyArray AnyCollection
Defines an array of arbitrary objects (used by Cassandra backend)
virtual void createBindForSelect(AnyArray &data, StatementTag statement_tag=NULL)=0
Create BIND array to receive C++ data.