]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
Cassandra update
authorAndrei Pavel <andrei.pavel@qualitance.com>
Wed, 14 Dec 2016 12:47:57 +0000 (14:47 +0200)
committerAndrei Pavel <andrei.pavel@qualitance.com>
Wed, 14 Dec 2016 13:48:43 +0000 (15:48 +0200)
Replaced unrecommended backticks with $() in cql_version() in bash scripts.

*_execute() and *_execute_script() functions from src/bin/admin/admin-utils.sh now pass the parameters to the underlying backend binary whenever they are given rather than when there are 2 or more.

Corrected cql_version() return error in src/bin/admin/admin-utils.sh.

Removed redundant "USE" from cql_init() in src/bin/admin/kea-admin.in.

Inserted a newline in src/bin/admin/tests/Makefile.am to separate unrelated targets.

Style changes in cql_*_test() functions in src/bin/admin/tests/cql_tests.sh.in.

src/bin/admin/tests/dhcpdb_create_1.0.cql:
    "perfromance" typo
    Added comment headers
    Added index on expire since it is used in WHERE clauses (further performance testing may be required)
    Removed dhcp4_options and dhcp6_options table since they are not required for Cassandra

Added DROP INDEX in src/share/database/scripts/cql/dhcpdb_drop.cql.

Added sql_common.h
Added cql_exchange.h and cql_exchange.cc which mediate communication with Cassandra.
Added cql_lease_mgr.h and cql_lease_mgr.cc

Parameterized reconnect-wait-time, connect-timeout, request-timeout, tcp-keepalive, tcp-nodelay for Cassandra in kea.conf. Changes are in src/lib/dhcpsrv/cql_connection.cc and src/lib/dhcpsrv/parsers/dbaccess_parser.cc.

Reformated x != NULL into !x as specified in the Kea style guidelines

src/lib/dhcpsrv/cql_connection.cc:
    Added range check for port
    Added CqlConnection:setConsistency
    Added CqlConnection::startTransaction  which is a noop
    Added CqlTransaction method implementations.
    Corrected ending brace of namespace declaration, it doesn't need semicolon.

src/lib/dhcpsrv/cql_connection.h:
    Added explicit on CqlConnection constructor. Unlikely that this class will ever be derived, but it's good practice.
    Changed some comments.
    Added CqlTransaction class definition.

src/lib/dhcpsrv/cql_lease_mgr.cc:
    Formatted the entire code.
    Changed data types to cass_ types.

Added some log messages.

Moved structs, enums and typedefs from src/lib/dhcpsrv/lease_mgr.h to src/lib/dhcpsrv/sql_common.h

Added some missing tests in src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc

19 files changed:
src/bin/admin/admin-utils.sh
src/bin/admin/kea-admin.in
src/bin/admin/tests/Makefile.am
src/bin/admin/tests/cql_tests.sh.in
src/bin/admin/tests/dhcpdb_create_1.0.cql
src/lib/dhcpsrv/Makefile.am
src/lib/dhcpsrv/cql_connection.cc
src/lib/dhcpsrv/cql_connection.h
src/lib/dhcpsrv/cql_exchange.cc [new file with mode: 0644]
src/lib/dhcpsrv/cql_exchange.h [new file with mode: 0644]
src/lib/dhcpsrv/cql_lease_mgr.cc
src/lib/dhcpsrv/cql_lease_mgr.h
src/lib/dhcpsrv/dhcpsrv_messages.mes
src/lib/dhcpsrv/lease_mgr.h
src/lib/dhcpsrv/parsers/dbaccess_parser.cc
src/lib/dhcpsrv/sql_common.h [new file with mode: 0644]
src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc
src/share/database/scripts/cql/dhcpdb_create.cql
src/share/database/scripts/cql/dhcpdb_drop.cql

index 3df7d4fc4efde98f5f18cfdd82992253c403e9bc..aa2736a057ba834efa19462e8606aec355ea038e 100755 (executable)
@@ -18,7 +18,7 @@
 mysql_execute() {
     QUERY=$1
     shift
-    if [ $# -gt 1 ]; then
+    if [ $# -ge 1 ]; then
         mysql -N -B  $* -e "${QUERY}"
         retcode=$?
     else
@@ -47,7 +47,7 @@ mysql_version() {
 pgsql_execute() {
     QUERY=$1
     shift
-    if [ $# -gt 0 ]; then
+    if [ $# -ge 1 ]; then
         echo $QUERY | psql --set ON_ERROR_STOP=1 -A -t -h localhost -q $*
         retcode=$?
     else
@@ -71,7 +71,7 @@ pgsql_execute() {
 pgsql_execute_script() {
     file=$1
     shift
-    if [ $# -gt 0 ]; then
+    if [ $# -ge 1 ]; then
         psql --set ON_ERROR_STOP=1 -A -t -h localhost -q -f $file $*
         retcode=$?
     else
@@ -90,8 +90,8 @@ pgsql_version() {
 cql_execute() {
     query=$1
     shift
-    if [ $# -gt 1 ]; then
-        cqlsh $* -e "$query"
+    if [ $# -ge 1 ]; then
+        cqlsh "$@" -e "$query"
         retcode=$?
     else
         cqlsh -u $db_user -p $db_password -k $db_name -e "$query"
@@ -109,8 +109,8 @@ cql_execute() {
 cql_execute_script() {
     file=$1
     shift
-    if [ $# -gt 1 ]; then
-        cqlsh $* -e "$file"
+    if [ $# -ge 1 ]; then
+        cqlsh "$@" -f "$file"
         retcode=$?
     else
         cqlsh -u $db_user -p $db_password -k $db_name -f "$file"
@@ -126,8 +126,9 @@ cql_execute_script() {
 }
 
 cql_version() {
-    version=`cql_execute "SELECT version, minor FROM schema_version" "$@"`
-    version=`echo "$version" | grep -A 1 "+" | grep -v "+" | tr -d ' ' | cut -d "|" -f 1-2 --output-delimiter="."`
-    echo $version
-    return $?
+  version=$(cql_execute "SELECT version, minor FROM schema_version" "$@")
+  error=$?
+  version=$(echo "$version" | grep -A 1 "+" | grep -v "+" | tr -d ' ' | cut -d "|" -f 1-2 --output-delimiter=".")
+  echo "$version"
+  return $error
 }
index 32a27a315491484bfbe618a0e6bd5b491ef2a89e..01310fd67de63a5c9ae6a411963870dafa6e14f8 100644 (file)
@@ -203,8 +203,8 @@ pgsql_init() {
 cql_init() {
     printf "Checking if there is a database initialized already... Please ignore errors.\n"
 
-    result=`cql_execute "USE $db_name; DESCRIBE tables;"`
-    if [ "$result"="<empty>" ]; then
+    result=$(cql_execute "DESCRIBE tables;")
+    if [ $(echo "$result" | grep "<empty>" | wc -w) -gt 0 ]; then
         printf "Creating and initializing tables using script %s...\n" $scripts_dir/cql/dhcpdb_create.cql
         cql_execute_script $scripts_dir/cql/dhcpdb_create.cql
     else
@@ -212,8 +212,8 @@ cql_init() {
         exit 2
     fi
 
-    version=`cql_version`
-    printf "Lease DB version reported after initialization: $version\n"
+    version=$(cql_version)
+    printf "Lease DB version reported after initialization: %s\n" "$version"
 
     exit 0
 }
index ea0f4370e69ca0afbaa8ea40bbf15e4c2466f063..8092f27bd9e9e4d2fa429179a007b2892c140e19 100644 (file)
@@ -13,6 +13,7 @@ endif
 if HAVE_CQL
 SHTESTS += cql_tests.sh
 endif
+
 noinst_SCRIPTS = $(SHTESTS)
 
 EXTRA_DIST = dhcpdb_create_1.0.mysql
index 80673d1774c0cfaffa5f7a4cf467c662fd3f899a..6d282b0683e981626a85b06c287eb25e6f1e9c89 100644 (file)
@@ -44,8 +44,7 @@ cql_lease_init_test() {
     assert_eq 0 $? "lease4 table check failed, expected exit code: %d, actual: %d"
 
     # Check lease6 table
-    cql_execute "SELECT address, duid, valid_lifetime, expire, subnet_id, pref_lifetime, lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname,\
-     state FROM lease6;"
+    cql_execute "SELECT address, duid, valid_lifetime, expire, subnet_id, pref_lifetime, lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, state FROM lease6;"
     assert_eq 0 $? "lease6 table check failed, expected exit code: %d, actual: %d"
 
     # Check lease6_types table
@@ -83,7 +82,7 @@ cql_lease_version_test() {
 
     # Verfiy that kea-admin lease-version returns the correct version.
     version=$($keaadmin lease-version cql -u $db_user -p $db_password -n $db_name)
-    assert_str_eq "1.0" $version "Expected kea-admin to return %s, returned value was %s"
+    assert_str_eq "2.0" $version "Expected kea-admin to return %s, returned value was %s"
 
     # Wipe the database.
     cql_execute_script $db_scripts_dir/cql/dhcpdb_drop.cql
@@ -151,20 +150,20 @@ cql_lease4_dump_test() {
     # 1430694930 corresponds to 2015-04-04 01:15:30
     # 1433464245 corresponds to 2015-05-05 02:30:45
     # 1436173267 corresponds to 2015-06-06 11:01:07
-    insert_sql="\
-insert into lease4(address, hwaddr, client_id, valid_lifetime, expire, subnet_id,\
+    insert_cql="\
+INSERT INTO lease4(address, hwaddr, client_id, valid_lifetime, expire, subnet_id,\
  fqdn_fwd, fqdn_rev, hostname, state)\
values(-1073741302,textAsBlob('20'),textAsBlob('30'),40,1430694930,50,true,true,\
VALUES(-1073741302,textAsBlob('20'),textAsBlob('30'),40,1430694930,50,true,true,\
  'one.example.com', 0);\
-insert into lease4(address, hwaddr, client_id, valid_lifetime, expire, subnet_id,\
+INSERT INTO lease4(address, hwaddr, client_id, valid_lifetime, expire, subnet_id,\
  fqdn_fwd, fqdn_rev, hostname, state)\
values(-1073741301,NULL,textAsBlob('123'),40,1433464245,50,true,true,'', 1);\
-insert into lease4(address, hwaddr, client_id, valid_lifetime, expire, subnet_id,\
VALUES(-1073741301,NULL,textAsBlob('123'),40,1433464245,50,true,true,'', 1);\
+INSERT INTO lease4(address, hwaddr, client_id, valid_lifetime, expire, subnet_id,\
  fqdn_fwd, fqdn_rev, hostname, state)\
values(-1073741300,textAsBlob('22'),NULL,40,1436173267,50,true,true,\
VALUES(-1073741300,textAsBlob('22'),NULL,40,1436173267,50,true,true,\
  'three.example.com', 2);"
 
-    cql_execute "$insert_sql"
+    cql_execute "$insert_cql"
     assert_eq 0 $? "insert into lease4 failed, expected exit code %d, actual %d"
 
     # Dump lease4 to output_file.
@@ -220,24 +219,24 @@ cql_lease6_dump_test() {
     # 1430694930 corresponds to 2015-04-04 01:15:30
     # 1433464245 corresponds to 2015-05-05 02:30:45
     # 1436173267 corresponds to 2015-06-06 11:01:07
-    insert_sql="\
-insert into lease6(address, duid, valid_lifetime, expire, subnet_id,\
+    insert_cql="\
+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('2001:db8::10',textAsBlob('20'),30,1430694930,40,50,1,60,70,true,true,\
VALUES('2001:db8::10',textAsBlob('20'),30,1430694930,40,50,1,60,70,true,true,\
  'one.example.com',textAsBlob('80'),90,16,0);\
-insert into lease6(address, duid, valid_lifetime, expire, subnet_id,\
+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('2001:db8::11',NULL,30,1433464245,40,50,1,60,70,true,true,\
VALUES('2001:db8::11',NULL,30,1433464245,40,50,1,60,70,true,true,\
  '',textAsBlob('80'),90,1,1);\
-insert into lease6(address, duid, valid_lifetime, expire, subnet_id,\
+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('2001:db8::12',textAsBlob('21'),30,1436173267,40,50,1,60,70,true,true,\
VALUES('2001:db8::12',textAsBlob('21'),30,1436173267,40,50,1,60,70,true,true,\
  'three.example.com',textAsBlob('80'),90,4,2);"
 
-    cql_execute "$insert_sql"
+    cql_execute "$insert_cql"
     assert_eq 0 $? "insert into lease6 failed, expected exit code %d, actual %d"
 
     # Dump lease4 to output_file.
index e015a260f353485f839e1098212cb34e13679fd3..2d6c45a93a573dd612c06c227a5a1aaf79cccfc4 100644 (file)
 -- 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.
+-- 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.
+-- -----------------------------------------------------
+-- Table `lease4`
+-- -----------------------------------------------------
 CREATE TABLE lease4 (
     address int,
     hwaddr blob,
@@ -61,11 +64,15 @@ CREATE TABLE lease4 (
 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);
+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).
+-- -----------------------------------------------------
+-- Table `lease6`
+-- -----------------------------------------------------
 CREATE TABLE lease6 (
     address varchar,
     duid blob,
@@ -91,13 +98,17 @@ 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);
+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)
+-- -----------------------------------------------------
+-- Table `lease6_types`
+-- -----------------------------------------------------
 CREATE TABLE lease6_types (
     lease_type int,                             -- Lease type code.
     name varchar,                               -- Name of the lease type
@@ -107,13 +118,15 @@ INSERT INTO lease6_types (lease_type, name) VALUES (0, 'IA_NA');   -- Non-tempor
 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
 
-
 -- 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.
+-- -----------------------------------------------------
+-- Table `lease_hwaddr_source`
+-- -----------------------------------------------------
 CREATE TABLE lease_hwaddr_source (
     hwaddr_source int,
     name varchar,
@@ -141,47 +154,12 @@ INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (32, 'HWADDR_SOURCE
 -- 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.
+-- -----------------------------------------------------
+-- Table `lease_state`
+-- -----------------------------------------------------
 CREATE TABLE lease_state (
     state int,
     name varchar,
@@ -197,11 +175,18 @@ INSERT INTO lease_state (state, name) VALUES (2, 'expired-reclaimed');
 -- This table is only modified during schema upgrades.  For historical reasons
 -- (related to the names of the columns in the BIND 10 DNS database file), the
 -- first column is called "version" and not "major".
--- Note: This MUST be kept in step with src/share/database/scripts/cassandra/dhcpdb_create.cql,
--- which defines the schema for the unit tests.
+-- Note: This MUST be kept synchronized with
+-- src/share/database/scripts/cql/dhcpdb_create.cql which defines the schema for
+-- the unit tests.
+-- -----------------------------------------------------
+-- Table `schema_version`
+-- -----------------------------------------------------
 CREATE TABLE schema_version (
     version int,
     minor int,
     PRIMARY KEY (version)
 );
+
 INSERT INTO schema_version (version, minor) VALUES (1, 0);
+
+-- This line concludes database initalization to version 1.0.
index 6c8f48bb0f3e2b1eba56f72a4be41f65f578b1ac..b62f9231371a1c5b8623c3abd9bac84a58599a57 100644 (file)
@@ -124,6 +124,7 @@ libkea_dhcpsrv_la_SOURCES += logging.cc logging.h
 libkea_dhcpsrv_la_SOURCES += logging_info.cc logging_info.h
 libkea_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h
 libkea_dhcpsrv_la_SOURCES += memfile_lease_storage.h
+libkea_dhcpsrv_la_SOURCES += sql_common.h
 
 if HAVE_MYSQL
 libkea_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h
@@ -139,10 +140,13 @@ libkea_dhcpsrv_la_SOURCES += pgsql_exchange.cc pgsql_exchange.h
 libkea_dhcpsrv_la_SOURCES += pgsql_host_data_source.cc pgsql_host_data_source.h
 libkea_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h
 endif
+
 if HAVE_CQL
-libkea_dhcpsrv_la_SOURCES += cql_lease_mgr.cc cql_lease_mgr.h
 libkea_dhcpsrv_la_SOURCES += cql_connection.cc cql_connection.h
+libkea_dhcpsrv_la_SOURCES += cql_exchange.cc cql_exchange.h
+libkea_dhcpsrv_la_SOURCES += cql_lease_mgr.cc cql_lease_mgr.h
 endif
+
 libkea_dhcpsrv_la_SOURCES += pool.cc pool.h
 libkea_dhcpsrv_la_SOURCES += srv_config.cc srv_config.h
 libkea_dhcpsrv_la_SOURCES += subnet.cc subnet.h
@@ -236,5 +240,3 @@ libkea_dhcpsrv_include_HEADERS = \
 
 install-data-local:
        $(mkinstalldirs) $(DESTDIR)$(dhcp_data_dir)
-
-
index 1dec234ebf53133a768c44aeee22446c47575f5f..b4e20c78567e4f1a537550ee40e81ee5353827e9 100644 (file)
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <config.h>
+
 #include <dhcpsrv/cql_connection.h>
-#include <string>
+#include <dhcpsrv/cql_exchange.h>
+
+#include <boost/shared_ptr.hpp>
 
 using namespace std;
 
 namespace isc {
 namespace dhcp {
 
-CqlConnection::CqlConnection(const ParameterMap& parameters) :
-        DatabaseConnection(parameters), cluster_(NULL), session_(NULL),
-        tagged_statements_(NULL) {
+CqlConnection::CqlConnection(const ParameterMap& parameters)
+    : DatabaseConnection(parameters), cluster_(NULL), session_(NULL),
+      force_consistency_(true), consistency_(CASS_CONSISTENCY_QUORUM),
+      tagged_statements_(NULL) {
 }
 
 CqlConnection::~CqlConnection() {
-    // Free up the prepared statements, ignoring errors.
-    // Session and connection resources are deallocated.
+    // Free up the prepared statements, ignoring errors. Session and connection
+    // resources are deallocated.
     CassError rc = CASS_OK;
     std::string error;
 
@@ -43,7 +48,8 @@ CqlConnection::~CqlConnection() {
     if (session_) {
         CassFuture* close_future = cass_session_close(session_);
         cass_future_wait(close_future);
-        checkStatementError(error, close_future, "could not close connection to DB");
+        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_);
@@ -55,10 +61,9 @@ CqlConnection::~CqlConnection() {
         cluster_ = NULL;
     }
 
-    // We're closing the connection anyway. Let's not throw at this
-    // stage
     if (rc != CASS_OK) {
-        isc_throw(DbOpenError, error);
+        // We're closing the connection anyway. Let's not throw at this stage.
+        LOG_ERROR(dhcpsrv_logger, DHCPSRV_CQL_DEALLOC_ERROR).arg(error);
     }
 }
 
@@ -67,25 +72,25 @@ CqlConnection::openDatabase() {
     CassError rc;
     // Set up the values of the parameters
     const char* contact_points = "127.0.0.1";
-    string scontact_points;
+    std::string scontact_points;
     try {
         scontact_points = getParameter("contact_points");
         contact_points = scontact_points.c_str();
     } catch (...) {
-        // No host. Fine, we'll use "localhost".
+        // No host. Fine, we'll use "127.0.0.1".
     }
 
     const char* port = NULL;
-    string sport;
+    std::string sport;
     try {
         sport = getParameter("port");
         port = sport.c_str();
     } catch (...) {
-        // No port. Fine, we'll use "default".
+        // No port. Fine, we'll use the default "9042".
     }
 
     const char* user = NULL;
-    string suser;
+    std::string suser;
     try {
         suser = getParameter("user");
         user = suser.c_str();
@@ -94,7 +99,7 @@ CqlConnection::openDatabase() {
     }
 
     const char* password = NULL;
-    string spassword;
+    std::string spassword;
     try {
         spassword = getParameter("password");
         password = spassword.c_str();
@@ -103,36 +108,170 @@ CqlConnection::openDatabase() {
     }
 
     const char* keyspace = "keatest";
-    string skeyspace;
+    std::string skeyspace;
     try {
         skeyspace = getParameter("keyspace");
         keyspace = skeyspace.c_str();
     } catch (...) {
-        // No keyspace name. Fine, we'll use default "keatest".
+        // No keyspace name. Fine, we'll use "keatest".
+    }
+
+    const char* reconnect_wait_time = NULL;
+    std::string sreconnect_wait_time;
+    try {
+        sreconnect_wait_time = getParameter("reconnect-wait-time");
+        reconnect_wait_time = sreconnect_wait_time.c_str();
+    } catch (...) {
+        // No reconnect wait time. Fine, we'll use the default "2000".
+    }
+
+    const char* connect_timeout = NULL;
+    std::string sconnect_timeout;
+    try {
+        sconnect_timeout = getParameter("connect-timeout");
+        connect_timeout = sconnect_timeout.c_str();
+    } catch (...) {
+        // No connect timeout. Fine, we'll use the default "5000".
+    }
+
+    const char* request_timeout = NULL;
+    std::string srequest_timeout;
+    try {
+        srequest_timeout = getParameter("request-timeout");
+        request_timeout = srequest_timeout.c_str();
+    } catch (...) {
+        // No request timeout. Fine, we'll use the default "12000".
+    }
+
+    const char* tcp_keepalive = NULL;
+    std::string stcp_keepalive;
+    try {
+        stcp_keepalive = getParameter("tcp-keepalive");
+        tcp_keepalive = stcp_keepalive.c_str();
+    } catch (...) {
+        // No tcp-keepalive. Fine, we'll not use TCP keepalive.
+    }
+
+    std::string stcp_nodelay;
+    try {
+        stcp_nodelay = getParameter("tcp-nodelay");
+    } catch (...) {
+        // No tcp-nodelay. Fine, we'll use the default false.
     }
 
     cluster_ = cass_cluster_new();
     cass_cluster_set_contact_points(cluster_, contact_points);
 
-    if (user != NULL && password != NULL) {
+    if (user && password) {
         cass_cluster_set_credentials(cluster_, user, password);
     }
 
-    if (port != NULL) {
+    if (port) {
         int port_number;
         try {
             port_number = boost::lexical_cast<int>(port);
-        } catch (const std::exception& ex) {
-            isc_throw(DbOperationError, "Invalid int data: " << port
-                      << " : " << ex.what());
+            if (port_number < 1 || port_number > 65535) {
+                isc_throw(
+                    DbOperationError,
+                    "Port outside of range, expected 1-65535, instead got "
+                        << port);
+            }
+        } catch (const boost::bad_lexical_cast& ex) {
+            isc_throw(DbOperationError,
+                      "Invalid port, castable to int expected, instead got \""
+                          << port << "\", " << ex.what());
         }
         cass_cluster_set_port(cluster_, port_number);
     }
 
+    if (reconnect_wait_time) {
+        int reconnect_wait_time_number;
+        try {
+            reconnect_wait_time_number =
+                boost::lexical_cast<int>(reconnect_wait_time);
+            if (reconnect_wait_time_number < 0) {
+                isc_throw(DbOperationError,
+                          "Invalid reconnect wait time, positive number "
+                          "expected, instead got "
+                              << reconnect_wait_time);
+            }
+        } catch (const boost::bad_lexical_cast& ex) {
+            isc_throw(DbOperationError, "Invalid reconnect wait time, castable "
+                                        "to int expected, instead got \""
+                                            << reconnect_wait_time << "\", "
+                                            << ex.what());
+        }
+        cass_cluster_set_reconnect_wait_time(cluster_,
+                                             reconnect_wait_time_number);
+    }
+
+    if (connect_timeout) {
+        int connect_timeout_number;
+        try {
+            connect_timeout_number = boost::lexical_cast<int>(connect_timeout);
+            if (connect_timeout_number < 0) {
+                isc_throw(DbOperationError,
+                          "Invalid connect timeout, positive number expected, "
+                          "instead got "
+                              << connect_timeout);
+            }
+        } catch (const boost::bad_lexical_cast& ex) {
+            isc_throw(DbOperationError, "Invalid connect timeout, castable to "
+                                        "int expected, instead got \""
+                                            << connect_timeout << "\", "
+                                            << ex.what());
+        }
+        cass_cluster_set_connect_timeout(cluster_, connect_timeout_number);
+    }
+
+    if (request_timeout) {
+        int request_timeout_number;
+        try {
+            request_timeout_number = boost::lexical_cast<int>(request_timeout);
+            if (request_timeout_number < 0) {
+                isc_throw(DbOperationError,
+                          "Invalid request timeout, positive number expected, "
+                          "instead got "
+                              << request_timeout);
+            }
+        } catch (const boost::bad_lexical_cast& ex) {
+            isc_throw(DbOperationError, "Invalid request timeout, castable to "
+                                        "int expected, instead got \""
+                                            << request_timeout << "\", "
+                                            << ex.what());
+        }
+        cass_cluster_set_request_timeout(cluster_, request_timeout_number);
+    }
+
+    if (tcp_keepalive) {
+        int tcp_keepalive_number;
+        try {
+            tcp_keepalive_number = boost::lexical_cast<int>(tcp_keepalive);
+            if (tcp_keepalive_number < 0) {
+                isc_throw(DbOperationError,
+                          "Invalid TCP keepalive, positive number expected, "
+                          "instead got "
+                              << tcp_keepalive);
+            }
+        } catch (const boost::bad_lexical_cast& ex) {
+            isc_throw(
+                DbOperationError,
+                "Invalid TCP keepalive, castable to int expected, instead got "
+                "\"" << tcp_keepalive
+                     << "\", " << ex.what());
+        }
+        cass_cluster_set_tcp_keepalive(cluster_, cass_true,
+                                       tcp_keepalive_number);
+    }
+
+    if (stcp_nodelay == "true") {
+        cass_cluster_set_tcp_nodelay(cluster_, cass_true);
+    }
+
     session_ = cass_session_new();
 
-    CassFuture* connect_future = cass_session_connect_keyspace(session_,
-        cluster_, keyspace);
+    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");
@@ -148,11 +287,12 @@ CqlConnection::openDatabase() {
 }
 
 void
-CqlConnection::prepareStatements(CqlTaggedStatement *statements) {
+CqlConnection::prepareStatements(CqlTaggedStatementstatements) {
     CassError rc = CASS_OK;
     uint32_t size = 0;
     tagged_statements_ = statements;
-    for (; tagged_statements_[size].params_; size++);
+    for (; tagged_statements_[size].params_; size++) {
+    }
     statements_.resize(size);
     for (uint32_t i = 0; i < size; i++) {
         const char* query = tagged_statements_[i].text_;
@@ -174,6 +314,19 @@ CqlConnection::prepareStatements(CqlTaggedStatement *statements) {
     }
 }
 
+void
+CqlConnection::setConsistency(bool force, CassConsistency consistency) {
+    force_consistency_ = force;
+    consistency_ = consistency;
+}
+
+void
+CqlConnection::startTransaction() {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_CQL_BEGIN_TRANSACTION);
+    // No-op
+}
+
 void
 CqlConnection::commit() {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_COMMIT);
@@ -185,27 +338,31 @@ CqlConnection::rollback() {
 }
 
 void
-CqlConnection::checkStatementError(std::string& error, CassFuture* future,
-                                   uint32_t stindex, const char* what) const {
+CqlConnection::checkStatementError(std::string& error,
+                                   CassFuture* future,
+                                   uint32_t stindex,
+                                   const char* what) const {
     CassError rc;
     const char* errorMessage;
     size_t errorMessageSize;
     std::stringstream stream;
-    stream << "no error for: " << tagged_statements_[stindex].name_;
+    stream << "no error for statement " << tagged_statements_[stindex].name_;
 
     rc = cass_future_error_code(future);
     cass_future_error_message(future, &errorMessage, &errorMessageSize);
 
     if (rc != CASS_OK) {
         stream.str(std::string());
-        stream << what << " for: " << tagged_statements_[stindex].name_
-               << " reason: " << errorMessage << " error code: " << rc;
+        stream << what << " for statement " << tagged_statements_[stindex].name_
+               << ". Future error: " << errorMessage
+               << ". Error description: " << cass_error_desc(rc);
     }
     error = stream.str();
 }
 
 void
-CqlConnection::checkStatementError(std::string& error, CassFuture* future,
+CqlConnection::checkStatementError(std::string& error,
+                                   CassFuture* future,
                                    const char* what) const {
     CassError rc;
     const char* errorMessage;
@@ -218,10 +375,29 @@ CqlConnection::checkStatementError(std::string& error, CassFuture* future,
 
     if (rc != CASS_OK) {
         stream.str(std::string());
-        stream << what << " reason: " << errorMessage << " error code: " << rc;
+        stream << what << ". Future error: " << errorMessage
+               << ". Error description: " << cass_error_desc(rc);
     }
     error = stream.str();
 }
 
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
+CqlTransaction::CqlTransaction(CqlConnection& conn)
+    : conn_(conn), committed_(false) {
+    conn_.startTransaction();
+}
+
+CqlTransaction::~CqlTransaction() {
+    // Rollback if commit() wasn't explicitly called.
+    if (!committed_) {
+        conn_.rollback();
+    }
+}
+
+void
+CqlTransaction::commit() {
+    conn_.commit();
+    committed_ = true;
+}
+
+}  // namespace dhcp
+}  // namespace isc
index 30bbeed1f0ff535f289a0a0fcb631e6ff9cd507b..7abcfbc5e3a4c98e30d3f15ed0aee85bd6c00c0a 100644 (file)
 
 #include <dhcpsrv/database_connection.h>
 #include <dhcpsrv/dhcpsrv_log.h>
-#include <inttypes.h>
+
 #include <cassandra.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <inttypes.h>
+
+#include <map>
+#include <string>
 #include <vector>
 
 namespace isc {
 namespace dhcp {
 
-/// @brief  Defines a single query
+/// @brief Defines a single statement
 ///
-/// @param params_ Bind parameter names
-/// @param name_ Short name of the query.
-/// @param text_ Text representation of the actual query.
+/// @param params_ parameter names
+/// @param name_ short description of the query
+/// @param text_ text representation of the actual query
 struct CqlTaggedStatement {
     const char** params_;
     const char* name_;
     const char* text_;
 };
 
-// Defines CQL backend version: 2.3
+// Define CQL backend version: 2.3
 const uint32_t CQL_DRIVER_VERSION_MAJOR = CASS_VERSION_MAJOR;
 const uint32_t CQL_DRIVER_VERSION_MINOR = CASS_VERSION_MINOR;
 
-/// Defines CQL schema version: 1.0
+/// Define CQL schema version: 1.0
 const uint32_t CQL_SCHEMA_VERSION_MAJOR = 1;
 const uint32_t CQL_SCHEMA_VERSION_MINOR = 0;
 
+/// @brief Common CQL connector pool
+///
+/// Provides common operations for the Cassandra database connection used by
+/// CqlLeaseMgr, CqlHostDataSource and CqlSrvConfigMgr. Manages the connection
+/// to the Cassandra database and preparing of compiled statements. Its fields
+/// are public because they are used (both set and retrieved) in classes that
+/// use instances of CqlConnection.
 class CqlConnection : public DatabaseConnection {
 public:
-
     /// @brief Constructor
     ///
     /// Initialize CqlConnection object with parameters needed for connection.
-    CqlConnection(const ParameterMap& parameters);
+    explicit CqlConnection(const ParameterMap& parameters);
 
     /// @brief Destructor
     virtual ~CqlConnection();
@@ -61,21 +74,27 @@ public:
     /// Creates the prepared statements for all of the CQL statements used
     /// by the CQL 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(CqlTaggedStatement *statements);
+    /// @throw isc::dhcp::DbOperationError if an operation on the open database
+    ///     has failed
+    /// @throw isc::InvalidParameter if there is an invalid access in the
+    ///     vector. This represents an internal error within the code.
+    void prepareStatements(CqlTaggedStatementstatements);
 
-    /// @brief Open Database
+    /// @brief Open database
     ///
     /// Opens the database using the information supplied in the parameters
     /// passed to the constructor. If no parameters are supplied, the default
-    /// values will be used (keyspace keatest).
+    /// values will be used (e.g. keyspace 'keatest', port 9042).
     ///
-    /// @throw DbOpenError Error opening the database
+    /// @throw DbOpenError error opening the database
     void openDatabase();
 
+    /// @brief Set consistency
+    void setConsistency(bool force, CassConsistency consistency);
+
+    /// @brief Start transaction
+    void startTransaction();
+
     /// @brief Commit Transactions
     ///
     /// This is a no-op for Cassandra.
@@ -86,17 +105,20 @@ public:
     /// This is a no-op for Cassandra.
     virtual void rollback();
 
-    /// @brief Check Error
+    /// @brief Check for errors
     ///
-    /// Chech error for current database operation.
-    void checkStatementError(std::string& error, CassFuture* future,
-        uint32_t stindex, const char* what) const;
+    /// Check for errors on the current database operation.
+    void checkStatementError(std::string& error,
+                             CassFuture* future,
+                             uint32_t stindex,
+                             const char* what) const;
 
-    /// @brief Check Error
+    /// @brief Check for errors
     ///
-    /// Chech error for current database operation.
-    void checkStatementError(std::string& error, CassFuture* future,
-        const char* what) const;
+    /// Check for errors on the current database operation.
+    void checkStatementError(std::string& error,
+                             CassFuture* future,
+                             const char* what) const;
 
     /// @brief CQL connection handle
     CassCluster* cluster_;
@@ -104,16 +126,69 @@ public:
     /// @brief CQL session handle
     CassSession* session_;
 
-    /// @brief CQL prepared statements - used for faster statement execution using
-    /// bind functionality
+    /// @brief CQL consistency enabled
+    bool force_consistency_;
+
+    /// @brief CQL consistency
+    CassConsistency consistency_;
+
+    /// @brief CQL prepared statements - used for faster statement execution
+    ///     using bind functionality
     std::vector<const CassPrepared*> statements_;
 
-    /// @brief Pointer to external array of tagged statements containing statement
-    /// name, array of names of bind parameters and text query
+    /// @brief Pointer to external array of tagged statements containing
+    ///     statement name, array of names of bind parameters and text query
     CqlTaggedStatement* tagged_statements_;
 };
 
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
+/// @brief RAII object representing CQL transaction.
+///
+/// An instance of this class should be created in a scope where multiple
+/// INSERT statements should be executed within a single transaction. The
+/// transaction is started when the constructor of this class is invoked.
+/// The transaction is ended when the @ref CqlTransaction::commit is
+/// explicitly called or when the instance of this class is destroyed.
+/// The @ref CqlTransaction::commit commits changes to the database
+/// and the changes remain in the database when the instance of the
+/// class is destroyed. If the class instance is destroyed before the
+/// @ref CqlTransaction::commit is called, the transaction is rolled
+/// back. The rollback on destruction guarantees that partial data is
+/// not stored in the database when there is an error during any
+/// of the operations belonging to a transaction.
+class CqlTransaction : public boost::noncopyable {
+public:
+    /// @brief Constructor
+    ///
+    /// Starts transaction by making a "START TRANSACTION" query.
+    ///
+    /// @param conn CQL connection to use for the transaction. This connection
+    ///     will be later used to commit or rollback changes.
+    ///
+    /// @throw DbOperationError if "START TRANSACTION" query fails.
+    explicit CqlTransaction(CqlConnection& conn);
+
+    /// @brief Destructor
+    ///
+    /// Rolls back the transaction if changes haven't been committed.
+    ~CqlTransaction();
+
+    /// @brief Commits transaction
+    ///
+    /// Calls @ref CqlConnection::commit()..
+    void commit();
+
+private:
+    /// @brief Holds reference to the CQL database connection.
+    CqlConnection& conn_;
+
+    /// @brief Boolean flag indicating if the transaction has been committed.
+    ///
+    /// This flag is used in the class destructor to assess if the
+    /// transaction should be rolled back.
+    bool committed_;
+};
+
+}  // namespace dhcp
+}  // namespace isc
 
-#endif // CQL_CONNECTION_H
+#endif  // CQL_CONNECTION_H
diff --git a/src/lib/dhcpsrv/cql_exchange.cc b/src/lib/dhcpsrv/cql_exchange.cc
new file mode 100644 (file)
index 0000000..c73f74b
--- /dev/null
@@ -0,0 +1,485 @@
+// Copyright (C) 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 <dhcpsrv/cql_exchange.h>
+
+#include <dhcpsrv/cql_connection.h>
+#include <dhcpsrv/db_exceptions.h>
+
+/// @name CqlBind auxiliary functions for binding data into Cassandra format:
+/// @{
+
+/// @todo These void* cast are unsafe. See ticket #4525.
+static CassError
+CqlBindNone(CassStatement* statement, size_t index, void*) {
+    return cass_statement_bind_null(statement, index);
+}
+
+static CassError
+CqlBindBool(CassStatement* statement, size_t index, void* value) {
+    return cass_statement_bind_bool(statement, index,
+                                    *(static_cast<cass_bool_t*>(value)));
+}
+
+static CassError
+CqlBindInt32(CassStatement* statement, size_t index, void* value) {
+    return cass_statement_bind_int32(statement, index,
+                                     *(static_cast<cass_int32_t*>(value)));
+}
+
+static CassError
+CqlBindInt64(CassStatement* statement, size_t index, void* value) {
+    return cass_statement_bind_int64(statement, index,
+                                     *(static_cast<cass_int64_t*>(value)));
+}
+
+static CassError
+CqlBindTimestamp(CassStatement* statement, size_t index, void* value) {
+    return cass_statement_bind_int64(statement, index,
+                                     *(static_cast<cass_int64_t*>(value)));
+}
+
+static CassError
+CqlBindString(CassStatement* statement, size_t index, void* value) {
+    return cass_statement_bind_string(
+        statement, index, static_cast<std::string*>(value)->c_str());
+}
+
+static CassError
+CqlBindBytes(CassStatement* statement, size_t index, void* value) {
+    return cass_statement_bind_bytes(
+        statement, index, static_cast<std::vector<cass_byte_t>*>(value)->data(),
+        static_cast<std::vector<cass_byte_t>*>(value)->size());
+}
+
+static CassError
+CqlBindUuid(CassStatement* statement, size_t index, void* value) {
+    return cass_statement_bind_uuid(statement, index,
+                                    *static_cast<CassUuid*>(value));
+}
+/// @}
+
+static CassError
+CqlGetNone(const CassValue*, void*) {
+    return CASS_OK;
+}
+
+static CassError
+CqlGetBool(const CassValue* value, void* data) {
+    return cass_value_get_bool(value, static_cast<cass_bool_t*>(data));
+}
+
+static CassError
+CqlGetInt32(const CassValue* value, void* data) {
+    return cass_value_get_int32(value, static_cast<cass_int32_t*>(data));
+}
+
+static CassError
+CqlGetInt64(const CassValue* value, void* data) {
+    return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
+}
+
+static CassError
+CqlGetTimestamp(const CassValue* value, void* data) {
+    return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
+}
+
+static CassError
+CqlGetString(const CassValue* value, void* data) {
+    const char* dataValue;
+    size_t sizeValue;
+    CassError cassError = cass_value_get_string(
+        value, static_cast<const char**>(&dataValue), &sizeValue);
+    static_cast<std::string*>(data)->assign(dataValue, dataValue + sizeValue);
+    return cassError;
+}
+
+static CassError
+CqlGetBytes(const CassValue* value, void* data) {
+    const cass_byte_t* dataValue;
+    size_t sizeValue;
+    CassError cassError = cass_value_get_bytes(
+        value, static_cast<const cass_byte_t**>(&dataValue), &sizeValue);
+    static_cast<std::vector<cass_byte_t>*>(data)->assign(dataValue,
+                                                         dataValue + sizeValue);
+    return cassError;
+}
+
+static CassError
+CqlGetUuid(const CassValue* value, void* data) {
+    return cass_value_get_uuid(value, static_cast<CassUuid*>(data));
+}
+
+namespace isc {
+namespace dhcp {
+
+struct CqlFunctionData CqlFunctions[] = {
+    {CqlBindNone, CqlGetNone},           {CqlBindBool, CqlGetBool},
+    {CqlBindInt32, CqlGetInt32},         {CqlBindInt64, CqlGetInt64},
+    {CqlBindTimestamp, CqlGetTimestamp}, {CqlBindString, CqlGetString},
+    {CqlBindBytes, CqlGetBytes},         {CqlBindUuid, CqlGetUuid}};
+
+ExchangeDataType
+CqlCommon::getDataType(const uint32_t stindex,
+                       int pindex,
+                       const SqlExchange& exchange,
+                       const CqlTaggedStatement* tagged_statements) {
+    if (tagged_statements[stindex].params_ &&
+        tagged_statements[stindex].params_[pindex]) {
+        const ExchangeColumnInfoContainerName& idx =
+            exchange.parameters_.get<1>();
+        const ExchangeColumnInfoContainerNameRange& range =
+            idx.equal_range(tagged_statements[stindex].params_[pindex]);
+        if (std::distance(range.first, range.second) > 0) {
+            return (*range.first)->type_;
+        }
+    }
+    return EXCHANGE_DATA_TYPE_NONE;
+}
+
+void
+CqlCommon::bindData(CassStatement* statement,
+                    uint32_t stindex,
+                    const CqlDataArray& data,
+                    const SqlExchange& exchange,
+                    const CqlTaggedStatement* tagged_statements) {
+    if (!tagged_statements[stindex].params_) {
+        return;
+    }
+    for (int i = 0; tagged_statements[stindex].params_[i]; i++) {
+        ExchangeDataType type =
+            CqlCommon::getDataType(stindex, i, exchange, tagged_statements);
+        if (type >= sizeof(CqlFunctions) / sizeof(CqlFunctions[0])) {
+            isc_throw(BadValue, "index " << stindex << " out of bounds");
+        }
+        CqlFunctions[type].cqlBindFunction_(statement, i, data.values_[i]);
+    }
+}
+
+void
+CqlCommon::getData(const CassRow* row,
+                   int pindex,
+                   int dindex,
+                   const SqlExchange& exchange,
+                   CqlDataArray& data) {
+    if (pindex >= exchange.parameters_.size()) {
+        return;
+    }
+    const ExchangeColumnInfoContainerIndex& idx = exchange.parameters_.get<2>();
+    const ExchangeColumnInfoContainerIndexRange& range =
+        idx.equal_range(pindex);
+    if (std::distance(range.first, range.second) > 0) {
+        std::string name = (*range.first)->name_;
+        ExchangeDataType type = (*range.first)->type_;
+        const CassValue* value = cass_row_get_column_by_name(row, name.c_str());
+        if (!value) {
+            isc_throw(BadValue, "column name " << name << " doesn't exist");
+        }
+        if (type >= sizeof(CqlFunctions) / sizeof(CqlFunctions[0])) {
+            isc_throw(BadValue, "index " << type << " out of bounds");
+        }
+        CassError cassError =
+            CqlFunctions[type].cqlGetFunction_(value, data.values_[dindex]);
+        if (cassError != CASS_OK) {
+            isc_throw(BadValue, "Cassandra error for column "
+                                    << name << " with message: "
+                                    << cass_error_desc(cassError));
+        }
+    }
+}
+
+CqlExchange::CqlExchange() {
+}
+
+CqlExchange::~CqlExchange() {
+}
+
+void
+CqlExchange::convertToDatabaseTime(const time_t& cltt,
+                                   const cass_int64_t& valid_lifetime,
+                                   cass_int64_t& expire) {
+    // Calculate expiry time. Store it in the 64-bit value so as we can
+    // detect overflows.
+    cass_int64_t expire_time = static_cast<cass_int64_t>(cltt) + valid_lifetime;
+
+    if (expire_time > DatabaseConnection::MAX_DB_TIME) {
+        isc_throw(BadValue, "Time value is too large: " << expire_time);
+    }
+
+    expire = expire_time;
+}
+
+void
+CqlExchange::convertFromDatabaseTime(const cass_int64_t& expire,
+                                     const cass_int64_t& valid_lifetime,
+                                     time_t& cltt) {
+    // Convert to local time
+    cltt = static_cast<time_t>(expire - valid_lifetime);
+}
+
+void
+CqlExchange::createBindForReceive(CqlDataArray& /* data */,
+                                  const int /* statementIndex = -1 */) {
+    isc_throw(NotImplemented,
+              "CqlExchange::createBindForReceive() not implemented yet.");
+}
+
+CqlDataArray
+CqlExchange::executeRead(const CqlConnection& connection,
+                         const CqlDataArray& data,
+                         const int statementIndex,
+                         const bool single /* = false */,
+                         const std::vector<std::string>&
+                             parameters /* = std::vector<std::string>() */) {
+    CassError rc;
+    CassStatement* statement = NULL;
+    CassFuture* future = NULL;
+
+    statement = cass_prepared_bind(connection.statements_[statementIndex]);
+    if (!statement) {
+        isc_throw(DbOperationError,
+                  "unable to bind statement "
+                      << connection.tagged_statements_[statementIndex].name_);
+    }
+
+    if (connection.force_consistency_) {
+        rc = cass_statement_set_consistency(statement, connection.consistency_);
+        if (rc != CASS_OK) {
+            cass_statement_free(statement);
+            isc_throw(
+                DbOperationError,
+                "unable to set statement consistency for statement "
+                    << connection.tagged_statements_[statementIndex].name_);
+        }
+    }
+
+    CqlCommon::bindData(statement, statementIndex, data, *this,
+                        connection.tagged_statements_);
+
+    future = cass_session_execute(connection.session_, statement);
+    if (!future) {
+        cass_statement_free(statement);
+        isc_throw(DbOperationError,
+                  "unable to execute statement "
+                      << connection.tagged_statements_[statementIndex].name_);
+    }
+    cass_future_wait(future);
+    std::string error;
+    connection.checkStatementError(error, future, statementIndex,
+                                   "unable to execute statement");
+    rc = cass_future_error_code(future);
+    if (rc != CASS_OK) {
+        cass_future_free(future);
+        cass_statement_free(statement);
+        isc_throw(DbOperationError, error);
+    }
+
+    // Get column values.
+    const CassResult* resultCollection = cass_future_get_result(future);
+    if (single && cass_result_row_count(resultCollection) > 1) {
+        cass_result_free(resultCollection);
+        cass_future_free(future);
+        cass_statement_free(statement);
+        isc_throw(MultipleRecords,
+                  "multiple records were found in the "
+                  "database where only one was expected for statement "
+                      << connection.tagged_statements_[statementIndex].name_);
+    }
+
+    // Get results.
+    CqlDataArray returnValues;
+    CqlDataArray collection;
+    CassIterator* rows = cass_iterator_from_result(resultCollection);
+    while (cass_iterator_next(rows)) {
+        const CassRow* row = cass_iterator_get_row(rows);
+        createBindForReceive(returnValues, statementIndex);
+        for (size_t i = 0U; i < returnValues.size(); i++) {
+            uint32_t pindex = i;
+            if (!parameters.empty()) {
+                const ExchangeColumnInfoContainerName& idx =
+                    parameters_.get<1>();
+                const ExchangeColumnInfoContainerNameRange& range =
+                    idx.equal_range(parameters[i]);
+                if (std::distance(range.first, range.second) > 0) {
+                    pindex = (*range.first)->index_;
+                }
+            }
+            CqlCommon::getData(row, pindex, i, *this, returnValues);
+        }
+        collection.add(retrieve());
+    }
+
+    // Free resources.
+    cass_iterator_free(rows);
+    cass_result_free(resultCollection);
+    cass_future_free(future);
+    cass_statement_free(statement);
+
+    return collection;
+}
+
+void
+CqlExchange::executeWrite(const CqlConnection& connection,
+                          const CqlDataArray& data,
+                          const int statementIndex) {
+    CassError rc;
+    CassStatement* statement = NULL;
+    CassFuture* future = NULL;
+
+    statement = cass_prepared_bind(connection.statements_[statementIndex]);
+    if (!statement) {
+        isc_throw(DbOperationError,
+                  "unable to bind statement "
+                      << connection.tagged_statements_[statementIndex].name_);
+    }
+
+    if (connection.force_consistency_) {
+        rc = cass_statement_set_consistency(statement, connection.consistency_);
+        if (rc != CASS_OK) {
+            cass_statement_free(statement);
+            isc_throw(
+                DbOperationError,
+                "unable to set statement consistency for statement "
+                    << connection.tagged_statements_[statementIndex].name_);
+        }
+    }
+
+    CqlCommon::bindData(statement, statementIndex, data, *this,
+                        connection.tagged_statements_);
+
+    future = cass_session_execute(connection.session_, statement);
+    if (!future) {
+        cass_statement_free(statement);
+        isc_throw(DbOperationError,
+                  "unable to execute statement "
+                      << connection.tagged_statements_[statementIndex].name_);
+    }
+    cass_future_wait(future);
+    std::string error;
+    connection.checkStatementError(error, future, statementIndex,
+                                   "unable to execute statement");
+    rc = cass_future_error_code(future);
+    if (rc != CASS_OK) {
+        cass_future_free(future);
+        cass_statement_free(statement);
+        isc_throw(DbOperationError, error);
+    }
+
+    // Check if statement has been applied.
+    bool applied = hasStatementBeenApplied(future);
+
+    // Free resources.
+    cass_future_free(future);
+    cass_statement_free(statement);
+
+    if (!applied) {
+        isc_throw(DuplicateEntry,
+                  "[applied] is false. Entry already exists. "
+                  "Statement "
+                      << connection.tagged_statements_[statementIndex].name_
+                      << "has not been applied");
+    }
+}
+
+bool
+CqlExchange::hasStatementBeenApplied(CassFuture* future,
+                                     size_t* row_count,
+                                     size_t* column_count) {
+    const CassResult* resultCollection = cass_future_get_result(future);
+    if (row_count) {
+        *row_count = cass_result_row_count(resultCollection);
+    }
+    if (column_count) {
+        *column_count = cass_result_column_count(resultCollection);
+    }
+    CassIterator* rows = cass_iterator_from_result(resultCollection);
+    CqlDataArray data;
+    cass_bool_t applied = cass_true;
+    while (cass_iterator_next(rows)) {
+        const CassRow* row = cass_iterator_get_row(rows);
+        // [applied]: bool
+        data.add(reinterpret_cast<void*>(&applied));
+
+        const ExchangeColumnInfoContainerName& idx = parameters_.get<1>();
+        const ExchangeColumnInfoContainerNameRange& range =
+            idx.equal_range("[applied]");
+        if (std::distance(range.first, range.second) > 0) {
+            CqlCommon::getData(row, (*range.first)->index_, 0, *this, data);
+        }
+    }
+    cass_iterator_free(rows);
+    cass_result_free(resultCollection);
+    return applied == cass_true;
+}
+
+void*
+CqlExchange::retrieve() {
+    isc_throw(NotImplemented, "CqlExchange::retrieve() not implemented yet.");
+}
+
+CqlVersionExchange::CqlVersionExchange() {
+    // Set the column names
+    parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+        "version", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+        EXCHANGE_DATA_TYPE_INT32)));
+    parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+        "minor", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+        EXCHANGE_DATA_TYPE_INT32)));
+    BOOST_ASSERT(parameters_.size() == 2U);
+}
+
+CqlVersionExchange::~CqlVersionExchange() {
+}
+
+void
+CqlVersionExchange::createBindForReceive(CqlDataArray& data,
+                                         const int /* statementIndex = -1 */) {
+    // Start with a fresh array.
+    data.clear();
+    // id: blob
+    data.add(reinterpret_cast<void*>(&version_));
+    // host_identifier: blob
+    data.add(reinterpret_cast<void*>(&minor_));
+}
+
+void*
+CqlVersionExchange::retrieve() {
+    pair_ = VersionPair(version_, minor_);
+    return reinterpret_cast<void*>(&pair_);
+}
+
+VersionPair
+CqlVersionExchange::retrieveVersion(const CqlConnection& connection,
+                                    int statementIndex) {
+    // Run statement.
+    const CqlDataArray whereValues;
+    CqlDataArray versionCollection =
+        executeRead(connection, whereValues, statementIndex, true);
+
+    VersionPair result;
+
+    if (!versionCollection.empty()) {
+        result = *(reinterpret_cast<VersionPair*>(
+            *versionCollection.begin()));
+    }
+
+    return result;
+}
+
+}  // namespace dhcp
+}  // namespace isc
diff --git a/src/lib/dhcpsrv/cql_exchange.h b/src/lib/dhcpsrv/cql_exchange.h
new file mode 100644 (file)
index 0000000..191dc5f
--- /dev/null
@@ -0,0 +1,325 @@
+// Copyright (C) 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 CQL_EXCHANGE_H
+#define CQL_EXCHANGE_H
+
+#include <cassandra.h>
+#include <dhcpsrv/cql_connection.h>
+#include <dhcpsrv/sql_common.h>
+#include <exceptions/exceptions.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Binds a C++ object to a Cassandra statement's parameter. Used in all
+///     statements.
+typedef CassError (*CqlBindFunction)(CassStatement* statement,
+                                     size_t index,
+                                     void* value);
+
+/// @brief Converts a single Cassandra column value to a C++ object. Used in
+///     SELECT statements.
+typedef CassError (*CqlGetFunction)(const CassValue* value, void* data);
+
+/// @brief Pair containing major and minor versions.
+typedef std::pair<uint32_t, uint32_t> VersionPair;
+
+/// @brief Wrapper over the bind and get functions that interface with Cassandra
+struct CqlFunctionData {
+    /// @brief Binds a C++ object to a Cassandra statement's parameter. Used in
+    /// all
+    ///     statements.
+    CqlBindFunction cqlBindFunction_;
+    /// @brief Converts a single Cassandra column value to a C++ object. Used in
+    ///     SELECT statements.
+    CqlGetFunction cqlGetFunction_;
+};
+
+/// @brief Collection of bind and get functions, one pair for each
+///     parameter/column
+extern struct CqlFunctionData CqlFunctions[];
+
+/// @brief Structure used to bind C++ input values to dynamic CQL parameters
+///
+/// The structure contains a vector which stores the input values. The vector is
+/// passed directly into the CQL execute call. Note that the data values are
+/// stored as pointers. These pointers need to be valid for the duration of the
+/// CQL statement execution. In other words, populating them with pointers that
+/// go out of scope before the statement is executed results in undefined
+/// behaviour.
+struct CqlDataArray {
+    /// @brief Constructor
+    CqlDataArray() {
+    }
+
+    /// @brief Copy constructor
+    CqlDataArray(const CqlDataArray& other) : values_(other.values_) {
+    }
+
+    /// @brief Destructor
+    virtual ~CqlDataArray() {
+    }
+
+    /// @brief Add a at the end of the vector.
+    void add(void* value) {
+        values_.push_back(value);
+    }
+
+    /// @brief Remove the void pointer to the data value from a specified
+    ///     position inside the vector.
+    void remove(int index) {
+        if (values_.size() <= index) {
+            isc_throw(BadValue, "Index " << index << " out of bounds: [0, "
+                                         << (values_.size() - 1) << "]");
+        }
+        values_.erase(values_.begin() + index);
+    }
+
+    /// @brief Remove all data from the vector.
+    void clear() {
+        values_.clear();
+    }
+
+    /// @brief Get the number of elements inside the vector.
+    size_t size() const {
+        return values_.size();
+    }
+
+    /// @brief Check if the vector is empty.
+    bool empty() const {
+        return values_.empty();
+    }
+
+    /// @brief Square brackets operator overload
+    ///
+    /// Retrieves void pointer at specified position.
+    void* operator[](int i) const {
+        return values_[i];
+    }
+
+    /// @brief Iterator pointing to the first element
+    std::vector<void*>::const_iterator begin() const {
+        return values_.begin();
+    }
+
+    /// @brief Iterator pointing to the past-the-end element
+    std::vector<void*>::const_iterator end() const {
+        return values_.end();
+    }
+
+    /// @brief Vector of pointers to the data values
+    std::vector<void*> values_;
+};
+
+/// @brief Cassandra Exchange
+///
+/// Used to convert between Cassandra CQL and C++ data types. A different
+/// exchange is made for every distinct set of columns. Multiple tables may use
+/// the same exchange if they have the same columns.
+class CqlExchange : public virtual SqlExchange {
+public:
+    /// @brief Constructor
+    ///
+    /// Empty body. Derived constructors specify table columns.
+    CqlExchange();
+
+    /// @brief Destructor
+    virtual ~CqlExchange();
+
+    /// @name Time conversion:
+    /// @{
+    static void convertToDatabaseTime(const time_t& cltt,
+                                      const cass_int64_t& valid_lifetime,
+                                      cass_int64_t& expire);
+    static void convertFromDatabaseTime(const cass_int64_t& expire,
+                                        const cass_int64_t& valid_lifetime,
+                                        time_t& cltt);
+    /// @}
+
+    /// @brief Create BIND array to receive C++ data.
+    ///
+    /// Used in executeRead() to retrieve from database
+    ///
+    /// @param data array of bound objects representing data to be retrieved
+    /// @param statementIndex prepared statement to be executed; defaults to an
+    ///     invalid index
+    virtual void createBindForReceive(CqlDataArray& data,
+                                      const int statementIndex = -1);
+
+    /// @brief Executes select statements.
+    ///
+    /// @param connection connection used to communicate with the Cassandra
+    ///     database
+    /// @param whereValues array of bound objects used to filter the results
+    /// @param statementIndex prepared statement being executed
+    /// @param single true if a single row should be returned; by default,
+    /// multiple rows are allowed
+    /// @param parameters Output parameters of a statement ( used in WHERE
+    ///     clause ); optional, needed only if parameters in the statement are
+    ///     in a different order than in the schema
+    ///
+    /// @return collection of void* objects
+    ///
+    /// @throw DbOperationError
+    /// @throw MultipleRecords
+    CqlDataArray executeRead(const CqlConnection& connection,
+                             const CqlDataArray& whereValues,
+                             const int statementIndex,
+                             const bool single = false,
+                             const std::vector<std::string>& parameters =
+                                 std::vector<std::string>());
+
+    /// @brief Executes insert, update, delete or other statements.
+    ///
+    /// @param connection connection used to communicate with the Cassandra
+    ///     database
+    /// @param assignedValues array of bound objects to be used when inserting
+    ///     values
+    /// @param statementIndex prepared statement to be executed
+    void executeWrite(const CqlConnection& connection,
+                      const CqlDataArray& assignedValues,
+                      const int statementIndex);
+
+    /// @brief Check if CQL statement has been applied.
+    ///
+    /// @param future structure used to wait on statement executions
+    /// @param row_count number of rows returned
+    /// @param column_count number of columns queried
+    ///
+    /// @return true if statement has been succesfully applied, false otherwise
+    bool hasStatementBeenApplied(CassFuture* future,
+                                 size_t* row_count = NULL,
+                                 size_t* column_count = NULL);
+
+    /// @brief Copy received data into the derived class' object.
+    ///
+    /// Copies information about the entity to be retrieved into a holistic
+    /// object. Called in @ref executeRead(). Not implemented for base class
+    /// CqlExchange. To be implemented in derived classes.
+    ///
+    /// @return a pointer to the object retrieved.
+    virtual void* retrieve();
+};
+
+/// @brief Exchange used to retrieve schema version from the keyspace.
+class CqlVersionExchange : public virtual CqlExchange {
+public:
+    /// @brief Constructor
+    ///
+    /// Specifies table columns.
+    CqlVersionExchange();
+
+    /// @brief Destructor
+    virtual ~CqlVersionExchange();
+
+    /// @brief Create BIND array to receive C++ data.
+    ///
+    /// Used in executeRead() to retrieve from database
+    ///
+    /// @param data array of bound objects representing data to be retrieved
+    /// @param statementIndex prepared statement to be executed; defaults to an
+    ///     invalid index
+    virtual void createBindForReceive(CqlDataArray& data,
+                                      const int statementIndex = -1);
+
+    /// @brief Standalone method used to retrieve schema version
+    ///
+    /// @param connection array of bound objects representing data to be
+    /// retrieved
+    /// @param statementIndex prepared statement to be executed
+    ///
+    /// @return version of schema specified in the prepared statement in the
+    /// @ref CqlConnection parameter
+    virtual VersionPair
+    retrieveVersion(const CqlConnection& connection, int statementIndex);
+
+    /// @brief Copy received data into the <version,minor> pair.
+    ///
+    /// Copies information about the version to be retrieved into a pair. Called
+    /// in executeRead().
+    ///
+    /// @return a pointer to the object retrieved.
+    virtual void* retrieve();
+
+private:
+    cass_int32_t version_;
+    cass_int32_t minor_;
+    VersionPair pair_;
+};
+
+/// @brief Common operations in Cassandra exchanges
+class CqlCommon {
+public:
+    /// @brief Returns type of data for specific parameter.
+    ///
+    /// Returns type of a given parameter of a given statement.
+    ///
+    /// @param stindex Index of statement being executed.
+    /// @param pindex Index of the parameter for a given statement.
+    /// @param exchange Exchange object to use
+    /// @param tagged_statements CqlTaggedStatement array to use
+    static ExchangeDataType
+    getDataType(const uint32_t stindex,
+                int pindex,
+                const SqlExchange& exchange,
+                const CqlTaggedStatement* tagged_statements);
+
+    /// @brief Binds data specified in data
+    ///
+    /// Calls one of cass_value_bind_([none|bool|int32|int64|string|bytes]).
+    /// It is used to bind C++ data types used by Kea into formats used by
+    /// Cassandra.
+    ///
+    /// @param statement Statement object representing the query
+    /// @param stindex Index of statement being executed
+    /// @param data array that has been created for the type of lease in
+    ///     question.
+    /// @param exchange Exchange object to use
+    /// @param tagged_statements CqlTaggedStatement array to use
+    static void bindData(CassStatement* statement,
+                         uint32_t stindex,
+                         const CqlDataArray& data,
+                         const SqlExchange& exchange,
+                         const CqlTaggedStatement* tagged_statements);
+
+    /// @brief Retrieves data returned by Cassandra.
+    ///
+    /// Calls one of cass_value_bind_([none|bool|int32|int64|string|bytes]).
+    /// Used to retrieve data returned by Cassandra into standard C++ types used
+    /// by Kea.
+    ///
+    /// @param row row of data returned by CQL library
+    /// @param pindex Index of statement being executed
+    /// @param dindex data index (specifies which entry in size array is used)
+    /// @param exchange Exchange object to use
+    /// @param data array that has been created for the type of lease in
+    ///     question.
+    static void getData(const CassRow* row,
+                        int pindex,
+                        int dindex,
+                        const SqlExchange& exchange,
+                        CqlDataArray& data);
+};
+
+}  // namespace dhcp
+}  // namespace isc
+
+#endif  // CQL_EXCHANGE_H
index f7486c6054cfead2769a0502f00026332f3d86d4..7175dd23eae24beeba7099aa74afb51336dbbf10 100644 (file)
 // 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/cql_lease_mgr.h>
-#include <boost/static_assert.hpp>
-#include <iostream>
-#include <iomanip>
-#include <sstream>
-#include <string>
-#include <ctime>
+#include <dhcpsrv/dhcpsrv_log.h>
+
+#include <limits>
 
 using namespace isc;
 using namespace isc::dhcp;
@@ -37,247 +34,158 @@ namespace dhcp {
 static const size_t HOSTNAME_MAX_LEN = 255U;
 static const size_t ADDRESS6_TEXT_MAX_LEN = 39U;
 
-/// @name CqlBind auxiliary methods for binding data into Cassandra format:
 /// @{
-
-/// @todo These void* cast are unsafe. See ticket #4525.
-static CassError CqlBindNone(CassStatement* statement, size_t index, void*) {
-    return cass_statement_bind_null(statement, index);
-}
-
-static CassError CqlBindBool(CassStatement* statement, size_t index,
-        void* value) {
-    return cass_statement_bind_bool(statement, index,
-        *(static_cast<cass_bool_t*>(value)));
-}
-
-static CassError CqlBindInt32(CassStatement* statement, size_t index,
-        void* value) {
-    return cass_statement_bind_int32(statement, index,
-        *(static_cast<cass_int32_t*>(value)));
-}
-
-static CassError CqlBindInt64(CassStatement* statement, size_t index,
-        void* value) {
-    return cass_statement_bind_int64(statement, index,
-        *(static_cast<cass_int64_t*>(value)));
-}
-
-static CassError CqlBindTimestamp(CassStatement* statement, size_t index,
-        void* value) {
-    return cass_statement_bind_int64(statement, index,
-        *(static_cast<cass_int64_t*>(value)));
-}
-
-static CassError CqlBindString(CassStatement* statement, size_t index,
-        void* value) {
-    return cass_statement_bind_string(statement, index,
-        (static_cast<const char*>(value)));
-}
-
-static CassError CqlBindBytes(CassStatement* statement, size_t index,
-        void* value) {
-    return cass_statement_bind_bytes(statement, index,
-        static_cast<std::vector<cass_byte_t>*>(value)->data(),
-        static_cast<std::vector<cass_byte_t>*>(value)->size());
-}
-/// @}
-
-static CassError CqlGetNone(const CassValue*, void*, size_t*) {
-    return CASS_OK;
-}
-
-static CassError CqlGetBool(const CassValue* value, void* data, size_t*) {
-    return cass_value_get_bool(value, static_cast<cass_bool_t*>(data));
-}
-
-static CassError CqlGetInt32(const CassValue* value, void* data, size_t*) {
-    return cass_value_get_int32(value, static_cast<cass_int32_t*>(data));
-}
-
-static CassError CqlGetInt64(const CassValue* value, void* data, size_t*) {
-    return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
-}
-
-static CassError CqlGetTimestamp(const CassValue* value, void* data, size_t*) {
-    return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
-}
-
-static CassError CqlGetString(const CassValue* value, void* data,
-        size_t* size) {
-    return cass_value_get_string(value, static_cast<const char**>(data), size);
-}
-
-static CassError CqlGetBytes(const CassValue* value, void* data, size_t* size) {
-    return cass_value_get_bytes(value, static_cast<const cass_byte_t**>(data),
-        size);
-}
-
-typedef CassError (*CqlBindFunction)(CassStatement* statement, size_t index,
-        void* value);
-typedef CassError (*CqlGetFunction)(const CassValue* value, void* data,
-        size_t* size);
-
-struct CqlFunctionData {
-    CqlBindFunction sqlBindFunction_;
-    CqlGetFunction sqlGetFunction_;
-};
-
-static struct CqlFunctionData CqlFunctions[] = {
-    {CqlBindNone, CqlGetNone},
-    {CqlBindBool, CqlGetBool},
-    {CqlBindInt32, CqlGetInt32},
-    {CqlBindInt64, CqlGetInt64},
-    {CqlBindTimestamp, CqlGetTimestamp},
-    {CqlBindString, CqlGetString},
-    {CqlBindBytes, CqlGetBytes}
-};
-
-/// @brief Catalog of all the SQL statements currently supported. Note
+///
+/// @brief Catalog of all the CQL statements currently supported. Note
 /// that the order columns appear in statement body must match the order they
 /// that the occur in the table. This does not apply to the where clause.
-
 static const char* delete_lease4_params[] = {
-        static_cast<const char*>("address"),
-        NULL };
+    static_cast<const char*>("address"),
+    NULL };
 static const char* delete_expired_lease4_params[] = {
-        static_cast<const char*>("state"),
-        static_cast<const char*>("expire"),
-        NULL };
+    static_cast<const char*>("state"),
+    static_cast<const char*>("expire"),
+    NULL };
 static const char* delete_lease6_params[] = {
-        static_cast<const char*>("address"),
-        NULL };
+    static_cast<const char*>("address"),
+    NULL };
 static const char* delete_expired_lease6_params[] = {
-        static_cast<const char*>("state"),
-        static_cast<const char*>("expire"),
-        NULL };
+    static_cast<const char*>("state"),
+    static_cast<const char*>("expire"),
+    NULL };
 static const char* get_lease4_addr_params[] = {
-        static_cast<const char*>("address"),
-        NULL };
+    static_cast<const char*>("address"),
+    NULL };
 static const char* get_lease4_clientid_params[] = {
-        static_cast<const char*>("client_id"),
-        NULL };
+    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_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_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_cast<const char*>("hwaddr"),
+    static_cast<const char*>("subnet_id"),
+    NULL };
 static const char* get_lease4_expired_params[] = {
-        static_cast<const char*>("state"),
-        static_cast<const char*>("expire"),
-        static_cast<const char*>("limit"),
-        NULL };
+    static_cast<const char*>("state"),
+    static_cast<const char*>("expire"),
+    static_cast<const char*>("limit"),
+    NULL };
 static const char* get_lease6_addr_params[] = {
-        static_cast<const char*>("address"),
-        static_cast<const char*>("lease_type"),
-        NULL };
+    static_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_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_cast<const char*>("duid"),
+    static_cast<const char*>("iaid"),
+    static_cast<const char*>("subnet_id"),
+    static_cast<const char*>("lease_type"),
+    NULL };
 static const char* get_lease6_expired_params[] = {
-        static_cast<const char*>("state"),
-        static_cast<const char*>("expire"),
-        static_cast<const char*>("limit"),
-        NULL };
+    static_cast<const char*>("state"),
+    static_cast<const char*>("expire"),
+    static_cast<const char*>("limit"),
+    NULL };
 static const char* get_version_params[] = {
-        NULL };
+    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_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_cast<const char*>("address"),
+    static_cast<const char*>("duid"),
+    static_cast<const char*>("valid_lifetime"),
+    static_cast<const char*>("expire"),
+    static_cast<const char*>("subnet_id"),
+    static_cast<const char*>("pref_lifetime"),
+    static_cast<const char*>("lease_type"),
+    static_cast<const char*>("iaid"),
+    static_cast<const char*>("prefix_len"),
+    static_cast<const char*>("fqdn_fwd"),
+    static_cast<const char*>("fqdn_rev"),
+    static_cast<const char*>("hostname"),
+    static_cast<const char*>("hwaddr"),
+    static_cast<const char*>("hwtype"),
+    static_cast<const char*>("hwaddr_source"),
+    static_cast<const char*>("state"),
+    NULL };
 static const char* update_lease4_params[] = {
-        static_cast<const char*>("hwaddr"),
-        static_cast<const char*>("client_id"),
-        static_cast<const char*>("valid_lifetime"),
-        static_cast<const char*>("expire"),
-        static_cast<const char*>("subnet_id"),
-        static_cast<const char*>("fqdn_fwd"),
-        static_cast<const char*>("fqdn_rev"),
-        static_cast<const char*>("hostname"),
-        static_cast<const char*>("state"),
-        static_cast<const char*>("address"),
-        NULL };
+    static_cast<const char*>("hwaddr"),
+    static_cast<const char*>("client_id"),
+    static_cast<const char*>("valid_lifetime"),
+    static_cast<const char*>("expire"),
+    static_cast<const char*>("subnet_id"),
+    static_cast<const char*>("fqdn_fwd"),
+    static_cast<const char*>("fqdn_rev"),
+    static_cast<const char*>("hostname"),
+    static_cast<const char*>("state"),
+    static_cast<const char*>("address"),
+    NULL };
 static const char* update_lease6_params[] = {
-        static_cast<const char*>("duid"),
-        static_cast<const char*>("valid_lifetime"),
-        static_cast<const char*>("expire"),
-        static_cast<const char*>("subnet_id"),
-        static_cast<const char*>("pref_lifetime"),
-        static_cast<const char*>("lease_type"),
-        static_cast<const char*>("iaid"),
-        static_cast<const char*>("prefix_len"),
-        static_cast<const char*>("fqdn_fwd"),
-        static_cast<const char*>("fqdn_rev"),
-        static_cast<const char*>("hostname"),
-        static_cast<const char*>("hwaddr"),
-        static_cast<const char*>("hwtype"),
-        static_cast<const char*>("hwaddr_source"),
-        static_cast<const char*>("state"),
-        static_cast<const char*>("address"),
-        NULL };
+    static_cast<const char*>("duid"),
+    static_cast<const char*>("valid_lifetime"),
+    static_cast<const char*>("expire"),
+    static_cast<const char*>("subnet_id"),
+    static_cast<const char*>("pref_lifetime"),
+    static_cast<const char*>("lease_type"),
+    static_cast<const char*>("iaid"),
+    static_cast<const char*>("prefix_len"),
+    static_cast<const char*>("fqdn_fwd"),
+    static_cast<const char*>("fqdn_rev"),
+    static_cast<const char*>("hostname"),
+    static_cast<const char*>("hwaddr"),
+    static_cast<const char*>("hwtype"),
+    static_cast<const char*>("hwaddr_source"),
+    static_cast<const char*>("state"),
+    static_cast<const char*>("address"),
+    NULL };
+/// @}
 
 CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
     // DELETE_LEASE4
     { delete_lease4_params,
       "delete_lease4",
       "DELETE FROM lease4 WHERE address = ? "
-      "IF EXISTS" },
+      "IF EXISTS "
+    },
 
     // DELETE_LEASE4_STATE_EXPIRED
     { delete_expired_lease4_params,
       "delete_lease4_expired",
       "SELECT address, hwaddr, client_id, "
         "valid_lifetime, expire, subnet_id, "
-        "fqdn_fwd, fqdn_rev, hostname, state "
+        "fqdn_fwd, fqdn_rev, hostname, "
+        "state "
       "FROM lease4 "
-      "WHERE state = ? AND expire < ? "
-      "ALLOW FILTERING" },
+      "WHERE state = ? "
+      "AND expire < ? "
+      "ALLOW FILTERING "
+    },
 
     // DELETE_LEASE6
     { delete_lease6_params,
       "delete_lease6",
       "DELETE FROM lease6 WHERE address = ? "
-      "IF EXISTS" },
+      "IF EXISTS "
+    },
 
     // DELETE_LEASE6_STATE_EXPIRED
     { delete_expired_lease6_params,
@@ -285,68 +193,86 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
       "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 "
+        "hwaddr, hwtype, hwaddr_source, "
+        "state "
       "FROM lease6 "
-      "WHERE state = ? AND expire < ? "
-      "ALLOW FILTERING" },
+      "WHERE state = ? "
+      "AND expire < ? "
+      "ALLOW FILTERING "
+    },
 
     // GET_LEASE4_ADDR
     { get_lease4_addr_params,
       "get_lease4_addr",
       "SELECT address, hwaddr, client_id, "
         "valid_lifetime, expire, subnet_id, "
-        "fqdn_fwd, fqdn_rev, hostname, state "
+        "fqdn_fwd, fqdn_rev, hostname, "
+        "state "
       "FROM lease4 "
-      "WHERE address = ?" },
+      "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 "
+        "fqdn_fwd, fqdn_rev, hostname, "
+        "state "
       "FROM lease4 "
-      "WHERE client_id = ?" },
+      "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 "
+        "fqdn_fwd, fqdn_rev, hostname, "
+        "state "
       "FROM lease4 "
-      "WHERE client_id = ? AND subnet_id = ? "
-      "ALLOW FILTERING" },
+      "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 "
+        "fqdn_fwd, fqdn_rev, hostname, "
+        "state "
       "FROM lease4 "
-      "WHERE hwaddr = ?" },
+      "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 "
+        "fqdn_fwd, fqdn_rev, hostname, "
+        "state "
       "FROM lease4 "
-      "WHERE hwaddr = ? AND subnet_id = ? "
-      "ALLOW FILTERING" },
+      "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 "
+        "fqdn_fwd, fqdn_rev, hostname, "
+        "state "
       "FROM lease4 "
-      "WHERE state = ? AND expire < ? "
+      "WHERE state = ? "
+      "AND expire < ? "
       "LIMIT ? "
-      "ALLOW FILTERING" },
+      "ALLOW FILTERING "
+    },
 
     // GET_LEASE6_ADDR
     { get_lease6_addr_params,
@@ -354,21 +280,28 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
       "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 "
+        "hwaddr, hwtype, hwaddr_source, "
+        "state "
       "FROM lease6 "
-      "WHERE address = ? AND lease_type = ? "
-      "ALLOW FILTERING" },
+      "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",
+      "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,
@@ -376,10 +309,15 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
       "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 "
+        "hwaddr, hwtype, hwaddr_source, "
+        "state "
       "FROM lease6 "
-      "WHERE duid = ? AND iaid = ? AND subnet_id = ? AND lease_type = ? "
-      "ALLOW FILTERING" },
+      "WHERE duid = ? "
+      "AND iaid = ? "
+      "AND subnet_id = ? "
+      "AND lease_type = ? "
+      "ALLOW FILTERING "
+    },
 
     // GET_LEASE6_EXPIRE
     { get_lease6_expired_params,
@@ -387,16 +325,20 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
       "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 "
+        "hwaddr, hwtype, hwaddr_source, "
+        "state "
       "FROM lease6 "
-      "WHERE state = ? AND expire < ? "
+      "WHERE state = ? "
+      "AND expire < ? "
       "LIMIT ? "
-      "ALLOW FILTERING" },
+      "ALLOW FILTERING "
+    },
 
     // GET_VERSION
     { get_version_params,
       "get_version",
-      "SELECT version, minor FROM schema_version" },
+      "SELECT version, minor FROM schema_version "
+    },
 
     // INSERT_LEASE4
     { insert_lease4_params,
@@ -404,8 +346,10 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
       "INSERT INTO lease4(address, hwaddr, client_id, "
         "valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, "
         "state) "
-      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
-      "IF NOT EXISTS" },
+      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, "
+      "?) "
+      "IF NOT EXISTS "
+    },
 
     // INSERT_LEASE6
     { insert_lease6_params,
@@ -413,18 +357,23 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
       "INSERT INTO lease6(address, duid, valid_lifetime, "
         "expire, subnet_id, pref_lifetime, "
         "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, "
-        "hwtype, hwaddr_source, state) "
-      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
-      "IF NOT EXISTS" },
+        "hwtype, hwaddr_source, "
+        "state) "
+      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, "
+      "?) "
+      "IF NOT EXISTS "
+    },
 
     // UPDATE_LEASE4
     { update_lease4_params,
       "update_lease4",
       "UPDATE lease4 SET hwaddr = ?, "
         "client_id = ?, valid_lifetime = ?, expire = ?, "
-        "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, hostname = ?, state = ? "
+        "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, hostname = ?, "
+        "state = ? "
       "WHERE address = ? "
-      "IF EXISTS" },
+      "IF EXISTS "
+    },
 
     // UPDATE_LEASE6
     { update_lease6_params,
@@ -433,9 +382,12 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
         "valid_lifetime = ?, expire = ?, subnet_id = ?, "
         "pref_lifetime = ?, lease_type = ?, iaid = ?, "
         "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, hostname = ?, "
-        "hwaddr = ?, hwtype = ?, hwaddr_source = ?, state = ? "
+        "hwaddr = ?, hwtype = ?, hwaddr_source = ?, "
+        "state = ? "
       "WHERE address = ? "
-      "IF EXISTS" },
+      "IF EXISTS "
+    },
+
 
     // End of list sentinel
     { NULL, NULL, NULL }
@@ -449,47 +401,22 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
 /// base to both of them, containing some common methods.
 class CqlLeaseExchange : public CqlExchange {
 public:
-    CqlLeaseExchange() : 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_));
+    CqlLeaseExchange()
+        : valid_lifetime_(0), expire_(0), subnet_id_(0), fqdn_fwd_(cass_false),
+          fqdn_rev_(cass_false), state_(0) {
     }
-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 + 1];
-                                        ///< Client hostname
-    unsigned long   hostname_length_;   ///< Client hostname length
-    uint32_t        state_;             ///< Lease state
-};
 
-class CqlVersionExchange : public virtual CqlExchange {
-public:
-    /// @brief Constructor
-    ///
-    /// The initialization of the variables here is only to satisfy cppcheck -
-    /// all variables are initialized/set in the methods before they are used.
-    CqlVersionExchange() {
-        const size_t MAX_COLUMNS = 2U;
-        // Set the column names
-        size_t offset = 0U;
-        BOOST_STATIC_ASSERT(2U == MAX_COLUMNS);
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("version",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("minor",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
-        BOOST_ASSERT(parameters_.size() == MAX_COLUMNS);
-    }
+protected:
+    std::vector<cass_byte_t> hwaddr_;  ///< Hardware address
+    cass_int64_t valid_lifetime_;      ///< Lease time
+    cass_int64_t expire_;              ///< Lease expiry time
+    cass_int32_t subnet_id_;           ///< Subnet identification
+    cass_bool_t fqdn_fwd_;             ///< Has forward DNS update
+                                       /// been performed
+    cass_bool_t fqdn_rev_;             ///< Has reverse DNS update
+                                       /// been performed
+    std::string hostname_;             ///< Client hostname
+    cass_int32_t state_;               ///< Lease state
 };
 
 /// @brief Exchange CQL and Lease4 Data
@@ -511,39 +438,45 @@ public:
     ///
     /// The initialization of the variables here is only to satisfy cppcheck -
     /// all variables are initialized/set in the methods before they are used.
-    CqlLease4Exchange() : addr4_(0), client_id_length_(0),
-                            client_id_null_(false) {
-        const size_t MAX_COLUMNS = 12U;
-        memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
-
+    CqlLease4Exchange() : addr4_(0) {
         // Set the column names
-        size_t offset = 0U;
-        BOOST_STATIC_ASSERT(12U == MAX_COLUMNS);
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("address",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("hwaddr",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("client_id",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("valid_lifetime",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("expire",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_TIMESTAMP)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("subnet_id",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("fqdn_fwd",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("fqdn_rev",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("hostname",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("state",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("limit",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("[applied]",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL)));
-        BOOST_ASSERT(parameters_.size() == MAX_COLUMNS);
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "address", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_INT32)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "hwaddr", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_BYTES)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "client_id", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_BYTES)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "valid_lifetime", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_INT64)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "expire", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_TIMESTAMP)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "subnet_id", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_INT32)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "fqdn_fwd", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_BOOL)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "fqdn_rev", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_BOOL)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "hostname", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_STRING)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "state", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_INT32)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "limit", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_INT32)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "[applied]", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_BOOL)));
+        BOOST_ASSERT(parameters_.size() == 12U);
     }
 
     /// @brief Create CQL_BIND objects for Lease4 Pointer
@@ -552,7 +485,7 @@ public:
     /// the database.
     void createBindForSend(const Lease4Ptr& lease, CqlDataArray& data) {
         if (!lease) {
-            isc_throw(BadValue, "createBindForSend:: Lease4 object is NULL");
+            isc_throw(BadValue, "createBindForSend(): Lease4 object is NULL");
         }
         // Store lease object to ensure it remains valid.
         lease_ = lease;
@@ -563,24 +496,24 @@ public:
             // address: int
             // The address in the Lease structure is an IOAddress object.
             // Convert this to an integer for storage.
-            addr4_ = lease_->addr_.toUint32();
-            data.add(&addr4_);
+            addr4_ = static_cast<cass_int32_t>(lease_->addr_.toUint32());
+            data.add(reinterpret_cast<void*>(&addr4_));
 
             // hwaddr: blob
-            HWAddrPtr hwaddr = lease_->hwaddr_;
-            if (hwaddr) {
-                if (hwaddr->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
-                    isc_throw(DbOperationError, "Hardware address length " <<
-                              lease_->hwaddr_->hwaddr_.size() <<
-                              " exceeds maximum allowed of " <<
-                              HWAddr::MAX_HWADDR_LEN);
+            if (lease_->hwaddr_) {
+                if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+                    isc_throw(DbOperationError,
+                              "hardware address "
+                                  << lease_->hwaddr_->toText() << " of length "
+                                  << lease_->hwaddr_->hwaddr_.size()
+                                  << " exceeds maximum allowed length of "
+                                  << HWAddr::MAX_HWADDR_LEN);
                 }
-                hwaddr_ = hwaddr->hwaddr_;
+                hwaddr_ = lease_->hwaddr_->hwaddr_;
             } else {
                 hwaddr_.clear();
             }
-            hwaddr_length_ = hwaddr_.size();
-            data.add(&hwaddr_);
+            data.add(reinterpret_cast<void*>(&hwaddr_));
 
             // client_id: blob
             if (lease_->client_id_) {
@@ -588,174 +521,149 @@ public:
             } else {
                 client_id_.clear();
             }
-            client_id_length_ = client_id_.size();
-            data.add(&client_id_);
+            data.add(reinterpret_cast<void*>(&client_id_));
 
             // valid lifetime: bigint
-            valid_lifetime_ = lease_->valid_lft_;
-            data.add(&valid_lifetime_);
+            valid_lifetime_ = static_cast<cass_int32_t>(lease_->valid_lft_);
+            data.add(reinterpret_cast<void*>(&valid_lifetime_));
 
             // expire: bigint
-            // The lease structure holds the client last transmission time (cltt_)
+            // 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_
-            CqlLeaseExchange::convertToDatabaseTime(lease_->cltt_,
-                lease_->valid_lft_, expire_);
-            data.add(&expire_);
+            CqlExchange::convertToDatabaseTime(lease_->cltt_,
+                                               lease_->valid_lft_, expire_);
+            data.add(reinterpret_cast<void*>(&expire_));
 
             // subnet_id: int
-            // Can use lease_->subnet_id_ directly as it is of type uint32_t.
-            subnet_id_ = lease_->subnet_id_;
-            data.add(&subnet_id_);
+            subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
+            data.add(reinterpret_cast<void*>(&subnet_id_));
 
             // fqdn_fwd: boolean
-            fqdn_fwd_ = lease_->fqdn_fwd_;
-            data.add(&fqdn_fwd_);
+            fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
+            data.add(reinterpret_cast<void*>(&fqdn_fwd_));
 
             // fqdn_rev: boolean
-            fqdn_rev_ = lease_->fqdn_rev_;
-            data.add(&fqdn_rev_);
+            fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
+            data.add(reinterpret_cast<void*>(&fqdn_rev_));
 
             // hostname: varchar
-            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_);
+            if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
+                isc_throw(BadValue, "hostname "
+                                        << lease_->hostname_ << " of length "
+                                        << lease_->hostname_.size()
+                                        << " exceeds maximum allowed length of "
+                                        << HOSTNAME_MAX_LEN);
             }
-            hostname_buffer_[hostname_length_] = '\0';
-            data.add(hostname_buffer_);
+            hostname_ = lease_->hostname_;
+            data.add(reinterpret_cast<void*>(&hostname_));
 
             // state: int
-            state_ = lease_->state_;
-            data.add(&state_);
-
+            state_ = static_cast<cass_int32_t>(lease_->state_);
+            data.add(reinterpret_cast<void*>(&state_));
         } catch (const std::exception& ex) {
             isc_throw(DbOperationError,
-                      "Could not create bind array from Lease4: "
-                      << lease_->addr_.toText() << ", reason: " << ex.what());
+                      "could not create bind array from Lease4: "
+                          << lease_->addr_.toText()
+                          << ", reason: " << ex.what());
         }
     }
 
     /// @brief Create BIND array to receive data
     ///
     /// Creates a CQL_BIND array to receive Lease4 data from the database.
-    Lease4Ptr createBindForReceive(const CassRow* row) {
-        try {
-            unsigned char* hwaddr_buffer = NULL;
-            const char* client_id_buffer = NULL;
-            const char* hostname_buffer = NULL;
-            CqlDataArray data;
-            CqlDataArray size;
-
-            // address: int
-            data.add(reinterpret_cast<void*>(&addr4_));
-            size.add(NULL);
-
-            // hwaddr: blob
-            data.add(reinterpret_cast<void*>(&hwaddr_buffer));
-            size.add(reinterpret_cast<void*>(&hwaddr_length_));
+    virtual void createBindForReceive(CqlDataArray& data,
+                                      const int /* statementIndex */ = -1) {
+        // address: int
+        data.add(reinterpret_cast<void*>(&addr4_));
 
-            // client_id: blob
-            data.add(reinterpret_cast<void*>(&client_id_buffer));
-            size.add(reinterpret_cast<void*>(&client_id_length_));
+        // hwaddr: blob
+        data.add(reinterpret_cast<void*>(&hwaddr_));
 
-            // valid_lifetime: bigint
-            data.add(reinterpret_cast<void*>(&valid_lifetime_));
-            size.add(NULL);
+        // client_id: blob
+        data.add(reinterpret_cast<void*>(&client_id_));
 
-            // expire: bigint
-            data.add(reinterpret_cast<void*>(&expire_));
-            size.add(NULL);
+        // valid_lifetime: bigint
+        data.add(reinterpret_cast<void*>(&valid_lifetime_));
 
-            // subnet_id: int
-            data.add(reinterpret_cast<void*>(&subnet_id_));
-            size.add(NULL);
+        // expire: bigint
+        data.add(reinterpret_cast<void*>(&expire_));
 
-            // fqdn_fwd: boolean
-            data.add(reinterpret_cast<void*>(&fqdn_fwd_));
-            size.add(NULL);
+        // subnet_id: int
+        data.add(reinterpret_cast<void*>(&subnet_id_));
 
-            // fqdn_rev: boolean
-            data.add(reinterpret_cast<void*>(&fqdn_rev_));
-            size.add(NULL);
+        // fqdn_fwd: boolean
+        data.add(reinterpret_cast<void*>(&fqdn_fwd_));
 
-            // hostname: varchar
-            data.add(reinterpret_cast<void*>(&hostname_buffer));
-            size.add(reinterpret_cast<void*>(&hostname_length_));
-
-            // state: int
-            data.add(reinterpret_cast<void*>(&state_));
-            size.add(NULL);
+        // fqdn_rev: boolean
+        data.add(reinterpret_cast<void*>(&fqdn_rev_));
 
-            for (size_t i = 0; i < data.size(); i++) {
-                CqlLeaseMgr::getData(row, i, data, size, i, *this);
-            }
+        // hostname: varchar
+        data.add(reinterpret_cast<void*>(&hostname_));
 
-            // hwaddr: blob
-            hwaddr_.assign(hwaddr_buffer, hwaddr_buffer + hwaddr_length_);
+        // state: int
+        data.add(reinterpret_cast<void*>(&state_));
+    }
 
-            // client_id: blob
-            client_id_.assign(client_id_buffer, client_id_buffer +
-                client_id_length_);
-            if (client_id_length_ >= sizeof(client_id_buffer_)) {
-                isc_throw(BadValue, "client id value is too large: " <<
-                    client_id_buffer);
-            }
-            if (client_id_length_) {
-                memcpy(client_id_buffer_, client_id_buffer, client_id_length_);
+    Lease4Ptr retrieveLease() {
+        try {
+            // Sanity checks
+            if (hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+                isc_throw(BadValue, "hardware address "
+                                        << HWAddr(hwaddr_, HTYPE_ETHER).toText()
+                                        << " of length " << hwaddr_.size()
+                                        << " exceeds maximum allowed length of "
+                                        << HWAddr::MAX_HWADDR_LEN);
             }
-            client_id_buffer_[client_id_length_] = '\0';
-
-            // hostname: varchar
-            if (hostname_length_ >= sizeof(hostname_buffer_)) {
-                isc_throw(BadValue, "hostname value is too large: " <<
-                    hostname_buffer);
+            if (client_id_.size() > ClientId::MAX_CLIENT_ID_LEN) {
+                isc_throw(BadValue, "client ID "
+                                        << ClientId(client_id_).toText()
+                                        << " of length " << client_id_.size()
+                                        << " exceeds maximum allowed length of "
+                                        << ClientId::MAX_CLIENT_ID_LEN);
             }
-            if (hostname_length_) {
-                memcpy(hostname_buffer_, hostname_buffer, hostname_length_);
+            if (hostname_.size() > HOSTNAME_MAX_LEN) {
+                isc_throw(BadValue, "hostname"
+                                        << hostname_ << " of length "
+                                        << hostname_.size()
+                                        << " exceeds maximum allowed length of "
+                                        << HOSTNAME_MAX_LEN);
             }
-            hostname_buffer_[hostname_length_] = '\0';
 
             time_t cltt = 0;
-            CqlLeaseExchange::convertFromDatabaseTime(expire_, valid_lifetime_,
-                cltt);
+            CqlExchange::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,
+            Lease4Ptr result(new Lease4(addr4_, hwaddr, client_id_.data(),
+                                        client_id_.size(), valid_lifetime_, 0,
                                         0, cltt, subnet_id_, fqdn_fwd_,
-                                        fqdn_rev_, hostname));
+                                        fqdn_rev_, hostname_));
 
             result->state_ = state_;
 
-            return (result);
+            return result;
         } catch (const std::exception& ex) {
             isc_throw(DbOperationError,
-                      "Could not convert data to Lease4, reason: "
-                       << ex.what());
+                      "createBindForReceive(): "
+                      "could not convert data to Lease4, reason: "
+                          << ex.what());
         }
-        return (Lease4Ptr());
+        return Lease4Ptr();
+    }
+
+    void* retrieve() {
+        isc_throw(NotImplemented, "retrieve(): Not implemented yet.");
     }
 
 private:
-    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?
+    Lease4Ptr lease_;                     ///< Pointer to lease object
+    cass_int32_t addr4_;                  ///< IPv4 address
+    std::vector<cass_byte_t> client_id_;  ///< Client identification
 };
 
 /// @brief Exchange CQL and Lease6 Data
@@ -777,53 +685,65 @@ public:
     ///
     /// The initialization of the variables here is nonly to satisfy cppcheck -
     /// all variables are initialized/set in the methods before they are used.
-    CqlLease6Exchange() : addr6_length_(0), duid_length_(0), iaid_(0),
-                          lease_type_(0), prefixlen_(0), pref_lifetime_(0),
-                          hwaddr_null_(false), hwtype_(0), hwaddr_source_(0) {
-        const size_t MAX_COLUMNS = 18U;
-        memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
-        memset(duid_buffer_, 0, sizeof(duid_buffer_));
-
+    CqlLease6Exchange()
+        : pref_lifetime_(0), lease_type_(0), iaid_(0), prefixlen_(0),
+          hwtype_(0), hwaddr_source_(0) {
         // Set the column names
-        size_t offset = 0U;
-        BOOST_STATIC_ASSERT(18U == MAX_COLUMNS);
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("address",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("duid",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("valid_lifetime",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("expire",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_TIMESTAMP)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("subnet_id",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("pref_lifetime",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("lease_type",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("iaid",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("prefix_len",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("fqdn_fwd",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("fqdn_rev",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("hostname",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("hwaddr",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("hwtype",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("hwaddr_source",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("state",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("limit",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
-        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("[applied]",
-            offset++, EXCHANGE_DATA_TYPE_IO_OUT, EXCHANGE_DATA_TYPE_BOOL)));
-        BOOST_ASSERT(parameters_.size() == MAX_COLUMNS);
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "address", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_STRING)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "duid", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_BYTES)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "valid_lifetime", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_INT64)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "expire", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_TIMESTAMP)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "subnet_id", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_INT32)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "pref_lifetime", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_INT64)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "lease_type", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_INT32)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "iaid", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_INT32)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "prefix_len", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_INT32)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "fqdn_fwd", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_BOOL)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "fqdn_rev", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_BOOL)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "hostname", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_STRING)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "hwaddr", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_BYTES)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "hwtype", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_INT32)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "hwaddr_source", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_INT32)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "state", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_INT32)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "limit", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_INT32)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+            "[applied]", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+            EXCHANGE_DATA_TYPE_BOOL)));
+        BOOST_ASSERT(parameters_.size() == 18U);
     }
 
     /// @brief Create CQL_BIND objects for Lease6 Pointer
@@ -832,7 +752,7 @@ public:
     /// the database.
     void createBindForSend(const Lease6Ptr& lease, CqlDataArray& data) {
         if (!lease) {
-            isc_throw(BadValue, "createBindForSend:: Lease6 object is NULL");
+            isc_throw(BadValue, "createBindForSend(): Lease6 object is NULL");
         }
         // Store lease object to ensure it remains valid.
         lease_ = lease;
@@ -841,245 +761,216 @@ public:
         // structure.
         try {
             // address: varchar
-            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);
+            addr6_ = lease_->addr_.toText();
+            if (addr6_.size() > ADDRESS6_TEXT_MAX_LEN) {
+                isc_throw(BadValue,
+                          "address " << addr6_ << " of length " << addr6_.size()
+                                     << " exceeds maximum allowed length of "
+                                     << ADDRESS6_TEXT_MAX_LEN);
             }
-            if (addr6_length_) {
-                memcpy(addr6_buffer_, text_buffer.c_str(), addr6_length_);
-            }
-            addr6_buffer_[addr6_length_] = '\0';
-            data.add(addr6_buffer_);
+            data.add(reinterpret_cast<void*>(&addr6_));
 
             // duid: blob
             if (!lease_->duid_) {
-                isc_throw(DbOperationError, "lease6 for address " <<
-                    addr6_buffer_ << " is missing mandatory client-id");
+                isc_throw(DbOperationError,
+                          "lease6 for address "
+                              << addr6_ << " is missing mandatory duid");
             }
             duid_ = lease_->duid_->getDuid();
-            duid_length_ = duid_.size();
-            data.add(&duid_);
+            data.add(reinterpret_cast<void*>(&duid_));
 
             // valid lifetime: bigint
-            valid_lifetime_ = lease_->valid_lft_;
-            data.add(&valid_lifetime_);
+            valid_lifetime_ = static_cast<cass_int64_t>(lease_->valid_lft_);
+            data.add(reinterpret_cast<void*>(&valid_lifetime_));
 
             // expire: bigint
-            // The lease structure holds the client last transmission time (cltt_)
+            // 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_
-            CqlLeaseExchange::convertToDatabaseTime(lease_->cltt_,
-                lease_->valid_lft_, expire_);
-            data.add(&expire_);
+            CqlExchange::convertToDatabaseTime(lease_->cltt_,
+                                               lease_->valid_lft_, expire_);
+            data.add(reinterpret_cast<void*>(&expire_));
 
             // subnet_id: int
-            // Can use lease_->subnet_id_ directly as it is of type uint32_t.
-            subnet_id_ = lease_->subnet_id_;
-            data.add(&subnet_id_);
+            subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
+            data.add(reinterpret_cast<void*>(&subnet_id_));
 
             // pref_lifetime: bigint
-            // Can use lease_->preferred_lft_ directly as it is of type uint32_t.
-            pref_lifetime_ = lease_->preferred_lft_;
-            data.add(&pref_lifetime_);
+            pref_lifetime_ = static_cast<cass_int64_t>(lease_->preferred_lft_);
+            data.add(reinterpret_cast<void*>(&pref_lifetime_));
 
             // lease_type: int
-            // Must convert to uint8_t as lease_->type_ is a LeaseType variable.
-            lease_type_ = lease_->type_;
-            data.add(&lease_type_);
+            lease_type_ = static_cast<cass_int32_t>(lease_->type_);
+            data.add(reinterpret_cast<void*>(&lease_type_));
 
             // iaid: int
-            // Can use lease_->iaid_ directly as it is of type uint32_t.
-            iaid_ = lease_->iaid_;
-            data.add(&iaid_);
+            iaid_ = static_cast<cass_int32_t>(lease_->iaid_);
+            data.add(reinterpret_cast<void*>(&iaid_));
 
             // prefix_len: int
-            // Can use lease_->prefixlen_ directly as it is uint32_t.
-            prefixlen_ = lease_->prefixlen_;
-            data.add(&prefixlen_);
+            prefixlen_ = static_cast<cass_int32_t>(lease_->prefixlen_);
+            data.add(reinterpret_cast<void*>(&prefixlen_));
 
             // fqdn_fwd: boolean
-            fqdn_fwd_ = lease_->fqdn_fwd_;
-            data.add(&fqdn_fwd_);
+            fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
+            data.add(reinterpret_cast<void*>(&fqdn_fwd_));
 
             // fqdn_rev: boolean
-            fqdn_rev_ = lease_->fqdn_rev_;
-            data.add(&fqdn_rev_);
+            fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
+            data.add(reinterpret_cast<void*>(&fqdn_rev_));
 
             // hostname: varchar
-            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_);
+            if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
+                isc_throw(BadValue, "hostname"
+                                        << lease_->hostname_ << " of length "
+                                        << lease_->hostname_.size()
+                                        << " exceeds maximum allowed length of "
+                                        << HOSTNAME_MAX_LEN);
             }
-            hostname_buffer_[hostname_length_] = '\0';
-            data.add(hostname_buffer_);
+            hostname_ = lease_->hostname_;
+            data.add(reinterpret_cast<void*>(&hostname_));
 
             // hwaddr: blob
-            HWAddrPtr hwaddr = lease_->hwaddr_;
-            if (hwaddr) {
-                if (hwaddr->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
-                    isc_throw(DbOperationError, "Hardware address length : "
-                              << lease_->hwaddr_->hwaddr_.size()
-                              << " exceeds maximum allowed of: "
-                              << HWAddr::MAX_HWADDR_LEN);
+            if (lease_->hwaddr_) {
+                if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+                    isc_throw(BadValue,
+                              "hardware address "
+                                  << lease_->hwaddr_->toText() << " of length "
+                                  << lease_->hwaddr_->hwaddr_.size()
+                                  << " exceeds maximum allowed length of "
+                                  << HWAddr::MAX_HWADDR_LEN);
                 }
-                hwaddr_ = hwaddr->hwaddr_;
+                hwaddr_ = lease_->hwaddr_->hwaddr_;
             } else {
                 hwaddr_.clear();
             }
-            hwaddr_length_ = hwaddr_.size();
-            data.add(&hwaddr_);
+            data.add(reinterpret_cast<void*>(&hwaddr_));
 
             // hwtype: int
-            if (hwaddr) {
-                hwtype_ = lease->hwaddr_->htype_;
+            if (lease_->hwaddr_) {
+                hwtype_ = static_cast<cass_int32_t>(lease_->hwaddr_->htype_);
             } else {
                 hwtype_ = 0;
             }
-            data.add(&hwtype_);
+            data.add(reinterpret_cast<void*>(&hwtype_));
 
             // hwaddr_source: int
-            if (hwaddr) {
-                hwaddr_source_ = lease->hwaddr_->source_;
+            if (lease_->hwaddr_) {
+                hwaddr_source_ =
+                    static_cast<cass_int32_t>(lease_->hwaddr_->source_);
             } else {
                 hwaddr_source_ = 0;
             }
-            data.add(&hwaddr_source_);
+            data.add(reinterpret_cast<void*>(&hwaddr_source_));
 
             // state: int
-            state_ = lease_->state_;
-            data.add(&state_);
-
+            state_ = static_cast<cass_int32_t>(lease_->state_);
+            data.add(reinterpret_cast<void*>(&state_));
         } catch (const std::exception& ex) {
             isc_throw(DbOperationError,
-                      "Could not create bind array from Lease6: "
-                      << lease_->addr_.toText() << ", reason: " << ex.what());
+                      "createBindForSend(): "
+                      "could not create bind array from Lease6: "
+                          << lease_->addr_.toText()
+                          << ", reason: " << ex.what());
         }
     }
 
     /// @brief Create BIND array to receive data
     ///
     /// Creates a CQL_BIND array to receive Lease6 data from the database.
-    Lease6Ptr createBindForReceive(const CassRow* row) {
-        try {
-            unsigned char* duid_buffer = NULL;
-            unsigned char* hwaddr_buffer = NULL;
-            const char* address_buffer = NULL;
-            const char* hostname_buffer = NULL;
-            CqlDataArray data;
-            CqlDataArray size;
+    void createBindForReceive(CqlDataArray& data,
+                              const int /* statementIndex */ = -1) {
+        // address: varchar
+        data.add(reinterpret_cast<void*>(&addr6_));
 
-            // address: varchar
-            data.add(reinterpret_cast<void*>(&address_buffer));
-            size.add(reinterpret_cast<void*>(&addr6_length_));
+        // duid: blob
+        data.add(reinterpret_cast<void*>(&duid_));
 
-            // duid: blob
-            data.add(reinterpret_cast<void*>(&duid_buffer));
-            size.add(reinterpret_cast<void*>(&duid_length_));
+        // valid_lifetime_: bigint
+        data.add(reinterpret_cast<void*>(&valid_lifetime_));
 
-            // valid_lifetime_: bigint
-            data.add(reinterpret_cast<void*>(&valid_lifetime_));
-            size.add(NULL);
+        // expire: bigint
+        data.add(reinterpret_cast<void*>(&expire_));
 
-            // expire: bigint
-            data.add(reinterpret_cast<void*>(&expire_));
-            size.add(NULL);
+        // subnet_id: int
+        data.add(reinterpret_cast<void*>(&subnet_id_));
 
-            // subnet_id: int
-            data.add(reinterpret_cast<void*>(&subnet_id_));
-            size.add(NULL);
+        // pref_lifetime: bigint
+        data.add(reinterpret_cast<void*>(&pref_lifetime_));
 
-            // pref_lifetime: bigint
-            data.add(reinterpret_cast<void*>(&pref_lifetime_));
-            size.add(NULL);
+        // lease_type: int
+        data.add(reinterpret_cast<void*>(&lease_type_));
 
-            // lease_type: int
-            data.add(reinterpret_cast<void*>(&lease_type_));
-            size.add(NULL);
-
-            // iaid: int
-            data.add(reinterpret_cast<void*>(&iaid_));
-            size.add(NULL);
-
-            // prefix_len: int
-            data.add(reinterpret_cast<void*>(&prefixlen_));
-            size.add(NULL);
+        // iaid: int
+        data.add(reinterpret_cast<void*>(&iaid_));
 
-            // fqdn_fwd: boolean
-            data.add(reinterpret_cast<void*>(&fqdn_fwd_));
-            size.add(NULL);
+        // prefix_len: int
+        data.add(reinterpret_cast<void*>(&prefixlen_));
 
-            // fqdn_rev: boolean
-            data.add(reinterpret_cast<void*>(&fqdn_rev_));
-            size.add(NULL);
+        // fqdn_fwd: boolean
+        data.add(reinterpret_cast<void*>(&fqdn_fwd_));
 
-            // hostname: varchar
-            data.add(reinterpret_cast<void*>(&hostname_buffer));
-            size.add(reinterpret_cast<void*>(&hostname_length_));
+        // fqdn_rev: boolean
+        data.add(reinterpret_cast<void*>(&fqdn_rev_));
 
-            // hwaddr: blob
-            data.add(reinterpret_cast<void*>(&hwaddr_buffer));
-            size.add(reinterpret_cast<void*>(&hwaddr_length_));
+        // hostname: varchar
+        data.add(reinterpret_cast<void*>(&hostname_));
 
-            // hwtype: int
-            data.add(reinterpret_cast<void*>(&hwtype_));
-            size.add(NULL);
+        // hwaddr: blob
+        data.add(reinterpret_cast<void*>(&hwaddr_));
 
-            // hwaddr_source: int
-            data.add(reinterpret_cast<void*>(&hwaddr_source_));
-            size.add(NULL);
+        // hwtype: int
+        data.add(reinterpret_cast<void*>(&hwtype_));
 
-            // state: int
-            data.add(reinterpret_cast<void*>(&state_));
-            size.add(NULL);
+        // hwaddr_source: int
+        data.add(reinterpret_cast<void*>(&hwaddr_source_));
 
-            for (size_t i = 0; i < data.size(); i++) {
-                CqlLeaseMgr::getData(row, i, data, size, i, *this);
-            }
+        // state: int
+        data.add(reinterpret_cast<void*>(&state_));
+    }
 
-            // address: varchar
-            if (addr6_length_ >= sizeof(addr6_buffer_)) {
-                isc_throw(BadValue, "address value is too large: " <<
-                    address_buffer);
+    Lease6Ptr retrieveLease() {
+        try {
+            // Sanity checks
+            if (addr6_.size() > ADDRESS6_TEXT_MAX_LEN) {
+                isc_throw(BadValue,
+                          "address " << addr6_ << " of length " << addr6_.size()
+                                     << " exceeds maximum allowed length of "
+                                     << ADDRESS6_TEXT_MAX_LEN);
             }
-            if (addr6_length_) {
-                memcpy(addr6_buffer_, address_buffer, addr6_length_);
+            if (duid_.size() > DUID::MAX_DUID_LEN) {
+                isc_throw(BadValue, "duid "
+                                        << DUID(duid_).toText() << " of length "
+                                        << duid_.size()
+                                        << " exceeds maximum allowed length of "
+                                        << DUID::MAX_DUID_LEN);
             }
-            addr6_buffer_[addr6_length_] = '\0';
-
-            // duid: blob
-            duid_.assign(duid_buffer, duid_buffer + duid_length_);
-
-            // hostname: varchar
-            if (hostname_length_ >= sizeof(hostname_buffer_)) {
-                isc_throw(BadValue, "hostname value is too large: " <<
-                    hostname_buffer);
+            if (lease_type_ != Lease::TYPE_NA &&
+                lease_type_ != Lease::TYPE_TA &&
+                lease_type_ != Lease::TYPE_PD) {
+                isc_throw(BadValue, "invalid lease type "
+                                        << lease_type_
+                                        << " for lease with address " << addr6_
+                                        << ". Expected 0, 1 or 2.");
             }
-            if (hostname_length_) {
-                memcpy(hostname_buffer_, hostname_buffer, hostname_length_);
+            if (hostname_.size() > HOSTNAME_MAX_LEN) {
+                isc_throw(BadValue, "hostname "
+                                        << hostname_ << " of length "
+                                        << hostname_.size()
+                                        << " exceeds maximum allowed length of "
+                                        << HOSTNAME_MAX_LEN);
             }
-            hostname_buffer_[hostname_length_] = '\0';
-
-            // hwaddr: blob
-            hwaddr_.assign(hwaddr_buffer, hwaddr_buffer + hwaddr_length_);
-
-            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");
+            if (hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+                isc_throw(BadValue,
+                          "hwaddr " << HWAddr(hwaddr_, hwtype_).toText(false)
+                                    << " of length " << hwaddr_.size()
+                                    << " exceeds maximum allowed length of "
+                                    << HWAddr::MAX_HWADDR_LEN);
             }
 
-            isc::asiolink::IOAddress addr(addr6_buffer_);
+            isc::asiolink::IOAddress addr(addr6_);
             DuidPtr duid(new DUID(duid_));
             HWAddrPtr hwaddr;
             if (hwaddr_.size()) {
@@ -1087,53 +978,50 @@ public:
                 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_));
+            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;
-            CqlLeaseExchange::convertFromDatabaseTime(expire_, valid_lifetime_,
-                cltt);
+            CqlExchange::convertFromDatabaseTime(expire_, valid_lifetime_,
+                                                 cltt);
             result->cltt_ = cltt;
 
             result->state_ = state_;
-
-            return (result);
+            return result;
         } catch (const std::exception& ex) {
             isc_throw(DbOperationError,
-                      "Could not convert data to Lease4, reason: "
-                       << ex.what());
+                      "createBindForReceive(): "
+                      "could not convert data to Lease4, reason: "
+                          << ex.what());
         }
-        return (Lease6Ptr());
+        return Lease6Ptr();
+    }
+
+    void* retrieve() {
+        isc_throw(NotImplemented, "retrieve(): Not implemented yet.");
     }
 
 private:
-    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
+    Lease6Ptr lease_;                ///< Pointer to lease object
+    std::string addr6_;              ///< IPv6 address
+    std::vector<cass_byte_t> duid_;  ///< Client identification
+    cass_int64_t pref_lifetime_;     ///< Preferred lifetime
+    cass_int32_t lease_type_;        ///< Lease type
+    cass_int32_t iaid_;              ///< Identity association ID
+    cass_int32_t prefixlen_;         ///< Prefix length
+    cass_int32_t hwtype_;            ///< Hardware type
+    cass_int32_t hwaddr_source_;     ///< Source of the hardware
+                                     /// address
 };
 
 CqlLeaseMgr::CqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
     : LeaseMgr(), dbconn_(parameters), exchange4_(new CqlLease4Exchange()),
-    exchange6_(new CqlLease6Exchange()), versionExchange_(new CqlVersionExchange()) {
+      exchange6_(new CqlLease6Exchange()),
+      versionExchange_(new CqlVersionExchange()) {
     dbconn_.openDatabase();
     dbconn_.prepareStatements(CqlLeaseMgr::tagged_statements_);
 }
@@ -1148,81 +1036,45 @@ CqlLeaseMgr::getDBVersion() {
     std::stringstream tmp;
     tmp << "CQL backend " << CQL_SCHEMA_VERSION_MAJOR;
     tmp << "." << CQL_SCHEMA_VERSION_MINOR;
-    tmp << ", library " << "cassandra_static";
-    return (tmp.str());
-}
-
-ExchangeDataType
-CqlLeaseMgr::getDataType(const StatementIndex stindex, int pindex,
-        const SqlExchange& exchange) {
-    if (CqlLeaseMgr::tagged_statements_[stindex].params_ &&
-            CqlLeaseMgr::tagged_statements_[stindex].params_[pindex]) {
-        const ExchangeColumnInfoContainerName& idx = exchange.parameters_.get<1>();
-        const ExchangeColumnInfoContainerNameRange& range =
-            idx.equal_range(CqlLeaseMgr::tagged_statements_[stindex].params_[pindex]);
-        if (std::distance(range.first, range.second) > 0) {
-            return (*range.first)->type_;
-        }
-    }
-    return EXCHANGE_DATA_TYPE_NONE;
-}
-
-void
-CqlLeaseMgr::bindData(CassStatement* statement, const StatementIndex stindex,
-        CqlDataArray& data, const SqlExchange& exchange) {
-    if (CqlLeaseMgr::tagged_statements_[stindex].params_ == NULL) {
-        return;
-    }
-    for (int i = 0; CqlLeaseMgr::tagged_statements_[stindex].params_[i]; i++) {
-        ExchangeDataType type = CqlLeaseMgr::getDataType(stindex, i, exchange);
-        if (type >= sizeof(CqlFunctions) / sizeof(CqlFunctions[0])) {
-            isc_throw(BadValue, "index " << stindex << " out of bounds");
-        }
-        CqlFunctions[type].sqlBindFunction_(statement, i, data.values_[i]);
-    }
-}
-
-void
-CqlLeaseMgr::getData(const CassRow* row, const int pindex, CqlDataArray& data,
-        CqlDataArray& size, const int dindex, const SqlExchange& exchange) {
-    if (pindex >= exchange.parameters_.size()) {
-        return;
-    }
-    const ExchangeColumnInfoContainerIndex& idx = exchange.parameters_.get<2>();
-    const ExchangeColumnInfoContainerIndexRange& range = idx.equal_range(pindex);
-    if (std::distance(range.first, range.second) > 0) {
-        std::string name = (*range.first)->name_;
-        ExchangeDataType type = (*range.first)->type_;
-        const CassValue* value = cass_row_get_column_by_name(row, name.c_str());
-        if (NULL == value) {
-            isc_throw(BadValue, "column name " << name << " doesn't exist");
-        }
-        if (type >= sizeof(CqlFunctions) / sizeof(CqlFunctions[0])) {
-            isc_throw(BadValue, "index " << type << " out of bounds");
-        }
-        CqlFunctions[type].sqlGetFunction_(value, data.values_[dindex],
-            reinterpret_cast<size_t *>(size.values_[dindex]));
-    }
+    tmp << ", library cassandra_static";
+    return tmp.str();
 }
 
 bool
 CqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
-                              CqlDataArray& data, CqlLeaseExchange& exchange) {
+                            CqlDataArray& data,
+                            CqlLeaseExchange& exchange) {
     CassError rc;
     CassStatement* statement = NULL;
     CassFuture* future = NULL;
 
     statement = cass_prepared_bind(dbconn_.statements_[stindex]);
-    if (NULL == statement) {
-        isc_throw(DbOperationError, "unable to bind statement");
+    if (!statement) {
+        isc_throw(DbOperationError,
+                  "unable to bind statement "
+                      << dbconn_.tagged_statements_[stindex].name_);
     }
 
-    CqlLeaseMgr::bindData(statement, stindex, data, exchange);
+    if (dbconn_.force_consistency_) {
+        rc = cass_statement_set_consistency(statement, dbconn_.consistency_);
+        if (rc != CASS_OK) {
+            cass_statement_free(statement);
+            isc_throw(
+                DbOperationError,
+                "unable to set statement consistency for statement "
+                    << dbconn_.tagged_statements_[stindex].name_);
+        }
+    }
+
+    CqlCommon::bindData(statement, stindex, data, exchange,
+                        CqlLeaseMgr::tagged_statements_);
 
     future = cass_session_execute(dbconn_.session_, statement);
-    if (NULL == future) {
+    if (!future) {
         cass_statement_free(statement);
-        isc_throw(DbOperationError, "unable to execute statement");
+        isc_throw(DbOperationError,
+                  "unable to execute statement "
+                      << dbconn_.tagged_statements_[stindex].name_);
     }
     cass_future_wait(future);
     std::string error;
@@ -1235,78 +1087,81 @@ CqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
     }
 
     // Check if statement has been applied.
-    const CassResult* resultCollection = cass_future_get_result(future);
-    CassIterator* rows = cass_iterator_from_result(resultCollection);
-    CqlDataArray appliedData;
-    CqlDataArray appliedSize;
-    bool applied = false;
-    while (cass_iterator_next(rows)) {
-        const CassRow* row = cass_iterator_get_row(rows);
-        // [applied]: bool
-        appliedData.add(reinterpret_cast<void*>(&applied));
-        appliedSize.add(NULL);
-        CqlLeaseMgr::getData(row, exchange.parameters_.size() - 1, appliedData,
-                             appliedSize, 0, exchange);
-    }
+    bool applied = exchange.hasStatementBeenApplied(future);
 
     // Free resources.
-    cass_iterator_free(rows);
-    cass_result_free(resultCollection);
     cass_future_free(future);
     cass_statement_free(statement);
-
     return applied;
 }
 
 bool
 CqlLeaseMgr::addLease(const Lease4Ptr& lease) {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_ADD_ADDR4).arg(lease->addr_.toText());
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ADD_ADDR4)
+        .arg(lease->addr_.toText());
 
     CqlDataArray data;
     exchange4_->createBindForSend(lease, data);
-    return (addLeaseCommon(INSERT_LEASE4, data, *exchange4_));
+    return addLeaseCommon(INSERT_LEASE4, data, *exchange4_);
 }
 
 bool
 CqlLeaseMgr::addLease(const Lease6Ptr& lease) {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_ADD_ADDR6).arg(lease->addr_.toText());
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ADD_ADDR6)
+        .arg(lease->addr_.toText());
 
     CqlDataArray data;
     exchange6_->createBindForSend(lease, data);
-    return (addLeaseCommon(INSERT_LEASE6, data, *exchange6_));
+    return addLeaseCommon(INSERT_LEASE6, data, *exchange6_);
 }
 
 template <typename Exchange, typename LeaseCollection>
-void CqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
-                                       CqlDataArray& data,
-                                       Exchange& exchange,
-                                       LeaseCollection& result,
-                                       bool single) const {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_GET_ADDR4).arg(dbconn_.tagged_statements_[stindex].name_);
+void
+CqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
+                                CqlDataArray& data,
+                                Exchange& exchange,
+                                LeaseCollection& result,
+                                bool single) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR4)
+        .arg(dbconn_.tagged_statements_[stindex].name_);
 
     CassError rc;
     CassStatement* statement = NULL;
     CassFuture* future = NULL;
-    const CqlLeaseExchange& leaseExchange = static_cast<CqlLeaseExchange>(*exchange);
 
     statement = cass_prepared_bind(dbconn_.statements_[stindex]);
-    if (NULL == statement) {
+    if (!statement) {
         isc_throw(DbOperationError, "unable to bind statement");
     }
 
-    CqlLeaseMgr::bindData(statement, stindex, data, leaseExchange);
+    if (dbconn_.force_consistency_) {
+        rc = cass_statement_set_consistency(statement, dbconn_.consistency_);
+        if (rc != CASS_OK) {
+            cass_statement_free(statement);
+            isc_throw(
+                DbOperationError,
+                "unable to set statement consistency for statement "
+                    << dbconn_.tagged_statements_[stindex].name_);
+        }
+    }
+
+    CqlCommon::bindData(statement, stindex, data,
+                        static_cast<SqlExchange>(*exchange),
+                        CqlLeaseMgr::tagged_statements_);
 
     future = cass_session_execute(dbconn_.session_, statement);
-    if (NULL == future) {
+    if (!future) {
         cass_statement_free(statement);
-        isc_throw(DbOperationError, "unable to execute statement");
+        isc_throw(DbOperationError,
+                  "unable to execute statement "
+                      << dbconn_.tagged_statements_[stindex].name_);
     }
     cass_future_wait(future);
     std::string error;
-    dbconn_.checkStatementError(error, future, "unable to GET");
+    std::stringstream message;
+    message << "unable to GET using statement "
+            << CqlLeaseMgr::tagged_statements_[stindex].name_;
+    dbconn_.checkStatementError(error, future, message.str().c_str());
     rc = cass_future_error_code(future);
     if (rc != CASS_OK) {
         cass_future_free(future);
@@ -1324,7 +1179,13 @@ void CqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
             break;
         }
         const CassRow* row = cass_iterator_get_row(rows);
-        result.push_back(exchange->createBindForReceive(row));
+        CqlDataArray data;
+        exchange->createBindForReceive(data);
+        // Get data.
+        for (size_t i = 0U; i < data.size(); i++) {
+            CqlCommon::getData(row, i, i, *exchange, data);
+        }
+        result.push_back(exchange->retrieveLease());
     }
 
     cass_iterator_free(rows);
@@ -1332,15 +1193,17 @@ void CqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
     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 "
+        isc_throw(MultipleRecords,
+                  "multiple records were found in the "
+                  "database where only one was expected for statement "
                       << dbconn_.tagged_statements_[stindex].name_);
     }
 }
 
 void
-CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data,
-                             Lease4Ptr& result) const {
+CqlLeaseMgr::getLease(StatementIndex stindex,
+                      CqlDataArray& data,
+                      Lease4Ptr& result) const {
     // Create appropriate collection object and get all leases matching
     // the selection criteria. The "single" parameter is true to indicate
     // that the called method should throw an exception if multiple
@@ -1358,8 +1221,9 @@ CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data,
 }
 
 void
-CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data,
-                             Lease6Ptr& result) const {
+CqlLeaseMgr::getLease(StatementIndex stindex,
+                      CqlDataArray& data,
+                      Lease6Ptr& result) const {
     // Create appropriate collection object and get all leases matching
     // the selection criteria. The "single" parameter is true to indicate
     // that the called method should throw an exception if multiple
@@ -1381,246 +1245,260 @@ CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data,
 
 Lease4Ptr
 CqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_GET_ADDR4).arg(addr.toText());
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR4)
+        .arg(addr.toText());
 
     // Set up the WHERE clause value
     CqlDataArray data;
 
-    uint32_t addr4_data = addr.toUint32();
-    data.add(&addr4_data);
+    cass_int32_t addr4_data = static_cast<cass_int32_t>(addr.toUint32());
+    data.add(reinterpret_cast<void*>(&addr4_data));
 
     // Get the data
     Lease4Ptr result;
     getLease(GET_LEASE4_ADDR, data, result);
 
-    return (result);
+    return result;
 }
 
 Lease4Collection
 CqlLeaseMgr::getLease4(const HWAddr& hwaddr) const {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_GET_HWADDR).arg(hwaddr.toText());
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_HWADDR)
+        .arg(hwaddr.toText());
 
     // Set up the WHERE clause value
     CqlDataArray data;
 
-    std::vector<uint8_t>hwaddr_data = hwaddr.hwaddr_;
-    data.add(&hwaddr_data);
+    std::vector<cass_byte_t> hwaddr_data = hwaddr.hwaddr_;
+    data.add(reinterpret_cast<void*>(&hwaddr_data));
 
     // Get the data
     Lease4Collection result;
     getLeaseCollection(GET_LEASE4_HWADDR, data, result);
 
-    return (result);
+    return result;
 }
 
 Lease4Ptr
 CqlLeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_GET_SUBID_HWADDR)
-              .arg(subnet_id).arg(hwaddr.toText());
+        .arg(subnet_id)
+        .arg(hwaddr.toText());
 
     // Set up the WHERE clause value
     CqlDataArray data;
 
-    std::vector<uint8_t>hwaddr_data = hwaddr.hwaddr_;
-    data.add(&hwaddr_data);
+    std::vector<cass_byte_t> hwaddr_data = hwaddr.hwaddr_;
+    data.add(reinterpret_cast<void*>(&hwaddr_data));
 
-    uint32_t subnet_id_data = subnet_id;
-    data.add(&subnet_id_data);
+    cass_int32_t subnet_id_data = static_cast<cass_int32_t>(subnet_id);
+    data.add(reinterpret_cast<void*>(&subnet_id_data));
 
     // Get the data
     Lease4Ptr result;
     getLease(GET_LEASE4_HWADDR_SUBID, data, result);
 
-    return (result);
+    return result;
 }
 
 Lease4Collection
 CqlLeaseMgr::getLease4(const ClientId& clientid) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_GET_CLIENTID).arg(clientid.toText());
+              DHCPSRV_CQL_GET_CLIENTID)
+        .arg(clientid.toText());
 
     // Set up the WHERE clause value
     CqlDataArray data;
 
-    std::vector<uint8_t> client_id_data = clientid.getClientId();
-    data.add(&client_id_data);
+    std::vector<cass_byte_t> client_id_data = clientid.getClientId();
+    data.add(reinterpret_cast<void*>(&client_id_data));
 
     // Get the data
     Lease4Collection result;
     getLeaseCollection(GET_LEASE4_CLIENTID, data, result);
 
-    return (result);
+    return result;
 }
 
 Lease4Ptr
-CqlLeaseMgr::getLease4(const ClientId& clientid, const HWAddr& hwaddr,
-        SubnetID subnet_id) const {
-    /// This function is currently not implemented because allocation engine
+CqlLeaseMgr::getLease4(const ClientId& clientid,
+                       const HWAddr& hwaddr,
+                       SubnetID subnet_id) const {
+    /// This method is currently not implemented because allocation engine
     /// searches for the lease using HW address or client identifier.
     /// It never uses both parameters in the same time. We need to
-    /// consider if this function is needed at all.
+    /// consider if this method is needed at all.
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_GET_CLIENTID_HWADDR_SUBID).arg(clientid.toText())
-              .arg(hwaddr.toText()).arg(subnet_id);
+              DHCPSRV_CQL_GET_CLIENTID_HWADDR_SUBID)
+        .arg(clientid.toText())
+        .arg(hwaddr.toText())
+        .arg(subnet_id);
 
-    isc_throw(NotImplemented, "The CqlLeaseMgr::getLease4 function was"
-              " called, but it is not implemented");
+    isc_throw(NotImplemented, "The CqlLeaseMgr::getLease4 method was"
+                              " called, but it is not implemented");
 }
 
 Lease4Ptr
 CqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_GET_SUBID_CLIENTID)
-              .arg(subnet_id).arg(clientid.toText());
+        .arg(subnet_id)
+        .arg(clientid.toText());
 
     // Set up the WHERE clause value
     CqlDataArray data;
 
     std::vector<uint8_t> client_id_data = clientid.getClientId();
-    data.add(&client_id_data);
+    data.add(reinterpret_cast<void*>(&client_id_data));
 
-    uint32_t subnet_id_data = subnet_id;
-    data.add(&subnet_id_data);
+    cass_int32_t subnet_id_data = static_cast<cass_int32_t>(subnet_id);
+    data.add(reinterpret_cast<void*>(&subnet_id_data));
 
     // Get the data
     Lease4Ptr result;
     getLease(GET_LEASE4_CLIENTID_SUBID, data, result);
 
-    return (result);
+    return result;
 }
 
 Lease6Ptr
 CqlLeaseMgr::getLease6(Lease::Type lease_type,
-                         const isc::asiolink::IOAddress& addr) const {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_GET_ADDR6).arg(addr.toText())
-              .arg(lease_type);
+                       const isc::asiolink::IOAddress& addr) const {
+    std::string addr_data = addr.toText();
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR6)
+        .arg(addr_data)
+        .arg(lease_type);
 
     // Set up the WHERE clause value
     CqlDataArray data;
 
-    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);
+    if (addr_data.size() > ADDRESS6_TEXT_MAX_LEN) {
+        isc_throw(BadValue, "getLease6(): "
+                            "address "
+                                << addr_data << " of length "
+                                << addr_data.size()
+                                << " exceeds maximum allowed length of "
+                                << ADDRESS6_TEXT_MAX_LEN);
     }
-    addr6_buffer[addr6_length] = '\0';
-    data.add(addr6_buffer);
+    data.add(reinterpret_cast<void*>(&addr_data));
 
-    uint32_t lease_type_data = lease_type;
-    data.add(&lease_type_data);
+    cass_int32_t lease_type_data = static_cast<cass_int32_t>(lease_type);
+    data.add(reinterpret_cast<void*>(&lease_type_data));
 
     Lease6Ptr result;
     getLease(GET_LEASE6_ADDR, data, result);
 
-    return (result);
+    return result;
 }
 
 Lease6Collection
 CqlLeaseMgr::getLeases6(Lease::Type lease_type,
-                          const DUID& duid, uint32_t iaid) const {
+                        const DUID& duid,
+                        uint32_t iaid) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_GET_IAID_DUID).arg(iaid).arg(duid.toText())
-              .arg(lease_type);
+              DHCPSRV_CQL_GET_IAID_DUID)
+        .arg(iaid)
+        .arg(duid.toText())
+        .arg(lease_type);
 
     // Set up the WHERE clause value
     CqlDataArray data;
 
-    std::vector<uint8_t> duid_data = duid.getDuid();
-    data.add(&duid_data);
+    std::vector<cass_byte_t> duid_data = duid.getDuid();
+    data.add(reinterpret_cast<void*>(&duid_data));
 
-    uint32_t iaid_data = iaid;
-    data.add(&iaid_data);
+    cass_int32_t iaid_data = static_cast<cass_int32_t>(iaid);
+    data.add(reinterpret_cast<void*>(&iaid_data));
 
-    uint32_t lease_type_data = lease_type;
-    data.add(&lease_type_data);
+    cass_int32_t lease_type_data = static_cast<cass_int32_t>(lease_type);
+    data.add(reinterpret_cast<void*>(&lease_type_data));
 
     // ... and get the data
     Lease6Collection result;
     getLeaseCollection(GET_LEASE6_DUID_IAID, data, result);
 
-    return (result);
+    return result;
 }
 
 Lease6Collection
 CqlLeaseMgr::getLeases6(Lease::Type lease_type,
-                          const DUID& duid, uint32_t iaid,
-                          SubnetID subnet_id) const {
+                        const DUID& duid,
+                        uint32_t iaid,
+                        SubnetID subnet_id) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_GET_IAID_SUBID_DUID)
-              .arg(iaid).arg(subnet_id).arg(duid.toText())
-              .arg(lease_type);
+        .arg(iaid)
+        .arg(subnet_id)
+        .arg(duid.toText())
+        .arg(lease_type);
 
     // Set up the WHERE clause value
     CqlDataArray data;
 
-    std::vector<uint8_t> duid_data = duid.getDuid();
-    data.add(&duid_data);
+    std::vector<cass_byte_t> duid_data = duid.getDuid();
+    data.add(reinterpret_cast<void*>(&duid_data));
 
-    uint32_t iaid_data = iaid;
-    data.add(&iaid_data);
+    cass_int32_t iaid_data = static_cast<cass_int32_t>(iaid);
+    data.add(reinterpret_cast<void*>(&iaid_data));
 
-    uint32_t subnet_id_data = subnet_id;
-    data.add(&subnet_id_data);
+    cass_int32_t subnet_id_data = static_cast<cass_int32_t>(subnet_id);
+    data.add(reinterpret_cast<void*>(&subnet_id_data));
 
-    uint32_t lease_type_data = lease_type;
-    data.add(&lease_type_data);
+    cass_int32_t lease_type_data = static_cast<cass_int32_t>(lease_type);
+    data.add(reinterpret_cast<void*>(&lease_type_data));
 
     // ... and get the data
     Lease6Collection result;
     getLeaseCollection(GET_LEASE6_DUID_IAID_SUBID, data, result);
 
-    return (result);
+    return result;
 }
 
 void
 CqlLeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases,
-                                 const size_t max_leases) const {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_EXPIRED6)
+                               const size_t max_leases) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_CQL_GET_EXPIRED6)
         .arg(max_leases);
     getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE6_EXPIRE);
 }
 
 void
 CqlLeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases,
-                                 const size_t max_leases) const {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_EXPIRED4)
+                               const size_t max_leases) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_CQL_GET_EXPIRED4)
         .arg(max_leases);
     getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE4_EXPIRE);
 }
 
-template<typename LeaseCollection>
+template <typename LeaseCollection>
 void
 CqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
-                                       const size_t max_leases,
-                                       StatementIndex statement_index) const {
+                                    const size_t max_leases,
+                                    StatementIndex statement_index) const {
     // Set up the WHERE clause value
-    uint32_t keepState = Lease::STATE_EXPIRED_RECLAIMED;
-    uint64_t timestamp = static_cast<int64_t>(time(NULL));
+    cass_int32_t keepState = Lease::STATE_EXPIRED_RECLAIMED;
+    cass_int64_t timestamp = static_cast<cass_int64_t>(time(NULL));
 
     // If the number of leases is 0, we will return all leases. This is
     // achieved by setting the limit to a very high value.
-    uint32_t limit = max_leases > 0 ? static_cast<int32_t>(max_leases) :
-        std::numeric_limits<int32_t>::max();
+    cass_int32_t limit = max_leases > 0 ?
+                             static_cast<cass_int32_t>(max_leases) :
+                             std::numeric_limits<cass_int32_t>::max();
 
-    for (uint32_t state = Lease::STATE_DEFAULT;
-            state <= Lease::STATE_EXPIRED_RECLAIMED; state++) {
+    for (cass_int32_t state = Lease::STATE_DEFAULT;
+         state <= Lease::STATE_EXPIRED_RECLAIMED; state++) {
         if (state == keepState) {
             continue;
         }
         LeaseCollection tempCollection;
         CqlDataArray data;
 
-        data.add(&state);
-        data.add(&timestamp);
-        data.add(&limit);
+        data.add(reinterpret_cast<void*>(&state));
+        data.add(reinterpret_cast<void*>(&timestamp));
+        data.add(reinterpret_cast<void*>(&limit));
 
         // Retrieve leases from the database.
         getLeaseCollection(statement_index, data, tempCollection);
@@ -1628,35 +1506,48 @@ CqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
         typedef typename LeaseCollection::iterator LeaseCollectionIt;
 
         for (LeaseCollectionIt it = tempCollection.begin();
-                it != tempCollection.end(); ++it) {
+             it != tempCollection.end(); ++it) {
             expired_leases.push_back((*it));
         }
     }
 }
 
-template <typename LeasePtr>
 void
 CqlLeaseMgr::updateLeaseCommon(StatementIndex stindex,
-                                 CqlDataArray& data,
-                                 const LeasePtr&, CqlLeaseExchange& exchange) {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_ADD_ADDR4).arg(dbconn_.tagged_statements_[stindex].name_);
+                               CqlDataArray& data,
+                               CqlLeaseExchange& exchange) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ADD_ADDR4)
+        .arg(dbconn_.tagged_statements_[stindex].name_);
 
     CassError rc;
     CassStatement* statement = NULL;
     CassFuture* future = NULL;
 
     statement = cass_prepared_bind(dbconn_.statements_[stindex]);
-    if (NULL == statement) {
+    if (!statement) {
         isc_throw(DbOperationError, "unable to bind statement");
     }
 
-    CqlLeaseMgr::bindData(statement, stindex, data, exchange);
+    if (dbconn_.force_consistency_) {
+        rc = cass_statement_set_consistency(statement, dbconn_.consistency_);
+        if (rc != CASS_OK) {
+            cass_statement_free(statement);
+            isc_throw(
+                DbOperationError,
+                "unable to set statement consistency for statement "
+                    << dbconn_.tagged_statements_[stindex].name_);
+        }
+    }
+
+    CqlCommon::bindData(statement, stindex, data, exchange,
+                        CqlLeaseMgr::tagged_statements_);
 
     future = cass_session_execute(dbconn_.session_, statement);
-    if (NULL == future) {
+    if (!future) {
         cass_statement_free(statement);
-        isc_throw(DbOperationError, "unable to execute statement");
+        isc_throw(DbOperationError,
+                  "unable to execute statement "
+                      << dbconn_.tagged_statements_[stindex].name_);
     }
     cass_future_wait(future);
     std::string error;
@@ -1669,23 +1560,12 @@ CqlLeaseMgr::updateLeaseCommon(StatementIndex stindex,
     }
 
     // Check if statement has been applied.
-    const CassResult* resultCollection = cass_future_get_result(future);
-    CassIterator* rows = cass_iterator_from_result(resultCollection);
-    CqlDataArray appliedData;
-    CqlDataArray appliedSize;
-    bool applied = false;
-    while (cass_iterator_next(rows)) {
-        const CassRow* row = cass_iterator_get_row(rows);
-        // [applied]: bool
-        appliedData.add(reinterpret_cast<void*>(&applied));
-        appliedSize.add(NULL);
-        CqlLeaseMgr::getData(row, exchange.parameters_.size() - 1, appliedData,
-                             appliedSize, 0, exchange);
-    }
+    size_t row_count;
+    size_t column_count;
+    bool applied =
+        exchange.hasStatementBeenApplied(future, &row_count, &column_count);
 
     // Free resources.
-    cass_iterator_free(rows);
-    cass_result_free(resultCollection);
     cass_future_free(future);
     cass_statement_free(statement);
 
@@ -1699,27 +1579,30 @@ CqlLeaseMgr::updateLease4(const Lease4Ptr& lease) {
     const StatementIndex stindex = UPDATE_LEASE4;
 
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_UPDATE_ADDR4).arg(lease->addr_.toText());
+              DHCPSRV_CQL_UPDATE_ADDR4)
+        .arg(lease->addr_.toText());
 
     // Create the BIND array for the data being updated
     CqlDataArray data;
     exchange4_->createBindForSend(lease, data);
     data.remove(0);
 
-    // Set up the WHERE clause and append it to the SQL_BIND array
-    uint32_t addr4_data = lease->addr_.toUint32();
-    data.add(&addr4_data);
+    // Set up the WHERE clause and append it to the bind array.
+    cass_int32_t addr4_data =
+        static_cast<cass_int32_t>(lease->addr_.toUint32());
+    data.add(reinterpret_cast<void*>(&addr4_data));
 
     // Drop to common update code
-    updateLeaseCommon(stindex, data, lease, *exchange4_);
+    updateLeaseCommon(stindex, data, *exchange4_);
 }
 
 void
 CqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
     const StatementIndex stindex = UPDATE_LEASE6;
-
+    std::string lease_addr_data = lease->addr_.toText();
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_UPDATE_ADDR6).arg(lease->addr_.toText());
+              DHCPSRV_CQL_UPDATE_ADDR6)
+        .arg(lease_addr_data);
 
     // Create the BIND array for the data being updated
     CqlDataArray data;
@@ -1727,40 +1610,54 @@ CqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
     data.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 (lease_addr_data.size() > ADDRESS6_TEXT_MAX_LEN) {
+        isc_throw(BadValue, "updateLease6(): "
+                            "address "
+                                << lease_addr_data << " of length "
+                                << lease_addr_data.size()
+                                << " exceeds maximum allowed length "
+                                   "of "
+                                << ADDRESS6_TEXT_MAX_LEN);
     }
-    if (addr6_length) {
-        memcpy(addr6_buffer, text_buffer.c_str(), addr6_length);
-    }
-    addr6_buffer[addr6_length] = '\0';
-    data.add(addr6_buffer);
+    data.add(reinterpret_cast<void*>(&lease_addr_data));
 
     // Drop to common update code
-    updateLeaseCommon(stindex, data, lease, *exchange6_);
+    updateLeaseCommon(stindex, data, *exchange6_);
 }
 
 bool
 CqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex,
-                                 CqlDataArray& data, CqlLeaseExchange& exchange) {
+                               CqlDataArray& data,
+                               CqlLeaseExchange& exchange) {
     CassError rc;
     CassStatement* statement = NULL;
     CassFuture* future = NULL;
 
     statement = cass_prepared_bind(dbconn_.statements_[stindex]);
-    if (NULL == statement) {
+    if (!statement) {
         isc_throw(DbOperationError, "unable to bind statement");
     }
 
-    CqlLeaseMgr::bindData(statement, stindex, data, exchange);
+    if (dbconn_.force_consistency_) {
+        rc = cass_statement_set_consistency(statement, dbconn_.consistency_);
+        if (rc != CASS_OK) {
+            cass_statement_free(statement);
+            isc_throw(
+                DbOperationError,
+                "unable to set statement consistency for statement "
+                    << dbconn_.tagged_statements_[stindex].name_);
+        }
+    }
+
+    CqlCommon::bindData(statement, stindex, data, exchange,
+                        CqlLeaseMgr::tagged_statements_);
 
     future = cass_session_execute(dbconn_.session_, statement);
-    if (NULL == future) {
+    if (!future) {
         cass_statement_free(statement);
-        isc_throw(DbOperationError, "unable to execute statement");
+        isc_throw(DbOperationError,
+                  "unable to execute statement "
+                      << dbconn_.tagged_statements_[stindex].name_);
     }
     cass_future_wait(future);
     std::string error;
@@ -1773,23 +1670,9 @@ CqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex,
     }
 
     // Check if statement has been applied.
-    const CassResult* resultCollection = cass_future_get_result(future);
-    CassIterator* rows = cass_iterator_from_result(resultCollection);
-    CqlDataArray appliedData;
-    CqlDataArray appliedSize;
-    bool applied = false;
-    while (cass_iterator_next(rows)) {
-        const CassRow* row = cass_iterator_get_row(rows);
-        // [applied]: bool
-        appliedData.add(reinterpret_cast<void*>(&applied));
-        appliedSize.add(NULL);
-        CqlLeaseMgr::getData(row, exchange.parameters_.size() - 1, appliedData,
-                             appliedSize, 0, exchange);
-    }
+    bool applied = exchange.hasStatementBeenApplied(future);
 
     // Free resources.
-    cass_iterator_free(rows);
-    cass_result_free(resultCollection);
     cass_future_free(future);
     cass_statement_free(statement);
 
@@ -1798,29 +1681,29 @@ CqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex,
 
 bool
 CqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_CQL_DELETE_ADDR).arg(addr.toText());
+    std::string addr_data = addr.toText();
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_DELETE_ADDR)
+        .arg(addr_data);
 
     // Set up the WHERE clause value
     CqlDataArray data;
 
     if (addr.isV4()) {
-        uint32_t addr4_data = addr.toUint32();
-        data.add(&addr4_data);
-        return (deleteLeaseCommon(DELETE_LEASE4, data, *exchange4_));
+        cass_int32_t addr4_data = static_cast<cass_int32_t>(addr.toUint32());
+        data.add(reinterpret_cast<void*>(&addr4_data));
+        return deleteLeaseCommon(DELETE_LEASE4, data, *exchange4_);
     } else {
-        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);
+        if (addr_data.size() > ADDRESS6_TEXT_MAX_LEN) {
+            isc_throw(BadValue, "deleteLease(): "
+                                "address "
+                                    << addr_data << " of length "
+                                    << addr_data.size()
+                                    << " exceeds maximum allowed length "
+                                       "of "
+                                    << ADDRESS6_TEXT_MAX_LEN);
         }
-        addr6_buffer[addr6_length] = '\0';
-        data.add(addr6_buffer);
-        return (deleteLeaseCommon(DELETE_LEASE6, data, *exchange6_));
+        data.add(reinterpret_cast<void*>(&addr_data));
+        return deleteLeaseCommon(DELETE_LEASE6, data, *exchange6_);
     }
 }
 
@@ -1829,7 +1712,8 @@ CqlLeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t secs) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_DELETE_EXPIRED_RECLAIMED4)
         .arg(secs);
-    return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE4_STATE_EXPIRED));
+    return deleteExpiredReclaimedLeasesCommon(secs,
+                                              DELETE_LEASE4_STATE_EXPIRED);
 }
 
 uint64_t
@@ -1837,25 +1721,27 @@ CqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_DELETE_EXPIRED_RECLAIMED6)
         .arg(secs);
-    return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE6_STATE_EXPIRED));
+    return deleteExpiredReclaimedLeasesCommon(secs,
+                                              DELETE_LEASE6_STATE_EXPIRED);
 }
 
 uint64_t
-CqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
-        StatementIndex statement_index) {
+CqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(
+    const uint32_t secs, StatementIndex statement_index) {
     // Set up the WHERE clause value
 
     CqlDataArray data;
-    uint32_t result = 0;
+    uint64_t result = 0;
 
     // State is reclaimed.
-    uint32_t state = Lease::STATE_EXPIRED_RECLAIMED;
-    data.add(&state);
+    cass_int32_t state =
+        static_cast<cass_int32_t>(Lease::STATE_EXPIRED_RECLAIMED);
+    data.add(reinterpret_cast<void*>(&state));
 
     // Expiration timestamp.
-    uint64_t expiration = static_cast<int64_t>(time(NULL) -
-        static_cast<time_t>(secs));
-    data.add(&expiration);
+    cass_int64_t expiration =
+        static_cast<cass_int64_t>(time(NULL) - static_cast<time_t>(secs));
+    data.add(reinterpret_cast<void*>(&expiration));
 
     // Get the data
     Lease4Collection result4Leases;
@@ -1871,18 +1757,18 @@ CqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
         break;
     }
     for (Lease4Collection::iterator it = result4Leases.begin();
-            it != result4Leases.end(); ++it) {
+         it != result4Leases.end(); ++it) {
         if (deleteLease((*it)->addr_)) {
             result++;
         }
     }
     for (Lease6Collection::iterator it = result6Leases.begin();
-            it != result6Leases.end(); ++it) {
+         it != result6Leases.end(); ++it) {
         if (deleteLease((*it)->addr_)) {
             result++;
         }
     }
-    return (result);
+    return result;
 }
 
 std::string
@@ -1893,7 +1779,7 @@ CqlLeaseMgr::getName() const {
     } catch (...) {
         // Return an empty name
     }
-    return (name);
+    return name;
 }
 
 std::string
@@ -1901,29 +1787,42 @@ CqlLeaseMgr::getDescription() const {
     return std::string("Cassandra Database");
 }
 
-pair<uint32_t, uint32_t>
+std::pair<unsigned int, unsigned int>
 CqlLeaseMgr::getVersion() const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_GET_VERSION);
-    uint32_t version;
-    uint32_t minor;
+    cass_int32_t version;
+    cass_int32_t minor;
     CassError rc;
     CassStatement* statement = NULL;
     CassFuture* future = NULL;
 
     statement = cass_prepared_bind(dbconn_.statements_[GET_VERSION]);
-    if (NULL == statement) {
+    if (!statement) {
         isc_throw(DbOperationError, "unable to bind statement");
     }
 
+    if (dbconn_.force_consistency_) {
+        rc = cass_statement_set_consistency(statement, dbconn_.consistency_);
+        if (rc != CASS_OK) {
+            cass_statement_free(statement);
+            isc_throw(
+                DbOperationError,
+                "unable to set statement consistency for statement "
+                    << dbconn_.tagged_statements_[GET_VERSION].name_);
+        }
+    }
+
     future = cass_session_execute(dbconn_.session_, statement);
-    if (NULL == future) {
+    if (!future) {
         cass_statement_free(statement);
-        isc_throw(DbOperationError, "unable to execute statement");
+        isc_throw(DbOperationError,
+                  "unable to execute statement "
+                      << dbconn_.tagged_statements_[GET_VERSION].name_);
     }
     cass_future_wait(future);
     std::string error;
-    dbconn_.checkStatementError(error, future, "unable to GET");
+    dbconn_.checkStatementError(error, future, "unable to GET version");
     rc = cass_future_error_code(future);
     if (rc != CASS_OK) {
         cass_future_free(future);
@@ -1935,17 +1834,14 @@ CqlLeaseMgr::getVersion() const {
     const CassResult* resultCollection = cass_future_get_result(future);
     CassIterator* rows = cass_iterator_from_result(resultCollection);
     CqlDataArray data;
-    CqlDataArray size;
     while (cass_iterator_next(rows)) {
         const CassRow* row = cass_iterator_get_row(rows);
-        // version: uint32_t
+        // version: int
         data.add(reinterpret_cast<void*>(&version));
-        size.add(NULL);
-        // minor: uint32_t
+        // minor: int
         data.add(reinterpret_cast<void*>(&minor));
-        size.add(NULL);
-        for (size_t i = 0; i < data.size(); i++) {
-            CqlLeaseMgr::getData(row, i, data, size, i, *versionExchange_);
+        for (size_t i = 0U; i < data.size(); i++) {
+            CqlCommon::getData(row, i, i, *versionExchange_, data);
         }
     }
 
@@ -1954,18 +1850,21 @@ CqlLeaseMgr::getVersion() const {
     cass_future_free(future);
     cass_statement_free(statement);
 
-    return (make_pair(version, minor));
+    return std::pair<unsigned int, unsigned int>(
+        static_cast<unsigned int>(version), static_cast<unsigned int>(minor));
 }
 
 void
 CqlLeaseMgr::commit() {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_COMMIT);
+    dbconn_.commit();
 }
 
 void
 CqlLeaseMgr::rollback() {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ROLLBACK);
+    dbconn_.rollback();
 }
 
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
+}  // namespace dhcp
+}  // namespace isc
index 76f96c859e518f46258c38718b6a837e9bc9c8d8..8fcf5c8d96ae82400dbb784a5053b8f9b0b19770 100644 (file)
 #ifndef CQL_LEASE_MGR_H
 #define CQL_LEASE_MGR_H
 
+#include <cassandra.h>
+
 #include <dhcp/hwaddr.h>
-#include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/cql_connection.h>
+#include <dhcpsrv/cql_exchange.h>
+#include <dhcpsrv/lease_mgr.h>
+
 #include <boost/scoped_ptr.hpp>
 #include <boost/utility.hpp>
-#include <cassandra.h>
+
+#include <string>
+#include <utility>
 #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
-/// CQL execute call.
-///
-/// Note that the data values are stored as pointers. These pointers need
-/// to be valid for the duration of the CQL statement execution. In other
-/// words populating them with pointers to values that go out of scope
-/// before statement is executed is a bad idea.
-struct CqlDataArray {
-    /// Add void pointer to a vector of pointers to the data values.
-    std::vector<void*> values_;
-    void add(void* value) {
-        values_.push_back(value);
-    }
-    /// Remove void pointer from a vector of pointers to the data values.
-    void remove(int index) {
-        if (values_.size() <= index) {
-            isc_throw(BadValue, "Index " << index << " out of bounds: [0, " <<
-                (values_.size() - 1)  << "]");
-        }
-        values_.erase(values_.begin() + index);
-    }
-    /// Get size.
-    size_t size() {
-        return values_.size();
-    }
-};
-
 class CqlVersionExchange;
 class CqlLeaseExchange;
 class CqlLease4Exchange;
 class CqlLease6Exchange;
 
-/// @brief Cassandra Exchange
-///
-/// This class provides the basic conversion functions between Cassandra CQL and
-/// base types.
-class CqlExchange : public virtual SqlExchange {
-public:
-    // 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);
-    }
-};
-
 /// @brief Cassandra Lease Manager
 ///
 /// This class provides the \ref isc::dhcp::LeaseMgr interface to the Cassandra
-/// database. Use of this backend presupposes that a CQL database is available
+/// database. Use of this backend implies that a CQL database is available
 /// and that the Kea schema has been created within it.
 class CqlLeaseMgr : public LeaseMgr {
 public:
-
     /// @brief Constructor
     ///
     /// Uses the following keywords in the parameters passed to it to
@@ -115,7 +59,7 @@ public:
     /// schema_version table will be checked against hard-coded value in
     /// the implementation file.
     ///
-    /// Finally, all the SQL commands are pre-compiled.
+    /// Finally, all the CQL commands are pre-compiled.
     ///
     /// @param parameters A data structure relating keywords and values
     ///        concerned with the database.
@@ -124,7 +68,7 @@ public:
     /// @throw isc::dhcp::DbOpenError Error opening the database
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    CqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters);
+    explicit CqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters);
 
     /// @brief Destructor (closes database)
     virtual ~CqlLeaseMgr();
@@ -225,9 +169,8 @@ public:
     /// @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,
+    virtual Lease4Ptr getLease4(const ClientId& client_id,
+                                const HWAddr& hwaddr,
                                 SubnetID subnet_id) const;
 
     /// @brief Returns existing IPv4 lease for specified client-id
@@ -280,8 +223,8 @@ public:
     ///        lease type field.
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
-                                       uint32_t iaid) const;
+    virtual Lease6Collection
+    getLeases6(Lease::Type type, const DUID& duid, uint32_t iaid) const;
 
     /// @brief Returns existing IPv6 lease for a given DUID+IA combination
     ///
@@ -296,8 +239,10 @@ public:
     ///        lease type field.
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
-                                        uint32_t iaid, SubnetID subnet_id) const;
+    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
@@ -308,8 +253,8 @@ public:
     /// 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;
+    virtual void getExpiredLeases6(Lease6Collection& expired_leases,
+                                   const size_t max_leases) const;
 
     /// @brief Returns a collection of expired DHCPv4 leases.
     ///
@@ -321,8 +266,8 @@ public:
     /// 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;
+    virtual void getExpiredLeases4(Lease4Collection& expired_leases,
+                                   const size_t max_leases) const;
 
     /// @brief Updates IPv4 lease.
     ///
@@ -368,7 +313,7 @@ public:
     /// time will not be deleted.
     ///
     /// @return Number of leases deleted.
-    virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t );
+    virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs);
 
     /// @brief Deletes all expired and reclaimed DHCPv6 leases.
     ///
@@ -377,7 +322,7 @@ public:
     /// time will not be deleted.
     ///
     /// @return Number of leases deleted.
-    virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t );
+    virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs);
 
     /// @brief Return backend type
     ///
@@ -405,7 +350,7 @@ public:
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual std::pair<uint32_t, uint32_t> getVersion() const;
+    virtual std::pair<unsigned int, unsigned int> getVersion() const;
 
     /// @brief Commit Transactions
     ///
@@ -419,70 +364,35 @@ public:
 
     /// @brief Statement Tags
     ///
-    /// The contents of the enum are indexes into the list of compiled SQL
+    /// The contents of the enum are indexes into the list of compiled CQL
     /// statements
     enum StatementIndex {
-        DELETE_LEASE4,              // Delete from lease4 by address
-        DELETE_LEASE4_STATE_EXPIRED,// Delete expired lease4s in certain state
-        DELETE_LEASE6,              // Delete from lease6 by address
-        DELETE_LEASE6_STATE_EXPIRED,// Delete expired lease6s in certain state
-        GET_LEASE4_ADDR,            // Get lease4 by address
-        GET_LEASE4_CLIENTID,        // Get lease4 by client ID
-        GET_LEASE4_CLIENTID_SUBID,  // Get lease4 by client ID & subnet ID
-        GET_LEASE4_HWADDR,          // Get lease4 by HW address
-        GET_LEASE4_HWADDR_SUBID,    // Get lease4 by HW address & subnet ID
-        GET_LEASE4_EXPIRE,          // Get expired lease4
-        GET_LEASE6_ADDR,            // Get lease6 by address
-        GET_LEASE6_DUID_IAID,       // Get lease6 by DUID and IAID
-        GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and subnet ID
-        GET_LEASE6_EXPIRE,          // Get expired lease6
-        GET_VERSION,                // Obtain version number
-        INSERT_LEASE4,              // Add entry to lease4 table
-        INSERT_LEASE6,              // Add entry to lease6 table
-        UPDATE_LEASE4,              // Update a Lease4 entry
-        UPDATE_LEASE6,              // Update a Lease6 entry
-        NUM_STATEMENTS              // Number of statements
+        DELETE_LEASE4,                // Delete from lease4 by address
+        DELETE_LEASE4_STATE_EXPIRED,  // Delete expired lease4s in certain
+                                      // state
+        DELETE_LEASE6,                // Delete from lease6 by address
+        DELETE_LEASE6_STATE_EXPIRED,  // Delete expired lease6s in certain
+                                      // state
+        GET_LEASE4_ADDR,              // Get lease4 by address
+        GET_LEASE4_CLIENTID,          // Get lease4 by client ID
+        GET_LEASE4_CLIENTID_SUBID,    // Get lease4 by client ID & subnet ID
+        GET_LEASE4_HWADDR,            // Get lease4 by HW address
+        GET_LEASE4_HWADDR_SUBID,      // Get lease4 by HW address & subnet ID
+        GET_LEASE4_EXPIRE,            // Get expired lease4
+        GET_LEASE6_ADDR,              // Get lease6 by address
+        GET_LEASE6_DUID_IAID,         // Get lease6 by DUID and IAID
+        GET_LEASE6_DUID_IAID_SUBID,   // Get lease6 by DUID, IAID and subnet
+                                      // ID
+        GET_LEASE6_EXPIRE,            // Get expired lease6
+        GET_VERSION,                  // Obtain version number
+        INSERT_LEASE4,                // Add entry to lease4 table
+        INSERT_LEASE6,                // Add entry to lease6 table
+        UPDATE_LEASE4,                // Update a Lease4 entry
+        UPDATE_LEASE6,                // Update a Lease6 entry
+        NUM_STATEMENTS                // Number of statements
     };
 
-    /// @brief Binds data specified in data
-    ///
-    /// This method calls one of cass_value_bind_{none,bool,int32,int64,string,bytes}.
-    /// It is used to bind C++ data types used by Kea into formats used by Cassandra.
-    ///
-    /// @param statement Statement object representing the query
-    /// @param stindex Index of statement being executed
-    /// @param data array that has been created for the type of lease in question.
-    /// @param exchange Exchange object to use
-    static void bindData(CassStatement* statement, const StatementIndex stindex,
-        CqlDataArray& data, const SqlExchange& exchange);
-
-    /// @brief Returns type of data for specific parameter.
-    ///
-    /// Returns type of a given parameter of a given statement.
-    ///
-    /// @param stindex Index of statement being executed.
-    /// @param pindex Index of the parameter for a given statement.
-    /// @param exchange Exchange object to use
-    static ExchangeDataType getDataType(const StatementIndex stindex, int pindex,
-        const SqlExchange& exchange);
-
-    /// @brief Retrieves data returned by Cassandra.
-    ///
-    /// This method calls one of cass_value_get_{none,bool,int32,int64,string,bytes}.
-    /// It is used to retrieve data returned by Cassandra into standard C++ types
-    /// used by Kea.
-    ///
-    /// @param row row of data returned by CQL library
-    /// @param pindex Index of statement being executed
-    /// @param data array that has been created for the type of lease in question.
-    /// @param size a structure that holds information about size
-    /// @param dindex data index (specifies which entry in size array is used)
-    /// @param exchange Exchange object to use
-    static void getData(const CassRow* row, const int pindex, CqlDataArray& data,
-        CqlDataArray& size, const int dindex, const SqlExchange& exchange);
-
 private:
-
     /// @brief Add Lease Common Code
     ///
     /// This method performs the common actions for both flavours (V4 and V6)
@@ -499,8 +409,9 @@ private:
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    bool addLeaseCommon(StatementIndex stindex, CqlDataArray& data,
-        CqlLeaseExchange& exchange);
+    bool addLeaseCommon(StatementIndex stindex,
+                        CqlDataArray& data,
+                        CqlLeaseExchange& exchange);
 
     /// @brief Get Lease Collection Common Code
     ///
@@ -523,8 +434,10 @@ private:
     /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
     ///        from the database where only one was expected.
     template <typename Exchange, typename LeaseCollection>
-    void getLeaseCollection(StatementIndex stindex, CqlDataArray& data_array,
-                            Exchange& exchange, LeaseCollection& result,
+    void getLeaseCollection(StatementIndex stindex,
+                            CqlDataArray& data_array,
+                            Exchange& exchange,
+                            LeaseCollection& result,
                             bool single = false) const;
 
     /// @brief Gets Lease4 Collection
@@ -543,7 +456,8 @@ private:
     ///        failed.
     /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
     ///        from the database where only one was expected.
-    void getLeaseCollection(StatementIndex stindex, CqlDataArray& data,
+    void getLeaseCollection(StatementIndex stindex,
+                            CqlDataArray& data,
                             Lease4Collection& result) const {
         getLeaseCollection(stindex, data, exchange4_, result);
     }
@@ -563,7 +477,8 @@ private:
     ///        failed.
     /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
     ///        from the database where only one was expected.
-    void getLeaseCollection(StatementIndex stindex, CqlDataArray& data,
+    void getLeaseCollection(StatementIndex stindex,
+                            CqlDataArray& data,
                             Lease6Collection& result) const {
         getLeaseCollection(stindex, data, exchange6_, result);
     }
@@ -577,7 +492,8 @@ private:
     /// @param stindex Index of statement being executed
     /// @param data array containing input parameters for the query
     /// @param lease Lease4 object returned
-    void getLease(StatementIndex stindex, CqlDataArray& data,
+    void getLease(StatementIndex stindex,
+                  CqlDataArray& data,
                   Lease4Ptr& result) const;
 
     /// @brief Get Lease6 Common Code
@@ -589,7 +505,8 @@ private:
     /// @param stindex Index of statement being executed
     /// @param data array containing input parameters for the query
     /// @param lease Lease6 object returned
-    void getLease(StatementIndex stindex, CqlDataArray& data,
+    void getLease(StatementIndex stindex,
+                  CqlDataArray& data,
                   Lease6Ptr& result) const;
 
     /// @brief Get expired leases common code.
@@ -606,7 +523,7 @@ private:
     ///        @c GET_LEASE6_EXPIRE.
     ///
     /// @tparam One of the @c Lease4Collection or @c Lease6Collection.
-    template<typename LeaseCollection>
+    template <typename LeaseCollection>
     void getExpiredLeasesCommon(LeaseCollection& expired_leases,
                                 const size_t max_leases,
                                 StatementIndex statement_index) const;
@@ -626,9 +543,9 @@ private:
     ///        the address given.
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    template <typename LeasePtr>
-    void updateLeaseCommon(StatementIndex stindex, CqlDataArray& data,
-                           const LeasePtr& lease, CqlLeaseExchange& exchange);
+    void updateLeaseCommon(StatementIndex stindex,
+                           CqlDataArray& data,
+                           CqlLeaseExchange& exchange);
 
     /// @brief Delete lease common code
     ///
@@ -644,8 +561,9 @@ private:
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    bool deleteLeaseCommon(StatementIndex stindex, CqlDataArray& data,
-        CqlLeaseExchange& exchange);
+    bool deleteLeaseCommon(StatementIndex stindex,
+                           CqlDataArray& data,
+                           CqlLeaseExchange& exchange);
 
     /// @brief Delete expired-reclaimed leases.
     ///
@@ -659,21 +577,27 @@ private:
     uint64_t deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
                                                 StatementIndex statement_index);
 
-    /// CQL queries used by CQL backend
+    /// @brief CQL queries used by CQL backend
     static CqlTaggedStatement tagged_statements_[];
-    /// Database connection object
-    CqlConnection dbconn_;
 
+    /// @brief Database connection object
+    mutable CqlConnection dbconn_;
+
+    /// @{
     /// The exchange objects are used for transfer of data to/from the database.
     /// They are pointed-to objects as the contents may change in "const" calls,
     /// while the rest of this object does not. (At alternative would be to
     /// declare them as "mutable".)
-    boost::scoped_ptr<CqlLease4Exchange> exchange4_; ///< Exchange object
-    boost::scoped_ptr<CqlLease6Exchange> exchange6_; ///< Exchange object
-    boost::scoped_ptr<CqlVersionExchange> versionExchange_; ///< Exchange object
+    /// @brief Exchange object for IPv4
+    boost::scoped_ptr<CqlLease4Exchange> exchange4_;
+    /// @brief Exchange object for IPv4
+    boost::scoped_ptr<CqlLease6Exchange> exchange6_;
+    /// @brief Exchange object for version
+    boost::scoped_ptr<CqlVersionExchange> versionExchange_;
+    //// @}
 };
 
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
+}  // namespace dhcp
+}  // namespace isc
 
-#endif // CQL_LEASE_MGR_H
+#endif  // CQL_LEASE_MGR_H
index 9fa51bd16d2e2925a8d6e0473bd1307c29b15bf4..8bb91008d8e8b66e00dc5c2bce6173aa9425b2e6 100644 (file)
@@ -175,12 +175,22 @@ with the specified address to the Cassandra backend database.
 % DHCPSRV_CQL_COMMIT committing to Cassandra database
 A commit call been issued on the server. For Cassandra, this is a no-op.
 
+% DHCPSRV_CQL_BEGIN_TRANSACTION committing to Cassandra database.
+The server has issued a begin transaction call.
+
 % DHCPSRV_CQL_DB opening Cassandra lease database: %1
 This informational message is logged when a DHCP server (either V4 or
 V6) is about to open a Cassandra lease database.  The parameters of
 the connection including database name and username needed to access it
 (but not the password if any) are logged.
 
+% DHCPSRV_CQL_DEALLOC_ERROR An error occurred while closing the CQL connection: %1
+This is an error message issued when a DHCP server (either V4 or V6) experienced
+and error freeing CQL database resources as part of closing its connection to
+the Cassandra database. The connection is closed as part of normal server
+shutdown. This error is most likely a programmatic issue that is highly
+unlikely to occur or negatively impact server operation.
+
 % DHCPSRV_CQL_DELETE_ADDR deleting lease for address %1
 A debug message issued when the server is attempting to delete a lease from the
 Cassandra database for the specified address.
@@ -709,7 +719,7 @@ with the specified address to the PostgreSQL backend database.
 A debug message issued when the server is about to add an IPv6 lease
 with the specified address to the PostgreSQL backend database.
 
-% DHCPSRV_PGSQL_COMMIT committing to MySQL database
+% DHCPSRV_PGSQL_COMMIT committing to PostgreSQL database
 The code has issued a commit call.  All outstanding transactions will be
 committed to the database.  Note that depending on the PostgreSQL settings,
 the committal may not include a write to disk.
index a07cd476488943551f33a082f0343f2b2d8762ac..8361813ad57183b1cad375f1c9c57f3cda58f651 100644 (file)
@@ -15,6 +15,7 @@
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/db_exceptions.h>
+#include <dhcpsrv/sql_common.h>
 
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
 namespace isc {
 namespace dhcp {
 
-/// @brief Used to map server data types with internal backend storage data
-/// types.
-enum ExchangeDataType {
-    EXCHANGE_DATA_TYPE_NONE,
-    EXCHANGE_DATA_TYPE_BOOL,
-    EXCHANGE_DATA_TYPE_INT32,
-    EXCHANGE_DATA_TYPE_INT64,
-    EXCHANGE_DATA_TYPE_TIMESTAMP,
-    EXCHANGE_DATA_TYPE_STRING,
-    EXCHANGE_DATA_TYPE_BYTES
-};
-
-/// @brief Used to specify the direction of the data exchange between the
-/// database and the server.
-enum ExchangeDataTypeIO {
-    EXCHANGE_DATA_TYPE_IO_IN,
-    EXCHANGE_DATA_TYPE_IO_OUT,
-    EXCHANGE_DATA_TYPE_IO_IN_OUT
-};
-
-/// @brief Used to map the column name with internal backend storage data types.
-struct ExchangeColumnInfo {
-    ExchangeColumnInfo () : name_(""), index_(0), type_io_(EXCHANGE_DATA_TYPE_IO_IN),
-                            type_(EXCHANGE_DATA_TYPE_NONE) {};
-    ExchangeColumnInfo (const char* name, const uint32_t index,
-        const ExchangeDataTypeIO type_io, const ExchangeDataType type) :
-        name_(name), index_(index), type_io_(type_io), type_(type) {};
-    std::string name_;
-    uint32_t index_;
-    ExchangeDataTypeIO type_io_;
-    ExchangeDataType type_;
-};
-
-typedef boost::shared_ptr<ExchangeColumnInfo> ExchangeColumnInfoPtr;
-
-typedef boost::multi_index_container<
-    // Container comprises elements of ExchangeColumnInfoPtr type.
-    ExchangeColumnInfoPtr,
-    // Here we start enumerating various indexes.
-    boost::multi_index::indexed_by<
-        // Sequenced index allows accessing elements in the same way as elements
-        // in std::list.
-        // Sequenced is an index #0.
-        boost::multi_index::sequenced<>,
-        // Start definition of index #1.
-        boost::multi_index::hashed_non_unique<
-            boost::multi_index::member<
-                ExchangeColumnInfo,
-                std::string,
-                &ExchangeColumnInfo::name_
-             >
-        >,
-        // Start definition of index #2.
-        boost::multi_index::hashed_non_unique<
-            boost::multi_index::member<
-                ExchangeColumnInfo,
-                uint32_t,
-                &ExchangeColumnInfo::index_
-            >
-        >
-    >
-> ExchangeColumnInfoContainer;
-
-/// Pointer to the ExchangeColumnInfoContainer object.
-typedef boost::shared_ptr<ExchangeColumnInfoContainer> ExchangeColumnInfoContainerPtr;
-/// Type of the index #1 - name.
-typedef ExchangeColumnInfoContainer::nth_index<1>::type ExchangeColumnInfoContainerName;
-/// Pair of iterators to represent the range of ExchangeColumnInfo having the
-/// same name value. The first element in this pair represents
-/// the beginning of the range, the second element represents the end.
-typedef std::pair<ExchangeColumnInfoContainerName::const_iterator,
-                  ExchangeColumnInfoContainerName::const_iterator> ExchangeColumnInfoContainerNameRange;
-/// Type of the index #2 - index.
-typedef ExchangeColumnInfoContainer::nth_index<2>::type ExchangeColumnInfoContainerIndex;
-/// Pair of iterators to represent the range of ExchangeColumnInfo having the
-/// same index value. The first element in this pair represents
-/// the beginning of the range, the second element represents the end.
-typedef std::pair<ExchangeColumnInfoContainerIndex::const_iterator,
-                  ExchangeColumnInfoContainerIndex::const_iterator> ExchangeColumnInfoContainerIndexRange;
-
-class SqlExchange {
-public:
-    SqlExchange () {};
-    virtual ~SqlExchange() {};
-    ExchangeColumnInfoContainer parameters_;   ///< Column names and types
-};
-
 /// @brief Contains a single row of lease statistical data
 ///
 /// The contents of the row consist of a subnet ID, a lease
index 6b7fa51eec46bc0f818a72e38c6fb614b9002f9c..329d0c50e5f0fe8f8387b3521e3466d8d8d364fb 100644 (file)
@@ -53,7 +53,8 @@ DbAccessParser::build(isc::data::ConstElementPtr config_value) {
     // 2. Update the copy with the passed keywords.
     BOOST_FOREACH(ConfigPair param, config_value->mapValue()) {
         try {
-            if ((param.first == "persist") || (param.first == "readonly")) {
+            if ((param.first == "persist") || (param.first == "readonly") ||
+                (param.first == "tcp-nodelay")) {
                 values_copy[param.first] = (param.second->boolValue() ?
                                             "true" : "false");
 
@@ -67,6 +68,20 @@ DbAccessParser::build(isc::data::ConstElementPtr config_value) {
                 values_copy[param.first] =
                     boost::lexical_cast<std::string>(timeout);
 
+            } else if (param.first == "reconnect-wait-time") {
+                timeout = param.second->intValue();
+                values_copy[param.first] =
+                    boost::lexical_cast<std::string>(timeout);
+
+            } else if (param.first == "request-timeout") {
+                timeout = param.second->intValue();
+                values_copy[param.first] =
+                    boost::lexical_cast<std::string>(timeout);
+
+            } else if (param.first == "tcp-keepalive") {
+                timeout = param.second->intValue();
+                values_copy[param.first] =
+                    boost::lexical_cast<std::string>(timeout);
             } else {
                 values_copy[param.first] = param.second->stringValue();
             }
diff --git a/src/lib/dhcpsrv/sql_common.h b/src/lib/dhcpsrv/sql_common.h
new file mode 100644 (file)
index 0000000..bc23dbf
--- /dev/null
@@ -0,0 +1,152 @@
+// Copyright (C) 2016 Deutsche Telekom AG.
+//
+// Author: Cristian Secăreanu <cristian.secareanu@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 SQL_COMMON_H
+#define SQL_COMMON_H
+
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Used to map server data types with internal backend storage data
+/// types.
+enum ExchangeDataType {
+    EXCHANGE_DATA_TYPE_NONE,
+    EXCHANGE_DATA_TYPE_BOOL,
+    EXCHANGE_DATA_TYPE_INT32,
+    EXCHANGE_DATA_TYPE_INT64,
+    EXCHANGE_DATA_TYPE_TIMESTAMP,
+    EXCHANGE_DATA_TYPE_STRING,
+    EXCHANGE_DATA_TYPE_BYTES,
+    EXCHANGE_DATA_TYPE_UUID
+};
+
+/// @brief Used to specify the direction of the data exchange between the
+/// database and the server.
+enum ExchangeDataTypeIO {
+    EXCHANGE_DATA_TYPE_IO_IN,
+    EXCHANGE_DATA_TYPE_IO_OUT,
+    EXCHANGE_DATA_TYPE_IO_IN_OUT
+};
+
+/// @brief Used to map the column name with internal backend storage data types.
+struct ExchangeColumnInfo {
+    ExchangeColumnInfo()
+        : name_(""), index_(0), type_io_(EXCHANGE_DATA_TYPE_IO_IN_OUT),
+          type_(EXCHANGE_DATA_TYPE_NONE){};
+    ExchangeColumnInfo(const char* name,
+                       const uint32_t index,
+                       const ExchangeDataTypeIO type_io,
+                       const ExchangeDataType type)
+        : name_(name), index_(index), type_io_(type_io), type_(type){};
+    std::string name_;
+    uint32_t index_;
+    ExchangeDataTypeIO type_io_;
+    ExchangeDataType type_;
+};
+
+/// @brief Smart pointer to an @ref ExchangeColumnInfo
+typedef boost::shared_ptr<ExchangeColumnInfo> ExchangeColumnInfoPtr;
+
+/// @brief Multimap that allows indexing @ref ExchangeColumnInfoPtr
+///     sequentially, by index and by name.
+typedef boost::multi_index_container<
+    // Container comprises elements of ExchangeColumnInfoPtr type.
+    ExchangeColumnInfoPtr,
+    // Here we start enumerating various indexes.
+    boost::multi_index::indexed_by<
+        // Index #0. Sequenced index allows accessing elements in the same way
+        // as in std::list.
+        boost::multi_index::sequenced<>,
+        // Index #1
+        boost::multi_index::hashed_non_unique<
+            boost::multi_index::member<
+                ExchangeColumnInfo,
+                std::string,
+                &ExchangeColumnInfo::name_
+             >
+        >,
+        // Index #2
+        boost::multi_index::hashed_non_unique<
+            boost::multi_index::member<
+                ExchangeColumnInfo,
+                uint32_t,
+                &ExchangeColumnInfo::index_
+            >
+        >
+    >
+> ExchangeColumnInfoContainer;
+
+/// @brief Pointer to the ExchangeColumnInfoContainer object.
+typedef boost::shared_ptr<ExchangeColumnInfoContainer>
+    ExchangeColumnInfoContainerPtr;
+
+/// @brief Type of the index #1 - name.
+typedef ExchangeColumnInfoContainer::nth_index<1>::type
+    ExchangeColumnInfoContainerName;
+
+/// @brief Pair of iterators to represent the range of ExchangeColumnInfo having
+/// the
+/// same name value. The first element in this pair represents
+/// the beginning of the range, the second element represents the end.
+typedef std::pair<ExchangeColumnInfoContainerName::const_iterator,
+                  ExchangeColumnInfoContainerName::const_iterator>
+    ExchangeColumnInfoContainerNameRange;
+
+/// @brief Type of the index #2 - index.
+typedef ExchangeColumnInfoContainer::nth_index<2>::type
+    ExchangeColumnInfoContainerIndex;
+
+/// @brief Pair of iterators to represent the range of ExchangeColumnInfo having
+/// the
+/// same index value. The first element in this pair represents
+/// the beginning of the range, the second element represents the end.
+typedef std::pair<ExchangeColumnInfoContainerIndex::const_iterator,
+                  ExchangeColumnInfoContainerIndex::const_iterator>
+    ExchangeColumnInfoContainerIndexRange;
+
+/// @brief Base class for backend exchanges.
+class SqlExchange {
+public:
+    /// @brief Constructor
+    SqlExchange() {
+    }
+
+    /// @brief Destructor
+    virtual ~SqlExchange() {
+    }
+
+    /// @brief Column names and types
+    ExchangeColumnInfoContainer parameters_;
+};
+
+}  // namespace dhcp
+}  // namespace isc
+
+#endif  // SQL_COMMON_H
index baa2f7190f65c629c1f066aca0b63fdb1a065e9c..fa2edfa3a88e4741df35face089ecca546d343c1 100644 (file)
@@ -352,6 +352,31 @@ TEST(CqlOpenTest, OpenDatabase) {
         INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
         InvalidType);
 
+    // Check that invalid login data does not cause an exception, CQL should use
+    // default values.
+    EXPECT_NO_THROW(LeaseMgrFactory::create(connectionString(
+        CQL_VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)));
+    EXPECT_NO_THROW(LeaseMgrFactory::create(connectionString(
+        CQL_VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)));
+    EXPECT_NO_THROW(LeaseMgrFactory::create(connectionString(
+        CQL_VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)));
+    EXPECT_NO_THROW(LeaseMgrFactory::create(connectionString(
+        CQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)));
+
+    // Check that invalid timeouts throw DbOperationError.
+    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+                     CQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER,
+                     VALID_PASSWORD, INVALID_TIMEOUT_1)),
+                 DbOperationError);
+    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+                     CQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER,
+                     VALID_PASSWORD, INVALID_TIMEOUT_2)),
+                 DbOperationError);
+
+    // Check that CQL allows the hostname to not be specified.
+    EXPECT_NO_THROW(LeaseMgrFactory::create(connectionString(
+        CQL_VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)));
+
     // Tidy up after the test
     destroyCqlSchema(false, true);
 }
@@ -375,8 +400,8 @@ TEST_F(CqlLeaseMgrTest, getType) {
 /// This test checks that the conversion is correct.
 TEST_F(CqlLeaseMgrTest, checkTimeConversion) {
     const time_t cltt = time(NULL);
-    const uint32_t valid_lft = 86400;       // 1 day
-    uint64_t cql_expire;
+    const cass_int64_t valid_lft = 86400; // 1 day
+    cass_int64_t cql_expire;
 
     // Convert to the database time
     CqlExchange::convertToDatabaseTime(cltt, valid_lft, cql_expire);
@@ -643,4 +668,4 @@ TEST_F(CqlLeaseMgrTest, deleteExpiredReclaimedLeases4) {
     testDeleteExpiredReclaimedLeases4();
 }
 
-}; // Of anonymous namespace
+}  // namespace
index eda5fe2fc9d482bc03fd2282257f4b66ac0369e0..82f47fc8dd50098c2fd72c799caca6b29d552f32 100644 (file)
@@ -41,6 +41,9 @@
 -- This line starts database initialization to 1.0.
 
 -- Holds the IPv4 leases.
+-- -----------------------------------------------------
+-- Table `lease4`
+-- -----------------------------------------------------
 CREATE TABLE lease4 (
     address int,
     hwaddr blob,
@@ -65,6 +68,9 @@ 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).
+-- -----------------------------------------------------
+-- Table `lease6`
+-- -----------------------------------------------------
 CREATE TABLE lease6 (
     address varchar,
     duid blob,
@@ -98,6 +104,9 @@ CREATE INDEX lease6index6 ON lease6 (state);
 -- 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)
+-- -----------------------------------------------------
+-- Table `lease6_types`
+-- -----------------------------------------------------
 CREATE TABLE lease6_types (
     lease_type int,                             -- Lease type code.
     name varchar,                               -- Name of the lease type
@@ -113,6 +122,9 @@ INSERT INTO lease6_types (lease_type, name) VALUES (2, 'IA_PD');   -- Prefix del
 -- 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.
+-- -----------------------------------------------------
+-- Table `lease_hwaddr_source`
+-- -----------------------------------------------------
 CREATE TABLE lease_hwaddr_source (
     hwaddr_source int,
     name varchar,
@@ -140,47 +152,12 @@ INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (32, 'HWADDR_SOURCE
 -- 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.
+-- -----------------------------------------------------
+-- Table `lease_state`
+-- -----------------------------------------------------
 CREATE TABLE lease_state (
     state int,
     name varchar,
@@ -196,9 +173,15 @@ INSERT INTO lease_state (state, name) VALUES (2, 'expired-reclaimed');
 -- This table is only modified during schema upgrades.  For historical reasons
 -- (related to the names of the columns in the BIND 10 DNS database file), the
 -- first column is called "version" and not "major".
+-- -----------------------------------------------------
+-- Table `schema_version`
+-- -----------------------------------------------------
 CREATE TABLE schema_version (
     version int,
     minor int,
     PRIMARY KEY (version)
 );
+
 INSERT INTO schema_version (version, minor) VALUES (1, 0);
+
+-- This line concludes database initalization to version 1.0.
index 7ec86fadc33e0254edd19ef7e3d61a2decbf390b..aa0631937a1d926eb6b6ad049d2fd7a161ffa3b4 100644 (file)
@@ -25,3 +25,16 @@ DROP TABLE IF EXISTS dhcp4_options;
 DROP TABLE IF EXISTS dhcp6_options;
 DROP TABLE IF EXISTS host_identifier_type;
 DROP TABLE IF EXISTS lease_state;
+
+DROP INDEX IF EXISTS lease4index1;
+DROP INDEX IF EXISTS lease4index2;
+DROP INDEX IF EXISTS lease4index3;
+DROP INDEX IF EXISTS lease4index4;
+DROP INDEX IF EXISTS lease4index5;
+
+DROP INDEX IF EXISTS lease6index1;
+DROP INDEX IF EXISTS lease6index2;
+DROP INDEX IF EXISTS lease6index3;
+DROP INDEX IF EXISTS lease6index4;
+DROP INDEX IF EXISTS lease6index5;
+DROP INDEX IF EXISTS lease6index6;