List:Commits« Previous MessageNext Message »
From:Magnus Svensson Date:September 15 2008 1:04pm
Subject:bzr commit into mysql-5.1 branch (msvensson:2763) WL#4350
View as plain text  
#At file:///home/msvensson/mysql/6.4/

 2763 Magnus Svensson	2008-09-15
      WL#4350 Extend Config
added:
  storage/ndb/src/mgmsrv/testConfig.cpp
modified:
  storage/ndb/src/mgmsrv/Config.cpp
  storage/ndb/src/mgmsrv/Config.hpp
  storage/ndb/src/mgmsrv/ConfigInfo.cpp
  storage/ndb/src/mgmsrv/Makefile.am

per-file messages:
  storage/ndb/src/mgmsrv/Config.cpp
    Add functions for diff, printing the diff and detecting if a config change is illegal
    Add utility functions for set/get generation
    Add wrapper for 'pack'
  storage/ndb/src/mgmsrv/Config.hpp
    Add functions for diff, printing the diff and detecting if a config change is illegal
    Add utility functions for set/get generation
    Add wrapper for 'pack'
  storage/ndb/src/mgmsrv/ConfigInfo.cpp
    Add missing initializer of m_section_name
  storage/ndb/src/mgmsrv/Makefile.am
    Add test program for config to be built ad testConfig
  storage/ndb/src/mgmsrv/testConfig.cpp
    Test program for Config
=== modified file 'storage/ndb/src/mgmsrv/Config.cpp'
--- a/storage/ndb/src/mgmsrv/Config.cpp	2008-07-29 14:04:27 +0000
+++ b/storage/ndb/src/mgmsrv/Config.cpp	2008-09-15 11:04:42 +0000
@@ -20,12 +20,25 @@
 #include "ConfigInfo.hpp"
 
 
+static void require(bool b)
+{
+  if (!b)
+    abort();
+}
+
+
 Config::Config(struct ndb_mgm_configuration *config_values) :
   m_configValues(config_values)
 {
 }
 
 
+Config::Config(ConfigValues *config_values) :
+  m_configValues((struct ndb_mgm_configuration*)config_values)
+{
+}
+
+
 Config::~Config() {
   if(m_configValues != 0){
     free(m_configValues);
@@ -47,7 +60,7 @@ Config::print() const {
 
   for(unsigned i= 0; i < num_sections; i++) {
     unsigned section= sections[i];
-    ndb_mgm_configuration_iterator it(*m_configValues, section);
+    ConfigIter it(this, section);
 
     if (it.first())
       continue;
@@ -81,3 +94,552 @@ Config::print() const {
     }
   }
 }
+
+
+
+Uint32
+Config::getGeneration() const
+{
+  Uint32 generation;
+  ConfigIter iter(this, CFG_SECTION_SYSTEM);
+
+  if (iter.get(CFG_SYS_CONFIG_GENERATION, &generation))
+    return 0;
+
+  return generation;
+}
+
+
+
+bool
+Config::setValue(Uint32 section, Uint32 section_no,
+                 Uint32 id, Uint32 new_val)
+{
+  ConfigValues::Iterator iter(m_configValues->m_config);
+  if (iter.openSection(section, section_no)){
+    if (!iter.set(id, new_val))
+      return false;
+  }
+  else
+  {
+    ConfigValuesFactory cf(&m_configValues->m_config);
+    if (!cf.openSection(section, section_no))
+      return false;
+    if (!cf.put(CFG_TYPE_OF_SECTION, section))
+      return false;
+    if (!cf.put(id, new_val))
+      return false;
+    cf.closeSection();
+
+    m_configValues= (struct ndb_mgm_configuration*)cf.getConfigValues();
+  }
+
+  return true;
+}
+
+
+bool
+Config::setGeneration(Uint32 new_gen)
+{
+  return setValue(CFG_SECTION_SYSTEM, 0,
+                  CFG_SYS_CONFIG_GENERATION,
+                  new_gen);
+}
+
+
+Uint32
+Config::pack(UtilBuffer& buf) const
+{
+  return m_configValues->m_config.pack(buf);
+}
+
+
+
+enum diff_types {
+  DT_DIFF,            // Value differed
+  DT_MISSING_VALUE,   // Value didn't exist
+  DT_MISSING_SECTION, // Section missing
+  DT_ILLEGAL_CHANGE    // Illegal change detected
+};
+
+
+static void
+add_diff(const char* name, const char* key,
+         Properties& diff,
+         const char* value_name, Properties* value)
+{
+
+  Properties *section;
+  // Create a new section if it did not exist
+  if (!diff.getCopy(key, &section)){
+    Properties new_section(true);
+    new_section.put("Key", key);
+    new_section.put("Name", name);
+
+    require(diff.put(key, &new_section));
+
+    // Get copy of section
+    require(diff.getCopy(key, &section));
+  }
+
+  // Make sure type of diff has been set
+  Uint32 type;
+  require(value->get("Type", &type));
+
+  // Add the value to the section if not already added
+  if (!section->put(value_name, value))
+    require(section->getPropertiesErrno() ==
+            E_PROPERTIES_ELEMENT_ALREADY_EXISTS);
+
+  // Put the updated section into the diff
+  require(diff.put(key, section, true));
+}
+
+
+static void
+compare_value(const char* name, const char* key,
+              const ConfigInfo::ParamInfo* pinfo,
+              ndb_mgm_configuration_iterator& it,
+              ndb_mgm_configuration_iterator& it2,
+              Properties& diff)
+{
+  Uint32 pid= pinfo->_paramId;
+  {
+    Uint32 val;
+    if (it.get(pid, &val) == 0) {
+      Uint32 val2;
+      if (it2.get(pid, &val2) == 0) {
+        if (val != val2){
+          Properties info(true);
+          info.put("Type", DT_DIFF);
+          info.put("New", val2);
+          info.put("Old", val);
+          add_diff(name, key,
+                   diff,
+                   pinfo->_fname, &info);
+        }
+      }
+      else
+      {
+        Properties info(true);
+        info.put("Type", DT_MISSING_VALUE);
+        info.put("Old", val);
+        add_diff(name, key,
+                 diff,
+                 pinfo->_fname, &info);
+      }
+      return;
+    }
+  }
+
+  {
+    Uint64 val;
+    if (it.get(pid, &val) == 0) {
+      Uint64 val2;
+      if (it2.get(pid, &val2) == 0) {
+        if (val != val2) {
+          Properties info(true);
+          info.put("Type", DT_DIFF);
+          info.put("New", val2);
+          info.put("Old", val);
+          add_diff(name, key,
+                   diff,
+                   pinfo->_fname, &info);
+        }
+      }
+      else
+      {
+        Properties info(true);
+        info.put("Type", DT_MISSING_VALUE);
+        info.put("Old", val);
+        add_diff(name, key,
+                 diff,
+                 pinfo->_fname, &info);
+      }
+      return;
+    }
+  }
+
+  {
+    const char* val;
+    if (it.get(pid, &val) == 0) {
+      const char* val2;
+      if (it2.get(pid, &val2) == 0) {
+        if (strcmp(val, val2)) {
+          Properties info(true);
+          info.put("Type", DT_DIFF);
+          info.put("New", val2);
+          info.put("Old", val);
+          add_diff(name, key,
+                   diff,
+                   pinfo->_fname, &info);
+        }
+      }
+      else
+      {
+        Properties info(true);
+        info.put("Type", DT_MISSING_VALUE);
+        info.put("Old", val);
+        add_diff(name, key,
+                 diff,
+                 pinfo->_fname, &info);
+      }
+      return;
+    }
+  }
+}
+
+
+static void
+diff_system(const Config* a, const Config* b, Properties& diff)
+{
+  ConfigIter itA(a, CFG_SECTION_SYSTEM);
+  ConfigIter itB(b, CFG_SECTION_SYSTEM);
+
+  // Check each possible configuration value
+  const ConfigInfo::ParamInfo* pinfo= NULL;
+  ConfigInfo::ParamInfoIter param_iter(g_info,
+                                       CFG_SECTION_SYSTEM,
+                                       CFG_SECTION_SYSTEM);
+  while((pinfo= param_iter.next())) {
+    /*  Loop through the section and compare values */
+    compare_value("SYSTEM", "", pinfo, itA, itB, diff);
+  }
+}
+
+
+static void
+diff_nodes(const Config* a, const Config* b, Properties& diff)
+{
+  ConfigIter itA(a, CFG_SECTION_NODE);
+
+  for(;itA.valid(); itA.next())
+  {
+
+    /* Get typ of Node */
+    Uint32 nodeType;
+    require(itA.get(CFG_TYPE_OF_SECTION, &nodeType) == 0);
+
+    BaseString name(g_info.sectionName(CFG_SECTION_NODE, nodeType));
+
+    /* Get NodeId which is "primary key" */
+    Uint32 nodeId;
+    require(itA.get(CFG_NODE_ID, &nodeId) == 0);
+
+    BaseString key;
+    key.assfmt("NodeId=%d", nodeId);
+
+    /* Position itB in the section with same NodeId */
+    ConfigIter itB(b, CFG_SECTION_NODE);
+    if (itB.find(CFG_NODE_ID, nodeId) != 0)
+    {
+      // A whole node has been removed
+      Properties info(true);
+      info.put("Type", DT_MISSING_SECTION);
+      info.put("Why", "Node removed");
+      add_diff(name.c_str(), key.c_str(),
+               diff,
+               "Node removed", &info);
+
+      continue;
+    }
+
+    /* Make sure it has the same node type */
+    Uint32 nodeType2;
+    require(itB.get(CFG_TYPE_OF_SECTION, &nodeType2) == 0);
+    if ((nodeType == NODE_TYPE_DB || nodeType == NODE_TYPE_MGM) &&
+        nodeType != nodeType2)
+    {
+      // DB or MGM node has changed type -> not allowed change
+      Properties info(true);
+      info.put("Type", DT_ILLEGAL_CHANGE);
+      info.put("Why", "Node has changed type");
+      add_diff(name.c_str(), key.c_str(),
+               diff,
+               "Node type changed", &info);
+      continue;
+    }
+
+    // Check each possible configuration value
+    const ConfigInfo::ParamInfo* pinfo= NULL;
+    ConfigInfo::ParamInfoIter param_iter(g_info, CFG_SECTION_NODE, nodeType);
+    while((pinfo= param_iter.next())) {
+      /*  Loop through the section and compare values */
+      compare_value(name.c_str(), key.c_str(), pinfo, itA, itB, diff);
+    }
+  }
+}
+
+
+static void
+diff_connections(const Config* a, const Config* b, Properties& diff)
+{
+  ConfigIter itA(a, CFG_SECTION_CONNECTION);
+
+  for(;itA.valid(); itA.next())
+  {
+    /* Get typ of connection */
+    Uint32 connectionType;
+    require(itA.get(CFG_TYPE_OF_SECTION, &connectionType) == 0);
+
+    BaseString name(g_info.sectionName(CFG_SECTION_CONNECTION, connectionType));
+
+    /* Get NodeId1 and NodeId2 which is "primary key" */
+    Uint32 nodeId1_A, nodeId2_A;
+    require(itA.get(CFG_CONNECTION_NODE_1, &nodeId1_A) == 0);
+    require(itA.get(CFG_CONNECTION_NODE_2, &nodeId2_A) == 0);
+
+    BaseString key;
+    key.assfmt("NodeId1=%d;NodeId2=%d", nodeId1_A, nodeId2_A);
+
+    /* Find the connecton in other config */
+    ConfigIter itB(b, CFG_SECTION_CONNECTION);
+    bool found= false;
+    Uint32 nodeId1_B, nodeId2_B;
+    while(itB.get(CFG_CONNECTION_NODE_1, &nodeId1_B) == 0 &&
+          itB.get(CFG_CONNECTION_NODE_2, &nodeId2_B) == 0)
+    {
+      if (nodeId1_A == nodeId1_B && nodeId2_A == nodeId2_B)
+      {
+        found= true;
+        break;
+      }
+
+      if(itB.next() != 0)
+        break;
+    }
+
+    if (!found)
+    {
+      // A connection has been removed
+      Properties info(true);
+      info.put("Type", DT_MISSING_SECTION);
+      info.put("Why", "Connection removed");
+      add_diff(name.c_str(), key.c_str(),
+               diff,
+               "Connection removed", &info);
+
+      continue;
+    }
+
+    // Check each possible configuration value
+    const ConfigInfo::ParamInfo* pinfo= NULL;
+    ConfigInfo::ParamInfoIter param_iter(g_info,
+                                         CFG_SECTION_CONNECTION,
+                                         connectionType);
+    while((pinfo= param_iter.next())) {
+      /*  Loop through the section and compare values */
+      compare_value(name.c_str(), key.c_str(), pinfo, itA, itB, diff);
+    }
+  }
+}
+
+
+static bool
+include_section(const unsigned* exclude, unsigned section){
+  if (exclude == NULL)
+    return true;
+
+  while(*exclude){
+    if (*exclude == section)
+      return false;
+    exclude++;
+  }
+  return true;
+}
+
+
+/**
+  Generate a diff list
+*/
+
+void Config::diff(const Config* other, Properties& diff,
+                  const unsigned* exclude) const {
+
+  if (include_section(exclude, CFG_SECTION_SYSTEM)) {
+    diff_system(this, other, diff);
+    diff_system(other, this, diff);
+  }
+  if (include_section(exclude, CFG_SECTION_NODE)) {
+    diff_nodes(this, other, diff);
+    diff_nodes(other, this, diff);
+  }
+  if (include_section(exclude, CFG_SECTION_CONNECTION)) {
+    diff_connections(this, other, diff);
+    diff_connections(other, this, diff);
+  }
+}
+
+
+static const char*
+p2s(const Properties* prop, const char* name, BaseString& buf){
+  PropertiesType type;
+  require(prop->getTypeOf(name, &type));
+  switch(type){
+  case PropertiesType_Uint32:
+  {
+    Uint32 val;
+    require(prop->get(name, &val));
+    buf.assfmt("%d", val);
+    break;
+  }
+  case PropertiesType_Uint64:
+  {
+    Uint64 val;
+    require(prop->get(name, &val));
+    buf.assfmt("%lld", val);
+    break;
+  }
+  case PropertiesType_char:
+  {
+    require(prop->get(name, buf));
+    break;
+  }
+  default:
+    require(false);
+    break;
+  }
+  return buf.c_str();
+}
+
+
+const char*
+Config::diff2str(const Properties& diff_list, BaseString& str) const
+{
+  const char* name;
+  Properties::Iterator prop_it(&diff_list);
+  while ((name= prop_it.next())){
+
+    const Properties *node;
+    require(diff_list.get(name, &node));
+
+    require(node->get("Name", &name));
+    str.appfmt("[%s]\n", name);
+
+    BaseString key;
+    require(node->get("Key", key));
+    if (key.length() > 0){
+      Vector<BaseString> keys;
+      key.split(keys, ";");
+      for (unsigned i= 0; i < keys.size(); i++)
+        str.appfmt("%s\n", keys[i].c_str());
+    }
+
+    BaseString buf;
+    Properties::Iterator prop_it2(node);
+    while ((name= prop_it2.next())){
+
+      const Properties *what;
+      if (!node->get(name, &what))
+        continue;
+
+      Uint32 type;
+      require(what->get("Type", &type));
+      switch (type) {
+      case DT_DIFF:
+      {
+        str.appfmt("-%s=%s\n", name, p2s(what, "Old", buf));
+        str.appfmt("+%s=%s\n", name, p2s(what, "New", buf));
+        break;
+      }
+
+      case DT_MISSING_VALUE:
+      {
+        str.appfmt("-%s=%s\n", name, p2s(what, "Old", buf));
+        break;
+      }
+
+      case DT_MISSING_SECTION:
+      {
+        const char* why;
+        if (what->get("Why", &why))
+          str.appfmt("%s\n", why);
+        break;
+      }
+
+      case DT_ILLEGAL_CHANGE:
+      {
+        const char* why;
+        str.appfmt("Illegal change\n");
+        if (what->get("Why", &why))
+          str.appfmt("%s\n", why);
+        break;
+      }
+
+      default:
+        str.appfmt("Illegal 'type' found in diff_list\n");
+        require(false);
+        break;
+      }
+    }
+    str.appfmt("\n");
+  }
+  return str.c_str();
+}
+
+
+void Config::print_diff(const Config* other) const {
+  Properties diff_list;
+  diff(other, diff_list);
+  BaseString str;
+  ndbout_c("%s", diff2str(diff_list, str));
+}
+
+
+const char*
+Config::diff2str(const Config* other, BaseString& str) const {
+  Properties diff_list;
+  diff(other, diff_list);
+  return diff2str(diff_list, str);
+}
+
+
+bool Config::equal(const Properties& diff_list) const {
+  int count= 0;
+  Properties::Iterator prop_it(&diff_list);
+  while ((prop_it.next()))
+    count++;
+  return (count == 0);
+}
+
+
+bool Config::equal(const Config* other, const unsigned * exclude) const {
+  Properties diff_list;
+  diff(other, diff_list, exclude);
+  return equal(diff_list);
+}
+
+
+
+bool Config::illegal_change(const Properties& diff_list) const {
+  bool illegal= false;
+  const char* name;
+  Properties::Iterator prop_it(&diff_list);
+  while ((name= prop_it.next())){
+
+    const Properties *node;
+    require(diff_list.get(name, &node));
+
+    Properties::Iterator prop_it2(node);
+    while ((name= prop_it2.next())){
+
+      const Properties *what;
+      if (!node->get(name, &what))
+        continue;
+
+      Uint32 type;
+      require(what->get("Type", &type));
+      if (type == DT_ILLEGAL_CHANGE)
+        illegal= true;
+    }
+  }
+  return illegal;
+}
+
+
+bool Config::illegal_change(const Config* other) const {
+  Properties diff_list;
+  diff(other, diff_list);
+  return illegal_change(diff_list);
+}
+

=== modified file 'storage/ndb/src/mgmsrv/Config.hpp'
--- a/storage/ndb/src/mgmsrv/Config.hpp	2008-07-29 13:38:18 +0000
+++ b/storage/ndb/src/mgmsrv/Config.hpp	2008-09-15 11:04:42 +0000
@@ -32,11 +32,59 @@
 class Config {
 public:
   Config(struct ndb_mgm_configuration *config_values = NULL);
+  Config(ConfigValues* config_values);
   virtual ~Config();
 
   void print() const;
 
+  /*
+    Returns generation of the config
+    0 => not set(yet), ie. config has never been committed
+   */
+  Uint32 getGeneration() const;
+  bool setGeneration(Uint32);
+
+  /*
+   Pack the config into a UtilBuffer and return it's size in bytes
+  */
+  Uint32 pack(UtilBuffer&) const;
+
+  /*
+    Compare against another config and return a list of
+    differences in a Properties object
+  */
+  void diff(const Config* other, Properties& diff_list,
+            const unsigned* exclude=NULL) const;
+
+  /*
+    Print the difference against another config
+   */
+  void print_diff(const Config* other) const;
+
+  /*
+    Print the difference to string buffer
+  */
+  const char* diff2str(const Config* other, BaseString& str) const;
+
+  /*
+    Determine if changing to the other config is illegal
+  */
+  bool illegal_change(const Config* other) const;
+
+  /*
+    Check if the config is equal to another config
+  */
+  bool equal(const Config*, const unsigned* exclude = NULL) const;
+
   struct ndb_mgm_configuration * m_configValues;
+
+private:
+  bool setValue(Uint32 section, Uint32 section_no,
+                Uint32 id, Uint32 new_gen);
+
+  bool illegal_change(const Properties&) const;
+  bool equal(const Properties&) const;
+  const char* diff2str(const Properties&, BaseString& str) const;
 };
 
 class ConfigIter : public ndb_mgm_configuration_iterator {

=== modified file 'storage/ndb/src/mgmsrv/ConfigInfo.cpp'
--- a/storage/ndb/src/mgmsrv/ConfigInfo.cpp	2008-09-12 12:55:29 +0000
+++ b/storage/ndb/src/mgmsrv/ConfigInfo.cpp	2008-09-15 11:04:42 +0000
@@ -4420,6 +4420,7 @@ ConfigInfo::ParamInfoIter::ParamInfoIter
                                          Uint32 section,
                                          Uint32 section_type) :
   m_info(info),
+  m_section_name(NULL),
   m_curr_param(0)
 {
   /* Find the section's name */

=== modified file 'storage/ndb/src/mgmsrv/Makefile.am'
--- a/storage/ndb/src/mgmsrv/Makefile.am	2008-06-09 10:52:24 +0000
+++ b/storage/ndb/src/mgmsrv/Makefile.am	2008-09-15 11:04:42 +0000
@@ -30,6 +30,15 @@ ndb_mgmd_SOURCES = \
 	InitConfigFileParser.cpp \
 	Config.cpp
 
+
+noinst_PROGRAMS = testConfig
+
+testConfig_SOURCES = \
+	testConfig.cpp \
+	InitConfigFileParser.cpp \
+	ConfigInfo.cpp \
+	Config.cpp
+
 INCLUDES_LOC = -I$(top_srcdir)/storage/ndb/src/ndbapi \
                -I$(top_srcdir)/storage/ndb/src/mgmapi \
                -I$(top_srcdir)/storage/ndb/src/common/mgmcommon \

=== added file 'storage/ndb/src/mgmsrv/testConfig.cpp'
--- a/storage/ndb/src/mgmsrv/testConfig.cpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/src/mgmsrv/testConfig.cpp	2008-09-15 11:04:42 +0000
@@ -0,0 +1,166 @@
+#include <ndb_global.h>
+#include "InitConfigFileParser.hpp"
+#include "ConfigInfo.hpp"
+#include "Config.hpp"
+
+my_bool opt_core= 1;
+my_bool opt_ndb_shm= 0;
+
+
+#define CHECK(x) \
+  if (!(x)) {\
+    fprintf(stderr, "testConfig: '"#x"' failed on line %d\n", __LINE__); \
+    exit(1); \
+  }
+
+
+static const ConfigInfo g_info;
+
+/*
+  Create a small config.ini with the given parameter and run
+  it through InitConfigFileParser
+ */
+bool
+check_param(const ConfigInfo::ParamInfo & param)
+{
+
+  FILE* config_file= tmpfile();
+  CHECK(config_file);
+
+  const char* section= g_info.nameToAlias(param._section);
+  if (section == NULL)
+    section= param._section;
+
+  if (param._type == ConfigInfo::CI_SECTION)
+    return true;
+
+  if(param._default == MANDATORY)
+  {
+    // Mandatory parameter
+    return true;
+  }
+  else
+  {
+    fprintf(config_file, "[%s]\n", section);
+    fprintf(config_file, "%s=%s\n", param._fname, param._default);
+  }
+
+  // Fill in lines needed for a minimal config
+  if (strcmp(section, "NDBD") != 0)
+    fprintf(config_file, "[ndbd]\n");
+  if (strcmp(param._fname, "NoOfReplicas") != 0)
+    fprintf(config_file, "NoOfReplicas=1\n");
+
+  if (strcmp(section, "NDB_MGMD") != 0)
+    fprintf(config_file, "[ndb_mgmd]\n");
+  if (strcmp(param._fname, "Hostname") != 0)
+    fprintf(config_file, "HostName=localhost\n");
+
+  if (strcmp(section, "MYSQLD") != 0)
+    fprintf(config_file, "[mysqld]\n");
+
+  rewind(config_file);
+
+  // Run the config file through InitConfigFileParser
+  // throw away the error messages for now.
+  FILE* err_file= tmpfile();
+  InitConfigFileParser parser(err_file);
+  Config* conf = parser.parseConfig(config_file);
+  fclose(config_file);
+  fclose(err_file);
+
+  return (conf != NULL);
+}
+
+
+bool
+check_params(void)
+{
+  bool ok= true;
+  for (int j=0; j<g_info.m_NoOfParams; j++) {
+    const ConfigInfo::ParamInfo & param= g_info.m_ParamInfo[j];
+    if (!check_param(param))
+    {
+      ok= false;
+    }
+  }
+
+  return true; // Ignore ok for now
+}
+
+
+
+Config*
+create_config(const char* first, ...)
+{
+  va_list args;
+
+  FILE* config_file= tmpfile();
+  CHECK(config_file);
+
+  va_start(args, first);
+  const char* str= first;
+  do
+    fprintf(config_file, "%s\n", str);
+  while((str= va_arg(args, const char*)) != NULL);
+  va_end(args);
+
+#if 0
+  rewind(config_file);
+
+  char buf[100];
+  while(fgets(buf, sizeof(buf), config_file))
+    ndbout_c(buf);
+#endif
+
+  rewind(config_file);
+
+  InitConfigFileParser parser;
+  Config* conf = parser.parseConfig(config_file);
+  CHECK(conf);
+  fclose(config_file);
+
+  return conf;
+}
+
+
+
+
+void
+diff_config(void)
+{
+  Config* c1=
+    create_config("[ndbd]", "NoOfReplicas=1",
+                  "[ndb_mgmd]", "HostName=localhost",
+                  "[mysqld]", NULL);
+  Config* c2=
+    create_config("[ndbd]", "NoOfReplicas=1",
+                  "[ndb_mgmd]", "HostName=localhost",
+                  "[mysqld]", "[mysqld]", NULL);
+
+  CHECK(c1->equal(c1));
+
+  CHECK(!c1->equal(c2));
+  CHECK(!c2->equal(c1));
+  CHECK(!c2->illegal_change(c1));
+  CHECK(!c1->illegal_change(c2));
+
+
+  ndbout_c("==================");
+  ndbout_c("c1->print_diff(c2)");
+  c1->print_diff(c2);
+  ndbout_c("==================");
+  ndbout_c("c2->print_diff(c1)");
+  c2->print_diff(c1);
+  ndbout_c("==================");
+}
+
+
+int
+main(void){
+  ndbout_c("1..1");
+  diff_config();
+  CHECK(check_params());
+  ndbout_c("ok");
+  exit(0);
+}

Thread
bzr commit into mysql-5.1 branch (msvensson:2763) WL#4350Magnus Svensson15 Sep