#At file:///home/tomas/mysql_src/mysql-5.1-telco-6.3/
2693 Tomas Ulin 2008-09-30 [merge]
merge
added:
mysql-test/suite/ndb/r/ndb_discover_db.result
mysql-test/suite/ndb/r/ndb_discover_db2.result
mysql-test/suite/ndb/t/ndb_discover_db.test
mysql-test/suite/ndb/t/ndb_discover_db2-master.opt
mysql-test/suite/ndb/t/ndb_discover_db2.test
sql/ha_ndbcluster_lock_ext.h
modified:
sql/Makefile.am
sql/ha_ndbcluster.cc
sql/ha_ndbcluster.h
sql/ha_ndbcluster_binlog.cc
sql/ha_ndbcluster_binlog.h
sql/handler.cc
sql/handler.h
sql/sql_db.cc
sql/sql_delete.cc
sql/sql_parse.cc
sql/sql_rename.cc
sql/sql_table.cc
=== added file 'mysql-test/suite/ndb/r/ndb_discover_db.result'
--- a/mysql-test/suite/ndb/r/ndb_discover_db.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/ndb/r/ndb_discover_db.result 2008-09-30 09:14:44 +0000
@@ -0,0 +1,7 @@
+drop database if exists discover_db;
+drop database if exists discover_db_2;
+create database discover_db;
+create table discover_db.t1 (a int key, b int) engine ndb;
+create database discover_db_2;
+alter database discover_db_2 character set binary;
+create table discover_db_2.t1 (a int key, b int) engine ndb;
=== added file 'mysql-test/suite/ndb/r/ndb_discover_db2.result'
--- a/mysql-test/suite/ndb/r/ndb_discover_db2.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/ndb/r/ndb_discover_db2.result 2008-09-30 09:14:44 +0000
@@ -0,0 +1,28 @@
+show create database discover_db;
+Database Create Database
+discover_db CREATE DATABASE `discover_db` /*!40100 DEFAULT CHARACTER SET latin1 */
+show create database discover_db_2;
+Database Create Database
+discover_db_2 CREATE DATABASE `discover_db_2` /*!40100 DEFAULT CHARACTER SET binary */
+reset master;
+insert into discover_db.t1 values (1,1);
+show binlog events from <binlog_start>;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin1.000001 # Query 102 # BEGIN
+master-bin1.000001 # Table_map 102 # table_id: # (discover_db.t1)
+master-bin1.000001 # Table_map 102 # table_id: # (mysql.ndb_apply_status)
+master-bin1.000001 # Write_rows 102 # table_id: #
+master-bin1.000001 # Write_rows 102 # table_id: # flags: STMT_END_F
+master-bin1.000001 # Query 102 # COMMIT
+reset master;
+insert into discover_db_2.t1 values (1,1);
+show binlog events from <binlog_start>;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin1.000001 # Query 102 # BEGIN
+master-bin1.000001 # Table_map 102 # table_id: # (discover_db_2.t1)
+master-bin1.000001 # Table_map 102 # table_id: # (mysql.ndb_apply_status)
+master-bin1.000001 # Write_rows 102 # table_id: #
+master-bin1.000001 # Write_rows 102 # table_id: # flags: STMT_END_F
+master-bin1.000001 # Query 102 # COMMIT
+drop database discover_db;
+drop database discover_db_2;
=== added file 'mysql-test/suite/ndb/t/ndb_discover_db.test'
--- a/mysql-test/suite/ndb/t/ndb_discover_db.test 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/ndb/t/ndb_discover_db.test 2008-09-30 09:14:44 +0000
@@ -0,0 +1,33 @@
+-- source include/have_multi_ndb.inc
+-- source include/have_binlog_format_mixed_or_row.inc
+
+-- disable_warnings
+drop database if exists discover_db;
+drop database if exists discover_db_2;
+-- enable_warnings
+
+#
+# Prepare for testing database discovery by creating
+# databases, and removing them on one mysqld
+# The discovery happens in ndb_discover_db2.test
+#
+
+# check that created database is discovered
+create database discover_db;
+create table discover_db.t1 (a int key, b int) engine ndb;
+
+# check that altered database is discovered
+create database discover_db_2;
+alter database discover_db_2 character set binary;
+create table discover_db_2.t1 (a int key, b int) engine ndb;
+
+-- remove_file $MYSQLTEST_VARDIR/master1-data/discover_db/t1.frm
+-- remove_file $MYSQLTEST_VARDIR/master1-data/discover_db/t1.ndb
+-- remove_file $MYSQLTEST_VARDIR/master1-data/discover_db/db.opt
+-- rmdir $MYSQLTEST_VARDIR/master1-data/discover_db
+
+-- remove_file $MYSQLTEST_VARDIR/master1-data/discover_db_2/t1.frm
+-- remove_file $MYSQLTEST_VARDIR/master1-data/discover_db_2/t1.ndb
+-- remove_file $MYSQLTEST_VARDIR/master1-data/discover_db_2/db.opt
+-- rmdir $MYSQLTEST_VARDIR/master1-data/discover_db_2
+
=== added file 'mysql-test/suite/ndb/t/ndb_discover_db2-master.opt'
--- a/mysql-test/suite/ndb/t/ndb_discover_db2-master.opt 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/ndb/t/ndb_discover_db2-master.opt 2008-09-30 09:14:44 +0000
@@ -0,0 +1 @@
+--skip-external-locking
=== added file 'mysql-test/suite/ndb/t/ndb_discover_db2.test'
--- a/mysql-test/suite/ndb/t/ndb_discover_db2.test 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/ndb/t/ndb_discover_db2.test 2008-09-30 09:14:44 +0000
@@ -0,0 +1,21 @@
+-- source include/have_multi_ndb.inc
+-- source include/have_binlog_format_mixed_or_row.inc
+
+#
+# When this test started there no database on disk for server2
+# Check that table has been discovered correctly, and that the
+# binlog is updated correctly
+#
+
+-- connection server2
+show create database discover_db;
+show create database discover_db_2;
+reset master;
+insert into discover_db.t1 values (1,1);
+--source include/show_binlog_events2.inc
+reset master;
+insert into discover_db_2.t1 values (1,1);
+--source include/show_binlog_events2.inc
+
+drop database discover_db;
+drop database discover_db_2;
=== modified file 'sql/Makefile.am'
--- a/sql/Makefile.am 2008-09-01 12:28:57 +0000
+++ b/sql/Makefile.am 2008-09-30 10:42:32 +0000
@@ -60,6 +60,7 @@ noinst_HEADERS = item.h item_func.h item
ha_ndbcluster.h ha_ndbcluster_cond.h \
ha_ndbcluster_binlog.h ha_ndbcluster_tables.h \
ha_ndbcluster_connection.h ha_ndbcluster_connection.h \
+ ha_ndbcluster_lock_ext.h \
ha_partition.h rpl_constants.h \
opt_range.h protocol.h rpl_tblmap.h rpl_utility.h \
rpl_reporting.h \
=== modified file 'sql/ha_ndbcluster.cc'
--- a/sql/ha_ndbcluster.cc 2008-09-30 07:40:45 +0000
+++ b/sql/ha_ndbcluster.cc 2008-09-30 10:42:32 +0000
@@ -71,9 +71,12 @@ enum ndb_distribution opt_ndb_distributi
// Default value for parallelism
static const int parallelism= 0;
-// Default value for max number of transactions
-// createable against NDB from this handler
-static const int max_transactions= 3; // should really be 2 but there is a transaction to much allocated when loch table is used
+/*
+ Default value for max number of transactions createable against NDB from
+ the handler. Should really be 2 but there is a transaction to much allocated
+ when lock table is used, and one extra to used for global schema lock.
+*/
+static const int max_transactions= 4;
static uint ndbcluster_partition_flags();
static int ndbcluster_init(void *);
@@ -617,6 +620,8 @@ Thd_ndb::Thd_ndb()
m_max_violation_count= 0;
m_old_violation_count= 0;
m_conflict_fn_usage_count= 0;
+ global_schema_lock_trans= NULL;
+ global_schema_lock_count= 0;
init_alloc_root(&m_batch_mem_root, BATCH_FLUSH_SIZE/4, 0);
}
@@ -6278,18 +6283,6 @@ int ha_ndbcluster::create(const char *na
NDBDICT *dict= ndb->getDictionary();
DBUG_PRINT("info", ("Tablespace %s,%s", form->s->tablespace, create_info->tablespace));
- if (is_truncate)
- {
- {
- Ndb_table_guard ndbtab_g(dict, m_tabname);
- if (!(m_table= ndbtab_g.get_table()))
- ERR_RETURN(dict->getNdbError());
- m_table= NULL;
- }
- DBUG_PRINT("info", ("Dropping and re-creating table for TRUNCATE"));
- if ((my_errno= delete_table(name)))
- DBUG_RETURN(my_errno);
- }
table= form;
if (create_from_engine)
{
@@ -6305,6 +6298,12 @@ int ha_ndbcluster::create(const char *na
DBUG_RETURN(my_errno);
}
+ Thd_ndb *thd_ndb= get_thd_ndb(thd);
+
+ if (!((thd_ndb->options & TNO_NO_LOCK_SCHEMA_OP) ||
+ ndbcluster_has_global_schema_lock(thd_ndb)))
+ ndbcluster_no_global_schema_lock_abort
+ (thd, "ha_ndbcluster::create");
/*
Don't allow table creation unless
schema distribution table is setup
@@ -6330,6 +6329,19 @@ int ha_ndbcluster::create(const char *na
}
}
+ if (is_truncate)
+ {
+ {
+ Ndb_table_guard ndbtab_g(dict, m_tabname);
+ if (!(m_table= ndbtab_g.get_table()))
+ ERR_RETURN(dict->getNdbError());
+ m_table= NULL;
+ }
+ DBUG_PRINT("info", ("Dropping and re-creating table for TRUNCATE"));
+ if ((my_errno= delete_table(name)))
+ DBUG_RETURN(my_errno);
+ }
+
DBUG_PRINT("table", ("name: %s", m_tabname));
if (tab.setName(m_tabname))
{
@@ -6942,6 +6954,12 @@ int ha_ndbcluster::rename_table(const ch
if (check_ndb_connection(thd))
DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION);
+#ifdef HAVE_NDB_BINLOG
+ if (!ndbcluster_has_global_schema_lock(get_thd_ndb(thd)))
+ ndbcluster_no_global_schema_lock_abort
+ (thd, "ha_ndbcluster::rename_table");
+#endif
+
Ndb *ndb= get_ndb(thd);
ndb->setDatabaseName(old_dbname);
dict= ndb->getDictionary();
@@ -7335,6 +7353,13 @@ int ha_ndbcluster::delete_table(const ch
}
ndb= get_ndb(thd);
+
+#ifdef HAVE_NDB_BINLOG
+ if (!ndbcluster_has_global_schema_lock(get_thd_ndb(thd)))
+ ndbcluster_no_global_schema_lock_abort
+ (thd, "ha_ndbcluster::delete_table");
+#endif
+
/*
Drop table in ndb.
If it was already gone it might have been dropped
@@ -8395,6 +8420,12 @@ int ndbcluster_find_files(handlerton *ht
if (dir)
DBUG_RETURN(0); // Discover of databases not yet supported
+
+#ifdef HAVE_NDB_BINLOG
+ Ndbcluster_global_schema_lock_guard ndbcluster_global_schema_lock_guard(thd);
+ if (ndbcluster_global_schema_lock_guard.lock())
+ DBUG_RETURN(HA_ERR_NO_CONNECTION);
+#endif
// List tables in NDB
NDBDICT *dict= ndb->getDictionary();
=== modified file 'sql/ha_ndbcluster.h'
--- a/sql/ha_ndbcluster.h 2008-08-12 09:45:38 +0000
+++ b/sql/ha_ndbcluster.h 2008-09-30 10:42:32 +0000
@@ -263,7 +263,12 @@ typedef enum ndb_query_state_bits {
enum THD_NDB_OPTIONS
{
- TNO_NO_LOG_SCHEMA_OP= 1 << 0
+ TNO_NO_LOG_SCHEMA_OP= 1 << 0,
+ /*
+ In participating mysqld, do not try to acquire global schema
+ lock, as one other mysqld already has the lock.
+ */
+ TNO_NO_LOCK_SCHEMA_OP= 1 << 1
};
enum THD_NDB_TRANS_OPTIONS
@@ -322,6 +327,9 @@ class Thd_ndb
uint m_max_violation_count;
uint m_old_violation_count;
uint m_conflict_fn_usage_count;
+
+ NdbTransaction *global_schema_lock_trans;
+ uint global_schema_lock_count;
};
int ndbcluster_commit(handlerton *hton, THD *thd, bool all);
=== modified file 'sql/ha_ndbcluster_binlog.cc'
--- a/sql/ha_ndbcluster_binlog.cc 2008-09-30 07:40:45 +0000
+++ b/sql/ha_ndbcluster_binlog.cc 2008-09-30 10:42:32 +0000
@@ -144,7 +144,6 @@ static void ndb_free_schema_object(NDB_S
/*
Helper functions
*/
-
static bool ndbcluster_check_if_local_table(const char *dbname, const char *tabname);
static bool ndbcluster_check_if_local_tables_in_db(THD *thd, const char *dbname);
@@ -725,14 +724,115 @@ static bool ndbcluster_flush_logs(handle
return FALSE;
}
+/*
+ Global schema lock across mysql servers
+*/
+int ndbcluster_has_global_schema_lock(Thd_ndb *thd_ndb)
+{
+ if (thd_ndb->global_schema_lock_trans)
+ {
+ thd_ndb->global_schema_lock_trans->refresh();
+ return 1;
+ }
+ return 0;
+}
+
+void ndbcluster_no_global_schema_lock_abort(THD *thd, const char *msg)
+{
+ sql_print_error("NDB: programming error, no lock taken while running "
+ "query %s. Message: %s", thd->query, msg);
+ abort();
+}
+
+#include "ha_ndbcluster_lock_ext.h"
+
+static int ndbcluster_global_schema_lock(THD *thd,
+ int report_cluster_disconnected)
+{
+ Ndb *ndb= check_ndb_in_thd(thd);
+ Thd_ndb *thd_ndb= get_thd_ndb(thd);
+ NdbError ndb_error;
+ if (thd_ndb->options & TNO_NO_LOCK_SCHEMA_OP)
+ return 0;
+ DBUG_ENTER("ndbcluster_global_schema_lock");
+ if (thd_ndb->global_schema_lock_trans)
+ {
+ thd_ndb->global_schema_lock_trans->refresh();
+ thd_ndb->global_schema_lock_count++;
+ DBUG_PRINT("exit", ("global_schema_lock_count: %d",
+ thd_ndb->global_schema_lock_count));
+ DBUG_RETURN(0);
+ }
+ DBUG_ASSERT(thd_ndb->global_schema_lock_count == 0);
+ thd_ndb->global_schema_lock_count= 0;
+
+ if ((thd_ndb->global_schema_lock_trans=
+ ndbcluster_global_schema_lock_ext(ndb, ndb_error)) != NULL)
+ {
+ thd_ndb->global_schema_lock_count= 1;
+ DBUG_RETURN(0);
+ }
+
+ if (ndb_error.code != 4009 || report_cluster_disconnected)
+ {
+ sql_print_warning("NDB: Could not acquire global schema lock (%d)%s",
+ ndb_error.code, ndb_error.message);
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ ndb_error.code, ndb_error.message,
+ "ndb. Could not acquire global schema lock");
+ }
+ DBUG_RETURN(-1);
+}
+static int ndbcluster_global_schema_unlock(THD *thd)
+{
+ Thd_ndb *thd_ndb= get_thd_ndb(thd);
+ DBUG_ASSERT(thd_ndb != 0);
+ if (thd_ndb == 0 || (thd_ndb->options & TNO_NO_LOCK_SCHEMA_OP))
+ return 0;
+ Ndb *ndb= thd_ndb->ndb;
+ DBUG_ASSERT(ndb != NULL);
+ if (ndb == NULL)
+ return 0;
+ DBUG_ENTER("ndbcluster_global_schema_unlock");
+ NdbTransaction *trans= thd_ndb->global_schema_lock_trans;
+ if (trans)
+ {
+ thd_ndb->global_schema_lock_count--;
+ if (thd_ndb->global_schema_lock_count != 0)
+ {
+ DBUG_PRINT("exit", ("global_schema_lock_count: %d",
+ thd_ndb->global_schema_lock_count));
+ DBUG_RETURN(0);
+ }
+ thd_ndb->global_schema_lock_trans= NULL;
+ NdbError ndb_error;
+ if (ndbcluster_global_schema_unlock_ext(ndb, trans, ndb_error))
+ {
+ sql_print_warning("NDB: Releasing global schema lock (%d)%s",
+ ndb_error.code, ndb_error.message);
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ ndb_error.code,
+ ndb_error.message,
+ "ndb. Releasing global schema lock");
+ DBUG_RETURN(-1);
+ }
+ }
+ DBUG_PRINT("exit", ("global_schema_lock_count: %d",
+ thd_ndb->global_schema_lock_count));
+ DBUG_RETURN(0);
+}
+
static int ndbcluster_binlog_func(handlerton *hton, THD *thd,
enum_binlog_func fn,
void *arg)
{
+ DBUG_ENTER("ndbcluster_binlog_func");
switch(fn)
{
case BFN_RESET_LOGS:
- return ndbcluster_reset_logs(thd);
+ DBUG_RETURN(ndbcluster_reset_logs(thd));
case BFN_RESET_SLAVE:
ndbcluster_reset_slave(thd);
break;
@@ -745,8 +845,28 @@ static int ndbcluster_binlog_func(handle
case BFN_BINLOG_PURGE_FILE:
ndbcluster_binlog_index_purge_file(thd, (const char *)arg);
break;
+ case BFN_GLOBAL_SCHEMA_LOCK:
+ ndbcluster_global_schema_lock(thd, 1);
+ break;
+ case BFN_GLOBAL_SCHEMA_UNLOCK:
+ ndbcluster_global_schema_unlock(thd);
+ break;
}
- return 0;
+ DBUG_RETURN(0);
+}
+Ndbcluster_global_schema_lock_guard::Ndbcluster_global_schema_lock_guard(THD *thd)
+ : m_thd(thd), m_lock(0)
+{
+}
+Ndbcluster_global_schema_lock_guard::~Ndbcluster_global_schema_lock_guard()
+{
+ if (m_lock)
+ ndbcluster_global_schema_unlock(m_thd);
+}
+int Ndbcluster_global_schema_lock_guard::lock()
+{
+ m_lock= !ndbcluster_global_schema_lock(m_thd, 0);
+ return !m_lock;
}
void ndbcluster_binlog_init_handlerton()
@@ -954,8 +1074,185 @@ static int ndbcluster_create_schema_tabl
DBUG_RETURN(0);
}
+class Thd_ndb_options_guard
+{
+public:
+ Thd_ndb_options_guard(Thd_ndb *thd_ndb)
+ : m_val(thd_ndb->options), m_save_val(thd_ndb->options) {}
+ ~Thd_ndb_options_guard() { m_val= m_save_val; }
+ void set(uint32 flag) { m_val|= flag; }
+private:
+ uint32 &m_val;
+ uint32 m_save_val;
+};
+
+/*
+ Ndb has no representation of the database schema objects.
+ The mysql.ndb_schema table contains the latest schema operations
+ done via a mysqld, and thus reflects databases created/dropped/altered
+ while a mysqld was disconnected. This function tries to recover
+ the correct state w.r.t created databases using the information in
+ that table.
+
+ Function should only be called while ndbcluster_global_schema_lock
+ is held, to ensure that ndb_schema table is not being updated while
+ scanning.
+*/
+static int ndbcluster_find_all_databases(THD *thd)
+{
+ Ndb *ndb= check_ndb_in_thd(thd);
+ Thd_ndb *thd_ndb= get_thd_ndb(thd);
+ Thd_ndb_options_guard thd_ndb_options(thd_ndb);
+ NDBDICT *dict= ndb->getDictionary();
+ NdbTransaction *trans= NULL;
+ NdbError ndb_error;
+ int retries= 100;
+ int retry_sleep= 30; /* 30 milliseconds, transaction */
+ DBUG_ENTER("ndbcluster_find_all_databases");
+ /* Ensure that we have the right lock */
+ if (!ndbcluster_has_global_schema_lock(thd_ndb))
+ ndbcluster_no_global_schema_lock_abort
+ (thd, "ndbcluster_find_all_databases");
+ ndb->setDatabaseName(NDB_REP_DB);
+ thd_ndb_options.set(TNO_NO_LOG_SCHEMA_OP);
+ thd_ndb_options.set(TNO_NO_LOCK_SCHEMA_OP);
+ while (1)
+ {
+ char db_buffer[FN_REFLEN];
+ char *db= db_buffer+1;
+ char name[FN_REFLEN];
+ char query[64000];
+ Ndb_table_guard ndbtab_g(dict, NDB_SCHEMA_TABLE);
+ const NDBTAB *ndbtab= ndbtab_g.get_table();
+ NdbScanOperation *op;
+ NdbBlob *query_blob_handle;
+ int r= 0;
+ if (ndbtab == NULL)
+ {
+ ndb_error= dict->getNdbError();
+ goto error;
+ }
+ trans= ndb->startTransaction();
+ if (trans == NULL)
+ {
+ ndb_error= ndb->getNdbError();
+ goto error;
+ }
+ op= trans->getNdbScanOperation(ndbtab);
+ if (op == NULL)
+ abort();
+ op->readTuples(NdbScanOperation::LM_Read,
+ NdbScanOperation::SF_TupScan, 1);
+
+ r|= op->getValue("db", db_buffer) == NULL;
+ r|= op->getValue("name", name) == NULL;
+ r|= (query_blob_handle= op->getBlobHandle("query")) == NULL;
+ r|= query_blob_handle->getValue(query, sizeof(query));
+
+ if (r)
+ abort();
+
+ if (trans->execute(NdbTransaction::NoCommit))
+ {
+ ndb_error= trans->getNdbError();
+ goto error;
+ }
+
+ while ((r= op->nextResult()) == 0)
+ {
+ unsigned db_len= db_buffer[0];
+ unsigned name_len= name[0];
+ /*
+ name_len == 0 means no table name, hence the row
+ is for a database
+ */
+ if (db_len > 0 && name_len == 0)
+ {
+ /* database found */
+ Uint64 query_length= 0;
+ if (query_blob_handle->getLength(query_length))
+ abort();
+ db[db_len]= 0;
+ query[query_length]= 0;
+ build_table_filename(name, sizeof(name), db, "", "", 0);
+ int database_exists= !my_access(name, F_OK);
+ if (strncasecmp("CREATE", query, 6) == 0)
+ {
+ /* Database should exist */
+ if (!database_exists)
+ {
+ /* create missing database */
+ sql_print_information("NDB: Discovered missing database '%s'", db);
+ const int no_print_error[1]= {0};
+ run_query(thd, query, query + query_length,
+ no_print_error, /* print error */
+ TRUE); /* don't binlog the query */
+ /* always reset here */
+ thd->main_da.reset_diagnostics_area();
+ }
+ }
+ else if (strncasecmp("ALTER", query, 5) == 0)
+ {
+ /* Database should exist */
+ if (!database_exists)
+ {
+ /* create missing database */
+ sql_print_information("NDB: Discovered missing database '%s'", db);
+ const int no_print_error[1]= {0};
+ name_len= snprintf(name, sizeof(name), "CREATE DATABASE %s", db);
+ run_query(thd, name, name + name_len,
+ no_print_error, /* print error */
+ TRUE); /* don't binlog the query */
+ thd->main_da.reset_diagnostics_area();
+ run_query(thd, query, query + query_length,
+ no_print_error, /* print error */
+ TRUE); /* don't binlog the query */
+ /* always reset here */
+ thd->main_da.reset_diagnostics_area();
+ }
+ }
+ else if (strncasecmp("DROP", query, 4) == 0)
+ {
+ /* Database should not exist */
+ if (database_exists)
+ {
+ /* drop missing database */
+ sql_print_information("NDB: Discovered reamining database '%s'", db);
+ }
+ }
+ }
+ }
+ if (r == -1)
+ {
+ ndb_error= op->getNdbError();
+ goto error;
+ }
+ ndb->closeTransaction(trans);
+ trans= NULL;
+ DBUG_RETURN(0); // success
+ error:
+ if (trans)
+ {
+ ndb->closeTransaction(trans);
+ trans= NULL;
+ }
+ if (ndb_error.status == NdbError::TemporaryError)
+ {
+ if (retries--)
+ {
+ do_retry_sleep(retry_sleep);
+ continue; // retry
+ }
+ }
+ DBUG_RETURN(1); // not temp error or too many retries
+ }
+}
+
int ndbcluster_setup_binlog_table_shares(THD *thd)
{
+ Ndbcluster_global_schema_lock_guard global_schema_lock_guard(thd);
+ if (global_schema_lock_guard.lock())
+ return 1;
if (!ndb_schema_share &&
ndbcluster_check_ndb_schema_share() == 0)
{
@@ -984,6 +1281,11 @@ int ndbcluster_setup_binlog_table_shares
}
}
+ if (ndbcluster_find_all_databases(thd))
+ {
+ return 1;
+ }
+
if (!ndbcluster_find_all_files(thd))
{
pthread_mutex_lock(&LOCK_open);
@@ -1170,7 +1472,8 @@ ndbcluster_update_slock(THD *thd,
if (ndbtab == 0)
{
- abort();
+ if (dict->getNdbError().code != 4009)
+ abort();
DBUG_RETURN(0);
}
@@ -1868,6 +2171,8 @@ ndb_binlog_thread_handle_schema_event(TH
if (ev_type == NDBEVENT::TE_UPDATE ||
ev_type == NDBEVENT::TE_INSERT)
{
+ Thd_ndb *thd_ndb= get_thd_ndb(thd);
+ Thd_ndb_options_guard thd_ndb_options(thd_ndb);
Cluster_schema *schema= (Cluster_schema *)
sql_alloc(sizeof(Cluster_schema));
MY_BITMAP slock;
@@ -1940,6 +2245,7 @@ ndb_binlog_thread_handle_schema_event(TH
}
if (! ndbcluster_check_if_local_table(schema->db, schema->name))
{
+ thd_ndb_options.set(TNO_NO_LOCK_SCHEMA_OP);
const int no_print_error[1]=
{ER_BAD_TABLE_ERROR}; /* ignore missing table */
run_query(thd, schema->query,
@@ -2001,6 +2307,7 @@ ndb_binlog_thread_handle_schema_event(TH
break;
// fall through
case SOT_CREATE_TABLE:
+ thd_ndb_options.set(TNO_NO_LOCK_SCHEMA_OP);
pthread_mutex_lock(&LOCK_open);
if (ndbcluster_check_if_local_table(schema->db, schema->name))
{
@@ -2028,6 +2335,7 @@ ndb_binlog_thread_handle_schema_event(TH
break;
case SOT_DROP_DB:
/* Drop the database locally if it only contains ndb tables */
+ thd_ndb_options.set(TNO_NO_LOCK_SCHEMA_OP);
if (! ndbcluster_check_if_local_tables_in_db(thd, schema->db))
{
const int no_print_error[1]= {0};
@@ -2059,6 +2367,7 @@ ndb_binlog_thread_handle_schema_event(TH
/* fall through */
case SOT_ALTER_DB:
{
+ thd_ndb_options.set(TNO_NO_LOCK_SCHEMA_OP);
const int no_print_error[1]= {0};
run_query(thd, schema->query,
schema->query + schema->query_length,
@@ -2217,8 +2526,10 @@ ndb_binlog_thread_handle_schema_event_po
return;
DBUG_ENTER("ndb_binlog_thread_handle_schema_event_post_epoch");
Cluster_schema *schema;
+ Thd_ndb *thd_ndb= get_thd_ndb(thd);
while ((schema= post_epoch_log_list->pop()))
{
+ Thd_ndb_options_guard thd_ndb_options(thd_ndb);
DBUG_PRINT("info",
("%s.%s: log query_length: %d query: '%s' type: %d",
schema->db, schema->name,
@@ -2366,6 +2677,7 @@ ndb_binlog_thread_handle_schema_event_po
free_share(&share);
share= 0;
}
+ thd_ndb_options.set(TNO_NO_LOCK_SCHEMA_OP);
pthread_mutex_lock(&LOCK_open);
if (ndbcluster_check_if_local_table(schema->db, schema->name))
{
@@ -2545,6 +2857,7 @@ ndb_binlog_thread_handle_schema_event_po
free_share(&share);
share= 0;
}
+ thd_ndb_options.set(TNO_NO_LOCK_SCHEMA_OP);
pthread_mutex_lock(&LOCK_open);
if (ndbcluster_check_if_local_table(schema->db, schema->name))
{
=== modified file 'sql/ha_ndbcluster_binlog.h'
--- a/sql/ha_ndbcluster_binlog.h 2008-09-30 07:40:45 +0000
+++ b/sql/ha_ndbcluster_binlog.h 2008-09-30 10:42:32 +0000
@@ -286,3 +286,17 @@ inline void do_retry_sleep(unsigned mill
{
my_sleep(1000*(milli_sleep + 5*(rand()%(milli_sleep/5))));
}
+
+int ndbcluster_has_global_schema_lock(Thd_ndb *thd_ndb);
+void ndbcluster_no_global_schema_lock_abort(THD *thd, const char *msg);
+
+class Ndbcluster_global_schema_lock_guard
+{
+public:
+ Ndbcluster_global_schema_lock_guard(THD *thd);
+ ~Ndbcluster_global_schema_lock_guard();
+ int lock();
+private:
+ THD *m_thd;
+ int m_lock;
+};
=== added file 'sql/ha_ndbcluster_lock_ext.h'
--- a/sql/ha_ndbcluster_lock_ext.h 1970-01-01 00:00:00 +0000
+++ b/sql/ha_ndbcluster_lock_ext.h 2008-09-30 09:14:44 +0000
@@ -0,0 +1,91 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ These functions are shared with ndb_restore so that the creating of
+ tables through ndb_restore is syncronized correctly with the mysqld's
+
+ The lock/unlock functions use the BACKUP_SEQUENCE row in SYSTAB_0
+*/
+static NdbTransaction *
+ndbcluster_global_schema_lock_ext(Ndb *ndb, NdbError &ndb_error)
+{
+ ndb->setDatabaseName("sys");
+ ndb->setDatabaseSchemaName("def");
+ NdbDictionary::Dictionary *dict= ndb->getDictionary();
+ Ndb_table_guard ndbtab_g(dict, "SYSTAB_0");
+ const NdbDictionary::Table *ndbtab;
+ NdbOperation *op;
+ NdbTransaction *trans= NULL;
+ int retries= 100;
+ int retry_sleep= 50; /* 50 milliseconds, transaction */
+ if (!(ndbtab= ndbtab_g.get_table()))
+ {
+ ndb_error= dict->getNdbError();
+ goto error_handler;
+ }
+ while (1)
+ {
+ trans= ndb->startTransaction();
+ if (trans == NULL)
+ {
+ ndb_error= ndb->getNdbError();
+ goto error_handler;
+ }
+
+ op= trans->getNdbOperation(ndbtab);
+ op->readTuple(NdbOperation::LM_Exclusive);
+ op->equal("SYSKEY_0", NDB_BACKUP_SEQUENCE);
+
+ if (trans->execute(NdbTransaction::NoCommit) == 0)
+ break;
+
+ if (trans->getNdbError().status == NdbError::TemporaryError)
+ {
+ if (retries--)
+ {
+ ndb->closeTransaction(trans);
+ trans= NULL;
+ do_retry_sleep(retry_sleep);
+ continue; // retry
+ }
+ }
+ ndb_error= trans->getNdbError();
+ goto error_handler;
+ }
+ return trans;
+
+ error_handler:
+ if (trans)
+ {
+ ndb->closeTransaction(trans);
+ }
+ return NULL;
+}
+
+static int
+ndbcluster_global_schema_unlock_ext(Ndb *ndb, NdbTransaction *trans,
+ NdbError &ndb_error)
+{
+ if (trans->execute(NdbTransaction::Commit))
+ {
+ ndb_error= trans->getNdbError();
+ ndb->closeTransaction(trans);
+ return -1;
+ }
+ ndb->closeTransaction(trans);
+ return 0;
+}
=== modified file 'sql/handler.cc'
--- a/sql/handler.cc 2008-09-02 16:31:03 +0000
+++ b/sql/handler.cc 2008-09-30 10:42:32 +0000
@@ -3852,6 +3852,24 @@ int ha_binlog_index_purge_file(THD *thd,
return 0;
}
+int ha_global_schema_lock(THD *thd)
+{
+ binlog_func_st bfn= {BFN_GLOBAL_SCHEMA_LOCK, 0};
+ binlog_func_foreach(thd, &bfn);
+ if (thd->main_da.is_error())
+ return 1;
+ return 0;
+}
+
+int ha_global_schema_unlock(THD *thd)
+{
+ binlog_func_st bfn= {BFN_GLOBAL_SCHEMA_UNLOCK, 0};
+ binlog_func_foreach(thd, &bfn);
+ if (thd->main_da.is_error())
+ return 1;
+ return 0;
+}
+
struct binlog_log_query_st
{
enum_binlog_command binlog_command;
=== modified file 'sql/handler.h'
--- a/sql/handler.h 2008-09-02 16:31:03 +0000
+++ b/sql/handler.h 2008-09-30 10:42:32 +0000
@@ -324,7 +324,9 @@ enum enum_binlog_func {
BFN_RESET_SLAVE= 2,
BFN_BINLOG_WAIT= 3,
BFN_BINLOG_END= 4,
- BFN_BINLOG_PURGE_FILE= 5
+ BFN_BINLOG_PURGE_FILE= 5,
+ BFN_GLOBAL_SCHEMA_LOCK= 6,
+ BFN_GLOBAL_SCHEMA_UNLOCK=7
};
enum enum_binlog_command {
@@ -2214,6 +2216,29 @@ void ha_binlog_log_query(THD *thd, handl
const char *db, const char *table_name);
void ha_binlog_wait(THD *thd);
int ha_binlog_end(THD *thd);
+int ha_global_schema_lock(THD *thd);
+int ha_global_schema_unlock(THD *thd);
+class Ha_global_schema_lock_guard
+{
+public:
+ Ha_global_schema_lock_guard(THD *thd)
+ : m_thd(thd), m_lock(0)
+ {
+ }
+ ~Ha_global_schema_lock_guard()
+ {
+ if (m_lock)
+ ha_global_schema_unlock(m_thd);
+ }
+ int lock()
+ {
+ m_lock= !ha_global_schema_lock(m_thd);
+ return !m_lock;
+ }
+private:
+ THD *m_thd;
+ int m_lock;
+};
#else
inline int ha_int_dummy() { return 0; }
#define ha_reset_logs(a) ha_int_dummy()
@@ -2222,4 +2247,13 @@ inline int ha_int_dummy() { return 0; }
#define ha_binlog_log_query(a,b,c,d,e,f,g) do {} while (0)
#define ha_binlog_wait(a) do {} while (0)
#define ha_binlog_end(a) do {} while (0)
+#define ha_global_schema_lock(a) ha_int_dummy()
+#define ha_global_schema_unlock(a) ha_int_dummy()
+class Ha_global_schema_lock_guard
+{
+public:
+ Ha_global_schema_lock_guard(THD *thd) {}
+ ~Ha_global_schema_lock_guard() {}
+ int lock() { return 0; }
+};
#endif
=== modified file 'sql/sql_db.cc'
--- a/sql/sql_db.cc 2008-08-27 08:40:11 +0000
+++ b/sql/sql_db.cc 2008-09-30 09:14:44 +0000
@@ -625,6 +625,8 @@ int mysql_create_db(THD *thd, char *db,
DBUG_RETURN(-1);
}
+ ha_global_schema_lock(thd);
+
/*
Do not create database if another thread is holding read lock.
Wait for global read lock before acquiring LOCK_mysql_create_db.
@@ -756,6 +758,7 @@ exit:
VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
start_waiting_global_read_lock(thd);
exit2:
+ ha_global_schema_unlock(thd);
DBUG_RETURN(error);
}
@@ -769,6 +772,8 @@ bool mysql_alter_db(THD *thd, const char
int error= 0;
DBUG_ENTER("mysql_alter_db");
+ ha_global_schema_lock(thd);
+
/*
Do not alter database if another thread is holding read lock.
Wait for global read lock before acquiring LOCK_mysql_create_db.
@@ -832,6 +837,7 @@ exit:
VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
start_waiting_global_read_lock(thd);
exit2:
+ ha_global_schema_unlock(thd);
DBUG_RETURN(error);
}
@@ -863,6 +869,8 @@ bool mysql_rm_db(THD *thd,char *db,bool
TABLE_LIST* dropped_tables= 0;
DBUG_ENTER("mysql_rm_db");
+ ha_global_schema_lock(thd);
+
/*
Do not drop database if another thread is holding read lock.
Wait for global read lock before acquiring LOCK_mysql_create_db.
@@ -1028,6 +1036,7 @@ exit:
VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
start_waiting_global_read_lock(thd);
exit2:
+ ha_global_schema_unlock(thd);
DBUG_RETURN(error);
}
=== modified file 'sql/sql_delete.cc'
--- a/sql/sql_delete.cc 2008-09-02 16:31:03 +0000
+++ b/sql/sql_delete.cc 2008-09-30 10:42:32 +0000
@@ -989,6 +989,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST
TABLE *table;
bool error;
uint path_length;
+ Ha_global_schema_lock_guard global_schema_lock_guard(thd);
DBUG_ENTER("mysql_truncate");
bzero((char*) &create_info,sizeof(create_info));
@@ -1039,6 +1040,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST
HTON_CAN_RECREATE))
goto trunc_by_del;
+ if (table_type == DB_TYPE_NDBCLUSTER)
+ global_schema_lock_guard.lock();
if (lock_and_wait_for_table_name(thd, table_list))
DBUG_RETURN(TRUE);
}
=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc 2008-09-01 12:28:57 +0000
+++ b/sql/sql_parse.cc 2008-09-30 09:14:44 +0000
@@ -2419,6 +2419,7 @@ mysql_execute_command(THD *thd)
copy.
*/
Alter_info alter_info(lex->alter_info, thd->mem_root);
+ Ha_global_schema_lock_guard global_schema_lock_guard(thd);
if (thd->is_fatal_error)
{
@@ -2454,6 +2455,8 @@ mysql_execute_command(THD *thd)
create_info.default_table_charset= create_info.table_charset;
create_info.table_charset= 0;
}
+ if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
+ global_schema_lock_guard.lock();
/*
The create-select command will open and read-lock the select table
and then create, open and write-lock the new table. If a global
=== modified file 'sql/sql_rename.cc'
--- a/sql/sql_rename.cc 2008-02-19 12:45:21 +0000
+++ b/sql/sql_rename.cc 2008-09-30 09:14:44 +0000
@@ -37,6 +37,7 @@ bool mysql_rename_tables(THD *thd, TABLE
TABLE_LIST *ren_table= 0;
int to_table;
char *rename_log_table[2]= {NULL, NULL};
+ Ha_global_schema_lock_guard global_schema_lock_guard(thd);
DBUG_ENTER("mysql_rename_tables");
/*
@@ -52,6 +53,8 @@ bool mysql_rename_tables(THD *thd, TABLE
}
mysql_ha_rm_tables(thd, table_list, FALSE);
+
+ global_schema_lock_guard.lock();
if (wait_if_global_read_lock(thd,0,1))
DBUG_RETURN(1);
=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc 2008-09-02 09:51:07 +0000
+++ b/sql/sql_table.cc 2008-09-30 09:14:44 +0000
@@ -1474,12 +1474,14 @@ bool mysql_rm_table(THD *thd,TABLE_LIST
my_bool drop_temporary)
{
bool error= FALSE, need_start_waiters= FALSE;
+ Ha_global_schema_lock_guard global_schema_lock_guard(thd);
DBUG_ENTER("mysql_rm_table");
/* mark for close and remove all cached entries */
if (!drop_temporary)
{
+ global_schema_lock_guard.lock();
if ((error= wait_if_global_read_lock(thd, 0, 1)))
{
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->table_name);
@@ -6595,6 +6597,8 @@ view_err:
DBUG_RETURN(error);
}
+ Ha_global_schema_lock_guard global_schema_lock_guard(thd);
+ global_schema_lock_guard.lock();
if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ)))
DBUG_RETURN(TRUE);
table->use_all_columns();
| Thread |
|---|
| • bzr commit into mysql-5.1 branch (tomas.ulin:2693) | Tomas Ulin | 30 Sep |