Browse Source

change requests from isc

# Conflicts:
#	src/bin/admin/tests/data/pgsql.lease6_dump_test.reference.csv
Tomek Mrugalski 8 years ago
parent
commit
d47b8dfe5a

+ 19 - 0
src/bin/admin/admin-utils.sh

@@ -106,6 +106,25 @@ cql_execute() {
     return $retcode
     return $retcode
 }
 }
 
 
+cql_execute_script() {
+    file=$1
+    shift
+    if [ $# -gt 1 ]; then
+        cqlsh $* -e "$file"
+        retcode=$?
+    else
+        cqlsh -u $db_user -p $db_password -k $db_name -f "$file"
+        retcode=$?
+    fi
+
+    if [ $retcode -ne 0 ]; then
+        printf "cqlsh returned with exit status $retcode\n"
+        exit $retcode
+    fi
+
+    return $retcode
+}
+
 cql_version() {
 cql_version() {
     version=`cql_execute "SELECT version, minor FROM schema_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="."`
     version=`echo "$version" | grep -A 1 "+" | grep -v "+" | tr -d ' ' | cut -d "|" -f 1-2 --output-delimiter="."`

+ 15 - 20
src/bin/admin/kea-admin.in

@@ -208,7 +208,8 @@ cql_init() {
         printf "Creating and initializing tables using script %s...\n" $scripts_dir/cql/dhcpdb_create.cql
         printf "Creating and initializing tables using script %s...\n" $scripts_dir/cql/dhcpdb_create.cql
         cql_execute_script $scripts_dir/cql/dhcpdb_create.cql
         cql_execute_script $scripts_dir/cql/dhcpdb_create.cql
     else
     else
-        printf "Tables are already created.\n"
+        log_error "Expected empty database $db_name, but the following tables are present \n$result. Aborting."
+        exit 2
     fi
     fi
 
 
     version=`cql_version`
     version=`cql_version`
@@ -485,44 +486,38 @@ cql_dump() {
 
 
     # Fetch the correct SQL text. Note this function will exit
     # Fetch the correct SQL text. Note this function will exit
     # if it fails.
     # if it fails.
-    get_dump_query $version
+
+    select_where_clause=""
+    if [ $dump_type -eq 4 ]; then
+        dump_qry="SELECT address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state FROM keatest.lease4"
+        select_where_clause=" WHERE address = 0" # invalid address
+    elif [ $dump_type -eq 6 ]; then
+        dump_qry="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 keatest.lease6"
+        select_where_clause=" WHERE address = ::" # invalid address
+    fi
 
 
     # Make sure they specified a file
     # Make sure they specified a file
     if [ "$dump_file" = "" ]; then
     if [ "$dump_file" = "" ]; then
         log_error "you must specify an output file for lease-dump"
         log_error "you must specify an output file for lease-dump"
         usage
         usage
         exit 1
         exit 1
-
     fi
     fi
 
 
     # If output file exists, notify user, allow them a chance to bail
     # If output file exists, notify user, allow them a chance to bail
     check_file_overwrite $dump_file
     check_file_overwrite $dump_file
 
 
-    # Check the temp file too
-    tmp_file="$dump_file.tmp"
-    check_file_overwrite $tmp_file
-
-    # Run the sql to output tab-delimited lease data to a temp file.
-    # By using a temp file we can check for MySQL errors before using
-    # 'tr' to translate tabs to commas.  We do not use MySQL's output
-    # to file as that requires linux superuser privileges to execute
-    # the select.
-    cql_execute "${dump_qry}" > $tmp_file
-    retcode=$?
-    if [ $retcode -ne 0 ]; then
+    cql_execute "${dump_qry}${select_where_clause}" | head -n 2 | tail -n 1 | sed -e 's/\s*//g' | sed -e 's/|/,/g' > $dump_file
+    if [ $? -ne 0 ]; then
         log_error "lease-dump: cql_execute failed, exit code $retcode";
         log_error "lease-dump: cql_execute failed, exit code $retcode";
         exit 1
         exit 1
     fi
     fi
 
 
-    # Now translate tabs to commas.
-    cat $tmp_file | tr '\t' ',' >$dump_file
+    cql_execute "${dump_qry}" | tail -n +4 | head -n -2 | sed -e 's/\s*//g' | sed -e 's/|/,/g' | sort -r >> $dump_file
     if [ $? -ne 0 ]; then
     if [ $? -ne 0 ]; then
-        log_error "lease-dump: reformatting failed";
+        log_error "lease-dump: cql_execute failed, exit code $retcode";
         exit 1
         exit 1
     fi
     fi
 
 
-    # delete the tmp file on success
-    rm $tmp_file
     echo lease$dump_type successfully dumped to $dump_file
     echo lease$dump_type successfully dumped to $dump_file
     exit 0
     exit 0
 }
 }

+ 155 - 7
src/bin/admin/tests/cql_tests.sh.in

@@ -22,7 +22,7 @@ db_name="keatest"
 # Set location of the kea-admin.
 # Set location of the kea-admin.
 keaadmin=@abs_top_builddir@/src/bin/admin/kea-admin
 keaadmin=@abs_top_builddir@/src/bin/admin/kea-admin
 
 
-cql_init_test() {
+cql_lease_init_test() {
     test_start "cql.init"
     test_start "cql.init"
 
 
     # Wipe the database.
     # Wipe the database.
@@ -56,7 +56,7 @@ cql_init_test() {
     cql_execute "SELECT state, name FROM lease_state;"
     cql_execute "SELECT state, name FROM lease_state;"
     assert_eq 0 $? "lease_state table check failed, expected exit code: %d, actual: %d"
     assert_eq 0 $? "lease_state table check failed, expected exit code: %d, actual: %d"
 
 
-    # Trying to create it again should fail.  This verifies the db present
+    # Trying to create it again should fail. This verifies the db present
     # check
     # check
     echo ""
     echo ""
     echo "Making sure keyspace creation fails the second time..."
     echo "Making sure keyspace creation fails the second time..."
@@ -70,18 +70,18 @@ cql_init_test() {
     test_finish 0
     test_finish 0
 }
 }
 
 
-cql_version_test() {
+cql_lease_version_test() {
     test_start "cql.version"
     test_start "cql.version"
 
 
     # Wipe the database.
     # Wipe the database.
     cql_execute_script $db_scripts_dir/cql/dhcpdb_drop.cql
     cql_execute_script $db_scripts_dir/cql/dhcpdb_drop.cql
     assert_eq 0 $? "drop table query failed, exit code %d, expected %d"
     assert_eq 0 $? "drop table query failed, exit code %d, expected %d"
 
 
-    # Create the database
+    # Create the database.
     $keaadmin lease-init cql -u $db_user -p $db_password -n $db_name -d $db_scripts_dir
     $keaadmin lease-init cql -u $db_user -p $db_password -n $db_name -d $db_scripts_dir
     assert_eq 0 $? "kea-admin lease-init cql failed, expected exit code: %d, actual: %d"
     assert_eq 0 $? "kea-admin lease-init cql failed, expected exit code: %d, actual: %d"
 
 
-    # Verfiy that kea-admin lease-version returns the correct version
+    # Verfiy that kea-admin lease-version returns the correct version.
     version=$($keaadmin lease-version cql -u $db_user -p $db_password -n $db_name)
     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 "1.0" $version "Expected kea-admin to return %s, returned value was %s"
 
 
@@ -113,6 +113,154 @@ cql_upgrade_test() {
     test_finish 0
     test_finish 0
 }
 }
 
 
-cql_init_test
-cql_version_test
+cql_lease4_dump_test() {
+    test_start "cql.lease4_dump_test"
+
+    test_dir="@abs_top_srcdir@/src/bin/admin/tests"
+    output_dir="@abs_top_builddir@/src/bin/admin/tests"
+    script_dir="@abs_top_srcdir@/src/bin/admin/scripts"
+
+    output_file="$output_dir/data/cql.lease4_dump_test.output.csv"
+    tmp_file="$output_file.tmp"
+
+    ref_file="$test_dir/data/cql.lease4_dump_test.reference.csv"
+
+    # Wipe out any residuals from prior failed runs.
+    if [ -e $output_file ]
+    then
+        rm $output_file
+    fi
+
+    if [ -e $tmp_file ]
+    then
+        rm $tmp_file
+    fi
+
+    # Wipe the database.
+    cql_execute_script $db_scripts_dir/cql/dhcpdb_drop.cql
+    assert_eq 0 $? "drop table query failed, exit code %d, expected %d"
+
+    # Create the database
+    $keaadmin lease-init cql -u $db_user -p $db_password -n $db_name -d $db_scripts_dir
+    assert_eq 0 $? "kea-admin lease-init cql failed, expected exit code: %d, actual: %d"
+
+    # Insert the reference record.
+    # -1073741302 corresponds to 192.0.2.10
+    # -1073741301 corresponds to 192.0.2.11
+    # -1073741300 corresponds to 192.0.2.12
+    # 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,\
+ fqdn_fwd, fqdn_rev, hostname, state)\
+ 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,\
+ 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,\
+ fqdn_fwd, fqdn_rev, hostname, state)\
+ values(-1073741300,textAsBlob('22'),NULL,40,1436173267,50,true,true,\
+ 'three.example.com', 2);"
+
+    cql_execute "$insert_sql"
+    assert_eq 0 $? "insert into lease4 failed, expected exit code %d, actual %d"
+
+    # Dump lease4 to output_file.
+    $keaadmin lease-dump cql -4 -u $db_user -p $db_password -n $db_name -d $db_scripts_dir -o $output_file
+    assert_eq 0 $? "kea-admin lease-dump -4 failed, expected exit code %d, actual %d"
+
+    # Compare the dump output to reference file, they should be identical.
+    cmp -s $output_file  $ref_file
+    assert_eq 0 $? "dump file does not match reference file, expected exit code %d, actual %d"
+
+    # remove the output file.
+    rm $output_file
+
+    # Wipe the database.
+    cql_execute_script $db_scripts_dir/cql/dhcpdb_drop.cql
+    assert_eq 0 $? "drop table query failed, exit code %d, expected %d"
+
+    test_finish 0
+}
+
+cql_lease6_dump_test() {
+    test_start "cql.lease6_dump_test"
+
+    test_dir="@abs_top_srcdir@/src/bin/admin/tests"
+    output_dir="@abs_top_builddir@/src/bin/admin/tests"
+    script_dir="@abs_top_srcdir@/src/bin/admin/scripts"
+
+    output_file="$output_dir/data/cql.lease6_dump_test.output.csv"
+    tmp_file="$output_file.tmp"
+
+    ref_file="$test_dir/data/cql.lease6_dump_test.reference.csv"
+
+    # Wipe out any residuals from prior failed runs.
+    if [ -e $output_file ]
+    then
+        rm $output_file
+    fi
+
+    if [ -e $tmp_file ]
+    then
+        rm $tmp_file
+    fi
+
+    # Wipe the database.
+    cql_execute_script $db_scripts_dir/cql/dhcpdb_drop.cql
+    assert_eq 0 $? "drop table query failed, exit code %d, expected %d"
+
+    # Create the database.
+    $keaadmin lease-init cql -u $db_user -p $db_password -n $db_name -d $db_scripts_dir
+    assert_eq 0 $? "could not create database, expected exit code %d, actual %d"
+
+    # Insert the reference record.
+    # 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,\
+ 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,\
+ 'one.example.com',textAsBlob('80'),90,16,0);\
+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,\
+ '',textAsBlob('80'),90,1,1);\
+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,\
+ 'three.example.com',textAsBlob('80'),90,4,2);"
+
+    cql_execute "$insert_sql"
+    assert_eq 0 $? "insert into lease6 failed, expected exit code %d, actual %d"
+
+    # Dump lease4 to output_file.
+    $keaadmin lease-dump cql -6 -u $db_user -p $db_password -n $db_name -d $db_scripts_dir -o $output_file
+    assert_eq 0 $? "kea-admin lease-dump -6 failed, status code %d"
+
+    # Compare the dump output to reference file, they should be identical.
+    cmp -s $output_file  $ref_file
+    assert_eq 0 $? "dump file does not match reference file, expected exit code %d, actual %d"
+
+    # remove the output file.
+    rm $output_file
+
+    # Wipe the database.
+    cql_execute_script $db_scripts_dir/cql/dhcpdb_drop.cql
+    assert_eq 0 $? "drop table query failed, exit code %d, expected %d"
+
+    test_finish 0
+}
+
+# Run tests.
+cql_lease_init_test
+cql_lease_version_test
 cql_upgrade_test
 cql_upgrade_test
+cql_lease4_dump_test
+cql_lease6_dump_test

+ 4 - 4
src/bin/admin/tests/data/cql.lease4_dump_test.reference.csv

@@ -1,4 +1,4 @@
-address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname
-0.0.0.10,3230,3330,40,2015-01-01 01:15:30,50,1,1,one.example.com
-0.0.0.11,,313233,40,2015-02-02 02:30:45,50,1,1,
-0.0.0.12,3232,,40,2015-03-03 11:01:07,50,1,1,three.example.com
+address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state
+-1073741302,0x3230,0x3330,40,1430694930,50,True,True,one.example.com,0
+-1073741301,null,0x313233,40,1433464245,50,True,True,,1
+-1073741300,0x3232,null,40,1436173267,50,True,True,three.example.com,2

+ 4 - 4
src/bin/admin/tests/data/cql.lease6_dump_test.reference.csv

@@ -1,4 +1,4 @@
-address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,hwtype,hwaddr_source
-10,3230,30,2015-04-04 01:15:30,40,50,IA_TA,60,70,1,1,one.example.com,3830,90,100
-11,,30,2015-05-05 02:30:45,40,50,IA_TA,60,70,1,1,,3830,90,100
-12,3231,30,2015-06-06 11:01:07,40,50,IA_TA,60,70,1,1,three.example.com,3830,90,100
+address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,hwtype,hwaddr_source,state
+2001:db8::12,0x3231,30,1436173267,40,50,1,60,70,True,True,three.example.com,0x3830,90,4,2
+2001:db8::11,null,30,1433464245,40,50,1,60,70,True,True,,0x3830,90,1,1
+2001:db8::10,0x3230,30,1430694930,40,50,1,60,70,True,True,one.example.com,0x3830,90,16,0

+ 2 - 1
src/bin/admin/tests/dhcpdb_create_1.0.cql

@@ -197,7 +197,8 @@ INSERT INTO lease_state (state, name) VALUES (2, 'expired-reclaimed');
 -- This table is only modified during schema upgrades.  For historical reasons
 -- 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
 -- (related to the names of the columns in the BIND 10 DNS database file), the
 -- first column is called "version" and not "major".
 -- 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.
 CREATE TABLE schema_version (
 CREATE TABLE schema_version (
     version int,
     version int,
     minor int,
     minor int,

+ 5 - 8
src/bin/admin/tests/dhcpdb_create_1.0.mysql

@@ -36,7 +36,7 @@ CREATE TABLE lease4 (
     ) ENGINE = INNODB;
     ) ENGINE = INNODB;
 
 
 
 
-# Create search indexes for lease4 table 
+# Create search indexes for lease4 table.
 # index by hwaddr and subnet_id
 # index by hwaddr and subnet_id
 CREATE INDEX lease4_by_hwaddr_subnet_id ON lease4 (hwaddr, subnet_id);
 CREATE INDEX lease4_by_hwaddr_subnet_id ON lease4 (hwaddr, subnet_id);
 
 
@@ -63,8 +63,8 @@ CREATE TABLE lease6 (
 
 
     ) ENGINE = INNODB;
     ) ENGINE = INNODB;
 
 
-# Create search indexes for lease4 table 
-# index by iaid, subnet_id, and duid 
+# Create search indexes for lease4 table.
+# index by iaid, subnet_id, and duid
 CREATE INDEX lease6_by_iaid_subnet_id_duid ON lease6 (iaid, subnet_id, duid);
 CREATE INDEX lease6_by_iaid_subnet_id_duid ON lease6 (iaid, subnet_id, duid);
 
 
 # ... and a definition of lease6 types.  This table is a convenience for
 # ... and a definition of lease6 types.  This table is a convenience for
@@ -86,11 +86,8 @@ COMMIT;
 # This table is only modified during schema upgrades.  For historical reasons
 # 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
 # (related to the names of the columns in the BIND 10 DNS database file), the
 # first column is called "version" and not "major".
 # first column is called "version" and not "major".
-#
-# NOTE: this MUST be kept in step with src/lib/dhcpsrv/tests/schema_copy.h,
-#       which defines the schema for the unit tests.  If you are updating
-#       the version number, the schema has changed: please ensure that
-#       schema_copy.h has been updated as well.
+# Note: This MUST be kept in step with src/share/database/scripts/mysql/dhcpdb_create.mysql,
+# which defines the schema for the unit tests.
 CREATE TABLE schema_version (
 CREATE TABLE schema_version (
     version INT PRIMARY KEY NOT NULL,       # Major version number
     version INT PRIMARY KEY NOT NULL,       # Major version number
     minor INT                               # Minor version number
     minor INT                               # Minor version number

+ 2 - 1
src/bin/admin/tests/dhcpdb_create_1.0.pgsql

@@ -83,11 +83,8 @@ COMMIT;
 -- This table is only modified during schema upgrades.  For historical reasons
 -- 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
 -- (related to the names of the columns in the BIND 10 DNS database file), the
 -- first column is called "version" and not "major".
 -- first column is called "version" and not "major".
-
+-- Note: This MUST be kept in step with src/share/database/scripts/pgsql/dhcpdb_create.pgsql,
+-- which defines the schema for the unit tests.
 CREATE TABLE schema_version (
 CREATE TABLE schema_version (
     version INT PRIMARY KEY NOT NULL,       -- Major version number
     version INT PRIMARY KEY NOT NULL,       -- Major version number
     minor INT                               -- Minor version number
     minor INT                               -- Minor version number

+ 1 - 1
src/bin/admin/tests/pgsql_tests.sh.in

@@ -27,7 +27,7 @@ pgsql_wipe() {
     printf "Wiping whole database %s\n" $db_name
     printf "Wiping whole database %s\n" $db_name
     export PGPASSWORD=$db_password
     export PGPASSWORD=$db_password
 
 
-    cat $db_scripts_dir/pgsql/dhcpdb_drop.pgsql | psql --set ON_ERROR_STOP=1 -A -t -q -U keatest -d keatest >/dev/null 2>&1
+    cat $db_scripts_dir/pgsql/dhcpdb_drop.pgsql | psql --set ON_ERROR_STOP=1 -A -t -h localhost -q -U keatest -d keatest >/dev/null 2>&1
     assert_eq 0 $?  "pgsql_wipe drop failed, expected exit code: %d, actual: %d"
     assert_eq 0 $?  "pgsql_wipe drop failed, expected exit code: %d, actual: %d"
 }
 }
 
 

+ 16 - 15
src/lib/dhcpsrv/cql_connection.cc

@@ -22,14 +22,16 @@ using namespace std;
 namespace isc {
 namespace isc {
 namespace dhcp {
 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),
+        tagged_statements_(NULL) {
 }
 }
 
 
 CqlConnection::~CqlConnection() {
 CqlConnection::~CqlConnection() {
+    // Free up the prepared statements, ignoring errors.
+    // Session and connection resources are deallocated.
     CassError rc;
     CassError rc;
-    for (int i = 0; i < statements_.size(); i++)
-    {
+    for (int i = 0; i < statements_.size(); i++) {
         if (statements_[i]) {
         if (statements_[i]) {
             cass_prepared_free(statements_[i]);
             cass_prepared_free(statements_[i]);
         }
         }
@@ -60,7 +62,7 @@ CqlConnection::openDatabase() {
         scontact_points = getParameter("contact_points");
         scontact_points = getParameter("contact_points");
         contact_points = scontact_points.c_str();
         contact_points = scontact_points.c_str();
     } catch (...) {
     } catch (...) {
-        // No host.  Fine, we'll use "localhost"
+        // No host. Fine, we'll use "localhost".
     }
     }
 
 
     const char* port = NULL;
     const char* port = NULL;
@@ -69,7 +71,7 @@ CqlConnection::openDatabase() {
         sport = getParameter("port");
         sport = getParameter("port");
         port = sport.c_str();
         port = sport.c_str();
     } catch (...) {
     } catch (...) {
-        // No port.  Fine, we'll use "default"
+        // No port. Fine, we'll use "default".
     }
     }
 
 
     const char* user = NULL;
     const char* user = NULL;
@@ -78,7 +80,7 @@ CqlConnection::openDatabase() {
         suser = getParameter("user");
         suser = getParameter("user");
         user = suser.c_str();
         user = suser.c_str();
     } catch (...) {
     } catch (...) {
-        // No user.  Fine, we'll use NULL
+        // No user. Fine, we'll use NULL.
     }
     }
 
 
     const char* password = NULL;
     const char* password = NULL;
@@ -87,7 +89,7 @@ CqlConnection::openDatabase() {
         spassword = getParameter("password");
         spassword = getParameter("password");
         password = spassword.c_str();
         password = spassword.c_str();
     } catch (...) {
     } catch (...) {
-        // No password.  Fine, we'll use NULL
+        // No password. Fine, we'll use NULL.
     }
     }
 
 
     const char* keyspace = "keatest";
     const char* keyspace = "keatest";
@@ -96,7 +98,7 @@ CqlConnection::openDatabase() {
         skeyspace = getParameter("keyspace");
         skeyspace = getParameter("keyspace");
         keyspace = skeyspace.c_str();
         keyspace = skeyspace.c_str();
     } catch (...) {
     } catch (...) {
-        // No database name.  Fine, we'll use default 'keatest'
+        // No database name. Fine, we'll use default "keatest".
     }
     }
 
 
     cluster_ = cass_cluster_new();
     cluster_ = cass_cluster_new();
@@ -202,8 +204,7 @@ CqlConnection::rollback() {
 
 
 void
 void
 CqlConnection::checkStatementError(std::string& error, CassFuture* future,
 CqlConnection::checkStatementError(std::string& error, CassFuture* future,
-    uint32_t stindex, const char* what) const
-{
+        uint32_t stindex, const char* what) const {
     CassError rc;
     CassError rc;
     const char* errorMessage;
     const char* errorMessage;
     size_t errorMessageSize;
     size_t errorMessageSize;
@@ -215,15 +216,15 @@ CqlConnection::checkStatementError(std::string& error, CassFuture* future,
 
 
     if (rc != CASS_OK) {
     if (rc != CASS_OK) {
         stream.str(std::string());
         stream.str(std::string());
-        stream << what << " for: " << tagged_statements_[stindex].name_ << " reason: " <<
-            errorMessage << " error code: " << rc;
+        stream << what << " for: " << tagged_statements_[stindex].name_ <<
+            " reason: " << errorMessage << " error code: " << rc;
     }
     }
     error = stream.str();
     error = stream.str();
 }
 }
 
 
 void
 void
-CqlConnection::checkStatementError(std::string& error, CassFuture* future, const char* what) const
-{
+CqlConnection::checkStatementError(std::string& error, CassFuture* future,
+        const char* what) const {
     CassError rc;
     CassError rc;
     const char* errorMessage;
     const char* errorMessage;
     size_t errorMessageSize;
     size_t errorMessageSize;

+ 22 - 17
src/lib/dhcpsrv/cql_connection.h

@@ -27,20 +27,19 @@ namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
 /// @brief  Defines a single query
 /// @brief  Defines a single query
+///
+/// @param params_ Bind parameter names
+/// @param name_ Short name of the query.
+/// @param text_ Text representation of the actual query.
 struct CqlTaggedStatement {
 struct CqlTaggedStatement {
-    /// Param name.
     const char** params_;
     const char** params_;
-
-    /// Short name of the query.
     const char* name_;
     const char* name_;
-
-    /// Text representation of the actual query.
     const char* text_;
     const char* text_;
 };
 };
 
 
-/// Defines CQL backend version: 1.0
-const uint32_t CQL_CURRENT_VERSION = 1;
-const uint32_t CQL_CURRENT_MINOR = 0;
+// Defines CQL backend version: 2.3
+const uint32_t CQL_CURRENT_VERSION = CASS_VERSION_MAJOR;
+const uint32_t CQL_CURRENT_MINOR = CASS_VERSION_MINOR;
 
 
 class CqlConnection : public DatabaseConnection {
 class CqlConnection : public DatabaseConnection {
 public:
 public:
@@ -55,28 +54,26 @@ public:
 
 
     /// @brief Prepare statements
     /// @brief Prepare statements
     ///
     ///
-    /// Creates the prepared statements for all of the SQL statements used
+    /// Creates the prepared statements for all of the CQL statements used
     /// by the CQL backend.
     /// by the CQL backend.
     ///
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
     ///        failed.
-    /// @throw isc::InvalidParameter 'index' is not valid for the vector.  This
+    /// @throw isc::InvalidParameter 'index' is not valid for the vector. This
     ///        represents an internal error within the code.
     ///        represents an internal error within the code.
     void prepareStatements(CqlTaggedStatement *statements);
     void prepareStatements(CqlTaggedStatement *statements);
 
 
     /// @brief Open Database
     /// @brief Open Database
     ///
     ///
     /// Opens the database using the information supplied in the parameters
     /// Opens the database using the information supplied in the parameters
-    /// passed to the constructor.
+    /// passed to the constructor. If no parameters are supplied, the default
+    /// values will be used (keyspace keatest).
     ///
     ///
-    /// @throw NoDatabaseName Mandatory database name not given
     /// @throw DbOpenError Error opening the database
     /// @throw DbOpenError Error opening the database
     void openDatabase();
     void openDatabase();
 
 
     /// @brief Return backend type
     /// @brief Return backend type
     ///
     ///
-    /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
-    ///
     /// @return Type of the backend.
     /// @return Type of the backend.
     virtual std::string getType() const {
     virtual std::string getType() const {
         return (std::string("cql"));
         return (std::string("cql"));
@@ -96,7 +93,7 @@ public:
 
 
     /// @brief Returns backend version.
     /// @brief Returns backend version.
     ///
     ///
-    /// @return Version number as a pair of unsigned integers.  "first" is the
+    /// @return Version number as a pair of unsigned integers. "first" is the
     ///         major version number, "second" the minor number.
     ///         major version number, "second" the minor number.
     ///
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
@@ -129,10 +126,18 @@ public:
     void checkStatementError(std::string& error, CassFuture* future,
     void checkStatementError(std::string& error, CassFuture* future,
         const char* what) const;
         const char* what) const;
 
 
-    /// CQL connection handle
+    /// @brief CQL connection handle
     CassCluster* cluster_;
     CassCluster* cluster_;
+
+    /// @brief CQL session handle
     CassSession* session_;
     CassSession* session_;
-    std::vector<const CassPrepared*> statements_;       ///< Prepared statements
+
+    /// @brief CQL prepared statements - used for faster statement execution using
+    /// bind functionality
+    std::vector<const CassPrepared*> statements_;
+
+    /// @brief Pointer to external array of tagged statements containing statement
+    /// name, array of names of bind parameters and text query
     CqlTaggedStatement* tagged_statements_;
     CqlTaggedStatement* tagged_statements_;
 };
 };
 
 

+ 273 - 138
src/lib/dhcpsrv/cql_lease_mgr.cc

@@ -37,34 +37,49 @@ namespace dhcp {
 static const size_t HOSTNAME_MAX_LEN = 255;
 static const size_t HOSTNAME_MAX_LEN = 255;
 static const size_t ADDRESS6_TEXT_MAX_LEN = 39;
 static const size_t ADDRESS6_TEXT_MAX_LEN = 39;
 
 
+/// @name CqlBind auxiliary methods for binding data into Cassandra format:
+/// @{
 static CassError CqlBindNone(CassStatement* statement, size_t index, void*) {
 static CassError CqlBindNone(CassStatement* statement, size_t index, void*) {
     return cass_statement_bind_null(statement, index);
     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 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 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 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 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 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)))[0],
-            static_cast<std::vector<cass_byte_t>*>(value)->size());
+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*) {
 static CassError CqlGetNone(const CassValue*, void*, size_t*) {
     return CASS_OK;
     return CASS_OK;
@@ -86,16 +101,20 @@ static CassError CqlGetTimestamp(const CassValue* value, void* data, size_t*) {
     return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
     return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
 }
 }
 
 
-static CassError CqlGetString(const CassValue* value, void* data, size_t* size) {
+static CassError CqlGetString(const CassValue* value, void* data,
+        size_t* size) {
     return cass_value_get_string(value, static_cast<const char**>(data), size);
     return cass_value_get_string(value, static_cast<const char**>(data), size);
 }
 }
 
 
 static CassError CqlGetBytes(const CassValue* value, void* data, size_t* 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);
+    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);
+typedef CassError (*CqlBindFunction)(CassStatement* statement, size_t index,
+        void* value);
+typedef CassError (*CqlGetFunction)(const CassValue* value, void* data,
+        size_t* size);
 
 
 struct CqlFunctionData {
 struct CqlFunctionData {
     CqlBindFunction sqlBindFunction_;
     CqlBindFunction sqlBindFunction_;
@@ -112,9 +131,9 @@ static struct CqlFunctionData CqlFunctions[] = {
     {CqlBindBytes, CqlGetBytes}
     {CqlBindBytes, CqlGetBytes}
 };
 };
 
 
-/// @brief Catalog of all the SQL statements currently supported.  Note
+/// @brief Catalog of all the SQL statements currently supported. Note
 /// that the order columns appear in statement body must match the order they
 /// that the 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.
+/// that the occur in the table. This does not apply to the where clause.
 
 
 static const char* delete_lease4_params[] = {
 static const char* delete_lease4_params[] = {
         static_cast<const char*>("address"),
         static_cast<const char*>("address"),
@@ -173,7 +192,6 @@ static const char* get_lease6_expired_params[] = {
         static_cast<const char*>("limit"),
         static_cast<const char*>("limit"),
         NULL };
         NULL };
 static const char* get_version_params[] = {
 static const char* get_version_params[] = {
-        static_cast<const char*>("version"),
         NULL };
         NULL };
 static const char* insert_lease4_params[] = {
 static const char* insert_lease4_params[] = {
         static_cast<const char*>("address"),
         static_cast<const char*>("address"),
@@ -318,7 +336,6 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
         "valid_lifetime, expire, subnet_id, "
         "valid_lifetime, expire, subnet_id, "
         "fqdn_fwd, fqdn_rev, hostname, state "
         "fqdn_fwd, fqdn_rev, hostname, state "
       "FROM lease4 "
       "FROM lease4 "
-      //"WHERE state != ? AND expire < ? ORDER BY expire ASC "
       "WHERE state = ? AND expire < ? "
       "WHERE state = ? AND expire < ? "
       "LIMIT ? ALLOW FILTERING" },
       "LIMIT ? ALLOW FILTERING" },
 
 
@@ -373,7 +390,8 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
     { insert_lease4_params,
     { insert_lease4_params,
       "insert_lease4",
       "insert_lease4",
       "INSERT INTO lease4(address, hwaddr, client_id, "
       "INSERT INTO lease4(address, hwaddr, client_id, "
-        "valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, state) "
+        "valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, "
+        "state) "
       "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
       "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
     },
     },
 
 
@@ -382,7 +400,8 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
       "insert_lease6",
       "insert_lease6",
       "INSERT INTO lease6(address, duid, valid_lifetime, "
       "INSERT INTO lease6(address, duid, valid_lifetime, "
         "expire, subnet_id, pref_lifetime, "
         "expire, subnet_id, pref_lifetime, "
-        "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, hwaddr_source, state) "
+        "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, "
+        "hwtype, hwaddr_source, state) "
       "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
       "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
     },
     },
 
 
@@ -410,23 +429,15 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
     { NULL, NULL, NULL }
     { NULL, NULL, NULL }
 };
 };
 
 
-
-/// @brief Common CQL and Lease Data Methods
-///
-/// The CqlLease4Exchange and CqlLease6Exchange classes provide the
-/// functionality to set up binding information between variables in the
-/// program and data extracted from the database.  This class is the common
-/// base to both of them, containing some common methods.
-
 class CqlExchange : public virtual SqlExchange {
 class CqlExchange : public virtual SqlExchange {
 public:
 public:
     // Time conversion methods.
     // Time conversion methods.
     static void
     static void
     convertToDatabaseTime(const time_t& cltt,
     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.
+                          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) +
         int64_t expire_time = static_cast<int64_t>(cltt) +
             static_cast<int64_t>(valid_lifetime);
             static_cast<int64_t>(valid_lifetime);
 
 
@@ -439,18 +450,24 @@ public:
 
 
     static void
     static void
     convertFromDatabaseTime(const uint64_t& expire,
     convertFromDatabaseTime(const uint64_t& expire,
-                                           const uint32_t& valid_lifetime,
-                                           time_t& cltt) {
+                            const uint32_t& valid_lifetime,
+                            time_t& cltt) {
         // Convert to local time
         // Convert to local time
         cltt = expire - static_cast<int64_t>(valid_lifetime);
         cltt = expire - static_cast<int64_t>(valid_lifetime);
     }
     }
 };
 };
 
 
+/// @brief Common CQL and Lease Data Methods
+///
+/// The CqlLease4Exchange and CqlLease6Exchange classes provide the
+/// functionality to set up binding information between variables in the
+/// program and data extracted from the database. This class is the common
+/// base to both of them, containing some common methods.
 class CqlLeaseExchange : public CqlExchange {
 class CqlLeaseExchange : public CqlExchange {
 public:
 public:
-    CqlLeaseExchange() : hwaddr_length_(0), expire_(0),
-                            subnet_id_(0), valid_lifetime_(0),
-                            fqdn_fwd_(false), fqdn_rev_(false), hostname_length_(0), state_(0) {
+    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(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
         memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
         memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
     }
     }
@@ -473,17 +490,38 @@ protected:
 };
 };
 
 
 
 
+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 = 2;
+        // Set the column names (for error messages)
+        size_t offset = 0;
+        BOOST_ASSERT(2 == MAX_COLUMNS);
+        parameters_.resize(MAX_COLUMNS);
+        parameters_[offset++] = ExchangeColumnInfo("version",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
+        parameters_[offset++] = ExchangeColumnInfo("minor",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
+        BOOST_ASSERT(offset == MAX_COLUMNS);
+    }
+};
+
+
 /// @brief Exchange CQL and Lease4 Data
 /// @brief Exchange CQL and Lease4 Data
 ///
 ///
 /// On any CQL operation, arrays of CQL BIND structures must be built to
 /// On any CQL operation, arrays of CQL BIND structures must be built to
-/// describe the parameters in the prepared statements.  Where information is
+/// describe the parameters in the prepared statements. Where information is
 /// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
 /// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
-/// structure is identical.  This class handles the creation of that array.
+/// structure is identical. This class handles the creation of that array.
 ///
 ///
 /// Owing to the CQL API, the process requires some intermediate variables
 /// Owing to the CQL API, the process requires some intermediate variables
-/// to hold things like data length etc.  This object holds those variables.
+/// to hold things like data length etc. This object holds those variables.
 ///
 ///
-/// @note There are no unit tests for this class.  It is tested indirectly
+/// @note There are no unit tests for this class. It is tested indirectly
 /// in all CqlLeaseMgr::xxx4() calls where it is used.
 /// in all CqlLeaseMgr::xxx4() calls where it is used.
 
 
 class CqlLease4Exchange : public CqlLeaseExchange {
 class CqlLease4Exchange : public CqlLeaseExchange {
@@ -494,25 +532,36 @@ public:
     /// all variables are initialized/set in the methods before they are used.
     /// all variables are initialized/set in the methods before they are used.
     CqlLease4Exchange() : addr4_(0), client_id_length_(0),
     CqlLease4Exchange() : addr4_(0), client_id_length_(0),
                             client_id_null_(false) {
                             client_id_null_(false) {
+        const size_t MAX_COLUMNS = 11;
         memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
         memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
 
 
         // Set the column names (for error messages)
         // Set the column names (for error messages)
-        uint32_t size;
-        uint32_t offset = 0;
-        size = 12;
-        parameters_.resize(size);
-        parameters_[offset++] = ExchangeColumnInfo("address", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
-        parameters_[offset++] = ExchangeColumnInfo("hwaddr", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES);
-        parameters_[offset++] = ExchangeColumnInfo("client_id", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES);
-        parameters_[offset++] = ExchangeColumnInfo("valid_lifetime", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64);
-        parameters_[offset++] = ExchangeColumnInfo("expire", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_TIMESTAMP);
-        parameters_[offset++] = ExchangeColumnInfo("subnet_id", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
-        parameters_[offset++] = ExchangeColumnInfo("fqdn_fwd", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL);
-        parameters_[offset++] = ExchangeColumnInfo("fqdn_rev", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL);
-        parameters_[offset++] = ExchangeColumnInfo("hostname", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING);
-        parameters_[offset++] = ExchangeColumnInfo("state", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
-        parameters_[offset++] = ExchangeColumnInfo("limit", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
-        parameters_[offset++] = ExchangeColumnInfo("version", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_NONE);
+        size_t offset = 0;
+        BOOST_STATIC_ASSERT(11 == MAX_COLUMNS);
+        parameters_.resize(MAX_COLUMNS);
+        parameters_[offset++] = ExchangeColumnInfo("address",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
+        parameters_[offset++] = ExchangeColumnInfo("hwaddr",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES);
+        parameters_[offset++] = ExchangeColumnInfo("client_id",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES);
+        parameters_[offset++] = ExchangeColumnInfo("valid_lifetime",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64);
+        parameters_[offset++] = ExchangeColumnInfo("expire",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_TIMESTAMP);
+        parameters_[offset++] = ExchangeColumnInfo("subnet_id",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
+        parameters_[offset++] = ExchangeColumnInfo("fqdn_fwd",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL);
+        parameters_[offset++] = ExchangeColumnInfo("fqdn_rev",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL);
+        parameters_[offset++] = ExchangeColumnInfo("hostname",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING);
+        parameters_[offset++] = ExchangeColumnInfo("state",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
+        parameters_[offset++] = ExchangeColumnInfo("limit",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
+        BOOST_ASSERT(offset == MAX_COLUMNS);
     }
     }
 
 
     /// @brief Create CQL_BIND objects for Lease4 Pointer
     /// @brief Create CQL_BIND objects for Lease4 Pointer
@@ -530,8 +579,8 @@ public:
 
 
         try {
         try {
             // address: uint32_t
             // address: uint32_t
-            // The address in the Lease structure is an IOAddress object.  Convert
-            // this to an integer for storage.
+            // The address in the Lease structure is an IOAddress object.
+            // Convert this to an integer for storage.
             addr4_ = static_cast<uint32_t>(lease_->addr_);
             addr4_ = static_cast<uint32_t>(lease_->addr_);
             data.add(&addr4_);
             data.add(&addr4_);
 
 
@@ -554,10 +603,11 @@ public:
             // expire: timestamp
             // expire: timestamp
             // 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
             // For convenience for external tools, this is converted to lease
-            // expiry time (expire).  The relationship is given by:
+            // expiry time (expire). The relationship is given by:
             //
             //
             // expire = cltt_ + valid_lft_
             // expire = cltt_ + valid_lft_
-            CqlLeaseExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_, expire_);
+            CqlLeaseExchange::convertToDatabaseTime(lease_->cltt_,
+                lease_->valid_lft_, expire_);
             data.add(&expire_);
             data.add(&expire_);
 
 
             // subnet_id: unsigned int
             // subnet_id: unsigned int
@@ -576,10 +626,12 @@ public:
             // hostname: varchar(255)
             // hostname: varchar(255)
             hostname_length_  = lease_->hostname_.length();
             hostname_length_  = lease_->hostname_.length();
             if (hostname_length_ >= sizeof(hostname_buffer_)) {
             if (hostname_length_ >= sizeof(hostname_buffer_)) {
-                isc_throw(BadValue, "hostname value is too large: " << lease_->hostname_.c_str());
+                isc_throw(BadValue, "hostname value is too large: " <<
+                    lease_->hostname_.c_str());
             }
             }
             if (hostname_length_) {
             if (hostname_length_) {
-                memcpy(hostname_buffer_, lease_->hostname_.c_str(), hostname_length_);
+                memcpy(hostname_buffer_, lease_->hostname_.c_str(),
+                    hostname_length_);
             }
             }
             hostname_buffer_[hostname_length_] = '\0';
             hostname_buffer_[hostname_length_] = '\0';
             data.add(hostname_buffer_);
             data.add(hostname_buffer_);
@@ -655,9 +707,11 @@ public:
             hwaddr_.assign(hwaddr_buffer, hwaddr_buffer + hwaddr_length_);
             hwaddr_.assign(hwaddr_buffer, hwaddr_buffer + hwaddr_length_);
 
 
             // client_id: varbinary(128)
             // client_id: varbinary(128)
-            client_id_.assign(client_id_buffer, client_id_buffer + client_id_length_);
+            client_id_.assign(client_id_buffer, client_id_buffer +
+                client_id_length_);
             if (client_id_length_ >= sizeof(client_id_buffer_)) {
             if (client_id_length_ >= sizeof(client_id_buffer_)) {
-                isc_throw(BadValue, "client id value is too large: " << client_id_buffer);
+                isc_throw(BadValue, "client id value is too large: " <<
+                    client_id_buffer);
             }
             }
             if (client_id_length_) {
             if (client_id_length_) {
                 memcpy(client_id_buffer_, client_id_buffer, client_id_length_);
                 memcpy(client_id_buffer_, client_id_buffer, client_id_length_);
@@ -666,7 +720,8 @@ public:
 
 
             // hostname: varchar(255)
             // hostname: varchar(255)
             if (hostname_length_ >= sizeof(hostname_buffer_)) {
             if (hostname_length_ >= sizeof(hostname_buffer_)) {
-                isc_throw(BadValue, "hostname value is too large: " << hostname_buffer);
+                isc_throw(BadValue, "hostname value is too large: " <<
+                    hostname_buffer);
             }
             }
             if (hostname_length_) {
             if (hostname_length_) {
                 memcpy(hostname_buffer_, hostname_buffer, hostname_length_);
                 memcpy(hostname_buffer_, hostname_buffer, hostname_length_);
@@ -674,7 +729,8 @@ public:
             hostname_buffer_[hostname_length_] = '\0';
             hostname_buffer_[hostname_length_] = '\0';
 
 
             time_t cltt = 0;
             time_t cltt = 0;
-            CqlLeaseExchange::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
+            CqlLeaseExchange::convertFromDatabaseTime(expire_, valid_lifetime_,
+                cltt);
 
 
             // Recreate the hardware address.
             // Recreate the hardware address.
             HWAddrPtr hwaddr(new HWAddr(hwaddr_, HTYPE_ETHER));
             HWAddrPtr hwaddr(new HWAddr(hwaddr_, HTYPE_ETHER));
@@ -682,11 +738,10 @@ public:
             std::string hostname(hostname_buffer_,
             std::string hostname(hostname_buffer_,
                                  hostname_buffer_ + hostname_length_);
                                  hostname_buffer_ + hostname_length_);
 
 
-            Lease4Ptr result(new Lease4(addr4_, hwaddr,
-                                                     client_id_buffer_, client_id_length_,
-                                                     valid_lifetime_, 0, 0, cltt,
-                                                     subnet_id_, fqdn_fwd_, fqdn_rev_,
-                                                     hostname));
+            Lease4Ptr result(new Lease4(addr4_, hwaddr, client_id_buffer_,
+                                        client_id_length_, valid_lifetime_, 0,
+                                        0, cltt, subnet_id_, fqdn_fwd_,
+                                        fqdn_rev_, hostname));
 
 
             result->state_ = state_;
             result->state_ = state_;
 
 
@@ -713,19 +768,17 @@ private:
     bool            client_id_null_;    ///< Is Client ID null?
     bool            client_id_null_;    ///< Is Client ID null?
 };
 };
 
 
-
-
 /// @brief Exchange CQL and Lease6 Data
 /// @brief Exchange CQL and Lease6 Data
 ///
 ///
 /// On any CQL operation, arrays of CQL BIND structures must be built to
 /// On any CQL operation, arrays of CQL BIND structures must be built to
-/// describe the parameters in the prepared statements.  Where information is
+/// describe the parameters in the prepared statements. Where information is
 /// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
 /// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
-/// structure is identical.  This class handles the creation of that array.
+/// structure is identical. This class handles the creation of that array.
 ///
 ///
 /// Owing to the CQL API, the process requires some intermediate variables
 /// Owing to the CQL API, the process requires some intermediate variables
-/// to hold things like data length etc.  This object holds those variables.
+/// to hold things like data length etc. This object holds those variables.
 ///
 ///
-/// @note There are no unit tests for this class.  It is tested indirectly
+/// @note There are no unit tests for this class. It is tested indirectly
 /// in all CqlLeaseMgr::xxx6() calls where it is used.
 /// in all CqlLeaseMgr::xxx6() calls where it is used.
 
 
 class CqlLease6Exchange : public CqlLeaseExchange {
 class CqlLease6Exchange : public CqlLeaseExchange {
@@ -738,32 +791,49 @@ public:
                             iaid_(0), lease_type_(0), prefixlen_(0),
                             iaid_(0), lease_type_(0), prefixlen_(0),
                             pref_lifetime_(0), hwaddr_null_(false), hwtype_(0),
                             pref_lifetime_(0), hwaddr_null_(false), hwtype_(0),
                             hwaddr_source_(0) {
                             hwaddr_source_(0) {
+        const size_t MAX_COLUMNS = 17;
         memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
         memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
         memset(duid_buffer_, 0, sizeof(duid_buffer_));
         memset(duid_buffer_, 0, sizeof(duid_buffer_));
 
 
         // Set the column names (for error messages)
         // Set the column names (for error messages)
-        uint32_t size;
-        uint32_t offset = 0;
-        size = 18;
-        parameters_.resize(size);
-        parameters_[offset++] = ExchangeColumnInfo("address", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING);
-        parameters_[offset++] = ExchangeColumnInfo("duid", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES);
-        parameters_[offset++] = ExchangeColumnInfo("valid_lifetime", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64);
-        parameters_[offset++] = ExchangeColumnInfo("expire", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_TIMESTAMP);
-        parameters_[offset++] = ExchangeColumnInfo("subnet_id", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
-        parameters_[offset++] = ExchangeColumnInfo("pref_lifetime", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64);
-        parameters_[offset++] = ExchangeColumnInfo("lease_type", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
-        parameters_[offset++] = ExchangeColumnInfo("iaid", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
-        parameters_[offset++] = ExchangeColumnInfo("prefix_len", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
-        parameters_[offset++] = ExchangeColumnInfo("fqdn_fwd", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL);
-        parameters_[offset++] = ExchangeColumnInfo("fqdn_rev", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL);
-        parameters_[offset++] = ExchangeColumnInfo("hostname", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING);
-        parameters_[offset++] = ExchangeColumnInfo("hwaddr", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES);
-        parameters_[offset++] = ExchangeColumnInfo("hwtype", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
-        parameters_[offset++] = ExchangeColumnInfo("hwaddr_source", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
-        parameters_[offset++] = ExchangeColumnInfo("state", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
-        parameters_[offset++] = ExchangeColumnInfo("limit", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
-        parameters_[offset++] = ExchangeColumnInfo("version", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_NONE);
+        size_t offset = 0;
+        BOOST_STATIC_ASSERT(17 == MAX_COLUMNS);
+        parameters_.resize(MAX_COLUMNS);
+        parameters_[offset++] = ExchangeColumnInfo("address",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING);
+        parameters_[offset++] = ExchangeColumnInfo("duid",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES);
+        parameters_[offset++] = ExchangeColumnInfo("valid_lifetime",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64);
+        parameters_[offset++] = ExchangeColumnInfo("expire",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_TIMESTAMP);
+        parameters_[offset++] = ExchangeColumnInfo("subnet_id",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
+        parameters_[offset++] = ExchangeColumnInfo("pref_lifetime",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64);
+        parameters_[offset++] = ExchangeColumnInfo("lease_type",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
+        parameters_[offset++] = ExchangeColumnInfo("iaid",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
+        parameters_[offset++] = ExchangeColumnInfo("prefix_len",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
+        parameters_[offset++] = ExchangeColumnInfo("fqdn_fwd",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL);
+        parameters_[offset++] = ExchangeColumnInfo("fqdn_rev",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL);
+        parameters_[offset++] = ExchangeColumnInfo("hostname",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING);
+        parameters_[offset++] = ExchangeColumnInfo("hwaddr",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES);
+        parameters_[offset++] = ExchangeColumnInfo("hwtype",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
+        parameters_[offset++] = ExchangeColumnInfo("hwaddr_source",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
+        parameters_[offset++] = ExchangeColumnInfo("state",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
+        parameters_[offset++] = ExchangeColumnInfo("limit",
+            EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32);
+        BOOST_ASSERT(offset == MAX_COLUMNS);
     }
     }
 
 
     /// @brief Create CQL_BIND objects for Lease6 Pointer
     /// @brief Create CQL_BIND objects for Lease6 Pointer
@@ -776,15 +846,16 @@ public:
         }
         }
         // Store lease object to ensure it remains valid.
         // Store lease object to ensure it remains valid.
         lease_ = lease;
         lease_ = lease;
+
         // Set up the structures for the various components of the lease4
         // Set up the structures for the various components of the lease4
         // structure.
         // structure.
-
         try {
         try {
             // address: varchar(39)
             // address: varchar(39)
             std::string text_buffer = lease_->addr_.toText();
             std::string text_buffer = lease_->addr_.toText();
             addr6_length_ = text_buffer.size();
             addr6_length_ = text_buffer.size();
             if (addr6_length_ >= sizeof(addr6_buffer_)) {
             if (addr6_length_ >= sizeof(addr6_buffer_)) {
-                isc_throw(BadValue, "address value is too large: " << text_buffer);
+                isc_throw(BadValue, "address value is too large: " <<
+                    text_buffer);
             }
             }
             if (addr6_length_) {
             if (addr6_length_) {
                 memcpy(addr6_buffer_, text_buffer.c_str(), addr6_length_);
                 memcpy(addr6_buffer_, text_buffer.c_str(), addr6_length_);
@@ -794,8 +865,8 @@ public:
 
 
             // duid: varchar(128)
             // duid: varchar(128)
             if (!lease_->duid_) {
             if (!lease_->duid_) {
-                isc_throw(DbOperationError, "lease6 for address " << addr6_buffer_
-                          << " is missing mandatory client-id.");
+                isc_throw(DbOperationError, "lease6 for address " <<
+                    addr6_buffer_ << " is missing mandatory client-id.");
             }
             }
             duid_ = lease_->duid_->getDuid();
             duid_ = lease_->duid_->getDuid();
             duid_length_ = duid_.size();
             duid_length_ = duid_.size();
@@ -808,10 +879,11 @@ public:
             // expire: timestamp
             // expire: timestamp
             // 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
             // For convenience for external tools, this is converted to lease
-            // expiry time (expire).  The relationship is given by:
+            // expiry time (expire). The relationship is given by:
             //
             //
             // expire = cltt_ + valid_lft_
             // expire = cltt_ + valid_lft_
-            CqlLeaseExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_, expire_);
+            CqlLeaseExchange::convertToDatabaseTime(lease_->cltt_,
+                lease_->valid_lft_, expire_);
             data.add(&expire_);
             data.add(&expire_);
 
 
             // subnet_id: unsigned int
             // subnet_id: unsigned int
@@ -850,10 +922,12 @@ public:
             // hostname: varchar(255)
             // hostname: varchar(255)
             hostname_length_  = lease_->hostname_.length();
             hostname_length_  = lease_->hostname_.length();
             if (hostname_length_ >= sizeof(hostname_buffer_)) {
             if (hostname_length_ >= sizeof(hostname_buffer_)) {
-                isc_throw(BadValue, "hostname value is too large: " << lease_->hostname_.c_str());
+                isc_throw(BadValue, "hostname value is too large: " <<
+                    lease_->hostname_.c_str());
             }
             }
             if (hostname_length_) {
             if (hostname_length_) {
-                memcpy(hostname_buffer_, lease_->hostname_.c_str(), hostname_length_);
+                memcpy(hostname_buffer_, lease_->hostname_.c_str(),
+                    hostname_length_);
             }
             }
             hostname_buffer_[hostname_length_] = '\0';
             hostname_buffer_[hostname_length_] = '\0';
             data.add(hostname_buffer_);
             data.add(hostname_buffer_);
@@ -977,7 +1051,8 @@ public:
 
 
             // address: varchar(39)
             // address: varchar(39)
             if (addr6_length_ >= sizeof(addr6_buffer_)) {
             if (addr6_length_ >= sizeof(addr6_buffer_)) {
-                isc_throw(BadValue, "address value is too large: " << address_buffer);
+                isc_throw(BadValue, "address value is too large: " <<
+                    address_buffer);
             }
             }
             if (addr6_length_) {
             if (addr6_length_) {
                 memcpy(addr6_buffer_, address_buffer, addr6_length_);
                 memcpy(addr6_buffer_, address_buffer, addr6_length_);
@@ -989,7 +1064,8 @@ public:
 
 
             // hostname: varchar(255)
             // hostname: varchar(255)
             if (hostname_length_ >= sizeof(hostname_buffer_)) {
             if (hostname_length_ >= sizeof(hostname_buffer_)) {
-                isc_throw(BadValue, "hostname value is too large: " << hostname_buffer);
+                isc_throw(BadValue, "hostname value is too large: " <<
+                    hostname_buffer);
             }
             }
             if (hostname_length_) {
             if (hostname_length_) {
                 memcpy(hostname_buffer_, hostname_buffer, hostname_length_);
                 memcpy(hostname_buffer_, hostname_buffer, hostname_length_);
@@ -1021,13 +1097,15 @@ public:
 
 
             // Create the lease and set the cltt (after converting from the
             // Create the lease and set the cltt (after converting from the
             // expire time retrieved from the database).
             // 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;
             time_t cltt = 0;
-            CqlLeaseExchange::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
+            CqlLeaseExchange::convertFromDatabaseTime(expire_, valid_lifetime_,
+                cltt);
             result->cltt_ = cltt;
             result->cltt_ = cltt;
 
 
             result->state_ = state_;
             result->state_ = state_;
@@ -1063,12 +1141,14 @@ private:
 
 
 CqlLeaseMgr::CqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
 CqlLeaseMgr::CqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
     : LeaseMgr(), dbconn_(parameters), exchange4_(new CqlLease4Exchange()),
     : LeaseMgr(), dbconn_(parameters), exchange4_(new CqlLease4Exchange()),
-    exchange6_(new CqlLease6Exchange()) {
+    exchange6_(new CqlLease6Exchange()), versionExchange_(new CqlVersionExchange()) {
     dbconn_.openDatabase();
     dbconn_.openDatabase();
     dbconn_.prepareStatements(CqlLeaseMgr::tagged_statements_);
     dbconn_.prepareStatements(CqlLeaseMgr::tagged_statements_);
 }
 }
 
 
 CqlLeaseMgr::~CqlLeaseMgr() {
 CqlLeaseMgr::~CqlLeaseMgr() {
+    // There is no need to close the database in this destructor: it is
+    // closed in the destructor of the dbconn_ member variable.
 }
 }
 
 
 std::string
 std::string
@@ -1081,11 +1161,13 @@ CqlLeaseMgr::getDBVersion() {
 }
 }
 
 
 void
 void
-CqlLeaseMgr::getDataType(const StatementIndex stindex, int pindex, const SqlExchange& exchange, ExchangeDataType& type) {
+CqlLeaseMgr::getDataType(const StatementIndex stindex, int pindex,
+        const SqlExchange& exchange, ExchangeDataType& type) {
     if (CqlLeaseMgr::tagged_statements_[stindex].params_ &&
     if (CqlLeaseMgr::tagged_statements_[stindex].params_ &&
             CqlLeaseMgr::tagged_statements_[stindex].params_[pindex]) {
             CqlLeaseMgr::tagged_statements_[stindex].params_[pindex]) {
         for (int i = 0; exchange.parameters_.size(); i++) {
         for (int i = 0; exchange.parameters_.size(); i++) {
-            if (!strcmp(CqlLeaseMgr::tagged_statements_[stindex].params_[pindex], exchange.parameters_[i].column_)) {
+            if (!strcmp(CqlLeaseMgr::tagged_statements_[stindex].params_[pindex],
+                    exchange.parameters_[i].column_)) {
                 type = exchange.parameters_[i].type_;
                 type = exchange.parameters_[i].type_;
                 return;
                 return;
             }
             }
@@ -1095,7 +1177,8 @@ CqlLeaseMgr::getDataType(const StatementIndex stindex, int pindex, const SqlExch
 }
 }
 
 
 void
 void
-CqlLeaseMgr::bindData(CassStatement* statement, const StatementIndex stindex, CqlDataArray& data, const SqlExchange& exchange) {
+CqlLeaseMgr::bindData(CassStatement* statement, const StatementIndex stindex,
+        CqlDataArray& data, const SqlExchange& exchange) {
     if (CqlLeaseMgr::tagged_statements_[stindex].params_ == NULL) {
     if (CqlLeaseMgr::tagged_statements_[stindex].params_ == NULL) {
         return;
         return;
     }
     }
@@ -1107,13 +1190,19 @@ CqlLeaseMgr::bindData(CassStatement* statement, const StatementIndex stindex, Cq
 }
 }
 
 
 void
 void
-CqlLeaseMgr::getData(const CassRow* row, int pindex, CqlDataArray& data, CqlDataArray& size, const SqlExchange& exchange) {
+CqlLeaseMgr::getData(const CassRow* row, int pindex, CqlDataArray& data,
+        CqlDataArray& size, const SqlExchange& exchange) {
     const CassValue* value;
     const CassValue* value;
     if (pindex >= exchange.parameters_.size()) {
     if (pindex >= exchange.parameters_.size()) {
         return;
         return;
     }
     }
     value = cass_row_get_column_by_name(row, exchange.parameters_[pindex].column_);
     value = cass_row_get_column_by_name(row, exchange.parameters_[pindex].column_);
-    CqlFunctions[exchange.parameters_[pindex].type_].sqlGetFunction_(value, data.values_[pindex], reinterpret_cast<size_t *>(size.values_[pindex]));
+    if (NULL == value) {
+        isc_throw(BadValue, "Column name "
+            << exchange.parameters_[pindex].column_ << "doesn't exist");
+    }
+    CqlFunctions[exchange.parameters_[pindex].type_].sqlGetFunction_(value,
+        data.values_[pindex], reinterpret_cast<size_t *>(size.values_[pindex]));
 }
 }
 
 
 bool
 bool
@@ -1200,8 +1289,7 @@ void CqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
     int rowCount = 0;
     int rowCount = 0;
     while (cass_iterator_next(rows)) {
     while (cass_iterator_next(rows)) {
         rowCount++;
         rowCount++;
-        if (single && rowCount > 1)
-        {
+        if (single && rowCount > 1) {
             result.clear();
             result.clear();
             break;
             break;
         }
         }
@@ -1224,7 +1312,7 @@ void
 CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data,
 CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data,
                              Lease4Ptr& result) const {
                              Lease4Ptr& result) const {
     // Create appropriate collection object and get all leases matching
     // Create appropriate collection object and get all leases matching
-    // the selection criteria.  The "single" parameter is true to indicate
+    // the selection criteria. The "single" parameter is true to indicate
     // that the called method should throw an exception if multiple
     // that the called method should throw an exception if multiple
     // matching records are found: this particular method is called when only
     // matching records are found: this particular method is called when only
     // one or zero matches is expected.
     // one or zero matches is expected.
@@ -1244,7 +1332,7 @@ void
 CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data,
 CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data,
                              Lease6Ptr& result) const {
                              Lease6Ptr& result) const {
     // Create appropriate collection object and get all leases matching
     // Create appropriate collection object and get all leases matching
-    // the selection criteria.  The "single" parameter is true to indicate
+    // the selection criteria. The "single" parameter is true to indicate
     // that the called method should throw an exception if multiple
     // that the called method should throw an exception if multiple
     // matching records are found: this particular method is called when only
     // matching records are found: this particular method is called when only
     // one or zero matches is expected.
     // one or zero matches is expected.
@@ -1259,7 +1347,7 @@ CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data,
     }
     }
 }
 }
 
 
-// Basic lease access methods.  Obtain leases from the database using various
+// Basic lease access methods. Obtain leases from the database using various
 // criteria.
 // criteria.
 
 
 Lease4Ptr
 Lease4Ptr
@@ -1342,7 +1430,8 @@ CqlLeaseMgr::getLease4(const ClientId& clientid) const {
 }
 }
 
 
 Lease4Ptr
 Lease4Ptr
-CqlLeaseMgr::getLease4(const ClientId& clientid, const HWAddr& hwaddr, SubnetID subnet_id) const {
+CqlLeaseMgr::getLease4(const ClientId& clientid, const HWAddr& hwaddr,
+        SubnetID subnet_id) const {
     /// This function is currently not implemented because allocation engine
     /// This function is currently not implemented because allocation engine
     /// searches for the lease using HW address or client identifier.
     /// searches for the lease using HW address or client identifier.
     /// It never uses both parameters in the same time. We need to
     /// It never uses both parameters in the same time. We need to
@@ -1498,7 +1587,8 @@ CqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
     uint32_t limit = max_leases > 0 ? static_cast<uint32_t>(max_leases) :
     uint32_t limit = max_leases > 0 ? static_cast<uint32_t>(max_leases) :
         std::numeric_limits<uint32_t>::max();
         std::numeric_limits<uint32_t>::max();
 
 
-    for (uint32_t state = Lease::STATE_DEFAULT; state <= Lease::STATE_EXPIRED_RECLAIMED; state++) {
+    for (uint32_t state = Lease::STATE_DEFAULT;
+            state <= Lease::STATE_EXPIRED_RECLAIMED; state++) {
         if (state == keepState) {
         if (state == keepState) {
             continue;
             continue;
         }
         }
@@ -1514,7 +1604,8 @@ CqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
 
 
         typedef typename LeaseCollection::iterator LeaseCollectionIt;
         typedef typename LeaseCollection::iterator LeaseCollectionIt;
 
 
-        for (LeaseCollectionIt it = tempCollection.begin(); it != tempCollection.end(); ++it) {
+        for (LeaseCollectionIt it = tempCollection.begin();
+                it != tempCollection.end(); ++it) {
             expired_leases.push_back((*it));
             expired_leases.push_back((*it));
         }
         }
     }
     }
@@ -1673,7 +1764,8 @@ CqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) {
 }
 }
 
 
 uint64_t
 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
     // Set up the WHERE clause value
     //"WHERE state = ? AND expire < ? ALLOW FILTERING"
     //"WHERE state = ? AND expire < ? ALLOW FILTERING"
 
 
@@ -1685,7 +1777,8 @@ CqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs, StatementIn
     data.add(&state);
     data.add(&state);
 
 
     // Expiration timestamp.
     // Expiration timestamp.
-    uint64_t expiration = static_cast<int64_t>(time(NULL) - static_cast<time_t>(secs));
+    uint64_t expiration = static_cast<int64_t>(time(NULL) -
+        static_cast<time_t>(secs));
     data.add(&expiration);
     data.add(&expiration);
 
 
     // Get the data
     // Get the data
@@ -1701,12 +1794,14 @@ CqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs, StatementIn
     default:
     default:
         break;
         break;
     }
     }
-    for (Lease4Collection::iterator it = result4Leases.begin(); it != result4Leases.end(); ++it) {
+    for (Lease4Collection::iterator it = result4Leases.begin();
+            it != result4Leases.end(); ++it) {
         if (deleteLease((*it)->addr_)) {
         if (deleteLease((*it)->addr_)) {
             result++;
             result++;
         }
         }
     }
     }
-    for (Lease6Collection::iterator it = result6Leases.begin(); it != result6Leases.end(); ++it) {
+    for (Lease6Collection::iterator it = result6Leases.begin();
+            it != result6Leases.end(); ++it) {
         if (deleteLease((*it)->addr_)) {
         if (deleteLease((*it)->addr_)) {
             result++;
             result++;
         }
         }
@@ -1734,9 +1829,49 @@ pair<uint32_t, uint32_t>
 CqlLeaseMgr::getVersion() const {
 CqlLeaseMgr::getVersion() const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_CQL_GET_VERSION);
               DHCPSRV_CQL_GET_VERSION);
+    uint32_t version;
+    uint32_t minor;
+    CassError rc;
+    CassStatement* statement = NULL;
+    CassFuture* future = NULL;
+
+    statement = cass_prepared_bind(dbconn_.statements_[GET_VERSION]);
+
+    future = cass_session_execute(dbconn_.session_, statement);
+    cass_future_wait(future);
+    std::string error;
+    dbconn_.checkStatementError(error, future, "unable to GET");
+    rc = cass_future_error_code(future);
+    if (rc != CASS_OK) {
+        cass_future_free(future);
+        cass_statement_free(statement);
+        isc_throw(DbOperationError, error);
+    }
 
 
-    uint32_t version = CASS_VERSION_MAJOR;
-    uint32_t minor = CASS_VERSION_MINOR;
+    const CassResult* resultCollection = cass_future_get_result(future);
+    CassIterator* rows = cass_iterator_from_result(resultCollection);
+    CqlDataArray data;
+    CqlDataArray size;
+    int rowCount = 0;
+    while (cass_iterator_next(rows)) {
+        rowCount++;
+        const CassRow* row = cass_iterator_get_row(rows);
+        // version: uint32_t
+        data.add(reinterpret_cast<void*>(&version));
+        size.add(NULL);
+        // minor: uint32_t
+        data.add(reinterpret_cast<void*>(&minor));
+        size.add(NULL);
+
+        for (int i = 0; i < 2; i++) {
+            CqlLeaseMgr::getData(row, i, data, size, *versionExchange_);
+        }
+    }
+
+    cass_iterator_free(rows);
+    cass_result_free(resultCollection);
+    cass_future_free(future);
+    cass_statement_free(statement);
 
 
     return make_pair<uint32_t, uint32_t>(version, minor);
     return make_pair<uint32_t, uint32_t>(version, minor);
 }
 }

+ 62 - 30
src/lib/dhcpsrv/cql_lease_mgr.h

@@ -34,7 +34,7 @@ namespace dhcp {
 /// CQL execute call.
 /// CQL execute call.
 ///
 ///
 /// Note that the data values are stored as pointers. These pointers need to
 /// Note that the data values are stored as pointers. These pointers need to
-/// valid for the duration of the CQL statement execution.  In other
+/// valid for the duration of the CQL statement execution. In other
 /// words populating them with pointers to values that go out of scope before
 /// words populating them with pointers to values that go out of scope before
 /// statement is executed is a bad idea.
 /// statement is executed is a bad idea.
 
 
@@ -45,20 +45,24 @@ struct CqlDataArray {
         values_.push_back(value);
         values_.push_back(value);
     }
     }
     void remove(int index) {
     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);
         values_.erase(values_.begin() + index);
     }
     }
 };
 };
 
 
-class CqlExchange;
+class CqlVersionExchange;
 class CqlLeaseExchange;
 class CqlLeaseExchange;
 class CqlLease4Exchange;
 class CqlLease4Exchange;
 class CqlLease6Exchange;
 class CqlLease6Exchange;
 
 
 /// @brief Cassandra Lease Manager
 /// @brief Cassandra Lease Manager
 ///
 ///
-/// This class provides the \ref isc::dhcp::LeaseMgr interface to the CQL - Cassandra
-/// database.  Use of this backend presupposes that a CQL database is
-/// available and that the Kea schema has been created within it.
+/// This class provides the \ref isc::dhcp::LeaseMgr interface to the Cassandra
+/// database. Use of this backend presupposes that a CQL database is available
+/// and that the Kea schema has been created within it.
 class CqlLeaseMgr : public LeaseMgr {
 class CqlLeaseMgr : public LeaseMgr {
 public:
 public:
 
 
@@ -314,7 +318,7 @@ public:
 
 
     /// @brief Deletes a lease.
     /// @brief Deletes a lease.
     ///
     ///
-    /// @param addr Address of the lease to be deleted.  This can be an IPv4
+    /// @param addr Address of the lease to be deleted. This can be an IPv4
     ///             address or an IPv6 address.
     ///             address or an IPv6 address.
     ///
     ///
     /// @return true if deletion was successful, false if no such lease exists
     /// @return true if deletion was successful, false if no such lease exists
@@ -343,8 +347,6 @@ public:
 
 
     /// @brief Return backend type
     /// @brief Return backend type
     ///
     ///
-    /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
-    ///
     /// @return Type of the backend.
     /// @return Type of the backend.
     virtual std::string getType() const {
     virtual std::string getType() const {
         return (std::string("cql"));
         return (std::string("cql"));
@@ -364,7 +366,7 @@ public:
 
 
     /// @brief Returns backend version.
     /// @brief Returns backend version.
     ///
     ///
-    /// @return Version number as a pair of unsigned integers.  "first" is the
+    /// @return Version number as a pair of unsigned integers. "first" is the
     ///         major version number, "second" the minor number.
     ///         major version number, "second" the minor number.
     ///
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
@@ -373,16 +375,12 @@ public:
 
 
     /// @brief Commit Transactions
     /// @brief Commit Transactions
     ///
     ///
-    /// Commits all pending database operations.
-    ///
-    /// @throw DbOperationError If the commit failed.
+    /// This is a no-op for Cassandra.
     virtual void commit();
     virtual void commit();
 
 
     /// @brief Rollback Transactions
     /// @brief Rollback Transactions
     ///
     ///
-    /// Rolls back all pending database operations.
-    ///
-    /// @throw DbOperationError If the rollback failed.
+    /// This is a no-op for Cassandra.
     virtual void rollback();
     virtual void rollback();
 
 
 
 
@@ -412,30 +410,61 @@ public:
         UPDATE_LEASE6,              // Update a Lease6 entry
         UPDATE_LEASE6,              // Update a Lease6 entry
         NUM_STATEMENTS              // Number of statements
         NUM_STATEMENTS              // Number of statements
     };
     };
-    static void bindData(CassStatement* statement, const StatementIndex stindex, CqlDataArray& data, const SqlExchange& exchange);
 
 
-    static void getDataType(const StatementIndex stindex, int param, const SqlExchange& exchange, ExchangeDataType& type);
+    /// @brief TODO
+    ///
+    /// TODO
+    ///
+    /// @param statement TODO
+    /// @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 TODO
+    ///
+    /// TODO
+    ///
+    /// @param stindex Index of statement being executed
+    /// @param param TODO
+    /// @param exchange Exchange object to use
+    /// @param type TODO
+    static void getDataType(const StatementIndex stindex, int param,
+        const SqlExchange& exchange, ExchangeDataType& type);
 
 
-    static void getData(const CassRow* row, int pindex, CqlDataArray& data, CqlDataArray& size, const SqlExchange& exchange);
+    /// @brief TODO
+    ///
+    /// TODO
+    ///
+    /// @param row TODO
+    /// @param pindex Index of statement being executed
+    /// @param data array that has been created for the type of lease in question.
+    /// @param data size TODO
+    /// @param exchange Exchange object to use
+    static void getData(const CassRow* row, int pindex, CqlDataArray& data,
+        CqlDataArray& size, const SqlExchange& exchange);
 
 
 private:
 private:
 
 
     /// @brief Add Lease Common Code
     /// @brief Add Lease Common Code
     ///
     ///
     /// This method performs the common actions for both flavours (V4 and V6)
     /// This method performs the common actions for both flavours (V4 and V6)
-    /// of the addLease method.  It binds the contents of the lease object to
+    /// of the addLease method. It binds the contents of the lease object to
     /// the prepared statement and adds it to the database.
     /// the prepared statement and adds it to the database.
     ///
     ///
     /// @param stindex Index of statement being executed
     /// @param stindex Index of statement being executed
     /// @param data array that has been created for the type
     /// @param data array that has been created for the type
     ///        of lease in question.
     ///        of lease in question.
+    /// @param exchange Exchange object to use
     ///
     ///
     /// @return true if the lease was added, false if it was not added because
     /// @return true if the lease was added, false if it was not added because
     ///         a lease with that address already exists in the database.
     ///         a lease with that address already exists in the database.
     ///
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
     ///        failed.
-    bool addLeaseCommon(StatementIndex stindex, CqlDataArray& data, CqlLeaseExchange& exchange);
+    bool addLeaseCommon(StatementIndex stindex, CqlDataArray& data,
+        CqlLeaseExchange& exchange);
 
 
     /// @brief Get Lease Collection Common Code
     /// @brief Get Lease Collection Common Code
     ///
     ///
@@ -464,12 +493,12 @@ private:
 
 
     /// @brief Gets Lease4 Collection
     /// @brief Gets Lease4 Collection
     ///
     ///
-    /// Gets a collection of Lease4 objects.  This is just an interface to
+    /// Gets a collection of Lease4 objects. This is just an interface to
     /// the get lease collection common code.
     /// the get lease collection common code.
     ///
     ///
     /// @param stindex Index of statement being executed
     /// @param stindex Index of statement being executed
     /// @param data array containing the where clause input parameters
     /// @param data array containing the where clause input parameters
-    /// @param lease LeaseCollection object returned.  Note that any leases in
+    /// @param result LeaseCollection object returned. Note that any leases in
     ///        the collection when this method is called are not erased: the
     ///        the collection when this method is called are not erased: the
     ///        new data is appended to the end.
     ///        new data is appended to the end.
     ///
     ///
@@ -485,12 +514,12 @@ private:
 
 
     /// @brief Get Lease6 Collection
     /// @brief Get Lease6 Collection
     ///
     ///
-    /// Gets a collection of Lease6 objects.  This is just an interface to
+    /// Gets a collection of Lease6 objects. This is just an interface to
     /// the get lease collection common code.
     /// the get lease collection common code.
     ///
     ///
     /// @param stindex Index of statement being executed
     /// @param stindex Index of statement being executed
     /// @param data array containing input parameters for the query
     /// @param data array containing input parameters for the query
-    /// @param lease LeaseCollection object returned.  Note that any existing
+    /// @param result LeaseCollection object returned. Note that any existing
     ///        data in the collection is erased first.
     ///        data in the collection is erased first.
     ///
     ///
     /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
     /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
@@ -506,7 +535,7 @@ private:
     /// @brief Get Lease4 Common Code
     /// @brief Get Lease4 Common Code
     ///
     ///
     /// This method performs the common actions for the various getLease4()
     /// This method performs the common actions for the various getLease4()
-    /// methods.  It acts as an interface to the getLeaseCollection() method,
+    /// methods. It acts as an interface to the getLeaseCollection() method,
     /// but retrieveing only a single lease.
     /// but retrieveing only a single lease.
     ///
     ///
     /// @param stindex Index of statement being executed
     /// @param stindex Index of statement being executed
@@ -518,7 +547,7 @@ private:
     /// @brief Get Lease6 Common Code
     /// @brief Get Lease6 Common Code
     ///
     ///
     /// This method performs the common actions for the various getLease4()
     /// This method performs the common actions for the various getLease4()
-    /// methods.  It acts as an interface to the getLeaseCollection() method,
+    /// methods. It acts as an interface to the getLeaseCollection() method,
     /// but retrieveing only a single lease.
     /// but retrieveing only a single lease.
     ///
     ///
     /// @param stindex Index of statement being executed
     /// @param stindex Index of statement being executed
@@ -548,7 +577,7 @@ private:
 
 
     /// @brief Update lease common code
     /// @brief Update lease common code
     ///
     ///
-    /// Holds the common code for updating a lease.  It binds the parameters
+    /// Holds the common code for updating a lease. It binds the parameters
     /// to the prepared statement, executes it, then checks how many rows
     /// to the prepared statement, executes it, then checks how many rows
     /// were affected.
     /// were affected.
     ///
     ///
@@ -567,7 +596,7 @@ private:
 
 
     /// @brief Delete lease common code
     /// @brief Delete lease common code
     ///
     ///
-    /// Holds the common code for deleting a lease.  It binds the parameters
+    /// Holds the common code for deleting a lease. It binds the parameters
     /// to the prepared statement, executes the statement and checks to
     /// to the prepared statement, executes the statement and checks to
     /// see how many rows were deleted.
     /// see how many rows were deleted.
     ///
     ///
@@ -579,7 +608,8 @@ private:
     ///
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
     ///        failed.
-    bool deleteLeaseCommon(StatementIndex stindex, CqlDataArray& data, CqlLeaseExchange& exchange);
+    bool deleteLeaseCommon(StatementIndex stindex, CqlDataArray& data,
+        CqlLeaseExchange& exchange);
 
 
     /// @brief Delete expired-reclaimed leases.
     /// @brief Delete expired-reclaimed leases.
     ///
     ///
@@ -593,16 +623,18 @@ private:
     uint64_t deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
     uint64_t deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
                                                 StatementIndex statement_index);
                                                 StatementIndex statement_index);
 
 
+    /// TODO
     static CqlTaggedStatement tagged_statements_[];
     static CqlTaggedStatement tagged_statements_[];
     /// Database connection object
     /// Database connection object
     CqlConnection dbconn_;
     CqlConnection dbconn_;
 
 
     /// The exchange objects are used for transfer of data to/from the database.
     /// 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,
     /// 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
+    /// while the rest of this object does not. (At alternative would be to
     /// declare them as "mutable".)
     /// declare them as "mutable".)
     boost::scoped_ptr<CqlLease4Exchange> exchange4_; ///< Exchange object
     boost::scoped_ptr<CqlLease4Exchange> exchange4_; ///< Exchange object
     boost::scoped_ptr<CqlLease6Exchange> exchange6_; ///< Exchange object
     boost::scoped_ptr<CqlLease6Exchange> exchange6_; ///< Exchange object
+    boost::scoped_ptr<CqlVersionExchange> versionExchange_; ///< Exchange object
 };
 };
 
 
 }; // end of isc::dhcp namespace
 }; // end of isc::dhcp namespace

+ 5 - 0
src/lib/dhcpsrv/lease_mgr.h

@@ -60,6 +60,8 @@
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
+/// @brief Used to map server data types with internal backend storage data
+/// types.
 enum ExchangeDataType {
 enum ExchangeDataType {
     EXCHANGE_DATA_TYPE_NONE,
     EXCHANGE_DATA_TYPE_NONE,
     EXCHANGE_DATA_TYPE_BOOL,
     EXCHANGE_DATA_TYPE_BOOL,
@@ -70,12 +72,15 @@ enum ExchangeDataType {
     EXCHANGE_DATA_TYPE_BYTES
     EXCHANGE_DATA_TYPE_BYTES
 };
 };
 
 
+/// @brief Used to specify the direction of the data exchange between the
+/// database and the server.
 enum ExchangeDataTypeIO {
 enum ExchangeDataTypeIO {
     EXCHANGE_DATA_TYPE_IO_IN,
     EXCHANGE_DATA_TYPE_IO_IN,
     EXCHANGE_DATA_TYPE_IO_OUT,
     EXCHANGE_DATA_TYPE_IO_OUT,
     EXCHANGE_DATA_TYPE_IO_IN_OUT
     EXCHANGE_DATA_TYPE_IO_IN_OUT
 };
 };
 
 
+/// @brief Used to map the column name with internal backend storage data types.
 struct ExchangeColumnInfo {
 struct ExchangeColumnInfo {
     ExchangeColumnInfo () : column_(NULL), type_io_(EXCHANGE_DATA_TYPE_IO_IN), type_(EXCHANGE_DATA_TYPE_NONE) {};
     ExchangeColumnInfo () : column_(NULL), type_io_(EXCHANGE_DATA_TYPE_IO_IN), type_(EXCHANGE_DATA_TYPE_NONE) {};
     ExchangeColumnInfo (const char *column, ExchangeDataTypeIO type_io, ExchangeDataType type) : column_(column),  type_io_(type_io), type_(type) {};
     ExchangeColumnInfo (const char *column, ExchangeDataTypeIO type_io, ExchangeDataType type) : column_(column),  type_io_(type_io), type_(type) {};

+ 15 - 10
src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc

@@ -31,7 +31,6 @@ using namespace std;
 
 
 namespace {
 namespace {
 
 
-
 /// @brief Test fixture class for testing Cassandra Lease Manager
 /// @brief Test fixture class for testing Cassandra Lease Manager
 ///
 ///
 /// Opens the database prior to each test and closes it afterwards.
 /// Opens the database prior to each test and closes it afterwards.
@@ -64,8 +63,8 @@ public:
 
 
     /// @brief Destructor
     /// @brief Destructor
     ///
     ///
-    /// Rolls back all pending transactions.  The deletion of lmptr_ will close
-    /// the database.  Then reopen it and delete everything created by the test.
+    /// Rolls back all pending transactions. The deletion of lmptr_ will close
+    /// the database. Then reopen it and delete everything created by the test.
     virtual ~CqlLeaseMgrTest() {
     virtual ~CqlLeaseMgrTest() {
         lmptr_->rollback();
         lmptr_->rollback();
         LeaseMgrFactory::destroy();
         LeaseMgrFactory::destroy();
@@ -74,7 +73,7 @@ public:
 
 
     /// @brief Reopen the database
     /// @brief Reopen the database
     ///
     ///
-    /// Closes the database and re-open it.  Anything committed should be
+    /// Closes the database and re-open it. Anything committed should be
     /// visible.
     /// visible.
     ///
     ///
     /// Parameter is ignored for CQL backend as the v4 and v6 leases share
     /// Parameter is ignored for CQL backend as the v4 and v6 leases share
@@ -89,9 +88,9 @@ public:
 
 
 /// @brief Check that database can be opened
 /// @brief Check that database can be opened
 ///
 ///
-/// This test checks if the CqlLeaseMgr can be instantiated.  This happens
-/// only if the database can be opened.  Note that this is not part of the
-/// CqlLeaseMgr test fixure set.  This test checks that the database can be
+/// This test checks if the CqlLeaseMgr can be instantiated. This happens
+/// only if the database can be opened. Note that this is not part of the
+/// CqlLeaseMgr test fixure set. This test checks that the database can be
 /// opened: the fixtures assume that and check basic operations.
 /// opened: the fixtures assume that and check basic operations.
 
 
 TEST(CqlOpenTest, OpenDatabase) {
 TEST(CqlOpenTest, OpenDatabase) {
@@ -114,7 +113,7 @@ TEST(CqlOpenTest, OpenDatabase) {
     }
     }
 
 
     // Check that lease manager open the database opens correctly with a longer
     // Check that lease manager open the database opens correctly with a longer
-    // timeout.  If it fails, print the error message.
+    // timeout. If it fails, print the error message.
     try {
     try {
         string connection_string = validCqlConnectionString() + string(" ") +
         string connection_string = validCqlConnectionString() + string(" ") +
                                    string(VALID_TIMEOUT);
                                    string(VALID_TIMEOUT);
@@ -187,7 +186,7 @@ TEST_F(CqlLeaseMgrTest, getType) {
 
 
 /// @brief Check conversion functions
 /// @brief Check conversion functions
 ///
 ///
-/// The server works using cltt and valid_filetime.  In the database, the
+/// The server works using cltt and valid_filetime. In the database, the
 /// information is stored as expire_time and valid-lifetime, which are
 /// information is stored as expire_time and valid-lifetime, which are
 /// related by
 /// related by
 ///
 ///
@@ -196,12 +195,18 @@ TEST_F(CqlLeaseMgrTest, getType) {
 /// This test checks that the conversion is correct.
 /// This test checks that the conversion is correct.
 TEST_F(CqlLeaseMgrTest, checkTimeConversion) {
 TEST_F(CqlLeaseMgrTest, checkTimeConversion) {
     const time_t cltt = time(NULL);
     const time_t cltt = time(NULL);
+    const uint32_t valid_lft = 86400;       // 1 day
+    uint64_t cql_expire;
+
+    // Convert to the database time
+    CqlExchange::convertToDatabaseTime(cltt, valid_lft, cql_expire);
 
 
+    // Convert back
     time_t converted_cltt = 0;
     time_t converted_cltt = 0;
+    CqlExchange::convertFromDatabaseTime(cql_expire, valid_lft, converted_cltt);
     EXPECT_EQ(cltt, converted_cltt);
     EXPECT_EQ(cltt, converted_cltt);
 }
 }
 
 
-
 /// @brief Check getName() returns correct database name
 /// @brief Check getName() returns correct database name
 TEST_F(CqlLeaseMgrTest, getName) {
 TEST_F(CqlLeaseMgrTest, getName) {
     EXPECT_EQ(std::string("keatest"), lmptr_->getName());
     EXPECT_EQ(std::string("keatest"), lmptr_->getName());

+ 6 - 6
src/lib/dhcpsrv/testutils/cql_schema.h

@@ -32,8 +32,8 @@ std::string validCqlConnectionString();
 /// will fail. The output of stderr is suppressed unless the parameter,
 /// will fail. The output of stderr is suppressed unless the parameter,
 /// show_err is true.
 /// show_err is true.
 ///
 ///
-/// @param force_wipe forces wipe of the database, even if KEA_TEST_CASSANDRA_WIPE
-///                   is set.
+/// @param force_wipe forces wipe of the database, even if
+/// KEA_TEST_CASSANDRA_WIPE is set.
 /// @param show_err flag which governs whether or not stderr is suppressed.
 /// @param show_err flag which governs whether or not stderr is suppressed.
 void destroyCqlSchema(bool force_wipe, bool show_err = false);
 void destroyCqlSchema(bool force_wipe, bool show_err = false);
 
 
@@ -47,17 +47,17 @@ void destroyCqlSchema(bool force_wipe, bool show_err = false);
 /// will fail. The output of stderr is suppressed unless the parameter,
 /// will fail. The output of stderr is suppressed unless the parameter,
 /// show_err is true.
 /// show_err is true.
 ///
 ///
-/// @param force_wipe forces wipe of the database, even if KEA_TEST_CASSANDRA_WIPE
-///                   is set.
+/// @param force_wipe forces wipe of the database, even if
+/// KEA_TEST_CASSANDRA_WIPE is set.
 /// @param show_err flag which governs whether or not stderr is suppressed.
 /// @param show_err flag which governs whether or not stderr is suppressed.
 void createCqlSchema(bool force_wipe, bool show_err = false);
 void createCqlSchema(bool force_wipe, bool show_err = false);
 
 
 /// @brief Run a CQL script against the CQL unit test database
 /// @brief Run a CQL script against the CQL unit test database
 ///
 ///
 /// Submits the given SQL script to CQL via cqlsh CLI. The output of
 /// Submits the given SQL script to CQL via cqlsh CLI. The output of
-/// stderr is suppressed unless the parameter, show_err is true.  The is done
+/// stderr is suppressed unless the parameter, show_err is true. The is done
 /// to suppress warnings that might otherwise make test output needlessly
 /// to suppress warnings that might otherwise make test output needlessly
-/// noisy.  A gtest assertion occurs if the script fails to execute.
+/// noisy. A gtest assertion occurs if the script fails to execute.
 ///
 ///
 /// @param path - path (if not blank) of the script to execute
 /// @param path - path (if not blank) of the script to execute
 /// @param script_name - file name of the path to execute
 /// @param script_name - file name of the path to execute

+ 0 - 5
src/share/database/scripts/cql/dhcpdb_create.cql

@@ -103,11 +103,9 @@ CREATE TABLE lease6_types (
     name varchar,                               -- Name of the lease type
     name varchar,                               -- Name of the lease type
     PRIMARY KEY (lease_type)
     PRIMARY KEY (lease_type)
 );
 );
---START TRANSACTION;
 INSERT INTO lease6_types (lease_type, name) VALUES (0, 'IA_NA');   -- Non-temporary v6 addresses
 INSERT INTO lease6_types (lease_type, name) VALUES (0, 'IA_NA');   -- Non-temporary v6 addresses
 INSERT INTO lease6_types (lease_type, name) VALUES (1, 'IA_TA');   -- Temporary v6 addresses
 INSERT INTO lease6_types (lease_type, name) VALUES (1, 'IA_TA');   -- Temporary v6 addresses
 INSERT INTO lease6_types (lease_type, name) VALUES (2, 'IA_PD');   -- Prefix delegations
 INSERT INTO lease6_types (lease_type, name) VALUES (2, 'IA_PD');   -- Prefix delegations
---COMMIT;
 
 
 -- Kea keeps track of the hardware/MAC address source, i.e. how the address
 -- 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
 -- was obtained. Depending on the technique and your network topology, it may
@@ -198,16 +196,9 @@ INSERT INTO lease_state (state, name) VALUES (2, 'expired-reclaimed');
 -- This table is only modified during schema upgrades.  For historical reasons
 -- 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
 -- (related to the names of the columns in the BIND 10 DNS database file), the
 -- first column is called "version" and not "major".
 -- first column is called "version" and not "major".
-
 CREATE TABLE schema_version (
 CREATE TABLE schema_version (
     version int,
     version int,
     minor int,
     minor int,
     PRIMARY KEY (version)
     PRIMARY KEY (version)
 );
 );
---START TRANSACTION;
 INSERT INTO schema_version (version, minor) VALUES (1, 0);
 INSERT INTO schema_version (version, minor) VALUES (1, 0);
---COMMIT;

+ 0 - 5
src/share/database/scripts/mysql/dhcpdb_create.mysql

@@ -95,11 +95,6 @@ COMMIT;
 # This table is only modified during schema upgrades.  For historical reasons
 # 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
 # (related to the names of the columns in the BIND 10 DNS database file), the
 # first column is called "version" and not "major".
 # first column is called "version" and not "major".
-#
-# NOTE: this MUST be kept in step with src/lib/dhcpsrv/tests/schema_copy.h,
-#       which defines the schema for the unit tests.  If you are updating
-#       the version number, the schema has changed: please ensure that
-#       schema_copy.h has been updated as well.
 CREATE TABLE schema_version (
 CREATE TABLE schema_version (
     version INT PRIMARY KEY NOT NULL,       # Major version number
     version INT PRIMARY KEY NOT NULL,       # Major version number
     minor INT                               # Minor version number
     minor INT                               # Minor version number

+ 0 - 1
src/share/database/scripts/pgsql/dhcpdb_create.pgsql

@@ -82,11 +82,6 @@ INSERT INTO lease6_types VALUES (2, 'IA_PD');   -- Prefix delegations
 -- This table is only modified during schema upgrades.  For historical reasons
 -- 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
 -- (related to the names of the columns in the BIND 10 DNS database file), the
 -- first column is called "version" and not "major".
 -- first column is called "version" and not "major".
-
 CREATE TABLE schema_version (
 CREATE TABLE schema_version (
     version INT PRIMARY KEY NOT NULL,       -- Major version number
     version INT PRIMARY KEY NOT NULL,       -- Major version number
     minor INT                               -- Minor version number
     minor INT                               -- Minor version number