]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
added support for datastax cassandra
authorRazvan Becheriu <razvan.becheriu@qualitance.com>
Thu, 19 May 2016 14:10:36 +0000 (17:10 +0300)
committerTomek Mrugalski <tomasz@isc.org>
Thu, 23 Jun 2016 12:19:28 +0000 (14:19 +0200)
15 files changed:
src/bin/admin/tests/data/dscsql.lease4_dump_test.reference.csv [new file with mode: 0644]
src/bin/admin/tests/data/dscsql.lease6_dump_test.reference.csv [new file with mode: 0644]
src/bin/admin/tests/dhcpdb_create_1.0.cql [new file with mode: 0644]
src/bin/admin/tests/dscsql_tests.sh.in [new file with mode: 0644]
src/lib/dhcpsrv/dscsql_connection.cc [new file with mode: 0644]
src/lib/dhcpsrv/dscsql_connection.h [new file with mode: 0644]
src/lib/dhcpsrv/dscsql_lease_mgr.cc [new file with mode: 0644]
src/lib/dhcpsrv/dscsql_lease_mgr.h [new file with mode: 0644]
src/lib/dhcpsrv/tests/dscsql_lease_mgr_unittest.cc [new file with mode: 0644]
src/lib/dhcpsrv/testutils/dscsql_schema.cc [new file with mode: 0644]
src/lib/dhcpsrv/testutils/dscsql_schema.h [new file with mode: 0644]
src/share/database/scripts/dscsql/.gitignore [new file with mode: 0644]
src/share/database/scripts/dscsql/Makefile.am [new file with mode: 0644]
src/share/database/scripts/dscsql/dhcpdb_create.cql [new file with mode: 0644]
src/share/database/scripts/dscsql/dhcpdb_drop.cql [new file with mode: 0755]

diff --git a/src/bin/admin/tests/data/dscsql.lease4_dump_test.reference.csv b/src/bin/admin/tests/data/dscsql.lease4_dump_test.reference.csv
new file mode 100644 (file)
index 0000000..a2917b7
--- /dev/null
@@ -0,0 +1,4 @@
+address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname
+0.0.0.10,3230,3330,40,2015-01-01 01:15:30,50,1,1,one.example.com
+0.0.0.11,,313233,40,2015-02-02 02:30:45,50,1,1,
+0.0.0.12,3232,,40,2015-03-03 11:01:07,50,1,1,three.example.com
diff --git a/src/bin/admin/tests/data/dscsql.lease6_dump_test.reference.csv b/src/bin/admin/tests/data/dscsql.lease6_dump_test.reference.csv
new file mode 100644 (file)
index 0000000..31a6c3f
--- /dev/null
@@ -0,0 +1,4 @@
+address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,hwtype,hwaddr_source
+10,3230,30,2015-04-04 01:15:30,40,50,IA_TA,60,70,1,1,one.example.com,3830,90,100
+11,,30,2015-05-05 02:30:45,40,50,IA_TA,60,70,1,1,,3830,90,100
+12,3231,30,2015-06-06 11:01:07,40,50,IA_TA,60,70,1,1,three.example.com,3830,90,100
diff --git a/src/bin/admin/tests/dhcpdb_create_1.0.cql b/src/bin/admin/tests/dhcpdb_create_1.0.cql
new file mode 100644 (file)
index 0000000..303d437
--- /dev/null
@@ -0,0 +1,213 @@
+-- Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+
+-- Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+
+--           http://www.apache.org/licenses/LICENSE-2.0
+
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+-- This is the Kea schema 1.0 specification for DataStax Cassandra SQL.
+-- Note: this is outdated version on purpose and it used to test upgrade
+-- process. Do not update this file to 2.0 or any later.
+
+-- The schema is reasonably portable (with the exception of the engine
+-- specification, which is Datastax Cassandra SQL-specific).  Minor changes might be needed for
+-- other databases.
+
+-- To create the schema, either type the command:
+
+-- cqlsh -u <user> -p <password> -k <database> -f dhcpdb_create.cql
+
+-- ... at the command prompt, or log in to the DSC SQL database and at the "cqlsh>"
+-- prompt, issue the command:
+
+-- SOURCE dhcpdb_create.cql
+
+-- This script is also called from kea-admin, see kea-admin init dscsql
+
+-- Over time, Kea database schema will evolve. Each version is marked with
+-- major.minor version. This file is organized sequentially, i.e. database
+-- is initialized to 1.0, then upgraded to 2.0 etc. This may be somewhat
+-- sub-optimal, but it ensues consistency with upgrade scripts. (It is much
+-- easier to maintain init and upgrade scripts if they look the same).
+-- Since initialization is done only once, it's perfromance is not an issue.
+
+-- This line starts database initialization to 1.0.
+
+-- Holds the IPv4 leases.
+CREATE TABLE lease4 (
+    address int,
+    hwaddr blob,
+    client_id blob,
+    valid_lifetime bigint,
+    expire bigint,
+    subnet_id int,
+    fqdn_fwd boolean,
+    fqdn_rev boolean,
+    hostname varchar,
+    state int,
+    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 (state);
+
+-- Holds the IPv6 leases.
+-- N.B. The use of a VARCHAR for the address is temporary for development:
+-- it will eventually be replaced by BINARY(16).
+CREATE TABLE lease6 (
+    address varchar,
+    duid blob,
+    valid_lifetime bigint,
+    expire bigint,
+    subnet_id int,
+    pref_lifetime bigint,
+    lease_type int,
+    iaid int,
+    prefix_len int,
+    fqdn_fwd boolean,
+    fqdn_rev boolean,
+    hostname varchar,
+    hwaddr blob,
+    hwtype int,
+    hwaddr_source int,
+    state int,
+    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 (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
+-- type names, they can join this table with the lease6 table.
+-- Make sure those values match Lease6::LeaseType enum (see src/bin/dhcpsrv/
+-- lease_mgr.h)
+CREATE TABLE lease6_types (
+    lease_type int,                             -- Lease type code.
+    name varchar,                               -- Name of the lease type
+    PRIMARY KEY (lease_type)
+);
+--START TRANSACTION;
+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
+INSERT INTO lease6_types (lease_type, name) VALUES (2, 'IA_PD');   -- Prefix delegations
+--COMMIT;
+
+-- Kea keeps track of the hardware/MAC address source, i.e. how the address
+-- was obtained. Depending on the technique and your network topology, it may
+-- be more or less trustworthy. This table is a convenience for
+-- users of the database - if they want to view the lease table and use the
+-- type names, they can join this table with the lease6 table. For details,
+-- see constants defined in src/lib/dhcp/dhcp/pkt.h for detailed explanation.
+CREATE TABLE lease_hwaddr_source (
+    hwaddr_source int,
+    name varchar,
+    PRIMARY KEY (hwaddr_source)
+);
+
+-- Hardware address obtained from raw sockets
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (1, 'HWADDR_SOURCE_RAW');
+
+-- Hardware address converted from IPv6 link-local address with EUI-64
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (2, 'HWADDR_SOURCE_IPV6_LINK_LOCAL');
+
+-- Hardware address extracted from client-id (duid)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (4, 'HWADDR_SOURCE_DUID');
+
+-- Hardware address extracted from client address relay option (RFC6939)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (8, 'HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION');
+
+-- Hardware address extracted from remote-id option (RFC4649)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (16, 'HWADDR_SOURCE_REMOTE_ID');
+
+-- Hardware address extracted from subscriber-id option (RFC4580)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (32, 'HWADDR_SOURCE_SUBSCRIBER_ID');
+
+-- Hardware address extracted from docsis options
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (64, 'HWADDR_SOURCE_DOCSIS_CMTS');
+
+-- -----------------------------------------------------
+-- Table `dhcp4_options`
+-- -----------------------------------------------------
+CREATE TABLE dhcp4_options (
+    option_id int,
+    code int,
+    value blob,
+    formatted_value varchar,
+    space varchar,
+    persistent int,
+    dhcp_client_class varchar,
+    dhcp4_subnet_id int,
+    host_id int,
+    PRIMARY KEY (option_id)
+);
+
+-- Create search indexes for dhcp4_options table
+CREATE INDEX dhcp4_optionsindex1 ON dhcp4_options (host_id);
+
+-- -----------------------------------------------------
+-- Table `dhcp6_options`
+-- -----------------------------------------------------
+CREATE TABLE dhcp6_options (
+    option_id int,
+    code int,
+    value blob,
+    formatted_value varchar,
+    space varchar,
+    persistent int,
+    dhcp_client_class varchar,
+    dhcp6_subnet_id int,
+    host_id int,
+    PRIMARY KEY (option_id)
+);
+
+-- Create search indexes for dhcp6_options table
+CREATE INDEX dhcp6_optionsindex1 ON dhcp6_options (host_id);
+
+-- Create table holding mapping of the lease states to their names.
+-- This is not used in queries from the DHCP server but rather in
+-- direct queries from the lease database management tools.
+CREATE TABLE lease_state (
+    state int,
+    name varchar,
+    PRIMARY KEY (state)
+);
+
+-- Insert currently defined state names.
+INSERT INTO lease_state (state, name) VALUES (0, 'default');
+INSERT INTO lease_state (state, name) VALUES (1, 'declined');
+INSERT INTO lease_state (state, name) VALUES (2, 'expired-reclaimed');
+
+-- Finally, the version of the schema.  We start at 1.0 during development.
+-- 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 in step with src/lib/dhcpsrv/tests/schema_copy.h,
+--       which defines the schema for the unit tests.  If you are updating
+--       the version number, the schema has changed: please ensure that
+--       schema_copy.h has been updated as well.
+CREATE TABLE schema_version (
+    version int,
+    minor int,
+    PRIMARY KEY (version)
+);
+--START TRANSACTION;
+INSERT INTO schema_version (version, minor) VALUES (1, 0);
+--COMMIT;
diff --git a/src/bin/admin/tests/dscsql_tests.sh.in b/src/bin/admin/tests/dscsql_tests.sh.in
new file mode 100644 (file)
index 0000000..d7cc195
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+# Copyright (C) 2014-2015 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
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# Include common test library.
+. @abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh
+
+dscsql_init_test() {
+    test_start "dscsql.init"
+    
+    # @todo: Implement this
+
+    test_finish 0
+}
+
+dscsql_version_test() {
+    test_start "dscsql.version"
+    
+    # @todo: Implement this
+
+    test_finish 0
+}
+
+dscsql_upgrade_test() {
+    test_start "dscsql.upgrade"
+    
+    # @todo: Implement this
+
+    test_finish 0
+}
+
+dscsql_init_test
+dscsql_version_test
+dscsql_upgrade_test
diff --git a/src/lib/dhcpsrv/dscsql_connection.cc b/src/lib/dhcpsrv/dscsql_connection.cc
new file mode 100644 (file)
index 0000000..8c26065
--- /dev/null
@@ -0,0 +1,243 @@
+// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+//
+// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//           http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <dhcpsrv/dscsql_connection.h>
+#include <string>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+DSCSqlConnection::DSCSqlConnection(const ParameterMap& parameters) : DatabaseConnection(parameters),
+        cluster_(NULL), session_(NULL), tagged_statements_(NULL) {
+}
+
+DSCSqlConnection::~DSCSqlConnection() {
+    CassError rc;
+    for (int i = 0; i < statements_.size(); i++)
+    {
+        if (statements_[i]) {
+            cass_prepared_free(statements_[i]);
+        }
+        statements_[i] = NULL;
+    }
+    CassFuture* close_future = cass_session_close(session_);
+    cass_future_wait(close_future);
+    std::string error;
+    checkStatementError(error, close_future, "could not close connection to DB");
+    rc = cass_future_error_code(close_future);
+    cass_future_free(close_future);
+    cass_session_free(session_);
+    session_ = NULL;
+    cass_cluster_free(cluster_);
+    cluster_ = NULL;
+    if (rc != CASS_OK) {
+        isc_throw(DbOpenError, error);
+    }
+}
+
+void
+DSCSqlConnection::openDatabase() {
+    CassError rc;
+    // Set up the values of the parameters
+    const char* contact_points = "127.0.0.1";
+    string scontact_points;
+    try {
+        scontact_points = getParameter("contact_points");
+        contact_points = scontact_points.c_str();
+    } catch (...) {
+        // No host.  Fine, we'll use "localhost"
+    }
+
+    const char* port = NULL;
+    string sport;
+    try {
+        sport = getParameter("port");
+        port = sport.c_str();
+    } catch (...) {
+        // No port.  Fine, we'll use "default"
+    }
+
+    const char* user = NULL;
+    string suser;
+    try {
+        suser = getParameter("user");
+        user = suser.c_str();
+    } catch (...) {
+        // No user.  Fine, we'll use NULL
+    }
+
+    const char* password = NULL;
+    string spassword;
+    try {
+        spassword = getParameter("password");
+        password = spassword.c_str();
+    } catch (...) {
+        // No password.  Fine, we'll use NULL
+    }
+
+    const char* keyspace = "keatest";
+    string skeyspace;
+    try {
+        skeyspace = getParameter("keyspace");
+        keyspace = skeyspace.c_str();
+    } catch (...) {
+        // No database name.  Fine, we'll use default 'keatest'
+    }
+
+    cluster_ = cass_cluster_new();
+    cass_cluster_set_contact_points(cluster_, contact_points);
+
+    if (user != NULL && password != NULL) {
+        cass_cluster_set_credentials(cluster_, user, password);
+    }
+
+    if (port != NULL) {
+        int port_nr;
+        try {
+            port_nr = boost::lexical_cast<int>(port);
+        } catch (const std::exception& ex) {
+            isc_throw(DbOperationError, "Invalid int data: " << port
+                      << " : " << ex.what());
+        }
+        cass_cluster_set_port(cluster_, port_nr);
+    }
+
+    session_ = cass_session_new();
+
+    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");
+    rc = cass_future_error_code(connect_future);
+    cass_future_free(connect_future);
+    if (rc != CASS_OK) {
+        cass_session_free(session_);
+        session_ = NULL;
+        cass_cluster_free(cluster_);
+        cluster_ = NULL;
+        isc_throw(DbOpenError, error);
+    }
+}
+
+void
+DSCSqlConnection::prepareStatements(DSCSqlTaggedStatement *statements) {
+    CassError rc = CASS_OK;
+    CassFuture* future = NULL;
+    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_;
+
+        future = cass_session_prepare(session_, query);
+        cass_future_wait(future);
+        std::string error;
+        checkStatementError(error, future, i, "could not prepare statement");
+        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);
+        }
+
+        cass_future_free(future);
+    }
+}
+
+string
+DSCSqlConnection::getName() const {
+    string name = "";
+    try {
+        name = getParameter("name");
+    } catch (...) {
+        // Return an empty name
+    }
+    return (name);
+}
+
+string
+DSCSqlConnection::getDescription() const {
+    return (string("DataStax Cassandra Database"));
+}
+
+pair<uint32_t, uint32_t>
+DSCSqlConnection::getVersion() const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_GET_VERSION);
+
+    uint32_t version = CASS_VERSION_MAJOR;
+    uint32_t minor = CASS_VERSION_MINOR;
+
+    return make_pair<uint32_t, uint32_t>(version, minor);
+}
+
+void
+DSCSqlConnection::commit() {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_DSCSQL_COMMIT);
+}
+
+void
+DSCSqlConnection::rollback() {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_DSCSQL_ROLLBACK);
+}
+
+
+void
+DSCSqlConnection::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: " << 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: " << tagged_statements_[stindex].name_ << " reason: " <<
+            errorMessage << " error code: " << rc;
+    }
+    error = stream.str();
+}
+
+void
+DSCSqlConnection::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 << " reason: " << errorMessage << " error code: " << rc;
+    }
+    error = stream.str();
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/lib/dhcpsrv/dscsql_connection.h b/src/lib/dhcpsrv/dscsql_connection.h
new file mode 100644 (file)
index 0000000..a2fd4d1
--- /dev/null
@@ -0,0 +1,140 @@
+// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+//
+// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//           http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DSCSQL_CONNECTION_H
+#define DSCSQL_CONNECTION_H
+
+#include <dhcpsrv/database_connection.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <inttypes.h>
+#include <cassandra.h>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief  Defines a single query
+struct DSCSqlTaggedStatement {
+    /// Param name.
+    const char** params_;
+
+    /// Short name of the query.
+    const char* name_;
+
+    /// Text representation of the actual query.
+    const char* text_;
+};
+
+/// Defines DSC SQL backend version: 1.0
+const uint32_t DSCSQL_CURRENT_VERSION = 1;
+const uint32_t DSCSQL_CURRENT_MINOR = 0;
+
+class DSCSqlConnection : public DatabaseConnection {
+public:
+
+    /// @brief Constructor
+    ///
+    /// Initialize DSCSqlConnection object with parameters needed for connection.
+    DSCSqlConnection(const ParameterMap& parameters);
+
+    /// @brief Destructor
+    virtual ~DSCSqlConnection();
+
+    /// @brief Prepare statements
+    ///
+    /// Creates the prepared statements for all of the SQL statements used
+    /// by the PostgreSQL backend.
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    /// @throw isc::InvalidParameter 'index' is not valid for the vector.  This
+    ///        represents an internal error within the code.
+    void prepareStatements(DSCSqlTaggedStatement *statements);
+
+    /// @brief Open Database
+    ///
+    /// Opens the database using the information supplied in the parameters
+    /// passed to the constructor.
+    ///
+    /// @throw NoDatabaseName Mandatory database name not given
+    /// @throw DbOpenError Error opening the database
+    void openDatabase();
+
+    /// @brief Return backend type
+    ///
+    /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
+    ///
+    /// @return Type of the backend.
+    virtual std::string getType() const {
+        return (std::string("cassandra"));
+    }
+
+    /// @brief Returns name of the database.
+    ///
+    /// @return database name
+    virtual std::string getName() const;
+
+    /// @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;
+
+    /// @brief Returns backend version.
+    ///
+    /// @return Version number as a pair of unsigned integers.  "first" is the
+    ///         major version number, "second" the minor number.
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual std::pair<uint32_t, uint32_t> getVersion() const;
+
+    /// @brief Commit Transactions
+    ///
+    /// Commits all pending database operations.
+    ///
+    /// @throw DbOperationError Iif the commit failed.
+    virtual void commit();
+
+    /// @brief Rollback Transactions
+    ///
+    /// Rolls back all pending database operations.
+    ///
+    /// @throw DbOperationError If the rollback failed.
+    virtual void rollback();
+
+    /// @brief Check Error
+    ///
+    /// Chech error for current database operation.
+    void checkStatementError(std::string& error, CassFuture* future, uint32_t stindex, const char* what) const;
+
+    /// @brief Check Error
+    ///
+    /// Chech error for current database operation.
+    void checkStatementError(std::string& error, CassFuture* future, const char* what) const;
+
+    /// DSC SQL connection handle
+    CassCluster* cluster_;
+    CassSession* session_;
+    std::vector<const CassPrepared*> statements_;       ///< Prepared statements
+    DSCSqlTaggedStatement *tagged_statements_;
+};
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // DSCSQL_CONNECTION_H
diff --git a/src/lib/dhcpsrv/dscsql_lease_mgr.cc b/src/lib/dhcpsrv/dscsql_lease_mgr.cc
new file mode 100644 (file)
index 0000000..6e3bbe6
--- /dev/null
@@ -0,0 +1,1910 @@
+// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+//
+// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//           http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <config.h>
+#include <asiolink/io_address.h>
+#include <dhcp/duid.h>
+#include <dhcp/hwaddr.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/dscsql_lease_mgr.h>
+#include <boost/static_assert.hpp>
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <ctime>
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+static const size_t HOSTNAME_MAX_LEN = 255;
+static const size_t ADDRESS6_TEXT_MAX_LEN = 39;
+
+static CassError DSCSqlBindNone(CassStatement* statement, size_t index, void*) {
+    return cass_statement_bind_null(statement, index);
+}
+
+static CassError DSCSqlBindBool(CassStatement* statement, size_t index, void* value) {
+    return cass_statement_bind_bool(statement, index, *(static_cast<cass_bool_t*>(value)));
+}
+
+static CassError DSCSqlBindInt32(CassStatement* statement, size_t index, void* value) {
+    return cass_statement_bind_int32(statement, index, *(static_cast<cass_int32_t*>(value)));
+}
+
+static CassError DSCSqlBindInt64(CassStatement* statement, size_t index, void* value) {
+    return cass_statement_bind_int64(statement, index, *(static_cast<cass_int64_t*>(value)));
+}
+
+static CassError DSCSqlBindTimestamp(CassStatement* statement, size_t index, void* value) {
+    return cass_statement_bind_int64(statement, index, *(static_cast<cass_int64_t*>(value)));
+}
+
+static CassError DSCSqlBindString(CassStatement* statement, size_t index, void* value) {
+    return cass_statement_bind_string(statement, index, (static_cast<const char*>(value)));
+}
+
+static CassError DSCSqlBindBytes(CassStatement* statement, size_t index, void* value) {
+    return cass_statement_bind_bytes(statement, index, &(*(static_cast<std::vector<cass_byte_t>*>(value)))[0],
+            static_cast<std::vector<cass_byte_t>*>(value)->size());
+}
+
+static CassError (*DSCSqlBindFunctions[])(CassStatement* statement, size_t index, void* value) = {
+    DSCSqlBindNone,
+    DSCSqlBindBool,
+    DSCSqlBindInt32,
+    DSCSqlBindInt64,
+    DSCSqlBindTimestamp,
+    DSCSqlBindString,
+    DSCSqlBindBytes
+};
+
+struct ColumnInfo {
+    ColumnInfo () : column_(NULL), type_(DSCSQL_DATA_TYPE_NONE) {}
+    ColumnInfo (const char *column, DSCSqlDataType type) : column_(column), type_(type) {}
+    const char* column_;
+    DSCSqlDataType type_;
+};
+
+/// @brief Catalog of all the SQL 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"),
+        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"),
+        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*>("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*>("limit"),
+        NULL };
+static const char* get_version_params[] = {
+        static_cast<const char*>("version"),
+        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"),
+#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+        static_cast<const char*>("expire"),
+#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+        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"),
+#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+        static_cast<const char*>("expire"),
+#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+        NULL };
+
+DSCSqlTaggedStatement DSCSqlLeaseMgr::tagged_statements_[] = {
+    // DELETE_LEASE4
+    { delete_lease4_params,
+      "delete_lease4",
+      "DELETE FROM lease4 WHERE address = ?" },
+
+    // DELETE_LEASE4_STATE_EXPIRED
+    { delete_expired_lease4_params,
+      "delete_lease4_expired",
+      //"WHERE state = ? AND expire < ? ALLOW FILTERING"
+      "SELECT address, hwaddr, client_id, "
+        "valid_lifetime, expire, subnet_id, "
+        "fqdn_fwd, fqdn_rev, hostname, state "
+      "FROM lease4 "
+      "WHERE state = ? ALLOW FILTERING" },
+
+    // DELETE_LEASE6
+    { delete_lease6_params,
+      "delete_lease6",
+      "DELETE FROM lease6 WHERE address = ?" },
+
+    // DELETE_LEASE6_STATE_EXPIRED
+    { delete_expired_lease6_params,
+      "delete_lease6_expired",
+      //"WHERE state = ? AND expire < ? ALLOW FILTERING"
+      "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 = ? 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 < ? ORDER BY expire ASC "
+      "WHERE state = ? "
+      "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 < ? ORDER BY expire ASC "
+      "WHERE state = ? "
+      "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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
+#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+      "IF NOT EXISTS"
+#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+    },
+
+    // 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
+#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+      "IF NOT EXISTS"
+#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+    },
+
+    // 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 = ? "
+#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+      "IF expire = ?"
+#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+    },
+
+    // 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 = ? "
+#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+      "IF expire = ?"
+#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+    },
+
+    // End of list sentinel
+    { NULL, NULL, NULL }
+};
+
+
+/// @brief Common DSC and Lease Data Methods
+///
+/// The DSCSqlLease4Exchange and DSCSqlLease6Exchange classes provide the
+/// functionality to set up binding information between variables in the
+/// program and data extracted from the database.  This class is the common
+/// base to both of them, containing some common methods.
+
+class DSCSqlLeaseExchange {
+public:
+    DSCSqlLeaseExchange() : hwaddr_length_(0), expire_(0),
+                            subnet_id_(0), valid_lifetime_(0),
+                            fqdn_fwd_(false), fqdn_rev_(false), hostname_length_(0), state_(0) {
+        memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
+        memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
+    }
+    // Time conversion methods.
+    static void
+    convertToDatabaseTime(const time_t& cltt,
+                                             const uint32_t& valid_lifetime,
+                                             uint64_t& expire) {
+        // Calculate expiry time. Store it in the 64-bit value so as we can detect
+        // overflows.
+        int64_t expire_time = static_cast<int64_t>(cltt) +
+            static_cast<int64_t>(valid_lifetime);
+
+        if (expire_time > DatabaseConnection::MAX_DB_TIME) {
+            isc_throw(BadValue, "Time value is too large: " << expire_time);
+        }
+
+        expire = expire_time;
+    }
+
+    static void
+    convertFromDatabaseTime(const uint64_t& expire,
+                                           const uint32_t& valid_lifetime,
+                                           time_t& cltt) {
+        // Convert to local time
+        cltt = expire - static_cast<int64_t>(valid_lifetime);
+    }
+
+    std::vector<ColumnInfo> columns_;   ///< Column names and types
+protected:
+    std::vector<uint8_t> hwaddr_;       ///< Hardware address
+    uint8_t         hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN];
+                                        ///< Hardware address buffer
+    unsigned long   hwaddr_length_;     ///< Hardware address length
+    uint64_t        expire_;            ///< Lease expiry time
+    uint32_t        subnet_id_;         ///< Subnet identification
+    uint64_t        valid_lifetime_;    ///< Lease time
+    uint32_t        fqdn_fwd_;          ///< Has forward DNS update been
+                                        ///< performed
+    uint32_t        fqdn_rev_;          ///< Has reverse DNS update been
+                                        ///< performed
+    char            hostname_buffer_[HOSTNAME_MAX_LEN];
+                                        ///< Client hostname
+    unsigned long   hostname_length_;   ///< Client hostname length
+    uint32_t        state_;             ///< Lease state
+};
+
+
+/// @brief Exchange DSC and Lease4 Data
+///
+/// On any DSC SQL operation, arrays of DSC SQL BIND structures must be built to
+/// describe the parameters in the prepared statements.  Where information is
+/// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
+/// structure is identical.  This class handles the creation of that array.
+///
+/// Owing to the DSC SQL API, the process requires some intermediate variables
+/// to hold things like data length etc.  This object holds those variables.
+///
+/// @note There are no unit tests for this class.  It is tested indirectly
+/// in all DSCSqlLeaseMgr::xxx4() calls where it is used.
+
+class DSCSqlLease4Exchange : public DSCSqlLeaseExchange {
+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.
+    DSCSqlLease4Exchange() : addr4_(0), client_id_length_(0),
+                            client_id_null_(false) {
+        memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
+
+        // Set the column names (for error messages)
+        uint32_t size;
+        uint32_t offset = 0;
+        size = 12;
+        columns_.resize(size);
+        columns_[offset++] = ColumnInfo("address", DSCSQL_DATA_TYPE_INT32);
+        columns_[offset++] = ColumnInfo("hwaddr", DSCSQL_DATA_TYPE_BYTES);
+        columns_[offset++] = ColumnInfo("client_id", DSCSQL_DATA_TYPE_BYTES);
+        columns_[offset++] = ColumnInfo("valid_lifetime", DSCSQL_DATA_TYPE_INT64);
+        columns_[offset++] = ColumnInfo("expire", DSCSQL_DATA_TYPE_TIMESTAMP);
+        columns_[offset++] = ColumnInfo("subnet_id", DSCSQL_DATA_TYPE_INT32);
+        columns_[offset++] = ColumnInfo("fqdn_fwd", DSCSQL_DATA_TYPE_BOOL);
+        columns_[offset++] = ColumnInfo("fqdn_rev", DSCSQL_DATA_TYPE_BOOL);
+        columns_[offset++] = ColumnInfo("hostname", DSCSQL_DATA_TYPE_STRING);
+        columns_[offset++] = ColumnInfo("state", DSCSQL_DATA_TYPE_INT32);
+        columns_[offset++] = ColumnInfo("limit", DSCSQL_DATA_TYPE_INT32);
+        columns_[offset++] = ColumnInfo("version", DSCSQL_DATA_TYPE_NONE);
+    }
+
+    /// @brief Create DSCSQL_BIND objects for Lease4 Pointer
+    ///
+    /// Fills in the DSCSQL_BIND array for sending data in the Lease4 object to
+    /// the database.
+    void createBindForSend(const Lease4Ptr& lease, DSCSqlBindArray& bind_array) {
+        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: uint32_t
+            // The address in the Lease structure is an IOAddress object.  Convert
+            // this to an integer for storage.
+            addr4_ = static_cast<uint32_t>(lease_->addr_);
+            bind_array.add(&addr4_);
+
+            hwaddr_ = lease_->hwaddr_->hwaddr_;
+            hwaddr_length_ = hwaddr_.size();
+            bind_array.add(&hwaddr_);
+
+            // client_id: varbinary(128)
+            if (lease_->client_id_) {
+                client_id_ = lease_->client_id_->getClientId();
+            } else {
+                client_id_.clear();
+            }
+            client_id_length_ = client_id_.size();
+            bind_array.add(&client_id_);
+
+            // valid lifetime: unsigned int
+            valid_lifetime_ = lease_->valid_lft_;
+            bind_array.add(&valid_lifetime_);
+            // expire: timestamp
+            // 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_
+            DSCSqlLeaseExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_, expire_);
+            bind_array.add(&expire_);
+
+            // subnet_id: unsigned int
+            // Can use lease_->subnet_id_ directly as it is of type uint32_t.
+            subnet_id_ = lease_->subnet_id_;
+            bind_array.add(&subnet_id_);
+
+            // fqdn_fwd: boolean
+            fqdn_fwd_ = lease_->fqdn_fwd_;
+            bind_array.add(&fqdn_fwd_);
+
+            // fqdn_rev: boolean
+            fqdn_rev_ = lease_->fqdn_rev_;
+            bind_array.add(&fqdn_rev_);
+
+            // hostname: varchar(255)
+            hostname_length_  = lease_->hostname_.length();
+            if (hostname_length_ >= sizeof(hostname_buffer_)) {
+                isc_throw(BadValue, "hostname value is too large: " << lease_->hostname_.c_str());
+            }
+            if (hostname_length_) {
+                memcpy(hostname_buffer_, lease_->hostname_.c_str(), hostname_length_);
+            }
+            hostname_buffer_[hostname_length_] = '\0';
+            bind_array.add(hostname_buffer_);
+
+            // state: uint32_t
+            state_ = lease_->state_;
+            bind_array.add(&state_);
+
+        } catch (const std::exception& ex) {
+            isc_throw(DbOperationError,
+                      "Could not create bind array from Lease4: "
+                      << lease_->addr_.toText() << ", reason: " << ex.what());
+        }
+    }
+
+    /// @brief Create BIND array to receive data
+    ///
+    /// Creates a DSCSQL_BIND array to receive Lease4 data from the database.
+    Lease4Ptr createBindForReceive(const CassRow* row) {
+        try {
+            const CassValue* value;
+            unsigned char* buffer = NULL;
+            const char* text_buffer = NULL;
+
+            // address: uint32_t
+            value = cass_row_get_column_by_name(row, columns_[0].column_);
+            cass_value_get_int32(value, reinterpret_cast<cass_int32_t*>(&addr4_));
+
+            // hwaddr: varbinary(20)
+            value = cass_row_get_column_by_name(row, columns_[1].column_);
+            cass_value_get_bytes(value, const_cast<const cass_byte_t**>(&buffer), &hwaddr_length_);
+            hwaddr_.assign(buffer, buffer + hwaddr_length_);
+
+            // client_id: varbinary(128)
+            value = cass_row_get_column_by_name(row, columns_[2].column_);
+            cass_value_get_string(value, &text_buffer, &client_id_length_);
+            client_id_.assign(text_buffer, text_buffer + client_id_length_);
+
+            // lease_time: unsigned int
+            value = cass_row_get_column_by_name(row, columns_[3].column_);
+            cass_value_get_int64(value, reinterpret_cast<cass_int64_t*>(&valid_lifetime_));
+
+            // expire: timestamp
+            value = cass_row_get_column_by_name(row, columns_[4].column_);
+            cass_value_get_int64(value, reinterpret_cast<cass_int64_t*>(&expire_));
+
+            // subnet_id: unsigned int
+            value = cass_row_get_column_by_name(row, columns_[5].column_);
+            cass_value_get_int32(value, reinterpret_cast<cass_int32_t*>(&subnet_id_));
+
+            // fqdn_fwd: boolean
+            value = cass_row_get_column_by_name(row, columns_[6].column_);
+            cass_value_get_bool(value, reinterpret_cast<cass_bool_t*>(&fqdn_fwd_));
+
+            // fqdn_rev: boolean
+            value = cass_row_get_column_by_name(row, columns_[7].column_);
+            cass_value_get_bool(value, reinterpret_cast<cass_bool_t*>(&fqdn_rev_));
+
+            // hostname: varchar(255)
+            value = cass_row_get_column_by_name(row, columns_[8].column_);
+            cass_value_get_string(value, &text_buffer, &hostname_length_);
+            if (hostname_length_ >= sizeof(hostname_buffer_)) {
+                isc_throw(BadValue, "hostname value is too large: " << text_buffer);
+            }
+            if (hostname_length_) {
+                memcpy(hostname_buffer_, text_buffer, hostname_length_);
+            }
+            hostname_buffer_[hostname_length_] = '\0';
+
+            // state: uint32_t
+            value = cass_row_get_column_by_name(row, columns_[9].column_);
+            cass_value_get_int32(value, reinterpret_cast<cass_int32_t*>(&state_));
+
+            time_t cltt = 0;
+            DSCSqlLeaseExchange::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
+
+            // Recreate the hardware address.
+            HWAddrPtr hwaddr(new HWAddr(hwaddr_, HTYPE_ETHER));
+
+            std::string hostname(hostname_buffer_,
+                                 hostname_buffer_ + hostname_length_);
+
+            Lease4Ptr result(new Lease4(addr4_, hwaddr,
+                                                     client_id_buffer_, client_id_length_,
+                                                     valid_lifetime_, 0, 0, cltt,
+                                                     subnet_id_, fqdn_fwd_, fqdn_rev_,
+                                                     hostname));
+
+            result->state_ = state_;
+
+            return (result);
+        } catch (const std::exception& ex) {
+            isc_throw(DbOperationError,
+                      "Could not convert data to Lease4, reason: "
+                       << ex.what());
+        }
+        return (Lease4Ptr());
+    }
+
+private:
+
+    // Note: All array lengths are equal to the corresponding variable in the
+    //       schema.
+    // Note: Arrays are declared fixed length for speed of creation
+    Lease4Ptr       lease_;             ///< Pointer to lease object
+    uint32_t        addr4_;             ///< IPv4 address
+    std::vector<uint8_t> client_id_;    ///< Client identification
+    uint8_t         client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN];
+                                        ///< Client ID buffer
+    unsigned long   client_id_length_;  ///< Client ID address length
+    bool            client_id_null_;    ///< Is Client ID null?
+};
+
+
+
+/// @brief Exchange DSC and Lease6 Data
+///
+/// On any DSC SQL operation, arrays of DSC SQL BIND structures must be built to
+/// describe the parameters in the prepared statements.  Where information is
+/// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
+/// structure is identical.  This class handles the creation of that array.
+///
+/// Owing to the DSC SQL API, the process requires some intermediate variables
+/// to hold things like data length etc.  This object holds those variables.
+///
+/// @note There are no unit tests for this class.  It is tested indirectly
+/// in all DSCSqlLeaseMgr::xxx6() calls where it is used.
+
+class DSCSqlLease6Exchange : public DSCSqlLeaseExchange {
+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.
+    DSCSqlLease6Exchange() : addr6_length_(0), duid_length_(0),
+                            iaid_(0), lease_type_(0), prefixlen_(0),
+                            pref_lifetime_(0), hwaddr_null_(false), hwtype_(0),
+                            hwaddr_source_(0) {
+        memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
+        memset(duid_buffer_, 0, sizeof(duid_buffer_));
+
+        // Set the column names (for error messages)
+        uint32_t size;
+        uint32_t offset = 0;
+        size = 18;
+        columns_.resize(size);
+        columns_[offset++] = ColumnInfo("address", DSCSQL_DATA_TYPE_STRING);
+        columns_[offset++] = ColumnInfo("duid", DSCSQL_DATA_TYPE_BYTES);
+        columns_[offset++] = ColumnInfo("valid_lifetime", DSCSQL_DATA_TYPE_INT64);
+        columns_[offset++] = ColumnInfo("expire", DSCSQL_DATA_TYPE_TIMESTAMP);
+        columns_[offset++] = ColumnInfo("subnet_id", DSCSQL_DATA_TYPE_INT32);
+        columns_[offset++] = ColumnInfo("pref_lifetime", DSCSQL_DATA_TYPE_INT64);
+        columns_[offset++] = ColumnInfo("lease_type", DSCSQL_DATA_TYPE_INT32);
+        columns_[offset++] = ColumnInfo("iaid", DSCSQL_DATA_TYPE_INT32);
+        columns_[offset++] = ColumnInfo("prefix_len", DSCSQL_DATA_TYPE_INT32);
+        columns_[offset++] = ColumnInfo("fqdn_fwd", DSCSQL_DATA_TYPE_BOOL);
+        columns_[offset++] = ColumnInfo("fqdn_rev", DSCSQL_DATA_TYPE_BOOL);
+        columns_[offset++] = ColumnInfo("hostname", DSCSQL_DATA_TYPE_STRING);
+        columns_[offset++] = ColumnInfo("hwaddr", DSCSQL_DATA_TYPE_BYTES);
+        columns_[offset++] = ColumnInfo("hwtype", DSCSQL_DATA_TYPE_INT32);
+        columns_[offset++] = ColumnInfo("hwaddr_source", DSCSQL_DATA_TYPE_INT32);
+        columns_[offset++] = ColumnInfo("state", DSCSQL_DATA_TYPE_INT32);
+        columns_[offset++] = ColumnInfo("limit", DSCSQL_DATA_TYPE_INT32);
+        columns_[offset++] = ColumnInfo("version", DSCSQL_DATA_TYPE_NONE);
+    }
+
+    /// @brief Create DSCSQL_BIND objects for Lease6 Pointer
+    ///
+    /// Fills in the DSCSQL_BIND array for sending data in the Lease6 object to
+    /// the database.
+    void createBindForSend(const Lease6Ptr& lease, DSCSqlBindArray& bind_array) {
+        if (!lease) {
+            isc_throw(BadValue, "createBindForSend:: 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(39)
+            std::string text_buffer = lease_->addr_.toText();
+            addr6_length_ = text_buffer.size();
+            if (addr6_length_ >= sizeof(addr6_buffer_)) {
+                isc_throw(BadValue, "address value is too large: " << text_buffer);
+            }
+            if (addr6_length_) {
+                memcpy(addr6_buffer_, text_buffer.c_str(), addr6_length_);
+            }
+            addr6_buffer_[addr6_length_] = '\0';
+            bind_array.add(addr6_buffer_);
+
+            // duid: varchar(128)
+            if (!lease_->duid_) {
+                isc_throw(DbOperationError, "lease6 for address " << addr6_buffer_
+                          << " is missing mandatory client-id.");
+            }
+            duid_ = lease_->duid_->getDuid();
+            duid_length_ = duid_.size();
+            bind_array.add(&duid_);
+
+            // valid lifetime: unsigned int
+            valid_lifetime_ = lease_->valid_lft_;
+            bind_array.add(&valid_lifetime_);
+
+            // expire: timestamp
+            // 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_
+            DSCSqlLeaseExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_, expire_);
+            bind_array.add(&expire_);
+
+            // subnet_id: unsigned int
+            // Can use lease_->subnet_id_ directly as it is of type uint32_t.
+            subnet_id_ = lease_->subnet_id_;
+            bind_array.add(&subnet_id_);
+
+            // pref_lifetime: unsigned int
+            // Can use lease_->preferred_lft_ directly as it is of type uint32_t.
+            pref_lifetime_ = lease_->preferred_lft_;
+            bind_array.add(&pref_lifetime_);
+
+            // lease_type: tinyint
+            // Must convert to uint8_t as lease_->type_ is a LeaseType variable.
+            lease_type_ = lease_->type_;
+            bind_array.add(&lease_type_);
+
+            // iaid: unsigned int
+            // Can use lease_->iaid_ directly as it is of type uint32_t.
+            iaid_ = lease_->iaid_;
+            bind_array.add(&iaid_);
+
+            // prefix_len: unsigned tinyint
+            // Can use lease_->prefixlen_ directly as it is uint32_t.
+            prefixlen_ = lease_->prefixlen_;
+            bind_array.add(&prefixlen_);
+
+            // fqdn_fwd: boolean
+            fqdn_fwd_ = lease_->fqdn_fwd_;
+            bind_array.add(&fqdn_fwd_);
+
+            // fqdn_rev: boolean
+            fqdn_rev_ = lease_->fqdn_rev_;
+            bind_array.add(&fqdn_rev_);
+
+            // hostname: varchar(255)
+            hostname_length_  = lease_->hostname_.length();
+            if (hostname_length_ >= sizeof(hostname_buffer_)) {
+                isc_throw(BadValue, "hostname value is too large: " << lease_->hostname_.c_str());
+            }
+            if (hostname_length_) {
+                memcpy(hostname_buffer_, lease_->hostname_.c_str(), hostname_length_);
+            }
+            hostname_buffer_[hostname_length_] = '\0';
+            bind_array.add(hostname_buffer_);
+
+            // hwaddr: varbinary(20) - hardware/MAC address
+            HWAddrPtr hwaddr = lease_->hwaddr_;
+            if (hwaddr) {
+                hwaddr_ = hwaddr->hwaddr_;
+            } else {
+                hwaddr_.clear();
+            }
+            hwaddr_length_ = hwaddr_.size();
+            bind_array.add(&hwaddr_);
+
+            // hwtype
+            if (hwaddr) {
+                hwtype_ = lease->hwaddr_->htype_;
+            } else {
+                hwtype_ = 0;
+            }
+            bind_array.add(&hwtype_);
+
+            /// Hardware source
+            if (hwaddr) {
+                hwaddr_source_ = lease->hwaddr_->source_;
+            } else {
+                hwaddr_source_ = 0;
+            }
+            bind_array.add(&hwaddr_source_);
+
+            // state: uint32_t
+            state_ = lease_->state_;
+            bind_array.add(&state_);
+
+        } catch (const std::exception& ex) {
+            isc_throw(DbOperationError,
+                      "Could not create bind array from Lease6: "
+                      << lease_->addr_.toText() << ", reason: " << ex.what());
+        }
+    }
+
+    /// @brief Create BIND array to receive data
+    ///
+    /// Creates a DSCSQL_BIND array to receive Lease6 data from the database.
+    Lease6Ptr createBindForReceive(const CassRow* row) {
+        try {
+            const CassValue* value;
+            unsigned char* buffer = NULL;
+            const char* text_buffer = NULL;
+
+            // address:  uint32_t
+            value = cass_row_get_column_by_name(row, columns_[0].column_);
+            cass_value_get_string(value, &text_buffer, &addr6_length_);
+            if (addr6_length_ >= sizeof(addr6_buffer_)) {
+                isc_throw(BadValue, "address value is too large: " << text_buffer);
+            }
+            if (addr6_length_) {
+                memcpy(addr6_buffer_, text_buffer, addr6_length_);
+            }
+            addr6_buffer_[addr6_length_] = '\0';
+
+            // client_id: varbinary(128)
+            value = cass_row_get_column_by_name(row, columns_[1].column_);
+            cass_value_get_bytes(value, const_cast<const cass_byte_t**>(&buffer), &duid_length_);
+            duid_.assign(buffer, buffer + duid_length_);
+
+            // lease_time: unsigned int
+            value = cass_row_get_column_by_name(row, columns_[2].column_);
+            cass_value_get_int64(value, reinterpret_cast<cass_int64_t*>(&valid_lifetime_));
+
+            // expire: timestamp
+            value = cass_row_get_column_by_name(row, columns_[3].column_);
+            cass_value_get_int64(value, reinterpret_cast<cass_int64_t*>(&expire_));
+
+            // subnet_id: unsigned int
+            value = cass_row_get_column_by_name(row, columns_[4].column_);
+            cass_value_get_int32(value, reinterpret_cast<cass_int32_t*>(&subnet_id_));
+
+            // pref_lifetime: unsigned int
+            value = cass_row_get_column_by_name(row, columns_[5].column_);
+            cass_value_get_int32(value, reinterpret_cast<cass_int32_t*>(&pref_lifetime_));
+
+            // lease_type: tinyint
+            value = cass_row_get_column_by_name(row, columns_[6].column_);
+            cass_value_get_int32(value, reinterpret_cast<cass_int32_t*>(&lease_type_));
+
+            // iaid: unsigned int
+            value = cass_row_get_column_by_name(row, columns_[7].column_);
+            cass_value_get_int32(value, reinterpret_cast<cass_int32_t*>(&iaid_));
+
+            // prefix_len: unsigned tinyint
+            value = cass_row_get_column_by_name(row, columns_[8].column_);
+            cass_value_get_int32(value, reinterpret_cast<cass_int32_t*>(&prefixlen_));
+
+            // fqdn_fwd: boolean
+            value = cass_row_get_column_by_name(row, columns_[9].column_);
+            cass_value_get_bool(value, reinterpret_cast<cass_bool_t*>(&fqdn_fwd_));
+
+            // fqdn_rev: boolean
+            value = cass_row_get_column_by_name(row, columns_[10].column_);
+            cass_value_get_bool(value, reinterpret_cast<cass_bool_t*>(&fqdn_rev_));
+
+            // hostname: varchar(255)
+            value = cass_row_get_column_by_name(row, columns_[11].column_);
+            cass_value_get_string(value, &text_buffer, &hostname_length_);
+            if (hostname_length_ >= sizeof(hostname_buffer_)) {
+                isc_throw(BadValue, "hostname value is too large: " << text_buffer);
+            }
+            if (hostname_length_) {
+                memcpy(hostname_buffer_, text_buffer, hostname_length_);
+            }
+            hostname_buffer_[hostname_length_] = '\0';
+
+            // hardware address
+            // hwaddr: varbinary(20)
+            value = cass_row_get_column_by_name(row, columns_[12].column_);
+            cass_value_get_bytes(value, const_cast<const cass_byte_t**>(&buffer), &hwaddr_length_);
+            hwaddr_.assign(buffer, buffer + hwaddr_length_);
+
+            // hardware type: unsigned short int (16 bits)
+            value = cass_row_get_column_by_name(row, columns_[13].column_);
+            cass_value_get_int32(value, reinterpret_cast<cass_int32_t*>(&hwtype_));
+
+            // hardware source: unsigned int (32 bits)
+            value = cass_row_get_column_by_name(row, columns_[14].column_);
+            cass_value_get_int32(value, reinterpret_cast<cass_int32_t*>(&hwaddr_source_));
+
+            // state: uint32_t
+            value = cass_row_get_column_by_name(row, columns_[15].column_);
+            cass_value_get_int32(value, reinterpret_cast<cass_int32_t*>(&state_));
+
+            if (lease_type_ != Lease::TYPE_NA && lease_type_ != Lease::TYPE_TA &&
+                    lease_type_ != Lease::TYPE_PD) {
+                isc_throw(BadValue, "invalid lease type returned (" <<
+                    static_cast<int>(lease_type_) << ") for lease with "
+                    << "address " << addr6_buffer_ << ". Only 0, 1, or 2 are "
+                    << "allowed.");
+            }
+
+            isc::asiolink::IOAddress addr(addr6_buffer_);
+            DuidPtr duid(new DUID(duid_));
+            HWAddrPtr hwaddr;
+            if (hwaddr_.size()) {
+                hwaddr.reset(new HWAddr(hwaddr_, hwtype_));
+                hwaddr->source_ = hwaddr_source_;
+            }
+
+            std::string hostname(hostname_buffer_,
+                                 hostname_buffer_ + hostname_length_);
+
+            // 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;
+            DSCSqlLeaseExchange::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
+            result->cltt_ = cltt;
+#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+            result->old_cltt_ = cltt;
+#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+
+            result->state_ = state_;
+
+            return (result);
+        } catch (const std::exception& ex) {
+            isc_throw(DbOperationError,
+                      "Could not convert data to Lease4, reason: "
+                       << ex.what());
+        }
+        return (Lease6Ptr());
+    }
+
+private:
+    // Note: All array lengths are equal to the corresponding variable in the
+    // schema.
+    // Note: arrays are declared fixed length for speed of creation
+    Lease6Ptr       lease_;             ///< Pointer to lease object
+    char            addr6_buffer_[ADDRESS6_TEXT_MAX_LEN + 1];  ///< Character
+                                        ///< array form of V6 address
+    unsigned long   addr6_length_;      ///< Length of the address
+    std::vector<uint8_t> duid_;         ///< Client identification
+    uint8_t         duid_buffer_[DUID::MAX_DUID_LEN]; ///< Buffer form of DUID
+    unsigned long   duid_length_;       ///< Length of the DUID
+    uint32_t        iaid_;              ///< Identity association ID
+    uint32_t        lease_type_;        ///< Lease type
+    uint32_t        prefixlen_;         ///< Prefix length
+    uint32_t        pref_lifetime_;     ///< Preferred lifetime
+    bool            hwaddr_null_;       ///< Used when HWAddr is null
+    uint32_t        hwtype_;            ///< Hardware type
+    uint32_t        hwaddr_source_;     ///< Source of the hardware address
+};
+
+#ifdef TERASTREAM_DB_LOGIC
+class DSCSqlCustomLeaseExchange : public virtual SqlLeaseExchange {
+public:
+    DSCSqlCustomLeaseExchange() {}
+    virtual ~DSCSqlCustomLeaseExchange() {}
+    virtual void executeInternal(LeaseExchangeData& exchange, bool has_allocated_ip, bool has_requested_ip, bool has_reserved_ip) {
+        if (has_allocated_ip) {
+        }
+        if (has_requested_ip) {
+        }
+        if (has_reserved_ip) {
+        }
+        if (exchange.in_fake_) {
+        }
+    }
+};
+
+class DSCSqlLease4DiscoverNoReqNoResExchange : public SqlLease4DiscoverNoReqNoResExchange, public DSCSqlCustomLeaseExchange {
+public:
+    DSCSqlLease4DiscoverNoReqNoResExchange() : DSCSqlCustomLeaseExchange() {}
+    virtual ~DSCSqlLease4DiscoverNoReqNoResExchange() {}
+    virtual void execute(LeaseExchangeData& exchange) {
+        executeInternal(exchange, true, false, false);
+    }
+private:
+};
+
+/// @brief Supports exchanging IPv4 leases with SQL for discover.
+class DSCSqlLease4DiscoverNoReqResExchange : public SqlLease4DiscoverNoReqResExchange, public DSCSqlCustomLeaseExchange {
+public:
+    DSCSqlLease4DiscoverNoReqResExchange() : DSCSqlCustomLeaseExchange() {}
+    virtual ~DSCSqlLease4DiscoverNoReqResExchange() {}
+    virtual void execute(LeaseExchangeData& exchange) {
+        executeInternal(exchange, true, false, true);
+    }
+private:
+};
+
+/// @brief Supports exchanging IPv4 leases with SQL for discover.
+class DSCSqlLease4DiscoverReqNoResExchange : public SqlLease4DiscoverReqNoResExchange, public DSCSqlCustomLeaseExchange {
+public:
+    DSCSqlLease4DiscoverReqNoResExchange() : DSCSqlCustomLeaseExchange() {}
+    virtual ~DSCSqlLease4DiscoverReqNoResExchange() {}
+    virtual void execute(LeaseExchangeData& exchange) {
+        executeInternal(exchange, true, true, false);
+    }
+private:
+};
+
+/// @brief Supports exchanging IPv4 leases with SQL for discover.
+class DSCSqlLease4DiscoverReqResExchange : public SqlLease4DiscoverReqResExchange, public DSCSqlCustomLeaseExchange {
+public:
+    DSCSqlLease4DiscoverReqResExchange() : DSCSqlCustomLeaseExchange() {}
+    virtual ~DSCSqlLease4DiscoverReqResExchange() {}
+    virtual void execute(LeaseExchangeData& exchange) {
+        executeInternal(exchange, true, true, true);
+    }
+private:
+};
+
+/// @brief Supports exchanging IPv4 leases with SQL for request.
+class DSCSqlLease4RequestNoReqNoResExchange : public SqlLease4RequestNoReqNoResExchange, public DSCSqlCustomLeaseExchange {
+public:
+    DSCSqlLease4RequestNoReqNoResExchange() : DSCSqlCustomLeaseExchange() {}
+    virtual ~DSCSqlLease4RequestNoReqNoResExchange() {}
+    virtual void execute(LeaseExchangeData& exchange) {
+        executeInternal(exchange, true, false, false);
+    }
+private:
+};
+
+/// @brief Supports exchanging IPv4 leases with SQL for request.
+class DSCSqlLease4RequestNoReqResExchange : public SqlLease4RequestNoReqResExchange, public DSCSqlCustomLeaseExchange {
+public:
+    DSCSqlLease4RequestNoReqResExchange() : DSCSqlCustomLeaseExchange() {}
+    virtual ~DSCSqlLease4RequestNoReqResExchange() {}
+    virtual void execute(LeaseExchangeData& exchange) {
+        executeInternal(exchange, false, false, true);
+    }
+private:
+};
+
+/// @brief Supports exchanging IPv4 leases with SQL for request.
+class DSCSqlLease4RequestReqNoResExchange : public SqlLease4RequestReqNoResExchange, public DSCSqlCustomLeaseExchange {
+public:
+    DSCSqlLease4RequestReqNoResExchange() : DSCSqlCustomLeaseExchange() {}
+    virtual ~DSCSqlLease4RequestReqNoResExchange() {}
+    virtual void execute(LeaseExchangeData& exchange) {
+        executeInternal(exchange, false, true, false);
+    }
+private:
+};
+
+/// @brief Supports exchanging IPv4 leases with SQL for request.
+class DSCSqlLease4RequestReqResExchange : public SqlLease4RequestReqResExchange, public DSCSqlCustomLeaseExchange {
+public:
+    DSCSqlLease4RequestReqResExchange() : DSCSqlCustomLeaseExchange() {}
+    virtual ~DSCSqlLease4RequestReqResExchange() {}
+    virtual void execute(LeaseExchangeData& exchange) {
+        executeInternal(exchange, false, true, true);
+    }
+private:
+};
+#endif // TERASTREAM_DB_LOGIC
+
+DSCSqlLeaseMgr::DSCSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
+    : LeaseMgr(), dbconn_(parameters), exchange4_(new DSCSqlLease4Exchange()),
+    exchange6_(new DSCSqlLease6Exchange()) {
+    dbconn_.openDatabase();
+    dbconn_.prepareStatements(DSCSqlLeaseMgr::tagged_statements_);
+}
+
+DSCSqlLeaseMgr::~DSCSqlLeaseMgr() {
+}
+
+std::string
+DSCSqlLeaseMgr::getDBVersion() {
+    std::stringstream tmp;
+    tmp << "DSC SQL backend " << DSCSQL_CURRENT_VERSION;
+    tmp << "." << DSCSQL_CURRENT_MINOR;
+    tmp << ", library " << "cassandra_static";
+    return (tmp.str());
+}
+
+
+void
+DSCSqlLeaseMgr::getDataType(const StatementIndex stindex, int pindex, const DSCSqlLeaseExchange& exchange, DSCSqlDataType& type) {
+    if (DSCSqlLeaseMgr::tagged_statements_[stindex].params_ &&
+            DSCSqlLeaseMgr::tagged_statements_[stindex].params_[pindex]) {
+        for (int i = 0; exchange.columns_.size(); i++) {
+            if (!strcmp(DSCSqlLeaseMgr::tagged_statements_[stindex].params_[pindex], exchange.columns_[i].column_)) {
+                type = exchange.columns_[i].type_;
+                return;
+            }
+        }
+    }
+    type = DSCSQL_DATA_TYPE_NONE;
+}
+
+void
+DSCSqlLeaseMgr::bindData(CassStatement* statement, const StatementIndex stindex, DSCSqlBindArray& bind_array, const DSCSqlLeaseExchange& exchange) {
+    if (DSCSqlLeaseMgr::tagged_statements_[stindex].params_ == NULL) {
+        return;
+    }
+    for (int i = 0; DSCSqlLeaseMgr::tagged_statements_[stindex].params_[i]; i++) {
+        DSCSqlDataType type;
+        DSCSqlLeaseMgr::getDataType(stindex, i, exchange, type);
+        DSCSqlBindFunctions[type](statement, i, bind_array.values_[i]);
+    }
+}
+
+bool
+DSCSqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
+                              DSCSqlBindArray& bind_array, DSCSqlLeaseExchange& exchange) {
+    CassError rc;
+    CassStatement* statement = NULL;
+    CassFuture* future = NULL;
+
+    statement = cass_prepared_bind(dbconn_.statements_[stindex]);
+
+    DSCSqlLeaseMgr::bindData(statement, stindex, bind_array, exchange);
+
+    future = cass_session_execute(dbconn_.session_, statement);
+    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);
+        isc_throw(DbOperationError, error);
+    }
+    const CassResult* resultCollection = cass_future_get_result(future);
+    int row_count = cass_result_row_count(resultCollection);
+    int column_count = cass_result_column_count(resultCollection);
+    cass_result_free(resultCollection);
+    cass_future_free(future);
+    cass_statement_free(statement);
+#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+    if (row_count != 1) {
+        return (false);
+    } else if (column_count != 1) {
+        isc_throw(TransactionException, "add transaction failed");
+    }
+#else
+    if (row_count && column_count) {
+    }
+#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+
+    return (true);
+}
+
+bool
+DSCSqlLeaseMgr::addLease(const Lease4Ptr& lease) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_ADD_ADDR4).arg(lease->addr_.toText());
+
+    DSCSqlBindArray bind_array;
+    exchange4_->createBindForSend(lease, bind_array);
+    return (addLeaseCommon(INSERT_LEASE4, bind_array, *exchange4_));
+}
+
+bool
+DSCSqlLeaseMgr::addLease(const Lease6Ptr& lease) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_ADD_ADDR6).arg(lease->addr_.toText());
+
+    DSCSqlBindArray bind_array;
+    exchange6_->createBindForSend(lease, bind_array);
+    return (addLeaseCommon(INSERT_LEASE6, bind_array, *exchange6_));
+}
+
+template <typename Exchange, typename LeaseCollection>
+void DSCSqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
+                                       DSCSqlBindArray& bind_array,
+                                       Exchange& exchange,
+                                       LeaseCollection& result,
+                                       bool single) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_GET_ADDR4).arg(dbconn_.tagged_statements_[stindex].name_);
+
+    CassError rc;
+    CassStatement* statement = NULL;
+    CassFuture* future = NULL;
+    const DSCSqlLeaseExchange& leaseExchange = static_cast<DSCSqlLeaseExchange>(*exchange);
+
+    statement = cass_prepared_bind(dbconn_.statements_[stindex]);
+
+    DSCSqlLeaseMgr::bindData(statement, stindex, bind_array, leaseExchange);
+
+    future = cass_session_execute(dbconn_.session_, statement);
+    cass_future_wait(future);
+    std::string error;
+    dbconn_.checkStatementError(error, future, "unable to GET");
+    rc = cass_future_error_code(future);
+    if (rc != CASS_OK) {
+        cass_future_free(future);
+        cass_statement_free(statement);
+        isc_throw(DbOperationError, error);
+    }
+
+    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);
+        result.push_back(exchange->createBindForReceive(row));
+    }
+
+    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 query "
+                      << dbconn_.tagged_statements_[stindex].name_);
+    }
+}
+
+void
+DSCSqlLeaseMgr::getLease(StatementIndex stindex, DSCSqlBindArray& bind_array,
+                             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, bind_array, exchange4_, collection, true);
+
+    // Return single record if present, else clear the lease.
+    if (collection.empty()) {
+        result.reset();
+    } else {
+        result = *collection.begin();
+    }
+}
+
+
+void
+DSCSqlLeaseMgr::getLease(StatementIndex stindex, DSCSqlBindArray& bind_array,
+                             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, bind_array, exchange6_, collection, true);
+
+    // Return single record if present, else clear the lease.
+    if (collection.empty()) {
+        result.reset();
+    } else {
+        result = *collection.begin();
+    }
+}
+
+// Basic lease access methods.  Obtain leases from the database using various
+// criteria.
+
+Lease4Ptr
+DSCSqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_GET_ADDR4).arg(addr.toText());
+
+    // Set up the WHERE clause value
+    DSCSqlBindArray bind_array;
+
+    uint32_t addr4_data = static_cast<uint32_t>(addr);
+    bind_array.add(&addr4_data);
+
+    // Get the data
+    Lease4Ptr result;
+    getLease(GET_LEASE4_ADDR, bind_array, result);
+
+    return (result);
+}
+
+
+Lease4Collection
+DSCSqlLeaseMgr::getLease4(const HWAddr& hwaddr) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_GET_HWADDR).arg(hwaddr.toText());
+
+    // Set up the WHERE clause value
+    DSCSqlBindArray bind_array;
+
+    std::vector<uint8_t>hwaddr_data = hwaddr.hwaddr_;
+    bind_array.add(&hwaddr_data);
+
+    // Get the data
+    Lease4Collection result;
+    getLeaseCollection(GET_LEASE4_HWADDR, bind_array, result);
+
+    return (result);
+}
+
+
+Lease4Ptr
+DSCSqlLeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_GET_SUBID_HWADDR)
+              .arg(subnet_id).arg(hwaddr.toText());
+
+    // Set up the WHERE clause value
+    DSCSqlBindArray bind_array;
+
+    std::vector<uint8_t>hwaddr_data = hwaddr.hwaddr_;
+    bind_array.add(&hwaddr_data);
+
+    uint32_t subnet_id_data = subnet_id;
+    bind_array.add(&subnet_id_data);
+
+    // Get the data
+    Lease4Ptr result;
+    getLease(GET_LEASE4_HWADDR_SUBID, bind_array, result);
+
+    return (result);
+}
+
+
+Lease4Collection
+DSCSqlLeaseMgr::getLease4(const ClientId& clientid) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_GET_CLIENTID).arg(clientid.toText());
+
+    // Set up the WHERE clause value
+    DSCSqlBindArray bind_array;
+
+    std::vector<uint8_t> client_id_data = clientid.getClientId();
+    bind_array.add(&client_id_data);
+
+    // Get the data
+    Lease4Collection result;
+    getLeaseCollection(GET_LEASE4_CLIENTID, bind_array, result);
+
+    return (result);
+}
+
+Lease4Ptr
+DSCSqlLeaseMgr::getLease4(const ClientId& clientid, const HWAddr& hwaddr, SubnetID subnet_id) const {
+    /// This function is currently not implemented because allocation engine
+    /// searches for the lease using HW address or client identifier.
+    /// It never uses both parameters in the same time. We need to
+    /// consider if this function is needed at all.
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_GET_CLIENTID_HWADDR_SUBID).arg(clientid.toText())
+              .arg(hwaddr.toText()).arg(subnet_id);
+
+    isc_throw(NotImplemented, "The DSCSqlLeaseMgr::getLease4 function was"
+              " called, but it is not implemented");
+}
+
+Lease4Ptr
+DSCSqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_GET_SUBID_CLIENTID)
+              .arg(subnet_id).arg(clientid.toText());
+
+    // Set up the WHERE clause value
+    DSCSqlBindArray bind_array;
+
+    std::vector<uint8_t> client_id_data = clientid.getClientId();
+    bind_array.add(&client_id_data);
+
+    uint32_t subnet_id_data = subnet_id;
+    bind_array.add(&subnet_id_data);
+
+    // Get the data
+    Lease4Ptr result;
+    getLease(GET_LEASE4_CLIENTID_SUBID, bind_array, result);
+
+    return (result);
+}
+
+
+Lease6Ptr
+DSCSqlLeaseMgr::getLease6(Lease::Type lease_type,
+                         const isc::asiolink::IOAddress& addr) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_GET_ADDR6).arg(addr.toText())
+              .arg(lease_type);
+
+    // Set up the WHERE clause value
+    DSCSqlBindArray bind_array;
+
+    std::string text_buffer = addr.toText();
+    uint32_t addr6_length = text_buffer.size();
+    char addr6_buffer[ADDRESS6_TEXT_MAX_LEN + 1];
+    if (addr6_length >= sizeof(addr6_buffer)) {
+        isc_throw(BadValue, "address value is too large: " << text_buffer);
+    }
+    if (addr6_length) {
+        memcpy(addr6_buffer, text_buffer.c_str(), addr6_length);
+    }
+    addr6_buffer[addr6_length] = '\0';
+    bind_array.add(addr6_buffer);
+
+    uint32_t lease_type_data = lease_type;
+    bind_array.add(&lease_type_data);
+
+    Lease6Ptr result;
+    getLease(GET_LEASE6_ADDR, bind_array, result);
+
+    return (result);
+}
+
+
+Lease6Collection
+DSCSqlLeaseMgr::getLeases6(Lease::Type lease_type,
+                          const DUID& duid, uint32_t iaid) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_GET_IAID_DUID).arg(iaid).arg(duid.toText())
+              .arg(lease_type);
+
+    // Set up the WHERE clause value
+    DSCSqlBindArray bind_array;
+
+    std::vector<uint8_t> duid_data = duid.getDuid();
+    bind_array.add(&duid_data);
+
+    uint32_t iaid_data = iaid;
+    bind_array.add(&iaid_data);
+
+    uint32_t lease_type_data = lease_type;
+    bind_array.add(&lease_type_data);
+
+    // ... and get the data
+    Lease6Collection result;
+    getLeaseCollection(GET_LEASE6_DUID_IAID, bind_array, result);
+
+    return (result);
+}
+
+Lease6Collection
+DSCSqlLeaseMgr::getLeases6(Lease::Type lease_type,
+                          const DUID& duid, uint32_t iaid,
+                          SubnetID subnet_id) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_GET_IAID_SUBID_DUID)
+              .arg(iaid).arg(subnet_id).arg(duid.toText())
+              .arg(lease_type);
+
+    // Set up the WHERE clause value
+    DSCSqlBindArray bind_array;
+
+    std::vector<uint8_t> duid_data = duid.getDuid();
+    bind_array.add(&duid_data);
+
+    uint32_t iaid_data = iaid;
+    bind_array.add(&iaid_data);
+
+    uint32_t subnet_id_data = subnet_id;
+    bind_array.add(&subnet_id_data);
+
+    uint32_t lease_type_data = lease_type;
+    bind_array.add(&lease_type_data);
+
+    // ... and get the data
+    Lease6Collection result;
+    getLeaseCollection(GET_LEASE6_DUID_IAID_SUBID, bind_array, result);
+
+    return (result);
+}
+
+void
+DSCSqlLeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases,
+                                 const size_t max_leases) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_DSCSQL_GET_EXPIRED6)
+        .arg(max_leases);
+    getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE6_EXPIRE);
+}
+
+void
+DSCSqlLeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases,
+                                 const size_t max_leases) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_DSCSQL_GET_EXPIRED4)
+        .arg(max_leases);
+    getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE4_EXPIRE);
+}
+
+template<typename LeaseCollection>
+void
+DSCSqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
+                                       const size_t max_leases,
+                                       StatementIndex statement_index) const {
+    // Set up the WHERE clause value
+    //"WHERE state != ? AND expire < ? ORDER BY expire ASC "
+    uint32_t keepState = Lease::STATE_EXPIRED_RECLAIMED;
+    uint64_t timestamp = static_cast<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.
+    uint32_t limit = max_leases > 0 ? static_cast<uint32_t>(max_leases) :
+        std::numeric_limits<uint32_t>::max();
+
+    for (uint32_t state = Lease::STATE_DEFAULT; state <= Lease::STATE_EXPIRED_RECLAIMED; state++) {
+        if (state == keepState) {
+            continue;
+        }
+        LeaseCollection tempCollection;
+        DSCSqlBindArray bind_array;
+
+        bind_array.add(&state);
+        bind_array.add(&limit);
+
+        // Retrieve leases from the database.
+        getLeaseCollection(statement_index, bind_array, tempCollection);
+
+        typedef typename LeaseCollection::iterator LeaseCollectionIt;
+
+        for (LeaseCollectionIt it = tempCollection.begin(); it != tempCollection.end(); ++it) {
+            if ((*it)->getExpirationTime() < timestamp) {
+                expired_leases.push_back((*it));
+            }
+        }
+    }
+}
+
+template <typename LeasePtr>
+void
+DSCSqlLeaseMgr::updateLeaseCommon(StatementIndex stindex,
+                                 DSCSqlBindArray& bind_array,
+                                 const LeasePtr& lease, DSCSqlLeaseExchange& exchange) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_ADD_ADDR4).arg(dbconn_.tagged_statements_[stindex].name_);
+
+    CassError rc;
+    CassStatement* statement = NULL;
+    CassFuture* future = NULL;
+
+    statement = cass_prepared_bind(dbconn_.statements_[stindex]);
+
+    DSCSqlLeaseMgr::bindData(statement, stindex, bind_array, exchange);
+
+    future = cass_session_execute(dbconn_.session_, statement);
+    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);
+    }
+
+    const CassResult* resultCollection = cass_future_get_result(future);
+    int row_count = cass_result_row_count(resultCollection);
+    int column_count = cass_result_column_count(resultCollection);
+    cass_result_free(resultCollection);
+    cass_future_free(future);
+    cass_statement_free(statement);
+
+#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+    // Check success case first as it is the most likely outcome.
+    if (row_count > 1) {
+        // Should not happen - primary key constraint should only have selected
+        // one row.
+        isc_throw(DbOperationError, "apparently updated more than one lease "
+                      "that had the address " << lease->addr_.toText());
+    }
+
+    // If no rows affected, lease doesn't exist.
+    if (row_count == 0) {
+        isc_throw(NoSuchLease, "unable to update lease for address " <<
+                  lease->addr_.toText() << " as it does not exist");
+    }
+
+    if (row_count != 1) {
+        isc_throw(DbOperationError, "apparently updated transaction failed for the lease "
+                              "that had the address " << lease->addr_.toText());
+    } else if (column_count != 1) {
+        isc_throw(TransactionException, "update transaction failed");
+    }
+#else
+        if (row_count && column_count && lease) {
+        }
+#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+}
+
+
+void
+DSCSqlLeaseMgr::updateLease4(const Lease4Ptr& lease) {
+    const StatementIndex stindex = UPDATE_LEASE4;
+
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_UPDATE_ADDR4).arg(lease->addr_.toText());
+
+    // Create the BIND array for the data being updated
+    DSCSqlBindArray bind_array;
+    exchange4_->createBindForSend(lease, bind_array);
+    bind_array.remove(0);
+
+    // Set up the WHERE clause and append it to the SQL_BIND array
+    uint32_t addr4_data = static_cast<uint32_t>(lease->addr_);
+    bind_array.add(&addr4_data);
+
+#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+    uint64_t old_expire;
+    DSCSqlLeaseExchange::convertToDatabaseTime(lease->old_cltt_, lease->old_valid_lft_, old_expire);
+    bind_array.add(&old_expire);
+#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+
+    // Drop to common update code
+    updateLeaseCommon(stindex, bind_array, lease, *exchange4_);
+}
+
+void
+DSCSqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
+    const StatementIndex stindex = UPDATE_LEASE6;
+
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_UPDATE_ADDR6).arg(lease->addr_.toText());
+
+    // Create the BIND array for the data being updated
+    DSCSqlBindArray bind_array;
+    exchange6_->createBindForSend(lease, bind_array);
+    bind_array.remove(0);
+
+    // Set up the WHERE clause and append it to the BIND array
+    std::string text_buffer = lease->addr_.toText();
+    uint32_t addr6_length = text_buffer.size();
+    char addr6_buffer[ADDRESS6_TEXT_MAX_LEN + 1];
+    if (addr6_length >= sizeof(addr6_buffer)) {
+        isc_throw(BadValue, "address value is too large: " << text_buffer);
+    }
+    if (addr6_length) {
+        memcpy(addr6_buffer, text_buffer.c_str(), addr6_length);
+    }
+    addr6_buffer[addr6_length] = '\0';
+    bind_array.add(addr6_buffer);
+
+#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+    uint64_t old_expire;
+    DSCSqlLeaseExchange::convertToDatabaseTime(lease->old_cltt_, lease->old_valid_lft_, old_expire);
+    bind_array.add(&old_expire);
+#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS
+
+    // Drop to common update code
+    updateLeaseCommon(stindex, bind_array, lease, *exchange6_);
+}
+
+bool
+DSCSqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex,
+                                 DSCSqlBindArray& bind_array, DSCSqlLeaseExchange& exchange) {
+    CassError rc;
+    CassStatement* statement = NULL;
+    CassFuture* future = NULL;
+
+    statement = cass_prepared_bind(dbconn_.statements_[stindex]);
+
+    DSCSqlLeaseMgr::bindData(statement, stindex, bind_array, exchange);
+
+    future = cass_session_execute(dbconn_.session_, statement);
+    cass_future_wait(future);
+    std::string error;
+    dbconn_.checkStatementError(error, future, stindex, "unable to DELETE");
+    rc = cass_future_error_code(future);
+    cass_future_free(future);
+    cass_statement_free(statement);
+    if (rc != CASS_OK) {
+       isc_throw(DbOperationError, error);
+    }
+
+    return (true);
+}
+
+bool
+DSCSqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_DELETE_ADDR).arg(addr.toText());
+
+    // Set up the WHERE clause value
+    DSCSqlBindArray bind_array;
+
+    if (addr.isV4()) {
+        uint32_t addr4_data = static_cast<uint32_t>(addr);
+        bind_array.add(&addr4_data);
+        return (deleteLeaseCommon(DELETE_LEASE4, bind_array, *exchange4_));
+    } else {
+        std::string text_buffer = addr.toText();
+        uint32_t addr6_length = text_buffer.size();
+        char addr6_buffer[ADDRESS6_TEXT_MAX_LEN + 1];
+        if (addr6_length >= sizeof(addr6_buffer)) {
+            isc_throw(BadValue, "address value is too large: " << text_buffer);
+        }
+        if (addr6_length) {
+            memcpy(addr6_buffer, text_buffer.c_str(), addr6_length);
+        }
+        addr6_buffer[addr6_length] = '\0';
+        bind_array.add(addr6_buffer);
+        return (deleteLeaseCommon(DELETE_LEASE6, bind_array, *exchange6_));
+    }
+}
+
+uint64_t
+DSCSqlLeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t secs) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_DELETE_EXPIRED_RECLAIMED4)
+        .arg(secs);
+    return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE4_STATE_EXPIRED));
+}
+
+uint64_t
+DSCSqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_DELETE_EXPIRED_RECLAIMED6)
+        .arg(secs);
+    return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE6_STATE_EXPIRED));
+}
+
+uint64_t
+DSCSqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs, StatementIndex statement_index) {
+    // Set up the WHERE clause value
+    //"WHERE state = ? AND expire < ? ALLOW FILTERING"
+
+    DSCSqlBindArray bind_array;
+    uint32_t result = 0;
+
+    // State is reclaimed.
+    uint32_t state = Lease::STATE_EXPIRED_RECLAIMED;
+    bind_array.add(&state);
+
+    // Expiration timestamp.
+    uint64_t expiration = static_cast<int64_t>(time(NULL) - static_cast<time_t>(secs));
+
+    // Get the data
+    Lease4Collection result4Leases;
+    Lease6Collection result6Leases;
+    switch (statement_index) {
+    case DELETE_LEASE4_STATE_EXPIRED:
+        getLeaseCollection(statement_index, bind_array, result4Leases);
+        break;
+    case DELETE_LEASE6_STATE_EXPIRED:
+        getLeaseCollection(statement_index, bind_array, result6Leases);
+        break;
+    default:
+        break;
+    }
+    for (Lease4Collection::iterator it = result4Leases.begin(); it != result4Leases.end(); ++it) {
+        if ((*it)->getExpirationTime() < expiration) {
+            if(deleteLease((*it)->addr_)) {
+                result++;
+            }
+        }
+    }
+    for (Lease6Collection::iterator it = result6Leases.begin(); it != result6Leases.end(); ++it) {
+        if ((*it)->getExpirationTime() < expiration) {
+            if(deleteLease((*it)->addr_)) {
+                result++;
+            }
+        }
+    }
+    return (result);
+}
+
+string
+DSCSqlLeaseMgr::getName() const {
+    string name = "";
+    try {
+        name = dbconn_.getParameter("name");
+    } catch (...) {
+        // Return an empty name
+    }
+    return (name);
+}
+
+string
+DSCSqlLeaseMgr::getDescription() const {
+    return (string("DataStax Cassandra Database"));
+}
+
+pair<uint32_t, uint32_t>
+DSCSqlLeaseMgr::getVersion() const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_GET_VERSION);
+
+    uint32_t version = CASS_VERSION_MAJOR;
+    uint32_t minor = CASS_VERSION_MINOR;
+
+    return make_pair<uint32_t, uint32_t>(version, minor);
+}
+
+void
+DSCSqlLeaseMgr::commit() {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_DSCSQL_COMMIT);
+}
+
+void
+DSCSqlLeaseMgr::rollback() {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_DSCSQL_ROLLBACK);
+}
+
+#ifdef TERASTREAM_DB_LOGIC
+void
+DSCSqlLeaseMgr::discoverLease4NoReqNoRes(LeaseExchangeData &data) {
+    DSCSqlLease4DiscoverNoReqNoResExchange exchange;
+    exchange.execute(data);
+}
+
+void
+DSCSqlLeaseMgr::discoverLease4NoReqRes(LeaseExchangeData &data) {
+    DSCSqlLease4DiscoverNoReqResExchange exchange;
+    exchange.execute(data);
+}
+
+void
+DSCSqlLeaseMgr::discoverLease4ReqNoRes(LeaseExchangeData &data) {
+    DSCSqlLease4DiscoverReqNoResExchange exchange;
+    exchange.execute(data);
+}
+
+void
+DSCSqlLeaseMgr::discoverLease4ReqRes(LeaseExchangeData &data) {
+    DSCSqlLease4DiscoverReqResExchange exchange;
+    exchange.execute(data);
+}
+
+void
+DSCSqlLeaseMgr::requestLease4NoReqNoRes(LeaseExchangeData &data) {
+    DSCSqlLease4RequestNoReqNoResExchange exchange;
+    exchange.execute(data);
+}
+
+void
+DSCSqlLeaseMgr::requestLease4NoReqRes(LeaseExchangeData &data) {
+    DSCSqlLease4RequestNoReqResExchange exchange;
+    exchange.execute(data);
+}
+
+void
+DSCSqlLeaseMgr::requestLease4ReqNoRes(LeaseExchangeData &data) {
+    DSCSqlLease4RequestReqNoResExchange exchange;
+    exchange.execute(data);
+}
+
+void
+DSCSqlLeaseMgr::requestLease4ReqRes(LeaseExchangeData &data) {
+    DSCSqlLease4RequestReqResExchange exchange;
+    exchange.execute(data);
+}
+#endif // TERASTREAM_DB_LOGIC
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/lib/dhcpsrv/dscsql_lease_mgr.h b/src/lib/dhcpsrv/dscsql_lease_mgr.h
new file mode 100644 (file)
index 0000000..1b7becf
--- /dev/null
@@ -0,0 +1,640 @@
+// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+//
+// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//           http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DSCSQL_LEASE_MGR_H
+#define DSCSQL_LEASE_MGR_H
+
+#include <dhcp/hwaddr.h>
+#include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/dscsql_connection.h>
+#include <boost/scoped_ptr.hpp>
+#include <boost/utility.hpp>
+#include <cassandra.h>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Structure used to bind C++ input values to dynamic SQL parameters
+/// The structure contains a vector which store the input values,
+/// This vector is passed directly into the
+/// DSC SQL execute call.
+///
+/// Note that the data values are stored as pointers. These pointers need to
+/// valid for the duration of the DSC SQL statement execution.  In other
+/// words populating them with pointers to values that go out of scope before
+/// statement is executed is a bad idea.
+
+struct DSCSqlBindArray {
+    /// @brief Vector of pointers to the data values.
+    std::vector<void*> values_;
+    void add(void* value) {
+        values_.push_back(value);
+    }
+    void remove(int index) {
+        values_.erase(values_.begin() + index);
+    }
+};
+
+class DSCSqlLeaseExchange;
+class DSCSqlLease4Exchange;
+class DSCSqlLease6Exchange;
+
+enum DSCSqlDataType {
+    DSCSQL_DATA_TYPE_NONE,
+    DSCSQL_DATA_TYPE_BOOL,
+    DSCSQL_DATA_TYPE_INT32,
+    DSCSQL_DATA_TYPE_INT64,
+    DSCSQL_DATA_TYPE_TIMESTAMP,
+    DSCSQL_DATA_TYPE_STRING,
+    DSCSQL_DATA_TYPE_BYTES
+};
+
+/// @brief DataStax Cassandra Lease Manager
+///
+/// This class provides the \ref isc::dhcp::LeaseMgr interface to the DSC - DataStax Cassandra
+/// database.  Use of this backend presupposes that a DSC database is
+/// available and that the Kea schema has been created within it.
+class DSCSqlLeaseMgr : public LeaseMgr {
+public:
+
+    /// @brief Constructor
+    ///
+    /// Uses the following keywords in the parameters passed to it to
+    /// connect to the database:
+    /// - name - Name of the database to which to connect (mandatory)
+    /// - host - Host to which to connect (optional, defaults to "localhost")
+    /// - user - Username under which to connect (optional)
+    /// - password - Password for "user" on the database (optional)
+    ///
+    /// If the database is successfully opened, the version number in the
+    /// schema_version table will be checked against hard-coded value in
+    /// the implementation file.
+    ///
+    /// Finally, all the SQL commands are pre-compiled.
+    ///
+    /// @param parameters A data structure relating keywords and values
+    ///        concerned with the database.
+    ///
+    /// @throw isc::dhcp::NoDatabaseName Mandatory database name not given
+    /// @throw isc::dhcp::DbOpenError Error opening the database
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    DSCSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters);
+
+    /// @brief Destructor (closes database)
+    virtual ~DSCSqlLeaseMgr();
+
+    /// @brief Local version of getDBVersion() class method
+    static std::string getDBVersion();
+
+    /// @brief Adds an IPv4 lease
+    ///
+    /// @param lease lease to be added
+    ///
+    /// @result true if the lease was added, false if not (because a lease
+    ///         with the same address was already there).
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual bool addLease(const Lease4Ptr& lease);
+
+    /// @brief Adds an IPv6 lease
+    ///
+    /// @param lease lease to be added
+    ///
+    /// @result true if the lease was added, false if not (because a lease
+    ///         with the same address was already there).
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual bool addLease(const Lease6Ptr& lease);
+
+    /// @brief Returns an IPv4 lease for specified IPv4 address
+    ///
+    /// This method return a lease that is associated with a given address.
+    /// For other query types (by hardware addr, by Client ID) there can be
+    /// several leases in different subnets (e.g. for mobile clients that
+    /// got address in different subnets). However, for a single address
+    /// there can be only one lease, so this method returns a pointer to
+    /// a single lease, not a container of leases.
+    ///
+    /// @param addr address of the searched lease
+    ///
+    /// @return smart pointer to the lease (or NULL if a lease is not found)
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
+
+    /// @brief Returns existing IPv4 leases for specified hardware address.
+    ///
+    /// Although in the usual case there will be only one lease, for mobile
+    /// clients or clients with multiple static/fixed/reserved leases there
+    /// can be more than one. Thus return type is a container, not a single
+    /// pointer.
+    ///
+    /// @param hwaddr hardware address of the client
+    ///
+    /// @return lease collection
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual Lease4Collection getLease4(const isc::dhcp::HWAddr& hwaddr) const;
+
+    /// @brief Returns existing IPv4 leases for specified hardware address
+    ///        and a subnet
+    ///
+    /// There can be at most one lease for a given HW address in a single
+    /// pool, so this method with either return a single lease or NULL.
+    ///
+    /// @param hwaddr hardware address of the client
+    /// @param subnet_id identifier of the subnet that lease must belong to
+    ///
+    /// @return a pointer to the lease (or NULL if a lease is not found)
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual Lease4Ptr getLease4(const isc::dhcp::HWAddr& hwaddr,
+                                SubnetID subnet_id) const;
+
+    /// @brief Returns existing IPv4 leases for specified client-id
+    ///
+    /// Although in the usual case there will be only one lease, for mobile
+    /// clients or clients with multiple static/fixed/reserved leases there
+    /// can be more than one. Thus return type is a container, not a single
+    /// pointer.
+    ///
+    /// @param clientid client identifier
+    ///
+    /// @return lease collection
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual Lease4Collection getLease4(const ClientId& clientid) const;
+
+    /// @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 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 function is currently
+    /// not implemented for the PostgreSQL backend.
+    virtual Lease4Ptr getLease4(const ClientId& client_id, const HWAddr& hwaddr,
+                                SubnetID subnet_id) const;
+
+    /// @brief Returns existing IPv4 lease for specified client-id
+    ///
+    /// There can be at most one lease for a given HW address in a single
+    /// pool, so this method with either return a single lease or NULL.
+    ///
+    /// @param clientid client identifier
+    /// @param subnet_id identifier of the subnet that lease must belong to
+    ///
+    /// @return a pointer to the lease (or NULL if a lease is not found)
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual Lease4Ptr getLease4(const ClientId& clientid,
+                                SubnetID subnet_id) const;
+
+    /// @brief Returns existing IPv6 lease for a given IPv6 address.
+    ///
+    /// For a given address, we assume that there will be only one lease.
+    /// The assumption here is that there will not be site or link-local
+    /// addresses used, so there is no way of having address duplication.
+    ///
+    /// @param type specifies lease type: (NA, TA or PD)
+    /// @param addr address of the searched lease
+    ///
+    /// @return smart pointer to the lease (or NULL if a lease is not found)
+    ///
+    /// @throw isc::BadValue record retrieved from database had an invalid
+    ///        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;
+
+    /// @brief Returns existing IPv6 leases for a given DUID+IA combination
+    ///
+    /// Although in the usual case there will be only one lease, for mobile
+    /// clients or clients with multiple static/fixed/reserved leases there
+    /// can be more than one. Thus return type is a container, not a single
+    /// pointer.
+    ///
+    /// @param type specifies lease type: (NA, TA or PD)
+    /// @param duid client DUID
+    /// @param iaid IA identifier
+    ///
+    /// @return smart pointer to the lease (or NULL if a lease is not found)
+    ///
+    /// @throw isc::BadValue record retrieved from database had an invalid
+    ///        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;
+
+    /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+    ///
+    /// @param type specifies lease type: (NA, TA or PD)
+    /// @param duid client DUID
+    /// @param iaid IA identifier
+    /// @param subnet_id subnet id of the subnet the lease belongs to
+    ///
+    /// @return lease collection (may be empty if no lease is found)
+    ///
+    /// @throw isc::BadValue record retrieved from database had an invalid
+    ///        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, SubnetID subnet_id) const;
+    /// @brief Returns a collection of expired DHCPv6 leases.
+    ///
+    /// This method returns at most @c max_leases expired leases. The leases
+    /// returned haven't been reclaimed, i.e. the database query must exclude
+    /// reclaimed leases from the results returned.
+    ///
+    /// @param [out] expired_leases A container to which expired leases returned
+    /// by the database backend are added.
+    /// @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& ,
+                                   const size_t ) const;
+
+
+    /// @brief Returns a collection of expired DHCPv4 leases.
+    ///
+    /// This method returns at most @c max_leases expired leases. The leases
+    /// returned haven't been reclaimed, i.e. the database query must exclude
+    /// reclaimed leases from the results returned.
+    ///
+    /// @param [out] expired_leases A container to which expired leases returned
+    /// by the database backend are added.
+    /// @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& ,
+                                   const size_t ) const;
+
+
+    /// @brief Updates IPv4 lease.
+    ///
+    /// Updates the record of the lease in the database (as identified by the
+    /// address) with the data in the passed lease object.
+    ///
+    /// @param lease4 The lease to be updated.
+    ///
+    /// @throw isc::dhcp::NoSuchLease Attempt to update a lease that did not
+    ///        exist.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual void updateLease4(const Lease4Ptr& lease4);
+
+    /// @brief Updates IPv6 lease.
+    ///
+    /// Updates the record of the lease in the database (as identified by the
+    /// address) with the data in the passed lease object.
+    ///
+    /// @param lease6 The lease to be updated.
+    ///
+    /// @throw isc::dhcp::NoSuchLease Attempt to update a lease that did not
+    ///        exist.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual void updateLease6(const Lease6Ptr& lease6);
+
+    /// @brief Deletes a lease.
+    ///
+    /// @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);
+
+    /// @brief Deletes all expired and reclaimed DHCPv4 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.
+    ///
+    /// @return Number of leases deleted.
+    virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t );
+
+    /// @brief Deletes all expired and reclaimed DHCPv6 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.
+    ///
+    /// @return Number of leases deleted.
+    virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t );
+
+    /// @brief Return backend type
+    ///
+    /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
+    ///
+    /// @return Type of the backend.
+    virtual std::string getType() const {
+        return (std::string("cassandra"));
+    }
+
+    /// @brief Returns name of the database.
+    ///
+    /// @return database name
+    virtual std::string getName() const;
+
+    /// @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;
+
+    /// @brief Returns backend version.
+    ///
+    /// @return Version number as a pair of unsigned integers.  "first" is the
+    ///         major version number, "second" the minor number.
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual std::pair<uint32_t, uint32_t> getVersion() const;
+
+    /// @brief Commit Transactions
+    ///
+    /// Commits all pending database operations.
+    ///
+    /// @throw DbOperationError Iif the commit failed.
+    virtual void commit();
+
+    /// @brief Rollback Transactions
+    ///
+    /// Rolls back all pending database operations.
+    ///
+    /// @throw DbOperationError If the rollback failed.
+    virtual void rollback();
+
+
+    /// @brief Statement Tags
+    ///
+    /// The contents of the enum are indexes into the list of compiled SQL
+    /// 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
+#ifdef TERASTREAM_DB_LOGIC
+        DISCOVER_LEASE4_NOREQNORES,
+        DISCOVER_LEASE4_NOREQRES,
+        DISCOVER_LEASE4_REQNORES,
+        DISCOVER_LEASE4_REQRES,
+        REQUEST_LEASE4_NOREQNORES,
+        REQUEST_LEASE4_NOREQRES,
+        REQUEST_LEASE4_REQNORES,
+        REQUEST_LEASE4_REQRES,
+#endif // TERASTREAM_DB_LOGIC
+        NUM_STATEMENTS              // Number of statements
+    };
+
+#ifdef TERASTREAM_DB_LOGIC
+    virtual void discoverLease4NoReqNoRes(LeaseExchangeData &data);
+    virtual void discoverLease4NoReqRes(LeaseExchangeData &data);
+    virtual void discoverLease4ReqNoRes(LeaseExchangeData &data);
+    virtual void discoverLease4ReqRes(LeaseExchangeData &data);
+    virtual void requestLease4NoReqNoRes(LeaseExchangeData &data);
+    virtual void requestLease4NoReqRes(LeaseExchangeData &data);
+    virtual void requestLease4ReqNoRes(LeaseExchangeData &data);
+    virtual void requestLease4ReqRes(LeaseExchangeData &data);
+#endif // TERASTREAM_DB_LOGIC
+
+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 bind_array array that has been created for the type
+    ///        of lease in question.
+    ///
+    /// @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, DSCSqlBindArray& bind_array, DSCSqlLeaseExchange& 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 bind_array 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, DSCSqlBindArray& bind_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 bind_array array containing the where clause input parameters
+    /// @param lease 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, DSCSqlBindArray& bind_array,
+                            Lease4Collection& result) const {
+        getLeaseCollection(stindex, bind_array, 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 bind_array array containing input parameters for the query
+    /// @param lease 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, DSCSqlBindArray& bind_array,
+                            Lease6Collection& result) const {
+        getLeaseCollection(stindex, bind_array, 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 retrieveing only a single lease.
+    ///
+    /// @param stindex Index of statement being executed
+    /// @param bind_array array containing input parameters for the query
+    /// @param lease Lease4 object returned
+    void getLease(StatementIndex stindex, DSCSqlBindArray& bind_array,
+                  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 retrieveing only a single lease.
+    ///
+    /// @param stindex Index of statement being executed
+    /// @param bind_array array containing input parameters for the query
+    /// @param lease Lease6 object returned
+    void getLease(StatementIndex stindex, DSCSqlBindArray& bind_array,
+                  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 bind_array 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.
+    template <typename LeasePtr>
+    void updateLeaseCommon(StatementIndex stindex, DSCSqlBindArray& bind_array,
+                           const LeasePtr& lease, DSCSqlLeaseExchange& 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 bind_array 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, DSCSqlBindArray& bind_array, DSCSqlLeaseExchange& 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);
+
+    static void bindData(CassStatement* statement, const StatementIndex stindex, DSCSqlBindArray& bind_array, const DSCSqlLeaseExchange& exchange);
+
+    static void getDataType(const StatementIndex stindex, int param, const DSCSqlLeaseExchange& exchange, DSCSqlDataType& type);
+
+    static DSCSqlTaggedStatement tagged_statements_[];
+    /// Database connection object
+    DSCSqlConnection 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".)
+    boost::scoped_ptr<DSCSqlLease4Exchange> exchange4_; ///< Exchange object
+    boost::scoped_ptr<DSCSqlLease6Exchange> exchange6_; ///< Exchange object
+};
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // DSCSQL_LEASE_MGR_H
diff --git a/src/lib/dhcpsrv/tests/dscsql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/dscsql_lease_mgr_unittest.cc
new file mode 100644 (file)
index 0000000..e35f7c0
--- /dev/null
@@ -0,0 +1,434 @@
+// Copyright (C) 2012-2015 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/io_address.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/dscsql_connection.h>
+#include <dhcpsrv/dscsql_lease_mgr.h>
+#include <dhcpsrv/tests/test_utils.h>
+#include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
+#include <dhcpsrv/testutils/dscsql_schema.h>
+#include <exceptions/exceptions.h>
+
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <utility>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace std;
+
+namespace {
+
+
+/// @brief Test fixture class for testing DataStax Cassandra Lease Manager
+///
+/// Opens the database prior to each test and closes it afterwards.
+/// All pending transactions are deleted prior to closure.
+
+class DSCSqlLeaseMgrTest : public GenericLeaseMgrTest {
+public:
+    /// @brief Constructor
+    ///
+    /// Deletes everything from the database and opens it.
+    DSCSqlLeaseMgrTest() {
+
+        // Ensure schema is the correct one.
+        destroyDSCSQLSchema();
+        createDSCSQLSchema();
+
+        // Connect to the database
+        try {
+            LeaseMgrFactory::create(validDSCSQLConnectionString());
+        } catch (...) {
+            std::cerr << "*** ERROR: unable to open database. The test\n"
+                         "*** environment is broken and must be fixed before\n"
+                         "*** the DSC SQL tests will run correctly.\n"
+                         "*** The reason for the problem is described in the\n"
+                         "*** accompanying exception output.\n";
+            throw;
+        }
+        lmptr_ = &(LeaseMgrFactory::instance());
+    }
+
+    /// @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 ~DSCSqlLeaseMgrTest() {
+        lmptr_->rollback();
+        LeaseMgrFactory::destroy();
+        destroyDSCSQLSchema();
+    }
+
+    /// @brief Reopen the database
+    ///
+    /// Closes the database and re-open it.  Anything committed should be
+    /// visible.
+    ///
+    /// Parameter is ignored for DSC SQL backend as the v4 and v6 leases share
+    /// the same database.
+    void reopen(Universe) {
+        LeaseMgrFactory::destroy();
+        LeaseMgrFactory::create(validConnectionString());
+        lmptr_ = &(LeaseMgrFactory::instance());
+    }
+
+};
+
+/// @brief Check that database can be opened
+///
+/// This test checks if the DSCSqlLeaseMgr can be instantiated.  This happens
+/// only if the database can be opened.  Note that this is not part of the
+/// DSCSqlLeaseMgr test fixure set.  This test checks that the database can be
+/// opened: the fixtures assume that and check basic operations.
+
+TEST(DSCSqlOpenTest, OpenDatabase) {
+
+    // Schema needs to be created for the test to work.
+    destroyDSCSQLSchema();
+    createDSCSQLSchema();
+
+    // Check that lease manager open the database opens correctly and tidy up.
+    //  If it fails, print the error message.
+    try {
+        LeaseMgrFactory::create(validDSCSQLConnectionString());
+        EXPECT_NO_THROW((void) LeaseMgrFactory::instance());
+        LeaseMgrFactory::destroy();
+    } catch (const isc::Exception& ex) {
+        FAIL() << "*** ERROR: unable to open database, reason:\n"
+               << "    " << ex.what() << "\n"
+               << "*** The test environment is broken and must be fixed\n"
+               << "*** before the DSC SQL tests will run correctly.\n";
+    }
+
+    // Check that attempting to get an instance of the lease manager when
+    // none is set throws an exception.
+    EXPECT_THROW(LeaseMgrFactory::instance(), NoLeaseManager);
+
+    // Check that wrong specification of backend throws an exception.
+    // (This is really a check on LeaseMgrFactory, but is convenient to
+    // perform here.)
+    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+        NULL, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
+        InvalidParameter);
+    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+        INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
+        InvalidType);
+
+    // Check that invalid login data causes an exception.
+    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+        DSCSQL_VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
+        DbOpenError);
+    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+        DSCSQL_VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
+        DbOpenError);
+    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+        DSCSQL_VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
+        DbOpenError);
+    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+        DSCSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)),
+        DbOpenError);
+
+    // Check for missing parameters
+    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+        DSCSQL_VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
+        NoDatabaseName);
+
+    // Tidy up after the test
+    destroyDSCSQLSchema();
+}
+
+/// @brief Check the getType() method
+///
+/// getType() returns a string giving the type of the backend, which should
+/// always be "cassandra".
+TEST_F(DSCSqlLeaseMgrTest, getType) {
+    EXPECT_EQ(std::string("cassandra"), lmptr_->getType());
+}
+
+/// @brief Check conversion functions
+///
+/// The server works using cltt and valid_filetime.  In the database, the
+/// information is stored as expire_time and valid-lifetime, which are
+/// related by
+///
+/// expire_time = cltt + valid_lifetime
+///
+/// This test checks that the conversion is correct.
+TEST_F(DSCSqlLeaseMgrTest, checkTimeConversion) {
+    const time_t cltt = time(NULL);
+
+    time_t converted_cltt = 0;
+    EXPECT_EQ(cltt, converted_cltt);
+}
+
+
+/// @brief Check getName() returns correct database name
+TEST_F(DSCSqlLeaseMgrTest, getName) {
+    EXPECT_EQ(std::string("keatest"), lmptr_->getName());
+}
+
+/// @brief Check that getVersion() returns the expected version
+TEST_F(DSCSqlLeaseMgrTest, checkVersion) {
+    // Check version
+    pair<uint32_t, uint32_t> version;
+    ASSERT_NO_THROW(version = lmptr_->getVersion());
+    EXPECT_EQ(CURRENT_VERSION_VERSION, version.first);
+    EXPECT_EQ(CURRENT_VERSION_MINOR, version.second);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// LEASE4 /////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+/// @brief Basic Lease4 Checks
+///
+/// Checks that the addLease, getLease4 (by address) and deleteLease (with an
+/// IPv4 address) works.
+TEST_F(DSCSqlLeaseMgrTest, basicLease4) {
+    testBasicLease4();
+}
+
+/// @brief Check that Lease4 code safely handles invalid dates.
+TEST_F(DSCSqlLeaseMgrTest, maxDate4) {
+    testMaxDate4();
+}
+
+/// @brief Lease4 update tests
+///
+/// Checks that we are able to update a lease in the database.
+TEST_F(DSCSqlLeaseMgrTest, updateLease4) {
+    testUpdateLease4();
+}
+
+/// @brief Check GetLease4 methods - access by Hardware Address
+TEST_F(DSCSqlLeaseMgrTest, getLease4HWAddr1) {
+    testGetLease4HWAddr1();
+}
+
+/// @brief Check GetLease4 methods - access by Hardware Address
+TEST_F(DSCSqlLeaseMgrTest, getLease4HWAddr2) {
+    testGetLease4HWAddr2();
+}
+
+// @brief Get lease4 by hardware address (2)
+//
+// Check that the system can cope with getting a hardware address of
+// any size.
+TEST_F(DSCSqlLeaseMgrTest, getLease4HWAddrSize) {
+    testGetLease4HWAddrSize();
+}
+
+/// @brief Check GetLease4 methods - access by Hardware Address & Subnet ID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of hardware address and subnet ID
+TEST_F(DSCSqlLeaseMgrTest, getLease4HwaddrSubnetId) {
+    testGetLease4HWAddrSubnetId();
+}
+
+// @brief Get lease4 by hardware address and subnet ID (2)
+//
+// Check that the system can cope with getting a hardware address of
+// any size.
+TEST_F(DSCSqlLeaseMgrTest, getLease4HWAddrSubnetIdSize) {
+    testGetLease4HWAddrSubnetIdSize();
+}
+
+// This test was derived from memfile.
+TEST_F(DSCSqlLeaseMgrTest, getLease4ClientId) {
+    testGetLease4ClientId();
+}
+
+/// @brief Check GetLease4 methods - access by Client ID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// the Client ID.
+TEST_F(DSCSqlLeaseMgrTest, getLease4ClientId2) {
+    testGetLease4ClientId2();
+}
+
+// @brief Get Lease4 by client ID (2)
+//
+// Check that the system can cope with a client ID of any size.
+TEST_F(DSCSqlLeaseMgrTest, getLease4ClientIdSize) {
+    testGetLease4ClientIdSize();
+}
+
+/// @brief Check GetLease4 methods - access by Client ID & Subnet ID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of client and subnet IDs.
+TEST_F(DSCSqlLeaseMgrTest, getLease4ClientIdSubnetId) {
+    testGetLease4ClientIdSubnetId();
+}
+
+/// @brief Basic Lease4 Checks
+///
+/// Checks that the addLease, getLease4(by address), getLease4(hwaddr,subnet_id),
+/// updateLease4() and deleteLease (IPv4 address) can handle NULL client-id.
+/// (client-id is optional and may not be present)
+TEST_F(DSCSqlLeaseMgrTest, lease4NullClientId) {
+    testLease4NullClientId();
+}
+
+/// @brief Verify that too long hostname for Lease4 is not accepted.
+///
+/// Checks that the it is not possible to create a lease when the hostname
+/// length exceeds 255 characters.
+TEST_F(DSCSqlLeaseMgrTest, lease4InvalidHostname) {
+    testLease4InvalidHostname();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// LEASE6 /////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+// Test checks whether simple add, get and delete operations are possible
+// on Lease6
+TEST_F(DSCSqlLeaseMgrTest, testAddGetDelete6) {
+    testAddGetDelete6(false);
+}
+
+/// @brief Basic Lease6 Checks
+///
+/// Checks that the addLease, getLease6 (by address) and deleteLease (with an
+/// IPv6 address) works.
+TEST_F(DSCSqlLeaseMgrTest, basicLease6) {
+    testBasicLease6();
+}
+
+/// @brief Check that Lease6 code safely handles invalid dates.
+TEST_F(DSCSqlLeaseMgrTest, maxDate6) {
+    testMaxDate6();
+}
+
+/// @brief Verify that too long hostname for Lease6 is not accepted.
+///
+/// Checks that the it is not possible to create a lease when the hostname
+/// length exceeds 255 characters.
+TEST_F(DSCSqlLeaseMgrTest, lease6InvalidHostname) {
+    testLease6InvalidHostname();
+}
+
+/// @brief Check GetLease6 methods - access by DUID/IAID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of DUID and IAID.
+TEST_F(DSCSqlLeaseMgrTest, getLeases6DuidIaid) {
+    testGetLeases6DuidIaid();
+}
+
+// Check that the system can cope with a DUID of allowed size.
+TEST_F(DSCSqlLeaseMgrTest, getLeases6DuidSize) {
+    testGetLeases6DuidSize();
+}
+
+/// @brief Check that getLease6 methods discriminate by lease type.
+///
+/// Adds six leases, two per lease type all with the same duid and iad but
+/// with alternating subnet_ids.
+/// It then verifies that all of getLeases6() method variants correctly
+/// discriminate between the leases based on lease type alone.
+TEST_F(DSCSqlLeaseMgrTest, lease6LeaseTypeCheck) {
+    testLease6LeaseTypeCheck();
+}
+
+/// @brief Check GetLease6 methods - access by DUID/IAID/SubnetID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of DIUID and IAID.
+TEST_F(DSCSqlLeaseMgrTest, getLease6DuidIaidSubnetId) {
+    testGetLease6DuidIaidSubnetId();
+}
+
+// Test checks that getLease6() works with different DUID sizes
+TEST_F(DSCSqlLeaseMgrTest, getLease6DuidIaidSubnetIdSize) {
+    testGetLease6DuidIaidSubnetIdSize();
+}
+
+/// @brief Lease6 update tests
+///
+/// Checks that we are able to update a lease in the database.
+TEST_F(DSCSqlLeaseMgrTest, updateLease6) {
+    testUpdateLease6();
+}
+
+/// @brief DHCPv4 Lease recreation tests
+///
+/// Checks that the lease can be created, deleted and recreated with
+/// different parameters. It also checks that the re-created lease is
+/// correctly stored in the lease database.
+TEST_F(DSCSqlLeaseMgrTest, testRecreateLease4) {
+    testRecreateLease4();
+}
+
+/// @brief DHCPv6 Lease recreation tests
+///
+/// Checks that the lease can be created, deleted and recreated with
+/// different parameters. It also checks that the re-created lease is
+/// correctly stored in the lease database.
+TEST_F(DSCSqlLeaseMgrTest, testRecreateLease6) {
+    testRecreateLease6();
+}
+
+/// @brief Checks that null DUID is not allowed.
+TEST_F(DSCSqlLeaseMgrTest, nullDuid) {
+    testNullDuid();
+}
+
+/// @brief Tests whether memfile can store and retrieve hardware addresses
+TEST_F(DSCSqlLeaseMgrTest, testLease6Mac) {
+    testLease6MAC();
+}
+
+/// @brief Tests whether memfile can store and retrieve hardware addresses
+TEST_F(DSCSqlLeaseMgrTest, testLease6HWTypeAndSource) {
+    testLease6HWTypeAndSource();
+}
+
+/// @brief Check that the expired DHCPv4 leases can be retrieved.
+///
+/// This test adds a number of leases to the lease database and marks
+/// some of them as expired. Then it queries for expired leases and checks
+/// whether only expired leases are returned, and that they are returned in
+/// the order from most to least expired. It also checks that the lease
+/// which is marked as 'reclaimed' is not returned.
+TEST_F(DSCSqlLeaseMgrTest, getExpiredLeases4) {
+    testGetExpiredLeases4();
+}
+
+/// @brief Check that the expired DHCPv6 leases can be retrieved.
+///
+/// This test adds a number of leases to the lease database and marks
+/// some of them as expired. Then it queries for expired leases and checks
+/// whether only expired leases are returned, and that they are returned in
+/// the order from most to least expired. It also checks that the lease
+/// which is marked as 'reclaimed' is not returned.
+TEST_F(DSCSqlLeaseMgrTest, getExpiredLeases6) {
+    testGetExpiredLeases6();
+}
+
+/// @brief Check that expired reclaimed DHCPv6 leases are removed.
+TEST_F(DSCSqlLeaseMgrTest, deleteExpiredReclaimedLeases6) {
+    testDeleteExpiredReclaimedLeases6();
+}
+
+/// @brief Check that expired reclaimed DHCPv4 leases are removed.
+TEST_F(DSCSqlLeaseMgrTest, deleteExpiredReclaimedLeases4) {
+    testDeleteExpiredReclaimedLeases4();
+}
+
+}; // Of anonymous namespace
diff --git a/src/lib/dhcpsrv/testutils/dscsql_schema.cc b/src/lib/dhcpsrv/testutils/dscsql_schema.cc
new file mode 100644 (file)
index 0000000..579dd28
--- /dev/null
@@ -0,0 +1,62 @@
+// Copyright (C) 2015-2016 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <string>
+#include <cassandra.h>
+#include <dhcpsrv/dscsql_connection.h>
+#include <dhcpsrv/testutils/dscsql_schema.h>
+#include <gtest/gtest.h>
+
+#include <fstream>
+#include <sstream>
+#include <stdlib.h>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+const char* DSCSQL_VALID_TYPE = "type=cassandra";
+
+string
+validDSCSQLConnectionString() {
+    return (connectionString(DSCSQL_VALID_TYPE, VALID_NAME, VALID_HOST,
+                             VALID_USER, VALID_PASSWORD));
+}
+
+void destroyDSCSQLSchema(bool show_err) {
+    runDSCSQLScript(DATABASE_SCRIPTS_DIR, "dscsql/dhcpdb_drop.cql", show_err);
+}
+
+void createDSCSQLSchema(bool show_err) {
+    runDSCSQLScript(DATABASE_SCRIPTS_DIR, "dscsql/dhcpdb_create.cql",
+                   show_err);
+}
+
+void runDSCSQLScript(const std::string& path, const std::string& script_name,
+                    bool show_err) {
+    std::ostringstream cmd;
+    cmd << "cqlsh -u keatest -p keatest -k keatest -f";
+    if (!show_err) {
+        cmd << " 2>/dev/null ";
+    }
+
+    if (!path.empty()) {
+        cmd << " < " << path << "/";
+    }
+
+    cmd << script_name;
+
+    int retval = ::system(cmd.str().c_str());
+    ASSERT_EQ(0, retval) << "runDSCSQLSchema failed:" << cmd.str();
+}
+
+
+};
+};
+};
diff --git a/src/lib/dhcpsrv/testutils/dscsql_schema.h b/src/lib/dhcpsrv/testutils/dscsql_schema.h
new file mode 100644 (file)
index 0000000..1f927c1
--- /dev/null
@@ -0,0 +1,68 @@
+// Copyright (C) 2015-2016 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef TEST_DSCSQL_SCHEMA_H
+#define TEST_DSCSQL_SCHEMA_H
+
+#include <config.h>
+#include <dhcpsrv/testutils/schema.h>
+#include <string>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+extern const char* DSCSQL_VALID_TYPE;
+
+/// Return valid connection string
+///
+/// @return valid DSCSQL connection string.
+std::string validDSCSQLConnectionString();
+
+/// @brief Clear everything from the database
+///
+/// Submits the current schema drop script:
+///
+///  <TEST_ADMIN_SCRIPTS_DIR>/dscsql/dhcpdb_drop.cql
+///
+/// to the unit test DSCSQL database. If the script fails, the invoking test
+/// will fail. The output of stderr is suppressed unless the parameter,
+/// show_err is true.
+///
+/// @param show_err flag which governs whether or not stderr is suppressed.
+void destroyDSCSQLSchema(bool show_err = false);
+
+/// @brief Create the DSCSQL Schema
+///
+/// Submits the current schema creation script:
+///
+///  <TEST_ADMIN_SCRIPTS_DIR>/dscsql/dhcpdb_create.cql
+///
+/// to the unit test DSCSQL database. If the script fails, the invoking test
+/// will fail. The output of stderr is suppressed unless the parameter,
+/// show_err is true.
+///
+/// @param show_err flag which governs whether or not stderr is suppressed.
+void createDSCSQLSchema(bool show_err = false);
+
+/// @brief Run a DSCSQL SQL script against the DSCSQL unit test database
+///
+/// Submits the given SQL script to DSCSQL via cqlsh CLI. The output of
+/// stderr is suppressed unless the parameter, show_err is true.  The is done
+/// to suppress warnings that might otherwise make test output needlessly
+/// noisy.  A gtest assertion occurs if the script fails to execute.
+///
+/// @param path - path (if not blank) of the script to execute
+/// @param script_name - file name of the path to execute
+/// @param show_err flag which governs whether or not stderr is suppressed.
+void runDSCSQLScript(const std::string& path, const std::string& script_name,
+                    bool show_err);
+
+};
+};
+};
+
+#endif
diff --git a/src/share/database/scripts/dscsql/.gitignore b/src/share/database/scripts/dscsql/.gitignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/share/database/scripts/dscsql/Makefile.am b/src/share/database/scripts/dscsql/Makefile.am
new file mode 100644 (file)
index 0000000..fd2831b
--- /dev/null
@@ -0,0 +1,6 @@
+SUBDIRS = .
+
+sqlscriptsdir = ${datarootdir}/${PACKAGE_NAME}/scripts/dscsql
+sqlscripts_DATA = dhcpdb_create.cql
+
+EXTRA_DIST = ${sqlscripts_DATA}
diff --git a/src/share/database/scripts/dscsql/dhcpdb_create.cql b/src/share/database/scripts/dscsql/dhcpdb_create.cql
new file mode 100644 (file)
index 0000000..c8924ca
--- /dev/null
@@ -0,0 +1,213 @@
+-- Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+
+-- Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+
+--           http://www.apache.org/licenses/LICENSE-2.0
+
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+-- This is the Kea schema specification for DataStax Cassandra SQL.
+
+-- The schema is reasonably portable (with the exception of the engine
+-- specification, which is Datastax Cassandra SQL-specific).  Minor changes might be needed for
+-- other databases.
+
+-- To create the schema, either type the command:
+
+-- cqlsh -u <user> -p <password> -k <database> -f dhcpdb_create.cql
+
+-- ... at the command prompt, or log in to the DSC SQL database and at the "cqlsh>"
+-- prompt, issue the command:
+
+-- SOURCE dhcpdb_create.cql
+
+-- This script is also called from kea-admin, see kea-admin init dscsql
+
+-- Over time, Kea database schema will evolve. Each version is marked with
+-- major.minor version. This file is organized sequentially, i.e. database
+-- is initialized to 1.0, then upgraded to 2.0 etc. This may be somewhat
+-- sub-optimal, but it ensues consistency with upgrade scripts. (It is much
+-- easier to maintain init and upgrade scripts if they look the same).
+-- Since initialization is done only once, it's performance is not an issue.
+
+-- This line starts database initialization to 1.0.
+
+-- Holds the IPv4 leases.
+CREATE TABLE lease4 (
+    address int,
+    hwaddr blob,
+    client_id blob,
+    valid_lifetime bigint,
+    expire bigint,
+    subnet_id int,
+    fqdn_fwd boolean,
+    fqdn_rev boolean,
+    hostname varchar,
+    state int,
+    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);
+
+-- Holds the IPv6 leases.
+-- N.B. The use of a VARCHAR for the address is temporary for development:
+-- it will eventually be replaced by BINARY(16).
+CREATE TABLE lease6 (
+    address varchar,
+    duid blob,
+    valid_lifetime bigint,
+    expire bigint,
+    subnet_id int,
+    pref_lifetime bigint,
+    lease_type int,
+    iaid int,
+    prefix_len int,
+    fqdn_fwd boolean,
+    fqdn_rev boolean,
+    hostname varchar,
+    hwaddr blob,
+    hwtype int,
+    hwaddr_source int,
+    state int,
+    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);
+
+-- ... 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
+-- type names, they can join this table with the lease6 table.
+-- Make sure those values match Lease6::LeaseType enum (see src/bin/dhcpsrv/
+-- lease_mgr.h)
+CREATE TABLE lease6_types (
+    lease_type int,                             -- Lease type code.
+    name varchar,                               -- Name of the lease type
+    PRIMARY KEY (lease_type)
+);
+--START TRANSACTION;
+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
+INSERT INTO lease6_types (lease_type, name) VALUES (2, 'IA_PD');   -- Prefix delegations
+--COMMIT;
+
+-- Kea keeps track of the hardware/MAC address source, i.e. how the address
+-- was obtained. Depending on the technique and your network topology, it may
+-- be more or less trustworthy. This table is a convenience for
+-- users of the database - if they want to view the lease table and use the
+-- type names, they can join this table with the lease6 table. For details,
+-- see constants defined in src/lib/dhcp/dhcp/pkt.h for detailed explanation.
+CREATE TABLE lease_hwaddr_source (
+    hwaddr_source int,
+    name varchar,
+    PRIMARY KEY (hwaddr_source)
+);
+
+-- Hardware address obtained from raw sockets
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (1, 'HWADDR_SOURCE_RAW');
+
+-- Hardware address converted from IPv6 link-local address with EUI-64
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (2, 'HWADDR_SOURCE_IPV6_LINK_LOCAL');
+
+-- Hardware address extracted from client-id (duid)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (4, 'HWADDR_SOURCE_DUID');
+
+-- Hardware address extracted from client address relay option (RFC6939)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (8, 'HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION');
+
+-- Hardware address extracted from remote-id option (RFC4649)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (16, 'HWADDR_SOURCE_REMOTE_ID');
+
+-- Hardware address extracted from subscriber-id option (RFC4580)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (32, 'HWADDR_SOURCE_SUBSCRIBER_ID');
+
+-- Hardware address extracted from docsis options
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (64, 'HWADDR_SOURCE_DOCSIS_CMTS');
+
+-- -----------------------------------------------------
+-- Table `dhcp4_options`
+-- -----------------------------------------------------
+CREATE TABLE dhcp4_options (
+    option_id int,
+    code int,
+    value blob,
+    formatted_value varchar,
+    space varchar,
+    persistent int,
+    dhcp_client_class varchar,
+    dhcp4_subnet_id int,
+    host_id int,
+    PRIMARY KEY (option_id)
+);
+
+-- Create search indexes for dhcp4_options table
+CREATE INDEX dhcp4_optionsindex1 ON dhcp4_options (host_id);
+
+-- -----------------------------------------------------
+-- Table `dhcp6_options`
+-- -----------------------------------------------------
+CREATE TABLE dhcp6_options (
+    option_id int,
+    code int,
+    value blob,
+    formatted_value varchar,
+    space varchar,
+    persistent int,
+    dhcp_client_class varchar,
+    dhcp6_subnet_id int,
+    host_id int,
+    PRIMARY KEY (option_id)
+);
+
+-- Create search indexes for dhcp6_options table
+CREATE INDEX dhcp6_optionsindex1 ON dhcp6_options (host_id);
+
+-- Create table holding mapping of the lease states to their names.
+-- This is not used in queries from the DHCP server but rather in
+-- direct queries from the lease database management tools.
+CREATE TABLE lease_state (
+    state int,
+    name varchar,
+    PRIMARY KEY (state)
+);
+
+-- Insert currently defined state names.
+INSERT INTO lease_state (state, name) VALUES (0, 'default');
+INSERT INTO lease_state (state, name) VALUES (1, 'declined');
+INSERT INTO lease_state (state, name) VALUES (2, 'expired-reclaimed');
+
+-- Finally, the version of the schema.  We start at 1.0 during development.
+-- 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 in step with src/lib/dhcpsrv/tests/schema_copy.h,
+--       which defines the schema for the unit tests.  If you are updating
+--       the version number, the schema has changed: please ensure that
+--       schema_copy.h has been updated as well.
+CREATE TABLE schema_version (
+    version int,
+    minor int,
+    PRIMARY KEY (version)
+);
+--START TRANSACTION;
+INSERT INTO schema_version (version, minor) VALUES (1, 0);
+--COMMIT;
diff --git a/src/share/database/scripts/dscsql/dhcpdb_drop.cql b/src/share/database/scripts/dscsql/dhcpdb_drop.cql
new file mode 100755 (executable)
index 0000000..7ec86fa
--- /dev/null
@@ -0,0 +1,27 @@
+-- Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+
+-- Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+
+--           http://www.apache.org/licenses/LICENSE-2.0
+
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+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;