Below is the list of changes that have just been committed into a local
5.1 repository of tomas. When tomas 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.1948 05/11/09 15:21:19 tomas@stripped +48 -0
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
sql/sql_update.cc
1.176 05/11/09 15:21:12 tomas@stripped +2 -4
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
sql/sql_table.cc
1.277 05/11/09 15:21:12 tomas@stripped +34 -10
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
sql/sql_delete.cc
1.159 05/11/09 15:21:12 tomas@stripped +1 -3
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
sql/sql_class.h
1.268 05/11/09 15:21:12 tomas@stripped +9 -0
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
sql/slave.h
1.93 05/11/09 15:21:12 tomas@stripped +1 -2
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
sql/slave.cc
1.257 05/11/09 15:21:12 tomas@stripped +13 -13
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
sql/share/errmsg.txt
1.53 05/11/09 15:21:12 tomas@stripped +4 -7
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
sql/set_var.cc
1.143 05/11/09 15:21:12 tomas@stripped +1 -0
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
sql/mysqld.cc
1.489 05/11/09 15:21:12 tomas@stripped +7 -0
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
sql/mysql_priv.h
1.342 05/11/09 15:21:12 tomas@stripped +0 -0
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
sql/log.cc
1.175 05/11/09 15:21:12 tomas@stripped +1 -4
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
sql/handler.h
1.166 05/11/09 15:21:12 tomas@stripped +4 -4
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
sql/handler.cc
1.192 05/11/09 15:21:11 tomas@stripped +50 -48
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
sql/Makefile.am
1.121 05/11/09 15:21:11 tomas@stripped +1 -0
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
mysql-test/Makefile.am
1.66 05/11/09 15:21:11 tomas@stripped +0 -0
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
configure.in
1.309 05/11/09 15:21:11 tomas@stripped +1 -0
manual merge, fresh mysql-5.0-wl1012 -> mysql-5.1-new
storage/ndb/src/mgmsrv/Services.cpp
1.58 05/11/09 14:48:17 tomas@stripped +0 -0
Auto merged
sql/table.h
1.116 05/11/09 14:48:17 tomas@stripped +0 -0
Auto merged
sql/table.cc
1.187 05/11/09 14:48:17 tomas@stripped +0 -1
Auto merged
sql/sql_udf.cc
1.58 05/11/09 14:48:17 tomas@stripped +0 -0
Auto merged
sql/sql_show.cc
1.276 05/11/09 14:48:17 tomas@stripped +0 -0
Auto merged
sql/sql_select.cc
1.368 05/11/09 14:48:16 tomas@stripped +0 -0
Auto merged
sql/sql_repl.h
1.40 05/11/09 14:48:16 tomas@stripped +0 -0
Auto merged
sql/sql_prepare.cc
1.154 05/11/09 14:48:16 tomas@stripped +0 -0
Auto merged
sql/sql_parse.cc
1.481 05/11/09 14:48:16 tomas@stripped +0 -0
Auto merged
sql/sql_load.cc
1.86 05/11/09 14:48:15 tomas@stripped +0 -0
Auto merged
sql/sql_insert.cc
1.174 05/11/09 14:48:14 tomas@stripped +0 -0
Auto merged
sql/sql_class.cc
1.220 05/11/09 14:48:14 tomas@stripped +0 -0
Auto merged
sql/sql_base.cc
1.278 05/11/09 14:48:14 tomas@stripped +0 -0
Auto merged
sql/sql_acl.cc
1.159 05/11/09 14:48:14 tomas@stripped +0 -0
Auto merged
sql/sp_head.cc
1.195 05/11/09 14:48:14 tomas@stripped +0 -0
Auto merged
sql/sp.cc
1.94 05/11/09 14:48:13 tomas@stripped +0 -0
Auto merged
sql/log_event.cc
1.190 05/11/09 14:48:13 tomas@stripped +0 -1
Auto merged
sql/ha_innodb.cc
1.236 05/11/09 14:48:12 tomas@stripped +0 -0
Auto merged
mysys/Makefile.am
1.72 05/11/09 14:48:12 tomas@stripped +0 -0
Auto merged
mysql-test/t/disabled.def
1.12 05/11/09 14:48:12 tomas@stripped +0 -0
Auto merged
mysql-test/mysql-test-run.sh
1.279 05/11/09 14:48:12 tomas@stripped +0 -0
Auto merged
mysql-test/mysql-test-run.pl
1.41 05/11/09 14:48:12 tomas@stripped +0 -0
Auto merged
libmysqld/Makefile.am
1.71 05/11/09 14:48:12 tomas@stripped +0 -0
Auto merged
include/my_global.h
1.114 05/11/09 14:48:11 tomas@stripped +0 -0
Auto merged
include/my_base.h
1.74 05/11/09 14:48:11 tomas@stripped +0 -0
Auto merged
include/Makefile.am
1.56 05/11/09 14:48:11 tomas@stripped +0 -0
Auto merged
Makefile.am
1.79 05/11/09 14:48:11 tomas@stripped +0 -0
Auto merged
BitKeeper/etc/config
1.25 05/11/09 14:48:11 tomas@stripped +0 -0
Auto merged
BitKeeper/deleted/.del-rpl_filter.h
1.2 05/11/09 14:48:11 tomas@stripped +0 -0
Delete: sql/rpl_filter.h
BUILD/SETUP.sh
1.54 05/11/09 14:48:11 tomas@stripped +0 -0
Auto merged
BitKeeper/deleted/.del-rpl_filter.cc
1.3 05/11/09 14:48:09 tomas@stripped +0 -0
Delete: sql/rpl_filter.cc
storage/ndb/src/mgmsrv/Services.cpp
1.45.11.3 05/11/09 14:47:52 tomas@stripped +0 -0
Merge rename: ndb/src/mgmsrv/Services.cpp -> storage/ndb/src/mgmsrv/Services.cpp
BitKeeper/etc/gone
1.109 05/11/09 14:47:38 tomas@stripped +0 -0
auto-union
# 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: tomas
# Host: poseidon.ndb.mysql.com
# Root: /home/tomas/mysql-5.1-wl1012-new/RESYNC
--- 1.78/Makefile.am 2005-11-07 16:24:33 +01:00
+++ 1.79/Makefile.am 2005-11-09 14:48:11 +01:00
@@ -110,24 +110,32 @@
test:
cd mysql-test ; \
- ./mysql-test-run && \
- ./mysql-test-run --ps-protocol
+ ./mysql-test-run.pl --mysqld=--binlog-format=statement $(MYSQL_TEST_RUN_ARGS) && \
+ ./mysql-test-run.pl --ps-protocol --mysqld=--binlog-format=statement $(MYSQL_TEST_RUN_ARGS) &&\
+ ./mysql-test-run.pl --mysqld=--binlog-format=row --do-test=rpl $(MYSQL_TEST_RUN_ARGS) && \
+ ./mysql-test-run.pl --ps-protocol --mysqld=--binlog-format=row --do-test=rpl $(MYSQL_TEST_RUN_ARGS)
test-force:
- cd mysql-test; \
- ./mysql-test-run --force && \
- ./mysql-test-run --ps-protocol --force
+ cd mysql-test ; \
+ ./mysql-test-run --force --mysqld=--binlog-format=statement $(MYSQL_TEST_RUN_ARGS) ; \
+ ./mysql-test-run --ps-protocol --force --mysqld=--binlog-format=statement $(MYSQL_TEST_RUN_ARGS) ; \
+ ./mysql-test-run --force --mysqld=--binlog-format=row --do-test=rpl $(MYSQL_TEST_RUN_ARGS) ; \
+ ./mysql-test-run --ps-protocol --force --mysqld=--binlog-format=statement --do-test=rpl $(MYSQL_TEST_RUN_ARGS)
# We are testing a new Perl version of the test script
test-pl:
- cd mysql-test; \
- ./mysql-test-run.pl && \
- ./mysql-test-run.pl --ps-protocol
+ cd mysql-test ; \
+ ./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --mysqld=--binlog-format=statement && \
+ ./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --ps-protocol --mysqld=--binlog-format=statement $(MYSQL_TEST_RUN_ARGS) && \
+ ./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --mysqld=--binlog-format=row --do-test=rpl $(MYSQL_TEST_RUN_ARGS) && \
+ ./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --ps-protocol --mysqld=--binlog-format=row --do-test=rpl $(MYSQL_TEST_RUN_ARGS)
test-force-pl:
- cd mysql-test; \
- ./mysql-test-run.pl --force && \
- ./mysql-test-run.pl --ps-protocol --force
+ cd mysql-test ; \
+ ./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --force --mysqld=--binlog-format=statement
+ ./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --ps-protocol --force --mysqld=--binlog-format=statement
+ ./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --force --mysqld=--binlog-format=row --do-test=rpl
+ ./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --ps-protocol --force --mysqld=--binlog-format=statement --do-test=rpl
# Don't update the files from bitkeeper
%::SCCS/s.%
--- 1.308/configure.in 2005-11-08 07:36:52 +01:00
+++ 1.309/configure.in 2005-11-09 15:21:11 +01:00
@@ -37,6 +37,7 @@
sinclude(config/ac-macros/misc.m4)
sinclude(config/ac-macros/openssl.m4)
sinclude(config/ac-macros/readline.m4)
+sinclude(config/ac-macros/replication.m4)
sinclude(config/ac-macros/yassl.m4)
sinclude(config/ac-macros/zlib.m4)
@@ -1799,7 +1800,8 @@
AC_FUNC_UTIME_NULL
AC_FUNC_VPRINTF
-AC_CHECK_FUNCS(alarm bcmp bfill bmove bzero chsize cuserid fchmod fcntl \
+AC_CHECK_FUNCS(alarm bcmp bfill bmove bsearch bzero \
+ chsize cuserid fchmod fcntl \
fconvert fdatasync finite fpresetsticky fpsetmask fsync ftruncate \
getcwd gethostbyaddr_r gethostbyname_r getpass getpassphrase getpwnam \
getpwuid getrlimit getrusage getwd gmtime_r index initgroups isnan \
@@ -2345,6 +2347,7 @@
AC_SUBST(readline_h_ln_cmd)
MYSQL_CHECK_BIG_TABLES
+MYSQL_CHECK_REPLICATION
MYSQL_STORAGE_ENGINE(innobase,,innodb,,,,storage/innobase,ha_innodb.o,[ dnl
\$(top_builddir)/storage/innobase/usr/libusr.a dnl
--- 1.55/include/Makefile.am 2005-11-05 12:20:31 +01:00
+++ 1.56/include/Makefile.am 2005-11-09 14:48:11 +01:00
@@ -30,7 +30,8 @@
my_nosys.h my_alarm.h queues.h rijndael.h sha1.h \
my_aes.h my_tree.h my_trie.h hash.h thr_alarm.h \
thr_lock.h t_ctype.h violite.h md5.h base64.h \
- mysql_version.h.in my_handler.h my_time.h decimal.h
+ mysql_version.h.in my_handler.h my_time.h decimal.h \
+ my_vle.h
# mysql_version.h are generated
CLEANFILES = mysql_version.h my_config.h readline
--- 1.73/include/my_base.h 2005-11-05 12:20:31 +01:00
+++ 1.74/include/my_base.h 2005-11-09 14:48:11 +01:00
@@ -309,8 +309,9 @@
#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_LAST 159 /*Copy last error nr.*/
+#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.278/mysql-test/mysql-test-run.sh 2005-11-04 21:09:53 +01:00
+++ 1.279/mysql-test/mysql-test-run.sh 2005-11-09 14:48:12 +01:00
@@ -240,6 +240,8 @@
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=""
@@ -796,12 +798,13 @@
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_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"
MYSQL_IMPORT="$MYSQL_IMPORT -uroot --socket=$MASTER_MYSOCK --password=$DBPASSWD $EXTRA_MYSQLDUMP_OPT"
MYSQL_FIX_SYSTEM_TABLES="$MYSQL_FIX_SYSTEM_TABLES --no-defaults --host=localhost --port=$MASTER_MYPORT --socket=$MASTER_MYSOCK --user=root --password=$DBPASSWD --basedir=$BASEDIR --bindir=$CLIENT_BINDIR --verbose"
MYSQL="$MYSQL --no-defaults --host=localhost --port=$MASTER_MYPORT --socket=$MASTER_MYSOCK --user=root --password=$DBPASSWD"
-export MYSQL MYSQL_CHECK MYSQL_DUMP MYSQL_SHOW MYSQL_BINLOG MYSQL_FIX_SYSTEM_TABLES MYSQL_IMPORT
+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 NDB_TOOLS_DIR
export NDB_MGM
--- 1.71/mysys/Makefile.am 2005-11-04 21:09:53 +01:00
+++ 1.72/mysys/Makefile.am 2005-11-09 14:48:12 +01:00
@@ -35,6 +35,7 @@
mf_tempdir.c my_lock.c mf_brkhant.c my_alarm.c \
my_malloc.c my_realloc.c my_once.c mulalloc.c \
my_alloc.c safemalloc.c my_new.cc \
+ my_vle.c \
my_fopen.c my_fstream.c my_getsystime.c \
my_error.c errors.c my_div.c my_messnc.c \
mf_format.c mf_same.c mf_dirname.c mf_fn_ext.c \
--- 1.120/sql/Makefile.am 2005-11-08 06:40:58 +01:00
+++ 1.121/sql/Makefile.am 2005-11-09 15:21:11 +01:00
@@ -51,6 +51,7 @@
procedure.h sql_class.h sql_lex.h sql_list.h \
sql_manager.h sql_map.h sql_string.h unireg.h \
sql_error.h field.h handler.h mysqld_suffix.h \
+ rpl_tblmap.h rpl_filter.h \
ha_heap.h ha_myisam.h ha_myisammrg.h ha_partition.h \
opt_range.h protocol.h \
sql_select.h structs.h table.h sql_udf.h hash_filo.h\
@@ -82,6 +83,7 @@
unireg.cc des_key_file.cc \
discover.cc time.cc opt_range.cc opt_sum.cc \
records.cc filesort.cc handler.cc \
+ rpl_tblmap.cc rpl_filter.cc \
ha_heap.cc ha_myisam.cc ha_myisammrg.cc \
sql_db.cc sql_table.cc sql_rename.cc sql_crypt.cc \
sql_load.cc mf_iocache.cc field_conv.cc sql_show.cc \
--- 1.191/sql/handler.cc 2005-11-07 16:24:40 +01:00
+++ 1.192/sql/handler.cc 2005-11-09 15:21:11 +01:00
@@ -22,6 +22,7 @@
#endif
#include "mysql_priv.h"
+#include "rpl_filter.h"
#include "ha_heap.h"
#include "ha_myisam.h"
#include "ha_myisammrg.h"
@@ -40,6 +41,8 @@
extern handlerton *sys_table_types[];
+#define BITMAP_STACKBUF_SIZE (128/8)
+
/* static functions defined in this file */
static SHOW_COMP_OPTION have_yes= SHOW_OPTION_YES;
@@ -679,6 +682,83 @@
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=2967
+ 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 */
/*
@@ -1775,6 +1855,9 @@
my_error(ER_NO_SUCH_TABLE, MYF(0), db, table->alias);
break;
}
+ case HA_ERR_RBR_LOGGING_FAILED:
+ textno= ER_BINLOG_ROW_LOGGING_FAILED;
+ break;
default:
{
/* The error was "unknown" to this function.
@@ -2567,6 +2650,102 @@
send_eof(thd);
return FALSE;
}
+
+/*
+ Function to check if the conditions for row-based binlogging is
+ correct for the table.
+
+ 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)
+*/
+
+#ifdef HAVE_ROW_BASED_REPLICATION
+static bool check_table_binlog_row_based(TABLE *table)
+{
+ return
+ binlog_row_based &&
+ (table->s->tmp_table == NO_TMP_TABLE);
+}
+
+template<class RowsEventT> int binlog_log_row(TABLE* table,
+ const byte *before_record,
+ const byte *after_record)
+{
+ bool error= 0;
+ if (check_table_binlog_row_based(table))
+ {
+ MY_BITMAP cols;
+ /* Potential buffer on the stack for the bitmap */
+ uchar bitbuf[BITMAP_STACKBUF_SIZE];
+ uint n_fields= table->s->fields;
+ my_bool use_bitbuf= n_fields <= sizeof(bitbuf)*8;
+ if (likely(!(error= bitmap_init(&cols,
+ use_bitbuf ? bitbuf : NULL,
+ (n_fields + 7) & ~7UL,
+ false))))
+ {
+ bitmap_set_all(&cols);
+ error=
+ RowsEventT::binlog_row_logging_function(current_thd, table,
+ table->file->has_transactions(),
+ &cols, table->s->fields,
+ before_record, after_record);
+ if (!use_bitbuf)
+ bitmap_free(&cols);
+ }
+ }
+ return error ? HA_ERR_RBR_LOGGING_FAILED : 0;
+}
+
+
+/*
+ Instantiate the versions we need for the above template function, because we
+ have -fno-implicit-template as compiling option.
+*/
+
+template int binlog_log_row<Write_rows_log_event>(TABLE *, const byte *, const byte *);
+template int binlog_log_row<Delete_rows_log_event>(TABLE *, const byte *, const byte *);
+template int binlog_log_row<Update_rows_log_event>(TABLE *, const byte *, const byte *);
+
+#endif /* HAVE_ROW_BASED_REPLICATION */
+
+int handler::ha_write_row(byte *buf)
+{
+ int error;
+ if (likely(!(error= write_row(buf))))
+ {
+#ifdef HAVE_ROW_BASED_REPLICATION
+ error= binlog_log_row<Write_rows_log_event>(table, 0, buf);
+#endif
+ }
+ return error;
+}
+
+int handler::ha_update_row(const byte *old_data, byte *new_data)
+{
+ int error;
+ if (likely(!(error= update_row(old_data, new_data))))
+ {
+#ifdef HAVE_ROW_BASED_REPLICATION
+ error= binlog_log_row<Update_rows_log_event>(table, old_data, new_data);
+#endif
+ }
+ return error;
+}
+
+int handler::ha_delete_row(const byte *buf)
+{
+ int error;
+ if (likely(!(error= delete_row(buf))))
+ {
+#ifdef HAVE_ROW_BASED_REPLICATION
+ error= binlog_log_row<Delete_rows_log_event>(table, buf, 0);
+#endif
+ }
+ return error;
+}
#ifdef HAVE_REPLICATION
--- 1.165/sql/handler.h 2005-11-07 16:24:40 +01:00
+++ 1.166/sql/handler.h 2005-11-09 15:21:12 +01:00
@@ -73,6 +73,13 @@
*/
#define HA_CAN_INSERT_DELAYED (1 << 14)
#define HA_PRIMARY_KEY_IN_READ_INDEX (1 << 15)
+/*
+ If HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS is set, it means that the engine can
+ do this: the position of an arbitrary record can be retrieved using
+ position() when the table has a primary key, effectively allowing random
+ access on the table based on a given record.
+*/
+#define HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS (1 << 16)
#define HA_NOT_DELETE_WITH_CACHE (1 << 18)
#define HA_NO_PREFIX_CHAR_KEYS (1 << 20)
#define HA_CAN_FULLTEXT (1 << 21)
@@ -1037,11 +1044,10 @@
uint get_index(void) const { return active_index; }
virtual int open(const char *name, int mode, uint test_if_locked)=0;
virtual int close(void)=0;
- virtual int write_row(byte * buf) { return HA_ERR_WRONG_COMMAND; }
- virtual int update_row(const byte * old_data, byte * new_data)
- { return HA_ERR_WRONG_COMMAND; }
- virtual int delete_row(const byte * buf)
- { return HA_ERR_WRONG_COMMAND; }
+ virtual int ha_write_row(byte * buf);
+ virtual int ha_update_row(const byte * old_data, byte * new_data);
+ virtual int ha_delete_row(const byte * buf);
+
/*
SYNOPSIS
start_bulk_update()
@@ -1387,6 +1393,30 @@
virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info,
uint table_changes)
{ return COMPATIBLE_DATA_NO; }
+
+private:
+
+ /*
+ Row-level primitives for storage engines.
+ These should be overridden by the storage engine class. To call
+ these methods, use the corresponding 'ha_*' method above.
+ */
+ friend int ndb_add_binlog_index(THD *, void *);
+
+ virtual int write_row(byte *buf)
+ {
+ return HA_ERR_WRONG_COMMAND;
+ }
+
+ virtual int update_row(const byte *old_data, byte *new_data)
+ {
+ return HA_ERR_WRONG_COMMAND;
+ }
+
+ virtual int delete_row(const byte *buf)
+ {
+ return HA_ERR_WRONG_COMMAND;
+ }
};
/* Some extern variables used with handlers */
--- 1.174/sql/log.cc 2005-11-07 16:24:41 +01:00
+++ 1.175/sql/log.cc 2005-11-09 15:21:12 +01:00
@@ -105,6 +105,7 @@
{
int error=0;
DBUG_ENTER("binlog_end_trans");
+
if (end_ev)
error= mysql_bin_log.write(thd, trans_log, end_ev);
@@ -370,6 +371,7 @@
prepared_xids(0), log_type(LOG_CLOSED), file_id(1), open_count(1),
readers_count(0), reset_pending(FALSE), write_error(FALSE), inited(FALSE),
need_start_event(TRUE),
+ m_next_table_id(0), m_table_map_version(0),
description_event_for_exec(0), description_event_for_queue(0)
{
/*
@@ -398,6 +400,7 @@
(void) pthread_mutex_destroy(&LOCK_index);
(void) pthread_mutex_destroy(&LOCK_readers);
(void) pthread_cond_destroy(&update_cond);
+ (void) pthread_mutex_destroy(&LOCK_next_table_id);
(void) pthread_cond_destroy(&reset_cond);
}
DBUG_VOID_RETURN;
@@ -445,6 +448,7 @@
(void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW);
(void) pthread_mutex_init(&LOCK_readers, MY_MUTEX_INIT_SLOW);
(void) pthread_cond_init(&update_cond, 0);
+ (void) pthread_mutex_init(&LOCK_next_table_id, MY_MUTEX_INIT_FAST);
(void) pthread_cond_init(&reset_cond, 0);
}
@@ -1723,7 +1727,11 @@
of the SQL command
*/
- if (thd)
+ /*
+ If row-based binlogging, Insert_id, Rand and other kind of "setting
+ context" events are not needed.
+ */
+ if (thd && !binlog_row_based)
{
if (thd->last_insert_id_used)
{
@@ -2341,6 +2349,51 @@
pthread_cond_broadcast(&update_cond);
DBUG_VOID_RETURN;
}
+
+#ifndef MYSQL_CLIENT
+
+ulong MYSQL_LOG::get_table_id(TABLE* table)
+{
+ 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);
+}
+
+void MYSQL_LOG::
+update_table_map_version()
+{
+ /* BUG this is an incrementation without LOCK_log mutex */
+ m_table_map_version++;
+}
+
+#endif /* !defined(MYSQL_CLIENT) */
void MYSQL_LOG::readers_addref()
{
--- 1.189/sql/log_event.cc 2005-11-04 21:09:55 +01:00
+++ 1.190/sql/log_event.cc 2005-11-09 14:48:13 +01:00
@@ -21,11 +21,13 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "mysql_priv.h"
#include "slave.h"
#include "rpl_filter.h"
#include <my_dir.h>
#endif /* MYSQL_CLIENT */
+#include <my_bitmap.h>
+#include <my_vle.h>
#define log_cs &my_charset_latin1
@@ -232,6 +234,7 @@
commands just before it prints a query.
*/
+#ifdef MYSQL_CLIENT
static void print_set_option(FILE* file, uint32 bits_changed, uint32 option,
uint32 flags, const char* name, bool* need_comma)
{
@@ -243,6 +246,7 @@
*need_comma= 1;
}
}
+#endif
/**************************************************************************
Log_event methods (= the parent class of all events)
@@ -271,6 +275,10 @@
case XID_EVENT: return "Xid";
case USER_VAR_EVENT: return "User var";
case FORMAT_DESCRIPTION_EVENT: return "Format_desc";
+ case TABLE_MAP_EVENT: return "Table_map";
+ case WRITE_ROWS_EVENT: return "Write_rows";
+ case UPDATE_ROWS_EVENT: return "Update_rows";
+ case DELETE_ROWS_EVENT: return "Delete_rows";
case BEGIN_LOAD_QUERY_EVENT: return "Begin_load_query";
case EXECUTE_LOAD_QUERY_EVENT: return "Execute_load_query";
default: return "Unknown"; /* impossible */
@@ -778,6 +786,9 @@
DBUG_RETURN(NULL); // general sanity check - will fail on a partial read
}
+ /* To check the integrity of the Log_event_type enumeration */
+ DBUG_ASSERT(buf[EVENT_TYPE_OFFSET] < ENUM_END_EVENT);
+
switch(buf[EVENT_TYPE_OFFSET]) {
case QUERY_EVENT:
ev = new Query_log_event(buf, event_len, description_event, QUERY_EVENT);
@@ -829,6 +840,20 @@
case FORMAT_DESCRIPTION_EVENT:
ev = new Format_description_log_event(buf, event_len, description_event);
break;
+#if defined(HAVE_REPLICATION) && defined(HAVE_ROW_BASED_REPLICATION)
+ case WRITE_ROWS_EVENT:
+ ev = new Write_rows_log_event(buf, event_len, description_event);
+ break;
+ case UPDATE_ROWS_EVENT:
+ ev = new Update_rows_log_event(buf, event_len, description_event);
+ break;
+ case DELETE_ROWS_EVENT:
+ ev = new Delete_rows_log_event(buf, event_len, description_event);
+ break;
+ case TABLE_MAP_EVENT:
+ ev = new Table_map_log_event(buf, event_len, description_event);
+ break;
+#endif
case BEGIN_LOAD_QUERY_EVENT:
ev = new Begin_load_query_log_event(buf, event_len, description_event);
break;
@@ -2049,6 +2074,10 @@
post_header_len[DELETE_FILE_EVENT-1]= DELETE_FILE_HEADER_LEN;
post_header_len[NEW_LOAD_EVENT-1]= post_header_len[LOAD_EVENT-1];
post_header_len[FORMAT_DESCRIPTION_EVENT-1]= FORMAT_DESCRIPTION_HEADER_LEN;
+ post_header_len[TABLE_MAP_EVENT-1]= TABLE_MAP_HEADER_LEN;
+ 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;
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;
}
@@ -2745,6 +2774,9 @@
thd->query_length= 0; // Should not be needed
thd->query_error= 0;
clear_all_errors(thd, rli);
+
+ /* see Query_log_event::exec_event() and BUG#13360 */
+ DBUG_ASSERT(!rli->m_table_map.count());
/*
Usually mysql_init_query() is called by mysql_parse(), but we need it here
as the present method does not call mysql_parse().
@@ -4936,3 +4968,1644 @@
}
return buf;
}
+
+
+#ifdef HAVE_ROW_BASED_REPLICATION
+
+/**************************************************************************
+ Rows_log_event member functions
+**************************************************************************/
+
+#ifndef MYSQL_CLIENT
+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);
+
+ if (thd_arg->options & OPTION_NO_FOREIGN_KEY_CHECKS)
+ set_flags(NO_FOREIGN_KEY_CHECKS_F);
+ if (thd_arg->options & OPTION_RELAXED_UNIQUE_CHECKS)
+ set_flags(RELAXED_UNIQUE_CHECKS_F);
+ /* if bitmap_init fails, catched in is_valid() */
+ if (likely(!bitmap_init(&m_cols,
+ m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
+ (m_width + 7) & ~7UL,
+ false)))
+ memcpy(m_cols.bitmap, cols->bitmap, byte_count(cols));
+}
+#endif
+
+Rows_log_event::Rows_log_event(const char *buf, uint event_len,
+ Log_event_type event_type,
+ const Format_description_log_event
+ *description_event)
+ : Log_event(buf, description_event),
+ m_rows_buf(0), m_rows_cur(0), m_rows_end(0)
+{
+ DBUG_ENTER("Rows_log_event::Rows_log_event(const char*,...)");
+ uint8 const common_header_len= description_event->common_header_len;
+ uint8 const post_header_len= description_event->post_header_len[event_type-1];
+
+ DBUG_PRINT("enter",("event_len=%ld, common_header_len=%d, "
+ "post_header_len=%d",
+ 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);
+
+ m_flags= uint2korr(post_begin + RW_FLAGS_OFFSET);
+
+ byte const *const var_begin= post_begin + ROWS_HEADER_LEN;
+ byte const *const ptr_width= var_begin;
+ 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;
+
+ 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",
+ m_table_id, m_flags, m_width, data_size));
+
+ m_rows_buf= my_malloc(data_size, MYF(MY_WME));
+ if (m_rows_buf)
+ {
+ /* if bitmap_init fails, catched in is_valid() */
+ if (likely(!bitmap_init(&m_cols,
+ m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
+ (m_width + 7) & ~7UL,
+ false)))
+ memcpy(m_cols.bitmap, ptr_after_width, byte_count);
+ m_rows_end= m_rows_buf + data_size;
+ m_rows_cur= m_rows_end;
+ memcpy(m_rows_buf, ptr_rows_data, data_size);
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+Rows_log_event::~Rows_log_event()
+{
+ my_free(m_rows_buf, MYF(MY_ALLOW_ZERO_PTR));
+}
+
+#ifndef MYSQL_CLIENT
+int Rows_log_event::do_add_row_data(byte *const row_data,
+ my_size_t const length)
+{
+ /*
+ When the table has a primary key, we would probably want, by default, to
+ log only the primary key value instead of the entire "before image". This
+ would save binlog space. TODO
+ */
+ DBUG_ENTER("Rows_log_event::do_add_row_data(byte *data, my_size_t length)");
+ DBUG_PRINT("enter", ("row_data= %p, length= %lu", row_data, length));
+ DBUG_DUMP("row_data", row_data, min(length, 32));
+
+ DBUG_ASSERT(m_rows_buf <= m_rows_cur);
+ DBUG_ASSERT(m_rows_buf < m_rows_end);
+ DBUG_ASSERT(m_rows_cur <= m_rows_end);
+
+ /* The cast will always work since m_rows_cur <= m_rows_end */
+ if (static_cast<my_size_t>(m_rows_end - m_rows_cur) < length)
+ {
+ my_size_t const block_size= 1024;
+ my_ptrdiff_t const old_alloc= m_rows_end - m_rows_buf;
+ my_ptrdiff_t const new_alloc=
+ old_alloc + block_size * (length / block_size + block_size - 1);
+ my_ptrdiff_t const cur_size= m_rows_cur - m_rows_buf;
+
+ byte* const new_buf= my_realloc(m_rows_buf, new_alloc, MYF(MY_WME));
+ if (unlikely(!new_buf))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ /* If the memory moved, we need to move the pointers */
+ if (new_buf != m_rows_buf)
+ {
+ m_rows_buf= new_buf;
+ m_rows_cur= m_rows_buf + cur_size;
+ }
+
+ /*
+ The end pointer should always be changed to point to the end of
+ the allocated memory.
+ */
+ m_rows_end= m_rows_buf + new_alloc;
+ }
+
+ DBUG_ASSERT(m_rows_cur + length < m_rows_end);
+ memcpy(m_rows_cur, row_data, length);
+ m_rows_cur+= length;
+ DBUG_RETURN(0);
+}
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+/*
+ Unpack a row into a record. The row is assumed to only consist of the fields
+ 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,
+ char *record, char const *row,
+ MY_BITMAP const *cols)
+{
+ DBUG_ASSERT(record && row);
+
+ my_size_t const n_null_bytes= table->s->null_bytes;
+ my_ptrdiff_t const offset= record - (byte*) table->record[0];
+
+ memcpy(record, row, n_null_bytes);
+ char const *ptr= row + n_null_bytes;
+
+ Field **const begin_ptr = table->field;
+ for (Field **field_ptr= begin_ptr ; *field_ptr ; ++field_ptr)
+ {
+ Field *const f= *field_ptr;
+
+ if (bitmap_is_set(cols, field_ptr - begin_ptr))
+ {
+ /* Field...::unpack() cannot return 0 */
+ ptr= f->unpack(f->ptr + offset, ptr);
+ f->query_id= thd->query_id;
+ }
+ else
+ {
+ f->query_id= 0UL;
+ }
+ }
+ return ptr;
+}
+
+int Rows_log_event::exec_event(st_relay_log_info *rli)
+{
+ DBUG_ENTER("Rows_log_event::exec_event(st_relay_log_info*)");
+ int error= 0;
+ TABLE* table= rli->m_table_map.get_table(m_table_id);
+ char const *row_start= m_rows_buf;
+
+ /*
+ 'thd' has been set by exec_relay_log_event(), just before calling
+ exec_event(). We still check here to prevent future coding errors.
+ */
+ DBUG_ASSERT(rli->sql_thd == thd);
+
+ /*
+ lock_tables() reads the contents of thd->lex, so they must be
+ initialized, so we should call lex_start(); to be even safer, we call
+ mysql_init_query() which does a more complete set of inits.
+ */
+ mysql_init_query(thd, NULL, 0);
+
+ if (table)
+ {
+ /*
+ table == NULL means that this table should not be
+ replicated (this was set up by Table_map_log_event::exec_event() which
+ tested replicate-* rules).
+ */
+ TABLE_LIST table_list;
+ bool need_reopen;
+ uint count= 1;
+ bzero(&table_list, sizeof(table_list));
+ table_list.lock_type= TL_WRITE;
+ table_list.next_global= table_list.next_local= 0;
+ table_list.table= table;
+
+ for ( ; ; )
+ {
+ table_list.db= const_cast<char*>(table->s->db);
+ table_list.alias= table_list.table_name= const_cast<char*>(table->s->table_name);
+
+ 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);
+ 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));
+
+ if (db == 0 || table_name == 0)
+ {
+ /*
+ Since the lock_tables() failed, the table is not locked, so
+ we don't need to unlock them.
+ */
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ }
+
+ /*
+ We also needs to flush the pending RBR event, since it keeps a
+ pointer to an open table.
+
+ ALTERNATIVE SOLUTION: Extract a pointer to the pending RBR
+ event and reset the table pointer after the tables has been
+ reopened.
+ */
+ thd->binlog_flush_pending_rows_event(false);
+
+ close_tables_for_reopen(thd, &table_list);
+
+ /* open the table again, same as in Table_map_event::exec_event */
+ table_list.db= const_cast<char*>(db);
+ table_list.alias= table_list.table_name= const_cast<char*>(table_name);
+ table_list.updating= 1;
+ TABLE_LIST *tables= &table_list;
+ if ((error= open_tables(thd, &tables, &count, 0)) == 0)
+ {
+ /* reset some variables for the table list*/
+ table_list.updating= 0;
+ /* retrieve the new table reference and update the table map */
+ table= table_list.table;
+ error= rli->m_table_map.set_table(m_table_id, table);
+ }
+ else /* error in open_tables */
+ {
+ if (thd->query_error || thd->is_fatal_error)
+ {
+ /*
+ Error reporting borrowed from Query_log_event with many excessive
+ 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);
+ thd->query_error= 1;
+ }
+ }
+ my_free((char*) db, MYF(MY_ALLOW_ZERO_PTR));
+ my_free((char*) table_name, MYF(MY_ALLOW_ZERO_PTR));
+
+ if (error)
+ DBUG_RETURN(error);
+ }
+
+ /*
+ There are a few flags that are replicated with each row event.
+ Make sure to set/clear them before executing the main body of
+ the event.
+ */
+ if (get_flags(NO_FOREIGN_KEY_CHECKS_F))
+ thd->options|= OPTION_NO_FOREIGN_KEY_CHECKS;
+ else
+ thd->options&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
+
+ if (get_flags(RELAXED_UNIQUE_CHECKS_F))
+ thd->options|= OPTION_RELAXED_UNIQUE_CHECKS;
+ else
+ thd->options&= ~OPTION_RELAXED_UNIQUE_CHECKS;
+ /* A small test to verify that objects have consistent types */
+ DBUG_ASSERT(sizeof(thd->options) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
+
+ error= do_before_row_operations(table);
+ while (error == 0 && row_start < m_rows_end) {
+ char const *row_end= do_prepare_row(thd, table, row_start);
+ DBUG_ASSERT(row_end != NULL); // cannot happen
+ DBUG_ASSERT(row_end <= m_rows_end);
+
+ error= do_exec_row(table, rli);
+ 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());
+ thd->query_error= 1;
+ break;
+ }
+
+ row_start= row_end;
+ }
+ error= do_after_row_operations(table, error);
+ }
+
+ if (error)
+ { /* error has occured during the transaction */
+ 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.
+ 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);
+ 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))
+ {
+ /*
+ This is the end of a statement or transaction, so close (and
+ unlock) the tables we opened when processing the
+ Table_map_log_event starting the statement.
+
+ OBSERVER. This will clear *all* mappings, not only those that
+ are open for the table. There is not good handle for on-close
+ actions for tables.
+
+ NOTE. Even if we have no table ('table' == 0) we still need to be
+ here, so that we increase the group relay log position. If we didn't, we
+ could have a group relay log position which lags behind "forever"
+ (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();
+ /*
+ If this event is not in a transaction, the call below will, if some
+ transactional storage engines are involved, commit the statement into
+ them and flush the pending event to binlog.
+ If this event is in a transaction, the call will do nothing, but a
+ Xid_log_event will come next which will, if some transactional engines
+ are involved, commit the transaction and flush the pending event to the
+ binlog.
+ */
+ 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.
+ */
+
+ close_thread_tables(thd); // unlocks and closes tables
+ 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;
+ }
+ 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);
+ }
+ DBUG_RETURN(error);
+ }
+
+ if (table)
+ {
+ /*
+ As "table" is not NULL, we did a successful lock_tables(), without any
+ prior LOCK TABLES and are not in prelocked mode, so this assertion should
+ be true.
+ */
+ DBUG_ASSERT(thd->lock);
+ /*
+ If we are here, there are more events to come which may use our mappings
+ and our table. So don't clear mappings or close tables, just unlock
+ tables.
+ Why don't we lock the table once for all in
+ Table_map_log_event::exec_event() ? Because we could have in binlog:
+ BEGIN;
+ Table_map t1 -> 1
+ Write_rows to id 1
+ Table_map t2 -> 2
+ Write_rows to id 2
+ Xid_log_event
+ So we cannot lock t1 when executing the first Table_map, because at that
+ moment we don't know we'll also have to lock t2, and all tables must be
+ locked at once in MySQL.
+ */
+ mysql_unlock_tables(thd, thd->lock);
+ thd->lock= 0;
+ }
+
+ DBUG_ASSERT(error == 0);
+ rli->inc_event_relay_log_pos();
+
+ DBUG_RETURN(0);
+}
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+bool Rows_log_event::write_data_header(IO_CACHE *file)
+{
+ byte buf[ROWS_HEADER_LEN]; // No need to init the buffer
+ int4store(buf + RW_MAPID_OFFSET, m_table_id);
+ int2store(buf + RW_FLAGS_OFFSET, m_flags);
+ return (my_b_safe_write(file, buf, ROWS_HEADER_LEN));
+}
+
+bool Rows_log_event::write_data_body(IO_CACHE*file)
+{
+ /*
+ Note that this should be the number of *bits*, not the number of
+ bytes.
+ */
+ byte sbuf[my_vle_sizeof(m_width)];
+ my_ptrdiff_t const data_size= m_rows_cur - m_rows_buf;
+
+ char *const sbuf_end= my_vle_encode(sbuf, sizeof(sbuf), m_width);
+ DBUG_ASSERT(static_cast<my_size_t>(sbuf_end - sbuf) <= sizeof(sbuf));
+
+ return (my_b_safe_write(file, sbuf, sbuf_end - sbuf) ||
+ my_b_safe_write(file, reinterpret_cast<byte*>(m_cols.bitmap),
+ byte_count(&m_cols)) ||
+ my_b_safe_write(file, m_rows_buf, data_size));
+}
+
+#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 dbnam= m_table->s->db;
+ char const *const tblnam= m_table->s->table_name;
+ my_size_t bytes= snprintf(buf, sizeof(buf),
+ "%s.%s - %s", dbnam, tblnam, flagstr);
+ protocol->store(buf, bytes, &my_charset_bin);
+}
+#endif
+
+
+/**************************************************************************
+ Table_map_log_event member functions
+**************************************************************************/
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ Mats says tbl->s lives longer than this event so it's ok to copy pointers
+ (tbl->s->db etc) and not pointer content.
+ */
+#if !defined(MYSQL_CLIENT)
+Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
+ 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_colcnt(tbl->s->fields), m_coltype(0),
+ m_table_id(tid),
+ m_flags(flags)
+{
+ m_data_size= TABLE_MAP_HEADER_LEN;
+ 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
+
+ /* If malloc fails, catched in is_valid() */
+ if ((m_memory= my_malloc(m_colcnt, MYF(MY_WME))))
+ {
+ m_coltype= reinterpret_cast<unsigned char*>(m_memory);
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ m_coltype[i]= m_table->field[i]->type();
+ }
+}
+#endif /* !defined(MYSQL_CLIENT) */
+
+/*
+ Constructor used by slave to read the event from the binary log.
+ */
+#if defined(HAVE_REPLICATION)
+Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
+ const Format_description_log_event
+ *description_event)
+
+ : Log_event(buf, description_event),
+#ifndef MYSQL_CLIENT
+ m_table(NULL),
+#endif
+ m_memory(NULL)
+{
+ DBUG_ENTER("Table_map_log_event::Table_map_log_event(const char*,uint,...)");
+
+ uint8 common_header_len= description_event->common_header_len;
+ uint8 post_header_len= description_event->post_header_len[TABLE_MAP_EVENT-1];
+ DBUG_PRINT("info",("event_len=%ld, common_header_len=%d, post_header_len=%d",
+ event_len, common_header_len, post_header_len));
+
+ DBUG_DUMP("event buffer", buf, event_len);
+
+ /* Read the post-header */
+ const char *const post_start= buf + common_header_len;
+
+ m_table_id= uint4korr(post_start + TM_MAPID_OFFSET);
+
+ m_flags= uint2korr(post_start + TM_FLAGS_OFFSET);
+
+ /* Read the variable part of the event */
+ const char *const vpart= post_start + post_header_len;
+
+ /* Extract the length of the various parts from the buffer */
+ byte const* const ptr_dblen= vpart + 0;
+ m_dblen= *(unsigned char*) ptr_dblen;
+
+ /* Length of database name + counter + terminating null */
+ byte const* const ptr_tbllen= ptr_dblen + m_dblen + 2;
+ m_tbllen= *(unsigned char*) ptr_tbllen;
+
+ /* Length of table name + counter + terminating null */
+ byte const* const ptr_colcnt= ptr_tbllen + m_tbllen + 2;
+ byte const* const ptr_after_colcnt= my_vle_decode(&m_colcnt, ptr_colcnt);
+
+ DBUG_PRINT("info",("m_dblen=%d off=%d m_tbllen=%d off=%d m_colcnt=%d off=%d",
+ m_dblen, ptr_dblen-vpart, m_tbllen, ptr_tbllen-vpart,
+ m_colcnt, ptr_colcnt-vpart));
+
+ /* Allocate mem for all fields in one go. If fails, catched in is_valid() */
+ m_memory= my_multi_malloc(MYF(MY_WME),
+ &m_dbnam, m_dblen + 1,
+ &m_tblnam, m_tbllen + 1,
+ &m_coltype, m_colcnt,
+ NULL);
+
+ if (m_memory)
+ {
+ /* Copy the different parts into their memory */
+ strncpy(const_cast<char*>(m_dbnam), ptr_dblen + 1, m_dblen + 1);
+ strncpy(const_cast<char*>(m_tblnam), ptr_tbllen + 1, m_tbllen + 1);
+ memcpy(m_coltype, ptr_after_colcnt, m_colcnt);
+ }
+
+ DBUG_VOID_RETURN;
+}
+#endif
+
+Table_map_log_event::~Table_map_log_event()
+{
+ my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR));
+}
+
+/*
+ Find a table based on database name and table name.
+
+ DESCRIPTION
+
+ Currently, only the first table of the 'table_list' is located. If the
+ table is found in the list of open tables for the thread, the 'table'
+ field of 'table_list' is filled in.
+
+ PARAMETERS
+
+ thd Thread structure
+ table_list List of tables to locate in the thd->open_tables list.
+ count Pointer to a variable that will be set to the number of
+ tables found. If the pointer is NULL, nothing will be stored.
+
+ RETURN VALUE
+
+ The number of tables found.
+
+ TO DO
+
+ Replace the list of table searches with a hash based on the combined
+ database and table name. The handler_tables_hash is inappropriate since
+ it hashes on the table alias. At the same time, the function can be
+ extended to handle a full list of table names, in the same spirit as
+ open_tables() and lock_tables().
+*/
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+static uint find_tables(THD *thd, TABLE_LIST *table_list, uint *count)
+{
+ uint result= 0;
+
+ /* we verify that the caller knows our limitation */
+ 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)
+ {
+ /* Copy the table pointer into the table list. */
+ table_list->table= table;
+ result= 1;
+ break;
+ }
+ }
+
+ if (count)
+ *count= result;
+ return result;
+}
+#endif
+
+/*
+ Return value is an error code, one of:
+
+ -1 Failure to open table [from open_tables()]
+ 0 Success
+ 1 No room for more tables [from set_table()]
+ 2 Out of memory [from set_table()]
+ 3 Wrong table definition
+
+ */
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int Table_map_log_event::exec_event(st_relay_log_info *rli)
+{
+ DBUG_ENTER("Table_map_log_event::exec_event(st_relay_log_info*)");
+
+ DBUG_ASSERT(rli->sql_thd == thd);
+
+ /* Step the query id to mark what columns that are actually used. */
+ pthread_mutex_lock(&LOCK_thread_count);
+ thd->query_id= next_query_id();
+ pthread_mutex_unlock(&LOCK_thread_count);
+
+ TABLE_LIST table_list;
+ uint32 dummy_len;
+ bzero(&table_list, sizeof(table_list));
+ table_list.db= const_cast<char *>(rewrite_db(m_dbnam, &dummy_len));
+ table_list.alias= table_list.table_name= const_cast<char*>(m_tblnam);
+ table_list.lock_type= TL_WRITE;
+ table_list.next_global= table_list.next_local= 0;
+ table_list.updating= 1;
+
+ int error= 0;
+
+ if (db_ok(table_list.db, replicate_do_db, replicate_ignore_db) &&
+ (!table_rules_on || tables_ok(thd, &table_list)))
+ {
+ /*
+ Open the table if it is not already open and add the table to table map.
+ If the table should not be replicated, we don't bother to do anything.
+ The table map will return NULL and the row-level event will effectively
+ be a no-op.
+ */
+ uint count;
+ if (find_tables(thd, &table_list, &count) == 0)
+ {
+ TABLE_LIST *tables= &table_list;
+ if ((error= open_tables(thd, &tables, &count, 0)))
+ {
+ if (thd->query_error || thd->is_fatal_error)
+ {
+ /*
+ Error reporting borrowed from Query_log_event with many excessive
+ 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);
+ thd->query_error= 1;
+ }
+ DBUG_RETURN(error);
+ }
+ }
+
+ m_table= table_list.table;
+
+ /*
+ This will fail later otherwise, the 'in_use' field should be
+ set to the current thread.
+ */
+ DBUG_ASSERT(m_table->in_use);
+
+ /*
+ Check that the number of columns and the field types in the
+ event match the number of columns and field types in the opened
+ table.
+ */
+ uint col= m_table->s->fields;
+
+ if (col == m_colcnt)
+ {
+ while (col-- > 0)
+ if (m_table->field[col]->type() != m_coltype[col])
+ break;
+ }
+
+ TABLE_SHARE const *const tsh= m_table->s;
+
+ /*
+ Check the following termination conditions:
+
+ (col == m_table->s->fields)
+ ==> (m_table->s->fields != m_colcnt)
+ (0 <= col < m_table->s->fields)
+ ==> (m_table->field[col]->type() != m_coltype[col])
+
+ Logically, A ==> B is equivalent to !A || B
+
+ Since col is unsigned, is suffices to check that col <=
+ tsh->fields. If col wrapped (by decreasing col when it is 0),
+ the number will be UINT_MAX, which is greater than tsh->fields.
+ */
+ DBUG_ASSERT(!(col == tsh->fields) || tsh->fields != m_colcnt);
+ DBUG_ASSERT(!(col < tsh->fields) ||
+ (m_table->field[col]->type() != m_coltype[col]));
+
+ if (col <= tsh->fields)
+ {
+ /*
+ If we get here, the number of columns in the event didn't
+ match the number of columns in the table on the slave, *or*
+ there were a column in the table on the slave that did not
+ have the same type as given in the event.
+
+ If 'col' has the value that was assigned to it, it was a
+ mismatch between the number of columns on the master and the
+ slave.
+ */
+ 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);
+ }
+ 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());
+ }
+
+ thd->query_error= 1;
+ DBUG_RETURN(3);
+ }
+
+ /*
+ We record in the slave's information that the number m_table_id is
+ mapped to the m_table object
+ */
+ if (!error)
+ error= rli->m_table_map.set_table(m_table_id, m_table);
+
+ /*
+ Tell the RLI that we are touching a table.
+
+ TODO: Maybe we can combine this with the previous operation?
+ */
+ if (!error)
+ rli->touching_table(m_dbnam, m_tblnam, m_table_id);
+ }
+
+ /*
+ We explicitly do not call Log_event::exec_event() here since we do not
+ want the relay log position to be flushed to disk. The flushing will be
+ done by the last Rows_log_event that either ends a statement (outside a
+ transaction) or a transaction.
+
+ A table map event can *never* end a transaction or a statement, so we
+ just step the relay log position.
+ */
+
+ if (likely(!error))
+ rli->inc_event_relay_log_pos();
+
+ DBUG_RETURN(error);
+}
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+#ifndef MYSQL_CLIENT
+bool Table_map_log_event::write_data_header(IO_CACHE *file)
+{
+ byte buf[TABLE_MAP_HEADER_LEN];
+ int4store(buf + TM_MAPID_OFFSET, m_table_id);
+ int2store(buf + TM_FLAGS_OFFSET, m_flags);
+ return (my_b_safe_write(file, buf, TABLE_MAP_HEADER_LEN));
+}
+
+bool Table_map_log_event::write_data_body(IO_CACHE *file)
+{
+ DBUG_ASSERT(m_dbnam != NULL);
+ DBUG_ASSERT(m_tblnam != NULL);
+ /* We use only one byte per length for storage in event: */
+ DBUG_ASSERT(m_dblen < 128);
+ DBUG_ASSERT(m_tbllen < 128);
+
+ byte const dbuf[]= { m_dblen };
+ byte const tbuf[]= { m_tbllen };
+
+ byte cbuf[my_vle_sizeof(m_colcnt)];
+ byte *const cbuf_end= my_vle_encode(cbuf, sizeof(cbuf), m_colcnt);
+ DBUG_ASSERT(static_cast<my_size_t>(cbuf_end - cbuf) <= sizeof(cbuf));
+
+ return (my_b_safe_write(file, dbuf, sizeof(dbuf)) ||
+ my_b_safe_write(file, m_dbnam, m_dblen+1) ||
+ my_b_safe_write(file, tbuf, sizeof(tbuf)) ||
+ my_b_safe_write(file, m_tblnam, m_tbllen+1) ||
+ my_b_safe_write(file, cbuf, cbuf_end - cbuf) ||
+ my_b_safe_write(file, reinterpret_cast<char*>(m_coltype), m_colcnt));
+ }
+#endif
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+
+/*
+ Print some useful information for the SHOW BINARY LOG information
+ field.
+ */
+
+void Table_map_log_event::pack_info(Protocol *protocol)
+{
+ char buf[256];
+ my_size_t bytes= snprintf(buf, sizeof(buf), "%s.%s", m_dbnam, m_tblnam);
+ protocol->store(buf, bytes, &my_charset_bin);
+}
+
+#endif
+
+
+#ifdef MYSQL_CLIENT
+void Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
+{
+ if (!print_event_info->short_form)
+ {
+ print_header(file, print_event_info);
+ fprintf(file, "\tTable_map\t"
+ "`%s`.`%s` mapped to number %lu\n",
+ m_dbnam, m_tblnam, m_table_id);
+ }
+}
+#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
+**************************************************************************/
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ */
+#if !defined(MYSQL_CLIENT)
+Write_rows_log_event::Write_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
+ ulong tid_arg,
+ MY_BITMAP const *cols,
+ bool is_transactional)
+ : Rows_log_event(thd_arg, tbl_arg, tid_arg, cols, is_transactional)
+{
+}
+#endif
+
+/*
+ Constructor used by slave to read the event from the binary log.
+ */
+#ifdef HAVE_REPLICATION
+Write_rows_log_event::Write_rows_log_event(const char *buf, uint event_len,
+ const Format_description_log_event
+ *description_event)
+: Rows_log_event(buf, event_len, WRITE_ROWS_EVENT, description_event)
+{
+}
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int Write_rows_log_event::do_before_row_operations(TABLE *table)
+{
+ int error= 0;
+
+ /*
+ We are using REPLACE semantics and not INSERT IGNORE semantics
+ when writing rows, that is: new rows replace old rows. We need to
+ inform the storage engine that it should use this behaviour.
+ */
+
+ /* Tell the SE that we are using REPLACE semantics. */
+ thd->lex->duplicates= DUP_REPLACE;
+
+ /*
+ Pretend we're executing a REPLACE command: this is needed for
+ InnoDB and NDB Cluster since they are not (properly) checking the
+ lex->duplicates flag.
+ */
+ 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. */
+
+ /*
+ 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
+ from the start.
+ */
+ table->file->start_bulk_insert(0);
+ return error;
+}
+
+int Write_rows_log_event::do_after_row_operations(TABLE *table, int error)
+{
+ DBUG_ENTER("Write_rows_log_event::do_exec_row");
+ if (error == 0)
+ error= table->file->end_bulk_insert();
+ DBUG_RETURN(error);
+}
+
+char const *Write_rows_log_event::do_prepare_row(THD *thd, TABLE *table,
+ char const *row_start)
+{
+ char const *ptr= row_start;
+ DBUG_ASSERT(table != NULL);
+ /*
+ This assertion actually checks that there is at least as many
+ columns on the slave as on the master.
+ */
+ DBUG_ASSERT(table->s->fields >= m_width);
+ DBUG_ASSERT(ptr);
+ ptr= unpack_row(thd, table, table->record[0], ptr, &m_cols);
+ return ptr;
+}
+
+/*
+ Check if there are more UNIQUE keys after the given key.
+*/
+static int
+last_uniq_key(TABLE *table, uint keyno)
+{
+ while (++keyno < table->s->keys)
+ if (table->key_info[keyno].flags & HA_NOSAME)
+ return 0;
+ return 1;
+}
+
+// Anonymous namespace for template functions/classes
+namespace {
+
+ /*
+ Smart pointer that will automatically call my_afree (a macro) when
+ the pointer goes out of scope. This is used so that I do not have
+ to remember to call my_afree() before each return. There is no
+ overhead associated with this, since all functions are inline.
+
+ I (Matz) would prefer to use the free function as a template
+ parameter, but that is not possible when the "function" is a
+ macro.
+ */
+ template <class Obj>
+ class auto_afree_ptr
+ {
+ Obj* m_ptr;
+ public:
+ auto_afree_ptr(Obj* ptr) : m_ptr(ptr) { }
+ ~auto_afree_ptr() { if (m_ptr) my_afree(m_ptr); }
+ void assign(Obj* ptr) {
+ /* Only to be called if it hasn't been given a value before. */
+ DBUG_ASSERT(m_ptr == NULL);
+ m_ptr= ptr;
+ }
+ Obj* get() { return m_ptr; }
+ };
+
+}
+
+
+/*
+ Replace the provided record in the database.
+
+ Similar to how it is done in <code>mysql_insert()</code>, we first
+ try to do a <code>ha_write_row()</code> and of that fails due to
+ duplicated keys (or indices), we do an <code>ha_update_row()</code>
+ or a <code>ha_delete_row()</code> instead.
+
+ @param thd Thread context for writing the record.
+ @param table Table to which record should be written.
+
+ @return Error code on failure, 0 on success.
+ */
+static int
+replace_record(THD *thd, TABLE *table)
+{
+ DBUG_ASSERT(table != NULL && thd != NULL);
+
+ int error;
+ int keynum;
+ auto_afree_ptr<char> key(NULL);
+
+ while ((error= table->file->ha_write_row(table->record[0])))
+ {
+ if ((keynum= table->file->get_dup_key(error)) < 0)
+ {
+ /* We failed to retrieve the duplicate key */
+ return HA_ERR_FOUND_DUPP_KEY;
+ }
+
+ /*
+ We need to retrieve the old row into record[1] to be able to
+ either update or delete the offending record. We either:
+
+ - use rnd_pos() with a row-id (available as dupp_row) to the
+ offending row, if that is possible (MyISAM and Blackhole), or else
+
+ - use index_read_idx() with the key that is duplicated, to
+ retrieve the offending row.
+ */
+ if (table->file->table_flags() & HA_DUPP_POS)
+ {
+ error= table->file->rnd_pos(table->record[1], table->file->dupp_ref);
+ if (error)
+ return error;
+ }
+ else
+ {
+ if (table->file->extra(HA_EXTRA_FLUSH_CACHE))
+ {
+ return my_errno;
+ }
+
+ if (key.get() == NULL)
+ {
+ key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length)));
+ if (key.get() == NULL)
+ return ENOMEM;
+ }
+
+ key_copy(key.get(), table->record[0], table->key_info + keynum, 0);
+ error= table->file->index_read_idx(table->record[1], keynum, key.get(),
+ table->key_info[keynum].key_length,
+ HA_READ_KEY_EXACT);
+ if (error)
+ return error;
+ }
+
+ /*
+ Now, table->record[1] should contain the offending row. That
+ will enable us to update it or, alternatively, delete it (so
+ that we can insert the new row afterwards).
+
+ REPLACE is defined as either INSERT or DELETE + INSERT. If
+ possible, we can replace it with an UPDATE, but that will not
+ work on InnoDB if FOREIGN KEY checks are necessary.
+
+ I (Matz) am not sure of the reason for the last_uniq_key()
+ check as, but I'm guessing that it's something along the
+ following lines.
+
+ Suppose that we got the duplicate key to be a key that is not
+ the last unique key for the table and we perform an update:
+ then there might be another key for which the unique check will
+ fail, so we're better off just deleting the row and inserting
+ the correct row.
+ */
+ if (last_uniq_key(table, keynum) &&
+ !table->file->referenced_by_foreign_key())
+ {
+ error=table->file->ha_update_row(table->record[1],
+ table->record[0]);
+ return error;
+ }
+ else
+ {
+ 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)
+{
+ DBUG_ASSERT(table != NULL);
+ int error= replace_record(thd, table);
+ return error;
+}
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+#ifdef MYSQL_CLIENT
+void Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info)
+{
+ 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
+ }
+}
+#endif
+
+/**************************************************************************
+ Delete_rows_log_event member functions
+**************************************************************************/
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+static int record_compare(TABLE *table, byte const *a, byte const *b)
+{
+ for (my_size_t i= 0 ; i < table->s->fields ; ++i)
+ {
+ uint const off= table->field[i]->offset();
+ uint const res= table->field[i]->cmp_binary(a + off, b + off);
+ if (res != 0) {
+ return res;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ Find the row given by 'key', if the table has keys, or else use a table scan
+ to find (and fetch) the row. If the engine allows random access of the
+ records, a combination of position() and rnd_pos() will be used.
+
+ The 'record_buf' will be used as buffer for records while locating the
+ correct row.
+ */
+static int find_and_fetch_row(TABLE *table, byte *key, byte *record_buf)
+{
+ DBUG_ENTER("find_and_fetch_row(TABLE *table, byte *key, byte *record)");
+ DBUG_PRINT("enter", ("table=%p, key=%p, record=%p",
+ table, key, record_buf));
+
+ DBUG_ASSERT(table->in_use != NULL);
+
+ if ((table->file->table_flags() & HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS)
+ && table->s->primary_key < MAX_KEY)
+ {
+ /*
+ Use a more efficient method to fetch the record given by
+ table->record[0] if the engine allows it. We first compute a
+ row reference using the position() member function (it will be
+ stored in table->file->ref) and the use rnd_pos() to position
+ the "cursor" at the correct row.
+ */
+ table->file->position(table->record[0]);
+ DBUG_RETURN(table->file->rnd_pos(table->record[0], table->file->ref));
+ }
+
+ DBUG_ASSERT(record_buf);
+
+ if (table->s->keys > 0)
+ {
+ int error;
+ if ((error= table->file->index_read_idx(record_buf, 0, key,
+ table->key_info->key_length,
+ HA_READ_KEY_EXACT)))
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+
+ /*
+ Below is a minor "optimization". If the key (i.e., key number
+ 0) has the HA_NOSAME flag set, we know that we have found the
+ correct record (since there can be no duplicates); otherwise, we
+ have to compare the record with the one found to see if it is
+ the correct one.
+
+ CAVEAT! This behaviour is essential for the replication of,
+ e.g., the mysql.proc table since the correct record *shall* be
+ found using the primary key *only*. There shall be no
+ comparison of non-PK columns to decide if the correct record is
+ found. I can see no scenario where it would be incorrect to
+ chose the row to change only using a PK or an UNNI.
+ */
+ if (table->key_info->flags & HA_NOSAME)
+ DBUG_RETURN(0);
+
+ while (record_compare(table, table->record[0], record_buf) != 0)
+ {
+ int error;
+ if ((error= table->file->index_next(record_buf)))
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+ }
+ else
+ {
+ /* Continue until we find the right record or have made a full loop */
+ int restart_count= 0; // Number of times scanning has restarted from top
+ int error= 0;
+ do
+ {
+ error= table->file->rnd_next(record_buf);
+ switch (error)
+ {
+ case 0:
+ case HA_ERR_RECORD_DELETED:
+ break;
+
+ case HA_ERR_END_OF_FILE:
+ if (++restart_count < 2)
+ table->file->ha_rnd_init(1);
+ break;
+
+ default:
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+ while (restart_count < 2 &&
+ record_compare(table, table->record[0], record_buf) != 0);
+
+ DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == 0);
+ DBUG_RETURN(error);
+ }
+
+ DBUG_RETURN(0);
+}
+#endif
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ */
+
+#ifndef MYSQL_CLIENT
+Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
+ ulong tid, MY_BITMAP const *cols,
+ bool is_transactional)
+ : Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
+#ifdef HAVE_REPLICATION
+ ,m_memory(NULL), m_key(NULL), m_search_record(NULL)
+#endif
+{
+}
+#endif /* #if !defined(MYSQL_CLIENT) */
+
+/*
+ Constructor used by slave to read the event from the binary log.
+ */
+#ifdef HAVE_REPLICATION
+Delete_rows_log_event::Delete_rows_log_event(const char *buf, uint event_len,
+ const Format_description_log_event
+ *description_event)
+#if defined(MYSQL_CLIENT)
+ : Rows_log_event(buf, event_len, DELETE_ROWS_EVENT, description_event)
+#else
+ : Rows_log_event(buf, event_len, DELETE_ROWS_EVENT, description_event),
+ m_memory(NULL), m_key(NULL), m_search_record(NULL)
+#endif
+{
+}
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int Delete_rows_log_event::do_before_row_operations(TABLE *table)
+{
+ DBUG_ASSERT(m_memory == NULL);
+
+ if ((table->file->table_flags() & HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS) &&
+ table->s->primary_key < MAX_KEY)
+ {
+ /*
+ We don't need to allocate any memory for m_search_record and
+ m_key since they are not used.
+ */
+ return 0;
+ }
+
+ int error= 0;
+
+ if (table->s->keys > 0)
+ {
+ m_memory=
+ my_multi_malloc(MYF(MY_WME),
+ &m_search_record, table->s->reclength,
+ &m_key, table->key_info->key_length,
+ NULL);
+ }
+ else
+ {
+ m_memory= m_search_record= my_malloc(table->s->reclength, MYF(MY_WME));
+ m_key= NULL;
+ }
+ if (!m_memory)
+ return HA_ERR_OUT_OF_MEM;
+
+ if (table->s->keys > 0)
+ {
+ /* We have a key: search the table using the index */
+ if (!table->file->inited)
+ error= table->file->ha_index_init(0);
+ }
+ else
+ {
+ /* We doesn't have a key: search the table using rnd_next() */
+ error= table->file->ha_rnd_init(1);
+ }
+
+ return error;
+}
+
+int Delete_rows_log_event::do_after_row_operations(TABLE *table, int error)
+{
+ /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
+ table->file->ha_index_or_rnd_end();
+ my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc
+ m_memory= m_search_record= m_key= NULL;
+
+ return error;
+}
+
+char const *Delete_rows_log_event::do_prepare_row(THD *thd, TABLE *table,
+ char const *row_start)
+{
+ char const *ptr= row_start;
+ DBUG_ASSERT(ptr);
+ /*
+ This assertion actually checks that there is at least as many
+ columns on the slave as on the master.
+ */
+ DBUG_ASSERT(table->s->fields >= m_width);
+
+ DBUG_ASSERT(ptr != NULL);
+ ptr= unpack_row(thd, table, table->record[0], ptr, &m_cols);
+
+ /*
+ If we will access rows using the random access method, m_key will
+ be set to NULL, so we do not need to make a key copy in that case.
+ */
+ 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);
+ }
+
+ return ptr;
+}
+
+int Delete_rows_log_event::do_exec_row(TABLE *table, st_relay_log_info *rli)
+{
+ 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);
+ }
+
+ /*
+ Now we should have the right row to delete. We are using
+ record[0] since it is guaranteed to point to a record with the
+ correct value.
+ */
+ error= table->file->ha_delete_row(table->record[0]);
+
+ DBUG_RETURN(error);
+}
+
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+#ifdef MYSQL_CLIENT
+void Delete_rows_log_event::print(FILE *file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ 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
+ }
+}
+#endif
+
+
+/**************************************************************************
+ Update_rows_log_event member functions
+**************************************************************************/
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ */
+#if !defined(MYSQL_CLIENT)
+Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
+ ulong tid, MY_BITMAP const *cols,
+ bool is_transactional)
+: Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
+#ifdef HAVE_REPLICATION
+ , m_memory(NULL), m_key(NULL)
+#endif
+{
+}
+#endif /* !defined(MYSQL_CLIENT) */
+
+/*
+ Constructor used by slave to read the event from the binary log.
+ */
+#ifdef HAVE_REPLICATION
+Update_rows_log_event::Update_rows_log_event(const char *buf, uint event_len,
+ const
+ Format_description_log_event
+ *description_event)
+#if defined(MYSQL_CLIENT)
+ : Rows_log_event(buf, event_len, UPDATE_ROWS_EVENT, description_event)
+#else
+ : Rows_log_event(buf, event_len, UPDATE_ROWS_EVENT, description_event),
+ m_memory(NULL), m_key(NULL)
+#endif
+{
+}
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int Update_rows_log_event::do_before_row_operations(TABLE *table)
+{
+ DBUG_ASSERT(m_memory == NULL);
+
+ if ((table->file->table_flags() & HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS) &&
+ table->s->primary_key < MAX_KEY)
+ {
+ /*
+ We don't need to allocate any memory for m_search_record and
+ m_key since they are not used.
+ */
+ return 0;
+ }
+
+ int error= 0;
+
+ if (table->s->keys > 0)
+ {
+ m_memory=
+ my_multi_malloc(MYF(MY_WME),
+ &m_search_record, table->s->reclength,
+ &m_key, table->key_info->key_length,
+ NULL);
+ }
+ else
+ {
+ m_memory= m_search_record= my_malloc(table->s->reclength, MYF(MY_WME));
+ m_key= NULL;
+ }
+ if (!m_memory)
+ return HA_ERR_OUT_OF_MEM;
+
+ if (table->s->keys > 0)
+ {
+ /* We have a key: search the table using the index */
+ if (!table->file->inited)
+ error= table->file->ha_index_init(0);
+ }
+ else
+ {
+ /* We doesn't have a key: search the table using rnd_next() */
+ error= table->file->ha_rnd_init(1);
+ }
+
+ return error;
+}
+
+int Update_rows_log_event::do_after_row_operations(TABLE *table, int error)
+{
+ /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
+ table->file->ha_index_or_rnd_end();
+ my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR));
+ m_memory= m_search_record= m_key= NULL;
+
+ return error;
+}
+
+char const *Update_rows_log_event::do_prepare_row(THD *thd, TABLE *table,
+ char const *row_start)
+{
+ char const *ptr= row_start;
+ DBUG_ASSERT(ptr);
+ /*
+ This assertion actually checks that there is at least as many
+ columns on the slave as on the master.
+ */
+ 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);
+ DBUG_ASSERT(ptr != NULL);
+ /* record[1] is the after image for the update */
+ ptr= unpack_row(thd, table, table->record[1], ptr, &m_cols);
+
+ /*
+ If we will access rows using the random access method, m_key will
+ be set to NULL, so we do not need to make a key copy in that case.
+ */
+ 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);
+ }
+
+ return ptr;
+}
+
+int Update_rows_log_event::do_exec_row(TABLE *table, st_relay_log_info *rli)
+{
+ 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);
+ }
+
+ /*
+ Now we should have the right row to update. The record that has
+ been fetched is guaranteed to be in record[0], so we use that.
+ */
+ error= table->file->ha_update_row(table->record[0], table->record[1]);
+
+ DBUG_RETURN(error);
+}
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+#ifdef MYSQL_CLIENT
+void Update_rows_log_event::print(FILE *file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ 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
+ }
+}
+#endif
+
+#endif /* defined(HAVE_ROW_BASED_REPLICATION) */
--- 1.341/sql/mysql_priv.h 2005-11-07 16:24:41 +01:00
+++ 1.342/sql/mysql_priv.h 2005-11-09 15:21:12 +01:00
@@ -236,50 +236,50 @@
TODO: separate three contexts above, move them to separate bitfields.
*/
-#define SELECT_DISTINCT (1L << 0) // SELECT, user
-#define SELECT_STRAIGHT_JOIN (1L << 1) // SELECT, user
-#define SELECT_DESCRIBE (1L << 2) // SELECT, user
-#define SELECT_SMALL_RESULT (1L << 3) // SELECT, user
-#define SELECT_BIG_RESULT (1L << 4) // SELECT, user
-#define OPTION_FOUND_ROWS (1L << 5) // SELECT, user
-#define OPTION_TO_QUERY_CACHE (1L << 6) // SELECT, user
-#define SELECT_NO_JOIN_CACHE (1L << 7) // intern
-#define OPTION_BIG_TABLES (1L << 8) // THD, user
-#define OPTION_BIG_SELECTS (1L << 9) // THD, user
-#define OPTION_LOG_OFF (1L << 10) // THD, user
-#define OPTION_UPDATE_LOG (1L << 11) // THD, user, unused
-#define TMP_TABLE_ALL_COLUMNS (1L << 12) // SELECT, intern
-#define OPTION_WARNINGS (1L << 13) // THD, user
-#define OPTION_AUTO_IS_NULL (1L << 14) // THD, user, binlog
-#define OPTION_FOUND_COMMENT (1L << 15) // SELECT, intern, parser
-#define OPTION_SAFE_UPDATES (1L << 16) // THD, user
-#define OPTION_BUFFER_RESULT (1L << 17) // SELECT, user
-#define OPTION_BIN_LOG (1L << 18) // THD, user
-#define OPTION_NOT_AUTOCOMMIT (1L << 19) // THD, user
-#define OPTION_BEGIN (1L << 20) // THD, intern
-#define OPTION_TABLE_LOCK (1L << 21) // THD, intern
-#define OPTION_QUICK (1L << 22) // SELECT (for DELETE)
-#define OPTION_QUOTE_SHOW_CREATE (1L << 23) // THD, user
+#define SELECT_DISTINCT (LL(1) << 0) // SELECT, user
+#define SELECT_STRAIGHT_JOIN (LL(1) << 1) // SELECT, user
+#define SELECT_DESCRIBE (LL(1) << 2) // SELECT, user
+#define SELECT_SMALL_RESULT (LL(1) << 3) // SELECT, user
+#define SELECT_BIG_RESULT (LL(1) << 4) // SELECT, user
+#define OPTION_FOUND_ROWS (LL(1) << 5) // SELECT, user
+#define OPTION_TO_QUERY_CACHE (LL(1) << 6) // SELECT, user
+#define SELECT_NO_JOIN_CACHE (LL(1) << 7) // intern
+#define OPTION_BIG_TABLES (LL(1) << 8) // THD, user
+#define OPTION_BIG_SELECTS (LL(1) << 9) // THD, user
+#define OPTION_LOG_OFF (LL(1) << 10) // THD, user
+#define OPTION_UPDATE_LOG (LL(1) << 11) // THD, user, unused
+#define TMP_TABLE_ALL_COLUMNS (LL(1) << 12) // SELECT, intern
+#define OPTION_WARNINGS (LL(1) << 13) // THD, user
+#define OPTION_AUTO_IS_NULL (LL(1) << 14) // THD, user, binlog
+#define OPTION_FOUND_COMMENT (LL(1) << 15) // SELECT, intern, parser
+#define OPTION_SAFE_UPDATES (LL(1) << 16) // THD, user
+#define OPTION_BUFFER_RESULT (LL(1) << 17) // SELECT, user
+#define OPTION_BIN_LOG (LL(1) << 18) // THD, user
+#define OPTION_NOT_AUTOCOMMIT (LL(1) << 19) // THD, user
+#define OPTION_BEGIN (LL(1) << 20) // THD, intern
+#define OPTION_TABLE_LOCK (LL(1) << 21) // THD, intern
+#define OPTION_QUICK (LL(1) << 22) // SELECT (for DELETE)
+#define OPTION_QUOTE_SHOW_CREATE (LL(1) << 23) // THD, user
/* Thr following is used to detect a conflict with DISTINCT
in the user query has requested */
-#define SELECT_ALL (1L << 24) // SELECT, user, parser
+#define SELECT_ALL (LL(1) << 24) // SELECT, user, parser
/* Set if we are updating a non-transaction safe table */
-#define OPTION_STATUS_NO_TRANS_UPDATE (1L << 25) // THD, intern
+#define OPTION_STATUS_NO_TRANS_UPDATE (LL(1) << 25) // THD, intern
/* The following can be set when importing tables in a 'wrong order'
to suppress foreign key checks */
-#define OPTION_NO_FOREIGN_KEY_CHECKS (1L << 26) // THD, user, binlog
+#define OPTION_NO_FOREIGN_KEY_CHECKS (LL(1) << 26) // THD, user, binlog
/* The following speeds up inserts to InnoDB tables by suppressing unique
key checks in some cases */
-#define OPTION_RELAXED_UNIQUE_CHECKS (1L << 27) // THD, user, binlog
-#define SELECT_NO_UNLOCK (1L << 28) // SELECT, intern
-#define OPTION_SCHEMA_TABLE (1L << 29) // SELECT, intern
+#define OPTION_RELAXED_UNIQUE_CHECKS (LL(1) << 27) // THD, user, binlog
+#define SELECT_NO_UNLOCK (LL(1) << 28) // SELECT, intern
+#define OPTION_SCHEMA_TABLE (LL(1) << 29) // SELECT, intern
/* Flag set if setup_tables already done */
-#define OPTION_SETUP_TABLES_DONE (1L << 30) // intern
+#define OPTION_SETUP_TABLES_DONE (LL(1) << 30) // intern
/* If not set then the thread will ignore all warnings with level notes. */
-#define OPTION_SQL_NOTES (1UL << 31) // THD, user
+#define OPTION_SQL_NOTES (LL(1) << 31) // THD, user
/*
Force the used temporary table to be a MyISAM table (because we will use
fulltext functions when reading from it.
@@ -1170,6 +1170,13 @@
extern ulong query_buff_size, thread_stack;
extern ulong binlog_cache_size, max_binlog_cache_size, open_files_limit;
extern ulong max_binlog_size, max_relay_log_size;
+extern const char *opt_binlog_format;
+#ifdef HAVE_ROW_BASED_REPLICATION
+extern my_bool binlog_row_based;
+extern ulong opt_binlog_rows_event_max_size;
+#else
+extern const my_bool binlog_row_based;
+#endif
extern ulong rpl_recovery_rank, thread_cache_size;
extern ulong back_log;
extern ulong specialflag, current_pid;
@@ -1306,6 +1313,7 @@
#endif
extern SHOW_COMP_OPTION have_isam;
+extern SHOW_COMP_OPTION have_row_based_replication;
extern SHOW_COMP_OPTION have_raid, have_openssl, have_symlink;
extern SHOW_COMP_OPTION have_query_cache;
extern SHOW_COMP_OPTION have_geometry, have_rtree_keys;
--- 1.488/sql/mysqld.cc 2005-11-07 16:24:41 +01:00
+++ 1.489/sql/mysqld.cc 2005-11-09 15:21:12 +01:00
@@ -432,6 +432,33 @@
my_bool opt_noacl;
my_bool sp_automatic_privileges= 1;
+#ifdef HAVE_ROW_BASED_REPLICATION
+/*
+ This variable below serves as an optimization for (opt_binlog_format ==
+ BF_ROW) as we need to do this test for every row. Stmt-based is default.
+*/
+my_bool binlog_row_based= FALSE;
+ulong opt_binlog_rows_event_max_size;
+const char *binlog_format_names[]= {"STATEMENT", "ROW", NullS};
+/*
+ Note that BF_UNSPECIFIED is last, after the end of binlog_format_names: it
+ has no corresponding cell in this array. We use this value to be able to
+ know if the user has explicitely specified a binlog format (then we require
+ also --log-bin) or not (then we fall back to statement-based).
+*/
+enum binlog_format { BF_STMT= 0, BF_ROW= 1, BF_UNSPECIFIED= 2 };
+#else
+const my_bool binlog_row_based= FALSE;
+const char *binlog_format_names[]= {"STATEMENT", NullS};
+enum binlog_format { BF_STMT= 0, BF_UNSPECIFIED= 2 };
+#endif
+
+TYPELIB binlog_format_typelib=
+ { array_elements(binlog_format_names)-1,"",
+ binlog_format_names, NULL };
+const char *opt_binlog_format= 0;
+enum binlog_format opt_binlog_format_id= BF_UNSPECIFIED;
+
#ifdef HAVE_INITGROUPS
static bool calling_initgroups= FALSE; /* Used in SIGSEGV handler. */
#endif
@@ -518,6 +545,7 @@
CHARSET_INFO *system_charset_info, *files_charset_info ;
CHARSET_INFO *national_charset_info, *table_alias_charset;
+SHOW_COMP_OPTION have_row_based_replication;
SHOW_COMP_OPTION have_raid, have_openssl, have_symlink, have_query_cache;
SHOW_COMP_OPTION have_geometry, have_rtree_keys;
SHOW_COMP_OPTION have_crypt, have_compress;
@@ -3002,9 +3030,45 @@
{
sql_print_warning("You need to use --log-bin to make "
"--log-slave-updates work.");
- unireg_abort(1);
+ unireg_abort(1);
}
+ if (!opt_bin_log && (opt_binlog_format_id != BF_UNSPECIFIED))
+ {
+ sql_print_warning("You need to use --log-bin to make "
+ "--binlog-format work.");
+ unireg_abort(1);
+ }
+ 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 (opt_binlog_format_id == BF_ROW)
+ {
+ binlog_row_based= TRUE;
+ /*
+ Row-based binlogging turns on InnoDB unsafe locking, because the locks
+ are not needed when using row-based binlogging. In fact
+ innodb-locks-unsafe-for-binlog is unsafe only for stmt-based, it's
+ safe for row-based.
+ */
+#ifdef HAVE_INNOBASE_DB
+ innobase_locks_unsafe_for_binlog= TRUE;
+#endif
+ /* Trust routine creators because they can do no harm */
+ trust_routine_creators= 1;
+ }
+#endif
+ /* Check that we have not let the format to unspecified at this point */
+ DBUG_ASSERT((uint)opt_binlog_format_id <=
+ array_elements(binlog_format_names)-1);
+ opt_binlog_format= binlog_format_names[opt_binlog_format_id];
+
if (opt_slow_log)
mysql_slow_log.open_slow_log(opt_slow_logname);
@@ -4478,6 +4542,10 @@
OPT_SQL_BIN_UPDATE_SAME, OPT_REPLICATE_DO_DB,
OPT_REPLICATE_IGNORE_DB, OPT_LOG_SLAVE_UPDATES,
OPT_BINLOG_DO_DB, OPT_BINLOG_IGNORE_DB,
+ OPT_BINLOG_FORMAT,
+#ifdef HAVE_ROW_BASED_REPLICATION
+ OPT_BINLOG_ROWS_EVENT_MAX_SIZE,
+#endif
OPT_WANT_CORE, OPT_CONCURRENT_INSERT,
OPT_MEMLOCK, OPT_MYISAM_RECOVER,
OPT_REPLICATE_REWRITE_DB, OPT_SERVER_ID,
@@ -4696,12 +4764,38 @@
{"bind-address", OPT_BIND_ADDRESS, "IP address to bind to.",
(gptr*) &my_bind_addr_str, (gptr*) &my_bind_addr_str, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"binlog-format", OPT_BINLOG_FORMAT,
+#ifdef HAVE_ROW_BASED_REPLICATION
+ "Tell the master the form of binary logging to use: either 'row' for "
+ "row-based binary logging (which automatically turns on "
+ "innodb_locks_unsafe_for_binlog as it is safe in this case), or "
+ "'statement' for statement-based logging. ",
+#else
+ "Tell the master the form of binary logging to use: this release build "
+ "supports only statement-based binary logging, so only 'statement' is "
+ "a legal value; MySQL-Max release builds support row-based binary logging "
+ "in addition.",
+#endif
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
{"binlog-do-db", OPT_BINLOG_DO_DB,
"Tells the master it should log updates for the specified database, and exclude all others not explicitly mentioned.",
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"binlog-ignore-db", OPT_BINLOG_IGNORE_DB,
"Tells the master that updates to the given database should not be logged tothe binary log.",
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#ifdef HAVE_ROW_BASED_REPLICATION
+ {"binlog-row-event-max-size", OPT_BINLOG_ROWS_EVENT_MAX_SIZE,
+ "The maximum size of a row-based binary log event in bytes. Rows will be "
+ "grouped into events smaller than this size if possible. "
+ "The value has to be a multiple of 256.",
+ (gptr*) &opt_binlog_rows_event_max_size,
+ (gptr*) &opt_binlog_rows_event_max_size, 0,
+ GET_ULONG, REQUIRED_ARG,
+ /* def_value */ 1024, /* min_value */ 256, /* max_value */ ULONG_MAX,
+ /* sub_size */ 0, /* block_size */ 256,
+ /* app_type */ 0
+ },
+#endif
{"bootstrap", OPT_BOOTSTRAP, "Used by mysql installation scripts.", 0, 0, 0,
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"character-set-client-handshake", OPT_CHARACTER_SET_CLIENT_HANDSHAKE,
@@ -4869,7 +4963,9 @@
(gptr*) &innobase_unix_file_flush_method, 0, GET_STR, REQUIRED_ARG, 0, 0, 0,
0, 0, 0},
{"innodb_locks_unsafe_for_binlog", OPT_INNODB_LOCKS_UNSAFE_FOR_BINLOG,
- "Force InnoDB not to use next-key locking. Instead use only row-level locking",
+ "Force InnoDB not to use next-key locking, to use only row-level locking."
+ " This is unsafe if you are using statement-based binary logging, and safe"
+ " if you are using row-based binary logging.",
(gptr*) &innobase_locks_unsafe_for_binlog,
(gptr*) &innobase_locks_unsafe_for_binlog, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"innodb_log_arch_dir", OPT_INNODB_LOG_ARCH_DIR,
@@ -4936,9 +5032,13 @@
*/
{"log-bin-trust-routine-creators", OPT_LOG_BIN_TRUST_ROUTINE_CREATORS,
"If equal to 0 (the default), then when --log-bin is used, creation of "
- "a routine is allowed only to users having the SUPER privilege and only"
- "if this routine may not break binary logging",
- (gptr*) &trust_routine_creators, (gptr*) &trust_routine_creators, 0,
+ "a routine is allowed only to users having the SUPER privilege (because of "
+ "security issues) and only if this routine may not break binary logging."
+#ifdef HAVE_ROW_BASED_REPLICATION
+ " If using --binlog-format=row, the security issues do not exist and the "
+ "binary logging cannot break so this option is automatically set to 1."
+#endif
+ ,(gptr*) &trust_routine_creators, (gptr*) &trust_routine_creators, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"log-error", OPT_ERROR_LOG_FILE, "Error log file.",
(gptr*) &log_error_file_ptr, (gptr*) &log_error_file_ptr, 0, GET_STR,
@@ -6382,6 +6482,7 @@
"d:t:i:o,/tmp/mysqld.trace");
#endif
opt_error_log= IF_WIN(1,0);
+
#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
have_ndbcluster=SHOW_OPTION_DISABLED;
global_system_variables.ndb_index_stat_enable=TRUE;
@@ -6393,6 +6494,11 @@
#else
have_ndbcluster=SHOW_OPTION_NO;
#endif
+#ifdef HAVE_ROW_BASED_REPLICATION
+ have_row_based_replication= SHOW_OPTION_YES;
+#else
+ have_row_based_replication= SHOW_OPTION_NO;
+#endif
#ifdef USE_RAID
have_raid=SHOW_OPTION_YES;
#else
@@ -6605,6 +6711,28 @@
binlog_filter->add_ignore_db(argument);
break;
}
+ case OPT_BINLOG_FORMAT:
+ {
+ int id;
+ if ((id= find_type(argument, &binlog_format_typelib, 2)) <= 0)
+ {
+#ifdef HAVE_ROW_BASED_REPLICATION
+ fprintf(stderr,
+ "Unknown binary log format: '%s' "
+ "(should be '%s' or '%s')\n",
+ argument,
+ binlog_format_names[BF_STMT],
+ binlog_format_names[BF_ROW]);
+#else
+ fprintf(stderr,
+ "Unknown binary log format: '%s' (only legal value is '%s')\n",
+ argument, binlog_format_names[BF_STMT]);
+#endif
+ exit(1);
+ }
+ opt_binlog_format_id= (enum binlog_format)(id-1);
+ break;
+ }
case (int)OPT_BINLOG_DO_DB:
{
binlog_filter->add_do_db(argument);
@@ -7144,6 +7272,7 @@
init_global_datetime_format(MYSQL_TIMESTAMP_DATETIME,
&global_system_variables.datetime_format))
exit(1);
+
}
--- 1.256/sql/slave.cc 2005-11-04 21:09:55 +01:00
+++ 1.257/sql/slave.cc 2005-11-09 15:21:12 +01:00
@@ -16,8 +16,6 @@
#include "mysql_priv.h"
-#ifdef HAVE_REPLICATION
-
#include <mysql.h>
#include <myisam.h>
#include "slave.h"
@@ -28,6 +26,10 @@
#include <my_dir.h>
#include <sql_common.h>
+#ifdef HAVE_REPLICATION
+
+#include "rpl_tblmap.h"
+
#define MAX_SLAVE_RETRY_PAUSE 5
bool use_slave_mask = 0;
MY_BITMAP slave_error_mask;
@@ -806,6 +808,19 @@
}
+static void free_string_array(DYNAMIC_ARRAY *a)
+{
+ uint i;
+ for (i = 0; i < a->elements; i++)
+ {
+ char* p;
+ get_dynamic(a, (gptr) &p, i);
+ my_free(p, MYF(MY_WME));
+ }
+ delete_dynamic(a);
+}
+
+
#ifdef NOT_USED_YET
static int end_slave_on_walk(MASTER_INFO* mi, gptr /*unused*/)
{
@@ -2244,12 +2259,11 @@
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)
+ until_log_pos(0), retried_trans(0), m_reload_flags(RELOAD_NONE_F)
{
group_relay_log_name[0]= event_relay_log_name[0]=
group_master_log_name[0]= 0;
last_slave_error[0]= until_log_name[0]= ign_master_log_name_end[0]= 0;
-
bzero((char*) &info_file, sizeof(info_file));
bzero((char*) &cache_buf, sizeof(cache_buf));
cached_charset_invalidate();
@@ -2948,7 +2962,7 @@
thd->lex->current_select= 0;
if (!ev->when)
ev->when = time(NULL);
- ev->thd = thd;
+ ev->thd = thd; // because up to this point, ev->thd == 0
exec_res = ev->exec_event(rli);
DBUG_ASSERT(rli->sql_thd==thd);
/*
@@ -3518,6 +3532,17 @@
RPL_LOG_NAME, llstr(rli->group_master_log_pos,llbuff));
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
+
VOID(pthread_mutex_lock(&LOCK_thread_count));
/*
Some extra safety, which should not been needed (normally, event deletion
@@ -4735,11 +4760,86 @@
DBUG_VOID_RETURN;
}
+/*
+ Some system tables needed to be re-read by the MySQL server after it has
+ updated them; in statement-based replication, the GRANT and other commands
+ are sent verbatim to the slave which then reloads; in row-based replication,
+ changes to these tables are done through ordinary Rows binlog events, so
+ master must add some flag for the slave to know it has to reload the tables.
+*/
+struct st_reload_entry
+{
+ char const *table;
+ st_relay_log_info::enum_reload_flag flag;
+};
+
+/*
+ Sorted array of table names, please keep it sorted since we are
+ using bsearch() on it below.
+ */
+static st_reload_entry s_mysql_tables[] =
+{
+ { "columns_priv", st_relay_log_info::RELOAD_GRANT_F },
+ { "db", st_relay_log_info::RELOAD_ACCESS_F },
+ { "host", st_relay_log_info::RELOAD_ACCESS_F },
+ { "procs_priv", st_relay_log_info::RELOAD_GRANT_F },
+ { "tables_priv", st_relay_log_info::RELOAD_GRANT_F },
+ { "user", st_relay_log_info::RELOAD_ACCESS_F }
+};
+
+static const my_size_t s_mysql_tables_size =
+ sizeof(s_mysql_tables)/sizeof(*s_mysql_tables);
+
+static int reload_entry_compare(const void *lhs, const void *rhs)
+{
+ const char *lstr = static_cast<const char *>(lhs);
+ const char *rstr = static_cast<const st_reload_entry*>(rhs)->table;
+ return strcmp(lstr, rstr);
+}
+
+void st_relay_log_info::touching_table(char const* db, char const* table,
+ ulong table_id)
+{
+ if (strcmp(db,"mysql") == 0)
+ {
+#if defined(HAVE_BSEARCH) && defined(HAVE_SIZE_T)
+ void *const ptr= bsearch(table, s_mysql_tables,
+ s_mysql_tables_size,
+ sizeof(*s_mysql_tables), reload_entry_compare);
+ st_reload_entry const *const entry= static_cast<st_reload_entry*>(ptr);
+#else
+ /*
+ Fall back to full scan, there are few rows anyway and updating the
+ "mysql" database is rare.
+ */
+ st_reload_entry const *entry= s_mysql_tables;
+ for ( ; entry < s_mysql_tables + s_mysql_tables_size ; entry++)
+ if (reload_entry_compare(table, entry) == 0)
+ break;
+#endif
+ if (entry)
+ m_reload_flags|= entry->flag;
+ }
+}
+
+void st_relay_log_info::transaction_end(THD* thd)
+{
+ if (m_reload_flags != RELOAD_NONE_F)
+ {
+ if (m_reload_flags & RELOAD_ACCESS_F)
+ acl_reload(thd);
+
+ if (m_reload_flags & RELOAD_GRANT_F)
+ grant_reload(thd);
+
+ m_reload_flags= RELOAD_NONE_F;
+ }
+}
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
template class I_List_iterator<i_string>;
template class I_List_iterator<i_string_pair>;
#endif
-
#endif /* HAVE_REPLICATION */
+
--- 1.158/sql/sql_acl.cc 2005-11-04 21:09:56 +01:00
+++ 1.159/sql/sql_acl.cc 2005-11-09 14:48:14 +01:00
@@ -1467,8 +1467,7 @@
acl_user->host.hostname ? acl_user->host.hostname : "",
new_password));
thd->clear_error();
- Query_log_event qinfo(thd, buff, query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::ROW_QUERY_TYPE, buff, query_length, FALSE, FALSE);
}
end:
close_thread_tables(thd);
@@ -1653,7 +1652,7 @@
}
store_record(table,record[1]);
table->field[2]->store(new_password, new_password_len, system_charset_info);
- if ((error=table->file->update_row(table->record[1],table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],table->record[0])))
{
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
DBUG_RETURN(1);
@@ -1867,14 +1866,14 @@
*/
table->file->ha_retrieve_all_cols();
if (cmp_record(table,record[1]) &&
- (error=table->file->update_row(table->record[1],table->record[0])))
+ (error=table->file->ha_update_row(table->record[1],table->record[0])))
{ // This should never happen
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
error= -1; /* purecov: deadcode */
goto end; /* purecov: deadcode */
}
}
- else if ((error=table->file->write_row(table->record[0]))) // insert
+ else if ((error=table->file->ha_write_row(table->record[0]))) // insert
{ // This should never happen
if (error && error != HA_ERR_FOUND_DUPP_KEY &&
error != HA_ERR_FOUND_DUPP_UNIQUE) /* purecov: inspected */
@@ -1984,16 +1983,17 @@
if (rights)
{
table->file->ha_retrieve_all_cols();
- if ((error=table->file->update_row(table->record[1],table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],
+ table->record[0])))
goto table_error; /* purecov: deadcode */
}
else /* must have been a revoke of all privileges */
{
- if ((error = table->file->delete_row(table->record[1])))
+ if ((error = table->file->ha_delete_row(table->record[1])))
goto table_error; /* purecov: deadcode */
}
}
- else if (rights && (error=table->file->write_row(table->record[0])))
+ else if (rights && (error=table->file->ha_write_row(table->record[0])))
{
if (error && error != HA_ERR_FOUND_DUPP_KEY) /* purecov: inspected */
goto table_error; /* purecov: deadcode */
@@ -2361,9 +2361,9 @@
{
GRANT_COLUMN *grant_column;
if (privileges)
- error=table->file->update_row(table->record[1],table->record[0]);
+ error=table->file->ha_update_row(table->record[1],table->record[0]);
else
- error=table->file->delete_row(table->record[1]);
+ error=table->file->ha_delete_row(table->record[1]);
if (error)
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
@@ -2378,7 +2378,7 @@
else // new grant
{
GRANT_COLUMN *grant_column;
- if ((error=table->file->write_row(table->record[0])))
+ if ((error=table->file->ha_write_row(table->record[0])))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
result= -1; /* purecov: inspected */
@@ -2430,8 +2430,8 @@
if (privileges)
{
int tmp_error;
- if ((tmp_error=table->file->update_row(table->record[1],
- table->record[0])))
+ if ((tmp_error=table->file->ha_update_row(table->record[1],
+ table->record[0])))
{ /* purecov: deadcode */
table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
result= -1; /* purecov: deadcode */
@@ -2443,7 +2443,7 @@
else
{
int tmp_error;
- if ((tmp_error = table->file->delete_row(table->record[1])))
+ if ((tmp_error = table->file->ha_delete_row(table->record[1])))
{ /* purecov: deadcode */
table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
result= -1; /* purecov: deadcode */
@@ -2551,15 +2551,15 @@
{
if (store_table_rights || store_col_rights)
{
- if ((error=table->file->update_row(table->record[1],table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],table->record[0])))
goto table_error; /* purecov: deadcode */
}
- else if ((error = table->file->delete_row(table->record[1])))
+ else if ((error = table->file->ha_delete_row(table->record[1])))
goto table_error; /* purecov: deadcode */
}
else
{
- error=table->file->write_row(table->record[0]);
+ error=table->file->ha_write_row(table->record[0]);
if (error && error != HA_ERR_FOUND_DUPP_KEY)
goto table_error; /* purecov: deadcode */
}
@@ -2668,15 +2668,15 @@
{
if (store_proc_rights)
{
- if ((error=table->file->update_row(table->record[1],table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],table->record[0])))
goto table_error;
}
- else if ((error= table->file->delete_row(table->record[1])))
+ else if ((error= table->file->ha_delete_row(table->record[1])))
goto table_error;
}
else
{
- error=table->file->write_row(table->record[0]);
+ error=table->file->ha_write_row(table->record[0]);
if (error && error != HA_ERR_FOUND_DUPP_KEY)
goto table_error;
}
@@ -4601,13 +4601,13 @@
system_charset_info);
user_field->store(user_to->user.str, user_to->user.length,
system_charset_info);
- if ((error= table->file->update_row(table->record[1], table->record[0])))
+ if ((error= table->file->ha_update_row(table->record[1], table->record[0])))
table->file->print_error(error, MYF(0));
}
else
{
/* delete */
- if ((error=table->file->delete_row(table->record[0])))
+ if ((error=table->file->ha_delete_row(table->record[0])))
table->file->print_error(error, MYF(0));
}
@@ -5614,7 +5614,7 @@
table->field[i++]->store(column, col_length, cs);
table->field[i++]->store(priv, priv_length, cs);
table->field[i]->store(is_grantable, strlen(is_grantable), cs);
- table->file->write_row(table->record[0]);
+ table->file->ha_write_row(table->record[0]);
}
--- 1.277/sql/sql_base.cc 2005-11-04 21:09:56 +01:00
+++ 1.278/sql/sql_base.cc 2005-11-09 14:48:14 +01:00
@@ -636,7 +636,8 @@
next=table->next;
close_temporary(table, 1);
}
- if (query && found_user_tables && mysql_bin_log.is_open())
+ if (query && found_user_tables && mysql_bin_log.is_open() &&
+ !binlog_row_based) // CREATE TEMP TABLE not binlogged if row-based
{
/* The -1 is to remove last ',' */
thd->clear_error();
@@ -1868,10 +1869,11 @@
uint query_buf_size= 20 + 2*NAME_LEN + 1;
if ((query= (char*)my_malloc(query_buf_size,MYF(MY_WME))))
{
+ /* this DELETE FROM is needed even with row-based binlogging */
end = strxmov(strmov(query, "DELETE FROM `"),
db,"`.`",name,"`", NullS);
- Query_log_event qinfo(thd, query, (ulong)(end-query), 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::STMT_QUERY_TYPE,
+ query, (ulong)(end-query), FALSE, FALSE);
my_free(query, MYF(0));
}
else
--- 1.219/sql/sql_class.cc 2005-11-03 23:28:58 +01:00
+++ 1.220/sql/sql_class.cc 2005-11-09 14:48:14 +01:00
@@ -292,7 +292,7 @@
variables.date_format);
variables.datetime_format= date_time_format_copy((THD*) 0,
variables.datetime_format);
-#ifdef HAVE_NDBCLUSTER_DB
+#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
variables.ndb_use_transactions= 1;
#endif
pthread_mutex_unlock(&LOCK_global_system_variables);
@@ -907,7 +907,7 @@
return 0;
}
-#ifdef HAVE_INNOBASE_DB
+#ifdef WITH_INNOBASE_STORAGE_ENGINE
/*
We may be passing the control from mysqld to the client: release the
InnoDB adaptive hash S-latch to avoid thread deadlocks if it was reserved
@@ -943,7 +943,7 @@
bool select_send::send_eof()
{
-#ifdef HAVE_INNOBASE_DB
+#ifdef WITH_INNOBASE_STORAGE_ENGINE
/* We may be passing the control from mysqld to the client: release the
InnoDB adaptive hash S-latch to avoid thread deadlocks if it was reserved
by thd */
--- 1.267/sql/sql_class.h 2005-11-07 16:24:42 +01:00
+++ 1.268/sql/sql_class.h 2005-11-09 15:21:12 +01:00
@@ -23,12 +23,15 @@
// TODO: create log.h and move all the log header stuff there
+#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;
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME };
@@ -232,6 +235,11 @@
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;
+
public:
/*
These describe the log's format. This is used only for relay logs.
@@ -256,6 +264,15 @@
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;
@@ -1117,6 +1134,13 @@
public Open_tables_state
{
public:
+ enum enum_error {
+ ALL_OK,
+ TABLE_MAP_WRITE_FAILURE,
+ BINLOG_WRITE_FAILURE,
+ COUNT
+ };
+
/*
Constant for THD::where initialization in the beginning of every query.
@@ -1221,12 +1245,141 @@
/* container for handler's private per-connection data */
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)
+
+ /*
+ Public interface to write rows to the binlog
+ */
+ int binlog_write_row(TABLE* table, bool is_transactional,
+ MY_BITMAP const* cols, my_size_t colcnt,
+ const byte *buf);
+ int binlog_delete_row(TABLE* table, bool is_transactional,
+ MY_BITMAP const* cols, my_size_t colcnt,
+ const byte *buf);
+ int binlog_update_row(TABLE* table, bool is_transactional,
+ MY_BITMAP const* cols, my_size_t colcnt,
+ const byte *old_data, const byte *new_data);
+
+ void set_server_id(uint32 sid) { server_id = sid; }
+
+ /*
+ Member functions to handle pending event for row-level logging.
+ */
+ template <class RowsEventT> Rows_log_event*
+ binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
+ MY_BITMAP const* cols,
+ 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*);
+
+ enum enum_binlog_query_type {
+ /* The query can be logged row-based or statement-based */
+ ROW_QUERY_TYPE,
+ /* The query has to be logged statement-based */
+ STMT_QUERY_TYPE
+ };
+
+ int binlog_query(enum_binlog_query_type qtype,
+ char const *query, ulong query_len,
+ bool is_trans, bool suppress_use);
+
+ 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
+ {
+ TABLE_SHARE *table_s= table->s;
+ my_size_t length= table_s->reclength + 2 * table_s->fields;
+ if (table_s->blob_fields == 0)
+ return length;
+
+ return (length+max_row_length_blob(table,data));
+ }
+
+ my_size_t pack_row(TABLE* table, MY_BITMAP const* cols, byte *row_data,
+ my_size_t max_len, const byte *data) const;
+
+ int binlog_flush_pending_rows_event(bool transaction_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 */
+
+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
THD_TRANS stmt; // Trans for current statement
bool on; // see ha_enable_transaction()
+ XID xid; // transaction identifier
+ enum xa_states xa_state; // used by external XA only
XID_STATE xid_state;
+#ifdef HAVE_ROW_BASED_REPLICATION
+ Rows_log_event *m_pending_rows_event;
+#endif
+
/*
Tables changed in transaction (that must be invalidated in query cache).
List contain only transactional tables, that not invalidated in query
--- 1.158/sql/sql_delete.cc 2005-11-04 21:09:56 +01:00
+++ 1.159/sql/sql_delete.cc 2005-11-09 15:21:12 +01:00
@@ -40,6 +40,7 @@
ha_rows deleted;
uint usable_index= MAX_KEY;
SELECT_LEX *select_lex= &thd->lex->select_lex;
+ bool ha_delete_row_bypassed= 0;
DBUG_ENTER("mysql_delete");
if (open_and_lock_tables(thd, table_list))
@@ -77,15 +78,18 @@
!(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) &&
!(table->triggers && table->triggers->has_delete_triggers()))
{
- deleted= table->file->records;
+ ha_rows const maybe_deleted= table->file->records;
if (!(error=table->file->delete_all_rows()))
{
error= -1; // ok
+ deleted= maybe_deleted;
+ ha_delete_row_bypassed= 1;
goto cleanup;
}
if (error != HA_ERR_WRONG_COMMAND)
{
table->file->print_error(error,MYF(0));
+ ha_delete_row_bypassed= 1;
error=0;
goto cleanup;
}
@@ -211,7 +215,7 @@
break;
}
- if (!(error= table->file->delete_row(table->record[0])))
+ if (!(error= table->file->ha_delete_row(table->record[0])))
{
deleted++;
if (table->triggers &&
@@ -293,10 +297,24 @@
{
if (error < 0)
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- transactional_table, FALSE);
- if (mysql_bin_log.write(&qinfo) && transactional_table)
+
+ /*
+ If 'handler::delete_all_rows()' was called, we replicate
+ statement-based; otherwise, 'ha_delete_row()' was used to
+ delete specific rows which we might log row-based.
+ */
+ THD::enum_binlog_query_type const
+ query_type(ha_delete_row_bypassed ?
+ THD::STMT_QUERY_TYPE :
+ THD::ROW_QUERY_TYPE);
+ int log_result= thd->binlog_query(query_type,
+ thd->query, thd->query_length,
+ transactional_table, FALSE);
+
+ if (log_result && transactional_table)
+ {
error=1;
+ }
}
if (!transactional_table)
thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
@@ -592,7 +610,7 @@
TRG_ACTION_BEFORE, FALSE))
DBUG_RETURN(1);
table->status|= STATUS_DELETED;
- if (!(error=table->file->delete_row(table->record[0])))
+ if (!(error=table->file->ha_delete_row(table->record[0])))
{
deleted++;
if (table->triggers &&
@@ -705,7 +723,7 @@
local_error= 1;
break;
}
- if ((local_error=table->file->delete_row(table->record[0])))
+ if ((local_error=table->file->ha_delete_row(table->record[0])))
{
table->file->print_error(local_error,MYF(0));
break;
@@ -772,10 +790,13 @@
{
if (local_error == 0)
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- transactional_tables, FALSE);
- if (mysql_bin_log.write(&qinfo) && !normal_tables)
+ if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length,
+ transactional_tables, FALSE) &&
+ !normal_tables)
+ {
local_error=1; // Log write failed: roll back the SQL statement
+ }
}
if (!transactional_tables)
thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
@@ -876,10 +897,13 @@
{
if (mysql_bin_log.is_open())
{
+ /*
+ TRUNCATE must always be statement-based binlogged (not row-based) so
+ we don't test binlog_row_based.
+ */
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::STMT_QUERY_TYPE,
+ thd->query, thd->query_length, FALSE, FALSE);
}
send_ok(thd); // This should return record count
}
--- 1.173/sql/sql_insert.cc 2005-11-04 21:09:56 +01:00
+++ 1.174/sql/sql_insert.cc 2005-11-09 14:48:14 +01:00
@@ -604,10 +604,13 @@
{
if (error <= 0)
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- transactional_table, FALSE);
- if (mysql_bin_log.write(&qinfo) && transactional_table)
- error=1;
+ if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length,
+ transactional_table, FALSE) &&
+ transactional_table)
+ {
+ error=1;
+ }
}
if (!transactional_table)
thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
@@ -991,10 +994,11 @@
DBUG_ENTER("write_record");
info->records++;
+
if (info->handle_duplicates == DUP_REPLACE ||
info->handle_duplicates == DUP_UPDATE)
{
- while ((error=table->file->write_row(table->record[0])))
+ while ((error=table->file->ha_write_row(table->record[0])))
{
uint key_nr;
if (error != HA_WRITE_SKIP)
@@ -1078,7 +1082,7 @@
thd->clear_next_insert_id= 0;
thd->next_insert_id= 0;
}
- if ((error=table->file->update_row(table->record[1],table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],table->record[0])))
{
if ((error == HA_ERR_FOUND_DUPP_KEY) && info->ignore)
goto ok_or_after_trg_err;
@@ -1117,8 +1121,8 @@
thd->clear_next_insert_id= 0;
thd->next_insert_id= 0;
}
- if ((error=table->file->update_row(table->record[1],
- table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],
+ table->record[0])))
goto err;
info->deleted++;
trg_error= (table->triggers &&
@@ -1135,7 +1139,7 @@
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_BEFORE, TRUE))
goto before_trg_err;
- if ((error=table->file->delete_row(table->record[1])))
+ if ((error=table->file->ha_delete_row(table->record[1])))
goto err;
info->deleted++;
if (!table->file->has_transactions())
@@ -1156,7 +1160,7 @@
table->triggers->process_triggers(thd, TRG_EVENT_INSERT,
TRG_ACTION_AFTER, TRUE));
}
- else if ((error=table->file->write_row(table->record[0])))
+ else if ((error=table->file->ha_write_row(table->record[0])))
{
if (!info->ignore ||
(error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE))
@@ -1242,16 +1246,15 @@
class delayed_row :public ilink {
public:
- char *record,*query;
+ char *record;
enum_duplicates dup;
time_t start_time;
bool query_start_used,last_insert_id_used,insert_id_used, ignore, log_query;
ulonglong last_insert_id;
timestamp_auto_set_type timestamp_field_type;
- uint query_length;
delayed_row(enum_duplicates dup_arg, bool ignore_arg, bool log_query_arg)
- :record(0), query(0), dup(dup_arg), ignore(ignore_arg), log_query(log_query_arg) {}
+ :record(0), dup(dup_arg), ignore(ignore_arg), log_query(log_query_arg) {}
~delayed_row()
{
x_free(record);
@@ -1261,6 +1264,9 @@
class delayed_insert :public ilink {
uint locks_in_memory;
+ char *query;
+ ulong query_length;
+ ulong query_allocated;
public:
THD thd;
TABLE *table;
@@ -1274,7 +1280,7 @@
TABLE_LIST table_list; // Argument
delayed_insert()
- :locks_in_memory(0),
+ :locks_in_memory(0), query(0), query_length(0), query_allocated(0),
table(0),tables_in_use(0),stacked_inserts(0), status(0), dead(0),
group_count(0)
{
@@ -1300,6 +1306,7 @@
}
~delayed_insert()
{
+ my_free(query, MYF(MY_WME|MY_ALLOW_ZERO_PTR));
/* The following is not really needed, but just for safety */
delayed_row *row;
while ((row=rows.get()))
@@ -1319,6 +1326,25 @@
VOID(pthread_cond_broadcast(&COND_thread_count)); /* Tell main we are ready */
}
+ int set_query(char const *q, ulong qlen) {
+ if (q && qlen > 0)
+ {
+ if (query_allocated < qlen + 1)
+ {
+ ulong const flags(MY_WME|MY_FREE_ON_ERROR|MY_ALLOW_ZERO_PTR);
+ query= my_realloc(query, qlen + 1, MYF(flags));
+ if (query == 0)
+ return HA_ERR_OUT_OF_MEM;
+ query_allocated= qlen;
+ }
+ query_length= qlen;
+ memcpy(query, q, qlen + 1);
+ }
+ else
+ query_length= 0;
+ return 0;
+ }
+
/* The following is for checking when we can delete ourselves */
inline void lock()
{
@@ -1611,18 +1637,22 @@
if (thd->killed || !(row= new delayed_row(duplic, ignore, log_on)))
goto err;
+#if 0
if (!query)
query_length=0;
- if (!(row->record= (char*) my_malloc(table->s->reclength+query_length+1,
- MYF(MY_WME))))
+#endif
+ if (!(row->record= (char*) my_malloc(table->s->reclength, MYF(MY_WME))))
goto err;
memcpy(row->record, table->record[0], table->s->reclength);
+ di->set_query(query, query_length);
+#if 0
if (query_length)
{
row->query= row->record+table->s->reclength;
memcpy(row->query,query,query_length+1);
}
row->query_length= query_length;
+#endif
row->start_time= thd->start_time;
row->query_start_used= thd->query_start_used;
row->last_insert_id_used= thd->last_insert_id_used;
@@ -1945,7 +1975,21 @@
{
int error;
ulong max_rows;
- bool using_ignore=0, using_bin_log=mysql_bin_log.is_open();
+ bool using_ignore=0,
+ using_bin_log= mysql_bin_log.is_open();
+
+#if 0
+ /*
+ The actual text for the query is added to the first row in the
+ list. Since the row is destroyed, with all it's memory, we need
+ to take a copy of it to be able to log it after all rows have been
+ applied.
+ */
+ uint const query_length= rows.head()->query_length;
+ char *const query= static_cast<char*>(my_alloca(query_length+1));
+ memcpy(query, rows.head()->query, query_length);
+#endif
+
delayed_row *row;
DBUG_ENTER("handle_inserts");
@@ -2011,11 +2055,6 @@
using_ignore=0;
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
}
- if (row->query && row->log_query && using_bin_log)
- {
- Query_log_event qinfo(&thd, row->query, row->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
if (table->s->blob_fields)
free_delayed_insert_blobs(table);
thread_safe_sub(delayed_rows_in_use,1,&LOCK_delayed_status);
@@ -2030,8 +2069,7 @@
on this table until all entries has been processed
*/
if (group_count++ >= max_rows && (row= rows.head()) &&
- (!(row->log_query & using_bin_log) ||
- row->query))
+ (!(row->log_query & using_bin_log)))
{
group_count=0;
if (stacked_inserts || tables_in_use) // Let these wait a while
@@ -2066,6 +2104,10 @@
thd.proc_info=0;
table->next_number_field=0;
pthread_mutex_unlock(&mutex);
+
+ /* After releasing the mutex, to prevent deadlocks. */
+ thd.binlog_query(THD::ROW_QUERY_TYPE, query, query_length, FALSE, FALSE);
+
if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
{ // This shouldn't happen
table->file->print_error(error,MYF(0));
@@ -2415,9 +2457,8 @@
thd->insert_id(last_insert_id); // For binary log
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- table->file->has_transactions(), FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length,
+ table->file->has_transactions(), FALSE);
}
if (!table->s->tmp_table)
thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
@@ -2458,9 +2499,9 @@
{
if (!error)
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- table->file->has_transactions(), FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length,
+ table->file->has_transactions(), FALSE);
}
if ((error2=ha_autocommit_or_rollback(thd,error)) && ! error)
error=error2;
--- 1.85/sql/sql_load.cc 2005-11-04 21:09:56 +01:00
+++ 1.86/sql/sql_load.cc 2005-11-09 14:48:15 +01:00
@@ -415,37 +415,52 @@
if (mysql_bin_log.is_open())
{
/*
- Make sure last block (the one which caused the error) gets logged.
- This is needed because otherwise after write of
- (to the binlog, not to read_info (which is a cache))
- Delete_file_log_event the bad block will remain in read_info (because
- pre_read is not called at the end of the last block; remember pre_read
- is called whenever a new block is read from disk).
- At the end of mysql_load(), the destructor of read_info will call
- end_io_cache() which will flush read_info, so we will finally have
- this in the binlog:
- Append_block # The last successfull block
- Delete_file
- Append_block # The failing block
- which is nonsense.
- Or could also be (for a small file)
- Create_file # The failing block
- which is nonsense (Delete_file is not written in this case, because:
- Create_file has not been written, so Delete_file is not written, then
- when read_info is destroyed end_io_cache() is called which writes
- Create_file.
+ We need to do the job that is normally done inside
+ binlog_query() here, which is to ensure that the pending event
+ is written before tables are unlocked and before any other
+ events are written. We also need to update the table map
+ version for the binary log to mark that table maps are invalid
+ after this point.
*/
- read_info.end_io_cache();
- /* If the file was not empty, wrote_create_file is true */
- if (lf_info.wrote_create_file)
+ if (binlog_row_based)
+ thd->binlog_flush_pending_rows_event(true);
+ else
{
- if ((info.copied || info.deleted) && !transactional_table)
- write_execute_load_query_log_event(thd, handle_duplicates,
- ignore, transactional_table);
- else
+ /*
+ Make sure last block (the one which caused the error) gets
+ logged. This is needed because otherwise after write of (to
+ the binlog, not to read_info (which is a cache))
+ Delete_file_log_event the bad block will remain in read_info
+ (because pre_read is not called at the end of the last
+ block; remember pre_read is called whenever a new block is
+ read from disk). At the end of mysql_load(), the destructor
+ of read_info will call end_io_cache() which will flush
+ read_info, so we will finally have this in the binlog:
+
+ Append_block # The last successfull block
+ Delete_file
+ Append_block # The failing block
+ which is nonsense.
+ Or could also be (for a small file)
+ Create_file # The failing block
+ which is nonsense (Delete_file is not written in this case, because:
+ Create_file has not been written, so Delete_file is not written, then
+ when read_info is destroyed end_io_cache() is called which writes
+ Create_file.
+ */
+ read_info.end_io_cache();
+ /* If the file was not empty, wrote_create_file is true */
+ if (lf_info.wrote_create_file)
{
- Delete_file_log_event d(thd, db, transactional_table);
- mysql_bin_log.write(&d);
+ if ((info.copied || info.deleted) && !transactional_table)
+ write_execute_load_query_log_event(thd, handle_duplicates,
+ ignore, transactional_table);
+ else
+ {
+ Delete_file_log_event d(thd, db, transactional_table);
+ mysql_bin_log.write(&d);
+ }
+ mysql_bin_log.update_table_map_version();
}
}
}
@@ -463,14 +478,31 @@
if (mysql_bin_log.is_open())
{
/*
- As already explained above, we need to call end_io_cache() or the last
- block will be logged only after Execute_load_query_log_event (which is
- wrong), when read_info is destroyed.
- */
- read_info.end_io_cache();
- if (lf_info.wrote_create_file)
- write_execute_load_query_log_event(thd, handle_duplicates,
- ignore, transactional_table);
+ We need to do the job that is normally done inside
+ binlog_query() here, which is to ensure that the pending event
+ is written before tables are unlocked and before any other
+ events are written. We also need to update the table map
+ version for the binary log to mark that table maps are invalid
+ after this point.
+ */
+ if (binlog_row_based)
+ thd->binlog_flush_pending_rows_event(true);
+ else
+ {
+ /*
+ As already explained above, we need to call end_io_cache() or the last
+ block will be logged only after Execute_load_query_log_event (which is
+ wrong), when read_info is destroyed.
+ */
+ read_info.end_io_cache();
+ if (lf_info.wrote_create_file)
+ {
+ write_execute_load_query_log_event(thd, handle_duplicates,
+ ignore, transactional_table);
+ mysql_bin_log.update_table_map_version();
+ }
+
+ }
}
#endif /*!EMBEDDED_LIBRARY*/
if (transactional_table)
@@ -909,7 +941,7 @@
if (get_it_from_net)
cache.read_function = _my_b_net_read;
- if (mysql_bin_log.is_open())
+ if (!binlog_row_based && mysql_bin_log.is_open())
cache.pre_read = cache.pre_close =
(IO_CACHE_CALLBACK) log_loaded_block;
#endif
--- 1.480/sql/sql_parse.cc 2005-11-07 16:24:43 +01:00
+++ 1.481/sql/sql_parse.cc 2005-11-09 14:48:16 +01:00
@@ -3197,13 +3197,13 @@
break;
#ifdef HAVE_REPLICATION
- /* Check slave filtering rules */
- if (thd->slave_thread && all_tables_not_ok(thd, all_tables))
- {
- /* we warn the slave SQL thread */
- my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
- break;
- }
+ /* Check slave filtering rules */
+ if (thd->slave_thread && all_tables_not_ok(thd, all_tables))
+ {
+ /* we warn the slave SQL thread */
+ my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
+ break;
+ }
#endif /* HAVE_REPLICATION */
res= mysql_multi_update(thd, all_tables,
@@ -3678,8 +3678,8 @@
{
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length, FALSE, FALSE);
}
send_ok(thd);
}
@@ -3696,8 +3696,8 @@
{
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length, FALSE, FALSE);
}
send_ok(thd);
}
@@ -3714,8 +3714,8 @@
{
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length, FALSE, FALSE);
}
send_ok(thd);
}
@@ -3730,8 +3730,8 @@
{
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length, FALSE, FALSE);
}
send_ok(thd);
}
@@ -3810,8 +3810,8 @@
if (!res && mysql_bin_log.is_open())
{
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length, FALSE, FALSE);
}
}
else
@@ -3830,8 +3830,8 @@
if (mysql_bin_log.is_open())
{
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length, FALSE, FALSE);
}
if (lex->sql_command == SQLCOM_GRANT)
{
@@ -4277,7 +4277,7 @@
So just execute the statement.
*/
res= sp->execute_procedure(thd, &lex->value_list);
- if (mysql_bin_log.is_open() &&
+ if (!binlog_row_based && mysql_bin_log.is_open() &&
(sp->m_chistics->daccess == SP_CONTAINS_SQL ||
sp->m_chistics->daccess == SP_MODIFIES_SQL_DATA))
{
@@ -4288,7 +4288,6 @@
else
thd->clear_error();
}
-
/*
If warnings have been cleared, we have to clear total_warn_count
too, otherwise the clients get confused.
@@ -4370,8 +4369,8 @@
if (mysql_bin_log.is_open())
{
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length, FALSE, FALSE);
}
send_ok(thd);
break;
@@ -4457,8 +4456,8 @@
if (mysql_bin_log.is_open())
{
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length, FALSE, FALSE);
}
send_ok(thd);
break;
@@ -4555,8 +4554,8 @@
buff.append(" AS ", 4);
buff.append(first_table->source.str, first_table->source.length);
- Query_log_event qinfo(thd, buff.ptr(), buff.length(), 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::STMT_QUERY_TYPE,
+ buff.ptr(), buff.length(), FALSE, FALSE);
}
break;
}
@@ -4569,8 +4568,8 @@
mysql_bin_log.is_open())
{
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::STMT_QUERY_TYPE,
+ thd->query, thd->query_length, FALSE, FALSE);
}
break;
}
--- 1.367/sql/sql_select.cc 2005-11-06 08:28:50 +01:00
+++ 1.368/sql/sql_select.cc 2005-11-09 14:48:16 +01:00
@@ -8929,11 +8929,11 @@
*/
while (!table->file->rnd_next(new_table.record[1]))
{
- if ((write_err=new_table.file->write_row(new_table.record[1])))
+ if ((write_err=new_table.file->ha_write_row(new_table.record[1])))
goto err;
}
/* copy row that filled HEAP table */
- if ((write_err=new_table.file->write_row(table->record[0])))
+ if ((write_err=new_table.file->ha_write_row(table->record[0])))
{
if (write_err != HA_ERR_FOUND_DUPP_KEY &&
write_err != HA_ERR_FOUND_DUPP_UNIQUE || !ignore_last_dupp_key_error)
@@ -10322,7 +10322,7 @@
{
int error;
join->found_records++;
- if ((error=table->file->write_row(table->record[0])))
+ if ((error=table->file->ha_write_row(table->record[0])))
{
if (error == HA_ERR_FOUND_DUPP_KEY ||
error == HA_ERR_FOUND_DUPP_UNIQUE)
@@ -10384,8 +10384,8 @@
{ /* Update old record */
restore_record(table,record[1]);
update_tmptable_sum_func(join->sum_funcs,table);
- if ((error=table->file->update_row(table->record[1],
- table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],
+ table->record[0])))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
@@ -10408,7 +10408,7 @@
}
init_tmptable_sum_functions(join->sum_funcs);
copy_funcs(join->tmp_table_param.items_to_copy);
- if ((error=table->file->write_row(table->record[0])))
+ if ((error=table->file->ha_write_row(table->record[0])))
{
if (create_myisam_from_heap(join->thd, table, &join->tmp_table_param,
error, 0))
@@ -10444,7 +10444,7 @@
copy_fields(&join->tmp_table_param); // Groups are copied twice.
copy_funcs(join->tmp_table_param.items_to_copy);
- if (!(error=table->file->write_row(table->record[0])))
+ if (!(error=table->file->ha_write_row(table->record[0])))
join->send_records++; // New group
else
{
@@ -10460,8 +10460,8 @@
}
restore_record(table,record[1]);
update_tmptable_sum_func(join->sum_funcs,table);
- if ((error=table->file->update_row(table->record[1],
- table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],
+ table->record[0])))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
@@ -10504,7 +10504,7 @@
join->sum_funcs_end[send_group_parts]);
if (!join->having || join->having->val_int())
{
- int error= table->file->write_row(table->record[0]);
+ int error= table->file->ha_write_row(table->record[0]);
if (error && create_myisam_from_heap(join->thd, table,
&join->tmp_table_param,
error, 0))
@@ -11362,7 +11362,7 @@
}
if (having && !having->val_int())
{
- if ((error=file->delete_row(record)))
+ if ((error=file->ha_delete_row(record)))
goto err;
error=file->rnd_next(record);
continue;
@@ -11389,7 +11389,7 @@
}
if (compare_record(table, first_field) == 0)
{
- if ((error=file->delete_row(record)))
+ if ((error=file->ha_delete_row(record)))
goto err;
}
else if (!found)
@@ -11486,7 +11486,7 @@
}
if (having && !having->val_int())
{
- if ((error=file->delete_row(record)))
+ if ((error=file->ha_delete_row(record)))
goto err;
continue;
}
@@ -11503,7 +11503,7 @@
if (hash_search(&hash, org_key_pos, key_length))
{
/* Duplicated found ; Remove the row */
- if ((error=file->delete_row(record)))
+ if ((error=file->ha_delete_row(record)))
goto err;
}
else
@@ -13329,7 +13329,7 @@
item->save_in_result_field(1);
}
copy_sum_funcs(sum_funcs_end[i+1], sum_funcs_end[i]);
- if ((error= table->file->write_row(table->record[0])))
+ if ((error= table->file->ha_write_row(table->record[0])))
{
if (create_myisam_from_heap(thd, table, &tmp_table_param,
error, 0))
--- 1.275/sql/sql_show.cc 2005-11-07 16:24:44 +01:00
+++ 1.276/sql/sql_show.cc 2005-11-09 14:48:17 +01:00
@@ -1736,7 +1736,7 @@
static bool schema_table_store_record(THD *thd, TABLE *table)
{
int error;
- if ((error= table->file->write_row(table->record[0])))
+ if ((error= table->file->ha_write_row(table->record[0])))
{
if (create_myisam_from_heap(thd, table,
table->pos_in_table_list->schema_table_param,
--- 1.276/sql/sql_table.cc 2005-11-07 16:24:44 +01:00
+++ 1.277/sql/sql_table.cc 2005-11-09 15:21:12 +01:00
@@ -1778,14 +1778,24 @@
}
thd->tmp_table_used= 1;
}
- if (!internal_tmp_table && mysql_bin_log.is_open())
+
+ /*
+ Don't write statement if:
+ - It is an internal temporary table,
+ - Row-based logging is used and it we are creating a temporary table, or
+ - The binary log is not open.
+ */
+ if (!internal_tmp_table &&
+ !(binlog_row_based &&
+ (create_info->options & HA_LEX_CREATE_TMP_TABLE)) &&
+ mysql_bin_log.is_open())
{
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::STMT_QUERY_TYPE,
+ thd->query, thd->query_length, FALSE, FALSE);
}
-
error= FALSE;
+
unlock_and_end:
VOID(pthread_mutex_unlock(&LOCK_open));
start_waiting_global_read_lock(thd);
@@ -2882,7 +2892,16 @@
}
// Must be written before unlock
- write_bin_log(thd, TRUE);
+ if (!binlog_row_based && mysql_bin_log.is_open())
+ {
+ thd->clear_error();
+ /*
+ CREATE TABLE ... LIKE ... cannot be logged statement-based nor
+ row-based. This is filed as WL#2867.
+ */
+ Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
+ mysql_bin_log.write(&qinfo);
+ }
res= FALSE;
goto err;
@@ -3951,7 +3970,12 @@
}
if (!error)
{
- write_bin_log(thd, TRUE);
+ if (mysql_bin_log.is_open())
+ {
+ thd->clear_error();
+ thd->binlog_query(THD::STMT_QUERY_TYPE,
+ thd->query, thd->query_length, FALSE, FALSE);
+ }
if (do_send_ok)
send_ok(thd);
}
@@ -4489,7 +4513,13 @@
my_free((gptr) new_table,MYF(0));
goto err;
}
- write_bin_log(thd, TRUE);
+ /* We don't replicate alter table statement on temporary tables */
+ if (!binlog_row_based && mysql_bin_log.is_open())
+ {
+ thd->clear_error();
+ Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
+ mysql_bin_log.write(&qinfo);
+ }
goto end_temporary;
}
@@ -4620,7 +4650,14 @@
goto err;
}
thd->proc_info="end";
- write_bin_log(thd, TRUE);
+ if (mysql_bin_log.is_open())
+ {
+ DBUG_ASSERT(!(binlog_row_based &&
+ (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
+ thd->clear_error();
+ thd->binlog_query(THD::STMT_QUERY_TYPE,
+ thd->query, thd->query_length, FALSE, FALSE);
+ }
VOID(pthread_cond_broadcast(&COND_refresh));
VOID(pthread_mutex_unlock(&LOCK_open));
/*
@@ -4797,7 +4834,7 @@
{
copy_ptr->do_copy(copy_ptr);
}
- if ((error=to->file->write_row((byte*) to->record[0])))
+ if ((error=to->file->ha_write_row((byte*) to->record[0])))
{
if ((!ignore &&
handle_duplicates != DUP_REPLACE) ||
--- 1.57/sql/sql_udf.cc 2005-11-07 17:14:56 +01:00
+++ 1.58/sql/sql_udf.cc 2005-11-09 14:48:17 +01:00
@@ -454,7 +454,7 @@
table->field[2]->store(u_d->dl,(uint) strlen(u_d->dl), system_charset_info);
if (table->s->fields >= 4) // If not old func format
table->field[3]->store((longlong) u_d->type, TRUE);
- error = table->file->write_row(table->record[0]);
+ error = table->file->ha_write_row(table->record[0]);
close_thread_tables(thd);
if (error)
@@ -513,7 +513,7 @@
HA_READ_KEY_EXACT))
{
int error;
- if ((error = table->file->delete_row(table->record[0])))
+ if ((error = table->file->ha_delete_row(table->record[0])))
table->file->print_error(error, MYF(0));
}
close_thread_tables(thd);
--- 1.175/sql/sql_update.cc 2005-11-07 16:24:44 +01:00
+++ 1.176/sql/sql_update.cc 2005-11-09 15:21:12 +01:00
@@ -498,8 +498,8 @@
else
{
/* Non-batched update */
- error= table->file->update_row((byte*) table->record[1],
- (byte*) table->record[0]);
+ error= table->file->ha_update_row((byte*) table->record[1],
+ (byte*) table->record[0]);
}
if (!error)
{
@@ -619,10 +619,13 @@
{
if (error < 0)
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- transactional_table, FALSE);
- if (mysql_bin_log.write(&qinfo) && transactional_table)
+ if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length,
+ transactional_table, FALSE) &&
+ transactional_table)
+ {
error=1; // Rollback update
+ }
}
if (!transactional_table)
thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
@@ -1358,8 +1361,8 @@
*/
main_table->file->extra(HA_EXTRA_PREPARE_FOR_UPDATE);
}
- if ((error=table->file->update_row(table->record[1],
- table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],
+ table->record[0])))
{
updated--;
if (!ignore || error != HA_ERR_FOUND_DUPP_KEY)
@@ -1390,7 +1393,7 @@
memcpy((char*) tmp_table->field[0]->ptr,
(char*) table->file->ref, table->file->ref_length);
/* Write row, ignoring duplicated updates to a row */
- if ((error= tmp_table->file->write_row(tmp_table->record[0])) &&
+ if ((error= tmp_table->file->ha_write_row(tmp_table->record[0])) &&
(error != HA_ERR_FOUND_DUPP_KEY &&
error != HA_ERR_FOUND_DUPP_UNIQUE))
{
@@ -1505,8 +1508,8 @@
if (compare_record(table, thd->query_id))
{
- if ((local_error=table->file->update_row(table->record[1],
- table->record[0])))
+ if ((local_error=table->file->ha_update_row(table->record[1],
+ table->record[0])))
{
if (!ignore || local_error != HA_ERR_FOUND_DUPP_KEY)
goto err;
@@ -1585,10 +1588,13 @@
{
if (local_error == 0)
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- transactional_tables, FALSE);
- if (mysql_bin_log.write(&qinfo) && trans_safe)
+ if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length,
+ transactional_tables, FALSE) &&
+ trans_safe)
+ {
local_error= 1; // Rollback update
+ }
}
if (!transactional_tables)
thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
--- 1.186/sql/table.cc 2005-11-07 16:24:45 +01:00
+++ 1.187/sql/table.cc 2005-11-09 14:48:17 +01:00
@@ -424,6 +424,7 @@
if (!(record= (char *) alloc_root(&outparam->mem_root,
rec_buff_length * records)))
goto err; /* purecov: inspected */
+ outparam->write_row_record= NULL;
share->default_values= (byte *) record;
if (my_pread(file,(byte*) record, (uint) share->reclength,
@@ -1032,6 +1033,16 @@
if (use_hash)
(void) hash_check(&share->name_hash);
#endif
+#ifdef HAVE_REPLICATION
+ share->table_map_id= table_mapping::NO_TABLE;
+ /*
+ This constant is used to mark that no table map version has been
+ assigned. No arithmetic is done on the value: it will be
+ overwritten with a value taken from MYSQL_BIN_LOG.
+ */
+ share->table_map_version= ~(ulonglong)0;
+#endif
+
DBUG_RETURN (0);
err:
--- 1.115/sql/table.h 2005-11-07 16:24:45 +01:00
+++ 1.116/sql/table.h 2005-11-09 14:48:17 +01:00
@@ -178,6 +178,8 @@
my_bool crashed;
my_bool is_view;
my_bool name_lock, replace_with_name_lock;
+ ulong table_map_id;
+ ulonglong table_map_version;
/*
TRUE if this is a system table like 'mysql.proc', which we want to be
able to open and lock even when we already have some tables open and
@@ -203,6 +205,8 @@
Field **field; /* Pointer to fields */
byte *record[2]; /* Pointer to records */
+ byte *write_row_record; /* Used as optimisation in
+ THD::write_row */
byte *insert_values; /* used by INSERT ... UPDATE */
key_map quick_keys, used_keys, keys_in_use_for_query;
KEY *key_info; /* data of keys in database */
--- 1.52/sql/share/errmsg.txt 2005-11-06 13:12:49 +01:00
+++ 1.53/sql/share/errmsg.txt 2005-11-09 15:21:12 +01:00
@@ -5419,6 +5419,10 @@
eng "Cannot delete or update a parent row: a foreign key constraint fails (%.192s)"
ER_NO_REFERENCED_ROW_2 23000
eng "Cannot add or update a child row: a foreign key constraint fails (%.192s)"
+ER_BINLOG_ROW_LOGGING_FAILED
+ eng "Writing one row to the row-based binary log failed"
+ER_BINLOG_ROW_WRONG_TABLE_DEF
+ eng "Table definition on master and slave does not match"
ER_SP_BAD_VAR_SHADOW 42000
eng "Variable '%-.64s' must be quoted with `...`, or renamed"
ER_PARTITION_REQUIRES_VALUES_ERROR
--- 1.40/mysql-test/mysql-test-run.pl 2005-11-04 21:09:53 +01:00
+++ 1.41/mysql-test/mysql-test-run.pl 2005-11-09 14:48:12 +01:00
@@ -2452,6 +2452,10 @@
my $cmdline_mysqldump= "$exe_mysqldump --no-defaults -uroot " .
"--port=$master->[0]->{'path_myport'} " .
"--socket=$master->[0]->{'path_mysock'} --password=";
+
+ my $cmdline_mysqldumpslave= "$exe_mysqldump --no-defaults -uroot " .
+ "--socket=$slave->[0]->{'path_mysock'} --password=";
+
if ( $opt_debug )
{
$cmdline_mysqldump .=
@@ -2519,6 +2523,7 @@
$ENV{'MYSQL_CHECK'}= $cmdline_mysqlcheck;
$ENV{'MYSQL_DUMP'}= $cmdline_mysqldump;
$ENV{'MYSQL_IMPORT'}= $cmdline_mysqlimport;
+ $ENV{'MYSQL_DUMP_SLAVE'}= $cmdline_mysqldumpslave;
$ENV{'MYSQL_SHOW'}= $cmdline_mysqlshow;
$ENV{'MYSQL_BINLOG'}= $cmdline_mysqlbinlog;
$ENV{'MYSQL_FIX_SYSTEM_TABLES'}= $cmdline_mysql_fix_system_tables;
--- 1.11/mysql-test/t/disabled.def 2005-11-06 00:04:23 +01:00
+++ 1.12/mysql-test/t/disabled.def 2005-11-09 14:48:12 +01:00
@@ -6,7 +6,7 @@
#
# <testcasename> : Comment test
#
-# Don't use any TAB characters for whitespace.
+# Do not use any TAB characters for whitespace.
#
##############################################################################
@@ -15,5 +15,15 @@
rpl_until : Unstable test case, bug#12429
rpl_deadlock : Unstable test case, bug#12429
kill : Unstable test case, bug#9712
+rpl_row_replicate_do:Causes wrong result on slave Bug 12421
+rpl_row_mystery22:shows rbr slave issues Bug 12418
+rpl_row_relayrotate:Bug 14082
+rpl_row_timezone:Timestamp incorrectly replicated Bug 12443
+rpl_row_000002:create table from temporary Bug 12345
+rpl_row_sp006_InnoDB:Bug 12586
+rpl_row_func002:Bug 12510
+rpl_row_view01:Bug 12687
+rpl_row_NOW:Bug 12574
+rpl_bit_npk:Bug 13418
archive_gis : The test fails on 32bit Linux
compress : Magnus will fix
--- 1.235/sql/ha_innodb.cc 2005-11-07 17:14:55 +01:00
+++ 1.236/sql/ha_innodb.cc 2005-11-09 14:48:12 +01:00
@@ -832,6 +832,7 @@
HA_CAN_INDEX_BLOBS |
HA_CAN_SQL_HANDLER |
HA_NOT_EXACT_COUNT |
+ HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS |
HA_PRIMARY_KEY_IN_READ_INDEX |
HA_CAN_GEOMETRY |
HA_TABLE_SCAN_ON_INDEX),
--- 1.113/include/my_global.h 2005-11-06 14:26:18 +01:00
+++ 1.114/include/my_global.h 2005-11-09 14:48:11 +01:00
@@ -825,6 +825,14 @@
typedef long long my_ptrdiff_t;
#endif
+#if HAVE_SIZE_T
+typedef size_t my_size_t;
+#elif SIZEOF_CHARP <= SIZEOF_LONG
+typedef unsigned long my_size_t;
+#else
+typedef unsigned long long my_size_t;
+#endif
+
#define MY_ALIGN(A,L) (((A) + (L) - 1) & ~((L) - 1))
#define ALIGN_SIZE(A) MY_ALIGN((A),sizeof(double))
/* Size to make adressable obj. */
--- 1.142/sql/set_var.cc 2005-11-07 17:14:56 +01:00
+++ 1.143/sql/set_var.cc 2005-11-09 15:21:12 +01:00
@@ -630,6 +630,7 @@
{"bdb_shared_data", (char*) &berkeley_shared_data, SHOW_BOOL},
{"bdb_tmpdir", (char*) &berkeley_tmpdir, SHOW_CHAR_PTR},
{sys_binlog_cache_size.name,(char*) &sys_binlog_cache_size, SHOW_SYS},
+ {"binlog_format", (char*) &opt_binlog_format, SHOW_CHAR_PTR},
{sys_bulk_insert_buff_size.name,(char*) &sys_bulk_insert_buff_size,SHOW_SYS},
{sys_character_set_client.name,(char*) &sys_character_set_client, SHOW_SYS},
{sys_character_set_connection.name,(char*) &sys_character_set_connection,SHOW_SYS},
@@ -682,6 +683,7 @@
{sys_have_raid.name, (char*) &have_raid, SHOW_HAVE},
{sys_have_rtree_keys.name, (char*) &have_rtree_keys, SHOW_HAVE},
{sys_have_symlink.name, (char*) &have_symlink, SHOW_HAVE},
+ {"have_row_based_replication",(char*) &have_row_based_replication,SHOW_HAVE},
{"init_connect", (char*) &sys_init_connect, SHOW_SYS},
{"init_file", (char*) &opt_init_file, SHOW_CHAR_PTR},
{"init_slave", (char*) &sys_init_slave, SHOW_SYS},
--- 1.70/libmysqld/Makefile.am 2005-11-07 16:24:34 +01:00
+++ 1.71/libmysqld/Makefile.am 2005-11-09 14:48:12 +01:00
@@ -45,6 +45,7 @@
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\
--- 1.93/sql/sp.cc 2005-11-04 21:09:55 +01:00
+++ 1.94/sql/sp.cc 2005-11-09 14:48:13 +01:00
@@ -581,14 +581,14 @@
}
ret= SP_OK;
- if (table->file->write_row(table->record[0]))
+ if (table->file->ha_write_row(table->record[0]))
ret= SP_WRITE_ROW_FAILED;
else if (mysql_bin_log.is_open())
{
thd->clear_error();
/* Such a statement can always go directly to binlog, no trans cache */
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length, FALSE, FALSE);
}
}
@@ -614,7 +614,7 @@
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK)
{
- if (table->file->delete_row(table->record[0]))
+ if (table->file->ha_delete_row(table->record[0]))
ret= SP_DELETE_ROW_FAILED;
}
close_thread_tables(thd);
@@ -649,7 +649,7 @@
table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment.str,
chistics->comment.length,
system_charset_info);
- if ((table->file->update_row(table->record[1],table->record[0])))
+ if ((table->file->ha_update_row(table->record[1],table->record[0])))
ret= SP_WRITE_ROW_FAILED;
}
close_thread_tables(thd);
@@ -869,7 +869,7 @@
do
{
- if (! table->file->delete_row(table->record[0]))
+ if (! table->file->ha_delete_row(table->record[0]))
deleted= TRUE; /* We deleted something */
else
{
--- 1.194/sql/sp_head.cc 2005-10-26 19:12:23 +02:00
+++ 1.195/sql/sp_head.cc 2005-11-09 14:48:14 +01:00
@@ -87,11 +87,11 @@
case SQLCOM_SHOW_ERRORS:
case SQLCOM_SHOW_FIELDS:
case SQLCOM_SHOW_GRANTS:
- case SQLCOM_SHOW_INNODB_STATUS:
+ case SQLCOM_SHOW_ENGINE_STATUS:
+ case SQLCOM_SHOW_ENGINE_LOGS:
+ case SQLCOM_SHOW_ENGINE_MUTEX:
case SQLCOM_SHOW_KEYS:
- case SQLCOM_SHOW_LOGS:
case SQLCOM_SHOW_MASTER_STAT:
- case SQLCOM_SHOW_MUTEX_STATUS:
case SQLCOM_SHOW_NEW_MASTER:
case SQLCOM_SHOW_OPEN_TABLES:
case SQLCOM_SHOW_PRIVILEGES:
--- 1.92/sql/slave.h 2005-11-04 21:09:55 +01:00
+++ 1.93/sql/slave.h 2005-11-09 15:21:12 +01:00
@@ -14,19 +14,23 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#ifdef HAVE_REPLICATION
-
#ifndef SLAVE_H
#define SLAVE_H
+#ifdef HAVE_REPLICATION
+
#include "mysql.h"
#include "my_list.h"
#include "rpl_filter.h"
+#include "rpl_tblmap.h"
#define SLAVE_NET_TIMEOUT 3600
#define MAX_SLAVE_ERRMSG 1024
#define MAX_SLAVE_ERROR 2000
+/* Forward declarations */
+class table_mapping;
+
/*****************************************************************************
MySQL Replication
@@ -357,6 +361,9 @@
return ((until_condition == UNTIL_MASTER_POS) ? group_master_log_pos :
group_relay_log_pos);
}
+
+ table_mapping m_table_map;
+
/*
Last charset (6 bytes) seen by slave SQL thread is cached here; it helps
the thread save 3 get_charset() per Query_log_event if the charset is not
@@ -365,6 +372,24 @@
*/
void cached_charset_invalidate();
bool cached_charset_compare(char *charset);
+
+ /*
+ To reload special tables when they are changes, we introduce a set
+ of functions that will mark whenever special functions need to be
+ called after modifying tables. Right now, the tables are either
+ ACL tables or grants tables.
+ */
+ enum enum_reload_flag
+ {
+ RELOAD_NONE_F = 0UL,
+ RELOAD_GRANT_F = (1UL << 0),
+ RELOAD_ACCESS_F = (1UL << 1)
+ };
+
+ ulong m_reload_flags;
+
+ void touching_table(char const* db, char const* table, ulong table_id);
+ void transaction_end(THD*);
} RELAY_LOG_INFO;
@@ -481,10 +506,6 @@
#define IO_RPL_LOG_NAME (mi->master_log_name[0] ? mi->master_log_name :\
"FIRST")
-/* masks for start/stop operations on io and sql slave threads */
-#define SLAVE_IO 1
-#define SLAVE_SQL 2
-
/*
If the following is set, if first gives an error, second will be
tried. Otherwise, if first fails, we fail.
@@ -579,8 +600,12 @@
extern I_List<THD> threads;
-#endif
-#else
+#endif /* HAVE_REPLICATION */
+
+/* masks for start/stop operations on io and sql slave threads */
#define SLAVE_IO 1
#define SLAVE_SQL 2
-#endif /* HAVE_REPLICATION */
+
+#endif
+
+
--- 1.108/BitKeeper/etc/gone 2005-05-31 19:59:44 +02:00
+++ 1.109/BitKeeper/etc/gone 2005-11-09 14:47:38 +01:00
@@ -1190,6 +1190,9 @@
nick@stripped|mysql-test/t/rpl_empty_master_crash.test|20020531235552|52328|99464e737639ccc6
reggie@mdk10.(none)|BitKeeper/deleted/.del-reserved_win_names-master.opt~e56da049a7ce9a5b|20050523193219|41081
reggie@mdk10.(none)|mysql-test/t/reserved_win_names-master.opt|20050520210356|14878|e56da049a7ce9a5b
+rpl_row_err_ignoredtable.test
+rpl_row_loaddata_m.test
+rpl_row_multi_query.test
sasha@stripped|BitKeeper/etc/logging_ok|20000801000905|12967|5b7d847a2158554
sasha@stripped|build-tags|20011125054855|05181|7afb7e785b80f97
sasha@stripped|build-tags|20011201050944|25384|b6f6fff142121618
--- 1.65/mysql-test/Makefile.am 2005-10-26 19:57:30 +02:00
+++ 1.66/mysql-test/Makefile.am 2005-11-09 15:21:11 +01:00
@@ -17,18 +17,8 @@
## Process this file with automake to create Makefile.in
-if HAVE_NDBCLUSTER_DB
SUBDIRS = ndb
DIST_SUBDIRS=ndb
-USE_NDBCLUSTER=\"--ndbcluster\"
-else
-# If one uses automake conditionals, automake will automatically
-# include all possible branches to DIST_SUBDIRS goal.
-# Reset DIST_SUBDIRS if we don't use NDB
-SUBDIRS=
-DIST_SUBDIRS=
-USE_NDBCLUSTER=\"\"
-endif
if HAVE_ROW_BASED_REPLICATION
EXTRA_MASTER_MYSQLD_OPT=\"--binlog-format=row\"
@@ -149,7 +139,7 @@
-e 's!@''MYSQL_TCP_PORT''@!@MYSQL_TCP_PORT@!' \
-e 's!@''MYSQL_NO_DASH_VERSION''@!@MYSQL_NO_DASH_VERSION@!' \
-e 's!@''MYSQL_SERVER_SUFFIX''@!@MYSQL_SERVER_SUFFIX@!' \
- -e 's!@''USE_NDBCLUSTER''@!$(USE_NDBCLUSTER)!g' \
+ -e 's!@''USE_NDBCLUSTER''@!@TEST_NDBCLUSTER@!g' \
-e 's!@''EXTRA_MASTER_MYSQLD_OPT''@!$(EXTRA_MASTER_MYSQLD_OPT)!g' \
-e 's!@''EXTRA_SLAVE_MYSQLD_OPT''@!$(EXTRA_SLAVE_MYSQLD_OPT)!g' \
$< > $@-t
--- 1.39/sql/sql_repl.h 2005-03-22 10:04:18 +01:00
+++ 1.40/sql/sql_repl.h 2005-11-09 14:48:16 +01:00
@@ -14,6 +14,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+#include "rpl_filter.h"
+
#ifdef HAVE_REPLICATION
#include "slave.h"
--- 1.53/BUILD/SETUP.sh 2005-11-04 10:43:44 +01:00
+++ 1.54/BUILD/SETUP.sh 2005-11-09 14:48:11 +01:00
@@ -56,9 +56,9 @@
#debug_extra_warnings="-Wuninitialized"
c_warnings="$global_warnings -Wunused"
cxx_warnings="$global_warnings -Woverloaded-virtual -Wsign-promo -Wreorder -Wctor-dtor-privacy -Wnon-virtual-dtor"
-base_max_configs="--with-innodb --with-berkeley-db --with-ndbcluster --with-archive-storage-engine --with-big-tables --with-blackhole-storage-engine --with-federated-storage-engine --with-csv-storage-engine $SSL_LIBRARY"
+base_max_configs="--with-innodb --with-berkeley-db --with-ndbcluster --with-archive-storage-engine --with-big-tables --with-blackhole-storage-engine --with-federated-storage-engine --with-csv-storage-engine --with-partition $SSL_LIBRARY"
base_max_no_ndb_configs="--with-innodb --with-berkeley-db --without-ndbcluster --with-archive-storage-engine --with-big-tables --with-blackhole-storage-engine --with-federated-storage-engine --with-csv-storage-engine $SSL_LIBRARY"
-max_leave_isam_configs="--with-innodb --with-berkeley-db --with-ndbcluster --with-archive-storage-engine --with-federated-storage-engine --with-blackhole-storage-engine --with-csv-storage-engine $SSL_LIBRARY --with-embedded-server --with-big-tables"
+max_leave_isam_configs="--with-innodb --with-berkeley-db --with-ndbcluster --with-archive-storage-engine --with-federated-storage-engine --with-blackhole-storage-engine --with-csv-storage-engine --with-embedded-server --with-big-tables --with-partition $SSL_LIBRARY"
max_configs="$base_max_configs --with-embedded-server --with-row-based-replication"
max_no_ndb_configs="$base_max_no_ndb_configs --with-embedded-server --with-row-based-replication"
| Thread |
|---|
| • bk commit into 5.1 tree (tomas:1.1948) | tomas | 9 Nov |