[prev in list] [next in list] [prev in thread] [next in thread] 

List:       mysql-internals
Subject:    bk commit into 5.1 tree (lars:1.1980)
From:       Lars Thalmann <lars () mysql ! com>
Date:       2005-12-29 20:16:52
Message-ID: 200512292016.jBTKGq7X004811 () dl145j ! mysql ! com
[Download RAW message or body]

Below is the list of changes that have just been committed into a local
5.1 repository of lthalmann. When lthalmann does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet
  1.1980 05/12/29 21:16:34 lars@mysql.com +39 -0
  Merge mysql.com:/users/lthalmann/bkroot/mysql-5.1-wl1012-v3
  into  mysql.com:/users/lthalmann/bk/mysql-5.1-wl2325-v3-max

  sql/log_event.cc
    1.196 05/12/29 21:16:18 lars@mysql.com +2 -4
    Merge

  sql/ha_ndbcluster.cc
    1.226 05/12/29 21:16:17 lars@mysql.com +0 -0
    Merge

  mysql-test/t/disabled.def
    1.16 05/12/29 21:16:17 lars@mysql.com +0 -1
    Merge

  mysql-test/mysql-test-run.sh
    1.284 05/12/29 21:16:17 lars@mysql.com +1 -0
    Merge

  include/my_base.h
    1.76 05/12/29 21:16:16 lars@mysql.com +1 -2
    Merge

  storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp
    1.64 05/12/29 20:48:17 lars@mysql.com +0 -0
    Auto merged

  storage/ndb/src/kernel/blocks/backup/Backup.cpp
    1.28 05/12/29 20:48:15 lars@mysql.com +0 -0
    Auto merged

  sql/sql_repl.cc
    1.148 05/12/29 20:48:15 lars@mysql.com +0 -0
    Auto merged

  sql/sql_parse.cc
    1.487 05/12/29 20:48:15 lars@mysql.com +0 -0
    Auto merged

  sql/sql_class.h
    1.271 05/12/29 20:48:14 lars@mysql.com +0 -0
    Auto merged

  sql/slave.cc
    1.260 05/12/29 20:48:13 lars@mysql.com +0 -0
    Auto merged

  sql/set_var.cc
    1.150 05/12/29 20:48:13 lars@mysql.com +0 -0
    Auto merged

  sql/mysqld.cc
    1.506 05/12/29 20:48:12 lars@mysql.com +0 -0
    Auto merged

  sql/mysql_priv.h
    1.347 05/12/29 20:48:11 lars@mysql.com +0 -0
    Auto merged

  sql/log.cc
    1.178 05/12/29 20:48:10 lars@mysql.com +0 -0
    Auto merged

  sql/handler.h
    1.170 05/12/29 20:48:10 lars@mysql.com +0 -0
    Auto merged

  sql/handler.cc
    1.195 05/12/29 20:48:10 lars@mysql.com +0 -0
    Auto merged

  sql/ha_ndbcluster.h
    1.101 05/12/29 20:48:09 lars@mysql.com +0 -0
    Auto merged

  sql/Makefile.am
    1.124 05/12/29 20:48:09 lars@mysql.com +0 -0
    Auto merged

  mysql-test/t/sp.test
    1.169 05/12/29 20:48:08 lars@mysql.com +0 -0
    Auto merged

  mysql-test/t/ndb_multi.test
    1.8 05/12/29 20:48:08 lars@mysql.com +0 -0
    Auto merged

  mysql-test/t/ndb_alter_table.test
    1.30 05/12/29 20:48:08 lars@mysql.com +0 -0
    Auto merged

  mysql-test/t/mysqltest.test
    1.21 05/12/29 20:48:08 lars@mysql.com +0 -0
    Auto merged

  mysql-test/t/information_schema.test
    1.64 05/12/29 20:48:08 lars@mysql.com +0 -0
    Auto merged

  mysql-test/t/count_distinct3.test
    1.6 05/12/29 20:48:07 lars@mysql.com +0 -0
    Auto merged

  mysql-test/r/show_check.result
    1.87 05/12/29 20:48:07 lars@mysql.com +0 -0
    Auto merged

  mysql-test/r/schema.result
    1.4 05/12/29 20:48:07 lars@mysql.com +0 -0
    Auto merged

  mysql-test/r/ndb_multi.result
    1.7 05/12/29 20:48:07 lars@mysql.com +0 -0
    Auto merged

  mysql-test/r/ndb_config.result
    1.8 05/12/29 20:48:07 lars@mysql.com +0 -5
    Auto merged

  mysql-test/r/ndb_alter_table.result
    1.36 05/12/29 20:48:07 lars@mysql.com +0 -0
    Auto merged

  mysql-test/r/mysqltest.result
    1.17 05/12/29 20:48:06 lars@mysql.com +0 -0
    Auto merged

  mysql-test/r/information_schema.result
    1.92 05/12/29 20:48:06 lars@mysql.com +0 -0
    Auto merged

  mysql-test/ndb/ndbcluster.sh
    1.45 05/12/29 20:48:06 lars@mysql.com +0 -50
    Auto merged

  mysql-test/ndb/ndb_config_2_node.ini
    1.17 05/12/29 20:48:06 lars@mysql.com +0 -12
    Auto merged

  mysql-test/mysql-test-run.pl
    1.44 05/12/29 20:48:06 lars@mysql.com +0 -0
    Auto merged

  libmysqld/Makefile.am
    1.73 05/12/29 20:48:05 lars@mysql.com +0 -1
    Auto merged

  config/ac-macros/zlib.m4
    1.6 05/12/29 20:48:05 lars@mysql.com +0 -1
    Auto merged

  client/mysqltest.c
    1.166 05/12/29 20:48:05 lars@mysql.com +0 -0
    Auto merged

  BitKeeper/deleted/.del-ndb_config_1_node.ini
    1.2 05/12/29 20:48:01 lars@mysql.com +0 -0
    Delete: mysql-test/ndb/ndb_config_1_node.ini

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	lars
# Host:	dl145j.mysql.com
# Root:	/users/lthalmann/bk/mysql-5.1-wl2325-v3-max/RESYNC

--- 1.75/include/my_base.h	2005-11-24 09:59:00 +01:00
+++ 1.76/include/my_base.h	2005-12-29 21:16:16 +01:00
@@ -155,6 +155,7 @@
   HA_EXTRA_KEYREAD_PRESERVE_FIELDS,
   HA_EXTRA_IGNORE_NO_KEY,		/* Tuple not found don't rollback everything*/
   HA_EXTRA_NO_IGNORE_NO_KEY
+  HA_EXTRA_MMAP
 };

 	/* The following is parameter to ha_panic() */
@@ -246,7 +247,7 @@
 #define HA_OPTION_CHECKSUM		32
 #define HA_OPTION_DELAY_KEY_WRITE	64
 #define HA_OPTION_NO_PACK_KEYS		128  /* Reserved for MySQL */
-#define HA_OPTION_CREATE_FROM_ENGINE    256
+#define HA_OPTION_CREATE_FROM_ENGINE    256
 #define HA_OPTION_TEMP_COMPRESS_RECORD	((uint) 16384)	/* set by isamchk */
 #define HA_OPTION_READ_ONLY_DATA	((uint) 32768)	/* Set by isamchk */

@@ -258,15 +259,48 @@
 #define HA_CREATE_CHECKSUM	8
 #define HA_CREATE_DELAY_KEY_WRITE 64

-	/* Bits in flag to _status */
+/*
+  The following flags (OR-ed) are passed to handler::info() method.
+  The method copies misc handler information out of the storage engine
+  to data structures accessible from MySQL
+
+  Same flags are also passed down to mi_status, myrg_status, etc.
+*/

-#define HA_STATUS_POS		1		/* Return position */
-#define HA_STATUS_NO_LOCK	2		/* Don't use external lock */
-#define HA_STATUS_TIME		4		/* Return update time */
-#define HA_STATUS_CONST		8		/* Return constants values */
-#define HA_STATUS_VARIABLE	16
-#define HA_STATUS_ERRKEY	32
-#define HA_STATUS_AUTO		64
+/* this one is not used */
+#define HA_STATUS_POS            1
+/*
+  assuming the table keeps shared actual copy of the 'info' and
+  local, possibly outdated copy, the following flag means that
+  it should not try to get the actual data (locking the shared structure)
+  slightly outdated version will suffice
+*/
+#define HA_STATUS_NO_LOCK        2
+/* update the time of the last modification (in handler::update_time) */
+#define HA_STATUS_TIME           4
+/*
+  update the 'constant' part of the info:
+  handler::max_data_file_length, max_index_file_length, create_time
+  sortkey, ref_length, block_size, data_file_name, index_file_name.
+  handler::table->s->keys_in_use, keys_for_keyread, rec_per_key
+*/
+#define HA_STATUS_CONST          8
+/*
+  update the 'variable' part of the info:
+  handler::records, deleted, data_file_length, index_file_length,
+  delete_length, check_time, mean_rec_length
+*/
+#define HA_STATUS_VARIABLE      16
+/*
+  get the information about the key that caused last duplicate value error
+  update handler::errkey and handler::dupp_ref
+  see handler::get_dup_key()
+*/
+#define HA_STATUS_ERRKEY        32
+/*
+  update handler::auto_increment_value
+*/
+#define HA_STATUS_AUTO          64

 	/* Errorcodes given by functions */

@@ -311,9 +345,12 @@
 #define HA_ERR_NO_CONNECTION     157  /* Could not connect to storage engine */
 #define HA_ERR_NULL_IN_SPATIAL   158  /* NULLs are not supported in spatial index */
 #define HA_ERR_TABLE_DEF_CHANGED 159  /* The table changed in storage engine */
-#define HA_ERR_RBR_LOGGING_FAILED 160  /* row-based binlogging of row failed */
+#define HA_ERR_NO_PARTITION_FOUND 160  /* There's no partition in table for
+                                          given value */
+#define HA_ERR_RBR_LOGGING_FAILED 161  /* Row-based binlogging of row failed */
+
+#define HA_ERR_LAST               161  /* Copy last error no */

-#define HA_ERR_LAST              160  /*Copy last error nr.*/
 /* Add error numbers before HA_ERR_LAST and change it accordingly. */
 #define HA_ERR_ERRORS            (HA_ERR_LAST - HA_ERR_FIRST + 1)


--- 1.283/mysql-test/mysql-test-run.sh	2005-11-24 17:32:54 +01:00
+++ 1.284/mysql-test/mysql-test-run.sh	2005-12-29 21:16:17 +01:00
@@ -243,11 +243,10 @@
 FAILED_CASES
 EXTRA_MASTER_OPT=""
-EXTRA_MASTER_MYSQLD_OPT=@EXTRA_MASTER_MYSQLD_OPT@
-EXTRA_SLAVE_MYSQLD_OPT=@EXTRA_SLAVE_MYSQLD_OPT@
 EXTRA_MYSQL_TEST_OPT=""
 EXTRA_MYSQLCHECK_OPT=""
 EXTRA_MYSQLDUMP_OPT=""
+EXTRA_MYSQLSLAP_OPT=""
 EXTRA_MYSQLSHOW_OPT=""
 EXTRA_MYSQLBINLOG_OPT=""
 USE_RUNNING_SERVER=0
@@ -289,7 +288,7 @@
 STRESS_SUITE="main"
 STRESS_MODE="random"
 STRESS_THREADS=5
-STRESS_TEST_COUNT 
+STRESS_TEST_COUNT=""
 STRESS_LOOP_COUNT=""
 STRESS_TEST_DURATION=""
 STRESS_INIT_FILE=""
@@ -546,6 +545,8 @@
        --debug=d:t:A,$MYSQL_TEST_DIR/var/log/mysqlcheck.trace"
       EXTRA_MYSQLDUMP_OPT="$EXTRA_MYSQLDUMP_OPT \
        --debug=d:t:A,$MYSQL_TEST_DIR/var/log/mysqldump.trace"
+      EXTRA_MYSQLSLAP_OPT="$EXTRA_MYSQLSLAP_OPT \
+       --debug=d:t:A,$MYSQL_TEST_DIR/var/log/mysqlslap.trace"
       EXTRA_MYSQLSHOW_OPT="$EXTRA_MYSQLSHOW_OPT \
        --debug=d:t:A,$MYSQL_TEST_DIR/var/log/mysqlshow.trace"
       EXTRA_MYSQLBINLOG_OPT="$EXTRA_MYSQLBINLOG_OPT \
@@ -660,6 +661,11 @@
  else
    MYSQL_DUMP="$BASEDIR/client/mysqldump"
  fi
+ if [ -f "$BASEDIR/client/.libs/mysqlslap" ] ; then
+   MYSQL_SLAP="$BASEDIR/client/.libs/mysqlslap"
+ else
+   MYSQL_SLAP="$BASEDIR/client/mysqlslap"
+ fi
  if [ -f "$BASEDIR/client/.libs/mysqlimport" ] ; then
    MYSQL_IMPORT="$BASEDIR/client/.libs/mysqlimport"
  else
@@ -739,6 +745,8 @@
  fi
  MYSQL_TEST="$CLIENT_BINDIR/mysqltest"
  MYSQL_CHECK="$CLIENT_BINDIR/mysqlcheck"
+ MYSQL_DUMP="$CLIENT_BINDIR/mysqldump"
+ MYSQL_SLAP="$CLIENT_BINDIR/mysqlslap"
  MYSQL_SHOW="$CLIENT_BINDIR/mysqlshow"
  MYSQL_IMPORT="$CLIENT_BINDIR/mysqlimport"
  MYSQL_BINLOG="$CLIENT_BINDIR/mysqlbinlog"
@@ -833,6 +841,7 @@
 export MYSQL_DUMP_DIR
 MYSQL_CHECK="$MYSQL_CHECK --no-defaults -uroot --socket=$MASTER_MYSOCK \
--password=$DBPASSWD $EXTRA_MYSQLCHECK_OPT"  MYSQL_DUMP="$MYSQL_DUMP --no-defaults \
-uroot --socket=$MASTER_MYSOCK --password=$DBPASSWD $EXTRA_MYSQLDUMP_OPT" \
+MYSQL_SLAP="$MYSQL_SLAP -uroot --socket=$MASTER_MYSOCK --password=$DBPASSWD \
$EXTRA_MYSQLSLAP_OPT"  MYSQL_DUMP_SLAVE="$MYSQL_DUMP_DIR --no-defaults -uroot \
--socket=$SLAVE_MYSOCK --password=$DBPASSWD $EXTRA_MYSQLDUMP_OPT"  \
MYSQL_SHOW="$MYSQL_SHOW -uroot --socket=$MASTER_MYSOCK --password=$DBPASSWD \
$EXTRA_MYSQLSHOW_OPT"  MYSQL_BINLOG="$MYSQL_BINLOG --no-defaults \
--local-load=$MYSQL_TMP_DIR  --character-sets-dir=$CHARSETSDIR \
$EXTRA_MYSQLBINLOG_OPT" @@ -841,6 +850,7 @@
 MYSQL="$MYSQL --no-defaults --host=localhost --port=$MASTER_MYPORT \
--socket=$MASTER_MYSOCK --user=root --password=$DBPASSWD"  export MYSQL MYSQL_CHECK \
MYSQL_DUMP MYSQL_DUMP_SLAVE MYSQL_SHOW MYSQL_BINLOG MYSQL_FIX_SYSTEM_TABLES \
MYSQL_IMPORT  export CLIENT_BINDIR MYSQL_CLIENT_TEST CHARSETSDIR \
MYSQL_MY_PRINT_DEFAULTS +export MYSQL_SLAP
 export NDB_TOOLS_DIR
 export NDB_MGM
 export NDB_BACKUP_DIR
@@ -1324,6 +1334,7 @@
           --innodb_data_file_path=ibdata1:128M:autoextend \
 	  --open-files-limit24 \
           --log-bin-trust-function-creators \
+          --loose-binlog-show-xid=0 \
 	   $MASTER_40_ARGS \
            $SMALL_SERVER \
            $EXTRA_MASTER_MYSQLD_OPT $EXTRA_MASTER_OPT \
@@ -1345,6 +1356,7 @@
           --language=$LANGUAGE \
           --innodb_data_file_path=ibdata1:128M:autoextend \
           --log-bin-trust-function-creators \
+          --loose-binlog-show-xid=0 \
 	   $MASTER_40_ARGS \
            $SMALL_SERVER \
            $EXTRA_MASTER_MYSQLD_OPT $EXTRA_MASTER_OPT \
@@ -1368,16 +1380,16 @@

   if [ x$DO_DDD = x1 ]
   then
-    $ECHO "set args $master_args" > $GDB_MASTER_INIT
+    $ECHO "set args $master_args" > $GDB_MASTER_INIT$1
     manager_launch master ddd -display $DISPLAY --debugger \
-    "gdb -x $GDB_MASTER_INIT" $MASTER_MYSQLD
+    "gdb -x $GDB_MASTER_INIT$1" $MASTER_MYSQLD
   elif [ x$DO_GDB = x1 ]
   then
     if [ x$MANUAL_GDB = x1 ]
     then
-      $ECHO "set args $master_args" > $GDB_MASTER_INIT
+      $ECHO "set args $master_args" > $GDB_MASTER_INIT$1
       $ECHO "To start gdb for the master , type in another window:"
-      $ECHO "cd $CWD ; gdb -x $GDB_MASTER_INIT $MASTER_MYSQLD"
+      $ECHO "cd $CWD ; gdb -x $GDB_MASTER_INIT$1 $MASTER_MYSQLD"
       wait_for_master00
     else
       ( $ECHO set args $master_args;
@@ -1389,9 +1401,9 @@
 end
 r
 EOF
-      fi )  > $GDB_MASTER_INIT
+      fi )  > $GDB_MASTER_INIT$1
       manager_launch master $XTERM -display $DISPLAY \
-      -title "Master" -e gdb -x $GDB_MASTER_INIT $MASTER_MYSQLD
+      -title "Master" -e gdb -x $GDB_MASTER_INIT$1 $MASTER_MYSQLD
     fi
   else
     manager_launch master $MASTER_MYSQLD $master_args
@@ -1511,6 +1523,7 @@
           --master-retry-count \
           -O slave_net_timeout \
           --log-bin-trust-function-creators \
+          --loose-binlog-show-xid=0 \
            $SMALL_SERVER \
            $EXTRA_SLAVE_MYSQLD_OPT $EXTRA_SLAVE_OPT \
            $USE_NDBCLUSTER_SLAVE_OPT"
@@ -2015,7 +2028,7 @@
                     --stress-basedir=$STRESS_BASEDIR \
                     --server-logs-dir=$STRESS_BASEDIR \
                     --stress-mode=$STRESS_MODE \
-                    --mysqltest=$BASEDIR/client/mysqltest \
+                    --mysqltest=$CLIENT_BINDIR/mysqltest \
                     --threads=$STRESS_THREADS \
                     --verbose \
                     --cleanup \
@@ -2026,6 +2039,14 @@
     STRESS_TEST_ARGS="$STRESS_TEST_ARGS --stress-init-file=$STRESS_INIT_FILE"
   fi

+  if [ -z "$STRESS_LOOP_COUNT" -a -z  "$STRESS_TEST_COUNT" -a
+       -z "$STRESS_TEST_DURATION" ] ; then
+
+    #Limit stress testing with 20 loops in case when any limit parameter
+    #was specified
+    STRESS_TEST_COUNT 
+  fi
+
   if [ -n "$STRESS_LOOP_COUNT" ] ; then
     STRESS_TEST_ARGS="$STRESS_TEST_ARGS --loop-count=$STRESS_LOOP_COUNT"
   fi
@@ -2074,10 +2095,10 @@
     $MYSQLADMIN --no-defaults --socket=$MASTER_MYSOCK -u root -O connect_timeout=5 \
                -O shutdown_timeout  shutdown > /dev/null 2>&1
     $MYSQLADMIN --no-defaults --socket=$MASTER_MYSOCK1 -u root -O connect_timeout=5 \
                -O shutdown_timeout  shutdown > /dev/null 2>&1
     $MYSQLADMIN --no-defaults --socket=$SLAVE_MYSOCK -u root -O connect_timeout=5 -O \
                shutdown_timeout  shutdown > /dev/null 2>&1
-    $MYSQLADMIN --no-defaults --host=$hostname --port=$MASTER_MYPORT -u root -O \
                connect_timeout=5 -O shutdown_timeout  shutdown > /dev/null 2>&1
-    $MYSQLADMIN --no-defaults --host=$hostname --port=`expr $MASTER_MYPORT+1` -u \
                root -O connect_timeout=5 -O shutdown_timeout  shutdown > /dev/null \
                2>&1
-    $MYSQLADMIN --no-defaults --host=$hostname --port=$SLAVE_MYPORT -u root -O \
                connect_timeout=5 -O shutdown_timeout  shutdown > /dev/null 2>&1
-    $MYSQLADMIN --no-defaults --host=$hostname --port=`expr $SLAVE_MYPORT + 1` -u \
root -O connect_timeout=5 -O shutdown_timeout  shutdown > /dev/null 2>&1 +    \
$MYSQLADMIN --no-defaults --host=$hostname --port=$MASTER_MYPORT --protocol=tcp -u \
root -O connect_timeout=5 -O shutdown_timeout  shutdown > /dev/null 2>&1 +    \
$MYSQLADMIN --no-defaults --host=$hostname --protocol=tcp --port=`expr \
$MASTER_MYPORT+1` -u root -O connect_timeout=5 -O shutdown_timeout  shutdown > \
/dev/null 2>&1 +    $MYSQLADMIN --no-defaults --host=$hostname --protocol=tcp \
--port=$SLAVE_MYPORT -u root -O connect_timeout=5 -O shutdown_timeout  shutdown > \
/dev/null 2>&1 +    $MYSQLADMIN --no-defaults --host=$hostname --protocol=tcp \
--port=`expr $SLAVE_MYPORT + 1` -u root -O connect_timeout=5 -O shutdown_timeout  \
shutdown > /dev/null 2>&1  sleep_until_file_deleted 0 $MASTER_MYPID
     sleep_until_file_deleted 0 $MASTER_MYPID"1"
     sleep_until_file_deleted 0 $SLAVE_MYPID

--- 1.123/sql/Makefile.am	2005-11-24 09:59:02 +01:00
+++ 1.124/sql/Makefile.am	2005-12-29 20:48:09 +01:00
@@ -46,6 +46,7 @@
                         @yassl_libs@ @openssl_libs@
 noinst_HEADERS =	item.h item_func.h item_sum.h item_cmpfunc.h \
 			item_strfunc.h item_timefunc.h item_uniq.h \
+			item_xmlfunc.h \
 			item_create.h item_subselect.h item_row.h \
 			mysql_priv.h item_geofunc.h sql_bitmap.h \
 			procedure.h sql_class.h sql_lex.h sql_list.h \
@@ -63,12 +64,12 @@
 			sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \
 			parse_file.h sql_view.h	sql_trigger.h \
 			sql_array.h sql_cursor.h \
-			sql_plugin.h
+			sql_plugin.h authors.h
 mysqld_SOURCES =	sql_lex.cc sql_handler.cc sql_partition.cc \
 			item.cc item_sum.cc item_buff.cc item_func.cc \
 			item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \
 			thr_malloc.cc item_create.cc item_subselect.cc \
-			item_row.cc item_geofunc.cc \
+			item_row.cc item_geofunc.cc item_xmlfunc.cc \
 			field.cc strfunc.cc key.cc sql_class.cc sql_list.cc \
 			net_serv.cc protocol.cc sql_state.c \
 			lock.cc my_lock.c \
@@ -97,7 +98,7 @@
                         tztime.cc my_time.c my_decimal.cc\
 			sp_head.cc sp_pcontext.cc  sp_rcontext.cc sp.cc \
 			sp_cache.cc parse_file.cc sql_trigger.cc \
-			sql_plugin.cc\
+			sql_plugin.cc sql_binlog.cc \
 			handlerton.cc
 EXTRA_mysqld_SOURCES =	ha_innodb.cc ha_berkeley.cc ha_archive.cc \
 			ha_innodb.h  ha_berkeley.h  ha_archive.h \
@@ -120,23 +121,23 @@
 			@DEFS@

 BUILT_SOURCES =		sql_yacc.cc sql_yacc.h lex_hash.h
-EXTRA_DIST =		udf_example.cc $(BUILT_SOURCES)
+EXTRA_DIST =		udf_example.cc handlerton-win.cc $(BUILT_SOURCES)
 DISTCLEANFILES =        lex_hash.h
 AM_YFLAGS =		-d

 mysql_tzinfo_to_sql.cc:
 	rm -f mysql_tzinfo_to_sql.cc
-	@LN_CP_F@ tztime.cc mysql_tzinfo_to_sql.cc
+	@LN_CP_F@ $(srcdir)/tztime.cc mysql_tzinfo_to_sql.cc

 link_sources: mysql_tzinfo_to_sql.cc
 	rm -f mini_client_errors.c
-	@LN_CP_F@ ../libmysql/errmsg.c mini_client_errors.c
+	@LN_CP_F@ $(top_srcdir)/libmysql/errmsg.c mini_client_errors.c
 	rm -f pack.c
-	@LN_CP_F@ ../sql-common/pack.c pack.c
+	@LN_CP_F@ $(top_srcdir)/sql-common/pack.c pack.c
 	rm -f client.c
-	@LN_CP_F@ ../sql-common/client.c client.c
+	@LN_CP_F@ $(top_srcdir)/sql-common/client.c client.c
 	rm -f my_time.c
-	@LN_CP_F@ ../sql-common/my_time.c my_time.c
+	@LN_CP_F@ $(top_srcdir)/sql-common/my_time.c my_time.c

 mysql_tzinfo_to_sql.o:	$(mysql_tzinfo_to_sql_SOURCES)
 			$(CXXCOMPILE) -c $(INCLUDES) -DTZINFO2SQL $<

--- 1.194/sql/handler.cc	2005-11-24 09:59:02 +01:00
+++ 1.195/sql/handler.cc	2005-12-29 20:48:10 +01:00
@@ -190,7 +190,8 @@
 } /* ha_checktype */


-handler *get_new_handler(TABLE *table, MEM_ROOT *alloc, enum db_type db_type)
+handler *get_new_handler(TABLE_SHARE *share, MEM_ROOT *alloc,
+                         enum db_type db_type)
 {
   handler *file= NULL;
   handlerton **types;
@@ -206,7 +207,7 @@
     if (db_type == (*types)->db_type && (*types)->create)
     {
       file= ((*types)->state == SHOW_OPTION_YES) ?
-		(*types)->create(table) : NULL;
+		(*types)->create(share) : NULL;
       break;
     }
   }
@@ -217,7 +218,7 @@
     enum db_type def=(enum db_type) current_thd->variables.table_type;
     /* Try first with 'default table type' */
     if (db_type != def)
-      return get_new_handler(table, alloc, def);
+      return get_new_handler(share, alloc, def);
   }
   if (file)
   {
@@ -680,83 +681,6 @@
       thd->variables.tx_isolation=thd->session_tx_isolation;
       thd->transaction.cleanup();
     }
-    /*
-      TODO: REORGANIZATION (Guilhem 27 Oct 2005).
-
-      1) as the pending event is a member of thd->transaction, it would be
-      tidy to move prepare_for_commit|rollback into the st_transaction class;
-      roughly they are dependent on nothing which is in thd and which is not in
-      thd->transaction. So we would do:
-      thd->transaction.prepare_for_rollback().
-
-
-      2) prepare_for_rollback(), intended to do thread's preparations for
-      rollback, not only related to binlog, currently does only binlog things,
-      while some preparation is done in ha_rollback_trans(thd). Not optimal.
-      Guilhem thinks that prepare_for_commit|rollback() is not a symmetric
-      couple in reality: see how in ha_rollback_trans we have to call
-      prepare_for_rollback while in ha_commit_trans we don't have to call
-      prepare_for_commit (because it was already called by binlog_query).
-      So maybe names should not be symmetric...
-
-      3) prepare_for_rollback() can be seen as some cleanup to do: when they
-      execute a statement (on a transactional table), engines allocate
-      resources, write to them, and they free these resources above in
-      (*ht)->rollback. The pending rows event is also a resource allocated by
-      THD during the statement, and at rollback THD also needs to free this
-      resource.
-
-      4) What bothers Guilhem is that this resource is in THD, so again it makes
-      the binlog a special case, by requiring an explicit line "rollback
-      binlog" here in ha_rollback_trans(). In his XA patch, Serg had organized
-      things so that the binlog looks like a storage engine, registers in the
-      transaction if needed, and so does its cleanup in (*ht)->rollback
-      above. But "pending" happens earlier in the process, so we may have a
-      pending event, without the binlog being registered as a participant in
-      the transaction.
-      As "pending" is "handler's private per-connection data" (quoting
-      sql_class.h) it should be in thd->ha_data[binlog_hton.slot], Serg
-      confirms.
-      Then we should register the binlog as soon as we create a pending event.
-      Then pending would be wiped out by binlog_rollback() and that would be
-      tidy.
-      The fact to have a binlog transaction cache is considered as having
-      binlog a participant. But a pending rows event is yet another binlog
-      transaction cache, so it should be placed in the same containers.
-
-      https://intranet.mysql.com/worklog/Server-RawIdeaBin/?tid)67
-      is the Worklog entry for this reorganization.
-      When this is done, we won't need the explicit call below, as binlog will
-      be registered as participant even in the case where this is an
-      autocommit statement on a transactional table, one row has been updated
-      but the statement rolls back because it fails on another row. With the
-      current code and without the call below, the updated row causes a
-      pending event which is not flushed (because binlog_query() is not called
-      because the statement is going to be rolled back); this pending event
-      then influences the next statement. Testcase extracted
-      from the sp_trans test, which causes a crash without the below call:
--- source include/have_innodb.inc
-delimiter |;
-create table t1 (id int) engine=innodb|
-create table t2 (id int primary key, j int) engine=innodb|
-      insert into t2 values (1, 0)|
-create function bug10015_5(i int) returns int
-  begin
-    if (i = 5) then
-      insert into t2 values (1, 0);
-    end if;
-    return i;
-  end|
---error 1062
-insert into t1 values (bug10015_5(4)), (bug10015_5(5))|
-### if you exit here master won't crash
-drop function bug10015_5|
-# if you exit here master will crash when it shuts down
-
-    */
-
-    thd->prepare_for_rollback();
-
   }
 #endif /* USING_TRANSACTIONS */
   /*
@@ -1104,10 +1028,10 @@
 int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv)
 {
   int error=0;
-  THD_TRANS *trans=&thd->transaction.all;
+  THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
+                                        &thd->transaction.all);
   handlerton **ht=trans->ht, **end_ht;
   DBUG_ENTER("ha_rollback_to_savepoint");
-  DBUG_ASSERT(thd->transaction.stmt.ht[0] == 0);

   trans->nht=sv->nht;
   trans->no_2pc=0;
@@ -1125,7 +1049,8 @@
       my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
       error=1;
     }
-    statistic_increment(thd->status_var.ha_savepoint_rollback_count,&LOCK_status);
+    statistic_increment(thd->status_var.ha_savepoint_rollback_count,
+                        &LOCK_status);
     trans->no_2pc|=(*ht)->prepare == 0;
   }
   /*
@@ -1135,7 +1060,7 @@
   for (; *ht ; ht++)
   {
     int err;
-    if ((err= (*(*ht)->rollback)(thd, 1)))
+    if ((err= (*(*ht)->rollback)(thd, !thd->in_sub_stmt)))
     { // cannot happen
       my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
       error=1;
@@ -1155,10 +1080,10 @@
 int ha_savepoint(THD *thd, SAVEPOINT *sv)
 {
   int error=0;
-  THD_TRANS *trans=&thd->transaction.all;
+  THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
+                                        &thd->transaction.all);
   handlerton **ht=trans->ht;
   DBUG_ENTER("ha_savepoint");
-  DBUG_ASSERT(thd->transaction.stmt.ht[0] == 0);
 #ifdef USING_TRANSACTIONS
   for (; *ht; ht++)
   {
@@ -1184,9 +1109,10 @@
 int ha_release_savepoint(THD *thd, SAVEPOINT *sv)
 {
   int error=0;
-  handlerton **ht=thd->transaction.all.ht, **end_ht;
+  THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
+                                        &thd->transaction.all);
+  handlerton **ht=trans->ht, **end_ht;
   DBUG_ENTER("ha_release_savepoint");
-  DBUG_ASSERT(thd->transaction.stmt.ht[0] == 0);

   end_ht=ht+sv->nht;
   for (; ht < end_ht; ht++)
@@ -1254,7 +1180,7 @@
 */

 int ha_delete_table(THD *thd, enum db_type table_type, const char *path,
-                    const char *alias, bool generate_warning)
+                    const char *db, const char *alias, bool generate_warning)
 {
   handler *file;
   char tmp_path[FN_REFLEN];
@@ -1269,7 +1195,7 @@

   /* DB_TYPE_UNKNOWN is used in ALTER TABLE when renaming only .frm files */
   if (table_type == DB_TYPE_UNKNOWN ||
-      ! (file=get_new_handler(&dummy_table, thd->mem_root, table_type)))
+      ! (file=get_new_handler(&dummy_share, thd->mem_root, table_type)))
     DBUG_RETURN(ENOENT);

   if (lower_case_table_names == 2 && !(file->table_flags() & HA_FILE_BASED))
@@ -1302,7 +1228,12 @@
     thd->net.last_error[0]= 0;

     /* Fill up strucutures that print_error may need */
-    dummy_table.s->path= path;
+    dummy_share.path.str= (char*) path;
+    dummy_share.path.length= strlen(path);
+    dummy_share.db.str= (char*) db;
+    dummy_share.db.length= strlen(db);
+    dummy_share.table_name.str= (char*) alias;
+    dummy_share.table_name.length= strlen(alias);
     dummy_table.alias= alias;

     file->print_error(error, 0);
@@ -1324,16 +1255,26 @@
 ** General handler functions
 ****************************************************************************/

-	/* Open database-handler. Try O_RDONLY if can't open as O_RDWR */
-	/* Don't wait for locks if not HA_OPEN_WAIT_IF_LOCKED is set */
+/*
+  Open database-handler.
+
+  IMPLEMENTATION
+    Try O_RDONLY if cannot open as O_RDWR
+    Don't wait for locks if not HA_OPEN_WAIT_IF_LOCKED is set
+*/

-int handler::ha_open(const char *name, int mode, int test_if_locked)
+int handler::ha_open(TABLE *table_arg, const char *name, int mode,
+                     int test_if_locked)
 {
   int error;
   DBUG_ENTER("handler::ha_open");
-  DBUG_PRINT("enter",("name: %s  db_type: %d  db_stat: %d  mode: %d  lock_test: %d",
-                      name, table->s->db_type, table->db_stat, mode,
-                      test_if_locked));
+  DBUG_PRINT("enter",
+             ("name: %s  db_type: %d  db_stat: %d  mode: %d  lock_test: %d",
+              name, table_share->db_type, table_arg->db_stat, mode,
+              test_if_locked));
+
+  table= table_arg;
+  DBUG_ASSERT(table->s == table_share);

   if ((error=open(name,mode,test_if_locked)))
   {
@@ -1346,7 +1287,7 @@
   }
   if (error)
   {
-    my_errno=error;			/* Safeguard */
+    my_errno= error;                            /* Safeguard */
     DBUG_PRINT("error",("error: %d  errno: %d",error,errno));
   }
   else
@@ -1364,74 +1305,51 @@
     }
     else
       dupp_ref=ref+ALIGN_SIZE(ref_length);
+
+    if (ha_allocate_read_write_set(table->s->fields))
+      error= 1;
   }
   DBUG_RETURN(error);
 }

+
 int handler::ha_initialise()
 {
   DBUG_ENTER("ha_initialise");
-  if (table && table->s->fields &&
-      ha_allocate_read_write_set(table->s->fields))
-  {
-    DBUG_RETURN(TRUE);
-  }
   DBUG_RETURN(FALSE);
 }

+
+/*
+  Initalize bit maps for used fields
+
+  Called from open_table_from_share()
+*/
+
 int handler::ha_allocate_read_write_set(ulong no_fields)
 {
-  uint bitmap_size= 4*(((no_fields+1)+31)/32);
+  uint bitmap_size= bitmap_buffer_size(no_fields+1);
   uint32 *read_buf, *write_buf;
-#ifndef DEBUG_OFF
-  my_bool r;
-#endif
   DBUG_ENTER("ha_allocate_read_write_set");
   DBUG_PRINT("enter", ("no_fields = %d", no_fields));

-  if (table)
+  if (!multi_alloc_root(&table->mem_root,
+                        &read_set, sizeof(MY_BITMAP),
+                        &write_set, sizeof(MY_BITMAP),
+                        &read_buf, bitmap_size,
+                        &write_buf, bitmap_size,
+                        NullS))
   {
-    if (table->read_set == NULL)
-    {
-      read_set= (MY_BITMAP*)sql_alloc(sizeof(MY_BITMAP));
-      write_set= (MY_BITMAP*)sql_alloc(sizeof(MY_BITMAP));
-      read_buf= (uint32*)sql_alloc(bitmap_size);
-      write_buf= (uint32*)sql_alloc(bitmap_size);
-      if (!read_set || !write_set || !read_buf || !write_buf)
-      {
-        ha_deallocate_read_write_set();
-        DBUG_RETURN(TRUE);
-      }
-#ifndef DEBUG_OFF
-      r -#endif
-        bitmap_init(read_set, read_buf, no_fields+1, FALSE);
-      DBUG_ASSERT(!r /*bitmap_init(read_set...)*/);
-#ifndef DEBUG_OFF
-      r -#endif
-        bitmap_init(write_set, write_buf, no_fields+1, FALSE);
-      DBUG_ASSERT(!r /*bitmap_init(write_set...)*/);
-      table->read_set= read_set;
-      table->write_set= write_set;
-      ha_clear_all_set();
-    }
-    else
-    {
-      read_set= table->read_set;
-      write_set= table->write_set;
-    }
+    DBUG_RETURN(TRUE);
   }
+  bitmap_init(read_set, read_buf, no_fields+1, FALSE);
+  bitmap_init(write_set, write_buf, no_fields+1, FALSE);
+  table->read_set= read_set;
+  table->write_set= write_set;
+  ha_clear_all_set();
   DBUG_RETURN(FALSE);
 }

-void handler::ha_deallocate_read_write_set()
-{
-  DBUG_ENTER("ha_deallocate_read_write_set");
-  read_set=write_set=0;
-  DBUG_VOID_RETURN;
-}
-
 void handler::ha_clear_all_set()
 {
   DBUG_ENTER("ha_clear_all_set");
@@ -1473,6 +1391,7 @@
 }


+
 /*
   Read first row (only) from a table
   This is never called for InnoDB or BDB tables, as these table types
@@ -1484,7 +1403,8 @@
   register int error;
   DBUG_ENTER("handler::read_first_row");

-  statistic_increment(current_thd->status_var.ha_read_first_count,&LOCK_status);
+  statistic_increment(table->in_use->status_var.ha_read_first_count,
+                      &LOCK_status);

   /*
     If there is very few deleted rows in the table, find the first row by
@@ -1750,15 +1670,16 @@
     uint key_nr=get_dup_key(error);
     if ((int) key_nr >= 0)
     {
-      /* Write the dupplicated key in the error message */
+      /* Write the duplicated key in the error message */
       char key[MAX_KEY_LENGTH];
       String str(key,sizeof(key),system_charset_info);
+      /* Table is opened and defined at this point */
       key_unpack(&str,table,(uint) key_nr);
       uint max_length=MYSQL_ERRMSG_SIZE-(uint) strlen(ER(ER_DUP_ENTRY));
       if (str.length() >= max_length)
       {
 	str.length(max_length-4);
-	str.append("...");
+	str.append(STRING_WITH_LEN("..."));
       }
       my_error(ER_DUP_ENTRY, MYF(0), str.c_ptr(), key_nr+1);
       DBUG_VOID_RETURN;
@@ -1839,20 +1760,9 @@
     textno=ER_TABLE_DEF_CHANGED;
     break;
   case HA_ERR_NO_SUCH_TABLE:
-  {
-    /*
-      We have to use path to find database name instead of using
-      table->table_cache_key because if the table didn't exist, then
-      table_cache_key was not set up
-    */
-    char *db;
-    char buff[FN_REFLEN];
-    uint length= dirname_part(buff,table->s->path);
-    buff[length-1]=0;
-    db=buff+dirname_length(buff);
-    my_error(ER_NO_SUCH_TABLE, MYF(0), db, table->alias);
+    my_error(ER_NO_SUCH_TABLE, MYF(0), table_share->db.str,
+             table_share->table_name.str);
     break;
-  }
   case HA_ERR_RBR_LOGGING_FAILED:
     textno= ER_BINLOG_ROW_LOGGING_FAILED;
     break;
@@ -1876,7 +1786,7 @@
       DBUG_VOID_RETURN;
     }
   }
-  my_error(textno, errflag, table->alias, error);
+  my_error(textno, errflag, table_share->table_name.str, error);
   DBUG_VOID_RETURN;
 }

@@ -2021,23 +1931,37 @@

 /*
   Initiates table-file and calls apropriate database-creator
-  Returns 1 if something got wrong
+
+  NOTES
+    We must have a write lock on LOCK_open to be sure no other thread
+    interfers with table
+
+  RETURN
+   0  ok
+   1  error
 */

-int ha_create_table(const char *name, HA_CREATE_INFO *create_info,
+int ha_create_table(THD *thd, const char *path,
+                    const char *db, const char *table_name,
+                    HA_CREATE_INFO *create_info,
 		    bool update_create_info)
 {
-  int error;
+  int error= 1;
   TABLE table;
   char name_buff[FN_REFLEN];
+  const char *name;
+  TABLE_SHARE share;
   DBUG_ENTER("ha_create_table");
+
+  init_tmp_table_share(&share, db, 0, table_name, path);
+  if (open_table_def(thd, &share, 0) ||
+      open_table_from_share(thd, &share, "", 0, (uint) READ_ALL, 0, &table))
+    goto err;

-  if (openfrm(current_thd, name,"",0,(uint) READ_ALL, 0, &table))
-    DBUG_RETURN(1);
   if (update_create_info)
-  {
     update_create_info_from_table(create_info, &table);
-  }
+
+  name= share.path.str;
   if (lower_case_table_names == 2 &&
       !(table.file->table_flags() & HA_FILE_BASED))
   {
@@ -2047,27 +1971,32 @@
     name= name_buff;
   }

-  error=table.file->create(name,&table,create_info);
-  VOID(closefrm(&table));
+  error= table.file->create(name, &table, create_info);
+  VOID(closefrm(&table, 0));
   if (error)
-    my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), name,error);
+  {
+    strxmov(name_buff, db, ".", table_name, NullS);
+    my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), name_buff, error);
+  }
+err:
+  free_table_share(&share);
   DBUG_RETURN(error != 0);
 }

 /*
-  Try to discover table from engine and
-  if found, write the frm file to disk.
+  Try to discover table from engine
+
+  NOTES
+    If found, write the frm file to disk.

   RETURN VALUES:
-  -1 : Table did not exists
-   0 : Table created ok
-   > 0 : Error, table existed but could not be created
+  -1    Table did not exists
+   0    Table created ok
+   > 0  Error, table existed but could not be created

 */

-int ha_create_table_from_engine(THD* thd,
-				const char *db,
-				const char *name)
+int ha_create_table_from_engine(THD* thd, const char *db, const char *name)
 {
   int error;
   const void *frmblob;
@@ -2075,6 +2004,7 @@
   char path[FN_REFLEN];
   HA_CREATE_INFO create_info;
   TABLE table;
+  TABLE_SHARE share;
   DBUG_ENTER("ha_create_table_from_engine");
   DBUG_PRINT("enter", ("name '%s'.'%s'", db, name));

@@ -2090,15 +2020,23 @@
     frmblob and frmlen are set, write the frm to disk
   */

-  (void)strxnmov(path,FN_REFLEN,mysql_data_home,"/",db,"/",name,NullS);
+  (void)strxnmov(path,FN_REFLEN-1,mysql_data_home,"/",db,"/",name,NullS);
   // Save the frm file
   error= writefrm(path, frmblob, frmlen);
   my_free((char*) frmblob, MYF(0));
   if (error)
     DBUG_RETURN(2);

-  if (openfrm(thd, path,"",0,(uint) READ_ALL, 0, &table))
+  init_tmp_table_share(&share, db, 0, name, path);
+  if (open_table_def(thd, &share, 0))
+  {
     DBUG_RETURN(3);
+  }
+  if (open_table_from_share(thd, &share, "" ,0, 0, 0, &table))
+  {
+    free_table_share(&share);
+    DBUG_RETURN(3);
+  }

   update_create_info_from_table(&create_info, &table);
   create_info.table_options|= HA_OPTION_CREATE_FROM_ENGINE;
@@ -2110,7 +2048,7 @@
     my_casedn_str(files_charset_info, path);
   }
   error=table.file->create(path,&table,&create_info);
-  VOID(closefrm(&table));
+  VOID(closefrm(&table, 1));

   DBUG_RETURN(error != 0);
 }
@@ -2608,7 +2546,7 @@
     {
       if ((*types)->state == SHOW_OPTION_YES)
       {
-	handler *file= get_new_handler(0, mem_root,
+	handler *file= get_new_handler((TABLE_SHARE*) 0, mem_root,
                                        (enum db_type) (*types)->db_type);
 	for (ext= file->bas_ext(); *ext; ext++)
 	{
@@ -2694,14 +2632,16 @@
   A row in the given table should be replicated if:
   - Row-based replication is on
   - It is not a temporary table
-  - The table shall be binlogged (binlog_*_db rules)
+  - The binlog is enabled
+  - The table shall be binlogged (binlog_*_db rules) [Seems disabled /Matz]
 */

 #ifdef HAVE_ROW_BASED_REPLICATION
-static bool check_table_binlog_row_based(TABLE *table)
+static bool check_table_binlog_row_based(THD *thd, TABLE *table)
 {
   return
     binlog_row_based &&
+    thd && (thd->options & OPTION_BIN_LOG) &&
     (table->s->tmp_table == NO_TMP_TABLE);
 }

@@ -2712,7 +2652,9 @@
   if (h->is_injective())
     return 0;
   bool error= 0;
-  if (check_table_binlog_row_based(table))
+  THD *const thd= current_thd;
+
+  if (check_table_binlog_row_based(thd, table))
   {
     MY_BITMAP cols;
     /* Potential buffer on the stack for the bitmap */
@@ -2726,7 +2668,7 @@
     {
       bitmap_set_all(&cols);
       error-        RowsEventT::binlog_row_logging_function(current_thd, table,
+        RowsEventT::binlog_row_logging_function(thd, table,
                                                 table->file->has_transactions(),
                                                 &cols, table->s->fields,
                                                 before_record, after_record);

--- 1.169/sql/handler.h	2005-11-24 09:59:02 +01:00
+++ 1.170/sql/handler.h	2005-12-29 20:48:10 +01:00
@@ -93,6 +93,7 @@
 #define HA_CAN_BIT_FIELD       (1 << 28) /* supports bit fields */
 #define HA_NEED_READ_RANGE_BUFFER (1 << 29) /* for read_multi_range */
 #define HA_ANY_INDEX_MAY_BE_UNIQUE (1 << 30)
+#define HA_NO_COPY_ON_ALTER    (1 << 31)

 /* Flags for partition handlers */
 #define HA_CAN_PARTITION       (1 << 0) /* Partition support */
@@ -318,6 +319,7 @@

 struct st_table;
 typedef struct st_table TABLE;
+typedef struct st_table_share TABLE_SHARE;
 struct st_foreign_key_info;
 typedef struct st_foreign_key_info FOREIGN_KEY_INFO;
 typedef bool (stat_print_fn)(THD *thd, const char *type, const char *file,
@@ -418,7 +420,7 @@
    void *(*create_cursor_read_view)();
    void (*set_cursor_read_view)(void *);
    void (*close_cursor_read_view)(void *);
-   handler *(*create)(TABLE *table);
+   handler *(*create)(TABLE_SHARE *table);
    void (*drop_database)(char* path);
    int (*panic)(enum ha_panic_function flag);
    int (*release_temporary_latches)(THD *thd);
@@ -722,6 +724,9 @@
 bool is_partition_in_list(char *part_name, List<char> list_part_names);
 bool is_partitions_in_table(partition_info *new_part_info,
                             partition_info *old_part_info);
+bool check_reorganise_list(partition_info *new_part_info,
+                           partition_info *old_part_info,
+                           List<char> list_part_names);
 bool set_up_defaults_for_partitioning(partition_info *part_info,
                                       handler *file,
                                       ulonglong max_rows,
@@ -746,8 +751,9 @@
                                KEY *key_info,
                                const key_range *key_spec,
                                part_id_range *part_spec);
-bool mysql_unpack_partition(THD *thd, uchar *part_buf, uint part_info_len,
-                            TABLE* table, enum db_type default_db_type);
+bool mysql_unpack_partition(THD *thd, const uchar *part_buf,
+                            uint part_info_len, TABLE *table,
+                            enum db_type default_db_type);
 #endif


@@ -772,7 +778,8 @@
  friend class ha_partition;
 #endif
  protected:
-  struct st_table *table;		/* The table definition */
+  struct st_table_share *table_share;   /* The table definition */
+  struct st_table *table;               /* The current open table */

   virtual int index_init(uint idx, bool sorted) { active_index=idx; return 0; }
   virtual int index_end() { active_index=MAX_KEY; return 0; }
@@ -833,8 +840,8 @@
   MY_BITMAP *read_set;
   MY_BITMAP *write_set;

-  handler(const handlerton *ht_arg, TABLE *table_arg) :table(table_arg),
-    ht(ht_arg),
+  handler(const handlerton *ht_arg, TABLE_SHARE *share_arg)
+    :table_share(share_arg), ht(ht_arg),
     ref(0), data_file_length(0), max_data_file_length(0), index_file_length(0),
     delete_length(0), auto_increment_value(0),
     records(0), deleted(0), mean_rec_length(0),
@@ -846,16 +853,19 @@
     {}
   virtual ~handler(void)
   {
-    ha_deallocate_read_write_set();
     /* TODO: DBUG_ASSERT(inited == NONE); */
   }
   virtual int ha_initialise();
-  int ha_open(const char *name, int mode, int test_if_locked);
+  int ha_open(TABLE *table, const char *name, int mode, int test_if_locked);
   bool update_auto_increment();
   virtual void print_error(int error, myf errflag);
   virtual bool get_error_message(int error, String *buf);
   uint get_dup_key(int error);
-  void change_table_ptr(TABLE *table_arg) { table=table_arg; }
+  void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share)
+  {
+    table= table_arg;
+    table_share= share;
+  }
   virtual double scan_time()
     { return ulonglong2double(data_file_length) / IO_SIZE + 2; }
   virtual double read_time(uint index, uint ranges, ha_rows rows)
@@ -1041,7 +1051,6 @@
   }
   void ha_set_primary_key_in_read_set();
   int ha_allocate_read_write_set(ulong no_fields);
-  void ha_deallocate_read_write_set();
   void ha_clear_all_set();
   uint get_index(void) const { return active_index; }
   virtual int open(const char *name, int mode, uint test_if_locked)=0;
@@ -1179,12 +1188,32 @@
                                    key_range *max_key)
     { return (ha_rows) 10; }
   virtual void position(const byte *record)=0;
-  virtual void info(uint)=0;
+  virtual void info(uint)=0; // see my_base.h for full description
   virtual int extra(enum ha_extra_function operation)
   { return 0; }
   virtual int extra_opt(enum ha_extra_function operation, ulong cache_size)
   { return extra(operation); }
   virtual int external_lock(THD *thd, int lock_type) { return 0; }
+  /*
+    In an UPDATE or DELETE, if the row under the cursor was locked by another
+    transaction, and the engine used an optimistic read of the last
+    committed row value under the cursor, then the engine returns 1 from this
+    function. MySQL must NOT try to update this optimistic value. If the
+    optimistic value does not match the WHERE condition, MySQL can decide to
+    skip over this row. Currently only works for InnoDB. This can be used to
+    avoid unnecessary lock waits.
+
+    If this method returns nonzero, it will also signal the storage
+    engine that the next read will be a locking re-read of the row.
+  */
+  virtual bool was_semi_consistent_read() { return 0; }
+  /*
+    Tell the engine whether it should avoid unnecessary lock waits.
+    If yes, in an UPDATE or DELETE, if the row under the cursor was locked
+    by another transaction, the engine may try an optimistic read of
+    the last committed row value under the cursor.
+  */
+  virtual void try_semi_consistent_read(bool) {}
   virtual void unlock_row() {}
   virtual int start_stmt(THD *thd, thr_lock_type lock_type) {return 0;}
   /*
@@ -1278,6 +1307,7 @@
 #ifdef WITH_PARTITION_STORAGE_ENGINE
   virtual ulong partition_flags(void) const { return 0;}
   virtual int get_default_no_partitions(ulonglong max_rows) { return 1;}
+  virtual void set_part_info(partition_info *part_info) { return; }
 #endif
   virtual ulong index_flags(uint idx, uint part, bool all_parts) const =0;
   virtual ulong index_ddl_flags(KEY *wanted_index) const
@@ -1410,17 +1440,18 @@
   */
   friend int ndb_add_binlog_index(THD *, void *);

-  virtual int write_row(byte *buf)
+  virtual int write_row(byte *buf __attribute__((unused)))
   {
     return HA_ERR_WRONG_COMMAND;
   }

-  virtual int update_row(const byte *old_data, byte *new_data)
+  virtual int update_row(const byte *old_data __attribute__((unused)),
+                         byte *new_data __attribute__((unused)))
   {
     return HA_ERR_WRONG_COMMAND;
   }

-  virtual int delete_row(const byte *buf)
+  virtual int delete_row(const byte *buf __attribute__((unused)))
   {
     return HA_ERR_WRONG_COMMAND;
   }
@@ -1443,7 +1474,8 @@
 /* lookups */
 enum db_type ha_resolve_by_name(const char *name, uint namelen);
 const char *ha_get_storage_engine(enum db_type db_type);
-handler *get_new_handler(TABLE *table, MEM_ROOT *alloc, enum db_type db_type);
+handler *get_new_handler(TABLE_SHARE *share, MEM_ROOT *alloc,
+                         enum db_type db_type);
 enum db_type ha_checktype(THD *thd, enum db_type database_type,
                           bool no_substitute, bool report_error);
 bool ha_check_storage_engine_flag(enum db_type db_type, uint32 flag);
@@ -1457,10 +1489,12 @@
 my_bool ha_storage_engine_is_enabled(enum db_type database_type);
 bool ha_flush_logs(enum db_type db_typeÛ_TYPE_DEFAULT);
 void ha_drop_database(char* path);
-int ha_create_table(const char *name, HA_CREATE_INFO *create_info,
+int ha_create_table(THD *thd, const char *path,
+                    const char *db, const char *table_name,
+                    HA_CREATE_INFO *create_info,
 		    bool update_create_info);
 int ha_delete_table(THD *thd, enum db_type db_type, const char *path,
-                    const char *alias, bool generate_warning);
+                    const char *db, const char *alias, bool generate_warning);

 /* statistics and info */
 bool ha_show_status(THD *thd, enum db_type db_type, enum ha_stat_type stat);

--- 1.177/sql/log.cc	2005-11-24 09:59:02 +01:00
+++ 1.178/sql/log.cc	2005-12-29 20:48:10 +01:00
@@ -47,6 +47,19 @@
 static int binlog_rollback(THD *thd, bool all);
 static int binlog_prepare(THD *thd, bool all);

+/*
+  This is a POD. Please keep it that way!
+
+  Don't add constructors, destructors, or virtual functions.
+*/
+struct binlog_trx_data {
+  bool empty() const {
+    return pending == NULL && my_b_tell(&trans_log) == 0;
+  }
+  IO_CACHE trans_log;                         // The transaction cache
+  Rows_log_event *pending;                // The pending binrows event
+};
+
 handlerton binlog_hton = {
   "binlog",
   SHOW_OPTION_YES,
@@ -94,20 +107,45 @@

 static int binlog_close_connection(THD *thd)
 {
-  IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
-  DBUG_ASSERT(mysql_bin_log.is_open() && !my_b_tell(trans_log));
+  binlog_trx_data *const trx_data+    (binlog_trx_data*) \
thd->ha_data[binlog_hton.slot]; +  IO_CACHE *trans_log= &trx_data->trans_log;
+  DBUG_ASSERT(mysql_bin_log.is_open() && trx_data->empty());
   close_cached_file(trans_log);
-  my_free((gptr)trans_log, MYF(0));
+  thd->ha_data[binlog_hton.slot]= 0;
+  my_free((gptr)trx_data, MYF(0));
   return 0;
 }

-static int binlog_end_trans(THD *thd, IO_CACHE *trans_log, Log_event *end_ev)
+static int
+binlog_end_trans(THD *thd, binlog_trx_data *trx_data, Log_event *end_ev)
 {
-  int error=0;
   DBUG_ENTER("binlog_end_trans");
+  int error=0;
+  IO_CACHE *trans_log= &trx_data->trans_log;

   if (end_ev)
+  {
+    thd->binlog_flush_pending_rows_event(true);
     error= mysql_bin_log.write(thd, trans_log, end_ev);
+  }
+  else
+  {
+    thd->binlog_delete_pending_rows_event();
+  }
+
+  /*
+    We need to step the table map version both after writing the
+    entire transaction to the log file and after rolling back the
+    transaction.
+
+    We need to step the table map version after writing the
+    transaction cache to disk.  In addition, we need to step the table
+    map version on a rollback to ensure that a new table map event is
+    generated instead of the one that was written to the thrown-away
+    transaction cache.
+  */
+  ++mysql_bin_log.m_table_map_version;

   statistic_increment(binlog_cache_use, &LOCK_status);
   if (trans_log->disk_writes != 0)
@@ -133,32 +171,36 @@

 static int binlog_commit(THD *thd, bool all)
 {
-  IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
   DBUG_ENTER("binlog_commit");
+  binlog_trx_data *const trx_data+    (binlog_trx_data*) \
thd->ha_data[binlog_hton.slot]; +  IO_CACHE *trans_log= &trx_data->trans_log;
   DBUG_ASSERT(mysql_bin_log.is_open() &&
      (all || !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))));

-  if (!my_b_tell(trans_log))
+  if (trx_data->empty())
   {
     // we're here because trans_log was flushed in MYSQL_LOG::log()
     DBUG_RETURN(0);
   }
-  Query_log_event qev(thd, "COMMIT", 6, TRUE, FALSE);
-  DBUG_RETURN(binlog_end_trans(thd, trans_log, &qev));
+  Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE);
+  DBUG_RETURN(binlog_end_trans(thd, trx_data, &qev));
 }

 static int binlog_rollback(THD *thd, bool all)
 {
-  int error=0;
-  IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
   DBUG_ENTER("binlog_rollback");
+  int error=0;
+  binlog_trx_data *const trx_data+    (binlog_trx_data*) \
thd->ha_data[binlog_hton.slot]; +  IO_CACHE *trans_log= &trx_data->trans_log;
   /*
     First assert is guaranteed - see trans_register_ha() call below.
     The second must be true. If it is not, we're registering
     unnecessary, doing extra work. The cause should be found and eliminated
   */
   DBUG_ASSERT(all || !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)));
-  DBUG_ASSERT(mysql_bin_log.is_open() && my_b_tell(trans_log));
+  DBUG_ASSERT(mysql_bin_log.is_open() && !trx_data->empty());
   /*
     Update the binary log with a BEGIN/ROLLBACK block if we have
     cached some queries and we updated some non-transactional
@@ -167,11 +209,11 @@
   */
   if (unlikely(thd->options & OPTION_STATUS_NO_TRANS_UPDATE))
   {
-    Query_log_event qev(thd, "ROLLBACK", 8, TRUE, FALSE);
-    error= binlog_end_trans(thd, trans_log, &qev);
+    Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, FALSE);
+    error= binlog_end_trans(thd, trx_data, &qev);
   }
   else
-    error= binlog_end_trans(thd, trans_log, 0);
+    error= binlog_end_trans(thd, trx_data, 0);
   DBUG_RETURN(error);
 }

@@ -198,8 +240,10 @@

 static int binlog_savepoint_set(THD *thd, void *sv)
 {
-  IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
   DBUG_ENTER("binlog_savepoint_set");
+  binlog_trx_data *const trx_data+    (binlog_trx_data*) \
thd->ha_data[binlog_hton.slot]; +  IO_CACHE *trans_log= &trx_data->trans_log;
   DBUG_ASSERT(mysql_bin_log.is_open() && my_b_tell(trans_log));

   *(my_off_t *)sv= my_b_tell(trans_log);
@@ -210,8 +254,10 @@

 static int binlog_savepoint_rollback(THD *thd, void *sv)
 {
-  IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
   DBUG_ENTER("binlog_savepoint_rollback");
+  binlog_trx_data *const trx_data+    (binlog_trx_data*) \
thd->ha_data[binlog_hton.slot]; +  IO_CACHE *trans_log= &trx_data->trans_log;
   DBUG_ASSERT(mysql_bin_log.is_open() && my_b_tell(trans_log));

   /*
@@ -370,7 +416,7 @@
   :bytes_written(0), last_time(0), query_start(0), name(0),
    prepared_xids(0), log_type(LOG_CLOSED), file_id(1), open_count(1),
    write_error(FALSE), inited(FALSE), need_start_event(TRUE),
-   m_next_table_id(0), m_table_map_version(0),
+   m_table_map_version(0),
    description_event_for_exec(0), description_event_for_queue(0)
 {
   /*
@@ -398,7 +444,6 @@
     (void) pthread_mutex_destroy(&LOCK_log);
     (void) pthread_mutex_destroy(&LOCK_index);
     (void) pthread_cond_destroy(&update_cond);
-    (void) pthread_mutex_destroy(&LOCK_next_table_id);
   }
   DBUG_VOID_RETURN;
 }
@@ -444,7 +489,6 @@
   (void) pthread_mutex_init(&LOCK_log,MY_MUTEX_INIT_SLOW);
   (void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW);
   (void) pthread_cond_init(&update_cond, 0);
-  (void) pthread_mutex_init(&LOCK_next_table_id, MY_MUTEX_INIT_FAST);
 }

 const char *MYSQL_LOG::generate_name(const char *log_name,
@@ -1376,7 +1420,7 @@
         to change base names at some point.
       */
       THD *thd = current_thd; /* may be 0 if we are reacting to SIGHUP */
-      Rotate_log_event r(thd,new_name+dirname_length(new_name),
+      Rotate_log_event r(new_name+dirname_length(new_name),
                          0, LOG_EVENT_OFFSET, 0);
       r.write(&log_file);
       bytes_written += r.data_written;
@@ -1602,6 +1646,162 @@
           query_id_param >= thd->binlog_evt_union.first_query_id);
 }

+
+/*
+  These functions are placed in this file since they need access to
+  binlog_hton, which has internal linkage.
+*/
+
+int THD::binlog_setup_trx_data()
+{
+  DBUG_ENTER("THD::binlog_setup_trx_data");
+  binlog_trx_data *trx_data+    (binlog_trx_data*) ha_data[binlog_hton.slot];
+
+  if (trx_data)
+    DBUG_RETURN(0);                             // Already set up
+
+  ha_data[binlog_hton.slot]= trx_data+    (binlog_trx_data*) \
my_malloc(sizeof(binlog_trx_data), MYF(MY_ZEROFILL)); +  if (!trx_data ||
+      open_cached_file(&trx_data->trans_log, mysql_tmpdir,
+                       LOG_PREFIX, binlog_cache_size, MYF(MY_WME)))
+  {
+    my_free((gptr)trx_data, MYF(MY_ALLOW_ZERO_PTR));
+    ha_data[binlog_hton.slot]= 0;
+    DBUG_RETURN(1);                      // Didn't manage to set it up
+  }
+  trx_data->trans_log.end_of_file= max_binlog_cache_size;
+  DBUG_RETURN(0);
+}
+
+Rows_log_event*
+THD::binlog_get_pending_rows_event() const
+{
+  binlog_trx_data *const trx_data+    (binlog_trx_data*) ha_data[binlog_hton.slot];
+  /*
+    This is less than ideal, but here's the story: If there is no
+    trx_data, prepare_pending_rows_event() has never been called
+    (since the trx_data is set up there). In that case, we just return
+    NULL.
+   */
+  return trx_data ? trx_data->pending : NULL;
+}
+
+void
+THD::binlog_set_pending_rows_event(Rows_log_event* ev)
+{
+  binlog_trx_data *const trx_data+    (binlog_trx_data*) ha_data[binlog_hton.slot];
+  DBUG_ASSERT(trx_data);
+  trx_data->pending= ev;
+}
+
+
+/*
+  Moves the last bunch of rows from the pending Rows event to the binlog
+  (either cached binlog if transaction, or disk binlog). Sets a new pending
+  event.
+*/
+int MYSQL_LOG::flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event)
+{
+  DBUG_ENTER("MYSQL_LOG::flush_and_set_pending_rows_event(event)");
+  DBUG_ASSERT(binlog_row_based && mysql_bin_log.is_open());
+  DBUG_PRINT("enter", ("event=%p", event));
+
+  int error= 0;
+
+  binlog_trx_data *const trx_data+    (binlog_trx_data*) \
thd->ha_data[binlog_hton.slot]; +
+  DBUG_ASSERT(trx_data);
+
+  if (Rows_log_event* pending= trx_data->pending)
+  {
+    IO_CACHE *file= &log_file;
+
+    /*
+      Decide if we should write to the log file directly or to the
+      transaction log.
+    */
+    if (pending->get_cache_stmt() || my_b_tell(&trx_data->trans_log))
+      file= &trx_data->trans_log;
+
+    /*
+      If we are writing to the log file directly, we could avoid
+      locking the log. This does not work since we need to step the
+      m_table_map_version below, and that change has to be protected
+      by the LOCK_log mutex.
+    */
+    pthread_mutex_lock(&LOCK_log);
+
+    /*
+      Write a table map if necessary
+    */
+    if (pending->maybe_write_table_map(thd, file, this))
+    {
+      pthread_mutex_unlock(&LOCK_log);
+      DBUG_RETURN(2);
+    }
+
+    /*
+      Write pending event to log file or transaction cache
+    */
+    if (pending->write(file))
+    {
+      pthread_mutex_unlock(&LOCK_log);
+      DBUG_RETURN(1);
+    }
+
+    /*
+      We step the table map version if we are writing an event
+      representing the end of a statement.  We do this regardless of
+      wheather we write to the transaction cache or to directly to the
+      file.
+
+      In an ideal world, we could avoid stepping the table map version
+      if we were writing to a transaction cache, since we could then
+      reuse the table map that was written earlier in the transaction
+      cache.  This does not work since STMT_END_F implies closing all
+      table mappings on the slave side.
+
+      TODO: Find a solution so that table maps does not have to be
+      written several times within a transaction.
+     */
+    if (pending->get_flags(Rows_log_event::STMT_END_F))
+      ++m_table_map_version;
+
+    delete pending;
+
+    if (file == &log_file)
+    {
+      error= flush_and_sync();
+      if (!error)
+      {
+        signal_update();
+        rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED);
+      }
+    }
+
+    pthread_mutex_unlock(&LOCK_log);
+  }
+  else if (event && event->get_cache_stmt())  /* && pending == 0 */
+  {
+    /*
+      If we are setting a non-null event for a table that is
+      transactional, we start a transaction here as well.
+     */
+    trans_register_ha(thd,
+                      thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN),
+                      &binlog_hton);
+  }
+
+  trx_data->pending= event;
+
+  DBUG_RETURN(error);
+}
+
 /*
   Write an event to the binary log
 */
@@ -1622,7 +1822,29 @@
     thd->binlog_evt_union.unioned_events_trans |= event_info->cache_stmt;
     DBUG_RETURN(0);
   }
-
+
+  /*
+    Flush the pending rows event to the transaction cache or to the
+    log file.  Since this function potentially aquire the LOCK_log
+    mutex, we do this before aquiring the LOCK_log mutex in this
+    function.
+
+    This is not optimal, but necessary in the current implementation
+    since there is code that writes rows to system tables without
+    using some way to flush the pending event (e.g., binlog_query()).
+
+    TODO: There shall be no writes to any system table after calling
+    binlog_query(), so these writes has to be moved to before the call
+    of binlog_query() for correct functioning.
+
+    This is necessesary not only for RBR, but the master might crash
+    after binlogging the query but before changing the system tables.
+    This means that the slave and the master are not in the same state
+    (after the master has restarted), so therefore we have to
+    eliminate this problem.
+  */
+  thd->binlog_flush_pending_rows_event(true);
+
   pthread_mutex_lock(&LOCK_log);

   /*
@@ -1662,37 +1884,26 @@
     */
     if (opt_using_transactions && thd)
     {
-      IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
+      if (thd->binlog_setup_trx_data())
+        goto err;

-      if (event_info->get_cache_stmt())
-      {
-        if (!trans_log)
-        {
-          thd->ha_data[binlog_hton.slot]= trans_log= (IO_CACHE *)
-            my_malloc(sizeof(IO_CACHE), MYF(MY_ZEROFILL));
-          if (!trans_log || open_cached_file(trans_log, mysql_tmpdir,
-                                             LOG_PREFIX,
-                                             binlog_cache_size, MYF(MY_WME)))
-          {
-            my_free((gptr)trans_log, MYF(MY_ALLOW_ZERO_PTR));
-            thd->ha_data[binlog_hton.slot]= trans_log= 0;
-            goto err;
-          }
-          trans_log->end_of_file= max_binlog_cache_size;
-          trans_register_ha(thd,
-                            thd->options & (OPTION_NOT_AUTOCOMMIT |
-                                            OPTION_BEGIN),
-                            &binlog_hton);
-        }
-        else if (!my_b_tell(trans_log))
-          trans_register_ha(thd,
-                            thd->options & (OPTION_NOT_AUTOCOMMIT |
-                                            OPTION_BEGIN),
-                            &binlog_hton);
-        file= trans_log;
-      }
-      else if (trans_log && my_b_tell(trans_log))
+      binlog_trx_data *const trx_data+        (binlog_trx_data*) \
thd->ha_data[binlog_hton.slot]; +      IO_CACHE *trans_log= &trx_data->trans_log;
+
+      if (event_info->get_cache_stmt() && !my_b_tell(trans_log))
+        trans_register_ha(thd,
+                          thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN),
+                          &binlog_hton);
+
+      if (event_info->get_cache_stmt() || my_b_tell(trans_log))
         file= trans_log;
+      /*
+        Note: as Mats suggested, for all the cases above where we write to
+        trans_log, it sounds unnecessary to lock LOCK_log. We should rather
+        test first if we want to write to trans_log, and if not, lock
+        LOCK_log. TODO.
+      */
     }
 #endif
     DBUG_PRINT("info",("event type=%d",event_info->get_type_code()));
@@ -1711,42 +1922,45 @@
       If row-based binlogging, Insert_id, Rand and other kind of "setting
       context" events are not needed.
     */
-    if (thd && !binlog_row_based)
+    if (thd)
     {
-      if (thd->last_insert_id_used)
-      {
-	Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT,
-			   thd->current_insert_id);
-	if (e.write(file))
-	  goto err;
-      }
-      if (thd->insert_id_used)
-      {
-	Intvar_log_event e(thd,(uchar) INSERT_ID_EVENT,thd->last_insert_id);
-	if (e.write(file))
-	  goto err;
-      }
-      if (thd->rand_used)
-      {
-	Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2);
-	if (e.write(file))
-	  goto err;
-      }
-      if (thd->user_var_events.elements)
+      if (!binlog_row_based)
       {
-	for (uint i= 0; i < thd->user_var_events.elements; i++)
-	{
-	  BINLOG_USER_VAR_EVENT *user_var_event;
-	  get_dynamic(&thd->user_var_events,(gptr) &user_var_event, i);
-          User_var_log_event e(thd, user_var_event->user_var_event->name.str,
-                               user_var_event->user_var_event->name.length,
-                               user_var_event->value,
-                               user_var_event->length,
-                               user_var_event->type,
-			       user_var_event->charset_number);
-	  if (e.write(file))
-	    goto err;
-	}
+        if (thd->last_insert_id_used)
+        {
+          Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT,
+                             thd->current_insert_id);
+          if (e.write(file))
+            goto err;
+        }
+        if (thd->insert_id_used)
+        {
+          Intvar_log_event e(thd,(uchar) INSERT_ID_EVENT,thd->last_insert_id);
+          if (e.write(file))
+            goto err;
+        }
+        if (thd->rand_used)
+        {
+          Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2);
+          if (e.write(file))
+            goto err;
+        }
+        if (thd->user_var_events.elements)
+        {
+          for (uint i= 0; i < thd->user_var_events.elements; i++)
+          {
+            BINLOG_USER_VAR_EVENT *user_var_event;
+            get_dynamic(&thd->user_var_events,(gptr) &user_var_event, i);
+            User_var_log_event e(thd, user_var_event->user_var_event->name.str,
+                                 user_var_event->user_var_event->name.length,
+                                 user_var_event->value,
+                                 user_var_event->length,
+                                 user_var_event->type,
+                                 user_var_event->charset_number);
+            if (e.write(file))
+              goto err;
+          }
+        }
       }
     }

@@ -1777,6 +1991,9 @@
     }
   }

+  if (event_info->flags & LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F)
+    ++m_table_map_version;
+
   pthread_mutex_unlock(&LOCK_log);
   DBUG_RETURN(error);
 }
@@ -1848,7 +2065,7 @@
     */
     if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
     {
-      Query_log_event qinfo(thd, "BEGIN", 5, TRUE, FALSE);
+      Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), TRUE, FALSE);
       /*
         Imagine this is rollback due to net timeout, after all statements of
         the transaction succeeded. Then we want a zero-error code in BEGIN.
@@ -2325,46 +2542,39 @@
 }

 #ifndef MYSQL_CLIENT
-
-ulong MYSQL_LOG::get_table_id(TABLE* table)
+bool MYSQL_LOG::write_table_map(THD *thd, IO_CACHE *file, TABLE* table,
+                                bool is_transactional)
 {
-  DBUG_ENTER("MYSQL_LOG::get_table_id(TABLE*)");
-  DBUG_PRINT("enter", ("table=%p (%s.%s)",
-                       table, table->s->db, table->s->table_name));
-  DBUG_ASSERT(table != NULL);
-  ulong tid= table->s->table_map_id;
-  if (tid == table_mapping::NO_TABLE)
-  {
-    pthread_mutex_lock(&LOCK_next_table_id);
-    /*
-      need to double check under mutex that no other thread has snuck in
-      and set the table_map_id.
-      Guilhem asks: so first we check without mutex. Imagine the == was false,
-      and another thread just made it true with mutex. It may be that the
-      first == test still sees that == is false, thus not even entering the
-      second == test. I wonder if this is ok. I hope something in this
-      scenario makes it impossible to happen?
-    */
-    if ((tid= table->s->table_map_id) == table_mapping::NO_TABLE)
-    {
-      /* get next id */
-      tid= m_next_table_id++;
-      /* There is one reserved number that cannot be used. */
-      if (unlikely(tid == table_mapping::NO_TABLE))
-        tid= m_next_table_id++;
-      table->s->table_map_id= tid;
-    }
-    pthread_mutex_unlock(&LOCK_next_table_id);
-  }
-  DBUG_PRINT("return", ("table_id=%d", tid));
-  DBUG_RETURN(tid);
-}
+  DBUG_ENTER("MYSQL_LOG::write_table_map()");
+  DBUG_PRINT("enter", ("table=%p (%s: %u)",
+                       table, table->s->table_name, table->s->table_map_id));
+
+  /* Pre-conditions */
+  DBUG_ASSERT(binlog_row_based && is_open());
+  DBUG_ASSERT(table->s->table_map_id != ULONG_MAX);

-void MYSQL_LOG::
-update_table_map_version()
-{
-  /* BUG this is an incrementation without LOCK_log mutex */
-  m_table_map_version++;
+#ifndef DBUG_OFF
+  /*
+    We only need to execute under the LOCK_log mutex if we are writing
+    to the log file; otherwise, we are writing to a thread-specific
+    transaction cache and there is no need to serialize this event
+    with events in other threads.
+   */
+  if (file == &log_file)
+    safe_mutex_assert_owner(&LOCK_log);
+#endif
+
+  Table_map_log_event::flag_set const
+    flags= Table_map_log_event::NO_FLAGS;
+
+  Table_map_log_event
+    the_event(thd, table, table->s->table_map_id, is_transactional, flags);
+
+  if (the_event.write(file))
+    DBUG_RETURN(1);
+
+  table->s->table_map_version= m_table_map_version;
+  DBUG_RETURN(0);
 }
 #endif /* !defined(MYSQL_CLIENT) */

@@ -3075,9 +3285,11 @@
 */
 int TC_LOG_BINLOG::log(THD *thd, my_xid xid)
 {
+  DBUG_ENTER("TC_LOG_BINLOG::log");
   Xid_log_event xle(thd, xid);
-  IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
-  return !binlog_end_trans(thd, trans_log, &xle);  // invert return value
+  binlog_trx_data *trx_data+    (binlog_trx_data*) thd->ha_data[binlog_hton.slot];
+  DBUG_RETURN(!binlog_end_trans(thd, trx_data, &xle));  // invert return value
 }

 void TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)

--- 1.195/sql/log_event.cc	2005-12-08 15:27:52 +01:00
+++ 1.196/sql/log_event.cc	2005-12-29 21:16:18 +01:00
@@ -26,6 +26,7 @@
 #include "rpl_filter.h"
 #include <my_dir.h>
 #endif /* MYSQL_CLIENT */
+#include <base64.h>
 #include <my_bitmap.h>
 #include <my_vle.h>

@@ -176,7 +177,7 @@
      we cannot meet Start_log event in the middle of events from one
      LOAD DATA.
   */
-  p= strmake(prefbuf,"SQL_LOAD-",9);
+  p= strmake(prefbuf, STRING_WITH_LEN("SQL_LOAD-"));
   p= int10_to_str(::server_id, p, 10);
   *(p++)= '-';
   *p= 0;
@@ -940,14 +941,16 @@
     /* Pretty-print event common header if header is exactly 19 bytes */
     if (print_event_info->common_header_len == LOG_EVENT_MINIMAL_HEADER_LEN)
     {
+      DBUG_ASSERT(hexdump_from == (unsigned long) hexdump_from);
       fprintf(file, "# Position  Timestamp   Type   Master ID        "
 	      "Size      Master Pos    Flags \n");
       fprintf(file, "# %8.8lx %02x %02x %02x %02x   %02x   "
 	      "%02x %02x %02x %02x   %02x %02x %02x %02x   "
 	      "%02x %02x %02x %02x   %02x %02x\n",
-	      hexdump_from, ptr[0], ptr[1], ptr[2], ptr[3], ptr[4],
-	      ptr[5], ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11],
-	      ptr[12], ptr[13], ptr[14], ptr[15], ptr[16], ptr[17], ptr[18]);
+	      (unsigned long) hexdump_from,
+              ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6],
+              ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13],
+              ptr[14], ptr[15], ptr[16], ptr[17], ptr[18]);
       ptr += LOG_EVENT_MINIMAL_HEADER_LEN;
       hexdump_from += LOG_EVENT_MINIMAL_HEADER_LEN;
     }
@@ -964,8 +967,10 @@

       if (i % 16 == 15)
       {
+        DBUG_ASSERT(hexdump_from == (unsigned long) hexdump_from);
 	fprintf(file, "# %8.8lx %-48.48s |%16s|\n",
-		hexdump_from + (i & 0xfffffff0), hex_string, char_string);
+		(unsigned long) (hexdump_from + (i & 0xfffffff0)),
+                hex_string, char_string);
 	hex_string[0]= 0;
 	char_string[0]= 0;
 	c= char_string;
@@ -977,13 +982,33 @@

     /* Non-full last line */
     if (hex_string[0]) {
-      printf("# %8.8lx %-48.48s |%s|\n# ",
-	     hexdump_from + (i & 0xfffffff0), hex_string, char_string);
+      DBUG_ASSERT(hexdump_from == (unsigned long) hexdump_from);
+      fprintf(file, "# %8.8lx %-48.48s |%s|\n# ",
+	     (unsigned long) (hexdump_from + (i & 0xfffffff0)),
+             hex_string, char_string);
     }
   }
 }


+void Log_event::print_base64(FILE* file, PRINT_EVENT_INFO* print_event_info)
+{
+  uchar *ptr= (uchar*)temp_buf;
+  my_off_t size= uint4korr(ptr + EVENT_LEN_OFFSET);
+
+  char *tmp_str+    (char *) my_malloc(base64_needed_encoded_length(size), \
MYF(MY_WME)); +  if (!tmp_str) {
+    fprintf(stderr, "\nError: Out of memory. "
+            "Could not print correct binlog event.\n");
+    return;
+  }
+  int res= base64_encode(ptr, size, tmp_str);
+  fprintf(file, "\nBINLOG '\n%s\n';\n", tmp_str);
+  my_free(tmp_str, MYF(0));
+}
+
+
 /*
   Log_event::print_timestamp()
 */
@@ -1746,7 +1771,7 @@
         clear_all_errors(thd, rli);        /* Can ignore query */
       else
       {
-        slave_print_error(rli,expected_error,
+        slave_print_msg(ERROR_LEVEL, rli, expected_error,
                           "\
 Query partially completed on the master (error on master: %d) \
 and was aborted. There is a chance that your master is inconsistent at this \
@@ -1775,16 +1800,16 @@
  	!ignored_error_code(actual_error) &&
  	!ignored_error_code(expected_error))
     {
-      slave_print_error(rli, 0,
- 			"\
-Query caused different errors on master and slave. \
+      slave_print_msg(ERROR_LEVEL, rli, 0,
+                      "\
+Query caused different errors on master and slave.     \
 Error on master: '%s' (%d), Error on slave: '%s' (%d). \
 Default database: '%s'. Query: '%s'",
-			ER_SAFE(expected_error),
-			expected_error,
-			actual_error ? thd->net.last_error: "no error",
-			actual_error,
-			print_slave_db_safe(db), query_arg);
+                      ER_SAFE(expected_error),
+                      expected_error,
+                      actual_error ? thd->net.last_error: "no error",
+                      actual_error,
+                      print_slave_db_safe(db), query_arg);
       thd->query_error= 1;
     }
     /*
@@ -1801,11 +1826,11 @@
     */
     else if (thd->query_error || thd->is_fatal_error)
     {
-      slave_print_error(rli,actual_error,
-			"Error '%s' on query. Default database: '%s'. Query: '%s'",
-			(actual_error ? thd->net.last_error :
-			 "unexpected success or fatal error"),
-			print_slave_db_safe(thd->db), query_arg);
+      slave_print_msg(ERROR_LEVEL, rli, actual_error,
+                      "Error '%s' on query. Default database: '%s'. Query: '%s'",
+                      (actual_error ? thd->net.last_error :
+                       "unexpected success or fatal error"),
+                      print_slave_db_safe(thd->db), query_arg);
       thd->query_error= 1;
     }

@@ -2091,6 +2116,21 @@
       post_header_len[WRITE_ROWS_EVENT-1]=   ROWS_HEADER_LEN;
       post_header_len[UPDATE_ROWS_EVENT-1]=  ROWS_HEADER_LEN;
       post_header_len[DELETE_ROWS_EVENT-1]=  ROWS_HEADER_LEN;
+      /*
+        We here have the possibility to simulate a master of before we changed
+        the table map id to be stored in 6 bytes: when it was stored in 4
+        bytes (=> post_header_len was 6). This is used to test backward
+        compatibility.
+        This code can be removed after a few months (today is Dec 21st 2005),
+        when we know that the 4-byte masters are not deployed anymore (check
+        with Tomas Ulin first!), and the accompanying test (rpl_row_4_bytes)
+        too.
+      */
+      DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
+                      post_header_len[TABLE_MAP_EVENT-1]+                      \
post_header_len[WRITE_ROWS_EVENT-1]+                      \
post_header_len[UPDATE_ROWS_EVENT-1]+                      \
                post_header_len[DELETE_ROWS_EVENT-1]= 6;);
       post_header_len[BEGIN_LOAD_QUERY_EVENT-1]= \
                post_header_len[APPEND_BLOCK_EVENT-1];
       post_header_len[EXECUTE_LOAD_QUERY_EVENT-1]= EXECUTE_LOAD_QUERY_HEADER_LEN;
     }
@@ -2225,10 +2265,8 @@
     As a transaction NEVER spans on 2 or more binlogs:
     if we have an active transaction at this point, the master died
     while writing the transaction to the binary log, i.e. while
-    flushing the binlog cache to the binlog. As the write was started,
-    the transaction had been committed on the master, so we lack of
-    information to replay this transaction on the slave; all we can do
-    is stop with error.
+    flushing the binlog cache to the binlog. XA guarantees that master has
+    rolled back. So we roll back.
     Note: this event could be sent by the master to inform us of the
     format of its binlog; in other words maybe it is not at its
     original place when it comes to us; we'll know this by checking
@@ -2236,11 +2274,13 @@
   */
   if (!artificial_event && created && thd->transaction.all.nht)
   {
-    slave_print_error(rli, 0, "Rolling back unfinished transaction (no "
-                      "COMMIT or ROLLBACK) from relay log. A probable cause "
-                      "is that the master died while writing the transaction "
-                      "to its binary log.");
-    end_trans(thd, ROLLBACK);
+    /* This is not an error (XA is safe), just an information */
+    slave_print_msg(INFORMATION_LEVEL, rli, 0,
+                    "Rolling back unfinished transaction (no COMMIT "
+                    "or ROLLBACK in relay log). A probable cause is that "
+                    "the master died while writing the transaction to "
+                    "its binary log, thus rolled back too.");
+    rli->cleanup_context(thd, 1);
   }
 #endif
   /*
@@ -3001,9 +3041,9 @@
       sql_errno=ER_UNKNOWN_ERROR;
       err=ER(sql_errno);
     }
-    slave_print_error(rli,sql_errno,"\
+    slave_print_msg(ERROR_LEVEL, rli, sql_errno,"\
 Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
-		      err, (char*)table_name, print_slave_db_safe(save_db));
+                    err, (char*)table_name, print_slave_db_safe(save_db));
     free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
     return 1;
   }
@@ -3011,9 +3051,9 @@

   if (thd->is_fatal_error)
   {
-    slave_print_error(rli,ER_UNKNOWN_ERROR, "\
+    slave_print_msg(ERROR_LEVEL, rli, ER_UNKNOWN_ERROR, "\
 Fatal error running LOAD DATA INFILE on table '%s'. Default database: '%s'",
-		      (char*)table_name, print_slave_db_safe(save_db));
+                    (char*)table_name, print_slave_db_safe(save_db));
     return 1;
   }

@@ -3037,7 +3077,7 @@
   String tmp(buf1, sizeof(buf1), log_cs);
   tmp.length(0);
   tmp.append(new_log_ident, ident_len);
-  tmp.append(";pos=");
+  tmp.append(STRING_WITH_LEN(";pos="));
   tmp.append(llstr(pos,buf));
   protocol->store(tmp.ptr(), tmp.length(), &my_charset_bin);
 }
@@ -3074,8 +3114,7 @@


 #ifndef MYSQL_CLIENT
-Rotate_log_event::Rotate_log_event(THD* thd_arg,
-                                   const char* new_log_ident_arg,
+Rotate_log_event::Rotate_log_event(const char* new_log_ident_arg,
                                    uint ident_len_arg, ulonglong pos_arg,
                                    uint flags_arg)
   :Log_event(), new_log_ident(new_log_ident_arg),
@@ -3084,7 +3123,7 @@
 {
 #ifndef DBUG_OFF
   char buff[22];
-  DBUG_ENTER("Rotate_log_event::Rotate_log_event(THD*,...)");
+  DBUG_ENTER("Rotate_log_event::Rotate_log_event(...,flags)");
   DBUG_PRINT("enter",("new_log_ident %s pos %s flags %lu", new_log_ident_arg,
                       llstr(pos_arg, buff), flags));
 #endif
@@ -3392,12 +3431,24 @@
   Xid_log_event methods
 **************************************************************************/

+#if !defined(DBUG_OFF) && !defined(MYSQL_CLIENT)
+/*
+  This static class member could be removed when mysqltest is made to support
+  a --replace-regex command: then tests which have XIDs in their output can
+  use this command to suppress non-deterministic XID values.
+*/
+my_bool Xid_log_event::show_xid;
+#endif
+
 #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
 void Xid_log_event::pack_info(Protocol *protocol)
 {
   char buf[128], *pos;
   pos= strmov(buf, "COMMIT /* xid=");
-  pos= longlong10_to_str(xid, pos, 10);
+#if !defined(DBUG_OFF) && !defined(MYSQL_CLIENT)
+  if (show_xid)
+#endif
+    pos= longlong10_to_str(xid, pos, 10);
   pos= strmov(pos, " */");
   protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
 }
@@ -4209,7 +4260,7 @@
   bzero((char*)&file, sizeof(file));
   p = slave_load_file_stem(fname_buf, file_id, server_id);
   strmov(p, ".info");			// strmov takes less code than memcpy
-  strnmov(proc_info, "Making temp file ", 17); // no end 0
+  strnmov(proc_info, STRING_WITH_LEN("Making temp file ")); // no end 0
   thd->proc_info= proc_info;
   my_delete(fname_buf, MYF(0)); // old copy may exist already
   if ((fd= my_create(fname_buf, CREATE_MODE,
@@ -4218,7 +4269,8 @@
       init_io_cache(&file, fd, IO_SIZE, WRITE_CACHE, (my_off_t)0, 0,
 		    MYF(MY_WME|MY_NABP)))
   {
-    slave_print_error(rli,my_errno, "Error in Create_file event: could not open file \
'%s'", fname_buf); +    slave_print_msg(ERROR_LEVEL, rli, my_errno, "Error in \
Create_file event: " +                    "could not open file '%s'", fname_buf);
     goto err;
   }

@@ -4229,9 +4281,9 @@
   if (write_base(&file))
   {
     strmov(p, ".info"); // to have it right in the error message
-    slave_print_error(rli,my_errno,
-		      "Error in Create_file event: could not write to file '%s'",
-		      fname_buf);
+    slave_print_msg(ERROR_LEVEL, rli, my_errno,
+                    "Error in Create_file event: could not write to file '%s'",
+                    fname_buf);
     goto err;
   }
   end_io_cache(&file);
@@ -4243,12 +4295,14 @@
 		     O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
 		     MYF(MY_WME))) < 0)
   {
-    slave_print_error(rli,my_errno, "Error in Create_file event: could not open file \
'%s'", fname_buf); +    slave_print_msg(ERROR_LEVEL, rli, my_errno, "Error in \
Create_file event: " +                    "could not open file '%s'", fname_buf);
     goto err;
   }
   if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP)))
   {
-    slave_print_error(rli,my_errno, "Error in Create_file event: write to '%s' \
failed", fname_buf); +    slave_print_msg(ERROR_LEVEL, rli, my_errno, "Error in \
Create_file event: " +                    "write to '%s' failed", fname_buf);
     goto err;
   }
   error=0;					// Everything is ok
@@ -4378,7 +4432,7 @@
   DBUG_ENTER("Append_block_log_event::exec_event");

   memcpy(p, ".data", 6);
-  strnmov(proc_info, "Making temp file ", 17); // no end 0
+  strnmov(proc_info, STRING_WITH_LEN("Making temp file ")); // no end 0
   thd->proc_info= proc_info;
   if (get_create_or_append())
   {
@@ -4387,25 +4441,25 @@
 		       O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
 		       MYF(MY_WME))) < 0)
     {
-      slave_print_error(rli, my_errno,
-			"Error in %s event: could not create file '%s'",
-			get_type_str(), fname);
+      slave_print_msg(ERROR_LEVEL, rli, my_errno,
+                      "Error in %s event: could not create file '%s'",
+                      get_type_str(), fname);
       goto err;
     }
   }
   else if ((fd = my_open(fname, O_WRONLY | O_APPEND | O_BINARY | O_NOFOLLOW,
                          MYF(MY_WME))) < 0)
   {
-    slave_print_error(rli, my_errno,
-                      "Error in %s event: could not open file '%s'",
-                      get_type_str(), fname);
+    slave_print_msg(ERROR_LEVEL, rli, my_errno,
+                    "Error in %s event: could not open file '%s'",
+                    get_type_str(), fname);
     goto err;
   }
   if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP)))
   {
-    slave_print_error(rli, my_errno,
-                      "Error in %s event: write to '%s' failed",
-                      get_type_str(), fname);
+    slave_print_msg(ERROR_LEVEL, rli, my_errno,
+                    "Error in %s event: write to '%s' failed",
+                    get_type_str(), fname);
     goto err;
   }
   error=0;
@@ -4612,7 +4666,8 @@
       init_io_cache(&file, fd, IO_SIZE, READ_CACHE, (my_off_t)0, 0,
 		    MYF(MY_WME|MY_NABP)))
   {
-    slave_print_error(rli,my_errno, "Error in Exec_load event: could not open file \
'%s'", fname); +    slave_print_msg(ERROR_LEVEL, rli, my_errno, "Error in Exec_load \
event: " +                    "could not open file '%s'", fname);
     goto err;
   }
   if (!(lev = (Load_log_event*)Log_event::read_log_event(&file,
@@ -4620,7 +4675,8 @@
                                                          \
rli->relay_log.description_event_for_exec)) ||  lev->get_type_code() != \
NEW_LOAD_EVENT)  {
-    slave_print_error(rli,0, "Error in Exec_load event: file '%s' appears \
corrupted", fname); +    slave_print_msg(ERROR_LEVEL, rli, 0, "Error in Exec_load \
event: " +                    "file '%s' appears corrupted", fname);
     goto err;
   }

@@ -4646,10 +4702,10 @@
     char *tmp= my_strdup(rli->last_slave_error,MYF(MY_WME));
     if (tmp)
     {
-      slave_print_error(rli,
-			rli->last_slave_errno, /* ok to re-use error code */
-			"%s. Failed executing load from '%s'",
-			tmp, fname);
+      slave_print_msg(ERROR_LEVEL, rli,
+                      rli->last_slave_errno, /* ok to re-use error code */
+                      "%s. Failed executing load from '%s'",
+                      tmp, fname);
       my_free(tmp,MYF(0));
     }
     goto err;
@@ -4855,30 +4911,30 @@
   if (!(buf = my_malloc(q_len + 1 - (fn_pos_end - fn_pos_start) +
                         (FN_REFLEN + 10) + 10 + 8 + 5, MYF(MY_WME))))
   {
-    slave_print_error(rli, my_errno, "Not enough memory");
+    slave_print_msg(ERROR_LEVEL, rli, my_errno, "Not enough memory");
     return 1;
   }

   p= buf;
   memcpy(p, query, fn_pos_start);
   p+= fn_pos_start;
-  fname= (p= strmake(p, " INFILE \'", 9));
+  fname= (p= strmake(p, STRING_WITH_LEN(" INFILE \'")));
   p= slave_load_file_stem(p, file_id, server_id);
-  fname_end= (p= strmake(p, ".data", 5));
+  fname_end= (p= strmake(p, STRING_WITH_LEN(".data")));
   *(p++)='\'';
   switch (dup_handling)
   {
   case LOAD_DUP_IGNORE:
-    p= strmake(p, " IGNORE", 7);
+    p= strmake(p, STRING_WITH_LEN(" IGNORE"));
     break;
   case LOAD_DUP_REPLACE:
-    p= strmake(p, " REPLACE", 8);
+    p= strmake(p, STRING_WITH_LEN(" REPLACE"));
     break;
   default:
     /* Ordinary load data */
     break;
   }
-  p= strmake(p, " INTO", 5);
+  p= strmake(p, STRING_WITH_LEN(" INTO"));
   p= strmake(p, query+fn_pos_end, q_len-fn_pos_end);

   error= Query_log_event::exec_event(rli, buf, p-buf);
@@ -4993,22 +5049,16 @@
 Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
                                MY_BITMAP const *cols, bool is_transactional)
   : Log_event(thd_arg, 0, is_transactional),
-    m_dbnam(tbl_arg->s->db), m_dblen(m_dbnam ? strlen(m_dbnam) : 0),
     m_table(tbl_arg),
-    m_tblnam(tbl_arg->s->table_name),
-    m_tbllen(strlen(tbl_arg->s->table_name)),
     m_table_id(tid),
     m_width(tbl_arg->s->fields),
-    /*
-      TODO: allocating several kB for maybe no use (e.g. read-only session)
-      is too much.
-    */
     m_rows_buf(my_malloc(opt_binlog_rows_event_max_size * sizeof(*m_rows_buf), \
MYF(MY_WME))),  m_rows_cur(m_rows_buf),
     m_rows_end(m_rows_buf + opt_binlog_rows_event_max_size),
     m_flags(0)
 {
   DBUG_ASSERT(m_table && m_table->s);
+  DBUG_ASSERT(m_table_id != ULONG_MAX);

   if (thd_arg->options & OPTION_NO_FOREIGN_KEY_CHECKS)
       set_flags(NO_FOREIGN_KEY_CHECKS_F);
@@ -5041,20 +5091,33 @@
 		      event_len, common_header_len,
 		      post_header_len));

-  char const* const post_begin= buf + common_header_len;
-  m_table_id= uint4korr(post_begin + RW_MAPID_OFFSET);
+  const char *post_start= buf + common_header_len;
+  post_start+= RW_MAPID_OFFSET;
+  if (post_header_len == 6)
+  {
+    /* Master is of an intermediate source tree before 5.1.4. Id is 4 bytes */
+    m_table_id= uint4korr(post_start);
+    post_start+= 4;
+  }
+  else
+  {
+    m_table_id= uint6korr(post_start);
+    post_start+= RW_FLAGS_OFFSET;
+  }
+
+  DBUG_ASSERT(m_table_id != ULONG_MAX);

-  m_flags= uint2korr(post_begin + RW_FLAGS_OFFSET);
+  m_flags= uint2korr(post_start);

-  byte const *const var_begin= post_begin + ROWS_HEADER_LEN;
-  byte const *const ptr_width= var_begin;
+  byte const *const var_start= buf + common_header_len + post_header_len;
+  byte const *const ptr_width= var_start;
   byte const *const ptr_after_width= my_vle_decode(&m_width, ptr_width);

   const uint byte_count= (m_width + 7) / 8;
-  const char* const ptr_rows_data= var_begin + byte_count + 1;
+  const char* const ptr_rows_data= var_start + byte_count + 1;

   my_size_t const data_size= event_len - (ptr_rows_data - buf);
-  DBUG_PRINT("info",("m_table_id=%d, m_flags=%d, m_width=%u, data_size=%lu",
+  DBUG_PRINT("info",("m_table_id=%lu, m_flags=%d, m_width=%u, data_size=%lu",
                      m_table_id, m_flags, m_width, data_size));

   m_rows_buf= my_malloc(data_size, MYF(MY_WME));
@@ -5141,7 +5204,7 @@
   for which the bitset represented by 'arr' and 'bits'; the other parts of the
   record are left alone.
  */
-static char const *unpack_row(THD *thd, TABLE *table,
+static char const *unpack_row(TABLE *table,
                               char *record, char const *row,
                               MY_BITMAP const *cols)
 {
@@ -5174,9 +5237,10 @@
 int Rows_log_event::exec_event(st_relay_log_info *rli)
 {
   DBUG_ENTER("Rows_log_event::exec_event(st_relay_log_info*)");
+  DBUG_ASSERT(m_table_id != ULONG_MAX);
   int error= 0;
-  TABLE* table= rli->m_table_map.get_table(m_table_id);
   char const *row_start= m_rows_buf;
+  TABLE* table= rli->m_table_map.get_table(m_table_id);

   /*
     'thd' has been set by exec_relay_log_event(), just before calling
@@ -5208,24 +5272,25 @@

     for ( ; ; )
     {
-      table_list.db= const_cast<char*>(table->s->db);
-      table_list.alias= table_list.table_name= \
const_cast<char*>(table->s->table_name); +      table_list.db= \
const_cast<char*>(table->s->db.str); +      table_list.alias= table_list.table_name+  \
const_cast<char*>(table->s->table_name.str);

       if ((error= lock_tables(thd, &table_list, count, &need_reopen)) == 0)
         break;
       if (!need_reopen)
       {
-        slave_print_error(rli, error,
-                            "Error in %s event: error during table %s.%s lock",
-                            get_type_str(), table->s->db, table->s->table_name);
+        slave_print_msg(ERROR_LEVEL, rli, error,
+                        "Error in %s event: error during table %s.%s lock",
+                        get_type_str(), table->s->db, table->s->table_name);
         DBUG_RETURN(error);
       }
       /*
         we need to store a local copy of the table names since the table object
         will become invalid after close_tables_for_reopen
       */
-      char *db= my_strdup(table->s->db,MYF(MY_WME));
-      char *table_name= my_strdup(table->s->table_name,MYF(MY_WME));
+      char *db= my_strdup(table->s->db.str, MYF(MY_WME));
+      char *table_name= my_strdup(table->s->table_name.str, MYF(MY_WME));

       if (db == 0 || table_name == 0)
       {
@@ -5270,11 +5335,11 @@
             simplifications (we don't honour --slave-skip-errors)
           */
           uint actual_error= thd->net.last_errno;
-          slave_print_error(rli,actual_error,
-                            "Error '%s' on reopening table `%s`.`%s`",
-                            (actual_error ? thd->net.last_error :
-                             "unexpected success or fatal error"),
-                            db, table_name);
+          slave_print_msg(ERROR_LEVEL, rli, actual_error,
+                          "Error '%s' on reopening table `%s`.`%s`",
+                          (actual_error ? thd->net.last_error :
+                           "unexpected success or fatal error"),
+                          db, table_name);
           thd->query_error= 1;
         }
       }
@@ -5317,32 +5382,38 @@
       DBUG_ASSERT(row_end != NULL); // cannot happen
       DBUG_ASSERT(row_end <= m_rows_end);

-      error= do_exec_row(table, rli);
+      /* in_use can have been set to NULL in close_tables_for_reopen */
+      THD* old_thd= table->in_use;
+      if (!table->in_use)
+        table->in_use= thd;
+      error= do_exec_row(table);
+      table->in_use = old_thd;
       switch (error)
       {
         /* Some recoverable errors */
       case HA_ERR_RECORD_CHANGED:
       case HA_ERR_KEY_NOT_FOUND:	/* Idempotency support: OK if
                                            tuple does not exist */
-	/*
-          ToDo: clear and warnings/errors that has been pushed to the
-          thread?
-        */
 	error= 0;
       case 0:
 	break;

       default:
-	slave_print_error(rli, error,
-			  "Error in %s event: row application failed",
-			  get_type_str());
+	slave_print_msg(ERROR_LEVEL, rli, error,
+                        "Error in %s event: row application failed",
+                        get_type_str());
 	thd->query_error= 1;
 	break;
       }

       row_start= row_end;
     }
+    DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event",
+                    rli->abort_slave=1;);
     error= do_after_row_operations(table, error);
+    if (!cache_stmt)
+      thd->options|= OPTION_STATUS_NO_TRANS_UPDATE;
+
   }

   if (error)
@@ -5351,33 +5422,22 @@
 		      "Error in %s event: error during transaction execution "
 		      "on table %s.%s",
 		      get_type_str(), table->s->db, table->s->table_name);
-    rli->m_table_map.clear_tables();
     /*
-      We do the two next calls to be sure to rollback whether it was
-      autocommit or in a transaction.
       If one day we honour --skip-slave-errors in row-based replication, and
       the error should be skipped, then we would clear mappings, rollback,
       close tables, but the slave SQL thread would not stop and then may
       assume the mapping is still available, the tables are still open...
       So then we should clear mappings/rollback/close here only if this is a
-      TRANS_END_F.
+      STMT_END_F.
       For now we code, knowing that error is not skippable and so slave SQL
       thread is certainly going to stop.
     */
-    ha_autocommit_or_rollback(thd, 1);
-    ha_rollback(thd);
-
-    close_thread_tables(thd);
+    rli->cleanup_context(thd, 1);
     thd->query_error= 1;
     DBUG_RETURN(error);
   }

-  /*
-    Some of this is copied from the Log_event::exec_event(). We step
-    the relay log positions ourself, since the generic version does
-    not behave the way we want.
-   */
-  if (get_flags(TRANS_END_F))
+  if (get_flags(STMT_END_F))
   {
     /*
       This is the end of a statement or transaction, so close (and
@@ -5394,9 +5454,7 @@
       (assume the last master's transaction is ignored by the slave because of
       replicate-ignore rules).
     */
-    rli->m_table_map.clear_tables();
-
-    thd->prepare_for_commit();
+    thd->binlog_flush_pending_rows_event(true);
     /*
       If this event is not in a transaction, the call below will, if some
       transactional storage engines are involved, commit the statement into
@@ -5408,30 +5466,36 @@
     */
     error= ha_autocommit_or_rollback(thd, 0);
     /*
-      Now what if this is not a transactional engine? we still need to flush
-      the pending event to the binlog; we did it with
-      thd->prepare_for_commit(). Note that we imitate what is done for real
-      queries: a call to ha_autocommit_or_rollback() (sometimes only if
-      involves a transactional engine), and a call to be sure to have the
-      pending event flushed.
+      Now what if this is not a transactional engine? we still need to
+      flush the pending event to the binlog; we did it with
+      thd->binlog_flush_pending_rows_event(). Note that we imitate
+      what is done for real queries: a call to
+      ha_autocommit_or_rollback() (sometimes only if involves a
+      transactional engine), and a call to be sure to have the pending
+      event flushed.
     */

-    close_thread_tables(thd); // unlocks and closes tables
+    rli->cleanup_context(thd, 0);
     rli->transaction_end(thd);

     if (error == 0)
     {
-      rli->inc_group_relay_log_pos(log_pos);
-      flush_relay_log_info(rli);
-      rli->last_master_timestamp= when;
+      /*
+        Clear any errors pushed in thd->net.last_err* if for example "no key
+        found" (as this is allowed). This is a safety measure; apparently
+        those errors (e.g. when executing a Delete_rows_log_event of a
+        non-existing row, like in rpl_row_mystery22.test,
+        thd->net.last_error = "Can't find record in 't1'" and last_errno32)
+        do not become visible. We still prefer to wipe them out.
+      */
+      thd->clear_error();
+      error= Log_event::exec_event(rli);
     }
     else
-    {
-      slave_print_error(rli, error,
-			"Error in %s event: commit of row events failed, "
-			"table `%s`.`%s`",
-			get_type_str(), table->s->db, table->s->table_name);
-    }
+      slave_print_msg(ERROR_LEVEL, rli, error,
+                      "Error in %s event: commit of row events failed, "
+                      "table `%s`.`%s`",
+                      get_type_str(), table->s->db, table->s->table_name);
     DBUG_RETURN(error);
   }

@@ -5461,19 +5525,48 @@
     */
     mysql_unlock_tables(thd, thd->lock);
     thd->lock= 0;
+    if ((table->s->primary_key == MAX_KEY) &&
+        !cache_stmt)
+    {
+      /*
+        ------------ Temporary fix until WL#2975 is implemented ---------
+        This event is not the last one (no STMT_END_F). If we stop now (in
+        case of terminate_slave_thread()), how will we restart? We have to
+        restart from Table_map_log_event, but as this table is not
+        transactional, the rows already inserted will still be present, and
+        idempotency is not guaranteed (no PK) so we risk that repeating leads
+        to double insert. So we desperately try to continue, hope we'll
+        eventually leave this buggy situation (by executing the final
+        Rows_log_event). If we are in a hopeless wait (reached end of last
+        relay log and nothing gets appended there), we timeout after one
+        minute, and notify DBA about the problem.
+        When WL#2975 is implemented, just remove the member
+        st_relay_log_info::unsafe_to_stop_at and all its occurences.
+      */
+      rli->unsafe_to_stop_at= time(0);
+    }
   }

   DBUG_ASSERT(error == 0);
+  thd->clear_error();
   rli->inc_event_relay_log_pos();

   DBUG_RETURN(0);
 }
 #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */

+#ifndef MYSQL_CLIENT
 bool Rows_log_event::write_data_header(IO_CACHE *file)
 {
+  DBUG_ASSERT(m_table_id != ULONG_MAX);
   byte buf[ROWS_HEADER_LEN];	// No need to init the buffer
-  int4store(buf + RW_MAPID_OFFSET, m_table_id);
+  DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
+                  {
+                    int4store(buf + 0, m_table_id);
+                    int2store(buf + 4, m_flags);
+                    return (my_b_safe_write(file, buf, 6));
+                  });
+  int6store(buf + RW_MAPID_OFFSET, (ulonglong)m_table_id);
   int2store(buf + RW_FLAGS_OFFSET, m_flags);
   return (my_b_safe_write(file, buf, ROWS_HEADER_LEN));
 }
@@ -5495,12 +5588,13 @@
                           no_bytes_in_map(&m_cols)) ||
           my_b_safe_write(file, m_rows_buf, data_size));
 }
+#endif

 #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) && defined(DBUG_RBR)
 void Rows_log_event::pack_info(Protocol *protocol)
 {
     char buf[256];
-    char const *const flagstr= get_flags(TRANS_END_F) ? "TRANS_END_F" : "";
+    char const *const flagstr= get_flags(STMT_END_F) ? "STMT_END_F" : "";
     char const *const dbnam= m_table->s->db;
     char const *const tblnam= m_table->s->table_name;
     my_size_t bytes= snprintf(buf, sizeof(buf),
@@ -5509,7 +5603,6 @@
 }
 #endif

-
 /**************************************************************************
 	Table_map_log_event member functions
 **************************************************************************/
@@ -5524,15 +5617,28 @@
                                          bool is_transactional, uint16 flags)
   : Log_event(thd, 0, is_transactional),
     m_table(tbl),
-    m_dbnam(tbl->s->db),
-    m_dblen(m_dbnam ? strlen(m_dbnam) : 0),
-    m_tblnam(tbl->s->table_name),
-    m_tbllen(strlen(m_tblnam)),
+    m_dbnam(tbl->s->db.str),
+    m_dblen(m_dbnam ? tbl->s->db.length : 0),
+    m_tblnam(tbl->s->table_name.str),
+    m_tbllen(tbl->s->table_name.length),
     m_colcnt(tbl->s->fields), m_coltype(0),
     m_table_id(tid),
     m_flags(flags)
 {
+  DBUG_ASSERT(m_table_id != ULONG_MAX);
+  /*
+    In TABLE_SHARE, "db" and "table_name" are 0-terminated (see this comment in
+    table.cc / alloc_table_share():
+      Use the fact the key is db/0/table_name/0
+    As we rely on this let's assert it.
+  */
+  DBUG_ASSERT((tbl->s->db.str == 0) ||
+              (tbl->s->db.str[tbl->s->db.length] == 0));
+  DBUG_ASSERT(tbl->s->table_name.str[tbl->s->table_name.length] == 0);
+
+
   m_data_size=  TABLE_MAP_HEADER_LEN;
+  DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", m_data_size= 6;)
   m_data_size+= m_dblen + 2;	// Include length and terminating \0
   m_data_size+= m_tbllen + 2;	// Include length and terminating \0
   m_data_size+= 1 + m_colcnt;	// COLCNT and column types
@@ -5571,14 +5677,28 @@
   DBUG_DUMP("event buffer", buf, event_len);

   /* Read the post-header */
-  const char *const post_start= buf + common_header_len;
+  const char *post_start= buf + common_header_len;

-  m_table_id= uint4korr(post_start + TM_MAPID_OFFSET);
+  post_start+= TM_MAPID_OFFSET;
+  if (post_header_len == 6)
+  {
+    /* Master is of an intermediate source tree before 5.1.4. Id is 4 bytes */
+    m_table_id= uint4korr(post_start);
+    post_start+= 4;
+  }
+  else
+  {
+    DBUG_ASSERT(post_header_len == TABLE_MAP_HEADER_LEN);
+    m_table_id= uint6korr(post_start);
+    post_start+= TM_FLAGS_OFFSET;
+  }
+
+  DBUG_ASSERT(m_table_id != ULONG_MAX);

-  m_flags= uint2korr(post_start + TM_FLAGS_OFFSET);
+  m_flags= uint2korr(post_start);

   /* Read the variable part of the event */
-  const char *const vpart= post_start + post_header_len;
+  const char *const vpart= buf + common_header_len + post_header_len;

   /* Extract the length of the various parts from the buffer */
   byte const* const ptr_dblen= vpart + 0;
@@ -5657,8 +5777,8 @@
   DBUG_ASSERT(table_list->next_global == 0);
   for (TABLE *table= thd->open_tables; table ; table= table->next)
   {
-    if (strcmp(table->s->db, table_list->db) == 0
-	&& strcmp(table->s->table_name, table_list->table_name) == 0)
+    if (strcmp(table->s->db.str, table_list->db) == 0
+	&& strcmp(table->s->table_name.str, table_list->table_name) == 0)
     {
       /* Copy the table pointer into the table list. */
       table_list->table= table;
@@ -5720,11 +5840,11 @@
     if (mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG) &&
         !binlog_row_based)
     {
-      slave_print_error(rli, ER_BINLOG_ROW_RBR_TO_SBR,
-                        "It is not possible to use statement-based binlogging "
-                        "on a slave that replicates row-based.  Please use "
-                        "--binrow-format=row on slave if you want to use "
-                        "--log-slave-updates and read row-based binlog events.");
+      slave_print_msg(ERROR_LEVEL, rli, ER_BINLOG_ROW_RBR_TO_SBR,
+                      "It is not possible to use statement-based binlogging "
+                      "on a slave that replicates row-based.  Please use "
+                      "--binrow-format=row on slave if you want to use "
+                      "--log-slave-updates and read row-based binlog events.");
       DBUG_RETURN(ERR_RBR_TO_SBR);
     }

@@ -5753,11 +5873,11 @@
             simplifications (we don't honour --slave-skip-errors)
           */
           uint actual_error= thd->net.last_errno;
-          slave_print_error(rli,actual_error,
-                            "Error '%s' on opening table `%s`.`%s`",
-                            (actual_error ? thd->net.last_error :
-                             "unexpected success or fatal error"),
-                            table_list.db, table_list.table_name);
+          slave_print_msg(ERROR_LEVEL, rli, actual_error,
+                          "Error '%s' on opening table `%s`.`%s`",
+                          (actual_error ? thd->net.last_error :
+                           "unexpected success or fatal error"),
+                          table_list.db, table_list.table_name);
           thd->query_error= 1;
         }
         DBUG_RETURN(error);
@@ -5820,21 +5940,21 @@
        */
       if (col == tsh->fields)
       {
-        DBUG_ASSERT(tsh->db && tsh->table_name);
-        slave_print_error(rli, ER_BINLOG_ROW_WRONG_TABLE_DEF,
-                          "Table width mismatch - "
-                          "received %u columns, %s.%s has %u columns",
-                          m_colcnt, tsh->db, tsh->table_name, tsh->fields);
+        DBUG_ASSERT(tsh->db.str && tsh->table_name.str);
+        slave_print_msg(ERROR_LEVEL, rli, ER_BINLOG_ROW_WRONG_TABLE_DEF,
+                        "Table width mismatch - "
+                        "received %u columns, %s.%s has %u columns",
+                        m_colcnt, tsh->db.str, tsh->table_name.str, tsh->fields);
       }
       else
       {
         DBUG_ASSERT(col < m_colcnt && col < tsh->fields);
-        DBUG_ASSERT(tsh->db && tsh->table_name);
-        slave_print_error(rli, ER_BINLOG_ROW_WRONG_TABLE_DEF,
-                          "Column %d type mismatch - "
-                          "received type %d, %s.%s has type %d",
-                          col, m_coltype[col], tsh->db, tsh->table_name,
-                          m_table->field[col]->type());
+        DBUG_ASSERT(tsh->db.str && tsh->table_name.str);
+        slave_print_msg(ERROR_LEVEL, rli, ER_BINLOG_ROW_WRONG_TABLE_DEF,
+                        "Column %d type mismatch - "
+                        "received type %d, %s.%s has type %d",
+                        col, m_coltype[col], tsh->db.str, tsh->table_name.str,
+                        m_table->field[col]->type());
       }

       thd->query_error= 1;
@@ -5877,8 +5997,15 @@
 #ifndef MYSQL_CLIENT
 bool Table_map_log_event::write_data_header(IO_CACHE *file)
 {
+  DBUG_ASSERT(m_table_id != ULONG_MAX);
   byte buf[TABLE_MAP_HEADER_LEN];
-  int4store(buf + TM_MAPID_OFFSET, m_table_id);
+  DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
+                  {
+                    int4store(buf + 0, m_table_id);
+                    int2store(buf + 4, m_flags);
+                    return (my_b_safe_write(file, buf, 6));
+                  });
+  int6store(buf + TM_MAPID_OFFSET, (ulonglong)m_table_id);
   int2store(buf + TM_FLAGS_OFFSET, m_flags);
   return (my_b_safe_write(file, buf, TABLE_MAP_HEADER_LEN));
 }
@@ -5930,28 +6057,13 @@
   if (!print_event_info->short_form)
   {
     print_header(file, print_event_info);
-    fprintf(file, "\tTable_map\t"
-            "`%s`.`%s` mapped to number %lu\n",
+    fprintf(file, "\tTable_map: `%s`.`%s` mapped to number %lu\n",
             m_dbnam, m_tblnam, m_table_id);
+    print_base64(file, print_event_info);
   }
 }
 #endif

-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-void Table_map_log_event::cleanup(THD *thd_arg, struct st_relay_log_info *rli)
-{
-  /*
-    Instances of Table_map_log_event, if ::exec_event() was called on them,
-    may have opened tables, which we cannot be sure have been closed (because
-    maybe the Rows_log_event have not been found or will not be, because slave
-    SQL thread is stopping, or relay log has a missing tail etc). So we close
-    all thread's tables. And so the table mappings have to be cancelled.
-  */
-  rli->m_table_map.clear_tables();
-  close_thread_tables(thd_arg);
-}
-#endif
-
 /**************************************************************************
 	Write_rows_log_event member functions
 **************************************************************************/
@@ -5992,7 +6104,7 @@
     inform the storage engine that it should use this behaviour.
   */

-  /* Tell the SE that we are using REPLACE semantics. */
+  /* Tell the storage engine that we are using REPLACE semantics. */
   thd->lex->duplicates= DUP_REPLACE;

   /*
@@ -6002,11 +6114,8 @@
   */
   thd->lex->sql_command= SQLCOM_REPLACE;

-  // needed for ndbcluster
-  table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
-  /* TODO: Ensure that myisam allow write_row() with duplicate key. */
-  table->file->extra(HA_EXTRA_IGNORE_NO_KEY); // needed for ndbcluster
-
+  table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);  // Needed for ndbcluster
+  table->file->extra(HA_EXTRA_IGNORE_NO_KEY);   // Needed for ndbcluster
   /*
     TODO: the cluster team (Tomas?) says that it's better if the engine knows
     how many rows are going to be inserted, then it can allocate needed memory
@@ -6034,10 +6143,9 @@

 int Write_rows_log_event::do_after_row_operations(TABLE *table, int error)
 {
-  DBUG_ENTER("Write_rows_log_event::do_after_row_operations");
   if (error == 0)
     error= table->file->end_bulk_insert();
-  DBUG_RETURN(error);
+  return error;
 }

 char const *Write_rows_log_event::do_prepare_row(THD *thd, TABLE *table,
@@ -6051,7 +6159,7 @@
   */
   DBUG_ASSERT(table->s->fields >= m_width);
   DBUG_ASSERT(ptr);
-  ptr= unpack_row(thd, table, table->record[0], ptr, &m_cols);
+  ptr= unpack_row(table, table->record[0], ptr, &m_cols);
   return ptr;
 }

@@ -6067,7 +6175,7 @@
   return 1;
 }

-// Anonymous namespace for template functions/classes
+/* Anonymous namespace for template functions/classes */
 namespace {

   /*
@@ -6196,19 +6304,13 @@
     {
       if ((error= table->file->ha_delete_row(table->record[1])))
         return error;
-
-      /*
-         Matz: Do we need to set no_trans_update for non-transactional
-         tables ???
-      */
-
       /* Will retry ha_write_row() with the offending row removed. */
     }
   }
   return error;
 }

-int Write_rows_log_event::do_exec_row(TABLE *table, st_relay_log_info *rli)
+int Write_rows_log_event::do_exec_row(TABLE *table)
 {
   DBUG_ASSERT(table != NULL);
   int error= replace_record(thd, table);
@@ -6222,7 +6324,8 @@
   if (!print_event_info->short_form)
   {
     print_header(file, print_event_info);
-    fprintf(file, "\nWrite_rows: table id %lu\n", m_table_id); // TODO: WL#2321
+    fprintf(file, "\tWrite_rows: table id %lu", m_table_id);
+    print_base64(file, print_event_info);
   }
 }
 #endif
@@ -6457,7 +6560,7 @@
   DBUG_ASSERT(table->s->fields >= m_width);

   DBUG_ASSERT(ptr != NULL);
-  ptr= unpack_row(thd, table, table->record[0], ptr, &m_cols);
+  ptr= unpack_row(table, table->record[0], ptr, &m_cols);

   /*
     If we will access rows using the random access method, m_key will
@@ -6465,11 +6568,6 @@
    */
   if (m_key)
   {
-    /*
-      We have a key, let's use the primary key if there is one.
-      Otherwise, we use the first key (index) we find for the table.
-    */
-    uint const pk= table->s->primary_key;
     KEY *const key_info= table->key_info;

     key_copy(m_key, table->record[0], key_info, 0);
@@ -6478,19 +6576,13 @@
   return ptr;
 }

-int Delete_rows_log_event::do_exec_row(TABLE *table, st_relay_log_info *rli)
+int Delete_rows_log_event::do_exec_row(TABLE *table)
 {
-  DBUG_ENTER("Delete_rows_log_event::do_exec_row(TABLE*,...)");
-  DBUG_PRINT("enter", ("table=%p (%s), rli=%p",
-		       table, table->s->table_name, rli));
   DBUG_ASSERT(table != NULL);

   int error= find_and_fetch_row(table, m_key, m_search_record);
   if (error)
-  {
-    DBUG_PRINT("return", ("error: %d", error));
-    DBUG_RETURN(error);
-  }
+    return error;

   /*
      Now we should have the right row to delete.  We are using
@@ -6499,7 +6591,7 @@
   */
   error= table->file->ha_delete_row(table->record[0]);

-  DBUG_RETURN(error);
+  return error;
 }

 #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
@@ -6511,7 +6603,8 @@
   if (!print_event_info->short_form)
   {
     print_header(file, print_event_info);
-    fprintf(file, "\nDelete_rows: table id %lu\n", m_table_id); // TODO: WL#2321
+    fprintf(file, "\tDelete_rows: table id %lu", m_table_id);
+    print_base64(file, print_event_info);
   }
 }
 #endif
@@ -6625,10 +6718,10 @@
   DBUG_ASSERT(table->s->fields >= m_width);

   /* record[0] is the before image for the update */
-  ptr= unpack_row(thd, table, table->record[0], ptr, &m_cols);
+  ptr= unpack_row(table, table->record[0], ptr, &m_cols);
   DBUG_ASSERT(ptr != NULL);
   /* record[1] is the after image for the update */
-  ptr= unpack_row(thd, table, table->record[1], ptr, &m_cols);
+  ptr= unpack_row(table, table->record[1], ptr, &m_cols);

   /*
     If we will access rows using the random access method, m_key will
@@ -6636,11 +6729,6 @@
    */
   if (m_key)
   {
-    /*
-      We have a key, let's use the primary key if there is one.
-      Otherwise, we use the first key (index) we find for the table.
-    */
-    uint const pk= table->s->primary_key;
     KEY *const key_info= table->key_info;

     key_copy(m_key, table->record[0], key_info, 0);
@@ -6649,19 +6737,13 @@
   return ptr;
 }

-int Update_rows_log_event::do_exec_row(TABLE *table, st_relay_log_info *rli)
+int Update_rows_log_event::do_exec_row(TABLE *table)
 {
-  DBUG_ENTER("Update_rows_log_event::do_exec_row(TABLE*,...)");
-  DBUG_PRINT("enter", ("table=%p (%s), rli=%p",
-		       table, table->s->table_name, rli));
   DBUG_ASSERT(table != NULL);

   int error= find_and_fetch_row(table, m_key, m_search_record);
   if (error)
-  {
-    DBUG_PRINT("return", ("error=%d", error));
-    DBUG_RETURN(error);
-  }
+    return error;

   /*
     Now we should have the right row to update.  The record that has
@@ -6669,7 +6751,7 @@
   */
   error= table->file->ha_update_row(table->record[0], table->record[1]);

-  DBUG_RETURN(error);
+  return error;
 }
 #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */

@@ -6680,7 +6762,8 @@
   if (!print_event_info->short_form)
   {
     print_header(file, print_event_info);
-    fprintf(file, "\nUpdate_rows: table id %lu\n", m_table_id); // TODO: WL#2321
+    fprintf(file, "\tUpdate_rows: table id %lu", m_table_id);
+    print_base64(file, print_event_info);
   }
 }
 #endif

--- 1.346/sql/mysql_priv.h	2005-11-24 09:59:02 +01:00
+++ 1.347/sql/mysql_priv.h	2005-12-29 20:48:11 +01:00
@@ -42,8 +42,13 @@

 /* TODO convert all these three maps to Bitmap classes */
 typedef ulonglong table_map;          /* Used for table bits in join */
-typedef Bitmap<64> key_map;           /* Used for finding keys */
+#if MAX_INDEXES <= 64
+typedef Bitmap<64>  key_map;          /* Used for finding keys */
+#else
+typedef Bitmap<((MAX_INDEXES+7)/8*8)> key_map; /* Used for finding keys */
+#endif
 typedef ulong key_part_map;           /* Used for finding key parts */
+typedef ulong nesting_map;  /* Used for flags of nesting constructs */
 /*
   Used to identify NESTED_JOIN structures within a join (applicable only to
   structures that have not been simplified away and embed more the one
@@ -101,6 +106,7 @@
 #define MAX_FIELDS_BEFORE_HASH	32
 #define USER_VARS_HASH_SIZE     16
 #define STACK_MIN_SIZE		8192	// Abort if less stack during eval.
+#define STACK_MIN_SIZE_FOR_OPEN 1024*80
 #define STACK_BUFF_ALLOC	256	// For stack overrun checks
 #ifndef MYSQLD_NET_RETRY_COUNT
 #define MYSQLD_NET_RETRY_COUNT  10	// Abort read after this many int.
@@ -595,6 +601,7 @@
 bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create);
 bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent);
 void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags);
+void mysql_client_binlog_statement(THD *thd);
 bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
                     my_bool drop_temporary);
 int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
@@ -602,8 +609,8 @@
 int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables,
 				   bool if_exists, bool drop_temporary,
 				   bool log_query);
-int quick_rm_table(enum db_type base,const char *db,
-		   const char *table_name);
+bool quick_rm_table(enum db_type base,const char *db,
+                    const char *table_name);
 void close_cached_table(THD *thd, TABLE *table);
 bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list);
 bool mysql_change_db(THD *thd,const char *name,bool no_access_check);
@@ -634,7 +641,10 @@

 bool table_cache_init(void);
 void table_cache_free(void);
-uint cached_tables(void);
+bool table_def_init(void);
+void table_def_free(void);
+uint cached_open_tables(void);
+uint cached_table_definitions(void);
 void kill_mysql(void);
 void close_connection(THD *thd, uint errcode, bool lock);
 bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
@@ -679,6 +689,7 @@
 bool mysql_preload_keys(THD* thd, TABLE_LIST* table_list);
 int reassign_keycache_tables(THD* thd, KEY_CACHE *src_cache,
                              KEY_CACHE *dst_cache);
+TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list);

 bool mysql_xa_recover(THD *thd);

@@ -781,15 +792,18 @@
                   bool reset_auto_increment);
 bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok);
 bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create);
+uint create_table_def_key(THD *thd, char *key, TABLE_LIST *table_list,
+                          bool tmp_table);
+TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key,
+                             uint key_length, uint db_flags, int *error);
+void release_table_share(TABLE_SHARE *share, enum release_type type);
+TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name);
 TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update);
 TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem,
 		  bool *refresh, uint flags);
 bool reopen_name_locked_table(THD* thd, TABLE_LIST* table);
 TABLE *find_locked_table(THD *thd, const char *db,const char *table_name);
-bool reopen_table(TABLE *table,bool locked);
 bool reopen_tables(THD *thd,bool get_locks,bool in_refresh);
-void close_old_data_files(THD *thd, TABLE *table, bool abort_locks,
-			  bool send_refresh);
 bool close_data_tables(THD *thd,const char *db, const char *table_name);
 bool wait_for_tables(THD *thd);
 bool table_is_used(TABLE *table, bool wait_for_name_lock);
@@ -810,18 +824,15 @@
                      bool check_privileges, bool register_tree_change);
 Field *
 find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
-                        const char *name, const char *item_name,
-                        const char *table_name, const char *db_name,
-                        uint length, Item **ref,
-                        bool check_grants_table, bool check_grants_view,
-                        bool allow_rowid,
+                        const char *name, uint length,
+                        const char *item_name, const char *db_name,
+                        const char *table_name, Item **ref,
+                        bool check_privileges, bool allow_rowid,
                         uint *cached_field_index_ptr,
                         bool register_tree_change, TABLE_LIST **actual_table);
 Field *
-find_field_in_table(THD *thd, TABLE *table, const char *name,
-                    uint length, bool check_grants, bool allow_rowid,
-                    uint *cached_field_index_ptr,
-                    Security_context *sctx);
+find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
+                    bool allow_rowid, uint *cached_field_index_ptr);
 Field *
 find_field_in_table_sef(TABLE *table, const char *name);

@@ -940,8 +951,9 @@
 				uint uint_geom_type);
 void store_position_for_column(const char *name);
 bool add_to_list(THD *thd, SQL_LIST &list,Item *group,bool asc);
-Name_resolution_context *make_join_on_context(THD *thd, TABLE_LIST *left_op,
-                                              TABLE_LIST *right_op);
+bool push_new_name_resolution_context(THD *thd,
+                                      TABLE_LIST *left_op,
+                                      TABLE_LIST *right_op);
 void add_join_on(TABLE_LIST *b,Item *expr);
 void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List<String> *using_fields);
 bool add_proc_to_list(THD *thd, Item *item);
@@ -986,7 +998,8 @@
 		COND **conds);
 int setup_ftfuncs(SELECT_LEX* select);
 int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
-void wait_for_refresh(THD *thd);
+void wait_for_condition(THD *thd, pthread_mutex_t *mutex,
+                        pthread_cond_t *cond);
 int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags);
 int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
 bool open_and_lock_tables(THD *thd,TABLE_LIST *tables);
@@ -1005,9 +1018,12 @@
                                const char *db_name,
                                const char *table_name);
 TABLE_LIST *unique_table(TABLE_LIST *table, TABLE_LIST *table_list);
-TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name);
-bool close_temporary_table(THD *thd, const char *db, const char *table_name);
-void close_temporary(TABLE *table, bool delete_table);
+TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name);
+TABLE *find_temporary_table(THD *thd, TABLE_LIST *table_list);
+bool close_temporary_table(THD *thd, TABLE_LIST *table_list);
+void close_temporary_table(THD *thd, TABLE *table, bool free_share,
+                           bool delete_table);
+void close_temporary(TABLE *table, bool free_share, bool delete_table);
 bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db,
 			    const char *table_name);
 void remove_db_from_cache(const char *db);
@@ -1086,7 +1102,7 @@
 #endif
 void mysql_print_status();
 /* key.cc */
-int find_ref_key(TABLE *form,Field *field, uint *offset);
+int find_ref_key(KEY *key, uint key_count, Field *field, uint *key_length);
 void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length);
 void key_restore(byte *to_record, byte *from_key, KEY *key_info,
                  uint key_length);
@@ -1126,8 +1142,8 @@
 uint check_word(TYPELIB *lib, const char *val, const char *end,
 		const char **end_of_word);

-bool is_keyword(const char *name, uint len);

+bool is_keyword(const char *name, uint len);

 #define MY_DB_OPT_FILE "db.opt"
 bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
@@ -1176,7 +1192,7 @@
 extern ulong slave_open_temp_tables;
 extern ulong query_cache_size, query_cache_min_res_unit;
 extern ulong slow_launch_threads, slow_launch_time;
-extern ulong table_cache_size;
+extern ulong table_cache_size, table_def_size;
 extern ulong max_connections,max_connect_errors, connect_timeout;
 extern ulong slave_net_timeout, slave_trans_retries;
 extern uint max_user_connections;
@@ -1212,7 +1228,7 @@
 extern bool mysql_proc_table_exists;
 extern uint volatile thread_count, thread_running, global_read_lock;
 extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types;
-extern my_bool opt_safe_show_db, opt_local_infile;
+extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap;
 extern my_bool opt_slave_compressed_protocol, use_temp_pool;
 extern my_bool opt_readonly, lower_case_file_system;
 extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
@@ -1379,23 +1395,36 @@

 void unireg_init(ulong options);
 void unireg_end(void);
-bool mysql_create_frm(THD *thd, my_string file_name,
+bool mysql_create_frm(THD *thd, const char *file_name,
                       const char *db, const char *table,
 		      HA_CREATE_INFO *create_info,
 		      List<create_field> &create_field,
 		      uint key_count,KEY *key_info,handler *db_type);
-int rea_create_table(THD *thd, my_string file_name,
-                     const char *db, const char *table,
+int rea_create_table(THD *thd, const char *path,
+                     const char *db, const char *table_name,
                      HA_CREATE_INFO *create_info,
-		     List<create_field> &create_field,
-		     uint key_count,KEY *key_info, handler *file);
+  		     List<create_field> &create_field,
+                     uint key_count,KEY *key_info,
+                     handler *file);
 int format_number(uint inputflag,uint max_length,my_string pos,uint length,
 		  my_string *errpos);
+
+/* table.cc */
+TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
+                               uint key_length);
+void init_tmp_table_share(TABLE_SHARE *share, const char *key, uint key_length,
+                          const char *table_name, const char *path);
+void free_table_share(TABLE_SHARE *share);
+int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags);
+void open_table_error(TABLE_SHARE *share, int error, int db_errno, int errarg);
+int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
+                          uint db_stat, uint prgflag, uint ha_open_flags,
+                          TABLE *outparam);
 int openfrm(THD *thd, const char *name,const char *alias,uint filestat,
             uint prgflag, uint ha_open_flags, TABLE *outparam);
 int readfrm(const char *name, const void** data, uint* length);
 int writefrm(const char* name, const void* data, uint len);
-int closefrm(TABLE *table);
+int closefrm(TABLE *table, bool free_share);
 int read_string(File file, gptr *to, uint length);
 void free_blobs(TABLE *table);
 int set_zone(int nr,int min_zone,int max_zone);
@@ -1448,14 +1477,14 @@
 uint calc_week(TIME *l_time, uint week_behaviour, uint *year);
 void find_date(char *pos,uint *vek,uint flag);
 TYPELIB *convert_strings_to_array_type(my_string *typelibs, my_string *end);
-TYPELIB *typelib(List<String> &strings);
+TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings);
 ulong get_form_pos(File file, uchar *head, TYPELIB *save_names);
 ulong make_new_entry(File file,uchar *fileinfo,TYPELIB *formnames,
 		     const char *newname);
 ulong next_io_size(ulong pos);
 void append_unescaped(String *res, const char *pos, uint length);
-int create_frm(THD *thd, char *name, const char *db, const char *table,
-               uint reclength,uchar *fileinfo,
+int create_frm(THD *thd, const char *name, const char *db, const char *table,
+               uint reclength, uchar *fileinfo,
 	       HA_CREATE_INFO *create_info, uint keys);
 void update_create_info_from_table(HA_CREATE_INFO *info, TABLE *form);
 int rename_file_ext(const char * from,const char * to,const char * ext);

--- 1.505/sql/mysqld.cc	2005-12-21 18:10:20 +01:00
+++ 1.506/sql/mysqld.cc	2005-12-29 20:48:12 +01:00
@@ -1132,6 +1132,10 @@

   mysql_log.cleanup();
   mysql_slow_log.cleanup();
+  /* make sure that handlers finish up
+     what they have that is dependent on the binlog
+  */
+  ha_binlog_end(current_thd);
   mysql_bin_log.cleanup();

 #ifdef HAVE_REPLICATION
@@ -3059,11 +3063,15 @@
   }
   if (opt_binlog_format_id == BF_UNSPECIFIED)
   {
-    /*
-      We use statement-based by default, but could change this to be row-based
-      if this is a cluster build (i.e. have_ndbcluster is true)...
-    */
-    opt_binlog_format_id= BF_STMT;
+#ifdef HAVE_ROW_BASED_REPLICATION
+    if (have_ndbcluster == SHOW_OPTION_YES)
+    {
+      rpl_filter->add_ignore_table("cluster_replication.binlog_index");
+      opt_binlog_format_id= BF_ROW;
+    }
+    else
+#endif
+      opt_binlog_format_id= BF_STMT;
   }
 #ifdef HAVE_ROW_BASED_REPLICATION
   if (opt_binlog_format_id == BF_ROW)
@@ -4597,6 +4605,8 @@
   OPT_NDB_DISTRIBUTION,
   OPT_NDB_INDEX_STAT_ENABLE,
   OPT_NDB_INDEX_STAT_CACHE_ENTRIES, OPT_NDB_INDEX_STAT_UPDATE_FREQ,
+  OPT_NDB_REPORT_THRESH_BINLOG_EPOCH_SLIP,
+  OPT_NDB_REPORT_THRESH_BINLOG_MEM_USAGE,
   OPT_SKIP_SAFEMALLOC,
   OPT_TEMP_POOL, OPT_TX_ISOLATION, OPT_COMPLETION_TYPE,
   OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS,
@@ -5249,6 +5259,24 @@
    (gptr*) &global_system_variables.ndb_force_send,
    (gptr*) &global_system_variables.ndb_force_send,
    0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
+#ifdef HAVE_NDB_BINLOG
+  {"ndb-report-thresh-binlog-epoch-slip", OPT_NDB_REPORT_THRESH_BINLOG_EPOCH_SLIP,
+   "Threshold on number of epochs to be behind before reporting binlog status. "
+   "E.g. 3 means that if the difference between what epoch has been received "
+   "from the storage nodes and what has been applied to the binlog is 3 or more, "
+   "a status message will be sent to the cluster log.",
+   (gptr*) &ndb_report_thresh_binlog_epoch_slip,
+   (gptr*) &ndb_report_thresh_binlog_epoch_slip,
+   0, GET_ULONG, REQUIRED_ARG, 3, 0, 256, 0, 0, 0},
+  {"ndb-report-thresh-binlog-mem-usage", OPT_NDB_REPORT_THRESH_BINLOG_MEM_USAGE,
+   "Threshold on percentage of free memory before reporting binlog status. E.g. "
+   "10 means that if amount of available memory for receiving binlog data from "
+   "the storage nodes goes below 10%, "
+   "a status message will be sent to the cluster log.",
+   (gptr*) &ndb_report_thresh_binlog_mem_usage,
+   (gptr*) &ndb_report_thresh_binlog_mem_usage,
+   0, GET_ULONG, REQUIRED_ARG, 10, 0, 100, 0, 0, 0},
+#endif
   {"ndb-use-exact-count", OPT_NDB_USE_EXACT_COUNT,
    "Use exact records count during query planning and for fast "
    "select count(*), disable for faster queries.",

--- 1.259/sql/slave.cc	2005-11-24 09:59:03 +01:00
+++ 1.260/sql/slave.cc	2005-12-29 20:48:13 +01:00
@@ -18,6 +18,7 @@

 #include <mysql.h>
 #include <myisam.h>
+#include "rpl_rli.h"
 #include "slave.h"
 #include "sql_repl.h"
 #include "rpl_filter.h"
@@ -50,8 +51,6 @@
 */

 int disconnect_slave_event_count = 0, abort_slave_event_count = 0;
-int events_till_abort = -1;
-static int events_till_disconnect = -1;

 typedef enum { SLAVE_THD_IO, SLAVE_THD_SQL} SLAVE_THD_TYPE;

@@ -515,7 +514,7 @@
       Don't ask for disk deletion. For now, anyway they will be deleted when
       slave restarts, but it is a better intention to not delete them.
     */
-    close_temporary(table, 0);
+    close_temporary(table, 1, 0);
   }
   save_temporary_tables= 0;
   slave_open_temp_tables= 0;
@@ -862,19 +861,48 @@
 {
   DBUG_ASSERT(rli->sql_thd == thd);
   DBUG_ASSERT(rli->slave_running == 1);// tracking buffer overrun
-  return rli->abort_slave || abort_loop || thd->killed;
+  if (abort_loop || thd->killed || rli->abort_slave)
+  {
+    /*
+      If we are in an unsafe situation (stopping could corrupt replication),
+      we give one minute to the slave SQL thread of grace before really
+      terminating, in the hope that it will be able to read more events and
+      the unsafe situation will soon be left. Note that this one minute starts
+      from the last time anything happened in the slave SQL thread. So it's
+      really one minute of idleness, we don't timeout if the slave SQL thread
+      is actively working.
+    */
+    if (!rli->unsafe_to_stop_at)
+      return 1;
+    DBUG_PRINT("info", ("Slave SQL thread is in an unsafe situation, giving "
+                        "it some grace period"));
+    if (difftime(time(0), rli->unsafe_to_stop_at) > 60)
+    {
+      slave_print_msg(ERROR_LEVEL, rli, 0,
+                      "SQL thread had to stop in an unsafe situation, in "
+                      "the middle of applying updates to a "
+                      "non-transactional table without any primary key. "
+                      "There is a risk of duplicate updates when the slave "
+                      "SQL thread is restarted. Please check your tables' "
+                      "contents after restart.");
+      return 1;
+    }
+  }
+  return 0;
 }


 /*
-  Writes an error message to rli->last_slave_error and rli->last_slave_errno
-  (which will be displayed by SHOW SLAVE STATUS), and prints it to stderr.
+  Writes a message to stderr, and if it's an error message, to
+  rli->last_slave_error and rli->last_slave_errno (which will be displayed by
+  SHOW SLAVE STATUS).

   SYNOPSIS
-    slave_print_error()
-    rli
+    slave_print_msg()
+    level       The severity level
+    rli
     err_code    The error code
-    msg         The error message (usually related to the error code, but can
+    msg         The message (usually related to the error code, but can
                 contain more information).
     ...         (this is printf-like format, with % symbols in msg)

@@ -882,22 +910,47 @@
     void
 */

-void slave_print_error(RELAY_LOG_INFO* rli, int err_code, const char* msg, ...)
+void slave_print_msg(enum loglevel level, RELAY_LOG_INFO* rli,
+                     int err_code, const char* msg, ...)
 {
+  void (*report_function)(const char *, ...);
+  char buff[MAX_SLAVE_ERRMSG], *pbuff= buff;
+  uint pbuffsize= sizeof(buff);
   va_list args;
   va_start(args,msg);
-  my_vsnprintf(rli->last_slave_error,
-	       sizeof(rli->last_slave_error), msg, args);
-  rli->last_slave_errno = err_code;
-  /* If the error string ends with '.', do not add a ',' it would be ugly */
-  if (rli->last_slave_error[0] &&
-      (*(strend(rli->last_slave_error)-1) == '.'))
-    sql_print_error("Slave: %s Error_code: %d", rli->last_slave_error,
-                    err_code);
+  switch (level)
+  {
+  case ERROR_LEVEL:
+    /*
+      This my_error call only has effect in client threads.
+      Slave threads do nothing in my_error().
+    */
+    my_error(ER_UNKNOWN_ERROR, MYF(0), msg);
+    /*
+      It's an error, it must be reported in Last_error and Last_errno in SHOW
+      SLAVE STATUS.
+    */
+    pbuff= rli->last_slave_error;
+    pbuffsize= sizeof(rli->last_slave_error);
+    rli->last_slave_errno = err_code;
+    report_function= sql_print_error;
+    break;
+  case WARNING_LEVEL:
+    report_function= sql_print_warning;
+    break;
+  case INFORMATION_LEVEL:
+    report_function= sql_print_information;
+    break;
+  default:
+    DBUG_ASSERT(0); // should not come here
+    return; // don't crash production builds, just do nothing
+  }
+  my_vsnprintf(pbuff, pbuffsize, msg, args);
+  /* If the msg string ends with '.', do not add a ',' it would be ugly */
+  if (pbuff[0] && (*(strend(pbuff)-1) == '.'))
+    (*report_function)("Slave: %s Error_code: %d", pbuff, err_code);
   else
-    sql_print_error("Slave: %s, Error_code: %d", rli->last_slave_error,
-                    err_code);
-
+    (*report_function)("Slave: %s, Error_code: %d", pbuff, err_code);
 }

 /*
@@ -1068,7 +1121,7 @@
   MYSQL_RES *master_res= 0;
   MYSQL_ROW master_row;

-  if (!mysql_real_query(mysql, "SELECT UNIX_TIMESTAMP()", 23) &&
+  if (!mysql_real_query(mysql, STRING_WITH_LEN("SELECT UNIX_TIMESTAMP()")) &&
       (master_res= mysql_store_result(mysql)) &&
       (master_row= mysql_fetch_row(master_res)))
   {
@@ -1094,7 +1147,8 @@
     Note: we could have put a @@SERVER_ID in the previous SELECT
     UNIX_TIMESTAMP() instead, but this would not have worked on 3.23 masters.
   */
-  if (!mysql_real_query(mysql, "SHOW VARIABLES LIKE 'SERVER_ID'", 31) &&
+  if (!mysql_real_query(mysql,
+                        STRING_WITH_LEN("SHOW VARIABLES LIKE 'SERVER_ID'")) &&
       (master_res= mysql_store_result(mysql)))
   {
     if ((master_row= mysql_fetch_row(master_res)) &&
@@ -1129,7 +1183,8 @@
     goto err;

   if ((*mysql->server_version == '4') &&
-      !mysql_real_query(mysql, "SELECT @@GLOBAL.COLLATION_SERVER", 32) &&
+      !mysql_real_query(mysql,
+                        STRING_WITH_LEN("SELECT @@GLOBAL.COLLATION_SERVER")) &&
       (master_res= mysql_store_result(mysql)))
   {
     if ((master_row= mysql_fetch_row(master_res)) &&
@@ -1156,7 +1211,7 @@
     those were alpha).
   */
   if ((*mysql->server_version == '4') &&
-      !mysql_real_query(mysql, "SELECT @@GLOBAL.TIME_ZONE", 25) &&
+      !mysql_real_query(mysql, STRING_WITH_LEN("SELECT @@GLOBAL.TIME_ZONE")) &&
       (master_res= mysql_store_result(mysql)))
   {
     if ((master_row= mysql_fetch_row(master_res)) &&
@@ -1294,7 +1349,7 @@
   error=file->repair(thd,&check_opt) != 0;
   thd->net.vio = save_vio;
   if (error)
-    my_error(ER_INDEX_REBUILD, MYF(0), tables.table->s->table_name);
+    my_error(ER_INDEX_REBUILD, MYF(0), tables.table->s->table_name.str);

 err:
   close_thread_tables(thd);
@@ -1377,6 +1432,7 @@
   const char* msg = 0;
   int error = 0;
   DBUG_ENTER("init_relay_log_info");
+  DBUG_ASSERT(!rli->no_storage);         // Don't init if there is no storage

   if (rli->inited)                       // Set if this function called
     DBUG_RETURN(0);
@@ -1672,7 +1728,7 @@
   if (rli->ign_master_log_name_end[0])
   {
     DBUG_PRINT("info",("writing a Rotate event to track down ignored events"));
-    Rotate_log_event *ev= new Rotate_log_event(thd, rli->ign_master_log_name_end,
+    Rotate_log_event *ev= new Rotate_log_event(rli->ign_master_log_name_end,
                                                0, rli->ign_master_log_pos_end,
                                                Rotate_log_event::DUP_NAME);
     rli->ign_master_log_name_end[0]= 0;
@@ -2239,12 +2295,13 @@


 st_relay_log_info::st_relay_log_info()
-  :info_fd(-1), cur_log_fd(-1), save_temporary_tables(0),
+  :no_storage(FALSE), info_fd(-1), cur_log_fd(-1), save_temporary_tables(0),
    cur_log_old_open_count(0), group_master_log_pos(0), log_space_total(0),
    ignore_log_space_limit(0), last_master_timestamp(0), slave_skip_counter(0),
    abort_pos_wait(0), slave_run_id(0), sql_thd(0), last_slave_errno(0),
    inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE),
-   until_log_pos(0), retried_trans(0), m_reload_flags(RELOAD_NONE_F)
+   until_log_pos(0), retried_trans(0), m_reload_flags(RELOAD_NONE_F),
+   unsafe_to_stop_at(0)
 {
   group_relay_log_name[0]= event_relay_log_name[0]     group_master_log_name[0]= 0;
@@ -2668,11 +2725,9 @@
   /*
     my_real_read() will time us out
     We check if we were told to die, and if not, try reading again
-
-    TODO:  Move 'events_till_disconnect' to the MASTER_INFO structure
   */
 #ifndef DBUG_OFF
-  if (disconnect_slave_event_count && !(events_till_disconnect--))
+  if (disconnect_slave_event_count && !(mi->events_till_disconnect--))
     return packet_error;
 #endif

@@ -3048,7 +3103,7 @@
   else
   {
     pthread_mutex_unlock(&rli->data_lock);
-    slave_print_error(rli, 0, "\
+    slave_print_msg(ERROR_LEVEL, rli, 0, "\
 Could not parse relay log event entry. The possible reasons are: the master's \
 binary log is corrupted (you can check this by running 'mysqlbinlog' on the \
 binary log), the slave's relay log is corrupted (you can check this by running \
@@ -3077,9 +3132,6 @@
   my_thread_init();
   DBUG_ENTER("handle_slave_io");

-#ifndef DBUG_OFF
-slave_begin:
-#endif
   DBUG_ASSERT(mi->inited);
   mysql= NULL ;
   retry_count= 0;
@@ -3089,13 +3141,14 @@
   mi->slave_run_id++;

 #ifndef DBUG_OFF
-  mi->events_till_abort = abort_slave_event_count;
+  mi->events_till_disconnect = disconnect_slave_event_count;
 #endif

   thd= new THD; // note that contructor of THD uses DBUG_ !
   THD_CHECK_SENTRY(thd);

   pthread_detach_this_thread();
+  thd->thread_stack= (char*) &thd; // remember where our stack is
   if (init_slave_thread(thd, SLAVE_THD_IO))
   {
     pthread_cond_broadcast(&mi->start_cond);
@@ -3104,7 +3157,6 @@
     goto err;
   }
   mi->io_thd = thd;
-  thd->thread_stack = (char*)&thd; // remember where our stack is
   pthread_mutex_lock(&LOCK_thread_count);
   threads.append(thd);
   pthread_mutex_unlock(&LOCK_thread_count);
@@ -3327,14 +3379,6 @@
 log space");
 	  goto err;
 	}
-      // TODO: check debugging abort code
-#ifndef DBUG_OFF
-      if (abort_slave_event_count && !--events_till_abort)
-      {
-	sql_print_error("Slave I/O thread: debugging abort");
-	goto err;
-      }
-#endif
     }
   }

@@ -3373,10 +3417,6 @@
   pthread_mutex_unlock(&LOCK_thread_count);
   pthread_cond_broadcast(&mi->stop_cond);	// tell the world we are done
   pthread_mutex_unlock(&mi->run_lock);
-#ifndef DBUG_OFF
-  if (abort_slave_event_count && !events_till_abort)
-    goto slave_begin;
-#endif
   my_thread_end();
   pthread_exit(0);
   DBUG_RETURN(0);				// Can't return anything here
@@ -3396,10 +3436,6 @@
   my_thread_init();
   DBUG_ENTER("handle_slave_sql");

-#ifndef DBUG_OFF
-slave_begin:
-#endif
-
   DBUG_ASSERT(rli->inited);
   pthread_mutex_lock(&rli->run_lock);
   DBUG_ASSERT(!rli->slave_running);
@@ -3573,16 +3609,13 @@

  err:

-#ifdef HAVE_ROW_BASED_REPLICATION
   /*
     Some events set some playgrounds, which won't be cleared because thread
     stops. Stopping of this thread may not be known to these events ("stop"
     request is detected only by the present function, not by events), so we
     must "proactively" clear playgrounds:
   */
-  Table_map_log_event::cleanup(thd, rli);
-#endif
-
+  rli->cleanup_context(thd, 1);
   VOID(pthread_mutex_lock(&LOCK_thread_count));
   /*
     Some extra safety, which should not been needed (normally, event deletion
@@ -3628,10 +3661,6 @@
   pthread_cond_broadcast(&rli->stop_cond);
   // tell the world we are done
   pthread_mutex_unlock(&rli->run_lock);
-#ifndef DBUG_OFF // TODO: reconsider the code below
-  if (abort_slave_event_count && !rli->events_till_abort)
-    goto slave_begin;
-#endif
   my_thread_end();
   pthread_exit(0);
   DBUG_RETURN(0);				// Can't return anything here
@@ -3784,7 +3813,7 @@
     rotate event forever, so we need to not disconnect after one.
   */
   if (disconnect_slave_event_count)
-    events_till_disconnect++;
+    mi->events_till_disconnect++;
 #endif

   /*
@@ -4240,7 +4269,7 @@
   DBUG_ENTER("connect_to_master");

 #ifndef DBUG_OFF
-  events_till_disconnect = disconnect_slave_event_count;
+  mi->events_till_disconnect = disconnect_slave_event_count;
 #endif
   ulong client_flag= CLIENT_REMEMBER_OPTIONS;
   if (opt_slave_compressed_protocol)
@@ -4374,6 +4403,10 @@
 bool flush_relay_log_info(RELAY_LOG_INFO* rli)
 {
   bool error=0;
+
+  if (unlikely(rli->no_storage))
+    return 0;
+
   IO_CACHE *file = &rli->info_file;
   char buff[FN_REFLEN*2+22*2+4], *pos;

@@ -4390,6 +4423,7 @@
     error=1;
   if (flush_io_cache(file))
     error=1;
+
   /* Flushing the relay log is done by the slave I/O thread */
   return error;
 }
@@ -4420,7 +4454,7 @@
 }


-Log_event* next_event(RELAY_LOG_INFO* rli)
+static Log_event* next_event(RELAY_LOG_INFO* rli)
 {
   Log_event* ev;
   IO_CACHE* cur_log = rli->cur_log;
@@ -4431,6 +4465,11 @@
   DBUG_ENTER("next_event");
   DBUG_ASSERT(thd != 0);

+#ifndef DBUG_OFF
+  if (abort_slave_event_count && !rli->events_till_abort--)
+    DBUG_RETURN(0);
+#endif
+
   /*
     For most operations we need to protect rli members with data_lock,
     so we assume calling function acquired this mutex for us and we will
@@ -4552,7 +4591,7 @@
         {
           /* We generate and return a Rotate, to make our positions advance */
           DBUG_PRINT("info",("seeing an ignored end segment"));
-          ev= new Rotate_log_event(thd, rli->ign_master_log_name_end,
+          ev= new Rotate_log_event(rli->ign_master_log_name_end,
                                    0, rli->ign_master_log_pos_end,
                                    Rotate_log_event::DUP_NAME);
           rli->ign_master_log_name_end[0]= 0;
@@ -4875,6 +4914,34 @@
     m_reload_flags= RELOAD_NONE_F;
   }
 }
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+void st_relay_log_info::cleanup_context(THD *thd, bool error)
+{
+  DBUG_ASSERT(sql_thd == thd);
+  /*
+    1) Instances of Table_map_log_event, if ::exec_event() was called on them,
+    may have opened tables, which we cannot be sure have been closed (because
+    maybe the Rows_log_event have not been found or will not be, because slave
+    SQL thread is stopping, or relay log has a missing tail etc). So we close
+    all thread's tables. And so the table mappings have to be cancelled.
+    2) Rows_log_event::exec_event() may even have started statements or
+    transactions on them, which we need to rollback in case of error.
+    3) If finding a Format_description_log_event after a BEGIN, we also need
+    to rollback before continuing with the next events.
+    4) so we need this "context cleanup" function.
+  */
+  if (error)
+  {
+    ha_autocommit_or_rollback(thd, 1); // if a "statement transaction"
+    end_trans(thd, ROLLBACK); // if a "real transaction"
+  }
+  m_table_map.clear_tables();
+  close_thread_tables(thd);
+  unsafe_to_stop_at= 0;
+}
+#endif
+

 #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
 template class I_List_iterator<i_string>;

--- 1.270/sql/sql_class.h	2005-11-24 09:59:03 +01:00
+++ 1.271/sql/sql_class.h	2005-12-29 20:48:14 +01:00
@@ -21,14 +21,13 @@
 #pragma interface			/* gcc class implementation */
 #endif

-// TODO: create log.h and move all the log header stuff there
-
+#include "log.h"
+#include "rpl_rli.h"
 #include "rpl_tblmap.h"

 class Query_log_event;
 class Load_log_event;
 class Slave_log_event;
-class Format_description_log_event;
 class sp_rcontext;
 class sp_cache;
 class Rows_log_event;
@@ -36,7 +35,6 @@
 enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
 enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME };
 enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_UPDATE };
-enum enum_log_type { LOG_CLOSED, LOG_TO_BE_OPENED, LOG_NORMAL, LOG_NEW, LOG_BIN};
 enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON,
 			    DELAY_KEY_WRITE_ALL };

@@ -53,117 +51,6 @@
 #define TC_HEURISTIC_RECOVER_ROLLBACK 2
 extern uint tc_heuristic_recover;

-/*
-  Transaction Coordinator log - a base abstract class
-  for two different implementations
-*/
-class TC_LOG
-{
-  public:
-  int using_heuristic_recover();
-  TC_LOG() {}
-  virtual ~TC_LOG() {}
-
-  virtual int open(const char *opt_name)=0;
-  virtual void close()=0;
-  virtual int log(THD *thd, my_xid xid)=0;
-  virtual void unlog(ulong cookie, my_xid xid)=0;
-};
-
-class TC_LOG_DUMMY: public TC_LOG // use it to disable the logging
-{
-  public:
-  int open(const char *opt_name)        { return 0; }
-  void close()                          { }
-  int log(THD *thd, my_xid xid)         { return 1; }
-  void unlog(ulong cookie, my_xid xid)  { }
-};
-
-#ifdef HAVE_MMAP
-class TC_LOG_MMAP: public TC_LOG
-{
-  public:                // only to keep Sun Forte on sol9x86 happy
-  typedef enum {
-    POOL,                 // page is in pool
-    ERROR,                // last sync failed
-    DIRTY                 // new xids added since last sync
-  } PAGE_STATE;
-
-  private:
-  typedef struct st_page {
-    struct st_page *next; // page a linked in a fifo queue
-    my_xid *start, *end;  // usable area of a page
-    my_xid *ptr;          // next xid will be written here
-    int size, free;       // max and current number of free xid slots on the page
-    int waiters;          // number of waiters on condition
-    PAGE_STATE state;     // see above
-    pthread_mutex_t lock; // to access page data or control structure
-    pthread_cond_t  cond; // to wait for a sync
-  } PAGE;
-
-  char logname[FN_REFLEN];
-  File fd;
-  my_off_t file_length;
-  uint npages, inited;
-  uchar *data;
-  struct st_page *pages, *syncing, *active, *pool, *pool_last;
-  /*
-    note that, e.g. LOCK_active is only used to protect
-    'active' pointer, to protect the content of the active page
-    one has to use active->lock.
-    Same for LOCK_pool and LOCK_sync
-  */
-  pthread_mutex_t LOCK_active, LOCK_pool, LOCK_sync;
-  pthread_cond_t COND_pool, COND_active;
-
-  public:
-  TC_LOG_MMAP(): inited(0) {}
-  int open(const char *opt_name);
-  void close();
-  int log(THD *thd, my_xid xid);
-  void unlog(ulong cookie, my_xid xid);
-  int recover();
-
-  private:
-  void get_active_from_pool();
-  int sync();
-  int overflow();
-};
-#else
-#define TC_LOG_MMAP TC_LOG_DUMMY
-#endif
-
-extern TC_LOG *tc_log;
-extern TC_LOG_MMAP tc_log_mmap;
-extern TC_LOG_DUMMY tc_log_dummy;
-
-/* log info errors */
-#define LOG_INFO_EOF -1
-#define LOG_INFO_IO  -2
-#define LOG_INFO_INVALID -3
-#define LOG_INFO_SEEK -4
-#define LOG_INFO_MEM -6
-#define LOG_INFO_FATAL -7
-#define LOG_INFO_IN_USE -8
-
-/* bitmap to SQL_LOG::close() */
-#define LOG_CLOSE_INDEX		1
-#define LOG_CLOSE_TO_BE_OPENED	2
-#define LOG_CLOSE_STOP_EVENT	4
-
-struct st_relay_log_info;
-
-typedef struct st_log_info
-{
-  char log_file_name[FN_REFLEN];
-  my_off_t index_file_offset, index_file_start_offset;
-  my_off_t pos;
-  bool fatal; // if the purge happens to give us a negative offset
-  pthread_mutex_t lock;
-  st_log_info():fatal(0) { pthread_mutex_init(&lock, MY_MUTEX_INIT_FAST);}
-  ~st_log_info() { pthread_mutex_destroy(&lock);}
-} LOG_INFO;
-
 typedef struct st_user_var_events
 {
   user_var_entry *user_var_event;
@@ -176,201 +63,6 @@
 #define RP_LOCK_LOG_IS_ALREADY_LOCKED 1
 #define RP_FORCE_ROTATE               2

-class Log_event;
-
-/*
-  TODO split MYSQL_LOG into base MYSQL_LOG and
-  MYSQL_QUERY_LOG, MYSQL_SLOW_LOG, MYSQL_BIN_LOG
-  most of the code from MYSQL_LOG should be in the MYSQL_BIN_LOG
-  only (TC_LOG included)
-
-  TODO use mmap instead of IO_CACHE for binlog
-  (mmap+fsync is two times faster than write+fsync)
-*/
-
-class MYSQL_LOG: public TC_LOG
-{
- private:
-  /* LOCK_log and LOCK_index are inited by init_pthread_objects() */
-  pthread_mutex_t LOCK_log, LOCK_index;
-  pthread_mutex_t LOCK_prep_xids;
-  pthread_cond_t  COND_prep_xids;
-  pthread_cond_t update_cond;
-  ulonglong bytes_written;
-  time_t last_time,query_start;
-  IO_CACHE log_file;
-  IO_CACHE index_file;
-  char *name;
-  char time_buff[20],db[NAME_LEN+1];
-  char log_file_name[FN_REFLEN],index_file_name[FN_REFLEN];
-  /*
-     The max size before rotation (usable only if log_type == LOG_BIN: binary
-     logs and relay logs).
-     For a binlog, max_size should be max_binlog_size.
-     For a relay log, it should be max_relay_log_size if this is non-zero,
-     max_binlog_size otherwise.
-     max_size is set in init(), and dynamically changed (when one does SET
-     GLOBAL MAX_BINLOG_SIZE|MAX_RELAY_LOG_SIZE) by fix_max_binlog_size and
-     fix_max_relay_log_size).
-  */
-  ulong max_size;
-  ulong prepared_xids; /* for tc log - number of xids to remember */
-  volatile enum_log_type log_type;
-  enum cache_type io_cache_type;
-  // current file sequence number for load data infile binary logging
-  uint file_id;
-  uint open_count;				// For replication
-  int readers_count;
-  bool reset_pending;
-  bool write_error, inited;
-  bool need_start_event;
-  /*
-    no_auto_events means we don't want any of these automatic events :
-    Start/Rotate/Stop. That is, in 4.x when we rotate a relay log, we don't
-    want a Rotate_log event to be written to the relay log. When we start a
-    relay log etc. So in 4.x this is 1 for relay logs, 0 for binlogs.
-    In 5.0 it's 0 for relay logs too!
-  */
-  bool no_auto_events;
-  friend class Log_event;
-
-  pthread_mutex_t LOCK_next_table_id;
-  ulong m_next_table_id;
-public:
-  ulonglong m_table_map_version;
-
-  /*
-    These describe the log's format. This is used only for relay logs.
-    _for_exec is used by the SQL thread, _for_queue by the I/O thread. It's
-    necessary to have 2 distinct objects, because the I/O thread may be reading
-    events in a different format from what the SQL thread is reading (consider
-    the case of a master which has been upgraded from 5.0 to 5.1 without doing
-    RESET MASTER, or from 4.x to 5.0).
-  */
-  Format_description_log_event *description_event_for_exec,
-    *description_event_for_queue;
-
-  MYSQL_LOG();
-  /*
-    note that there's no destructor ~MYSQL_LOG() !
-    The reason is that we don't want it to be automatically called
-    on exit() - but only during the correct shutdown process
-  */
-
-  int open(const char *opt_name);
-  void close();
-  int log(THD *thd, my_xid xid);
-  void unlog(ulong cookie, my_xid xid);
-  int recover(IO_CACHE *log, Format_description_log_event *fdle);
-#if !defined(MYSQL_CLIENT)
-  /*
-    This will return a table id for the table. If the table is not known, a
-    new table id will be invented and returned.
-  */
-  ulong get_table_id(TABLE* table);
-  void update_table_map_version();
-#endif /* !defined(MYSQL_CLIENT) */
-  void reset_bytes_written()
-  {
-    bytes_written = 0;
-  }
-  void harvest_bytes_written(ulonglong* counter)
-  {
-#ifndef DBUG_OFF
-    char buf1[22],buf2[22];
-#endif
-    DBUG_ENTER("harvest_bytes_written");
-    (*counter)+=bytes_written;
-    DBUG_PRINT("info",("counter: %s  bytes_written: %s", llstr(*counter,buf1),
-		       llstr(bytes_written,buf2)));
-    bytes_written=0;
-    DBUG_VOID_RETURN;
-  }
-  void set_max_size(ulong max_size_arg);
-  void signal_update();
-  void wait_for_update(THD* thd, bool master_or_slave);
-  void set_need_start_event() { need_start_event = 1; }
-  void init(enum_log_type log_type_arg,
-	    enum cache_type io_cache_type_arg,
-	    bool no_auto_events_arg, ulong max_size);
-  void init_pthread_objects();
-  void cleanup();
-  bool open(const char *log_name,
-            enum_log_type log_type,
-            const char *new_name,
-	    enum cache_type io_cache_type_arg,
-	    bool no_auto_events_arg, ulong max_size,
-            bool null_created);
-  const char *generate_name(const char *log_name, const char *suffix,
-                            bool strip_ext, char *buff);
-  /* simplified open_xxx wrappers for the gigantic open above */
-  bool open_query_log(const char *log_name)
-  {
-    char buf[FN_REFLEN];
-    return open(generate_name(log_name, ".log", 0, buf),
-                LOG_NORMAL, 0, WRITE_CACHE, 0, 0, 0);
-  }
-  bool open_slow_log(const char *log_name)
-  {
-    char buf[FN_REFLEN];
-    return open(generate_name(log_name, "-slow.log", 0, buf),
-                LOG_NORMAL, 0, WRITE_CACHE, 0, 0, 0);
-  }
-  bool open_index_file(const char *index_file_name_arg,
-                       const char *log_name);
-  void new_file(bool need_lock);
-  bool write(THD *thd, enum enum_server_command command,
-	     const char *format,...);
-  bool write(THD *thd, const char *query, uint query_length,
-	     time_t query_start=0);
-  bool write(Log_event* event_info); // binary log write
-  bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event);
-
-  void start_union_events(THD *thd);
-  void stop_union_events(THD *thd);
-  bool is_query_in_union(THD *thd, query_id_t query_id_param);
-
-  /*
-    v stands for vector
-    invoked as appendv(buf1,len1,buf2,len2,...,bufn,lenn,0)
-  */
-  bool appendv(const char* buf,uint len,...);
-  bool append(Log_event* ev);
-
-  int generate_new_name(char *new_name,const char *old_name);
-  void make_log_name(char* buf, const char* log_ident);
-  bool is_active(const char* log_file_name);
-  int update_log_index(LOG_INFO* linfo, bool need_update_threads);
-  void rotate_and_purge(uint flags);
-  bool flush_and_sync();
-  int purge_logs(const char *to_log, bool included,
-                 bool need_mutex, bool need_update_threads,
-                 ulonglong *decrease_log_space);
-  int purge_logs_before_date(time_t purge_time);
-  int purge_first_log(struct st_relay_log_info* rli, bool included);
-  bool reset_logs(THD* thd);
-  void close(uint exiting);
-
-  // iterating through the log index file
-  int find_log_pos(LOG_INFO* linfo, const char* log_name,
-		   bool need_mutex);
-  int find_next_log(LOG_INFO* linfo, bool need_mutex);
-  int get_current_log(LOG_INFO* linfo);
-  uint next_file_id();
-  inline bool is_open() { return log_type != LOG_CLOSED; }
-  inline char* get_index_fname() { return index_file_name;}
-  inline char* get_log_fname() { return log_file_name; }
-  inline char* get_name() { return name; }
-  inline pthread_mutex_t* get_log_lock() { return &LOCK_log; }
-  inline IO_CACHE* get_log_file() { return &log_file; }
-
-  inline void lock_index() { pthread_mutex_lock(&LOCK_index);}
-  inline void unlock_index() { pthread_mutex_unlock(&LOCK_index);}
-  inline IO_CACHE *get_index_file() { return &index_file;}
-  inline uint32 get_open_count() { return open_count; }
-};
-
-
 typedef struct st_copy_info {
   ha_rows records;
   ha_rows deleted;
@@ -477,28 +169,6 @@

 #include "sql_lex.h"				/* Must be here */

-/* Needed to be able to have an I_List of char* strings in mysqld.cc. */
-
-class i_string: public ilink
-{
-public:
-  const char* ptr;
-  i_string():ptr(0) { }
-  i_string(const char* s) : ptr(s) {}
-};
-
-/* needed for linked list of two strings for replicate-rewrite-db */
-class i_string_pair: public ilink
-{
-public:
-  const char* key;
-  const char* val;
-  i_string_pair():key(0),val(0) { }
-  i_string_pair(const char* key_arg, const char* val_arg) :
-    key(key_arg),val(val_arg) {}
-};
-
-
 class delayed_insert;
 class select_result;

@@ -548,6 +218,7 @@
   ulong completion_type;
   /* Determines which non-standard SQL behaviour should be enabled */
   ulong sql_mode;
+  ulong max_sp_recursion_depth;
   /* check of key presence in updatable view */
   ulong updatable_views_with_limit;
   ulong default_week_format;
@@ -642,6 +313,7 @@

   ulong net_big_packet_count;
   ulong opened_tables;
+  ulong opened_shares;
   ulong select_full_join_count;
   ulong select_full_range_join_count;
   ulong select_range_count;
@@ -804,19 +476,6 @@
        and update_row
   */
   ulong set_query_id;
-  /*
-    This variable is used in post-parse stage to declare that sum-functions,
-    or functions which have sense only if GROUP BY is present, are allowed.
-    For example in queries
-    SELECT MIN(i) FROM foo
-    SELECT GROUP_CONCAT(a, b, MIN(i)) FROM ... GROUP BY ...
-    MIN(i) have no sense.
-    Though it's grammar-related issue, it's hard to catch it out during the
-    parse stage because GROUP BY clause goes in the end of query. This
-    variable is mainly used in setup_fields/fix_fields.
-    See item_sum.cc for details.
-  */
-  bool allow_sum_func;

   LEX_STRING name; /* name for named prepared statements */
   LEX *lex;                                     // parse tree descriptor
@@ -1114,8 +773,9 @@
   ha_rows    cuted_fields, sent_row_count, examined_row_count;
   ulong client_capabilities;
   uint in_sub_stmt;
-  bool enable_slow_log, insert_id_used;
+  bool enable_slow_log, insert_id_used, clear_next_insert_id;
   my_bool no_send_ok;
+  SAVEPOINT *savepoints;
 };


@@ -1128,12 +788,8 @@
            public Open_tables_state
 {
 public:
-  enum enum_error {
-    ALL_OK,
-    TABLE_MAP_WRITE_FAILURE,
-    BINLOG_WRITE_FAILURE,
-    COUNT
-  };
+  /* Used to execute base64 coded binlog events in MySQL server */
+  RELAY_LOG_INFO* rli_fake;

   /*
     Constant for THD::where initialization in the beginning of every query.
@@ -1241,11 +897,7 @@
   void *ha_data[MAX_HA];

 #ifdef HAVE_ROW_BASED_REPLICATION
-/*
-  When mysqlbinlog is made to understand Rows events, we may have
-  to include sql_class.h in client/ so Mats already put the ifndef below.
-*/
-#if !defined(MYSQL_CLIENT)
+#ifndef MYSQL_CLIENT

   /*
     Public interface to write rows to the binlog
@@ -1271,12 +923,10 @@
                                       my_size_t colcnt,
                                       my_size_t needed,
                                       bool is_transactional);
-  Rows_log_event* binlog_get_pending_rows_event() const
-  { return transaction.m_pending_rows_event; }
-  bool            binlog_set_pending_rows_event(Rows_log_event* ev)
-  { transaction.m_pending_rows_event= ev; return 0; }
-  int             binlog_flush_and_set_pending_rows_event(Rows_log_event*);
-
+  Rows_log_event* binlog_get_pending_rows_event() const;
+  void            binlog_set_pending_rows_event(Rows_log_event* ev);
+  int             binlog_setup_trx_data();
+
   my_size_t max_row_length_blob(TABLE* table, const byte *data) const;
   my_size_t max_row_length(TABLE* table, const byte *data) const
   {
@@ -1289,43 +939,31 @@
   }

   my_size_t pack_row(TABLE* table, MY_BITMAP const* cols, byte *row_data,
-                     my_size_t max_len, const byte *data) const;
+                     const byte *data) const;

-  int binlog_flush_pending_rows_event(bool transaction_end);
+  int binlog_flush_pending_rows_event(bool stmt_end);
   void binlog_delete_pending_rows_event();

-  ulong get_new_table_id(TABLE* table);
-  ulong get_table_id(TABLE* table)
-  {
-    ulong id= table->s->table_map_id;
-    if (id != table_mapping::NO_TABLE)
-      return id;
-    return get_new_table_id(table);
-  }
-
-  bool is_table_mapped(TABLE* table) const;
-
-  /*
-    Write a table map to the binary log 'mysql_bin_log'.
-
-    PARAMETERS
-      table     The table to write a table map for
-      is_trans  'true' if the handler is transactional,
-                'false' otherwise
-
-    RETURN VALUE
-      Error code, or zero if no error occured.
-  */
-  int binlog_write_table_map(TABLE* table, bool is_trans);
-
 #endif
 #endif /* HAVE_ROW_BASED_REPLICATION */
 #ifndef MYSQL_CLIENT
   enum enum_binlog_query_type {
-      /* The query can be logged row-based or statement-based */
+      /*
+        The query can be logged row-based or statement-based
+      */
       ROW_QUERY_TYPE,
-      /* The query has to be logged statement-based */
-      STMT_QUERY_TYPE
+
+      /*
+        The query has to be logged statement-based
+      */
+      STMT_QUERY_TYPE,
+
+      /*
+        The query represents a change to a table in the "mysql"
+        database and is currently mapped to ROW_QUERY_TYPE.
+      */
+      MYSQL_QUERY_TYPE,
+      QUERY_TYPE_COUNT
   };

   int binlog_query(enum_binlog_query_type qtype,
@@ -1335,34 +973,6 @@

 public:

-  /*
-    This function will be called from ha_commit_trans() to prepare for a
-    commit. The thread should take all necessary actions to prepare itself for
-    commit; presently it's writing the last buffered row-based binlog event to
-    the binlog.
-  */
-  int prepare_for_commit() {
-#if !defined(MYSQL_CLIENT) && defined(HAVE_ROW_BASED_REPLICATION)
-    return binlog_flush_pending_rows_event(true);
-#else
-    return 0;
-#endif
-  }
-
-  /*
-    This function will be called from ha_rollback_trans() to prepare for a
-    rollback. The thread should take all necessary actions to prepare itself
-    for rollback; presently it's discarding the last buffered row-based binlog
-    event (as the changes it contains are going to be discarded in the storage
-    engines).
-  */
-  int prepare_for_rollback() {
-#if !defined(MYSQL_CLIENT) && defined(HAVE_ROW_BASED_REPLICATION)
-    binlog_delete_pending_rows_event();
-#endif
-    return 0;
-  }
-
   struct st_transactions {
     SAVEPOINT *savepoints;
     THD_TRANS all;			// Trans since BEGIN WORK
@@ -1390,14 +1000,16 @@
       free_root(&mem_root,MYF(MY_KEEP_PREALLOC));
 #endif
     }
-#ifdef USING_TRANSACTIONS
     st_transactions()
     {
+#ifdef USING_TRANSACTIONS
       bzero((char*)this, sizeof(*this));
       xid_state.xid.null();
       init_sql_alloc(&mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
-    }
+#else
+      xid_state.xa_state= XA_NOTR;
 #endif
+    }
   } transaction;
   Field      *dupp_field;
 #ifndef __WIN__
@@ -1930,6 +1542,7 @@
   HA_CREATE_INFO *create_info;
   MYSQL_LOCK *lock;
   Field **field;
+  bool create_table_written;
 public:
   select_create (TABLE_LIST *table,
 		 HA_CREATE_INFO *create_info_par,
@@ -1938,9 +1551,11 @@
 		 List<Item> &select_fields,enum_duplicates duplic, bool ignore)
     :select_insert (NULL, NULL, &select_fields, 0, 0, duplic, ignore), \
                create_table(table),
     extra_fields(&fields_par),keys(&keys_par), create_info(create_info_par),
-    lock(0)
+    lock(0), create_table_written(FALSE)
     {}
   int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
+
+  void binlog_show_create_table();
   void store_values(List<Item> &values);
   void send_error(uint errcode,const char *err);
   bool send_eof();
@@ -1981,11 +1596,18 @@
   uint  convert_blob_length;
   CHARSET_INFO *table_charset;
   bool schema_table;
+  /*
+    True if GROUP BY and its aggregate functions are already computed
+    by a table access method (e.g. by loose index scan). In this case
+    query execution should not perform aggregation and should treat
+    aggregate functions as normal functions.
+  */
+  bool precomputed_group_by;

   TMP_TABLE_PARAM()
     :copy_field(0), group_parts(0),
      group_length(0), group_null_parts(0), convert_blob_length(0),
-     schema_table(0)
+     schema_table(0), precomputed_group_by(0)
   {}
   ~TMP_TABLE_PARAM()
   {
@@ -2247,6 +1869,13 @@
 class my_var : public Sql_alloc  {
 public:
   LEX_STRING s;
+#ifndef DBUG_OFF
+  /*
+    Routine to which this Item_splocal belongs. Used for checking if correct
+    runtime context is used for variable handling.
+  */
+  sp_head *sp;
+#endif
   bool local;
   uint offset;
   enum_field_types type;

--- 1.486/sql/sql_parse.cc	2005-11-24 09:59:03 +01:00
+++ 1.487/sql/sql_parse.cc	2005-12-29 20:48:15 +01:00
@@ -791,6 +791,9 @@

   DBUG_PRINT("info",
              ("New connection received on %s", vio_description(net->vio)));
+#ifdef SIGNAL_WITH_VIO_CLOSE
+  thd->set_active_vio(net->vio);
+#endif

   if (!thd->main_security_ctx.host)         // If TCP/IP connection
   {
@@ -1091,6 +1094,7 @@
   VOID(sigemptyset(&set));			// Get mask in use
   VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
 #endif
+  thd->thread_stack= (char*) &thd;
   if (thd->store_globals())
   {
     close_connection(thd, ER_OUT_OF_RESOURCES, 1);
@@ -1104,7 +1108,6 @@
     int error;
     NET *net= &thd->net;
     Security_context *sctx= thd->security_ctx;
-    thd->thread_stack= (char*) &thd;
     net->no_send_error= 0;

     if ((error=check_connection(thd)))
@@ -1176,6 +1179,7 @@
       or this thread has been schedule to handle the next query
     */
     thd= current_thd;
+    thd->thread_stack= (char*) &thd;
   } while (!(test_flags & TEST_NO_THREADS));
   /* The following is only executed if we are not using --one-thread */
   return(0);					/* purecov: deadcode */
@@ -1195,6 +1199,7 @@
   char *buff;

   /* The following must be called before DBUG_ENTER */
+  thd->thread_stack= (char*) &thd;
   if (my_thread_init() || thd->store_globals())
   {
 #ifndef EMBEDDED_LIBRARY
@@ -1978,7 +1983,8 @@
 	    uptime,
 	    (int) thread_count, (ulong) thd->query_id,
             (ulong) thd->status_var.long_query_count,
-	    thd->status_var.opened_tables, refresh_version, cached_tables(),
+	    thd->status_var.opened_tables, refresh_version,
+            cached_open_tables(),
 	    (uptime ? (ulonglong2double(thd->query_id) / (double) uptime) :
 	     (double) 0));
 #ifdef SAFEMALLOC
@@ -2608,7 +2614,8 @@
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
     res = mysql_backup_table(thd, first_table);
-
+    select_lex->table_list.first= (byte*) first_table;
+    lex->query_tables=all_tables;
     break;
   }
   case SQLCOM_RESTORE_TABLE:
@@ -2620,6 +2627,8 @@
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
     res = mysql_restore_table(thd, first_table);
+    select_lex->table_list.first= (byte*) first_table;
+    lex->query_tables=all_tables;
     break;
   }
   case SQLCOM_ASSIGN_TO_KEYCACHE:
@@ -3113,6 +3122,8 @@
         mysql_bin_log.write(&qinfo);
       }
     }
+    select_lex->table_list.first= (byte*) first_table;
+    lex->query_tables=all_tables;
     break;
   }
   case SQLCOM_CHECK:
@@ -3123,6 +3134,8 @@
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
     res = mysql_check_table(thd, first_table, &lex->check_opt);
+    select_lex->table_list.first= (byte*) first_table;
+    lex->query_tables=all_tables;
     break;
   }
   case SQLCOM_ANALYZE:
@@ -3143,6 +3156,8 @@
         mysql_bin_log.write(&qinfo);
       }
     }
+    select_lex->table_list.first= (byte*) first_table;
+    lex->query_tables=all_tables;
     break;
   }

@@ -3166,6 +3181,8 @@
         mysql_bin_log.write(&qinfo);
       }
     }
+    select_lex->table_list.first= (byte*) first_table;
+    lex->query_tables=all_tables;
     break;
   }
   case SQLCOM_UPDATE:
@@ -3661,8 +3678,6 @@
       my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
       break;
     }
-    if (check_access(thd,SELECT_ACL,lex->name,0,1,0,is_schema_db(lex->name)))
-      break;
     res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
     break;
   }
@@ -3671,7 +3686,8 @@
     if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0))
       break;
 #ifdef HAVE_DLOPEN
-    if (sp_find_function(thd, lex->spname))
+    if (sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
+                        &thd->sp_func_cache, FALSE))
     {
       my_error(ER_UDF_EXISTS, MYF(0), lex->spname->m_name.str);
       goto error;
@@ -3696,7 +3712,7 @@
     {
       if (mysql_bin_log.is_open())
       {
-        thd->binlog_query(THD::ROW_QUERY_TYPE,
+        thd->binlog_query(THD::MYSQL_QUERY_TYPE,
                           thd->query, thd->query_length, FALSE, FALSE);
       }
       send_ok(thd);
@@ -3714,7 +3730,7 @@
     {
       if (mysql_bin_log.is_open())
       {
-        thd->binlog_query(THD::ROW_QUERY_TYPE,
+        thd->binlog_query(THD::MYSQL_QUERY_TYPE,
                           thd->query, thd->query_length, FALSE, FALSE);
       }
       send_ok(thd);
@@ -3732,7 +3748,7 @@
     {
       if (mysql_bin_log.is_open())
       {
-        thd->binlog_query(THD::ROW_QUERY_TYPE,
+        thd->binlog_query(THD::MYSQL_QUERY_TYPE,
                           thd->query, thd->query_length, FALSE, FALSE);
       }
       send_ok(thd);
@@ -3748,7 +3764,7 @@
     {
       if (mysql_bin_log.is_open())
       {
-        thd->binlog_query(THD::ROW_QUERY_TYPE,
+        thd->binlog_query(THD::MYSQL_QUERY_TYPE,
                           thd->query, thd->query_length, FALSE, FALSE);
       }
       send_ok(thd);
@@ -3828,7 +3844,7 @@
       if (!res && mysql_bin_log.is_open())
       {
         thd->clear_error();
-        thd->binlog_query(THD::ROW_QUERY_TYPE,
+        thd->binlog_query(THD::MYSQL_QUERY_TYPE,
                           thd->query, thd->query_length, FALSE, FALSE);
       }
     }
@@ -3848,7 +3864,7 @@
 	if (mysql_bin_log.is_open())
 	{
           thd->clear_error();
-          thd->binlog_query(THD::ROW_QUERY_TYPE,
+          thd->binlog_query(THD::MYSQL_QUERY_TYPE,
                             thd->query, thd->query_length, FALSE, FALSE);
 	}
 	if (lex->sql_command == SQLCOM_GRANT)
@@ -4022,8 +4038,8 @@
     break;
   }
   case SQLCOM_SAVEPOINT:
-    if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) ||
-        !opt_using_transactions)
+    if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
+          thd->in_sub_stmt) || !opt_using_transactions)
       send_ok(thd);
     else
     {
@@ -4117,14 +4133,6 @@
       }
     }
 #endif
-    if (lex->sphead->m_type == TYPE_ENUM_FUNCTION &&
-	!(lex->sphead->m_flags & sp_head::HAS_RETURN))
-    {
-      my_error(ER_SP_NORETURN, MYF(0), name);
-      delete lex->sphead;
-      lex->sphead= 0;
-      goto error;
-    }

     /*
       We need to copy name and db in order to use them for
@@ -4153,12 +4161,12 @@
       			       db, name,
                                lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1))
       {
-        close_thread_tables(thd);
         if (sp_grant_privileges(thd, db, name,
                                 lex->sql_command == SQLCOM_CREATE_PROCEDURE))
           push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 	  	       ER_PROC_AUTO_GRANT_FAIL,
 		       ER(ER_PROC_AUTO_GRANT_FAIL));
+        close_thread_tables(thd);
       }
 #endif
       send_ok(thd);
@@ -4205,7 +4213,8 @@
         By this moment all needed SPs should be in cache so no need to look
         into DB.
       */
-      if (!(sp= sp_find_procedure(thd, lex->spname, TRUE)))
+      if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
+                                &thd->sp_proc_cache, TRUE)))
       {
 	my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
                  lex->spname->m_qname.str);
@@ -4329,9 +4338,11 @@

       memcpy(&chistics, &lex->sp_chistics, sizeof(chistics));
       if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
-	sp= sp_find_procedure(thd, lex->spname);
+        sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
+                            &thd->sp_proc_cache, FALSE);
       else
-	sp= sp_find_function(thd, lex->spname);
+        sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
+                            &thd->sp_func_cache, FALSE);
       mysql_reset_errors(thd, 0);
       if (! sp)
       {
@@ -4383,7 +4394,7 @@
         if (mysql_bin_log.is_open())
         {
           thd->clear_error();
-          thd->binlog_query(THD::ROW_QUERY_TYPE,
+          thd->binlog_query(THD::MYSQL_QUERY_TYPE,
                             thd->query, thd->query_length, FALSE, FALSE);
         }
 	send_ok(thd);
@@ -4407,9 +4418,11 @@
       char *db, *name;

       if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
-	sp= sp_find_procedure(thd, lex->spname);
+        sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
+                            &thd->sp_proc_cache, FALSE);
       else
-	sp= sp_find_function(thd, lex->spname);
+        sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
+                            &thd->sp_func_cache, FALSE);
       mysql_reset_errors(thd, 0);
       if (sp)
       {
@@ -4470,7 +4483,7 @@
         if (mysql_bin_log.is_open())
         {
           thd->clear_error();
-          thd->binlog_query(THD::ROW_QUERY_TYPE,
+          thd->binlog_query(THD::MYSQL_QUERY_TYPE,
                             thd->query, thd->query_length, FALSE, FALSE);
         }
 	send_ok(thd);
@@ -4537,6 +4550,33 @@
 					 lex->wild->ptr() : NullS));
       break;
     }
+#ifndef DBUG_OFF
+  case SQLCOM_SHOW_PROC_CODE:
+  case SQLCOM_SHOW_FUNC_CODE:
+    {
+      sp_head *sp;
+
+      if (lex->spname->m_name.length > NAME_LEN)
+      {
+	my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
+	goto error;
+      }
+      if (lex->sql_command == SQLCOM_SHOW_PROC_CODE)
+        sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
+                            &thd->sp_proc_cache, FALSE);
+      else
+        sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
+                            &thd->sp_func_cache, FALSE);
+      if (!sp || !sp->show_routine_code(thd))
+      {
+        /* We don't distinguish between errors for now */
+        my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
+                 SP_COM_STRING(lex), lex->spname->m_name.str);
+        goto error;
+      }
+      break;
+    }
+#endif // ifndef DBUG_OFF
   case SQLCOM_CREATE_VIEW:
     {
       if (end_active_trans(thd))
@@ -4555,7 +4595,7 @@
         buff.append(command[thd->lex->create_view_mode].str,
                     command[thd->lex->create_view_mode].length);
         view_store_options(thd, first_table, &buff);
-        buff.append("VIEW ", 5);
+        buff.append(STRING_WITH_LEN("VIEW "));
         /* Test if user supplied a db (ie: we did not use thd->db) */
         if (first_table->db != thd->db && first_table->db[0])
         {
@@ -4565,7 +4605,7 @@
         }
         append_identifier(thd, &buff, first_table->table_name,
                           first_table->table_name_length);
-        buff.append(" AS ", 4);
+        buff.append(STRING_WITH_LEN(" AS "));
         buff.append(first_table->source.str, first_table->source.length);

         thd->binlog_query(THD::STMT_QUERY_TYPE,
@@ -4786,15 +4826,24 @@
     if (! (res= mysql_uninstall_plugin(thd, &thd->lex->comment)))
       send_ok(thd);
     break;
+  case SQLCOM_BINLOG_BASE64_EVENT:
+  {
+#ifndef EMBEDDED_LIBRARY
+    mysql_client_binlog_statement(thd);
+#else /* EMBEDDED_LIBRARY */
+    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "embedded");
+#endif /* EMBEDDED_LIBRARY */
+    break;
+  }
   default:
     DBUG_ASSERT(0);                             /* Impossible */
     send_ok(thd);
     break;
   }
   thd->proc_info="query end";
-  /* Two binlog-related cleanups: */

   /*
+    Binlog-related cleanup:
     Reset system variables temporarily modified by SET ONE SHOT.

     Exception: If this is a SET, do nothing. This is to allow
@@ -4808,11 +4857,15 @@


   /*
-    The return value for ROW_COUNT() is "implementation dependent" if
-    the statement is not DELETE, INSERT or UPDATE (or a CALL executing
-    such a statement), but -1 is what JDBC and ODBC wants.
+    The return value for ROW_COUNT() is "implementation dependent" if the
+    statement is not DELETE, INSERT or UPDATE, but -1 is what JDBC and ODBC
+    wants.
+
+    We do not change the value for a CALL or EXECUTE statement, so the value
+    generated by the last called (or executed) statement is preserved.
    */
-  if (lex->sql_command != SQLCOM_CALL && uc_update_queries[lex->sql_command]<2)
+  if (lex->sql_command != SQLCOM_CALL && lex->sql_command != SQLCOM_EXECUTE &&
+      uc_update_queries[lex->sql_command]<2)
     thd->row_count_func= -1;
   goto cleanup;

@@ -5253,6 +5306,7 @@
 			 char *buf __attribute__((unused)))
 {
   long stack_used;
+  DBUG_ASSERT(thd == current_thd);
   if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >       (long) \
(thread_stack - margin))  {
@@ -5383,6 +5437,8 @@
   select_lex->parent_lex= lex; /* Used in init_query. */
   select_lex->init_query();
   select_lex->init_select();
+  lex->nest_level++;
+  select_lex->nest_level= lex->nest_level;
   /*
     Don't evaluate this subquery during statement prepare even if
     it's a constant one. The flag is switched off in the end of
@@ -5524,7 +5580,6 @@
   lex->query_tables_last= &lex->query_tables;
 }

-
 /*
   When you modify mysql_parse(), you may need to mofify
   mysql_test_parse_for_slave() in this same file.
@@ -5723,9 +5778,10 @@
                         buf, "TIMESTAMP");
   }

-  if (!(new_field= new_create_field(thd, field_name, type, length, decimals,
-		type_modifier, default_value, on_update_value,
-		comment, change, interval_list, cs, uint_geom_type)))
+  if (!(new_field= new create_field()) ||
+      new_field->init(thd, field_name, type, length, decimals, type_modifier,
+                      default_value, on_update_value, comment, change,
+                      interval_list, cs, uint_geom_type))
     DBUG_RETURN(1);

   lex->create_list.push_back(new_field);
@@ -5733,327 +5789,6 @@
   DBUG_RETURN(0);
 }

-/*****************************************************************************
-** Create field definition for create
-** Return 0 on failure, otherwise return create_field instance
-******************************************************************************/
-
-create_field *
-new_create_field(THD *thd, char *field_name, enum_field_types type,
-		 char *length, char *decimals,
-		 uint type_modifier,
-		 Item *default_value, Item *on_update_value,
-		 LEX_STRING *comment,
-		 char *change, List<String> *interval_list, CHARSET_INFO *cs,
-		 uint uint_geom_type)
-{
-  register create_field *new_field;
-  uint sign_len, allowed_type_modifier=0;
-  ulong max_field_charlength= MAX_FIELD_CHARLENGTH;
-  DBUG_ENTER("new_create_field");
-
-  if (!(new_field=new create_field()))
-    DBUG_RETURN(NULL);
-  new_field->field=0;
-  new_field->field_name=field_name;
-  new_field->def= default_value;
-  new_field->flags= type_modifier;
-  new_field->unireg_check= (type_modifier & AUTO_INCREMENT_FLAG ?
-			    Field::NEXT_NUMBER : Field::NONE);
-  new_field->decimals= decimals ? (uint)atoi(decimals) : 0;
-  if (new_field->decimals >= NOT_FIXED_DEC)
-  {
-    my_error(ER_TOO_BIG_SCALE, MYF(0), new_field->decimals, field_name,
-             NOT_FIXED_DEC-1);
-    DBUG_RETURN(NULL);
-  }
-
-  new_field->sql_type=type;
-  new_field->length=0;
-  new_field->change=change;
-  new_field->interval=0;
-  new_field->pack_length= new_field->key_length= 0;
-  new_field->charset=cs;
-  new_field->geom_type= (Field::geometry_type) uint_geom_type;
-
-  new_field->comment=*comment;
-  /*
-    Set flag if this field doesn't have a default value
-  */
-  if (!default_value && !(type_modifier & AUTO_INCREMENT_FLAG) &&
-      (type_modifier & NOT_NULL_FLAG) && type != FIELD_TYPE_TIMESTAMP)
-    new_field->flags|= NO_DEFAULT_VALUE_FLAG;
-
-  if (length && !(new_field->length= (uint) atoi(length)))
-    length=0; /* purecov: inspected */
-  sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1;
-
-  switch (type) {
-  case FIELD_TYPE_TINY:
-    if (!length) new_field->length=MAX_TINYINT_WIDTH+sign_len;
-    allowed_type_modifier= AUTO_INCREMENT_FLAG;
-    break;
-  case FIELD_TYPE_SHORT:
-    if (!length) new_field->length=MAX_SMALLINT_WIDTH+sign_len;
-    allowed_type_modifier= AUTO_INCREMENT_FLAG;
-    break;
-  case FIELD_TYPE_INT24:
-    if (!length) new_field->length=MAX_MEDIUMINT_WIDTH+sign_len;
-    allowed_type_modifier= AUTO_INCREMENT_FLAG;
-    break;
-  case FIELD_TYPE_LONG:
-    if (!length) new_field->length=MAX_INT_WIDTH+sign_len;
-    allowed_type_modifier= AUTO_INCREMENT_FLAG;
-    break;
-  case FIELD_TYPE_LONGLONG:
-    if (!length) new_field->length=MAX_BIGINT_WIDTH;
-    allowed_type_modifier= AUTO_INCREMENT_FLAG;
-    break;
-  case FIELD_TYPE_NULL:
-    break;
-  case FIELD_TYPE_NEWDECIMAL:
-    if (!length && !new_field->decimals)
-      new_field->length= 10;
-    if (new_field->length > DECIMAL_MAX_PRECISION)
-    {
-      my_error(ER_TOO_BIG_PRECISION, MYF(0), new_field->length, field_name,
-               DECIMAL_MAX_PRECISION);
-      DBUG_RETURN(NULL);
-    }
-    if (new_field->length < new_field->decimals)
-    {
-      my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name);
-      DBUG_RETURN(NULL);
-    }
-    new_field->length-      my_decimal_precision_to_length(new_field->length, \
                new_field->decimals,
-                                     type_modifier & UNSIGNED_FLAG);
-    new_field->pack_length-      my_decimal_get_binary_size(new_field->length, \
                new_field->decimals);
-    break;
-  case MYSQL_TYPE_VARCHAR:
-    /*
-      Long VARCHAR's are automaticly converted to blobs in mysql_prepare_table
-      if they don't have a default value
-    */
-    max_field_charlength= MAX_FIELD_VARCHARLENGTH;
-    break;
-  case MYSQL_TYPE_STRING:
-    break;
-  case FIELD_TYPE_BLOB:
-  case FIELD_TYPE_TINY_BLOB:
-  case FIELD_TYPE_LONG_BLOB:
-  case FIELD_TYPE_MEDIUM_BLOB:
-  case FIELD_TYPE_GEOMETRY:
-    if (default_value)				// Allow empty as default value
-    {
-      String str,*res;
-      resÞfault_value->val_str(&str);
-      if (res->length())
-      {
-	my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0),
-                 field_name); /* purecov: inspected */
-	DBUG_RETURN(NULL);
-      }
-      new_field->def=0;
-    }
-    new_field->flags|=BLOB_FLAG;
-    break;
-  case FIELD_TYPE_YEAR:
-    if (!length || new_field->length != 2)
-      new_field->length=4;			// Default length
-    new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
-    break;
-  case FIELD_TYPE_FLOAT:
-    /* change FLOAT(precision) to FLOAT or DOUBLE */
-    allowed_type_modifier= AUTO_INCREMENT_FLAG;
-    if (length && !decimals)
-    {
-      uint tmp_length=new_field->length;
-      if (tmp_length > PRECISION_FOR_DOUBLE)
-      {
-	my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name);
-	DBUG_RETURN(NULL);
-      }
-      else if (tmp_length > PRECISION_FOR_FLOAT)
-      {
-	new_field->sql_type=FIELD_TYPE_DOUBLE;
-	new_field->lengthÛL_DIG+7;			// -[digits].E+###
-      }
-      else
-	new_field->length=FLT_DIG+6;			// -[digits].E+##
-      new_field->decimals= NOT_FIXED_DEC;
-      break;
-    }
-    if (!length && !decimals)
-    {
-      new_field->length =  FLT_DIG+6;
-      new_field->decimals= NOT_FIXED_DEC;
-    }
-    if (new_field->length < new_field->decimals &&
-        new_field->decimals != NOT_FIXED_DEC)
-    {
-      my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name);
-      DBUG_RETURN(NULL);
-    }
-    break;
-  case FIELD_TYPE_DOUBLE:
-    allowed_type_modifier= AUTO_INCREMENT_FLAG;
-    if (!length && !decimals)
-    {
-      new_field->length = DBL_DIG+7;
-      new_field->decimals=NOT_FIXED_DEC;
-    }
-    if (new_field->length < new_field->decimals &&
-        new_field->decimals != NOT_FIXED_DEC)
-    {
-      my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name);
-      DBUG_RETURN(NULL);
-    }
-    break;
-  case FIELD_TYPE_TIMESTAMP:
-    if (!length)
-      new_field->length= 14;			// Full date YYYYMMDDHHMMSS
-    else if (new_field->length != 19)
-    {
-      /*
-        We support only even TIMESTAMP lengths less or equal than 14
-        and 19 as length of 4.1 compatible representation.
-      */
-      new_field->length=((new_field->length+1)/2)*2; /* purecov: inspected */
-      new_field->length= min(new_field->length,14); /* purecov: inspected */
-    }
-    new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
-    if (default_value)
-    {
-      /* Grammar allows only NOW() value for ON UPDATE clause */
-      if (default_value->type() == Item::FUNC_ITEM &&
-          ((Item_func*)default_value)->functype() == Item_func::NOW_FUNC)
-      {
-        new_field->unireg_check= (on_update_value?Field::TIMESTAMP_DNUN_FIELD:
-                                                  Field::TIMESTAMP_DN_FIELD);
-        /*
-          We don't need default value any longer moreover it is dangerous.
-          Everything handled by unireg_check further.
-        */
-        new_field->def= 0;
-      }
-      else
-        new_field->unireg_check= (on_update_value?Field::TIMESTAMP_UN_FIELD:
-                                                  Field::NONE);
-    }
-    else
-    {
-      /*
-        If we have default TIMESTAMP NOT NULL column without explicit DEFAULT
-        or ON UPDATE values then for the sake of compatiblity we should treat
-        this column as having DEFAULT NOW() ON UPDATE NOW() (when we don't
-        have another TIMESTAMP column with auto-set option before this one)
-        or DEFAULT 0 (in other cases).
-        So here we are setting TIMESTAMP_OLD_FIELD only temporary, and will
-        replace this value by TIMESTAMP_DNUN_FIELD or NONE later when
-        information about all TIMESTAMP fields in table will be availiable.
-
-        If we have TIMESTAMP NULL column without explicit DEFAULT value
-        we treat it as having DEFAULT NULL attribute.
-      */
-      new_field->unireg_check= (on_update_value ?
-                                Field::TIMESTAMP_UN_FIELD :
-                                (new_field->flags & NOT_NULL_FLAG ?
-                                 Field::TIMESTAMP_OLD_FIELD:
-                                 Field::NONE));
-    }
-    break;
-  case FIELD_TYPE_DATE:				// Old date type
-    if (protocol_version != PROTOCOL_VERSION-1)
-      new_field->sql_type=FIELD_TYPE_NEWDATE;
-    /* fall trough */
-  case FIELD_TYPE_NEWDATE:
-    new_field->length;
-    break;
-  case FIELD_TYPE_TIME:
-    new_field->length;
-    break;
-  case FIELD_TYPE_DATETIME:
-    new_field->length;
-    break;
-  case FIELD_TYPE_SET:
-    {
-      if (interval_list->elements > sizeof(longlong)*8)
-      {
-	my_error(ER_TOO_BIG_SET, MYF(0), field_name); /* purecov: inspected */
-	DBUG_RETURN(NULL);
-      }
-      new_field->pack_length= get_set_pack_length(interval_list->elements);
-
-      List_iterator<String> it(*interval_list);
-      String *tmp;
-      while ((tmp= it++))
-        new_field->interval_list.push_back(tmp);
-      /*
-        Set fake length to 1 to pass the below conditions.
-        Real length will be set in mysql_prepare_table()
-        when we know the character set of the column
-      */
-      new_field->length= 1;
-      break;
-    }
-  case FIELD_TYPE_ENUM:
-    {
-      // Should be safe
-      new_field->pack_length= get_enum_pack_length(interval_list->elements);
-
-      List_iterator<String> it(*interval_list);
-      String *tmp;
-      while ((tmp= it++))
-        new_field->interval_list.push_back(tmp);
-      new_field->length= 1; // See comment for FIELD_TYPE_SET above.
-      break;
-   }
-  case MYSQL_TYPE_VAR_STRING:
-    DBUG_ASSERT(0);                             // Impossible
-    break;
-  case MYSQL_TYPE_BIT:
-    {
-      if (!length)
-        new_field->length= 1;
-      if (new_field->length > MAX_BIT_FIELD_LENGTH)
-      {
-        my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), field_name,
-                 MAX_BIT_FIELD_LENGTH);
-        DBUG_RETURN(NULL);
-      }
-      new_field->pack_length= (new_field->length + 7) / 8;
-      break;
-    }
-  case FIELD_TYPE_DECIMAL:
-    DBUG_ASSERT(0); /* Was obsolete */
-  }
-
-  if (!(new_field->flags & BLOB_FLAG) &&
-      ((new_field->length > max_field_charlength && type != FIELD_TYPE_SET &&
-        type != FIELD_TYPE_ENUM &&
-        (type != MYSQL_TYPE_VARCHAR || default_value)) ||
-       (!new_field->length &&
-        type != MYSQL_TYPE_STRING &&
-        type != MYSQL_TYPE_VARCHAR && type != FIELD_TYPE_GEOMETRY)))
-  {
-    my_error((type == MYSQL_TYPE_VAR_STRING || type == MYSQL_TYPE_VARCHAR ||
-              type == MYSQL_TYPE_STRING) ?  ER_TOO_BIG_FIELDLENGTH :
-             ER_TOO_BIG_DISPLAYWIDTH,
-             MYF(0),
-             field_name, max_field_charlength); /* purecov: inspected */
-    DBUG_RETURN(NULL);
-  }
-  type_modifier&= AUTO_INCREMENT_FLAG;
-  if ((~allowed_type_modifier) & type_modifier)
-  {
-    my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name);
-    DBUG_RETURN(NULL);
-  }
-  DBUG_RETURN(new_field);
-}
-

 /* Store position for column in ALTER TABLE .. ADD column */

@@ -6169,12 +5904,16 @@
   if (!table)
     DBUG_RETURN(0);				// End of memory
   alias_str= alias ? alias->str : table->table.str;
-  if (check_table_name(table->table.str,table->table.length) ||
-      table->db.str && check_db_name(table->db.str))
+  if (check_table_name(table->table.str,table->table.length))
   {
     my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str);
     DBUG_RETURN(0);
   }
+  if (table->db.str && check_db_name(table->db.str))
+  {
+    my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str);
+    DBUG_RETURN(0);
+  }

   if (!alias)					/* Alias is case sensitive */
   {
@@ -6547,36 +6286,39 @@


 /*
-  Create a new name resolution context for a JOIN ... ON clause.
+  Push a new name resolution context for a JOIN ... ON clause to the
+  context stack of a query block.

   SYNOPSIS
-    make_join_on_context()
+    push_new_name_resolution_context()
     thd       pointer to current thread
     left_op   left  operand of the JOIN
     right_op  rigth operand of the JOIN

   DESCRIPTION
     Create a new name resolution context for a JOIN ... ON clause,
-    and set the first and last leaves of the list of table references
-    to be used for name resolution.
+    set the first and last leaves of the list of table references
+    to be used for name resolution, and push the newly created
+    context to the stack of contexts of the query.

   RETURN
-    A new context if all is OK
-    NULL - if a memory allocation error occured
+    FALSE  if all is OK
+    TRUE   if a memory allocation error occured
 */

-Name_resolution_context *
-make_join_on_context(THD *thd, TABLE_LIST *left_op, TABLE_LIST *right_op)
+bool
+push_new_name_resolution_context(THD *thd,
+                                 TABLE_LIST *left_op, TABLE_LIST *right_op)
 {
   Name_resolution_context *on_context;
   if (!(on_context= new (thd->mem_root) Name_resolution_context))
-    return NULL;
+    return TRUE;
   on_context->init();
   on_context->first_name_resolution_table     \
left_op->first_leaf_for_name_resolution();  on_context->last_name_resolution_table    \
                right_op->last_leaf_for_name_resolution();
-  return on_context;
+  return thd->lex->push_context(on_context);
 }


@@ -6702,7 +6444,10 @@
       allocate temporary THD for execution of acl_reload()/grant_reload().
     */
     if (!thd && (thd= (tmp_thd= new THD)))
+    {
+      thd->thread_stack= (char*) &tmp_thd;
       thd->store_globals();
+    }
     if (thd)
     {
       (void)acl_reload(thd);

--- 1.3/mysql-test/r/schema.result	2005-11-24 09:59:01 +01:00
+++ 1.4/mysql-test/r/schema.result	2005-12-29 20:48:07 +01:00
@@ -1,3 +1,4 @@
+drop database if exists mysqltest1;
 create schema foo;
 show create schema foo;
 Database	Create Database

--- 1.91/mysql-test/r/information_schema.result	2005-12-01 08:08:05 +01:00
+++ 1.92/mysql-test/r/information_schema.result	2005-12-29 20:48:06 +01:00
@@ -14,6 +14,7 @@
 select schema_name from information_schema.schemata;
 schema_name
 information_schema
+cluster_replication
 mysql
 test
 show databases like 't%';
@@ -22,6 +23,7 @@
 show databases;
 Database
 information_schema
+cluster_replication
 mysql
 test
 show databases where `database` = 't%';
@@ -34,7 +36,7 @@
 create table t5 (id int auto_increment primary key);
 insert into t5 values (10);
 create view v1 (c) as select table_name from information_schema.TABLES;
-select * from v1;
+select * from v1 where c not in ('apply_status');
 c
 CHARACTER_SETS
 COLLATIONS
@@ -52,6 +54,7 @@
 TRIGGERS
 VIEWS
 USER_PRIVILEGES
+binlog_index
 columns_priv
 db
 func
@@ -327,6 +330,7 @@
 select * from v0;
 c
 information_schema
+cluster_replication
 mysql
 test
 explain select * from v0;
@@ -793,8 +797,9 @@
 flush privileges;
 SELECT table_schema, count(*) FROM information_schema.TABLES GROUP BY TABLE_SCHEMA;
 table_schema	count(*)
-information_schema	16
-mysql	18
+cluster_replication	<count>
+information_schema	<count>
+mysql	<count>
 create table t1 (i int, j int);
 create trigger trg1 before insert on t1 for each row
 begin

--- 1.63/mysql-test/t/information_schema.test	2005-11-24 09:59:01 +01:00
+++ 1.64/mysql-test/t/information_schema.test	2005-12-29 20:48:08 +01:00
@@ -741,3 +741,13 @@
 select data_type, character_octet_length, character_maximum_length
   from information_schema.columns where table_name='t1';
 drop table t1;
+
+#
+# Bug#14476 `information_schema`.`TABLES`.`TABLE_TYPE` with empty value
+#
+create table t1 (f1 int(11));
+create view v1 as select * from t1;
+drop table t1;
+select table_type from information_schema.tables
+where table_name="v1";
+drop view v1;

--- 1.43/mysql-test/mysql-test-run.pl	2005-11-24 09:59:01 +01:00
+++ 1.44/mysql-test/mysql-test-run.pl	2005-12-29 20:48:06 +01:00
@@ -96,6 +96,7 @@
 require "lib/mtr_diff.pl";
 require "lib/mtr_match.pl";
 require "lib/mtr_misc.pl";
+require "lib/mtr_stress.pl";

 $Devel::Trace::TRACE= 1;

@@ -174,6 +175,7 @@
 our $exe_mysqld;
 our $exe_mysqlcheck;             # Called from test case
 our $exe_mysqldump;              # Called from test case
+our $exe_mysqlslap;              # Called from test case
 our $exe_mysqlimport;              # Called from test case
 our $exe_mysqlshow;              # Called from test case
 our $exe_mysql_fix_system_tables;
@@ -274,6 +276,16 @@
 our $opt_valgrind_all;
 our $opt_valgrind_options;

+our $opt_stress=               "";
+our $opt_stress_suite=     "main";
+our $opt_stress_mode=    "random";
+our $opt_stress_threads=        5;
+our $opt_stress_test_count=     0;
+our $opt_stress_loop_count=     0;
+our $opt_stress_test_duration=  0;
+our $opt_stress_init_file=     "";
+our $opt_stress_test_file=     "";
+
 our $opt_verbose;

 our $opt_wait_for_master;
@@ -401,6 +413,10 @@
   {
     run_benchmarks(shift);      # Shift what? Extra arguments?!
   }
+  elsif ( $opt_stress )
+  {
+    run_stress_test()
+  }
   else
   {
     run_tests();
@@ -564,6 +580,17 @@
              'valgrind-all:s'           => \$opt_valgrind_all,
              'valgrind-options=s'       => \$opt_valgrind_options,

+             # Stress testing
+             'stress'                   => \$opt_stress,
+             'stress-suite=s'           => \$opt_stress_suite,
+             'stress-threads=i'         => \$opt_stress_threads,
+             'stress-test-file=s'       => \$opt_stress_test_file,
+             'stress-init-file=s'       => \$opt_stress_init_file,
+             'stress-mode=s'            => \$opt_stress_mode,
+             'stress-loop-count=i'      => \$opt_stress_loop_count,
+             'stress-test-count=i'      => \$opt_stress_test_count,
+             'stress-test-duration=i'   => \$opt_stress_test_duration,
+
              # Misc
              'big-test'                 => \$opt_big_test,
              'debug'                    => \$opt_debug,
@@ -936,16 +963,22 @@
                                            "$path_client_bindir/mysqld-debug",);
       $path_language=      mtr_path_exists("$glob_basedir/share/english/");
       $path_charsetsdir=   mtr_path_exists("$glob_basedir/share/charsets");
+
+      $exe_my_print_defaults+        \
mtr_exe_exists("$path_client_bindir/my_print_defaults");  }
     else
     {
       $path_client_bindir= mtr_path_exists("$glob_basedir/client");
       $exe_mysqld=         mtr_exe_exists ("$glob_basedir/sql/mysqld");
+      $exe_mysqlslap=      mtr_exe_exists ("$path_client_bindir/mysqlslap");
       $path_language=      mtr_path_exists("$glob_basedir/sql/share/english/");
       $path_charsetsdir=   mtr_path_exists("$glob_basedir/sql/share/charsets");

       $exe_im= mtr_exe_exists(
         "$glob_basedir/server-tools/instance-manager/mysqlmanager");
+      $exe_my_print_defaults+        \
mtr_exe_exists("$glob_basedir/extra/my_print_defaults");  }

     if ( $glob_use_embedded_server )
@@ -972,8 +1005,6 @@
     $exe_mysql=          mtr_exe_exists("$path_client_bindir/mysql");
     $exe_mysql_fix_system_tables       \
                mtr_script_exists("$glob_basedir/scripts/mysql_fix_privilege_tables");
                
-    $exe_my_print_defaults-      \
                mtr_script_exists("$glob_basedir/extra/my_print_defaults");
     $path_ndb_tools_dir= mtr_path_exists("$glob_basedir/storage/ndb/tools");
     $exe_ndb_mgm=        "$glob_basedir/storage/ndb/src/mgmclient/ndb_mgm";
   }
@@ -991,7 +1022,7 @@
       mtr_script_exists("$path_client_bindir/mysql_fix_privilege_tables",
 			"$glob_basedir/scripts/mysql_fix_privilege_tables");
     $exe_my_print_defaults-      \
mtr_script_exists("$path_client_bindir/my_print_defaults"); +      \
mtr_exe_exists("$path_client_bindir/my_print_defaults");

     $path_language=      mtr_path_exists("$glob_basedir/share/mysql/english/",
                                          "$glob_basedir/share/english/");
@@ -1008,6 +1039,7 @@
     {
       $exe_mysqld=         mtr_exe_exists ("$glob_basedir/libexec/mysqld",
                                            "$glob_basedir/bin/mysqld");
+      $exe_mysqlslap=      mtr_exe_exists("$path_client_bindir/mysqlslap");
     }
     $exe_im= mtr_exe_exists("$glob_basedir/libexec/mysqlmanager",
                             "$glob_basedir/bin/mysqlmanager");
@@ -2128,6 +2160,7 @@
   mtr_add_arg($args, "%s--character-sets-dir=%s", $prefix, $path_charsetsdir);
   mtr_add_arg($args, "%s--core", $prefix);
   mtr_add_arg($args, "%s--log-bin-trust-function-creators", $prefix);
+  mtr_add_arg($args, "%s--loose-binlog-show-xid=0", $prefix);
   mtr_add_arg($args, "%s--default-character-set=latin1", $prefix);
   mtr_add_arg($args, "%s--language=%s", $prefix, $path_language);
   mtr_add_arg($args, "%s--tmpdir=$opt_tmpdir", $prefix);
@@ -2261,6 +2294,7 @@
   mtr_add_arg($args, "%s--sort_buffer%6K", $prefix);
   mtr_add_arg($args, "%s--max_heap_table_size=1M", $prefix);
   mtr_add_arg($args, "%s--log-bin-trust-function-creators", $prefix);
+  mtr_add_arg($args, "%s--loose-binlog-show-xid=0", $prefix);

   if ( $opt_ssl_supported )
   {
@@ -2596,6 +2630,21 @@
     $cmdline_mysqldump .       " --debug=d:t:A,$opt_vardir/log/mysqldump.trace";
   }
+
+  my $cmdline_mysqlslap;
+
+  unless ( $glob_win32 )
+  {
+    $cmdline_mysqlslap= "$exe_mysqlslap -uroot " .
+                         "--port=$master->[0]->{'path_myport'} " .
+                         "--socket=$master->[0]->{'path_mysock'} --password=";
+    if ( $opt_debug )
+    {
+      $cmdline_mysqlslap .+        " --debug=d:t:A,$opt_vardir/log/mysqldump.trace";
+    }
+  }
+
   my $cmdline_mysqlimport= "$exe_mysqlimport -uroot " .
                          "--port=$master->[0]->{'path_myport'} " .
                          "--socket=$master->[0]->{'path_mysock'} --password=";
@@ -2657,6 +2706,7 @@
   $ENV{'MYSQL'}=                    $cmdline_mysql;
   $ENV{'MYSQL_CHECK'}=              $cmdline_mysqlcheck;
   $ENV{'MYSQL_DUMP'}=               $cmdline_mysqldump;
+  $ENV{'MYSQL_SLAP'}=               $cmdline_mysqlslap unless $glob_win32;
   $ENV{'MYSQL_IMPORT'}=             $cmdline_mysqlimport;
   $ENV{'MYSQL_DUMP_SLAVE'}=         $cmdline_mysqldumpslave;
   $ENV{'MYSQL_SHOW'}=               $cmdline_mysqlshow;

--- 1.16/mysql-test/r/mysqltest.result	2005-11-24 09:59:01 +01:00
+++ 1.17/mysql-test/r/mysqltest.result	2005-12-29 20:48:06 +01:00
@@ -390,3 +390,25 @@
 --------------------------------------------------------------------------------
 this will be executed
 this will be executed
+mysqltest: At line 3: query 'create table t1 (a int primary key);
+insert into t1 values (1);
+select 'select-me';
+insertz 'error query'' failed: 1064: You have an error in your SQL syntax; check the \
manual that corresponds to your MySQL server version for the right syntax to use near \
'insertz 'error query'' at line 1 +drop table t1;
+drop table t1;
+create table t1 (a int primary key);
+insert into t1 values (1);
+select 'select-me';
+insertz error query||||
+select-me
+select-me
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds \
to your MySQL server version for the right syntax to use near 'insertz error query' \
at line 1 +drop table t1;
+create table t1 (a int primary key);
+insert into t1 values (1);
+select 'select-me';
+insertz error query||||
+select-me
+select-me
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds \
to your MySQL server version for the right syntax to use near 'insertz error query' \
at line 1 +drop table t1;

--- 1.15/mysql-test/t/disabled.def	2005-11-24 09:59:01 +01:00
+++ 1.16/mysql-test/t/disabled.def	2005-12-29 21:16:17 +01:00
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-#  List the test cases that are to be disabled temporarely.
+#  List the test cases that are to be disabled temporarily.
 #
 #  Separate the test case name and the comment with ':'.
 #
@@ -11,16 +11,17 @@
 ##############################################################################

 sp-goto         : GOTO is currently is disabled - will be fixed in the future
-rpl_relayrotate : Unstable test case, bug#12429
-rpl_until       : Unstable test case, bug#12429
-rpl_deadlock    : Unstable test case, bug#12429
 kill            : Unstable test case, bug#9712
-rpl_row_mystery22:shows rbr slave issues Bug 12418
-rpl_row_relayrotate:Bug 14082
-rpl_row_000002:create table from temporary Bug 12345
-rpl_row_sp006_InnoDB:Bug 12586
-rpl_row_view01:Bug 12687
-rpl_row_NOW:Bug 12574
-rpl_bit_npk:Bug 13418
-compress        : Magnus will fix
-rpl_server_id2:fails in wl2325
+rpl_bit_npk     : Bug #13418
+ndb_cache2      : Bug #15004
+ndb_cache_multi2: Bug #15004
+func_group      : Bug #15448
+func_math       : Bug #15448
+group_min_max   : Bug #15448
+#mysqlslap       : Bug #15483
+innodb_concurrent : Results are not deterministic, Elliot will fix (BUG#3300)
+subselect       : Bug#15706
+type_time       : Bug#15805
+rpl000002       : Bug#15920 Temporary tables are not binlogged in SBR
+ps_7ndb         : Bug#15923 Core dump in RBR mode when executing test suite
+sp_trans        : Bug#15924 Code dump in RBR mode when executing test suite

--- 1.20/mysql-test/t/mysqltest.test	2005-11-24 09:59:01 +01:00
+++ 1.21/mysql-test/t/mysqltest.test	2005-12-29 20:48:08 +01:00
@@ -942,3 +942,53 @@
 --enable_parsing
 select "this will be executed";
 --enable_query_log
+
+
+#
+# Bug #11731 mysqltest in multi-statement queries ignores errors in
+#            non-1st queries
+#
+
+# Failing multi statement query
+# PS does not support multi statement
+--exec echo "--disable_ps_protocol"                    > var/tmp/bug11731.sql
+--exec echo "delimiter ||||;"                         >> var/tmp/bug11731.sql
+--exec echo "create table t1 (a int primary key);"    >> var/tmp/bug11731.sql
+--exec echo "insert into t1 values (1);"              >> var/tmp/bug11731.sql
+--exec echo "select 'select-me';"                     >> var/tmp/bug11731.sql
+--exec echo "insertz 'error query'||||"               >> var/tmp/bug11731.sql
+--exec echo "delimiter ;||||"                         >> var/tmp/bug11731.sql
+
+--error 1
+--exec $MYSQL_TEST -x $MYSQL_TEST_DIR/var/tmp/bug11731.sql 2>&1
+drop table t1;
+
+--error 1
+--exec $MYSQL_TEST --record -x $MYSQL_TEST_DIR/var/tmp/bug11731.sql -R \
$MYSQL_TEST_DIR/var/tmp/bug11731.out +# The .out file should be empty
+--error 1
+--exec test -s $MYSQL_TEST_DIR/var/tmp/bug11731.out
+drop table t1;
+
+
+# Using expected error
+# PS does not support multi statement
+--exec echo "--disable_ps_protocol"                    > var/tmp/bug11731.sql
+--exec echo "delimiter ||||;"                         >> var/tmp/bug11731.sql
+--exec echo "--error 1064"                            >> var/tmp/bug11731.sql
+--exec echo "create table t1 (a int primary key);"    >> var/tmp/bug11731.sql
+--exec echo "insert into t1 values (1);"              >> var/tmp/bug11731.sql
+--exec echo "select 'select-me';"                     >> var/tmp/bug11731.sql
+--exec echo "insertz "error query"||||"               >> var/tmp/bug11731.sql
+--exec echo "delimiter ;||||"                         >> var/tmp/bug11731.sql
+
+# These two should work since the error is expected
+--exec $MYSQL_TEST -x $MYSQL_TEST_DIR/var/tmp/bug11731.sql  2>&1
+drop table t1;
+
+--exec $MYSQL_TEST --record -x $MYSQL_TEST_DIR/var/tmp/bug11731.sql -R \
$MYSQL_TEST_DIR/var/tmp/bug11731.out +--exec cat $MYSQL_TEST_DIR/var/tmp/bug11731.out
+drop table t1;
+
+
+

--- 1.5/mysql-test/t/count_distinct3.test	2005-11-24 09:59:01 +01:00
+++ 1.6/mysql-test/t/count_distinct3.test	2005-12-29 20:48:07 +01:00
@@ -19,7 +19,7 @@
   SET @rnd= RAND();
   SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
   SET @id_rev= @rnd_max - @id;
-  SET @grp= CAST(128.0 * @rnd AS UNSIGNED);
+  SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
   INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
   dec $1;
 }

--- 1.35/mysql-test/r/ndb_alter_table.result	2005-11-29 22:53:42 +01:00
+++ 1.36/mysql-test/r/ndb_alter_table.result	2005-12-29 20:48:07 +01:00
@@ -180,19 +180,14 @@
 alter table t1 drop index c;
 select * from t1 where b = 'two';
 ERROR HY000: Can't lock file (errno: 159)
-select * from t1 where b = 'two';
-a	b	c
-2	two	two
 drop table t1;
 create table t3 (a int primary key) engine=ndbcluster;
 begin;
 insert into t3 values (1);
 alter table t3 rename t4;
-delete from t3;
-insert into t3 values (1);
 commit;
 select * from t3;
-ERROR HY000: Can't lock file (errno: 155)
+ERROR 42S02: Table 'test.t3' doesn't exist
 select * from t4;
 a
 1

--- 1.29/mysql-test/t/ndb_alter_table.test	2005-11-28 20:07:09 +01:00
+++ 1.30/mysql-test/t/ndb_alter_table.test	2005-12-29 20:48:08 +01:00
@@ -155,7 +155,6 @@
 # This should fail since index information is not automatically refreshed
 --error 1015
 select * from t1 where b = 'two';
-select * from t1 where b = 'two';
 connection server1;
 drop table t1;
 --enable_ps_protocol
@@ -194,13 +193,15 @@
 alter table t3 rename t4;

 connection server2;
-# This should work as transaction is ongoing...
-delete from t3;
-insert into t3 values (1);
+# with rbr the below will not work as the "alter" event
+# explicitly invalidates the dictionary cache.
+## This should work as transaction is ongoing...
+#delete from t3;
+#insert into t3 values (1);
 commit;

 # This should fail as its a new transaction
---error 1015
+--error 1146
 select * from t3;
 select * from t4;
 drop table t4;

--- 1.27/storage/ndb/src/kernel/blocks/backup/Backup.cpp	2005-11-25 08:41:16 +01:00
+++ 1.28/storage/ndb/src/kernel/blocks/backup/Backup.cpp	2005-12-29 20:48:15 +01:00
@@ -802,13 +802,17 @@
       pos= &ref->nodeId - signal->getDataPtr();
       break;
     }
+    case GSN_WAIT_GCP_REQ:
+    case GSN_DROP_TRIG_REQ:
     case GSN_CREATE_TRIG_REQ:
     case GSN_ALTER_TRIG_REQ:
-    case GSN_WAIT_GCP_REQ:
+      ptr.p->setErrorCode(AbortBackupOrd::BackupFailureDueToNodeFail);
+      return;
     case GSN_UTIL_SEQUENCE_REQ:
     case GSN_UTIL_LOCK_REQ:
-    case GSN_DROP_TRIG_REQ:
       return;
+    default:
+      ndbrequire(false);
     }

     for(Uint32 i = 0; (i = mask.find(i+1)) != NdbNodeBitmask::NotFound; )
@@ -1821,7 +1825,7 @@
   const Uint32 nodeId = refToNode(signal->senderBlockRef());
   const Uint32 noOfBytes = conf->noOfBytes;
   const Uint32 noOfRecords = conf->noOfRecords;
-
+
   BackupRecordPtr ptr;
   c_backupPool.getPtr(ptr, ptrI);

@@ -1898,7 +1902,7 @@
       }
     }
   }
-  ndbrequire(false);
+  goto err;

 done:
   ptr.p->masterData.sendCounter--;
@@ -1910,7 +1914,8 @@
     masterAbort(signal, ptr);
     return;
   }//if
-
+
+err:
   AbortBackupOrd *ord = (AbortBackupOrd*)signal->getDataPtrSend();
   ord->backupId = ptr.p->backupId;
   ord->backupPtr = ptr.i;

--- 1.225/sql/ha_ndbcluster.cc	2005-12-20 20:53:23 +01:00
+++ 1.226/sql/ha_ndbcluster.cc	2005-12-29 21:16:17 +01:00
@@ -33,6 +33,11 @@
 #include <../util/Bitmask.hpp>
 #include <ndbapi/NdbIndexStat.hpp>

+#ifdef HAVE_NDB_BINLOG
+#include "rpl_injector.h"
+#include "slave.h"
+#endif
+
 // options from from mysqld.cc
 extern my_bool opt_ndb_optimized_node_selection;
 extern const char *opt_ndbcluster_connectstring;
@@ -127,7 +132,7 @@
 typedef NdbDictionary::Table NDBTAB;
 typedef NdbDictionary::Index  NDBINDEX;
 typedef NdbDictionary::Dictionary  NDBDICT;
-typedef NdbDictionary::Event  NDBEVENT;
+typedef NdbDictionary::Event NDBEVENT;

 static int ndbcluster_inited= 0;
 static int ndbcluster_util_inited= 0;
@@ -146,6 +151,11 @@
 static NDB_SHARE *get_share(const char *key,
                             bool create_if_not_exists= TRUE,
                             bool have_lock= FALSE);
+#ifdef HAVE_NDB_BINLOG
+/* you should have lock on ndbcluster_mutex when calling */
+static int handle_trailing_share(NDB_SHARE *share);
+static int rename_share(NDB_SHARE *share, const char *new_key);
+#endif
 static void free_share(NDB_SHARE **share, bool have_lock= FALSE);
 static void real_free_share(NDB_SHARE **share);
 static void ndb_set_fragmentation(NDBTAB &tab, TABLE *table, uint pk_len);
@@ -197,6 +207,75 @@
 */
 static uint32 dummy_buf;

+#ifdef HAVE_NDB_BINLOG
+#define INJECTOR_EVENT_LEN 200
+/* NDB Injector thread (used for binlog creation) */
+ulong ndb_report_thresh_binlog_epoch_slip;
+ulong ndb_report_thresh_binlog_mem_usage;
+static ulonglong ndb_latest_applied_binlog_epoch= 0;
+static ulonglong ndb_latest_handled_binlog_epoch= 0;
+static ulonglong ndb_latest_received_binlog_epoch= 0;
+static pthread_t ndb_binlog_thread;
+static int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key,
+                                          const char *db,
+                                          const char *table_name,
+                                          bool do_binlog,
+                                          NDB_SHARE *share= 0);
+static int ndbcluster_create_event(Ndb *ndb, const NDBTAB *table,
+                                   const char *event_name, NDB_SHARE *share);
+static int ndbcluster_create_event_ops(NDB_SHARE *share,
+                                       const NDBTAB *ndbtab,
+                                       const char *event_name);
+static int ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name,
+                                        NDB_SHARE *share);
+static void ndb_rep_event_name(String *event_name,
+                               const char *db, const char *tbl);
+#ifndef DBUG_OFF
+static void dbug_print_table(const char *info, TABLE *table);
+#endif
+static int ndbcluster_binlog_start();
+pthread_handler_t ndb_binlog_thread_func(void *arg);
+
+/*
+  Mutex and condition used for interacting between client sql thread
+  and injector thread
+*/
+pthread_mutex_t injector_mutex;
+pthread_cond_t  injector_cond;
+/*
+  Flag showing if the ndb injector thread is running, if so == 1
+*/
+static int ndb_binlog_thread_running= 0;
+
+/*
+  table cluster_replication.apply_status
+*/
+static int ndbcluster_create_apply_status_table(THD *thd);
+static NDB_SHARE *ndbcluster_check_apply_status_share();
+static NDB_SHARE *ndbcluster_get_apply_status_share();
+static NDB_SHARE *apply_status_share= 0;
+
+/*
+  Global reference to the ndb injector thread THD oject
+
+  Has one sole purpose, for setting the in_use table member variable
+  in get_share(...)
+*/
+static THD *injector_thd= 0;
+
+/*
+  Global reference to ndb injector thd object.
+
+  Used mainly by the binlog index thread, but exposed to the client sql
+  thread for one reason; to setup the events operations for a table
+  to enable ndb injector thread receiving events.
+
+  Must therefore always be used with a surrounding
+  pthread_mutex_lock(&injector_mutex), when doing create/dropEventOperation
+*/
+static Ndb *injector_ndb= 0;
+#endif /* HAVE_NDB_BINLOG */
+
 /*
   Stats that can be retrieved from ndb
 */
@@ -2755,6 +2834,7 @@
   statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status);
   DBUG_ENTER("ha_ndbcluster::index_read_idx");
   DBUG_PRINT("enter", ("index_no: %u, key_len: %u", index_no, key_len));
+  close_scan();
   index_init(index_no, 0);
   DBUG_RETURN(index_read(buf, key, key_len, find_flag));
 }
@@ -3151,6 +3231,16 @@
     m_use_write= FALSE;
     m_ignore_dup_key= FALSE;
     break;
+  case HA_EXTRA_IGNORE_NO_KEY:
+    DBUG_PRINT("info", ("HA_EXTRA_IGNORE_NO_KEY"));
+    DBUG_PRINT("info", ("Turning on AO_IgnoreError at Commit/NoCommit"));
+    m_ignore_no_key= TRUE;
+    break;
+  case HA_EXTRA_NO_IGNORE_NO_KEY:
+    DBUG_PRINT("info", ("HA_EXTRA_NO_IGNORE_NO_KEY"));
+    DBUG_PRINT("info", ("Turning on AO_IgnoreError at Commit/NoCommit"));
+    m_ignore_no_key= FALSE;
+    break;
   default:
     break;
   }
@@ -3976,6 +4066,11 @@
     */
     if ((my_errno= write_ndb_file()))
       DBUG_RETURN(my_errno);
+#ifdef HAVE_NDB_BINLOG
+    ndbcluster_create_binlog_setup(get_ndb(), name2, m_dbname, m_tabname,
+                                   ndb_binlog_thread_running > 0 &&
+                                   !is_prefix(m_tabname, tmp_file_prefix));
+#endif /* HAVE_NDB_BINLOG */
     DBUG_RETURN(my_errno);
   }

@@ -4122,6 +4217,69 @@
   if (!my_errno)
     my_errno= write_ndb_file();

+#ifdef HAVE_NDB_BINLOG
+  if (!my_errno)
+  {
+    NDB_SHARE *share= 0;
+    pthread_mutex_lock(&ndbcluster_mutex);
+    /*
+      First make sure we get a "fresh" share here, not an old trailing one...
+    */
+    {
+      const char *key= name2;
+      uint length= (uint) strlen(key);
+      if ((share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
+                                           (byte*) key, length)))
+        handle_trailing_share(share);
+    }
+    /*
+      get a new share
+    */
+    if (!(share= get_share(name2, true, true)))
+    {
+      sql_print_error("NDB: allocating table share for %s failed", name2);
+      /* my_errno is set */
+    }
+    pthread_mutex_unlock(&ndbcluster_mutex);
+
+    while (!is_prefix(m_tabname, tmp_file_prefix))
+    {
+      const NDBTAB *t= dict->getTable(m_tabname);
+      String event_name(INJECTOR_EVENT_LEN);
+      ndb_rep_event_name(&event_name,m_dbname,m_tabname);
+
+      /*
+        Always create an event for the table, as other mysql servers
+        expect it to be there.
+      */
+      if (ndbcluster_create_event(ndb, t, event_name.c_ptr(), share) < 0)
+      {
+        /* this is only a serious error if the binlog is on */
+	if (share && ndb_binlog_thread_running > 0)
+	{
+          push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+                              ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+                              "Creating event for logging table failed. "
+                              "See error log for details.");
+	}
+        break;
+      }
+      sql_print_information("NDB Binlog: CREATE TABLE Event: %s",
+                            event_name.c_ptr());
+
+      if (share && ndb_binlog_thread_running > 0 &&
+          ndbcluster_create_event_ops(share, t, event_name.c_ptr()) < 0)
+      {
+        sql_print_error("NDB Binlog: FAILED CREATE TABLE event operations."
+                        " Event: %s", name2);
+        /* a warning has been issued to the client */
+        break;
+      }
+      break;
+    }
+  }
+#endif /* HAVE_NDB_BINLOG */
+
   DBUG_RETURN(my_errno);
 }

@@ -4216,6 +4374,15 @@
     if (!(orig_tab= dict->getTable(m_tabname)))
       ERR_RETURN(dict->getNdbError());
   }
+#ifdef HAVE_NDB_BINLOG
+  NDB_SHARE *share= 0;
+  if (ndb_binlog_thread_running > 0 &&
+      (share= get_share(from, false)))
+  {
+    int r= rename_share(share, to);
+    DBUG_ASSERT(r == 0);
+  }
+#endif
   m_table= (void *)orig_tab;
   // Change current database to that of target table
   set_dbname(to);
@@ -4223,6 +4390,14 @@

   if ((result= alter_table_name(new_tabname)))
   {
+#ifdef HAVE_NDB_BINLOG
+    if (share)
+    {
+      int r= rename_share(share, from);
+      DBUG_ASSERT(r == 0);
+      free_share(&share);
+    }
+#endif
     DBUG_RETURN(result);
   }

@@ -4230,9 +4405,61 @@
   if ((result= handler::rename_table(from, to)))
   {
     // ToDo in 4.1 should rollback alter table...
+#ifdef HAVE_NDB_BINLOG
+    if (share)
+      free_share(&share);
+#endif
     DBUG_RETURN(result);
   }

+#ifdef HAVE_NDB_BINLOG
+  if (share && share->op)
+    dict->forceGCPWait();
+
+  /* handle old table */
+  if (!is_prefix(m_tabname, tmp_file_prefix))
+  {
+    String event_name(INJECTOR_EVENT_LEN);
+    ndb_rep_event_name(&event_name, from + sizeof(share_prefix) - 1, 0);
+    ndbcluster_handle_drop_table(ndb, event_name.c_ptr(), share);
+  }
+
+  if (!result && !is_prefix(new_tabname, tmp_file_prefix))
+  {
+    /* always create an event for the table */
+    String event_name(INJECTOR_EVENT_LEN);
+    ndb_rep_event_name(&event_name, to + sizeof(share_prefix) - 1, 0);
+    const NDBTAB *ndbtab= dict->getTable(new_tabname);
+
+    if (ndbcluster_create_event(ndb, ndbtab, event_name.c_ptr(), share) >= 0)
+    {
+      sql_print_information("NDB Binlog: RENAME Event: %s",
+                            event_name.c_ptr());
+      if (share)
+      {
+        if (ndbcluster_create_event_ops(share, ndbtab,
+                                        event_name.c_ptr()) < 0)
+        {
+          sql_print_error("NDB Binlog: FAILED create event operations "
+                          "during RENAME. Event %s", event_name.c_ptr());
+          /* a warning has been issued to the client */
+        }
+      }
+    }
+    else
+    {
+      sql_print_error("NDB Binlog: FAILED create event during RENAME."
+                      "Event: %s", event_name.c_ptr());
+      push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+                          ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+                          "Creating event for logging table failed. "
+                          "See error log for details.");
+    }
+  }
+  if (share)
+    free_share(&share);
+#endif
+
   DBUG_RETURN(result);
 }

@@ -4275,6 +4502,9 @@
 {
   DBUG_ENTER("ha_ndbcluster::ndbcluster_delete_table");
   NDBDICT *dict= ndb->getDictionary();
+#ifdef HAVE_NDB_BINLOG
+  NDB_SHARE *share= get_share(path, false);
+#endif

   /* Drop the table from NDB */

@@ -4291,9 +4521,66 @@

   if (res)
   {
+#ifdef HAVE_NDB_BINLOG
+    /* the drop table failed for some reason, drop the share anyways */
+    if (share)
+    {
+      pthread_mutex_lock(&ndbcluster_mutex);
+      if (share->state != NSS_DROPPED)
+      {
+        /*
+          The share kept by the server has not been freed, free it
+        */
+        share->state= NSS_DROPPED;
+        free_share(&share, TRUE);
+      }
+      /* free the share taken above */
+      free_share(&share, TRUE);
+      pthread_mutex_unlock(&ndbcluster_mutex);
+    }
+#endif
     DBUG_RETURN(res);
   }

+#ifdef HAVE_NDB_BINLOG
+  /* stop the logging of the dropped table, and cleanup */
+
+  /*
+    drop table is successful even if table does not exist in ndb
+    and in case table was actually not dropped, there is no need
+    to force a gcp, and setting the event_name to null will indicate
+    that there is no event to be dropped
+  */
+  int table_dropped= dict->getNdbError().code != 709;
+
+  if (table_dropped && share && share->op)
+    dict->forceGCPWait();
+
+  if (!is_prefix(table_name, tmp_file_prefix))
+  {
+    String event_name(INJECTOR_EVENT_LEN);
+    ndb_rep_event_name(&event_name, path + sizeof(share_prefix) - 1, 0);
+    ndbcluster_handle_drop_table(ndb,
+                                 table_dropped ? event_name.c_ptr() : 0,
+                                 share);
+  }
+
+  if (share)
+  {
+    pthread_mutex_lock(&ndbcluster_mutex);
+    if (share->state != NSS_DROPPED)
+    {
+      /*
+        The share kept by the server has not been freed, free it
+      */
+      share->state= NSS_DROPPED;
+      free_share(&share, TRUE);
+    }
+    /* free the share taken above */
+    free_share(&share, TRUE);
+    pthread_mutex_unlock(&ndbcluster_mutex);
+  }
+#endif
   DBUG_RETURN(0);
 }

@@ -4382,7 +4669,8 @@
                 HA_NO_PREFIX_CHAR_KEYS | \
                 HA_NEED_READ_RANGE_BUFFER | \
                 HA_CAN_GEOMETRY | \
-                HA_CAN_BIT_FIELD
+                HA_CAN_BIT_FIELD | \
+                HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS

 ha_ndbcluster::ha_ndbcluster(TABLE_SHARE *table_arg):
   handler(&ndbcluster_hton, table_arg),
@@ -4865,6 +5153,32 @@
         }
         pthread_mutex_unlock(&LOCK_open);
       }
+#ifdef HAVE_NDB_BINLOG
+      else
+      {
+        /* set up replication for this table */
+        NDB_SHARE *share;
+        pthread_mutex_lock(&ndbcluster_mutex);
+        if (((share= (NDB_SHARE*)hash_search(&ndbcluster_open_tables,
+                                            (byte*) key, strlen(key)))
+              && share->op == 0 && share->op_old == 0)
+            || share == 0)
+        {
+          /*
+            there is no binlog creation setup for this table
+            attempt to do it
+          */
+          pthread_mutex_unlock(&ndbcluster_mutex);
+          ndbcluster_create_binlog_setup(ndb, key, elmt.database, elmt.name,
+                                         ndb_binlog_thread_running > 0 &&
+                                         !is_prefix(elmt.name,
+                                                    tmp_file_prefix),
+                                         share);
+        }
+        else
+          pthread_mutex_unlock(&ndbcluster_mutex);
+      }
+#endif
     }
   }
   while (unhandled && retries--);
@@ -4972,6 +5286,39 @@
     }
   }

+#ifdef HAVE_NDB_BINLOG
+  /* setup logging to binlog for all discovered tables */
+  if (ndb_binlog_thread_running > 0)
+  {
+    char *end;
+    char *end1+      strxnmov(name, sizeof(name), mysql_data_home, "/", db, "/", \
NullS); +    NDB_SHARE *share;
+    pthread_mutex_lock(&ndbcluster_mutex);
+    for (i= 0; i < ok_tables.records; i++)
+    {
+      file_name= (char*)hash_element(&ok_tables, i);
+      end= strxnmov(end1, sizeof(name) - (end1 - name), file_name, NullS);
+      if ((share= (NDB_SHARE*)hash_search(&ndbcluster_open_tables,
+                                          (byte*)name, end - name))
+          && share->op == 0 && share->op_old == 0)
+      {
+        /*
+          there is no binlog creation setup for this table
+          attempt to do it
+	*/
+
+        pthread_mutex_unlock(&ndbcluster_mutex);
+        ndbcluster_create_binlog_setup(ndb, name, db, file_name,
+                                       !is_prefix(file_name, tmp_file_prefix),
+                                       share);
+        pthread_mutex_lock(&ndbcluster_mutex);
+      }
+    }
+    pthread_mutex_unlock(&ndbcluster_mutex);
+  }
+#endif
+
   // Check for new files to discover
   DBUG_PRINT("info", ("Checking for new files to discover"));
   List<char> create_list;
@@ -5119,6 +5466,22 @@
   (void) hash_init(&ndbcluster_open_tables,system_charset_info,32,0,0,
                    (hash_get_key) ndbcluster_get_key,0,0);
   pthread_mutex_init(&ndbcluster_mutex,MY_MUTEX_INIT_FAST);
+#ifdef HAVE_NDB_BINLOG
+  /* start the ndb injector thread */
+  if (opt_bin_log)
+  {
+    if (binlog_row_based)
+    {
+      if (ndbcluster_binlog_start())
+        goto ndbcluster_init_error;
+    }
+    else
+    {
+      sql_print_error("NDB: only row based binary logging is supported");
+    }
+  }
+#endif /* HAVE_NDB_BINLOG */
+
   pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST);
   pthread_cond_init(&COND_ndb_util_thread, NULL);

@@ -5156,11 +5519,11 @@
     ndbcluster_init()
 */

-int ndbcluster_end(ha_panic_function type)
+int ndbcluster_binlog_end()
 {
-  DBUG_ENTER("ndbcluster_end");
+  DBUG_ENTER("ndb_binlog_end");

-  if (!ndbcluster_inited)
+  if (!ndbcluster_util_inited)
     DBUG_RETURN(0);

   // Kill ndb utility thread
@@ -5169,6 +5532,58 @@
   (void) pthread_cond_signal(&COND_ndb_util_thread);
   (void) pthread_mutex_unlock(&LOCK_ndb_util_thread);

+#ifdef HAVE_NDB_BINLOG
+  /* wait for injector thread to finish */
+  if (ndb_binlog_thread_running > 0)
+  {
+    pthread_mutex_lock(&injector_mutex);
+    while (ndb_binlog_thread_running > 0)
+    {
+      struct timespec abstime;
+      set_timespec(abstime, 1);
+      pthread_cond_timedwait(&injector_cond, &injector_mutex, &abstime);
+    }
+    pthread_mutex_unlock(&injector_mutex);
+  }
+
+  /* remove all shares */
+  {
+    pthread_mutex_lock(&ndbcluster_mutex);
+    for (uint i= 0; i < ndbcluster_open_tables.records; i++)
+    {
+      NDB_SHARE *share+        (NDB_SHARE*) hash_element(&ndbcluster_open_tables, \
i); +      if (share->table)
+        DBUG_PRINT("share",
+                   ("table->s->db.table_name: %s.%s",
+                    share->table->s->db, share->table->s->table_name));
+      if (share->state != NSS_DROPPED && !--share->use_count)
+        real_free_share(&share);
+      else
+      {
+        DBUG_PRINT("share",
+                   ("[%d] 0x%lx  key: %s  key_length: %d",
+                    i, share, share->key, share->key_length));
+        DBUG_PRINT("share",
+                   ("db.tablename: %s.%s  use_count: %d  commit_count: %d",
+                    share->db, share->table_name,
+                    share->use_count, share->commit_count));
+      }
+    }
+    pthread_mutex_unlock(&ndbcluster_mutex);
+  }
+#endif
+  ndbcluster_util_inited= 0;
+  DBUG_RETURN(0);
+}
+
+int ndbcluster_end(ha_panic_function type)
+{
+  DBUG_ENTER("ndbcluster_end");
+
+  if (!ndbcluster_inited)
+    DBUG_RETURN(0);
+
   if (g_ndb)
   {
 #ifndef DBUG_OFF
@@ -5195,7 +5610,6 @@
   pthread_mutex_destroy(&LOCK_ndb_util_thread);
   pthread_cond_destroy(&COND_ndb_util_thread);
   ndbcluster_inited= 0;
-  ndbcluster_util_inited= 0;
   DBUG_RETURN(0);
 }

@@ -5745,6 +6159,12 @@
                ("db.tablename: %s.%s  use_count: %d  commit_count: %d",
                 share->db, share->table_name,
                 share->use_count, share->commit_count));
+#ifdef HAVE_NDB_BINLOG
+    if (share->table)
+      DBUG_PRINT("share",
+                 ("table->s->db.table_name: %s.%s",
+                  share->table->s->db, share->table->s->table_name));
+#endif
   }
   DBUG_VOID_RETURN;
 }
@@ -5752,6 +6172,163 @@
 #define dbug_print_open_tables()
 #endif

+#ifdef HAVE_NDB_BINLOG
+/*
+  For some reason a share is still around, try to salvage the situation
+  by closing all cached tables. If the share still exists, there is an
+  error somewhere but only report this to the error log.  Keep this
+  "trailing share" but rename it since there are still references to it
+  to avoid segmentation faults.  There is a risk that the memory for
+  this trailing share leaks.
+
+  Must be called with previous pthread_mutex_lock(&ndbcluster_mutex)
+*/
+static int handle_trailing_share(NDB_SHARE *share)
+{
+  static ulong trailing_share_id= 0;
+  DBUG_ENTER("handle_trailing_share");
+
+  ++share->use_count;
+  pthread_mutex_unlock(&ndbcluster_mutex);
+
+  close_cached_tables((THD*) 0, 0, (TABLE_LIST*) 0);
+
+  pthread_mutex_lock(&ndbcluster_mutex);
+  if (!--share->use_count)
+  {
+    DBUG_PRINT("info", ("NDB_SHARE: close_cashed_tables %s freed share.",
+               share->key));
+    real_free_share(&share);
+    DBUG_RETURN(0);
+  }
+
+  /*
+    share still exists, if share has not been dropped by server
+    release that share
+  */
+  if (share->state != NSS_DROPPED && !--share->use_count)
+  {
+    DBUG_PRINT("info", ("NDB_SHARE: %s already exists, "
+                        "use_count=%d  state != NSS_DROPPED.",
+                        share->key, share->use_count));
+    real_free_share(&share);
+    DBUG_RETURN(0);
+  }
+  DBUG_PRINT("error", ("NDB_SHARE: %s already exists  use_count=%d.",
+                       share->key, share->use_count));
+
+  sql_print_error("NDB_SHARE: %s already exists  use_count=%d."
+                  " Moving away for safety, but possible memleak.",
+                  share->key, share->use_count);
+  dbug_print_open_tables();
+
+  /*
+    This is probably an error.  We can however save the situation
+    at the cost of a possible mem leak, by "renaming" the share
+    - First remove from hash
+  */
+  hash_delete(&ndbcluster_open_tables, (byte*) share);
+
+  /*
+    now give it a new name, just a running number
+    if space is not enough allocate some more
+  */
+  {
+    const uint min_key_length= 10;
+    if (share->key_length < min_key_length)
+    {
+      share->key= alloc_root(&share->mem_root, min_key_length + 1);
+      share->key_length= min_key_length;
+    }
+    share->key_length+      my_snprintf(share->key, min_key_length + 1, "#leak%d",
+                  trailing_share_id++);
+  }
+  /* Keep it for possible the future trailing free */
+  my_hash_insert(&ndbcluster_open_tables, (byte*) share);
+
+  DBUG_RETURN(0);
+}
+
+/*
+  Rename share is used during rename table.
+*/
+static int rename_share(NDB_SHARE *share, const char *new_key)
+{
+  NDB_SHARE *tmp;
+  pthread_mutex_lock(&ndbcluster_mutex);
+  uint new_length= (uint) strlen(new_key);
+  DBUG_PRINT("rename_share", ("old_key: %s  old__length: %d",
+                              share->key, share->key_length));
+  if ((tmp= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
+                                     (byte*) new_key, new_length)))
+    handle_trailing_share(tmp);
+
+  /* remove the share from hash */
+  hash_delete(&ndbcluster_open_tables, (byte*) share);
+  dbug_print_open_tables();
+
+  /* save old stuff if insert should fail */
+  uint old_length= share->key_length;
+  char *old_key= share->key;
+
+  /*
+    now allocate and set the new key, db etc
+    enough space for key, db, and table_name
+  */
+  share->key= alloc_root(&share->mem_root, 2 * (new_length + 1));
+  strmov(share->key, new_key);
+  share->key_length= new_length;
+
+  if (my_hash_insert(&ndbcluster_open_tables, (byte*) share))
+  {
+    // ToDo free the allocated stuff above?
+    DBUG_PRINT("error", ("rename_share: my_hash_insert %s failed",
+                         share->key));
+    share->key= old_key;
+    share->key_length= old_length;
+    if (my_hash_insert(&ndbcluster_open_tables, (byte*) share))
+    {
+      sql_print_error("rename_share: failed to recover %s", share->key);
+      DBUG_PRINT("error", ("rename_share: my_hash_insert %s failed",
+                           share->key));
+    }
+    dbug_print_open_tables();
+    pthread_mutex_unlock(&ndbcluster_mutex);
+    return -1;
+  }
+  dbug_print_open_tables();
+
+  share->db= share->key + new_length + 1;
+  ha_ndbcluster::set_dbname(new_key, share->db);
+  share->table_name= share->db + strlen(share->db) + 1;
+  ha_ndbcluster::set_tabname(new_key, share->table_name);
+
+  DBUG_PRINT("rename_share",
+             ("0x%lx key: %s  key_length: %d",
+              share, share->key, share->key_length));
+  DBUG_PRINT("rename_share",
+             ("db.tablename: %s.%s  use_count: %d  commit_count: %d",
+              share->db, share->table_name,
+              share->use_count, share->commit_count));
+  DBUG_PRINT("rename_share",
+             ("table->s->db.table_name: %s.%s",
+              share->table->s->db, share->table->s->table_name));
+
+  if (share->op == 0)
+  {
+    share->table->s->db= share->db;
+    share->table->s->table_name= share->table_name;
+  }
+  /* else rename will be handled when the ALTER event comes */
+  share->old_names= old_key;
+  // ToDo free old_names after ALTER EVENT
+
+  pthread_mutex_unlock(&ndbcluster_mutex);
+  return 0;
+}
+#endif
+
 /*
   Increase refcount on existing share.
   Always returns share and cannot fail.
@@ -5836,6 +6413,50 @@
       ha_ndbcluster::set_dbname(key, share->db);
       share->table_name= share->db + strlen(share->db) + 1;
       ha_ndbcluster::set_tabname(key, share->table_name);
+#ifdef HAVE_NDB_BINLOG
+      share->op= 0;
+      share->table= 0;
+      while (ndb_binlog_thread_running > 0)
+      {
+        TABLE *table= (TABLE*) my_malloc(sizeof(*table), MYF(MY_WME));
+        int r;
+        if ((r= openfrm(current_thd, share->key, "", 0, (uint) READ_ALL,
+                        0, table)))
+        {
+          sql_print_error("Unable to open frm for %s, frmerror=%d",
+                          share->key, r);
+          DBUG_PRINT("error", ("openfrm failed %d", r));
+          my_free((gptr) table, MYF(0));
+          table= 0;
+          break;
+        }
+        if (!table->record[1] || table->record[1] == table->record[0])
+        {
+          table->record[1]= alloc_root(&table->mem_root,
+                                       table->s->rec_buff_length);
+        }
+        table->in_use= injector_thd;
+
+        table->s->db= share->db;
+        table->s->table_name= share->table_name;
+
+        share->table= table;
+#ifndef DBUG_OFF
+        dbug_print_table("table", table);
+#endif
+        /*
+          ! do not touch the contents of the table
+          it may be in use by the injector thread
+	*/
+        share->ndb_value[0]= (NdbValue*)
+          alloc_root(*root_ptr, sizeof(NdbValue) * table->s->fields
+                     + 1 /*extra for hidden key*/);
+        share->ndb_value[1]= (NdbValue*)
+          alloc_root(*root_ptr, sizeof(NdbValue) * table->s->fields
+                     +1 /*extra for hidden key*/);
+        break;
+      }
+#endif
       *root_ptr= old_root;
     }
     else
@@ -5878,6 +6499,23 @@
   pthread_mutex_destroy(&(*share)->mutex);
   free_root(&(*share)->mem_root, MYF(0));

+#ifdef HAVE_NDB_BINLOG
+  if ((*share)->table)
+  {
+    closefrm((*share)->table);
+#if 0 // todo ?
+    free_root(&(*share)->table->mem_root, MYF(0));
+#endif
+
+#ifndef DBUG_OFF
+    bzero((gptr)(*share)->table, sizeof(*(*share)->table));
+#endif
+    my_free((gptr) (*share)->table, MYF(0));
+#ifndef DBUG_OFF
+    (*share)->table= 0;
+#endif
+  }
+#endif
   my_free((gptr) *share, MYF(0));
   *share= 0;

@@ -6610,8 +7248,19 @@
   */
   ndbcluster_find_all_files(thd);

+#ifdef HAVE_NDB_BINLOG
+  /* create tables needed by the replication */
+  ndbcluster_create_apply_status_table(thd);
+#endif
+
   ndbcluster_util_inited= 1;

+#ifdef HAVE_NDB_BINLOG
+  /* If running, signal injector thread that all is setup */
+  if (ndb_binlog_thread_running > 0)
+    pthread_cond_signal(&injector_cond);
+#endif
+
   set_timespec(abstime, 0);
   for (;!abort_loop;)
   {
@@ -6628,6 +7277,19 @@
     if (abort_loop)
       break; /* Shutting down server */

+#ifdef HAVE_NDB_BINLOG
+    /*
+      Check that the apply_status_share has been created.
+      If not try to create it
+    */
+    if (!apply_status_share &&
+        ndbcluster_check_apply_status_share() == 0)
+    {
+      ndbcluster_find_all_files(thd);
+      ndbcluster_create_apply_status_table(thd);
+    }
+#endif
+
     if (ndb_cache_check_time == 0)
     {
       /* Wake up in 1 second to check if value has changed */
@@ -6641,6 +7303,12 @@
     for (uint i= 0; i < ndbcluster_open_tables.records; i++)
     {
       share= (NDB_SHARE *)hash_element(&ndbcluster_open_tables, i);
+#ifdef HAVE_NDB_BINLOG
+      if ((share->use_count - (int) (share->op != 0) - (int) (share->op != 0))
+          <= 0)
+        continue; // injector thread is the only user, skip statistics
+      share->util_lock= current_thd; // Mark that util thread has lock
+#endif /* HAVE_NDB_BINLOG */
       share->use_count++; /* Make sure the table can't be closed */
       DBUG_PRINT("ndb_util_thread",
                  ("Found open table[%d]: %s, use_count: %d",
@@ -6655,6 +7323,17 @@
     List_iterator_fast<NDB_SHARE> it(util_open_tables);
     while ((share= it++))
     {
+#ifdef HAVE_NDB_BINLOG
+      if ((share->use_count - (int) (share->op != 0) - (int) (share->op != 0))
+          <= 1)
+      {
+        /*
+          Util thread and injector thread is the only user, skip statistics
+	*/
+        free_share(&share);
+        continue;
+      }
+#endif /* HAVE_NDB_BINLOG */
       DBUG_PRINT("ndb_util_thread",
                  ("Fetching commit count for: %s",
                   share->key));
@@ -8061,6 +8740,7 @@
                        enum ha_stat_type stat_type)
 {
   char buf[IO_SIZE];
+  ulonglong ndb_latest_epoch= 0;
   DBUG_ENTER("ndbcluster_show_status");

   if (have_ndbcluster != SHOW_OPTION_YES)
@@ -8071,7 +8751,20 @@
   {
     DBUG_RETURN(FALSE);
   }
-
+
+  update_status_variables(g_ndb_cluster_connection);
+  my_snprintf(buf, sizeof(buf),
+              "cluster_node_id=%u, "
+              "connected_host=%s, "
+              "connected_port=%u, "
+              "number_of_storage_nodes=%u",
+              ndb_cluster_node_id,
+              ndb_connected_host,
+              ndb_connected_port,
+              ndb_number_of_storage_nodes);
+  if (stat_print(thd, ndbcluster_hton.name, "connection", buf))
+    DBUG_RETURN(TRUE);
+
   if (get_thd_ndb(thd) && get_thd_ndb(thd)->ndb)
   {
     Ndb* ndb= (get_thd_ndb(thd))->ndb;
@@ -8085,10 +8778,1449 @@
         DBUG_RETURN(TRUE);
     }
   }
-  send_eof(thd);
-
+#ifdef HAVE_NDB_BINLOG
+  pthread_mutex_lock(&injector_mutex);
+  if (injector_ndb)
+  {
+    ndb_latest_epoch= injector_ndb->getLatestGCI();
+    pthread_mutex_unlock(&injector_mutex);
+
+    snprintf(buf, sizeof(buf),
+             "latest_epoch=%llu, "
+             "latest_trans_epoch=%llu, "
+             "latest_received_binlog_epoch=%llu, "
+             "latest_handled_binlog_epoch=%llu, "
+             "latest_applied_binlog_epoch=%llu",
+             ndb_latest_epoch,
+             g_latest_trans_gci,
+             ndb_latest_received_binlog_epoch,
+             ndb_latest_handled_binlog_epoch,
+             ndb_latest_applied_binlog_epoch);
+    if (stat_print(thd, ndbcluster_hton.name, "binlog", buf))
+      DBUG_RETURN(TRUE);
+  }
+  else
+    pthread_mutex_unlock(&injector_mutex);
+#endif
+
   DBUG_RETURN(FALSE);
 }
+
+#ifdef HAVE_NDB_BINLOG
+
+/*
+  Run a query through mysql_parse
+
+  Used to:
+  - purging the cluster_replication.binlog_index
+  - creating the cluster_replication.apply_status table
+*/
+static void run_query(THD *thd, char *buf, char *end, my_bool print_error)
+{
+  ulong save_query_length= thd->query_length;
+  char *save_query= thd->query;
+  ulong save_thread_id= thd->variables.pseudo_thread_id;
+  NET save_net= thd->net;
+
+  bzero((char*) &thd->net, sizeof(NET));
+  thd->query_length= end - buf;
+  thd->query= buf;
+  thd->variables.pseudo_thread_id= thread_id;
+  DBUG_PRINT("query", ("%s", thd->query));
+
+  mysql_parse(thd, thd->query, thd->query_length);
+
+  if (print_error && thd->query_error)
+  {
+    sql_print_error("NDB: %s: error %s %d %d %d",
+                    buf, thd->net.last_error, thd->net.last_errno,
+                    thd->net.report_error, thd->query_error);
+  }
+
+  thd->query_length= save_query_length;
+  thd->query= save_query;
+  thd->variables.pseudo_thread_id= save_thread_id;
+  thd->net= save_net;
+}
+
+
+/*********************************************************************
+  Internal helper functions for handeling of the cluster replication tables
+  - cluster_replication.binlog_index
+  - cluster_replication.apply_status
+*********************************************************************/
+
+/*
+  defines for cluster replication table names
+*/
+#define NDB_REP_DB      "cluster_replication"
+#define NDB_REP_TABLE   "binlog_index"
+#define NDB_APPLY_TABLE "apply_status"
+#define NDB_APPLY_TABLE_FILE "./" NDB_REP_DB "/" NDB_APPLY_TABLE
+
+/*
+  Global variables for holding the binlog_index table reference
+*/
+TABLE *binlog_index= 0;
+TABLE_LIST binlog_tables;
+
+/*
+  struct to hold the data to be inserted into the
+  cluster_replication.binlog_index table
+*/
+struct Binlog_index_row {
+  longlong gci;
+  const char *master_log_file;
+  longlong master_log_pos;
+  longlong n_inserts;
+  longlong n_updates;
+  longlong n_deletes;
+  longlong n_schemaops;
+};
+
+/*
+  Open the cluster_replication.binlog_index table
+*/
+static int open_binlog_index(THD *thd, TABLE_LIST *tables,
+                             TABLE **binlog_index)
+{
+  static char repdb[]= NDB_REP_DB;
+  static char reptable[]= NDB_REP_TABLE;
+  const char *save_proc_info= thd->proc_info;
+
+  bzero((char*) tables, sizeof(*tables));
+  tables->db= repdb;
+  tables->alias= tables->table_name= reptable;
+  tables->lock_type= TL_WRITE;
+  thd->proc_info= "Opening " NDB_REP_DB "." NDB_REP_TABLE;
+  tables->required_type= FRMTYPE_TABLE;
+  uint counter;
+  thd->clear_error();
+  if (open_tables(thd, &tables, &counter, MYSQL_LOCK_IGNORE_FLUSH))
+  {
+    sql_print_error("NDB Binlog: Opening binlog_index: %d, '%s'",
+                    thd->net.last_errno,
+                    thd->net.last_error ? thd->net.last_error : "");
+    thd->proc_info= save_proc_info;
+    return -1;
+  }
+  *binlog_index= tables->table;
+  thd->proc_info= save_proc_info;
+  return 0;
+}
+
+/*
+  Insert one row in the cluster_replication.binlog_index
+
+  declared friend in handler.h to be able to call write_row directly
+  so that this insert is not replicated
+*/
+int ndb_add_binlog_index(THD *thd, void *_row)
+{
+  Binlog_index_row &row= *(Binlog_index_row *) _row;
+  int error= 0;
+  bool need_reopen;
+  for ( ; ; ) /* loop for need_reopen */
+  {
+    if (!binlog_index && open_binlog_index(thd, &binlog_tables, &binlog_index))
+    {
+      error= -1;
+      goto add_binlog_index_err;
+    }
+
+    if (lock_tables(thd, &binlog_tables, 1, &need_reopen))
+    {
+      if (need_reopen)
+      {
+        close_tables_for_reopen(thd, &binlog_tables);
+	binlog_index= 0;
+        continue;
+      }
+      sql_print_error("NDB Binlog: Unable to lock table binlog_index");
+      error= -1;
+      goto add_binlog_index_err;
+    }
+    break;
+  }
+
+  binlog_index->field[0]->store(row.master_log_pos);
+  binlog_index->field[1]->store(row.master_log_file,
+                                strlen(row.master_log_file),
+                                &my_charset_bin);
+  binlog_index->field[2]->store(row.gci);
+  binlog_index->field[3]->store(row.n_inserts);
+  binlog_index->field[4]->store(row.n_updates);
+  binlog_index->field[5]->store(row.n_deletes);
+  binlog_index->field[6]->store(row.n_schemaops);
+
+  int r;
+  if ((r= binlog_index->file->write_row(binlog_index->record[0])))
+  {
+    sql_print_error("NDB Binlog: Writing row to binlog_index: %d", r);
+    error= -1;
+    goto add_binlog_index_err;
+  }
+
+  mysql_unlock_tables(thd, thd->lock);
+  thd->lock= 0;
+  return 0;
+add_binlog_index_err:
+  close_thread_tables(thd);
+  binlog_index= 0;
+  return error;
+}
+
+/*
+  check the availability af the cluster_replication.apply_status share
+  - return share, but do not increase refcount
+  - return 0 if there is no share
+*/
+static NDB_SHARE *ndbcluster_check_apply_status_share()
+{
+  pthread_mutex_lock(&ndbcluster_mutex);
+
+  void *share= hash_search(&ndbcluster_open_tables,
+                           NDB_APPLY_TABLE_FILE,
+                           sizeof(NDB_APPLY_TABLE_FILE) - 1);
+  DBUG_PRINT("info",("ndbcluster_check_apply_status_share %s %p",
+                     NDB_APPLY_TABLE_FILE, share));
+  pthread_mutex_unlock(&ndbcluster_mutex);
+  return (NDB_SHARE*) share;
+}
+
+/*
+  Get the share for the cluster_replication.apply_status share
+
+  - return 0 if share does not exist
+*/
+static NDB_SHARE *ndbcluster_get_apply_status_share()
+{
+  return get_share(NDB_APPLY_TABLE_FILE, false);
+}
+
+/*
+  Create the cluster_replication.apply_status table
+*/
+static int ndbcluster_create_apply_status_table(THD *thd)
+{
+  DBUG_ENTER("ndbcluster_create_apply_status_table");
+
+  /*
+    Check if we already have the apply status table.
+    If so it should have been discovered at startup
+    and thus have a share
+  */
+
+  if (ndbcluster_check_apply_status_share())
+    DBUG_RETURN(0);
+
+  if (g_ndb_cluster_connection->get_no_ready() <= 0)
+    DBUG_RETURN(0);
+
+  char buf[1024], *end;
+
+  sql_print_information("NDB: Creating " NDB_REP_DB "." NDB_APPLY_TABLE);
+
+  /*
+    Check if apply status table exists in MySQL "dictionary"
+    if so, remove it since there is none in Ndb
+  */
+  {
+    strxnmov(buf, sizeof(buf),
+             mysql_data_home,
+             "/" NDB_REP_DB "/" NDB_APPLY_TABLE,
+             reg_ext, NullS);
+    unpack_filename(buf,buf);
+    my_delete(buf, MYF(0));
+  }
+
+  /*
+    Note, updating this table schema must be reflected in ndb_restore
+  */
+  end= strmov(buf, "CREATE TABLE IF NOT EXISTS "
+                   NDB_REP_DB "." NDB_APPLY_TABLE
+                   " ( server_id INT UNSIGNED NOT NULL,"
+                   " epoch BIGINT UNSIGNED NOT NULL, "
+                   " PRIMARY KEY USING HASH (server_id) ) ENGINE=NDB");
+
+  run_query(thd, buf, end, TRUE);
+
+  DBUG_RETURN(0);
+}
+
+
+/*********************************************************************
+  Functions for start, stop, wait for ndbcluster binlog thread
+*********************************************************************/
+
+static int do_ndbcluster_binlog_close_connection= 0;
+
+static int ndbcluster_binlog_start()
+{
+  DBUG_ENTER("ndbcluster_binlog_start");
+
+  pthread_mutex_init(&injector_mutex, MY_MUTEX_INIT_FAST);
+  pthread_cond_init(&injector_cond, NULL);
+
+  /* Create injector thread */
+  if (pthread_create(&ndb_binlog_thread, &connection_attrib,
+                     ndb_binlog_thread_func, 0))
+  {
+    DBUG_PRINT("error", ("Could not create ndb injector thread"));
+    pthread_cond_destroy(&injector_cond);
+    pthread_mutex_destroy(&injector_mutex);
+    DBUG_RETURN(-1);
+  }
+
+  /*
+    Wait for the ndb injector thread to finish starting up.
+  */
+  pthread_mutex_lock(&injector_mutex);
+  while (!ndb_binlog_thread_running)
+    pthread_cond_wait(&injector_cond, &injector_mutex);
+  pthread_mutex_unlock(&injector_mutex);
+
+  if (ndb_binlog_thread_running < 0)
+    DBUG_RETURN(-1);
+
+  DBUG_RETURN(0);
+}
+
+static void ndbcluster_binlog_close_connection(THD *thd)
+{
+  DBUG_ENTER("ndbcluster_binlog_close_connection");
+  const char *save_info= thd->proc_info;
+  thd->proc_info= "ndbcluster_binlog_close_connection";
+  do_ndbcluster_binlog_close_connection= 1;
+  while (ndb_binlog_thread_running > 0)
+    sleep(1);
+  thd->proc_info= save_info;
+  DBUG_VOID_RETURN;
+}
+
+/*
+  called in mysql_show_binlog_events and reset_logs to make sure we wait for
+  all events originating from this mysql server to arrive in the binlog
+
+  Wait for the last epoch in which the last transaction is a part of.
+
+  Wait a maximum of 30 seconds.
+*/
+void ndbcluster_binlog_wait(THD *thd)
+{
+  if (ndb_binlog_thread_running > 0)
+  {
+    DBUG_ENTER("ndbcluster_binlog_wait");
+    const char *save_info= thd ? thd->proc_info : 0;
+    ulonglong wait_epoch= g_latest_trans_gci;
+    int count= 30;
+    if (thd)
+      thd->proc_info= "Waiting for ndbcluster binlog update to "
+	"reach current position";
+    while (count && ndb_binlog_thread_running > 0 &&
+           ndb_latest_handled_binlog_epoch < wait_epoch)
+    {
+      count--;
+      sleep(1);
+    }
+    if (thd)
+      thd->proc_info= save_info;
+    DBUG_VOID_RETURN;
+  }
+}
+
+/*****************************************************************
+  functions called from master sql client threads
+****************************************************************/
+
+/*
+ Called from MYSQL_LOG::reset_logs in log.cc when binlog is emptied
+*/
+int ndbcluster_reset_logs(THD *thd)
+{
+  if (ndb_binlog_thread_running <= 0)
+    return 0;
+
+  DBUG_ENTER("ndbcluster_reset_logs");
+
+  /*
+    Wait for all events orifinating from this mysql server has
+    reached the binlog before continuing to reset
+  */
+  ndbcluster_binlog_wait(thd);
+
+  char buf[1024];
+  char *end= strmov(buf, "DELETE FROM " NDB_REP_DB "." NDB_REP_TABLE);
+
+  run_query(thd, buf, end, FALSE);
+
+  DBUG_RETURN(0);
+}
+
+/*
+  Called from MYSQL_LOG::purge_logs in log.cc when the binlog "file"
+  is removed
+*/
+
+int ndbcluster_binlog_index_purge_file(THD *thd, const char *file)
+{
+  if (ndb_binlog_thread_running <= 0)
+    return 0;
+
+  DBUG_ENTER("ndbcluster_binlog_index_purge_file");
+  DBUG_PRINT("enter", ("file: %s", file));
+
+  char buf[1024];
+  char *end= strmov(strmov(strmov(buf,
+                                  "DELETE FROM "
+                                  NDB_REP_DB "." NDB_REP_TABLE
+                                  " WHERE File='"), file), "'");
+
+  run_query(thd, buf, end, FALSE);
+
+  DBUG_RETURN(0);
+}
+
+/*****************************************************************
+  functions called from slave sql client threads
+****************************************************************/
+void ndbcluster_reset_slave(THD *thd)
+{
+  if (ndb_binlog_thread_running <= 0)
+    return;
+
+  DBUG_ENTER("ndbcluster_reset_slave");
+  char buf[1024];
+  char *end= strmov(buf, "DELETE FROM " NDB_REP_DB "." NDB_APPLY_TABLE);
+  run_query(thd, buf, end, FALSE);
+  DBUG_VOID_RETURN;
+}
+
+
+/**************************************************************
+  Internal helper functions for creating/dropping ndb events
+  used by the client sql threads
+**************************************************************/
+static void
+ndb_rep_event_name(String *event_name,const char *db, const char *tbl)
+{
+  event_name->set_ascii("REPL$", 5);
+  event_name->append(db);
+  if (tbl)
+  {
+    event_name->append('/');
+    event_name->append(tbl);
+  }
+}
+
+/*
+  Common function for setting up everything for logging a table at
+  create/discover.
+*/
+static int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key,
+                                          const char *db,
+                                          const char *table_name,
+                                          bool do_binlog,
+                                          NDB_SHARE *share)
+{
+  DBUG_ENTER("ndbcluster_create_binlog_setup");
+
+  pthread_mutex_lock(&ndbcluster_mutex);
+
+  /* Handle any trailing share */
+  if (share == 0)
+  {
+    share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
+                                    (byte*) key, strlen(key));
+    if (share)
+      handle_trailing_share(share);
+  }
+  else
+    handle_trailing_share(share);
+
+  /* Create share which is needed to hold replication information */
+  if (!(share= get_share(key, true, true)))
+  {
+    sql_print_error("NDB Binlog: "
+                    "allocating table share for %s failed", key);
+  }
+  pthread_mutex_unlock(&ndbcluster_mutex);
+
+  while (share && do_binlog)
+  {
+    /*
+      ToDo make sanity check of share so that the table is actually the same
+      I.e. we need to do openfrm in this case
+      Currently awaiting this to be fixed in the 4.1 tree in the general
+      case
+    */
+
+    /* Create the event in NDB */
+    ndb->setDatabaseName(db);
+
+    NDBDICT *dict= ndb->getDictionary();
+    const NDBTAB *ndbtab= dict->getTable(table_name);
+    if (ndbtab == 0)
+    {
+      sql_print_information("NDB Binlog: Failed to get table %s from ndb: "
+                            "%s, %d", key, dict->getNdbError().message,
+                            dict->getNdbError().code);
+      break; // error
+    }
+    String event_name(INJECTOR_EVENT_LEN);
+    ndb_rep_event_name(&event_name, db, table_name);
+    /*
+      event should have been created by someone else,
+      but let's make sure, and create if it doesn't exist
+    */
+    if (!dict->getEvent(event_name.c_ptr()))
+    {
+      if (ndbcluster_create_event(ndb, ndbtab, event_name.c_ptr(), share))
+      {
+        sql_print_error("NDB Binlog: "
+                        "FAILED CREATE (DISCOVER) TABLE Event: %s",
+                        event_name.c_ptr());
+        break; // error
+      }
+      sql_print_information("NDB Binlog: "
+                            "CREATE (DISCOVER) TABLE Event: %s",
+                            event_name.c_ptr());
+    }
+    else
+      sql_print_information("NDB Binlog: DISCOVER TABLE Event: %s",
+                            event_name.c_ptr());
+
+    /*
+      create the event operations for receiving logging events
+    */
+    if (ndbcluster_create_event_ops(share, ndbtab,
+                                    event_name.c_ptr()) < 0)
+    {
+      sql_print_error("NDB Binlog:"
+                      "FAILED CREATE (DISCOVER) EVENT OPERATIONS Event: %s",
+                      event_name.c_ptr());
+      /* a warning has been issued to the client */
+      DBUG_RETURN(0);
+    }
+    DBUG_RETURN(0);
+  }
+  DBUG_RETURN(-1);
+}
+
+static int
+ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab,
+                        const char *event_name,
+                        NDB_SHARE *share)
+{
+  DBUG_ENTER("ndbcluster_create_event");
+  NDBDICT *dict= ndb->getDictionary();
+
+  if (!dict)
+  {
+    sql_print_error("NDB Binlog: could not setup binlog, "
+                    "Invalid NdbDictionary");
+    DBUG_RETURN(-1);
+  }
+
+  NDBEVENT my_event(event_name);
+  my_event.setTable(*ndbtab);
+  my_event.addTableEvent(NDBEVENT::TE_ALL);
+  if (share->table->s->primary_key == MAX_KEY)
+    /* No primary key, susbscribe for all attributes */
+    my_event.setReport(NDBEVENT::ER_ALL);
+  else
+    my_event.setReport(NDBEVENT::ER_UPDATED);
+  /* add all columns to the event */
+  int n_cols= ndbtab->getNoOfColumns();
+  for(int a= 0; a < n_cols; a++)
+    my_event.addEventColumn(a);
+
+  if (dict->createEvent(my_event)) // Add event to database
+  {
+#ifdef NDB_BINLOG_EXTRA_WARNINGS
+    /*
+      failed, print a warning
+    */
+    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+                        ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+                        dict->getNdbError().code,
+                        dict->getNdbError().message, "NDB");
+#endif
+    if (dict->getNdbError().classification != NdbError::SchemaObjectExists)
+    {
+      sql_print_error("NDB Binlog: Unable to create event in database. "
+                      "Event: %s  Error Code: %d  Message: %s", event_name,
+                      dict->getNdbError().code, dict->getNdbError().message);
+      DBUG_RETURN(-1);
+    }
+
+    /*
+      trailing event from before; an error, but try to correct it
+    */
+    if (dict->dropEvent(my_event.getName()))
+    {
+      sql_print_error("NDB Binlog: Unable to create event in database. "
+                      " Attempt to correct with drop failed. "
+                      "Event: %s Error Code: %d Message: %s",
+                      event_name,
+                      dict->getNdbError().code,
+                      dict->getNdbError().message);
+      DBUG_RETURN(-1);
+    }
+
+    /*
+      try to add the event again
+    */
+    if (dict->createEvent(my_event))
+    {
+      sql_print_error("NDB Binlog: Unable to create event in database. "
+                      " Attempt to correct with drop ok, but create failed. "
+                      "Event: %s Error Code: %d Message: %s",
+                      event_name,
+                      dict->getNdbError().code,
+                      dict->getNdbError().message);
+      DBUG_RETURN(-1);
+    }
+#ifdef NDB_BINLOG_EXTRA_WARNINGS
+    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+                        ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+                        0, "NDB Binlog: Removed trailing event",
+                        "NDB");
+#endif
+  }
+
+  DBUG_RETURN(0);
+}
+
+inline int is_ndb_compatible_type(Field *field)
+{
+  return
+    !(field->flags & BLOB_FLAG) &&
+    field->type() != MYSQL_TYPE_BIT &&
+    field->pack_length() != 0;
+}
+
+/*
+  - create eventOperations for receiving log events
+  - setup ndb recattrs for reception of log event data
+  - "start" the event operation
+
+  used at create/discover of tables
+*/
+static int
+ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab,
+                            const char *event_name)
+{
+  /*
+    we are in either create table or rename table so table should be
+    locked, hence we can work with the share without locks
+  */
+
+  DBUG_ENTER("ndbcluster_create_event_ops");
+
+  DBUG_ASSERT(share != 0);
+
+  if (share->op)
+  {
+    assert(share->op->getCustomData() == (void *) share);
+
+    DBUG_ASSERT(share->use_count > 1);
+    sql_print_error("NDB Binlog: discover reusing old ev op");
+    free_share(&share); // old event op already has reference
+    DBUG_RETURN(0);
+  }
+
+  TABLE *table= share->table;
+  if (table)
+  {
+    /*
+      Logging of blob tables is not yet implemented, it would require:
+      1. setup of events also on the blob attribute tables
+      2. collect the pieces of the blob into one from an epoch to
+         provide a full blob to binlog
+    */
+    if (table->s->blob_fields)
+    {
+      sql_print_error("NDB Binlog: logging of blob table %s "
+                      "is not supported", share->key);
+      DBUG_RETURN(0);
+    }
+  }
+
+  pthread_mutex_lock(&injector_mutex);
+  if (injector_ndb == 0)
+  {
+    pthread_mutex_unlock(&injector_mutex);
+    DBUG_RETURN(-1);
+  }
+
+  NdbEventOperation *op= injector_ndb->createEventOperation(event_name);
+  if (!op)
+  {
+    pthread_mutex_unlock(&injector_mutex);
+    sql_print_error("NDB Binlog: Creating NdbEventOperation failed for"
+                    " %s",event_name);
+    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+                        ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+                        injector_ndb->getNdbError().code,
+                        injector_ndb->getNdbError().message,
+                        "NDB");
+    DBUG_RETURN(-1);
+  }
+
+  int n_columns= ndbtab->getNoOfColumns();
+  int n_fields= table ? table->s->fields : 0;
+  for (int j= 0; j < n_columns; j++)
+  {
+    const char *col_name= ndbtab->getColumn(j)->getName();
+    NdbRecAttr *attr0, *attr1;
+    if (j < n_fields)
+    {
+      Field *f= share->table->field[j];
+      if (is_ndb_compatible_type(f))
+      {
+        DBUG_PRINT("info", ("%s compatible", col_name));
+        attr0= op->getValue(col_name, f->ptr);
+        attr1= op->getPreValue(col_name, (f->ptr-share->table->record[0]) +
+                               share->table->record[1]);
+      }
+      else
+      {
+        DBUG_PRINT("info", ("%s non compatible", col_name));
+        attr0= op->getValue(col_name);
+        attr1= op->getPreValue(col_name);
+      }
+    }
+    else
+    {
+      DBUG_PRINT("info", ("%s hidden key", col_name));
+      attr0= op->getValue(col_name);
+      attr1= op->getPreValue(col_name);
+    }
+    share->ndb_value[0][j].rec= attr0;
+    share->ndb_value[1][j].rec= attr1;
+  }
+  op->setCustomData((void *) share); // set before execute
+  share->op= op; // assign op in NDB_SHARE
+  if (op->execute())
+  {
+    share->op= NULL;
+    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+                        ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+                        op->getNdbError().code, op->getNdbError().message,
+                        "NDB");
+    sql_print_error("NDB Binlog: ndbevent->execute failed for %s; %d %s",
+                    event_name,
+                    op->getNdbError().code, op->getNdbError().message);
+    injector_ndb->dropEventOperation(op);
+    pthread_mutex_unlock(&injector_mutex);
+    DBUG_RETURN(-1);
+  }
+  pthread_mutex_unlock(&injector_mutex);
+
+  get_share(share);
+
+  DBUG_PRINT("info",("%s share->op: 0x%lx, share->use_count: %u",
+                     share->key, share->op, share->use_count));
+
+  sql_print_information("NDB Binlog: logging %s", share->key);
+  DBUG_RETURN(0);
+}
+
+/*
+  when entering the calling thread should have a share lock id share != 0
+  then the injector thread will have  one as well, i.e. share->use_count == 0
+  (unless it has already dropped... then share->op == 0)
+*/
+static int
+ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name,
+                             NDB_SHARE *share)
+{
+  DBUG_ENTER("ndbcluster_handle_drop_table");
+
+  NDBDICT *dict= ndb->getDictionary();
+  if (event_name && dict->dropEvent(event_name))
+  {
+    /* drop event failed for some reason, issue a warning */
+    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+                        ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+                        dict->getNdbError().code,
+                        dict->getNdbError().message, "NDB");
+    if (dict->getNdbError().code != 4710)
+    {
+      /* error is not that the event did not exist */
+      sql_print_error("NDB Binlog: Unable to drop event in database. "
+                      "Event: %s Error Code: %d Message: %s",
+                      event_name,
+                      dict->getNdbError().code,
+                      dict->getNdbError().message);
+      /* ToDo; handle error? */
+      if (share && share->op &&
+          share->op->getState() == NdbEventOperation::EO_EXECUTING &&
+          dict->getNdbError().code != 4009)
+      {
+        DBUG_ASSERT(false);
+        DBUG_RETURN(-1);
+      }
+    }
+  }
+
+  if (share == 0 || share->op == 0)
+  {
+    DBUG_RETURN(0);
+  }
+
+/*
+  Syncronized drop between client thread and injector thread is
+  neccessary in order to maintain ordering in the binlog,
+  such that the drop occurs _after_ any inserts/updates/deletes.
+
+  The penalty for this is that the drop table becomes slow.
+
+  This wait is however not strictly neccessary to produce a binlog
+  that is usable.  However the slave does not currently handle
+  these out of order, thus we are keeping the SYNC_DROP_ defined
+  for now.
+*/
+#define SYNC_DROP_
+#ifdef SYNC_DROP_
+  (void) pthread_mutex_lock(&share->mutex);
+  int max_timeout= 10;
+  while (share->op)
+  {
+    struct timespec abstime;
+    set_timespec(abstime, 1);
+    (void) pthread_cond_timedwait(&injector_cond,
+                                  &share->mutex,
+                                  &abstime);
+    max_timeout--;
+    if (share->op == 0)
+      break;
+    if (max_timeout == 0)
+    {
+      sql_print_error("NDB delete table: timed out. Ignoring...");
+      break;
+    }
+    sql_print_information("NDB delete table: "
+                          "waiting max %u sec for drop table %s.",
+                          max_timeout, share->key);
+  }
+  (void) pthread_mutex_unlock(&share->mutex);
+#else
+  (void) pthread_mutex_lock(&share->mutex);
+  share->op_old= share->op;
+  share->op= 0;
+  (void) pthread_mutex_unlock(&share->mutex);
+#endif
+
+  DBUG_RETURN(0);
+}
+
+
+/********************************************************************
+  Internal helper functions for differentd events from the stoarage nodes
+  used by the ndb injector thread
+********************************************************************/
+
+/*
+  Handle error states on events from the storage nodes
+*/
+static int ndb_binlog_thread_handle_error(Ndb *ndb, NdbEventOperation *pOp,
+                                          Binlog_index_row &row)
+{
+  NDB_SHARE *share= (NDB_SHARE *)pOp->getCustomData();
+  DBUG_ENTER("ndb_binlog_thread_handle_error");
+
+  int overrun= pOp->isOverrun();
+  if (overrun)
+  {
+    /*
+      ToDo: this error should rather clear the binlog_index...
+      and continue
+    */
+    sql_print_error("NDB Binlog: Overrun in event buffer, "
+                    "this means we have dropped events. Cannot "
+                    "continue binlog for %s", share->key);
+    pOp->clearError();
+    DBUG_RETURN(-1);
+  }
+
+  if (!pOp->isConsistent())
+  {
+    /*
+      ToDo: this error should rather clear the binlog_index...
+      and continue
+    */
+    sql_print_error("NDB Binlog: Not Consistent. Cannot "
+                    "continue binlog for %s. Error code: %d"
+                    " Message: %s", share->key,
+                    pOp->getNdbError().code,
+                    pOp->getNdbError().message);
+    pOp->clearError();
+    DBUG_RETURN(-1);
+  }
+  sql_print_error("NDB Binlog: unhandled error %d for table %s",
+                  pOp->hasError(), share->key);
+  pOp->clearError();
+  DBUG_RETURN(0);
+}
+
+/*
+  Handle _non_ data events from the storage nodes
+*/
+static int
+ndb_binlog_thread_handle_non_data_event(Ndb *ndb, NdbEventOperation *pOp,
+                                        Binlog_index_row &row)
+{
+  NDB_SHARE *share= (NDB_SHARE *)pOp->getCustomData();
+  NDBEVENT::TableEvent type= pOp->getEventType();
+  int remote_drop_table= 0, do_close_cached_tables= 0;
+
+  /* make sure to flush any pending events as they can be dependent
+     on one of the tables being changed below
+  */
+  injector_thd->binlog_flush_pending_rows_event(true);
+
+  switch (type)
+  {
+  case NDBEVENT::TE_CLUSTER_FAILURE:
+    sql_print_information("NDB Binlog: cluster failure for %s.", share->key);
+
+    DBUG_PRINT("info", ("CLUSTER FAILURE EVENT: "
+                        "%s  received share: 0x%lx  op: %lx  share op: %lx  "
+                        "op_old: %lx",
+                       share->key, share, pOp, share->op, share->op_old));
+    if (apply_status_share)
+    {
+      free_share(&apply_status_share);
+      apply_status_share= 0;
+    }
+    break;
+  case NDBEVENT::TE_ALTER:
+    /* ToDo: remove printout */
+    sql_print_information("NDB Binlog: rename table %s%s/%s -> %s.",
+                          share_prefix, share->table->s->db,
+                          share->table->s->table_name,
+                          share->key);
+    /* do the rename of the table in the share */
+    share->table->s->db= share->db;
+    share->table->s->table_name= share->table_name;
+    goto drop_alter_common;
+  case NDBEVENT::TE_DROP:
+    /* ToDo: remove printout */
+    sql_print_information("NDB Binlog: drop table %s.",
+                          share->key);
+drop_alter_common:
+    row.n_schemaops++;
+    DBUG_PRINT("info", ("TABLE %s EVENT: %s  received share: 0x%lx  op: %lx  "
+                        "share op: %lx  op_old: %lx",
+                       type == NDBEVENT::TE_DROP ? "DROP" : "ALTER",
+                       share->key, share, pOp, share->op, share->op_old));
+    if (pOp->getReqNodeId() != ndb_cluster_node_id)
+    {
+      ndb->setDatabaseName(share->table->s->db);
+      ha_ndbcluster::invalidate_dictionary_cache(share->table,
+                                                 ndb,
+                                                 share->table->s->table_name,
+                                                 TRUE);
+      remote_drop_table= 1;
+    }
+    break;
+  default:
+    sql_print_error("NDB Binlog: unknown non data event %d for %s. "
+                    "Ignoring...", (unsigned) type, share->key);
+    return 0;
+  }
+
+  (void) pthread_mutex_lock(&share->mutex);
+  DBUG_ASSERT(share->op == pOp || share->op_old == pOp);
+  if (share->op_old == pOp)
+    share->op_old= 0;
+  else
+    share->op= 0;
+  // either just us or drop table handling as well
+
+  /* Signal ha_ndbcluster::delete/rename_table that drop is done */
+  (void) pthread_mutex_unlock(&share->mutex);
+  (void) pthread_cond_signal(&injector_cond);
+
+  pthread_mutex_lock(&ndbcluster_mutex);
+  free_share(&share, TRUE);
+  if (remote_drop_table && share && share->state != NSS_DROPPED)
+  {
+    DBUG_PRINT("info", ("remote drop table"));
+    if (share->use_count != 1)
+      do_close_cached_tables= 1;
+    share->state= NSS_DROPPED;
+    free_share(&share, TRUE);
+  }
+  pthread_mutex_unlock(&ndbcluster_mutex);
+
+  share= 0;
+  pOp->setCustomData(0);
+
+  pthread_mutex_lock(&injector_mutex);
+  injector_ndb->dropEventOperation(pOp);
+  pOp= 0;
+  pthread_mutex_unlock(&injector_mutex);
+
+  if (do_close_cached_tables)
+    close_cached_tables((THD*) 0, 0, (TABLE_LIST*) 0);
+
+  return 0;
+}
+
+/*
+  Handle data events from the storage nodes
+*/
+static int
+ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp,
+                                    Binlog_index_row &row,
+                                    injector::transaction &trans)
+{
+  NDB_SHARE *share= (NDB_SHARE*) pOp->getCustomData();
+  TABLE *table= share->table;
+
+  assert(table != 0);
+#ifndef DBUG_OFF
+  dbug_print_table("table", table);
+#endif
+  TABLE_SHARE *table_s= table->s;
+  uint n_fields= table_s->fields;
+  MY_BITMAP b;
+  /* Potential buffer for the bitmap */
+  uint32 bitbuf[128 / (sizeof(uint32) * 8)];
+  bitmap_init(&b, n_fields <= sizeof(bitbuf) * 8 ? bitbuf : NULL,
+              n_fields, false);
+  bitmap_set_all(&b);
+
+  /*
+   row data is already in table->record[0]
+   As we told the NdbEventOperation to do this
+   (saves moving data about many times)
+  */
+
+  switch(pOp->getEventType())
+  {
+  case NDBEVENT::TE_INSERT:
+    row.n_inserts++;
+    DBUG_PRINT("info", ("INSERT INTO %s", share->key));
+    {
+      ndb_unpack_record(table, share->ndb_value[0], &b, table->record[0]);
+      trans.write_row(::server_id, injector::transaction::table(table, true),
+                      &b, n_fields, table->record[0]);
+    }
+    break;
+  case NDBEVENT::TE_DELETE:
+    row.n_deletes++;
+    DBUG_PRINT("info",("DELETE FROM %s", share->key));
+    {
+      /*
+        table->record[0] contains only the primary key in this case
+        since we do not have an after image
+      */
+      int n;
+      if (table->s->primary_key != MAX_KEY)
+        n= 0; /*
+                use the primary key only as it save time and space and
+                it is the only thing needed to log the delete
+	      */
+      else
+        n= 1; /*
+                we use the before values since we don't have a primary key
+                since the mysql server does not handle the hidden primary
+                key
+	      */
+
+      ndb_unpack_record(table, share->ndb_value[n], &b, table->record[n]);
+      print_records(table, table->record[n]);
+      trans.delete_row(::server_id, injector::transaction::table(table, true),
+                       &b, n_fields, table->record[n]);
+    }
+    break;
+  case NDBEVENT::TE_UPDATE:
+    row.n_updates++;
+    DBUG_PRINT("info", ("UPDATE %s", share->key));
+    {
+      ndb_unpack_record(table, share->ndb_value[0],
+                        &b, table->record[0]);
+      print_records(table, table->record[0]);
+      if (table->s->primary_key != MAX_KEY)
+      {
+        /*
+          since table has a primary key, we can to a write
+          using only after values
+	*/
+        trans.write_row(::server_id, injector::transaction::table(table, true),
+                        &b, n_fields, table->record[0]);// after values
+      }
+      else
+      {
+        /*
+          mysql server cannot handle the ndb hidden key and
+          therefore needs the before image as well
+	*/
+        ndb_unpack_record(table, share->ndb_value[1], &b, table->record[1]);
+        print_records(table, table->record[1]);
+        trans.update_row(::server_id,
+                         injector::transaction::table(table, true),
+                         &b, n_fields,
+                         table->record[1], // before values
+                         table->record[0]);// after values
+      }
+    }
+    break;
+  default:
+    /* We should REALLY never get here. */
+    DBUG_PRINT("info", ("default - uh oh, a brain exploded."));
+    break;
+  }
+
+  return 0;
+}
+
+/*
+  Timer class for doing performance measurements
+*/
+//#define RUN_NDB_BINLOG_TIMER
+#ifdef RUN_NDB_BINLOG_TIMER
+class Timer
+{
+public:
+  Timer() { start(); }
+  void start() { gettimeofday(&m_start, 0); }
+  void stop() { gettimeofday(&m_stop, 0); }
+  ulong elapsed_ms()
+  {
+    return (ulong)
+      (((longlong) m_stop.tv_sec - (longlong) m_start.tv_sec) * 1000 +
+       ((longlong) m_stop.tv_usec -
+        (longlong) m_start.tv_usec + 999) / 1000);
+  }
+private:
+  struct timeval m_start,m_stop;
+};
+#endif
+
+
+/****************************************************************
+  Injector thread main loop
+****************************************************************/
+
+pthread_handler_t ndb_binlog_thread_func(void *arg)
+{
+  THD *thd; /* needs to be first for thread_stack */
+  Ndb *ndb= 0;
+  int ndb_update_binlog_index= 1;
+  injector *inj= injector::instance();
+
+  pthread_mutex_lock(&injector_mutex);
+  /*
+    Set up the Thread
+  */
+  my_thread_init();
+  DBUG_ENTER("ndb_binlog_thread");
+
+  thd= new THD; /* note that contructor of THD uses DBUG_ */
+  THD_CHECK_SENTRY(thd);
+
+  thd->thread_stack= (char*) &thd; /* remember where our stack is */
+  if (thd->store_globals())
+  {
+    thd->cleanup();
+    delete thd;
+    ndb_binlog_thread_running= -1;
+    pthread_mutex_unlock(&injector_mutex);
+    pthread_cond_signal(&injector_cond);
+    my_thread_end();
+    pthread_exit(0);
+    DBUG_RETURN(NULL);
+  }
+
+  thd->init_for_queries();
+  thd->command= COM_DAEMON;
+  injector_thd= thd;
+
+  /*
+    Set up ndb binlog
+  */
+  sql_print_information("Starting Cluster Binlog");
+
+  pthread_detach_this_thread();
+  thd->real_id= pthread_self();
+  pthread_mutex_lock(&LOCK_thread_count);
+  thd->thread_id= thread_id++;
+  threads.append(thd);
+  pthread_mutex_unlock(&LOCK_thread_count);
+  thd->lex->start_transaction_opt= 0;
+
+  if (!(ndb= new Ndb(g_ndb_cluster_connection, "")) ||
+      ndb->init())
+  {
+    sql_print_error("NDB Binlog: Getting Ndb object failed");
+    ndb_binlog_thread_running= -1;
+    pthread_mutex_unlock(&injector_mutex);
+    pthread_cond_signal(&injector_cond);
+    goto err;
+  }
+
+  /*
+    Expose global reference to our ndb object.
+
+    Used by both sql client thread and binlog thread to interact
+    with the storage
+    pthread_mutex_lock(&injector_mutex);
+  */
+  injector_ndb= ndb;
+  ndb_binlog_thread_running= 1;
+
+  /*
+    We signal the thread that started us that we've finished
+    starting up.
+  */
+  pthread_mutex_unlock(&injector_mutex);
+  pthread_cond_signal(&injector_cond);
+
+  thd->system_thread= SYSTEM_THREAD_NDBCLUSTER_BINLOG;
+  thd->version= refresh_version;
+  thd->set_time();
+  thd->main_security_ctx.host_or_ip= "";
+  thd->client_capabilities= 0;
+  my_net_init(&thd->net, 0);
+  thd->main_security_ctx.master_access= ~0;
+  thd->main_security_ctx.priv_user= 0;
+
+  thd->proc_info= "Waiting for ndbcluster to start";
+
+  pthread_mutex_lock(&injector_mutex);
+  while (!ndbcluster_util_inited)
+  {
+    /* ndb not connected yet */
+    struct timespec abstime;
+    set_timespec(abstime, 1);
+    pthread_cond_timedwait(&injector_cond, &injector_mutex, &abstime);
+    if (abort_loop)
+    {
+      pthread_mutex_unlock(&injector_mutex);
+      goto err;
+    }
+  }
+  pthread_mutex_unlock(&injector_mutex);
+
+  /*
+    Main NDB Injector loop
+  */
+
+  thd->query_id= 0; // to keep valgrind quiet
+  {
+    static char db[]= "";
+    thd->db= db;
+    open_binlog_index(thd, &binlog_tables, &binlog_index);
+    if (!(apply_status_share= ndbcluster_get_apply_status_share()))
+    {
+      sql_print_error("NDB: Could not get apply status share");
+    }
+    thd->db= db;
+  }
+
+#ifdef RUN_NDB_BINLOG_TIMER
+  Timer main_timer;
+#endif
+  for ( ; !((abort_loop || do_ndbcluster_binlog_close_connection) &&
+            ndb_latest_handled_binlog_epoch >= g_latest_trans_gci); )
+  {
+
+#ifdef RUN_NDB_BINLOG_TIMER
+    main_timer.stop();
+    sql_print_information("main_timer %ld ms",  main_timer.elapsed_ms());
+    main_timer.start();
+#endif
+
+    /*
+      now we don't want any events before next gci is complete
+    */
+    thd->proc_info= "Waiting for event from ndbcluster";
+    thd->set_time();
+
+    /* wait for event or 1000 ms */
+    Uint64 gci;
+    int res= ndb->pollEvents(1000, &gci);
+    ndb_latest_received_binlog_epoch= gci;
+
+    if ((abort_loop || do_ndbcluster_binlog_close_connection) &&
+        ndb_latest_handled_binlog_epoch >= g_latest_trans_gci)
+      break; /* Shutting down server */
+
+    if (binlog_index && binlog_index->s->version < refresh_version)
+    {
+      if (binlog_index->s->version < refresh_version)
+      {
+        close_thread_tables(thd);
+        binlog_index= 0;
+      }
+    }
+
+    if (res > 0)
+    {
+      DBUG_PRINT("info", ("pollEvents res: %d", res));
+#ifdef RUN_NDB_BINLOG_TIMER
+      Timer gci_timer, write_timer;
+      int event_count= 0;
+#endif
+      thd->proc_info= "Processing events";
+      NdbEventOperation *pOp= ndb->nextEvent();
+      Binlog_index_row row;
+      while (pOp != NULL)
+      {
+        ndb->
+          setReportThreshEventGCISlip(ndb_report_thresh_binlog_epoch_slip);
+        ndb->setReportThreshEventFreeMem(ndb_report_thresh_binlog_mem_usage);
+
+        assert(pOp->getGCI() <= ndb_latest_received_binlog_epoch);
+        if (!apply_status_share)
+        {
+          if (!(apply_status_share= ndbcluster_get_apply_status_share()))
+            sql_print_error("NDB: Could not get apply status share");
+        }
+        bzero((char*) &row, sizeof(row));
+        injector::transaction trans= inj->new_trans(thd);
+        gci= pOp->getGCI();
+        if (apply_status_share)
+        {
+          TABLE *table= apply_status_share->table;
+          MY_BITMAP b;
+          uint32 bitbuf;
+          DBUG_ASSERT(table->s->fields <= sizeof(bitbuf) * 8);
+          bitmap_init(&b, &bitbuf, table->s->fields, false);
+          bitmap_set_all(&b);
+          table->field[0]->store((longlong)::server_id);
+          table->field[1]->store((longlong)gci);
+          trans.write_row(::server_id,
+                          injector::transaction::table(table, true),
+                          &b, table->s->fields,
+                          table->record[0]);
+        }
+#ifdef RUN_NDB_BINLOG_TIMER
+        write_timer.start();
+#endif
+        do
+        {
+#ifdef RUN_NDB_BINLOG_TIMER
+          event_count++;
+#endif
+          if (pOp->hasError() &&
+              ndb_binlog_thread_handle_error(ndb, pOp, row) < 0)
+            goto err;
+
+#ifndef DBUG_OFF
+          {
+            NDB_SHARE *share= (NDB_SHARE*) pOp->getCustomData();
+            DBUG_PRINT("info",
+                       ("EVENT TYPE:%d  GCI:%lld  last applied: %lld  "
+                        "share: 0x%lx", pOp->getEventType(), gci,
+                        ndb_latest_applied_binlog_epoch, share));
+            DBUG_ASSERT(share != 0);
+          }
+#endif
+          if ((unsigned) pOp->getEventType() <
+              (unsigned) NDBEVENT::TE_FIRST_NON_DATA_EVENT)
+            ndb_binlog_thread_handle_data_event(ndb, pOp, row, trans);
+          else
+            ndb_binlog_thread_handle_non_data_event(ndb, pOp, row);
+
+          pOp= ndb->nextEvent();
+        } while (pOp && pOp->getGCI() == gci);
+
+        /*
+          note! pOp is not referring to an event in the next epoch
+          or is == 0
+	*/
+#ifdef RUN_NDB_BINLOG_TIMER
+        write_timer.stop();
+#endif
+
+        if (row.n_inserts || row.n_updates
+            || row.n_deletes || row.n_schemaops)
+        {
+          injector::transaction::binlog_pos start= trans.start_pos();
+          if (int r= trans.commit())
+          {
+            sql_print_error("NDB binlog:"
+                            "Error during COMMIT of GCI. Error: %d",
+                            r);
+            /* TODO: Further handling? */
+          }
+          row.gci= gci;
+          row.master_log_file= start.file_name();
+          row.master_log_pos= start.file_pos();
+
+          DBUG_PRINT("info",("COMMIT gci %lld",gci));
+          if (ndb_update_binlog_index)
+            ndb_add_binlog_index(thd, &row);
+          ndb_latest_applied_binlog_epoch= gci;
+        }
+        else
+          trans.commit();
+        ndb_latest_handled_binlog_epoch= gci;
+#ifdef RUN_NDB_BINLOG_TIMER
+        gci_timer.stop();
+        sql_print_information("gci %ld event_count %d write time "
+                              "%ld(%d e/s), total time %ld(%d e/s)",
+                              (ulong)gci, event_count,
+                              write_timer.elapsed_ms(),
+                              event_count / write_timer.elapsed_ms(),
+                              gci_timer.elapsed_ms(),
+                              event_count / gci_timer.elapsed_ms());
+#endif
+      }
+    }
+    ndb_latest_handled_binlog_epoch= ndb_latest_received_binlog_epoch;
+  }
+err:
+  DBUG_PRINT("info",("Shutting down cluster binlog thread"));
+  close_thread_tables(thd);
+  pthread_mutex_lock(&injector_mutex);
+  /* don't mess with the injector_ndb anymore from other threads */
+  injector_ndb= 0;
+  pthread_mutex_unlock(&injector_mutex);
+  thd->db= 0; // as not to try to free memory
+  sql_print_information("Stopping Cluster Binlog");
+
+  if (apply_status_share)
+    free_share(&apply_status_share);
+
+  /* remove all event operations */
+  if (ndb)
+  {
+    NdbEventOperation *op;
+    DBUG_PRINT("info",("removing all event operations"));
+    while ((op= ndb->getEventOperation()))
+    {
+      DBUG_PRINT("info",("removing event operation on %s",
+                         op->getEvent()->getName()));
+      NDB_SHARE *share= (NDB_SHARE*) op->getCustomData();
+      free_share(&share);
+      ndb->dropEventOperation(op);
+    }
+    delete ndb;
+    ndb= 0;
+  }
+
+  net_end(&thd->net);
+  thd->cleanup();
+  delete thd;
+
+  ndb_binlog_thread_running= -1;
+  (void) pthread_cond_signal(&injector_cond);
+
+  DBUG_PRINT("exit", ("ndb_binlog_thread"));
+  my_thread_end();
+
+  pthread_exit(0);
+  DBUG_RETURN(NULL);
+}
+#endif /* HAVE_NDB_BINLOG */
+

 /*
   Create a table in NDB Cluster

--- 1.100/sql/ha_ndbcluster.h	2005-11-25 09:08:29 +01:00
+++ 1.101/sql/ha_ndbcluster.h	2005-12-29 20:48:09 +01:00
@@ -25,6 +25,10 @@
 #pragma interface                       /* gcc class implementation */
 #endif

+#ifdef HAVE_ROW_BASED_REPLICATION
+#define HAVE_NDB_BINLOG
+#endif
+
 #include <NdbApi.hpp>
 #include <ndbapi_limits.h>

@@ -86,6 +90,14 @@
   ulonglong commit_count;
   char *db;
   char *table_name;
+#ifdef HAVE_NDB_BINLOG
+  NDB_SHARE_STATE state;
+  NdbEventOperation *op;
+  NdbEventOperation *op_old; // for rename table
+  char *old_names; // for rename table
+  TABLE *table;
+  NdbValue *ndb_value[2];
+#endif
 } NDB_SHARE;

 typedef enum ndb_item_type {
@@ -551,6 +563,9 @@

   bool low_byte_first() const;
   bool has_transactions();
+
+  virtual bool is_injective() const { return true; }
+
   const char* index_type(uint key_number);

   double scan_time();
@@ -786,3 +801,10 @@

 bool ndbcluster_show_status(THD*,stat_print_fn *,enum ha_stat_type);

+#ifdef HAVE_NDB_BINLOG
+int ndbcluster_reset_logs(THD *thd);
+int ndbcluster_binlog_index_purge_file(THD *thd, const char *file);
+void ndbcluster_reset_slave(THD *thd);
+void ndbcluster_binlog_wait(THD *thd);
+int ndbcluster_binlog_end();
+#endif

--- 1.86/mysql-test/r/show_check.result	2005-12-09 12:39:38 +01:00
+++ 1.87/mysql-test/r/show_check.result	2005-12-29 20:48:07 +01:00
@@ -53,6 +53,7 @@
 show databases;
 Database
 information_schema
+cluster_replication
 mysql
 test
 show databases like "test%";

--- 1.149/sql/set_var.cc	2005-11-24 09:59:02 +01:00
+++ 1.150/sql/set_var.cc	2005-12-29 20:48:13 +01:00
@@ -70,8 +70,10 @@
 extern uint innobase_flush_log_at_trx_commit;
 extern ulong innobase_fast_shutdown;
 extern long innobase_mirrored_log_groups, innobase_log_files_in_group;
-extern long innobase_log_file_size, innobase_log_buffer_size;
-extern long innobase_buffer_pool_size, innobase_additional_mem_pool_size;
+extern longlong innobase_log_file_size;
+extern long innobase_log_buffer_size;
+extern longlong innobase_buffer_pool_size;
+extern long innobase_additional_mem_pool_size;
 extern long innobase_buffer_pool_awe_mem_mb;
 extern long innobase_file_io_threads, innobase_lock_wait_timeout;
 extern long innobase_force_recovery;
@@ -298,6 +300,8 @@
                                                fix_max_relay_log_size);
 sys_var_thd_ulong	sys_max_sort_length("max_sort_length",
 					    &SV::max_sort_length);
+sys_var_thd_ulong	sys_max_sp_recursion_depth("max_sp_recursion_depth",
+                                                   &SV::max_sp_recursion_depth);
 sys_var_max_user_conn   sys_max_user_connections("max_user_connections");
 sys_var_thd_ulong	sys_max_tmp_tables("max_tmp_tables",
 					   &SV::max_tmp_tables);
@@ -310,6 +314,8 @@
 sys_var_thd_ulonglong	sys_myisam_max_sort_file_size("myisam_max_sort_file_size", \
&SV::myisam_max_sort_file_size, fix_myisam_max_sort_file_size, 1);  sys_var_thd_ulong \
sys_myisam_repair_threads("myisam_repair_threads", &SV::myisam_repair_threads);  \
sys_var_thd_ulong	sys_myisam_sort_buffer_size("myisam_sort_buffer_size", \
&SV::myisam_sort_buff_size); +sys_var_bool_ptr	sys_myisam_use_mmap("myisam_use_mmap",
+                                            &opt_myisam_use_mmap);

 sys_var_thd_enum        sys_myisam_stats_method("myisam_stats_method",
                                                 &SV::myisam_stats_method,
@@ -419,7 +425,9 @@
                                                &SV::sync_replication_timeout);
 #endif
 sys_var_bool_ptr	sys_sync_frm("sync_frm", &opt_sync_frm);
-sys_var_long_ptr	sys_table_cache_size("table_cache",
+sys_var_long_ptr	sys_table_def_size("table_definition_cache",
+                                           &table_def_size);
+sys_var_long_ptr	sys_table_cache_size("table_open_cache",
 					     &table_cache_size);
 sys_var_long_ptr	sys_table_lock_wait_timeout("table_lock_wait_timeout",
                                                     &table_lock_wait_timeout);
@@ -781,6 +789,8 @@
   {sys_max_relay_log_size.name, (char*) &sys_max_relay_log_size,    SHOW_SYS},
   {sys_max_seeks_for_key.name,  (char*) &sys_max_seeks_for_key,	    SHOW_SYS},
   {sys_max_sort_length.name,	(char*) &sys_max_sort_length,	    SHOW_SYS},
+  {sys_max_sp_recursion_depth.name,
+    (char*) &sys_max_sp_recursion_depth, SHOW_SYS},
   {sys_max_tmp_tables.name,	(char*) &sys_max_tmp_tables,	    SHOW_SYS},
   {sys_max_user_connections.name,(char*) &sys_max_user_connections, SHOW_SYS},
   {sys_max_write_lock_count.name, (char*) &sys_max_write_lock_count,SHOW_SYS},
@@ -794,6 +804,7 @@
   {sys_myisam_sort_buffer_size.name, (char*) &sys_myisam_sort_buffer_size, \
SHOW_SYS},

   {sys_myisam_stats_method.name, (char*) &sys_myisam_stats_method, SHOW_SYS},
+  {sys_myisam_use_mmap.name, (char*) &sys_myisam_use_mmap, SHOW_SYS},

 #ifdef __NT__
   {"named_pipe",	      (char*) &opt_enable_named_pipe,       SHOW_MY_BOOL},
@@ -890,8 +901,9 @@
 #ifdef HAVE_TZNAME
   {"system_time_zone",        system_time_zone,                     SHOW_CHAR},
 #endif
-  {"table_cache",             (char*) &table_cache_size,            SHOW_LONG},
+  {"table_definition_cache",  (char*) &table_def_size,              SHOW_LONG},
   {"table_lock_wait_timeout", (char*) &table_lock_wait_timeout,     SHOW_LONG },
+  {"table_open_cache",        (char*) &table_cache_size,            SHOW_LONG},
   {sys_table_type.name,	      (char*) &sys_table_type,	            SHOW_SYS},
   {sys_thread_cache_size.name,(char*) &sys_thread_cache_size,       SHOW_SYS},
 #ifdef HAVE_THR_SETCONCURRENCY

--- 1.72/libmysqld/Makefile.am	2005-11-24 09:59:00 +01:00
+++ 1.73/libmysqld/Makefile.am	2005-12-29 20:48:05 +01:00
@@ -45,10 +45,10 @@
 sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \
 	ha_heap.cc ha_myisam.cc ha_myisammrg.cc handler.cc sql_handler.cc \
 	hostname.cc init.cc password.c \
-	rpl_filter.cc \
 	item.cc item_buff.cc item_cmpfunc.cc item_create.cc \
 	item_func.cc item_strfunc.cc item_sum.cc item_timefunc.cc \
 	item_geofunc.cc item_uniq.cc item_subselect.cc item_row.cc\
+	item_xmlfunc.cc \
 	key.cc lock.cc log.cc log_event.cc sql_state.c \
 	protocol.cc net_serv.cc opt_range.cc \
 	opt_sum.cc procedure.cc records.cc sql_acl.cc \
@@ -145,19 +145,19 @@
 link_sources:
 	  set -x; \
 	  for f in $(sqlsources); do \
-	    rm -f $(srcdir)/$$f; \
-	    @LN_CP_F@ $(srcdir)/../sql/$$f $(srcdir)/$$f; \
+	    rm -f $$f; \
+	    @LN_CP_F@ $(top_srcdir)/sql/$$f $$f; \
 	  done; \
 	  for f in $(libmysqlsources); do \
-	    rm -f $(srcdir)/$$f; \
-	    @LN_CP_F@ $(srcdir)/../libmysql/$$f $(srcdir)/$$f; \
+	    rm -f $$f; \
+	    @LN_CP_F@ $(top_srcdir)/libmysql/$$f $$f; \
 	  done; \
 	  for f in $(sqlstoragesources); do \
-	    rm -f $(srcdir)/$$f; \
-	    @LN_CP_F@ `find $(srcdir)/../sql -name $$f` $(srcdir)/$$f; \
+	    rm -f $$f; \
+	    @LN_CP_F@ `find $(srcdir)/../sql -name $$f` $$f; \
 	  done; \
-	  rm -f $(srcdir)/client_settings.h; \
-	  @LN_CP_F@ $(srcdir)/../libmysql/client_settings.h $(srcdir)/client_settings.h;
+	  rm -f client_settings.h; \
+	  @LN_CP_F@ $(top_srcdir)/libmysql/client_settings.h client_settings.h


 clean-local:

--- 1.6/mysql-test/r/ndb_multi.result	2005-11-24 09:59:01 +01:00
+++ 1.7/mysql-test/r/ndb_multi.result	2005-12-29 20:48:07 +01:00
@@ -36,6 +36,7 @@
 Error	1296	Got error 241 'Invalid schema object version' from NDB
 Error	1412	Table definition has changed, please retry transaction
 Error	1105	Unknown error
+flush table t1;
 select * from t1;
 a
 2

--- 1.7/mysql-test/t/ndb_multi.test	2005-11-24 09:59:01 +01:00
+++ 1.8/mysql-test/t/ndb_multi.test	2005-12-29 20:48:08 +01:00
@@ -44,6 +44,7 @@
 --error 1412
 select * from t1;
 show warnings;
+flush table t1;
 select * from t1;

 # Connect to server2 and use the tables from there

--- 1.165/client/mysqltest.c	2005-12-12 19:17:51 +01:00
+++ 1.166/client/mysqltest.c	2005-12-29 20:48:05 +01:00
@@ -827,7 +827,7 @@
 int var_set(const char *var_name, const char *var_name_end,
             const char *var_val, const char *var_val_end)
 {
-  int digit;
+  int digit, result, env_var= 0;
   VAR* v;
   DBUG_ENTER("var_set");
   DBUG_PRINT("enter", ("var_name: '%.*s' = '%.*s' (length: %d)",
@@ -835,11 +835,11 @@
                        (int) (var_val_end - var_val), var_val,
                        (int) (var_val_end - var_val)));

-  if (*var_name++ != '$')
-  {
-    var_name--;
-    die("Variable name in %s does not start with '$'", var_name);
-  }
+  if (*var_name != '$')
+    env_var= 1;
+  else
+    var_name++;
+
   digit = *var_name - '0';
   if (!(digit < 10 && digit >= 0))
   {
@@ -847,7 +847,23 @@
   }
   else
     v = var_reg + digit;
-  DBUG_RETURN(eval_expr(v, var_val, (const char**)&var_val_end));
+
+  result= eval_expr(v, var_val, (const char**) &var_val_end);
+
+  if (env_var)
+  {
+    char buf[1024];
+    memcpy(buf, v->name, v->name_len);
+    buf[v->name_len]= 0;
+    if (v->int_dirty)
+    {
+      sprintf(v->str_val, "%d", v->int_val);
+      v->int_dirty= 0;
+      v->str_val_len= strlen(v->str_val);
+    }
+    setenv(buf, v->str_val, 1);
+  }
+  DBUG_RETURN(result);
 }


@@ -1452,6 +1468,91 @@
   rpl_parse = mysql_rpl_parse_enabled(mysql);
   mysql_disable_rpl_parse(mysql);

+#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
+  /*
+     Wait for ndb binlog to be up-to-date with all changes
+     done on the local mysql server
+  */
+  {
+    ulong have_ndbcluster;
+    if (mysql_query(mysql, query= "show variables like 'have_ndbcluster'"))
+      die("At line %u: failed in %s: %d: %s", start_lineno, query,
+          mysql_errno(mysql), mysql_error(mysql));
+    if (!(last_result= res= mysql_store_result(mysql)))
+      die("line %u: mysql_store_result() retuned NULL for '%s'", start_lineno,
+          query);
+    if (!(row= mysql_fetch_row(res)))
+      die("line %u: empty result in %s", start_lineno, query);
+
+    have_ndbcluster= strcmp("YES", row[1]) == 0;
+    mysql_free_result(res);
+    last_result= 0;
+
+    if (have_ndbcluster)
+    {
+      ulonglong epoch, tmp_epoch= 0;
+      int count= 0;
+
+      do
+      {
+        const char binlog[]= "binlog";
+        const char latest_trans_epoch[]+          "latest_trans_epoch=";
+        const char latest_applied_binlog_epoch[]+          \
"latest_applied_binlog_epoch="; +        if (count)
+          sleep(1);
+        if (mysql_query(mysql, query= "show engine ndb status"))
+          die("At line %u: failed in '%s': %d: %s", start_lineno, query,
+              mysql_errno(mysql), mysql_error(mysql));
+        if (!(last_result= res= mysql_store_result(mysql)))
+          die("line %u: mysql_store_result() retuned NULL for '%s'",
+              start_lineno, query);
+        while ((row= mysql_fetch_row(res)))
+        {
+          if (strcmp(row[1], binlog) == 0)
+          {
+            const char *status= row[2];
+            /* latest_trans_epoch */
+            if (count == 0)
+            {
+              while (*status && strncmp(status, latest_trans_epoch,
+                                        sizeof(latest_trans_epoch)-1))
+                status++;
+              if (*status)
+              {
+                status+= sizeof(latest_trans_epoch)-1;
+                epoch= strtoull(status, (char**) 0, 10);
+              }
+              else
+                die("line %u: result does not contain '%s' in '%s'",
+                    start_lineno, latest_trans_epoch, query);
+            }
+            /* latest_applied_binlog_epoch */
+            while (*status && strncmp(status, latest_applied_binlog_epoch,
+                                      sizeof(latest_applied_binlog_epoch)-1))
+              status++;
+            if (*status)
+            {
+              status+= sizeof(latest_applied_binlog_epoch)-1;
+              tmp_epoch= strtoull(status, (char**) 0, 10);
+            }
+            else
+              die("line %u: result does not contain '%s' in '%s'",
+                  start_lineno, latest_applied_binlog_epoch, query);
+            break;
+          }
+        }
+        mysql_free_result(res);
+        if (!row)
+          die("line %u: result does not contain '%s' in '%s'",
+              start_lineno, binlog, query);
+        last_result=0;
+        count++;
+      } while (tmp_epoch < epoch && count <= 3);
+    }
+  }
+#endif
   if (mysql_query(mysql, query= "show master status"))
     die("failed in show master status: %d: %s",
 	mysql_errno(mysql), mysql_error(mysql));
@@ -1502,7 +1603,8 @@
   while (*p && (*p != '=') && !my_isspace(charset_info,*p))
     p++;
   var_name_end= p;
-  if (var_name+1 == var_name_end)
+  if (var_name == var_name_end ||
+      (var_name+1 == var_name_end && *var_name == '$'))
     die("Missing variable name in let");
   while (my_isspace(charset_info,*p))
     p++;

--- 1.168/mysql-test/t/sp.test	2005-12-13 16:10:48 +01:00
+++ 1.169/mysql-test/t/sp.test	2005-12-29 20:48:08 +01:00
@@ -1,3 +1,7 @@
+# temporary disable for ndb until test is made independent on
+# rows in show processlist
+-- source include/not_ndb.inc
+
 #
 # Basic stored PROCEDURE tests
 #

--- 1.147/sql/sql_repl.cc	2005-12-06 18:50:14 +01:00
+++ 1.148/sql/sql_repl.cc	2005-12-29 20:48:15 +01:00
@@ -972,6 +972,9 @@
     error=1;
     goto err;
   }
+
+  ha_reset_slave(thd);
+
   // delete relay logs, clear relay log coordinates
   if ((error= purge_relay_logs(&mi->rli, thd,
 			       1 /* just reset */,
@@ -1315,6 +1318,11 @@
   if (protocol->send_fields(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
+
+  /* Wait for handlers to insert any pending information
+     into the binlog
+  */
+  ha_binlog_wait(thd);

   if (mysql_bin_log.is_open())
   {

--
MySQL Internals Mailing List
For list archives: http://lists.mysql.com/internals
To unsubscribe:    http://lists.mysql.com/internals?unsub=mysql-internals@progressive-comp.com



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic