]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
boost::any for cassandra and more:
authorAndrei Pavel <andrei.pavel@qualitance.com>
Fri, 18 Aug 2017 13:47:58 +0000 (16:47 +0300)
committerAndrei Pavel <andrei.pavel@qualitance.com>
Fri, 18 Aug 2017 13:47:58 +0000 (16:47 +0300)
- replaced void* with boost::any for type safety (might be just a bit less efficient)
- replaced a multi-index-container used for statement parameters ( with position index and name index ) and a vector of the same parameters with a compile-time type-based map
- refactored lease manager to use exchanges
- exchanges now support UDTs (user defined types) and collections to be used in Cassandra statements
- replaced size-ambiguous types (e.g. int) with size-explicit types (e.g. int32_t) in Cassandra-related code
- made exception messages and log messages more detailed and consistent in format

21 files changed:
src/bin/admin/tests/dhcpdb_create_1.0.cql
src/lib/dhcpsrv/cql_connection.cc
src/lib/dhcpsrv/cql_connection.h
src/lib/dhcpsrv/cql_exchange.cc
src/lib/dhcpsrv/cql_exchange.h
src/lib/dhcpsrv/cql_lease_mgr.cc
src/lib/dhcpsrv/cql_lease_mgr.h
src/lib/dhcpsrv/db_exceptions.h
src/lib/dhcpsrv/dhcpsrv_messages.mes
src/lib/dhcpsrv/lease_mgr.h
src/lib/dhcpsrv/lease_mgr_factory.cc
src/lib/dhcpsrv/memfile_lease_mgr.cc
src/lib/dhcpsrv/memfile_lease_mgr.h
src/lib/dhcpsrv/parsers/dbaccess_parser.cc
src/lib/dhcpsrv/sql_common.h
src/lib/dhcpsrv/tests/Makefile.am
src/lib/dhcpsrv/tests/cql_connection_unittest.cc [new file with mode: 0644]
src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc
src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
src/share/database/scripts/cql/dhcpdb_create.cql
src/share/database/scripts/cql/dhcpdb_drop.cql

index 2d6c45a93a573dd612c06c227a5a1aaf79cccfc4..577f2ae2772cd7f9d68f9dba2f7335bc40115d16 100644 (file)
@@ -1,4 +1,4 @@
--- Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+-- Copyright (C) 2015-2017 Deutsche Telekom AG.
 
 -- Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
 
@@ -46,7 +46,7 @@
 -- -----------------------------------------------------
 -- Table `lease4`
 -- -----------------------------------------------------
-CREATE TABLE lease4 (
+CREATE TABLE IF NOT EXISTS lease4 (
     address int,
     hwaddr blob,
     client_id blob,
@@ -57,15 +57,15 @@ CREATE TABLE lease4 (
     fqdn_rev boolean,
     hostname varchar,
     state int,
-    PRIMARY KEY (address)
+    PRIMARY KEY ((address))
 );
 
 -- Create search indexes for lease4 table
-CREATE INDEX lease4index1 ON lease4 (client_id);
-CREATE INDEX lease4index2 ON lease4 (subnet_id);
-CREATE INDEX lease4index3 ON lease4 (hwaddr);
-CREATE INDEX lease4index4 ON lease4 (expire);
-CREATE INDEX lease4index5 ON lease4 (state);
+CREATE INDEX IF NOT EXISTS lease4index1 ON lease4 (client_id);
+CREATE INDEX IF NOT EXISTS lease4index2 ON lease4 (subnet_id);
+CREATE INDEX IF NOT EXISTS lease4index3 ON lease4 (hwaddr);
+CREATE INDEX IF NOT EXISTS lease4index4 ON lease4 (expire);
+CREATE INDEX IF NOT EXISTS lease4index5 ON lease4 (state);
 
 -- Holds the IPv6 leases.
 -- N.B. The use of a VARCHAR for the address is temporary for development:
@@ -73,15 +73,15 @@ CREATE INDEX lease4index5 ON lease4 (state);
 -- -----------------------------------------------------
 -- Table `lease6`
 -- -----------------------------------------------------
-CREATE TABLE lease6 (
+CREATE TABLE IF NOT EXISTS lease6 (
     address varchar,
-    duid blob,
     valid_lifetime bigint,
     expire bigint,
     subnet_id int,
     pref_lifetime bigint,
-    lease_type int,
+    duid blob,
     iaid int,
+    lease_type int,
     prefix_len int,
     fqdn_fwd boolean,
     fqdn_rev boolean,
@@ -90,16 +90,16 @@ CREATE TABLE lease6 (
     hwtype int,
     hwaddr_source int,
     state int,
-    PRIMARY KEY (address)
+    PRIMARY KEY ((address))
 );
 
 -- Create search indexes for lease6 table
-CREATE INDEX lease6index1 ON lease6 (lease_type);
-CREATE INDEX lease6index2 ON lease6 (duid);
-CREATE INDEX lease6index3 ON lease6 (iaid);
-CREATE INDEX lease6index4 ON lease6 (subnet_id);
-CREATE INDEX lease6index5 ON lease6 (expire);
-CREATE INDEX lease6index6 ON lease6 (state);
+CREATE INDEX IF NOT EXISTS lease6index1 ON lease6 (duid);
+CREATE INDEX IF NOT EXISTS lease6index2 ON lease6 (iaid);
+CREATE INDEX IF NOT EXISTS lease6index3 ON lease6 (lease_type);
+CREATE INDEX IF NOT EXISTS lease6index4 ON lease6 (subnet_id);
+CREATE INDEX IF NOT EXISTS lease6index5 ON lease6 (expire);
+CREATE INDEX IF NOT EXISTS lease6index6 ON lease6 (state);
 
 -- ... and a definition of lease6 types.  This table is a convenience for
 -- users of the database - if they want to view the lease table and use the
@@ -109,10 +109,10 @@ CREATE INDEX lease6index6 ON lease6 (state);
 -- -----------------------------------------------------
 -- Table `lease6_types`
 -- -----------------------------------------------------
-CREATE TABLE lease6_types (
+CREATE TABLE IF NOT EXISTS lease6_types (
     lease_type int,                             -- Lease type code.
     name varchar,                               -- Name of the lease type
-    PRIMARY KEY (lease_type)
+    PRIMARY KEY ((lease_type))
 );
 INSERT INTO lease6_types (lease_type, name) VALUES (0, 'IA_NA');   -- Non-temporary v6 addresses
 INSERT INTO lease6_types (lease_type, name) VALUES (1, 'IA_TA');   -- Temporary v6 addresses
@@ -127,10 +127,10 @@ INSERT INTO lease6_types (lease_type, name) VALUES (2, 'IA_PD');   -- Prefix del
 -- -----------------------------------------------------
 -- Table `lease_hwaddr_source`
 -- -----------------------------------------------------
-CREATE TABLE lease_hwaddr_source (
+CREATE TABLE IF NOT EXISTS lease_hwaddr_source (
     hwaddr_source int,
     name varchar,
-    PRIMARY KEY (hwaddr_source)
+    PRIMARY KEY ((hwaddr_source))
 );
 
 -- Hardware address obtained from raw sockets
@@ -160,10 +160,10 @@ INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (64, 'HWADDR_SOURCE
 -- -----------------------------------------------------
 -- Table `lease_state`
 -- -----------------------------------------------------
-CREATE TABLE lease_state (
+CREATE TABLE IF NOT EXISTS lease_state (
     state int,
     name varchar,
-    PRIMARY KEY (state)
+    PRIMARY KEY ((state))
 );
 
 -- Insert currently defined state names.
@@ -175,18 +175,18 @@ INSERT INTO lease_state (state, name) VALUES (2, 'expired-reclaimed');
 -- This table is only modified during schema upgrades.  For historical reasons
 -- (related to the names of the columns in the BIND 10 DNS database file), the
 -- first column is called "version" and not "major".
--- Note: This MUST be kept synchronized with
--- src/share/database/scripts/cql/dhcpdb_create.cql which defines the schema for
--- the unit tests.
+-- Note: This MUST be kept in step with src/share/database/scripts/cassandra/dhcpdb_create.cql,
+-- which defines the schema for the unit tests.
 -- -----------------------------------------------------
 -- Table `schema_version`
 -- -----------------------------------------------------
-CREATE TABLE schema_version (
+CREATE TABLE IF NOT EXISTS schema_version (
     version int,
     minor int,
-    PRIMARY KEY (version)
+    PRIMARY KEY ((version))
 );
 
 INSERT INTO schema_version (version, minor) VALUES (1, 0);
 
--- This line concludes database initalization to version 1.0.
+-- This line concludes database initialization to version 1.0.
+
index 4e6a410c84b53cad222b8cf2fe0d42f63c118aaa..ead885196f2c57073eba0a30c5038acd7b8fada5 100644 (file)
@@ -1,6 +1,7 @@
-// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+// Copyright (C) 2015-2017 Deutsche Telekom AG.
 //
-// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//          Andrei Pavel <andrei.pavel@qualitance.com>
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 
 #include <dhcpsrv/cql_connection.h>
 #include <dhcpsrv/cql_exchange.h>
+#include <dhcpsrv/db_exceptions.h>
+#include <dhcpsrv/dhcpsrv_log.h>
 
-#include <boost/shared_ptr.hpp>
-
-using namespace std;
+#include <memory>  // for std::unique_ptr
 
 namespace isc {
 namespace dhcp {
 
 CqlConnection::CqlConnection(const ParameterMap& parameters)
-    : DatabaseConnection(parameters), cluster_(NULL), session_(NULL),
-      force_consistency_(true), consistency_(CASS_CONSISTENCY_QUORUM),
-      tagged_statements_(NULL) {
+    : DatabaseConnection(parameters), statements_(), cluster_(NULL),
+      session_(NULL), consistency_(CASS_CONSISTENCY_QUORUM), schema_meta_(NULL),
+      keyspace_meta_(NULL), force_consistency_(true) {
 }
 
 CqlConnection::~CqlConnection() {
@@ -38,18 +39,21 @@ CqlConnection::~CqlConnection() {
     CassError rc = CASS_OK;
     std::string error;
 
-    for (int i = 0; i < statements_.size(); i++) {
-        if (statements_[i]) {
-            cass_prepared_free(statements_[i]);
+    for (StatementMapEntry s : statements_) {
+        // typeid(s.second.first) is CassPrepared*
+        CqlTaggedStatement statement = s.second;
+        if (statement.prepared_statement_) {
+            cass_prepared_free(statement.prepared_statement_);
         }
-        statements_[i] = NULL;
     }
 
     if (session_) {
+        cass_schema_meta_free(schema_meta_);
         CassFuture* close_future = cass_session_close(session_);
         cass_future_wait(close_future);
-        checkStatementError(error, close_future, "could not close connection to"
-                                                 " DB");
+        error = checkFutureError(
+            "CqlConnection::~CqlConnection(): cass_sesssion_close() != CASS_OK",
+            close_future);
         rc = cass_future_error_code(close_future);
         cass_future_free(close_future);
         cass_session_free(session_);
@@ -167,98 +171,106 @@ CqlConnection::openDatabase() {
     }
 
     if (port) {
-        int port_number;
+        int32_t port_number;
         try {
-            port_number = boost::lexical_cast<int>(port);
+            port_number = boost::lexical_cast<int32_t>(port);
             if (port_number < 1 || port_number > 65535) {
-                isc_throw(
-                    DbOperationError,
-                    "Port outside of range, expected 1-65535, instead got "
-                        << port);
+                isc_throw(DbOperationError,
+                          "CqlConnection::openDatabase(): "
+                          "port outside of range, expected "
+                          "1-65535, instead got "
+                              << port);
             }
         } catch (const boost::bad_lexical_cast& ex) {
             isc_throw(DbOperationError,
-                      "Invalid port, castable to int expected, instead got \""
-                          << port << "\", " << ex.what());
+                      "CqlConnection::openDatabase(): invalid "
+                      "port, expected castable to int, instead got "
+                      "\"" << port
+                           << "\", " << ex.what());
         }
         cass_cluster_set_port(cluster_, port_number);
     }
 
     if (reconnect_wait_time) {
-        int reconnect_wait_time_number;
+        int32_t reconnect_wait_time_number;
         try {
             reconnect_wait_time_number =
-                boost::lexical_cast<int>(reconnect_wait_time);
+                boost::lexical_cast<int32_t>(reconnect_wait_time);
             if (reconnect_wait_time_number < 0) {
                 isc_throw(DbOperationError,
-                          "Invalid reconnect wait time, positive number "
-                          "expected, instead got "
+                          "CqlConnection::openDatabase(): invalid reconnect "
+                          "wait time, expected positive number, instead got "
                               << reconnect_wait_time);
             }
         } catch (const boost::bad_lexical_cast& ex) {
-            isc_throw(DbOperationError, "Invalid reconnect wait time, castable "
-                                        "to int expected, instead got \""
-                                            << reconnect_wait_time << "\", "
-                                            << ex.what());
+            isc_throw(DbOperationError,
+                      "CqlConnection::openDatabase(): "
+                      "invalid reconnect wait time, expected "
+                      "castable to int, instead got \""
+                          << reconnect_wait_time << "\", " << ex.what());
         }
         cass_cluster_set_reconnect_wait_time(cluster_,
                                              reconnect_wait_time_number);
     }
 
     if (connect_timeout) {
-        int connect_timeout_number;
+        int32_t connect_timeout_number;
         try {
-            connect_timeout_number = boost::lexical_cast<int>(connect_timeout);
+            connect_timeout_number =
+                boost::lexical_cast<int32_t>(connect_timeout);
             if (connect_timeout_number < 0) {
                 isc_throw(DbOperationError,
-                          "Invalid connect timeout, positive number expected, "
-                          "instead got "
+                          "CqlConnection::openDatabase(): "
+                          "invalid connect timeout, expected "
+                          "positive number, instead got "
                               << connect_timeout);
             }
         } catch (const boost::bad_lexical_cast& ex) {
-            isc_throw(DbOperationError, "Invalid connect timeout, castable to "
-                                        "int expected, instead got \""
-                                            << connect_timeout << "\", "
-                                            << ex.what());
+            isc_throw(DbOperationError,
+                      "CqlConnection::openDatabase(): invalid connect timeout, "
+                      "expected castable to int, instead got \""
+                          << connect_timeout << "\", " << ex.what());
         }
         cass_cluster_set_connect_timeout(cluster_, connect_timeout_number);
     }
 
     if (request_timeout) {
-        int request_timeout_number;
+        int32_t request_timeout_number;
         try {
-            request_timeout_number = boost::lexical_cast<int>(request_timeout);
+            request_timeout_number =
+                boost::lexical_cast<int32_t>(request_timeout);
             if (request_timeout_number < 0) {
                 isc_throw(DbOperationError,
-                          "Invalid request timeout, positive number expected, "
-                          "instead got "
+                          "CqlConnection::openDatabase(): "
+                          "invalid request timeout, expected "
+                          "positive number, instead got "
                               << request_timeout);
             }
         } catch (const boost::bad_lexical_cast& ex) {
-            isc_throw(DbOperationError, "Invalid request timeout, castable to "
-                                        "int expected, instead got \""
-                                            << request_timeout << "\", "
-                                            << ex.what());
+            isc_throw(DbOperationError,
+                      "CqlConnection::openDatabase(): invalid request timeout, "
+                      "expected castable to int, instead got \""
+                          << request_timeout << "\", " << ex.what());
         }
         cass_cluster_set_request_timeout(cluster_, request_timeout_number);
     }
 
     if (tcp_keepalive) {
-        int tcp_keepalive_number;
+        int32_t tcp_keepalive_number;
         try {
-            tcp_keepalive_number = boost::lexical_cast<int>(tcp_keepalive);
+            tcp_keepalive_number = boost::lexical_cast<int32_t>(tcp_keepalive);
             if (tcp_keepalive_number < 0) {
                 isc_throw(DbOperationError,
-                          "Invalid TCP keepalive, positive number expected, "
-                          "instead got "
+                          "CqlConnection::openDatabase(): "
+                          "invalid TCP keepalive, expected "
+                          "positive number, instead got "
                               << tcp_keepalive);
             }
         } catch (const boost::bad_lexical_cast& ex) {
-            isc_throw(
-                DbOperationError,
-                "Invalid TCP keepalive, castable to int expected, instead got "
-                "\"" << tcp_keepalive
-                     << "\", " << ex.what());
+            isc_throw(DbOperationError,
+                      "CqlConnection::openDatabase(): invalid TCP keepalive, "
+                      "expected castable to int, instead got \""
+                          << tcp_keepalive << "\", " << ex.what());
         }
         cass_cluster_set_tcp_keepalive(cluster_, cass_true,
                                        tcp_keepalive_number);
@@ -273,8 +285,10 @@ CqlConnection::openDatabase() {
     CassFuture* connect_future =
         cass_session_connect_keyspace(session_, cluster_, keyspace);
     cass_future_wait(connect_future);
-    std::string error;
-    checkStatementError(error, connect_future, "could not connect to DB");
+    const std::string error =
+        checkFutureError("CqlConnection::openDatabase(): "
+                         "cass_session_connect_keyspace() != CASS_OK",
+                         connect_future);
     rc = cass_future_error_code(connect_future);
     cass_future_free(connect_future);
     if (rc != CASS_OK) {
@@ -284,32 +298,43 @@ CqlConnection::openDatabase() {
         cluster_ = NULL;
         isc_throw(DbOpenError, error);
     }
+
+    // Get keyspace meta.
+    schema_meta_ = cass_session_get_schema_meta(session_);
+    keyspace_meta_ = cass_schema_meta_keyspace_by_name(schema_meta_, keyspace);
+    if (!keyspace_meta_) {
+        isc_throw(DbOpenError, "CqlConnection::openDatabase(): "
+                               "!cass_schema_meta_keyspace_by_name()");
+    }
 }
 
 void
-CqlConnection::prepareStatements(CqlTaggedStatement* statements) {
+CqlConnection::prepareStatements(StatementMap& statements) {
     CassError rc = CASS_OK;
-    uint32_t size = 0;
-    tagged_statements_ = statements;
-    for (; tagged_statements_[size].params_; size++) {
-    }
-    statements_.resize(size);
-    for (uint32_t i = 0; i < size; i++) {
-        const char* query = tagged_statements_[i].text_;
+    for (StatementMapEntry it : statements) {
+        CqlTaggedStatement& tagged_statement = it.second;
+        if (statements_.find(tagged_statement.name_) != statements_.end()) {
+            isc_throw(DbOperationError,
+                      "CqlConnection::prepareStatements(): "
+                      "duplicate statement with name "
+                          << tagged_statement.name_);
+        }
 
-        CassFuture* future = cass_session_prepare(session_, query);
+        CassFuture* future =
+            cass_session_prepare(session_, tagged_statement.text_);
         cass_future_wait(future);
-        std::string error;
-        checkStatementError(error, future, i, "could not prepare statement");
+        const std::string error =
+            checkFutureError("CqlConnection::prepareStatements():"
+                             " cass_session_prepare() != CASS_OK",
+                             future, tagged_statement.name_);
         rc = cass_future_error_code(future);
         if (rc != CASS_OK) {
             cass_future_free(future);
-            statements_[i] = NULL;
             isc_throw(DbOperationError, error);
-        } else {
-            statements_[i] = cass_future_get_prepared(future);
         }
 
+        tagged_statement.prepared_statement_ = cass_future_get_prepared(future);
+        statements_.insert(it);
         cass_future_free(future);
     }
 }
@@ -323,80 +348,47 @@ CqlConnection::setConsistency(bool force, CassConsistency consistency) {
 void
 CqlConnection::startTransaction() {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_BEGIN_TRANSACTION);
-    // No-op
+              DHCPSRV_CQL_CONNECTION_BEGIN_TRANSACTION);
 }
 
 void
 CqlConnection::commit() {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_COMMIT);
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_CQL_CONNECTION_COMMIT);
 }
 
 void
 CqlConnection::rollback() {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ROLLBACK);
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_CQL_CONNECTION_ROLLBACK);
 }
 
-void
-CqlConnection::checkStatementError(std::string& error,
-                                   CassFuture* future,
-                                   uint32_t stindex,
-                                   const char* what) const {
-    CassError rc;
-    const char* errorMessage;
-    size_t errorMessageSize;
-    std::stringstream stream;
-    stream << "no error for statement " << tagged_statements_[stindex].name_;
-
-    rc = cass_future_error_code(future);
-    cass_future_error_message(future, &errorMessage, &errorMessageSize);
-
-    if (rc != CASS_OK) {
-        stream.str(std::string());
-        stream << what << " for statement " << tagged_statements_[stindex].name_
-               << ". Future error: " << errorMessage
-               << ". Error description: " << cass_error_desc(rc);
-    }
-    error = stream.str();
-}
+const std::string
+CqlConnection::checkFutureError(const std::string& what,
+                                CassFuture* future,
+                                StatementTag statement_tag /* = NULL */) {
+    CassError cass_error = cass_future_error_code(future);
+    const char* error_message;
+    size_t error_message_size;
+    cass_future_error_message(future, &error_message, &error_message_size);
 
-void
-CqlConnection::checkStatementError(std::string& error,
-                                   CassFuture* future,
-                                   const char* what) const {
-    CassError rc;
-    const char* errorMessage;
-    size_t errorMessageSize;
     std::stringstream stream;
-    stream << "no error";
-
-    rc = cass_future_error_code(future);
-    cass_future_error_message(future, &errorMessage, &errorMessageSize);
-
-    if (rc != CASS_OK) {
-        stream.str(std::string());
-        stream << what << ". Future error: " << errorMessage
-               << ". Error description: " << cass_error_desc(rc);
+    if (statement_tag && std::strlen(statement_tag) > 0) {
+        // future is from cass_session_execute() call.
+        stream << "Statement ";
+        stream << statement_tag;
+    } else {
+        // future is from cass_session_*() call.
+        stream << "Session action ";
     }
-    error = stream.str();
-}
-
-CqlTransaction::CqlTransaction(CqlConnection& conn)
-    : conn_(conn), committed_(false) {
-    conn_.startTransaction();
-}
-
-CqlTransaction::~CqlTransaction() {
-    // Rollback if commit() wasn't explicitly called.
-    if (!committed_) {
-        conn_.rollback();
+    if (cass_error == CASS_OK) {
+        stream << " executed succesfully.";
+    } else {
+        stream << " failed, Kea error: " << what
+               << ", Cassandra error code: " << cass_error_desc(cass_error)
+               << ", Cassandra future error: " << error_message;
     }
-}
-
-void
-CqlTransaction::commit() {
-    conn_.commit();
-    committed_ = true;
+    return stream.str();
 }
 
 }  // namespace dhcp
index 7abcfbc5e3a4c98e30d3f15ed0aee85bd6c00c0a..a3b88d8c1525bd09fc51b5d566a7a63b7bf2aa49 100644 (file)
@@ -1,6 +1,7 @@
-// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+// Copyright (C) 2015-2017 Deutsche Telekom AG.
 //
-// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//          Andrei Pavel <andrei.pavel@qualitance.com>
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 #define CQL_CONNECTION_H
 
 #include <dhcpsrv/database_connection.h>
-#include <dhcpsrv/dhcpsrv_log.h>
 
 #include <cassandra.h>
 
-#include <boost/scoped_ptr.hpp>
-
-#include <inttypes.h>
-
+#include <cstring>
 #include <map>
+#include <memory>
 #include <string>
+#include <unordered_map>
+#include <utility>
 #include <vector>
 
 namespace isc {
 namespace dhcp {
 
-/// @brief Defines a single statement
+/// @brief Pair containing major and minor versions
+typedef std::pair<uint32_t, uint32_t> VersionPair;
+
+/// @brief Statement index representing the statement name
+typedef char const* const StatementTag;
+
+/// @brief Define CQL backend version: 2.3
+/// @{
+constexpr uint32_t CQL_DRIVER_VERSION_MAJOR = CASS_VERSION_MAJOR;
+constexpr uint32_t CQL_DRIVER_VERSION_MINOR = CASS_VERSION_MINOR;
+/// @}
+
+/// Define CQL schema version: 2.0
+/// @{
+constexpr uint32_t CQL_SCHEMA_VERSION_MAJOR = 2u;
+constexpr uint32_t CQL_SCHEMA_VERSION_MINOR = 0u;
+/// @}
+
+/// @brief Defines a single statement or query
 ///
-/// @param params_ parameter names
 /// @param name_ short description of the query
 /// @param text_ text representation of the actual query
+/// @param prepared_statement_ internal Cassandra object representing the
+///     prepared statement
+/// @param is_raw_statement_ shows if statement should be executed rawly or with
+///     binds
 struct CqlTaggedStatement {
-    const char** params_;
-    const char* name_;
-    const char* text_;
+    StatementTag name_;
+    char const* const text_;
+    const CassPrepared* prepared_statement_;
+    bool is_raw_statement_;
+
+    /// @brief Constructor
+    CqlTaggedStatement(StatementTag name, char const* const text)
+        : name_(name), text_(text), prepared_statement_(NULL),
+          is_raw_statement_(false) {
+    }
+
+    /// @brief Constructor
+    CqlTaggedStatement(StatementTag name,
+                       char const* const text,
+                       bool const& is_raw_statement)
+        : name_(name), text_(text), prepared_statement_(NULL),
+          is_raw_statement_(is_raw_statement) {
+    }
 };
 
-// Define CQL backend version: 2.3
-const uint32_t CQL_DRIVER_VERSION_MAJOR = CASS_VERSION_MAJOR;
-const uint32_t CQL_DRIVER_VERSION_MINOR = CASS_VERSION_MINOR;
+/// @brief Hash function for StatementMap keys
+///
+/// Delegates to std::hash<std::string>.
+struct StatementTagHash {
+    size_t operator()(StatementTag const& key) const {
+        return std::hash<std::string>{}(std::string(key));
+    }
+};
 
-/// Define CQL schema version: 1.0
-const uint32_t CQL_SCHEMA_VERSION_MAJOR = 1;
-const uint32_t CQL_SCHEMA_VERSION_MINOR = 0;
+/// @brief Equality function for StatementMap keys
+struct StatementTagEqual {
+    bool operator()(StatementTag const& lhs, StatementTag const& rhs) const {
+        return std::strcmp(lhs, rhs) == 0;
+    }
+};
+
+/// @brief Contains all statements.
+typedef std::unordered_map<StatementTag,
+                           CqlTaggedStatement,
+                           StatementTagHash,
+                           StatementTagEqual>
+    StatementMap;
+
+typedef std::pair<StatementTag, CqlTaggedStatement> StatementMapEntry;
 
 /// @brief Common CQL connector pool
 ///
 /// Provides common operations for the Cassandra database connection used by
-/// CqlLeaseMgr, CqlHostDataSource and CqlSrvConfigMgr. Manages the connection
-/// to the Cassandra database and preparing of compiled statements. Its fields
-/// are public because they are used (both set and retrieved) in classes that
+/// CqlLeaseMgr, CqlHostDataSource and CqlSrvConfigMgr. Manages the
+/// connection
+/// to the Cassandra database and preparing of compiled statements. Its
+/// fields
+/// are public because they are used (both set and retrieved) in classes
+/// that
 /// use instances of CqlConnection.
 class CqlConnection : public DatabaseConnection {
 public:
@@ -78,7 +134,7 @@ public:
     ///     has failed
     /// @throw isc::InvalidParameter if there is an invalid access in the
     ///     vector. This represents an internal error within the code.
-    void prepareStatements(CqlTaggedStatement* statements);
+    void prepareStatements(StatementMap& statements);
 
     /// @brief Open database
     ///
@@ -96,29 +152,22 @@ public:
     void startTransaction();
 
     /// @brief Commit Transactions
-    ///
-    /// This is a no-op for Cassandra.
     virtual void commit();
 
     /// @brief Rollback Transactions
-    ///
-    /// This is a no-op for Cassandra.
     virtual void rollback();
 
     /// @brief Check for errors
     ///
     /// Check for errors on the current database operation.
-    void checkStatementError(std::string& error,
-                             CassFuture* future,
-                             uint32_t stindex,
-                             const char* what) const;
+    static const std::string
+    checkFutureError(const std::string& what,
+                     CassFuture* future,
+                     StatementTag statement_tag = NULL);
 
-    /// @brief Check for errors
-    ///
-    /// Check for errors on the current database operation.
-    void checkStatementError(std::string& error,
-                             CassFuture* future,
-                             const char* what) const;
+    /// @brief Pointer to external array of tagged statements containing
+    ///     statement name, array of names of bind parameters and text query
+    StatementMap statements_;
 
     /// @brief CQL connection handle
     CassCluster* cluster_;
@@ -126,68 +175,21 @@ public:
     /// @brief CQL session handle
     CassSession* session_;
 
-    /// @brief CQL consistency enabled
-    bool force_consistency_;
-
     /// @brief CQL consistency
     CassConsistency consistency_;
 
-    /// @brief CQL prepared statements - used for faster statement execution
-    ///     using bind functionality
-    std::vector<const CassPrepared*> statements_;
-
-    /// @brief Pointer to external array of tagged statements containing
-    ///     statement name, array of names of bind parameters and text query
-    CqlTaggedStatement* tagged_statements_;
-};
-
-/// @brief RAII object representing CQL transaction.
-///
-/// An instance of this class should be created in a scope where multiple
-/// INSERT statements should be executed within a single transaction. The
-/// transaction is started when the constructor of this class is invoked.
-/// The transaction is ended when the @ref CqlTransaction::commit is
-/// explicitly called or when the instance of this class is destroyed.
-/// The @ref CqlTransaction::commit commits changes to the database
-/// and the changes remain in the database when the instance of the
-/// class is destroyed. If the class instance is destroyed before the
-/// @ref CqlTransaction::commit is called, the transaction is rolled
-/// back. The rollback on destruction guarantees that partial data is
-/// not stored in the database when there is an error during any
-/// of the operations belonging to a transaction.
-class CqlTransaction : public boost::noncopyable {
-public:
-    /// @brief Constructor
-    ///
-    /// Starts transaction by making a "START TRANSACTION" query.
-    ///
-    /// @param conn CQL connection to use for the transaction. This connection
-    ///     will be later used to commit or rollback changes.
-    ///
-    /// @throw DbOperationError if "START TRANSACTION" query fails.
-    explicit CqlTransaction(CqlConnection& conn);
-
-    /// @brief Destructor
-    ///
-    /// Rolls back the transaction if changes haven't been committed.
-    ~CqlTransaction();
+    // @brief Schema meta information, used for UDTs
+    const CassSchemaMeta* schema_meta_;
 
-    /// @brief Commits transaction
-    ///
-    /// Calls @ref CqlConnection::commit()..
-    void commit();
+    /// @brief Keyspace meta information, used for UDTs
+    const CassKeyspaceMeta* keyspace_meta_;
 
-private:
-    /// @brief Holds reference to the CQL database connection.
-    CqlConnection& conn_;
-
-    /// @brief Boolean flag indicating if the transaction has been committed.
-    ///
-    /// This flag is used in the class destructor to assess if the
-    /// transaction should be rolled back.
-    bool committed_;
+    /// @brief CQL consistency enabled
+    bool force_consistency_;
 };
 
+typedef std::shared_ptr<CqlConnection> CqlConnectionPtr;
+
 }  // namespace dhcp
 }  // namespace isc
 
index c73f74b2fae52cddc975382eb8069b2f703a38ef..ee43993caa5741b9294ec89e5ed81e132fde5152 100644 (file)
@@ -1,6 +1,7 @@
-// Copyright (C) 2016 Deutsche Telekom AG.
+// Copyright (C) 2016-2017 Deutsche Telekom AG.
 //
-// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//          Andrei Pavel <andrei.pavel@qualitance.com>
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 
 #include <config.h>
 
-#include <dhcpsrv/cql_exchange.h>
-
 #include <dhcpsrv/cql_connection.h>
+#include <dhcpsrv/cql_exchange.h>
 #include <dhcpsrv/db_exceptions.h>
+#include <dhcpsrv/sql_common.h>
+
+namespace isc {
+namespace dhcp {
 
-/// @name CqlBind auxiliary functions for binding data into Cassandra format:
+/// @brief Macro to return directly from caller function
+#define KEA_CASS_CHECK(cass_error)   \
+    {                                \
+        if (cass_error != CASS_OK) { \
+            return cass_error;       \
+        }                            \
+    }
+
+struct ExchangeDataTypeHash {
+public:
+    size_t operator()(const ExchangeDataType& key) const {
+        return std::hash<size_t>{}(static_cast<size_t>(key));
+    }
+};
+
+typedef std::unordered_map<ExchangeDataType, CqlFunction, ExchangeDataTypeHash>
+    CqlFunctionMap;
+extern CqlFunctionMap CQL_FUNCTIONS;
+
+/// @brief hash function for CassTypeMap
+///
+/// Required by c++ versions 5 and below.
+///
+/// @param key being hashed
+///
+/// @return hash value
+std::size_t
+hash_value(const CassValueType& key) {
+    return key;
+}
+
+/// @brief Map types used to determine exchange type
 /// @{
+typedef std::unordered_map<std::type_index, ExchangeDataType> AnyTypeMap;
+// Declare uint8_t as key here for compatibility with c++-5. Ideally, it would
+//     be CassValueType
+typedef std::unordered_map<uint8_t, ExchangeDataType> CassTypeMap;
+/// @}
 
-/// @todo These void* cast are unsafe. See ticket #4525.
+/// @brief Maps C++ type to exchange type
+static AnyTypeMap ANY_TYPE_MAP = {
+    {typeid(NULL), EXCHANGE_DATA_TYPE_NONE},
+    {typeid(cass_bool_t*), EXCHANGE_DATA_TYPE_BOOL},
+    {typeid(cass_int8_t*), EXCHANGE_DATA_TYPE_INT8},
+    {typeid(cass_int16_t*), EXCHANGE_DATA_TYPE_INT16},
+    {typeid(cass_int32_t*), EXCHANGE_DATA_TYPE_INT32},
+    {typeid(cass_int64_t*), EXCHANGE_DATA_TYPE_INT64},
+    {typeid(std::string*), EXCHANGE_DATA_TYPE_STRING},
+    {typeid(CassBlob*), EXCHANGE_DATA_TYPE_BYTES},
+    {typeid(CassUuid*), EXCHANGE_DATA_TYPE_UUID},
+    {typeid(Udt*), EXCHANGE_DATA_TYPE_UDT},
+    {typeid(Collection*), EXCHANGE_DATA_TYPE_COLLECTION}};
+
+/// @brief Maps Cassandra type to exchange type
+static CassTypeMap CASS_TYPE_MAP = {
+    {CASS_VALUE_TYPE_CUSTOM, EXCHANGE_DATA_TYPE_UDT},
+    {CASS_VALUE_TYPE_ASCII, EXCHANGE_DATA_TYPE_STRING},
+    {CASS_VALUE_TYPE_BIGINT, EXCHANGE_DATA_TYPE_INT64},
+    {CASS_VALUE_TYPE_BLOB, EXCHANGE_DATA_TYPE_BYTES},
+    {CASS_VALUE_TYPE_BOOLEAN, EXCHANGE_DATA_TYPE_BOOL},
+    {CASS_VALUE_TYPE_COUNTER, EXCHANGE_DATA_TYPE_INT32},
+    {CASS_VALUE_TYPE_DECIMAL, EXCHANGE_DATA_TYPE_INT32},
+    {CASS_VALUE_TYPE_DOUBLE, EXCHANGE_DATA_TYPE_INT64},
+    {CASS_VALUE_TYPE_FLOAT, EXCHANGE_DATA_TYPE_INT32},
+    {CASS_VALUE_TYPE_INT, EXCHANGE_DATA_TYPE_INT32},
+    {CASS_VALUE_TYPE_TEXT, EXCHANGE_DATA_TYPE_STRING},
+    {CASS_VALUE_TYPE_TIMESTAMP, EXCHANGE_DATA_TYPE_INT64},
+    {CASS_VALUE_TYPE_UUID, EXCHANGE_DATA_TYPE_UUID},
+    {CASS_VALUE_TYPE_VARCHAR, EXCHANGE_DATA_TYPE_STRING},
+    {CASS_VALUE_TYPE_VARINT, EXCHANGE_DATA_TYPE_INT32},
+    {CASS_VALUE_TYPE_TIMEUUID, EXCHANGE_DATA_TYPE_INT64},
+    {CASS_VALUE_TYPE_INET, EXCHANGE_DATA_TYPE_NONE},
+    {CASS_VALUE_TYPE_DATE, EXCHANGE_DATA_TYPE_INT64},
+    {CASS_VALUE_TYPE_TIME, EXCHANGE_DATA_TYPE_INT64},
+    {CASS_VALUE_TYPE_SMALL_INT, EXCHANGE_DATA_TYPE_INT16},
+    {CASS_VALUE_TYPE_TINY_INT, EXCHANGE_DATA_TYPE_INT8},
+    {CASS_VALUE_TYPE_LIST, EXCHANGE_DATA_TYPE_COLLECTION},
+    {CASS_VALUE_TYPE_MAP, EXCHANGE_DATA_TYPE_COLLECTION},
+    {CASS_VALUE_TYPE_SET, EXCHANGE_DATA_TYPE_COLLECTION},
+    {CASS_VALUE_TYPE_UDT, EXCHANGE_DATA_TYPE_UDT},
+    {CASS_VALUE_TYPE_TUPLE, EXCHANGE_DATA_TYPE_UDT}};
+
+/// @brief Udt method implementations
+/// @{
+Udt::Udt(const CqlConnection& connection, const std::string& name)
+    : AnyArray(), connection_(connection), name_(name) {
+    // Create type.
+    cass_data_type_ = cass_keyspace_meta_user_type_by_name(
+        connection_.keyspace_meta_, name_.c_str());
+    if (!cass_data_type_) {
+        isc_throw(DbOperationError,
+                  "Udt::Udt(): UDT " << name_ << " does not exist ");
+    }
+    // Create container.
+    cass_user_type_ = cass_user_type_new_from_data_type(cass_data_type_);
+    if (!cass_user_type_) {
+        isc_throw(DbOperationError,
+                  "Udt::Udt(): Type " << name_
+                                      << " is not a UDT as expected. ");
+    }
+}
+
+Udt::~Udt() {
+    // Bug: it seems that if there is no call to
+    //      cass_user_type_set_*(cass_user_type_), then
+    //      cass_user_type_free(cass_user_type_) might SIGSEGV, so we never
+    //      free. Udt objects should have application scope though.
+    // cass_user_type_free(cass_user_type_);
+}
+/// @}
+
+/// @brief AnyArray method implementations
+/// @{
+void
+AnyArray::add(const boost::any& value) {
+    push_back(value);
+}
+
+void
+AnyArray::remove(const size_t& index) {
+    if (size() <= index) {
+        isc_throw(BadValue,
+                  "AnyArray::remove(): index "
+                      << index << " out of bounds: [0, " << (size() - 1)
+                      << "]");
+    }
+    erase(begin() + index);
+}
+/// @}
+
+/// @name CqlBind functions for binding data into Cassandra format for
+///     insertion:
+/// @{
 static CassError
-CqlBindNone(CassStatement* statement, size_t index, void*) {
+CqlBindNone(const boost::any& /* value */,
+            const size_t& index,
+            CassStatement* statement) {
     return cass_statement_bind_null(statement, index);
 }
 
 static CassError
-CqlBindBool(CassStatement* statement, size_t index, void* value) {
+CqlBindBool(const boost::any& value,
+            const size_t& index,
+            CassStatement* statement) {
     return cass_statement_bind_bool(statement, index,
-                                    *(static_cast<cass_bool_t*>(value)));
+                                    *boost::any_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)));
+CqlBindInt8(const boost::any& value,
+            const size_t& index,
+            CassStatement* statement) {
+    return cass_statement_bind_int8(statement, index,
+                                    *boost::any_cast<cass_int8_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)));
+CqlBindInt16(const boost::any& value,
+             const size_t& index,
+             CassStatement* statement) {
+    return cass_statement_bind_int16(statement, index,
+                                     *boost::any_cast<cass_int16_t*>(value));
+}
+
+static CassError
+CqlBindInt32(const boost::any& value,
+             const size_t& index,
+             CassStatement* statement) {
+    return cass_statement_bind_int32(statement, index,
+                                     *boost::any_cast<cass_int32_t*>(value));
 }
 
 static CassError
-CqlBindTimestamp(CassStatement* statement, size_t index, void* value) {
+CqlBindInt64(const boost::any& value,
+             const size_t& index,
+             CassStatement* statement) {
     return cass_statement_bind_int64(statement, index,
-                                     *(static_cast<cass_int64_t*>(value)));
+                                     *boost::any_cast<cass_int64_t*>(value));
 }
 
 static CassError
-CqlBindString(CassStatement* statement, size_t index, void* value) {
+CqlBindString(const boost::any& value,
+              const size_t& index,
+              CassStatement* statement) {
     return cass_statement_bind_string(
-        statement, index, static_cast<std::string*>(value)->c_str());
+        statement, index, boost::any_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());
+CqlBindBytes(const boost::any& value,
+             const size_t& index,
+             CassStatement* statement) {
+    CassBlob* blob_value = boost::any_cast<CassBlob*>(value);
+    return cass_statement_bind_bytes(statement, index, blob_value->data(),
+                                     blob_value->size());
 }
 
 static CassError
-CqlBindUuid(CassStatement* statement, size_t index, void* value) {
+CqlBindUuid(const boost::any& value,
+            const size_t& index,
+            CassStatement* statement) {
     return cass_statement_bind_uuid(statement, index,
-                                    *static_cast<CassUuid*>(value));
+                                    *boost::any_cast<CassUuid*>(value));
+}
+
+static CassError
+CqlBindUdt(const boost::any& value,
+           const size_t& index,
+           CassStatement* statement) {
+    Udt* udt = boost::any_cast<Udt*>(value);
+
+    size_t i = 0u;
+    for (boost::any& element : *udt) {
+        try {
+            KEA_CASS_CHECK(
+                CQL_FUNCTIONS[exchangeType(element)].cqlUdtSetFunction_(
+                    element, i, udt->cass_user_type_));
+        } catch (const boost::bad_any_cast& exception) {
+            isc_throw(DbOperationError,
+                      "CqlCommon::udtSetData(): "
+                          << exception.what() << " when binding parameter "
+                          << " of type " << element.type().name()
+                          << "in UDT with function CQL_FUNCTIONS["
+                          << exchangeType(element) << "].cqlUdtSetFunction_");
+        }
+        ++i;
+    }
+
+    return cass_statement_bind_user_type(statement, index,
+                                         udt->cass_user_type_);
+}
+
+static CassError
+CqlBindCollection(const boost::any& value,
+                  const size_t& index,
+                  CassStatement* statement) {
+    Collection* elements = boost::any_cast<Collection*>(value);
+
+    CassCollection* collection =
+        cass_collection_new(CASS_COLLECTION_TYPE_SET, elements->size());
+
+    for (boost::any& element : *elements) {
+        ExchangeDataType type = exchangeType(element);
+        KEA_CASS_CHECK(CQL_FUNCTIONS[type].cqlCollectionAppendFunction_(
+            element, collection));
+    }
+
+    const CassError cass_error =
+        cass_statement_bind_collection(statement, index, collection);
+    cass_collection_free(collection);
+
+    return cass_error;
+}
+/// @}
+
+/// @name CqlUdtSet functions for binding data into Cassandra format for
+///     insertion of a UDT:
+/// @{
+static CassError
+CqlUdtSetNone(const boost::any& /* udt_member */,
+              const size_t& position,
+              CassUserType* cass_user_type) {
+    return cass_user_type_set_null(cass_user_type, position);
+}
+
+static CassError
+CqlUdtSetBool(const boost::any& udt_member,
+              const size_t& position,
+              CassUserType* cass_user_type) {
+    return cass_user_type_set_bool(cass_user_type, position,
+                                   *boost::any_cast<cass_bool_t*>(udt_member));
+}
+
+static CassError
+CqlUdtSetInt8(const boost::any& udt_member,
+              const size_t& position,
+              CassUserType* cass_user_type) {
+    return cass_user_type_set_int8(cass_user_type, position,
+                                   *boost::any_cast<cass_int8_t*>(udt_member));
+}
+
+static CassError
+CqlUdtSetInt16(const boost::any& udt_member,
+               const size_t& position,
+               CassUserType* cass_user_type) {
+    return cass_user_type_set_int16(
+        cass_user_type, position, *boost::any_cast<cass_int16_t*>(udt_member));
+}
+
+static CassError
+CqlUdtSetInt32(const boost::any& udt_member,
+               const size_t& position,
+               CassUserType* cass_user_type) {
+    return cass_user_type_set_int32(
+        cass_user_type, position, *boost::any_cast<cass_int32_t*>(udt_member));
+}
+
+static CassError
+CqlUdtSetInt64(const boost::any& udt_member,
+               const size_t& position,
+               CassUserType* cass_user_type) {
+    return cass_user_type_set_int64(
+        cass_user_type, position, *boost::any_cast<cass_int64_t*>(udt_member));
+}
+
+static CassError
+CqlUdtSetString(const boost::any& udt_member,
+                const size_t& position,
+                CassUserType* cass_user_type) {
+    return cass_user_type_set_string(
+        cass_user_type, position,
+        boost::any_cast<std::string*>(udt_member)->c_str());
+}
+
+static CassError
+CqlUdtSetBytes(const boost::any& udt_member,
+               const size_t& position,
+               CassUserType* cass_user_type) {
+    CassBlob* blob_value = boost::any_cast<CassBlob*>(udt_member);
+    return cass_user_type_set_bytes(cass_user_type, position,
+                                    blob_value->data(), blob_value->size());
+}
+
+static CassError
+CqlUdtSetUuid(const boost::any& udt_member,
+              const size_t& position,
+              CassUserType* cass_user_type) {
+    return cass_user_type_set_uuid(cass_user_type, position,
+                                   *boost::any_cast<CassUuid*>(udt_member));
+}
+
+static CassError
+CqlUdtSetUdt(const boost::any& udt_member,
+             const size_t& position,
+             CassUserType* cass_user_type) {
+    return cass_user_type_set_user_type(
+        cass_user_type, position,
+        boost::any_cast<Udt*>(udt_member)->cass_user_type_);
+}
+
+static CassError
+CqlUdtSetCollection(const boost::any& udt_member,
+                    const size_t& position,
+                    CassUserType* cass_user_type) {
+    return cass_user_type_set_collection(
+        cass_user_type, position, boost::any_cast<CassCollection*>(udt_member));
 }
 /// @}
 
+/// @name CqlCollectionAppend functions for binding data into Cassandra format
+///     for insertion of a collection:
+/// @{
 static CassError
-CqlGetNone(const CassValue*, void*) {
+CqlCollectionAppendNone(const boost::any& /* value */,
+                        CassCollection* /* collection */) {
     return CASS_OK;
 }
 
 static CassError
-CqlGetBool(const CassValue* value, void* data) {
-    return cass_value_get_bool(value, static_cast<cass_bool_t*>(data));
+CqlCollectionAppendBool(const boost::any& value, CassCollection* collection) {
+    return cass_collection_append_bool(collection,
+                                       *boost::any_cast<cass_bool_t*>(value));
 }
 
 static CassError
-CqlGetInt32(const CassValue* value, void* data) {
-    return cass_value_get_int32(value, static_cast<cass_int32_t*>(data));
+CqlCollectionAppendInt8(const boost::any& value, CassCollection* collection) {
+    return cass_collection_append_int8(collection,
+                                       *boost::any_cast<cass_int8_t*>(value));
 }
 
 static CassError
-CqlGetInt64(const CassValue* value, void* data) {
-    return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
+CqlCollectionAppendInt16(const boost::any& value, CassCollection* collection) {
+    return cass_collection_append_int16(collection,
+                                        *boost::any_cast<cass_int16_t*>(value));
 }
 
 static CassError
-CqlGetTimestamp(const CassValue* value, void* data) {
-    return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
+CqlCollectionAppendInt32(const boost::any& value, CassCollection* collection) {
+    return cass_collection_append_int32(collection,
+                                        *boost::any_cast<cass_int32_t*>(value));
 }
 
 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;
+CqlCollectionAppendInt64(const boost::any& value, CassCollection* collection) {
+    return cass_collection_append_int64(collection,
+                                        *boost::any_cast<cass_int64_t*>(value));
 }
 
 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;
+CqlCollectionAppendString(const boost::any& value, CassCollection* collection) {
+    return cass_collection_append_string(
+        collection, boost::any_cast<std::string*>(value)->c_str());
 }
 
 static CassError
-CqlGetUuid(const CassValue* value, void* data) {
-    return cass_value_get_uuid(value, static_cast<CassUuid*>(data));
+CqlCollectionAppendBytes(const boost::any& value, CassCollection* collection) {
+    CassBlob* blob_value = boost::any_cast<CassBlob*>(value);
+    return cass_collection_append_bytes(collection, blob_value->data(),
+                                        blob_value->size());
 }
 
-namespace isc {
-namespace dhcp {
+static CassError
+CqlCollectionAppendUuid(const boost::any& value, CassCollection* collection) {
+    return cass_collection_append_uuid(collection,
+                                       *boost::any_cast<CassUuid*>(value));
+}
 
-struct CqlFunctionData CqlFunctions[] = {
-    {CqlBindNone, CqlGetNone},           {CqlBindBool, CqlGetBool},
-    {CqlBindInt32, CqlGetInt32},         {CqlBindInt64, CqlGetInt64},
-    {CqlBindTimestamp, CqlGetTimestamp}, {CqlBindString, CqlGetString},
-    {CqlBindBytes, CqlGetBytes},         {CqlBindUuid, CqlGetUuid}};
+static CassError
+CqlCollectionAppendUdt(const boost::any& value, CassCollection* collection) {
+    Udt* udt = boost::any_cast<Udt*>(value);
+    size_t i = 0u;
+    for (boost::any& element : *udt) {
+        KEA_CASS_CHECK(CQL_FUNCTIONS[exchangeType(element)].cqlUdtSetFunction_(
+            element, i, udt->cass_user_type_));
+        ++i;
+    }
+    return cass_collection_append_user_type(collection, udt->cass_user_type_);
+}
 
-ExchangeDataType
-CqlCommon::getDataType(const uint32_t stindex,
-                       int pindex,
-                       const SqlExchange& exchange,
-                       const 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_;
+static CassError
+CqlCollectionAppendCollection(const boost::any& value,
+                              CassCollection* collection) {
+    return cass_collection_append_collection(
+        collection, boost::any_cast<CassCollection*>(value));
+}
+// @}
+
+/// @name CqlGet functions for retrieving data of the proper Cassandra format:
+/// @{
+static CassError
+CqlGetNone(const boost::any& /* data */, const CassValue* /* value */) {
+    return CASS_OK;
+}
+
+static CassError
+CqlGetBool(const boost::any& data, const CassValue* value) {
+    return cass_value_get_bool(value, boost::any_cast<cass_bool_t*>(data));
+}
+
+static CassError
+CqlGetInt8(const boost::any& data, const CassValue* value) {
+    return cass_value_get_int8(value, boost::any_cast<cass_int8_t*>(data));
+}
+
+static CassError
+CqlGetInt16(const boost::any& data, const CassValue* value) {
+    return cass_value_get_int16(value, boost::any_cast<cass_int16_t*>(data));
+}
+
+static CassError
+CqlGetInt32(const boost::any& data, const CassValue* value) {
+    return cass_value_get_int32(value, boost::any_cast<cass_int32_t*>(data));
+}
+
+static CassError
+CqlGetInt64(const boost::any& data, const CassValue* value) {
+    return cass_value_get_int64(value, boost::any_cast<cass_int64_t*>(data));
+}
+
+static CassError
+CqlGetString(const boost::any& data, const CassValue* value) {
+    char const* data_value;
+    size_t size_value;
+    CassError cass_error = cass_value_get_string(
+        value, static_cast<char const**>(&data_value), &size_value);
+    boost::any_cast<std::string*>(data)->assign(data_value,
+                                                data_value + size_value);
+    return cass_error;
+}
+
+static CassError
+CqlGetBytes(const boost::any& data, const CassValue* value) {
+    const cass_byte_t* data_value;
+    size_t size_value;
+    CassError cass_error = cass_value_get_bytes(
+        value, static_cast<const cass_byte_t**>(&data_value), &size_value);
+    boost::any_cast<CassBlob*>(data)->assign(data_value,
+                                             data_value + size_value);
+    return cass_error;
+}
+
+static CassError
+CqlGetUuid(const boost::any& data, const CassValue* value) {
+    return cass_value_get_uuid(value, boost::any_cast<CassUuid*>(data));
+}
+
+static CassError
+CqlGetUdt(const boost::any& data, const CassValue* value) {
+    Udt* udt = boost::any_cast<Udt*>(data);
+
+    CassIterator* fields = cass_iterator_fields_from_user_type(value);
+    if (!fields) {
+        isc_throw(DbOperationError, "CqlGetUdt(): column is not a UDT");
+    }
+    Udt::const_iterator it = udt->begin();
+    while (cass_iterator_next(fields)) {
+        const CassValue* field_value =
+            cass_iterator_get_user_type_field_value(fields);
+        if (cass_value_is_null(field_value)) {
+            isc_throw(DbOperationError,
+                      "CqlGetUdt(): null value returned in UDT");
         }
+        const CassValueType& type = cass_value_type(field_value);
+        KEA_CASS_CHECK(CQL_FUNCTIONS[exchangeType(type)].cqlGetFunction_(
+            *it, field_value));
+        ++it;
+        // If cqlGetFunction_() returns != CASS_OK, don't
+        // cass_iterator_free(items_iterator) because we're returning from this
+        // function and throwing from the callee.
     }
-    return EXCHANGE_DATA_TYPE_NONE;
+    cass_iterator_free(fields);
+    return CASS_OK;
 }
 
-void
-CqlCommon::bindData(CassStatement* statement,
-                    uint32_t stindex,
-                    const CqlDataArray& data,
-                    const SqlExchange& exchange,
-                    const CqlTaggedStatement* tagged_statements) {
-    if (!tagged_statements[stindex].params_) {
-        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");
+static CassError
+CqlGetCollection(const boost::any& data, const CassValue* value) {
+    Collection* collection = boost::any_cast<Collection*>(data);
+    BOOST_ASSERT(collection->size() == 1);
+
+    // @todo: Create a copy of the underlying object rather than referencing to
+    // it.
+    boost::any underlying_object = *collection->begin();
+
+    collection->clear();
+
+    CassIterator* items = cass_iterator_from_collection(value);
+    if (!items) {
+        isc_throw(DbOperationError,
+                  "CqlGetCollection(): column is not a collection");
+    }
+    while (cass_iterator_next(items)) {
+        const CassValue* item_value = cass_iterator_get_value(items);
+        if (cass_value_is_null(item_value)) {
+            isc_throw(DbOperationError,
+                      "CqlGetCollection(): null value returned in collection");
         }
-        CqlFunctions[type].cqlBindFunction_(statement, i, data.values_[i]);
+        const CassValueType& type = cass_value_type(item_value);
+
+        collection->push_back(underlying_object);
+        KEA_CASS_CHECK(CQL_FUNCTIONS[exchangeType(type)].cqlGetFunction_(
+            *collection->rbegin(), item_value));
+        // If cqlGetFunction_() returns != CASS_OK, don't
+        // cass_iterator_free(items_iterator) because we're returning from this
+        // function and throwing from the callee.
+    }
+    cass_iterator_free(items);
+    return CASS_OK;
+}
+/// @}
+
+/// @brief Functions used to interface with the Cassandra C++ driver
+CqlFunctionMap CQL_FUNCTIONS =  //
+    {{EXCHANGE_DATA_TYPE_NONE,
+      {CqlBindNone, CqlUdtSetNone, CqlCollectionAppendNone, CqlGetNone}},
+     {EXCHANGE_DATA_TYPE_BOOL,
+      {CqlBindBool, CqlUdtSetBool, CqlCollectionAppendBool, CqlGetBool}},
+     {EXCHANGE_DATA_TYPE_INT8,
+      {CqlBindInt8, CqlUdtSetInt8, CqlCollectionAppendInt8, CqlGetInt8}},
+     {EXCHANGE_DATA_TYPE_INT16,
+      {CqlBindInt16, CqlUdtSetInt16, CqlCollectionAppendInt16, CqlGetInt16}},
+     {EXCHANGE_DATA_TYPE_INT32,
+      {CqlBindInt32, CqlUdtSetInt32, CqlCollectionAppendInt32, CqlGetInt32}},
+     {EXCHANGE_DATA_TYPE_INT64,
+      {CqlBindInt64, CqlUdtSetInt64, CqlCollectionAppendInt64, CqlGetInt64}},
+     {EXCHANGE_DATA_TYPE_STRING,
+      {CqlBindString, CqlUdtSetString, CqlCollectionAppendString,
+       CqlGetString}},
+     {EXCHANGE_DATA_TYPE_BYTES,
+      {CqlBindBytes, CqlUdtSetBytes, CqlCollectionAppendBytes, CqlGetBytes}},
+     {EXCHANGE_DATA_TYPE_UUID,
+      {CqlBindUuid, CqlUdtSetUuid, CqlCollectionAppendUuid, CqlGetUuid}},
+     {EXCHANGE_DATA_TYPE_UDT,
+      {CqlBindUdt, CqlUdtSetUdt, CqlCollectionAppendUdt, CqlGetUdt}},
+     {EXCHANGE_DATA_TYPE_COLLECTION,
+      {CqlBindCollection, CqlUdtSetCollection, CqlCollectionAppendCollection,
+       CqlGetCollection}}};
+
+ExchangeDataType
+exchangeType(const boost::any& object) {
+    const std::type_index type = object.type();
+    AnyTypeMap::const_iterator exchange_type_it = ANY_TYPE_MAP.find(type);
+    if (exchange_type_it == ANY_TYPE_MAP.end()) {
+        isc_throw(DbOperationError,
+                  "exchangeType(): boost::any type "
+                      << type.name() << " does not map to any exchange type");
     }
+    const ExchangeDataType exchange_type = exchange_type_it->second;
+    if (exchange_type >= CQL_FUNCTIONS.size()) {
+        isc_throw(BadValue,
+                  "exchangeType(): index " << exchange_type << " out of bounds "
+                                           << 0 << " - "
+                                           << (CQL_FUNCTIONS.size() - 1));
+    }
+    return exchange_type;
+}
+
+ExchangeDataType
+exchangeType(const CassValueType& type) {
+    CassTypeMap::const_iterator exchange_type_it = CASS_TYPE_MAP.find(type);
+    if (exchange_type_it == CASS_TYPE_MAP.end()) {
+        isc_throw(DbOperationError,
+                  "exchangeType(): Cassandra value type "
+                      << type << " does not map to any exchange type");
+    }
+    const ExchangeDataType exchange_type = exchange_type_it->second;
+    if (exchange_type >= CQL_FUNCTIONS.size()) {
+        isc_throw(BadValue,
+                  "exchangeType(): index " << exchange_type << " out of bounds "
+                                           << 0 << " - "
+                                           << CQL_FUNCTIONS.size() - 1);
+    }
+    return exchange_type;
 }
 
 void
-CqlCommon::getData(const CassRow* row,
-                   int pindex,
-                   int dindex,
-                   const SqlExchange& exchange,
-                   CqlDataArray& data) {
-    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_;
-        const CassValue* value = cass_row_get_column_by_name(row, name.c_str());
-        if (!value) {
-            isc_throw(BadValue, "column name " << name << " doesn't exist");
+CqlCommon::bindData(const AnyArray& data, CassStatement* statement) {
+    size_t i = 0u;
+    for (const boost::any& element : data) {
+        CassError cass_error;
+        try {
+            cass_error = CQL_FUNCTIONS[exchangeType(element)].cqlBindFunction_(
+                element, i, statement);
+        } catch (const boost::bad_any_cast& exception) {
+            isc_throw(DbOperationError,
+                      "CqlCommon::bindData(): "
+                          << exception.what() << " when binding parameter " << i
+                          << " which is of type " << element.type().name()
+                          << " with function CQL_FUNCTIONS["
+                          << exchangeType(element) << "].cqlBindFunction_()");
+        }
+        if (cass_error != CASS_OK) {
+            isc_throw(DbOperationError,
+                      "CqlCommon::bindData(): unable to bind parameter "
+                          << i << " which is of type " << element.type().name()
+                          << " with function CQL_FUNCTIONS["
+                          << exchangeType(element)
+                          << "].cqlBindFunction_(), Cassandra error code: "
+                          << cass_error_desc(cass_error));
         }
-        if (type >= sizeof(CqlFunctions) / sizeof(CqlFunctions[0])) {
-            isc_throw(BadValue, "index " << type << " out of bounds");
+        ++i;
+    }
+}
+
+void
+CqlCommon::getData(const CassRow* row, AnyArray& data) {
+    size_t i = 0u;
+    for (boost::any& element : data) {
+        const CassValue* value = cass_row_get_column(row, i);
+        CassError cass_error;
+        try {
+            cass_error = CQL_FUNCTIONS[exchangeType(element)].cqlGetFunction_(
+                element, value);
+        } catch (const boost::bad_any_cast& exception) {
+            isc_throw(DbOperationError,
+                      "CqlCommon::getData(): "
+                          << exception.what() << " when retrieving parameter "
+                          << i << " which is of type " << element.type().name()
+                          << " with function CQL_FUNCTIONS["
+                          << exchangeType(element) << "].cqlGetFunction_()");
         }
-        CassError cassError =
-            CqlFunctions[type].cqlGetFunction_(value, data.values_[dindex]);
-        if (cassError != CASS_OK) {
-            isc_throw(BadValue, "Cassandra error for column "
-                                    << name << " with message: "
-                                    << cass_error_desc(cassError));
+        if (cass_error != CASS_OK) {
+            isc_throw(
+                DbOperationError,
+                "CqlCommon::getData(): Cassandra error when retrieving column "
+                    << i << ", Cassandra error code: "
+                    << cass_error_desc(cass_error));
         }
+        ++i;
     }
 }
 
@@ -218,7 +711,9 @@ CqlExchange::convertToDatabaseTime(const time_t& cltt,
     cass_int64_t expire_time = static_cast<cass_int64_t>(cltt) + valid_lifetime;
 
     if (expire_time > DatabaseConnection::MAX_DB_TIME) {
-        isc_throw(BadValue, "Time value is too large: " << expire_time);
+        isc_throw(BadValue,
+                  "CqlExchange(): convertToDatabaseTime(): time value "
+                      << expire_time << " is too large");
     }
 
     expire = expire_time;
@@ -232,56 +727,63 @@ CqlExchange::convertFromDatabaseTime(const cass_int64_t& expire,
     cltt = static_cast<time_t>(expire - valid_lifetime);
 }
 
-void
-CqlExchange::createBindForReceive(CqlDataArray& /* data */,
-                                  const int /* statementIndex = -1 */) {
-    isc_throw(NotImplemented,
-              "CqlExchange::createBindForReceive() not implemented yet.");
-}
-
-CqlDataArray
-CqlExchange::executeRead(const CqlConnection& connection,
-                         const CqlDataArray& data,
-                         const int statementIndex,
-                         const bool single /* = false */,
-                         const std::vector<std::string>&
-                             parameters /* = std::vector<std::string>() */) {
+AnyArray
+CqlExchange::executeSelect(const CqlConnection& connection,
+                           const AnyArray& data,
+                           StatementTag statement_tag,
+                           const bool& single /* = false */) {
     CassError rc;
     CassStatement* statement = NULL;
     CassFuture* future = NULL;
+    AnyArray local_data = data;
 
-    statement = cass_prepared_bind(connection.statements_[statementIndex]);
-    if (!statement) {
+    StatementMap::const_iterator it =
+        connection.statements_.find(statement_tag);
+    if (it == connection.statements_.end()) {
         isc_throw(DbOperationError,
-                  "unable to bind statement "
-                      << connection.tagged_statements_[statementIndex].name_);
+                  "CqlExchange::executeSelect(): Statement "
+                      << statement_tag << "has not been prepared.");
+    }
+    CqlTaggedStatement tagged_statement = it->second;
+    if (tagged_statement.is_raw_statement_) {
+        // The entire query is the first element in data.
+        std::string* query = boost::any_cast<std::string*>(local_data.back());
+        local_data.pop_back();
+        statement = cass_statement_new(query->c_str(), local_data.size());
+    } else {
+        statement = cass_prepared_bind(tagged_statement.prepared_statement_);
+        if (!statement) {
+            isc_throw(DbOperationError,
+                      "CqlExchange::executeSelect(): unable to bind statement "
+                          << tagged_statement.name_);
+        }
     }
 
     if (connection.force_consistency_) {
         rc = cass_statement_set_consistency(statement, connection.consistency_);
         if (rc != CASS_OK) {
             cass_statement_free(statement);
-            isc_throw(
-                DbOperationError,
-                "unable to set statement consistency for statement "
-                    << connection.tagged_statements_[statementIndex].name_);
+            isc_throw(DbOperationError,
+                      "CqlExchange::executeSelect(): unable to set statement "
+                      "consistency for statement "
+                          << tagged_statement.name_
+                          << ", Cassandra error code: " << cass_error_desc(rc));
         }
     }
 
-    CqlCommon::bindData(statement, statementIndex, data, *this,
-                        connection.tagged_statements_);
+    CqlCommon::bindData(local_data, statement);
 
     future = cass_session_execute(connection.session_, statement);
     if (!future) {
         cass_statement_free(statement);
         isc_throw(DbOperationError,
-                  "unable to execute statement "
-                      << connection.tagged_statements_[statementIndex].name_);
+                  "CqlExchange::executeSelect(): no CassFuture for statement "
+                      << tagged_statement.name_);
     }
     cass_future_wait(future);
-    std::string error;
-    connection.checkStatementError(error, future, statementIndex,
-                                   "unable to execute statement");
+    const std::string error = connection.checkFutureError(
+        "CqlExchange::executeSelect(): cass_session_execute() != CASS_OK",
+        future, statement_tag);
     rc = cass_future_error_code(future);
     if (rc != CASS_OK) {
         cass_future_free(future);
@@ -290,43 +792,32 @@ CqlExchange::executeRead(const CqlConnection& connection,
     }
 
     // Get column values.
-    const CassResult* resultCollection = cass_future_get_result(future);
-    if (single && cass_result_row_count(resultCollection) > 1) {
-        cass_result_free(resultCollection);
+    const CassResult* result_collection = cass_future_get_result(future);
+    if (single && cass_result_row_count(result_collection) > 1) {
+        cass_result_free(result_collection);
         cass_future_free(future);
         cass_statement_free(statement);
-        isc_throw(MultipleRecords,
-                  "multiple records were found in the "
-                  "database where only one was expected for statement "
-                      << connection.tagged_statements_[statementIndex].name_);
+        isc_throw(
+            MultipleRecords,
+            "CqlExchange::executeSelect(): multiple records were found in "
+            "the database where only one was expected for statement "
+                << tagged_statement.name_);
     }
 
     // Get results.
-    CqlDataArray returnValues;
-    CqlDataArray collection;
-    CassIterator* rows = cass_iterator_from_result(resultCollection);
+    AnyArray return_values;
+    AnyArray collection;
+    CassIterator* rows = cass_iterator_from_result(result_collection);
     while (cass_iterator_next(rows)) {
         const CassRow* row = cass_iterator_get_row(rows);
-        createBindForReceive(returnValues, statementIndex);
-        for (size_t i = 0U; i < returnValues.size(); i++) {
-            uint32_t pindex = i;
-            if (!parameters.empty()) {
-                const ExchangeColumnInfoContainerName& idx =
-                    parameters_.get<1>();
-                const ExchangeColumnInfoContainerNameRange& range =
-                    idx.equal_range(parameters[i]);
-                if (std::distance(range.first, range.second) > 0) {
-                    pindex = (*range.first)->index_;
-                }
-            }
-            CqlCommon::getData(row, pindex, i, *this, returnValues);
-        }
+        createBindForSelect(return_values, statement_tag);
+        CqlCommon::getData(row, return_values);
         collection.add(retrieve());
     }
 
     // Free resources.
     cass_iterator_free(rows);
-    cass_result_free(resultCollection);
+    cass_result_free(result_collection);
     cass_future_free(future);
     cass_statement_free(statement);
 
@@ -334,45 +825,54 @@ CqlExchange::executeRead(const CqlConnection& connection,
 }
 
 void
-CqlExchange::executeWrite(const CqlConnection& connection,
-                          const CqlDataArray& data,
-                          const int statementIndex) {
+CqlExchange::executeMutation(
+    const CqlConnection& connection,
+    const AnyArray& data,
+    StatementTag statement_tag) {
     CassError rc;
     CassStatement* statement = NULL;
     CassFuture* future = NULL;
 
-    statement = cass_prepared_bind(connection.statements_[statementIndex]);
+    StatementMap::const_iterator it =
+        connection.statements_.find(statement_tag);
+    if (it == connection.statements_.end()) {
+        isc_throw(DbOperationError,
+                  "CqlExchange::executeSelect(): Statement "
+                      << statement_tag << "has not been prepared.");
+    }
+    CqlTaggedStatement tagged_statement = it->second;
+    statement = cass_prepared_bind(tagged_statement.prepared_statement_);
     if (!statement) {
         isc_throw(DbOperationError,
-                  "unable to bind statement "
-                      << connection.tagged_statements_[statementIndex].name_);
+                  "CqlExchange::executeMutation(): unable to bind statement "
+                      << tagged_statement.name_);
     }
 
     if (connection.force_consistency_) {
         rc = cass_statement_set_consistency(statement, connection.consistency_);
         if (rc != CASS_OK) {
             cass_statement_free(statement);
-            isc_throw(
-                DbOperationError,
-                "unable to set statement consistency for statement "
-                    << connection.tagged_statements_[statementIndex].name_);
+            isc_throw(DbOperationError,
+                      "CqlExchange::executeMutation(): unable to set statement "
+                      "consistency for statement "
+                          << tagged_statement.name_
+                          << ", Cassandra error code: " << cass_error_desc(rc));
         }
     }
 
-    CqlCommon::bindData(statement, statementIndex, data, *this,
-                        connection.tagged_statements_);
+    CqlCommon::bindData(data, statement);
 
     future = cass_session_execute(connection.session_, statement);
     if (!future) {
         cass_statement_free(statement);
         isc_throw(DbOperationError,
-                  "unable to execute statement "
-                      << connection.tagged_statements_[statementIndex].name_);
+                  "CqlExchange::executeMutation(): unable to execute statement "
+                      << tagged_statement.name_);
     }
     cass_future_wait(future);
-    std::string error;
-    connection.checkStatementError(error, future, statementIndex,
-                                   "unable to execute statement");
+    const std::string error = connection.checkFutureError(
+        "CqlExchange::executeMutation(): cass_session_execute() != CASS_OK",
+        future, statement_tag);
     rc = cass_future_error_code(future);
     if (rc != CASS_OK) {
         cass_future_free(future);
@@ -388,11 +888,10 @@ CqlExchange::executeWrite(const CqlConnection& connection,
     cass_statement_free(statement);
 
     if (!applied) {
-        isc_throw(DuplicateEntry,
-                  "[applied] is false. Entry already exists. "
-                  "Statement "
-                      << connection.tagged_statements_[statementIndex].name_
-                      << "has not been applied");
+        isc_throw(
+            StatementNotApplied,
+            "CqlExchange::executeMutation(): [applied] is false for statement "
+                << tagged_statement.name_);
     }
 }
 
@@ -400,85 +899,72 @@ bool
 CqlExchange::hasStatementBeenApplied(CassFuture* future,
                                      size_t* row_count,
                                      size_t* column_count) {
-    const CassResult* resultCollection = cass_future_get_result(future);
+    const CassResult* result_collection = cass_future_get_result(future);
     if (row_count) {
-        *row_count = cass_result_row_count(resultCollection);
+        *row_count = cass_result_row_count(result_collection);
     }
     if (column_count) {
-        *column_count = cass_result_column_count(resultCollection);
+        *column_count = cass_result_column_count(result_collection);
     }
-    CassIterator* rows = cass_iterator_from_result(resultCollection);
-    CqlDataArray data;
+    CassIterator* rows = cass_iterator_from_result(result_collection);
+    AnyArray data;
     cass_bool_t applied = cass_true;
     while (cass_iterator_next(rows)) {
         const CassRow* row = cass_iterator_get_row(rows);
         // [applied]: bool
-        data.add(reinterpret_cast<void*>(&applied));
-
-        const ExchangeColumnInfoContainerName& idx = parameters_.get<1>();
-        const ExchangeColumnInfoContainerNameRange& range =
-            idx.equal_range("[applied]");
-        if (std::distance(range.first, range.second) > 0) {
-            CqlCommon::getData(row, (*range.first)->index_, 0, *this, data);
-        }
+        data.add(&applied);
+        CqlCommon::getData(row, data);
     }
     cass_iterator_free(rows);
-    cass_result_free(resultCollection);
+    cass_result_free(result_collection);
     return applied == cass_true;
 }
 
-void*
-CqlExchange::retrieve() {
-    isc_throw(NotImplemented, "CqlExchange::retrieve() not implemented yet.");
-}
+constexpr StatementTag CqlVersionExchange::GET_VERSION;
+
+StatementMap CqlVersionExchange::tagged_statements_ = {
+    {GET_VERSION,   //
+     {GET_VERSION,  //
+      "SELECT "
+      "version, minor "
+      "FROM schema_version "}}  //
+};
 
 CqlVersionExchange::CqlVersionExchange() {
-    // Set the column names
-    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() == 2U);
 }
 
 CqlVersionExchange::~CqlVersionExchange() {
 }
 
 void
-CqlVersionExchange::createBindForReceive(CqlDataArray& data,
-                                         const int /* statementIndex = -1 */) {
+CqlVersionExchange::createBindForSelect(
+    AnyArray& data, StatementTag /* statement_tag = NULL */) {
     // Start with a fresh array.
     data.clear();
     // id: blob
-    data.add(reinterpret_cast<void*>(&version_));
+    data.add(&version_);
     // host_identifier: blob
-    data.add(reinterpret_cast<void*>(&minor_));
+    data.add(&minor_);
 }
 
-void*
+boost::any
 CqlVersionExchange::retrieve() {
     pair_ = VersionPair(version_, minor_);
-    return reinterpret_cast<void*>(&pair_);
+    return &pair_;
 }
 
 VersionPair
-CqlVersionExchange::retrieveVersion(const CqlConnection& connection,
-                                    int statementIndex) {
+CqlVersionExchange::retrieveVersion(const CqlConnection& connection) {
     // Run statement.
-    const CqlDataArray whereValues;
-    CqlDataArray versionCollection =
-        executeRead(connection, whereValues, statementIndex, true);
-
-    VersionPair result;
+    const AnyArray where_values;
+    AnyArray version_collection =
+        executeSelect(connection, where_values, GET_VERSION, true);
 
-    if (!versionCollection.empty()) {
-        result = *(reinterpret_cast<VersionPair*>(
-            *versionCollection.begin()));
+    if (!version_collection.empty()) {
+        return *boost::any_cast<VersionPair*>(*version_collection.begin());
     }
 
-    return result;
+    return VersionPair();
 }
 
 }  // namespace dhcp
index 2d2e5e4b1cc0dec0964841ad89b6ab0e93ac7a20..2f4f86ee24624072b60a5c9261f844c44e45d7f5 100644 (file)
@@ -1,6 +1,7 @@
-// Copyright (C) 2016 Deutsche Telekom AG.
+// Copyright (C) 2016-2017 Deutsche Telekom AG.
 //
-// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//          Andrei Pavel <andrei.pavel@qualitance.com>
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 #ifndef CQL_EXCHANGE_H
 #define CQL_EXCHANGE_H
 
-#include <cassandra.h>
 #include <dhcpsrv/cql_connection.h>
 #include <dhcpsrv/sql_common.h>
-#include <exceptions/exceptions.h>
+
+#include <boost/any.hpp>  // for boost::any
 
 #include <string>
+#include <typeinfo>       // for std::type_info
+#include <unordered_map>  // for std::unordered_map
 #include <utility>
 #include <vector>
 
 namespace isc {
 namespace dhcp {
 
-/// @brief Binds a C++ object to a Cassandra statement's parameter. Used in all
-///     statements.
-typedef CassError (*CqlBindFunction)(CassStatement* statement,
-                                     size_t index,
-                                     void* value);
-
-/// @brief Converts a single Cassandra column value to a C++ object. Used in
-///     SELECT statements.
-typedef CassError (*CqlGetFunction)(const CassValue* value, void* data);
-
-/// @brief Pair containing major and minor versions
-typedef std::pair<uint32_t, uint32_t> VersionPair;
-
-/// @brief Wrapper over the bind and get functions that interface with Cassandra
-struct CqlFunctionData {
-    /// @brief Binds a C++ object to a Cassandra statement's parameter. Used in
-    /// all
-    ///     statements.
-    CqlBindFunction cqlBindFunction_;
-    /// @brief Converts a single Cassandra column value to a C++ object. Used in
-    ///     SELECT statements.
-    CqlGetFunction cqlGetFunction_;
-};
+/// @brief Host identifier converted to Cassandra data type
+typedef std::vector<cass_byte_t> CassBlob;
 
-/// @brief Collection of bind and get functions, one pair for each
-///     parameter/column
-extern struct CqlFunctionData CqlFunctions[];
+/// @brief Forward declaration to @ref CqlExchange
+class CqlExchange;
 
 /// @brief Structure used to bind C++ input values to dynamic CQL parameters
 ///
@@ -65,68 +46,79 @@ extern struct CqlFunctionData CqlFunctions[];
 /// CQL statement execution. In other words, populating them with pointers that
 /// go out of scope before the statement is executed results in undefined
 /// behaviour.
-struct CqlDataArray {
-    /// @brief Constructor
-    CqlDataArray() {
-    }
+class AnyArray : public std::vector<boost::any> {
+public:
+    /// @brief Add a at the end of the vector.
+    void add(const boost::any& value);
+
+    /// @brief Remove the void pointer to the data value from a specified
+    ///     position inside the vector.
+    void remove(const size_t& index);
+};
 
-    /// @brief Copy constructor
-    CqlDataArray(const CqlDataArray& other) : values_(other.values_) {
-    }
+// @brief Representation of a Cassandra User Defined Type
+class Udt : public AnyArray {
+public:
+    /// @brief Paramterized constructor
+    Udt(const CqlConnection& connection, const std::string& name);
 
     /// @brief Destructor
-    virtual ~CqlDataArray() {
-    }
+    ~Udt();
 
-    /// @brief Add a at the end of the vector.
-    void add(void* value) {
-        values_.push_back(value);
-    }
+    /// @brief Frees the underlying container.
+    void freeUserType();
 
-    /// @brief Remove the void pointer to the data value from a specified
-    ///     position inside the vector.
-    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);
-    }
-
-    /// @brief Remove all data from the vector.
-    void clear() {
-        values_.clear();
-    }
-
-    /// @brief Get the number of elements inside the vector.
-    size_t size() const {
-        return values_.size();
-    }
-
-    /// @brief Check if the vector is empty.
-    bool empty() const {
-        return values_.empty();
-    }
-
-    /// @brief Square brackets operator overload
-    ///
-    /// Retrieves void pointer at specified position.
-    void* operator[](int i) const {
-        return values_[i];
-    }
-
-    /// @brief Iterator pointing to the first element
-    std::vector<void*>::const_iterator begin() const {
-        return values_.begin();
-    }
-
-    /// @brief Iterator pointing to the past-the-end element
-    std::vector<void*>::const_iterator end() const {
-        return values_.end();
-    }
-
-    /// @brief Vector of pointers to the data values
-    std::vector<void*> values_;
+    /// @brief Creates the underlying container.
+    void newUserType();
+
+    /// @brief Connection to the Cassandra database
+    const CqlConnection& connection_;
+
+    /// @brief Name of the UDT in the schema: CREATE TYPE ___ { ... }
+    const std::string name_;
+
+    /// @brief Internal Cassandra driver object representing a Cassandra data
+    ///     type
+    const CassDataType* cass_data_type_;
+
+    /// @brief Internal Cassandra driver object representing a user defined type
+    CassUserType* cass_user_type_;
+};
+
+typedef AnyArray Collection;
+
+/// @brief Binds a C++ object to a Cassandra statement's parameter. Used in all
+///     statements.
+typedef CassError (*CqlBindFunction)(const boost::any& value,
+                                     const size_t& index,
+                                     CassStatement* statement);
+
+/// @brief Sets a member in a UDT. Used in INSERT & UPDATE statements.
+typedef CassError (*CqlUdtSetFunction)(const boost::any& udt_member,
+                                       const size_t& position,
+                                       CassUserType* cass_user_type);
+
+/// @brief Sets an item in a collection. Used in INSERT & UPDATE statements.
+typedef CassError (*CqlCollectionAppendFunction)(const boost::any& value,
+                                                 CassCollection* collection);
+
+/// @brief Converts a single Cassandra column value to a C++ object. Used in
+///     SELECT statements.
+typedef CassError (*CqlGetFunction)(const boost::any& data,
+                                    const CassValue* value);
+
+/// @brief Wrapper over the bind and get functions that interface with Cassandra
+struct CqlFunction {
+    /// @brief Binds a C++ object to a Cassandra statement's parameter. Used in
+    ///     all statements.
+    CqlBindFunction cqlBindFunction_;
+    /// @brief Sets a member in a UDT. Used in INSERT & UPDATE statements.
+    CqlUdtSetFunction cqlUdtSetFunction_;
+    /// @brief Sets an item in a collection. Used in INSERT & UPDATE statements.
+    CqlCollectionAppendFunction cqlCollectionAppendFunction_;
+    /// @brief Converts a single Cassandra column value to a C++ object. Used in
+    ///     SELECT statements.
+    CqlGetFunction cqlGetFunction_;
 };
 
 /// @brief Cassandra Exchange
@@ -156,47 +148,43 @@ public:
 
     /// @brief Create BIND array to receive C++ data.
     ///
-    /// Used in executeRead() to retrieve from database
+    /// Used in executeSelect() to retrieve from database
     ///
     /// @param data array of bound objects representing data to be retrieved
-    /// @param statementIndex prepared statement to be executed; defaults to an
+    /// @param statement_tag prepared statement being executed; defaults to an
     ///     invalid index
-    virtual void createBindForReceive(CqlDataArray& data,
-                                      const int statementIndex = -1);
+    virtual void createBindForSelect(AnyArray& data,
+                                     StatementTag statement_tag = NULL) = 0;
 
-    /// @brief Executes select statements.
+    /// @brief Executes SELECT statements.
     ///
     /// @param connection connection used to communicate with the Cassandra
     ///     database
-    /// @param whereValues array of bound objects used to filter the results
-    /// @param statementIndex prepared statement being executed
+    /// @param where_values array of bound objects used to filter the results
+    /// @param statement_tag prepared statement being executed
     /// @param single true if a single row should be returned; by default,
     /// multiple rows are allowed
-    /// @param parameters Output parameters of a statement ( used in WHERE
-    ///     clause ); optional, needed only if parameters in the statement are
-    ///     in a different order than in the schema
     ///
-    /// @return collection of void* objects
+    /// @return collection of boost::any objects
     ///
     /// @throw DbOperationError
     /// @throw MultipleRecords
-    CqlDataArray executeRead(const CqlConnection& connection,
-                             const CqlDataArray& whereValues,
-                             const int statementIndex,
-                             const bool single = false,
-                             const std::vector<std::string>& parameters =
-                                 std::vector<std::string>());
-
-    /// @brief Executes insert, update, delete or other statements.
+    AnyArray executeSelect(const CqlConnection& connection,
+                           const AnyArray& where_values,
+                           StatementTag statement_tag,
+                           const bool& single = false);
+
+    /// @brief Executes INSERT, UPDATE or DELETE statements.
     ///
     /// @param connection connection used to communicate with the Cassandra
     ///     database
-    /// @param assignedValues array of bound objects to be used when inserting
+    /// @param assigned_values array of bound objects to be used when inserting
     ///     values
-    /// @param statementIndex prepared statement to be executed
-    void executeWrite(const CqlConnection& connection,
-                      const CqlDataArray& assignedValues,
-                      const int statementIndex);
+    /// @param statement_tag prepared statement being executed
+    ///     applied, could be different for commit than it is for rollback
+    void executeMutation(const CqlConnection& connection,
+                         const AnyArray& assigned_values,
+                         StatementTag statement_tag);
 
     /// @brief Check if CQL statement has been applied.
     ///
@@ -204,6 +192,9 @@ public:
     /// @param row_count number of rows returned
     /// @param column_count number of columns queried
     ///
+    /// On insert, a false [applied] means there is a duplicate entry with the
+    ///     same priumary key.
+    ///
     /// @return true if statement has been succesfully applied, false otherwise
     bool hasStatementBeenApplied(CassFuture* future,
                                  size_t* row_count = NULL,
@@ -212,11 +203,11 @@ public:
     /// @brief Copy received data into the derived class' object.
     ///
     /// Copies information about the entity to be retrieved into a holistic
-    /// object. Called in @ref executeRead(). Not implemented for base class
+    /// object. Called in @ref executeSelect(). Not implemented for base class
     /// CqlExchange. To be implemented in derived classes.
     ///
     /// @return a pointer to the object retrieved.
-    virtual void* retrieve();
+    virtual boost::any retrieve() = 0;
 };
 
 /// @brief Exchange used to retrieve schema version from the keyspace.
@@ -232,93 +223,80 @@ public:
 
     /// @brief Create BIND array to receive C++ data.
     ///
-    /// Used in executeRead() to retrieve from database
+    /// Used in executeSelect() to retrieve from database
     ///
     /// @param data array of bound objects representing data to be retrieved
-    /// @param statementIndex prepared statement to be executed; defaults to an
+    /// @param statement_tag prepared statement being executed; defaults to an
     ///     invalid index
-    virtual void createBindForReceive(CqlDataArray& data,
-                                      const int statementIndex = -1);
+    virtual void
+    createBindForSelect(AnyArray& data,
+                        StatementTag statement_tag = NULL) override;
 
     /// @brief Standalone method used to retrieve schema version
     ///
     /// @param connection array of bound objects representing data to be
     /// retrieved
-    /// @param statementIndex prepared statement to be executed
     ///
     /// @return version of schema specified in the prepared statement in the
     /// @ref CqlConnection parameter
-    virtual VersionPair
-    retrieveVersion(const CqlConnection& connection, int statementIndex);
+    virtual VersionPair retrieveVersion(const CqlConnection& connection);
 
     /// @brief Copy received data into the <version,minor> pair.
     ///
     /// Copies information about the version to be retrieved into a pair. Called
-    /// in executeRead().
+    /// in executeSelect().
     ///
     /// @return a pointer to the object retrieved.
-    virtual void* retrieve();
+    virtual boost::any retrieve() override;
+
+    /// @brief Statement tags definitions
+    /// @{
+    static constexpr StatementTag GET_VERSION = "GET_VERSION";
+    /// @}
+
+    /// @brief Cassandra statements
+    static StatementMap tagged_statements_;
 
 private:
+    /// @brief Major version
     cass_int32_t version_;
+    /// @brief Minor version
     cass_int32_t minor_;
+    /// @brief Pair containing major and minor version
     VersionPair pair_;
 };
 
 /// @brief Common operations in Cassandra exchanges
 class CqlCommon {
 public:
-    /// @brief Returns type of data for specific parameter.
-    ///
-    /// Returns type of a given parameter of a given statement.
+    /// @brief Give values to every column of an INSERT or an UPDATE 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,
-                const CqlTaggedStatement* tagged_statements);
-
-    /// @brief Binds data specified in data
+    /// Calls cqlBindFunction_() for every column with it's respective type.
     ///
-    /// 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,
-                         uint32_t stindex,
-                         const CqlDataArray& data,
-                         const SqlExchange& exchange,
-                         const CqlTaggedStatement* tagged_statements);
+    /// @param data array containing column values to be passed to the statement
+    ///     being executed
+    /// @param statement internal Cassandra object representing the statement
+    ///     being executed
+    static void bindData(const AnyArray& data, CassStatement* statement);
 
     /// @brief Retrieves data returned by Cassandra.
     ///
-    /// Calls one of cass_value_bind_([none|bool|int32|int64|string|bytes]).
-    /// Used to retrieve data returned by Cassandra into standard C++ types used
-    /// by Kea.
+    /// Calls cqlGetFunction_() for every column with it's respective type.
     ///
-    /// @param row row of data returned by CQL library
-    /// @param pindex Index of statement being executed
-    /// @param dindex data index (specifies which entry in size array is used)
-    /// @param exchange Exchange object to use
-    /// @param data array that has been created for the type of lease in
-    ///     question.
-    static void getData(const CassRow* row,
-                        int pindex,
-                        int dindex,
-                        const SqlExchange& exchange,
-                        CqlDataArray& data);
+    /// @param row internal Cassandra object containing data returned by
+    ///     Cassandra
+    /// @param data array containing objects to be populated with results
+    static void getData(const CassRow* row, AnyArray& data);
 };
 
+/// @brief Determine exchange type based on boost::any type.
+ExchangeDataType
+exchangeType(const boost::any& object);
+
+/// @brief Determine exchange type based on CassValueType.
+ExchangeDataType
+exchangeType(const CassValueType& type);
+
 }  // namespace dhcp
 }  // namespace isc
 
index 35a79f90b56e4ff0fa783ded5a41e0c9e723c692..9b4314cc9e5a14a13b2605945aed5afaf4734366 100644 (file)
@@ -1,6 +1,7 @@
-// Copyright (C) 2015 - 2017 Deutsche Telekom AG.
+// Copyright (C) 2015-2017 Deutsche Telekom AG.
 //
-// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//          Andrei Pavel <andrei.pavel@qualitance.com>
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 
 #include <config.h>
 
-#include <asiolink/io_address.h>
-#include <dhcp/duid.h>
-#include <dhcp/hwaddr.h>
 #include <dhcpsrv/cql_lease_mgr.h>
 #include <dhcpsrv/dhcpsrv_log.h>
 
-#include <limits>
+#include <dhcp/duid.h>
+#include <dhcp/hwaddr.h>
 
-using namespace isc;
-using namespace isc::dhcp;
-using namespace std;
+#include <asiolink/io_address.h>
+
+using isc::asiolink::IOAddress;
 
 namespace isc {
 namespace dhcp {
 
-static const size_t HOSTNAME_MAX_LEN = 255U;
-static const size_t ADDRESS6_TEXT_MAX_LEN = 39U;
-
-/// @{
-///
-/// @brief Catalog of all the CQL statements currently supported. Note
-/// that the order columns appear in statement body must match the order they
-/// that the occur in the table. This does not apply to the where clause.
-static const char* delete_lease4_params[] = {
-    static_cast<const char*>("address"),
-    NULL };
-static const char* delete_expired_lease4_params[] = {
-    static_cast<const char*>("state"),
-    static_cast<const char*>("expire"),
-    NULL };
-static const char* delete_lease6_params[] = {
-    static_cast<const char*>("address"),
-    NULL };
-static const char* delete_expired_lease6_params[] = {
-    static_cast<const char*>("state"),
-    static_cast<const char*>("expire"),
-    NULL };
-static const char* get_lease4_addr_params[] = {
-    static_cast<const char*>("address"),
-    NULL };
-static const char* get_lease4_clientid_params[] = {
-    static_cast<const char*>("client_id"),
-    NULL };
-static const char* get_lease4_clientid_subid_params[] = {
-    static_cast<const char*>("client_id"),
-    static_cast<const char*>("subnet_id"),
-    NULL };
-static const char* get_lease4_hwaddr_params[] = {
-    static_cast<const char*>("hwaddr"),
-    NULL };
-static const char* get_lease4_hwaddr_subid_params[] = {
-    static_cast<const char*>("hwaddr"),
-    static_cast<const char*>("subnet_id"),
-    NULL };
-static const char* get_lease4_expired_params[] = {
-    static_cast<const char*>("state"),
-    static_cast<const char*>("expire"),
-    static_cast<const char*>("limit"),
-    NULL };
-static const char* get_lease6_addr_params[] = {
-    static_cast<const char*>("address"),
-    static_cast<const char*>("lease_type"),
-    NULL };
-static const char* get_lease6_duid_iaid_params[] = {
-    static_cast<const char*>("duid"),
-    static_cast<const char*>("iaid"),
-    static_cast<const char*>("lease_type"),
-    NULL };
-static const char* get_lease6_duid_iaid_subid_params[] = {
-    static_cast<const char*>("duid"),
-    static_cast<const char*>("iaid"),
-    static_cast<const char*>("subnet_id"),
-    static_cast<const char*>("lease_type"),
-    NULL };
-static const char* get_lease6_expired_params[] = {
-    static_cast<const char*>("state"),
-    static_cast<const char*>("expire"),
-    static_cast<const char*>("limit"),
-    NULL };
-static const char* get_version_params[] = {
-    NULL };
-static const char* insert_lease4_params[] = {
-    static_cast<const char*>("address"),
-    static_cast<const char*>("hwaddr"),
-    static_cast<const char*>("client_id"),
-    static_cast<const char*>("valid_lifetime"),
-    static_cast<const char*>("expire"),
-    static_cast<const char*>("subnet_id"),
-    static_cast<const char*>("fqdn_fwd"),
-    static_cast<const char*>("fqdn_rev"),
-    static_cast<const char*>("hostname"),
-    static_cast<const char*>("state"),
-    NULL };
-static const char* insert_lease6_params[] = {
-    static_cast<const char*>("address"),
-    static_cast<const char*>("duid"),
-    static_cast<const char*>("valid_lifetime"),
-    static_cast<const char*>("expire"),
-    static_cast<const char*>("subnet_id"),
-    static_cast<const char*>("pref_lifetime"),
-    static_cast<const char*>("lease_type"),
-    static_cast<const char*>("iaid"),
-    static_cast<const char*>("prefix_len"),
-    static_cast<const char*>("fqdn_fwd"),
-    static_cast<const char*>("fqdn_rev"),
-    static_cast<const char*>("hostname"),
-    static_cast<const char*>("hwaddr"),
-    static_cast<const char*>("hwtype"),
-    static_cast<const char*>("hwaddr_source"),
-    static_cast<const char*>("state"),
-    NULL };
-static const char* update_lease4_params[] = {
-    static_cast<const char*>("hwaddr"),
-    static_cast<const char*>("client_id"),
-    static_cast<const char*>("valid_lifetime"),
-    static_cast<const char*>("expire"),
-    static_cast<const char*>("subnet_id"),
-    static_cast<const char*>("fqdn_fwd"),
-    static_cast<const char*>("fqdn_rev"),
-    static_cast<const char*>("hostname"),
-    static_cast<const char*>("state"),
-    static_cast<const char*>("address"),
-    NULL };
-static const char* update_lease6_params[] = {
-    static_cast<const char*>("duid"),
-    static_cast<const char*>("valid_lifetime"),
-    static_cast<const char*>("expire"),
-    static_cast<const char*>("subnet_id"),
-    static_cast<const char*>("pref_lifetime"),
-    static_cast<const char*>("lease_type"),
-    static_cast<const char*>("iaid"),
-    static_cast<const char*>("prefix_len"),
-    static_cast<const char*>("fqdn_fwd"),
-    static_cast<const char*>("fqdn_rev"),
-    static_cast<const char*>("hostname"),
-    static_cast<const char*>("hwaddr"),
-    static_cast<const char*>("hwtype"),
-    static_cast<const char*>("hwaddr_source"),
-    static_cast<const char*>("state"),
-    static_cast<const char*>("address"),
-    NULL };
-/// @}
-
-CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
-    // DELETE_LEASE4
-    { delete_lease4_params,
-      "delete_lease4",
-      "DELETE FROM lease4 WHERE address = ? "
-      "IF EXISTS "
-    },
-
-    // DELETE_LEASE4_STATE_EXPIRED
-    { delete_expired_lease4_params,
-      "delete_lease4_expired",
-      "SELECT address, hwaddr, client_id, "
-        "valid_lifetime, expire, subnet_id, "
-        "fqdn_fwd, fqdn_rev, hostname, "
-        "state "
-      "FROM lease4 "
-      "WHERE state = ? "
-      "AND expire < ? "
-      "ALLOW FILTERING "
-    },
-
-    // DELETE_LEASE6
-    { delete_lease6_params,
-      "delete_lease6",
-      "DELETE FROM lease6 WHERE address = ? "
-      "IF EXISTS "
-    },
-
-    // DELETE_LEASE6_STATE_EXPIRED
-    { delete_expired_lease6_params,
-      "delete_lease6_expired",
-      "SELECT address, duid, valid_lifetime, "
-        "expire, subnet_id, pref_lifetime, "
-        "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
-        "hwaddr, hwtype, hwaddr_source, "
-        "state "
-      "FROM lease6 "
-      "WHERE state = ? "
-      "AND expire < ? "
-      "ALLOW FILTERING "
-    },
-
-    // GET_LEASE4_ADDR
-    { get_lease4_addr_params,
-      "get_lease4_addr",
-      "SELECT address, hwaddr, client_id, "
-        "valid_lifetime, expire, subnet_id, "
-        "fqdn_fwd, fqdn_rev, hostname, "
-        "state "
-      "FROM lease4 "
-      "WHERE address = ? "
-    },
-
-    // GET_LEASE4_CLIENTID
-    { get_lease4_clientid_params,
-      "get_lease4_clientid",
-      "SELECT address, hwaddr, client_id, "
-        "valid_lifetime, expire, subnet_id, "
-        "fqdn_fwd, fqdn_rev, hostname, "
-        "state "
-      "FROM lease4 "
-      "WHERE client_id = ? "
-    },
-
-    // GET_LEASE4_CLIENTID_SUBID
-    { get_lease4_clientid_subid_params,
-      "get_lease4_clientid_subid",
-      "SELECT address, hwaddr, client_id, "
-        "valid_lifetime, expire, subnet_id, "
-        "fqdn_fwd, fqdn_rev, hostname, "
-        "state "
-      "FROM lease4 "
-      "WHERE client_id = ? "
-      "AND subnet_id = ? "
-      "ALLOW FILTERING "
-    },
-
-    // GET_LEASE4_HWADDR
-    { get_lease4_hwaddr_params,
-      "get_lease4_hwaddr",
-      "SELECT address, hwaddr, client_id, "
-        "valid_lifetime, expire, subnet_id, "
-        "fqdn_fwd, fqdn_rev, hostname, "
-        "state "
-      "FROM lease4 "
-      "WHERE hwaddr = ? "
-    },
-
-    // GET_LEASE4_HWADDR_SUBID
-    { get_lease4_hwaddr_subid_params,
-      "get_lease4_hwaddr_subid",
-      "SELECT address, hwaddr, client_id, "
-        "valid_lifetime, expire, subnet_id, "
-        "fqdn_fwd, fqdn_rev, hostname, "
-        "state "
-      "FROM lease4 "
-      "WHERE hwaddr = ? "
-      "AND subnet_id = ? "
-      "ALLOW FILTERING "
-    },
-
-    // GET_LEASE4_EXPIRE
-    { get_lease4_expired_params,
-      "get_lease4_expired",
-      "SELECT address, hwaddr, client_id, "
-        "valid_lifetime, expire, subnet_id, "
-        "fqdn_fwd, fqdn_rev, hostname, "
-        "state "
-      "FROM lease4 "
-      "WHERE state = ? "
-      "AND expire < ? "
-      "LIMIT ? "
-      "ALLOW FILTERING "
-    },
-
-    // GET_LEASE6_ADDR
-    { get_lease6_addr_params,
-      "get_lease6_addr",
-      "SELECT address, duid, valid_lifetime, "
-        "expire, subnet_id, pref_lifetime, "
-        "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
-        "hwaddr, hwtype, hwaddr_source, "
-        "state "
-      "FROM lease6 "
-      "WHERE address = ? "
-      "AND lease_type = ? "
-      "ALLOW FILTERING "
-    },
-
-    // GET_LEASE6_DUID_IAID
-    { get_lease6_duid_iaid_params,
-      "get_lease6_duid_iaid",
-      "SELECT address, duid, valid_lifetime, "
-        "expire, subnet_id, pref_lifetime, "
-        "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
-        "hwaddr, hwtype, hwaddr_source, "
-        "state "
-      "FROM lease6 "
-      "WHERE duid = ? "
-      "AND iaid = ? "
-      "AND lease_type = ? "
-      "ALLOW FILTERING "
-    },
-
-    // GET_LEASE6_DUID_IAID_SUBID
-    { get_lease6_duid_iaid_subid_params,
-      "get_lease6_duid_iaid_subid",
-      "SELECT address, duid, valid_lifetime, "
-        "expire, subnet_id, pref_lifetime, "
-        "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
-        "hwaddr, hwtype, hwaddr_source, "
-        "state "
-      "FROM lease6 "
-      "WHERE duid = ? "
-      "AND iaid = ? "
-      "AND subnet_id = ? "
-      "AND lease_type = ? "
-      "ALLOW FILTERING "
-    },
-
-    // GET_LEASE6_EXPIRE
-    { get_lease6_expired_params,
-      "get_lease6_expired",
-      "SELECT address, duid, valid_lifetime, "
-        "expire, subnet_id, pref_lifetime, "
-        "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
-        "hwaddr, hwtype, hwaddr_source, "
-        "state "
-      "FROM lease6 "
-      "WHERE state = ? "
-      "AND expire < ? "
-      "LIMIT ? "
-      "ALLOW FILTERING "
-    },
-
-    // GET_VERSION
-    { get_version_params,
-      "get_version",
-      "SELECT version, minor FROM schema_version "
-    },
-
-    // INSERT_LEASE4
-    { insert_lease4_params,
-      "insert_lease4",
-      "INSERT INTO lease4(address, hwaddr, client_id, "
-        "valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, "
-        "state) "
-      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, "
-      "?) "
-      "IF NOT EXISTS "
-    },
-
-    // INSERT_LEASE6
-    { insert_lease6_params,
-      "insert_lease6",
-      "INSERT INTO lease6(address, duid, valid_lifetime, "
-        "expire, subnet_id, pref_lifetime, "
-        "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, "
-        "hwtype, hwaddr_source, "
-        "state) "
-      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, "
-      "?) "
-      "IF NOT EXISTS "
-    },
-
-    // UPDATE_LEASE4
-    { update_lease4_params,
-      "update_lease4",
-      "UPDATE lease4 SET hwaddr = ?, "
-        "client_id = ?, valid_lifetime = ?, expire = ?, "
-        "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, hostname = ?, "
-        "state = ? "
-      "WHERE address = ? "
-      "IF EXISTS "
-    },
-
-    // UPDATE_LEASE6
-    { update_lease6_params,
-      "update_lease6",
-      "UPDATE lease6 SET duid = ?, "
-        "valid_lifetime = ?, expire = ?, subnet_id = ?, "
-        "pref_lifetime = ?, lease_type = ?, iaid = ?, "
-        "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, hostname = ?, "
-        "hwaddr = ?, hwtype = ?, hwaddr_source = ?, "
-        "state = ? "
-      "WHERE address = ? "
-      "IF EXISTS "
-    },
-
+static constexpr size_t HOSTNAME_MAX_LEN = 255u;
+static constexpr size_t ADDRESS6_TEXT_MAX_LEN = 39u;
 
-    // End of list sentinel
-    { NULL, NULL, NULL }
-};
+static const CassBlob NULL_HWADDR({0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
+static const CassBlob NULL_CLIENTID({0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
+static const CassBlob NULL_PRIVACY_HASH({0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
 
 /// @brief Common CQL and Lease Data Methods
 ///
@@ -401,22 +45,59 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
 /// base to both of them, containing some common methods.
 class CqlLeaseExchange : public CqlExchange {
 public:
-    CqlLeaseExchange()
-        : valid_lifetime_(0), expire_(0), subnet_id_(0), fqdn_fwd_(cass_false),
-          fqdn_rev_(cass_false), state_(0) {
+    CqlLeaseExchange(const CqlConnection &connection)
+        : connection_(connection), valid_lifetime_(0), expire_(0),
+          subnet_id_(0), fqdn_fwd_(cass_false), fqdn_rev_(cass_false),
+          state_(0) {
     }
 
+    /// @brief Create BIND array to receive C++ data.
+    ///
+    /// Used in executeSelect() to retrieve from database
+    ///
+    /// @param data array of bound objects representing data to be retrieved
+    /// @param statement_tag prepared statement being executed; defaults to an
+    ///     invalid index
+    virtual void
+    createBindForSelect(AnyArray &data,
+                        StatementTag statement_tag = NULL) override = 0;
+
+    /// @brief Copy received data into the derived class' object.
+    ///
+    /// Copies information about the entity to be retrieved into a holistic
+    /// object. Called in @ref executeSelect(). Not implemented for base class
+    /// CqlExchange. To be implemented in derived classes.
+    ///
+    /// @return a pointer to the object retrieved.
+    virtual boost::any retrieve() override = 0;
+
 protected:
-    std::vector<cass_byte_t> hwaddr_;  ///< Hardware address
-    cass_int64_t valid_lifetime_;      ///< Lease time
-    cass_int64_t expire_;              ///< Lease expiry time
-    cass_int32_t subnet_id_;           ///< Subnet identification
-    cass_bool_t fqdn_fwd_;             ///< Has forward DNS update
-                                       /// been performed
-    cass_bool_t fqdn_rev_;             ///< Has reverse DNS update
-                                       /// been performed
-    std::string hostname_;             ///< Client hostname
-    cass_int32_t state_;               ///< Lease state
+    /// @brief Database connection
+    const CqlConnection &connection_;
+
+    /// @brief Hardware address
+    CassBlob hwaddr_;
+
+    /// @brief Lease timer
+    cass_int64_t valid_lifetime_;
+
+    /// @brief Lease expiry time
+    cass_int64_t expire_;
+
+    /// @brief Subnet identification
+    cass_int32_t subnet_id_;
+
+    /// @brief Has forward DNS update been performed
+    cass_bool_t fqdn_fwd_;
+
+    /// @brief Has reverse DNS update been performed
+    cass_bool_t fqdn_rev_;
+
+    /// @brief Client hostname
+    std::string hostname_;
+
+    /// @brief Lease state
+    cass_int32_t state_;
 };
 
 /// @brief Exchange CQL and Lease4 Data
@@ -431,240 +112,591 @@ protected:
 ///
 /// @note There are no unit tests for this class. It is tested indirectly
 /// in all CqlLeaseMgr::xxx4() calls where it is used.
-
 class CqlLease4Exchange : public CqlLeaseExchange {
 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.
-    CqlLease4Exchange() : addr4_(0) {
-        // Set the column names
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "address", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "hwaddr", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_BYTES)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "client_id", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_BYTES)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "valid_lifetime", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_INT64)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "expire", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_TIMESTAMP)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "subnet_id", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "fqdn_fwd", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_BOOL)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "fqdn_rev", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_BOOL)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "hostname", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_STRING)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "state", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "limit", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "[applied]", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_BOOL)));
-        BOOST_ASSERT(parameters_.size() == 12U);
-    }
+    explicit CqlLease4Exchange(const CqlConnection &connection);
 
     /// @brief Create CQL_BIND objects for Lease4 Pointer
     ///
     /// Fills in the CQL_BIND array for sending data in the Lease4 object to
-    /// the database.
-    void createBindForSend(const Lease4Ptr& lease, CqlDataArray& data) {
-        if (!lease) {
-            isc_throw(BadValue, "createBindForSend(): Lease4 object is NULL");
-        }
-        // Store lease object to ensure it remains valid.
-        lease_ = lease;
-        // Set up the structures for the various components of the lease4
-        // structure.
-
-        try {
-            // address: int
-            // The address in the Lease structure is an IOAddress object.
-            // Convert this to an integer for storage.
-            addr4_ = static_cast<cass_int32_t>(lease_->addr_.toUint32());
-            data.add(reinterpret_cast<void*>(&addr4_));
-
-            // hwaddr: blob
-            if (lease_->hwaddr_) {
-                if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
-                    isc_throw(DbOperationError,
-                              "hardware address "
-                                  << lease_->hwaddr_->toText() << " of length "
-                                  << lease_->hwaddr_->hwaddr_.size()
-                                  << " exceeds maximum allowed length of "
-                                  << HWAddr::MAX_HWADDR_LEN);
-                }
-                hwaddr_ = lease_->hwaddr_->hwaddr_;
-            } else {
-                hwaddr_.clear();
-            }
-            data.add(reinterpret_cast<void*>(&hwaddr_));
+    /// the database. Used for INSERT statements.
+    void createBindForInsert(const Lease4Ptr &lease, AnyArray &data);
 
-            // client_id: blob
-            if (lease_->client_id_) {
-                client_id_ = lease_->client_id_->getClientId();
-            } else {
-                client_id_.clear();
-            }
-            data.add(reinterpret_cast<void*>(&client_id_));
-
-            // valid lifetime: bigint
-            valid_lifetime_ = static_cast<cass_int32_t>(lease_->valid_lft_);
-            data.add(reinterpret_cast<void*>(&valid_lifetime_));
-
-            // expire: bigint
-            // The lease structure holds the client last transmission time
-            /// (cltt_)
-            // For convenience for external tools, this is converted to lease
-            // expiry time (expire). The relationship is given by:
-            // expire = cltt_ + valid_lft_
-            CqlExchange::convertToDatabaseTime(lease_->cltt_,
-                                               lease_->valid_lft_, expire_);
-            data.add(reinterpret_cast<void*>(&expire_));
-
-            // subnet_id: int
-            subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
-            data.add(reinterpret_cast<void*>(&subnet_id_));
-
-            // fqdn_fwd: boolean
-            fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
-            data.add(reinterpret_cast<void*>(&fqdn_fwd_));
-
-            // fqdn_rev: boolean
-            fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
-            data.add(reinterpret_cast<void*>(&fqdn_rev_));
-
-            // hostname: varchar
-            if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
-                isc_throw(BadValue, "hostname "
-                                        << lease_->hostname_ << " of length "
-                                        << lease_->hostname_.size()
-                                        << " exceeds maximum allowed length of "
-                                        << HOSTNAME_MAX_LEN);
-            }
-            hostname_ = lease_->hostname_;
-            data.add(reinterpret_cast<void*>(&hostname_));
+    /// @brief Create CQL_BIND objects for Lease4 Pointer
+    ///
+    /// Fills in the CQL_BIND array for sending data in the Lease4 object to
+    /// the database. Used for UPDATE statements.
+    void createBindForUpdate(const Lease4Ptr &lease,
+                             AnyArray &data,
+                             StatementTag statement_tag = NULL);
 
-            // state: int
-            state_ = static_cast<cass_int32_t>(lease_->state_);
-            data.add(reinterpret_cast<void*>(&state_));
-        } catch (const std::exception& ex) {
-            isc_throw(DbOperationError,
-                      "could not create bind array from Lease4: "
-                          << lease_->addr_.toText()
-                          << ", reason: " << ex.what());
-        }
-    }
+    /// @brief Create CQL_BIND objects for Lease4 Pointer
+    ///
+    /// Fills in the CQL_BIND array for sending data in the Lease4 object to
+    /// the database. Used for DELETE statements.
+    void createBindForDelete(const IOAddress &address,
+                             AnyArray &data,
+                             StatementTag statement_tag = NULL);
 
     /// @brief Create BIND array to receive data
     ///
     /// Creates a CQL_BIND array to receive Lease4 data from the database.
-    virtual void createBindForReceive(CqlDataArray& data,
-                                      const int /* statementIndex */ = -1) {
+    virtual void
+    createBindForSelect(AnyArray &data,
+                        StatementTag statement_tag = NULL) override;
+
+    virtual boost::any retrieve() override;
+
+    void getLeaseCollection(StatementTag &statement_tag,
+                            AnyArray &data,
+                            Lease4Collection &result);
+
+    void
+    getLease(StatementTag &statement_tag, AnyArray &data, Lease4Ptr &result);
+
+    void getExpiredLeases(const size_t &max_leases,
+                          Lease4Collection &expired_leases);
+
+    /// @brief Cassandra statements
+    static StatementMap tagged_statements_;
+
+    /// @brief Statement tags definitions
+    /// @{
+    // Add entry to lease4 table
+    static constexpr StatementTag INSERT_LEASE4 = "INSERT_LEASE4";
+    // Update a Lease4 entry
+    static constexpr StatementTag UPDATE_LEASE4 = "UPDATE_LEASE4";
+    // Delete from lease4 by address
+    static constexpr StatementTag DELETE_LEASE4 = "DELETE_LEASE4";
+    // Delete expired lease4s in certain state
+    static constexpr StatementTag GET_LEASE4_EXPIRE = "GET_LEASE4_EXPIRE";
+    // Get lease4 by address
+    static constexpr StatementTag GET_LEASE4_ADDR = "GET_LEASE4_ADDR";
+    // Get lease4 by client ID
+    static constexpr StatementTag GET_LEASE4_CLIENTID = "GET_LEASE4_CLIENTID";
+    // Get lease4 by client ID & subnet ID
+    static constexpr StatementTag GET_LEASE4_CLIENTID_SUBID =
+        "GET_LEASE4_CLIENTID_SUBID";
+    // Get lease4 by HW address
+    static constexpr StatementTag GET_LEASE4_HWADDR = "GET_LEASE4_HWADDR";
+    // Get lease4 by HW address & subnet ID
+    static constexpr StatementTag GET_LEASE4_HWADDR_SUBID =
+        "GET_LEASE4_HWADDR_SUBID";
+    /// @}
+
+private:
+    // Pointer to lease object
+    Lease4Ptr lease_;
+    // IPv4 address
+    cass_int32_t address_;
+    // Client identification
+    CassBlob client_id_;
+};  // CqlLease4Exchange
+
+constexpr StatementTag CqlLease4Exchange::INSERT_LEASE4;
+constexpr StatementTag CqlLease4Exchange::UPDATE_LEASE4;
+constexpr StatementTag CqlLease4Exchange::DELETE_LEASE4;
+constexpr StatementTag CqlLease4Exchange::GET_LEASE4_EXPIRE;
+constexpr StatementTag CqlLease4Exchange::GET_LEASE4_ADDR;
+constexpr StatementTag CqlLease4Exchange::GET_LEASE4_CLIENTID;
+constexpr StatementTag CqlLease4Exchange::GET_LEASE4_CLIENTID_SUBID;
+constexpr StatementTag CqlLease4Exchange::GET_LEASE4_HWADDR;
+constexpr StatementTag CqlLease4Exchange::GET_LEASE4_HWADDR_SUBID;
+
+StatementMap CqlLease4Exchange::tagged_statements_{
+
+    {INSERT_LEASE4,   //
+     {INSERT_LEASE4,  //
+      "INSERT INTO lease4( "
+      "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
+      "fqdn_fwd, fqdn_rev, hostname, state "
+      ") VALUES ( "
+      "?, ?, ?, ?, ?, ?, ?, ?, ?, ? "
+      ") "
+      "IF NOT EXISTS "}},
+
+    {UPDATE_LEASE4,   //
+     {UPDATE_LEASE4,  //
+      "UPDATE lease4 SET "
+      "hwaddr = ?, "
+      "client_id = ?, "
+      "subnet_id = ?, "
+      "valid_lifetime = ?, "
+      "expire = ?, "
+      "fqdn_fwd = ?, "
+      "fqdn_rev = ?, "
+      "hostname = ?, "
+      "state = ? "
+      "WHERE address = ? "
+      "IF EXISTS "}},
+
+    {DELETE_LEASE4,   //
+     {DELETE_LEASE4,  //
+      "DELETE FROM lease4 "
+      "WHERE address = ? "
+      "IF EXISTS "}},
+
+    {GET_LEASE4_EXPIRE,   //
+     {GET_LEASE4_EXPIRE,  //
+      "SELECT "
+      "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
+      "fqdn_fwd, fqdn_rev, hostname, state "
+      "FROM lease4 "
+      "WHERE state = ? "
+      "AND expire < ? "
+      "LIMIT ? "
+      "ALLOW FILTERING "}},
+
+    {GET_LEASE4_ADDR,   //
+     {GET_LEASE4_ADDR,  //
+      "SELECT "
+      "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
+      "fqdn_fwd, fqdn_rev, hostname, state "
+      "FROM lease4 "
+      "WHERE address = ? "}},
+
+    {GET_LEASE4_CLIENTID,   //
+     {GET_LEASE4_CLIENTID,  //
+      "SELECT "
+      "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
+      "fqdn_fwd, fqdn_rev, hostname, state "
+      "FROM lease4 "
+      "WHERE client_id = ? "
+      "ALLOW FILTERING "}},
+
+    {GET_LEASE4_CLIENTID_SUBID,   //
+     {GET_LEASE4_CLIENTID_SUBID,  //
+      "SELECT "
+      "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
+      "fqdn_fwd, fqdn_rev, hostname, state "
+      "FROM lease4 "
+      "WHERE client_id = ? "
+      "AND subnet_id = ? "
+      "ALLOW FILTERING "}},
+
+    {GET_LEASE4_HWADDR,   //
+     {GET_LEASE4_HWADDR,  //
+      "SELECT "
+      "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
+      "fqdn_fwd, fqdn_rev, hostname, state "
+      "FROM lease4 "
+      "WHERE hwaddr = ? "
+      "ALLOW FILTERING "}},
+
+    {GET_LEASE4_HWADDR_SUBID,   //
+     {GET_LEASE4_HWADDR_SUBID,  //
+      "SELECT "
+      "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
+      "fqdn_fwd, fqdn_rev, hostname, state "
+      "FROM lease4 "
+      "WHERE hwaddr = ? "
+      "AND subnet_id = ? "
+      "ALLOW FILTERING "}},
+
+};
+
+CqlLease4Exchange::CqlLease4Exchange(const CqlConnection &connection)
+    : CqlLeaseExchange(connection), address_(0) {
+}
+
+void
+CqlLease4Exchange::createBindForInsert(const Lease4Ptr &lease, AnyArray &data) {
+    if (!lease) {
+        isc_throw(BadValue, "CqlLease4Exchange::createBindForInsert(): "
+                            "Lease4 object is NULL");
+    }
+    // Store lease object to ensure it remains valid.
+    lease_ = lease;
+    // Set up the structures for the various components of the lease4
+    // structure.
+
+    try {
         // address: int
-        data.add(reinterpret_cast<void*>(&addr4_));
+        // The address in the Lease structure is an IOAddress object.
+        // Convert this to an integer for storage.
+        address_ = static_cast<cass_int32_t>(lease->addr_.toUint32());
 
         // hwaddr: blob
-        data.add(reinterpret_cast<void*>(&hwaddr_));
+        if (lease_->hwaddr_ && lease->hwaddr_->hwaddr_.size() > 0) {
+            if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+                isc_throw(DbOperationError,
+                          "hardware address "
+                              << lease_->hwaddr_->toText() << " of length "
+                              << lease_->hwaddr_->hwaddr_.size()
+                              << " exceeds maximum allowed length of "
+                              << HWAddr::MAX_HWADDR_LEN);
+            }
+            hwaddr_ = lease_->hwaddr_->hwaddr_;
+        } else {
+            hwaddr_.clear();
+        }
 
         // client_id: blob
-        data.add(reinterpret_cast<void*>(&client_id_));
+        if (lease_->client_id_ && lease->client_id_->getClientId().size() > 0) {
+            client_id_ = lease_->client_id_->getClientId();
+        } else {
+            client_id_.clear();
+        }
 
-        // valid_lifetime: bigint
-        data.add(reinterpret_cast<void*>(&valid_lifetime_));
+        // valid lifetime: bigint
+        valid_lifetime_ = static_cast<cass_int32_t>(lease_->valid_lft_);
 
         // expire: bigint
-        data.add(reinterpret_cast<void*>(&expire_));
+        // The lease structure holds the client last transmission time
+        /// (cltt_)
+        // For convenience for external tools, this is converted to lease
+        // expiry time (expire). The relationship is given by:
+        // expire = cltt_ + valid_lft_
+        CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
+                                           expire_);
 
         // subnet_id: int
-        data.add(reinterpret_cast<void*>(&subnet_id_));
+        subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
 
         // fqdn_fwd: boolean
-        data.add(reinterpret_cast<void*>(&fqdn_fwd_));
+        fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
 
         // fqdn_rev: boolean
-        data.add(reinterpret_cast<void*>(&fqdn_rev_));
+        fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
 
         // hostname: varchar
-        data.add(reinterpret_cast<void*>(&hostname_));
+        if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
+            isc_throw(BadValue,
+                      "hostname " << lease_->hostname_ << " of length "
+                                  << lease_->hostname_.size()
+                                  << " exceeds maximum allowed length of "
+                                  << HOSTNAME_MAX_LEN);
+        }
+        hostname_ = lease_->hostname_;
 
         // state: int
-        data.add(reinterpret_cast<void*>(&state_));
+        state_ = static_cast<cass_int32_t>(lease_->state_);
+
+        // Start with a fresh array.
+        data.clear();
+        data.add(&address_);
+        data.add(&hwaddr_);
+        data.add(&client_id_);
+        data.add(&valid_lifetime_);
+        data.add(&expire_);
+        data.add(&subnet_id_);
+        data.add(&fqdn_fwd_);
+        data.add(&fqdn_rev_);
+        data.add(&hostname_);
+        data.add(&state_);
+
+    } catch (const Exception &ex) {
+        isc_throw(DbOperationError,
+                  "CqlLease4Exchange::createBindForInsert(): "
+                  "could not create bind array from Lease4: "
+                      << lease_->addr_.toText() << ", reason: " << ex.what());
     }
+}
 
-    Lease4Ptr retrieveLease() {
-        try {
-            // Sanity checks
-            if (hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
-                isc_throw(BadValue, "hardware address "
-                                        << HWAddr(hwaddr_, HTYPE_ETHER).toText()
-                                        << " of length " << hwaddr_.size()
-                                        << " exceeds maximum allowed length of "
-                                        << HWAddr::MAX_HWADDR_LEN);
-            }
-            if (client_id_.size() > ClientId::MAX_CLIENT_ID_LEN) {
-                isc_throw(BadValue, "client ID "
-                                        << ClientId(client_id_).toText()
-                                        << " of length " << client_id_.size()
-                                        << " exceeds maximum allowed length of "
-                                        << ClientId::MAX_CLIENT_ID_LEN);
-            }
-            if (hostname_.size() > HOSTNAME_MAX_LEN) {
-                isc_throw(BadValue, "hostname"
-                                        << hostname_ << " of length "
-                                        << hostname_.size()
-                                        << " exceeds maximum allowed length of "
-                                        << HOSTNAME_MAX_LEN);
+void
+CqlLease4Exchange::createBindForUpdate(
+    const Lease4Ptr &lease,
+    AnyArray &data,
+    StatementTag statement_tag /* = NULL */) {
+    (void)statement_tag;  // [maybe_unused]
+
+    if (!lease) {
+        isc_throw(BadValue, "CqlLease4Exchange::createBindForUpdate(): "
+                            "Lease4 object is NULL");
+    }
+    // Store lease object to ensure it remains valid.
+    lease_ = lease;
+    // Set up the structures for the various components of the lease4
+    // structure.
+
+    try {
+        // address: int
+        // The address in the Lease structure is an IOAddress object.
+        // Convert this to an integer for storage.
+        address_ = static_cast<cass_int32_t>(lease->addr_.toUint32());
+
+        // hwaddr: blob
+        if (lease_->hwaddr_ && lease->hwaddr_->hwaddr_.size() > 0) {
+            if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+                isc_throw(DbOperationError,
+                          "hardware address "
+                              << lease_->hwaddr_->toText() << " of length "
+                              << lease_->hwaddr_->hwaddr_.size()
+                              << " exceeds maximum allowed length of "
+                              << HWAddr::MAX_HWADDR_LEN);
             }
+            hwaddr_ = lease_->hwaddr_->hwaddr_;
+        } else {
+            hwaddr_.clear();
+        }
+
+        // client_id: blob
+        if (lease_->client_id_ && lease->client_id_->getClientId().size() > 0) {
+            client_id_ = lease_->client_id_->getClientId();
+        } else {
+            client_id_.clear();
+        }
 
-            time_t cltt = 0;
-            CqlExchange::convertFromDatabaseTime(expire_, valid_lifetime_,
-                                                 cltt);
+        // valid lifetime: bigint
+        valid_lifetime_ = static_cast<cass_int32_t>(lease_->valid_lft_);
 
-            // Recreate the hardware address.
-            HWAddrPtr hwaddr(new HWAddr(hwaddr_, HTYPE_ETHER));
+        // expire: bigint
+        // The lease structure holds the client last transmission time
+        /// (cltt_)
+        // For convenience for external tools, this is converted to lease
+        // expiry time (expire). The relationship is given by:
+        // expire = cltt_ + valid_lft_
+        CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
+                                           expire_);
 
-            Lease4Ptr result(new Lease4(addr4_, hwaddr, client_id_.data(),
-                                        client_id_.size(), valid_lifetime_, 0,
-                                        0, cltt, subnet_id_, fqdn_fwd_,
-                                        fqdn_rev_, hostname_));
+        // subnet_id: int
+        subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
 
-            result->state_ = state_;
+        // fqdn_fwd: boolean
+        fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
 
-            return result;
-        } catch (const std::exception& ex) {
-            isc_throw(DbOperationError,
-                      "createBindForReceive(): "
-                      "could not convert data to Lease4, reason: "
-                          << ex.what());
+        // fqdn_rev: boolean
+        fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
+
+        // hostname: varchar
+        if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
+            isc_throw(BadValue,
+                      "hostname " << lease_->hostname_ << " of length "
+                                  << lease_->hostname_.size()
+                                  << " exceeds maximum allowed length of "
+                                  << HOSTNAME_MAX_LEN);
         }
-        return Lease4Ptr();
+        hostname_ = lease_->hostname_;
+
+        // state: int
+        state_ = static_cast<cass_int32_t>(lease_->state_);
+
+        // Start with a fresh array.
+        data.clear();
+        data.add(&hwaddr_);
+        data.add(&client_id_);
+        data.add(&subnet_id_);
+        data.add(&valid_lifetime_);
+        data.add(&expire_);
+        data.add(&fqdn_fwd_);
+        data.add(&fqdn_rev_);
+        data.add(&hostname_);
+        data.add(&state_);
+        data.add(&address_);
+
+    } catch (const Exception &ex) {
+        isc_throw(DbOperationError,
+                  "CqlLease4Exchange::createBindUpdate(): "
+                  "could not create bind array from Lease4: "
+                      << lease_->addr_.toText() << ", reason: " << ex.what());
     }
+}
+
+void
+CqlLease4Exchange::createBindForDelete(
+    const IOAddress &address,
+    AnyArray &data,
+    StatementTag statement_tag /* = NULL */) {
+    (void)statement_tag;  // [maybe_unused]
+
+    // Set up the structures for the various components of the lease4
+    // structure.
 
-    void* retrieve() {
-        isc_throw(NotImplemented, "retrieve(): Not implemented yet.");
+    try {
+        // address: int
+        // The address in the Lease structure is an IOAddress object.
+        // Convert this to an integer for storage.
+        address_ = static_cast<cass_int32_t>(address.toUint32());
+
+        // Start with a fresh array.
+        data.clear();
+        data.add(&address_);
+
+    } catch (const Exception &ex) {
+        isc_throw(DbOperationError,
+                  "CqlLease4Exchange::createBindForDelete(): "
+                  "could not create bind array from Lease4: "
+                      << lease_->addr_.toText() << ", reason: " << ex.what());
     }
+}
 
-private:
-    Lease4Ptr lease_;                     ///< Pointer to lease object
-    cass_int32_t addr4_;                  ///< IPv4 address
-    std::vector<cass_byte_t> client_id_;  ///< Client identification
-};
+/// @brief Create BIND array to receive data
+///
+/// Creates a CQL_BIND array to receive Lease4 data from the database.
+void
+CqlLease4Exchange::createBindForSelect(
+    AnyArray &data, StatementTag /* statement_tag = NULL */) {
+
+    // Start with a fresh array.
+    data.clear();
+
+    // address: blob
+    data.add(&address_);
+
+    // hwaddr: blob
+    data.add(&hwaddr_);
+
+    // client_id: blob
+    data.add(&client_id_);
+
+    // valid_lifetime: bigint
+    data.add(&valid_lifetime_);
+
+    // expire: bigint
+    data.add(&expire_);
+
+    // subnet_id: int
+    data.add(&subnet_id_);
+
+    // fqdn_fwd: boolean
+    data.add(&fqdn_fwd_);
+
+    // fqdn_rev: boolean
+    data.add(&fqdn_rev_);
+
+    // hostname: varchar
+    data.add(&hostname_);
+
+    // state: int
+    data.add(&state_);
+}
+
+boost::any
+CqlLease4Exchange::retrieve() {
+    try {
+        // Sanity checks
+        if (hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+            isc_throw(BadValue,
+                      "hardware address "
+                          << HWAddr(hwaddr_, HTYPE_ETHER).toText()
+                          << " of length " << hwaddr_.size()
+                          << " exceeds maximum allowed length of "
+                          << HWAddr::MAX_HWADDR_LEN);
+        }
+        if (client_id_.size() > ClientId::MAX_CLIENT_ID_LEN) {
+            isc_throw(BadValue,
+                      "client ID " << ClientId(client_id_).toText()
+                                   << " of length " << client_id_.size()
+                                   << " exceeds maximum allowed length of "
+                                   << ClientId::MAX_CLIENT_ID_LEN);
+        }
+        if (hostname_.size() > HOSTNAME_MAX_LEN) {
+            isc_throw(BadValue,
+                      "hostname" << hostname_ << " of length "
+                                 << hostname_.size()
+                                 << " exceeds maximum allowed length of "
+                                 << HOSTNAME_MAX_LEN);
+        }
+
+        time_t cltt = 0;
+        CqlExchange::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
+
+        // Recreate the hardware address.
+        HWAddrPtr hwaddr(new HWAddr(hwaddr_, HTYPE_ETHER));
+
+        uint32_t addr4 = static_cast<uint32_t>(address_);
+
+        if (hwaddr->hwaddr_ == NULL_HWADDR) {
+            hwaddr->hwaddr_.clear();
+        }
+
+        if (client_id_ == NULL_CLIENTID) {
+            client_id_.clear();
+        }
+
+        Lease4Ptr result(new Lease4(addr4, hwaddr, client_id_.data(),
+                                    client_id_.size(), valid_lifetime_, 0, 0,
+                                    cltt, subnet_id_, fqdn_fwd_, fqdn_rev_,
+                                    hostname_));
+
+        result->state_ = state_;
+
+        return result;
+    } catch (const Exception &ex) {
+        isc_throw(DbOperationError,
+                  "CqlLease4Exchange::retrieveLease(): "
+                  "could not convert data to Lease4, reason: "
+                      << ex.what());
+    }
+}
+
+void
+CqlLease4Exchange::getLeaseCollection(StatementTag &statement_tag,
+                                      AnyArray &data,
+                                      Lease4Collection &result) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR4)
+        .arg(statement_tag);
+
+    AnyArray collection = executeSelect(connection_, data, statement_tag);
+
+    // Transfer Lease4 objects to result.
+    for (boost::any &element : collection) {
+        result.push_back(boost::any_cast<Lease4Ptr>(element));
+    }
+}
+
+void
+CqlLease4Exchange::getLease(StatementTag &statement_tag,
+                            AnyArray &data,
+                            Lease4Ptr &result) {
+    // This particular method is called when only one or zero matches is
+    // expected.
+    Lease4Collection collection;
+    getLeaseCollection(statement_tag, data, collection);
+
+    // Return single record if present, else clear the lease.
+    const size_t collection_size = collection.size();
+    if (collection_size >= 2u) {
+        isc_throw(
+            MultipleRecords,
+            "CqlLease4Exchange::getLease(): multiple records were found in "
+            "the database where only one was expected for statement "
+                << statement_tag);
+    } else if (collection_size == 0u) {
+        result.reset();
+    } else {
+        result = *collection.begin();
+    }
+}
+
+void
+CqlLease4Exchange::getExpiredLeases(const size_t &max_leases,
+                                    Lease4Collection &expired_leases) {
+    // Set up the WHERE clause value
+    cass_int32_t keep_state = Lease::STATE_EXPIRED_RECLAIMED;
+    cass_int64_t timestamp = static_cast<cass_int64_t>(time(NULL));
+
+    // If the number of leases is 0, we will return all leases. This is
+    // achieved by setting the limit to a very high value.
+    cass_int32_t limit = max_leases > 0u ?
+                             static_cast<cass_int32_t>(max_leases) :
+                             std::numeric_limits<cass_int32_t>::max();
+
+    for (cass_int32_t state = Lease::STATE_DEFAULT;
+         state <= Lease::STATE_EXPIRED_RECLAIMED; state++) {
+        if (state == keep_state) {
+            continue;
+        }
+
+        AnyArray data;
+        data.add(&state);
+        data.add(&timestamp);
+        data.add(&limit);
+
+        // Retrieve leases from the database.
+        Lease4Collection temp_collection;
+        getLeaseCollection(CqlLease4Exchange::GET_LEASE4_EXPIRE, data,
+                           temp_collection);
+
+        for (Lease4Ptr &lease : temp_collection) {
+            expired_leases.push_back(lease);
+        }
+    }
+}
 
 /// @brief Exchange CQL and Lease6 Data
 ///
@@ -678,542 +710,681 @@ private:
 ///
 /// @note There are no unit tests for this class. It is tested indirectly
 /// in all CqlLeaseMgr::xxx6() calls where it is used.
-
 class CqlLease6Exchange : public CqlLeaseExchange {
 public:
     /// @brief Constructor
     ///
-    /// The initialization of the variables here is nonly to satisfy cppcheck -
-    /// all variables are initialized/set in the methods before they are used.
-    CqlLease6Exchange()
-        : pref_lifetime_(0), lease_type_(0), iaid_(0), prefixlen_(0),
-          hwtype_(0), hwaddr_source_(0) {
-        // Set the column names
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "address", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_STRING)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "duid", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_BYTES)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "valid_lifetime", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_INT64)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "expire", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_TIMESTAMP)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "subnet_id", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "pref_lifetime", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_INT64)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "lease_type", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "iaid", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "prefix_len", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "fqdn_fwd", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_BOOL)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "fqdn_rev", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_BOOL)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "hostname", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_STRING)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "hwaddr", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_BYTES)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "hwtype", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "hwaddr_source", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "state", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "limit", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
-            "[applied]", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
-            EXCHANGE_DATA_TYPE_BOOL)));
-        BOOST_ASSERT(parameters_.size() == 18U);
-    }
+    /// The initialization of the variables here is nonly to satisfy
+    /// cppcheck -
+    /// all variables are initialized/set in the methods before they are
+    /// used.
+    explicit CqlLease6Exchange(const CqlConnection &connection);
+
+    /// @brief Create CQL_BIND objects for Lease4 Pointer
+    ///
+    /// Fills in the CQL_BIND array for sending data in the Lease4 object to
+    /// the database. Used for INSERT statements.
+    void createBindForInsert(const Lease6Ptr &lease, AnyArray &data);
+
+    /// @brief Create CQL_BIND objects for Lease4 Pointer
+    ///
+    /// Fills in the CQL_BIND array for sending data in the Lease4 object to
+    /// the database. Used for UPDATE statements.
+    void createBindForUpdate(const Lease6Ptr &lease,
+                             AnyArray &data,
+                             StatementTag statement_tag = NULL);
+
+    /// @brief Create CQL_BIND objects for Lease4 Pointer
+    ///
+    /// Fills in the CQL_BIND array for sending data in the Lease4 object to
+    /// the database. Used for DELETE statements.
+    void createBindForDelete(const IOAddress &lease,
+                             AnyArray &data,
+                             StatementTag statement_tag = NULL);
 
-    /// @brief Create CQL_BIND objects for Lease6 Pointer
+    /// @brief Create BIND array to receive data
     ///
-    /// Fills in the CQL_BIND array for sending data in the Lease6 object to
-    /// the database.
-    void createBindForSend(const Lease6Ptr& lease, CqlDataArray& data) {
-        if (!lease) {
-            isc_throw(BadValue, "createBindForSend(): Lease6 object is NULL");
+    /// Creates a CQL_BIND array to receive Lease6 data from the database.
+    void createBindForSelect(AnyArray &data,
+                             StatementTag statement_tag = NULL) override;
+
+    boost::any retrieve() override;
+
+    void getLeaseCollection(StatementTag &statement_tag,
+                            AnyArray &data,
+                            Lease6Collection &result);
+
+    void
+    getLease(StatementTag &statement_tag, AnyArray &data, Lease6Ptr &result);
+
+    void getExpiredLeases(const size_t &max_leases,
+                          Lease6Collection &expired_leases);
+
+    /// @brief Cassandra statements
+    static StatementMap tagged_statements_;
+
+    /// @brief Statement tags definitions
+    /// @{
+    static constexpr StatementTag INSERT_LEASE6 = "INSERT_LEASE6";
+    static constexpr StatementTag UPDATE_LEASE6 = "UPDATE_LEASE6";
+    static constexpr StatementTag DELETE_LEASE6 = "DELETE_LEASE6";
+    static constexpr StatementTag GET_LEASE6_EXPIRE = "GET_LEASE6_EXPIRE";
+    static constexpr StatementTag GET_LEASE6_ADDR = "GET_LEASE6_ADDR";
+    static constexpr StatementTag GET_LEASE6_DUID_IAID = "GET_LEASE6_DUID_IAID";
+    static constexpr StatementTag GET_LEASE6_DUID_IAID_SUBID =
+        "GET_LEASE6_DUID_IAID_SUBID";
+    // @}
+
+private:
+    /// @brief Lease
+    Lease6Ptr lease_;
+
+    /// @brief IPv6 address
+    std::string address_;
+
+    /// @brief Preferred lifetime
+    cass_int64_t pref_lifetime_;
+
+    /// @brief Client identifier
+    CassBlob duid_;
+
+    /// @brief Identity association identifier
+    cass_int32_t iaid_;
+
+    /// @brief Lease type
+    cass_int32_t lease_type_;
+
+    /// @brief Prefix length
+    cass_int32_t prefix_len_;
+
+    /// @brief Hardware type
+    cass_int32_t hwtype_;
+
+    /// @brief Source of the hardware address
+    cass_int32_t hwaddr_source_;
+};  // CqlLease6Exchange
+
+constexpr StatementTag CqlLease6Exchange::INSERT_LEASE6;
+constexpr StatementTag CqlLease6Exchange::UPDATE_LEASE6;
+constexpr StatementTag CqlLease6Exchange::DELETE_LEASE6;
+constexpr StatementTag CqlLease6Exchange::GET_LEASE6_EXPIRE;
+constexpr StatementTag CqlLease6Exchange::GET_LEASE6_ADDR;
+constexpr StatementTag CqlLease6Exchange::GET_LEASE6_DUID_IAID;
+constexpr StatementTag CqlLease6Exchange::GET_LEASE6_DUID_IAID_SUBID;
+
+StatementMap CqlLease6Exchange::tagged_statements_ = {
+
+    {INSERT_LEASE6,   //
+     {INSERT_LEASE6,  //
+      "INSERT INTO lease6("
+      "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, "
+      "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, "
+      "hwaddr_source, state "
+      ") VALUES ("
+      "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?"
+      ") "
+      "IF NOT EXISTS "}},
+
+    {UPDATE_LEASE6,   //
+     {UPDATE_LEASE6,  //
+      "UPDATE lease6 SET "
+      "valid_lifetime = ?, "
+      "expire = ?, "
+      "pref_lifetime = ?, "
+      "duid = ?, "
+      "iaid = ?, "
+      "subnet_id = ?, "
+      "lease_type = ?, "
+      "prefix_len = ?, "
+      "fqdn_fwd = ?, "
+      "fqdn_rev = ?, "
+      "hostname = ?, "
+      "hwaddr = ?, "
+      "hwtype = ?, "
+      "hwaddr_source = ?, "
+      "state = ? "
+      "WHERE address = ? "
+      "IF EXISTS "}},
+
+    {DELETE_LEASE6,   //
+     {DELETE_LEASE6,  //
+      "DELETE FROM lease6 "
+      "WHERE address = ? "
+      "IF EXISTS "}},
+
+    {GET_LEASE6_EXPIRE,   //
+     {GET_LEASE6_EXPIRE,  //
+      "SELECT "
+      "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, "
+      "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, "
+      "hwaddr_source, state "
+      "FROM lease6 "
+      "WHERE state = ? "
+      "AND expire < ? "
+      "LIMIT ? "
+      "ALLOW FILTERING "}},
+
+    {GET_LEASE6_ADDR,   //
+     {GET_LEASE6_ADDR,  //
+      "SELECT "
+      "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, "
+      "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, "
+      "hwaddr_source, state "
+      "FROM lease6 "
+      "WHERE address = ? "
+      "AND lease_type = ? "
+      "ALLOW FILTERING "}},
+
+    {GET_LEASE6_DUID_IAID,   //
+     {GET_LEASE6_DUID_IAID,  //
+      "SELECT "
+      "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, "
+      "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, "
+      "hwaddr_source, state "
+      "FROM lease6 "
+      "WHERE duid = ? AND iaid = ? "
+      "AND lease_type = ? "
+      "ALLOW FILTERING "}},
+
+    {GET_LEASE6_DUID_IAID_SUBID,   //
+     {GET_LEASE6_DUID_IAID_SUBID,  //
+      "SELECT "
+      "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, "
+      "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, "
+      "hwaddr_source, state "
+      "FROM lease6 "
+      "WHERE duid = ? AND iaid = ? "
+      "AND lease_type = ? "
+      "AND subnet_id = ? "
+      "ALLOW FILTERING "}},
+
+};
+
+/// @brief Constructor
+///
+/// The initialization of the variables here is not only to satisfy
+/// cppcheck -
+/// all variables are initialized/set in the methods before they are
+/// used.
+CqlLease6Exchange::CqlLease6Exchange(const CqlConnection &connection)
+    : CqlLeaseExchange(connection), pref_lifetime_(0), iaid_(0), lease_type_(0),
+      prefix_len_(0), hwtype_(0), hwaddr_source_(0) {
+}
+/// @brief Create CQL_BIND objects for Lease6 Pointer
+///
+/// Fills in the CQL_BIND array for sending data in the Lease6 object to
+/// the database.
+void
+CqlLease6Exchange::createBindForInsert(const Lease6Ptr &lease, AnyArray &data) {
+    if (!lease) {
+        isc_throw(BadValue, "Lease6 object is NULL");
+    }
+    // Store lease object to ensure it remains valid.
+    lease_ = lease;
+
+    // Set up the structures for the various components of the lease4
+    // structure.
+    try {
+        // address: varchar
+        address_ = lease_->addr_.toText();
+        if (address_.size() > ADDRESS6_TEXT_MAX_LEN) {
+            isc_throw(BadValue,
+                      "address " << address_ << " of length " << address_.size()
+                                 << " exceeds maximum allowed length of "
+                                 << ADDRESS6_TEXT_MAX_LEN);
         }
-        // Store lease object to ensure it remains valid.
-        lease_ = lease;
-
-        // Set up the structures for the various components of the lease4
-        // structure.
-        try {
-            // address: varchar
-            addr6_ = lease_->addr_.toText();
-            if (addr6_.size() > ADDRESS6_TEXT_MAX_LEN) {
-                isc_throw(BadValue,
-                          "address " << addr6_ << " of length " << addr6_.size()
-                                     << " exceeds maximum allowed length of "
-                                     << ADDRESS6_TEXT_MAX_LEN);
-            }
-            data.add(reinterpret_cast<void*>(&addr6_));
 
-            // duid: blob
-            if (!lease_->duid_) {
+        // valid lifetime: bigint
+        valid_lifetime_ = static_cast<cass_int64_t>(lease_->valid_lft_);
+
+        // expire: bigint
+        // The lease structure holds the client last transmission time
+        // (cltt_)
+        // For convenience for external tools, this is converted to lease
+        // expiry time (expire). The relationship is given by:
+        // expire = cltt_ + valid_lft_
+        CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
+                                           expire_);
+
+        // subnet_id: int
+        subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
+
+        // pref_lifetime: bigint
+        pref_lifetime_ = static_cast<cass_int64_t>(lease_->preferred_lft_);
+
+        // duid: blob
+        if (!lease_->duid_) {
+            isc_throw(DbOperationError,
+                      "lease6 with address " << address_
+                                             << " is missing mandatory duid");
+        }
+        duid_ = lease_->duid_->getDuid();
+
+        // iaid: int
+        iaid_ = static_cast<cass_int32_t>(lease_->iaid_);
+
+        // lease_type: int
+        lease_type_ = static_cast<cass_int32_t>(lease_->type_);
+
+        // prefix_len: int
+        prefix_len_ = static_cast<cass_int32_t>(lease_->prefixlen_);
+
+        // fqdn_fwd: boolean
+        fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
+
+        // fqdn_rev: boolean
+        fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
+
+        // hostname: varchar
+        if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
+            isc_throw(BadValue,
+                      "hostname" << lease_->hostname_ << " of length "
+                                 << lease_->hostname_.size()
+                                 << " exceeds maximum allowed length of "
+                                 << HOSTNAME_MAX_LEN);
+        }
+        hostname_ = lease_->hostname_;
+
+        // hwaddr: blob
+        if (lease_->hwaddr_ && lease->hwaddr_->hwaddr_.size() > 0) {
+            if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
                 isc_throw(DbOperationError,
-                          "lease6 for address "
-                              << addr6_ << " is missing mandatory duid");
-            }
-            duid_ = lease_->duid_->getDuid();
-            data.add(reinterpret_cast<void*>(&duid_));
-
-            // valid lifetime: bigint
-            valid_lifetime_ = static_cast<cass_int64_t>(lease_->valid_lft_);
-            data.add(reinterpret_cast<void*>(&valid_lifetime_));
-
-            // expire: bigint
-            // The lease structure holds the client last transmission time
-            // (cltt_)
-            // For convenience for external tools, this is converted to lease
-            // expiry time (expire). The relationship is given by:
-            // expire = cltt_ + valid_lft_
-            CqlExchange::convertToDatabaseTime(lease_->cltt_,
-                                               lease_->valid_lft_, expire_);
-            data.add(reinterpret_cast<void*>(&expire_));
-
-            // subnet_id: int
-            subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
-            data.add(reinterpret_cast<void*>(&subnet_id_));
-
-            // pref_lifetime: bigint
-            pref_lifetime_ = static_cast<cass_int64_t>(lease_->preferred_lft_);
-            data.add(reinterpret_cast<void*>(&pref_lifetime_));
-
-            // lease_type: int
-            lease_type_ = static_cast<cass_int32_t>(lease_->type_);
-            data.add(reinterpret_cast<void*>(&lease_type_));
-
-            // iaid: int
-            iaid_ = static_cast<cass_int32_t>(lease_->iaid_);
-            data.add(reinterpret_cast<void*>(&iaid_));
-
-            // prefix_len: int
-            prefixlen_ = static_cast<cass_int32_t>(lease_->prefixlen_);
-            data.add(reinterpret_cast<void*>(&prefixlen_));
-
-            // fqdn_fwd: boolean
-            fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
-            data.add(reinterpret_cast<void*>(&fqdn_fwd_));
-
-            // fqdn_rev: boolean
-            fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
-            data.add(reinterpret_cast<void*>(&fqdn_rev_));
-
-            // hostname: varchar
-            if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
-                isc_throw(BadValue, "hostname"
-                                        << lease_->hostname_ << " of length "
-                                        << lease_->hostname_.size()
-                                        << " exceeds maximum allowed length of "
-                                        << HOSTNAME_MAX_LEN);
-            }
-            hostname_ = lease_->hostname_;
-            data.add(reinterpret_cast<void*>(&hostname_));
-
-            // hwaddr: blob
-            if (lease_->hwaddr_) {
-                if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
-                    isc_throw(BadValue,
-                              "hardware address "
-                                  << lease_->hwaddr_->toText() << " of length "
-                                  << lease_->hwaddr_->hwaddr_.size()
-                                  << " exceeds maximum allowed length of "
-                                  << HWAddr::MAX_HWADDR_LEN);
-                }
-                hwaddr_ = lease_->hwaddr_->hwaddr_;
-            } else {
-                hwaddr_.clear();
+                          "hardware address "
+                              << lease_->hwaddr_->toText() << " of length "
+                              << lease_->hwaddr_->hwaddr_.size()
+                              << " exceeds maximum allowed length of "
+                              << HWAddr::MAX_HWADDR_LEN);
             }
-            data.add(reinterpret_cast<void*>(&hwaddr_));
+            hwaddr_ = lease_->hwaddr_->hwaddr_;
+        } else {
+            hwaddr_.clear();
+        }
 
-            // hwtype: int
-            if (lease_->hwaddr_) {
-                hwtype_ = static_cast<cass_int32_t>(lease_->hwaddr_->htype_);
-            } else {
-                hwtype_ = 0;
-            }
-            data.add(reinterpret_cast<void*>(&hwtype_));
-
-            // hwaddr_source: int
-            if (lease_->hwaddr_) {
-                hwaddr_source_ =
-                    static_cast<cass_int32_t>(lease_->hwaddr_->source_);
-            } else {
-                hwaddr_source_ = 0;
-            }
-            data.add(reinterpret_cast<void*>(&hwaddr_source_));
+        // hwtype: int
+        if (lease_->hwaddr_) {
+            hwtype_ = static_cast<cass_int32_t>(lease_->hwaddr_->htype_);
+        } else {
+            hwtype_ = 0;
+        }
 
-            // state: int
-            state_ = static_cast<cass_int32_t>(lease_->state_);
-            data.add(reinterpret_cast<void*>(&state_));
-        } catch (const std::exception& ex) {
-            isc_throw(DbOperationError,
-                      "createBindForSend(): "
-                      "could not create bind array from Lease6: "
-                          << lease_->addr_.toText()
-                          << ", reason: " << ex.what());
+        // hwaddr_source: int
+        if (lease_->hwaddr_) {
+            hwaddr_source_ =
+                static_cast<cass_int32_t>(lease_->hwaddr_->source_);
+        } else {
+            hwaddr_source_ = 0;
         }
+
+        // state: int
+        state_ = static_cast<cass_int32_t>(lease_->state_);
+
+        // Start with a fresh array.
+        data.clear();
+
+        // Add them all to data.
+        data.add(&address_);
+        data.add(&valid_lifetime_);
+        data.add(&expire_);
+        data.add(&subnet_id_);
+        data.add(&pref_lifetime_);
+        data.add(&duid_);
+        data.add(&iaid_);
+        data.add(&lease_type_);
+        data.add(&prefix_len_);
+        data.add(&fqdn_fwd_);
+        data.add(&fqdn_rev_);
+        data.add(&hostname_);
+        data.add(&hwaddr_);
+        data.add(&hwtype_);
+        data.add(&hwaddr_source_);
+        data.add(&state_);
+
+    } catch (const Exception &ex) {
+        isc_throw(DbOperationError,
+                  "CqlLease6Exchange::createBindForInsert(): "
+                  "could not create bind array from Lease6: "
+                      << lease_->addr_.toText() << ", reason: " << ex.what());
     }
+}
+/// @brief Create CQL_BIND objects for Lease6 Pointer
+///
+/// Fills in the CQL_BIND array for sending data in the Lease6 object to
+/// the database.
+void
+CqlLease6Exchange::createBindForUpdate(
+    const Lease6Ptr &lease,
+    AnyArray &data,
+    StatementTag statement_tag /* = NULL */) {
+    (void)statement_tag;  // [maybe_unused]
+
+    if (!lease) {
+        isc_throw(BadValue, "Lease6 object is NULL");
+    }
+    // Store lease object to ensure it remains valid.
+    lease_ = lease;
 
-    /// @brief Create BIND array to receive data
-    ///
-    /// Creates a CQL_BIND array to receive Lease6 data from the database.
-    void createBindForReceive(CqlDataArray& data,
-                              const int /* statementIndex */ = -1) {
+    // Set up the structures for the various components of the lease4
+    // structure.
+    try {
         // address: varchar
-        data.add(reinterpret_cast<void*>(&addr6_));
-
-        // duid: blob
-        data.add(reinterpret_cast<void*>(&duid_));
+        address_ = lease_->addr_.toText();
+        if (address_.size() > ADDRESS6_TEXT_MAX_LEN) {
+            isc_throw(BadValue,
+                      "address " << address_ << " of length " << address_.size()
+                                 << " exceeds maximum allowed length of "
+                                 << ADDRESS6_TEXT_MAX_LEN);
+        }
 
-        // valid_lifetime_: bigint
-        data.add(reinterpret_cast<void*>(&valid_lifetime_));
+        // valid lifetime: bigint
+        valid_lifetime_ = static_cast<cass_int64_t>(lease_->valid_lft_);
 
         // expire: bigint
-        data.add(reinterpret_cast<void*>(&expire_));
+        // The lease structure holds the client last transmission time
+        // (cltt_)
+        // For convenience for external tools, this is converted to lease
+        // expiry time (expire). The relationship is given by:
+        // expire = cltt_ + valid_lft_
+        CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
+                                           expire_);
 
         // subnet_id: int
-        data.add(reinterpret_cast<void*>(&subnet_id_));
+        subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
 
         // pref_lifetime: bigint
-        data.add(reinterpret_cast<void*>(&pref_lifetime_));
+        pref_lifetime_ = static_cast<cass_int64_t>(lease_->preferred_lft_);
 
-        // lease_type: int
-        data.add(reinterpret_cast<void*>(&lease_type_));
+        // duid: blob
+        if (!lease_->duid_) {
+            isc_throw(DbOperationError,
+                      "lease6 with address " << address_
+                                             << " is missing mandatory duid");
+        }
+        duid_ = lease_->duid_->getDuid();
 
         // iaid: int
-        data.add(reinterpret_cast<void*>(&iaid_));
+        iaid_ = static_cast<cass_int32_t>(lease_->iaid_);
+
+        // lease_type: int
+        lease_type_ = static_cast<cass_int32_t>(lease_->type_);
 
         // prefix_len: int
-        data.add(reinterpret_cast<void*>(&prefixlen_));
+        prefix_len_ = static_cast<cass_int32_t>(lease_->prefixlen_);
 
         // fqdn_fwd: boolean
-        data.add(reinterpret_cast<void*>(&fqdn_fwd_));
+        fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
 
         // fqdn_rev: boolean
-        data.add(reinterpret_cast<void*>(&fqdn_rev_));
+        fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
 
         // hostname: varchar
-        data.add(reinterpret_cast<void*>(&hostname_));
+        if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
+            isc_throw(BadValue,
+                      "hostname" << lease_->hostname_ << " of length "
+                                 << lease_->hostname_.size()
+                                 << " exceeds maximum allowed length of "
+                                 << HOSTNAME_MAX_LEN);
+        }
+        hostname_ = lease_->hostname_;
 
         // hwaddr: blob
-        data.add(reinterpret_cast<void*>(&hwaddr_));
+        if (lease_->hwaddr_ && lease->hwaddr_->hwaddr_.size() > 0) {
+            if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+                isc_throw(DbOperationError,
+                          "hardware address "
+                              << lease_->hwaddr_->toText() << " of length "
+                              << lease_->hwaddr_->hwaddr_.size()
+                              << " exceeds maximum allowed length of "
+                              << HWAddr::MAX_HWADDR_LEN);
+            }
+            hwaddr_ = lease_->hwaddr_->hwaddr_;
+        } else {
+            hwaddr_.clear();
+        }
 
         // hwtype: int
-        data.add(reinterpret_cast<void*>(&hwtype_));
+        if (lease_->hwaddr_) {
+            hwtype_ = static_cast<cass_int32_t>(lease_->hwaddr_->htype_);
+        } else {
+            hwtype_ = 0;
+        }
 
         // hwaddr_source: int
-        data.add(reinterpret_cast<void*>(&hwaddr_source_));
+        if (lease_->hwaddr_) {
+            hwaddr_source_ =
+                static_cast<cass_int32_t>(lease_->hwaddr_->source_);
+        } else {
+            hwaddr_source_ = 0;
+        }
 
         // state: int
-        data.add(reinterpret_cast<void*>(&state_));
+        state_ = static_cast<cass_int32_t>(lease_->state_);
+
+        // Start with a fresh array.
+        data.clear();
+
+        // Add them all to data.
+        data.add(&valid_lifetime_);
+        data.add(&expire_);
+        data.add(&pref_lifetime_);
+        data.add(&duid_);
+        data.add(&iaid_);
+        data.add(&subnet_id_);
+        data.add(&lease_type_);
+        data.add(&prefix_len_);
+        data.add(&fqdn_fwd_);
+        data.add(&fqdn_rev_);
+        data.add(&hostname_);
+        data.add(&hwaddr_);
+        data.add(&hwtype_);
+        data.add(&hwaddr_source_);
+        data.add(&state_);
+        data.add(&address_);
+
+    } catch (const Exception &ex) {
+        isc_throw(DbOperationError,
+                  "CqlLease6Exchange::createBindForUpdate(): "
+                  "could not create bind array from Lease6: "
+                      << lease_->addr_.toText() << ", reason: " << ex.what());
     }
+}
 
-    Lease6Ptr retrieveLease() {
-        try {
-            // Sanity checks
-            if (addr6_.size() > ADDRESS6_TEXT_MAX_LEN) {
-                isc_throw(BadValue,
-                          "address " << addr6_ << " of length " << addr6_.size()
-                                     << " exceeds maximum allowed length of "
-                                     << ADDRESS6_TEXT_MAX_LEN);
-            }
-            if (duid_.size() > DUID::MAX_DUID_LEN) {
-                isc_throw(BadValue, "duid "
-                                        << DUID(duid_).toText() << " of length "
-                                        << duid_.size()
-                                        << " exceeds maximum allowed length of "
-                                        << DUID::MAX_DUID_LEN);
-            }
-            if (lease_type_ != Lease::TYPE_NA &&
-                lease_type_ != Lease::TYPE_TA &&
-                lease_type_ != Lease::TYPE_PD) {
-                isc_throw(BadValue, "invalid lease type "
-                                        << lease_type_
-                                        << " for lease with address " << addr6_
-                                        << ". Expected 0, 1 or 2.");
-            }
-            if (hostname_.size() > HOSTNAME_MAX_LEN) {
-                isc_throw(BadValue, "hostname "
-                                        << hostname_ << " of length "
-                                        << hostname_.size()
-                                        << " exceeds maximum allowed length of "
-                                        << HOSTNAME_MAX_LEN);
-            }
-            if (hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
-                isc_throw(BadValue,
-                          "hwaddr " << HWAddr(hwaddr_, hwtype_).toText(false)
-                                    << " of length " << hwaddr_.size()
-                                    << " exceeds maximum allowed length of "
-                                    << HWAddr::MAX_HWADDR_LEN);
-            }
+/// @brief Create CQL_BIND objects for Lease6 Pointer
+///
+/// Fills in the CQL_BIND array for sending data in the Lease6 object to
+/// the database.
+void
+CqlLease6Exchange::createBindForDelete(
+    const IOAddress &address,
+    AnyArray &data,
+    StatementTag statement_tag /* = NULL */) {
+    (void)statement_tag;  // [maybe_unused]
+
+    // Set up the structures for the various components of the lease4
+    // structure.
+    try {
+        // address: int
+        // The address in the Lease structure is an IOAddress object.
+        // Convert this to an integer for storage.
+        address_ = address.toText();
 
-            isc::asiolink::IOAddress addr(addr6_);
-            DuidPtr duid(new DUID(duid_));
-            HWAddrPtr hwaddr;
-            if (hwaddr_.size()) {
-                hwaddr.reset(new HWAddr(hwaddr_, hwtype_));
-                hwaddr->source_ = hwaddr_source_;
-            }
+        // Start with a fresh array.
+        data.clear();
+        data.add(&address_);
 
-            // Create the lease and set the cltt (after converting from the
-            // expire time retrieved from the database).
-            Lease6Ptr result(new Lease6(
-                static_cast<Lease::Type>(lease_type_), addr, duid, iaid_,
-                pref_lifetime_, valid_lifetime_, 0, 0, subnet_id_, fqdn_fwd_,
-                fqdn_rev_, hostname_, hwaddr, prefixlen_));
-
-            time_t cltt = 0;
-            CqlExchange::convertFromDatabaseTime(expire_, valid_lifetime_,
-                                                 cltt);
-            result->cltt_ = cltt;
-
-            result->state_ = state_;
-            return result;
-        } catch (const std::exception& ex) {
-            isc_throw(DbOperationError,
-                      "createBindForReceive(): "
-                      "could not convert data to Lease4, reason: "
-                          << ex.what());
-        }
-        return Lease6Ptr();
+    } catch (const Exception &ex) {
+        isc_throw(DbOperationError,
+                  "CqlLease6Exchange::createBindForDelete(): "
+                  "could not create bind array with address: "
+                      << address_ << ", reason: " << ex.what());
     }
+}
 
-    void* retrieve() {
-        isc_throw(NotImplemented, "retrieve(): Not implemented yet.");
-    }
+/// @brief Create BIND array to receive data
+///
+/// Creates a CQL_BIND array to receive Lease6 data from the database.
+void
+CqlLease6Exchange::createBindForSelect(
+    AnyArray &data, StatementTag /* statement_tag = NULL */) {
 
-private:
-    Lease6Ptr lease_;                ///< Pointer to lease object
-    std::string addr6_;              ///< IPv6 address
-    std::vector<cass_byte_t> duid_;  ///< Client identification
-    cass_int64_t pref_lifetime_;     ///< Preferred lifetime
-    cass_int32_t lease_type_;        ///< Lease type
-    cass_int32_t iaid_;              ///< Identity association ID
-    cass_int32_t prefixlen_;         ///< Prefix length
-    cass_int32_t hwtype_;            ///< Hardware type
-    cass_int32_t hwaddr_source_;     ///< Source of the hardware
-                                     /// address
-};
+    // Start with a fresh array.
+    data.clear();
 
-CqlLeaseMgr::CqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
-    : LeaseMgr(), dbconn_(parameters), exchange4_(new CqlLease4Exchange()),
-      exchange6_(new CqlLease6Exchange()),
-      versionExchange_(new CqlVersionExchange()) {
-    dbconn_.openDatabase();
-    dbconn_.prepareStatements(CqlLeaseMgr::tagged_statements_);
-}
+    // address: varchar
+    data.add(&address_);
 
-CqlLeaseMgr::~CqlLeaseMgr() {
-    // There is no need to close the database in this destructor: it is
-    // closed in the destructor of the dbconn_ member variable.
-}
+    // valid_lifetime_: bigint
+    data.add(&valid_lifetime_);
 
-std::string
-CqlLeaseMgr::getDBVersion() {
-    std::stringstream tmp;
-    tmp << "CQL backend " << CQL_SCHEMA_VERSION_MAJOR;
-    tmp << "." << CQL_SCHEMA_VERSION_MINOR;
-    tmp << ", library cassandra_static";
-    return tmp.str();
-}
+    // expire: bigint
+    data.add(&expire_);
 
-bool
-CqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
-                            CqlDataArray& data,
-                            CqlLeaseExchange& exchange) {
-    CassError rc;
-    CassStatement* statement = NULL;
-    CassFuture* future = NULL;
-
-    statement = cass_prepared_bind(dbconn_.statements_[stindex]);
-    if (!statement) {
-        isc_throw(DbOperationError,
-                  "unable to bind statement "
-                      << dbconn_.tagged_statements_[stindex].name_);
-    }
+    // subnet_id: int
+    data.add(&subnet_id_);
 
-    if (dbconn_.force_consistency_) {
-        rc = cass_statement_set_consistency(statement, dbconn_.consistency_);
-        if (rc != CASS_OK) {
-            cass_statement_free(statement);
-            isc_throw(
-                DbOperationError,
-                "unable to set statement consistency for statement "
-                    << dbconn_.tagged_statements_[stindex].name_);
-        }
-    }
+    // pref_lifetime: bigint
+    data.add(&pref_lifetime_);
 
-    CqlCommon::bindData(statement, stindex, data, exchange,
-                        CqlLeaseMgr::tagged_statements_);
+    // duid: blob
+    data.add(&duid_);
 
-    future = cass_session_execute(dbconn_.session_, statement);
-    if (!future) {
-        cass_statement_free(statement);
-        isc_throw(DbOperationError,
-                  "unable to execute statement "
-                      << dbconn_.tagged_statements_[stindex].name_);
-    }
-    cass_future_wait(future);
-    std::string error;
-    dbconn_.checkStatementError(error, future, stindex, "unable to INSERT");
-    rc = cass_future_error_code(future);
-    if (rc != CASS_OK) {
-        cass_future_free(future);
-        cass_statement_free(statement);
-        return false;
-    }
+    // iaid: int
+    data.add(&iaid_);
 
-    // Check if statement has been applied.
-    bool applied = exchange.hasStatementBeenApplied(future);
+    // lease_type: int
+    data.add(&lease_type_);
 
-    // Free resources.
-    cass_future_free(future);
-    cass_statement_free(statement);
-    return applied;
-}
+    // prefix_len: int
+    data.add(&prefix_len_);
 
-bool
-CqlLeaseMgr::addLease(const Lease4Ptr& lease) {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ADD_ADDR4)
-        .arg(lease->addr_.toText());
+    // fqdn_fwd: boolean
+    data.add(&fqdn_fwd_);
 
-    CqlDataArray data;
-    exchange4_->createBindForSend(lease, data);
-    return addLeaseCommon(INSERT_LEASE4, data, *exchange4_);
-}
+    // fqdn_rev: boolean
+    data.add(&fqdn_rev_);
 
-bool
-CqlLeaseMgr::addLease(const Lease6Ptr& lease) {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ADD_ADDR6)
-        .arg(lease->addr_.toText());
+    // hostname: varchar
+    data.add(&hostname_);
+
+    // hwaddr: blob
+    data.add(&hwaddr_);
+
+    // hwtype: int
+    data.add(&hwtype_);
+
+    // hwaddr_source: int
+    data.add(&hwaddr_source_);
 
-    CqlDataArray data;
-    exchange6_->createBindForSend(lease, data);
-    return addLeaseCommon(INSERT_LEASE6, data, *exchange6_);
+    // state: int
+    data.add(&state_);
 }
 
-template <typename Exchange, typename LeaseCollection>
-void
-CqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
-                                CqlDataArray& data,
-                                Exchange& exchange,
-                                LeaseCollection& result,
-                                bool single) const {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR4)
-        .arg(dbconn_.tagged_statements_[stindex].name_);
+boost::any
+CqlLease6Exchange::retrieve() {
+    try {
+        // Sanity checks
+        if (address_.size() > ADDRESS6_TEXT_MAX_LEN) {
+            isc_throw(BadValue,
+                      "address " << address_ << " of length " << address_.size()
+                                 << " exceeds maximum allowed length of "
+                                 << ADDRESS6_TEXT_MAX_LEN);
+        }
+        if (duid_.size() > DUID::MAX_DUID_LEN) {
+            isc_throw(BadValue,
+                      "duid " << DUID(duid_).toText() << " of length "
+                              << duid_.size()
+                              << " exceeds maximum allowed length of "
+                              << DUID::MAX_DUID_LEN);
+        }
+        if (lease_type_ != Lease::TYPE_NA && lease_type_ != Lease::TYPE_TA &&
+            lease_type_ != Lease::TYPE_PD) {
+            isc_throw(BadValue,
+                      "invalid lease type "
+                          << lease_type_ << " for lease with address "
+                          << address_ << ". Expected 0, 1 or 2.");
+        }
+        if (hostname_.size() > HOSTNAME_MAX_LEN) {
+            isc_throw(BadValue,
+                      "hostname " << hostname_ << " of length "
+                                  << hostname_.size()
+                                  << " exceeds maximum allowed length of "
+                                  << HOSTNAME_MAX_LEN);
+        }
+        if (hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+            isc_throw(BadValue,
+                      "hwaddr " << HWAddr(hwaddr_, hwtype_).toText(false)
+                                << " of length " << hwaddr_.size()
+                                << " exceeds maximum allowed length of "
+                                << HWAddr::MAX_HWADDR_LEN);
+        }
 
-    CassError rc;
-    CassStatement* statement = NULL;
-    CassFuture* future = NULL;
+        IOAddress addr(address_);
 
-    statement = cass_prepared_bind(dbconn_.statements_[stindex]);
-    if (!statement) {
-        isc_throw(DbOperationError, "unable to bind statement");
-    }
+        DuidPtr duid(new DUID(duid_));
+        HWAddrPtr hwaddr;
+        if (hwaddr_.size()) {
+            hwaddr.reset(new HWAddr(hwaddr_, hwtype_));
+            hwaddr->source_ = hwaddr_source_;
+        }
 
-    if (dbconn_.force_consistency_) {
-        rc = cass_statement_set_consistency(statement, dbconn_.consistency_);
-        if (rc != CASS_OK) {
-            cass_statement_free(statement);
-            isc_throw(
-                DbOperationError,
-                "unable to set statement consistency for statement "
-                    << dbconn_.tagged_statements_[stindex].name_);
+        if (hwaddr && hwaddr->hwaddr_ == NULL_HWADDR) {
+            hwaddr.reset();
         }
-    }
 
-    CqlCommon::bindData(statement, stindex, data,
-                        static_cast<SqlExchange>(*exchange),
-                        CqlLeaseMgr::tagged_statements_);
+        // Create the lease and set the cltt (after converting from the
+        // expire time retrieved from the database).
+        Lease6Ptr result(
+            new Lease6(static_cast<Lease::Type>(lease_type_), addr, duid, iaid_,
+                       pref_lifetime_, valid_lifetime_, 0, 0, subnet_id_,
+                       fqdn_fwd_, fqdn_rev_, hostname_, hwaddr, prefix_len_));
+
+        time_t cltt = 0;
+        CqlExchange::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
+        result->cltt_ = cltt;
 
-    future = cass_session_execute(dbconn_.session_, statement);
-    if (!future) {
-        cass_statement_free(statement);
+        result->state_ = state_;
+        return result;
+    } catch (const Exception &ex) {
         isc_throw(DbOperationError,
-                  "unable to execute statement "
-                      << dbconn_.tagged_statements_[stindex].name_);
-    }
-    cass_future_wait(future);
-    std::string error;
-    std::stringstream message;
-    message << "unable to GET using statement "
-            << CqlLeaseMgr::tagged_statements_[stindex].name_;
-    dbconn_.checkStatementError(error, future, message.str().c_str());
-    rc = cass_future_error_code(future);
-    if (rc != CASS_OK) {
-        cass_future_free(future);
-        cass_statement_free(statement);
-        isc_throw(DbOperationError, error);
+                  "CqlLease6Exchange::retrieve(): "
+                  "could not convert data to Lease4, reason: "
+                      << ex.what());
     }
+    return Lease6Ptr();
+}
 
-    const CassResult* resultCollection = cass_future_get_result(future);
-    CassIterator* rows = cass_iterator_from_result(resultCollection);
-    int rowCount = 0;
-    while (cass_iterator_next(rows)) {
-        rowCount++;
-        if (single && rowCount > 1) {
-            result.clear();
-            break;
-        }
-        const CassRow* row = cass_iterator_get_row(rows);
-        CqlDataArray data;
-        exchange->createBindForReceive(data);
-        // Get data.
-        for (size_t i = 0U; i < data.size(); i++) {
-            CqlCommon::getData(row, i, i, *exchange, data);
-        }
-        result.push_back(exchange->retrieveLease());
-    }
+void
+CqlLease6Exchange::getLeaseCollection(StatementTag &statement_tag,
+                                      AnyArray &data,
+                                      Lease6Collection &result) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR4)
+        .arg(statement_tag);
+
+    AnyArray collection = executeSelect(connection_, data, statement_tag);
 
-    cass_iterator_free(rows);
-    cass_result_free(resultCollection);
-    cass_future_free(future);
-    cass_statement_free(statement);
-    if (single && rowCount > 1) {
-        isc_throw(MultipleRecords,
-                  "multiple records were found in the "
-                  "database where only one was expected for statement "
-                      << dbconn_.tagged_statements_[stindex].name_);
+    // Transfer Lease6 objects to result.
+    for (boost::any &lease : collection) {
+        result.push_back(boost::any_cast<Lease6Ptr>(lease));
     }
 }
 
 void
-CqlLeaseMgr::getLease(StatementIndex stindex,
-                      CqlDataArray& data,
-                      Lease4Ptr& result) const {
-    // Create appropriate collection object and get all leases matching
-    // the selection criteria. The "single" parameter is true to indicate
-    // that the called method should throw an exception if multiple
-    // matching records are found: this particular method is called when only
-    // one or zero matches is expected.
-    Lease4Collection collection;
-    getLeaseCollection(stindex, data, exchange4_, collection, true);
+CqlLease6Exchange::getLease(StatementTag &statement_tag,
+                            AnyArray &data,
+                            Lease6Ptr &result) {
+    // This particular method is called when only one or zero matches is
+    // expected.
+    Lease6Collection collection;
+    getLeaseCollection(statement_tag, data, collection);
 
     // Return single record if present, else clear the lease.
-    if (collection.empty()) {
+    const size_t collection_size = collection.size();
+    if (collection_size >= 2u) {
+        isc_throw(
+            MultipleRecords,
+            "CqlLease6Exchange::getLease(): multiple records were found in "
+            "the database where only one was expected for statement "
+                << statement_tag);
+    } else if (collection_size == 0u) {
         result.reset();
     } else {
         result = *collection.begin();
@@ -1221,109 +1392,199 @@ CqlLeaseMgr::getLease(StatementIndex stindex,
 }
 
 void
-CqlLeaseMgr::getLease(StatementIndex stindex,
-                      CqlDataArray& data,
-                      Lease6Ptr& result) const {
-    // Create appropriate collection object and get all leases matching
-    // the selection criteria. The "single" parameter is true to indicate
-    // that the called method should throw an exception if multiple
-    // matching records are found: this particular method is called when only
-    // one or zero matches is expected.
-    Lease6Collection collection;
-    getLeaseCollection(stindex, data, exchange6_, collection, true);
+CqlLease6Exchange::getExpiredLeases(const size_t &max_leases,
+                                    Lease6Collection &expired_leases) {
+    // Set up the WHERE clause value
+    cass_int32_t keep_state = Lease::STATE_EXPIRED_RECLAIMED;
+    cass_int64_t timestamp = static_cast<cass_int64_t>(time(NULL));
 
-    // Return single record if present, else clear the lease.
-    if (collection.empty()) {
-        result.reset();
-    } else {
-        result = *collection.begin();
+    // If the number of leases is 0, we will return all leases. This is
+    // achieved by setting the limit to a very high value.
+    cass_int32_t limit = max_leases > 0u ?
+                             static_cast<cass_int32_t>(max_leases) :
+                             std::numeric_limits<cass_int32_t>::max();
+
+    for (cass_int32_t state = Lease::STATE_DEFAULT;
+         state <= Lease::STATE_EXPIRED_RECLAIMED; state++) {
+        if (state == keep_state) {
+            continue;
+        }
+
+        AnyArray data;
+        data.add(&state);
+        data.add(&timestamp);
+        data.add(&limit);
+
+        // Retrieve leases from the database.
+        Lease6Collection temp_collection;
+        getLeaseCollection(CqlLease6Exchange::GET_LEASE6_EXPIRE, data,
+                           temp_collection);
+
+        for (Lease6Ptr &lease : temp_collection) {
+            expired_leases.push_back(lease);
+        }
+    }
+}
+
+CqlLeaseMgr::CqlLeaseMgr(const DatabaseConnection::ParameterMap &parameters)
+    : LeaseMgr(), dbconn_(parameters) {
+    dbconn_.openDatabase();
+    dbconn_.prepareStatements(CqlLease4Exchange::tagged_statements_);
+    dbconn_.prepareStatements(CqlLease6Exchange::tagged_statements_);
+    dbconn_.prepareStatements(CqlVersionExchange::tagged_statements_);
+}
+
+CqlLeaseMgr::~CqlLeaseMgr() {
+    // There is no need to close the database in this destructor: it is
+    // closed in the destructor of the dbconn_ member variable.
+}
+
+std::string
+CqlLeaseMgr::getDBVersion() {
+    std::stringstream tmp;
+    tmp << "CQL backend " << CQL_SCHEMA_VERSION_MAJOR;
+    tmp << "." << CQL_SCHEMA_VERSION_MINOR;
+    tmp << ", library cassandra_static";
+    return tmp.str();
+}
+
+bool
+CqlLeaseMgr::addLease(const Lease4Ptr &lease) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ADD_ADDR4)
+        .arg(lease->addr_.toText());
+
+    AnyArray data;
+
+    std::unique_ptr<CqlLease4Exchange> exchange4(
+        new CqlLease4Exchange(dbconn_));
+    exchange4->createBindForInsert(lease, data);
+    try {
+        exchange4->executeMutation(dbconn_, data,
+                                   CqlLease4Exchange::INSERT_LEASE4);
+    } catch (const Exception &exception) {
+        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+                  DHCPSRV_CQL_LEASE_EXCEPTION_THROWN)
+            .arg(exception.what());
+        return false;
     }
+    return true;
 }
 
-// Basic lease access methods. Obtain leases from the database using various
-// criteria.
+bool
+CqlLeaseMgr::addLease(const Lease6Ptr &lease) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ADD_ADDR6)
+        .arg(lease->addr_.toText());
+
+    AnyArray data;
+
+    std::unique_ptr<CqlLease6Exchange> exchange6(
+        new CqlLease6Exchange(dbconn_));
+    exchange6->createBindForInsert(lease, data);
+    try {
+        exchange6->executeMutation(dbconn_, data,
+                                   CqlLease6Exchange::INSERT_LEASE6);
+    } catch (const Exception &exception) {
+        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+                  DHCPSRV_CQL_LEASE_EXCEPTION_THROWN)
+            .arg(exception.what());
+        return false;
+    }
+    return true;
+}
 
 Lease4Ptr
-CqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
+CqlLeaseMgr::getLease4(const IOAddress &addr) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR4)
         .arg(addr.toText());
 
     // Set up the WHERE clause value
-    CqlDataArray data;
+    AnyArray data;
 
-    cass_int32_t addr4_data = static_cast<cass_int32_t>(addr.toUint32());
-    data.add(reinterpret_cast<void*>(&addr4_data));
+    cass_int32_t address = static_cast<cass_int32_t>(addr.toUint32());
+    data.add(&address);
 
-    // Get the data
+    // Get the data.
     Lease4Ptr result;
-    getLease(GET_LEASE4_ADDR, data, result);
+
+    std::unique_ptr<CqlLease4Exchange> exchange4(
+        new CqlLease4Exchange(dbconn_));
+    exchange4->getLease(CqlLease4Exchange::GET_LEASE4_ADDR, data, result);
 
     return result;
 }
 
 Lease4Collection
-CqlLeaseMgr::getLease4(const HWAddrhwaddr) const {
+CqlLeaseMgr::getLease4(const HWAddr &hwaddr) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_HWADDR)
         .arg(hwaddr.toText());
 
     // Set up the WHERE clause value
-    CqlDataArray data;
+    AnyArray data;
 
-    std::vector<cass_byte_t> hwaddr_data = hwaddr.hwaddr_;
-    data.add(reinterpret_cast<void*>(&hwaddr_data));
+    CassBlob hwaddr_data(hwaddr.hwaddr_);
+    data.add(&hwaddr_data);
 
-    // Get the data
+    // Get the data.
     Lease4Collection result;
-    getLeaseCollection(GET_LEASE4_HWADDR, data, result);
+    std::unique_ptr<CqlLease4Exchange> exchange4(
+        new CqlLease4Exchange(dbconn_));
+    exchange4->getLeaseCollection(CqlLease4Exchange::GET_LEASE4_HWADDR, data,
+                                  result);
 
     return result;
 }
 
 Lease4Ptr
-CqlLeaseMgr::getLease4(const HWAddrhwaddr, SubnetID subnet_id) const {
+CqlLeaseMgr::getLease4(const HWAddr &hwaddr, SubnetID subnet_id) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_GET_SUBID_HWADDR)
         .arg(subnet_id)
         .arg(hwaddr.toText());
 
     // Set up the WHERE clause value
-    CqlDataArray data;
+    AnyArray data;
 
-    std::vector<cass_byte_t> hwaddr_data = hwaddr.hwaddr_;
-    data.add(reinterpret_cast<void*>(&hwaddr_data));
+    CassBlob hwaddr_data(hwaddr.hwaddr_);
+    data.add(&hwaddr_data);
 
     cass_int32_t subnet_id_data = static_cast<cass_int32_t>(subnet_id);
-    data.add(reinterpret_cast<void*>(&subnet_id_data));
+    data.add(&subnet_id_data);
 
-    // Get the data
+    // Get the data.
     Lease4Ptr result;
-    getLease(GET_LEASE4_HWADDR_SUBID, data, result);
+    std::unique_ptr<CqlLease4Exchange> exchange4(
+        new CqlLease4Exchange(dbconn_));
+    exchange4->getLease(CqlLease4Exchange::GET_LEASE4_HWADDR_SUBID, data,
+                        result);
 
     return result;
 }
 
 Lease4Collection
-CqlLeaseMgr::getLease4(const ClientIdclientid) const {
+CqlLeaseMgr::getLease4(const ClientId &clientid) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_GET_CLIENTID)
         .arg(clientid.toText());
 
     // Set up the WHERE clause value
-    CqlDataArray data;
+    AnyArray data;
 
-    std::vector<cass_byte_t> client_id_data = clientid.getClientId();
-    data.add(reinterpret_cast<void*>(&client_id_data));
+    CassBlob client_id_data(clientid.getClientId());
+    data.add(&client_id_data);
 
-    // Get the data
+    // Get the data.
     Lease4Collection result;
-    getLeaseCollection(GET_LEASE4_CLIENTID, data, result);
+    std::unique_ptr<CqlLease4Exchange> exchange4(
+        new CqlLease4Exchange(dbconn_));
+    exchange4->getLeaseCollection(CqlLease4Exchange::GET_LEASE4_CLIENTID, data,
+                                  result);
 
     return result;
 }
 
 Lease4Ptr
-CqlLeaseMgr::getLease4(const ClientIdclientid,
-                       const HWAddrhwaddr,
+CqlLeaseMgr::getLease4(const ClientId &clientid,
+                       const HWAddr &hwaddr,
                        SubnetID subnet_id) const {
     /// This method is currently not implemented because allocation engine
     /// searches for the lease using HW address or client identifier.
@@ -1335,66 +1596,69 @@ CqlLeaseMgr::getLease4(const ClientId& clientid,
         .arg(hwaddr.toText())
         .arg(subnet_id);
 
-    isc_throw(NotImplemented, "The CqlLeaseMgr::getLease4 method was"
-                              " called, but it is not implemented");
+    isc_throw(NotImplemented, "CqlLeaseMgr::getLease4() not implemented yet");
 }
 
 Lease4Ptr
-CqlLeaseMgr::getLease4(const ClientIdclientid, SubnetID subnet_id) const {
+CqlLeaseMgr::getLease4(const ClientId &clientid, SubnetID subnet_id) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_GET_SUBID_CLIENTID)
         .arg(subnet_id)
         .arg(clientid.toText());
 
     // Set up the WHERE clause value
-    CqlDataArray data;
+    AnyArray data;
 
-    std::vector<uint8_t> client_id_data = clientid.getClientId();
-    data.add(reinterpret_cast<void*>(&client_id_data));
+    CassBlob client_id_data(clientid.getClientId());
+    data.add(&client_id_data);
 
     cass_int32_t subnet_id_data = static_cast<cass_int32_t>(subnet_id);
-    data.add(reinterpret_cast<void*>(&subnet_id_data));
+    data.add(&subnet_id_data);
 
-    // Get the data
+    // Get the data.
     Lease4Ptr result;
-    getLease(GET_LEASE4_CLIENTID_SUBID, data, result);
+    std::unique_ptr<CqlLease4Exchange> exchange4(
+        new CqlLease4Exchange(dbconn_));
+    exchange4->getLease(CqlLease4Exchange::GET_LEASE4_CLIENTID_SUBID, data,
+                        result);
 
     return result;
 }
 
 Lease6Ptr
-CqlLeaseMgr::getLease6(Lease::Type lease_type,
-                       const isc::asiolink::IOAddress& addr) const {
+CqlLeaseMgr::getLease6(Lease::Type lease_type, const IOAddress &addr) const {
     std::string addr_data = addr.toText();
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR6)
         .arg(addr_data)
         .arg(lease_type);
 
     // Set up the WHERE clause value
-    CqlDataArray data;
+    AnyArray data;
 
     if (addr_data.size() > ADDRESS6_TEXT_MAX_LEN) {
-        isc_throw(BadValue, "getLease6(): "
-                            "address "
-                                << addr_data << " of length "
-                                << addr_data.size()
-                                << " exceeds maximum allowed length of "
-                                << ADDRESS6_TEXT_MAX_LEN);
+        isc_throw(BadValue,
+                  "CqlLeaseMgr::getLease6(): "
+                  "address "
+                      << addr_data << " of length " << addr_data.size()
+                      << " exceeds maximum allowed length of "
+                      << ADDRESS6_TEXT_MAX_LEN);
     }
-    data.add(reinterpret_cast<void*>(&addr_data));
+    data.add(&addr_data);
 
     cass_int32_t lease_type_data = static_cast<cass_int32_t>(lease_type);
-    data.add(reinterpret_cast<void*>(&lease_type_data));
+    data.add(&lease_type_data);
 
     Lease6Ptr result;
-    getLease(GET_LEASE6_ADDR, data, result);
+    std::unique_ptr<CqlLease6Exchange> exchange6(
+        new CqlLease6Exchange(dbconn_));
+    exchange6->getLease(CqlLease6Exchange::GET_LEASE6_ADDR, data, result);
 
     return result;
 }
 
 Lease6Collection
 CqlLeaseMgr::getLeases6(Lease::Type lease_type,
-                        const DUIDduid,
+                        const DUID &duid,
                         uint32_t iaid) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_GET_IAID_DUID)
@@ -1403,27 +1667,30 @@ CqlLeaseMgr::getLeases6(Lease::Type lease_type,
         .arg(lease_type);
 
     // Set up the WHERE clause value
-    CqlDataArray data;
-
-    std::vector<cass_byte_t> duid_data = duid.getDuid();
-    data.add(reinterpret_cast<void*>(&duid_data));
+    AnyArray data;
 
+    CassBlob duid_data(duid.getDuid());
     cass_int32_t iaid_data = static_cast<cass_int32_t>(iaid);
-    data.add(reinterpret_cast<void*>(&iaid_data));
+
+    data.add(&duid_data);
+    data.add(&iaid_data);
 
     cass_int32_t lease_type_data = static_cast<cass_int32_t>(lease_type);
-    data.add(reinterpret_cast<void*>(&lease_type_data));
+    data.add(&lease_type_data);
 
-    // ... and get the data
+    // Get the data.
     Lease6Collection result;
-    getLeaseCollection(GET_LEASE6_DUID_IAID, data, result);
+    std::unique_ptr<CqlLease6Exchange> exchange6(
+        new CqlLease6Exchange(dbconn_));
+    exchange6->getLeaseCollection(CqlLease6Exchange::GET_LEASE6_DUID_IAID, data,
+                                  result);
 
     return result;
 }
 
 Lease6Collection
 CqlLeaseMgr::getLeases6(Lease::Type lease_type,
-                        const DUIDduid,
+                        const DUID &duid,
                         uint32_t iaid,
                         SubnetID subnet_id) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
@@ -1434,277 +1701,128 @@ CqlLeaseMgr::getLeases6(Lease::Type lease_type,
         .arg(lease_type);
 
     // Set up the WHERE clause value
-    CqlDataArray data;
-
-    std::vector<cass_byte_t> duid_data = duid.getDuid();
-    data.add(reinterpret_cast<void*>(&duid_data));
+    AnyArray data;
 
+    CassBlob duid_data(duid.getDuid());
     cass_int32_t iaid_data = static_cast<cass_int32_t>(iaid);
-    data.add(reinterpret_cast<void*>(&iaid_data));
 
-    cass_int32_t subnet_id_data = static_cast<cass_int32_t>(subnet_id);
-    data.add(reinterpret_cast<void*>(&subnet_id_data));
+    data.add(&duid_data);
+    data.add(&iaid_data);
 
     cass_int32_t lease_type_data = static_cast<cass_int32_t>(lease_type);
-    data.add(reinterpret_cast<void*>(&lease_type_data));
+    data.add(&lease_type_data);
+
+    cass_int32_t subnet_id_data = static_cast<cass_int32_t>(subnet_id);
+    data.add(&subnet_id_data);
 
-    // ... and get the data
+    // Get the data.
     Lease6Collection result;
-    getLeaseCollection(GET_LEASE6_DUID_IAID_SUBID, data, result);
+    std::unique_ptr<CqlLease6Exchange> exchange6(
+        new CqlLease6Exchange(dbconn_));
+    exchange6->getLeaseCollection(CqlLease6Exchange::GET_LEASE6_DUID_IAID_SUBID,
+                                  data, result);
 
     return result;
 }
 
 void
-CqlLeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases,
+CqlLeaseMgr::getExpiredLeases4(Lease4Collection &expired_leases,
                                const size_t max_leases) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_GET_EXPIRED6)
+              DHCPSRV_CQL_GET_EXPIRED4)
         .arg(max_leases);
-    getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE6_EXPIRE);
+
+    std::unique_ptr<CqlLease4Exchange> exchange4(
+        new CqlLease4Exchange(dbconn_));
+    exchange4->getExpiredLeases(max_leases, expired_leases);
 }
 
 void
-CqlLeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases,
+CqlLeaseMgr::getExpiredLeases6(Lease6Collection &expired_leases,
                                const size_t max_leases) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_GET_EXPIRED4)
+              DHCPSRV_CQL_GET_EXPIRED6)
         .arg(max_leases);
-    getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE4_EXPIRE);
-}
-
-template <typename LeaseCollection>
-void
-CqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
-                                    const size_t max_leases,
-                                    StatementIndex statement_index) const {
-    // Set up the WHERE clause value
-    cass_int32_t keepState = Lease::STATE_EXPIRED_RECLAIMED;
-    cass_int64_t timestamp = static_cast<cass_int64_t>(time(NULL));
-
-    // If the number of leases is 0, we will return all leases. This is
-    // achieved by setting the limit to a very high value.
-    cass_int32_t limit = max_leases > 0 ?
-                             static_cast<cass_int32_t>(max_leases) :
-                             std::numeric_limits<cass_int32_t>::max();
-
-    for (cass_int32_t state = Lease::STATE_DEFAULT;
-         state <= Lease::STATE_EXPIRED_RECLAIMED; state++) {
-        if (state == keepState) {
-            continue;
-        }
-        LeaseCollection tempCollection;
-        CqlDataArray data;
-
-        data.add(reinterpret_cast<void*>(&state));
-        data.add(reinterpret_cast<void*>(&timestamp));
-        data.add(reinterpret_cast<void*>(&limit));
-
-        // Retrieve leases from the database.
-        getLeaseCollection(statement_index, data, tempCollection);
-
-        typedef typename LeaseCollection::iterator LeaseCollectionIt;
-
-        for (LeaseCollectionIt it = tempCollection.begin();
-             it != tempCollection.end(); ++it) {
-            expired_leases.push_back((*it));
-        }
-    }
-}
-
-void
-CqlLeaseMgr::updateLeaseCommon(StatementIndex stindex,
-                               CqlDataArray& data,
-                               CqlLeaseExchange& exchange) {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ADD_ADDR4)
-        .arg(dbconn_.tagged_statements_[stindex].name_);
-
-    CassError rc;
-    CassStatement* statement = NULL;
-    CassFuture* future = NULL;
-
-    statement = cass_prepared_bind(dbconn_.statements_[stindex]);
-    if (!statement) {
-        isc_throw(DbOperationError, "unable to bind statement");
-    }
-
-    if (dbconn_.force_consistency_) {
-        rc = cass_statement_set_consistency(statement, dbconn_.consistency_);
-        if (rc != CASS_OK) {
-            cass_statement_free(statement);
-            isc_throw(
-                DbOperationError,
-                "unable to set statement consistency for statement "
-                    << dbconn_.tagged_statements_[stindex].name_);
-        }
-    }
-
-    CqlCommon::bindData(statement, stindex, data, exchange,
-                        CqlLeaseMgr::tagged_statements_);
-
-    future = cass_session_execute(dbconn_.session_, statement);
-    if (!future) {
-        cass_statement_free(statement);
-        isc_throw(DbOperationError,
-                  "unable to execute statement "
-                      << dbconn_.tagged_statements_[stindex].name_);
-    }
-    cass_future_wait(future);
-    std::string error;
-    dbconn_.checkStatementError(error, future, stindex, "unable to UPDATE");
-    rc = cass_future_error_code(future);
-    if (rc != CASS_OK) {
-        cass_future_free(future);
-        cass_statement_free(statement);
-        isc_throw(DbOperationError, error);
-    }
-
-    // Check if statement has been applied.
-    size_t row_count;
-    size_t column_count;
-    bool applied =
-        exchange.hasStatementBeenApplied(future, &row_count, &column_count);
 
-    // Free resources.
-    cass_future_free(future);
-    cass_statement_free(statement);
-
-    if (!applied) {
-        isc_throw(NoSuchLease, "Statement has not been applied.");
-    }
+    std::unique_ptr<CqlLease6Exchange> exchange6(
+        new CqlLease6Exchange(dbconn_));
+    exchange6->getExpiredLeases(max_leases, expired_leases);
 }
 
 void
-CqlLeaseMgr::updateLease4(const Lease4Ptr& lease) {
-    const StatementIndex stindex = UPDATE_LEASE4;
-
+CqlLeaseMgr::updateLease4(const Lease4Ptr &lease) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_UPDATE_ADDR4)
         .arg(lease->addr_.toText());
 
-    // Create the BIND array for the data being updated
-    CqlDataArray data;
-    exchange4_->createBindForSend(lease, data);
-    data.remove(0);
-
-    // Set up the WHERE clause and append it to the bind array.
-    cass_int32_t addr4_data =
-        static_cast<cass_int32_t>(lease->addr_.toUint32());
-    data.add(reinterpret_cast<void*>(&addr4_data));
+    std::unique_ptr<CqlLease4Exchange> exchange4(
+        new CqlLease4Exchange(dbconn_));
 
-    // Drop to common update code
-    updateLeaseCommon(stindex, data, *exchange4_);
+    try {
+        AnyArray data;
+        exchange4->createBindForUpdate(lease, data,
+                                       CqlLease4Exchange::UPDATE_LEASE4);
+        exchange4->executeMutation(dbconn_, data,
+                                   CqlLease4Exchange::UPDATE_LEASE4);
+    } catch (const StatementNotApplied &exception) {
+        isc_throw(NoSuchLease, exception.what());
+    }
 }
 
 void
-CqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
-    const StatementIndex stindex = UPDATE_LEASE6;
-    std::string lease_addr_data = lease->addr_.toText();
+CqlLeaseMgr::updateLease6(const Lease6Ptr &lease) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_UPDATE_ADDR6)
-        .arg(lease_addr_data);
-
-    // Create the BIND array for the data being updated
-    CqlDataArray data;
-    exchange6_->createBindForSend(lease, data);
-    data.remove(0);
-
-    // Set up the WHERE clause and append it to the BIND array
-    if (lease_addr_data.size() > ADDRESS6_TEXT_MAX_LEN) {
-        isc_throw(BadValue, "updateLease6(): "
-                            "address "
-                                << lease_addr_data << " of length "
-                                << lease_addr_data.size()
-                                << " exceeds maximum allowed length "
-                                   "of "
-                                << ADDRESS6_TEXT_MAX_LEN);
-    }
-    data.add(reinterpret_cast<void*>(&lease_addr_data));
-
-    // Drop to common update code
-    updateLeaseCommon(stindex, data, *exchange6_);
-}
-
-bool
-CqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex,
-                               CqlDataArray& data,
-                               CqlLeaseExchange& exchange) {
-    CassError rc;
-    CassStatement* statement = NULL;
-    CassFuture* future = NULL;
-
-    statement = cass_prepared_bind(dbconn_.statements_[stindex]);
-    if (!statement) {
-        isc_throw(DbOperationError, "unable to bind statement");
-    }
-
-    if (dbconn_.force_consistency_) {
-        rc = cass_statement_set_consistency(statement, dbconn_.consistency_);
-        if (rc != CASS_OK) {
-            cass_statement_free(statement);
-            isc_throw(
-                DbOperationError,
-                "unable to set statement consistency for statement "
-                    << dbconn_.tagged_statements_[stindex].name_);
-        }
-    }
+        .arg(lease->addr_.toText());
 
-    CqlCommon::bindData(statement, stindex, data, exchange,
-                        CqlLeaseMgr::tagged_statements_);
+    std::unique_ptr<CqlLease6Exchange> exchange6(
+        new CqlLease6Exchange(dbconn_));
 
-    future = cass_session_execute(dbconn_.session_, statement);
-    if (!future) {
-        cass_statement_free(statement);
-        isc_throw(DbOperationError,
-                  "unable to execute statement "
-                      << dbconn_.tagged_statements_[stindex].name_);
-    }
-    cass_future_wait(future);
-    std::string error;
-    dbconn_.checkStatementError(error, future, stindex, "unable to DELETE");
-    rc = cass_future_error_code(future);
-    if (rc != CASS_OK) {
-        cass_future_free(future);
-        cass_statement_free(statement);
-        isc_throw(DbOperationError, error);
+    try {
+        AnyArray data;
+        exchange6->createBindForUpdate(lease, data,
+                                       CqlLease6Exchange::UPDATE_LEASE6);
+        exchange6->executeMutation(dbconn_, data,
+                                   CqlLease6Exchange::UPDATE_LEASE6);
+    } catch (const StatementNotApplied &exception) {
+        isc_throw(NoSuchLease, exception.what());
     }
-
-    // Check if statement has been applied.
-    bool applied = exchange.hasStatementBeenApplied(future);
-
-    // Free resources.
-    cass_future_free(future);
-    cass_statement_free(statement);
-
-    return applied;
 }
 
 bool
-CqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
+CqlLeaseMgr::deleteLease(const IOAddress &addr) {
     std::string addr_data = addr.toText();
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_DELETE_ADDR)
         .arg(addr_data);
 
     // Set up the WHERE clause value
-    CqlDataArray data;
+    AnyArray data;
 
-    if (addr.isV4()) {
-        cass_int32_t addr4_data = static_cast<cass_int32_t>(addr.toUint32());
-        data.add(reinterpret_cast<void*>(&addr4_data));
-        return deleteLeaseCommon(DELETE_LEASE4, data, *exchange4_);
-    } else {
-        if (addr_data.size() > ADDRESS6_TEXT_MAX_LEN) {
-            isc_throw(BadValue, "deleteLease(): "
-                                "address "
-                                    << addr_data << " of length "
-                                    << addr_data.size()
-                                    << " exceeds maximum allowed length "
-                                       "of "
-                                    << ADDRESS6_TEXT_MAX_LEN);
+    try {
+        if (addr.isV4()) {
+            std::unique_ptr<CqlLease4Exchange> exchange4(
+                new CqlLease4Exchange(dbconn_));
+            exchange4->createBindForDelete(addr, data,
+                                           CqlLease4Exchange::DELETE_LEASE4);
+            exchange4->executeMutation(dbconn_, data,
+                                       CqlLease4Exchange::DELETE_LEASE4);
+        } else if (addr.isV6()) {
+            std::unique_ptr<CqlLease6Exchange> exchange6(
+                new CqlLease6Exchange(dbconn_));
+            exchange6->createBindForDelete(addr, data,
+                                           CqlLease6Exchange::DELETE_LEASE6);
+            exchange6->executeMutation(dbconn_, data,
+                                       CqlLease6Exchange::DELETE_LEASE6);
+        } else {
+            return false;
         }
-        data.add(reinterpret_cast<void*>(&addr_data));
-        return deleteLeaseCommon(DELETE_LEASE6, data, *exchange6_);
+    } catch (const Exception &exception) {
+        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+                  DHCPSRV_CQL_LEASE_EXCEPTION_THROWN)
+            .arg(exception.what());
+        return false;
     }
+    return true;
 }
 
 uint64_t
@@ -1712,8 +1830,34 @@ CqlLeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t secs) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_DELETE_EXPIRED_RECLAIMED4)
         .arg(secs);
-    return deleteExpiredReclaimedLeasesCommon(secs,
-                                              DELETE_LEASE4_STATE_EXPIRED);
+    AnyArray data;
+    uint64_t n_of_deleted_leases = 0u;
+    cass_int32_t limit = 1024;
+
+    // State is reclaimed.
+    cass_int32_t state =
+        static_cast<cass_int32_t>(Lease::STATE_EXPIRED_RECLAIMED);
+    data.add(&state);
+
+    // Expiration timestamp.
+    cass_int64_t expiration =
+        static_cast<cass_int64_t>(time(NULL) - static_cast<time_t>(secs));
+    data.add(&expiration);
+
+    data.add(&limit);
+
+    // Get the data.
+    Lease4Collection leases;
+    std::unique_ptr<CqlLease4Exchange> exchange4(
+        new CqlLease4Exchange(dbconn_));
+    exchange4->getLeaseCollection(CqlLease4Exchange::GET_LEASE4_EXPIRE, data,
+                                  leases);
+    for (Lease4Ptr &lease : leases) {
+        if (deleteLease(lease->addr_)) {
+            ++n_of_deleted_leases;
+        }
+    }
+    return n_of_deleted_leases;
 }
 
 uint64_t
@@ -1721,64 +1865,46 @@ CqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_DELETE_EXPIRED_RECLAIMED6)
         .arg(secs);
-    return deleteExpiredReclaimedLeasesCommon(secs,
-                                              DELETE_LEASE6_STATE_EXPIRED);
-}
-
-uint64_t
-CqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(
-    const uint32_t secs, StatementIndex statement_index) {
-    // Set up the WHERE clause value
-
-    CqlDataArray data;
-    uint64_t result = 0;
+    AnyArray data;
+    uint64_t n_of_deleted_leases = 0u;
+    cass_int32_t limit = 1024;
 
     // State is reclaimed.
     cass_int32_t state =
         static_cast<cass_int32_t>(Lease::STATE_EXPIRED_RECLAIMED);
-    data.add(reinterpret_cast<void*>(&state));
+    data.add(&state);
 
     // Expiration timestamp.
     cass_int64_t expiration =
         static_cast<cass_int64_t>(time(NULL) - static_cast<time_t>(secs));
-    data.add(reinterpret_cast<void*>(&expiration));
-
-    // Get the data
-    Lease4Collection result4Leases;
-    Lease6Collection result6Leases;
-    switch (statement_index) {
-    case DELETE_LEASE4_STATE_EXPIRED:
-        getLeaseCollection(statement_index, data, result4Leases);
-        break;
-    case DELETE_LEASE6_STATE_EXPIRED:
-        getLeaseCollection(statement_index, data, result6Leases);
-        break;
-    default:
-        break;
-    }
-    for (Lease4Collection::iterator it = result4Leases.begin();
-         it != result4Leases.end(); ++it) {
-        if (deleteLease((*it)->addr_)) {
-            result++;
-        }
-    }
-    for (Lease6Collection::iterator it = result6Leases.begin();
-         it != result6Leases.end(); ++it) {
-        if (deleteLease((*it)->addr_)) {
-            result++;
+    data.add(&expiration);
+
+    data.add(&limit);
+
+    // Get the data.
+    Lease6Collection leases;
+    std::unique_ptr<CqlLease6Exchange> exchange6(
+        new CqlLease6Exchange(dbconn_));
+    exchange6->getLeaseCollection(CqlLease6Exchange::GET_LEASE6_EXPIRE, data,
+                                  leases);
+    for (Lease6Ptr &lease : leases) {
+        if (deleteLease(lease->addr_)) {
+            ++n_of_deleted_leases;
         }
     }
-    return result;
+    return n_of_deleted_leases;
 }
 
 size_t
-CqlLeaseMgr::wipeLeases4(const SubnetID& /*subnet_id*/) {
-    isc_throw(NotImplemented, "wipeLeases4 is not implemented for Cassandra backend");
+CqlLeaseMgr::wipeLeases4(const SubnetID & /*subnet_id*/) {
+    isc_throw(NotImplemented,
+              "wipeLeases4 is not implemented for Cassandra backend");
 }
 
 size_t
-CqlLeaseMgr::wipeLeases6(const SubnetID& /*subnet_id*/) {
-    isc_throw(NotImplemented, "wipeLeases6 is not implemented for Cassandra backend");
+CqlLeaseMgr::wipeLeases6(const SubnetID & /*subnet_id*/) {
+    isc_throw(NotImplemented,
+              "wipeLeases6 is not implemented for Cassandra backend");
 }
 
 std::string
@@ -1797,71 +1923,14 @@ CqlLeaseMgr::getDescription() const {
     return std::string("Cassandra Database");
 }
 
-std::pair<unsigned int, unsigned int>
+VersionPair
 CqlLeaseMgr::getVersion() const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_GET_VERSION);
-    cass_int32_t version;
-    cass_int32_t minor;
-    CassError rc;
-    CassStatement* statement = NULL;
-    CassFuture* future = NULL;
-
-    statement = cass_prepared_bind(dbconn_.statements_[GET_VERSION]);
-    if (!statement) {
-        isc_throw(DbOperationError, "unable to bind statement");
-    }
-
-    if (dbconn_.force_consistency_) {
-        rc = cass_statement_set_consistency(statement, dbconn_.consistency_);
-        if (rc != CASS_OK) {
-            cass_statement_free(statement);
-            isc_throw(
-                DbOperationError,
-                "unable to set statement consistency for statement "
-                    << dbconn_.tagged_statements_[GET_VERSION].name_);
-        }
-    }
-
-    future = cass_session_execute(dbconn_.session_, statement);
-    if (!future) {
-        cass_statement_free(statement);
-        isc_throw(DbOperationError,
-                  "unable to execute statement "
-                      << dbconn_.tagged_statements_[GET_VERSION].name_);
-    }
-    cass_future_wait(future);
-    std::string error;
-    dbconn_.checkStatementError(error, future, "unable to GET version");
-    rc = cass_future_error_code(future);
-    if (rc != CASS_OK) {
-        cass_future_free(future);
-        cass_statement_free(statement);
-        isc_throw(DbOperationError, error);
-    }
-
-    // Get major and minor versions.
-    const CassResult* resultCollection = cass_future_get_result(future);
-    CassIterator* rows = cass_iterator_from_result(resultCollection);
-    CqlDataArray data;
-    while (cass_iterator_next(rows)) {
-        const CassRow* row = cass_iterator_get_row(rows);
-        // version: int
-        data.add(reinterpret_cast<void*>(&version));
-        // minor: int
-        data.add(reinterpret_cast<void*>(&minor));
-        for (size_t i = 0U; i < data.size(); i++) {
-            CqlCommon::getData(row, i, i, *versionExchange_, data);
-        }
-    }
-
-    cass_iterator_free(rows);
-    cass_result_free(resultCollection);
-    cass_future_free(future);
-    cass_statement_free(statement);
 
-    return std::pair<unsigned int, unsigned int>(
-        static_cast<unsigned int>(version), static_cast<unsigned int>(minor));
+    std::unique_ptr<CqlVersionExchange> version_exchange(
+        new CqlVersionExchange());
+    return version_exchange->retrieveVersion(dbconn_);
 }
 
 void
index ad1f4312a3ebfb8bf12d9e6287c067c0af87c42b..5dd8dba96526c136cad9dc312b017f5162dbe684 100644 (file)
@@ -1,6 +1,7 @@
-// Copyright (C) 2015 - 2017 Deutsche Telekom AG.
+// Copyright (C) 2015-2017 Deutsche Telekom AG.
 //
-// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//          Andrei Pavel <andrei.pavel@qualitance.com>
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 #ifndef CQL_LEASE_MGR_H
 #define CQL_LEASE_MGR_H
 
-#include <cassandra.h>
-
 #include <dhcp/hwaddr.h>
 #include <dhcpsrv/cql_connection.h>
 #include <dhcpsrv/cql_exchange.h>
 #include <dhcpsrv/lease_mgr.h>
 
 #include <boost/scoped_ptr.hpp>
-#include <boost/utility.hpp>
 
 #include <string>
 #include <utility>
@@ -61,7 +59,7 @@ public:
     ///
     /// Finally, all the CQL commands are pre-compiled.
     ///
-    /// @param parameters A data structure relating keywords and values
+    /// @param parameters a data structure relating keywords and values
     ///        concerned with the database.
     ///
     /// @throw isc::dhcp::NoDatabaseName Mandatory database name not given
@@ -85,7 +83,7 @@ public:
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual bool addLease(const Lease4Ptr& lease);
+    virtual bool addLease(const Lease4Ptr& lease) override;
 
     /// @brief Adds an IPv6 lease
     ///
@@ -96,7 +94,11 @@ public:
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual bool addLease(const Lease6Ptr& lease);
+    virtual bool addLease(const Lease6Ptr& lease) override;
+
+    /// @brief Basic lease access methods. Obtain leases from the database using
+    ///     various criteria.
+    /// @{
 
     /// @brief Returns an IPv4 lease for specified IPv4 address
     ///
@@ -113,7 +115,8 @@ public:
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
+    virtual Lease4Ptr
+    getLease4(const isc::asiolink::IOAddress& addr) const override;
 
     /// @brief Returns existing IPv4 leases for specified hardware address.
     ///
@@ -128,7 +131,8 @@ public:
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual Lease4Collection getLease4(const isc::dhcp::HWAddr& hwaddr) const;
+    virtual Lease4Collection
+    getLease4(const isc::dhcp::HWAddr& hwaddr) const override;
 
     /// @brief Returns existing IPv4 leases for specified hardware address
     ///        and a subnet
@@ -144,7 +148,7 @@ public:
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
     virtual Lease4Ptr getLease4(const isc::dhcp::HWAddr& hwaddr,
-                                SubnetID subnet_id) const;
+                                SubnetID subnet_id) const override;
 
     /// @brief Returns existing IPv4 leases for specified client-id
     ///
@@ -159,19 +163,22 @@ public:
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual Lease4Collection getLease4(const ClientId& clientid) const;
+    virtual Lease4Collection getLease4(const ClientId& clientid) const override;
 
     /// @brief Returns IPv4 lease for the specified client identifier, HW
     /// address and subnet identifier.
     ///
     /// @param client_id A client identifier.
-    /// @param hwaddr Hardware address.
+    /// @param hwaddr hardware address.
     /// @param subnet_id A subnet identifier.
     ///
     /// @return A pointer to the lease or NULL if the lease is not found.
+    ///
+    /// @throw isc::NotImplemented On every call as this method is currently
+    /// not implemented for the CQL backend.
     virtual Lease4Ptr getLease4(const ClientId& client_id,
                                 const HWAddr& hwaddr,
-                                SubnetID subnet_id) const;
+                                SubnetID subnet_id) const override;
 
     /// @brief Returns existing IPv4 lease for specified client-id
     ///
@@ -186,7 +193,7 @@ public:
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
     virtual Lease4Ptr getLease4(const ClientId& clientid,
-                                SubnetID subnet_id) const;
+                                SubnetID subnet_id) const override;
 
     /// @brief Returns existing IPv6 lease for a given IPv6 address.
     ///
@@ -203,8 +210,9 @@ public:
     ///        lease type field.
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual Lease6Ptr getLease6(Lease::Type type,
-                                const isc::asiolink::IOAddress& addr) const;
+    virtual Lease6Ptr
+    getLease6(Lease::Type type,
+              const isc::asiolink::IOAddress& addr) const override;
 
     /// @brief Returns existing IPv6 leases for a given DUID+IA combination
     ///
@@ -223,8 +231,9 @@ public:
     ///        lease type field.
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual Lease6Collection
-    getLeases6(Lease::Type type, const DUID& duid, uint32_t iaid) const;
+    virtual Lease6Collection getLeases6(Lease::Type type,
+                                        const DUID& duid,
+                                        uint32_t iaid) const override;
 
     /// @brief Returns existing IPv6 lease for a given DUID+IA combination
     ///
@@ -242,7 +251,7 @@ public:
     virtual Lease6Collection getLeases6(Lease::Type type,
                                         const DUID& duid,
                                         uint32_t iaid,
-                                        SubnetID subnet_id) const;
+                                        SubnetID subnet_id) const override;
     /// @brief Returns a collection of expired DHCPv6 leases.
     ///
     /// This method returns at most @c max_leases expired leases. The leases
@@ -254,7 +263,7 @@ public:
     /// @param max_leases A maximum number of leases to be returned. If this
     /// value is set to 0, all expired (but not reclaimed) leases are returned.
     virtual void getExpiredLeases6(Lease6Collection& expired_leases,
-                                   const size_t max_leases) const;
+                                   const size_t max_leases) const override;
 
     /// @brief Returns a collection of expired DHCPv4 leases.
     ///
@@ -267,7 +276,9 @@ public:
     /// @param max_leases A maximum number of leases to be returned. If this
     /// value is set to 0, all expired (but not reclaimed) leases are returned.
     virtual void getExpiredLeases4(Lease4Collection& expired_leases,
-                                   const size_t max_leases) const;
+                                   const size_t max_leases) const override;
+
+    /// @}
 
     /// @brief Updates IPv4 lease.
     ///
@@ -280,7 +291,7 @@ public:
     ///        exist.
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual void updateLease4(const Lease4Ptr& lease4);
+    virtual void updateLease4(const Lease4Ptr& lease4) override;
 
     /// @brief Updates IPv6 lease.
     ///
@@ -293,7 +304,7 @@ public:
     ///        exist.
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual void updateLease6(const Lease6Ptr& lease6);
+    virtual void updateLease6(const Lease6Ptr& lease6) override;
 
     /// @brief Deletes a lease.
     ///
@@ -308,21 +319,23 @@ public:
 
     /// @brief Deletes all expired and reclaimed DHCPv4 leases.
     ///
-    /// @param secs Number of seconds since expiration of leases before
+    /// @param secs number of seconds since expiration of leases before
     /// they can be removed. Leases which have expired later than this
     /// time will not be deleted.
     ///
     /// @return Number of leases deleted.
-    virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs);
+    virtual uint64_t
+    deleteExpiredReclaimedLeases4(const uint32_t secs) override;
 
     /// @brief Deletes all expired and reclaimed DHCPv6 leases.
     ///
-    /// @param secs Number of seconds since expiration of leases before
+    /// @param secs number of seconds since expiration of leases before
     /// they can be removed. Leases which have expired later than this
     /// time will not be deleted.
     ///
     /// @return Number of leases deleted.
-    virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs);
+    virtual uint64_t
+    deleteExpiredReclaimedLeases6(const uint32_t secs) override;
 
     /// @brief Removes specified IPv4 leases.
     ///
@@ -349,21 +362,21 @@ public:
     /// @brief Return backend type
     ///
     /// @return Type of the backend.
-    virtual std::string getType() const {
+    virtual std::string getType() const override {
         return (std::string("cql"));
     }
 
     /// @brief Returns name of the database.
     ///
     /// @return database name
-    virtual std::string getName() const;
+    virtual std::string getName() const override;
 
     /// @brief Returns description of the backend.
     ///
     /// This description may be multiline text that describes the backend.
     ///
     /// @return Description of the backend.
-    virtual std::string getDescription() const;
+    virtual std::string getDescription() const override;
 
     /// @brief Returns backend version.
     ///
@@ -372,251 +385,21 @@ public:
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual std::pair<unsigned int, unsigned int> getVersion() const;
+    virtual VersionPair getVersion() const override;
 
     /// @brief Commit Transactions
     ///
     /// This is a no-op for Cassandra.
-    virtual void commit();
+    virtual void commit() override;
 
     /// @brief Rollback Transactions
     ///
     /// This is a no-op for Cassandra.
-    virtual void rollback();
-
-    /// @brief Statement Tags
-    ///
-    /// The contents of the enum are indexes into the list of compiled CQL
-    /// statements
-    enum StatementIndex {
-        DELETE_LEASE4,                // Delete from lease4 by address
-        DELETE_LEASE4_STATE_EXPIRED,  // Delete expired lease4s in certain
-                                      // state
-        DELETE_LEASE6,                // Delete from lease6 by address
-        DELETE_LEASE6_STATE_EXPIRED,  // Delete expired lease6s in certain
-                                      // state
-        GET_LEASE4_ADDR,              // Get lease4 by address
-        GET_LEASE4_CLIENTID,          // Get lease4 by client ID
-        GET_LEASE4_CLIENTID_SUBID,    // Get lease4 by client ID & subnet ID
-        GET_LEASE4_HWADDR,            // Get lease4 by HW address
-        GET_LEASE4_HWADDR_SUBID,      // Get lease4 by HW address & subnet ID
-        GET_LEASE4_EXPIRE,            // Get expired lease4
-        GET_LEASE6_ADDR,              // Get lease6 by address
-        GET_LEASE6_DUID_IAID,         // Get lease6 by DUID and IAID
-        GET_LEASE6_DUID_IAID_SUBID,   // Get lease6 by DUID, IAID and subnet
-                                      // ID
-        GET_LEASE6_EXPIRE,            // Get expired lease6
-        GET_VERSION,                  // Obtain version number
-        INSERT_LEASE4,                // Add entry to lease4 table
-        INSERT_LEASE6,                // Add entry to lease6 table
-        UPDATE_LEASE4,                // Update a Lease4 entry
-        UPDATE_LEASE6,                // Update a Lease6 entry
-        NUM_STATEMENTS                // Number of statements
-    };
+    virtual void rollback() override;
 
 private:
-    /// @brief Add Lease Common Code
-    ///
-    /// This method performs the common actions for both flavours (V4 and V6)
-    /// of the addLease method. It binds the contents of the lease object to
-    /// the prepared statement and adds it to the database.
-    ///
-    /// @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
-    ///
-    /// @return true if the lease was added, false if it was not added because
-    ///         a lease with that address already exists in the database.
-    ///
-    /// @throw isc::dhcp::DbOperationError An operation on the open database has
-    ///        failed.
-    bool addLeaseCommon(StatementIndex stindex,
-                        CqlDataArray& data,
-                        CqlLeaseExchange& exchange);
-
-    /// @brief Get Lease Collection Common Code
-    ///
-    /// This method performs the common actions for obtaining multiple leases
-    /// from the database.
-    ///
-    /// @param stindex Index of statement being executed
-    /// @param data array containing the where clause input parameters
-    /// @param exchange Exchange object to use
-    /// @param result Returned collection of Leases Note that any leases in
-    ///        the collection when this method is called are not erased: the
-    ///        new data is appended to the end.
-    /// @param single If true, only a single data item is to be retrieved.
-    ///        If more than one is present, a MultipleRecords exception will
-    ///        be thrown.
-    ///
-    /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
-    /// @throw isc::dhcp::DbOperationError An operation on the open database has
-    ///        failed.
-    /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
-    ///        from the database where only one was expected.
-    template <typename Exchange, typename LeaseCollection>
-    void getLeaseCollection(StatementIndex stindex,
-                            CqlDataArray& data_array,
-                            Exchange& exchange,
-                            LeaseCollection& result,
-                            bool single = false) const;
-
-    /// @brief Gets Lease4 Collection
-    ///
-    /// Gets a collection of Lease4 objects. This is just an interface to
-    /// the get lease collection common code.
-    ///
-    /// @param stindex Index of statement being executed
-    /// @param data array containing the where clause input parameters
-    /// @param result LeaseCollection object returned. Note that any leases in
-    ///        the collection when this method is called are not erased: the
-    ///        new data is appended to the end.
-    ///
-    /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
-    /// @throw isc::dhcp::DbOperationError An operation on the open database has
-    ///        failed.
-    /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
-    ///        from the database where only one was expected.
-    void getLeaseCollection(StatementIndex stindex,
-                            CqlDataArray& data,
-                            Lease4Collection& result) const {
-        getLeaseCollection(stindex, data, exchange4_, result);
-    }
-
-    /// @brief Get Lease6 Collection
-    ///
-    /// Gets a collection of Lease6 objects. This is just an interface to
-    /// the get lease collection common code.
-    ///
-    /// @param stindex Index of statement being executed
-    /// @param data array containing input parameters for the query
-    /// @param result LeaseCollection object returned. Note that any existing
-    ///        data in the collection is erased first.
-    ///
-    /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
-    /// @throw isc::dhcp::DbOperationError An operation on the open database has
-    ///        failed.
-    /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
-    ///        from the database where only one was expected.
-    void getLeaseCollection(StatementIndex stindex,
-                            CqlDataArray& data,
-                            Lease6Collection& result) const {
-        getLeaseCollection(stindex, data, exchange6_, result);
-    }
-
-    /// @brief Get Lease4 Common Code
-    ///
-    /// This method performs the common actions for the various getLease4()
-    /// methods. It acts as an interface to the getLeaseCollection() method,
-    /// but retrieving only a single lease.
-    ///
-    /// @param stindex Index of statement being executed
-    /// @param data array containing input parameters for the query
-    /// @param lease Lease4 object returned
-    void getLease(StatementIndex stindex,
-                  CqlDataArray& data,
-                  Lease4Ptr& result) const;
-
-    /// @brief Get Lease6 Common Code
-    ///
-    /// This method performs the common actions for the various getLease4()
-    /// methods. It acts as an interface to the getLeaseCollection() method,
-    /// but retrieving only a single lease.
-    ///
-    /// @param stindex Index of statement being executed
-    /// @param data array containing input parameters for the query
-    /// @param lease Lease6 object returned
-    void getLease(StatementIndex stindex,
-                  CqlDataArray& data,
-                  Lease6Ptr& result) const;
-
-    /// @brief Get expired leases common code.
-    ///
-    /// This method retrieves expired and not reclaimed leases from the
-    /// lease database. The returned leases are ordered by the expiration
-    /// time. The maximum number of leases to be returned is specified
-    /// as an argument.
-    ///
-    /// @param [out] expired_leases Reference to the container where the
-    ///        retrieved leases are put.
-    /// @param max_leases Maximum number of leases to be returned.
-    /// @param statement_index One of the @c GET_LEASE4_EXPIRE or
-    ///        @c GET_LEASE6_EXPIRE.
-    ///
-    /// @tparam One of the @c Lease4Collection or @c Lease6Collection.
-    template <typename LeaseCollection>
-    void getExpiredLeasesCommon(LeaseCollection& expired_leases,
-                                const size_t max_leases,
-                                StatementIndex statement_index) const;
-
-    /// @brief Update lease common code
-    ///
-    /// Holds the common code for updating a lease. It binds the parameters
-    /// to the prepared statement, executes it, then checks how many rows
-    /// were affected.
-    ///
-    /// @param stindex Index of prepared statement to be executed
-    /// @param data array containing lease values and where clause
-    /// parameters for the update.
-    /// @param lease Pointer to the lease object whose record is being updated.
-    ///
-    /// @throw NoSuchLease Could not update a lease because no lease matches
-    ///        the address given.
-    /// @throw isc::dhcp::DbOperationError An operation on the open database has
-    ///        failed.
-    void updateLeaseCommon(StatementIndex stindex,
-                           CqlDataArray& data,
-                           CqlLeaseExchange& exchange);
-
-    /// @brief Delete lease common code
-    ///
-    /// Holds the common code for deleting a lease. It binds the parameters
-    /// to the prepared statement, executes the statement and checks to
-    /// see how many rows were deleted.
-    ///
-    /// @param stindex Index of prepared statement to be executed
-    /// @param data array containing lease values and where clause
-    /// parameters for the delete
-    ///
-    /// @return Number of deleted leases.
-    ///
-    /// @throw isc::dhcp::DbOperationError An operation on the open database has
-    ///        failed.
-    bool deleteLeaseCommon(StatementIndex stindex,
-                           CqlDataArray& data,
-                           CqlLeaseExchange& exchange);
-
-    /// @brief Delete expired-reclaimed leases.
-    ///
-    /// @param secs Number of seconds since expiration of leases before
-    /// they can be removed. Leases which have expired later than this
-    /// time will not be deleted.
-    /// @param statement_index One of the @c DELETE_LEASE4_STATE_EXPIRED or
-    ///        @c DELETE_LEASE6_STATE_EXPIRED.
-    ///
-    /// @return Number of leases deleted.
-    uint64_t deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
-                                                StatementIndex statement_index);
-
-    /// @brief CQL queries used by CQL backend
-    static CqlTaggedStatement tagged_statements_[];
-
     /// @brief Database connection object
     mutable CqlConnection dbconn_;
-
-    /// @{
-    /// The exchange objects are used for transfer of data to/from the database.
-    /// They are pointed-to objects as the contents may change in "const" calls,
-    /// while the rest of this object does not. (At alternative would be to
-    /// declare them as "mutable".)
-    /// @brief Exchange object for IPv4
-    boost::scoped_ptr<CqlLease4Exchange> exchange4_;
-    /// @brief Exchange object for IPv6
-    boost::scoped_ptr<CqlLease6Exchange> exchange6_;
-    /// @brief Exchange object for version
-    boost::scoped_ptr<CqlVersionExchange> versionExchange_;
-    /// @}
 };
 
 }  // namespace dhcp
index d9cc4449dd5d21a6049d9d287b000f5f5bbd4423..e6d2eca2877d715ca1c0e7b2093e95ac2a4c525e 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 namespace isc {
 namespace dhcp {
 
+/// @brief Database statement not applied
+class StatementNotApplied : public Exception {
+public:
+    StatementNotApplied(const char* file, size_t line, const char* what)
+        : isc::Exception(file, line, what) {
+    }
+};
+
 /// @brief Multiple lease records found where one expected
 class MultipleRecords : public Exception {
 public:
@@ -47,7 +55,7 @@ public:
         isc::Exception(file, line, what) {}
 };
 
-};
-};
+}  // namespace isc
+}  // namespace dhcp
 
 #endif
index 1aa7369d2333d1434a2341d2ae40c87985af71f0..bb24bf495fefaed73fcb9102707dfde1b10efb5b 100644 (file)
@@ -185,17 +185,11 @@ with the specified address to the Cassandra backend database.
 A debug message issued when the server is about to add an IPv6 lease
 with the specified address to the Cassandra backend database.
 
-% DHCPSRV_CQL_COMMIT committing to Cassandra database
+% DHCPSRV_CQL_COMMIT committing to Cassandra database.
 A commit call been issued on the server. For Cassandra, this is a no-op.
 
-% DHCPSRV_CQL_BEGIN_TRANSACTION committing to Cassandra database.
-The server has issued a begin transaction call.
-
-% DHCPSRV_CQL_DB opening Cassandra lease database: %1
-This informational message is logged when a DHCP server (either V4 or
-V6) is about to open a Cassandra lease database.  The parameters of
-the connection including database name and username needed to access it
-(but not the password if any) are logged.
+% DHCPSRV_CQL_CONNECTION_COMMIT committing to Cassandra database on current connection.
+A commit call been issued on the server. For Cassandra, this is a no-op.
 
 % DHCPSRV_CQL_DEALLOC_ERROR An error occurred while closing the CQL connection: %1
 This is an error message issued when a DHCP server (either V4 or V6) experienced
@@ -204,6 +198,48 @@ the Cassandra database. The connection is closed as part of normal server
 shutdown. This error is most likely a programmatic issue that is highly
 unlikely to occur or negatively impact server operation.
 
+% DHCPSRV_CQL_TRANSACTION_MGR_TRANSACTION_START starting transaction %1.
+The server has issued a begin transaction call.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_REGISTER_TRANSACTION registering action on opened transaction %1.
+The server has issued a begin transaction call on an opened transaction.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_TRANSACTION_COMMIT applying commit on transaction %1.
+The server has issued a commit transaction call.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_REGISTER_TRANSACTION_COMMIT registering commit on opened transaction %1.
+The server has issued a commit transaction call on an opened transaction.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_TRANSACTION_ROLLBACK applying rollback on transaction %1.
+The server has issued a rollback transaction call.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_REGISTER_TRANSACTION_ROLLBACK registering rollback on opened transaction %1.
+The server has issued a rollback transaction call on an opened transaction.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_TRANSACTION_NOT_FOUND failed to select a transaction.
+The server failed to select a transaction to operate on.
+
+% DHCPSRV_CQL_BEGIN_TRANSACTION begin transaction.
+The server has issued a begin transaction call.
+
+% DHCPSRV_CQL_CONNECTION_BEGIN_TRANSACTION begin transaction on current connection.
+The server has issued a begin transaction call.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_START_TRANSACTION start transaction action has been initiated.
+The server has issued a start transaction call.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_COMMIT_TRANSACTION commit transaction action has been initiated.
+The server has issued a commit transaction call.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_ROLLBACK_TRANSACTION rollback transaction action has been initiated.
+The server has issued a rollback transaction call.
+
+% DHCPSRV_CQL_DB opening Cassandra lease database: %1
+This informational message is logged when a DHCP server (either V4 or
+V6) is about to open a Cassandra lease database.  The parameters of
+the connection including database name and username needed to access it
+(but not the password if any) are logged.
+
 % DHCPSRV_CQL_DELETE_ADDR deleting lease for address %1
 A debug message issued when the server is attempting to delete a lease from the
 Cassandra database for the specified address.
@@ -277,9 +313,11 @@ subnet ID and hardware address.
 A debug message issued when the server is about to obtain schema version
 information from the Cassandra database.
 
-% DHCPSRV_CQL_ROLLBACK rolling back Cassandra database
-The code has issued a rollback call. For Cassandra, this is
-a no-op.
+% DHCPSRV_CQL_ROLLBACK rolling back Cassandra database.
+The code has issued a rollback call. For Cassandra, this is a no-op.
+
+% DHCPSRV_CQL_CONNECTION_ROLLBACK rolling back Cassandra database on current connection.
+The code has issued a rollback call. For Cassandra, this is a no-op.
 
 % DHCPSRV_CQL_UPDATE_ADDR4 updating IPv4 lease for address %1
 A debug message issued when the server is attempting to update IPv4
@@ -289,6 +327,148 @@ lease from the Cassandra database for the specified address.
 A debug message issued when the server is attempting to update IPv6
 lease from the Cassandra database for the specified address.
 
+% DHCPSRV_CQL_INSERT_SRV_CONFIG4 Inserting a new DHCPv4 server configuration with id %1
+A debug message issued when there is no existing DHCPv4 server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_CQL_INSERT_SRV_CONFIG6 Inserting a new DHCPv6 server configuration with id %1
+A debug message issued when there is no existing DHCPv6 server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_CQL_UPDATE_SRV_CONFIG4 Updating the DHCPv4 server configuration with id %1.
+A debug message issued when there already exists a DHCPv4 server configuration
+in database and this configuration is going to be updated.
+
+% DHCPSRV_CQL_UPDATE_SRV_CONFIG6 Updating the DHCPv6 server configuration with id %1.
+A debug message issued when there already exists a DHCPv6 server configuration
+in database and this configuration is going to be updated.
+
+% DHCPSRV_CQL_REQUEST_UPDATE_SRV_CONFIG4 Received request to update the DHCPv4 database server configuration
+A debug message issued when a DHCPv4 server configuration database update request is received.
+If there already exists a configuration in the database then the configuration will be updated.
+Otherwise a new configuration will be inserted.
+
+% DHCPSRV_CQL_REQUEST_UPDATE_SRV_CONFIG6 Received request to update the DHCPv6 database server configuration
+A debug message issued when a DHCPv6 server configuration database update request is received.
+If there already exists a configuration in the database then the configuration will be updated.
+Otherwise a new configuration will be inserted.
+
+% DHCPSRV_CQL_UPDATE_SRV_CONFIG4_TIMESTAMP_CHANGED Cannot update the DHCPv4 database server configuration (old timestamp is %1, new timestamp is %2)
+A warning message issued when DHCPv4 database server configuration cannot be updated.
+On a database server configuration update the actual timestamp of the configuration is also provided to the server.
+If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails.
+
+% DHCPSRV_CQL_UPDATE_SRV_CONFIG6_TIMESTAMP_CHANGED Cannot update the DHCPv6 database server configuration (old timestamp is %1, new timestamp is %2)
+A warning message issued when DHCPv6 database server configuration cannot be updated.
+On a database server configuration update the actual timestamp of the configuration is also provided to the server.
+If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails.
+
+% DHCPSRV_CQL_GET_SRV_CONFIG4_TIMESTAMP Obtaining the DHCPv4 server configuration's version from database
+Obtains the DHCPv4 server configuration's version from database.
+
+% DHCPSRV_CQL_GET_SRV_CONFIG6_TIMESTAMP Obtaining the DHCPv6 server configuration's version from database
+Obtains the DHCPv6 server configuration's version from database.
+
+% DHCPSRV_CQL_GET_SRV_CONFIG4_JSON Obtaining the DHCPv4 server JSON configuration from database
+Obtains the DHCPv4 server JSON configuration from database.
+
+% DHCPSRV_CQL_GET_SRV_CONFIG6_JSON Obtaining the DHCPv6 server JSON configuration from database
+Obtains the DHCPv6 server JSON configuration from database.
+
+% DHCPSRV_CQL_GET_SRV_CONFIG4_GENERIC Obtaining the DHCPv4 server GENERIC configuration from database
+Obtains the DHCPv4 server GENERIC configuration from database.
+
+% DHCPSRV_CQL_GET_SRV_CONFIG6_GENERIC Obtaining the DHCPv6 server GENERIC configuration from database
+Obtains the DHCPv6 server GENERIC configuration from database.
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4 Obtaining the DHCPv4 server configuration from the master database id %1
+Obtains the DHCPv4 server configuration from the master database.
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6 Obtaining the DHCPv6 server configuration from the master database id %1
+Obtains the DHCPv6 server configuration from the master database.
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database
+Obtains the DHCPv4 server configuration from the master database for the specified shard database.
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database
+Obtains the DHCPv6 server configuration from the master database for the specified shard database.
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4_SHARDS_NAME Obtaining the DHCPv4 shards name from the master database
+Obtains the DHCPv4 shards name from the master database
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6_SHARDS_NAME Obtaining the DHCPv6 shards name from the master database
+Obtains the DHCPv6 shards name from the master database
+
+% DHCPSRV_CQL_CLEAR_SRV_MASTER_CONFIG4 Clearing the DHCPv4 master configuration from database
+Clears the DHCPv4 master configuration from database
+
+% DHCPSRV_CQL_CLEAR_SRV_MASTER_CONFIG6 Clearing the DHCPv6 master configuration from database
+Clears the DHCPv6 master configuration from database
+
+% DHCPSRV_CQL_DELETE_SRV_MASTER_CONFIG4 Deleting the DHCPv4 master configuration from database id %1
+Clears the DHCPv4 master configuration from database
+
+% DHCPSRV_CQL_DELETE_SRV_MASTER_CONFIG6 Deleting the DHCPv6 master configuration from database id %1
+Clears the DHCPv6 master configuration from database
+
+% DHCPSRV_CQL_INSERT_SRV_MASTER_CONFIG4 Inserting a new DHCPv4 master server configuration with id %1 and shard database %2
+A debug message issued when there is no existing DHCPv4 master server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_CQL_INSERT_SRV_MASTER_CONFIG6 Inserting a new DHCPv6 master server configuration with id %1 and shard database %2
+A debug message issued when there is no existing DHCPv6 master server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4_TIMESTAMP Obtaining the DHCPv4 master server configuration's version from database id %1
+Obtains the DHCPv4 master server configuration's version from database.
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6_TIMESTAMP Obtaining the DHCPv6 master server configuration's version from database id %1
+Obtains the DHCPv6 master server configuration's version from database.
+
+% DHCPSRV_CQL_HOST_DB Connecting to CQL hosts database: %1
+An informational message logged when the CQL hosts database is about to be
+connected to. The parameters of the connection including database name and
+username needed to access it (but not the password if any) are logged.
+
+% DHCPSRV_CQL_HOST_DB_GET_VERSION obtaining schema version information for the CQL hosts database
+A debug message issued when the server is about to obtain schema version
+information from the CQL hosts database.
+
+% DHCPSRV_CQL_HOST_ADD Adding host information to the database
+An informational message logged when options belonging to any reservation from a
+single host are inserted.
+
+% DHCPSRV_CQL_HOST_GET_ALL Retrieving multiple hosts from a CQL database
+An informational message logged when multiple hosts from a CQL database are retrieved.
+
+% DHCPSRV_CQL_HOST_GET4 Retrieving one DHCPv4 host from a CQL database
+An informational message logged when a DHCP server is about to retrieve one
+host from a CQL database by IPv4 criteria.
+
+% DHCPSRV_CQL_HOST_GET6 Retrieving one DHCPv6 host from a CQL database
+An informational message logged when a DHCP server is about to retrieve one
+host from a CQL database by IPv6 criteria.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_ID_4S Looking up primary key "id" filtering by host identifier.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_4S Looking up primary key "id" filtering by host IPv4 address.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_6S Looking up primary key "id" filtering by host IPv4 subnet ID.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_ID_6S Looking up primary key "id" filtering by host identifier type.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_4A Looking up primary key "id" filtering by host IPv4 address.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_4S_4A Looking up primary key "id" filtering by host IPv6 subnet ID.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_6S_6A Looking up primary key "id" filtering by reserved prefix address.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_6A_6L Looking up primary key "id" filtering by reserved prefix length.
+
+% DHCPSRV_CQL_HOST_SYNC_RESERVATIONS Syncing reservations in database with kea.conf.
+
+% DHCPSRV_CQL_LEASE_EXCEPTION_THROWN Exception thrown during Cassandra operation: %1
+
 % DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET received bad DHCPv4o6 packet: %1
 A bad DHCPv4o6 packet was received.
 
@@ -389,6 +569,10 @@ with the specified address to the memory file backend database.
 The code has issued a commit call.  For the memory file database, this is
 a no-op.
 
+% DHCPSRV_MEMFILE_BEGIN_TRANSACTION committing to memory file database
+The code has issued a begin transaction call.  For the memory file database, this is
+a no-op.
+
 % DHCPSRV_MEMFILE_CONVERTING_LEASE_FILES running LFC now to convert lease files to the current schema: %1.%2
 A warning message issued when the server has detected lease files that need
 to be either upgraded or downgraded to match the server's schema, and that
@@ -616,6 +800,9 @@ The code has issued a commit call.  All outstanding transactions will be
 committed to the database.  Note that depending on the MySQL settings,
 the committal may not include a write to disk.
 
+% DHCPSRV_MYSQL_BEGIN_TRANSACTION committing to MySQL database
+The code has issued a begin transaction call.
+
 % DHCPSRV_MYSQL_DB opening MySQL lease database: %1
 This informational message is logged when a DHCP server (either V4 or
 V6) is about to open a MySQL lease database.  The parameters of the
@@ -737,6 +924,96 @@ lease from the MySQL database for the specified address.
 A debug message issued when the server is attempting to update IPv6
 lease from the MySQL database for the specified address.
 
+% DHCPSRV_MYSQL_INSERT_SRV_CONFIG4 Inserting a new DHCPv4 configuration with id %1
+A debug message issued when there is no existing DHCPv4 server configuration in database and a configuration will be inserted.
+
+% DHCPSRV_MYSQL_INSERT_SRV_CONFIG6 Inserting a new DHCPv6 configuration with id %1
+A debug message issued when there is no existing DHCPv6 server configuration in database and a configuration will be inserted.
+
+% DHCPSRV_MYSQL_UPDATE_SRV_CONFIG4 Updating the DHCPv4 server configuration with id %1.
+A debug message issued when there already exists a DHCPv4 server configuration in database
+and this configuration is going to be updated.
+
+% DHCPSRV_MYSQL_UPDATE_SRV_CONFIG6 Updating the DHCPv6 server configuration with id %1.
+A debug message issued when there already exists a DHCPv6 server configuration in database
+and this configuration is going to be updated.
+
+% DHCPSRV_MYSQL_REQUEST_UPDATE_SRV_CONFIG4 Received request to update the DHCPv4 database server configuration
+A debug message issued when a DHCPv4 configuration database update request is received.
+If there already exists a configuration in the database then the configuration will be updated.
+Otherwise a new configuration will be inserted.
+
+% DHCPSRV_MYSQL_REQUEST_UPDATE_SRV_CONFIG6 Received request to update the DHCPv6 database server configuration
+A debug message issued when a DHCPv6 configuration database update request is received.
+If there already exists a configuration in the database then the configuration will be updated.
+Otherwise a new configuration will be inserted.
+
+% DHCPSRV_MYSQL_UPDATE_SRV_CONFIG4_TIMESTAMP_CHANGED Cannot update the DHCP4  database server configuration (old timestamp is %1, new timestamp is %2)
+A warning message issued when DHCP4 database server configuration cannot be updated.
+On a database server configuration update the actual timestamp of the configuration is also provided to the server.
+If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails.
+
+% DHCPSRV_MYSQL_UPDATE_SRV_CONFIG6_TIMESTAMP_CHANGED Cannot update the DHCPv6  database server configuration (old timestamp is %1, new timestamp is %2)
+A warning message issued when DHCPv6 database server configuration cannot be updated.
+On a database server configuration update the actual timestamp of the configuration is also provided to the server.
+If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails.
+
+% DHCPSRV_MYSQL_GET_SRV_CONFIG4_TIMESTAMP Obtaining the DHCPv4 server configuration's timestamp from database
+Obtains the DHCPv4 server configuration's timestamp from database.
+
+% DHCPSRV_MYSQL_GET_SRV_CONFIG6_TIMESTAMP Obtaining the DHCPv6 server configuration's timestamp from database
+Obtains the DHCPv6 server configuration's timestamp from database.
+
+% DHCPSRV_MYSQL_GET_SRV_CONFIG4_JSON Obtaining the DHCPv4 server JSON configuration from database
+Obtains the DHCPv4 server JSON configuration from database.
+
+% DHCPSRV_MYSQL_GET_SRV_CONFIG6_JSON Obtaining the DHCPv6 server JSON configuration from database
+Obtains the DHCPv6 server JSON configuration from database.
+
+% DHCPSRV_MYSQL_GET_SRV_CONFIG4_GENERIC Obtaining the DCHPv4 server GENERIC configuration from database
+Obtains the DCHPv4 server GENERIC configuration from database.
+
+% DHCPSRV_MYSQL_GET_SRV_CONFIG6_GENERIC Obtaining the DCHPv6 server GENERIC configuration from database
+Obtains the DCHPv6 server GENERIC configuration from database.
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4 Obtaining the DHCPv4 server configuration from the master database id %1
+Obtains the DHCPv4 server configuration from the master database.
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6 Obtaining the DHCPv6 server configuration from the master database id %1
+Obtains the DHCPv6 server configuration from the master database.
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database
+Obtains the DHCPv4 server configuration from the master database for the specified shard database.
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database
+Obtains the DHCPv6 server configuration from the master database for the specified shard database.
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4_SHARDS_NAME Obtaining the DHCPv4 shards name from the master database
+Obtains the DHCPv4 shards name from the master database
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6_SHARDS_NAME Obtaining the DHCPv6 shards name from the master database
+Obtains the DHCPv6 shards name from the master database
+
+% DHCPSRV_MYSQL_CLEAR_SRV_MASTER_CONFIG4 Clearing the DHCPv4 master configuration from database
+Clears the DHCPv4 master configuration from database
+
+% DHCPSRV_MYSQL_CLEAR_SRV_MASTER_CONFIG6 Clearing the DHCPv6 master configuration from database
+Clears the DHCPv6 master configuration from database
+
+% DHCPSRV_MYSQL_INSERT_SRV_MASTER_CONFIG4 Inserting a new DHCPv4 master server configuration with id %1 and shard database %2
+A debug message issued when there is no existing DHCPv4 master server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_MYSQL_INSERT_SRV_MASTER_CONFIG6 Inserting a new DHCPv6 master server configuration with id %1 and shard database %2
+A debug message issued when there is no existing DHCPv6 master server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4_TIMESTAMP Obtaining the DHCPv4 master server configuration's version from database id %1
+Obtains the DHCPv4 master server configuration's version from database.
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6_TIMESTAMP Obtaining the DHCPv6 master server configuration's version from database id %1
+Obtains the DHCPv6 master server configuration's version from database.
+
 % DHCPSRV_NOTYPE_DB no 'type' keyword to determine database backend: %1
 This is an error message, logged when an attempt has been made to access
 a database backend, but where no 'type' keyword has been included in
@@ -765,6 +1042,9 @@ The code has issued a commit call.  All outstanding transactions will be
 committed to the database.  Note that depending on the PostgreSQL settings,
 the committal may not include a write to disk.
 
+% DHCPSRV_PGSQL_BEGIN_TRANSACTION committing to PostgreSQL database
+The code has issued a begin transaction call.
+
 % DHCPSRV_PGSQL_DB opening PostgreSQL lease database: %1
 This informational message is logged when a DHCP server (either V4 or
 V6) is about to open a PostgreSQL lease database.  The parameters of the
@@ -888,6 +1168,96 @@ lease from the PostgreSQL database for the specified address.
 A debug message issued when the server is attempting to update IPv6
 lease from the PostgreSQL database for the specified address.
 
+% DHCPSRV_PGSQL_INSERT_SRV_CONFIG4 Inserting a new DHCPv4 server configuration with id %1
+A debug message issued when there is no existing DHCPv4 server configuration in database and a configuration will be inserted.
+
+% DHCPSRV_PGSQL_INSERT_SRV_CONFIG6 Inserting a new DHCPv6 server configuration with id %1
+A debug message issued when there is no existing DHCPv6 server configuration in database and a configuration will be inserted.
+
+% DHCPSRV_PGSQL_UPDATE_SRV_CONFIG4 Updating the DHCPv4 server configuration with id %1.
+A debug message issued when there already exists a DHCPv4 server configuration in database
+and this configuration is going to be updated.
+
+% DHCPSRV_PGSQL_UPDATE_SRV_CONFIG6 Updating the DHCPv6 server configuration with id %1.
+A debug message issued when there already exists a DHCPv6 server configuration in database
+and this configuration is going to be updated.
+
+% DHCPSRV_PGSQL_REQUEST_UPDATE_SRV_CONFIG4 Received request to update the IPv4 database server configuration
+A debug message issued when a IPv4 server configuration database update request is received.
+If there already exists a configuration in the database then the configuration will be updated.
+Otherwise a new configuration will be inserted.
+
+% DHCPSRV_PGSQL_REQUEST_UPDATE_SRV_CONFIG6 Received request to update the IPv6 database server configuration
+A debug message issued when a IPv6 server configuration database update request is received.
+If there already exists a configuration in the database then the configuration will be updated.
+Otherwise a new configuration will be inserted.
+
+% DHCPSRV_PGSQL_UPDATE_SRV_CONFIG4_TIMESTAMP_CHANGED Cannot update the IPv4 database server configuration (old timestamp is %1, new timestamp is %2)
+A warning message issued when IPv4 database server configuration cannot be updated.
+On a database server configuration update the actual timestamp of the configuration is also provided to the server.
+If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails.
+
+% DHCPSRV_PGSQL_UPDATE_SRV_CONFIG6_TIMESTAMP_CHANGED Cannot update the IPv6 database server configuration (old timestamp is %1, new timestamp is %2)
+A warning message issued when IPv6 database server configuration cannot be updated.
+On a database server configuration update the actual timestamp of the configuration is also provided to the server.
+If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails.
+
+% DHCPSRV_PGSQL_GET_SRV_CONFIG4_TIMESTAMP Obtaining the DHCPv4 server configuration's version from database
+Obtains the DHCPv4 server configuration's version from database.
+
+% DHCPSRV_PGSQL_GET_SRV_CONFIG6_TIMESTAMP Obtaining the DHCPv6 server configuration's version from database
+Obtains the DHCPv6 server configuration's version from database.
+
+% DHCPSRV_PGSQL_GET_SRV_CONFIG4_JSON Obtaining the DHCPv4 server JSON configuration from database
+Obtains the DHCPv4 server JSON configuration from database.
+
+% DHCPSRV_PGSQL_GET_SRV_CONFIG6_JSON Obtaining the DHCPv6 server JSON configuration from database
+Obtains the DHCPv6 server JSON configuration from database.
+
+% DHCPSRV_PGSQL_GET_SRV_CONFIG4_GENERIC Obtaining the DHCPv4 server GENERIC configuration from database
+Obtains the DHCPv4 server GENERIC configuration from database.
+
+% DHCPSRV_PGSQL_GET_SRV_CONFIG6_GENERIC Obtaining the DHCPv6 server GENERIC configuration from database
+Obtains the DHCPv6 server GENERIC configuration from database.
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4 Obtaining the DHCPv4 server configuration from the master database id %1
+Obtains the DHCPv4 server configuration from the master database.
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6 Obtaining the DHCPv6 server configuration from the master database id %1
+Obtains the DHCPv6 server configuration from the master database.
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database
+Obtains the DHCPv4 server configuration from the master database for the specified shard database.
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database
+Obtains the DHCPv6 server configuration from the master database for the specified shard database.
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4_SHARDS_NAME Obtaining the DHCPv4 shards name from the master database
+Obtains the DHCPv4 shards name from the master database
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6_SHARDS_NAME Obtaining the DHCPv6 shards name from the master database
+Obtains the DHCPv6 shards name from the master database
+
+% DHCPSRV_PGSQL_CLEAR_SRV_MASTER_CONFIG4 Clearing the DHCPv4 master configuration from database
+Clears the DHCPv4 master configuration from database
+
+% DHCPSRV_PGSQL_CLEAR_SRV_MASTER_CONFIG6 Clearing the DHCPv6 master configuration from database
+Clears the DHCPv6 master configuration from database
+
+% DHCPSRV_PGSQL_INSERT_SRV_MASTER_CONFIG4 Inserting a new DHCPv4 master server configuration with id %1 and shard database %2
+A debug message issued when there is no existing DHCPv4 master server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_PGSQL_INSERT_SRV_MASTER_CONFIG6 Inserting a new DHCPv6 master server configuration with id %1 and shard database %2
+A debug message issued when there is no existing DHCPv6 master server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4_TIMESTAMP Obtaining the DHCPv4 master server configuration's version from database id %1
+Obtains the DHCPv4 master server configuration's version from database.
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6_TIMESTAMP Obtaining the DHCPv6 master server configuration's version from database id %1
+Obtains the DHCPv6 master server configuration's version from database.
+
 % DHCPSRV_QUEUE_NCR %1: name change request to %2 DNS entry queued: %3
 A debug message which is logged when the NameChangeRequest to add or remove
 a DNS entries for a particular lease has been queued. The first argument
index 27fb5c49f1f2d2394dabfbb69a736323edf15c58..8dd78f8f606c5c6a84fc585da155be4d3498520f 100644 (file)
 namespace isc {
 namespace dhcp {
 
+/// @brief Pair containing major and minor versions
+typedef std::pair<uint32_t, uint32_t> VersionPair;
+
+
 /// @brief Contains a single row of lease statistical data
 ///
 /// The contents of the row consist of a subnet ID, a lease
@@ -171,7 +175,7 @@ public:
     ///
     /// @result true if the lease was added, false if not (because a lease
     ///         with the same address was already there).
-    virtual bool addLease(const isc::dhcp::Lease4Ptr& lease) = 0;
+    virtual bool addLease(const Lease4Ptr& lease) = 0;
 
     /// @brief Adds an IPv6 lease.
     ///
@@ -367,10 +371,13 @@ public:
 
     /// @brief Deletes a lease.
     ///
-    /// @param addr Address of the lease to be deleted. (This can be IPv4 or
-    ///        IPv6.)
+    /// @param addr Address of the lease to be deleted. This can be an IPv4
+    ///             address or an IPv6 address.
     ///
     /// @return true if deletion was successful, false if no such lease exists
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
     virtual bool deleteLease(const isc::asiolink::IOAddress& addr) = 0;
 
     /// @brief Deletes all expired and reclaimed DHCPv4 leases.
@@ -507,7 +514,7 @@ public:
     /// B>=A and B=C (it is ok to have newer backend, as it should be backward
     /// compatible)
     /// Also if B>C, some database upgrade procedure may be triggered
-    virtual std::pair<uint32_t, uint32_t> getVersion() const = 0;
+    virtual VersionPair getVersion() const = 0;
 
     /// @brief Commit Transactions
     ///
@@ -520,9 +527,13 @@ public:
     /// Rolls back all pending database operations.  On databases that don't
     /// support transactions, this is a no-op.
     virtual void rollback() = 0;
+
+    /// @todo: Add host management here
+    /// As host reservation is outside of scope for 2012, support for hosts
+    /// is currently postponed.
 };
 
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
+}  // namespace dhcp
+}  // namespace isc
 
 #endif // LEASE_MGR_H
index 01b8e1d82d7e8f30f5a7103923c1f4b601c53863..1c48f42d1808d327aa65a98f4301bea4caeb2e33 100644 (file)
@@ -101,7 +101,7 @@ LeaseMgrFactory::destroy() {
     getLeaseMgrPtr().reset();
 }
 
-bool 
+bool
 LeaseMgrFactory::haveInstance() {
     return (getLeaseMgrPtr().get());
 }
index 8f2267906a44822fc238aeb44d73676b1f8c9581..f02f5e45e7313f9a9d2aeb47b0a0c56529fd88b0 100644 (file)
@@ -1047,6 +1047,12 @@ Memfile_LeaseMgr::getDescription() const {
     return (std::string("In memory database with leases stored in a CSV file."));
 }
 
+bool Memfile_LeaseMgr::startTransaction() {
+  LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+            DHCPSRV_MEMFILE_BEGIN_TRANSACTION);
+  return true;
+}
+
 void
 Memfile_LeaseMgr::commit() {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_COMMIT);
index 3d76f94468c3112e6aa9f6093a7065dde769b30f..1a7897d4ffaa851d6a0b3e1373563e8f773e78ce 100644 (file)
@@ -420,6 +420,12 @@ public:
         return (std::make_pair(MAJOR_VERSION, MINOR_VERSION));
     }
 
+    /// @brief Start Transaction
+    ///
+    /// Start transaction for database operations. On databases that don't
+    /// support transactions, this is a no-op.
+    virtual bool startTransaction();
+
     /// @brief Commit Transactions
     ///
     /// Commits all pending database operations.  On databases that don't
index 6a6d0d944d88d7c7e86400951a38a61bbba98017..5fb6434c0e8c3c6a570fc703789030881493ff3b 100644 (file)
@@ -169,11 +169,9 @@ DbAccessParser::parse(CfgDbAccessPtr& cfg_db,
     // 5. Save the database access string in the Configuration Manager.
     if (type_ == LEASE_DB) {
         cfg_db->setLeaseDbAccessString(getDbAccessString());
-
-    } else {
+    } else if (type_ == HOSTS_DB) {
         cfg_db->setHostDbAccessString(getDbAccessString());
     }
-
 }
 
 // Create the database access string
@@ -199,5 +197,5 @@ DbAccessParser::getDbAccessString() const {
     return (dbaccess);
 }
 
-};  // namespace dhcp
-};  // namespace isc
+}  // namespace dhcp
+}  // namespace isc
index bc23dbf2ca147ad1e28cbf7ef7e6a94d86b55b48..3521410658e48b15261f3979b12b74dd5db19663 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2016 Deutsche Telekom AG.
+// Copyright (C) 2016-2017 Deutsche Telekom AG.
 //
 // Author: Cristian Secăreanu <cristian.secareanu@qualitance.com>
 //
@@ -39,98 +39,18 @@ namespace dhcp {
 enum ExchangeDataType {
     EXCHANGE_DATA_TYPE_NONE,
     EXCHANGE_DATA_TYPE_BOOL,
+    EXCHANGE_DATA_TYPE_INT8,
+    EXCHANGE_DATA_TYPE_INT16,
     EXCHANGE_DATA_TYPE_INT32,
     EXCHANGE_DATA_TYPE_INT64,
     EXCHANGE_DATA_TYPE_TIMESTAMP,
     EXCHANGE_DATA_TYPE_STRING,
     EXCHANGE_DATA_TYPE_BYTES,
-    EXCHANGE_DATA_TYPE_UUID
+    EXCHANGE_DATA_TYPE_UUID,
+    EXCHANGE_DATA_TYPE_UDT,
+    EXCHANGE_DATA_TYPE_COLLECTION
 };
 
-/// @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_OUT),
-          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_;
-};
-
-/// @brief Smart pointer to an @ref ExchangeColumnInfo
-typedef boost::shared_ptr<ExchangeColumnInfo> ExchangeColumnInfoPtr;
-
-/// @brief Multimap that allows indexing @ref ExchangeColumnInfoPtr
-///     sequentially, by index and by name.
-typedef boost::multi_index_container<
-    // Container comprises elements of ExchangeColumnInfoPtr type.
-    ExchangeColumnInfoPtr,
-    // Here we start enumerating various indexes.
-    boost::multi_index::indexed_by<
-        // Index #0. Sequenced index allows accessing elements in the same way
-        // as in std::list.
-        boost::multi_index::sequenced<>,
-        // Index #1
-        boost::multi_index::hashed_non_unique<
-            boost::multi_index::member<
-                ExchangeColumnInfo,
-                std::string,
-                &ExchangeColumnInfo::name_
-             >
-        >,
-        // Index #2
-        boost::multi_index::hashed_non_unique<
-            boost::multi_index::member<
-                ExchangeColumnInfo,
-                uint32_t,
-                &ExchangeColumnInfo::index_
-            >
-        >
-    >
-> ExchangeColumnInfoContainer;
-
-/// @brief Pointer to the ExchangeColumnInfoContainer object.
-typedef boost::shared_ptr<ExchangeColumnInfoContainer>
-    ExchangeColumnInfoContainerPtr;
-
-/// @brief Type of the index #1 - name.
-typedef ExchangeColumnInfoContainer::nth_index<1>::type
-    ExchangeColumnInfoContainerName;
-
-/// @brief 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;
-
-/// @brief Type of the index #2 - index.
-typedef ExchangeColumnInfoContainer::nth_index<2>::type
-    ExchangeColumnInfoContainerIndex;
-
-/// @brief 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;
-
 /// @brief Base class for backend exchanges.
 class SqlExchange {
 public:
@@ -141,9 +61,6 @@ public:
     /// @brief Destructor
     virtual ~SqlExchange() {
     }
-
-    /// @brief Column names and types
-    ExchangeColumnInfoContainer parameters_;
 };
 
 }  // namespace dhcp
index 97368608d0111dda4c12acd2a9aa81c25657cc40..520708319494910a4939d371ec24de5b82b3bfe2 100644 (file)
@@ -118,6 +118,7 @@ libdhcpsrv_unittests_SOURCES += pgsql_host_data_source_unittest.cc
 libdhcpsrv_unittests_SOURCES += pgsql_lease_mgr_unittest.cc
 endif
 if HAVE_CQL
+libdhcpsrv_unittests_SOURCES += cql_connection_unittest.cc
 libdhcpsrv_unittests_SOURCES += cql_lease_mgr_unittest.cc
 endif
 libdhcpsrv_unittests_SOURCES += pool_unittest.cc
diff --git a/src/lib/dhcpsrv/tests/cql_connection_unittest.cc b/src/lib/dhcpsrv/tests/cql_connection_unittest.cc
new file mode 100644 (file)
index 0000000..5d66802
--- /dev/null
@@ -0,0 +1,80 @@
+// Copyright (C) 2017 Deutsche Telekom AG.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <dhcpsrv/cql_connection.h>
+#include <dhcpsrv/cql_exchange.h>
+
+#include <cstring>
+
+#include <boost/any.hpp>
+
+#include <gtest/gtest.h>
+
+namespace {
+
+using isc::dhcp::CqlTaggedStatement;
+using isc::dhcp::StatementMap;
+using isc::dhcp::StatementTag;
+using isc::dhcp::StatementTagHash;
+using isc::dhcp::exchangeType;
+
+class CqlConnectionTest {
+public:
+    /// @brief Constructor
+    CqlConnectionTest() {
+    }
+
+    /// @brief Destructor
+    virtual ~CqlConnectionTest() {
+    }
+};
+
+/// @brief Check that the key is properly hashed for StatementMap.
+TEST(CqlConnection, statementMapHash) {
+    // Build std::strings to prevent optimizations on underlying C string.
+    std::string tag1_s = "same";
+    std::string tag2_s = "same";
+    StatementTag tag1 = tag1_s.c_str();
+    StatementTag tag2 = tag2_s.c_str();
+    StatementMap map;
+
+    // Make sure addresses are different.
+    EXPECT_NE(tag1, tag2);
+
+    // Insert two entries with the same key value.
+    map.insert({tag1, CqlTaggedStatement(tag1, "your fancy select here")});
+    map.insert({tag2, CqlTaggedStatement(tag2, "DELETE FROM world.evil")});
+
+    // Make sure the first one was overwritten.
+    char const* const tag1_text = map.find(tag1)->second.text_;
+    char const* const tag2_text = map.find(tag2)->second.text_;
+    EXPECT_TRUE(tag1_text);
+    EXPECT_TRUE(tag2_text);
+    ASSERT_EQ(std::strcmp(tag1_text, tag2_text), 0);
+    ASSERT_EQ(map.size(), 1u);
+}
+
+/// @brief Check anything related to exchange types.
+TEST(CqlConnection, exchangeTypeCoverage) {
+    // Check that const and non-const are supported and both point to the same
+    // exchange type.
+    int i = 1;
+
+    // non-const
+    int* pi = &i;
+    boost::any bi(pi);
+
+    // const
+    int* const cpi = &i;
+    boost::any bci(cpi);
+
+    ASSERT_EQ(exchangeType(bi), exchangeType(bci));
+}
+
+}  // namespace
+
index a8902cc346bf36c7bafef5331acf0f1aeb7a841d..1af75036778700310d80e720f6b4601b6581934d 100644 (file)
@@ -1,7 +1,7 @@
-// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+// Copyright (C) 2015-2017 Deutsche Telekom AG.
 //
-// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
-// Author: Andrei Pavel <andrei.pavel@qualitance.com>
+// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//          Andrei Pavel <andrei.pavel@qualitance.com>
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@
 
 #include <config.h>
 
+#include <gtest/gtest.h>
+
 #include <asiolink/io_address.h>
 #include <dhcpsrv/cql_connection.h>
 #include <dhcpsrv/cql_lease_mgr.h>
 #include <dhcpsrv/testutils/cql_schema.h>
 #include <exceptions/exceptions.h>
 
-#include <gtest/gtest.h>
-
 #include <algorithm>
-#include <iostream>
 #include <sstream>
 #include <string>
 #include <utility>
@@ -38,7 +37,6 @@ using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp::test;
-using namespace std;
 
 namespace {
 
@@ -46,13 +44,10 @@ namespace {
 ///
 /// Opens the database prior to each test and closes it afterwards.
 /// All pending transactions are deleted prior to closure.
-
 class CqlLeaseMgrTest : public GenericLeaseMgrTest {
 public:
-    /// @brief Constructor
-    ///
-    /// Deletes everything from the database and opens it.
-    CqlLeaseMgrTest() {
+    /// @brief Clears the database and opens connection to it.
+    void initializeTest() {
         // Ensure schema is the correct one.
         destroyCqlSchema(false, true);
         createCqlSchema(false, true);
@@ -68,17 +63,34 @@ public:
                          "*** accompanying exception output.\n";
             throw;
         }
+
         lmptr_ = &(LeaseMgrFactory::instance());
     }
 
+    /// @brief Destroys the LM and the schema.
+    void destroyTest() {
+        try {
+            lmptr_->rollback();
+        } catch (...) {
+            // Rollback may fail if backend is in read only mode. That's ok.
+        }
+        LeaseMgrFactory::destroy();
+        destroyCqlSchema(false, true);
+    }
+
+    /// @brief Constructor
+    ///
+    /// Deletes everything from the database and opens it.
+    CqlLeaseMgrTest() {
+        initializeTest();
+    }
+
     /// @brief Destructor
     ///
     /// Rolls back all pending transactions. The deletion of lmptr_ will close
     /// the database. Then reopen it and delete everything created by the test.
     virtual ~CqlLeaseMgrTest() {
-        lmptr_->rollback();
-        LeaseMgrFactory::destroy();
-        destroyCqlSchema(false, true);
+        destroyTest();
     }
 
     /// @brief Reopen the database
@@ -102,24 +114,23 @@ public:
     // columns. Because of that, the order check has been excluded.
     void testCqlGetExpiredLeases4() {
         // Get the leases to be used for the test.
-        vector<Lease4Ptr> leases = createLeases4();
+        std::vector<Lease4Ptr> leases = createLeases4();
         // Make sure we have at least 6 leases there.
-        ASSERT_GE(leases.size(), 6U);
+        ASSERT_GE(leases.size(), 6u);
 
         // Use the same current time for all leases.
         time_t current_time = time(NULL);
 
         // Add them to the database
-        for (size_t i = 0U; i < leases.size(); ++i) {
+        for (size_t i = 0u; i < leases.size(); ++i) {
             // Mark every other lease as expired.
-            if (i % 2U == 0U) {
+            if (i % 2u == 0u) {
                 // Set client last transmission time to the value older than the
                 // valid lifetime to make it expired. The expiration time also
                 // depends on the lease index, so as we can later check that the
                 // leases are ordered by the expiration time.
                 leases[i]->cltt_ =
                     current_time - leases[i]->valid_lft_ - 10 - i;
-
             } else {
                 // Set current time as cltt for remaining leases. These leases
                 // are
@@ -132,8 +143,9 @@ public:
         // Retrieve at most 1000 expired leases.
         Lease4Collection expired_leases;
         ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 1000));
+
         // Leases with even indexes should be returned as expired.
-        ASSERT_EQ(static_cast<size_t>(leases.size() / 2U),
+        ASSERT_EQ(static_cast<size_t>(leases.size() / 2u),
                   expired_leases.size());
 
         // Update current time for the next test.
@@ -144,9 +156,9 @@ public:
         // This time let's reverse the expiration time and see if they will be
         // returned
         // in the correct order.
-        for (size_t i = 0U; i < leases.size(); ++i) {
+        for (size_t i = 0u; i < leases.size(); ++i) {
             // Update the time of expired leases with even indexes.
-            if (i % 2U == 0U) {
+            if (i % 2u == 0u) {
                 leases[i]->cltt_ =
                     current_time - leases[i]->valid_lft_ - 1000 + i;
             } else {
@@ -157,11 +169,11 @@ public:
         }
 
         // Retrieve expired leases again. The limit of 0 means return all
-        // expired
-        // leases.
+        // expired leases.
         ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 0));
+
         // The same leases should be returned.
-        ASSERT_EQ(static_cast<size_t>(leases.size() / 2U),
+        ASSERT_EQ(static_cast<size_t>(leases.size() / 2u),
                   expired_leases.size());
 
         // Remember expired leases returned.
@@ -174,11 +186,11 @@ public:
         ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 2));
 
         // Make sure we have exactly 2 leases returned.
-        ASSERT_EQ(2U, expired_leases.size());
+        ASSERT_EQ(2u, expired_leases.size());
 
         // Mark every other expired lease as reclaimed.
-        for (size_t i = 0U; i < saved_expired_leases.size(); ++i) {
-            if (i % 2U != 0U) {
+        for (size_t i = 0u; i < saved_expired_leases.size(); ++i) {
+            if (i % 2u != 0u) {
                 saved_expired_leases[i]->state_ =
                     Lease::STATE_EXPIRED_RECLAIMED;
             }
@@ -189,16 +201,16 @@ public:
 
         // This the returned leases should exclude reclaimed ones. So the number
         // of returned leases should be roughly half of the expired leases.
-        ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 0U));
-        ASSERT_EQ(static_cast<size_t>(saved_expired_leases.size() / 2U),
+        ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 0u));
+        ASSERT_EQ(static_cast<size_t>(saved_expired_leases.size() / 2u),
                   expired_leases.size());
 
         // Make sure that returned leases are those that are not reclaimed, i.e.
         // those that have even index.
         for (Lease4Collection::iterator lease = expired_leases.begin();
              lease != expired_leases.end(); ++lease) {
-            int index =
-                static_cast<int>(std::distance(expired_leases.begin(), lease));
+            int32_t index = static_cast<int32_t>(
+                std::distance(expired_leases.begin(), lease));
             EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_);
         }
     }
@@ -211,24 +223,23 @@ public:
     // columns. Because of that, the order check has been excluded.
     void testCqlGetExpiredLeases6() {
         // Get the leases to be used for the test.
-        vector<Lease6Ptr> leases = createLeases6();
+        std::vector<Lease6Ptr> leases = createLeases6();
         // Make sure we have at least 6 leases there.
-        ASSERT_GE(leases.size(), 6U);
+        ASSERT_GE(leases.size(), 6u);
 
         // Use the same current time for all leases.
         time_t current_time = time(NULL);
 
         // Add them to the database
-        for (size_t i = 0U; i < leases.size(); ++i) {
+        for (size_t i = 0u; i < leases.size(); ++i) {
             // Mark every other lease as expired.
-            if (i % 2U == 0U) {
+            if (i % 2u == 0u) {
                 // Set client last transmission time to the value older than the
                 // valid lifetime to make it expired. The expiration time also
                 // depends on the lease index, so as we can later check that the
                 // leases are ordered by the expiration time.
                 leases[i]->cltt_ =
                     current_time - leases[i]->valid_lft_ - 10 - i;
-
             } else {
                 // Set current time as cltt for remaining leases. These leases
                 // are
@@ -241,8 +252,9 @@ public:
         // Retrieve at most 1000 expired leases.
         Lease6Collection expired_leases;
         ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 1000));
+
         // Leases with even indexes should be returned as expired.
-        ASSERT_EQ(static_cast<size_t>(leases.size() / 2U),
+        ASSERT_EQ(static_cast<size_t>(leases.size() / 2u),
                   expired_leases.size());
 
         // Update current time for the next test.
@@ -253,12 +265,11 @@ public:
         // This time let's reverse the expiration time and see if they will be
         // returned
         // in the correct order.
-        for (size_t i = 0U; i < leases.size(); ++i) {
+        for (size_t i = 0u; i < leases.size(); ++i) {
             // Update the time of expired leases with even indexes.
-            if (i % 2U == 0U) {
+            if (i % 2u == 0u) {
                 leases[i]->cltt_ =
                     current_time - leases[i]->valid_lft_ - 1000 + i;
-
             } else {
                 // Make sure remaining leases remain unexpired.
                 leases[i]->cltt_ = current_time + 100;
@@ -270,8 +281,9 @@ public:
         // expired
         // leases.
         ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 0));
+
         // The same leases should be returned.
-        ASSERT_EQ(static_cast<size_t>(leases.size() / 2U),
+        ASSERT_EQ(static_cast<size_t>(leases.size() / 2u),
                   expired_leases.size());
 
         // Remember expired leases returned.
@@ -284,11 +296,11 @@ public:
         ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 2));
 
         // Make sure we have exactly 2 leases returned.
-        ASSERT_EQ(2U, expired_leases.size());
+        ASSERT_EQ(2u, expired_leases.size());
 
         // Mark every other expired lease as reclaimed.
-        for (size_t i = 0U; i < saved_expired_leases.size(); ++i) {
-            if (i % 2U != 0U) {
+        for (size_t i = 0u; i < saved_expired_leases.size(); ++i) {
+            if (i % 2u != 0u) {
                 saved_expired_leases[i]->state_ =
                     Lease::STATE_EXPIRED_RECLAIMED;
             }
@@ -305,8 +317,8 @@ public:
         // those that have even index.
         for (Lease6Collection::iterator lease = expired_leases.begin();
              lease != expired_leases.end(); ++lease) {
-            int index =
-                static_cast<int>(std::distance(expired_leases.begin(), lease));
+            int32_t index = static_cast<int32_t>(
+                std::distance(expired_leases.begin(), lease));
             EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_);
         }
     }
@@ -407,7 +419,7 @@ TEST_F(CqlLeaseMgrTest, getType) {
     EXPECT_EQ(std::string("cql"), lmptr_->getType());
 }
 
-/// @brief Check conversion functions
+/// @brief Check conversion methods
 ///
 /// The server works using cltt and valid_filetime. In the database, the
 /// information is stored as expire_time and valid-lifetime, which are
@@ -418,13 +430,13 @@ TEST_F(CqlLeaseMgrTest, getType) {
 /// This test checks that the conversion is correct.
 TEST_F(CqlLeaseMgrTest, checkTimeConversion) {
     const time_t cltt = time(NULL);
-    const cass_int64_t valid_lft = 86400; // 1 day
+    const cass_int64_t valid_lft = 86400;  // 1 day
     cass_int64_t cql_expire;
 
-    // Convert to the database time
+    // Convert to the database time.
     CqlExchange::convertToDatabaseTime(cltt, valid_lft, cql_expire);
 
-    // Convert back
+    // Convert back.
     time_t converted_cltt = 0;
     CqlExchange::convertFromDatabaseTime(cql_expire, valid_lft, converted_cltt);
     EXPECT_EQ(cltt, converted_cltt);
@@ -438,7 +450,7 @@ TEST_F(CqlLeaseMgrTest, getName) {
 /// @brief Check that getVersion() returns the expected version
 TEST_F(CqlLeaseMgrTest, checkVersion) {
     // Check version
-    pair<uint32_t, uint32_t> version;
+    VersionPair version;
     ASSERT_NO_THROW(version = lmptr_->getVersion());
     EXPECT_EQ(CQL_SCHEMA_VERSION_MAJOR, version.first);
     EXPECT_EQ(CQL_SCHEMA_VERSION_MINOR, version.second);
@@ -534,7 +546,7 @@ TEST_F(CqlLeaseMgrTest, getLease4ClientIdSubnetId) {
 ///
 /// Checks that the addLease, getLease4(by address), getLease4(hwaddr,
 /// subnet_id),
-/// updateLease4() and deleteLease (IPv4 address) can handle NULL client-id.
+/// updateLease4() and deleteLease can handle NULL client-id.
 /// (client-id is optional and may not be present)
 TEST_F(CqlLeaseMgrTest, lease4NullClientId) {
     testLease4NullClientId();
@@ -698,4 +710,3 @@ TEST_F(CqlLeaseMgrTest, DISABLED_wipeLeases6) {
 }
 
 }  // namespace
-
index 05dccca6bc28d6560ed33654e9761455636a28db..9b385cb1e4b24733ef9c846181fd941eec538722 100644 (file)
@@ -268,6 +268,12 @@ public:
         return (make_pair(uint32_t(0), uint32_t(0)));
     }
 
+    /// @brief Start Transaction
+    ///
+    /// Start transaction for database operations. On databases that don't
+    /// support transactions, this is a no-op.
+    virtual bool startTransaction() { return true; };
+
     /// @brief Commit transactions
     virtual void commit() {
     }
index 82f47fc8dd50098c2fd72c799caca6b29d552f32..49f242ab0d2896e24bab5dd9de269bb7f420ed72 100644 (file)
@@ -1,4 +1,4 @@
--- Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+-- Copyright (C) 2015-2017 Deutsche Telekom AG.
 
 -- Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
 
@@ -44,7 +44,7 @@
 -- -----------------------------------------------------
 -- Table `lease4`
 -- -----------------------------------------------------
-CREATE TABLE lease4 (
+CREATE TABLE IF NOT EXISTS lease4 (
     address int,
     hwaddr blob,
     client_id blob,
@@ -55,15 +55,15 @@ CREATE TABLE lease4 (
     fqdn_rev boolean,
     hostname varchar,
     state int,
-    PRIMARY KEY (address)
+    PRIMARY KEY ((address))
 );
 
 -- Create search indexes for lease4 table
-CREATE INDEX lease4index1 ON lease4 (client_id);
-CREATE INDEX lease4index2 ON lease4 (subnet_id);
-CREATE INDEX lease4index3 ON lease4 (hwaddr);
-CREATE INDEX lease4index4 ON lease4 (expire);
-CREATE INDEX lease4index5 ON lease4 (state);
+CREATE INDEX IF NOT EXISTS lease4index1 ON lease4 (client_id);
+CREATE INDEX IF NOT EXISTS lease4index2 ON lease4 (subnet_id);
+CREATE INDEX IF NOT EXISTS lease4index3 ON lease4 (hwaddr);
+CREATE INDEX IF NOT EXISTS lease4index4 ON lease4 (expire);
+CREATE INDEX IF NOT EXISTS lease4index5 ON lease4 (state);
 
 -- Holds the IPv6 leases.
 -- N.B. The use of a VARCHAR for the address is temporary for development:
@@ -71,15 +71,15 @@ CREATE INDEX lease4index5 ON lease4 (state);
 -- -----------------------------------------------------
 -- Table `lease6`
 -- -----------------------------------------------------
-CREATE TABLE lease6 (
+CREATE TABLE IF NOT EXISTS lease6 (
     address varchar,
-    duid blob,
     valid_lifetime bigint,
     expire bigint,
     subnet_id int,
     pref_lifetime bigint,
-    lease_type int,
+    duid blob,
     iaid int,
+    lease_type int,
     prefix_len int,
     fqdn_fwd boolean,
     fqdn_rev boolean,
@@ -88,16 +88,16 @@ CREATE TABLE lease6 (
     hwtype int,
     hwaddr_source int,
     state int,
-    PRIMARY KEY (address)
+    PRIMARY KEY ((address))
 );
 
 -- Create search indexes for lease6 table
-CREATE INDEX lease6index1 ON lease6 (lease_type);
-CREATE INDEX lease6index2 ON lease6 (duid);
-CREATE INDEX lease6index3 ON lease6 (iaid);
-CREATE INDEX lease6index4 ON lease6 (subnet_id);
-CREATE INDEX lease6index5 ON lease6 (expire);
-CREATE INDEX lease6index6 ON lease6 (state);
+CREATE INDEX IF NOT EXISTS lease6index1 ON lease6 (duid);
+CREATE INDEX IF NOT EXISTS lease6index2 ON lease6 (iaid);
+CREATE INDEX IF NOT EXISTS lease6index3 ON lease6 (lease_type);
+CREATE INDEX IF NOT EXISTS lease6index4 ON lease6 (subnet_id);
+CREATE INDEX IF NOT EXISTS lease6index5 ON lease6 (expire);
+CREATE INDEX IF NOT EXISTS lease6index6 ON lease6 (state);
 
 -- ... and a definition of lease6 types.  This table is a convenience for
 -- users of the database - if they want to view the lease table and use the
@@ -107,10 +107,10 @@ CREATE INDEX lease6index6 ON lease6 (state);
 -- -----------------------------------------------------
 -- Table `lease6_types`
 -- -----------------------------------------------------
-CREATE TABLE lease6_types (
+CREATE TABLE IF NOT EXISTS lease6_types (
     lease_type int,                             -- Lease type code.
     name varchar,                               -- Name of the lease type
-    PRIMARY KEY (lease_type)
+    PRIMARY KEY ((lease_type))
 );
 INSERT INTO lease6_types (lease_type, name) VALUES (0, 'IA_NA');   -- Non-temporary v6 addresses
 INSERT INTO lease6_types (lease_type, name) VALUES (1, 'IA_TA');   -- Temporary v6 addresses
@@ -125,10 +125,10 @@ INSERT INTO lease6_types (lease_type, name) VALUES (2, 'IA_PD');   -- Prefix del
 -- -----------------------------------------------------
 -- Table `lease_hwaddr_source`
 -- -----------------------------------------------------
-CREATE TABLE lease_hwaddr_source (
+CREATE TABLE IF NOT EXISTS lease_hwaddr_source (
     hwaddr_source int,
     name varchar,
-    PRIMARY KEY (hwaddr_source)
+    PRIMARY KEY ((hwaddr_source))
 );
 
 -- Hardware address obtained from raw sockets
@@ -158,10 +158,10 @@ INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (64, 'HWADDR_SOURCE
 -- -----------------------------------------------------
 -- Table `lease_state`
 -- -----------------------------------------------------
-CREATE TABLE lease_state (
+CREATE TABLE IF NOT EXISTS lease_state (
     state int,
     name varchar,
-    PRIMARY KEY (state)
+    PRIMARY KEY ((state))
 );
 
 -- Insert currently defined state names.
@@ -176,12 +176,57 @@ INSERT INTO lease_state (state, name) VALUES (2, 'expired-reclaimed');
 -- -----------------------------------------------------
 -- Table `schema_version`
 -- -----------------------------------------------------
-CREATE TABLE schema_version (
+CREATE TABLE IF NOT EXISTS schema_version (
     version int,
     minor int,
-    PRIMARY KEY (version)
+    PRIMARY KEY ((version))
 );
 
 INSERT INTO schema_version (version, minor) VALUES (1, 0);
 
--- This line concludes database initalization to version 1.0.
+-- This line concludes database initialization to version 1.0.
+
+-- This line starts database upgrade to version 2.0
+
+-- -----------------------------------------------------
+-- Table `host_reservations`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS host_reservations (
+    host_identifier blob,
+    host_identifier_type int,
+    host_ipv4_subnet_id int,
+    host_ipv6_subnet_id int,
+    host_ipv4_address int,
+    hostname text,
+    host_ipv4_client_classes text,
+    host_ipv6_client_classes text,
+    -- reservation
+    reserved_ipv6_prefix_address text,
+    reserved_ipv6_prefix_length int,
+    reserved_ipv6_prefix_address_type int,
+    iaid int,
+    -- option
+    option_universe int,
+    option_code int,
+    option_value blob,
+    option_formatted_value text,
+    option_space text,
+    option_is_persistent boolean,
+    option_client_class text,
+    option_subnet_id int,
+    id bigint,
+    PRIMARY KEY ((id))
+);
+
+CREATE INDEX IF NOT EXISTS host_reservationsindex1 ON host_reservations (host_identifier);
+CREATE INDEX IF NOT EXISTS host_reservationsindex2 ON host_reservations (host_identifier_type);
+CREATE INDEX IF NOT EXISTS host_reservationsindex3 ON host_reservations (host_ipv4_subnet_id);
+CREATE INDEX IF NOT EXISTS host_reservationsindex4 ON host_reservations (host_ipv6_subnet_id);
+CREATE INDEX IF NOT EXISTS host_reservationsindex5 ON host_reservations (host_ipv4_address);
+CREATE INDEX IF NOT EXISTS host_reservationsindex6 ON host_reservations (reserved_ipv6_prefix_address);
+CREATE INDEX IF NOT EXISTS host_reservationsindex7 ON host_reservations (reserved_ipv6_prefix_length);
+
+TRUNCATE SCHEMA_VERSION;
+INSERT INTO schema_version (version, minor) VALUES(2, 0);
+
+-- This line concludes database upgrade to version 2.0
index aa0631937a1d926eb6b6ad049d2fd7a161ffa3b4..c2833ac9227ce7cfcf16bad389adf508c38c4a72 100644 (file)
@@ -1,4 +1,4 @@
--- Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+-- Copyright (C) 2015-2017 Deutsche Telekom AG.
 
 -- Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
 
@@ -18,13 +18,9 @@ DROP TABLE IF EXISTS lease4;
 DROP TABLE IF EXISTS lease6;
 DROP TABLE IF EXISTS lease6_types;
 DROP TABLE IF EXISTS lease_hwaddr_source;
-DROP TABLE IF EXISTS schema_version;
-DROP TABLE IF EXISTS ipv6_reservations;
-DROP TABLE IF EXISTS hosts;
-DROP TABLE IF EXISTS dhcp4_options;
-DROP TABLE IF EXISTS dhcp6_options;
-DROP TABLE IF EXISTS host_identifier_type;
 DROP TABLE IF EXISTS lease_state;
+DROP TABLE IF EXISTS schema_version;
+DROP TABLE IF EXISTS host_reservations;
 
 DROP INDEX IF EXISTS lease4index1;
 DROP INDEX IF EXISTS lease4index2;
@@ -38,3 +34,11 @@ DROP INDEX IF EXISTS lease6index3;
 DROP INDEX IF EXISTS lease6index4;
 DROP INDEX IF EXISTS lease6index5;
 DROP INDEX IF EXISTS lease6index6;
+
+DROP INDEX IF EXISTS host_reservationsindex1;
+DROP INDEX IF EXISTS host_reservationsindex2;
+DROP INDEX IF EXISTS host_reservationsindex3;
+DROP INDEX IF EXISTS host_reservationsindex4;
+DROP INDEX IF EXISTS host_reservationsindex5;
+DROP INDEX IF EXISTS host_reservationsindex6;
+DROP INDEX IF EXISTS host_reservationsindex7;