Browse Source

[4277] Bare bones implementation of PgSqlHostDataSource

src/lib/dhcpsrv
    pgsql_host_data_source.c
    pgsql_host_data_source.h  - new files, preliminary implementation

src/lib/dhcpsrv/Makefile.am
    Added new files pgsql_host_data_source.cc, pgsql_host_data_source.h

src/lib/dhcpsrv/dhcpsrv_messages.mes
    Added log messages DHCPSRV_PGSQL_HOST_DB_GET_VERSION, DHCPSRV_PGSQL_START_TRANSACTION

src/lib/dhcpsrv/pgsql_connection.cc
src/lib/dhcpsrv/pgsql_connection.h
    Added PgSqlTransaction
    Added PgSqlConnection::startTransaction()

src/lib/dhcpsrv/pgsql_exchange.cc
src/lib/dhcpsrv/pgsql_exchange.h
    PsqlBindArray
    - Added storage of conversion strings used for bound values
    - Added add() variants for uint8_t, IOAddress, uint8_t buffer
    - Added templated variant for miscellaneous types

    PgSqlExchange
    - Removed getColumnValue variants for various integers, replaced
    with templated version for miscellaneous types

src/lib/dhcpsrv/pgsql_lease_mgr.cc
    Added todo comment to remember to account for hwaddr columns added to lease6

src/lib/dhcpsrv/tests/pgsql_exchange_unittest.cc
    TEST(PsqlBindArray, basicOperation) - new test to exercise bind functions
Thomas Markwalder 8 years ago
parent
commit
334289573e

+ 1 - 0
src/lib/dhcpsrv/Makefile.am

@@ -136,6 +136,7 @@ libkea_dhcpsrv_la_SOURCES += ncr_generator.cc ncr_generator.h
 if HAVE_PGSQL
 libkea_dhcpsrv_la_SOURCES += pgsql_connection.cc pgsql_connection.h
 libkea_dhcpsrv_la_SOURCES += pgsql_exchange.cc pgsql_exchange.h
+libkea_dhcpsrv_la_SOURCES += pgsql_host_data_source.cc pgsql_host_data_source.h
 libkea_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h
 endif
 libkea_dhcpsrv_la_SOURCES += pool.cc pool.h

+ 13 - 0
src/lib/dhcpsrv/dhcpsrv_messages.mes

@@ -634,6 +634,10 @@ An error message indicating that communication with the MySQL database server
 has been lost.  When this occurs the server exits immediately with a non-zero
 exit code.  This is most likely due to a network issue.
 
+% DHCPSRV_PGSQL_HOST_DB_GET_VERSION obtaining schema version information for the PostgreSQL hosts database
+A debug message issued when the server is about to obtain schema version
+information from the PostgreSQL hosts database.
+
 % DHCPSRV_PGSQL_GET_ADDR4 obtaining IPv4 lease for address %1
 A debug message issued when the server is attempting to obtain an IPv4
 lease from the PostgreSQL database for the specified address.
@@ -696,6 +700,15 @@ connection including database name and username needed to access it
 The code has issued a rollback call.  All outstanding transaction will
 be rolled back and not committed to the database.
 
+% DHCPSRV_PGSQL_START_TRANSACTION starting a new PostgreSQL transaction
+A debug message issued when a new PostgreSQL transaction is being started.
+This message is typically not issued when inserting data into a
+single table because the server doesn't explicitly start
+transactions in this case. This message is issued when data is
+inserted into multiple tables with multiple INSERT statements
+and there may be a need to rollback the whole transaction if
+any of these INSERT statements fail.
+
 % DHCPSRV_PGSQL_UPDATE_ADDR4 updating IPv4 lease for address %1
 A debug message issued when the server is attempting to update IPv4
 lease from the PostgreSQL database for the specified address.

+ 31 - 0
src/lib/dhcpsrv/pgsql_connection.cc

@@ -35,6 +35,25 @@ const int PGSQL_DEFAULT_CONNECTION_TIMEOUT = 5; // seconds
 
 const char PgSqlConnection::DUPLICATE_KEY[] = ERRCODE_UNIQUE_VIOLATION;
 
+PgSqlTransaction::PgSqlTransaction(PgSqlConnection& conn)
+    : conn_(conn), committed_(false) {
+    conn_.startTransaction();
+}
+
+PgSqlTransaction::~PgSqlTransaction() {
+    // Rollback if the PgSqlTransaction::commit wasn't explicitly
+    // called.
+    if (!committed_) {
+        conn_.rollback();
+    }
+}
+
+void
+PgSqlTransaction::commit() {
+    conn_.commit();
+    committed_ = true;
+}
+
 PgSqlConnection::~PgSqlConnection() {
     if (conn_) {
         // Deallocate the prepared queries.
@@ -193,6 +212,18 @@ PgSqlConnection::checkStatementError(const PgSqlResult& r,
 }
 
 void
+PgSqlConnection::startTransaction() {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_PGSQL_START_TRANSACTION);
+    PgSqlResult r(PQexec(conn_, "START TRANSACTION"));
+    if (PQresultStatus(r) != PGRES_COMMAND_OK) {
+        const char* error_message = PQerrorMessage(conn_);
+        isc_throw(DbOperationError, "unable to start transaction" 
+                  << error_message);
+    }
+}
+
+void
 PgSqlConnection::commit() {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_COMMIT);
     PgSqlResult r(PQexec(conn_, "COMMIT"));

+ 66 - 4
src/lib/dhcpsrv/pgsql_connection.h

@@ -53,6 +53,7 @@ const size_t OID_NONE = 0;   // PostgreSQL infers proper type
 const size_t OID_BOOL = 16;
 const size_t OID_BYTEA = 17;
 const size_t OID_INT8 = 20;  // 8 byte int
+const size_t OID_INT4 = 23;  // 4 byte int
 const size_t OID_INT2 = 21;  // 2 byte int
 const size_t OID_TIMESTAMP = 1114;
 const size_t OID_VARCHAR = 1043;
@@ -176,6 +177,62 @@ private:
     PGconn* pgconn_;      ///< Postgresql connection
 };
 
+/// @brief Forward declaration to @ref PgSqlConnection.
+class PgSqlConnection;
+
+/// @brief RAII object representing PostgreSQL transaction.
+///
+/// An instance of this class should be created in a scope where multiple
+/// INSERT statements should be executed within a single transaction. The
+/// transaction is started when the constructor of this class is invoked.
+/// The transaction is ended when the @ref PgSqlTransaction::commit is
+/// explicitly called or when the instance of this class is destroyed.
+/// The @ref PgSqlTransaction::commit commits changes to the database
+/// and the changes remain in the database when the instance of the
+/// class is destroyed. If the class instance is destroyed before the
+/// @ref PgSqlTransaction::commit is called, the transaction is rolled
+/// back. The rollback on destruction guarantees that partial data is
+/// not stored in the database when there is an error during any
+/// of the operations belonging to a transaction.
+///
+/// The default PostgreSQL backend configuration enables 'autocommit'.
+/// Starting a transaction overrides 'autocommit' setting for this
+/// particular transaction only. It does not affect the global 'autocommit'
+/// setting for the database connection, i.e. all modifications to the
+/// database which don't use transactions will still be auto committed.
+class PgSqlTransaction : public boost::noncopyable {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Starts transaction by making a "START TRANSACTION" query.
+    ///
+    /// @param conn PostgreSQL connection to use for the transaction. This
+    /// connection will be later used to commit or rollback changes.
+    ///
+    /// @throw DbOperationError if "START TRANSACTION" query fails.
+    PgSqlTransaction(PgSqlConnection& conn);
+
+    /// @brief Destructor.
+    ///
+    /// Rolls back the transaction if changes haven't been committed.
+    ~PgSqlTransaction();
+
+    /// @brief Commits transaction.
+    void commit();
+
+private:
+
+    /// @brief Holds reference to the PostgreSQL database connection.
+    PgSqlConnection& conn_;
+
+    /// @brief Boolean flag indicating if the transaction has been committed.
+    ///
+    /// This flag is used in the class destructor to assess if the
+    /// transaction should be rolled back.
+    bool committed_;
+};
+
 /// @brief Common PgSql Connector Pool
 ///
 /// This class provides common operations for PgSql database connection
@@ -218,18 +275,23 @@ public:
     /// @throw DbOpenError Error opening the database
     void openDatabase();
 
+    /// @brief Start a transaction
+    ///
+    /// Starts a transaction.
+    ///
+    /// @throw DbOperationError If the transaction start failed.
+    void startTransaction();
+
     /// @brief Commit Transactions
     ///
-    /// Commits all pending database operations. On databases that don't
-    /// support transactions, this is a no-op.
+    /// Commits all pending database operations.
     ///
     /// @throw DbOperationError If the commit failed.
     void commit();
 
     /// @brief Rollback Transactions
     ///
-    /// Rolls back all pending database operations. On databases that don't
-    /// support transactions, this is a no-op.
+    /// Rolls back all pending database operations.
     ///
     /// @throw DbOperationError If the rollback failed.
     void rollback();

+ 28 - 26
src/lib/dhcpsrv/pgsql_exchange.cc

@@ -38,10 +38,38 @@ void PsqlBindArray::add(const std::vector<uint8_t>& data) {
     formats_.push_back(BINARY_FMT);
 }
 
+void PsqlBindArray::add(const uint8_t* data, const size_t len) {
+    values_.push_back(reinterpret_cast<const char*>(&(data[0])));
+    lengths_.push_back(len);
+    formats_.push_back(BINARY_FMT);
+}
+
 void PsqlBindArray::add(const bool& value)  {
     add(value ? TRUE_STR : FALSE_STR);
 }
 
+void PsqlBindArray::add(const uint8_t& byte) {
+    // We static_cast to an unsigned int, otherwise lexcial_cast may to
+    // treat byte as a character, which yields "" for unprintable values 
+    bindString(boost::lexical_cast<std::string>
+                              (static_cast<unsigned int>(byte)));
+}
+
+void PsqlBindArray::add(const isc::asiolink::IOAddress& addr) {
+    if (addr.isV4()) {
+        bindString(boost::lexical_cast<std::string>
+                   (static_cast<uint32_t>(addr)));
+    } else {
+        bindString(addr.toText());
+    }
+}
+
+// eventually this should replace add(std::string)
+void PsqlBindArray::bindString(const std::string& str) {
+    bound_strs_.push_back(StringPtr(new std::string(str)));
+    PsqlBindArray::add((bound_strs_.back())->c_str());
+}
+
 std::string PsqlBindArray::toText() const {
     std::ostringstream stream;
     for (int i = 0; i < values_.size(); ++i) {
@@ -137,32 +165,6 @@ PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
 
 void
 PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
-                              const size_t col, uint32_t &value) const {
-    const char* data = getRawColumnValue(r, row, col);
-    try {
-        value = boost::lexical_cast<uint32_t>(data);
-    } catch (const std::exception& ex) {
-        isc_throw(DbOperationError, "Invalid uint32_t data: " << data
-                  << " for: " << getColumnLabel(col) << " row:" << row
-                  << " : " << ex.what());
-    }
-}
-
-void
-PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
-                              const size_t col, int32_t &value) const {
-    const char* data = getRawColumnValue(r, row, col);
-    try {
-        value = boost::lexical_cast<int32_t>(data);
-    } catch (const std::exception& ex) {
-        isc_throw(DbOperationError, "Invalid int32_t data: " << data
-                  << " for: " << getColumnLabel(col) << " row:" << row
-                  << " : " << ex.what());
-    }
-}
-
-void
-PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
                               const size_t col, uint8_t &value) const {
     const char* data = getRawColumnValue(r, row, col);
     try {

+ 93 - 17
src/lib/dhcpsrv/pgsql_exchange.h

@@ -7,10 +7,16 @@
 #ifndef PGSQL_EXCHANGE_H
 #define PGSQL_EXCHANGE_H
 
+#include <asiolink/io_address.h>
 #include <dhcpsrv/pgsql_connection.h>
 
+#include <boost/lexical_cast.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
 #include <stdint.h>
 #include <vector>
+#include <iostream>
 
 namespace isc {
 namespace dhcp {
@@ -24,6 +30,11 @@ namespace dhcp {
 /// be valid for the duration of the PostgreSQL statement execution.  In other
 /// words populating them with pointers to values that go out of scope before
 /// statement is executed is a bad idea.
+
+/// @brief smart pointer to strings used by PsqlBindArray to ensure scope
+/// of strings supplying exchange values 
+typedef boost::shared_ptr<std::string> StringPtr;
+
 struct PsqlBindArray {
     /// @brief Vector of pointers to the data values.
     std::vector<const char *> values_;
@@ -74,14 +85,27 @@ struct PsqlBindArray {
     /// @param value std::string containing the value to add.
     void add(const std::string& value);
 
-    /// @brief Adds a binary value to the bind array.
+    /// @brief Adds a vector of binary data to the bind array.
     ///
     /// Adds a BINARY_FMT value to the end of the bind array using the
-    /// given vector as the data source.
+    /// given vector as the data source.  NOTE this does not replicate
+    /// the vector, so it must remain in scope until the bind array
+    /// is destroyed.
     ///
     /// @param data vector of binary bytes.
     void add(const std::vector<uint8_t>& data);
 
+    /// @brief Adds a buffer of binary data to the bind array.
+    ///
+    /// Adds a BINARY_FMT value to the end of the bind array using the
+    /// given vector as the data source. NOTE this does not replicate
+    /// the buffer, so it must remain in scope until the bind array
+    /// is destroyed.
+    ///
+    /// @param data buffer of binary data.
+    /// @param len  number of bytes of data in buffer 
+    void add(const uint8_t* data, const size_t len);
+
     /// @brief Adds a boolean value to the bind array.
     ///
     /// Converts the given boolean value to its corresponding to PostgreSQL
@@ -90,11 +114,62 @@ struct PsqlBindArray {
     /// @param value bool value to add.
     void add(const bool& value);
 
+    /// @brief Adds a uint8_t value to the bind array.
+    ///
+    /// Converts the given uint8_t value to its corresponding numeric string
+    /// literal and adds it as a TEXT_FMT value to the bind array.
+    ///
+    /// @param value bool value to add.
+    void add(const uint8_t& byte);
+
+    /// @brief Adds a the given IOAddress value to the bind array.
+    ///
+    /// Converts the IOAddress, based on its protocol family, to the 
+    /// corresponding string literal and adds it as a TEXT_FMT value to 
+    /// the bind array.
+    ///
+    /// @param value bool value to add.
+    void add(const isc::asiolink::IOAddress& addr);
+
+    /// @brief Adds a the given value to the bind array.
+    ///
+    /// Converts the given value its corresponding string literal
+    /// boost::lexical_cast and adds it as a TEXT_FMT value to the bind array.
+    ///
+    /// @param value bool value to add.
+    template<typename T>
+    void add(const T& numeric) {
+        bindString(boost::lexical_cast<std::string>(numeric));
+    }
+
+    /// @brief Binds a the given string to the bind array.
+    ///
+    /// Prior to added the The given string the vector of exchange values,
+    /// it duplicated as a StringPtr and saved internally.  This garauntees
+    /// the string remains in scope until the PsqlBindArray is destroyed,
+    /// without the caller maintaining the string values. 
+    ///
+    /// @param value bool value to add.
+    void bindString(const std::string& str);
+
+    //std::vector<const std::string> getBoundStrs() {
+    std::vector<StringPtr> getBoundStrs() {
+        return (bound_strs_);
+    }
+
     /// @brief Dumps the contents of the array to a string.
     /// @return std::string containing the dump
     std::string toText() const;
+
+private:
+    /// @brief vector of strings which supplied the values
+    std::vector<StringPtr> bound_strs_;
+
 };
 
+/// @brief Defines a smart pointer to PsqlBindArray
+typedef boost::shared_ptr<PsqlBindArray> PsqlBindArrayPtr;
+
 /// @brief Base class for marshalling data to and from PostgreSQL.
 ///
 /// Provides the common functionality to set up binding information between
@@ -179,7 +254,7 @@ public:
     void getColumnValue(const PgSqlResult& r, const int row, const size_t col,
                         bool &value) const;
 
-    /// @brief Fetches an integer text column as a uint32_t.
+    /// @brief Fetches an integer text column as a uint8_t.
     ///
     /// @param r the result set containing the query results
     /// @param row the row number within the result set
@@ -189,21 +264,12 @@ public:
     /// @throw  DbOperationError if the value cannot be fetched or is
     /// invalid.
     void getColumnValue(const PgSqlResult& r, const int row, const size_t col,
-                        uint32_t &value) const;
+                        uint8_t &value) const;
 
-    /// @brief Fetches an integer text column as a int32_t.
+    /// @brief Fetches a text column as the given value type
     ///
-    /// @param r the result set containing the query results
-    /// @param row the row number within the result set
-    /// @param col the column number within the row
-    /// @param[out] value parameter to receive the converted value
-    ///
-    /// @throw  DbOperationError if the value cannot be fetched or is
-    /// invalid.
-    void getColumnValue(const PgSqlResult& r, const int row, const size_t col,
-                        int32_t &value) const;
-
-    /// @brief Fetches an integer text column as a uint8_t.
+    /// Uses boost::lexicalcast to convert the text column value into
+    /// a value of type T. 
     ///
     /// @param r the result set containing the query results
     /// @param row the row number within the result set
@@ -212,8 +278,18 @@ public:
     ///
     /// @throw  DbOperationError if the value cannot be fetched or is
     /// invalid.
+    template<typename T>
     void getColumnValue(const PgSqlResult& r, const int row, const size_t col,
-                        uint8_t &value) const;
+                        T& value) const {
+        const char* data = getRawColumnValue(r, row, col);
+        try {
+            value = boost::lexical_cast<T>(data);
+        } catch (const std::exception& ex) {
+            isc_throw(DbOperationError, "Invalid data: " << data
+                      << " for: " << getColumnLabel(col) << " row:" << row
+                      << " : " << ex.what());
+        }
+    }
 
     /// @brief Converts a column in a row in a result set to a binary bytes
     ///

File diff suppressed because it is too large
+ 2220 - 0
src/lib/dhcpsrv/pgsql_host_data_source.cc


+ 255 - 0
src/lib/dhcpsrv/pgsql_host_data_source.h

@@ -0,0 +1,255 @@
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef PGSQL_HOST_DATA_SOURCE_H
+#define PGSQL_HOST_DATA_SOURCE_H
+
+#include <dhcpsrv/base_host_data_source.h>
+#include <dhcpsrv/pgsql_connection.h>
+#include <dhcpsrv/pgsql_exchange.h>
+
+namespace isc {
+namespace dhcp {
+
+/// Forward declaration to the implementation of the @ref PgSqlHostDataSource.
+class PgSqlHostDataSourceImpl;
+
+/// @brief PostgreSQL Host Data Source
+///
+/// This class implements the @ref isc::dhcp::BaseHostDataSource interface to
+/// the PostgreSQL database. Use of this backend presupposes that a PostgreSQL
+/// database is available and that the Kea schema has been created within it.
+class PgSqlHostDataSource: public BaseHostDataSource {
+public:
+
+    /// @brief Constructor
+    ///
+    /// Uses the following keywords in the parameters passed to it to
+    /// connect to the database:
+    /// - name - Name of the database to which to connect (mandatory)
+    /// - host - Host to which to connect (optional, defaults to "localhost")
+    /// - user - Username under which to connect (optional)
+    /// - password - Password for "user" on the database (optional)
+    ///
+    /// If the database is successfully opened, the version number in the
+    /// schema_version table will be checked against hard-coded value in
+    /// the implementation file.
+    ///
+    /// Finally, all the SQL commands are pre-compiled.
+    ///
+    /// @param parameters A data structure relating keywords and values
+    ///        concerned with the database.
+    ///
+    /// @throw isc::dhcp::NoDatabaseName Mandatory database name not given
+    /// @throw isc::dhcp::DbOpenError Error opening the database
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    PgSqlHostDataSource(const DatabaseConnection::ParameterMap& parameters);
+
+    /// @brief Virtual destructor.
+    ///
+    /// Releases prepared MySQL statements used by the backend.
+    virtual ~PgSqlHostDataSource();
+
+    /// @brief Return all hosts for the specified HW address or DUID.
+    ///
+    /// This method returns all @c Host objects which represent reservations
+    /// for the specified HW address or DUID. Note, that this method may
+    /// return multiple reservations because a particular client may have
+    /// reservations in multiple subnets and the same client may be identified
+    /// by HW address or DUID. The server is unable to verify that the specific
+    /// DUID and HW address belong to the same client, until the client sends
+    /// a DHCP message.
+    ///
+    /// Specifying both hardware address and DUID is allowed for this method
+    /// and results in returning all objects that are associated with hardware
+    /// address OR duid. For example: if one host is associated with the
+    /// specified hardware address and another host is associated with the
+    /// specified DUID, two hosts will be returned.
+    ///
+    /// @param hwaddr HW address of the client or NULL if no HW address
+    /// available.
+    /// @param duid client id or NULL if not available, e.g. DHCPv4 client case.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()) const;
+
+    /// @brief Return all hosts connected to any subnet for which reservations
+    /// have been made using a specified identifier.
+    ///
+    /// This method returns all @c Host objects which represent reservations
+    /// for a specified identifier. This method may return multiple hosts
+    /// because a particular client may have reservations in multiple subnets.
+    ///
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll(const Host::IdentifierType& identifier_type,
+           const uint8_t* identifier_begin, const size_t identifier_len) const;
+
+    /// @brief Returns a collection of hosts using the specified IPv4 address.
+    ///
+    /// This method may return multiple @c Host objects if they are connected
+    /// to different subnets.
+    ///
+    /// @param address IPv4 address for which the @c Host object is searched.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll4(const asiolink::IOAddress& address) const;
+
+    /// @brief Returns a host connected to the IPv4 subnet.
+    ///
+    /// Implementations of this method should guard against the case when
+    /// mutliple instances of the @c Host are present, e.g. when two
+    /// @c Host objects are found, one for the DUID, another one for the
+    /// HW address. In such case, an implementation of this method
+    /// should throw an MultipleRecords exception.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param hwaddr HW address of the client or NULL if no HW address
+    /// available.
+    /// @param duid client id or NULL if not available.
+    ///
+    /// @return Const @c Host object using a specified HW address or DUID.
+    virtual ConstHostPtr
+    get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
+         const DuidPtr& duid = DuidPtr()) const;
+
+    /// @brief Returns a host connected to the IPv4 subnet.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Const @c Host object for which reservation has been made using
+    /// the specified identifier.
+    virtual ConstHostPtr
+    get4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
+         const uint8_t* identifier_begin, const size_t identifier_len) const;
+
+    /// @brief Returns a host connected to the IPv4 subnet and having
+    /// a reservation for a specified IPv4 address.
+    ///
+    /// One of the use cases for this method is to detect collisions between
+    /// dynamically allocated addresses and reserved addresses. When the new
+    /// address is assigned to a client, the allocation mechanism should check
+    /// if this address is not reserved for some other host and do not allocate
+    /// this address if reservation is present.
+    ///
+    /// Implementations of this method should guard against invalid addresses,
+    /// such as IPv6 address.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv4 address.
+    ///
+    /// @return Const @c Host object using a specified IPv4 address.
+    virtual ConstHostPtr
+    get4(const SubnetID& subnet_id, const asiolink::IOAddress& address) const;
+
+    /// @brief Returns a host connected to the IPv6 subnet.
+    ///
+    /// Implementations of this method should guard against the case when
+    /// mutliple instances of the @c Host are present, e.g. when two
+    /// @c Host objects are found, one for the DUID, another one for the
+    /// HW address. In such case, an implementation of this method
+    /// should throw an MultipleRecords exception.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param hwaddr HW address of the client or NULL if no HW address
+    /// available.
+    /// @param duid DUID or NULL if not available.
+    ///
+    /// @return Const @c Host object using a specified HW address or DUID.
+    virtual ConstHostPtr
+    get6(const SubnetID& subnet_id, const DuidPtr& duid,
+            const HWAddrPtr& hwaddr = HWAddrPtr()) const;
+
+    /// @brief Returns a host connected to the IPv6 subnet.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Const @c Host object for which reservation has been made using
+    /// the specified identifier.
+    virtual ConstHostPtr
+    get6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
+         const uint8_t* identifier_begin, const size_t identifier_len) const;
+
+    /// @brief Returns a host using the specified IPv6 prefix.
+    ///
+    /// @param prefix IPv6 prefix for which the @c Host object is searched.
+    /// @param prefix_len IPv6 prefix length.
+    ///
+    /// @return Const @c Host object using a specified HW address or DUID.
+    virtual ConstHostPtr
+    get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const;
+
+    /// @brief Adds a new host to the collection.
+    ///
+    /// The implementations of this method should guard against duplicate
+    /// reservations for the same host, where possible. For example, when the
+    /// reservation for the same HW address and subnet id is added twice, the
+    /// addHost method should throw an DuplicateEntry exception. Note, that
+    /// usually it is impossible to guard against adding duplicated host, where
+    /// one instance is identified by HW address, another one by DUID.
+    ///
+    /// @param host Pointer to the new @c Host object being added.
+    virtual void add(const HostPtr& host);
+
+    /// @brief Return backend type
+    ///
+    /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
+    ///
+    /// @return Type of the backend.
+    virtual std::string getType() const {
+        return (std::string("mysql"));
+    }
+
+    /// @brief Returns backend name.
+    ///
+    /// Each backend have specific name.
+    ///
+    /// @return "mysql".
+    virtual std::string getName() const;
+
+    /// @brief Returns description of the backend.
+    ///
+    /// This description may be multiline text that describes the backend.
+    ///
+    /// @return Description of the backend.
+    virtual std::string getDescription() const;
+
+    /// @brief Returns backend version.
+    ///
+    /// @return Version number stored in the database, as a pair of unsigned
+    ///         integers. "first" is the major version number, "second" the
+    ///         minor number.
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database
+    ///        has failed.
+    virtual std::pair<uint32_t, uint32_t> getVersion() const;
+
+private:
+
+    /// @brief Pointer to the implementation of the @ref PgSqlHostDataSource.
+    PgSqlHostDataSourceImpl* impl_; 
+};
+
+}
+}
+
+#endif // PGSQL_HOST_DATA_SOURCE_H

+ 2 - 0
src/lib/dhcpsrv/pgsql_lease_mgr.cc

@@ -26,6 +26,8 @@ using namespace std;
 
 namespace {
 
+/// @todo TKM lease6 needs to accomodate hwaddr,hwtype, and hwaddr source columns
+
 /// @brief Catalog of all the SQL statements currently supported.  Note
 /// that the order columns appear in statement body must match the order they
 /// that the occur in the table.  This does not apply to the where clause.

+ 43 - 0
src/lib/dhcpsrv/tests/pgsql_exchange_unittest.cc

@@ -69,5 +69,48 @@ TEST(PgSqlExchangeTest, convertTimeTest) {
     EXPECT_EQ(ref_time, from_time);
 }
 
+TEST(PsqlBindArray, basicOperation) {
+    
+    PsqlBindArray b;
+
+    uint8_t small_int = 25;
+    b.add(small_int);
+
+    int reg_int = 376;
+    b.add(reg_int);
+
+    uint64_t big_int = 86749032;
+    b.add(big_int);
+
+    b.add((bool)(1));
+    b.add((bool)(0));
+
+    b.add(isc::asiolink::IOAddress("192.2.15.34"));
+    b.add(isc::asiolink::IOAddress("3001::1"));
+
+    std::string str("just a string");
+    b.add(str);
+
+    std::vector<uint8_t> bytes;
+    for (int i = 0; i < 10; i++) {
+        bytes.push_back(i+1);
+    }
+
+    b.add(bytes);
+
+    std::string expected = 
+        "0 : \"25\"\n"
+        "1 : \"376\"\n"
+        "2 : \"86749032\"\n"
+        "3 : \"TRUE\"\n"
+        "4 : \"FALSE\"\n"
+        "5 : \"3221360418\"\n"
+        "6 : \"3001::1\"\n"
+        "7 : \"just a string\"\n"
+        "8 : 0x010203040506070809\n";
+
+    EXPECT_EQ(expected, b.toText());
+}
+
 }; // namespace