From: Razvan Becheriu Date: Mon, 4 Jul 2016 09:52:45 +0000 (+0300) Subject: refactored cassandra backend and fixed issues X-Git-Tag: kea5574_base~10^2~1^2~43 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c164ebc2375f5bd4b1fda1fa64a0ffdda62d13a9;p=thirdparty%2Fkea.git refactored cassandra backend and fixed issues --- diff --git a/src/lib/cfgrpt/tests/.gitignore b/src/lib/cfgrpt/tests/.gitignore new file mode 100644 index 0000000000..d6d1ec87a1 --- /dev/null +++ b/src/lib/cfgrpt/tests/.gitignore @@ -0,0 +1 @@ +/run_unittests diff --git a/src/lib/dhcpsrv/cql_exchange.cc b/src/lib/dhcpsrv/cql_exchange.cc new file mode 100644 index 0000000000..b1be4b4336 --- /dev/null +++ b/src/lib/dhcpsrv/cql_exchange.cc @@ -0,0 +1,155 @@ +#include +/// @name CqlBind auxiliary methods for binding data into Cassandra format: +/// @{ + +/// @todo These void* cast are unsafe. See ticket #4525. +static CassError CqlBindNone(CassStatement* statement, size_t index, + void*) { + return cass_statement_bind_null(statement, index); +} + +static CassError CqlBindBool(CassStatement* statement, size_t index, + void* value) { + return cass_statement_bind_bool(statement, index, + *(static_cast(value))); +} + +static CassError CqlBindInt32(CassStatement* statement, size_t index, + void* value) { + return cass_statement_bind_int32(statement, index, + *(static_cast(value))); +} + +static CassError CqlBindInt64(CassStatement* statement, size_t index, + void* value) { + return cass_statement_bind_int64(statement, index, + *(static_cast(value))); +} + +static CassError CqlBindTimestamp(CassStatement* statement, size_t index, + void* value) { + return cass_statement_bind_int64(statement, index, + *(static_cast(value))); +} + +static CassError CqlBindString(CassStatement* statement, size_t index, + void* value) { + return cass_statement_bind_string(statement, index, + static_cast(value)->c_str()); +} + +static CassError CqlBindBytes(CassStatement* statement, size_t index, + void* value) { + return cass_statement_bind_bytes(statement, index, + static_cast*>(value)->data(), + static_cast*>(value)->size()); +} +/// @} + +static CassError CqlGetNone(const CassValue*, void*) { + return CASS_OK; +} + +static CassError CqlGetBool(const CassValue* value, void* data) { + return cass_value_get_bool(value, static_cast(data)); +} + +static CassError CqlGetInt32(const CassValue* value, void* data) { + return cass_value_get_int32(value, static_cast(data)); +} + +static CassError CqlGetInt64(const CassValue* value, void* data) { + return cass_value_get_int64(value, static_cast(data)); +} + +static CassError CqlGetTimestamp(const CassValue* value, void* data) { + return cass_value_get_int64(value, static_cast(data)); +} + +static CassError CqlGetString(const CassValue* value, void* data) { + const char* dataValue; + size_t sizeValue; + CassError cassError = cass_value_get_string(value, + static_cast(&dataValue), &sizeValue); + static_cast(data)->assign(dataValue, dataValue + sizeValue); + return cassError; +} + +static CassError CqlGetBytes(const CassValue* value, void* data) { + const cass_byte_t* dataValue; + size_t sizeValue; + CassError cassError = cass_value_get_bytes(value, + static_cast(&dataValue), &sizeValue); + static_cast*>(data)->assign(dataValue, dataValue + + sizeValue); + return cassError; +} + +namespace isc { +namespace dhcp { + +struct CqlFunctionData CqlFunctions[] = { + {CqlBindNone, CqlGetNone}, + {CqlBindBool, CqlGetBool}, + {CqlBindInt32, CqlGetInt32}, + {CqlBindInt64, CqlGetInt64}, + {CqlBindTimestamp, CqlGetTimestamp}, + {CqlBindString, CqlGetString}, + {CqlBindBytes, CqlGetBytes} +}; + +ExchangeDataType +CqlCommon::getDataType(const uint32_t stindex, int pindex, + const SqlExchange& exchange, CqlTaggedStatement* tagged_statements) { + if (tagged_statements[stindex].params_ && + tagged_statements[stindex].params_[pindex]) { + const ExchangeColumnInfoContainerName& idx = exchange.parameters_.get<1>(); + const ExchangeColumnInfoContainerNameRange& range = + idx.equal_range(tagged_statements[stindex].params_[pindex]); + if (std::distance(range.first, range.second) > 0) { + return (*range.first)->type_; + } + } + return EXCHANGE_DATA_TYPE_NONE; +} + +void +CqlCommon::bindData(CassStatement* statement, const uint32_t stindex, + CqlDataArray& data, const SqlExchange& exchange, CqlTaggedStatement* tagged_statements) { + if (tagged_statements[stindex].params_ == NULL) { + return; + } + for (int i = 0; tagged_statements[stindex].params_[i]; i++) { + ExchangeDataType type = CqlCommon::getDataType(stindex, i, exchange, tagged_statements); + if (type >= sizeof(CqlFunctions) / sizeof(CqlFunctions[0])) { + isc_throw(BadValue, "index " << stindex << " out of bounds"); + } + CqlFunctions[type].sqlBindFunction_(statement, i, data.values_[i]); + } +} + +void +CqlCommon::getData(const CassRow* row, const int pindex, CqlDataArray& data, + const int dindex, const SqlExchange& exchange) { + const CassValue* value; + if (pindex >= exchange.parameters_.size()) { + return; + } + const ExchangeColumnInfoContainerIndex& idx = exchange.parameters_.get<2>(); + const ExchangeColumnInfoContainerIndexRange& range = idx.equal_range(pindex); + if (std::distance(range.first, range.second) > 0) { + std::string name = (*range.first)->name_; + ExchangeDataType type = (*range.first)->type_; + value = cass_row_get_column_by_name(row, name.c_str()); + if (NULL == value) { + isc_throw(BadValue, "column name " << name << " doesn't exist"); + } + if (type >= sizeof(CqlFunctions) / sizeof(CqlFunctions[0])) { + isc_throw(BadValue, "index " << type << " out of bounds"); + } + CqlFunctions[type].sqlGetFunction_(value, data.values_[dindex]); + } +} + +}; // end of isc::dhcp namespace +}; // end of isc namespace diff --git a/src/lib/dhcpsrv/cql_exchange.h b/src/lib/dhcpsrv/cql_exchange.h new file mode 100644 index 0000000000..ab78b417f0 --- /dev/null +++ b/src/lib/dhcpsrv/cql_exchange.h @@ -0,0 +1,128 @@ +#ifndef CQL_EXCHANGE_H +#define CQL_EXCHANGE_H + +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief Defines a single query +/// +/// @param params_ Bind parameter names +/// @param name_ Short name of the query. +/// @param text_ Text representation of the actual query. +struct CqlTaggedStatement { + const char** params_; + const char* name_; + const char* text_; +}; + +typedef CassError (*CqlBindFunction)(CassStatement* statement, size_t index, + void* value); +typedef CassError (*CqlGetFunction)(const CassValue* value, void* data); + +struct CqlFunctionData { + CqlBindFunction sqlBindFunction_; + CqlGetFunction sqlGetFunction_; +}; + +extern struct CqlFunctionData CqlFunctions[]; + +/// @brief Structure used to bind C++ input values to dynamic SQL parameters +/// The structure contains a vector which store the input values, +/// This 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 to values that go out of scope +/// before statement is executed is a bad idea. +struct CqlDataArray { + /// Add void pointer to a vector of pointers to the data values. + std::vector values_; + void add(void* value) { + values_.push_back(value); + } + /// Remove void pointer from a vector of pointers to the data values. + void remove(int index) { + if (values_.size() <= index) { + isc_throw(BadValue, "Index " << index << " out of bounds: [0, " << + (values_.size() - 1) << "]"); + } + values_.erase(values_.begin() + index); + } + /// Get size. + size_t size() { + return values_.size(); + } +}; + +class CqlCommon { +public: + /// @brief Returns type of data for specific parameter. + /// + /// Returns type of a given parameter of a given statement. + /// + /// @param stindex Index of statement being executed. + /// @param pindex Index of the parameter for a given statement. + /// @param exchange Exchange object to use + /// @param tagged_statements CqlTaggedStatement array to use + static ExchangeDataType + getDataType(const uint32_t stindex, int pindex, + const SqlExchange& exchange, CqlTaggedStatement* tagged_statements); + + /// @brief Binds data specified in data + /// + /// This method calls one of cass_value_bind_{none,bool,int32,int64,string,bytes}. + /// It is used to bind C++ data types used by Kea into formats used by Cassandra. + /// + /// @param statement Statement object representing the query + /// @param stindex Index of statement being executed + /// @param data array that has been created for the type of lease in question. + /// @param exchange Exchange object to use + /// @param tagged_statements CqlTaggedStatement array to use + static void + bindData(CassStatement* statement, const uint32_t stindex, + CqlDataArray& data, const SqlExchange& exchange, CqlTaggedStatement* tagged_statements); + + /// @brief Retrieves data returned by Cassandra. + /// + /// This method calls one of cass_value_get_{none,bool,int32,int64,string,bytes}. + /// It is used to retrieve data returned by Cassandra into standard C++ types + /// used by Kea. + /// + /// @param row row of data returned by CQL library + /// @param pindex Index of statement being executed + /// @param data array that has been created for the type of lease in question. + /// @param size a structure that holds information about size + /// @param dindex data index (specifies which entry in size array is used) + /// @param exchange Exchange object to use + static void + getData(const CassRow* row, const int pindex, CqlDataArray& data, + const int dindex, const SqlExchange& exchange); +}; + +class CqlVersionExchange : public virtual SqlExchange { +public: + /// @brief Constructor + /// + /// The initialization of the variables here is only to satisfy cppcheck - + /// all variables are initialized/set in the methods before they are used. + CqlVersionExchange() { + // Set the column names + const size_t MAX_COLUMNS = 2U; + parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("version", + parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); + parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("minor", + parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); + BOOST_ASSERT(parameters_.size() == MAX_COLUMNS); + } +}; + +}; // end of isc::dhcp namespace +}; // end of isc namespace + +#endif // CQL_EXCHANGE_H diff --git a/src/lib/dhcpsrv/sql_common.h b/src/lib/dhcpsrv/sql_common.h new file mode 100644 index 0000000000..82c2e63d79 --- /dev/null +++ b/src/lib/dhcpsrv/sql_common.h @@ -0,0 +1,112 @@ +#ifndef SQL_COMMON_H +#define SQL_COMMON_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + + +/// @brief Used to map server data types with internal backend storage data +/// types. +enum ExchangeDataType { + EXCHANGE_DATA_TYPE_NONE, + EXCHANGE_DATA_TYPE_BOOL, + EXCHANGE_DATA_TYPE_INT32, + EXCHANGE_DATA_TYPE_INT64, + EXCHANGE_DATA_TYPE_TIMESTAMP, + EXCHANGE_DATA_TYPE_STRING, + EXCHANGE_DATA_TYPE_BYTES +}; + +/// @brief Used to specify the direction of the data exchange between the +/// database and the server. +enum ExchangeDataTypeIO { + EXCHANGE_DATA_TYPE_IO_IN, + EXCHANGE_DATA_TYPE_IO_OUT, + EXCHANGE_DATA_TYPE_IO_IN_OUT +}; + +/// @brief Used to map the column name with internal backend storage data types. +struct ExchangeColumnInfo { + ExchangeColumnInfo () : name_(""), index_(0), type_io_(EXCHANGE_DATA_TYPE_IO_IN), + type_(EXCHANGE_DATA_TYPE_NONE) {}; + ExchangeColumnInfo (const char* name, const uint32_t index, + const ExchangeDataTypeIO type_io, const ExchangeDataType type) : + name_(name), index_(index), type_io_(type_io), type_(type) {}; + std::string name_; + uint32_t index_; + ExchangeDataTypeIO type_io_; + ExchangeDataType type_; +}; + +typedef boost::shared_ptr ExchangeColumnInfoPtr; + +typedef boost::multi_index_container< + // Container comprises elements of ExchangeColumnInfoPtr type. + ExchangeColumnInfoPtr, + // Here we start enumerating various indexes. + boost::multi_index::indexed_by< + // Sequenced index allows accessing elements in the same way as elements + // in std::list. + // Sequenced is an index #0. + boost::multi_index::sequenced<>, + // Start definition of index #1. + boost::multi_index::hashed_non_unique< + boost::multi_index::member< + ExchangeColumnInfo, + std::string, + &ExchangeColumnInfo::name_ + > + >, + // Start definition of index #2. + boost::multi_index::hashed_non_unique< + boost::multi_index::member< + ExchangeColumnInfo, + uint32_t, + &ExchangeColumnInfo::index_ + > + > + > +> ExchangeColumnInfoContainer; + +/// Pointer to the ExchangeColumnInfoContainer object. +typedef boost::shared_ptr ExchangeColumnInfoContainerPtr; +/// Type of the index #1 - name. +typedef ExchangeColumnInfoContainer::nth_index<1>::type ExchangeColumnInfoContainerName; +/// Pair of iterators to represent the range of ExchangeColumnInfo having the +/// same name value. The first element in this pair represents +/// the beginning of the range, the second element represents the end. +typedef std::pair ExchangeColumnInfoContainerNameRange; +/// Type of the index #2 - index. +typedef ExchangeColumnInfoContainer::nth_index<2>::type ExchangeColumnInfoContainerIndex; +/// Pair of iterators to represent the range of ExchangeColumnInfo having the +/// same index value. The first element in this pair represents +/// the beginning of the range, the second element represents the end. +typedef std::pair ExchangeColumnInfoContainerIndexRange; + +class SqlExchange { +public: + SqlExchange () {} + virtual ~SqlExchange() {} + ExchangeColumnInfoContainer parameters_; ///< Column names and types +}; + +} //dhcp +} //isc + +#endif // SQL_COMMON_H diff --git a/src/lib/eval/tests/.gitignore b/src/lib/eval/tests/.gitignore new file mode 100644 index 0000000000..7ce9787e26 --- /dev/null +++ b/src/lib/eval/tests/.gitignore @@ -0,0 +1 @@ +/libeval_unittests