From 8ddf0c441464bcb19595ff7735d3f0de8a54318f Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Aug 2001 18:16:43 -0600 Subject: mysqlbinlog->client BitKeeper/etc/ignore: Added client/log_event.cc client/log_event.h client/mf_iocache.c client/mf_iocache.cc client/mysqlbinlog client/mysys_priv.h mysql.kdevprj to the ignore list Makefile.am: do symlink hack in the client directory ( originally needed to get log_event.cc for mysqlbinlog) client/mysqlbinlog.cc: fixes to make it compile in the client directory libmysql/Makefile.shared: link mysqlbinlog dependencies into libmysqlclient libmysql/libmysql.c: make simple_command and net_safe_read extern ( for mysqlbinlog) sql/log_event.cc: removed pthread dependency in mysqlbinlog sql/log_event.h: removed pthread dependency in mysqlbinlog --- .bzrignore | 7 + Makefile.am | 1 + client/Makefile.am | 17 +- client/mysqlbinlog.cc | 472 +++++++++++++++++++++++++++++++++++++++++++++++ libmysql/Makefile.shared | 3 +- libmysql/libmysql.c | 4 +- sql/Makefile.am | 4 - sql/log_event.cc | 31 ++-- sql/log_event.h | 4 + sql/mysqlbinlog.cc | 469 ---------------------------------------------- 10 files changed, 524 insertions(+), 488 deletions(-) create mode 100644 client/mysqlbinlog.cc delete mode 100644 sql/mysqlbinlog.cc diff --git a/.bzrignore b/.bzrignore index e18c49af87c..c6cfce671f6 100644 --- a/.bzrignore +++ b/.bzrignore @@ -291,3 +291,10 @@ myisam/test1.MYD myisam/test1.MYI .gdbinit .vimrc +client/log_event.cc +client/log_event.h +client/mf_iocache.c +client/mf_iocache.cc +client/mysqlbinlog +client/mysys_priv.h +mysql.kdevprj diff --git a/Makefile.am b/Makefile.am index bfe6a8a2e43..22e066ac9d4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,6 +37,7 @@ linked_include_sources: echo timestamp > linked_include_sources linked_client_sources: @linked_client_targets@ + cd client; $(MAKE) link_sources echo timestamp > linked_client_sources linked_libmysql_sources: diff --git a/client/Makefile.am b/client/Makefile.am index 24221dcab74..c05f6a396dc 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -21,7 +21,8 @@ INCLUDES = -I$(srcdir)/../include \ -I.. LIBS = @CLIENT_LIBS@ LDADD = @CLIENT_EXTRA_LDFLAGS@ ../libmysql/libmysqlclient.la -bin_PROGRAMS = mysql mysqladmin mysqlcheck mysqlshow mysqldump mysqlimport mysqltest +bin_PROGRAMS = mysql mysqladmin mysqlcheck mysqlshow \ + mysqldump mysqlimport mysqltest mysqlbinlog noinst_PROGRAMS = insert_test select_test thread_test noinst_HEADERS = sql_string.h completion_hash.h my_readline.h mysql_SOURCES = mysql.cc readline.cc sql_string.cc completion_hash.cc @@ -36,10 +37,24 @@ insert_test_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) select_test_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) mysqltest_SOURCES= mysqltest.c mysqltest_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) +mysqlbinlog_SOURCES = mysqlbinlog.cc +mysqlbinlog_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) +sql_src=log_event.h log_event.cc +mysys_src=mysys_priv.h # Fix for mit-threads DEFS = -DUNDEF_THREADS_HACK +link_sources: + for f in $(sql_src) ; do \ + rm -f $$f; \ + @LN_CP_F@ ../sql/$$f $$f; \ + done; \ + for f in $(mysys_src); do \ + rm -f $$f; \ + @LN_CP_F@ ../mysys/$$f $$f; \ + done; + thread_test.o: thread_test.c $(COMPILE) -c @MT_INCLUDES@ $(INCLUDES) $< diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc new file mode 100644 index 00000000000..a89b41fdfd3 --- /dev/null +++ b/client/mysqlbinlog.cc @@ -0,0 +1,472 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#define MYSQL_CLIENT +#undef MYSQL_SERVER +#include +#include +#include +#include +#include +#include +#include "log_event.h" + +#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES) + +extern "C" +{ + int simple_command(MYSQL *mysql,enum enum_server_command command, + const char *arg, + uint length, my_bool skipp_check); + int net_safe_read(MYSQL* mysql); +} + +char server_version[SERVER_VERSION_LENGTH]; +uint32 server_id = 0; + +// needed by net_serv.c +ulong bytes_sent = 0L, bytes_received = 0L; +ulong mysqld_net_retry_count = 10L; +ulong net_read_timeout= NET_READ_TIMEOUT; +ulong net_write_timeout= NET_WRITE_TIMEOUT; +uint test_flags = 0; +FILE *result_file; + +#ifndef DBUG_OFF +static const char* default_dbug_option = "d:t:o,/tmp/mysqlbinlog.trace"; +#endif + +static struct option long_options[] = +{ +#ifndef DBUG_OFF + {"debug", optional_argument, 0, '#'}, +#endif + {"help", no_argument, 0, '?'}, + {"host", required_argument, 0, 'h'}, + {"offset", required_argument, 0, 'o'}, + {"password", required_argument, 0, 'p'}, + {"port", required_argument, 0, 'P'}, + {"position", required_argument, 0, 'j'}, + {"result-file", required_argument, 0, 'r'}, + {"short-form", no_argument, 0, 's'}, + {"table", required_argument, 0, 't'}, + {"user", required_argument, 0, 'u'}, + {"version", no_argument, 0, 'V'}, +}; + +void sql_print_error(const char *format,...); + +static bool short_form = 0; +static ulonglong offset = 0; +static const char* host = "localhost"; +static int port = MYSQL_PORT; +static const char* user = "test"; +static const char* pass = ""; +static ulonglong position = 0; +static bool use_remote = 0; +static short binlog_flags = 0; +static MYSQL* mysql = NULL; +static const char* table = 0; + +static void dump_local_log_entries(const char* logname); +static void dump_remote_log_entries(const char* logname); +static void dump_log_entries(const char* logname); +static void dump_remote_file(NET* net, const char* fname); +static void dump_remote_table(NET* net, const char* db, const char* table); +static void die(const char* fmt, ...); +static MYSQL* safe_connect(); + + +void sql_print_error(const char *format,...) +{ + va_list args; + va_start(args, format); + fprintf(stderr, "ERROR: "); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + va_end(args); +} + +static void die(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + fprintf(stderr, "ERROR: "); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + exit(1); +} + +static void print_version() +{ + printf("%s Ver 1.5 for %s at %s\n",my_progname,SYSTEM_TYPE, MACHINE_TYPE); +} + + +static void usage() +{ + print_version(); + puts("By Sasha, for your professional use\n\ +This software comes with NO WARRANTY: see the file PUBLIC for details\n"); + + printf("\ +Dumps a MySQL binary log in a format usable for viewing or for pipeing to\n\ +the mysql command line client\n\n"); + printf("Usage: %s [options] log-files\n",my_progname); + puts("Options:"); +#ifndef DBUG_OFF + printf("-#, --debug[=...] Output debug log. (%s)\n", + default_dbug_option); +#endif + printf("\ +-?, --help Display this help and exit\n\ +-s, --short-form Just show the queries, no extra info\n\ +-o, --offset=N Skip the first N entries\n\ +-h, --host=server Get the binlog from server\n\ +-P, --port=port Use port to connect to the remote server\n\ +-u, --user=username Connect to the remove server as username\n\ +-p, --password=password Password to connect to remote server\n\ +-r, --result-file=file Direct output to a given file\n\ +-j, --position=N Start reading the binlog at position N\n\ +-t, --table=name Get raw table dump using COM_TABLE_DUMB\n\ +-V, --version Print version and exit.\n\ +"); +} + +static void dump_remote_file(NET* net, const char* fname) +{ + char buf[FN_REFLEN+1]; + uint len = (uint) strlen(fname); + buf[0] = 0; + memcpy(buf + 1, fname, len + 1); + if(my_net_write(net, buf, len +2) || net_flush(net)) + die("Failed requesting the remote dump of %s", fname); + for(;;) + { + uint packet_len = my_net_read(net); + if(packet_len == 0) + { + if(my_net_write(net, "", 0) || net_flush(net)) + die("Failed sending the ack packet"); + + // we just need to send something, as the server will read but + // not examine the packet - this is because mysql_load() sends an OK when it is done + break; + } + else if(packet_len == packet_error) + die("Failed reading a packet during the dump of %s ", fname); + + if(!short_form) + (void)my_fwrite(result_file, (byte*) net->read_pos, packet_len,MYF(0)); + } + + fflush(result_file); +} + +static int parse_args(int *argc, char*** argv) +{ + int c, opt_index = 0; + + result_file = stdout; + while((c = getopt_long(*argc, *argv, "so:#::h:j:u:p:P:r:t:?V", long_options, + &opt_index)) != EOF) + { + switch(c) + { +#ifndef DBUG_OFF + case '#': + DBUG_PUSH(optarg ? optarg : default_dbug_option); + break; +#endif + case 's': + short_form = 1; + break; + + case 'o': + offset = strtoull(optarg,(char**) 0, 10); + break; + + case 'j': + position = strtoull(optarg,(char**) 0, 10); + break; + + case 'h': + use_remote = 1; + host = my_strdup(optarg, MYF(0)); + break; + + case 'P': + use_remote = 1; + port = atoi(optarg); + break; + + case 'p': + use_remote = 1; + pass = my_strdup(optarg, MYF(0)); + break; + + case 'r': + if (!(result_file = my_fopen(optarg, O_WRONLY | O_BINARY, MYF(MY_WME)))) + exit(1); + break; + + case 'u': + use_remote = 1; + user = my_strdup(optarg, MYF(0)); + break; + + case 't': + table = my_strdup(optarg, MYF(0)); + break; + + case 'V': + print_version(); + exit(0); + + case '?': + default: + usage(); + exit(0); + + } + } + + (*argc)-=optind; + (*argv)+=optind; + + return 0; +} + +static MYSQL* safe_connect() +{ + MYSQL *local_mysql = mysql_init(NULL); + if(!local_mysql) + die("Failed on mysql_init"); + + if(!mysql_real_connect(local_mysql, host, user, pass, 0, port, 0, 0)) + die("failed on connect: %s", mysql_error(local_mysql)); + + return local_mysql; +} + +static void dump_log_entries(const char* logname) +{ + if(use_remote) + dump_remote_log_entries(logname); + else + dump_local_log_entries(logname); +} + +static void dump_remote_table(NET* net, const char* db, const char* table) +{ + char buf[1024]; + char * p = buf; + uint table_len = (uint) strlen(table); + uint db_len = (uint) strlen(db); + if(table_len + db_len > sizeof(buf) - 2) + die("Buffer overrun"); + + *p++ = db_len; + memcpy(p, db, db_len); + p += db_len; + *p++ = table_len; + memcpy(p, table, table_len); + + if(simple_command(mysql, COM_TABLE_DUMP, buf, p - buf + table_len, 1)) + die("Error sending the table dump command"); + + for(;;) + { + uint packet_len = my_net_read(net); + if(packet_len == 0) break; // end of file + if(packet_len == packet_error) + die("Error reading packet in table dump"); + my_fwrite(result_file, (byte*)net->read_pos, packet_len, MYF(MY_WME)); + fflush(result_file); + } +} + + +static void dump_remote_log_entries(const char* logname) +{ + char buf[128]; + char last_db[FN_REFLEN+1] = ""; + uint len; + NET* net = &mysql->net; + if(!position) position = 4; // protect the innocent from spam + if (position < 4) + { + position = 4; + // warn the guity + sql_print_error("Warning: The position in the binary log can't be less than 4.\nStarting from position 4\n"); + } + int4store(buf, position); + int2store(buf + 4, binlog_flags); + len = (uint) strlen(logname); + int4store(buf + 6, 0); + memcpy(buf + 10, logname,len); + if(simple_command(mysql, COM_BINLOG_DUMP, buf, len + 10, 1)) + die("Error sending the log dump command"); + + for(;;) + { + len = net_safe_read(mysql); + if (len == packet_error) + die("Error reading packet from server: %s", mysql_error(mysql)); + if(len == 1 && net->read_pos[0] == 254) + break; // end of data + DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n", + len, net->read_pos[5])); + Log_event * ev = Log_event::read_log_event( + (const char*) net->read_pos + 1 , + len - 1); + if(ev) + { + ev->print(result_file, short_form, last_db); + if(ev->get_type_code() == LOAD_EVENT) + dump_remote_file(net, ((Load_log_event*)ev)->fname); + delete ev; + } + else + die("Could not construct log event object"); + } +} + +static void dump_local_log_entries(const char* logname) +{ + File fd = -1; + IO_CACHE cache,*file= &cache; + ulonglong rec_count = 0; + char last_db[FN_REFLEN+1] = ""; + + if (logname && logname[0] != '-') + { + if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0) + exit(1); + if (init_io_cache(file, fd, 0, READ_CACHE, (my_off_t) position, 0, + MYF(MY_WME | MY_NABP))) + exit(1); + } + else + { + if (init_io_cache(file, fileno(result_file), 0, READ_CACHE, (my_off_t) 0, + 0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE))) + exit(1); + if (position) + { + /* skip 'position' characters from stdout */ + byte buff[IO_SIZE]; + my_off_t length,tmp; + for (length= (my_off_t) position ; length > 0 ; length-=tmp) + { + tmp=min(length,sizeof(buff)); + if (my_b_read(file,buff, (uint) tmp)) + exit(1); + } + } + file->pos_in_file=position; + file->seek_not_done=0; + } + + if (!position) + { + char magic[4]; + if (my_b_read(file, (byte*) magic, sizeof(magic))) + die("I/O error reading binlog magic number"); + if(memcmp(magic, BINLOG_MAGIC, 4)) + die("Bad magic number; The file is probably not a MySQL binary log"); + } + + for (;;) + { + char llbuff[21]; + my_off_t old_off = my_b_tell(file); + + Log_event* ev = Log_event::read_log_event(file); + if (!ev) + { + if (file->error) + die("\ +Could not read entry at offset %s : Error in log format or read error", + llstr(old_off,llbuff)); + // file->error == 0 means EOF, that's OK, we break in this case + break; + } + if (rec_count >= offset) + { + if (!short_form) + fprintf(result_file, "# at %s\n",llstr(old_off,llbuff)); + + ev->print(result_file, short_form, last_db); + } + rec_count++; + delete ev; + } + if(fd >= 0) + my_close(fd, MYF(MY_WME)); + end_io_cache(file); +} + + +int main(int argc, char** argv) +{ + MY_INIT(argv[0]); + parse_args(&argc, (char***)&argv); + + if(!argc && !table) + { + usage(); + return -1; + } + + if(use_remote) + { + mysql = safe_connect(); + } + + if (table) + { + if(!use_remote) + die("You must specify connection parameter to get table dump"); + char* db = (char*)table; + char* tbl = (char*) strchr(table, '.'); + if(!tbl) + die("You must use database.table syntax to specify the table"); + *tbl++ = 0; + dump_remote_table(&mysql->net, db, tbl); + } + else + { + while(--argc >= 0) + { + dump_log_entries(*(argv++)); + } + } + if (result_file != stdout) + my_fclose(result_file, MYF(0)); + if (use_remote) + mysql_close(mysql); + return 0; +} + +/* + We must include this here as it's compiled with different options for + the server +*/ + +#include "log_event.cc" diff --git a/libmysql/Makefile.shared b/libmysql/Makefile.shared index b6ea5832e04..e9e100e38b1 100644 --- a/libmysql/Makefile.shared +++ b/libmysql/Makefile.shared @@ -55,7 +55,8 @@ mysysobjects1 = my_init.lo my_static.lo my_malloc.lo my_realloc.lo \ mf_loadpath.lo my_pthread.lo my_thr_init.lo \ thr_mutex.lo mulalloc.lo string.lo default.lo \ my_compress.lo array.lo my_once.lo list.lo my_net.lo \ - charset.lo hash.lo + charset.lo hash.lo mf_iocache.lo my_seek.lo \ + my_pread.lo mf_cache.lo # Not needed in the minimum library mysysobjects2 = getopt.lo getopt1.lo getvar.lo my_lib.lo mysysobjects = $(mysysobjects1) $(mysysobjects2) diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 9221812ea65..4101fe18ea1 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -287,7 +287,7 @@ HANDLE create_named_pipe(NET *net, uint connect_timeout, char **arg_host, ** or packet is an error message *****************************************************************************/ -static uint +uint net_safe_read(MYSQL *mysql) { NET *net= &mysql->net; @@ -417,7 +417,7 @@ static void free_rows(MYSQL_DATA *cur) } -static int +int simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg, uint length, my_bool skipp_check) { diff --git a/sql/Makefile.am b/sql/Makefile.am index 28484f09b3d..f1941e1e25c 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -27,7 +27,6 @@ INCLUDES = @MT_INCLUDES@ \ -I$(srcdir) -I../include -I.. -I. WRAPLIBS= @WRAPLIBS@ SUBDIRS = share -bin_PROGRAMS = mysqlbinlog libexec_PROGRAMS = mysqld noinst_PROGRAMS = gen_lex_hash gen_lex_hash_LDFLAGS = @NOINST_LDFLAGS@ @@ -83,9 +82,6 @@ mysqld_SOURCES = sql_lex.cc \ md5.c stacktrace.c gen_lex_hash_SOURCES = gen_lex_hash.cc gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS) -mysqlbinlog_SOURCES = mysqlbinlog.cc mini_client.cc net_serv.cc \ - mini_client_errors.c violite.c password.c -mysqlbinlog_LDADD = $(LDADD) $(CXXLDFLAGS) # $(mysqld_LDADD) DEFS = -DMYSQL_SERVER \ -DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \ diff --git a/sql/log_event.cc b/sql/log_event.cc index ac985c266c8..1263d361b7f 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -111,18 +111,29 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet, #endif // MYSQL_CLIENT -// allocates memory - the caller is responsible for clean-up +#ifndef MYSQL_CLIENT +#define UNLOCK_MUTEX if(log_lock) pthread_mutex_unlock(log_lock); +#else +#define UNLOCK_MUTEX +#endif +// allocates memory - the caller is responsible for clean-up +#ifndef MYSQL_CLIENT Log_event* Log_event::read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock) +#else +Log_event* Log_event::read_log_event(IO_CACHE* file) +#endif { time_t timestamp; uint32 server_id; char buf[LOG_EVENT_HEADER_LEN-4]; +#ifndef MYSQL_CLIENT if(log_lock) pthread_mutex_lock(log_lock); +#endif if (my_b_read(file, (byte *) buf, sizeof(buf))) { - if (log_lock) pthread_mutex_unlock(log_lock); + UNLOCK_MUTEX return NULL; } timestamp = uint4korr(buf); @@ -133,7 +144,7 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock) case QUERY_EVENT: { Query_log_event* q = new Query_log_event(file, timestamp, server_id); - if(log_lock) pthread_mutex_unlock(log_lock); + UNLOCK_MUTEX if (!q->query) { delete q; @@ -145,7 +156,7 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock) case LOAD_EVENT: { Load_log_event* l = new Load_log_event(file, timestamp, server_id); - if(log_lock) pthread_mutex_unlock(log_lock); + UNLOCK_MUTEX if (!l->table_name) { delete l; @@ -158,8 +169,7 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock) case ROTATE_EVENT: { Rotate_log_event* r = new Rotate_log_event(file, timestamp, server_id); - if(log_lock) pthread_mutex_unlock(log_lock); - + UNLOCK_MUTEX if (!r->new_log_ident) { delete r; @@ -171,8 +181,7 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock) case INTVAR_EVENT: { Intvar_log_event* e = new Intvar_log_event(file, timestamp, server_id); - if(log_lock) pthread_mutex_unlock(log_lock); - + UNLOCK_MUTEX if (e->type == INVALID_INT_EVENT) { delete e; @@ -184,13 +193,13 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock) case START_EVENT: { Start_log_event* e = new Start_log_event(file, timestamp, server_id); - if(log_lock) pthread_mutex_unlock(log_lock); + UNLOCK_MUTEX return e; } case STOP_EVENT: { Stop_log_event* e = new Stop_log_event(file, timestamp, server_id); - if(log_lock) pthread_mutex_unlock(log_lock); + UNLOCK_MUTEX return e; } default: @@ -198,7 +207,7 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock) } // default - if (log_lock) pthread_mutex_unlock(log_lock); + UNLOCK_MUTEX return NULL; } diff --git a/sql/log_event.h b/sql/log_event.h index 8d8ac183f61..94f7cce0e35 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -105,8 +105,12 @@ public: void print_timestamp(FILE* file, time_t *ts = 0); void print_header(FILE* file); +#ifndef MYSQL_CLIENT // if mutex is 0, the read will proceed without mutex static Log_event* read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock); +#else // avoid having to link mysqlbinlog against libpthread + static Log_event* read_log_event(IO_CACHE* file); +#endif static Log_event* read_log_event(const char* buf, int event_len); #ifndef MYSQL_CLIENT diff --git a/sql/mysqlbinlog.cc b/sql/mysqlbinlog.cc deleted file mode 100644 index 5edfe6e0591..00000000000 --- a/sql/mysqlbinlog.cc +++ /dev/null @@ -1,469 +0,0 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - - -#define MYSQL_CLIENT -#undef MYSQL_SERVER -#include -#include -#include -#include -#include -#define MYSQL_SERVER // We want the C++ version of net -#include -#include "log_event.h" -#include "mini_client.h" - -#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES) - -char server_version[SERVER_VERSION_LENGTH]; -uint32 server_id = 0; - -// needed by net_serv.c -ulong bytes_sent = 0L, bytes_received = 0L; -ulong mysqld_net_retry_count = 10L; -ulong net_read_timeout= NET_READ_TIMEOUT; -ulong net_write_timeout= NET_WRITE_TIMEOUT; -uint test_flags = 0; -FILE *result_file; - -#ifndef DBUG_OFF -static const char* default_dbug_option = "d:t:o,/tmp/mysqlbinlog.trace"; -#endif - -static struct option long_options[] = -{ -#ifndef DBUG_OFF - {"debug", optional_argument, 0, '#'}, -#endif - {"help", no_argument, 0, '?'}, - {"host", required_argument, 0, 'h'}, - {"offset", required_argument, 0, 'o'}, - {"password", required_argument, 0, 'p'}, - {"port", required_argument, 0, 'P'}, - {"position", required_argument, 0, 'j'}, - {"result-file", required_argument, 0, 'r'}, - {"short-form", no_argument, 0, 's'}, - {"table", required_argument, 0, 't'}, - {"user", required_argument, 0, 'u'}, - {"version", no_argument, 0, 'V'}, -}; - -void sql_print_error(const char *format,...); - -static bool short_form = 0; -static ulonglong offset = 0; -static const char* host = "localhost"; -static int port = MYSQL_PORT; -static const char* user = "test"; -static const char* pass = ""; -static ulonglong position = 0; -static bool use_remote = 0; -static short binlog_flags = 0; -static MYSQL* mysql = NULL; -static const char* table = 0; - -static void dump_local_log_entries(const char* logname); -static void dump_remote_log_entries(const char* logname); -static void dump_log_entries(const char* logname); -static void dump_remote_file(NET* net, const char* fname); -static void dump_remote_table(NET* net, const char* db, const char* table); -static void die(const char* fmt, ...); -static MYSQL* safe_connect(); - - -void sql_print_error(const char *format,...) -{ - va_list args; - va_start(args, format); - fprintf(stderr, "ERROR: "); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); - va_end(args); -} - -static void die(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - fprintf(stderr, "ERROR: "); - vfprintf(stderr, fmt, args); - fprintf(stderr, "\n"); - va_end(args); - exit(1); -} - -static void print_version() -{ - printf("%s Ver 1.4 for %s at %s\n",my_progname,SYSTEM_TYPE, MACHINE_TYPE); -} - - -static void usage() -{ - print_version(); - puts("By Sasha, for your professional use\n\ -This software comes with NO WARRANTY: see the file PUBLIC for details\n"); - - printf("\ -Dumps a MySQL binary log in a format usable for viewing or for pipeing to\n\ -the mysql command line client\n\n"); - printf("Usage: %s [options] log-files\n",my_progname); - puts("Options:"); -#ifndef DBUG_OFF - printf("-#, --debug[=...] Output debug log. (%s)\n", - default_dbug_option); -#endif - printf("\ --?, --help Display this help and exit\n\ --s, --short-form Just show the queries, no extra info\n\ --o, --offset=N Skip the first N entries\n\ --h, --host=server Get the binlog from server\n\ --P, --port=port Use port to connect to the remote server\n\ --u, --user=username Connect to the remove server as username\n\ --p, --password=password Password to connect to remote server\n\ --r, --result-file=file Direct output to a given file\n\ --j, --position=N Start reading the binlog at position N\n\ --t, --table=name Get raw table dump using COM_TABLE_DUMB\n\ --V, --version Print version and exit.\n\ -"); -} - -static void dump_remote_file(NET* net, const char* fname) -{ - char buf[FN_REFLEN+1]; - uint len = (uint) strlen(fname); - buf[0] = 0; - memcpy(buf + 1, fname, len + 1); - if(my_net_write(net, buf, len +2) || net_flush(net)) - die("Failed requesting the remote dump of %s", fname); - for(;;) - { - uint packet_len = my_net_read(net); - if(packet_len == 0) - { - if(my_net_write(net, "", 0) || net_flush(net)) - die("Failed sending the ack packet"); - - // we just need to send something, as the server will read but - // not examine the packet - this is because mysql_load() sends an OK when it is done - break; - } - else if(packet_len == packet_error) - die("Failed reading a packet during the dump of %s ", fname); - - if(!short_form) - (void)my_fwrite(result_file, (byte*) net->read_pos, packet_len,MYF(0)); - } - - fflush(result_file); -} - -static int parse_args(int *argc, char*** argv) -{ - int c, opt_index = 0; - - result_file = stdout; - while((c = getopt_long(*argc, *argv, "so:#::h:j:u:p:P:r:t:?V", long_options, - &opt_index)) != EOF) - { - switch(c) - { -#ifndef DBUG_OFF - case '#': - DBUG_PUSH(optarg ? optarg : default_dbug_option); - break; -#endif - case 's': - short_form = 1; - break; - - case 'o': - offset = strtoull(optarg,(char**) 0, 10); - break; - - case 'j': - position = strtoull(optarg,(char**) 0, 10); - break; - - case 'h': - use_remote = 1; - host = my_strdup(optarg, MYF(0)); - break; - - case 'P': - use_remote = 1; - port = atoi(optarg); - break; - - case 'p': - use_remote = 1; - pass = my_strdup(optarg, MYF(0)); - break; - - case 'r': - if (!(result_file = my_fopen(optarg, O_WRONLY | O_BINARY, MYF(MY_WME)))) - exit(1); - break; - - case 'u': - use_remote = 1; - user = my_strdup(optarg, MYF(0)); - break; - - case 't': - table = my_strdup(optarg, MYF(0)); - break; - - case 'V': - print_version(); - exit(0); - - case '?': - default: - usage(); - exit(0); - - } - } - - (*argc)-=optind; - (*argv)+=optind; - - return 0; -} - -static MYSQL* safe_connect() -{ - MYSQL *local_mysql = mc_mysql_init(NULL); - if(!local_mysql) - die("Failed on mc_mysql_init"); - - if(!mc_mysql_connect(local_mysql, host, user, pass, 0, port, 0, 0)) - die("failed on connect: %s", mc_mysql_error(local_mysql)); - - return local_mysql; -} - -static void dump_log_entries(const char* logname) -{ - if(use_remote) - dump_remote_log_entries(logname); - else - dump_local_log_entries(logname); -} - -static void dump_remote_table(NET* net, const char* db, const char* table) -{ - char buf[1024]; - char * p = buf; - uint table_len = (uint) strlen(table); - uint db_len = (uint) strlen(db); - if(table_len + db_len > sizeof(buf) - 2) - die("Buffer overrun"); - - *p++ = db_len; - memcpy(p, db, db_len); - p += db_len; - *p++ = table_len; - memcpy(p, table, table_len); - - if(mc_simple_command(mysql, COM_TABLE_DUMP, buf, p - buf + table_len, 1)) - die("Error sending the table dump command"); - - for(;;) - { - uint packet_len = my_net_read(net); - if(packet_len == 0) break; // end of file - if(packet_len == packet_error) - die("Error reading packet in table dump"); - my_fwrite(result_file, (byte*)net->read_pos, packet_len, MYF(MY_WME)); - fflush(result_file); - } -} - - -static void dump_remote_log_entries(const char* logname) -{ - char buf[128]; - char last_db[FN_REFLEN+1] = ""; - uint len; - NET* net = &mysql->net; - if(!position) position = 4; // protect the innocent from spam - if (position < 4) - { - position = 4; - // warn the guity - sql_print_error("Warning: The position in the binary log can't be less than 4.\nStarting from position 4\n"); - } - int4store(buf, position); - int2store(buf + 4, binlog_flags); - len = (uint) strlen(logname); - int4store(buf + 6, 0); - memcpy(buf + 10, logname,len); - if(mc_simple_command(mysql, COM_BINLOG_DUMP, buf, len + 10, 1)) - die("Error sending the log dump command"); - - for(;;) - { - len = mc_net_safe_read(mysql); - if (len == packet_error) - die("Error reading packet from server: %s", mc_mysql_error(mysql)); - if(len == 1 && net->read_pos[0] == 254) - break; // end of data - DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n", - len, net->read_pos[5])); - Log_event * ev = Log_event::read_log_event( - (const char*) net->read_pos + 1 , - len - 1); - if(ev) - { - ev->print(result_file, short_form, last_db); - if(ev->get_type_code() == LOAD_EVENT) - dump_remote_file(net, ((Load_log_event*)ev)->fname); - delete ev; - } - else - die("Could not construct log event object"); - } -} - -static void dump_local_log_entries(const char* logname) -{ - File fd = -1; - IO_CACHE cache,*file= &cache; - ulonglong rec_count = 0; - char last_db[FN_REFLEN+1] = ""; - - if (logname && logname[0] != '-') - { - if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0) - exit(1); - if (init_io_cache(file, fd, 0, READ_CACHE, (my_off_t) position, 0, - MYF(MY_WME | MY_NABP))) - exit(1); - } - else - { - if (init_io_cache(file, fileno(result_file), 0, READ_CACHE, (my_off_t) 0, - 0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE))) - exit(1); - if (position) - { - /* skip 'position' characters from stdout */ - byte buff[IO_SIZE]; - my_off_t length,tmp; - for (length= (my_off_t) position ; length > 0 ; length-=tmp) - { - tmp=min(length,sizeof(buff)); - if (my_b_read(file,buff, (uint) tmp)) - exit(1); - } - } - file->pos_in_file=position; - file->seek_not_done=0; - } - - if (!position) - { - char magic[4]; - if (my_b_read(file, (byte*) magic, sizeof(magic))) - die("I/O error reading binlog magic number"); - if(memcmp(magic, BINLOG_MAGIC, 4)) - die("Bad magic number; The file is probably not a MySQL binary log"); - } - - for (;;) - { - char llbuff[21]; - my_off_t old_off = my_b_tell(file); - - Log_event* ev = Log_event::read_log_event(file, 0); - if (!ev) - { - if (file->error) - die("\ -Could not read entry at offset %s : Error in log format or read error", - llstr(old_off,llbuff)); - // file->error == 0 means EOF, that's OK, we break in this case - break; - } - if (rec_count >= offset) - { - if (!short_form) - fprintf(result_file, "# at %s\n",llstr(old_off,llbuff)); - - ev->print(result_file, short_form, last_db); - } - rec_count++; - delete ev; - } - if(fd >= 0) - my_close(fd, MYF(MY_WME)); - end_io_cache(file); -} - - -int main(int argc, char** argv) -{ - MY_INIT(argv[0]); - parse_args(&argc, (char***)&argv); - - if(!argc && !table) - { - usage(); - return -1; - } - - if(use_remote) - { -#ifndef __WIN__ - init_thr_alarm(10); // need to do this manually -#endif - mysql = safe_connect(); - } - - if (table) - { - if(!use_remote) - die("You must specify connection parameter to get table dump"); - char* db = (char*)table; - char* tbl = (char*) strchr(table, '.'); - if(!tbl) - die("You must use database.table syntax to specify the table"); - *tbl++ = 0; - dump_remote_table(&mysql->net, db, tbl); - } - else - { - while(--argc >= 0) - { - dump_log_entries(*(argv++)); - } - } - if (result_file != stdout) - my_fclose(result_file, MYF(0)); - if (use_remote) - mc_mysql_close(mysql); - return 0; -} - -/* - We must include this here as it's compiled with different options for - the server -*/ - -#include "log_event.cc" -- cgit v1.2.1