--- /dev/null
+/run_unittests
--- /dev/null
+#include <dhcpsrv/cql_exchange.h>
+/// @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<cass_bool_t*>(value)));
+}
+
+static CassError CqlBindInt32(CassStatement* statement, size_t index,
+ void* value) {
+ return cass_statement_bind_int32(statement, index,
+ *(static_cast<cass_int32_t*>(value)));
+}
+
+static CassError CqlBindInt64(CassStatement* statement, size_t index,
+ void* value) {
+ return cass_statement_bind_int64(statement, index,
+ *(static_cast<cass_int64_t*>(value)));
+}
+
+static CassError CqlBindTimestamp(CassStatement* statement, size_t index,
+ void* value) {
+ return cass_statement_bind_int64(statement, index,
+ *(static_cast<cass_int64_t*>(value)));
+}
+
+static CassError CqlBindString(CassStatement* statement, size_t index,
+ void* value) {
+ return cass_statement_bind_string(statement, index,
+ static_cast<std::string*>(value)->c_str());
+}
+
+static CassError CqlBindBytes(CassStatement* statement, size_t index,
+ void* value) {
+ return cass_statement_bind_bytes(statement, index,
+ static_cast<std::vector<cass_byte_t>*>(value)->data(),
+ static_cast<std::vector<cass_byte_t>*>(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<cass_bool_t*>(data));
+}
+
+static CassError CqlGetInt32(const CassValue* value, void* data) {
+ return cass_value_get_int32(value, static_cast<cass_int32_t*>(data));
+}
+
+static CassError CqlGetInt64(const CassValue* value, void* data) {
+ return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
+}
+
+static CassError CqlGetTimestamp(const CassValue* value, void* data) {
+ return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
+}
+
+static CassError CqlGetString(const CassValue* value, void* data) {
+ const char* dataValue;
+ size_t sizeValue;
+ CassError cassError = cass_value_get_string(value,
+ static_cast<const char**>(&dataValue), &sizeValue);
+ static_cast<std::string*>(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<const cass_byte_t**>(&dataValue), &sizeValue);
+ static_cast<std::vector<cass_byte_t>*>(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
--- /dev/null
+#ifndef CQL_EXCHANGE_H
+#define CQL_EXCHANGE_H
+
+#include <exceptions/exceptions.h>
+#include <dhcpsrv/sql_common.h>
+#include <vector>
+#include <cassandra.h>
+
+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<void*> 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
--- /dev/null
+#ifndef SQL_COMMON_H
+#define SQL_COMMON_H
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index/member.hpp>
+
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+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<ExchangeColumnInfo> 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<ExchangeColumnInfoContainer> 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<ExchangeColumnInfoContainerName::const_iterator,
+ ExchangeColumnInfoContainerName::const_iterator> 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<ExchangeColumnInfoContainerIndex::const_iterator,
+ ExchangeColumnInfoContainerIndex::const_iterator> ExchangeColumnInfoContainerIndexRange;
+
+class SqlExchange {
+public:
+ SqlExchange () {}
+ virtual ~SqlExchange() {}
+ ExchangeColumnInfoContainer parameters_; ///< Column names and types
+};
+
+} //dhcp
+} //isc
+
+#endif // SQL_COMMON_H
--- /dev/null
+/libeval_unittests