List:Commits« Previous MessageNext Message »
From:magnus.blaudd Date:May 23 2011 3:31pm
Subject:bzr push into mysql-5.1-telco-7.0 branch (magnus.blaudd:4399 to 4403)
Bug#12581954
View as plain text  
 4403 magnus.blaudd@stripped	2011-05-23
      Bug#12581954 - ndbinfo need to reload its cache of ndbinfo tables during online upgrade
       - When upgrading the MySQL Servers in a cluster before the datanodes, the
        ndbinfo API does not flush it's cache of ndbinfo tables and columns. Only
        full disconnect of the cluster is currently detected. And of course
        restarting the MySQL Server 
       - Make ndbinfo api detect when the "min db version" of cluster has changed.

    modified:
      storage/ndb/include/ndbapi/ndb_cluster_connection.hpp
      storage/ndb/src/ndbapi/NdbInfo.cpp
      storage/ndb/src/ndbapi/NdbInfo.hpp
      storage/ndb/src/ndbapi/TransporterFacade.hpp
      storage/ndb/src/ndbapi/ndb_cluster_connection.cpp
      storage/ndb/src/ndbapi/ndb_cluster_connection_impl.hpp
 4402 magnus.blaudd@stripped	2011-05-23
      Bug#12581895 - ndbinfo should support opening tables with more or differently named columns
       - Add support for opening a table with more fields than exist in NDBINFO,
         all fields not sent by NDB will be set to NULL
       - Add check to prevent opening a table with fields which are NOT NULL
       - Extend NdbInfoRecAttr with isNULL()

    modified:
      mysql-test/suite/ndb/r/ndbinfo.result
      mysql-test/suite/ndb/t/ndbinfo.test
      sql/ha_ndbinfo.cc
      storage/ndb/src/ndbapi/NdbInfoRecAttr.hpp
      storage/ndb/src/ndbapi/NdbInfoScanOperation.cpp
 4401 magnus.blaudd@stripped	2011-05-23
      ndbinfo
       - rename all option variables in ndbinfo to have "opt_ndbinfo_" as prefix 

    modified:
      sql/ha_ndbinfo.cc
 4400 magnus.blaudd@stripped	2011-05-23
      Bug#11885602 - mysql_upgrade fails upgrading from 7.1.8 to later release
       - It was allowed to CREATE TABLE which was not in NDB, but
         creating a view on that table failed since the table has to
         be opened as part of the create.
       - Make it possible to workaround this with a new "ndbinfo_offline"
         mode which allows tables to be created and opened although they
         don't exist or have different table definition. This is exactly
         the same behaviour as when NDBCLUSTER is disabled. SELECTs on a
         table which has been opened in offline mode, return no rows
         and a warning is printed(like before).

    modified:
      mysql-test/suite/ndb/r/ndbinfo.result
      mysql-test/suite/ndb/t/ndbinfo.test
      sql/ha_ndbinfo.cc
      sql/ha_ndbinfo.h
      storage/ndb/tools/ndbinfo_sql.cpp
 4399 Jonas Oreland	2011-05-23
      ndb - bug#12581213 fix accounting of MaxNoOfOpenFiles vs. unbound files (data files)

    added:
      mysql-test/suite/ndb/r/ndb_dd_bug12581213.result
      mysql-test/suite/ndb/t/ndb_dd_bug12581213.cnf
      mysql-test/suite/ndb/t/ndb_dd_bug12581213.test
    modified:
      storage/ndb/src/kernel/blocks/ndbfs/AsyncIoThread.cpp
      storage/ndb/src/kernel/blocks/ndbfs/AsyncIoThread.hpp
      storage/ndb/src/kernel/blocks/ndbfs/Ndbfs.cpp
      storage/ndb/src/kernel/blocks/ndbfs/Ndbfs.hpp
      storage/ndb/src/kernel/blocks/ndbfs/Pool.hpp
=== modified file 'mysql-test/suite/ndb/r/ndbinfo.result'
--- a/mysql-test/suite/ndb/r/ndbinfo.result	2010-11-03 09:48:25 +0000
+++ b/mysql-test/suite/ndb/r/ndbinfo.result	2011-05-23 13:45:57 +0000
@@ -136,6 +136,7 @@ Variable_name	Value
 ndbinfo_database	ndbinfo
 ndbinfo_max_bytes	0
 ndbinfo_max_rows	10
+ndbinfo_offline	OFF
 ndbinfo_show_hidden	OFF
 ndbinfo_table_prefix	ndb$
 ndbinfo_version	NDB_VERSION_D
@@ -179,20 +180,55 @@ node_id != 0
 1
 Warnings:
 Warning	40001	Table 'ndb$test' is defined differently in NDB, there are more columns available. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
-
-## 2) Column does not exist in NDB -> error, with warning
 DROP TABLE ndb$test;
+
+## 2) Column does not exist in NDB -> allowed, with warning, non existing
+##    column(s) return NULL
+## 2a) Extra column at end
 CREATE TABLE ndb$test (node_id int, non_existing int) ENGINE = ndbinfo;
-SELECT * FROM ndb$test;
-ERROR HY000: Got error 40001 'Incompatible table definitions' from NDBINFO
-SHOW WARNINGS;
-Level	Code	Message
+SELECT DISTINCT node_id, non_existing FROM ndb$test;
+node_id	non_existing
+1	NULL
+2	NULL
+Warnings:
 Error	40001	Table 'ndb$test' is defined differently in NDB, column 'non_existing' does not exist. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
-Error	1296	Got error 40001 'Incompatible table definitions' from NDBINFO
+Warning	40001	Table 'ndb$test' is defined differently in NDB, there are more columns available. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
+DROP TABLE ndb$test;
+
+## 2b) Extra column(s) in middle
+CREATE TABLE ndb$test (
+  node_id int unsigned,
+  non_existing int unsigned,
+  block_number int unsigned,
+  block_instance int unsigned,
+  counter int unsigned,
+  counter2 bigint unsigned
+) ENGINE = ndbinfo;
+SELECT DISTINCT node_id, non_existing, block_number FROM ndb$test;
+node_id	non_existing	block_number
+1	NULL	249
+2	NULL	249
+Warnings:
+Error	40001	Table 'ndb$test' is defined differently in NDB, column 'non_existing' does not exist. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
+DROP TABLE ndb$test;
+
+## 2c) Extra column first
+CREATE TABLE ndb$test (non_existing int, node_id int) ENGINE = ndbinfo;
+SELECT DISTINCT node_id, non_existing FROM ndb$test;
+node_id	non_existing
+1	NULL
+2	NULL
+Warnings:
+Error	40001	Table 'ndb$test' is defined differently in NDB, column 'non_existing' does not exist. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
+Warning	40001	Table 'ndb$test' is defined differently in NDB, there are more columns available. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
+SELECT DISTINCT non_existing, node_id FROM ndb$test;
+non_existing	node_id
+NULL	1
+NULL	2
+DROP TABLE ndb$test;
 
 ## 3) Incompatible column type -> error, with warning
 ## 3a) int instead of bigint
-DROP TABLE ndb$test;
 CREATE TABLE ndb$test (counter2 int) ENGINE = ndbinfo;
 SELECT * FROM ndb$test;
 ERROR HY000: Got error 40001 'Incompatible table definitions' from NDBINFO
@@ -219,6 +255,26 @@ Level	Code	Message
 Error	40001	Table 'ndb$test' is defined differently in NDB, column 'node_id' is not compatible. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
 Error	1296	Got error 40001 'Incompatible table definitions' from NDBINFO
 DROP TABLE ndb$test;
+## 3d) column which is NOT NULL
+CREATE TABLE ndb$test (node_id int unsigned NOT NULL) ENGINE = ndbinfo;
+SELECT * FROM ndb$test;
+ERROR HY000: Got error 40001 'Incompatible table definitions' from NDBINFO
+SHOW WARNINGS;
+Level	Code	Message
+Error	40001	Table 'ndb$test' is defined differently in NDB, column 'node_id' is NOT NULL. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
+Error	1296	Got error 40001 'Incompatible table definitions' from NDBINFO
+DROP TABLE ndb$test;
+## 3e) non existing column which is NOT NULL
+CREATE TABLE ndb$test (
+  block_number int unsigned,
+  non_existing int NOT NULL) ENGINE = ndbinfo;
+SELECT * FROM ndb$test;
+ERROR HY000: Got error 40001 'Incompatible table definitions' from NDBINFO
+SHOW WARNINGS;
+Level	Code	Message
+Error	40001	Table 'ndb$test' is defined differently in NDB, column 'non_existing' is NOT NULL. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
+Error	1296	Got error 40001 'Incompatible table definitions' from NDBINFO
+DROP TABLE ndb$test;
 
 ## 4) Table with primary key/indexes not supported
 CREATE TABLE ndb$test (node_id int, block_number int PRIMARY KEY) ENGINE = ndbinfo;
@@ -238,3 +294,55 @@ node_id
 1
 2
 
+set @@ndbinfo_offline=1;
+ERROR HY000: Variable 'ndbinfo_offline' is a GLOBAL variable and should be set with SET GLOBAL
+
+SELECT DISTINCT(node_id) FROM ndbinfo.counters ORDER BY node_id;
+node_id
+1
+2
+
+set @@global.ndbinfo_offline=TRUE;
+select @@ndbinfo_offline;
+@@ndbinfo_offline
+1
+
+CREATE TABLE ndb$does_not_exist_in_ndb(
+  node_id int,
+  message varchar(255)
+) ENGINE = ndbinfo;
+
+CREATE VIEW view_on_table_which_does_not_exist_in_ndb AS
+  SELECT node_id, message
+  FROM ndbinfo.ndb$does_not_exist_in_ndb;
+
+SHOW CREATE TABLE ndb$does_not_exist_in_ndb;
+Table	Create Table
+ndb$does_not_exist_in_ndb	CREATE TABLE `ndb$does_not_exist_in_ndb` (
+  `node_id` int(11) DEFAULT NULL,
+  `message` varchar(255) DEFAULT NULL
+) ENGINE=NDBINFO DEFAULT CHARSET=latin1
+
+SELECT * FROM view_on_table_which_does_not_exist_in_ndb;
+node_id	message
+Warnings:
+Note	1	'NDBINFO' has been started in offline mode since the 'NDBCLUSTER' engine is disabled or @@global.ndbinfo_offline is turned on - no rows can be returned
+SELECT * FROM ndb$does_not_exist_in_ndb;
+node_id	message
+Warnings:
+Note	1	'NDBINFO' has been started in offline mode since the 'NDBCLUSTER' engine is disabled or @@global.ndbinfo_offline is turned on - no rows can be returned
+SELECT DISTINCT(node_id) FROM ndbinfo.counters ORDER BY node_id;
+node_id
+Warnings:
+Note	1	'NDBINFO' has been started in offline mode since the 'NDBCLUSTER' engine is disabled or @@global.ndbinfo_offline is turned on - no rows can be returned
+
+DROP VIEW view_on_table_which_does_not_exist_in_ndb;
+DROP TABLE ndb$does_not_exist_in_ndb;
+
+set @@global.ndbinfo_offline = FALSE;
+
+SELECT DISTINCT(node_id) FROM ndbinfo.counters ORDER BY node_id;
+node_id
+1
+2
+

=== modified file 'mysql-test/suite/ndb/t/ndbinfo.test'
--- a/mysql-test/suite/ndb/t/ndbinfo.test	2010-11-03 09:48:25 +0000
+++ b/mysql-test/suite/ndb/t/ndbinfo.test	2011-05-23 13:45:57 +0000
@@ -98,17 +98,35 @@ SELECT count(*) >= 20 FROM blocks;
 DROP TABLE ndb$test;
 CREATE TABLE ndb$test (node_id int unsigned) ENGINE = ndbinfo;
 SELECT node_id != 0 FROM ndb$test LIMIT 1;
-
-## 2) Column does not exist in NDB -> error, with warning
 DROP TABLE ndb$test;
+
+## 2) Column does not exist in NDB -> allowed, with warning, non existing
+##    column(s) return NULL
+## 2a) Extra column at end
 CREATE TABLE ndb$test (node_id int, non_existing int) ENGINE = ndbinfo;
---error ER_GET_ERRMSG
-SELECT * FROM ndb$test;
-SHOW WARNINGS;
+SELECT DISTINCT node_id, non_existing FROM ndb$test;
+DROP TABLE ndb$test;
+
+## 2b) Extra column(s) in middle
+CREATE TABLE ndb$test (
+  node_id int unsigned,
+  non_existing int unsigned,
+  block_number int unsigned,
+  block_instance int unsigned,
+  counter int unsigned,
+  counter2 bigint unsigned
+) ENGINE = ndbinfo;
+SELECT DISTINCT node_id, non_existing, block_number FROM ndb$test;
+DROP TABLE ndb$test;
+
+## 2c) Extra column first
+CREATE TABLE ndb$test (non_existing int, node_id int) ENGINE = ndbinfo;
+SELECT DISTINCT node_id, non_existing FROM ndb$test;
+SELECT DISTINCT non_existing, node_id FROM ndb$test;
+DROP TABLE ndb$test;
 
 ## 3) Incompatible column type -> error, with warning
 ## 3a) int instead of bigint
-DROP TABLE ndb$test;
 CREATE TABLE ndb$test (counter2 int) ENGINE = ndbinfo;
 --error ER_GET_ERRMSG
 SELECT * FROM ndb$test;
@@ -126,6 +144,21 @@ CREATE TABLE ndb$test (node_id varchar(2
 SELECT * FROM ndb$test;
 SHOW WARNINGS;
 DROP TABLE ndb$test;
+## 3d) column which is NOT NULL
+CREATE TABLE ndb$test (node_id int unsigned NOT NULL) ENGINE = ndbinfo;
+--error ER_GET_ERRMSG
+SELECT * FROM ndb$test;
+SHOW WARNINGS;
+DROP TABLE ndb$test;
+## 3e) non existing column which is NOT NULL
+CREATE TABLE ndb$test (
+  block_number int unsigned,
+  non_existing int NOT NULL) ENGINE = ndbinfo;
+--error ER_GET_ERRMSG
+SELECT * FROM ndb$test;
+SHOW WARNINGS;
+DROP TABLE ndb$test;
+
 
 ## 4) Table with primary key/indexes not supported
 --error ER_TOO_MANY_KEYS
@@ -148,4 +181,52 @@ CREATE TABLE ndb$test (node_id int AUTO_
 select distinct node_id
 from ndbinfo.diskpagebuffer;
 
+
+#
+# BUG#11885602
+# - It was allowed to CREATE TABLE which was not in NDB, but
+#   creating a view on that table failed. Implement ndbinfo_offline
+#   mode which allows tables to be created and opened although they
+#   don't exists or have different table definition.
+#   This is exactly the same behaviour as when NDBCLUSTER
+#   is disabled
+#
+
+# Check ndbinfo_offline is GLOBAL variable
+--error ER_GLOBAL_VARIABLE
+set @@ndbinfo_offline=1;
+
+# Query used to check that open tables are closed
+# when offline mode is turned on and off
+let $q1 = SELECT DISTINCT(node_id) FROM ndbinfo.counters ORDER BY node_id;
+eval $q1;
+
+# Turn on ndbinfo_offline
+set @@global.ndbinfo_offline=TRUE;
+select @@ndbinfo_offline;
+
+CREATE TABLE ndb$does_not_exist_in_ndb(
+  node_id int,
+  message varchar(255)
+) ENGINE = ndbinfo;
+
+CREATE VIEW view_on_table_which_does_not_exist_in_ndb AS
+  SELECT node_id, message
+  FROM ndbinfo.ndb$does_not_exist_in_ndb;
+
+SHOW CREATE TABLE ndb$does_not_exist_in_ndb;
+
+# SELECTs return no rows in offline mode
+SELECT * FROM view_on_table_which_does_not_exist_in_ndb;
+SELECT * FROM ndb$does_not_exist_in_ndb;
+eval $q1;
+
+DROP VIEW view_on_table_which_does_not_exist_in_ndb;
+DROP TABLE ndb$does_not_exist_in_ndb;
+
+# Restore original value
+set @@global.ndbinfo_offline = FALSE;
+
+eval $q1;
+
 --source ndbinfo_drop.inc

=== modified file 'sql/ha_ndbinfo.cc'
--- a/sql/ha_ndbinfo.cc	2010-11-10 14:17:13 +0000
+++ b/sql/ha_ndbinfo.cc	2011-05-23 13:45:57 +0000
@@ -56,10 +56,10 @@ static MYSQL_THDVAR_BOOL(
   FALSE                              /* default */
 );
 
-static char* ndbinfo_dbname = (char*)"ndbinfo";
+static char* opt_ndbinfo_dbname = (char*)"ndbinfo";
 static MYSQL_SYSVAR_STR(
   database,                         /* name */
-  ndbinfo_dbname,                   /* var */
+  opt_ndbinfo_dbname,               /* var */
   PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
   "Name of the database used by ndbinfo",
   NULL,                             /* check func. */
@@ -67,10 +67,10 @@ static MYSQL_SYSVAR_STR(
   NULL                              /* default */
 );
 
-static char* table_prefix = (char*)"ndb$";
+static char* opt_ndbinfo_table_prefix = (char*)"ndb$";
 static MYSQL_SYSVAR_STR(
   table_prefix,                     /* name */
-  table_prefix,                     /* var */
+  opt_ndbinfo_table_prefix,         /* var */
   PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
   "Prefix to use for all virtual tables loaded from NDB",
   NULL,                             /* check func. */
@@ -78,10 +78,10 @@ static MYSQL_SYSVAR_STR(
   NULL                              /* default */
 );
 
-static Uint32 version = NDB_VERSION_D;
+static Uint32 opt_ndbinfo_version = NDB_VERSION_D;
 static MYSQL_SYSVAR_UINT(
   version,                          /* name */
-  version,                          /* var */
+  opt_ndbinfo_version,              /* var */
   PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_READONLY,
   "Compile version for ndbinfo",
   NULL,                             /* check func. */
@@ -92,6 +92,45 @@ static MYSQL_SYSVAR_UINT(
   0                                 /* block */
 );
 
+static my_bool opt_ndbinfo_offline;
+
+static
+void
+offline_update(THD* thd, struct st_mysql_sys_var* var,
+               void* var_ptr, const void* save)
+{
+  DBUG_ENTER("offline_update");
+
+  const my_bool new_offline =
+    (*(static_cast<const my_bool*>(save)) != 0);
+  if (new_offline == opt_ndbinfo_offline)
+  {
+    // No change
+    DBUG_VOID_RETURN;
+  }
+
+  // Set offline mode, any tables opened from here on will
+  // be opened in the new mode
+  opt_ndbinfo_offline = new_offline;
+
+  // Close any open tables which may be in the old mode
+  (void)close_cached_tables(thd, NULL, false, true, false);
+
+  DBUG_VOID_RETURN;
+}
+
+static MYSQL_SYSVAR_BOOL(
+  offline,                          /* name */
+  opt_ndbinfo_offline,              /* var */
+  PLUGIN_VAR_NOCMDOPT,
+  "Set ndbinfo in offline mode, tables and views can "
+  "be opened even if they don't exist or have different "
+  "definition in NDB. No rows will be returned.",
+  NULL,                             /* check func. */
+  offline_update,                   /* update func. */
+  0                                 /* default */
+);
+
 
 static NdbInfo* g_ndbinfo;
 
@@ -124,10 +163,15 @@ struct ha_ndbinfo_impl
   Vector<const NdbInfoRecAttr *> m_columns;
   bool m_first_use;
 
+  // Indicates if table has been opened in offline mode
+  // can only be reset by closing the table
+  bool m_offline;
+
   ha_ndbinfo_impl() :
     m_table(NULL),
     m_scan_op(NULL),
-    m_first_use(true)
+    m_first_use(true),
+    m_offline(false)
   {
   }
 };
@@ -211,7 +255,7 @@ static void
 generate_sql(const NdbInfo::Table* ndb_tab, BaseString& sql)
 {
   sql.appfmt("'CREATE TABLE `%s`.`%s%s` (",
-             ndbinfo_dbname, table_prefix, ndb_tab->getName());
+             opt_ndbinfo_dbname, opt_ndbinfo_table_prefix, ndb_tab->getName());
 
   const char* separator = "";
   for (unsigned i = 0; i < ndb_tab->columns(); i++)
@@ -265,7 +309,7 @@ warn_incompatible(const NdbInfo::Table* 
 
   msg.assfmt("Table '%s%s' is defined differently in NDB, %s. The "
              "SQL to regenerate is: ",
-             table_prefix, ndb_tab->getName(), explanation);
+             opt_ndbinfo_table_prefix, ndb_tab->getName(), explanation);
   generate_sql(ndb_tab, msg);
 
   const MYSQL_ERROR::enum_warning_level level =
@@ -289,12 +333,18 @@ bool ha_ndbinfo::is_open(void) const
   return m_impl.m_table != NULL;
 }
 
+bool ha_ndbinfo::is_offline(void) const
+{
+  return m_impl.m_offline;
+}
+
 int ha_ndbinfo::open(const char *name, int mode, uint test_if_locked)
 {
   DBUG_ENTER("ha_ndbinfo::open");
   DBUG_PRINT("enter", ("name: %s, mode: %d", name, mode));
 
   assert(is_closed());
+  assert(!is_offline()); // Closed table can not be offline
 
   if (mode == O_RDWR)
   {
@@ -307,9 +357,11 @@ int ha_ndbinfo::open(const char *name, i
     DBUG_ASSERT(false);
   }
 
-  if (ndbcluster_is_disabled())
+  if (opt_ndbinfo_offline ||
+      ndbcluster_is_disabled())
   {
-    // Allow table to be opened with ndbcluster disabled
+    // Mark table as being offline and allow it to be opened
+    m_impl.m_offline = true;
     DBUG_RETURN(0);
   }
 
@@ -321,21 +373,36 @@ int ha_ndbinfo::open(const char *name, i
     DBUG_RETURN(err2mysql(err));
   }
 
+  /*
+    Check table def. to detect incompatible differences which should
+    return an error. Differences which only generate a warning
+    is checked on first use
+  */
   DBUG_PRINT("info", ("Comparing MySQL's table def against NDB"));
   const NdbInfo::Table* ndb_tab = m_impl.m_table;
   for (uint i = 0; i < table->s->fields; i++)
   {
     const Field* field = table->field[i];
-    const NdbInfo::Column* col = ndb_tab->getColumn(field->field_name);
-    if (!col)
+
+    // Check if field is NULLable
+    if (const_cast<Field*>(field)->real_maybe_null() == false)
     {
-      // The column didn't exist
+      // Only NULLable fields supported
       warn_incompatible(ndb_tab, true,
-                        "column '%s' does not exist",
+                        "column '%s' is NOT NULL",
                         field->field_name);
       DBUG_RETURN(ERR_INCOMPAT_TABLE_DEF);
     }
 
+    // Check if column exist in NDB
+    const NdbInfo::Column* col = ndb_tab->getColumn(field->field_name);
+    if (!col)
+    {
+      // The column didn't exist
+      continue;
+    }
+
+    // Check compatible field and column type
     bool compatible = false;
     switch(col->m_type)
     {
@@ -378,7 +445,7 @@ int ha_ndbinfo::close(void)
 {
   DBUG_ENTER("ha_ndbinfo::close");
 
-  if (ndbcluster_is_disabled())
+  if (is_offline())
     DBUG_RETURN(0);
 
   assert(is_open());
@@ -395,12 +462,13 @@ int ha_ndbinfo::rnd_init(bool scan)
   DBUG_ENTER("ha_ndbinfo::rnd_init");
   DBUG_PRINT("info", ("scan: %d", scan));
 
-  if (ndbcluster_is_disabled())
+  if (is_offline())
   {
     push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE, 1,
-                 "'NDBINFO' has been started "
-                 "in limited mode since the 'NDBCLUSTER' "
-                 "engine is disabled - no rows can be returned");
+                 "'NDBINFO' has been started in offline mode "
+                 "since the 'NDBCLUSTER' engine is disabled "
+                 "or @@global.ndbinfo_offline is turned on "
+                 "- no rows can be returned");
     DBUG_RETURN(0);
   }
 
@@ -412,13 +480,30 @@ int ha_ndbinfo::rnd_init(bool scan)
     m_impl.m_first_use = false;
 
     /*
-      Due to different code paths in MySQL Server
-      for prepared statement protocol, some warnings
-      from 'handler::open' are lost and need to be
-      deffered to first use instead
+      Check table def. and generate warnings for incompatibilites
+      which is allowed but should generate a warning.
+      (Done this late due to different code paths in MySQL Server for
+      prepared statement protocol, where warnings from 'handler::open'
+      are lost).
     */
+    uint fields_found_in_ndb = 0;
     const NdbInfo::Table* ndb_tab = m_impl.m_table;
-    if (table->s->fields < ndb_tab->columns())
+    for (uint i = 0; i < table->s->fields; i++)
+    {
+      const Field* field = table->field[i];
+      const NdbInfo::Column* col = ndb_tab->getColumn(field->field_name);
+      if (!col)
+      {
+        // The column didn't exist
+        warn_incompatible(ndb_tab, true,
+                          "column '%s' does not exist",
+                          field->field_name);
+        continue;
+      }
+      fields_found_in_ndb++;
+    }
+
+    if (fields_found_in_ndb < ndb_tab->columns())
     {
       // There are more columns available in NDB
       warn_incompatible(ndb_tab, false,
@@ -466,7 +551,7 @@ int ha_ndbinfo::rnd_end()
 {
   DBUG_ENTER("ha_ndbinfo::rnd_end");
 
-  if (ndbcluster_is_disabled())
+  if (is_offline())
     DBUG_RETURN(0);
 
   assert(is_open());
@@ -486,7 +571,7 @@ int ha_ndbinfo::rnd_next(uchar *buf)
   int err;
   DBUG_ENTER("ha_ndbinfo::rnd_next");
 
-  if (ndbcluster_is_disabled())
+  if (is_offline())
     DBUG_RETURN(HA_ERR_END_OF_FILE);
 
   assert(is_open());
@@ -546,7 +631,7 @@ ha_ndbinfo::unpack_record(uchar *dst_row
   {
     Field *field = table->field[i];
     const NdbInfoRecAttr* record = m_impl.m_columns[i];
-    if (m_impl.m_columns[i])
+    if (record && !record->isNULL())
     {
       field->set_notnull();
       field->move_field_offset(dst_offset);
@@ -617,7 +702,7 @@ ndbinfo_find_files(handlerton *hton, THD
     List_iterator<LEX_STRING> it(*files);
     while ((dir_name=it++))
     {
-      if (strcmp(dir_name->str, ndbinfo_dbname))
+      if (strcmp(dir_name->str, opt_ndbinfo_dbname))
         continue;
 
       DBUG_PRINT("info", ("Hiding own databse '%s'", dir_name->str));
@@ -628,7 +713,7 @@ ndbinfo_find_files(handlerton *hton, THD
   }
 
   DBUG_ASSERT(db);
-  if (strcmp(db, ndbinfo_dbname))
+  if (strcmp(db, opt_ndbinfo_dbname))
     DBUG_RETURN(0); // Only hide files in "our" db
 
   /* Hide all files that start with "our" prefix */
@@ -636,7 +721,7 @@ ndbinfo_find_files(handlerton *hton, THD
   List_iterator<LEX_STRING> it(*files);
   while ((file_name=it++))
   {
-    if (is_prefix(file_name->str, table_prefix))
+    if (is_prefix(file_name->str, opt_ndbinfo_table_prefix))
     {
       DBUG_PRINT("info", ("Hiding '%s'", file_name->str));
       it.remove();
@@ -668,11 +753,11 @@ int ndbinfo_init(void *plugin)
 
   char prefix[FN_REFLEN];
   build_table_filename(prefix, sizeof(prefix) - 1,
-                       ndbinfo_dbname, table_prefix, "", 0);
+                       opt_ndbinfo_dbname, opt_ndbinfo_table_prefix, "", 0);
   DBUG_PRINT("info", ("prefix: '%s'", prefix));
   assert(g_ndb_cluster_connection);
   g_ndbinfo = new NdbInfo(g_ndb_cluster_connection, prefix,
-                          ndbinfo_dbname, table_prefix);
+                          opt_ndbinfo_dbname, opt_ndbinfo_table_prefix);
   if (!g_ndbinfo)
   {
     sql_print_error("Failed to create NdbInfo");
@@ -712,6 +797,7 @@ struct st_mysql_sys_var* ndbinfo_system_
   MYSQL_SYSVAR(database),
   MYSQL_SYSVAR(table_prefix),
   MYSQL_SYSVAR(version),
+  MYSQL_SYSVAR(offline),
 
   NULL
 };

=== modified file 'sql/ha_ndbinfo.h'
--- a/sql/ha_ndbinfo.h	2011-02-01 14:58:21 +0000
+++ b/sql/ha_ndbinfo.h	2011-05-23 11:57:55 +0000
@@ -83,6 +83,8 @@ private:
   bool is_open(void) const;
   bool is_closed(void) const { return ! is_open(); };
 
+  bool is_offline(void) const;
+
   struct ha_ndbinfo_impl& m_impl;
 
 };

=== modified file 'storage/ndb/include/ndbapi/ndb_cluster_connection.hpp'
--- a/storage/ndb/include/ndbapi/ndb_cluster_connection.hpp	2011-02-04 17:52:38 +0000
+++ b/storage/ndb/include/ndbapi/ndb_cluster_connection.hpp	2011-05-23 14:05:08 +0000
@@ -192,6 +192,7 @@ public:
   unsigned max_nodegroup();
   unsigned node_id();
   unsigned get_connect_count() const;
+  unsigned get_min_db_version() const;
 
   void init_get_next_node(Ndb_cluster_connection_node_iter &iter);
   unsigned int get_next_node(Ndb_cluster_connection_node_iter &iter);

=== modified file 'storage/ndb/src/ndbapi/NdbInfo.cpp'
--- a/storage/ndb/src/ndbapi/NdbInfo.cpp	2011-02-02 00:40:07 +0000
+++ b/storage/ndb/src/ndbapi/NdbInfo.cpp	2011-05-23 14:05:08 +0000
@@ -23,6 +23,7 @@ NdbInfo::NdbInfo(class Ndb_cluster_conne
                  const char* prefix, const char* dbname,
                  const char* table_prefix) :
   m_connect_count(connection->get_connect_count()),
+  m_min_db_version(0),
   m_connection(connection),
   m_tables_table(NULL), m_columns_table(NULL),
   m_prefix(prefix),
@@ -270,7 +271,9 @@ bool NdbInfo::load_tables()
   }
 
   // After sucessfull load of the tables, set connect count
+  // and the min db version of cluster
   m_connect_count = m_connection->get_connect_count();
+  m_min_db_version = m_connection->get_min_db_version();
   return true;
 }
 
@@ -328,12 +331,14 @@ void NdbInfo::flush_tables()
 bool
 NdbInfo::check_tables()
 {
-  if (m_connection->get_connect_count() != m_connect_count)
+  if (unlikely(m_connection->get_connect_count() != m_connect_count ||
+               m_connection->get_min_db_version() != m_min_db_version))
   {
-    // Connect count has changed -> flush the cached table definitions
+    // Connect count or min db version of cluster has changed
+    //  -> flush the cached table definitions
     flush_tables();
   }
-  if (m_tables.entries() <= NUM_HARDCODED_TABLES)
+  if (unlikely(m_tables.entries() <= NUM_HARDCODED_TABLES))
   {
     // Global table cache is not loaded yet or has been
     // flushed, try to load it

=== modified file 'storage/ndb/src/ndbapi/NdbInfo.hpp'
--- a/storage/ndb/src/ndbapi/NdbInfo.hpp	2011-02-02 00:40:07 +0000
+++ b/storage/ndb/src/ndbapi/NdbInfo.hpp	2011-05-23 14:05:08 +0000
@@ -89,8 +89,6 @@ public:
   bool init(void);
   ~NdbInfo();
 
-  void flush_tables();
-
   int openTable(const char* table_name, const Table**);
   int openTable(Uint32 tableId, const Table**);
   void closeTable(const Table* table);
@@ -103,6 +101,7 @@ public:
 private:
   static const size_t NUM_HARDCODED_TABLES = 2;
   unsigned m_connect_count;
+  unsigned m_min_db_version;
   class Ndb_cluster_connection* m_connection;
   pthread_mutex_t m_mutex;
   HashMap<BaseString, Table, BaseString_get_key> m_tables;
@@ -119,6 +118,7 @@ private:
   bool load_hardcoded_tables(void);
   bool load_tables();
   bool check_tables();
+  void flush_tables();
 
   BaseString mysql_table_name(const char* table_name) const;
 

=== modified file 'storage/ndb/src/ndbapi/NdbInfoRecAttr.hpp'
--- a/storage/ndb/src/ndbapi/NdbInfoRecAttr.hpp	2011-02-02 00:40:07 +0000
+++ b/storage/ndb/src/ndbapi/NdbInfoRecAttr.hpp	2011-05-23 13:45:57 +0000
@@ -46,13 +46,18 @@ public:
     return m_len;
   }
 
+  bool isNULL() const {
+    return !m_defined;
+  }
+
 protected:
   friend class NdbInfoScanOperation;
-  NdbInfoRecAttr() : m_data(NULL), m_len(0) {};
+  NdbInfoRecAttr() : m_data(NULL), m_len(0), m_defined(false) {};
   ~NdbInfoRecAttr() {};
 private:
   const char* m_data;
   Uint32 m_len;
+  bool m_defined;
 };
 
 #endif

=== modified file 'storage/ndb/src/ndbapi/NdbInfoScanOperation.cpp'
--- a/storage/ndb/src/ndbapi/NdbInfoScanOperation.cpp	2011-02-02 00:40:07 +0000
+++ b/storage/ndb/src/ndbapi/NdbInfoScanOperation.cpp	2011-05-23 13:45:57 +0000
@@ -418,34 +418,35 @@ NdbInfoScanOperation::execDBINFO_TRANSID
   m_rows_received++;
   DBUG_PRINT("info", ("rows received: %d", m_rows_received));
 
-  const Uint32* start = signal->ptr[0].p;
-  const Uint32* end = start + signal->ptr[0].sz;
-
-  DBUG_PRINT("info", ("start: %p, end: %p", start, end));
-  for (unsigned col = 0; col < m_table->columns(); col++)
+  // Reset all recattr values before reading the new row
+  for (unsigned i = 0; i < m_recAttrs.size(); i++)
   {
+    if (m_recAttrs[i])
+      m_recAttrs[i]->m_defined = false;
+  }
 
-    // Read attribute header
-    const AttributeHeader ah(*start);
-    const Uint32 len = ah.getByteSize();
+  // Read attributes from long signal section
+  AttributeHeader* attr = (AttributeHeader*)signal->ptr[0].p;
+  AttributeHeader* last = (AttributeHeader*)(signal->ptr[0].p +
+                                            signal->ptr[0].sz);
+  while (attr < last)
+  {
+    const Uint32 col = attr->getAttributeId();
+    const Uint32 len = attr->getByteSize();
     DBUG_PRINT("info", ("col: %u, len: %u", col, len));
-
-    // Step past attribute header
-    start += ah.getHeaderSize();
-
-    NdbInfoRecAttr* attr = m_recAttrs[col];
-    if (attr)
+    if (col < m_recAttrs.size())
     {
-      // Update NdbInfoRecAttr pointer and length
-      attr->m_data = (const char*)start;
-      attr->m_len = len;
+      NdbInfoRecAttr* rec_attr = m_recAttrs[col];
+      if (rec_attr)
+      {
+        // Update NdbInfoRecAttr pointer, length and defined flag
+        rec_attr->m_data = (const char*)attr->getDataPtr();
+        rec_attr->m_len = len;
+        rec_attr->m_defined = true;
+      }
     }
 
-    // Step to next attribute header
-    start += ah.getDataSize();
-
-    // No reading beyond end of signal size
-    assert(start <= end);
+    attr = attr->getNext();
   }
 
   DBUG_RETURN(false); // Don't wait more, process this row

=== modified file 'storage/ndb/src/ndbapi/TransporterFacade.hpp'
--- a/storage/ndb/src/ndbapi/TransporterFacade.hpp	2011-02-24 07:47:22 +0000
+++ b/storage/ndb/src/ndbapi/TransporterFacade.hpp	2011-05-23 14:05:08 +0000
@@ -303,6 +303,12 @@ unsigned Ndb_cluster_connection_impl::ge
 }
 
 inline
+unsigned Ndb_cluster_connection_impl::get_min_db_version() const
+{
+  return m_transporter_facade->getMinDbNodeVersion();
+}
+
+inline
 bool
 TransporterFacade::get_node_alive(NodeId n) const {
   if (theClusterMgr)

=== modified file 'storage/ndb/src/ndbapi/ndb_cluster_connection.cpp'
--- a/storage/ndb/src/ndbapi/ndb_cluster_connection.cpp	2011-04-15 06:29:59 +0000
+++ b/storage/ndb/src/ndbapi/ndb_cluster_connection.cpp	2011-05-23 14:05:08 +0000
@@ -332,6 +332,11 @@ unsigned Ndb_cluster_connection::get_con
   return m_impl.get_connect_count();
 }
 
+unsigned Ndb_cluster_connection::get_min_db_version() const
+{
+  return m_impl.get_min_db_version();
+}
+
 int Ndb_cluster_connection::get_latest_error() const
 {
   return m_impl.m_latest_error;

=== modified file 'storage/ndb/src/ndbapi/ndb_cluster_connection_impl.hpp'
--- a/storage/ndb/src/ndbapi/ndb_cluster_connection_impl.hpp	2011-02-04 17:52:38 +0000
+++ b/storage/ndb/src/ndbapi/ndb_cluster_connection_impl.hpp	2011-05-23 14:05:08 +0000
@@ -67,6 +67,7 @@ class Ndb_cluster_connection_impl : publ
   Uint32 get_next_alive_node(Ndb_cluster_connection_node_iter &iter);
 
   inline unsigned get_connect_count() const;
+  inline unsigned get_min_db_version() const;
 public:
   inline Uint64 *get_latest_trans_gci() { return &m_latest_trans_gci; }
 

=== modified file 'storage/ndb/tools/ndbinfo_sql.cpp'
--- a/storage/ndb/tools/ndbinfo_sql.cpp	2011-04-12 11:59:36 +0000
+++ b/storage/ndb/tools/ndbinfo_sql.cpp	2011-05-23 11:57:55 +0000
@@ -327,6 +327,12 @@ int main(int argc, char** argv){
   sql.assfmt("CREATE DATABASE IF NOT EXISTS `%s`", opt_ndbinfo_db);
   print_conditional_sql(sql);
 
+  printf("# Set NDBINFO in offline mode during (re)create of tables\n");
+  printf("# and views to avoid errors caused by no such table or\n");
+  printf("# different table definition in NDB\n");
+  sql.assfmt("SET @@global.ndbinfo_offline=TRUE");
+  print_conditional_sql(sql);
+
   printf("# Drop any old views in %s\n", opt_ndbinfo_db);
   for (size_t i = 0; i < num_views; i++)
   {
@@ -430,6 +436,10 @@ int main(int argc, char** argv){
     print_conditional_sql(sql);
   }
 
+  printf("# Finally turn off offline mode\n");
+  sql.assfmt("SET @@global.ndbinfo_offline=FALSE");
+  print_conditional_sql(sql);
+
   return 0;
 }
 

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-5.1-telco-7.0 branch (magnus.blaudd:4399 to 4403)Bug#12581954magnus.blaudd23 May