]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
refactored cassandra backend and fixed issues
authorRazvan Becheriu <razvan.becheriu@qualitance.com>
Mon, 4 Jul 2016 09:52:45 +0000 (12:52 +0300)
committerRazvan Becheriu <razvan.becheriu@qualitance.com>
Mon, 4 Jul 2016 09:52:45 +0000 (12:52 +0300)
src/lib/cfgrpt/tests/.gitignore [new file with mode: 0644]
src/lib/dhcpsrv/cql_exchange.cc [new file with mode: 0644]
src/lib/dhcpsrv/cql_exchange.h [new file with mode: 0644]
src/lib/dhcpsrv/sql_common.h [new file with mode: 0644]
src/lib/eval/tests/.gitignore [new file with mode: 0644]

diff --git a/src/lib/cfgrpt/tests/.gitignore b/src/lib/cfgrpt/tests/.gitignore
new file mode 100644 (file)
index 0000000..d6d1ec8
--- /dev/null
@@ -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 (file)
index 0000000..b1be4b4
--- /dev/null
@@ -0,0 +1,155 @@
+#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
diff --git a/src/lib/dhcpsrv/cql_exchange.h b/src/lib/dhcpsrv/cql_exchange.h
new file mode 100644 (file)
index 0000000..ab78b41
--- /dev/null
@@ -0,0 +1,128 @@
+#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
diff --git a/src/lib/dhcpsrv/sql_common.h b/src/lib/dhcpsrv/sql_common.h
new file mode 100644 (file)
index 0000000..82c2e63
--- /dev/null
@@ -0,0 +1,112 @@
+#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
diff --git a/src/lib/eval/tests/.gitignore b/src/lib/eval/tests/.gitignore
new file mode 100644 (file)
index 0000000..7ce9787
--- /dev/null
@@ -0,0 +1 @@
+/libeval_unittests