// Copyright (C) 2022-2025 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config.h>

#include <asiolink/addr_utilities.h>
#include <pgsql_cb_dhcp6.h>
#include <pgsql_cb_impl.h>
#include <pgsql_query_macros_dhcp.h>
#include <cc/data.h>
#include <config_backend/constants.h>
#include <database/database_connection.h>
#include <database/db_exceptions.h>
#include <dhcp/classify.h>
#include <dhcp/dhcp6.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option_data_types.h>
#include <dhcp/option_space.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/config_backend_dhcp6_mgr.h>
#include <dhcpsrv/network.h>
#include <dhcpsrv/pool.h>
#include <dhcpsrv/lease.h>
#include <dhcpsrv/timer_mgr.h>
#include <dhcpsrv/parsers/client_class_def_parser.h>
#include <util/buffer.h>
#include <util/boost_time_utils.h>
#include <util/dhcp_space.h>
#include <util/multi_threading_mgr.h>
#include <util/triplet.h>
#include <pgsql/pgsql_connection.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/pointer_cast.hpp>
#include <boost/scoped_ptr.hpp>

using namespace isc::cb;
using namespace isc::db;
using namespace isc::data;
using namespace isc::asiolink;
using namespace isc::log;
using namespace isc::util;

namespace isc {
namespace dhcp {

/// @brief Implementation of the PostgreSQL Configuration Backend.
class PgSqlConfigBackendDHCPv6Impl : public PgSqlConfigBackendImpl {
public:

    /// @brief Statement tags.
    ///
    /// The contents of the enum are indexes into the list of SQL statements.
    /// It is assumed that the order is such that the indices of statements
    /// reading the database are less than those of statements modifying the
    /// database.
    enum StatementIndex {
        CREATE_AUDIT_REVISION,
        CHECK_CLIENT_CLASS_KNOWN_DEPENDENCY_CHANGE,
        GET_GLOBAL_PARAMETER6,
        GET_ALL_GLOBAL_PARAMETERS6,
        GET_MODIFIED_GLOBAL_PARAMETERS6,
        GET_SUBNET6_ID_NO_TAG,
        GET_SUBNET6_ID_ANY,
        GET_SUBNET6_ID_UNASSIGNED,
        GET_SUBNET6_PREFIX_NO_TAG,
        GET_SUBNET6_PREFIX_ANY,
        GET_SUBNET6_PREFIX_UNASSIGNED,
        GET_ALL_SUBNETS6,
        GET_ALL_SUBNETS6_UNASSIGNED,
        GET_MODIFIED_SUBNETS6,
        GET_MODIFIED_SUBNETS6_UNASSIGNED,
        GET_SHARED_NETWORK_SUBNETS6,
        GET_POOL6_RANGE,
        GET_POOL6_RANGE_ANY,
        GET_PD_POOL,
        GET_PD_POOL_ANY,
        GET_SHARED_NETWORK6_NAME_NO_TAG,
        GET_SHARED_NETWORK6_NAME_ANY,
        GET_SHARED_NETWORK6_NAME_UNASSIGNED,
        GET_ALL_SHARED_NETWORKS6,
        GET_ALL_SHARED_NETWORKS6_UNASSIGNED,
        GET_MODIFIED_SHARED_NETWORKS6,
        GET_MODIFIED_SHARED_NETWORKS6_UNASSIGNED,
        GET_OPTION_DEF6_CODE_SPACE,
        GET_ALL_OPTION_DEFS6,
        GET_MODIFIED_OPTION_DEFS6,
        GET_OPTION6_CODE_SPACE,
        GET_ALL_OPTIONS6,
        GET_MODIFIED_OPTIONS6,
        GET_OPTION6_SUBNET_ID_CODE_SPACE,
        GET_OPTION6_POOL_ID_CODE_SPACE,
        GET_OPTION6_PD_POOL_ID_CODE_SPACE,
        GET_OPTION6_SHARED_NETWORK_CODE_SPACE,
        GET_CLIENT_CLASS6_NAME,
        GET_ALL_CLIENT_CLASSES6,
        GET_ALL_CLIENT_CLASSES6_UNASSIGNED,
        GET_MODIFIED_CLIENT_CLASSES6,
        GET_MODIFIED_CLIENT_CLASSES6_UNASSIGNED,
        GET_AUDIT_ENTRIES6_TIME,
        GET_SERVER6,
        GET_ALL_SERVERS6,
        INSERT_GLOBAL_PARAMETER6,
        INSERT_GLOBAL_PARAMETER6_SERVER,
        INSERT_SUBNET6,
        INSERT_SUBNET6_SERVER,
        INSERT_POOL6,
        INSERT_PD_POOL,
        INSERT_SHARED_NETWORK6,
        INSERT_SHARED_NETWORK6_SERVER,
        INSERT_OPTION_DEF6,
        INSERT_OPTION_DEF6_CLIENT_CLASS,
        INSERT_OPTION_DEF6_SERVER,
        INSERT_OPTION6,
        INSERT_OPTION6_SERVER,
        INSERT_CLIENT_CLASS6,
        INSERT_CLIENT_CLASS6_SERVER,
        INSERT_CLIENT_CLASS6_DEPENDENCY,
        INSERT_SERVER6,
        UPDATE_GLOBAL_PARAMETER6,
        UPDATE_SUBNET6,
        UPDATE_SHARED_NETWORK6,
        UPDATE_OPTION_DEF6,
        UPDATE_OPTION_DEF6_CLIENT_CLASS,
        UPDATE_OPTION6,
        UPDATE_OPTION6_SUBNET_ID,
        UPDATE_OPTION6_POOL_ID,
        UPDATE_OPTION6_PD_POOL_ID,
        UPDATE_OPTION6_SHARED_NETWORK,
        UPDATE_OPTION6_CLIENT_CLASS,
        UPDATE_CLIENT_CLASS6,
        UPDATE_CLIENT_CLASS6_SAME_POSITION,
        UPDATE_SERVER6,
        DELETE_GLOBAL_PARAMETER6,
        DELETE_ALL_GLOBAL_PARAMETERS6,
        DELETE_ALL_GLOBAL_PARAMETERS6_UNASSIGNED,
        DELETE_SUBNET6_ID_WITH_TAG,
        DELETE_SUBNET6_ID_ANY,
        DELETE_SUBNET6_PREFIX_WITH_TAG,
        DELETE_SUBNET6_PREFIX_ANY,
        DELETE_ALL_SUBNETS6,
        DELETE_ALL_SUBNETS6_UNASSIGNED,
        DELETE_ALL_SUBNETS6_SHARED_NETWORK_NAME,
        DELETE_SUBNET6_SERVER,
        DELETE_POOLS6,
        DELETE_PD_POOLS,
        DELETE_SHARED_NETWORK6_NAME_WITH_TAG,
        DELETE_SHARED_NETWORK6_NAME_ANY,
        DELETE_ALL_SHARED_NETWORKS6,
        DELETE_ALL_SHARED_NETWORKS6_UNASSIGNED,
        DELETE_SHARED_NETWORK6_SERVER,
        DELETE_OPTION_DEF6_CODE_NAME,
        DELETE_ALL_OPTION_DEFS6,
        DELETE_ALL_OPTION_DEFS6_UNASSIGNED,
        DELETE_OPTION_DEFS6_CLIENT_CLASS,
        DELETE_OPTION6,
        DELETE_ALL_GLOBAL_OPTIONS6_UNASSIGNED,
        DELETE_OPTION6_SUBNET_ID,
        DELETE_OPTION6_POOL_RANGE,
        DELETE_OPTION6_PD_POOL,
        DELETE_OPTION6_SHARED_NETWORK,
        DELETE_OPTIONS6_SUBNET_ID_PREFIX,
        DELETE_OPTIONS6_SHARED_NETWORK,
        DELETE_OPTIONS6_CLIENT_CLASS,
        DELETE_CLIENT_CLASS6_DEPENDENCY,
        DELETE_CLIENT_CLASS6_SERVER,
        DELETE_ALL_CLIENT_CLASSES6,
        DELETE_ALL_CLIENT_CLASSES6_UNASSIGNED,
        DELETE_CLIENT_CLASS6,
        DELETE_CLIENT_CLASS6_ANY,
        DELETE_SERVER6,
        DELETE_ALL_SERVERS6,
        GET_LAST_INSERT_ID6,
        NUM_STATEMENTS
    };

    /// @brief Constructor.
    ///
    /// @param parameters A data structure relating keywords and values
    /// concerned with the database.
    explicit PgSqlConfigBackendDHCPv6Impl(const DatabaseConnection::ParameterMap&
                                          parameters);

    /// @brief Destructor.
    ~PgSqlConfigBackendDHCPv6Impl();

    /// @brief Fetches the SQL statement for a given statement index.
    ///
    /// @param index index of the desired statement.
    /// @throw BadValue if there is no statement corresponding to
    /// the index.
    virtual PgSqlTaggedStatement& getStatement(size_t index) const;

    /// @brief Sends query to retrieve global parameter.
    ///
    /// @param server_selector Server selector.
    /// @param name Name of the parameter to be retrieved.
    ///
    /// @return Pointer to the retrieved value or null if such parameter
    /// doesn't exist.
    StampedValuePtr getGlobalParameter6(const ServerSelector& server_selector,
                                        const std::string& name) {
        StampedValueCollection parameters;

        auto const& tags = server_selector.getTags();
        for (auto const& tag : tags) {
            PsqlBindArray in_bindings;
            in_bindings.addTempString(tag.get());
            in_bindings.add(name);

            getGlobalParameters(GET_GLOBAL_PARAMETER6, in_bindings, parameters);
        }

        return (parameters.empty() ? StampedValuePtr() : *parameters.begin());
    }

    /// @brief Sends query to insert or update global parameter.
    ///
    /// @param server_selector Server selector.
    /// @param value StampedValue describing the parameter to create/update.
    void createUpdateGlobalParameter6(const db::ServerSelector& server_selector,
                                      const StampedValuePtr& value) {
        if (server_selector.amUnassigned()) {
            isc_throw(NotImplemented, "managing configuration for no particular server"
                      " (unassigned) is unsupported at the moment");
        }

        auto tag = getServerTag(server_selector, "creating or updating global parameter");

        PsqlBindArray in_bindings;
        in_bindings.addTempString(value->getName());
        in_bindings.addTempString(value->getValue());
        in_bindings.add(value->getType()),
        in_bindings.addTimestamp(value->getModificationTime()),
        in_bindings.addTempString(tag);
        in_bindings.addTempString(value->getName());

        PgSqlTransaction transaction(conn_);

        // Create scoped audit revision. As long as this instance exists
        // no new audit revisions are created in any subsequent calls.
        ScopedAuditRevision audit_revision(this,
                                           PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
                                           server_selector, "global parameter set",
                                           false);

        // Try to update the existing row.
        if (updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::UPDATE_GLOBAL_PARAMETER6,
                              in_bindings) == 0) {
            // No such parameter found, so let's insert it. We have to adjust the
            // bindings collection to match the prepared statement for insert.
            in_bindings.popBack();
            in_bindings.popBack();

            insertQuery(PgSqlConfigBackendDHCPv6Impl::INSERT_GLOBAL_PARAMETER6,
                        in_bindings);

            // Successfully inserted global parameter. Now, we have to associate it
            // with the server tag.
            PsqlBindArray attach_bindings;
            uint64_t pid = getLastInsertId("dhcp6_global_parameter", "id");
            attach_bindings.add(pid);   // id of newly inserted global.
            attach_bindings.addTimestamp(value->getModificationTime());
            attachElementToServers(PgSqlConfigBackendDHCPv6Impl::INSERT_GLOBAL_PARAMETER6_SERVER,
                                   server_selector, attach_bindings);
        }

        transaction.commit();
    }

    /// @brief Sends query to the database to retrieve multiple subnets.
    ///
    /// Query should order subnets by subnet_id.
    ///
    /// @param index Index of the query to be used.
    /// @param server_selector Server selector.
    /// @param in_bindings Input bindings specifying selection criteria. The
    /// size of the bindings collection must match the number of placeholders
    /// in the prepared statement. The input bindings collection must be empty
    /// if the query contains no WHERE clause.
    /// @param [out] subnets Reference to the container where fetched subnets
    /// will be inserted.
    void getSubnets6(const StatementIndex& index,
                     const ServerSelector& server_selector,
                     const PsqlBindArray& in_bindings,
                     Subnet6Collection& subnets) {
        uint64_t last_pool_id = 0;
        uint64_t last_pd_pool_id = 0;
        uint64_t last_pool_option_id = 0;
        uint64_t last_pd_pool_option_id = 0;
        uint64_t last_option_id = 0;
        Pool6Ptr last_pool;
        Pool6Ptr last_pd_pool;
        std::string last_tag;

        // Execute actual query.
        selectQuery(index, in_bindings,
                    [this, &subnets, &last_pool,  &last_pd_pool,
                     &last_pool_id, &last_pd_pool_id,
                     &last_pool_option_id, &last_pd_pool_option_id,
                     &last_option_id, &last_tag](PgSqlResult& r, int row) {
            // Create a convenience worker for the row.
            PgSqlResultRowWorker worker(r, row);

            // Get pointer to the last subnet in the collection.
            Subnet6Ptr last_subnet;
            if (!subnets.empty()) {
                last_subnet = *subnets.rbegin();
            }
            // Subnet_id is column 0.
            SubnetID subnet_id = worker.getInt(0) ;

            // Subnet has been returned. Assuming that subnets are ordered by
            // subnet identifier, if the subnet identifier of the current row
            // is different than the subnet identifier of the previously returned
            // row, it means that we have to construct new subnet object.
            if (!last_subnet || (last_subnet->getID() != subnet_id)) {
                // Reset per subnet component tracking and server tag because
                // we're now starting to process a new subnet.
                last_pool_id = 0;
                last_pd_pool_id = 0;
                last_pool_option_id = 0;
                last_pd_pool_option_id = 0;
                last_option_id = 0;
                last_pool.reset();
                last_pd_pool.reset();
                last_tag.clear();

                // Get subnet parameters required by the constructor first.

                // subnet_prefix at 1.
                std::string subnet_prefix = worker.getString(1);
                auto prefix_pair = Subnet6::parsePrefix(subnet_prefix);

                // preferred_lifetime (5)
                // min_preferred_lifetime (75)
                // max_preferred_lifetime (76)
                auto preferred_lifetime = worker.getTriplet(5, 75, 76);

                // renew_timer at 9.
                auto renew_timer = worker.getTriplet(9);

                // rebind_timer at 7.
                auto rebind_timer = worker.getTriplet(7);

                // valid_lifetime at 14.
                // min_valid_lifetime at 77.
                // max_valid_lifetime at 78.
                auto valid_lifetime = worker.getTriplet(14, 77, 78);

                // Create subnet with basic settings.
                last_subnet = Subnet6::create(prefix_pair.first, prefix_pair.second,
                                              renew_timer, rebind_timer,
                                              preferred_lifetime,
                                              valid_lifetime, subnet_id);

                // 0 and 1 are subnet_id and subnet_prefix

                // client_class at 2.
                clientClassesFromColumn(worker, 2, "client_classes",
                                        last_subnet->getMutableClientClasses());

                // interface at 3.
                if (!worker.isColumnNull(3)) {
                    last_subnet->setIface(worker.getString(3));
                }

                // modification_ts at 4.
                last_subnet->setModificationTime(worker.getTimestamp(4));

                // preferred_lifetime is 5.

                if (!worker.isColumnNull(6)) {
                    last_subnet->setRapidCommit(worker.getBool(6));
                }

                // rebind_timer is 7.

                // Relay addresses at 8.
                setRelays(worker, 8, *last_subnet);

                // renew_timer is 9.

                // evaluate_additional_classes at 10.
                clientClassesFromColumn(worker, 10, "evaluate_additional_classes",
                                        last_subnet->getMutableAdditionalClasses());

                // reservations_global at 11.
                if (!worker.isColumnNull(11)) {
                    last_subnet->setReservationsGlobal(worker.getBool(11));
                }

                // shared_network_name at 12.
                if (!worker.isColumnNull(12)) {
                    last_subnet->setSharedNetworkName(worker.getString(12));
                }

                // user_context at 13.
                if (!worker.isColumnNull(13)) {
                    ElementPtr user_context = worker.getJSON(13);
                    if (user_context) {
                        last_subnet->setContext(user_context);
                    }
                }

                // valid_lifetime at 14.

                // 15 to 19 are pool
                // 20 to 25 are pd pool
                // 26 to 40 are pool option
                // 41 to 55 are pd pool option
                // 56 to 70 are option

                // calculate_tee_times at 71.
                if (!worker.isColumnNull(71)) {
                    last_subnet->setCalculateTeeTimes(worker.getBool(71));
                }

                // t1_percent at 72.
                if (!worker.isColumnNull(72)) {
                    last_subnet->setT1Percent(worker.getDouble(72));
                }

                // t2_percent at 73.
                if (!worker.isColumnNull(73)) {
                    last_subnet->setT2Percent(worker.getDouble(73));
                }

                // interface_id at 74.
                setInterfaceId(*last_subnet, worker, 74);

                // 75 and 76 are {min,max}_preferred_lifetime

                // 77 and 78 are {min,max}_valid_lifetime

                // 79 is pool client_classes
                // 80 is pool evaluate_additional_classes
                // 81 is pool user_context
                // 82 is pd pool excluded_prefix
                // 83 is pd pool excluded_prefix_length
                // 84 is pd pool client_class
                // 85 is pd pool evaluate_additional_classes
                // 86 is pd pool user_context

                // ddns_send_updates at 87.
                if (!worker.isColumnNull(87)) {
                    last_subnet->setDdnsSendUpdates(worker.getBool(87));
                }

                // ddns_override_no_update at 88.
                if (!worker.isColumnNull(88)) {
                    last_subnet->setDdnsOverrideNoUpdate(worker.getBool(88));
                }

                // ddns_override_client_update at 89.
                if (!worker.isColumnNull(89)) {
                    last_subnet->setDdnsOverrideClientUpdate(worker.getBool(89));
                }

                // ddns_replace_client_name at 90.
                if (!worker.isColumnNull(90)) {
                    last_subnet->setDdnsReplaceClientNameMode(
                        static_cast<D2ClientConfig::ReplaceClientNameMode>(worker.getSmallInt(90)));
                }

                // ddns_generated_prefix at 91.
                if (!worker.isColumnNull(91)) {
                    last_subnet->setDdnsGeneratedPrefix(worker.getString(91));
                }

                // ddns_qualifying_suffix at 92.
                if (!worker.isColumnNull(92)) {
                    last_subnet->setDdnsQualifyingSuffix(worker.getString(92));
                }

                // reservations_in_subnet at 93.
                if (!worker.isColumnNull(93)) {
                    last_subnet->setReservationsInSubnet(worker.getBool(93));
                }

                // reservations_out_of_pool at 94.
                if (!worker.isColumnNull(94)) {
                    last_subnet->setReservationsOutOfPool(worker.getBool(94));
                }

                // cache_threshold at 95.
                if (!worker.isColumnNull(95)) {
                    last_subnet->setCacheThreshold(worker.getDouble(95));
                }

                // cache_max_age at 96.
                if (!worker.isColumnNull(96)) {
                    last_subnet->setCacheMaxAge(worker.getInt(96));
                }

                // allocator at 97.
                if (!worker.isColumnNull(97)) {
                    last_subnet->setAllocatorType(worker.getString(97));
                }

                // pd_allocator at 98.
                if (!worker.isColumnNull(98)) {
                    last_subnet->setPdAllocatorType(worker.getString(98));
                }

                // ddns_ttl_percent at 99.
                if (!worker.isColumnNull(99)) {
                    last_subnet->setDdnsTtlPercent(worker.getDouble(99));
                }

                // ddns_ttl at 100.
                if (!worker.isColumnNull(100)) {
                    last_subnet->setDdnsTtl(worker.getInt(100));
                }

                // ddns_ttl_min at 101.
                if (!worker.isColumnNull(101)) {
                    last_subnet->setDdnsTtlMin(worker.getInt(101));
                }

                // ddns_ttl_max at 102.
                if (!worker.isColumnNull(102)) {
                    last_subnet->setDdnsTtlMax(worker.getInt(102));
                }

                // server_tag at 103.

                // Subnet ready. Add it to the list.
                auto ret = subnets.insert(last_subnet);

                // subnets is a multi index container with unique indexes
                // but these indexes are unique too in the database,
                // so this is for sanity only.
                if (!ret.second) {
                    isc_throw(Unexpected, "add subnet failed");
                }
            }

            // Check for new server tags at 103.
            if (!worker.isColumnNull(103)) {
                std::string new_tag = worker.getString(103);
                if (last_tag != new_tag) {
                    if (!new_tag.empty() && !last_subnet->hasServerTag(ServerTag(new_tag))) {
                        last_subnet->setServerTag(new_tag);
                    }

                    last_tag = new_tag;
                }
            }

            // Pool is between 15 and 19 with extra between 79 and 81

            // If the row contains information about the pool and it
            // appears to be new pool entry (checked by comparing pool
            // id), let's create the new pool and add it to the
            // subnet.
            // pool id (15)
            // pool start_address (16)
            // pool end_address (17)
            if (!worker.isColumnNull(15) &&
                (worker.getInet6(16) != 0) &&
                (worker.getInet6(17) != 0) &&
                (worker.getBigInt(15) > last_pool_id)) {
                last_pool_id = worker.getBigInt(15);
                last_pool = Pool6::create(Lease::TYPE_NA,
                                          IOAddress(worker.getInet6(16)),
                                          IOAddress(worker.getInet6(17)));

                // pool subnet_id at 18 (ignored)
                // pool modification_ts at 19 (ignored)

                // pool client_classes at 79.
                clientClassesFromColumn(worker, 79, "client_classes",
                                        last_pool->getMutableClientClasses());

                // pool evaluate_additional_classes at 80.
                clientClassesFromColumn(worker, 80, "evaluate_additional_classes",
                                        last_pool->getMutableAdditionalClasses());

                // pool user_context at 81.
                if (!worker.isColumnNull(81)) {
                    ElementPtr user_context = worker.getJSON(81);
                    if (user_context) {
                        last_pool->setContext(user_context);
                    }
                }

                last_subnet->addPool(last_pool);
            }

            // Pd Pool is between 20 and 25 with extra between 82 and 86

            // If the row contains information about the pd pool and
            // it appears to be new pd pool entry (checked by
            // comparing pd pool id), let's create the new pd pool and
            // add it to the subnet.
            // pd pool id (20)
            // pd pool prefix (21)
            // pd pool prefix_length (22)
            // pd pool delegated_prefix_length (23)
            if (!worker.isColumnNull(20) &&
                (!worker.getString(21).empty()) &&
                (worker.getSmallInt(22) != 0) &&
                (worker.getSmallInt(23) != 0) &&
                (worker.getBigInt(20) > last_pd_pool_id)) {

                // 24 is pd pool subnet_id (ignored)
                // 25 is pd pool modification_ts (ignored)

                // excluded_prefix (82) and excluded_prefix_length (83)
                IOAddress excluded_prefix = IOAddress::IPV6_ZERO_ADDRESS();
                if (!worker.isColumnNull(82)) {
                    excluded_prefix = worker.getInet6(82);
                }

                last_pd_pool_id = worker.getBigInt(20);
                last_pd_pool = Pool6::create(worker.getInet6(21),
                                             static_cast<uint8_t>(worker.getSmallInt(22)),
                                             static_cast<uint8_t>(worker.getSmallInt(23)),
                                             excluded_prefix,
                                             static_cast<uint8_t>(worker.getSmallInt(83)));

                // pd pool client_classes (84)
                clientClassesFromColumn(worker, 84, "client_classes",
                                        last_pd_pool->getMutableClientClasses());

                // pd pool evaluate_additional_classes at 85.
                clientClassesFromColumn(worker, 85, "evaluate_additional_classes",
                                        last_pd_pool->getMutableAdditionalClasses());

                // pd pool user_context at 86.
                if (!worker.isColumnNull(86)) {
                    ElementPtr user_context = worker.getJSON(86);
                    if (user_context) {
                        last_pd_pool->setContext(user_context);
                    }
                }

                last_subnet->addPool(last_pd_pool);
            }

            // Parse pool-specific option from 26 to 40.
            if (last_pool && !worker.isColumnNull(26) &&
                (last_pool_option_id < worker.getBigInt(26))) {
                last_pool_option_id = worker.getBigInt(26);

                OptionDescriptorPtr desc = processOptionRow(Option::V6, worker, 26);
                if (desc) {
                    last_pool->getCfgOption()->add(*desc, desc->space_name_);
                }
            }

            // Parse pd pool-specific option from 41 to 55.
            if (last_pd_pool && !worker.isColumnNull(41) &&
                (last_pd_pool_option_id < worker.getBigInt(41))) {
                last_pd_pool_option_id = worker.getBigInt(41);

                OptionDescriptorPtr desc = processOptionRow(Option::V6, worker, 41);
                if (desc) {
                    last_pd_pool->getCfgOption()->add(*desc, desc->space_name_);
                }
            }

            // Parse subnet-specific option from 56 to 70.
            if (!worker.isColumnNull(56) &&
                (last_option_id < worker.getBigInt(56))) {
                last_option_id = worker.getBigInt(56);

                OptionDescriptorPtr desc = processOptionRow(Option::V6, worker, 56);
                if (desc) {
                    last_subnet->getCfgOption()->add(*desc, desc->space_name_);
                }
            }
        });

        // Now that we're done fetching the whole subnet, we have to
        // check if it has matching server tags and toss it if it
        // doesn't. We skip matching the server tags if we're asking
        // for ANY subnet.
        auto& subnet_index = subnets.get<SubnetSubnetIdIndexTag>();
        tossNonMatchingElements(server_selector, subnet_index);
    }

    /// @brief Sends query to retrieve single subnet by id.
    ///
    /// @param server_selector Server selector.
    /// @param subnet_id Subnet identifier.
    ///
    /// @return Pointer to the returned subnet or NULL if such subnet
    /// doesn't exist.
    Subnet6Ptr getSubnet6(const ServerSelector& server_selector,
                          const SubnetID& subnet_id) {
        if (server_selector.hasMultipleTags()) {
            isc_throw(InvalidOperation, "expected one server tag to be specified"
                      " while fetching a subnet. Got: "
                      << getServerTagsAsText(server_selector));
        }

        PsqlBindArray in_bindings;
        in_bindings.add(subnet_id);

        auto index = GET_SUBNET6_ID_NO_TAG;
        if (server_selector.amUnassigned()) {
            index = GET_SUBNET6_ID_UNASSIGNED;
        } else if (server_selector.amAny()) {
            index = GET_SUBNET6_ID_ANY;
        }

        Subnet6Collection subnets;
        getSubnets6(index, server_selector, in_bindings, subnets);

        return (subnets.empty() ? Subnet6Ptr() : *subnets.begin());
    }

    /// @brief Sends query to retrieve single subnet by prefix.
    ///
    /// The prefix should be in the following format: "2001:db8:1::/64".
    ///
    /// @param server_selector Server selector.
    /// @param subnet_id Subnet identifier.
    ///
    /// @return Pointer to the returned subnet or NULL if such subnet
    /// doesn't exist.
    Subnet6Ptr getSubnet6(const ServerSelector& server_selector,
                          const std::string& subnet_prefix) {
        if (server_selector.hasMultipleTags()) {
            isc_throw(InvalidOperation, "expected one server tag to be specified"
                      " while fetching a subnet. Got: "
                      << getServerTagsAsText(server_selector));
        }

        PsqlBindArray in_bindings;
        in_bindings.add(subnet_prefix);

        auto index = GET_SUBNET6_PREFIX_NO_TAG;
        if (server_selector.amUnassigned()) {
            index = GET_SUBNET6_PREFIX_UNASSIGNED;
        } else if (server_selector.amAny()) {
            index = GET_SUBNET6_PREFIX_ANY;
        }

        Subnet6Collection subnets;
        getSubnets6(index, server_selector, in_bindings, subnets);

        return (subnets.empty() ? Subnet6Ptr() : *subnets.begin());
    }

    /// @brief Sends query to retrieve all subnets.
    ///
    /// @param server_selector Server selector.
    /// @param [out] subnets Reference to the subnet collection structure where
    /// subnets should be inserted.
    void getAllSubnets6(const ServerSelector& server_selector,
                        Subnet6Collection& subnets) {
        if (server_selector.amAny()) {
            isc_throw(InvalidOperation, "fetching all subnets for ANY "
                      "server is not supported");
        }

        auto index = (server_selector.amUnassigned() ? GET_ALL_SUBNETS6_UNASSIGNED :
                      GET_ALL_SUBNETS6);
        PsqlBindArray in_bindings;
        getSubnets6(index, server_selector, in_bindings, subnets);
    }

    /// @brief Sends query to retrieve modified subnets.
    ///
    /// @param server_selector Server selector.
    /// @param modification_ts Lower bound modification timestamp.
    /// @param [out] subnets Reference to the subnet collection structure where
    /// subnets should be inserted.
    void getModifiedSubnets6(const ServerSelector& server_selector,
                             const boost::posix_time::ptime& modification_ts,
                             Subnet6Collection& subnets) {
        if (server_selector.amAny()) {
            isc_throw(InvalidOperation, "fetching modified subnets for ANY "
                      "server is not supported");
        }

        PsqlBindArray in_bindings;
        in_bindings.addTimestamp(modification_ts);

        auto index = (server_selector.amUnassigned() ? GET_MODIFIED_SUBNETS6_UNASSIGNED :
                      GET_MODIFIED_SUBNETS6);
        getSubnets6(index, server_selector, in_bindings, subnets);
    }

    /// @brief Sends query to retrieve all subnets belonging to a shared network.
    ///
    /// @param server_selector Server selector.
    /// @param shared_network_name Name of the shared network for which the
    /// subnets should be retrieved.
    /// @param [out] subnets Reference to the subnet collection structure where
    /// subnets should be inserted.
    void getSharedNetworkSubnets6(const ServerSelector& server_selector,
                                  const std::string& shared_network_name,
                                  Subnet6Collection& subnets) {
        PsqlBindArray in_bindings;
        in_bindings.add(shared_network_name);
        getSubnets6(GET_SHARED_NETWORK_SUBNETS6, server_selector, in_bindings, subnets);
    }

    /// @brief Sends query to retrieve multiple pools.
    ///
    /// Query should order pools by id.
    ///
    /// @param index Index of the query to be used.
    /// @param in_bindings Input bindings specifying selection criteria. The
    /// size of the bindings collection must match the number of placeholders
    /// in the prepared statement. The input bindings collection must be empty
    /// if the query contains no WHERE clause.
    /// @param [out] pools Reference to the container where fetched pools
    /// will be inserted.
    /// @param [out] pool_ids Identifiers of the pools returned in @c pools
    /// argument.
    void getPools(const StatementIndex& index,
                  const PsqlBindArray& in_bindings,
                  PoolCollection& pools,
                  std::vector<uint64_t>& pool_ids) {
        uint64_t last_pool_id = 0;
        uint64_t last_pool_option_id = 0;
        Pool6Ptr last_pool;

        selectQuery(index, in_bindings,
                    [this, &last_pool_id, &last_pool_option_id, &last_pool, &pools, &pool_ids]
                    (PgSqlResult& r, int row) {
            // Create a convenience worker for the row.
            PgSqlResultRowWorker worker(r, row);

            // Pool id is column 0.
            auto id = worker.getBigInt(0) ;
            if (id > last_pool_id) {
                // pool start_address (1)
                // pool end_address (2)
                last_pool_id = id;

                last_pool = Pool6::create(Lease::TYPE_NA, worker.getInet6(1), worker.getInet6(2));

                // pool subnet_id (3) (ignored)

                // pool client_classes (4)
                clientClassesFromColumn(worker, 4, "client_classes",
                                        last_pool->getMutableClientClasses());

                // pool evaluate_additional_classes (5)
                clientClassesFromColumn(worker, 5, "evaluate_additional_classes",
                                        last_pool->getMutableAdditionalClasses());

                // pool user_context (6)
                if (!worker.isColumnNull(6)) {
                    ElementPtr user_context = worker.getJSON(6);
                    if (user_context) {
                        last_pool->setContext(user_context);
                    }
                }

                // pool: modification_ts (7) (ignored)

                pools.push_back(last_pool);
                pool_ids.push_back(last_pool_id);
            }

            // Parse pool specific option (8).
            if (last_pool && !worker.isColumnNull(8) &&
                (last_pool_option_id < worker.getBigInt(8))) {
                last_pool_option_id = worker.getBigInt(8);

                OptionDescriptorPtr desc = processOptionRow(Option::V6, worker, 8);
                if (desc) {
                    last_pool->getCfgOption()->add(*desc, desc->space_name_);
                }
            }
        });
    }

    /// @brief Sends query to retrieve multiple pd pools.
    ///
    /// Query should order pd pools by id.
    ///
    /// @param index Index of the query to be used.
    /// @param in_bindings Input bindings specifying selection criteria. The
    /// size of the bindings collection must match the number of placeholders
    /// in the prepared statement. The input bindings collection must be empty
    /// if the query contains no WHERE clause.
    /// @param [out] pd_pools Reference to the container where fetched pools
    /// will be inserted.
    /// @param [out] pd_pool_ids Identifiers of the pd pools returned in
    /// @c pd_pools argument.
    void getPdPools(const StatementIndex& index,
                    const PsqlBindArray& in_bindings,
                    PoolCollection& pd_pools,
                    std::vector<uint64_t>& pd_pool_ids) {
        uint64_t last_pd_pool_id = 0;
        uint64_t last_pd_pool_option_id = 0;
        Pool6Ptr last_pd_pool;

        selectQuery(index, in_bindings,
                    [this, &last_pd_pool_id, &last_pd_pool_option_id,
                     &last_pd_pool, &pd_pools, &pd_pool_ids]
                    (PgSqlResult& r, int row) {
            // Create a convenience worker for the row.
            PgSqlResultRowWorker worker(r, row);

            // Pool id is column 0.
            auto id = worker.getBigInt(0) ;
            if (id > last_pd_pool_id) {
                last_pd_pool_id = id;

                // pd pool prefix (1)
                // pd pool prefix_length (2)
                // pd pool delegated_prefix_length (3)

                // excluded_prefix (5) and excluded_prefix_length (6)
                IOAddress excluded_prefix = IOAddress::IPV6_ZERO_ADDRESS();
                if (!worker.isColumnNull(5)) {
                    excluded_prefix = worker.getInet6(5);
                }

                last_pd_pool = Pool6::create(worker.getInet6(1),
                                             static_cast<uint8_t>(worker.getSmallInt(2)),
                                             static_cast<uint8_t>(worker.getSmallInt(3)),
                                             excluded_prefix,
                                             static_cast<uint8_t>(worker.getSmallInt(6)));

                // pd pool subnet_id (4) (ignored)

                // pool client_class (7)
                clientClassesFromColumn(worker, 7, "client_classes",
                                        last_pd_pool->getMutableClientClasses());

                // pool evaluate_additional_classes (8)
                clientClassesFromColumn(worker, 8, "evaluate_additional_classes",
                                        last_pd_pool->getMutableAdditionalClasses());

                // pd pool user_context (9)
                if (!worker.isColumnNull(9)) {
                    ElementPtr user_context = worker.getJSON(9);
                    if (user_context) {
                        last_pd_pool->setContext(user_context);
                    }
                }

                // pd pool modification_ts (10) (ignored)

                pd_pools.push_back(last_pd_pool);
                pd_pool_ids.push_back(last_pd_pool_id);
            }

            // Parse pd pool specific option between 11 and 25.
            if (last_pd_pool && !worker.isColumnNull(11) &&
                (last_pd_pool_option_id < worker.getBigInt(11))) {
                last_pd_pool_option_id = worker.getBigInt(11);

                OptionDescriptorPtr desc = processOptionRow(Option::V6, worker, 11);
                if (desc) {
                    last_pd_pool->getCfgOption()->add(*desc, desc->space_name_);
                }
            }
        });
    }

    /// @brief Sends query to retrieve single pool by address range.
    ///
    /// @param server_selector Server selector.
    /// @param pool_start_address Lower bound pool address.
    /// @param pool_end_address Upper bound pool address.
    /// @param pool_id Pool identifier for the returned pool.
    /// @return Pointer to the pool or null if no such pool found.
    Pool6Ptr getPool6(const ServerSelector& server_selector,
                      const IOAddress& pool_start_address,
                      const IOAddress& pool_end_address,
                      uint64_t& pool_id) {
        PoolCollection pools;
        std::vector<uint64_t> pool_ids;

        if (server_selector.amAny()) {
            PsqlBindArray in_bindings;
            in_bindings.addInet6(pool_start_address);
            in_bindings.addInet6(pool_end_address);
            getPools(GET_POOL6_RANGE_ANY, in_bindings, pools, pool_ids);
        } else {
            auto const& tags = server_selector.getTags();
            for (auto const& tag : tags) {
                PsqlBindArray in_bindings;
                in_bindings.addTempString(tag.get());
                in_bindings.addInet6(pool_start_address);
                in_bindings.addInet6(pool_end_address);

                getPools(GET_POOL6_RANGE, in_bindings, pools, pool_ids);
                // Break if something is found?
            }
        }

        // Return upon the first pool found.
        if (!pools.empty()) {
            pool_id = pool_ids[0];
            return (boost::dynamic_pointer_cast<Pool6>(*pools.begin()));
        }

        pool_id = 0;

        return (Pool6Ptr());
    }

    /// @brief Sends query to retrieve single pd pool.
    ///
    /// @param server_selector Server selector.
    /// @param pd_pool_prefix Address part of the pd pool prefix.
    /// @param pd_pool_prefix_length Length of the pd pool prefix.
    /// @param pd_pool_id Pool identifier for the returned pool.
    /// @return Pointer to the pool or null if no such pool found.
    Pool6Ptr getPdPool6(const ServerSelector& server_selector,
                        const asiolink::IOAddress& pd_pool_prefix,
                        const uint8_t pd_pool_prefix_length,
                        uint64_t& pd_pool_id) {
        PoolCollection pd_pools;
        std::vector<uint64_t> pd_pool_ids;

        if (server_selector.amAny()) {
            PsqlBindArray in_bindings;
            in_bindings.addTempString(pd_pool_prefix.toText());
            in_bindings.add(pd_pool_prefix_length);
            getPdPools(GET_PD_POOL_ANY, in_bindings, pd_pools, pd_pool_ids);
        } else {
            auto const& tags = server_selector.getTags();
            for (auto const& tag : tags) {
                PsqlBindArray in_bindings;
                in_bindings.addTempString(tag.get());
                in_bindings.addTempString(pd_pool_prefix.toText());
                in_bindings.add(pd_pool_prefix_length);
                getPdPools(GET_PD_POOL, in_bindings, pd_pools, pd_pool_ids);
            }
        }

        if (!pd_pools.empty()) {
            pd_pool_id = pd_pool_ids[0];
            return (boost::dynamic_pointer_cast<Pool6>(*pd_pools.begin()));
        }

        pd_pool_id = 0;

        return (Pool6Ptr());
    }

    /// @brief Sends query to insert or update subnet.
    ///
    /// @param server_selector Server selector.
    /// @param subnet Pointer to the subnet to be inserted or updated.
    void createUpdateSubnet6(const ServerSelector& server_selector,
                             const Subnet6Ptr& subnet) {
        if (server_selector.amAny()) {
            isc_throw(InvalidOperation, "creating or updating a subnet for ANY"
                      " server is not supported");

        } else if (server_selector.amUnassigned()) {
            isc_throw(NotImplemented, "managing configuration for no particular server"
                      " (unassigned) is unsupported at the moment");
        }

        // Create input bindings.
        PsqlBindArray in_bindings;
        in_bindings.add(subnet->getID());
        in_bindings.addTempString(subnet->toText());
        addClientClassesBinding(in_bindings, subnet->getClientClasses());
        in_bindings.addOptional(subnet->getIface(Network::Inheritance::NONE));
        in_bindings.addTimestamp(subnet->getModificationTime());
        in_bindings.add(subnet->getPreferred(Network::Inheritance::NONE));
        in_bindings.addMin(subnet->getPreferred(Network::Inheritance::NONE));
        in_bindings.addMax(subnet->getPreferred(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getRapidCommit(Network::Inheritance::NONE));
        in_bindings.add(subnet->getT2(Network::Inheritance::NONE));
        addRelayBinding(in_bindings, subnet);
        in_bindings.add(subnet->getT1(Network::Inheritance::NONE));
        addAdditionalClassesBinding(in_bindings, subnet);
        in_bindings.addOptional(subnet->getReservationsGlobal(Network::Inheritance::NONE));

        // Add shared network.
        SharedNetwork6Ptr shared_network;
        subnet->getSharedNetwork(shared_network);

        // Check if the subnet is associated with a shared network instance.
        // If it is, create the binding using the name of the shared network.
        if (shared_network) {
            in_bindings.addTempString(shared_network->getName());

        // If the subnet is associated with a shared network by name (no
        // shared network instance), use this name to create the binding.
        // This may be the case if the subnet is added as a result of
        // receiving a control command that merely specifies shared
        // network name. In that case, it is expected that the shared
        // network data is already stored in the database.
        } else if (!subnet->getSharedNetworkName().empty()) {
            in_bindings.addTempString(subnet->getSharedNetworkName());

        // If the subnet is not associated with a shared network, create
        // null binding.
        } else {
             in_bindings.addNull();
        }

        in_bindings.add(subnet->getContext());
        in_bindings.add(subnet->getValid(Network::Inheritance::NONE));
        in_bindings.addMin(subnet->getValid(Network::Inheritance::NONE));
        in_bindings.addMax(subnet->getValid(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getCalculateTeeTimes(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getT1Percent(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getT2Percent(Network::Inheritance::NONE));
        addInterfaceIdBinding(in_bindings, *subnet);
        in_bindings.addOptional(subnet->getDdnsSendUpdates(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getDdnsOverrideNoUpdate(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getDdnsOverrideClientUpdate(Network::Inheritance::NONE));
        addDdnsReplaceClientNameBinding(in_bindings, subnet);
        in_bindings.addOptional(subnet->getDdnsGeneratedPrefix(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getDdnsQualifyingSuffix(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getReservationsInSubnet(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getReservationsOutOfPool(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getCacheThreshold(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getCacheMaxAge(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getAllocatorType(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getPdAllocatorType(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getDdnsTtlPercent(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getDdnsTtl(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getDdnsTtlMin(Network::Inheritance::NONE));
        in_bindings.addOptional(subnet->getDdnsTtlMax(Network::Inheritance::NONE));

        // Start transaction.
        PgSqlTransaction transaction(conn_);

        // Create scoped audit revision. As long as this instance exists
        // no new audit revisions are created in any subsequent calls.
        ScopedAuditRevision audit_revision(this,
                                           PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
                                           server_selector, "subnet set", true);

        // Create a savepoint in case we are called as part of larger
        // transaction.
        conn_.createSavepoint("createUpdateSubnet6");

        try {

            insertQuery(PgSqlConfigBackendDHCPv6Impl::INSERT_SUBNET6, in_bindings);

        } catch (const DuplicateEntry&) {
            // It already exists, rollback to the savepoint to preserve
            // any prior work.
            conn_.rollbackToSavepoint("createUpdateSubnet6");

            // We're updating, so we need to remove any existing pools and options.
            deletePools6(subnet);
            deletePdPools6(subnet);
            deleteOptions6(ServerSelector::ANY(), subnet);

            // Now we need to add two more bindings for WHERE clause.
            in_bindings.add(subnet->getID());
            in_bindings.addTempString(subnet->toText());

            // Attempt the update.
            auto cnt = updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::UPDATE_SUBNET6,
                                         in_bindings);
            if (!cnt) {
                // Possible only if someone deleted it since we tried to insert it
                // or the query is broken.
                isc_throw(Unexpected, "Update subnet failed to find subnet id: "
                          << subnet->getID() << ", prefix: " << subnet->toText());
            }

            // Remove existing server association.
            PsqlBindArray server_bindings;
            server_bindings.add(subnet->getID());
            updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_SERVER,
                              server_bindings);
        }

        // Subnet was successfully created/updated.

        // Insert associations with the servers.
        PsqlBindArray attach_bindings;
        attach_bindings.add(subnet->getID());
        attach_bindings.addTimestamp(subnet->getModificationTime());
        attachElementToServers(PgSqlConfigBackendDHCPv6Impl::INSERT_SUBNET6_SERVER,
                               server_selector, attach_bindings);

        // (Re)create pools.
        for (auto const& pool : subnet->getPools(Lease::TYPE_NA)) {
            createPool6(server_selector, boost::dynamic_pointer_cast<Pool6>(pool),
                        subnet);
        }

        // (Re)create pd pools.
        for (auto const& pd_pool : subnet->getPools(Lease::TYPE_PD)) {
            createPdPool6(server_selector, boost::dynamic_pointer_cast<Pool6>(pd_pool),
                          subnet);
        }

        // (Re)create options.
        auto option_spaces = subnet->getCfgOption()->getOptionSpaceNames();
        for (auto const& option_space : option_spaces) {
            OptionContainerPtr options = subnet->getCfgOption()->getAll(option_space);
            for (auto const& desc : *options) {
                OptionDescriptorPtr desc_copy = OptionDescriptor::create(desc);
                desc_copy->space_name_ = option_space;
                createUpdateOption6(server_selector, subnet->getID(), desc_copy,
                                    true);
            }
        }

        // Commit the work.
        transaction.commit();
    }

    /// @brief Inserts new IPv6 pool to the database.
    ///
    /// @param server_selector Server selector.
    /// @param pool Pointer to the pool to be inserted.
    /// @param subnet Pointer to the subnet that this pool belongs to.
    void createPool6(const ServerSelector& server_selector,
                     const Pool6Ptr& pool,
                     const Subnet6Ptr& subnet) {
        // Create the input bindings.
        PsqlBindArray in_bindings;
        in_bindings.addInet6(pool->getFirstAddress());
        in_bindings.addInet6(pool->getLastAddress());
        in_bindings.add(subnet->getID());
        addClientClassesBinding(in_bindings, pool->getClientClasses());
        addAdditionalClassesBinding(in_bindings, pool);
        in_bindings.add(pool->getContext());
        in_bindings.addTimestamp(subnet->getModificationTime());

        // Attempt to INSERT the pool.
        insertQuery(PgSqlConfigBackendDHCPv6Impl::INSERT_POOL6, in_bindings);

        // Get the id of the newly inserted pool.
        uint64_t pool_id = getLastInsertId("dhcp6_pool", "id");

        // Add the pool's options.
        auto option_spaces = pool->getCfgOption()->getOptionSpaceNames();
        for (auto const& option_space : option_spaces) {
            OptionContainerPtr options = pool->getCfgOption()->getAll(option_space);
            for (auto const& desc : *options) {
                OptionDescriptorPtr desc_copy = OptionDescriptor::create(desc);
                desc_copy->space_name_ = option_space;
                createUpdateOption6(server_selector, Lease::TYPE_NA,
                                    pool_id, desc_copy, true);
            }
        }
    }

    /// @brief Inserts new IPv6 pd pool to the database.
    ///
    /// @param server_selector Server selector.
    /// @param pd_pool Pointer to the pd pool to be inserted.
    /// @param subnet Pointer to the subnet that this pd pool belongs to.
    void createPdPool6(const ServerSelector& server_selector,
                       const Pool6Ptr& pd_pool,
                       const Subnet6Ptr& subnet) {
        int plen = prefixLengthFromRange(pd_pool->getFirstAddress(),
                                         pd_pool->getLastAddress());

        // Extract excluded prefix components.
        Optional<std::string> xprefix_txt;
        uint8_t xlen = 0;
        const Option6PDExcludePtr& xopt = pd_pool->getPrefixExcludeOption();
        if (xopt) {
            const IOAddress& prefix = pd_pool->getFirstAddress();
            const IOAddress& xprefix = xopt->getExcludedPrefix(prefix, pd_pool->getLength());
            xprefix_txt = xprefix.toText();
            xlen = xopt->getExcludedPrefixLength();
        }

        // Create the input bindings.
        PsqlBindArray in_bindings;
        in_bindings.addInet6(pd_pool->getFirstAddress());
        in_bindings.add(plen);
        in_bindings.add(pd_pool->getLength());
        in_bindings.add(subnet->getID());
        in_bindings.addOptional(xprefix_txt);
        in_bindings.add(xlen);
        addClientClassesBinding(in_bindings, pd_pool->getClientClasses());
        addAdditionalClassesBinding(in_bindings, pd_pool);
        in_bindings.add(pd_pool->getContext());
        in_bindings.addTimestamp(subnet->getModificationTime());

        // Attempt to INSERT the pool.
        insertQuery(PgSqlConfigBackendDHCPv6Impl::INSERT_PD_POOL, in_bindings);

        // Get the id of the newly inserted pool.
        uint64_t pd_pool_id = getLastInsertId("dhcp6_pd_pool", "id");

        // Add the pool's options.
        auto option_spaces = pd_pool->getCfgOption()->getOptionSpaceNames();
        for (auto const& option_space : option_spaces) {
            OptionContainerPtr options = pd_pool->getCfgOption()->getAll(option_space);
            for (auto const& desc : *options) {
                OptionDescriptorPtr desc_copy = OptionDescriptor::create(desc);
                desc_copy->space_name_ = option_space;
                createUpdateOption6(server_selector, Lease::TYPE_PD,
                                    pd_pool_id, desc_copy, true);
            }
        }
    }

    /// @brief Sends a query to delete data from a table.
    ///
    /// If creates a new audit revision for this change if such audit
    /// revision doesn't exist yet (using ScopedAuditRevision mechanism).
    ///
    /// @tparam Args type of the arguments to be passed to one of the existing
    /// @c deleteFromTable methods.
    /// @param server_selector server selector.
    /// @param operation operation which results in calling this function. This is
    /// used for logging purposes.
    /// @param log_message log message to be associated with the audit revision.
    /// @param cascade_delete boolean flag indicating if we're performing
    /// cascade delete. If set to true, the audit entries for the child
    /// objects (e.g. DHCPoptions) won't be created.
    /// @param keys arguments to be passed to one of the existing
    /// @c deleteFromTable methods.
    ///
    /// @return Number of deleted entries.
    template<typename... Args>
    uint64_t deleteTransactional(const int index,
                                 const db::ServerSelector& server_selector,
                                 const std::string& operation,
                                 const std::string& log_message,
                                 const bool cascade_delete,
                                 Args&&... keys) {
        PgSqlTransaction transaction(conn_);

        // Create scoped audit revision. As long as this instance exists
        // no new audit revisions are created in any subsequent calls.
        ScopedAuditRevision
            audit_revision(this,
                           PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
                           server_selector, log_message, cascade_delete);

        auto count = deleteFromTable(index, server_selector, operation, keys...);

        transaction.commit();

        return (count);
    }

    /// @brief Sends query to delete subnet by id.
    ///
    /// @param server_selector Server selector.
    /// @param subnet_id Identifier of the subnet to be deleted.
    /// @return Number of deleted subnets.
    uint64_t deleteSubnet6(const ServerSelector& server_selector,
                           const SubnetID& subnet_id) {
        int index = (server_selector.amAny() ?
                     PgSqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_ID_ANY :
                     PgSqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_ID_WITH_TAG);
        return (deleteTransactional(index, server_selector,
                                    "deleting a subnet", "subnet deleted",
                                    true, static_cast<uint32_t>(subnet_id)));
    }

    /// @brief Sends query to delete subnet by id.
    ///
    /// @param server_selector Server selector.
    /// @param subnet_prefix Prefix of the subnet to be deleted.
    /// @return Number of deleted subnets.
    uint64_t deleteSubnet6(const ServerSelector& server_selector,
                           const std::string& subnet_prefix) {
        int index = (server_selector.amAny() ?
                     PgSqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX_ANY :
                     PgSqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX_WITH_TAG);
        return (deleteTransactional(index, server_selector,
                                    "deleting a subnet", "subnet deleted",
                                    true, subnet_prefix));
    }

    /// @brief Deletes pools belonging to a subnet from the database.
    ///
    /// The query deletes all pools associated with the subnet's
    /// identifier or prefix.
    /// @param subnet Pointer to the subnet for which pools should be
    /// deleted.
    uint64_t deletePools6(const Subnet6Ptr& subnet) {
        PsqlBindArray in_bindings;
        in_bindings.add(subnet->getID());
        in_bindings.addTempString(subnet->toText());

        // Run DELETE.
        return (updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::DELETE_POOLS6,
                                  in_bindings));
    }

    /// @brief Deletes prefix delegation pools belonging to a subnet from
    /// the database.
    ///
    /// The query deletes all pd pools associated with the subnet's
    /// identifier or prefix.
    /// @param subnet Pointer to the subnet for which pd pools should be
    /// deleted.
    uint64_t deletePdPools6(const Subnet6Ptr& subnet) {
        PsqlBindArray in_bindings;
        in_bindings.add(subnet->getID());
        in_bindings.addTempString(subnet->toText());

        // Run DELETE.
        return (updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::DELETE_PD_POOLS,
                                  in_bindings));
    }

    /// @brief Sends query to the database to retrieve multiple shared
    /// networks.
    ///
    /// Query should order shared networks by id.
    ///
    /// @param index Index of the query to be used.
    /// @param server_selector Server selector.
    /// @param in_bindings Input bindings specifying selection criteria. The
    /// size of the bindings collection must match the number of placeholders
    /// in the prepared statement. The input bindings collection must be empty
    /// if the query contains no WHERE clause.
    /// @param [out] shared_networks Reference to the container where fetched
    /// shared networks will be inserted.
    void getSharedNetworks6(const StatementIndex& index,
                            const ServerSelector& server_selector,
                            const PsqlBindArray& in_bindings,
                            SharedNetwork6Collection& shared_networks) {
        uint64_t last_network_id = 0;
        uint64_t last_option_id = 0;
        std::string last_tag;

        selectQuery(index, in_bindings,
                    [this, &shared_networks, &last_network_id, &last_option_id, &last_tag]
                    (PgSqlResult& r, int row) {
            // Create a convenience worker for the row.
            PgSqlResultRowWorker worker(r, row);

            SharedNetwork6Ptr last_network;
            if (!shared_networks.empty()) {
                last_network = *shared_networks.rbegin();
            }

            // Network id is column 0.
            auto network_id = worker.getBigInt(0) ;

            // If this is the first shared network or the shared network id in this
            // row points to the next shared network we use the data in the
            // row to create the new shared network instance.
            if (last_network_id != network_id) {
                last_network_id = network_id;

                // Reset per shared network component tracking and server tag because
                // we're now starting to process a new shared network.
                last_option_id = 0;
                last_tag.clear();

                // name at 1.
                last_network = SharedNetwork6::create(worker.getString(1));
                last_network->setId(last_network_id);

                // client_classes at 2.
                clientClassesFromColumn(worker, 2, "client_classes",
                                        last_network->getMutableClientClasses());

                // interface at 3.
                if (!worker.isColumnNull(3)) {
                    last_network->setIface(worker.getString(3));
                }

                // modification_ts at 4.
                last_network->setModificationTime(worker.getTimestamp(4));

                // preferred_lifetime (5)
                // min_preferred_lifetime (33)
                // max_preferred_lifetime (34)
                last_network->setPreferred(worker.getTriplet(5, 33, 34));

                // rapid_commit at 6.
                if (!worker.isColumnNull(6)) {
                    last_network->setRapidCommit(worker.getBool(6));
                }

                // rebind_timer at 7.
                if (!worker.isColumnNull(7)) {
                    last_network->setT2(worker.getTriplet(7));
                }

                // Relay addresses at 8.
                setRelays(worker, 8, *last_network);

                // renew_timer at 9.
                if (!worker.isColumnNull(9)) {
                    last_network->setT1(worker.getTriplet(9));
                }

                // evaluate_additional_classes at 10.
                clientClassesFromColumn(worker, 10, "evaluate_additional_classes",
                                        last_network->getMutableAdditionalClasses());

                // reservations_global at 11.
                if (!worker.isColumnNull(11)) {
                    last_network->setReservationsGlobal(worker.getBool(11));
                }

                // user_context at 12.
                if (!worker.isColumnNull(12)) {
                    ElementPtr user_context = worker.getJSON(12);
                    if (user_context) {
                        last_network->setContext(user_context);
                    }
                }

                // valid_lifetime at 13.
                // min_valid_lifetime at 35.
                // max_valid_lifetime at 36.
                if (!worker.isColumnNull(13)) {
                    last_network->setValid(worker.getTriplet(13, 35, 36));
                }

                // option from 14 to 28.

                // calculate_tee_times at 29.
                if (!worker.isColumnNull(29)) {
                    last_network->setCalculateTeeTimes(worker.getBool(29));
                }

                // t1_percent at 30.
                if (!worker.isColumnNull(30)) {
                    last_network->setT1Percent(worker.getDouble(30));
                }

                // t2_percent at 31.
                if (!worker.isColumnNull(31)) {
                    last_network->setT2Percent(worker.getDouble(31));
                }

                // interface_id at 32.
                setInterfaceId(*last_network, worker, 32);

                // min_preferred_lifetime at 33.
                // max_preferred_lifetime at 34.
                // min_valid_lifetime at 35.
                // max_valid_lifetime at 36.

                // ddns_send_updates at 37.
                if (!worker.isColumnNull(37)) {
                    last_network->setDdnsSendUpdates(worker.getBool(37));
                }

                // ddns_override_no_update at 38.
                if (!worker.isColumnNull(38)) {
                    last_network->setDdnsOverrideNoUpdate(worker.getBool(38));
                }

                // ddns_override_client_update at 39.
                if (!worker.isColumnNull(39)) {
                    last_network->setDdnsOverrideClientUpdate(worker.getBool(39));
                }

                // ddns_replace_client_name at 40.
                if (!worker.isColumnNull(40)) {
                    last_network->setDdnsReplaceClientNameMode(
                        static_cast<D2ClientConfig::ReplaceClientNameMode>(worker.getSmallInt(40)));
                }

                // ddns_generated_prefix at 41.
                if (!worker.isColumnNull(41)) {
                    last_network->setDdnsGeneratedPrefix(worker.getString(41));
                }

                // ddns_qualifying_suffix at 42.
                if (!worker.isColumnNull(42)) {
                    last_network->setDdnsQualifyingSuffix(worker.getString(42));
                }

                // reservations_in_subnet at 43.
                if (!worker.isColumnNull(43)) {
                    last_network->setReservationsInSubnet(worker.getBool(43));
                }

                // reservations_in_subnet at 44.
                if (!worker.isColumnNull(44)) {
                    last_network->setReservationsOutOfPool(worker.getBool(44));
                }

                // cache_threshold at 45.
                if (!worker.isColumnNull(45)) {
                    last_network->setCacheThreshold(worker.getDouble(45));
                }

                // cache_max_age at 46.
                if (!worker.isColumnNull(46)) {
                    last_network->setCacheMaxAge(worker.getInt(46));
                }

                // allocator at 47.
                if (!worker.isColumnNull(47)) {
                    last_network->setAllocatorType(worker.getString(47));
                }

                // pd_allocator at 48.
                if (!worker.isColumnNull(48)) {
                    last_network->setPdAllocatorType(worker.getString(48));
                }

                // ddns_ttl_percent at 49.
                if (!worker.isColumnNull(49)) {
                    last_network->setDdnsTtlPercent(worker.getDouble(49));
                }

                // ddns_ttl at 50.
                if (!worker.isColumnNull(50)) {
                    last_network->setDdnsTtl(worker.getInt(50));
                }

                // ddns_ttl_min at 51.
                if (!worker.isColumnNull(51)) {
                    last_network->setDdnsTtlMin(worker.getInt(51));
                }

                // ddns_ttl_max at 52.
                if (!worker.isColumnNull(52)) {
                    last_network->setDdnsTtlMax(worker.getInt(52));
                }

                // server_tag at 53.

                // Add the shared network.
                auto ret = shared_networks.push_back(last_network);

                // shared_networks is a multi index container with an unique
                // index but this index is unique too in the database,
                // so this is for sanity only.
                if (!ret.second) {
                    isc_throw(Unexpected, "add shared network failed");
                }
            }

            // Check for new server tags.
            if (!worker.isColumnNull(53)) {
                std::string new_tag = worker.getString(53);
                if (last_tag != new_tag) {
                    if (!new_tag.empty() && !last_network->hasServerTag(ServerTag(new_tag))) {
                        last_network->setServerTag(new_tag);
                    }

                    last_tag = new_tag;
                }
            }

            // Parse network-specific option from 14 to 28.
            if (!worker.isColumnNull(14) &&
                (last_option_id < worker.getBigInt(14))) {
                last_option_id = worker.getBigInt(14);

                OptionDescriptorPtr desc = processOptionRow(Option::V6, worker, 14);
                if (desc) {
                    last_network->getCfgOption()->add(*desc, desc->space_name_);
                }
            }
        });

        // Now that we're done fetching the whole network, we have to
        // check if it has matching server tags and toss it if it
        // doesn't. We skip matching the server tags if we're asking
        // for ANY shared network.
        auto& sn_index = shared_networks.get<SharedNetworkRandomAccessIndexTag>();
        tossNonMatchingElements(server_selector, sn_index);
    }

    /// @brief Sends query to retrieve single shared network by name.
    ///
    /// @param server_selector Server selector.
    /// @param name Shared network name.
    ///
    /// @return Pointer to the returned shared network or NULL if such shared
    /// network doesn't exist.
    SharedNetwork6Ptr getSharedNetwork6(const ServerSelector& server_selector,
                                        const std::string& name) {
        if (server_selector.hasMultipleTags()) {
            isc_throw(InvalidOperation, "expected one server tag to be specified"
                      " while fetching a shared network. Got: "
                      << getServerTagsAsText(server_selector));
        }

        PsqlBindArray in_bindings;
        in_bindings.add(name);

        auto index = PgSqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK6_NAME_NO_TAG;
        if (server_selector.amUnassigned()) {
            index = PgSqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK6_NAME_UNASSIGNED;
        } else if (server_selector.amAny()) {
            index = PgSqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK6_NAME_ANY;
        }

        SharedNetwork6Collection shared_networks;
        getSharedNetworks6(index, server_selector, in_bindings, shared_networks);

        return (shared_networks.empty() ? SharedNetwork6Ptr() : *shared_networks.begin());
    }

    /// @brief Sends query to retrieve all shared networks.
    ///
    /// @param server_selector Server selector.
    /// @param [out] shared_networks Reference to the shared networks collection
    /// structure where shared networks should be inserted.
    void getAllSharedNetworks6(const ServerSelector& server_selector,
                               SharedNetwork6Collection& shared_networks) {
        if (server_selector.amAny()) {
            isc_throw(InvalidOperation, "fetching all shared networks for ANY "
                      "server is not supported");
        }

        auto index = (server_selector.amUnassigned() ?
                      PgSqlConfigBackendDHCPv6Impl::GET_ALL_SHARED_NETWORKS6_UNASSIGNED :
                      PgSqlConfigBackendDHCPv6Impl::GET_ALL_SHARED_NETWORKS6);

        PsqlBindArray in_bindings;
        getSharedNetworks6(index, server_selector, in_bindings, shared_networks);
    }

    /// @brief Sends query to retrieve modified shared networks.
    ///
    /// @param server_selector Server selector.
    /// @param modification_ts Lower bound modification timestamp.
    /// @param [out] shared_networks Reference to the shared networks collection
    /// structure where shared networks should be inserted.
    void getModifiedSharedNetworks6(const ServerSelector& server_selector,
                                    const boost::posix_time::ptime& modification_ts,
                                    SharedNetwork6Collection& shared_networks) {
        if (server_selector.amAny()) {
            isc_throw(InvalidOperation, "fetching modified shared networks for ANY "
                      "server is not supported");
        }

        PsqlBindArray in_bindings;
        in_bindings.addTimestamp(modification_ts);

        auto index = (server_selector.amUnassigned() ?
                      PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_SHARED_NETWORKS6_UNASSIGNED :
                      PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_SHARED_NETWORKS6);
        getSharedNetworks6(index, server_selector, in_bindings, shared_networks);
    }

    /// @brief Sends query to insert or update shared network.
    ///
    /// @param server_selector Server selector.
    /// @param subnet Pointer to the shared network to be inserted or updated.
    void createUpdateSharedNetwork6(const ServerSelector& server_selector,
                                    const SharedNetwork6Ptr& shared_network) {
        if (server_selector.amAny()) {
            isc_throw(InvalidOperation, "creating or updating a shared network for ANY"
                      " server is not supported");

        } else if (server_selector.amUnassigned()) {
            isc_throw(NotImplemented, "managing configuration for no particular server"
                      " (unassigned) is unsupported at the moment");
        }

        PsqlBindArray in_bindings;
        in_bindings.addTempString(shared_network->getName());
        addClientClassesBinding(in_bindings, shared_network->getClientClasses());
        in_bindings.addOptional(shared_network->getIface(Network::Inheritance::NONE));
        in_bindings.addTimestamp(shared_network->getModificationTime());
        in_bindings.add(shared_network->getPreferred(Network::Inheritance::NONE));
        in_bindings.addMin(shared_network->getPreferred(Network::Inheritance::NONE));
        in_bindings.addMax(shared_network->getPreferred(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getRapidCommit(Network::Inheritance::NONE));
        in_bindings.add(shared_network->getT2(Network::Inheritance::NONE));
        addRelayBinding(in_bindings, shared_network);
        in_bindings.add(shared_network->getT1(Network::Inheritance::NONE));
        addAdditionalClassesBinding(in_bindings, shared_network);
        in_bindings.addOptional(shared_network->getReservationsGlobal(Network::Inheritance::NONE));
        in_bindings.add(shared_network->getContext());
        in_bindings.add(shared_network->getValid(Network::Inheritance::NONE));
        in_bindings.addMin(shared_network->getValid(Network::Inheritance::NONE));
        in_bindings.addMax(shared_network->getValid(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getCalculateTeeTimes(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getT1Percent(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getT2Percent(Network::Inheritance::NONE));
        addInterfaceIdBinding(in_bindings, *shared_network);
        in_bindings.addOptional(shared_network->getDdnsSendUpdates(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getDdnsOverrideNoUpdate(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getDdnsOverrideClientUpdate(Network::Inheritance::NONE));
        addDdnsReplaceClientNameBinding(in_bindings, shared_network);
        in_bindings.addOptional(shared_network->getDdnsGeneratedPrefix(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getDdnsQualifyingSuffix(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getReservationsInSubnet(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getReservationsOutOfPool(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getCacheThreshold(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getCacheMaxAge(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getAllocatorType(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getPdAllocatorType(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getDdnsTtlPercent(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getDdnsTtl(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getDdnsTtlMin(Network::Inheritance::NONE));
        in_bindings.addOptional(shared_network->getDdnsTtlMax(Network::Inheritance::NONE));

        // Start transaction.
        PgSqlTransaction transaction(conn_);

        // Create scoped audit revision. As long as this instance exists
        // no new audit revisions are created in any subsequent calls.
        ScopedAuditRevision
            audit_revision(this,
                           PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
                           server_selector, "shared network set", true);

        // Create a savepoint in case we are called as part of larger
        // transaction.
        conn_.createSavepoint("createUpdateSharedNetwork6");

        try {

            // Try to insert shared network. The shared network name must be unique,
            // so if inserting fails with DuplicateEntry exception we'll need to
            // update existing shared network entry.
            insertQuery(PgSqlConfigBackendDHCPv6Impl::INSERT_SHARED_NETWORK6,
                        in_bindings);

        } catch (const DuplicateEntry&) {
            // It already exists, rollback to the savepoint to preserve
            // any prior work.
            conn_.rollbackToSavepoint("createUpdateSharedNetwork6");

            // We're updating, so we need to remove any options.
            deleteOptions6(ServerSelector::ANY(), shared_network);

            // Need to add one more binding for WHERE clause.
            in_bindings.addTempString(shared_network->getName());

            // Try the update.
            updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::UPDATE_SHARED_NETWORK6,
                              in_bindings);

            // Remove existing server association.
            PsqlBindArray server_bindings;
            server_bindings.addTempString(shared_network->getName());
            updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::DELETE_SHARED_NETWORK6_SERVER,
                              server_bindings);
        }

        // Associate the shared network with the servers.
        PsqlBindArray attach_bindings;
        attach_bindings.addTempString(shared_network->getName());
        attach_bindings.addTimestamp(shared_network->getModificationTime());
        attachElementToServers(PgSqlConfigBackendDHCPv6Impl::INSERT_SHARED_NETWORK6_SERVER,
                               server_selector, attach_bindings);

        // (Re)create options.
        auto option_spaces = shared_network->getCfgOption()->getOptionSpaceNames();
        for (auto const& option_space : option_spaces) {
            OptionContainerPtr options = shared_network->getCfgOption()->getAll(option_space);
            for (auto const& desc : *options) {
                OptionDescriptorPtr desc_copy = OptionDescriptor::create(desc);
                desc_copy->space_name_ = option_space;
                createUpdateOption6(server_selector, shared_network->getName(),
                                    desc_copy, true);
            }
        }

        // Commit the work.
        transaction.commit();
    }

    /// @brief Sends query to insert DHCP option.
    ///
    /// This method expects that the server selector contains exactly one
    /// server tag.  It is intended to be used within a transaction.
    ///
    /// @param server_selector Server selector.
    /// @param in_bindings Collection of bindings representing an option.
    /// @param modification_ts option's modification timestamp
    void insertOption6(const ServerSelector& server_selector,
                       const PsqlBindArray& in_bindings,
                       const boost::posix_time::ptime& modification_ts) {
        // Attempt the insert.
        insertQuery(PgSqlConfigBackendDHCPv6Impl::INSERT_OPTION6, in_bindings);

        // Fetch primary key value of the inserted option. We will use it in the
        // next INSERT statement to associate this option with the server.
        auto option_id = getLastInsertId("dhcp6_options", "option_id");

        PsqlBindArray attach_bindings;
        attach_bindings.add(option_id);   // id of newly inserted global.
        attach_bindings.addTimestamp(modification_ts);

        // Associate the option with the servers.
        attachElementToServers(PgSqlConfigBackendDHCPv6Impl::INSERT_OPTION6_SERVER,
                               server_selector, attach_bindings);
    }

    /// @brief Sends query to insert or update global DHCP option.
    ///
    /// @param server_selector Server selector.
    /// @param option Pointer to the option descriptor encapsulating the option.
    void createUpdateOption6(const ServerSelector& server_selector,
                             const OptionDescriptorPtr& option) {
        if (server_selector.amUnassigned()) {
            isc_throw(NotImplemented, "managing configuration for no particular server"
                      " (unassigned) is unsupported at the moment");
        }

        auto tag = getServerTag(server_selector, "creating or updating global option");

        // Create the input parameter bindings.
        PsqlBindArray in_bindings;
        in_bindings.add(option->option_->getType());
        addOptionValueBinding(in_bindings, option);
        in_bindings.addOptional(option->formatted_value_);
        in_bindings.addOptional(option->space_name_);
        in_bindings.add(option->persistent_);
        in_bindings.add(option->cancelled_);
        in_bindings.addNull();
        in_bindings.addNull();
        in_bindings.add(0);
        in_bindings.add(option->getContext());
        in_bindings.addNull();
        in_bindings.addNull();
        in_bindings.addTimestamp(option->getModificationTime());
        addClientClassesBinding(in_bindings, option->client_classes_);
        in_bindings.addNull();

        // Remember the size before we add where clause arguments.
        size_t pre_where_size = in_bindings.size();

        // Now we add the update where clause parameters
        in_bindings.add(tag);
        in_bindings.add(option->option_->getType());
        in_bindings.addOptional(option->space_name_);
        addClientClassesBinding(in_bindings, option->client_classes_);

        // Start transaction.
        PgSqlTransaction transaction(conn_);

        // Create scoped audit revision. As long as this instance exists
        // no new audit revisions are created in any subsequent calls.
        ScopedAuditRevision
            audit_revision(this,
                           PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
                           server_selector, "global option set", false);

        // Try to update the option.
        if (updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6,
                              in_bindings) == 0) {
            // The option doesn't exist, so we'll try to insert it.
            // Remove the update where clause bindings.
            while (in_bindings.size() > pre_where_size) {
                in_bindings.popBack();
            }

            // Try to insert the option.
            insertOption6(server_selector, in_bindings,
                          option->getModificationTime());
        }

        // Commit the work.
        transaction.commit();
    }

    /// @brief Sends query to insert or update DHCP option in a subnet.
    ///
    /// @param server_selector Server selector.
    /// @param subnet_id Identifier of the subnet the option belongs to.
    /// @param option Pointer to the option descriptor encapsulating the option.
    /// @param cascade_update Boolean value indicating whether the update is
    /// performed as part of the owning element, e.g. subnet.
    void createUpdateOption6(const ServerSelector& server_selector,
                             const SubnetID& subnet_id,
                             const OptionDescriptorPtr& option,
                             const bool cascade_update) {
        if (server_selector.amUnassigned()) {
            isc_throw(NotImplemented, "managing configuration for no particular server"
                      " (unassigned) is unsupported at the moment");
        }

        // Create input bindings.
        PsqlBindArray in_bindings;
        in_bindings.add(option->option_->getType());
        addOptionValueBinding(in_bindings, option);
        in_bindings.addOptional(option->formatted_value_);
        in_bindings.addOptional(option->space_name_);
        in_bindings.add(option->persistent_);
        in_bindings.add(option->cancelled_);
        in_bindings.addNull();
        in_bindings.add(subnet_id);
        in_bindings.add(1);
        in_bindings.add(option->getContext());
        in_bindings.addNull();
        in_bindings.addNull();
        in_bindings.addTimestamp(option->getModificationTime());
        addClientClassesBinding(in_bindings, option->client_classes_);
        in_bindings.addNull();

        // Remember the size before we add where clause arguments.
        size_t pre_where_size = in_bindings.size();

        // Now we add the update where clause parameters
        in_bindings.add(subnet_id);
        in_bindings.add(option->option_->getType());
        in_bindings.addOptional(option->space_name_);
        addClientClassesBinding(in_bindings, option->client_classes_);

        // Start transaction.
        PgSqlTransaction transaction(conn_);

        // Create scoped audit revision. As long as this instance exists
        // no new audit revisions are created in any subsequent calls.
        ScopedAuditRevision
            audit_revision(this,
                           PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
                           server_selector, "subnet specific option set",
                           cascade_update);

        // Try to update the subnet option.
        if (updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_SUBNET_ID,
                              in_bindings) == 0) {
            // The option doesn't exist, so we'll try to insert it.
            // Remove the update where clause bindings.
            while (in_bindings.size() > pre_where_size) {
                in_bindings.popBack();
            }

            // Try to insert the option.
            insertOption6(server_selector, in_bindings,
                          option->getModificationTime());
        }

        // Commit the work.
        transaction.commit();
    }

    /// @brief Sends query to insert or update DHCP option in a pool.
    ///
    /// @param server_selector Server selector.
    /// @param pool_start_address Lower bound address of the pool.
    /// @param pool_end_address Upper bound address of the pool.
    /// @param option Pointer to the option descriptor encapsulating the option.
    void createUpdateOption6(const ServerSelector& server_selector,
                             const IOAddress& pool_start_address,
                             const IOAddress& pool_end_address,
                             const OptionDescriptorPtr& option) {
        uint64_t pool_id = 0;
        Pool6Ptr pool = getPool6(server_selector, pool_start_address, pool_end_address,
                                 pool_id);
        if (!pool) {
            isc_throw(BadValue, "no pool found for range of "
                      << pool_start_address << " : "
                      << pool_end_address);
        }

        createUpdateOption6(server_selector, Lease::TYPE_NA,
                            pool_id, option, false);
    }

    /// @brief Sends query to insert or update DHCP option in a pd pool.
    ///
    /// @param server_selector Server selector.
    /// @param pd_pool_prefix Address part of the pd pool prefix.
    /// @param pd_pool_prefix_length Length of the pd pool prefix.
    /// @param option Pointer to the option descriptor encapsulating the option.
    void createUpdateOption6(const ServerSelector& server_selector,
                             const asiolink::IOAddress& pd_pool_prefix,
                             const uint8_t pd_pool_prefix_length,
                             const OptionDescriptorPtr& option) {
        uint64_t pd_pool_id = 0;
        Pool6Ptr pd_pool = getPdPool6(server_selector,
                                      pd_pool_prefix,
                                      pd_pool_prefix_length,
                                      pd_pool_id);
        if (!pd_pool) {
            isc_throw(BadValue, "no prefix delegation pool found for prefix "
                      << "of " << pd_pool_prefix << "/"
                      << static_cast<unsigned>(pd_pool_prefix_length));
        }

        createUpdateOption6(server_selector, Lease::TYPE_PD,
                            pd_pool_id, option, false);
    }

    /// @brief Sends query to insert or update DHCP option in an address
    /// or prefix delegation pool.
    ///
    /// @param selector Server selector.
    /// @param pool_type Pool type (Lease::TYPE_NA or Lease::TYPE_PD).
    /// @param pool_id Identifier of the address or prefix delegation pool
    /// the option belongs to.
    /// @param option Pointer to the option descriptor encapsulating the option.
    /// @param cascade_update Boolean value indicating whether the update is
    /// performed as part of the owning element, e.g. subnet.
    void createUpdateOption6(const ServerSelector& server_selector,
                             const Lease::Type& pool_type,
                             const uint64_t pool_id,
                             const OptionDescriptorPtr& option,
                             const bool cascade_update) {
        if (server_selector.amUnassigned()) {
            isc_throw(NotImplemented, "managing configuration for no particular server"
                      " (unassigned) is unsupported at the moment");
        }

        std::string msg = "creating or updating ";
        if (pool_type == Lease::TYPE_PD) {
            msg += "prefix delegation";
        } else {
            msg += "address";
        }
        msg += " pool level option";

        PsqlBindArray in_bindings;

        in_bindings.add(option->option_->getType());
        addOptionValueBinding(in_bindings, option);
        in_bindings.addOptional(option->formatted_value_);
        in_bindings.addOptional(option->space_name_);
        in_bindings.add(option->persistent_);
        in_bindings.add(option->cancelled_);
        in_bindings.addNull();
        in_bindings.addNull();

        // scope_id
        if (pool_type == Lease::TYPE_NA) {
            in_bindings.add(5);
        } else {
            in_bindings.add(6);
        }

        in_bindings.add(option->getContext());
        in_bindings.addNull();

        // pool_id
        if (pool_type == Lease::TYPE_NA) {
            in_bindings.add(pool_id);
        } else {
            in_bindings.addNull();
        }

        in_bindings.addTimestamp(option->getModificationTime());
        addClientClassesBinding(in_bindings, option->client_classes_);

        // pd_pool_id
        if (pool_type == Lease::TYPE_PD) {
            in_bindings.add(pool_id);
        } else {
            in_bindings.addNull();
        }

        // Remember the size before we add where clause arguments.
        size_t pre_where_size = in_bindings.size();

        // Now we add the update where clause parameters
        in_bindings.add(pool_id);
        in_bindings.add(option->option_->getType());
        in_bindings.addOptional(option->space_name_);
        addClientClassesBinding(in_bindings, option->client_classes_);

        // Start transaction.
        PgSqlTransaction transaction(conn_);

        // Create scoped audit revision. As long as this instance exists
        // no new audit revisions are created in any subsequent calls.
        if (pool_type == Lease::TYPE_PD) {
            msg = "prefix delegation";
        } else {
            msg = "address";
        }
        msg += " pool specific option set";
        ScopedAuditRevision
            audit_revision(this,
                           PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
                           server_selector, msg, cascade_update);

        auto index = (pool_type == Lease::TYPE_NA ?
                      PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_POOL_ID :
                      PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_PD_POOL_ID);

        // Try to update the option.
        if (updateDeleteQuery(index, in_bindings) == 0) {
            // The option doesn't exist, so we'll try to insert it.
            // Remove the update where clause bindings.
            while (in_bindings.size() > pre_where_size) {
                in_bindings.popBack();
            }

            // Try to insert the option.
            insertOption6(server_selector, in_bindings,
                          option->getModificationTime());
        }

        // Commit the work.
        transaction.commit();
    }

    /// @brief Sends query to insert or update DHCP option in a shared network.
    ///
    /// @param selector Server selector.
    /// @param shared_network_name Name of the shared network the option
    /// belongs to.
    /// @param option Pointer to the option descriptor encapsulating the option.
    /// @param cascade_update Boolean value indicating whether the update is
    /// performed as part of the owning element, e.g. shared network.
    void createUpdateOption6(const ServerSelector& server_selector,
                             const std::string& shared_network_name,
                             const OptionDescriptorPtr& option,
                             const bool cascade_update) {
        if (server_selector.amUnassigned()) {
            isc_throw(NotImplemented, "managing configuration for no particular server"
                      " (unassigned) is unsupported at the moment");
        }

        // Create input bindings.
        PsqlBindArray in_bindings;
        in_bindings.add(option->option_->getType());
        addOptionValueBinding(in_bindings, option);
        in_bindings.addOptional(option->formatted_value_);
        in_bindings.addOptional(option->space_name_);
        in_bindings.add(option->persistent_);
        in_bindings.add(option->cancelled_);
        in_bindings.addNull();
        in_bindings.addNull();
        in_bindings.add(4);
        in_bindings.add(option->getContext());
        in_bindings.add(shared_network_name);
        in_bindings.addNull();
        in_bindings.addTimestamp(option->getModificationTime());
        addClientClassesBinding(in_bindings, option->client_classes_);
        in_bindings.addNull();

        // Remember the size before we add where clause arguments.
        size_t pre_where_size = in_bindings.size();

        // Now we add the update where clause parameters
        in_bindings.add(shared_network_name);
        in_bindings.add(option->option_->getType());
        in_bindings.addOptional(option->space_name_);
        addClientClassesBinding(in_bindings, option->client_classes_);

        // Start transaction.
        PgSqlTransaction transaction(conn_);

        // Create scoped audit revision. As long as this instance exists
        // no new audit revisions are created in any subsequent calls.
        ScopedAuditRevision
            audit_revision(this,
                           PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
                           server_selector, "shared network specific option set",
                           cascade_update);

        // Try to update the option.
        if (updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_SHARED_NETWORK,
                              in_bindings) == 0) {
            // The option doesn't exist, so we'll try to insert it.
            // Remove the update where clause bindings.
            while (in_bindings.size() > pre_where_size) {
                in_bindings.popBack();
            }

            // Try to insert the option.
            insertOption6(server_selector, in_bindings,
                          option->getModificationTime());
        }

        // Commit the work.
        transaction.commit();
    }

    /// @brief Sends query to insert or update DHCP option in a client class.
    ///
    /// @param selector Server selector.
    /// @param client_class Pointer to the client_class the option belongs to.
    /// @param option Pointer to the option descriptor encapsulating the option..
    void createUpdateOption6(const ServerSelector& server_selector,
                             const ClientClassDefPtr& client_class,
                             const OptionDescriptorPtr& option) {
        if (server_selector.amUnassigned()) {
            isc_throw(NotImplemented, "managing configuration for no particular server"
                      " (unassigned) is unsupported at the moment");
        }

        PsqlBindArray in_bindings;
        std::string class_name = client_class->getName();
        in_bindings.add(option->option_->getType());
        addOptionValueBinding(in_bindings, option);
        in_bindings.addOptional(option->formatted_value_);
        in_bindings.addOptional(option->space_name_);
        in_bindings.add(option->persistent_);
        in_bindings.add(option->cancelled_);
        in_bindings.add(class_name);
        in_bindings.addNull();
        in_bindings.add(2);
        in_bindings.add(option->getContext());
        in_bindings.addNull();
        in_bindings.addNull();
        in_bindings.addTimestamp(option->getModificationTime());
        addClientClassesBinding(in_bindings, option->client_classes_);
        in_bindings.addNull();

        // Remember the size before we add where clause arguments.
        size_t pre_where_size = in_bindings.size();

        // Now we add the update where clause parameters
        in_bindings.add(class_name);
        in_bindings.add(option->option_->getType());
        in_bindings.addOptional(option->space_name_);
        addClientClassesBinding(in_bindings, option->client_classes_);

        // Create scoped audit revision. As long as this instance exists
        // no new audit revisions are created in any subsequent calls.
        ScopedAuditRevision
            audit_revision(this,
                           PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
                           server_selector, "client class specific option set",
                           true);

        if (updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_CLIENT_CLASS,
                              in_bindings) == 0) {
            // The option doesn't exist, so we'll try to insert it.
            // Remove the update where clause bindings.
            while (in_bindings.size() > pre_where_size) {
                in_bindings.popBack();
            }

            insertOption6(server_selector, in_bindings,
                          option->getModificationTime());
        }
    }

    /// @brief Sends query to insert or update option definition.
    ///
    /// @param server_selector Server selector.
    /// @param option_def Pointer to the option definition to be inserted or updated.
    void createUpdateOptionDef6(const ServerSelector& server_selector,
                                const OptionDefinitionPtr& option_def) {
        createUpdateOptionDef(server_selector, Option::V6, option_def, DHCP6_OPTION_SPACE,
                              PgSqlConfigBackendDHCPv6Impl::GET_OPTION_DEF6_CODE_SPACE,
                              PgSqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6,
                              PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION_DEF6,
                              PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
                              PgSqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6_SERVER);
    }

    /// @brief Sends query to insert or update option definition
    /// for a client class.
    ///
    /// @param server_selector Server selector.
    /// @param option_def Pointer to the option definition to be inserted or updated.
    /// @param client_class Client class name.
    void createUpdateOptionDef6(const ServerSelector& server_selector,
                                const OptionDefinitionPtr& option_def,
                                const std::string& client_class_name) {
        createUpdateOptionDef(server_selector, Option::V6, option_def, DHCP6_OPTION_SPACE,
                              PgSqlConfigBackendDHCPv6Impl::GET_OPTION_DEF6_CODE_SPACE,
                              PgSqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6_CLIENT_CLASS,
                              PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION_DEF6_CLIENT_CLASS,
                              PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
                              PgSqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6_SERVER,
                              client_class_name);
    }

    /// @brief Sends query to delete option definition by code and
    /// option space name.
    ///
    /// @param server_selector Server selector.
    /// @param code Option code.
    /// @param name Option name.
    /// @param force When true, delete is done without checking for
    /// dependent options.
    /// @return Number of deleted option definitions.
    /// @throw NotImplemented if server selector is "unassigned".
    /// @throw InvalidOperation if force is false and there is an option
    /// matching server, code, and space.
    uint64_t deleteOptionDef6(const ServerSelector& server_selector,
                              const uint16_t code,
                              const std::string& space,
                              bool force) {
        if (!force) {
            auto option = getOption(GET_OPTION6_CODE_SPACE, Option::V6,
                                    server_selector, code, space);
            if (option) {
                isc_throw(InvalidOperation, "option exists for option definition: "
                          << space << "." << code);
            }
        }

        PsqlBindArray in_bindings;
        in_bindings.add(code);
        in_bindings.add(space);

        // Run DELETE.
        return (deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION_DEF6_CODE_NAME,
                                    server_selector,
                                    "deleting option definition",
                                    "option definition deleted",
                                    false,
                                    in_bindings));
    }

    /// @brief Sends query to delete option definitions for a client class.
    ///
    /// @param server_selector Server selector.
    /// @param client_class Pointer to the client class for which option
    /// definitions should be deleted.
    /// @return Number of deleted option definitions.
    uint64_t deleteOptionDefs6(const ServerSelector& server_selector,
                               const ClientClassDefPtr& client_class) {
        PsqlBindArray in_bindings;
        in_bindings.addTempString(client_class->getName());

        // Run DELETE.
        return (deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION_DEFS6_CLIENT_CLASS,
                                    server_selector,
                                    "deleting option definition for a client class",
                                    "option definition deleted",
                                    true,
                                    in_bindings));
    }

    /// @brief Deletes global option.
    ///
    /// @param server_selector Server selector.
    /// @param code Code of the deleted option.
    /// @param space Option space of the deleted option.
    /// @param client_classes Optional client classes list of the option to be deleted.
    /// @return Number of deleted options.
    uint64_t deleteOption6(const ServerSelector& server_selector,
                           const uint16_t code,
                           const std::string& space,
                           const ClientClassesPtr client_classes) {
        PsqlBindArray in_bindings;
        in_bindings.add(code);
        in_bindings.add(space);
        addClientClassesForWhereClause(in_bindings, client_classes);

        // Run DELETE.
        return (deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION6,
                                    server_selector,
                                    "deleting global option",
                                    "global option deleted",
                                    false,
                                    in_bindings));
    }

    /// @brief Deletes subnet level option.
    ///
    /// @param server_selector Server selector.
    /// @param subnet_id Identifier of the subnet to which deleted option
    /// belongs.
    /// @param code Code of the deleted option.
    /// @param space Option space of the deleted option.
    /// @param client_classes Optional client classes list of the option to be deleted.
    /// @return Number of deleted options.
    uint64_t deleteOption6(const ServerSelector& server_selector,
                           const SubnetID& subnet_id,
                           const uint16_t code,
                           const std::string& space,
                           const ClientClassesPtr client_classes) {
        PsqlBindArray in_bindings;
        in_bindings.add(subnet_id);
        in_bindings.add(code);
        in_bindings.add(space);
        addClientClassesForWhereClause(in_bindings, client_classes);

        // Run DELETE.
        return (deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION6_SUBNET_ID,
                                    server_selector,
                                    "deleting option for a subnet",
                                    "subnet specific option deleted",
                                    false,
                                    in_bindings));
    }

    /// @brief Deletes pool level option.
    ///
    /// @param server_selector Server selector.
    /// @param pool_start_address Lower bound pool address.
    /// @param pool_end_address Upper bound pool address.
    /// @param code Code of the deleted option.
    /// @param space Option space of the deleted option.
    /// @param client_classes Optional client classes list of the option to be deleted.
    /// @return Number of deleted options.
    uint64_t deleteOption6(const db::ServerSelector& server_selector,
                           const IOAddress& pool_start_address,
                           const IOAddress& pool_end_address,
                           const uint16_t code,
                           const std::string& space,
                           const ClientClassesPtr client_classes) {
        PsqlBindArray in_bindings;
        in_bindings.addInet6(pool_start_address);
        in_bindings.addInet6(pool_end_address);
        in_bindings.add(code);
        in_bindings.add(space);
        addClientClassesForWhereClause(in_bindings, client_classes);

        // Run DELETE.
        return (deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION6_POOL_RANGE,
                                    server_selector,
                                    "deleting option for an address pool",
                                    "address pool specific option deleted",
                                    false,
                                    in_bindings));
    }

    /// @brief Deletes pd pool level option.
    ///
    /// @param server_selector Server selector.
    /// @param pd_pool_prefix Address part of the pd pool prefix.
    /// @param pd_pool_prefix_length Length of the pd pool prefix.
    /// @param code Code of the deleted option.
    /// @param space Option space of the deleted option.
    /// @param client_classes Optional client classes list of the option to be deleted.
    /// @return Number of deleted options.
    uint64_t deleteOption6(const db::ServerSelector& server_selector,
                           const asiolink::IOAddress& pd_pool_prefix,
                           const uint8_t pd_pool_prefix_length,
                           const uint16_t code,
                           const std::string& space,
                           const ClientClassesPtr client_classes) {
        PsqlBindArray in_bindings;
        in_bindings.addTempString(pd_pool_prefix.toText());
        in_bindings.add(pd_pool_prefix_length);
        in_bindings.add(code);
        in_bindings.add(space);
        addClientClassesForWhereClause(in_bindings, client_classes);

        // Run DELETE.
        return (deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION6_PD_POOL,
                                    server_selector,
                                    "deleting option for a prefix delegation pool",
                                    "prefix delegation pool specific option deleted",
                                    false,
                                    in_bindings));
    }

    /// @brief Deletes shared network level option.
    ///
    /// @param server_selector Server selector.
    /// @param shared_network_name Name of the shared network which deleted
    /// option belongs to
    /// @param code Code of the deleted option.
    /// @param space Option space of the deleted option.
    /// @param client_classes Optional client classes list of the option to be deleted.
    /// @return Number of deleted options.
    uint64_t deleteOption6(const db::ServerSelector& server_selector,
                           const std::string& shared_network_name,
                           const uint16_t code,
                           const std::string& space,
                           const ClientClassesPtr client_classes) {
        PsqlBindArray in_bindings;
        in_bindings.add(shared_network_name);
        in_bindings.add(code);
        in_bindings.add(space);
        addClientClassesForWhereClause(in_bindings, client_classes);

        // Run DELETE.
        return (deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION6_SHARED_NETWORK,
                                    server_selector,
                                    "deleting option for a shared network",
                                    "shared network specific option deleted",
                                    false,
                                    in_bindings));
    }

    /// @brief Deletes options belonging to a subnet from the database.
    ///
    /// @param server_selector Server selector.
    /// @param subnet Pointer to the subnet for which options should be
    /// deleted.
    /// @return Number of deleted options.
    uint64_t deleteOptions6(const ServerSelector& server_selector,
                            const Subnet6Ptr& subnet) {
        PsqlBindArray in_bindings;
        in_bindings.add(subnet->getID());
        in_bindings.addTempString(subnet->toText());

        // Run DELETE.
        return (deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_OPTIONS6_SUBNET_ID_PREFIX,
                                    server_selector,
                                    "deleting options for a subnet",
                                    "subnet specific options deleted",
                                    true, in_bindings));
    }

    /// @brief Deletes options belonging to a shared network from the database.
    ///
    /// @param server_selector Server selector.
    /// @param subnet Pointer to the subnet for which options should be
    /// deleted.
    /// @return Number of deleted options.
    uint64_t deleteOptions6(const ServerSelector& server_selector,
                            const SharedNetwork6Ptr& shared_network) {
        PsqlBindArray in_bindings;
        in_bindings.addTempString(shared_network->getName());

        // Run DELETE.
        return (deleteTransactional(PgSqlConfigBackendDHCPv6Impl::
                                    DELETE_OPTIONS6_SHARED_NETWORK, server_selector,
                                    "deleting options for a shared network",
                                    "shared network specific options deleted",
                                    true, in_bindings));
    }

    /// @brief Deletes options belonging to a client class from the database.
    ///
    /// @param server_selector Server selector.
    /// @param client_class Pointer to the client class for which options
    /// should be deleted.
    /// @return Number of deleted options.
    uint64_t deleteOptions6(const ServerSelector& server_selector,
                            const ClientClassDefPtr& client_class) {
        PsqlBindArray in_bindings;
        in_bindings.addTempString(client_class->getName());

        // Run DELETE.
        return (deleteTransactional(PgSqlConfigBackendDHCPv6Impl::
                                    DELETE_OPTIONS6_CLIENT_CLASS, server_selector,
                                    "deleting options for a client class",
                                    "client class specific options deleted",
                                    true, in_bindings));
    }

    /// @brief Common function to retrieve client classes.
    ///
    /// @param index Index of the query to be used.
    /// @param server_selector Server selector.
    /// @param in_bindings Input bindings specifying selection criteria. The
    /// size of the bindings collection must match the number of placeholders
    /// in the prepared statement. The input bindings collection must be empty
    /// if the query contains no WHERE clause.
    /// @param [out] client_classes Reference to a container where fetched client
    /// classes will be inserted.
    void getClientClasses6(const StatementIndex& index,
                           const ServerSelector& server_selector,
                           const PsqlBindArray& in_bindings,
                           ClientClassDictionary& client_classes) {
        std::list<ClientClassDefPtr> class_list;
        uint64_t last_option_id = 0;
        uint64_t last_option_def_id = 0;
        std::string last_tag;

        selectQuery(index, in_bindings,
                    [this, &class_list, &last_option_id, &last_option_def_id, &last_tag]
                    (PgSqlResult& r, int row) {
            // Create a convenience worker for the row.
            PgSqlResultRowWorker worker(r, row);

            ClientClassDefPtr last_client_class;
            if (!class_list.empty()) {
                last_client_class = *class_list.rbegin();
            }

            // Class ID is column 0.
            uint64_t id = worker.getBigInt(0) ;

            if (!last_client_class || (last_client_class->getId() != id)) {
                last_option_id = 0;
                last_option_def_id = 0;
                last_tag.clear();

                auto options = boost::make_shared<CfgOption>();
                auto option_defs = boost::make_shared<CfgOptionDef>();

                last_client_class = boost::make_shared<ClientClassDef>(worker.getString(1),
                                                                       ExpressionPtr(), options);
                last_client_class->setCfgOptionDef(option_defs);

                // id
                last_client_class->setId(id);

                // name
                last_client_class->setName(worker.getString(1));

                // test
                if (!worker.isColumnNull(2)) {
                    last_client_class->setTest(worker.getString(2));
                }

                // additional
                if (!worker.isColumnNull(3)) {
                    last_client_class->setAdditional(worker.getBool(3));
                }

                // valid lifetime: default, min, max
                last_client_class->setValid(worker.getTriplet(4, 5, 6));

                // depend on known directly or indirectly
                last_client_class->setDependOnKnown(worker.getBool(7) || worker.getBool(8));

                // modification_ts
                last_client_class->setModificationTime(worker.getTimestamp(9));

                // user_context at 10.
                if (!worker.isColumnNull(10)) {
                    ElementPtr user_context = worker.getJSON(10);
                    if (user_context) {
                        last_client_class->setContext(user_context);
                    }
                }

                // class specific option definition from 11 to 20.
                // class specific option from 21 to 33.

                // preferred lifetime: default, min, max
                last_client_class->setPreferred(worker.getTriplet(36, 37, 38));

                class_list.push_back(last_client_class);
            }

            // Check for new server tags at 35.
            if (!worker.isColumnNull(35)) {
                std::string new_tag = worker.getString(35);
                if (last_tag != new_tag) {
                    if (!new_tag.empty() && !last_client_class->hasServerTag(ServerTag(new_tag))) {
                        last_client_class->setServerTag(new_tag);
                    }

                    last_tag = new_tag;
                }
            }

            // Parse client class specific option definition from 11 to 20.
            if (!worker.isColumnNull(11) &&
                (last_option_def_id < worker.getBigInt(11))) {
                last_option_def_id = worker.getBigInt(11);

                auto def = processOptionDefRow(worker, 11);
                if (def) {
                    last_client_class->getCfgOptionDef()->add(def);
                }
            }

            // Parse client class specific option from 21 to 34.
            if (!worker.isColumnNull(21) &&
                (last_option_id < worker.getBigInt(21))) {
                last_option_id = worker.getBigInt(21);

                OptionDescriptorPtr desc = processOptionRow(Option::V6, worker, 21);
                if (desc) {
                    last_client_class->getCfgOption()->add(*desc, desc->space_name_);
                }
            }
        });

        tossNonMatchingElements(server_selector, class_list);

        for (auto const& c : class_list) {
            client_classes.addClass(c);
        }
    }

    /// @brief Sends query to retrieve a client class by name.
    ///
    /// @param server_selector Server selector.
    /// @param name Name of the class to be retrieved.
    /// @return Pointer to the client class or null if the class is not found.
    ClientClassDefPtr getClientClass6(const ServerSelector& server_selector,
                                      const std::string& name) {
        PsqlBindArray in_bindings;
        in_bindings.add(name);

        ClientClassDictionary client_classes;
        getClientClasses6(PgSqlConfigBackendDHCPv6Impl::GET_CLIENT_CLASS6_NAME,
                          server_selector, in_bindings, client_classes);
        return (client_classes.getClasses()->empty() ? ClientClassDefPtr() :
                (*client_classes.getClasses()->begin()));
    }

    /// @brief Sends query to retrieve all client classes.
    ///
    /// @param server_selector Server selector.
    /// @param [out] client_classes Reference to the client classes collection
    /// where retrieved classes will be stored.
    void getAllClientClasses6(const ServerSelector& server_selector,
                              ClientClassDictionary& client_classes) {
        PsqlBindArray in_bindings;
        getClientClasses6(server_selector.amUnassigned() ?
                          PgSqlConfigBackendDHCPv6Impl::GET_ALL_CLIENT_CLASSES6_UNASSIGNED :
                          PgSqlConfigBackendDHCPv6Impl::GET_ALL_CLIENT_CLASSES6,
                          server_selector, in_bindings, client_classes);
    }

    /// @brief Sends query to retrieve modified client classes.
    ///
    /// @param server_selector Server selector.
    /// @param modification_ts Lower bound modification timestamp.
    /// @param [out] client_classes Reference to the client classes collection
    /// where retrieved classes will be stored.
    void getModifiedClientClasses6(const ServerSelector& server_selector,
                                   const boost::posix_time::ptime& modification_ts,
                                   ClientClassDictionary& client_classes) {
        if (server_selector.amAny()) {
            isc_throw(InvalidOperation, "fetching modified client classes for ANY "
                      "server is not supported");
        }

        PsqlBindArray in_bindings;
        in_bindings.addTimestamp(modification_ts);
        getClientClasses6(server_selector.amUnassigned() ?
                          PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_CLIENT_CLASSES6_UNASSIGNED :
                          PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_CLIENT_CLASSES6,
                          server_selector, in_bindings, client_classes);
    }

    /// @brief Upserts client class.
    ///
    /// @param server_selector Server selector.
    /// @param client_class Pointer to the upserted client class.
    /// @param follow_class_name name of the class after which the
    /// new or updated class should be positioned. An empty value
    /// causes the class to be appended at the end of the class
    /// hierarchy.
    void createUpdateClientClass6(const ServerSelector& server_selector,
                                  const ClientClassDefPtr& client_class,
                                  const std::string& follow_class_name) {
        // We need to evaluate class expression to see if it references any
        // other classes (dependencies). As part of this evaluation we will
        // also check if the client class depends on KNOWN/UNKNOWN built-in
        // classes.
        std::list<std::string> dependencies;
        auto depend_on_known = false;
        if (!client_class->getTest().empty()) {
            ExpressionPtr expression;
            ExpressionParser parser;
            // Parse the test expression. The callback function is normally used to
            // interrupt config file parsing when one of the classes refers to a
            // non-existing client class. It returns false in this case. Here,
            // we use the callback to capture client classes referenced by the
            // upserted client class and record whether this class depends on
            // KNOWN/UNKNOWN built-ins. The callback always returns true to avoid
            // reporting the parsing error. The dependency check is performed later
            // at the database level.
            parser.parse(expression, Element::create(client_class->getTest()), AF_INET6,
                         [&dependencies, &depend_on_known](const ClientClass& cc) -> bool {
                if (isClientClassBuiltIn(cc)) {
                    if ((cc == "KNOWN") || (cc == "UNKNOWN")) {
                        depend_on_known = true;
                    }
                } else {
                    dependencies.push_back(cc);
                }
                return (true);
            });
        }

        PsqlBindArray in_bindings;
        std::string class_name = client_class->getName();
        in_bindings.add(class_name);
        in_bindings.addTempString(client_class->getTest());
        in_bindings.add(client_class->getAdditional());
        in_bindings.add(client_class->getValid());
        in_bindings.add(client_class->getValid().getMin());
        in_bindings.add(client_class->getValid().getMax());
        in_bindings.add(depend_on_known);

        // follow-class-name (8)
        if (follow_class_name.empty()) {
            in_bindings.addNull();
        } else {
            in_bindings.add(follow_class_name);
        }

        in_bindings.add(client_class->getPreferred());
        in_bindings.add(client_class->getPreferred().getMin());
        in_bindings.add(client_class->getPreferred().getMax());
        in_bindings.addTimestamp(client_class->getModificationTime());
        in_bindings.add(client_class->getContext());

        PgSqlTransaction transaction(conn_);

        ScopedAuditRevision audit_revision(this, PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
                                           server_selector, "client class set", true);

        // Create a savepoint in case we are called as part of larger
        // transaction.
        conn_.createSavepoint("createUpdateClass6");

        // Keeps track of whether the client class is inserted or updated.
        auto update = false;
        try {
            insertQuery(PgSqlConfigBackendDHCPv6Impl::INSERT_CLIENT_CLASS6, in_bindings);

        } catch (const DuplicateEntry&) {
            // It already exists, rollback to the savepoint to preserve
            // any prior work.
            conn_.rollbackToSavepoint("createUpdateClass6");

            // Delete options and option definitions. They will be re-created from the new class
            // instance.
            deleteOptions6(ServerSelector::ANY(), client_class);
            deleteOptionDefs6(ServerSelector::ANY(), client_class);

            // Note: follow_class_name is left in the bindings even though it is
            // not needed in both cases. This allows us to use one base query.

            // Add the class name for the where clause.
            in_bindings.add(class_name);
            if (follow_class_name.empty()) {
                // If position is not specified, leave the class at the same position.
                updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::UPDATE_CLIENT_CLASS6_SAME_POSITION,
                                  in_bindings);
            } else {
                // Update with follow_class_name specifying the position.
                updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::UPDATE_CLIENT_CLASS6,
                                  in_bindings);
            }

            // Delete class associations with the servers and dependencies. We will re-create
            // them according to the new class specification.
            PsqlBindArray in_assoc_bindings;
            in_assoc_bindings.add(class_name);
            updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::DELETE_CLIENT_CLASS6_DEPENDENCY,
                              in_assoc_bindings);
            updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::DELETE_CLIENT_CLASS6_SERVER,
                              in_assoc_bindings);
            update = true;
        }

        // Associate client class with the servers.
        PsqlBindArray attach_bindings;
        attach_bindings.add(class_name);
        attach_bindings.addTimestamp(client_class->getModificationTime());

        attachElementToServers(PgSqlConfigBackendDHCPv6Impl::INSERT_CLIENT_CLASS6_SERVER,
                               server_selector, attach_bindings);

        // Iterate over the captured dependencies and try to insert them into the database.
        for (auto const& dependency : dependencies) {
            try {
                PsqlBindArray in_dependency_bindings;
                in_dependency_bindings.add(class_name);
                in_dependency_bindings.add(dependency);

                // We deleted earlier dependencies, so we can simply insert new ones.
                insertQuery(PgSqlConfigBackendDHCPv6Impl::INSERT_CLIENT_CLASS6_DEPENDENCY,
                            in_dependency_bindings);
            } catch (const std::exception& ex) {
                isc_throw(InvalidOperation, "unmet dependency on client class: " << dependency);
            }
        }

        // If we performed client class update we also have to verify that its dependency
        // on KNOWN/UNKNOWN client classes hasn't changed.
        if (update) {
            PsqlBindArray in_check_bindings;
            insertQuery(PgSqlConfigBackendDHCPv6Impl::CHECK_CLIENT_CLASS_KNOWN_DEPENDENCY_CHANGE,
                              in_check_bindings);
        }

        // (Re)create option definitions.
        if (client_class->getCfgOptionDef()) {
            auto option_defs = client_class->getCfgOptionDef()->getContainer();
            auto option_spaces = option_defs.getOptionSpaceNames();
            for (auto const& option_space : option_spaces) {
                OptionDefContainerPtr defs = option_defs.getItems(option_space);
                for (auto const& def : *defs) {
                    createUpdateOptionDef6(server_selector, def, client_class->getName());
                }
            }
        }

        // (Re)create options.
        auto option_spaces = client_class->getCfgOption()->getOptionSpaceNames();
        for (auto const& option_space : option_spaces) {
            OptionContainerPtr options = client_class->getCfgOption()->getAll(option_space);
            for (auto const& desc : *options) {
                OptionDescriptorPtr desc_copy = OptionDescriptor::create(desc);
                desc_copy->space_name_ = option_space;
                createUpdateOption6(server_selector, client_class, desc_copy);
            }
        }

        // All ok. Commit the transaction.
        transaction.commit();
    }

    /// @brief Removes client class by name.
    ///
    /// @param server_selector Server selector.
    /// @param name Removed client class name.
    /// @return Number of deleted client classes.
    uint64_t deleteClientClass6(const ServerSelector& server_selector,
                                const std::string& name) {
        int index = server_selector.amAny() ?
            PgSqlConfigBackendDHCPv6Impl::DELETE_CLIENT_CLASS6_ANY :
            PgSqlConfigBackendDHCPv6Impl::DELETE_CLIENT_CLASS6;

        uint64_t result = deleteTransactional(index, server_selector,
                                              "deleting client class",
                                              "client class deleted",
                                              true,
                                              name);
        return (result);
    }

    /// @brief Removes unassigned global parameters, global options and
    /// option definitions.
    ///
    /// This function is called when one or more servers are deleted and
    /// it is likely that there are some orphaned configuration elements
    /// left in the database. This method removes those elements.
    void purgeUnassignedConfig() {
        multipleUpdateDeleteQueries(DELETE_ALL_GLOBAL_PARAMETERS6_UNASSIGNED,
                                    DELETE_ALL_GLOBAL_OPTIONS6_UNASSIGNED,
                                    DELETE_ALL_OPTION_DEFS6_UNASSIGNED);
    }

    /// @brief Attempts to delete a server having a given tag.
    ///
    /// @param server_tag Tag of the server to be deleted.
    /// @return Number of deleted servers.
    /// @throw isc::InvalidOperation when trying to delete the logical
    /// server 'all'.
    uint64_t deleteServer6(const data::ServerTag& server_tag) {
        // It is not allowed to delete 'all' logical server.
        if (server_tag.amAll()) {
            isc_throw(InvalidOperation, "'all' is a name reserved for the server tag which"
                      " associates the configuration elements with all servers connecting"
                      " to the database and may not be deleted");
        }

        PgSqlTransaction transaction(conn_);

        // Create scoped audit revision. As long as this instance exists
        // no new audit revisions are created in any subsequent calls.
        ScopedAuditRevision
            audit_revision(this, PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
                           ServerSelector::ALL(), "deleting a server", false);

        // Specify which server should be deleted.
        PsqlBindArray in_bindings;
        in_bindings.addTempString(server_tag.get());

        // Attempt to delete the server.
        auto count = updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::DELETE_SERVER6,
                                       in_bindings);

        // If we have deleted any servers we have to remove any dangling global
        // parameters, options and option definitions.
        if (count > 0) {
            purgeUnassignedConfig();
        }

        transaction.commit();

        return (count);
    }

    /// @brief Attempts to delete all servers.
    ///
    /// This method deletes all servers added by the user. It does not
    /// delete the logical server 'all'.
    ///
    /// @return Number of deleted servers.
    uint64_t deleteAllServers6() {
        // Start transaction.
        PgSqlTransaction transaction(conn_);

        // Create scoped audit revision. As long as this instance exists
        // no new audit revisions are created in any subsequent calls.
        ScopedAuditRevision
            audit_revision(this, PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
                           ServerSelector::ALL(), "deleting all servers",
                           false);

        // No arguments, hence empty input bindings.
        PsqlBindArray in_bindings;

        // Attempt to delete the servers.
        auto count = updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_SERVERS6,
                                       in_bindings);

        // If we have deleted any servers we have to remove any dangling global
        // parameters, options and option definitions.
        if (count > 0) {
            purgeUnassignedConfig();
        }

        // Commit the transaction.
        transaction.commit();

        return (count);
    }

    /// @brief Attempts to reconnect the server to the config DB backend manager.
    ///
    /// This is a self-rescheduling function that attempts to reconnect to the
    /// server's config DB backends after connectivity to one or more have been
    /// lost. Upon entry it will attempt to reconnect via
    /// @ref ConfigBackendDHCPv6Mgr.addBackend.
    /// If this is successful, DHCP servicing is re-enabled and server returns
    /// to normal operation.
    ///
    /// If reconnection fails and the maximum number of retries has not been
    /// exhausted, it will schedule a call to itself to occur at the
    /// configured retry interval. DHCP service remains disabled.
    ///
    /// If the maximum number of retries has been exhausted an error is logged
    /// and the server shuts down.
    ///
    /// @param db_reconnect_ctl pointer to the ReconnectCtl containing the
    /// configured reconnect parameters.
    /// @return true if connection has been recovered, false otherwise.
    static bool dbReconnect(ReconnectCtlPtr db_reconnect_ctl) {
        MultiThreadingCriticalSection cs;

        // Invoke application layer connection lost callback.
        if (!DatabaseConnection::invokeDbLostCallback(db_reconnect_ctl)) {
            return (false);
        }

        bool reopened = false;
        bool do_exit = false;

        const std::string timer_name = db_reconnect_ctl->timerName();
        bool check = db_reconnect_ctl->checkRetries();

        // At least one connection was lost.
        try {
            auto srv_cfg = CfgMgr::instance().getCurrentCfg();
            auto config_ctl = srv_cfg->getConfigControlInfo();

            // Something is definitely wrong. Did the configuration change
            // somehow and there is no configuration for CB?
            if (!config_ctl) {
                do_exit = true;
                isc_throw(Unexpected, "No CB configuration found!");
            }

            // Iterate over the configured DBs and instantiate them.
            for (auto const& db : config_ctl->getConfigDatabases()) {
                const std::string& access = db.getAccessString();
                auto parameters = db.getParameters();
                if (ConfigBackendDHCPv6Mgr::instance().delBackend(parameters["type"], access, true)) {
                    ConfigBackendDHCPv6Mgr::instance().addBackend(db.getAccessString());
                }
            }

            reopened = true;
        } catch (const std::exception& ex) {
            LOG_ERROR(pgsql_cb_logger, PGSQL_CB_RECONNECT_ATTEMPT_FAILED6)
                    .arg(ex.what());
            if (do_exit) {
                return (true);
            }
        }

        if (reopened) {
            // Cancel the timer.
            if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
                TimerMgr::instance()->unregisterTimer(timer_name);
            }

            // Invoke application layer connection recovered callback.
            if (!DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl)) {
                return (false);
            }
        } else {
            if (!check) {
                // We're out of retries, log it and initiate shutdown.
                LOG_ERROR(pgsql_cb_logger, PGSQL_CB_RECONNECT_FAILED6)
                        .arg(db_reconnect_ctl->maxRetries());

                // Cancel the timer.
                if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
                    TimerMgr::instance()->unregisterTimer(timer_name);
                }

                // Invoke application layer connection failed callback.
                DatabaseConnection::invokeDbFailedCallback(db_reconnect_ctl);

                return (false);
            }

            LOG_INFO(pgsql_cb_logger, PGSQL_CB_RECONNECT_ATTEMPT_SCHEDULE6)
                    .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1)
                    .arg(db_reconnect_ctl->maxRetries())
                    .arg(db_reconnect_ctl->retryInterval());

            // Start the timer.
            if (!TimerMgr::instance()->isTimerRegistered(timer_name)) {
                TimerMgr::instance()->registerTimer(timer_name,
                    std::bind(&PgSqlConfigBackendDHCPv6Impl::dbReconnect, db_reconnect_ctl),
                              db_reconnect_ctl->retryInterval(),
                              asiolink::IntervalTimer::ONE_SHOT);
            }
            TimerMgr::instance()->setup(timer_name);
        }

        return (true);
    }

    /// @brief Sets a Network6 interface ID from result set col.
    ///
    /// Sets the interface id of the Network6 to the value carried in the
    /// given column in a result set row.
    ///
    /// @param network shared network or subnet to receive the interface ID
    /// @param worker result set row worker contain the database row
    /// @param col column within the row from which to take the value
    void setInterfaceId(Network6& network, PgSqlResultRowWorker& worker, size_t col) {
        if (!worker.isColumnNull(col)) {
            std::vector<uint8_t> iface_id_data;
            worker.getBytes(col, iface_id_data);
            if (!iface_id_data.empty()) {
                OptionPtr opt_iface_id(new Option(Option::V6, D6O_INTERFACE_ID, iface_id_data));
                network.setInterfaceId(opt_iface_id);
            }
        }
    }

    /// @brief Adds network interface ID to a bind array.
    ///
    /// Adds the interface id to end of the given bind array as a vector of bytes.
    ///
    /// @param bindings PsqlBindArray to which the ID should be added.
    /// @param network Pointer to shared network or subnet for which ID binding
    /// should be created.
    void addInterfaceIdBinding(PsqlBindArray& bindings, const Network6& network) {
        auto opt_iface_id = network.getInterfaceId(Network::Inheritance::NONE);
        if (!opt_iface_id) {
            bindings.addNull();
        } else {
            auto iface_id_data = opt_iface_id->getData();
            if (iface_id_data.empty()) {
                bindings.addNull();
            } else {
                bindings.addTempBinary(iface_id_data);
            }
        }
    }

};

namespace {

/// @brief Array of tagged statements.
typedef std::array<PgSqlTaggedStatement, PgSqlConfigBackendDHCPv6Impl::NUM_STATEMENTS>
TaggedStatementArray;

/// @brief Prepared PgSQL statements used by the backend to insert and
/// retrieve data from the database. They must be in the same order as
/// PgSqlConfigBackendDHCPv6Impl::StatementIndex.  The statement is
/// the corresponding enum name.
TaggedStatementArray tagged_statements = { {
    {
        // PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
        4,
        {
            OID_TIMESTAMP,  // 1 audit_ts
            OID_VARCHAR,    // 2 server_tag
            OID_TEXT,       // 3 audit_log_message
            OID_BOOL        // 4 cascade_transaction
        },
        "CREATE_AUDIT_REVISION",
        "select createAuditRevisionDHCP6($1, $2, $3, $4)"
    },

    // Verify that dependency on KNOWN/UNKNOWN class has not changed.
    {
        // PgSqlConfigBackendDHCPv6Impl::CHECK_CLIENT_CLASS_KNOWN_DEPENDENCY_CHANGE,
        0,
        {
            OID_NONE
        },
        "CHECK_CLIENT_CLASS_KNOWN_DEPENDENCY_CHANGE",
        "select checkDHCPv6ClientClassKnownDependencyChange()"
    },

    // Select global parameter by name.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_GLOBAL_PARAMETER6,
        2,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_VARCHAR     // 2 name
        },
        "GET_GLOBAL_PARAMETER6",
        PGSQL_GET_GLOBAL_PARAMETER(dhcp6, AND g.name = $2)
    },

    // Select all global parameters.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_ALL_GLOBAL_PARAMETERS6,
        1,
        {
            OID_VARCHAR // 1 server_tag
        },
        "GET_ALL_GLOBAL_PARAMETERS6",
        PGSQL_GET_GLOBAL_PARAMETER(dhcp6)
    },

    // Select modified global parameters.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_GLOBAL_PARAMETERS6,
        2,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_TIMESTAMP   // 2 modification_ts
        },
        "GET_MODIFIED_GLOBAL_PARAMETERS6",
        PGSQL_GET_GLOBAL_PARAMETER(dhcp6, AND g.modification_ts >= $2)
    },

    // Select subnet by id.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_SUBNET6_ID_NO_TAG,
        1,
        {
            OID_INT8    // 1 subnet_id
        },
        "GET_SUBNET6_ID_NO_TAG",
        PGSQL_GET_SUBNET6_NO_TAG(WHERE s.subnet_id = $1)
    },

    // Select subnet by id without specifying server tags.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_SUBNET6_ID_ANY,
        1,
        {
            OID_INT8    // 1 subnet_id
        },
        "GET_SUBNET6_ID_ANY",
        PGSQL_GET_SUBNET6_ANY(WHERE s.subnet_id = $1)
    },

    // Select unassigned subnet by id.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_SUBNET6_ID_UNASSIGNED,
        1,
        {
            OID_INT8    // 1 subnet_id
        },
        "GET_SUBNET6_ID_UNASSIGNED",
        PGSQL_GET_SUBNET6_UNASSIGNED(AND s.subnet_id = $1)
    },

    // Select subnet by prefix.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_SUBNET6_PREFIX_NO_TAG,
        1,
        {
            OID_VARCHAR // 1 subnet_prefix
        },
        "GET_SUBNET6_PREFIX_NO_TAG",
        PGSQL_GET_SUBNET6_NO_TAG(WHERE s.subnet_prefix = $1)
    },

    // Select subnet by prefix without specifying server tags.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_SUBNET6_PREFIX_ANY,
        1,
        {
            OID_VARCHAR // 1 subnet_prefix
        },
        "GET_SUBNET6_PREFIX_ANY",
        PGSQL_GET_SUBNET6_ANY(WHERE s.subnet_prefix = $1)
    },

    // Select unassigned subnet by prefix.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_SUBNET6_PREFIX_UNASSIGNED,
        1,
        {
            OID_VARCHAR // 1 subnet_prefix
        },
        "GET_SUBNET6_PREFIX_UNASSIGNED",
        PGSQL_GET_SUBNET6_UNASSIGNED(AND s.subnet_prefix = $1)
    },

    // Select all subnets.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_ALL_SUBNETS6,
        0,
        {
            OID_NONE
        },
        "GET_ALL_SUBNETS6",
        PGSQL_GET_SUBNET6_NO_TAG()
    },

    // Select all unassigned subnets.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_ALL_SUBNETS6_UNASSIGNED,
        0,
        {
            OID_NONE
        },
        "GET_ALL_SUBNETS6_UNASSIGNED",
        PGSQL_GET_SUBNET6_UNASSIGNED()
    },

    // Select subnets having modification time later than X.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_SUBNETS6,
        1,
        {
            OID_TIMESTAMP // 1 modification_ts
        },
        "GET_MODIFIED_SUBNETS6",
        PGSQL_GET_SUBNET6_NO_TAG(WHERE s.modification_ts >= $1)
    },

    // Select modified and unassigned subnets.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_SUBNETS6_UNASSIGNED,
        1,
        {
            OID_TIMESTAMP // 1 modification_ts
        },
        "GET_MODIFIED_SUBNETS6_UNASSIGNED",
        PGSQL_GET_SUBNET6_UNASSIGNED(AND s.modification_ts >= $1)
    },

    // Select subnets belonging to a shared network.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK_SUBNETS6,
        1,
        {
            OID_VARCHAR // 1 share_network_name
        },
        "GET_SHARED_NETWORK_SUBNETS6",
        PGSQL_GET_SUBNET6_ANY(WHERE s.shared_network_name = $1)
    },

    // Select pool by address range for a server.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_POOL6_RANGE,
        3,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_TEXT,       // 2 start_address - cast as inet
            OID_TEXT        // 3 end_address - cast as inet
        },
        "GET_POOL6_RANGE",
        PGSQL_GET_POOL6_RANGE_WITH_TAG(WHERE (srv.tag = $1 OR srv.id = 1) \
                                       AND (p.start_address = cast($2 as inet)) \
                                       AND (p.end_address = cast($3 as inet)))
    },

    // Select pool by address range for any server.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_POOL6_RANGE_ANY,
        2,
        {
            OID_TEXT,       // 1 start_address - cast as inet
            OID_TEXT        // 2 end_address - cast as inet
        },
        "GET_POOL6_RANGE_ANY",
        PGSQL_GET_POOL6_RANGE_NO_TAG(WHERE (p.start_address = cast($1 as inet)) AND \
                                           (p.end_address = cast($2 as inet)))
    },

    // Select prefix delegation pool for a server.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_PD_POOL,
        3,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_VARCHAR,    // 2 pd pool prefix
            OID_INT2        // 3 prefix length
        },
        "GET_PD_POOL",
        PGSQL_GET_PD_POOL_WITH_TAG(WHERE (srv.tag = $1 OR srv.id = 1) AND p.prefix = $2 AND p.prefix_length = $3)
    },

    // Select prefix delegation pool for any server.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_PD_POOL_ANY,
        2,
        {
            OID_VARCHAR,    // 1 prefix
            OID_INT2        // 2 prefix length
        },
        "GET_PD_POOL_ANY",
        PGSQL_GET_PD_POOL_NO_TAG(WHERE p.prefix = $1 AND p.prefix_length = $2)
    },

    // Select shared network by name.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK6_NAME_NO_TAG,
        1,
        {
            OID_VARCHAR // name of network
        },
        "GET_SHARED_NETWORK6_NAME_NO_TAG",
        PGSQL_GET_SHARED_NETWORK6_NO_TAG(WHERE n.name = $1)
    },

    // Select shared network by name without specifying server tags.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK6_NAME_ANY,
        1,
        {
            OID_VARCHAR // name of network
        },
        "GET_SHARED_NETWORK6_NAME_ANY",
        PGSQL_GET_SHARED_NETWORK6_ANY(WHERE n.name = $1)
    },

    // Select unassigned shared network by name.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK6_NAME_UNASSIGNED,
        1,
        {
            OID_VARCHAR // name of network
        },
        "GET_SHARED_NETWORK6_NAME_UNASSIGNED",
        PGSQL_GET_SHARED_NETWORK6_UNASSIGNED(AND n.name = $1)
    },

    // Select all shared networks.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_ALL_SHARED_NETWORKS6,
        0,
        {
            OID_NONE
        },
        "GET_ALL_SHARED_NETWORKS6",
        PGSQL_GET_SHARED_NETWORK6_NO_TAG()
    },

    // Select all unassigned shared networks.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_ALL_SHARED_NETWORKS6_UNASSIGNED,
        0,
        {
            OID_NONE
        },
        "GET_ALL_SHARED_NETWORKS6_UNASSIGNED",
        PGSQL_GET_SHARED_NETWORK6_UNASSIGNED()
    },

    // Select modified shared networks.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_SHARED_NETWORKS6,
        1,
        {
            OID_TIMESTAMP  // 1 modification_ts
        },
        "GET_MODIFIED_SHARED_NETWORKS6",
        PGSQL_GET_SHARED_NETWORK6_NO_TAG(WHERE n.modification_ts >= $1)
    },

    // Select modified and unassigned shared networks.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_SHARED_NETWORKS6_UNASSIGNED,
        1,
        {
            OID_TIMESTAMP  // 1 modification_ts
        },
        "GET_MODIFIED_SHARED_NETWORKS6_UNASSIGNED",
        PGSQL_GET_SHARED_NETWORK6_UNASSIGNED(AND n.modification_ts >= $1)
    },

    // Retrieves option definition by code and space.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_OPTION_DEF6_CODE_SPACE,
        3,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_INT2,       // 2 code
            OID_VARCHAR     // 3 space
        },
        "GET_OPTION_DEF6_CODE_SPACE",
        PGSQL_GET_OPTION_DEF(dhcp6, AND d.code = $2 AND d.space = $3)
    },

    // Retrieves all option definitions.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_ALL_OPTION_DEFS6,
        1,
        {
            OID_VARCHAR // 1 server_tag
        },
        "GET_ALL_OPTION_DEFS6",
        PGSQL_GET_OPTION_DEF(dhcp6)
    },

    // Retrieves modified option definitions.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_OPTION_DEFS6,
        2,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_TIMESTAMP   // 2 modification_ts
        },
        "GET_MODIFIED_OPTION_DEFS6",
        PGSQL_GET_OPTION_DEF(dhcp6, AND d.modification_ts >= $2)
    },

    // Retrieves global option by code and space.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_OPTION6_CODE_SPACE,
        4,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_INT2,       // 2 code
            OID_VARCHAR,    // 3 space
            OID_TEXT        // 4 client_classes
        },
        "GET_OPTION6_CODE_SPACE",
        PGSQL_GET_OPTION6(AND o.scope_id = 0 AND o.code = $2 AND o.space = $3
                          AND o.client_classes LIKE $4)
    },

    // Retrieves all global options.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_ALL_OPTIONS6,
        1,
        {
            OID_VARCHAR // 1 server_tag
        },
        "GET_ALL_OPTIONS6",
        PGSQL_GET_OPTION6(AND o.scope_id = 0)
    },

    // Retrieves modified options.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_OPTIONS6,
        2,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_TIMESTAMP   // 2 modification_ts
        },
        "GET_MODIFIED_OPTIONS6",
        PGSQL_GET_OPTION6(AND o.scope_id = 0 AND o.modification_ts >= $2)
    },

    // Retrieves an option for a given subnet, option code and space.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_OPTION6_SUBNET_ID_CODE_SPACE,
        4,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_INT8,       // 2 subnet_id
            OID_INT2,       // 3 code
            OID_VARCHAR     // 4 space
        },
        "GET_OPTION6_SUBNET_ID_CODE_SPACE",
        PGSQL_GET_OPTION6(AND o.scope_id = 1 AND o.dhcp6_subnet_id = $2 AND o.code = $3 AND o.space = $4)
    },

    // Retrieves an option for a given pool, option code and space.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_OPTION6_POOL_ID_CODE_SPACE,
        4,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_INT8,       // 2 pool_id
            OID_INT2,       // 3 code
            OID_VARCHAR     // 4 space
        },
        "GET_OPTION6_POOL_ID_CODE_SPACE",
        PGSQL_GET_OPTION6(AND o.scope_id = 5 AND o.pool_id = $2 AND o.code = $3 AND o.space = $4)
    },

    // Retrieves an option for a given pd pool, option code and space.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_OPTION6_PD_POOL_ID_CODE_SPACE,
        4,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_INT8,       // 2 pd_pool_id
            OID_INT2,       // 3 code
            OID_VARCHAR     // 4 space
        },
        "GET_OPTION6_PD_POOL_ID_CODE_SPACE",
        PGSQL_GET_OPTION6(AND o.scope_id = 6 AND o.pd_pool_id = $2 AND o.code = $3 AND o.space = $4)
    },

    // Retrieves an option for a given shared network, option code and space.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_OPTION6_SHARED_NETWORK_CODE_SPACE,
        4,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_VARCHAR,    // 2 shared_network_name
            OID_INT2,       // 3 code
            OID_VARCHAR     // 4 space
        },
        "GET_OPTION6_SHARED_NETWORK_CODE_SPACE",
        PGSQL_GET_OPTION6(AND o.scope_id = 4 AND o.shared_network_name = $2 AND o.code = $3 AND o.space = $4)
    },

    // Select a client class by name.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_CLIENT_CLASS6_NAME,
        1,
        {
            OID_VARCHAR // name of class
        },
        "GET_CLIENT_CLASS6_NAME",
        PGSQL_GET_CLIENT_CLASS6_WITH_TAG(WHERE c.name = $1)
    },

    // Select all client classes.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_ALL_CLIENT_CLASSES6,
        0,
        {
            OID_NONE
        },
        "GET_ALL_CLIENT_CLASSES6",
        PGSQL_GET_CLIENT_CLASS6_WITH_TAG()
    },

    // Select all unassigned client classes.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_ALL_CLIENT_CLASSES6_UNASSIGNED,
        0,
        {
            OID_NONE
        },
        "GET_ALL_CLIENT_CLASSES6_UNASSIGNED",
        PGSQL_GET_CLIENT_CLASS6_UNASSIGNED()
    },

    // Select modified client classes.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_CLIENT_CLASSES6,
        1,
        {
            OID_TIMESTAMP // 1 modification_ts
        },
        "GET_MODIFIED_CLIENT_CLASSES6",
        PGSQL_GET_CLIENT_CLASS6_WITH_TAG(WHERE c.modification_ts >= $1)
    },

    // Select modified client classes.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_CLIENT_CLASSES6_UNASSIGNED,
        1,
        {
            OID_TIMESTAMP // 1 modification_ts
        },
        "GET_MODIFIED_CLIENT_CLASSES6_UNASSIGNED",
        PGSQL_GET_CLIENT_CLASS6_UNASSIGNED(AND c.modification_ts >= $1)
    },

    // Retrieves the most recent audit entries.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_AUDIT_ENTRIES6_TIME,
        3,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_TIMESTAMP,  // 2 modification_ts
            OID_INT8        // 3 revision id
        },
        "GET_AUDIT_ENTRIES6_TIME",
        PGSQL_GET_AUDIT_ENTRIES_TIME(dhcp6)
    },

    // Retrieves a server by tag.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_SERVER6,
        1,
        {
            OID_VARCHAR // 1 server_tag
        },
        "GET_SERVER6",
        PGSQL_GET_SERVER(dhcp6)
    },

    // Retrieves all servers.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_ALL_SERVERS6,
        0,
        {
            OID_NONE
        },
        "GET_ALL_SERVERS6",
        PGSQL_GET_ALL_SERVERS(dhcp6)
    },

    // Insert global parameter.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_GLOBAL_PARAMETER6,
        4,
        {
            OID_VARCHAR,    // 1 name
            OID_TEXT,       // 2 value
            OID_INT2,       // 3 parameter_type
            OID_TIMESTAMP   // 4 modification_ts
        },
        "INSERT_GLOBAL_PARAMETER6",
        PGSQL_INSERT_GLOBAL_PARAMETER(dhcp6)
    },

    // Insert association of the global parameter with a server.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_GLOBAL_PARAMETER6_SERVER,
        3,
        {
            OID_INT8,       // 1 parameter_id
            OID_TIMESTAMP,  // 2 modification_ts
            OID_VARCHAR     // 3 server_tag
        },
        "INSERT_GLOBAL_PARAMETER6_SERVER",
        PGSQL_INSERT_GLOBAL_PARAMETER_SERVER(dhcp6)
    },

    // Insert a subnet.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_SUBNET6,
        39,
        {
            OID_INT8,       //  1 subnet_id,
            OID_VARCHAR,    //  2 subnet_prefix
            OID_TEXT,       //  3 client_class
            OID_VARCHAR,    //  4 interface
            OID_TIMESTAMP,  //  5 modification_ts
            OID_INT8,       //  6 preferred_lifetime
            OID_INT8,       //  7 min_preferred_lifetime
            OID_INT8,       //  8 max_preferred_lifetime
            OID_BOOL,       //  9 rapid_commit
            OID_INT8,       // 10 rebind_timer
            OID_TEXT,       // 11 relay
            OID_INT8,       // 12 renew_timer
            OID_TEXT,       // 13 evaluate_additional_classes
            OID_BOOL,       // 14 reservations_global
            OID_VARCHAR,    // 15 shared_network_name
            OID_TEXT,       // 16 user_context - cast as json
            OID_INT8,       // 17 valid_lifetime
            OID_INT8,       // 18 min_valid_lifetime
            OID_INT8,       // 19 max_valid_lifetime
            OID_BOOL,       // 20 calculate_tee_times
            OID_TEXT,       // 21 t1_percent - cast as float
            OID_TEXT,       // 22 t2_percent - cast as float
            OID_BYTEA,      // 23 interface_id
            OID_BOOL,       // 24 ddns_send_updates
            OID_BOOL,       // 25 ddns_override_no_update
            OID_BOOL,       // 26 ddns_override_client_update
            OID_INT8,       // 27 ddns_replace_client_name
            OID_VARCHAR,    // 28 ddns_generated_prefix
            OID_VARCHAR,    // 29 ddns_qualifying_suffix
            OID_BOOL,       // 30 reservations_in_subnet
            OID_BOOL,       // 31 reservations_out_of_pool
            OID_TEXT,       // 32 cache_threshold - cast as float
            OID_INT8,       // 33 cache_max_age
            OID_VARCHAR,    // 34 allocator
            OID_VARCHAR,    // 35 pd_allocator
            OID_TEXT,       // 36 ddns_ttl_percent - cast as float
            OID_INT8,       // 37 ddns_ttl
            OID_INT8,       // 38 ddns_ttl_min
            OID_INT8        // 39 ddns_ttl_max
        },
        "INSERT_SUBNET6",
        "INSERT INTO dhcp6_subnet("
        "  subnet_id,"
        "  subnet_prefix,"
        "  client_classes,"
        "  interface,"
        "  modification_ts,"
        "  preferred_lifetime,"
        "  min_preferred_lifetime,"
        "  max_preferred_lifetime,"
        "  rapid_commit,"
        "  rebind_timer,"
        "  relay,"
        "  renew_timer,"
        "  evaluate_additional_classes,"
        "  reservations_global,"
        "  shared_network_name,"
        "  user_context,"
        "  valid_lifetime,"
        "  min_valid_lifetime,"
        "  max_valid_lifetime,"
        "  calculate_tee_times,"
        "  t1_percent,"
        "  t2_percent,"
        "  interface_id,"
        "  ddns_send_updates,"
        "  ddns_override_no_update,"
        "  ddns_override_client_update,"
        "  ddns_replace_client_name,"
        "  ddns_generated_prefix,"
        "  ddns_qualifying_suffix,"
        "  reservations_in_subnet,"
        "  reservations_out_of_pool,"
        "  cache_threshold,"
        "  cache_max_age,"
        "  allocator,"
        "  pd_allocator,"
        "  ddns_ttl_percent,"
        "  ddns_ttl,"
        "  ddns_ttl_min,"
        "  ddns_ttl_max"
        ") VALUES ("
        "   $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, "
        "   $11, $12, $13, $14, $15, cast($16 as json), $17, $18, $19, $20, "
        "   cast($21 as float), cast($22 as float), $23, $24, $25, $26, $27, $28, $29, $30, "
        "   $31, cast($32 as float), $33, $34, $35, "
        "   cast($36 as float), $37, $38, $39"
        ")"
    },

    // Insert association of the subnet with a server.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_SUBNET6_SERVER,
        3,
        {
            OID_INT8,       // 1 subnet_id
            OID_TIMESTAMP,  // 2 modification_ts
            OID_VARCHAR     // 3 server_tag
        },
        "INSERT_SUBNET6_SERVER",
        PGSQL_INSERT_SUBNET_SERVER(dhcp6)
    },

    // Insert pool for a subnet.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_POOL6,
        7,
        {
            OID_TEXT,       // 1 start_address - cast as inet
            OID_TEXT,       // 2 end_address - cast as inet
            OID_INT8,       // 3 subnet_id
            OID_TEXT,       // 4 client_classes
            OID_TEXT,       // 5 evaluate_additional_classes
            OID_TEXT,       // 6 user_context - cast as json
            OID_TIMESTAMP   // 7 modification_ts
        },
        "INSERT_POOL6",
        PGSQL_INSERT_POOL(dhcp6)
    },

    // Insert pd pool for a subnet.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_PD_POOL,
        10,
        {
            OID_VARCHAR,    //  1 prefix
            OID_INT2,       //  2 prefix_length
            OID_INT2,       //  3 delegated_prefix_length
            OID_INT8,       //  4 subnet_id
            OID_VARCHAR,    //  5 excluded_prefix
            OID_INT2,       //  6 excluded_prefix_length
            OID_TEXT,       //  7 client_classes
            OID_TEXT,       //  8 evaluate_additional_classes
            OID_TEXT,       //  9 user_context - cast as json
            OID_TIMESTAMP,  // 10 modification_ts
        },
        "INSERT_PD_POOL",
        PGSQL_INSERT_PD_POOL()
    },

    // Insert a shared network.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_SHARED_NETWORK6,
        37,
        {
            OID_VARCHAR,    //  1 name
            OID_TEXT,       //  2 client_classes
            OID_VARCHAR,    //  3 interface
            OID_TIMESTAMP,  //  4 modification_ts
            OID_INT8,       //  5 preferred_lifetime
            OID_INT8,       //  6 min_preferred_lifetime
            OID_INT8,       //  7 max_preferred_lifetime
            OID_BOOL,       //  8 rapid_commit
            OID_INT8,       //  9 rebind_timer
            OID_TEXT,       // 10 relay
            OID_INT8,       // 11 renew_timer
            OID_TEXT,       // 12 evaluate_additional_classes
            OID_BOOL,       // 13 reservations_global
            OID_TEXT,       // 14 user_context - cast as json
            OID_INT8,       // 15 valid_lifetime
            OID_INT8,       // 16 min_valid_lifetime
            OID_INT8,       // 17 max_valid_lifetime
            OID_BOOL,       // 18 calculate_tee_times
            OID_TEXT,       // 19 t1_percent - cast as float
            OID_TEXT,       // 20 t2_percent - cast as float
            OID_BYTEA,      // 21 interface-id
            OID_BOOL,       // 22 ddns_send_updates
            OID_BOOL,       // 23 ddns_override_no_update
            OID_BOOL,       // 24 ddns_override_client_update
            OID_INT8,       // 25 ddns_replace_client_name
            OID_VARCHAR,    // 26 ddns_generated_prefix
            OID_VARCHAR,    // 27 ddns_qualifying_suffix
            OID_BOOL,       // 28 reservations_in_subnet
            OID_BOOL,       // 29 reservations_out_of_pool
            OID_TEXT,       // 30 cache_threshold - cast as float
            OID_INT8,       // 31 cache_max_age
            OID_VARCHAR,    // 32 allocator
            OID_VARCHAR,    // 33 pd_allocator
            OID_TEXT,       // 34 ddns_ttl_percent - cast as float
            OID_INT8,       // 35 ddns_ttl
            OID_INT8,       // 36 ddns_ttl_min
            OID_INT8        // 37 ddns_ttl_max
        },
        "INSERT_SHARED_NETWORK6",
        "INSERT INTO dhcp6_shared_network("
        "  name,"
        "  client_classes,"
        "  interface,"
        "  modification_ts,"
        "  preferred_lifetime,"
        "  min_preferred_lifetime,"
        "  max_preferred_lifetime,"
        "  rapid_commit,"
        "  rebind_timer,"
        "  relay,"
        "  renew_timer,"
        "  evaluate_additional_classes,"
        "  reservations_global,"
        "  user_context,"
        "  valid_lifetime,"
        "  min_valid_lifetime,"
        "  max_valid_lifetime,"
        "  calculate_tee_times,"
        "  t1_percent,"
        "  t2_percent,"
        "  interface_id,"
        "  ddns_send_updates,"
        "  ddns_override_no_update,"
        "  ddns_override_client_update,"
        "  ddns_replace_client_name,"
        "  ddns_generated_prefix,"
        "  ddns_qualifying_suffix,"
        "  reservations_in_subnet,"
        "  reservations_out_of_pool,"
        "  cache_threshold,"
        "  cache_max_age,"
        "  allocator,"
        "  pd_allocator,"
        "  ddns_ttl_percent,"
        "  ddns_ttl,"
        "  ddns_ttl_min,"
        "  ddns_ttl_max"
        ") VALUES ("
        "   $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, "
        "   $11, $12, $13, cast($14 as json), $15, $16, $17, $18,"
        "   cast($19 as float), cast($20 as float), $21, $22, $23,"
        "   $24, $25, $26, $27, $28, $29, cast($30 as float), $31, $32, $33, "
        "   cast($34 as float), $35, $36, $37"
        ")"
    },

    // Insert association of the shared network with a server.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_SHARED_NETWORK6_SERVER,
        3,
        {
            OID_VARCHAR,    // 1 shared_network_name
            OID_TIMESTAMP,  // 2 modification_ts
            OID_VARCHAR     // 3 server_tag
        },
        "INSERT_SHARED_NETWORK6_SERVER",
        PGSQL_INSERT_SHARED_NETWORK_SERVER(dhcp6)
    },

    // Insert option definition.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6,
        10,
        {
            OID_INT2,       //  1 code
            OID_VARCHAR,    //  2 name
            OID_VARCHAR,    //  3 space
            OID_INT2,       //  4 type
            OID_TIMESTAMP,  //  5 modification_ts
            OID_BOOL,       //  6 is_array
            OID_VARCHAR,    //  7 encapsulate
            OID_VARCHAR,    //  8 record_types
            OID_VARCHAR,    //  9 user_context
            OID_INT8        // 10 class_id
        },
        "INSERT_OPTION_DEF6",
        PGSQL_INSERT_OPTION_DEF(dhcp6)
    },

    // Insert option definition for client class.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6_CLIENT_CLASS,
        10,
        {
            OID_INT2,       //  1 code
            OID_VARCHAR,    //  2 name
            OID_VARCHAR,    //  3 space
            OID_INT2,       //  4 type
            OID_TIMESTAMP,  //  5 modification_ts
            OID_BOOL,       //  6 is_array
            OID_VARCHAR,    //  7 encapsulate
            OID_VARCHAR,    //  8 record_types
            OID_VARCHAR,    //  9 user_context
            OID_VARCHAR     // 10 class name for where clause
        },
        "INSERT_OPTION_DEF6_CLIENT_CLASS",
        PGSQL_INSERT_OPTION_DEF_CLIENT_CLASS(dhcp6)
    },

    // Insert association of the option definition with a server.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6_SERVER,
        3,
        {
            OID_INT8,       // 1 option_def_id
            OID_TIMESTAMP,  // 2 modification_ts
            OID_VARCHAR     // 3 server_tag
        },
        "INSERT_OPTION_DEF6_SERVER",
        PGSQL_INSERT_OPTION_DEF_SERVER(dhcp6)
    },

    // Insert subnet specific option.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_OPTION6,
        15,
        {
            OID_INT2,       //  1 code
            OID_BYTEA,      //  2 value
            OID_TEXT,       //  3 formatted_value
            OID_VARCHAR,    //  4 space
            OID_BOOL,       //  5 persistent
            OID_BOOL,       //  6 cancelled
            OID_VARCHAR,    //  7 dhcp_client_class
            OID_INT8,       //  8 dhcp6_subnet_id
            OID_INT2,       //  9 scope_id
            OID_TEXT,       // 10 user_context
            OID_VARCHAR,    // 11 shared_network_name
            OID_INT8,       // 12 pool_id
            OID_TIMESTAMP,  // 13 modification_ts
            OID_TEXT,       // 14 client_classes
            OID_INT8        // 15 pd_pool_id
        },
        "INSERT_OPTION6",
        PGSQL_INSERT_OPTION6()
    },

    // Insert association of the DHCP option with a server.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_OPTION6_SERVER,
        3,
        {
            OID_INT8,       // 1 option_id
            OID_TIMESTAMP,  // 2 modification_ts
            OID_VARCHAR     // 3 server_tag
        },
        "INSERT_OPTION6_SERVER",
        PGSQL_INSERT_OPTION_SERVER(dhcp6)
    },

    // Insert client class.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_CLIENT_CLASS6,
        13,
        {
            OID_VARCHAR,    //  1 name
            OID_TEXT,       //  2 test
            OID_BOOL,       //  3 only_in_additional_list
            OID_INT8,       //  4 valid_lifetime
            OID_INT8,       //  5 min_valid_lifetime
            OID_INT8,       //  6 max_valid_lifetime
            OID_BOOL,       //  7 depend_on_known_directly
            OID_VARCHAR,    //  8 follow_class_name
            OID_INT8,       //  9 preferred_lifetime
            OID_INT8,       // 10 min_preferred_lifetime
            OID_INT8,       // 11 max_preferred_lifetime
            OID_TIMESTAMP,  // 12 modification_ts
            OID_TEXT        // 13 user_context cast as JSON
        },
        "INSERT_CLIENT_CLASS6",
        "INSERT INTO dhcp6_client_class("
        "  name,"
        "  test,"
        "  only_in_additional_list,"
        "  valid_lifetime,"
        "  min_valid_lifetime,"
        "  max_valid_lifetime,"
        "  depend_on_known_directly,"
        "  follow_class_name,"
        "  preferred_lifetime,"
        "  min_preferred_lifetime,"
        "  max_preferred_lifetime,"
        "  modification_ts,"
        "  user_context "
        ") VALUES ("
        "   $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, cast($13 as JSON)"
        ")"
    },

    // Insert association of a client class with a server.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_CLIENT_CLASS6_SERVER,
        3,
        {
            OID_VARCHAR,    // 1 class_name
            OID_TIMESTAMP,  // 2 modification_ts
            OID_VARCHAR     // 3 server_tag
        },
        "INSERT_CLIENT_CLASS6_SERVER",
        PGSQL_INSERT_CLIENT_CLASS_SERVER(dhcp6)
    },

    // Insert client class dependency.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_CLIENT_CLASS6_DEPENDENCY,
        2,
        {
            OID_VARCHAR,    // class name
            OID_VARCHAR     // dependency class name
        },
        "INSERT_CLIENT_CLASS6_DEPENDENCY",
        PGSQL_INSERT_CLIENT_CLASS_DEPENDENCY(dhcp6)
    },

    // Insert server with server tag and description.
    {
        // PgSqlConfigBackendDHCPv6Impl::INSERT_SERVER6,
        3,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_VARCHAR,    // 2 description
            OID_TIMESTAMP   // 3 modification_ts
        },
        "INSERT_SERVER6",
        PGSQL_INSERT_SERVER(dhcp6)
    },

    // Update existing global parameter.
    {
        // PgSqlConfigBackendDHCPv6Impl::UPDATE_GLOBAL_PARAMETER6,
        6,
        {
            OID_VARCHAR,    // 1 name
            OID_TEXT,       // 2 value
            OID_INT2,       // 3 parameter_type
            OID_TIMESTAMP,  // 4 modification_ts
            OID_VARCHAR,    // 5 server_tag
            OID_VARCHAR,    // 6 name (of global to update)
        },
        "UPDATE_GLOBAL_PARAMETER6",
        PGSQL_UPDATE_GLOBAL_PARAMETER(dhcp6)
    },

    // Update existing subnet.
    {
        // PgSqlConfigBackendDHCPv6Impl::UPDATE_SUBNET6,
        41,
        {
            OID_INT8,       //  1 subnet_id,
            OID_VARCHAR,    //  2 subnet_prefix
            OID_TEXT,       //  3 client_classes
            OID_VARCHAR,    //  4 interface
            OID_TIMESTAMP,  //  5 modification_ts
            OID_INT8,       //  6 preferred_lifetime
            OID_INT8,       //  7 min_preferred_lifetime
            OID_INT8,       //  8 max_preferred_lifetime
            OID_BOOL,       //  9 rapid_commit
            OID_INT8,       // 10 rebind_timer
            OID_TEXT,       // 11 relay
            OID_INT8,       // 12 renew_timer
            OID_TEXT,       // 13 evaluate_additional_classes
            OID_BOOL,       // 14 reservations_global
            OID_VARCHAR,    // 15 shared_network_name
            OID_TEXT,       // 16 user_context - cast as json
            OID_INT8,       // 17 valid_lifetime
            OID_INT8,       // 18 min_valid_lifetime
            OID_INT8,       // 19 max_valid_lifetime
            OID_BOOL,       // 20 calculate_tee_times
            OID_TEXT,       // 21 t1_percent - cast as float
            OID_TEXT,       // 22 t2_percent - cast as float
            OID_BYTEA,      // 23 interface_id
            OID_BOOL,       // 24 ddns_send_updates
            OID_BOOL,       // 25 ddns_override_no_update
            OID_BOOL,       // 26 ddns_override_client_update
            OID_INT8,       // 27 ddns_replace_client_name
            OID_VARCHAR,    // 28 ddns_generated_prefix
            OID_VARCHAR,    // 29 ddns_qualifying_suffix
            OID_BOOL,       // 30 reservations_in_subnet
            OID_BOOL,       // 31 reservations_out_of_pool
            OID_TEXT,       // 32 cache_threshold - cast as float
            OID_INT8,       // 33 cache_max_age
            OID_VARCHAR,    // 34 allocator
            OID_VARCHAR,    // 35 pd_allocator
            OID_TEXT,       // 36 ddns_ttl_percent - cast as float
            OID_INT8,       // 37 ddns_ttl
            OID_INT8,       // 38 ddns_ttl_min
            OID_INT8,       // 39 ddns_ttl_max
            OID_INT8,       // 40 subnet_id,
            OID_VARCHAR     // 41 subnet_prefix
        },
        "UPDATE_SUBNET6",
        "UPDATE dhcp6_subnet SET"
        "  subnet_id = $1,"
        "  subnet_prefix = $2,"
        "  client_classes = $3,"
        "  interface = $4,"
        "  modification_ts = $5,"
        "  preferred_lifetime = $6,"
        "  min_preferred_lifetime = $7,"
        "  max_preferred_lifetime = $8,"
        "  rapid_commit = $9,"
        "  rebind_timer = $10,"
        "  relay = $11,"
        "  renew_timer = $12,"
        "  evaluate_additional_classes = $13,"
        "  reservations_global = $14,"
        "  shared_network_name = $15,"
        "  user_context = cast($16 as json),"
        "  valid_lifetime = $17,"
        "  min_valid_lifetime = $18,"
        "  max_valid_lifetime = $19,"
        "  calculate_tee_times = $20,"
        "  t1_percent = cast($21 as float),"
        "  t2_percent = cast($22 as float),"
        "  interface_id = $23,"
        "  ddns_send_updates = $24,"
        "  ddns_override_no_update = $25,"
        "  ddns_override_client_update = $26,"
        "  ddns_replace_client_name = $27,"
        "  ddns_generated_prefix = $28,"
        "  ddns_qualifying_suffix = $29,"
        "  reservations_in_subnet = $30,"
        "  reservations_out_of_pool = $31,"
        "  cache_threshold = cast($32 as float),"
        "  cache_max_age =  $33,"
        "  allocator = $34,"
        "  pd_allocator = $35,"
        "  ddns_ttl_percent = cast($36 as float),"
        "  ddns_ttl = $37,"
        "  ddns_ttl_min = $38,"
        "  ddns_ttl_max = $39 "
        "WHERE subnet_id = $40 OR subnet_prefix = $41"
    },

    // Update existing shared network.
    {
        // PgSqlConfigBackendDHCPv6Impl::UPDATE_SHARED_NETWORK6,
        38,
        {
            OID_VARCHAR,    //  1 name
            OID_TEXT,       //  2 client_classes
            OID_VARCHAR,    //  3 interface
            OID_TIMESTAMP,  //  4 modification_ts
            OID_INT8,       //  5 preferred_lifetime
            OID_INT8,       //  6 min_preferred_lifetime
            OID_INT8,       //  7 max_preferred_lifetime
            OID_BOOL,       //  8 rapid_commit
            OID_INT8,       //  9 rebind_timer
            OID_TEXT,       // 10 relay
            OID_INT8,       // 11 renew_timer
            OID_TEXT,       // 12 evaluate_additional_classes
            OID_BOOL,       // 13 reservations_global
            OID_TEXT,       // 14 user_context - cast as json
            OID_INT8,       // 15 valid_lifetime
            OID_INT8,       // 16 min_valid_lifetime
            OID_INT8,       // 17 max_valid_lifetime
            OID_BOOL,       // 18 calculate_tee_times
            OID_TEXT,       // 19 t1_percent - cast as float
            OID_TEXT,       // 20 t2_percent - cast as float
            OID_BYTEA,      // 21 interface-id
            OID_BOOL,       // 22 ddns_send_updates
            OID_BOOL,       // 23 ddns_override_no_update
            OID_BOOL,       // 24 ddns_override_client_update
            OID_INT8,       // 25 ddns_replace_client_name
            OID_VARCHAR,    // 26 ddns_generated_prefix
            OID_VARCHAR,    // 27 ddns_qualifying_suffix
            OID_BOOL,       // 28 reservations_in_subnet
            OID_BOOL,       // 29 reservations_out_of_pool
            OID_TEXT,       // 30 cache_threshold - cast as float
            OID_INT8,       // 31 cache_max_age
            OID_VARCHAR,    // 32 allocator
            OID_VARCHAR,    // 33 pd_allocator
            OID_TEXT,       // 34 ddns_ttl_percent - cast as float
            OID_INT8,       // 35 ddns_ttl
            OID_INT8,       // 36 ddns_ttl_min
            OID_INT8,       // 37 ddns_ttl_max
            OID_VARCHAR     // 38 name
        },
        "UPDATE_SHARED_NETWORK6",
        "UPDATE dhcp6_shared_network SET"
        "  name = $1,"
        "  client_classes = $2,"
        "  interface = $3,"
        "  modification_ts = $4,"
        "  preferred_lifetime = $5,"
        "  min_preferred_lifetime = $6,"
        "  max_preferred_lifetime = $7,"
        "  rapid_commit = $8,"
        "  rebind_timer = $9,"
        "  relay = $10,"
        "  renew_timer = $11,"
        "  evaluate_additional_classes = $12,"
        "  reservations_global = $13,"
        "  user_context = cast($14 as json),"
        "  valid_lifetime = $15,"
        "  min_valid_lifetime = $16,"
        "  max_valid_lifetime = $17,"
        "  calculate_tee_times = $18,"
        "  t1_percent = cast($19 as float),"
        "  t2_percent = cast($20 as float),"
        "  interface_id = $21,"
        "  ddns_send_updates = $22,"
        "  ddns_override_no_update = $23,"
        "  ddns_override_client_update = $24,"
        "  ddns_replace_client_name = $25,"
        "  ddns_generated_prefix = $26,"
        "  ddns_qualifying_suffix = $27,"
        "  reservations_in_subnet = $28,"
        "  reservations_out_of_pool = $29,"
        "  cache_threshold = cast($30 as float),"
        "  cache_max_age = $31,"
        "  allocator = $32,"
        "  pd_allocator = $33,"
        "  ddns_ttl_percent = cast($34 as float),"
        "  ddns_ttl = $35,"
        "  ddns_ttl_min = $36,"
        "  ddns_ttl_max = $37 "
        "WHERE name = $38"
    },

    // Update existing option definition.
    {
        // PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION_DEF6,
        13,
        {
            OID_INT2,       //  1 code
            OID_VARCHAR,    //  2 name
            OID_VARCHAR,    //  3 space
            OID_INT2,       //  4 type
            OID_TIMESTAMP,  //  5 modification_ts
            OID_BOOL,       //  6 is_array
            OID_VARCHAR,    //  7 encapsulate
            OID_VARCHAR,    //  8 record_types
            OID_TEXT,       //  9 user_context
            OID_INT2,       // 10 class_id
            OID_VARCHAR,    // 11 server_tag
            OID_INT2,       // 12 code (of option to update)
            OID_VARCHAR,    // 13 space (of option to update)
        },
        "UPDATE_OPTION_DEF6",
        PGSQL_UPDATE_OPTION_DEF(dhcp6)
    },

    // Update existing client class option definition.
    {
        // PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION_DEF6_CLIENT_CLASS,
        13,
        {
            OID_INT2,       //  1 code
            OID_VARCHAR,    //  2 name
            OID_VARCHAR,    //  3 space
            OID_INT2,       //  4 type
            OID_TIMESTAMP,  //  5 modification_ts
            OID_BOOL,       //  6 is_array
            OID_VARCHAR,    //  7 encapsulate
            OID_VARCHAR,    //  8 record_types
            OID_TEXT,       //  9 user_context
            OID_VARCHAR,    // 10 name (of class option belongs to)
            OID_VARCHAR,    // 11 server_tag
            OID_INT2,       // 12 code (of option to update)
            OID_VARCHAR,    // 13 space (of option to update)
        },
        "UPDATE_OPTION_DEF6_CLIENT_CLASS",
        PGSQL_UPDATE_OPTION_DEF_CLIENT_CLASS(dhcp6)
    },

    // Update existing global option.
    {
        // PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6,
        19,
        {
            OID_INT2,       //  1 code
            OID_BYTEA,      //  2 value
            OID_TEXT,       //  3 formatted_value
            OID_VARCHAR,    //  4 space
            OID_BOOL,       //  5 persistent
            OID_BOOL,       //  6 cancelled
            OID_VARCHAR,    //  7 dhcp_client_class
            OID_INT8,       //  8 dhcp6_subnet_id
            OID_INT2,       //  9 scope_id
            OID_TEXT,       // 10 user_context
            OID_VARCHAR,    // 11 shared_network_name
            OID_INT8,       // 12 pool_id
            OID_TIMESTAMP,  // 13 modification_ts
            OID_TEXT,       // 14 client_classes
            OID_INT8,       // 15 pd_pool_id
            OID_VARCHAR,    // 16 server_tag
            OID_INT2,       // 17 code (of option to update)
            OID_VARCHAR,    // 18 space (of option to update)
            OID_TEXT        // 19 client_classes (of option to update)
        },
        "UPDATE_OPTION6",
        PGSQL_UPDATE_OPTION6_WITH_TAG(AND o.scope_id = 0 AND o.code = $17 AND o.space = $18
                                      AND o.client_classes = $19)
    },

    // Update existing subnet level option.
    {
        // PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_SUBNET_ID,
        19,
        {
            OID_INT2,       //  1 code
            OID_BYTEA,      //  2 value
            OID_TEXT,       //  3 formatted_value
            OID_VARCHAR,    //  4 space
            OID_BOOL,       //  5 persistent
            OID_BOOL,       //  6 cancelled
            OID_VARCHAR,    //  7 dhcp_client_class
            OID_INT8,       //  8 dhcp6_subnet_id
            OID_INT2,       //  9 scope_id
            OID_TEXT,       // 10 user_context
            OID_VARCHAR,    // 11 shared_network_name
            OID_INT8,       // 12 pool_id
            OID_TIMESTAMP,  // 13 modification_ts
            OID_TEXT,       // 14 client_classes
            OID_INT8,       // 15 pd_pool_id
            OID_INT8,       // 16 subnet_id (of option to update)
            OID_INT2,       // 17 code (of option to update)
            OID_VARCHAR,    // 18 space (of option to update)
            OID_TEXT        // 19 client_classes (of option to update)
        },
        "UPDATE_OPTION6_SUBNET_ID",
        PGSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 1 AND o.dhcp6_subnet_id = $16 AND o.code = $17 AND o.space = $18
                                    AND o.client_classes = $19)
    },

    // Update existing pool level option.
    {
        // PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_POOL_ID,
        19,
        {
            OID_INT2,       //  1 code
            OID_BYTEA,      //  2 value
            OID_TEXT,       //  3 formatted_value
            OID_VARCHAR,    //  4 space
            OID_BOOL,       //  5 persistent
            OID_BOOL,       //  6 cancelled
            OID_VARCHAR,    //  7 dhcp_client_class
            OID_INT8,       //  8 dhcp6_subnet_id
            OID_INT2,       //  9 scope_id
            OID_TEXT,       // 10 user_context
            OID_VARCHAR,    // 11 shared_network_name
            OID_INT8,       // 12 pool_id
            OID_TIMESTAMP,  // 13 modification_ts
            OID_TEXT,       // 14 client_classes
            OID_INT8,       // 15 pd_pool_id
            OID_INT8,       // 16 pool_id (of option to update)
            OID_INT2,       // 17 code (of option to update)
            OID_VARCHAR,    // 18 space (of option to update)
            OID_TEXT        // 19 client_classes (of option to update)
        },
        "UPDATE_OPTION6_POOL_ID",
        PGSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 5 AND o.pool_id = $16 AND o.code = $17 AND o.space = $18
                                    AND o.client_classes = $19)
    },

    // Update existing pd pool level option.
    {
        // PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_PD_POOL_ID,
        19,
        {
            OID_INT2,       //  1 code
            OID_BYTEA,      //  2 value
            OID_TEXT,       //  3 formatted_value
            OID_VARCHAR,    //  4 space
            OID_BOOL,       //  5 persistent
            OID_BOOL,       //  6 cancelled
            OID_VARCHAR,    //  7 dhcp_client_class
            OID_INT8,       //  8 dhcp6_subnet_id
            OID_INT2,       //  9 scope_id
            OID_TEXT,       // 10 user_context
            OID_VARCHAR,    // 11 shared_network_name
            OID_INT8,       // 12 pool_id
            OID_TIMESTAMP,  // 13 modification_ts
            OID_TEXT,       // 14 client_classes
            OID_INT8,       // 15 pd_pool_id
            OID_INT8,       // 16 pd_pool_id (of option to update)
            OID_INT2,       // 17 code (of option to update)
            OID_VARCHAR,    // 18 space (of option to update)
            OID_TEXT        // 19 client_classes (of option to update)
        },
        "UPDATE_OPTION6_PD_POOL_ID",
        PGSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 6 AND o.pd_pool_id = $16 AND o.code = $17 AND o.space = $18
                                    AND o.client_classes = $19)
    },

    // Update existing shared network level option.
    {
        // PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_SHARED_NETWORK,
        19,
        {
            OID_INT2,       //  1 code
            OID_BYTEA,      //  2 value
            OID_TEXT,       //  3 formatted_value
            OID_VARCHAR,    //  4 space
            OID_BOOL,       //  5 persistent
            OID_BOOL,       //  6 cancelled
            OID_VARCHAR,    //  7 dhcp_client_class
            OID_INT8,       //  8 dhcp6_subnet_id
            OID_INT2,       //  9 scope_id
            OID_TEXT,       // 10 user_context
            OID_VARCHAR,    // 11 shared_network_name
            OID_INT8,       // 12 pool_id
            OID_TIMESTAMP,  // 13 modification_ts
            OID_TEXT,       // 14 client_classes
            OID_INT8,       // 15 pd_pool_id
            OID_VARCHAR,    // 16 shared_network_name (of option to update)
            OID_INT2,       // 17 code (of option to update)
            OID_VARCHAR,    // 18 space (of option to update)
            OID_TEXT        // 19 client_classes (of option to update)
        },
        "UPDATE_OPTION6_SHARED_NETWORK",
        PGSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 4 AND o.shared_network_name = $16 AND o.code = $17 AND o.space = $18
                                    AND o.client_classes = $19)
    },

    // Update existing client class level option.
    {
        // PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_CLIENT_CLASS,
        19,
        {
            OID_INT2,       //  1 code
            OID_BYTEA,      //  2 value
            OID_TEXT,       //  3 formatted_value
            OID_VARCHAR,    //  4 space
            OID_BOOL,       //  5 persistent
            OID_BOOL,       //  6 cancelled
            OID_VARCHAR,    //  7 dhcp_client_class
            OID_INT8,       //  8 dhcp6_subnet_id
            OID_INT2,       //  9 scope_id
            OID_TEXT,       // 10 user_context
            OID_VARCHAR,    // 11 shared_network_name
            OID_INT8,       // 12 pool_id
            OID_TIMESTAMP,  // 13 modification_ts
            OID_TEXT,       // 14 client_classes
            OID_INT8,       // 15 pd_pool_id
            OID_VARCHAR,    // 16 client_class (of option to update)
            OID_INT2,       // 17 code (of option to update)
            OID_VARCHAR,    // 18 space (of option to update)
            OID_TEXT        // 19 client_classes (of option to update)
        },
        "UPDATE_OPTION6_CLIENT_CLASS",
        PGSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 2 AND o.dhcp_client_class = $16 AND o.code = $17 AND o.space = $18
                                    AND o.client_classes = $19)
    },

    // Update existing client class with specifying its position.
    {
        // PgSqlConfigBackendDHCPv6Impl::UPDATE_CLIENT_CLASS6,
        14,
        {
            OID_VARCHAR,    //  1 name
            OID_TEXT,       //  2 test
            OID_BOOL,       //  3 only_in_additional_list
            OID_INT8,       //  4 valid_lifetime
            OID_INT8,       //  5 min_valid_lifetime
            OID_INT8,       //  6 max_valid_lifetime
            OID_BOOL,       //  7 depend_on_known_directly
            OID_VARCHAR,    //  8 follow_class_name
            OID_INT8,       //  9 preferred_lifetime
            OID_INT8,       // 10 min_preferred_lifetime
            OID_INT8,       // 11 max_preferred_lifetime
            OID_TIMESTAMP,  // 12 modification_ts
            OID_TEXT,       // 13 user_context
            OID_VARCHAR     // 14 name (of class to update)
        },
        "UPDATE_CLIENT_CLASS6",
        PGSQL_UPDATE_CLIENT_CLASS6("follow_class_name = $8,")
    },

    // Update existing client class without specifying its position.
    {
        // PgSqlConfigBackendDHCPv6Impl::UPDATE_CLIENT_CLASS6_SAME_POSITION,
        14,
        {
            OID_VARCHAR,    //  1 name
            OID_TEXT,       //  2 test
            OID_BOOL,       //  3 only_in_additional_list
            OID_INT8,       //  4 valid_lifetime
            OID_INT8,       //  5 min_valid_lifetime
            OID_INT8,       //  6 max_valid_lifetime
            OID_BOOL,       //  7 depend_on_known_directly
            OID_VARCHAR,    //  8 follow_class_name
            OID_INT8,       //  9 preferred_lifetime
            OID_INT8,       // 10 min_preferred_lifetime
            OID_INT8,       // 11 max_preferred_lifetime
            OID_TIMESTAMP,  // 12 modification_ts
            OID_TEXT,       // 13 user_context
            OID_VARCHAR     // 14 name (of class to update)
        },
        "UPDATE_CLIENT_CLASS6_SAME_POSITION",
        PGSQL_UPDATE_CLIENT_CLASS6("")
    },

    // Update existing server, e.g. server description.
    {
        // PgSqlConfigBackendDHCPv6Impl::UPDATE_SERVER6,
        4,
        {
            OID_VARCHAR,    // 1 tag
            OID_VARCHAR,    // 2 description
            OID_TIMESTAMP,  // 3 modification_ts
            OID_VARCHAR     // 4 tag (of server to update)
        },
        "UPDATE_SERVER6",
        PGSQL_UPDATE_SERVER(dhcp6)
    },

    // Delete global parameter by name.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_GLOBAL_PARAMETER6,
        2,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_VARCHAR     // 2 name of parameter
        },
        "DELETE_GLOBAL_PARAMETER6",
        PGSQL_DELETE_GLOBAL_PARAMETER(dhcp6, AND g.name = $2)
    },

    // Delete all global parameters.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_GLOBAL_PARAMETERS6,
        1,
        {
            OID_VARCHAR // 1 server_tag
        },
        "DELETE_ALL_GLOBAL_PARAMETERS6",
        PGSQL_DELETE_GLOBAL_PARAMETER(dhcp6)
    },

    // Delete all global parameters which are unassigned to any servers.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_GLOBAL_PARAMETERS6_UNASSIGNED,
        0,
        {
            OID_NONE
        },
        "DELETE_ALL_GLOBAL_PARAMETERS6_UNASSIGNED",
        PGSQL_DELETE_GLOBAL_PARAMETER_UNASSIGNED(dhcp6)
    },

    // Delete subnet by id with specifying server tag.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_ID_WITH_TAG,
        2,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_INT8        // 2 subnet_id
        },
        "DELETE_SUBNET6_ID_WITH_TAG",
        PGSQL_DELETE_SUBNET_WITH_TAG(dhcp6, AND s.subnet_id = $2)
    },

    // Delete subnet by id without specifying server tag.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_ID_ANY,
        1,
        {
            OID_INT8    // 1 subnet_id
        },
        "DELETE_SUBNET6_ID_ANY",
        PGSQL_DELETE_SUBNET_ANY(dhcp6, WHERE s.subnet_id = $1)
    },

    // Delete subnet by prefix with specifying server tag.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX_WITH_TAG,
        2,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_VARCHAR     // 2 subnet_prefix
        },
        "DELETE_SUBNET6_PREFIX_WITH_TAG",
        PGSQL_DELETE_SUBNET_WITH_TAG(dhcp6, AND s.subnet_prefix = $2)
    },

    // Delete subnet by prefix without specifying server tag.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX_ANY,
        1,
        {
            OID_VARCHAR // 1 subnet_prefix
        },
        "DELETE_SUBNET6_PREFIX_ANY",
        PGSQL_DELETE_SUBNET_ANY(dhcp6, WHERE s.subnet_prefix = $1)
    },

    // Delete all subnets.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6,
        1,
        {
            OID_VARCHAR // 1 server_tag
        },
        "DELETE_ALL_SUBNETS6",
        PGSQL_DELETE_SUBNET_WITH_TAG(dhcp6)
    },

    // Delete all unassigned subnets.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6_UNASSIGNED,
        0,
        {
            OID_NONE
        },
        "DELETE_ALL_SUBNETS6_UNASSIGNED",
        PGSQL_DELETE_SUBNET_UNASSIGNED(dhcp6)
    },

    // Delete all subnets for a shared network.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6_SHARED_NETWORK_NAME,
        1,
        {
            OID_VARCHAR // 1 shared_network_name
        },
        "DELETE_ALL_SUBNETS6_SHARED_NETWORK_NAME",
        PGSQL_DELETE_SUBNET_ANY(dhcp6, WHERE s.shared_network_name = $1)
    },

    // Delete associations of a subnet with server.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_SERVER,
        1,
        {
            OID_INT8    // 1 subnet_id
        },
        "DELETE_SUBNET6_SERVER",
        PGSQL_DELETE_SUBNET_SERVER(dhcp6),
    },

    // Delete pools for a subnet.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_POOLS6,
        2,
        {
            OID_INT8,   // 1 subnet_id
            OID_VARCHAR // 2 subnet_prefix
        },
        "DELETE_POOLS6",
        PGSQL_DELETE_POOLS(dhcp6)
    },

    // Delete pd pools for a subnet.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_PD_POOLS,
        2,
        {
            OID_INT8,   // 1 subnet_id
            OID_VARCHAR // 2 subnet_prefix
        },
        "DELETE_PD_POOLS",
        PGSQL_DELETE_PD_POOLS()
    },

    // Delete shared network by name with specifying server tag.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_SHARED_NETWORK6_NAME_WITH_TAG,
        2,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_VARCHAR     // 2 shared_network_name
        },
        "DELETE_SHARED_NETWORK6_NAME_WITH_TAG",
        PGSQL_DELETE_SHARED_NETWORK_WITH_TAG(dhcp6, AND n.name = $2)
    },

    // Delete shared network by name without specifying server tag.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_SHARED_NETWORK6_NAME_ANY,
        1,
        {
            OID_VARCHAR // 1 shared_network_name
        },
        "DELETE_SHARED_NETWORK6_NAME_ANY",
        PGSQL_DELETE_SHARED_NETWORK_ANY(dhcp6, WHERE n.name = $1)
    },

    // Delete all shared networks.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_SHARED_NETWORKS6,
        1,
        {
            OID_VARCHAR // 1 server_tag
        },
        "DELETE_ALL_SHARED_NETWORKS6",
        PGSQL_DELETE_SHARED_NETWORK_WITH_TAG(dhcp6)
    },

    // Delete all unassigned shared networks.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_SHARED_NETWORKS6_UNASSIGNED,
        0,
        {
            OID_NONE
        },
        "DELETE_ALL_SHARED_NETWORKS6_UNASSIGNED",
        PGSQL_DELETE_SHARED_NETWORK_UNASSIGNED(dhcp6)
    },

    // Delete associations of a shared network with server.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_SHARED_NETWORK6_SERVER,
        1,
        {
            OID_VARCHAR // 1 shared_network_name
        },
        "DELETE_SHARED_NETWORK6_SERVER",
        PGSQL_DELETE_SHARED_NETWORK_SERVER(dhcp6)
    },

    // Delete option definition.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION_DEF6_CODE_NAME,
        3,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_INT2,       // 2 code
            OID_VARCHAR     // 3 space
        },
        "DELETE_OPTION_DEF6_CODE_NAME",
        PGSQL_DELETE_OPTION_DEF(dhcp6, AND code = $2 AND space = $3)
    },

    // Delete all option definitions.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_OPTION_DEFS6,
        1,
        {
            OID_VARCHAR // 1 server_tag
        },
        "DELETE_ALL_OPTION_DEFS6",
        PGSQL_DELETE_OPTION_DEF(dhcp6)
    },

    // Delete all option definitions which are assigned to no servers.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_OPTION_DEFS6_UNASSIGNED,
        0,
        {
            OID_NONE
        },
        "DELETE_ALL_OPTION_DEFS6_UNASSIGNED",
        PGSQL_DELETE_OPTION_DEF_UNASSIGNED(dhcp6)
    },

    // Delete client class specific option definitions.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION_DEFS6_CLIENT_CLASS,
        1,
        {
            OID_VARCHAR // 1 class name
        },
        "DELETE_OPTION_DEFS6_CLIENT_CLASS",
        PGSQL_DELETE_OPTION_DEFS_CLIENT_CLASS(dhcp6)
    },

    // Delete single global option.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION6,
        4,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_INT2,       // 2 code
            OID_VARCHAR,    // 3 space
            OID_TEXT        // 4 client_classes
        },
        "DELETE_OPTION6",
        PGSQL_DELETE_OPTION_WITH_TAG(dhcp6, AND o.scope_id = 0 AND o.code = $2 AND o.space = $3
                                     AND o.client_classes LIKE $4)
    },

    // Delete all global options which are unassigned to any servers.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_GLOBAL_OPTIONS6_UNASSIGNED,
        0,
        {
            OID_NONE
        },
        "DELETE_ALL_GLOBAL_OPTIONS6_UNASSIGNED",
        PGSQL_DELETE_OPTION_UNASSIGNED(dhcp6, AND o.scope_id = 0)
    },

    // Delete single option from a subnet.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION6_SUBNET_ID,
        4,
        {
            OID_INT8,       // 1 subnet_id
            OID_INT2,       // 2 code
            OID_VARCHAR,    // 3 space
            OID_TEXT        // 4 client_classes
        },
        "DELETE_OPTION6_SUBNET_ID",
        PGSQL_DELETE_OPTION_NO_TAG(dhcp6,
            WHERE o.scope_id = 1 AND o.dhcp6_subnet_id = $1 AND o.code = $2 AND o.space = $3
                  AND o.client_classes LIKE $4)
    },

    // Delete single option from a pool.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION6_POOL_RANGE,
        5,
        {
            OID_TEXT,       // 1 start_address - cast as inet
            OID_TEXT,       // 2 start_address - cast as inet
            OID_INT2,       // 3 code
            OID_VARCHAR,    // 4 space
            OID_TEXT        // 5 client_classes
        },
        "DELETE_OPTION6_POOL_RANGE",
        PGSQL_DELETE_OPTION_POOL_RANGE(dhcp6, o.scope_id = 5 AND o.code = $3 AND o.space = $4
                                       AND o.client_classes LIKE $5)
    },

    // Delete single option from a pd pool.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION6_PD_POOL,
        5,
        {
            OID_TEXT,       // 1 prefix
            OID_INT2,       // 2 prefix_length
            OID_INT2,       // 3 code
            OID_VARCHAR,    // 4 space
            OID_TEXT        // 5 client_classes
        },
        "DELETE_OPTION6_PD_POOL",
        PGSQL_DELETE_OPTION_PD_POOL(o.scope_id = 6 AND o.code = $3 AND o.space = $4
                                    AND o.client_classes LIKE $5)
    },

    // Delete single option from a shared network.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION6_SHARED_NETWORK,
        4,
        {
            OID_VARCHAR,    // 1 shared_network_name
            OID_INT2,       // 2 code
            OID_VARCHAR,    // 3 space
            OID_TEXT        // 4 client_classes
        },
        "DELETE_OPTION6_SHARED_NETWORK",
        PGSQL_DELETE_OPTION_NO_TAG(dhcp6,
            WHERE o.scope_id = 4 AND o.shared_network_name = $1 AND o.code = $2 AND o.space = $3
                  AND o.client_classes LIKE $4)
    },

    // Delete options belonging to a subnet.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_OPTIONS6_SUBNET_ID_PREFIX,
        2,
        {
            OID_INT8,   // 1 subnet_id
            OID_VARCHAR // 2 subnet_prefix
        },
        "DELETE_OPTIONS6_SUBNET_ID_PREFIX",
        PGSQL_DELETE_OPTION_SUBNET_ID_PREFIX(dhcp6)
    },

    // Delete options belonging to a shared_network.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_OPTIONS6_SHARED_NETWORK,
        1,
        {
            OID_VARCHAR // shared_network_name
        },
        "DELETE_OPTIONS6_SHARED_NETWORK",
        PGSQL_DELETE_OPTION_NO_TAG(dhcp6, WHERE o.scope_id = 4 AND o.shared_network_name = $1)
    },

    // Delete options belonging to a client class.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_OPTIONS6_CLIENT_CLASS,
        1,
        {
            OID_VARCHAR // dhcp_client_class
        },
        "DELETE_OPTIONS6_CLIENT_CLASS",
        PGSQL_DELETE_OPTION_NO_TAG(dhcp6, WHERE o.scope_id = 2 AND o.dhcp_client_class = $1)
    },

    // Delete all dependencies of a client class.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_CLIENT_CLASS6_DEPENDENCY,
        1,
        {
            OID_VARCHAR, // 1 class name
        },
        "DELETE_CLIENT_CLASS6_DEPENDENCY",
        PGSQL_DELETE_CLIENT_CLASS_DEPENDENCY(dhcp6)
    },

    // Delete associations of a client class with server.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_CLIENT_CLASS6_SERVER,
        1,
        {
            OID_VARCHAR // 1 class name
        },
        "DELETE_CLIENT_CLASS6_SERVER",
        PGSQL_DELETE_CLIENT_CLASS_SERVER(dhcp6),
    },

    // Delete all client classes.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_CLIENT_CLASSES6,
        1,
        {
            OID_VARCHAR // 1 server_tag
        },
        "DELETE_ALL_CLIENT_CLASSES6",
        PGSQL_DELETE_CLIENT_CLASS_WITH_TAG(dhcp6)
    },

    // Delete all unassigned client classes.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_CLIENT_CLASSES6_UNASSIGNED,
        0,
        {
            OID_NONE
        },
        "DELETE_ALL_CLIENT_CLASSES6_UNASSIGNED",
        PGSQL_DELETE_CLIENT_CLASS_UNASSIGNED(dhcp6)
    },

    // Delete specified client class.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_CLIENT_CLASS6,
        2,
        {
            OID_VARCHAR,    // 1 server_tag
            OID_VARCHAR     // 2 name
        },
        "DELETE_CLIENT_CLASS6",
        PGSQL_DELETE_CLIENT_CLASS_WITH_TAG(dhcp6, AND name = $2)
    },

    // Delete any client class with a given name.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_CLIENT_CLASS6_ANY,
        1,
        {
            OID_VARCHAR     // 1 name
        },
        "DELETE_CLIENT_CLASS6_ANY",
        PGSQL_DELETE_CLIENT_CLASS_ANY(dhcp6, AND name = $1)
    },

    // Delete a server by tag.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_SERVER6,
        1,
        {
            OID_VARCHAR // server_tag
        },
        "DELETE_SERVER6",
        PGSQL_DELETE_SERVER(dhcp6)
    },

    // Deletes all servers except logical server 'all'.
    {
        // PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_SERVERS6,
        0,
        {
            OID_NONE
        },
        "DELETE_ALL_SERVERS6",
        PGSQL_DELETE_ALL_SERVERS(dhcp6)
    },

    // Fetches the last sequence id for the given table and column.
    {
        // PgSqlConfigBackendDHCPv6Impl::GET_LAST_INSERT_ID6,
        // args are: table name, sequence column name
        2,
        {
            OID_VARCHAR,
            OID_VARCHAR
        },
        "GET_LAST_INSERT_ID6",
        "SELECT CURRVAL(PG_GET_SERIAL_SEQUENCE($1, $2))"
    }
}
};

} // end anonymous namespace

PgSqlConfigBackendDHCPv6Impl::PgSqlConfigBackendDHCPv6Impl(const DatabaseConnection::ParameterMap& parameters)
    : PgSqlConfigBackendImpl(std::string(cStringDhcpSpace<DHCPv6>()), parameters,
                             &PgSqlConfigBackendDHCPv6Impl::dbReconnect,
      PgSqlConfigBackendDHCPv6Impl::GET_LAST_INSERT_ID6) {
    // Prepare query statements. Those are will be only used to retrieve
    // information from the database, so they can be used even if the
    // database is read only for the current user.
    conn_.prepareStatements(tagged_statements.begin(),
                            tagged_statements.end());
// @todo As part of enabling read-only CB access, statements need to
// be limited:
//                            tagged_statements.begin() + WRITE_STMTS_BEGIN);
}

PgSqlConfigBackendDHCPv6Impl::~PgSqlConfigBackendDHCPv6Impl() {
}

PgSqlTaggedStatement&
PgSqlConfigBackendDHCPv6Impl::getStatement(size_t index) const {
    if (index >= tagged_statements.size()) {
        isc_throw(BadValue, "PgSqlConfigBackendDHCPv6Impl::getStatement index: "
                  << index << ", is invalid");
    }

    return(tagged_statements[index]);
}

PgSqlConfigBackendDHCPv6::PgSqlConfigBackendDHCPv6(const DatabaseConnection::ParameterMap& parameters)
    : impl_(new PgSqlConfigBackendDHCPv6Impl(parameters)), base_impl_(impl_) {
}

bool
PgSqlConfigBackendDHCPv6::isUnusable() {
    return (impl_->conn_.isUnusable());
}

DatabaseConnection::ParameterMap
PgSqlConfigBackendDHCPv6::getParameters() const {
    return (impl_->getParameters());
}

Subnet6Ptr
PgSqlConfigBackendDHCPv6::getSubnet6(const ServerSelector& server_selector,
                                     const std::string& subnet_prefix) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_SUBNET6_BY_PREFIX)
        .arg(subnet_prefix);
    return (impl_->getSubnet6(server_selector, subnet_prefix));
}

Subnet6Ptr
PgSqlConfigBackendDHCPv6::getSubnet6(const ServerSelector& server_selector,
                                     const SubnetID& subnet_id) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_SUBNET6_BY_SUBNET_ID)
        .arg(subnet_id);
    return (impl_->getSubnet6(server_selector, subnet_id));
}

Subnet6Collection
PgSqlConfigBackendDHCPv6::getAllSubnets6(const ServerSelector& server_selector) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_SUBNETS6);
    Subnet6Collection subnets;
    impl_->getAllSubnets6(server_selector, subnets);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_SUBNETS6_RESULT)
        .arg(subnets.size());
    return (subnets);
}

Subnet6Collection
PgSqlConfigBackendDHCPv6::getModifiedSubnets6(const ServerSelector& server_selector,
                                              const boost::posix_time::ptime& modification_time) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_SUBNETS6)
        .arg(util::ptimeToText(modification_time));
    Subnet6Collection subnets;
    impl_->getModifiedSubnets6(server_selector, modification_time, subnets);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_SUBNETS6_RESULT)
        .arg(subnets.size());
    return (subnets);
}

Subnet6Collection
PgSqlConfigBackendDHCPv6::getSharedNetworkSubnets6(const ServerSelector& /* server_selector */,
                                                   const std::string& shared_network_name) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_SHARED_NETWORK_SUBNETS6)
        .arg(shared_network_name);
    Subnet6Collection subnets;
    impl_->getSharedNetworkSubnets6(ServerSelector::ANY(), shared_network_name, subnets);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_SHARED_NETWORK_SUBNETS6_RESULT)
        .arg(subnets.size());
    return (subnets);
}

SharedNetwork6Ptr
PgSqlConfigBackendDHCPv6::getSharedNetwork6(const ServerSelector& server_selector,
                                            const std::string& name) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_SHARED_NETWORK6)
        .arg(name);
    return (impl_->getSharedNetwork6(server_selector, name));
}

SharedNetwork6Collection
PgSqlConfigBackendDHCPv6::getAllSharedNetworks6(const ServerSelector& server_selector) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_SHARED_NETWORKS6);
    SharedNetwork6Collection shared_networks;
    impl_->getAllSharedNetworks6(server_selector, shared_networks);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_SHARED_NETWORKS6_RESULT)
        .arg(shared_networks.size());
    return (shared_networks);
}

SharedNetwork6Collection
PgSqlConfigBackendDHCPv6::getModifiedSharedNetworks6(const ServerSelector& server_selector,
        const boost::posix_time::ptime& modification_time) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_SHARED_NETWORKS6)
        .arg(util::ptimeToText(modification_time));
    SharedNetwork6Collection shared_networks;
    impl_->getModifiedSharedNetworks6(server_selector, modification_time, shared_networks);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_SHARED_NETWORKS6_RESULT)
        .arg(shared_networks.size());
    return (shared_networks);
}

OptionDefinitionPtr
PgSqlConfigBackendDHCPv6::getOptionDef6(const ServerSelector& server_selector,
                                        const uint16_t code,
                                        const std::string& space) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_OPTION_DEF6)
        .arg(code).arg(space);
    return (impl_->getOptionDef(PgSqlConfigBackendDHCPv6Impl::GET_OPTION_DEF6_CODE_SPACE,
                                server_selector, code, space));
}

OptionDefContainer
PgSqlConfigBackendDHCPv6::getAllOptionDefs6(const ServerSelector& server_selector) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_OPTION_DEFS6);
    OptionDefContainer option_defs;
    impl_->getAllOptionDefs(PgSqlConfigBackendDHCPv6Impl::GET_ALL_OPTION_DEFS6,
                            server_selector, option_defs);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_OPTION_DEFS6_RESULT)
        .arg(option_defs.size());
    return (option_defs);
}

OptionDefContainer
PgSqlConfigBackendDHCPv6::getModifiedOptionDefs6(const ServerSelector& server_selector,
        const boost::posix_time::ptime& modification_time) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_OPTION_DEFS6)
        .arg(util::ptimeToText(modification_time));
    OptionDefContainer option_defs;
    impl_->getModifiedOptionDefs(PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_OPTION_DEFS6,
                                 server_selector, modification_time, option_defs);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_OPTION_DEFS6_RESULT)
        .arg(option_defs.size());
    return (option_defs);
}

OptionDescriptorPtr
PgSqlConfigBackendDHCPv6::getOption6(const ServerSelector& server_selector,
                                     const uint16_t code,
                                     const std::string& space,
                                     const ClientClassesPtr client_classes) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_OPTION6)
        .arg(code).arg(space);
    return (impl_->getOption(PgSqlConfigBackendDHCPv6Impl::GET_OPTION6_CODE_SPACE,
                             Option::V6, server_selector, code, space, client_classes));
}

OptionContainer
PgSqlConfigBackendDHCPv6::getAllOptions6(const ServerSelector& server_selector) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_OPTIONS6);
    OptionContainer options = impl_->getAllOptions(PgSqlConfigBackendDHCPv6Impl::GET_ALL_OPTIONS6,
            Option::V6, server_selector);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_OPTIONS6_RESULT)
        .arg(options.size());
    return (options);
}

OptionContainer
PgSqlConfigBackendDHCPv6::getModifiedOptions6(const ServerSelector& server_selector,
        const boost::posix_time::ptime& modification_time) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_OPTIONS6)
        .arg(util::ptimeToText(modification_time));
    OptionContainer options = impl_->getModifiedOptions(PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_OPTIONS6,
            Option::V6, server_selector, modification_time);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_OPTIONS6_RESULT)
        .arg(options.size());
    return (options);
}

StampedValuePtr
PgSqlConfigBackendDHCPv6::getGlobalParameter6(const ServerSelector& server_selector,
                                              const std::string& name) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_GLOBAL_PARAMETER6)
        .arg(name);
    return (impl_->getGlobalParameter6(server_selector, name));
}

StampedValueCollection
PgSqlConfigBackendDHCPv6::getAllGlobalParameters6(const ServerSelector& server_selector) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS6);
    StampedValueCollection parameters;
    auto const& tags = server_selector.getTags();
    for (auto const& tag : tags) {
        PsqlBindArray in_bindings;
        in_bindings.addTempString(tag.get());
        impl_->getGlobalParameters(PgSqlConfigBackendDHCPv6Impl::GET_ALL_GLOBAL_PARAMETERS6,
                                   in_bindings, parameters);
    }
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS6_RESULT)
        .arg(parameters.size());
    return (parameters);
}

StampedValueCollection
PgSqlConfigBackendDHCPv6::getModifiedGlobalParameters6(const db::ServerSelector& server_selector,
        const boost::posix_time::ptime& modification_time) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS6)
        .arg(util::ptimeToText(modification_time));
    StampedValueCollection parameters;
    auto const& tags = server_selector.getTags();
    for (auto const& tag : tags) {
        PsqlBindArray in_bindings;
        in_bindings.addTempString(tag.get());
        in_bindings.addTimestamp(modification_time);

        impl_->getGlobalParameters(PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_GLOBAL_PARAMETERS6,
                                   in_bindings, parameters);
    }
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS6_RESULT)
        .arg(parameters.size());
    return (parameters);
}

ClientClassDefPtr
PgSqlConfigBackendDHCPv6::getClientClass6(const db::ServerSelector& server_selector,
                                          const std::string& name) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_CLIENT_CLASS6)
        .arg(name);
    return (impl_->getClientClass6(server_selector, name));
}

ClientClassDictionary
PgSqlConfigBackendDHCPv6::getAllClientClasses6(const db::ServerSelector& server_selector) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_CLIENT_CLASSES6);
    ClientClassDictionary client_classes;
    impl_->getAllClientClasses6(server_selector, client_classes);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_CLIENT_CLASSES6_RESULT)
        .arg(client_classes.getClasses()->size());
    return (client_classes);
}

ClientClassDictionary
PgSqlConfigBackendDHCPv6::getModifiedClientClasses6(const db::ServerSelector& server_selector,
                                                    const boost::posix_time::ptime& modification_time) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES6)
        .arg(util::ptimeToText(modification_time));
    ClientClassDictionary client_classes;
    impl_->getModifiedClientClasses6(server_selector, modification_time, client_classes);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES6_RESULT)
        .arg(client_classes.getClasses()->size());
    return (client_classes);
}

AuditEntryCollection
PgSqlConfigBackendDHCPv6::getRecentAuditEntries(const db::ServerSelector& server_selector,
        const boost::posix_time::ptime& modification_time,
        const uint64_t& modification_id) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_RECENT_AUDIT_ENTRIES6)
      .arg(util::ptimeToText(modification_time))
      .arg(modification_id);
    AuditEntryCollection audit_entries;
    impl_->getRecentAuditEntries(PgSqlConfigBackendDHCPv6Impl::GET_AUDIT_ENTRIES6_TIME,
                                 server_selector, modification_time,
                                 modification_id, audit_entries);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_RECENT_AUDIT_ENTRIES6_RESULT)
        .arg(audit_entries.size());
    return (audit_entries);
}

ServerCollection
PgSqlConfigBackendDHCPv6::getAllServers6() const {
    ServerCollection servers;

    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_SERVERS6);
    impl_->getAllServers(PgSqlConfigBackendDHCPv6Impl::GET_ALL_SERVERS6,
                         servers);

    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_SERVERS6_RESULT)
        .arg(servers.size());
    return (servers);
}

ServerPtr
PgSqlConfigBackendDHCPv6::getServer6(const data::ServerTag& server_tag) const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_SERVER6)
        .arg(server_tag.get());
    return (impl_->getServer(PgSqlConfigBackendDHCPv6Impl::GET_SERVER6, server_tag));
}

void
PgSqlConfigBackendDHCPv6::createUpdateSubnet6(const ServerSelector& server_selector,
                                              const Subnet6Ptr& subnet) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_SUBNET6)
        .arg(subnet);
    impl_->createUpdateSubnet6(server_selector, subnet);
}

void
PgSqlConfigBackendDHCPv6::createUpdateSharedNetwork6(const ServerSelector& server_selector,
                                                     const SharedNetwork6Ptr& shared_network) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_SHARED_NETWORK6)
        .arg(shared_network->getName());
    impl_->createUpdateSharedNetwork6(server_selector, shared_network);
}

void
PgSqlConfigBackendDHCPv6::createUpdateOptionDef6(const ServerSelector& server_selector,
                                                 const OptionDefinitionPtr& option_def) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_OPTION_DEF6)
        .arg(option_def->getName()).arg(option_def->getCode());
    impl_->createUpdateOptionDef6(server_selector, option_def);
}

void
PgSqlConfigBackendDHCPv6::createUpdateOption6(const ServerSelector& server_selector,
                                              const OptionDescriptorPtr& option) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_OPTION6);
    impl_->createUpdateOption6(server_selector, option);
}

void
PgSqlConfigBackendDHCPv6::createUpdateOption6(const db::ServerSelector& server_selector,
                                              const std::string& shared_network_name,
                                              const OptionDescriptorPtr& option) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_SHARED_NETWORK_OPTION6)
        .arg(shared_network_name);
    impl_->createUpdateOption6(server_selector, shared_network_name, option, false);
}

void
PgSqlConfigBackendDHCPv6::createUpdateOption6(const ServerSelector& server_selector,
                                              const SubnetID& subnet_id,
                                              const OptionDescriptorPtr& option) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_BY_SUBNET_ID_OPTION6)
        .arg(subnet_id);
    impl_->createUpdateOption6(server_selector, subnet_id, option, false);
}

void
PgSqlConfigBackendDHCPv6::createUpdateOption6(const ServerSelector& server_selector,
                                              const asiolink::IOAddress& pool_start_address,
                                              const asiolink::IOAddress& pool_end_address,
                                              const OptionDescriptorPtr& option) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_BY_POOL_OPTION6)
        .arg(pool_start_address.toText()).arg(pool_end_address.toText());
    impl_->createUpdateOption6(server_selector, pool_start_address, pool_end_address,
                               option);
}

void
PgSqlConfigBackendDHCPv6::createUpdateOption6(const ServerSelector& server_selector,
                                              const asiolink::IOAddress& pd_pool_prefix,
                                              const uint8_t pd_pool_prefix_length,
                                              const OptionDescriptorPtr& option) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_BY_PREFIX_OPTION6)
        .arg(pd_pool_prefix.toText()).arg(pd_pool_prefix_length);
    impl_->createUpdateOption6(server_selector, pd_pool_prefix,
                               pd_pool_prefix_length, option);
}

void
PgSqlConfigBackendDHCPv6::createUpdateGlobalParameter6(const ServerSelector& server_selector,
                                                       const StampedValuePtr& value) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_GLOBAL_PARAMETER6)
        .arg(value->getName());
    impl_->createUpdateGlobalParameter6(server_selector, value);
}

void
PgSqlConfigBackendDHCPv6::createUpdateClientClass6(const db::ServerSelector& server_selector,
                                                   const ClientClassDefPtr& client_class,
                                                   const std::string& follow_class_name) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_CLIENT_CLASS6)
        .arg(client_class->getName());
    impl_->createUpdateClientClass6(server_selector, client_class, follow_class_name);
}

void
PgSqlConfigBackendDHCPv6::createUpdateServer6(const ServerPtr& server) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_SERVER6)
        .arg(server->getServerTagAsText());
    impl_->createUpdateServer(PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
                              PgSqlConfigBackendDHCPv6Impl::INSERT_SERVER6,
                              PgSqlConfigBackendDHCPv6Impl::UPDATE_SERVER6,
                              server);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteSubnet6(const ServerSelector& server_selector,
                                        const std::string& subnet_prefix) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_PREFIX_SUBNET6)
        .arg(subnet_prefix);
    uint64_t result = impl_->deleteSubnet6(server_selector, subnet_prefix);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_PREFIX_SUBNET6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteSubnet6(const ServerSelector& server_selector,
                                        const SubnetID& subnet_id) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET6)
        .arg(subnet_id);
    uint64_t result = impl_->deleteSubnet6(server_selector, subnet_id);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteAllSubnets6(const ServerSelector& server_selector) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_SUBNETS6);

    int index = (server_selector.amUnassigned() ?
                 PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6_UNASSIGNED :
                 PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6);
    uint64_t result = impl_->deleteTransactional(index, server_selector, "deleting all subnets",
                                                 "deleted all subnets", true);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_SUBNETS6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteSharedNetworkSubnets6(const db::ServerSelector& server_selector,
                                                      const std::string& shared_network_name) {
    if (!server_selector.amAny()) {
        isc_throw(InvalidOperation, "deleting all subnets from a shared "
                  "network requires using ANY server selector");
    }
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS6)
        .arg(shared_network_name);
    uint64_t result = impl_->deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6_SHARED_NETWORK_NAME,
                                                 server_selector,
                                                 "deleting all subnets for a shared network",
                                                 "deleted all subnets for a shared network",
                                                 true, shared_network_name);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteSharedNetwork6(const ServerSelector& server_selector,
                                               const std::string& name) {
    /// @todo Using UNASSIGNED selector is allowed by the CB API but we don't have
    /// dedicated query for this at the moment. The user should use ANY to delete
    /// the shared network by name.
    if (server_selector.amUnassigned()) {
        isc_throw(NotImplemented, "deleting an unassigned shared network requires "
                  "an explicit server tag or using ANY server. The UNASSIGNED server "
                  "selector is currently not supported");
    }

    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SHARED_NETWORK6)
        .arg(name);

    int index = (server_selector.amAny() ?
                 PgSqlConfigBackendDHCPv6Impl::DELETE_SHARED_NETWORK6_NAME_ANY :
                 PgSqlConfigBackendDHCPv6Impl::DELETE_SHARED_NETWORK6_NAME_WITH_TAG);
    uint64_t result = impl_->deleteTransactional(index, server_selector,
                                                 "deleting a shared network",
                                                 "shared network deleted", true, name);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SHARED_NETWORK6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteAllSharedNetworks6(const ServerSelector& server_selector) {
    if (server_selector.amAny()) {
        isc_throw(InvalidOperation, "deleting all shared networks for ANY server is not"
                  " supported");
    }

    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_SHARED_NETWORKS6);

    int index = (server_selector.amUnassigned() ?
                 PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_SHARED_NETWORKS6_UNASSIGNED :
                 PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_SHARED_NETWORKS6);
    uint64_t result = impl_->deleteTransactional(index, server_selector, "deleting all shared networks",
                                                 "deleted all shared networks", true);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_SHARED_NETWORKS6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteOptionDef6(const ServerSelector& server_selector,
                                           const uint16_t code,
                                           const std::string& space,
                                           bool force /* = false */) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_OPTION_DEF6)
        .arg(code).arg(space);
    uint64_t result = impl_->deleteOptionDef6(server_selector, code, space, force);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_OPTION_DEF6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteAllOptionDefs6(const ServerSelector& server_selector) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_OPTION_DEFS6);
    uint64_t result = impl_->deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_OPTION_DEFS6,
                                                 server_selector, "deleting all option definitions",
                                                 "deleted all option definitions", true);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_OPTION_DEFS6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& server_selector,
                                        const uint16_t code,
                                        const std::string& space,
                                        const ClientClassesPtr client_classes) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_OPTION6)
        .arg(code).arg(space);
    uint64_t result = impl_->deleteOption6(server_selector, code, space, client_classes);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_OPTION6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& /* server_selector */,
                                        const std::string& shared_network_name,
                                        const uint16_t code,
                                        const std::string& space,
                                        const ClientClassesPtr client_classes) {
    /// @todo In the future we might use the server selector to make sure that the
    /// option is only deleted if the pool belongs to a given server. For now, we
    /// just delete it when there is a match with the parent object.
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SHARED_NETWORK_OPTION6)
        .arg(shared_network_name).arg(code).arg(space);
    uint64_t result = impl_->deleteOption6(ServerSelector::ANY(), shared_network_name,
                                           code, space, client_classes);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SHARED_NETWORK_OPTION6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& /* server_selector */,
                                        const SubnetID& subnet_id,
                                        const uint16_t code,
                                        const std::string& space,
                                        const ClientClassesPtr client_classes) {
    /// @todo In the future we might use the server selector to make sure that the
    /// option is only deleted if the pool belongs to a given server. For now, we
    /// just delete it when there is a match with the parent object.
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_SUBNET_ID_OPTION6)
        .arg(subnet_id).arg(code).arg(space);
    uint64_t result = impl_->deleteOption6(ServerSelector::ANY(), subnet_id, code, space,
                                           client_classes);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_SUBNET_ID_OPTION6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& /* server_selector */,
                                        const asiolink::IOAddress& pool_start_address,
                                        const asiolink::IOAddress& pool_end_address,
                                        const uint16_t code,
                                        const std::string& space,
                                        const ClientClassesPtr client_classes) {
    /// @todo In the future we might use the server selector to make sure that the
    /// option is only deleted if the pool belongs to a given server. For now, we
    /// just delete it when there is a match with the parent object.
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_POOL_OPTION6)
        .arg(pool_start_address.toText()).arg(pool_end_address.toText()).arg(code).arg(space);
    uint64_t result = impl_->deleteOption6(ServerSelector::ANY(), pool_start_address,
                                           pool_end_address, code, space, client_classes);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_POOL_OPTION6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& /* server_selector */,
                                        const asiolink::IOAddress& pd_pool_prefix,
                                        const uint8_t pd_pool_prefix_length,
                                        const uint16_t code,
                                        const std::string& space,
                                        const ClientClassesPtr client_classes) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_POOL_PREFIX_OPTION6)
        .arg(pd_pool_prefix.toText()).arg(pd_pool_prefix_length).arg(code).arg(space);
    uint64_t result = impl_->deleteOption6(ServerSelector::ANY(), pd_pool_prefix,
                                           pd_pool_prefix_length, code, space, client_classes);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_POOL_PREFIX_OPTION6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteGlobalParameter6(const ServerSelector& server_selector,
                                                 const std::string& name) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_GLOBAL_PARAMETER6)
        .arg(name);
    uint64_t result = impl_->deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_GLOBAL_PARAMETER6,
                                                 server_selector, "deleting global parameter",
                                                 "global parameter deleted", false, name);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_GLOBAL_PARAMETER6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteAllGlobalParameters6(const ServerSelector& server_selector) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS6);
    uint64_t result = impl_->deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_GLOBAL_PARAMETERS6,
                                                 server_selector, "deleting all global parameters",
                                                 "all global parameters deleted", true);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteClientClass6(const db::ServerSelector& server_selector,
                                             const std::string& name) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_CLIENT_CLASS6)
        .arg(name);
    auto result = impl_->deleteClientClass6(server_selector, name);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_CLIENT_CLASS6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteAllClientClasses6(const db::ServerSelector& server_selector) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_CLIENT_CLASSES6);

    int index = (server_selector.amUnassigned() ?
                 PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_CLIENT_CLASSES6_UNASSIGNED :
                 PgSqlConfigBackendDHCPv6Impl::DELETE_ALL_CLIENT_CLASSES6);
    uint64_t result = impl_->deleteTransactional(index, server_selector, "deleting all client classes",
                                                 "deleted all client classes", true);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_CLIENT_CLASSES6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteServer6(const ServerTag& server_tag) {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SERVER6)
        .arg(server_tag.get());
    uint64_t result = impl_->deleteServer6(server_tag);
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SERVER6_RESULT)
        .arg(result);
    return (result);
}

uint64_t
PgSqlConfigBackendDHCPv6::deleteAllServers6() {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_SERVERS6);
    uint64_t result = impl_->deleteAllServers6();
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_SERVERS6_RESULT)
        .arg(result);
    return (result);
}

std::string
PgSqlConfigBackendDHCPv6::getType() const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_TYPE6);
    return (impl_->getType());
}

std::string
PgSqlConfigBackendDHCPv6::getHost() const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_HOST6);
    return (impl_->getHost());
}

uint16_t
PgSqlConfigBackendDHCPv6::getPort() const {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_PORT6);
    return (impl_->getPort());
}

bool
PgSqlConfigBackendDHCPv6::registerBackendType() {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_REGISTER_BACKEND_TYPE6);
    return (
        dhcp::ConfigBackendDHCPv6Mgr::instance().registerBackendFactory("postgresql",
            [](const db::DatabaseConnection::ParameterMap& params) -> dhcp::ConfigBackendDHCPv6Ptr {
            return (dhcp::PgSqlConfigBackendDHCPv6Ptr(new dhcp::PgSqlConfigBackendDHCPv6(params)));
        })
    );
}

void
PgSqlConfigBackendDHCPv6::unregisterBackendType() {
    LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_UNREGISTER_BACKEND_TYPE6);
    dhcp::ConfigBackendDHCPv6Mgr::instance().unregisterBackendFactory("postgresql");
}

} // end of namespace isc::dhcp
} // end of namespace isc
