]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3583] PgSQL CB V4 and V6 support option class tagging
authorThomas Markwalder <tmark@isc.org>
Wed, 2 Oct 2024 18:55:34 +0000 (14:55 -0400)
committerThomas Markwalder <tmark@isc.org>
Tue, 15 Oct 2024 17:51:57 +0000 (13:51 -0400)
/src/hooks/dhcp/pgsql_cb/pgsql_cb_dhcp4.cc
/src/hooks/dhcp/pgsql_cb/pgsql_cb_dhcp6.cc
/src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.cc
/src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.h
/src/hooks/dhcp/pgsql_cb/pgsql_query_macros_dhcp.h

src/hooks/dhcp/mysql/mysql_cb_impl.cc
src/hooks/dhcp/mysql/mysql_cb_impl.h
src/hooks/dhcp/pgsql/pgsql_cb_dhcp4.cc
src/hooks/dhcp/pgsql/pgsql_cb_dhcp6.cc
src/hooks/dhcp/pgsql/pgsql_cb_impl.cc
src/hooks/dhcp/pgsql/pgsql_cb_impl.h
src/hooks/dhcp/pgsql/pgsql_query_macros_dhcp.h

index 1fdbbfd0029c6a49cca3d9377a57a24ee0ea5605..c604c6045a15e0287f4cbac5644fffedea9394de 100644 (file)
@@ -1128,5 +1128,20 @@ MySqlConfigBackendImpl::getPort() const {
     return (0);
 }
 
+db::MySqlBindingPtr 
+MySqlConfigBackendImpl::createInputClientClassesBinding(const ClientClasses& client_classes) {
+    if (client_classes.empty()) {
+        return(db::MySqlBinding::createNull());
+    }
+
+    // Create JSON list of client classes.
+    data::ElementPtr client_classes_element = data::Element::createList();
+    for (auto const& client_class : client_classes) {
+        client_classes_element->add(data::Element::create(client_class));
+    }
+
+    return (db::MySqlBinding::createString(client_classes_element->str()));
+}
+
 } // end of namespace isc::dhcp
 } // end of namespace isc
index 19e88f110d524a66983c640527125f04257b3ac4..4a76c6e273cf1d6a2940b80479fa5176cfe5bded 100644 (file)
@@ -648,19 +648,7 @@ public:
     /// @param client_classes ClientClasses collection containing the class names
     /// @return Pointer to the binding (possibly null binding if there are no
     /// classes specified).
-    db::MySqlBindingPtr createInputClientClassesBinding(const ClientClasses& client_classes) {
-        if (client_classes.empty()) {
-            return(db::MySqlBinding::createNull());
-        }
-
-        // Create JSON list of client classes.
-        data::ElementPtr client_classes_element = data::Element::createList();
-        for (auto const& client_class : client_classes) {
-            client_classes_element->add(data::Element::create(client_class));
-        }
-
-        return (db::MySqlBinding::createString(client_classes_element->str()));
-    }
+    db::MySqlBindingPtr createInputClientClassesBinding(const ClientClasses& client_classes);
 
     /// @brief Creates input binding for user context parameter.
     ///
index 5b6988aaef8e1d4c5a36ace4ac2e90cc1ec36f83..5a2d14715020271ac04e1a91d939fe667ae70442 100644 (file)
@@ -325,9 +325,9 @@ public:
                 auto rebind_timer = worker.getTriplet(11);
 
                 // valid_lifetime at 19.
-                // min_valid_lifetime at 55.
-                // max_valid_lifetime at 56.
-                auto valid_lifetime = worker.getTriplet(19, 55, 56);
+                // min_valid_lifetime at 57.
+                // max_valid_lifetime at 58.
+                auto valid_lifetime = worker.getTriplet(19, 57, 58);
 
                 // Create subnet with basic settings.
                 last_subnet = Subnet4::create(prefix_pair.first, prefix_pair.second,
@@ -423,96 +423,96 @@ public:
 
                 // valid_lifetime at 19 (fetched before subnet create).
 
-                // pool and options from 20 to 50.
+                // pool and options from 20 to 52.
 
-                // calculate_tee_times at 51.
-                if (!worker.isColumnNull(51)) {
-                    last_subnet->setCalculateTeeTimes(worker.getBool(51));
+                // calculate_tee_times at 53.
+                if (!worker.isColumnNull(53)) {
+                    last_subnet->setCalculateTeeTimes(worker.getBool(53));
                 }
 
-                // t1_percent at 52.
-                if (!worker.isColumnNull(52)) {
-                    last_subnet->setT1Percent(worker.getDouble(52));
+                // t1_percent at 54.
+                if (!worker.isColumnNull(54)) {
+                    last_subnet->setT1Percent(worker.getDouble(54));
                 }
 
-                // t2_percent at 53.
-                if (!worker.isColumnNull(53)) {
-                    last_subnet->setT2Percent(worker.getDouble(53));
+                // t2_percent at 55.
+                if (!worker.isColumnNull(55)) {
+                    last_subnet->setT2Percent(worker.getDouble(55));
                 }
 
-                // authoritative at 54.
-                if (!worker.isColumnNull(54)) {
-                    last_subnet->setAuthoritative(worker.getBool(54));
+                // authoritative at 56.
+                if (!worker.isColumnNull(56)) {
+                    last_subnet->setAuthoritative(worker.getBool(56));
                 }
 
-                // min_valid_lifetime at 55 (fetched as part of triplet).
-                // max_valid_lifetime at 56 (fetched as part of triplet).
+                // min_valid_lifetime at 57 (fetched as part of triplet).
+                // max_valid_lifetime at 58 (fetched as part of triplet).
 
                 // pool client_class, require_client_classes and user_context
-                // from 57 to 59.
+                // from 59 to 61.
 
-                // ddns_send_updates at 60.
-                if (!worker.isColumnNull(60)) {
-                    last_subnet->setDdnsSendUpdates(worker.getBool(60));
-                }
-
-                // ddns_override_no_update at 61.
-                if (!worker.isColumnNull(61)) {
-                    last_subnet->setDdnsOverrideNoUpdate(worker.getBool(61));
-                }
-
-                // ddns_override_client_update at 62.
+                // ddns_send_updates at 62.
                 if (!worker.isColumnNull(62)) {
-                    last_subnet->setDdnsOverrideClientUpdate(worker.getBool(62));
+                    last_subnet->setDdnsSendUpdates(worker.getBool(62));
                 }
 
-                // ddns_replace_client_name at 63.
+                // ddns_override_no_update at 63.
                 if (!worker.isColumnNull(63)) {
-                    last_subnet->setDdnsReplaceClientNameMode(
-                        static_cast<D2ClientConfig::ReplaceClientNameMode>(worker.getSmallInt(63)));
+                    last_subnet->setDdnsOverrideNoUpdate(worker.getBool(63));
                 }
 
-                // ddns_generated_prefix at 64.
+                // ddns_override_client_update at 64.
                 if (!worker.isColumnNull(64)) {
-                    last_subnet->setDdnsGeneratedPrefix(worker.getString(64));
+                    last_subnet->setDdnsOverrideClientUpdate(worker.getBool(64));
                 }
 
-                // ddns_qualifying_suffix at 65.
+                // ddns_replace_client_name at 65.
                 if (!worker.isColumnNull(65)) {
-                    last_subnet->setDdnsQualifyingSuffix(worker.getString(65));
+                    last_subnet->setDdnsReplaceClientNameMode(
+                        static_cast<D2ClientConfig::ReplaceClientNameMode>(worker.getSmallInt(65)));
                 }
 
-                // reservations_in_subnet at 66.
+                // ddns_generated_prefix at 66.
                 if (!worker.isColumnNull(66)) {
-                    last_subnet->setReservationsInSubnet(worker.getBool(66));
+                    last_subnet->setDdnsGeneratedPrefix(worker.getString(66));
                 }
 
-                // reservations_out_of_pool at 67.
+                // ddns_qualifying_suffix at 67.
                 if (!worker.isColumnNull(67)) {
-                    last_subnet->setReservationsOutOfPool(worker.getBool(67));
+                    last_subnet->setDdnsQualifyingSuffix(worker.getString(67));
                 }
 
-                // cache_threshold at 68.
+                // reservations_in_subnet at 68.
                 if (!worker.isColumnNull(68)) {
-                    last_subnet->setCacheThreshold(worker.getDouble(68));
+                    last_subnet->setReservationsInSubnet(worker.getBool(68));
                 }
 
-                // cache_max_age at 69.
+                // reservations_out_of_pool at 69.
                 if (!worker.isColumnNull(69)) {
-                    last_subnet->setCacheMaxAge(worker.getInt(69));
+                    last_subnet->setReservationsOutOfPool(worker.getBool(69));
                 }
 
-                // offer_lifetime at 70.
+                // cache_threshold at 70.
                 if (!worker.isColumnNull(70)) {
-                    last_subnet->setOfferLft(worker.getInt(70));
+                    last_subnet->setCacheThreshold(worker.getDouble(70));
                 }
 
-                // allocator at 71.
+                // cache_max_age at 71.
                 if (!worker.isColumnNull(71)) {
-                    last_subnet->setAllocatorType(worker.getString(71));
+                    last_subnet->setCacheMaxAge(worker.getInt(71));
+                }
+
+                // offer_lifetime at 72.
+                if (!worker.isColumnNull(72)) {
+                    last_subnet->setOfferLft(worker.getInt(72));
+                }
+
+                // allocator at 73.
+                if (!worker.isColumnNull(73)) {
+                    last_subnet->setAllocatorType(worker.getString(73));
                 }
 
-                // server_tag at 72.
+                // server_tag at 74.
 
                 // Subnet ready. Add it to the list.
                 auto ret = subnets.insert(last_subnet);
@@ -525,9 +525,9 @@ public:
                 }
             }
 
-            // Check for new server tags at 71.
-            if (!worker.isColumnNull(72)) {
-                std::string new_tag = worker.getString(72);
+            // Check for new server tags at 74.
+            if (!worker.isColumnNull(74)) {
+                std::string new_tag = worker.getString(74);
                 if (last_tag != new_tag) {
                     if (!new_tag.empty() && !last_subnet->hasServerTag(ServerTag(new_tag))) {
                         last_subnet->setServerTag(new_tag);
@@ -553,19 +553,19 @@ public:
                 last_pool = Pool4::create(IOAddress(worker.getInet4(21)),
                                           IOAddress(worker.getInet4(22)));
 
-                // pool client_class at 57.
-                if (!worker.isColumnNull(57)) {
-                    last_pool->allowClientClass(worker.getString(57));
+                // pool client_class at 59.
+                if (!worker.isColumnNull(59)) {
+                    last_pool->allowClientClass(worker.getString(59));
                 }
 
-                // pool require_client_classes at 58.
-                setRequiredClasses(worker, 58, [&last_pool](const std::string& class_name) {
+                // pool require_client_classes at 60.
+                setRequiredClasses(worker, 60, [&last_pool](const std::string& class_name) {
                     last_pool->requireClientClass(class_name);
                 });
 
-                // pool user_context at 59.
-                if (!worker.isColumnNull(59)) {
-                    ElementPtr user_context = worker.getJSON(59);
+                // pool user_context at 61.
+                if (!worker.isColumnNull(61)) {
+                    ElementPtr user_context = worker.getJSON(61);
                     if (user_context) {
                         last_pool->setContext(user_context);
                     }
@@ -574,7 +574,7 @@ public:
                 last_subnet->addPool(last_pool);
             }
 
-            // Parse pool-specific option from 25 to 37.
+            // Parse pool-specific option from 25 to 38.
             if (last_pool && !worker.isColumnNull(25) &&
                 (last_pool_option_id < worker.getBigInt(25))) {
                 last_pool_option_id = worker.getBigInt(25);
@@ -585,12 +585,12 @@ public:
                 }
             }
 
-            // Parse subnet-specific option from 38 to 50.
-            if (!worker.isColumnNull(38) &&
-                (last_option_id < worker.getBigInt(38))) {
-                last_option_id = worker.getBigInt(38);
+            // Parse subnet-specific option from 39 to 52.
+            if (!worker.isColumnNull(39) &&
+                (last_option_id < worker.getBigInt(39))) {
+                last_option_id = worker.getBigInt(39);
 
-                OptionDescriptorPtr desc = processOptionRow(Option::V4, worker, 38);
+                OptionDescriptorPtr desc = processOptionRow(Option::V4, worker, 39);
                 if (desc) {
                     last_subnet->getCfgOption()->add(*desc, desc->space_name_);
                 }
@@ -1245,114 +1245,114 @@ public:
                 }
 
                 // valid_lifetime at 12.
-                // min_valid_lifetime at 33.
-                // max_valid_lifetime at 34.
+                // min_valid_lifetime at 34.
+                // max_valid_lifetime at 35.
                 if (!worker.isColumnNull(12)) {
-                    last_network->setValid(worker.getTriplet(12, 33, 34));
+                    last_network->setValid(worker.getTriplet(12, 34, 35));
                 }
 
-                // option from 13 to 25.
+                // option from 13 to 26.
 
-                // calculate_tee_times at 26.
-                if (!worker.isColumnNull(26)) {
-                    last_network->setCalculateTeeTimes(worker.getBool(26));
-                }
-
-                // t1_percent at 27.
+                // calculate_tee_times at 27.
                 if (!worker.isColumnNull(27)) {
-                    last_network->setT1Percent(worker.getDouble(27));
+                    last_network->setCalculateTeeTimes(worker.getBool(27));
                 }
 
-                // t2_percent at 28.
+                // t1_percent at 28.
                 if (!worker.isColumnNull(28)) {
-                    last_network->setT2Percent(worker.getDouble(28));
+                    last_network->setT1Percent(worker.getDouble(28));
                 }
 
-                // authoritative at 29.
+                // t2_percent at 29.
                 if (!worker.isColumnNull(29)) {
-                    last_network->setAuthoritative(worker.getBool(29));
+                    last_network->setT2Percent(worker.getDouble(29));
                 }
 
-                // boot_file_name at 30.
+                // authoritative at 30.
                 if (!worker.isColumnNull(30)) {
-                    last_network->setFilename(worker.getString(30));
+                    last_network->setAuthoritative(worker.getBool(30));
                 }
 
-                // next_server at 31.
+                // boot_file_name at 31.
                 if (!worker.isColumnNull(31)) {
-                    last_network->setSiaddr(worker.getInet4(31));
+                    last_network->setFilename(worker.getString(31));
                 }
 
-                // server_hostname at 32.
+                // next_server at 32.
                 if (!worker.isColumnNull(32)) {
-                    last_network->setSname(worker.getString(32));
+                    last_network->setSiaddr(worker.getInet4(32));
                 }
 
-                // min_valid_lifetime at 33.
-                // max_valid_lifetime at 34.
-
-                // ddns_send_updates at 35.
-                if (!worker.isColumnNull(35)) {
-                    last_network->setDdnsSendUpdates(worker.getBool(35));
+                // server_hostname at 33.
+                if (!worker.isColumnNull(33)) {
+                    last_network->setSname(worker.getString(33));
                 }
 
-                // ddns_override_no_update at 36.
+                // min_valid_lifetime at 34.
+                // max_valid_lifetime at 35.
+
+                // ddns_send_updates at 36.
                 if (!worker.isColumnNull(36)) {
-                    last_network->setDdnsOverrideNoUpdate(worker.getBool(36));
+                    last_network->setDdnsSendUpdates(worker.getBool(36));
                 }
 
-                // ddns_override_client_update at 37.
+                // ddns_override_no_update at 37.
                 if (!worker.isColumnNull(37)) {
-                    last_network->setDdnsOverrideClientUpdate(worker.getBool(37));
+                    last_network->setDdnsOverrideNoUpdate(worker.getBool(37));
                 }
 
-                // ddns_replace_client_name at 38.
+                // ddns_override_client_update at 38.
                 if (!worker.isColumnNull(38)) {
-                    last_network->setDdnsReplaceClientNameMode(
-                        static_cast<D2ClientConfig::ReplaceClientNameMode>(worker.getSmallInt(38)));
+                    last_network->setDdnsOverrideClientUpdate(worker.getBool(38));
                 }
 
-                // ddns_generated_prefix at 39.
+                // ddns_replace_client_name at 39.
                 if (!worker.isColumnNull(39)) {
-                    last_network->setDdnsGeneratedPrefix(worker.getString(39));
+                    last_network->setDdnsReplaceClientNameMode(
+                        static_cast<D2ClientConfig::ReplaceClientNameMode>(worker.getSmallInt(39)));
                 }
 
-                // ddns_qualifying_suffix at 40.
+                // ddns_generated_prefix at 40.
                 if (!worker.isColumnNull(40)) {
-                    last_network->setDdnsQualifyingSuffix(worker.getString(40));
+                    last_network->setDdnsGeneratedPrefix(worker.getString(40));
                 }
 
-                // reservations_in_subnet at 41.
+                // ddns_qualifying_suffix at 41.
                 if (!worker.isColumnNull(41)) {
-                    last_network->setReservationsInSubnet(worker.getBool(41));
+                    last_network->setDdnsQualifyingSuffix(worker.getString(41));
                 }
 
                 // reservations_in_subnet at 42.
                 if (!worker.isColumnNull(42)) {
-                    last_network->setReservationsOutOfPool(worker.getBool(42));
+                    last_network->setReservationsInSubnet(worker.getBool(42));
                 }
 
-                // cache_threshold at 43.
+                // reservations_in_subnet at 43.
                 if (!worker.isColumnNull(43)) {
-                    last_network->setCacheThreshold(worker.getDouble(43));
+                    last_network->setReservationsOutOfPool(worker.getBool(43));
                 }
 
-                // cache_max_age at 44.
+                // cache_threshold at 44.
                 if (!worker.isColumnNull(44)) {
-                    last_network->setCacheMaxAge(worker.getInt(44));
+                    last_network->setCacheThreshold(worker.getDouble(44));
                 }
 
-                // offer_lifetime at 45.
+                // cache_max_age at 45.
                 if (!worker.isColumnNull(45)) {
-                    last_network->setOfferLft(worker.getInt(45));
+                    last_network->setCacheMaxAge(worker.getInt(45));
                 }
 
-                // allocator at 46.
+                // offer_lifetime at 46.
                 if (!worker.isColumnNull(46)) {
-                    last_network->setAllocatorType(worker.getString(46));
+                    last_network->setOfferLft(worker.getInt(46));
+                }
+
+                // allocator at 47.
+                if (!worker.isColumnNull(47)) {
+                    last_network->setAllocatorType(worker.getString(47));
                 }
 
-                // server_tag at 47.
+                // server_tag at 48.
 
                 // Add the shared network.
                 auto ret = shared_networks.push_back(last_network);
@@ -1366,8 +1366,8 @@ public:
             }
 
             // Check for new server tags.
-            if (!worker.isColumnNull(47)) {
-                std::string new_tag = worker.getString(47);
+            if (!worker.isColumnNull(48)) {
+                std::string new_tag = worker.getString(48);
                 if (last_tag != new_tag) {
                     if (!new_tag.empty() && !last_network->hasServerTag(ServerTag(new_tag))) {
                         last_network->setServerTag(new_tag);
@@ -1377,7 +1377,7 @@ public:
                 }
             }
 
-            // Parse network-specific option from 13 to 25.
+            // Parse network-specific option from 13 to 26.
             if (!worker.isColumnNull(13) &&
                 (last_option_id < worker.getBigInt(13))) {
                 last_option_id = worker.getBigInt(13);
@@ -1643,6 +1643,7 @@ public:
         in_bindings.addNull();
         in_bindings.addNull();
         in_bindings.addTimestamp(option->getModificationTime());
+        addClientClassesBinding(in_bindings, option->client_classes_);
 
         // Remember the size before we add where clause arguments.
         size_t pre_where_size = in_bindings.size();
@@ -1711,6 +1712,7 @@ public:
         in_bindings.addNull();
         in_bindings.addNull();
         in_bindings.addTimestamp(option->getModificationTime());
+        addClientClassesBinding(in_bindings, option->client_classes_);
 
         // Remember the size before we add where clause arguments.
         size_t pre_where_size = in_bindings.size();
@@ -1802,6 +1804,7 @@ public:
         in_bindings.addNull();
         in_bindings.add(pool_id);
         in_bindings.addTimestamp(option->getModificationTime());
+        addClientClassesBinding(in_bindings, option->client_classes_);
 
         // Remember the size before we add where clause arguments.
         size_t pre_where_size = in_bindings.size();
@@ -1872,6 +1875,7 @@ public:
         in_bindings.add(shared_network_name);
         in_bindings.addNull();
         in_bindings.addTimestamp(option->getModificationTime());
+        addClientClassesBinding(in_bindings, option->client_classes_);
 
         // Remember the size before we add where clause arguments.
         size_t pre_where_size = in_bindings.size();
@@ -1938,6 +1942,7 @@ public:
         in_bindings.addNull();
         in_bindings.addNull();
         in_bindings.addTimestamp(option->getModificationTime());
+        addClientClassesBinding(in_bindings, option->client_classes_);
 
         // Remember the size before we add where clause arguments.
         size_t pre_where_size = in_bindings.size();
@@ -2304,9 +2309,9 @@ public:
                 class_list.push_back(last_client_class);
             }
 
-            // Check for new server tags at 38.
-            if (!worker.isColumnNull(38)) {
-                std::string new_tag = worker.getString(38);
+            // Check for new server tags at 39.
+            if (!worker.isColumnNull(39)) {
+                std::string new_tag = worker.getString(39);
                 if (last_tag != new_tag) {
                     if (!new_tag.empty() && !last_client_class->hasServerTag(ServerTag(new_tag))) {
                         last_client_class->setServerTag(new_tag);
@@ -3588,7 +3593,7 @@ TaggedStatementArray tagged_statements = { {
     // Insert subnet specific option.
     {
         // PgSqlConfigBackendDHCPv4Impl::INSERT_OPTION4,
-        13,
+        14,
         {
             OID_INT2,       //  1 code
             OID_BYTEA,      //  2 value
@@ -3602,7 +3607,8 @@ TaggedStatementArray tagged_statements = { {
             OID_TEXT,       // 10 user_context
             OID_VARCHAR,    // 11 shared_network_name
             OID_INT8,       // 12 pool_id
-            OID_TIMESTAMP   // 13 modification_ts
+            OID_TIMESTAMP,  // 13 modification_ts
+            OID_TEXT        // 14 client_classes
         },
         "INSERT_OPTION4",
         PGSQL_INSERT_OPTION4()
@@ -3932,7 +3938,7 @@ TaggedStatementArray tagged_statements = { {
     // Update existing global option.
     {
         // PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION4,
-        16,
+        17,
         {
             OID_INT2,       //  1 code
             OID_BYTEA,      //  2 value
@@ -3947,18 +3953,19 @@ TaggedStatementArray tagged_statements = { {
             OID_VARCHAR,    // 11 shared_network_name
             OID_INT8,       // 12 pool_id
             OID_TIMESTAMP,  // 13 modification_ts
-            OID_VARCHAR,    // 14 server_tag
-            OID_INT2,       // 15 code (of option to update)
-            OID_VARCHAR,    // 16 space (of option to update)
+            OID_TEXT,       // 14 client_classes
+            OID_VARCHAR,    // 15 server_tag
+            OID_INT2,       // 16 code (of option to update)
+            OID_VARCHAR,    // 17 space (of option to update)
         },
         "UPDATE_OPTION4",
-        PGSQL_UPDATE_OPTION4_WITH_TAG(AND o.scope_id = 0 AND o.code = $15 AND o.space = $16)
+        PGSQL_UPDATE_OPTION4_WITH_TAG(AND o.scope_id = 0 AND o.code = $16 AND o.space = $17)
     },
 
     // Update existing subnet level option.
     {
         // PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SUBNET_ID,
-        16,
+        17,
         {
             OID_INT2,       //  1 code
             OID_BYTEA,      //  2 value
@@ -3973,18 +3980,19 @@ TaggedStatementArray tagged_statements = { {
             OID_VARCHAR,    // 11 shared_network_name
             OID_INT8,       // 12 pool_id
             OID_TIMESTAMP,  // 13 modification_ts
-            OID_INT8,       // 14 subnet_id (of option to update)
-            OID_INT2,       // 15 code (of option to update)
-            OID_VARCHAR     // 16 space (of option to update)
+            OID_TEXT,       // 14 client_classes
+            OID_INT8,       // 15 subnet_id (of option to update)
+            OID_INT2,       // 16 code (of option to update)
+            OID_VARCHAR     // 17 space (of option to update)
         },
         "UPDATE_OPTION4_SUBNET_ID",
-        PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 1 AND o.dhcp4_subnet_id = $14 AND o.code = $15 AND o.space = $16)
+        PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 1 AND o.dhcp4_subnet_id = $15 AND o.code = $16 AND o.space = $17)
     },
 
     // Update existing pool level option.
     {
         // PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_POOL_ID,
-        16,
+        17,
         {
             OID_INT2,       //  1 code
             OID_BYTEA,      //  2 value
@@ -3999,18 +4007,19 @@ TaggedStatementArray tagged_statements = { {
             OID_VARCHAR,    // 11 shared_network_name
             OID_INT8,       // 12 pool_id
             OID_TIMESTAMP,  // 13 modification_ts
-            OID_INT8,       // 14 pool_id (of option to update)
-            OID_INT2,       // 15 code (of option to update)
-            OID_VARCHAR     // 16 space (of option to update)
+            OID_TEXT,       // 14 client_classes
+            OID_INT8,       // 15 pool_id (of option to update)
+            OID_INT2,       // 16 code (of option to update)
+            OID_VARCHAR     // 17 space (of option to update)
         },
         "UPDATE_OPTION4_POOL_ID",
-        PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 5 AND o.pool_id = $14 AND o.code = $15 AND o.space = $16)
+        PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 5 AND o.pool_id = $15 AND o.code = $16 AND o.space = $17)
     },
 
     // Update existing shared network level option.
     {
         // PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SHARED_NETWORK,
-        16,
+        17,
         {
             OID_INT2,       //  1 code
             OID_BYTEA,      //  2 value
@@ -4025,18 +4034,19 @@ TaggedStatementArray tagged_statements = { {
             OID_VARCHAR,    // 11 shared_network_name
             OID_INT8,       // 12 pool_id
             OID_TIMESTAMP,  // 13 modification_ts
-            OID_VARCHAR,    // 14 shared_network_name (of option to update)
-            OID_INT2,       // 15 code (of option to update)
-            OID_VARCHAR     // 16 space (of option to update)
+            OID_TEXT,       // 14 client_classes
+            OID_VARCHAR,    // 15 shared_network_name (of option to update)
+            OID_INT2,       // 16 code (of option to update)
+            OID_VARCHAR     // 17 space (of option to update)
         },
         "UPDATE_OPTION4_SHARED_NETWORK",
-        PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 4 AND o.shared_network_name = $14 AND o.code = $15 AND o.space = $16)
+        PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 4 AND o.shared_network_name = $15 AND o.code = $16 AND o.space = $17)
     },
 
     // Update existing client class level option.
     {
         // PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_CLIENT_CLASS,
-        16,
+        17,
         {
             OID_INT2,       //  1 code
             OID_BYTEA,      //  2 value
@@ -4051,12 +4061,13 @@ TaggedStatementArray tagged_statements = { {
             OID_VARCHAR,    // 11 shared_network_name
             OID_INT8,       // 12 pool_id
             OID_TIMESTAMP,  // 13 modification_ts
-            OID_VARCHAR,    // 14 dhcp_client_class (of option to update)
-            OID_INT2,       // 15 code (of option to update)
-            OID_VARCHAR,    // 16 space (of option to update)
+            OID_TEXT,       // 14 client_classes
+            OID_VARCHAR,    // 15 dhcp_client_class (of option to update)
+            OID_INT2,       // 16 code (of option to update)
+            OID_VARCHAR,    // 17 space (of option to update)
         },
         "UPDATE_OPTION4_CLIENT_CLASS",
-        PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 2 AND o.dhcp_client_class = $14 AND o.code = $15 AND o.space = $16)
+        PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 2 AND o.dhcp_client_class = $15 AND o.code = $16 AND o.space = $17)
     },
 
     // Update existing client class with specifying its position.
index 5f693b3b9d6c6863edaf165f0eb4dd0f99e2fe80..f5e71eeb52f430a46bbb0e8dbf6606f449ca8cba 100644 (file)
@@ -336,9 +336,9 @@ public:
                 auto prefix_pair = Subnet6::parsePrefix(subnet_prefix);
 
                 // preferred_lifetime (5)
-                // min_preferred_lifetime (72)
-                // max_preferred_lifetime (73)
-                auto preferred_lifetime = worker.getTriplet(5, 72, 73);
+                // 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);
@@ -347,9 +347,9 @@ public:
                 auto rebind_timer = worker.getTriplet(7);
 
                 // valid_lifetime at 14.
-                // min_valid_lifetime at 74.
-                // max_valid_lifetime at 75.
-                auto valid_lifetime = worker.getTriplet(14, 74, 75);
+                // 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,
@@ -412,103 +412,103 @@ public:
 
                 // 15 to 19 are pool
                 // 20 to 25 are pd pool
-                // 26 to 39 are pool option
-                // 40 to 53 are pd pool option
-                // 54 to 67 are option
+                // 26 to 40 are pool option
+                // 41 to 55 are pd pool option
+                // 56 to 70 are option
 
-                // calculate_tee_times at 68.
-                if (!worker.isColumnNull(68)) {
-                    last_subnet->setCalculateTeeTimes(worker.getBool(68));
+                // calculate_tee_times at 71.
+                if (!worker.isColumnNull(71)) {
+                    last_subnet->setCalculateTeeTimes(worker.getBool(71));
                 }
 
-                // t1_percent at 69.
-                if (!worker.isColumnNull(69)) {
-                    last_subnet->setT1Percent(worker.getDouble(69));
+                // t1_percent at 72.
+                if (!worker.isColumnNull(72)) {
+                    last_subnet->setT1Percent(worker.getDouble(72));
                 }
 
-                // t2_percent at 70.
-                if (!worker.isColumnNull(70)) {
-                    last_subnet->setT2Percent(worker.getDouble(70));
+                // t2_percent at 73.
+                if (!worker.isColumnNull(73)) {
+                    last_subnet->setT2Percent(worker.getDouble(73));
                 }
 
-                // interface_id at 71.
-                setInterfaceId(*last_subnet, worker, 71);
+                // interface_id at 74.
+                setInterfaceId(*last_subnet, worker, 74);
 
-                // 72 and 73 are {min,max}_preferred_lifetime
+                // 75 and 76 are {min,max}_preferred_lifetime
 
-                // 74 and 75 are {min,max}_valid_lifetime
+                // 77 and 78 are {min,max}_valid_lifetime
 
-                // 76 is pool client_class
-                // 77 is pool require_client_classes
-                // 78 is pool user_context
-                // 79 is pd pool excluded_prefix
-                // 80 is pd pool excluded_prefix_length
-                // 81 is pd pool client_class
-                // 82 is pd pool require_client_classes
-                // 83 is pd pool user_context
+                // 79 is pool client_class
+                // 80 is pool require_client_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 require_client_classes
+                // 86 is pd pool user_context
 
-                // ddns_send_updates at 84.
-                if (!worker.isColumnNull(84)) {
-                    last_subnet->setDdnsSendUpdates(worker.getBool(84));
-                }
-
-                // ddns_override_no_update at 85.
-                if (!worker.isColumnNull(85)) {
-                    last_subnet->setDdnsOverrideNoUpdate(worker.getBool(85));
-                }
-
-                // ddns_override_client_update at 86.
-                if (!worker.isColumnNull(86)) {
-                    last_subnet->setDdnsOverrideClientUpdate(worker.getBool(86));
-                }
-
-                // ddns_replace_client_name at 87.
+                // ddns_send_updates at 87.
                 if (!worker.isColumnNull(87)) {
-                    last_subnet->setDdnsReplaceClientNameMode(
-                        static_cast<D2ClientConfig::ReplaceClientNameMode>(worker.getSmallInt(87)));
+                    last_subnet->setDdnsSendUpdates(worker.getBool(87));
                 }
 
-                // ddns_generated_prefix at 88.
+                // ddns_override_no_update at 88.
                 if (!worker.isColumnNull(88)) {
-                    last_subnet->setDdnsGeneratedPrefix(worker.getString(88));
+                    last_subnet->setDdnsOverrideNoUpdate(worker.getBool(88));
                 }
 
-                // ddns_qualifying_suffix at 89.
+                // ddns_override_client_update at 89.
                 if (!worker.isColumnNull(89)) {
-                    last_subnet->setDdnsQualifyingSuffix(worker.getString(89));
+                    last_subnet->setDdnsOverrideClientUpdate(worker.getBool(89));
                 }
 
-                // reservations_in_subnet at 90.
+                // ddns_replace_client_name at 90.
                 if (!worker.isColumnNull(90)) {
-                    last_subnet->setReservationsInSubnet(worker.getBool(90));
+                    last_subnet->setDdnsReplaceClientNameMode(
+                        static_cast<D2ClientConfig::ReplaceClientNameMode>(worker.getSmallInt(90)));
                 }
 
-                // reservations_out_of_pool at 91.
+                // ddns_generated_prefix at 91.
                 if (!worker.isColumnNull(91)) {
-                    last_subnet->setReservationsOutOfPool(worker.getBool(91));
+                    last_subnet->setDdnsGeneratedPrefix(worker.getString(91));
                 }
 
-                // cache_threshold at 92.
+                // ddns_qualifying_suffix at 92.
                 if (!worker.isColumnNull(92)) {
-                    last_subnet->setCacheThreshold(worker.getDouble(92));
+                    last_subnet->setDdnsQualifyingSuffix(worker.getString(92));
                 }
 
-                // cache_max_age at 93.
+                // reservations_in_subnet at 93.
                 if (!worker.isColumnNull(93)) {
-                    last_subnet->setCacheMaxAge(worker.getInt(93));
+                    last_subnet->setReservationsInSubnet(worker.getBool(93));
                 }
 
-                // allocator at 94.
+                // reservations_out_of_pool at 94.
                 if (!worker.isColumnNull(94)) {
-                    last_subnet->setAllocatorType(worker.getString(94));
+                    last_subnet->setReservationsOutOfPool(worker.getBool(94));
                 }
 
-                // pd_allocator at 95.
+                // cache_threshold at 95.
                 if (!worker.isColumnNull(95)) {
-                    last_subnet->setPdAllocatorType(worker.getString(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));
                 }
 
-                // server_tag at 96.
+                // server_tag at 99.
 
                 // Subnet ready. Add it to the list.
                 auto ret = subnets.insert(last_subnet);
@@ -521,9 +521,9 @@ public:
                 }
             }
 
-            // Check for new server tags at 96.
-            if (!worker.isColumnNull(96)) {
-                std::string new_tag = worker.getString(96);
+            // Check for new server tags at 99.
+            if (!worker.isColumnNull(99)) {
+                std::string new_tag = worker.getString(99);
                 if (last_tag != new_tag) {
                     if (!new_tag.empty() && !last_subnet->hasServerTag(ServerTag(new_tag))) {
                         last_subnet->setServerTag(new_tag);
@@ -533,7 +533,7 @@ public:
                 }
             }
 
-            // Pool is between 15 and 19 with extra between 76 and 78
+            // 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
@@ -554,19 +554,19 @@ public:
                 // pool subnet_id at 18 (ignored)
                 // pool modification_ts at 19 (ignored)
 
-                // pool client_class at 76.
-                if (!worker.isColumnNull(76)) {
-                    last_pool->allowClientClass(worker.getString(76));
+                // pool client_class at 79.
+                if (!worker.isColumnNull(79)) {
+                    last_pool->allowClientClass(worker.getString(79));
                 }
 
-                // pool require_client_classes at 77.
-                setRequiredClasses(worker, 77, [&last_pool](const std::string& class_name) {
+                // pool require_client_classes at 80.
+                setRequiredClasses(worker, 80, [&last_pool](const std::string& class_name) {
                     last_pool->requireClientClass(class_name);
                 });
 
-                // pool user_context at 78.
-                if (!worker.isColumnNull(78)) {
-                    ElementPtr user_context = worker.getJSON(78);
+                // pool user_context at 81.
+                if (!worker.isColumnNull(81)) {
+                    ElementPtr user_context = worker.getJSON(81);
                     if (user_context) {
                         last_pool->setContext(user_context);
                     }
@@ -575,7 +575,7 @@ public:
                 last_subnet->addPool(last_pool);
             }
 
-            // Pd Pool is between 20 and 25 with extra between 79 and 83
+            // 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
@@ -594,10 +594,10 @@ public:
                 // 24 is pd pool subnet_id (ignored)
                 // 25 is pd pool modification_ts (ignored)
 
-                // excluded_prefix (79) and excluded_prefix_length (80)
+                // excluded_prefix (82) and excluded_prefix_length (83)
                 IOAddress excluded_prefix = IOAddress::IPV6_ZERO_ADDRESS();
-                if (!worker.isColumnNull(79)) {
-                    excluded_prefix = worker.getInet6(79);
+                if (!worker.isColumnNull(82)) {
+                    excluded_prefix = worker.getInet6(82);
                 }
 
                 last_pd_pool_id = worker.getBigInt(20);
@@ -605,21 +605,21 @@ public:
                                              static_cast<uint8_t>(worker.getSmallInt(22)),
                                              static_cast<uint8_t>(worker.getSmallInt(23)),
                                              excluded_prefix,
-                                             static_cast<uint8_t>(worker.getSmallInt(80)));
+                                             static_cast<uint8_t>(worker.getSmallInt(83)));
 
-                // pd pool client_class (81)
-                if (!worker.isColumnNull(81)) {
-                    last_pd_pool->allowClientClass(worker.getString(81));
+                // pd pool client_class (84)
+                if (!worker.isColumnNull(84)) {
+                    last_pd_pool->allowClientClass(worker.getString(84));
                 }
 
-                // pd pool require_client_classes at 82.
-                setRequiredClasses(worker, 82, [&last_pd_pool](const std::string& class_name) {
+                // pd pool require_client_classes at 85.
+                setRequiredClasses(worker, 85, [&last_pd_pool](const std::string& class_name) {
                     last_pd_pool->requireClientClass(class_name);
                 });
 
-                // pd pool user_context at 83.
-                if (!worker.isColumnNull(83)) {
-                    ElementPtr user_context = worker.getJSON(83);
+                // 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);
                     }
@@ -628,7 +628,7 @@ public:
                 last_subnet->addPool(last_pd_pool);
             }
 
-            // Parse pool-specific option from 26 to 39.
+            // 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);
@@ -639,23 +639,23 @@ public:
                 }
             }
 
-            // Parse pd pool-specific option from 40 to 53.
-            if (last_pd_pool && !worker.isColumnNull(40) &&
-                (last_pd_pool_option_id < worker.getBigInt(40))) {
-                last_pd_pool_option_id = worker.getBigInt(40);
+            // 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, 40);
+                OptionDescriptorPtr desc = processOptionRow(Option::V6, worker, 41);
                 if (desc) {
                     last_pd_pool->getCfgOption()->add(*desc, desc->space_name_);
                 }
             }
 
-            // Parse subnet-specific option from 54 to 67.
-            if (!worker.isColumnNull(54) &&
-                (last_option_id < worker.getBigInt(54))) {
-                last_option_id = worker.getBigInt(54);
+            // 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, 54);
+                OptionDescriptorPtr desc = processOptionRow(Option::V6, worker, 56);
                 if (desc) {
                     last_subnet->getCfgOption()->add(*desc, desc->space_name_);
                 }
@@ -1456,9 +1456,9 @@ public:
                 last_network->setModificationTime(worker.getTimestamp(4));
 
                 // preferred_lifetime (5)
-                // min_preferred_lifetime (32)
-                // max_preferred_lifetime (33)
-                last_network->setPreferred(worker.getTriplet(5, 32, 33));
+                // min_preferred_lifetime (33)
+                // max_preferred_lifetime (34)
+                last_network->setPreferred(worker.getTriplet(5, 33, 34));
 
                 // rapid_commit at 6.
                 if (!worker.isColumnNull(6)) {
@@ -1497,99 +1497,99 @@ public:
                 }
 
                 // valid_lifetime at 13.
-                // min_valid_lifetime at 34.
-                // max_valid_lifetime at 35.
+                // min_valid_lifetime at 35.
+                // max_valid_lifetime at 36.
                 if (!worker.isColumnNull(13)) {
-                    last_network->setValid(worker.getTriplet(13, 34, 35));
+                    last_network->setValid(worker.getTriplet(13, 35, 36));
                 }
 
-                // option from 14 to 27.
-
-                // calculate_tee_times at 28.
-                if (!worker.isColumnNull(28)) {
-                    last_network->setCalculateTeeTimes(worker.getBool(28));
-                }
+                // option from 14 to 28.
 
-                // t1_percent at 29.
+                // calculate_tee_times at 29.
                 if (!worker.isColumnNull(29)) {
-                    last_network->setT1Percent(worker.getDouble(29));
+                    last_network->setCalculateTeeTimes(worker.getBool(29));
                 }
 
-                // t2_percent at 30.
+                // t1_percent at 30.
                 if (!worker.isColumnNull(30)) {
-                    last_network->setT2Percent(worker.getDouble(30));
+                    last_network->setT1Percent(worker.getDouble(30));
                 }
 
-                // interface_id at 31.
-                setInterfaceId(*last_network, worker, 31);
+                // t2_percent at 31.
+                if (!worker.isColumnNull(31)) {
+                    last_network->setT2Percent(worker.getDouble(31));
+                }
 
-                // min_preferred_lifetime at 32.
-                // max_preferred_lifetime at 33.
-                // min_valid_lifetime at 34.
-                // max_valid_lifetime at 35.
+                // interface_id at 32.
+                setInterfaceId(*last_network, worker, 32);
 
-                // ddns_send_updates at 36.
-                if (!worker.isColumnNull(36)) {
-                    last_network->setDdnsSendUpdates(worker.getBool(36));
-                }
+                // min_preferred_lifetime at 33.
+                // max_preferred_lifetime at 34.
+                // min_valid_lifetime at 35.
+                // max_valid_lifetime at 36.
 
-                // ddns_override_no_update at 37.
+                // ddns_send_updates at 37.
                 if (!worker.isColumnNull(37)) {
-                    last_network->setDdnsOverrideNoUpdate(worker.getBool(37));
+                    last_network->setDdnsSendUpdates(worker.getBool(37));
                 }
 
-                // ddns_override_client_update at 38.
+                // ddns_override_no_update at 38.
                 if (!worker.isColumnNull(38)) {
-                    last_network->setDdnsOverrideClientUpdate(worker.getBool(38));
+                    last_network->setDdnsOverrideNoUpdate(worker.getBool(38));
                 }
 
-                // ddns_replace_client_name at 39.
+                // ddns_override_client_update at 39.
                 if (!worker.isColumnNull(39)) {
-                    last_network->setDdnsReplaceClientNameMode(
-                        static_cast<D2ClientConfig::ReplaceClientNameMode>(worker.getSmallInt(39)));
+                    last_network->setDdnsOverrideClientUpdate(worker.getBool(39));
                 }
 
-                // ddns_generated_prefix at 40.
+                // ddns_replace_client_name at 40.
                 if (!worker.isColumnNull(40)) {
-                    last_network->setDdnsGeneratedPrefix(worker.getString(40));
+                    last_network->setDdnsReplaceClientNameMode(
+                        static_cast<D2ClientConfig::ReplaceClientNameMode>(worker.getSmallInt(40)));
                 }
 
-                // ddns_qualifying_suffix at 41.
+                // ddns_generated_prefix at 41.
                 if (!worker.isColumnNull(41)) {
-                    last_network->setDdnsQualifyingSuffix(worker.getString(41));
+                    last_network->setDdnsGeneratedPrefix(worker.getString(41));
                 }
 
-                // reservations_in_subnet at 42.
+                // ddns_qualifying_suffix at 42.
                 if (!worker.isColumnNull(42)) {
-                    last_network->setReservationsInSubnet(worker.getBool(42));
+                    last_network->setDdnsQualifyingSuffix(worker.getString(42));
                 }
 
                 // reservations_in_subnet at 43.
                 if (!worker.isColumnNull(43)) {
-                    last_network->setReservationsOutOfPool(worker.getBool(43));
+                    last_network->setReservationsInSubnet(worker.getBool(43));
                 }
 
-                // cache_threshold at 44.
+                // reservations_in_subnet at 44.
                 if (!worker.isColumnNull(44)) {
-                    last_network->setCacheThreshold(worker.getDouble(44));
+                    last_network->setReservationsOutOfPool(worker.getBool(44));
                 }
 
-                // cache_max_age at 45.
+                // cache_threshold at 45.
                 if (!worker.isColumnNull(45)) {
-                    last_network->setCacheMaxAge(worker.getInt(45));
+                    last_network->setCacheThreshold(worker.getDouble(45));
                 }
 
-                // allocator at 46.
+                // cache_max_age at 46.
                 if (!worker.isColumnNull(46)) {
-                    last_network->setAllocatorType(worker.getString(46));
+                    last_network->setCacheMaxAge(worker.getInt(46));
                 }
 
-                // pd_allocator at 47.
+                // allocator at 47.
                 if (!worker.isColumnNull(47)) {
-                    last_network->setPdAllocatorType(worker.getString(47));
+                    last_network->setAllocatorType(worker.getString(47));
                 }
 
-                // server_tag at 48.
+                // pd_allocator at 48.
+                if (!worker.isColumnNull(48)) {
+                    last_network->setPdAllocatorType(worker.getString(48));
+                }
+
+                // server_tag at 49.
 
                 // Add the shared network.
                 auto ret = shared_networks.push_back(last_network);
@@ -1603,8 +1603,8 @@ public:
             }
 
             // Check for new server tags.
-            if (!worker.isColumnNull(48)) {
-                std::string new_tag = worker.getString(48);
+            if (!worker.isColumnNull(49)) {
+                std::string new_tag = worker.getString(49);
                 if (last_tag != new_tag) {
                     if (!new_tag.empty() && !last_network->hasServerTag(ServerTag(new_tag))) {
                         last_network->setServerTag(new_tag);
@@ -1614,7 +1614,7 @@ public:
                 }
             }
 
-            // Parse network-specific option from 14 to 27.
+            // Parse network-specific option from 14 to 28.
             if (!worker.isColumnNull(14) &&
                 (last_option_id < worker.getBigInt(14))) {
                 last_option_id = worker.getBigInt(14);
@@ -1880,6 +1880,7 @@ public:
         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.
@@ -1949,6 +1950,7 @@ public:
         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.
@@ -2093,6 +2095,7 @@ public:
         }
 
         in_bindings.addTimestamp(option->getModificationTime());
+        addClientClassesBinding(in_bindings, option->client_classes_);
 
         // pd_pool_id
         if (pool_type == Lease::TYPE_PD) {
@@ -2178,6 +2181,7 @@ public:
         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.
@@ -2245,6 +2249,7 @@ public:
         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.
@@ -2621,14 +2626,14 @@ public:
                 // class specific option from 21 to 33.
 
                 // preferred lifetime: default, min, max
-                last_client_class->setPreferred(worker.getTriplet(35, 36, 37));
+                last_client_class->setPreferred(worker.getTriplet(36, 37, 38));
 
                 class_list.push_back(last_client_class);
             }
 
-            // Check for new server tags at 34.
-            if (!worker.isColumnNull(34)) {
-                std::string new_tag = worker.getString(34);
+            // 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);
@@ -2649,7 +2654,7 @@ public:
                 }
             }
 
-            // Parse client class specific option from 21 to 33.
+            // 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);
@@ -4003,7 +4008,7 @@ TaggedStatementArray tagged_statements = { {
     // Insert subnet specific option.
     {
         // PgSqlConfigBackendDHCPv6Impl::INSERT_OPTION6,
-        14,
+        15,
         {
             OID_INT2,       //  1 code
             OID_BYTEA,      //  2 value
@@ -4018,7 +4023,8 @@ TaggedStatementArray tagged_statements = { {
             OID_VARCHAR,    // 11 shared_network_name
             OID_INT8,       // 12 pool_id
             OID_TIMESTAMP,  // 13 modification_ts
-            OID_INT8        // 14 pd_pool_id
+            OID_TEXT,       // 14 client_classes
+            OID_INT8        // 15 pd_pool_id
         },
         "INSERT_OPTION6",
         PGSQL_INSERT_OPTION6()
@@ -4337,7 +4343,7 @@ TaggedStatementArray tagged_statements = { {
     // Update existing global option.
     {
         // PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6,
-        17,
+        18,
         {
             OID_INT2,       //  1 code
             OID_BYTEA,      //  2 value
@@ -4352,19 +4358,20 @@ TaggedStatementArray tagged_statements = { {
             OID_VARCHAR,    // 11 shared_network_name
             OID_INT8,       // 12 pool_id
             OID_TIMESTAMP,  // 13 modification_ts
-            OID_INT8,       // 14 pd_pool_id
-            OID_VARCHAR,    // 15 server_tag
-            OID_INT2,       // 16 code (of option to update)
-            OID_VARCHAR,    // 17 space (of option to update)
+            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)
         },
         "UPDATE_OPTION6",
-        PGSQL_UPDATE_OPTION6_WITH_TAG(AND o.scope_id = 0 AND o.code = $16 AND o.space = $17)
+        PGSQL_UPDATE_OPTION6_WITH_TAG(AND o.scope_id = 0 AND o.code = $17 AND o.space = $18)
     },
 
     // Update existing subnet level option.
     {
         // PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_SUBNET_ID,
-        17,
+        18,
         {
             OID_INT2,       //  1 code
             OID_BYTEA,      //  2 value
@@ -4379,19 +4386,20 @@ TaggedStatementArray tagged_statements = { {
             OID_VARCHAR,    // 11 shared_network_name
             OID_INT8,       // 12 pool_id
             OID_TIMESTAMP,  // 13 modification_ts
-            OID_INT8,       // 14 pd_pool_id
-            OID_INT8,       // 15 subnet_id (of option to update)
-            OID_INT2,       // 16 code (of option to update)
-            OID_VARCHAR     // 17 space (of option to update)
+            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)
         },
         "UPDATE_OPTION6_SUBNET_ID",
-        PGSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 1 AND o.dhcp6_subnet_id = $15 AND o.code = $16 AND o.space = $17)
+        PGSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 1 AND o.dhcp6_subnet_id = $16 AND o.code = $17 AND o.space = $18)
     },
 
     // Update existing pool level option.
     {
         // PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_POOL_ID,
-        17,
+        18,
         {
             OID_INT2,       //  1 code
             OID_BYTEA,      //  2 value
@@ -4406,19 +4414,20 @@ TaggedStatementArray tagged_statements = { {
             OID_VARCHAR,    // 11 shared_network_name
             OID_INT8,       // 12 pool_id
             OID_TIMESTAMP,  // 13 modification_ts
-            OID_INT8,       // 14 pd_pool_id
-            OID_INT8,       // 15 pool_id (of option to update)
-            OID_INT2,       // 16 code (of option to update)
-            OID_VARCHAR     // 17 space (of option to update)
+            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)
         },
         "UPDATE_OPTION6_POOL_ID",
-        PGSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 5 AND o.pool_id = $15 AND o.code = $16 AND o.space = $17)
+        PGSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 5 AND o.pool_id = $16 AND o.code = $17 AND o.space = $18)
     },
 
     // Update existing pd pool level option.
     {
         // PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_PD_POOL_ID,
-        17,
+        18,
         {
             OID_INT2,       //  1 code
             OID_BYTEA,      //  2 value
@@ -4433,19 +4442,20 @@ TaggedStatementArray tagged_statements = { {
             OID_VARCHAR,    // 11 shared_network_name
             OID_INT8,       // 12 pool_id
             OID_TIMESTAMP,  // 13 modification_ts
-            OID_INT8,       // 14 pd_pool_id
-            OID_INT8,       // 15 pd_pool_id (of option to update)
-            OID_INT2,       // 16 code (of option to update)
-            OID_VARCHAR     // 17 space (of option to update)
+            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)
         },
         "UPDATE_OPTION6_PD_POOL_ID",
-        PGSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 6 AND o.pd_pool_id = $15 AND o.code = $16 AND o.space = $17)
+        PGSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 6 AND o.pd_pool_id = $16 AND o.code = $17 AND o.space = $18)
     },
 
     // Update existing shared network level option.
     {
         // PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_SHARED_NETWORK,
-        17,
+        18,
         {
             OID_INT2,       //  1 code
             OID_BYTEA,      //  2 value
@@ -4460,19 +4470,20 @@ TaggedStatementArray tagged_statements = { {
             OID_VARCHAR,    // 11 shared_network_name
             OID_INT8,       // 12 pool_id
             OID_TIMESTAMP,  // 13 modification_ts
-            OID_INT8,       // 14 pd_pool_id
-            OID_VARCHAR,    // 15 shared_network_name (of option to update)
-            OID_INT2,       // 16 code (of option to update)
-            OID_VARCHAR     // 17 space (of option to update)
+            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)
         },
         "UPDATE_OPTION6_SHARED_NETWORK",
-        PGSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 4 AND o.shared_network_name = $15 AND o.code = $16 AND o.space = $17)
+        PGSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 4 AND o.shared_network_name = $16 AND o.code = $17 AND o.space = $18)
     },
 
     // Update existing client class level option.
     {
         // PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_CLIENT_CLASS,
-        17,
+        18,
         {
             OID_INT2,       //  1 code
             OID_BYTEA,      //  2 value
@@ -4487,13 +4498,14 @@ TaggedStatementArray tagged_statements = { {
             OID_VARCHAR,    // 11 shared_network_name
             OID_INT8,       // 12 pool_id
             OID_TIMESTAMP,  // 13 modification_ts
-            OID_INT8,       // 14 pd_pool_id
-            OID_VARCHAR,    // 15 client_class (of option to update)
-            OID_INT2,       // 16 code (of option to update)
-            OID_VARCHAR     // 17 space (of option to update)
+            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)
         },
         "UPDATE_OPTION6_CLIENT_CLASS",
-        PGSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 2 AND o.dhcp_client_class = $15 AND o.code = $16 AND o.space = $17)
+        PGSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 2 AND o.dhcp_client_class = $16 AND o.code = $17 AND o.space = $18)
     },
 
     // Update existing client class with specifying its position.
index 76a643458f49007e57736f850fc95ae606ae944d..3d1d6ca0c95d197fa53954c5af821eeef23645eb 100644 (file)
@@ -736,7 +736,7 @@ PgSqlConfigBackendImpl::getOptions(const int index,
             OptionDescriptorPtr desc = processOptionRow(universe, worker, 0);
             if (desc) {
                 // server_tag for the global option
-                ServerTag last_option_server_tag(worker.getString(13));
+                ServerTag last_option_server_tag(worker.getString(14));
                 desc->setServerTag(last_option_server_tag.get());
 
                 // If we're fetching options for a given server (explicit server
@@ -828,6 +828,17 @@ PgSqlConfigBackendImpl::processOptionRow(const Option::Universe& universe,
     desc->space_name_ = space;
     desc->setModificationTime(worker.getTimestamp(first_col + 12));
 
+    // Set user context.
+    if (!worker.isColumnNull(first_col + 9)) {
+        ElementPtr user_context = worker.getJSON(first_col + 9);
+        if (user_context) {
+            desc->setContext(user_context);
+        }
+    }
+
+    // Populate client classes.
+    setClientClasses(worker, first_col + 13, desc->client_classes_);
+
     // Set database id for the option.
     // @todo Can this actually ever be null and if it is, isn't that an error?
     if (!worker.isColumnNull(first_col)) {
@@ -837,6 +848,31 @@ PgSqlConfigBackendImpl::processOptionRow(const Option::Universe& universe,
     return (desc);
 }
 
+void
+PgSqlConfigBackendImpl::setClientClasses(PgSqlResultRowWorker& worker, size_t col,
+                                         ClientClasses& client_classes) {
+    if (worker.isColumnNull(col)) {
+        return;
+    }
+
+    ElementPtr cclasses_element = worker.getJSON(col);
+    if (cclasses_element->getType() != Element::list) {
+        std::ostringstream ss;
+        cclasses_element->toJSON(ss);
+        isc_throw(BadValue, "invalid client_classes value " << ss.str());
+    }
+
+    for (auto i = 0; i < cclasses_element->size(); ++i) {
+        auto cclasses_item = cclasses_element->get(i);
+        if (cclasses_item->getType() != Element::string) {
+            isc_throw(BadValue, "elements of client_classes list must"
+                                "be valid strings");
+        }
+
+        client_classes.insert(cclasses_item->stringValue());
+    }
+}
+
 OptionDefinitionPtr
 PgSqlConfigBackendImpl::processOptionDefRow(PgSqlResultRowWorker& worker,
                                             const size_t first_col) {
@@ -1148,5 +1184,17 @@ PgSqlConfigBackendImpl::addOptionValueBinding(PsqlBindArray& bindings,
     }
 }
 
+void 
+PgSqlConfigBackendImpl::addClientClassesBinding(db::PsqlBindArray& bindings,
+                                                const ClientClasses& client_classes) {
+    // Create JSON list of client classes.
+    data::ElementPtr client_classes_element = data::Element::createList();
+    for (auto const& client_class : client_classes) {
+        client_classes_element->add(data::Element::create(client_class));
+    }
+
+    bindings.add(client_classes_element);
+}
+
 } // end of namespace isc::dhcp
 } // end of namespace isc
index 2c848082bbd6fe4f3ec4e1c6352b1ba3dd2bb682..a5b87a0ef93ab65a58f4467a8445a22ea02b57e7 100644 (file)
@@ -622,6 +622,29 @@ public:
     void setRequiredClasses(db::PgSqlResultRowWorker& worker, size_t col,
                             std::function<void(const std::string&)> setter);
 
+    /// @brief Addds 'client-classes' parameter to a bind array.
+    ///
+    /// Creates an Element tree of client class names and adds that to the end
+    /// of the given bind array.
+    ///
+    /// @param client_classes ClientClasses collection containing the class names
+    void addClientClassesBinding(db::PsqlBindArray& bindings, 
+                                 const ClientClasses& client_classes);
+
+    /// @brief Iterates over the class names in a JSON list element at a
+    /// given column, adding each name to the given ClientClasses instance.
+    ///
+    /// Has no effect if the column is null or is an empty list.
+    ///
+    /// @param worker result set row worker containing the row data
+    /// @param col column index of JSON element column
+    /// @param client_classes ClientCaLsses instance to populate
+    ///
+    /// @throw BadValue if the Element is not a list or if any of the
+    /// list's elements are not strings.
+    void setClientClasses(db::PgSqlResultRowWorker& worker, size_t col,
+                          ClientClasses& client_classes);
+
     /// @brief Adds an option value to a bind array.
     ///
     /// @param bindings PsqlBindArray to which the option value should be added.
index f30187c55eda7b911d10bad6f83bc79a9439b12b..3b9bac276cbc5a4a677bdf48cf98931ad45502ce 100644 (file)
@@ -84,6 +84,7 @@ namespace {
     "  x.shared_network_name," \
     "  x.pool_id," \
     "  gmt_epoch(x.modification_ts) as modification_ts, " \
+    "  x.client_classes," \
     "  o.option_id," \
     "  o.code," \
     "  o.value," \
@@ -97,6 +98,7 @@ namespace {
     "  o.shared_network_name," \
     "  o.pool_id," \
     "  gmt_epoch(o.modification_ts) as modification_ts, " \
+    "  o.client_classes," \
     "  s.calculate_tee_times," \
     "  s.t1_percent," \
     "  s.t2_percent," \
@@ -194,6 +196,7 @@ namespace {
     "  x.shared_network_name," \
     "  x.pool_id," \
     "  gmt_epoch(x.modification_ts) as modification_ts, " \
+    "  x.client_classes," \
     "  x.pd_pool_id," \
     "  y.option_id," \
     "  y.code," \
@@ -208,6 +211,7 @@ namespace {
     "  y.shared_network_name," \
     "  y.pool_id," \
     "  gmt_epoch(y.modification_ts) as modification_ts, " \
+    "  y.client_classes," \
     "  y.pd_pool_id," \
     "  o.option_id," \
     "  o.code," \
@@ -222,6 +226,7 @@ namespace {
     "  o.shared_network_name," \
     "  o.pool_id," \
     "  gmt_epoch(o.modification_ts) as modification_ts, " \
+    "  o.client_classes," \
     "  o.pd_pool_id, " \
     "  s.calculate_tee_times," \
     "  s.t1_percent," \
@@ -310,7 +315,8 @@ namespace {
       "  x.user_context," \
       "  x.shared_network_name," \
       "  x.pool_id," \
-      "  gmt_epoch(x.modification_ts) as modification_ts " \
+      "  gmt_epoch(x.modification_ts) as modification_ts," \
+      "  x.client_classes " \
       "FROM dhcp4_pool AS p " \
       server_join \
       "LEFT JOIN dhcp4_options AS x ON x.scope_id = 5 AND p.id = x.pool_id " \
@@ -352,6 +358,7 @@ namespace {
     "  x.shared_network_name," \
     "  x.pool_id," \
     "  gmt_epoch(x.modification_ts) as modification_ts, " \
+    "  x.client_classes," \
     "  x.pd_pool_id " \
     "FROM dhcp6_pool AS p " \
     server_join \
@@ -397,6 +404,7 @@ namespace {
     "  x.shared_network_name," \
     "  x.pool_id," \
     "  gmt_epoch(x.modification_ts) as modification_ts, " \
+    "  x.client_classes, " \
     "  x.pd_pool_id " \
     "FROM dhcp6_pd_pool AS p " \
     server_join \
@@ -444,6 +452,7 @@ namespace {
     "  o.shared_network_name," \
     "  o.pool_id," \
     "  gmt_epoch(o.modification_ts) as modification_ts, " \
+    "  o.client_classes, " \
     "  n.calculate_tee_times," \
     "  n.t1_percent," \
     "  n.t2_percent," \
@@ -527,6 +536,7 @@ namespace {
     "  o.shared_network_name," \
     "  o.pool_id," \
     "  gmt_epoch(o.modification_ts) as modification_ts, " \
+    "  o.client_classes, " \
     "  o.pd_pool_id, " \
     "  n.calculate_tee_times," \
     "  n.t1_percent," \
@@ -619,6 +629,7 @@ namespace {
     "  o.shared_network_name," \
     "  o.pool_id," \
     "  gmt_epoch(o.modification_ts) as modification_ts, " \
+    "  o.client_classes, " \
     "  s.tag " \
     pd_pool_id \
     "FROM " #table_prefix "_options AS o " \
@@ -715,6 +726,7 @@ namespace {
     "  x.shared_network_name," \
     "  x.pool_id," \
     "  gmt_epoch(x.modification_ts) as modification_ts, " \
+    "  x.client_classes, " \
     "  s.tag " \
     "FROM dhcp4_client_class AS c " \
     "INNER JOIN dhcp4_client_class_order AS o " \
@@ -779,6 +791,7 @@ namespace {
     "  x.shared_network_name," \
     "  x.pool_id," \
     "  gmt_epoch(x.modification_ts) as modification_ts, " \
+    "  x.client_classes, " \
     "  s.tag, " \
     "  c.preferred_lifetime," \
     "  c.min_preferred_lifetime, " \
@@ -935,15 +948,16 @@ namespace {
     "  user_context," \
     "  shared_network_name," \
     "  pool_id," \
-    "  modification_ts" \
+    "  modification_ts, " \
+    "  client_classes " \
     pd_pool_id \
-    ") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, cast($10 as json), $11, $12, $13" last ")"
+    ") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, cast($10 as json), $11, $12, $13, $14" last ")"
 
 #define PGSQL_INSERT_OPTION4() \
     PGSQL_INSERT_OPTION_COMMON(dhcp4, "", "")
 
 #define PGSQL_INSERT_OPTION6() \
-    PGSQL_INSERT_OPTION_COMMON(dhcp6, ", pd_pool_id ", ", $14")
+    PGSQL_INSERT_OPTION_COMMON(dhcp6, ", pd_pool_id ", ", $15")
 #endif
 
 #ifndef PGSQL_INSERT_OPTION_SERVER
@@ -1056,7 +1070,8 @@ namespace {
     "  user_context = cast($10 as json)," \
     "  shared_network_name = $11," \
     "  pool_id = $12," \
-    "  modification_ts = $13 " \
+    "  modification_ts = $13, " \
+    "  client_classes = $14 " \
     pd_pool_id \
     "WHERE " #__VA_ARGS__
 
@@ -1064,7 +1079,7 @@ namespace {
     PGSQL_UPDATE_OPTION_NO_TAG(dhcp4, "", __VA_ARGS__)
 
 #define PGSQL_UPDATE_OPTION6_NO_TAG(...) \
-    PGSQL_UPDATE_OPTION_NO_TAG(dhcp6, ", pd_pool_id = $14 ", __VA_ARGS__)
+    PGSQL_UPDATE_OPTION_NO_TAG(dhcp6, ", pd_pool_id = $15 ", __VA_ARGS__)
 #endif
 
 #ifndef PGSQL_UPDATE_OPTION_WITH_TAG
@@ -1083,7 +1098,8 @@ namespace {
     "  user_context = cast($10 as json)," \
     "  shared_network_name = $11," \
     "  pool_id = $12," \
-    "  modification_ts = $13 " \
+    "  modification_ts = $13, " \
+    "  client_classes = $14 " \
     pd_pool_id \
     "FROM " #table_prefix "_options_server as a, " \
     "     " #table_prefix "_server as s " \
@@ -1092,11 +1108,11 @@ namespace {
     #__VA_ARGS__
 
 #define PGSQL_UPDATE_OPTION4_WITH_TAG(...) \
-    PGSQL_UPDATE_OPTION_WITH_TAG(dhcp4, "", AND s.tag = $14 __VA_ARGS__)
+    PGSQL_UPDATE_OPTION_WITH_TAG(dhcp4, "", AND s.tag = $15 __VA_ARGS__)
 
 #define PGSQL_UPDATE_OPTION6_WITH_TAG(...) \
     PGSQL_UPDATE_OPTION_WITH_TAG(dhcp6, \
-    ", pd_pool_id = $14 ", AND s.tag = $15 __VA_ARGS__)
+    ", pd_pool_id = $15 ", AND s.tag = $16 __VA_ARGS__)
 #endif
 
 #ifndef PGSQL_UPDATE_CLIENT_CLASS4