2002-09-14 18:26:40 +02:00
/* Copyright (C) 2001 MySQL AB
2001-12-06 13:10:51 +01:00
2000-07-31 21:29:14 +02:00
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 .
2001-12-06 13:10:51 +01:00
2000-07-31 21:29:14 +02:00
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 .
2001-12-06 13:10:51 +01:00
2000-07-31 21:29:14 +02:00
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
2001-09-12 22:53:31 +02:00
# include "client_priv.h"
2001-08-30 20:16:39 +02:00
# include <time.h>
2003-07-04 22:06:17 +02:00
# include <assert.h>
2001-01-21 15:30:16 +01:00
# include "log_event.h"
2000-07-31 21:29:14 +02:00
2002-06-11 10:20:31 +02:00
# define BIN_LOG_HEADER_SIZE 4
2002-09-11 05:40:08 +02:00
# define PROBE_HEADER_LEN (EVENT_LEN_OFFSET+4)
2002-06-11 10:20:31 +02:00
2001-11-11 06:24:12 +01:00
2000-07-31 21:29:14 +02:00
# define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES)
2001-01-21 15:30:16 +01:00
char server_version [ SERVER_VERSION_LENGTH ] ;
2003-03-05 14:34:58 +01:00
ulong server_id = 0 ;
2000-07-31 21:29:14 +02:00
// needed by net_serv.c
ulong bytes_sent = 0L , bytes_received = 0L ;
ulong mysqld_net_retry_count = 10L ;
uint test_flags = 0 ;
2001-09-12 22:53:31 +02:00
static FILE * result_file ;
2000-07-31 21:29:14 +02:00
# ifndef DBUG_OFF
static const char * default_dbug_option = " d:t:o,/tmp/mysqlbinlog.trace " ;
# endif
2003-10-07 14:44:31 +02:00
static const char * load_default_groups [ ] = { " mysqlbinlog " , " client " , 0 } ;
2000-07-31 21:29:14 +02:00
2002-09-14 18:26:40 +02:00
void sql_print_error ( const char * format , . . . ) ;
2000-07-31 21:29:14 +02:00
2002-05-16 22:35:09 +02:00
static bool one_database = 0 ;
2003-10-08 00:30:10 +02:00
static const char * database = 0 ;
static my_bool force_opt = 0 , short_form = 0 , remote_opt = 0 ;
2001-01-03 15:54:46 +01:00
static ulonglong offset = 0 ;
2003-09-12 15:48:48 +02:00
static const char * host = 0 ;
2000-07-31 21:29:14 +02:00
static int port = MYSQL_PORT ;
2003-10-08 00:30:10 +02:00
static const char * sock = 0 ;
2003-09-12 15:48:48 +02:00
static const char * user = 0 ;
2003-10-21 12:22:24 +02:00
static char * pass = 0 ;
2001-01-03 15:54:46 +01:00
static ulonglong position = 0 ;
2000-07-31 21:29:14 +02:00
static short binlog_flags = 0 ;
static MYSQL * mysql = NULL ;
2003-07-04 22:06:17 +02:00
static const char * dirname_for_local_load = 0 ;
2000-07-31 21:29:14 +02:00
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 die ( const char * fmt , . . . ) ;
static MYSQL * safe_connect ( ) ;
2003-07-04 22:06:17 +02:00
class Load_log_processor
{
char target_dir_name [ MY_NFILE ] ;
int target_dir_name_len ;
DYNAMIC_ARRAY file_names ;
2004-02-06 17:57:11 +01:00
/*
Looking for new uniquie filename that doesn ' t exist yet by
adding postfix - % x
SYNOPSIS
create_unique_file ( )
filename buffer for filename
file_name_end tail of buffer that should be changed
should point to a memory enough to printf ( " -%x " , . . )
RETURN VALUES
values less than 0 - can ' t find new filename
values great or equal 0 - created file with found filename
*/
File create_unique_file ( char * filename , char * file_name_end )
2003-07-04 22:06:17 +02:00
{
2004-02-06 17:57:11 +01:00
File res ;
/* If we have to try more than 1000 times, something is seriously wrong */
for ( uint version = 0 ; version < 1000 ; version + + )
{
sprintf ( file_name_end , " -%x " , version ) ;
if ( ( res = my_create ( filename , 0 ,
O_CREAT | O_EXCL | O_BINARY | O_WRONLY , MYF ( 0 ) ) ) ! = - 1 )
return res ;
}
return - 1 ;
2003-07-04 22:06:17 +02:00
}
public :
Load_log_processor ( )
{
init_dynamic_array ( & file_names , sizeof ( Create_file_log_event * ) ,
100 , 100 CALLER_INFO ) ;
}
~ Load_log_processor ( )
{
destroy ( ) ;
delete_dynamic ( & file_names ) ;
}
2003-09-23 16:06:44 +02:00
void init_by_dir_name ( const char * dir )
2003-07-04 22:06:17 +02:00
{
2003-09-23 16:06:44 +02:00
target_dir_name_len = ( convert_dirname ( target_dir_name , dir , NullS ) -
target_dir_name ) ;
2003-07-04 22:06:17 +02:00
}
void init_by_cur_dir ( )
{
if ( my_getwd ( target_dir_name , sizeof ( target_dir_name ) , MYF ( MY_WME ) ) )
exit ( 1 ) ;
target_dir_name_len = strlen ( target_dir_name ) ;
}
void destroy ( )
{
Create_file_log_event * * ptr = ( Create_file_log_event * * ) file_names . buffer ;
Create_file_log_event * * end = ptr + file_names . elements ;
for ( ; ptr < end ; ptr + + )
{
if ( * ptr )
{
my_free ( ( char * ) ( * ptr ) - > fname , MYF ( MY_WME ) ) ;
delete * ptr ;
* ptr = 0 ;
}
}
}
Create_file_log_event * grab_event ( uint file_id )
{
2003-09-19 14:43:56 +02:00
if ( file_id > = file_names . elements )
return 0 ;
2003-07-04 22:06:17 +02:00
Create_file_log_event * * ptr =
( Create_file_log_event * * ) file_names . buffer + file_id ;
Create_file_log_event * res = * ptr ;
* ptr = 0 ;
return res ;
}
2004-02-06 17:57:11 +01:00
int process ( Create_file_log_event * ce ) ;
int process ( Append_block_log_event * ae )
2003-07-04 22:06:17 +02:00
{
2004-02-06 17:57:11 +01:00
File file ;
2003-09-19 18:34:58 +02:00
Create_file_log_event * ce = ( ae - > file_id < file_names . elements ) ?
* ( ( Create_file_log_event * * ) file_names . buffer + ae - > file_id ) : 0 ;
2003-09-19 14:43:56 +02:00
if ( ce )
2004-02-06 17:57:11 +01:00
{
if ( ( ( file = my_open ( ce - > fname ,
O_APPEND | O_BINARY | O_WRONLY , MYF ( MY_WME ) ) ) < 0 ) | |
my_write ( file , ( byte * ) ae - > block , ae - > block_len , MYF ( MY_WME | MY_NABP ) ) | |
my_close ( file , MYF ( MY_WME ) ) )
return - 1 ;
}
2003-09-19 14:43:56 +02:00
else
2003-09-23 16:06:44 +02:00
{
2003-08-19 15:12:44 +02:00
/*
There is no Create_file event ( a bad binlog or a big
- - position ) . Assuming it ' s a big - - position , we just do nothing and
print a warning .
*/
fprintf ( stderr , " Warning: ignoring Append_block as there is no \
Create_file event for file_id : % u \ n " ,ae->file_id);
2004-02-06 17:57:11 +01:00
return - 1 ;
2003-09-23 16:06:44 +02:00
}
2004-02-06 17:57:11 +01:00
return 0 ;
2003-07-04 22:06:17 +02:00
}
2004-02-06 17:57:11 +01:00
File prepare_new_file_for_old_format ( Load_log_event * le , char * filename ) ;
int load_old_format_file ( NET * net , const char * server_fname ,
uint server_fname_len , File file ) ;
2003-07-04 22:06:17 +02:00
} ;
2004-02-06 17:57:11 +01:00
File Load_log_processor : : prepare_new_file_for_old_format ( Load_log_event * le ,
char * filename )
{
uint len ;
char * tail ;
File file ;
fn_format ( filename , le - > fname , target_dir_name , " " , 1 ) ;
len = strlen ( filename ) ;
tail = filename + len ;
if ( ( file = create_unique_file ( filename , tail ) ) < 0 )
{
sql_print_error ( " Could not construct local filename %s " , filename ) ;
return - 1 ;
}
le - > set_fname_outside_temp_buf ( filename , len + strlen ( tail ) ) ;
return file ;
}
2003-09-23 16:06:44 +02:00
2004-02-06 17:57:11 +01:00
int Load_log_processor : : load_old_format_file ( NET * net , const char * server_fname ,
uint server_fname_len , File file )
{
char buf [ FN_REFLEN + 1 ] ;
buf [ 0 ] = 0 ;
memcpy ( buf + 1 , server_fname , server_fname_len + 1 ) ;
if ( my_net_write ( net , buf , server_fname_len + 2 ) | | net_flush ( net ) )
{
sql_print_error ( " Failed requesting the remote dump of %s " , server_fname ) ;
return - 1 ;
}
for ( ; ; )
{
uint packet_len = my_net_read ( net ) ;
if ( packet_len = = 0 )
{
if ( my_net_write ( net , " " , 0 ) | | net_flush ( net ) )
{
sql_print_error ( " Failed sending the ack packet " ) ;
return - 1 ;
}
/*
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 )
{
sql_print_error ( " Failed reading a packet during the dump of %s " ,
server_fname ) ;
return - 1 ;
}
if ( my_write ( file , ( byte * ) net - > read_pos , packet_len , MYF ( MY_WME | MY_NABP ) ) )
return - 1 ;
}
return 0 ;
}
int Load_log_processor : : process ( Create_file_log_event * ce )
2003-09-23 16:06:44 +02:00
{
const char * bname = ce - > fname + dirname_length ( ce - > fname ) ;
uint blen = ce - > fname_len - ( bname - ce - > fname ) ;
uint full_len = target_dir_name_len + blen + 9 + 9 + 1 ;
2004-02-06 17:57:11 +01:00
char * fname , * ptr ;
File file ;
2003-09-23 16:06:44 +02:00
2004-02-06 17:57:11 +01:00
if ( ! ( fname = my_malloc ( full_len , MYF ( MY_WME ) ) ) | |
2003-09-23 16:06:44 +02:00
set_dynamic ( & file_names , ( gptr ) & ce , ce - > file_id ) )
{
2004-02-06 17:57:11 +01:00
sql_print_error ( " Could not construct local filename %s%s " ,
target_dir_name , bname ) ;
return - 1 ;
2003-09-23 16:06:44 +02:00
}
2004-02-06 17:57:11 +01:00
memcpy ( fname , target_dir_name , target_dir_name_len ) ;
ptr = fname + target_dir_name_len ;
2003-09-23 16:06:44 +02:00
memcpy ( ptr , bname , blen ) ;
ptr + = blen ;
ptr + = my_sprintf ( ptr , ( ptr , " -%x " , ce - > file_id ) ) ;
2004-02-06 17:57:11 +01:00
if ( ( file = create_unique_file ( fname , ptr ) ) < 0 )
{
sql_print_error ( " Could not construct local filename %s%s " ,
target_dir_name , bname ) ;
return - 1 ;
}
ce - > set_fname_outside_temp_buf ( fname , strlen ( fname ) ) ;
2003-09-23 16:06:44 +02:00
2004-02-06 17:57:11 +01:00
if ( my_write ( file , ( byte * ) ce - > block , ce - > block_len , MYF ( MY_WME | MY_NABP ) ) | |
my_close ( file , MYF ( MY_WME ) ) )
return - 1 ;
}
Load_log_processor load_processor ;
void process_event ( ulonglong * rec_count , char * last_db , Log_event * ev ,
my_off_t pos , int old_format )
{
char ll_buff [ 21 ] ;
if ( ( * rec_count ) > = offset )
2003-09-23 16:06:44 +02:00
{
2004-02-06 17:57:11 +01:00
if ( ! short_form )
fprintf ( result_file , " # at %s \n " , llstr ( pos , ll_buff ) ) ;
switch ( ev - > get_type_code ( ) ) {
case QUERY_EVENT :
if ( one_database )
{
const char * log_dbname = ( ( Query_log_event * ) ev ) - > db ;
if ( ( log_dbname ! = NULL ) & & ( strcmp ( log_dbname , database ) ) )
{
( * rec_count ) + + ;
delete ev ;
return ; // next
}
}
ev - > print ( result_file , short_form , last_db ) ;
2003-09-23 16:06:44 +02:00
break ;
2004-02-06 17:57:11 +01:00
case CREATE_FILE_EVENT :
2003-09-23 16:06:44 +02:00
{
2004-02-06 17:57:11 +01:00
Create_file_log_event * ce = ( Create_file_log_event * ) ev ;
if ( one_database )
{
/*
We test if this event has to be ignored . If yes , we don ' t save
this event ; this will have the good side - effect of ignoring all
related Append_block and Exec_load .
Note that Load event from 3.23 is not tested .
*/
const char * log_dbname = ce - > db ;
if ( ( log_dbname ! = NULL ) & & ( strcmp ( log_dbname , database ) ) )
{
( * rec_count ) + + ;
delete ev ;
return ; // next
}
}
/*
We print the event , but with a leading ' # ' : this is just to inform
the user of the original command ; the command we want to execute
will be a derivation of this original command ( we will change the
filename and use LOCAL ) , prepared in the ' case EXEC_LOAD_EVENT '
below .
*/
ce - > print ( result_file , short_form , last_db , true ) ;
if ( ! old_format )
{
load_processor . process ( ce ) ;
ev = 0 ;
}
break ;
}
case APPEND_BLOCK_EVENT :
ev - > print ( result_file , short_form , last_db ) ;
load_processor . process ( ( Append_block_log_event * ) ev ) ;
break ;
case EXEC_LOAD_EVENT :
{
ev - > print ( result_file , short_form , last_db ) ;
Execute_load_log_event * exv = ( Execute_load_log_event * ) ev ;
Create_file_log_event * ce = load_processor . grab_event ( exv - > file_id ) ;
/*
if ce is 0 , it probably means that we have not seen the Create_file
event ( a bad binlog , or most probably - - position is after the
Create_file event ) . Print a warning comment .
*/
if ( ce )
{
ce - > print ( result_file , short_form , last_db , true ) ;
my_free ( ( char * ) ce - > fname , MYF ( MY_WME ) ) ;
delete ce ;
}
else
fprintf ( stderr , " Warning: ignoring Exec_load as there is no \
Create_file event for file_id : % u \ n " ,exv->file_id);
break ;
}
default :
ev - > print ( result_file , short_form , last_db ) ;
2003-09-23 16:06:44 +02:00
}
}
2004-02-06 17:57:11 +01:00
( * rec_count ) + + ;
if ( ev )
delete ev ;
2003-09-23 16:06:44 +02:00
}
2002-05-21 23:05:05 +02:00
static struct my_option my_long_options [ ] =
{
# ifndef DBUG_OFF
{ " debug " , ' # ' , " Output debug log. " , ( gptr * ) & default_dbug_option ,
( gptr * ) & default_dbug_option , 0 , GET_STR , OPT_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
# endif
2003-07-04 22:06:17 +02:00
{ " database " , ' d ' , " List entries for just this database (local log only). " ,
2002-05-21 23:05:05 +02:00
( gptr * ) & database , ( gptr * ) & database , 0 , GET_STR_ALLOC , REQUIRED_ARG ,
0 , 0 , 0 , 0 , 0 , 0 } ,
2003-07-04 22:06:17 +02:00
{ " force-read " , ' f ' , " Force reading unknown binlog events. " ,
2003-04-23 15:00:07 +02:00
( gptr * ) & force_opt , ( gptr * ) & force_opt , 0 , GET_BOOL , NO_ARG , 0 , 0 , 0 , 0 ,
0 , 0 } ,
2003-07-04 22:06:17 +02:00
{ " help " , ' ? ' , " Display this help and exit. " ,
2002-05-21 23:05:05 +02:00
0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
2003-07-04 22:06:17 +02:00
{ " host " , ' h ' , " Get the binlog from server. " , ( gptr * ) & host , ( gptr * ) & host ,
2002-05-21 23:05:05 +02:00
0 , GET_STR_ALLOC , REQUIRED_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
2003-07-04 22:06:17 +02:00
{ " offset " , ' o ' , " Skip the first N entries. " , ( gptr * ) & offset , ( gptr * ) & offset ,
2002-05-21 23:05:05 +02:00
0 , GET_ULL , REQUIRED_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
2003-07-04 22:06:17 +02:00
{ " password " , ' p ' , " Password to connect to remote server. " ,
2003-10-21 12:22:24 +02:00
0 , 0 , 0 , GET_STR , OPT_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
2003-07-04 22:06:17 +02:00
{ " port " , ' P ' , " Use port to connect to the remote server. " ,
2002-05-21 23:05:05 +02:00
( gptr * ) & port , ( gptr * ) & port , 0 , GET_INT , REQUIRED_ARG , MYSQL_PORT , 0 , 0 ,
0 , 0 , 0 } ,
2003-07-04 22:06:17 +02:00
{ " position " , ' j ' , " Start reading the binlog at position N. " ,
2002-05-21 23:05:05 +02:00
( gptr * ) & position , ( gptr * ) & position , 0 , GET_ULL , REQUIRED_ARG , 0 , 0 , 0 , 0 ,
0 , 0 } ,
2003-07-04 22:06:17 +02:00
{ " result-file " , ' r ' , " Direct output to a given file. " , 0 , 0 , 0 , GET_STR ,
2002-05-21 23:05:05 +02:00
REQUIRED_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
2003-10-08 09:23:54 +02:00
{ " read-from-remote-server " , ' R ' , " Read binary logs from a MySQL server " ,
2003-10-08 00:30:10 +02:00
( gptr * ) & remote_opt , ( gptr * ) & remote_opt , 0 , GET_BOOL , NO_ARG , 0 , 0 , 0 , 0 ,
0 , 0 } ,
2003-07-04 22:06:17 +02:00
{ " short-form " , ' s ' , " Just show the queries, no extra info. " ,
2002-05-21 23:05:05 +02:00
( gptr * ) & short_form , ( gptr * ) & short_form , 0 , GET_BOOL , NO_ARG , 0 , 0 , 0 , 0 ,
0 , 0 } ,
2003-07-04 22:06:17 +02:00
{ " socket " , ' S ' , " Socket file to use for connection. " ,
2004-02-06 17:57:11 +01:00
( gptr * ) & sock , ( gptr * ) & sock , 0 , GET_STR , REQUIRED_ARG , 0 , 0 , 0 , 0 ,
2003-07-04 22:06:17 +02:00
0 , 0 } ,
{ " user " , ' u ' , " Connect to the remote server as username. " ,
2002-05-21 23:05:05 +02:00
( gptr * ) & user , ( gptr * ) & user , 0 , GET_STR_ALLOC , REQUIRED_ARG , 0 , 0 , 0 , 0 ,
0 , 0 } ,
2003-07-04 22:06:17 +02:00
{ " local-load " , ' l ' , " Prepare files for local load in directory. " ,
( gptr * ) & dirname_for_local_load , ( gptr * ) & dirname_for_local_load , 0 ,
GET_STR_ALLOC , OPT_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
2002-05-21 23:05:05 +02:00
{ " version " , ' V ' , " Print version and exit. " , 0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 ,
0 , 0 , 0 , 0 , 0 } ,
{ 0 , 0 , 0 , 0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 }
} ;
2001-02-06 21:32:19 +01:00
void sql_print_error ( const char * format , . . . )
{
2000-07-31 21:29:14 +02:00
va_list args ;
va_start ( args , format ) ;
fprintf ( stderr , " ERROR: " ) ;
vfprintf ( stderr , format , args ) ;
fprintf ( stderr , " \n " ) ;
va_end ( args ) ;
2001-02-06 21:32:19 +01:00
}
2000-07-31 21:29:14 +02:00
2003-10-21 12:22:24 +02:00
static void cleanup ( )
{
my_free ( pass , MYF ( MY_ALLOW_ZERO_PTR ) ) ;
}
2000-07-31 21:29:14 +02:00
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 ) ;
2003-10-21 12:22:24 +02:00
cleanup ( ) ;
2000-07-31 21:29:14 +02:00
exit ( 1 ) ;
}
2001-02-06 21:32:19 +01:00
static void print_version ( )
{
2003-10-07 14:44:31 +02:00
printf ( " %s Ver 2.4 for %s at %s \n " , my_progname , SYSTEM_TYPE , MACHINE_TYPE ) ;
2001-02-06 21:32:19 +01:00
}
2000-07-31 21:29:14 +02:00
static void usage ( )
{
2001-02-06 21:32:19 +01:00
print_version ( ) ;
2002-04-23 17:17:00 +02:00
puts ( " By Monty and Sasha, for your professional use \n \
This software comes with NO WARRANTY : This is free software , \ n \
and you are welcome to modify and redistribute it under the GPL license \ n " );
2001-02-06 21:32:19 +01:00
printf ( " \
2002-09-14 18:12:02 +02:00
Dumps a MySQL binary log in a format usable for viewing or for piping to \ n \
2001-02-06 21:32:19 +01:00
the mysql command line client \ n \ n " );
2002-05-21 23:05:05 +02:00
printf ( " Usage: %s [options] log-files \n " , my_progname ) ;
my_print_help ( my_long_options ) ;
my_print_variables ( my_long_options ) ;
2000-07-31 21:29:14 +02:00
}
2002-11-07 11:49:02 +01:00
extern " C " my_bool
2002-05-21 23:05:05 +02:00
get_one_option ( int optid , const struct my_option * opt __attribute__ ( ( unused ) ) ,
char * argument )
{
2003-10-21 12:22:24 +02:00
bool tty_password = 0 ;
2003-07-04 22:06:17 +02:00
switch ( optid ) {
2000-07-31 21:29:14 +02:00
# ifndef DBUG_OFF
2002-05-21 23:05:05 +02:00
case ' # ' :
DBUG_PUSH ( argument ? argument : default_dbug_option ) ;
break ;
2000-07-31 21:29:14 +02:00
# endif
2002-05-21 23:05:05 +02:00
case ' d ' :
one_database = 1 ;
break ;
case ' p ' :
2003-10-21 12:22:24 +02:00
if ( argument )
{
my_free ( pass , MYF ( MY_ALLOW_ZERO_PTR ) ) ;
char * start = argument ;
pass = my_strdup ( argument , MYF ( MY_FAE ) ) ;
while ( * argument ) * argument + + = ' x ' ; /* Destroy argument */
if ( * start )
start [ 1 ] = 0 ; /* Cut length of argument */
}
else
tty_password = 1 ;
2002-05-21 23:05:05 +02:00
break ;
case ' r ' :
if ( ! ( result_file = my_fopen ( argument , O_WRONLY | O_BINARY , MYF ( MY_WME ) ) ) )
exit ( 1 ) ;
break ;
2003-10-08 00:30:10 +02:00
case ' R ' :
remote_opt = 1 ;
2002-05-21 23:05:05 +02:00
break ;
case ' V ' :
print_version ( ) ;
exit ( 0 ) ;
case ' ? ' :
usage ( ) ;
exit ( 0 ) ;
2000-07-31 21:29:14 +02:00
}
2003-10-21 12:22:24 +02:00
if ( tty_password )
pass = get_tty_password ( NullS ) ;
2002-05-21 23:05:05 +02:00
return 0 ;
}
2000-07-31 21:29:14 +02:00
2002-05-21 23:05:05 +02:00
static int parse_args ( int * argc , char * * * argv )
{
int ho_error ;
2000-07-31 21:29:14 +02:00
2002-05-21 23:05:05 +02:00
result_file = stdout ;
2003-10-07 14:44:31 +02:00
load_defaults ( " my " , load_default_groups , argc , argv ) ;
2002-05-21 23:05:05 +02:00
if ( ( ho_error = handle_options ( argc , argv , my_long_options , get_one_option ) ) )
2002-05-29 14:07:30 +02:00
exit ( ho_error ) ;
2000-07-31 21:29:14 +02:00
return 0 ;
}
static MYSQL * safe_connect ( )
{
2001-08-10 02:16:43 +02:00
MYSQL * local_mysql = mysql_init ( NULL ) ;
2000-07-31 21:29:14 +02:00
if ( ! local_mysql )
2001-08-10 02:16:43 +02:00
die ( " Failed on mysql_init " ) ;
2000-07-31 21:29:14 +02:00
2003-07-04 22:06:17 +02:00
if ( ! mysql_real_connect ( local_mysql , host , user , pass , 0 , port , sock , 0 ) )
2001-08-10 02:16:43 +02:00
die ( " failed on connect: %s " , mysql_error ( local_mysql ) ) ;
2000-07-31 21:29:14 +02:00
return local_mysql ;
}
static void dump_log_entries ( const char * logname )
{
2003-10-08 00:30:10 +02:00
if ( remote_opt )
2000-07-31 21:29:14 +02:00
dump_remote_log_entries ( logname ) ;
else
dump_local_log_entries ( logname ) ;
}
2001-11-11 06:24:12 +01:00
static int check_master_version ( MYSQL * mysql )
{
MYSQL_RES * res = 0 ;
MYSQL_ROW row ;
const char * version ;
int old_format = 0 ;
2001-12-06 13:10:51 +01:00
2002-06-11 10:20:31 +02:00
if ( mysql_query ( mysql , " SELECT VERSION() " ) | |
! ( res = mysql_store_result ( mysql ) ) )
2001-11-11 06:24:12 +01:00
{
mysql_close ( mysql ) ;
die ( " Error checking master version: %s " ,
mysql_error ( mysql ) ) ;
}
if ( ! ( row = mysql_fetch_row ( res ) ) )
{
mysql_free_result ( res ) ;
mysql_close ( mysql ) ;
die ( " Master returned no rows for SELECT VERSION() " ) ;
return 1 ;
}
if ( ! ( version = row [ 0 ] ) )
{
mysql_free_result ( res ) ;
mysql_close ( mysql ) ;
die ( " Master reported NULL for the version " ) ;
}
2001-12-06 13:10:51 +01:00
2002-06-11 10:20:31 +02:00
switch ( * version ) {
2001-11-11 06:24:12 +01:00
case ' 3 ' :
old_format = 1 ;
break ;
case ' 4 ' :
2002-06-11 10:20:31 +02:00
case ' 5 ' :
2001-11-11 06:24:12 +01:00
old_format = 0 ;
break ;
default :
sql_print_error ( " Master reported unrecognized MySQL version '%s' " ,
version ) ;
mysql_free_result ( res ) ;
mysql_close ( mysql ) ;
return 1 ;
}
mysql_free_result ( res ) ;
return old_format ;
}
2000-07-31 21:29:14 +02:00
2002-06-11 10:20:31 +02:00
2000-07-31 21:29:14 +02:00
static void dump_remote_log_entries ( const char * logname )
{
char buf [ 128 ] ;
2001-04-25 01:39:26 +02:00
char last_db [ FN_REFLEN + 1 ] = " " ;
2000-07-31 21:29:14 +02:00
uint len ;
NET * net = & mysql - > net ;
2001-11-11 06:24:12 +01:00
int old_format ;
old_format = check_master_version ( mysql ) ;
2001-12-06 13:10:51 +01:00
2002-06-11 10:20:31 +02:00
if ( ! position )
position = BIN_LOG_HEADER_SIZE ; // protect the innocent from spam
if ( position < BIN_LOG_HEADER_SIZE )
2001-05-29 12:46:17 +02:00
{
2002-06-11 10:20:31 +02:00
position = BIN_LOG_HEADER_SIZE ;
2001-05-29 12:46:17 +02:00
// warn the guity
2002-06-11 10:20:31 +02:00
sql_print_error ( " Warning: The position in the binary log can't be less than %d. \n Starting from position %d \n " , BIN_LOG_HEADER_SIZE , BIN_LOG_HEADER_SIZE ) ;
2001-05-29 12:46:17 +02:00
}
2000-07-31 21:29:14 +02:00
int4store ( buf , position ) ;
2002-06-11 10:20:31 +02:00
int2store ( buf + BIN_LOG_HEADER_SIZE , binlog_flags ) ;
2000-08-21 23:18:32 +02:00
len = ( uint ) strlen ( logname ) ;
2000-09-30 01:20:26 +02:00
int4store ( buf + 6 , 0 ) ;
memcpy ( buf + 10 , logname , len ) ;
2001-11-11 06:24:12 +01:00
if ( simple_command ( mysql , COM_BINLOG_DUMP , buf , len + 10 , 1 ) )
2000-07-31 21:29:14 +02:00
die ( " Error sending the log dump command " ) ;
2001-12-06 13:10:51 +01:00
2004-02-06 17:57:11 +01:00
my_off_t old_off = 0 ;
ulonglong rec_count = 0 ;
char fname [ FN_REFLEN + 1 ] ;
2002-06-11 10:20:31 +02:00
for ( ; ; )
2000-07-31 21:29:14 +02:00
{
2001-09-21 02:38:35 +02:00
const char * error ;
2001-08-10 02:16:43 +02:00
len = net_safe_read ( mysql ) ;
2000-07-31 21:29:14 +02:00
if ( len = = packet_error )
2001-08-10 02:16:43 +02:00
die ( " Error reading packet from server: %s " , mysql_error ( mysql ) ) ;
2003-07-02 22:56:27 +02:00
if ( len < 8 & & net - > read_pos [ 0 ] = = 254 )
2000-07-31 21:29:14 +02:00
break ; // end of data
DBUG_PRINT ( " info " , ( " len= %u, net->read_pos[5] = %d \n " ,
len , net - > read_pos [ 5 ] ) ) ;
2002-06-11 10:20:31 +02:00
Log_event * ev = Log_event : : read_log_event ( ( const char * ) net - > read_pos + 1 ,
len - 1 , & error , old_format ) ;
2004-02-06 17:57:11 +01:00
if ( ! ev )
2000-07-31 21:29:14 +02:00
{
2004-02-06 17:57:11 +01:00
die ( " Could not construct log event object " ) ;
}
else
{
Log_event_type type = ev - > get_type_code ( ) ;
if ( ! old_format | | ( type ! = LOAD_EVENT & & type ! = CREATE_FILE_EVENT ) )
{
process_event ( & rec_count , last_db , ev , old_off , old_format ) ;
}
else
{
Load_log_event * le = ( Load_log_event * ) ev ;
const char * old_fname = le - > fname ;
uint old_len = le - > fname_len ;
File file = load_processor . prepare_new_file_for_old_format ( le , fname ) ;
if ( file > = 0 )
{
process_event ( & rec_count , last_db , ev , old_off , old_format ) ;
load_processor . load_old_format_file ( net , old_fname , old_len , file ) ;
my_close ( file , MYF ( MY_WME ) ) ;
}
}
2000-07-31 21:29:14 +02:00
}
2004-02-06 17:57:11 +01:00
/*
Let ' s adjust offset for remote log as for local log to produce
similar text . .
*/
if ( old_off )
old_off + = len - 1 ;
2000-07-31 21:29:14 +02:00
else
2004-02-06 17:57:11 +01:00
old_off = BIN_LOG_HEADER_SIZE ;
2000-07-31 21:29:14 +02:00
}
}
2002-06-11 10:20:31 +02:00
2003-07-04 22:06:17 +02:00
static int check_header ( IO_CACHE * file )
2001-11-11 06:24:12 +01:00
{
2002-09-11 05:40:08 +02:00
byte header [ BIN_LOG_HEADER_SIZE ] ;
2001-12-09 23:08:24 +01:00
byte buf [ PROBE_HEADER_LEN ] ;
2002-06-11 10:20:31 +02:00
int old_format = 0 ;
2001-12-06 13:10:51 +01:00
2001-11-11 06:24:12 +01:00
my_off_t pos = my_b_tell ( file ) ;
my_b_seek ( file , ( my_off_t ) 0 ) ;
2002-09-11 05:40:08 +02:00
if ( my_b_read ( file , header , sizeof ( header ) ) )
die ( " Failed reading header; Probably an empty file " ) ;
if ( memcmp ( header , BINLOG_MAGIC , sizeof ( header ) ) )
2003-07-04 22:06:17 +02:00
die ( " File is not a binary log file " ) ;
2002-09-11 05:40:08 +02:00
if ( ! my_b_read ( file , buf , sizeof ( buf ) ) )
2001-11-11 06:24:12 +01:00
{
2002-09-11 05:40:08 +02:00
if ( buf [ 4 ] = = START_EVENT )
{
uint event_len ;
2003-06-20 15:48:52 +02:00
event_len = uint4korr ( buf + EVENT_LEN_OFFSET ) ;
old_format = ( event_len < ( LOG_EVENT_HEADER_LEN + START_HEADER_LEN ) ) ;
2002-09-11 05:40:08 +02:00
}
2001-11-11 06:24:12 +01:00
}
my_b_seek ( file , pos ) ;
return old_format ;
}
2002-09-11 05:40:08 +02:00
2000-07-31 21:29:14 +02:00
static void dump_local_log_entries ( const char * logname )
{
2000-11-17 01:48:13 +01:00
File fd = - 1 ;
2000-11-15 22:00:06 +01:00
IO_CACHE cache , * file = & cache ;
2001-01-03 15:54:46 +01:00
ulonglong rec_count = 0 ;
2002-09-19 16:49:41 +02:00
char last_db [ FN_REFLEN + 1 ] ;
byte tmp_buff [ BIN_LOG_HEADER_SIZE ] ;
2001-11-11 06:24:12 +01:00
bool old_format = 0 ;
2000-11-15 22:00:06 +01:00
2002-09-11 05:40:08 +02:00
last_db [ 0 ] = 0 ;
2000-11-15 22:00:06 +01:00
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 ) ;
2003-07-04 22:06:17 +02:00
old_format = check_header ( file ) ;
2000-11-15 22:00:06 +01:00
}
else
{
2001-04-26 00:12:51 +02:00
if ( init_io_cache ( file , fileno ( result_file ) , 0 , READ_CACHE , ( my_off_t ) 0 ,
2000-11-15 22:00:06 +01:00
0 , MYF ( MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE ) ) )
exit ( 1 ) ;
2003-07-04 22:06:17 +02:00
old_format = check_header ( file ) ;
2000-11-15 22:00:06 +01:00
if ( position )
{
/* skip 'position' characters from stdout */
2000-11-18 01:15:06 +01:00
byte buff [ IO_SIZE ] ;
2000-11-15 22:00:06 +01:00
my_off_t length , tmp ;
2001-01-03 15:54:46 +01:00
for ( length = ( my_off_t ) position ; length > 0 ; length - = tmp )
2000-11-15 22:00:06 +01:00
{
tmp = min ( length , sizeof ( buff ) ) ;
2002-06-11 10:20:31 +02:00
if ( my_b_read ( file , buff , ( uint ) tmp ) )
2000-11-15 22:00:06 +01:00
exit ( 1 ) ;
}
}
file - > pos_in_file = position ;
file - > seek_not_done = 0 ;
}
if ( ! position )
2002-09-11 05:40:08 +02:00
my_b_read ( file , tmp_buff , BIN_LOG_HEADER_SIZE ) ; // Skip header
2001-02-15 02:43:14 +01:00
for ( ; ; )
2000-11-15 22:00:06 +01:00
{
2001-01-03 15:54:46 +01:00
char llbuff [ 21 ] ;
2001-02-06 21:32:19 +01:00
my_off_t old_off = my_b_tell ( file ) ;
2001-11-11 06:24:12 +01:00
Log_event * ev = Log_event : : read_log_event ( file , old_format ) ;
2000-11-15 22:00:06 +01:00
if ( ! ev )
{
if ( file - > error )
2001-01-03 15:54:46 +01:00
die ( " \
Could not read entry at offset % s : Error in log format or read error " ,
2001-02-06 21:32:19 +01:00
llstr ( old_off , llbuff ) ) ;
2000-11-26 07:29:01 +01:00
// file->error == 0 means EOF, that's OK, we break in this case
2000-11-15 22:00:06 +01:00
break ;
}
2004-02-06 17:57:11 +01:00
process_event ( & rec_count , last_db , ev , old_off , false ) ;
2000-11-15 22:00:06 +01:00
}
2002-06-11 10:20:31 +02:00
if ( fd > = 0 )
2003-08-19 15:12:44 +02:00
my_close ( fd , MYF ( MY_WME ) ) ;
2000-11-15 22:00:06 +01:00
end_io_cache ( file ) ;
2000-07-31 21:29:14 +02:00
}
2003-07-04 22:06:17 +02:00
# if MYSQL_VERSION_ID < 40101
typedef struct st_my_tmpdir
{
char * * list ;
uint cur , max ;
} MY_TMPDIR ;
# if defined( __WIN__) || defined(OS2)
# define DELIM ';'
# else
# define DELIM ':'
# endif
my_bool init_tmpdir ( MY_TMPDIR * tmpdir , const char * pathlist )
{
char * end , * copy ;
char buff [ FN_REFLEN ] ;
DYNAMIC_ARRAY t_arr ;
if ( my_init_dynamic_array ( & t_arr , sizeof ( char * ) , 1 , 5 ) )
return TRUE ;
if ( ! pathlist | | ! pathlist [ 0 ] )
{
/* Get default temporary directory */
pathlist = getenv ( " TMPDIR " ) ; /* Use this if possible */
# if defined( __WIN__) || defined(OS2)
if ( ! pathlist )
pathlist = getenv ( " TEMP " ) ;
if ( ! pathlist )
pathlist = getenv ( " TMP " ) ;
# endif
if ( ! pathlist | | ! pathlist [ 0 ] )
pathlist = ( char * ) P_tmpdir ;
}
do
{
end = strcend ( pathlist , DELIM ) ;
convert_dirname ( buff , pathlist , end ) ;
if ( ! ( copy = my_strdup ( buff , MYF ( MY_WME ) ) ) )
return TRUE ;
if ( insert_dynamic ( & t_arr , ( gptr ) & copy ) )
return TRUE ;
pathlist = end + 1 ;
}
while ( * end ) ;
freeze_size ( & t_arr ) ;
tmpdir - > list = ( char * * ) t_arr . buffer ;
tmpdir - > max = t_arr . elements - 1 ;
tmpdir - > cur = 0 ;
return FALSE ;
}
char * my_tmpdir ( MY_TMPDIR * tmpdir )
{
char * dir ;
dir = tmpdir - > list [ tmpdir - > cur ] ;
tmpdir - > cur = ( tmpdir - > cur = = tmpdir - > max ) ? 0 : tmpdir - > cur + 1 ;
return dir ;
}
void free_tmpdir ( MY_TMPDIR * tmpdir )
{
uint i ;
for ( i = 0 ; i < = tmpdir - > max ; i + + )
my_free ( tmpdir - > list [ i ] , MYF ( 0 ) ) ;
my_free ( ( gptr ) tmpdir - > list , MYF ( 0 ) ) ;
}
# endif
2000-11-15 22:00:06 +01:00
2000-07-31 21:29:14 +02:00
int main ( int argc , char * * argv )
{
2003-10-07 14:44:31 +02:00
static char * * defaults_argv ;
2000-07-31 21:29:14 +02:00
MY_INIT ( argv [ 0 ] ) ;
2003-10-07 14:44:31 +02:00
2000-07-31 21:29:14 +02:00
parse_args ( & argc , ( char * * * ) & argv ) ;
2003-10-07 14:44:31 +02:00
defaults_argv = argv ;
2000-07-31 21:29:14 +02:00
2003-07-01 19:17:03 +02:00
if ( ! argc )
2000-11-15 22:00:06 +01:00
{
usage ( ) ;
2003-10-07 14:44:31 +02:00
free_defaults ( defaults_argv ) ;
2000-11-15 22:00:06 +01:00
return - 1 ;
}
2000-07-31 21:29:14 +02:00
2003-10-08 00:30:10 +02:00
if ( remote_opt )
2000-11-15 22:00:06 +01:00
mysql = safe_connect ( ) ;
2000-07-31 21:29:14 +02:00
2003-07-04 22:06:17 +02:00
MY_TMPDIR tmpdir ;
tmpdir . list = 0 ;
if ( ! dirname_for_local_load )
{
if ( init_tmpdir ( & tmpdir , 0 ) )
exit ( 1 ) ;
dirname_for_local_load = my_tmpdir ( & tmpdir ) ;
}
if ( dirname_for_local_load )
load_processor . init_by_dir_name ( dirname_for_local_load ) ;
else
load_processor . init_by_cur_dir ( ) ;
2003-07-01 19:17:03 +02:00
while ( - - argc > = 0 )
dump_log_entries ( * ( argv + + ) ) ;
2003-07-04 22:06:17 +02:00
if ( tmpdir . list )
free_tmpdir ( & tmpdir ) ;
2001-04-26 00:12:51 +02:00
if ( result_file ! = stdout )
my_fclose ( result_file , MYF ( 0 ) ) ;
2003-10-08 00:30:10 +02:00
if ( remote_opt )
2001-08-10 02:16:43 +02:00
mysql_close ( mysql ) ;
2003-10-21 12:22:24 +02:00
cleanup ( ) ;
2003-10-07 14:44:31 +02:00
free_defaults ( defaults_argv ) ;
my_end ( 0 ) ;
2000-07-31 21:29:14 +02:00
return 0 ;
}
/*
We must include this here as it ' s compiled with different options for
the server
*/
2001-08-11 13:10:27 +02:00
# ifdef __WIN__
# include "log_event.cpp"
# else
2000-07-31 21:29:14 +02:00
# include "log_event.cc"
2001-08-11 13:10:27 +02:00
# endif
2002-06-30 17:57:21 +02:00
FIX_GCC_LINKING_PROBLEM