diff options
Diffstat (limited to 'client')
-rw-r--r-- | client/Makefile.am | 2 | ||||
-rw-r--r-- | client/mysqlbinlog.cc | 330 | ||||
-rw-r--r-- | client/mysqldump.c | 226 | ||||
-rw-r--r-- | client/mysqltest.c | 10 |
4 files changed, 462 insertions, 106 deletions
diff --git a/client/Makefile.am b/client/Makefile.am index 58398548b75..cb69ca5cb3b 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -43,7 +43,7 @@ mysqlbinlog_SOURCES = mysqlbinlog.cc ../mysys/mf_tempdir.c mysqlbinlog_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) $(DEPLIB) mysqlmanagerc_SOURCES = mysqlmanagerc.c mysqlmanagerc_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) $(DEPLIB) -sql_src=log_event.h log_event.cc +sql_src=log_event.h mysql_priv.h log_event.cc # Fix for mit-threads DEFS = -DUNDEF_THREADS_HACK diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 8015871428e..ff90c77a2e8 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -14,11 +14,28 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + + TODO: print the catalog (some USE catalog.db ????). + + Standalone program to read a MySQL binary log (or relay log); + can read files produced by 3.23, 4.x, 5.0 servers. + + Can read binlogs from 3.23/4.x/5.0 and relay logs from 4.x/5.0. + Should be able to read any file of these categories, even with + --start-position. + An important fact: the Format_desc event of the log is at most the 3rd event + of the log; if it is the 3rd then there is this combination: + Format_desc_of_slave, Rotate_of_master, Format_desc_of_master. +*/ + #define MYSQL_CLIENT #undef MYSQL_SERVER #include "client_priv.h" #include <my_time.h> #include "log_event.h" +/* That one is necessary for defines of OPTION_NO_FOREIGN_KEY_CHECKS etc */ +#include "mysql_priv.h" #define BIN_LOG_HEADER_SIZE 4 #define PROBE_HEADER_LEN (EVENT_LEN_OFFSET+4) @@ -66,6 +83,14 @@ static MYSQL* mysql = NULL; static const char* dirname_for_local_load= 0; static bool stop_passed= 0; +/* + check_header() will set the pointer below. + Why do we need here a pointer on an event instead of an event ? + This is because the event will be created (alloced) in read_log_event() + (which returns a pointer) in check_header(). +*/ +Format_description_log_event* description_event; + static int dump_local_log_entries(const char* logname); static int dump_remote_log_entries(const char* logname); static int dump_log_entries(const char* logname); @@ -305,8 +330,8 @@ int Load_log_processor::process(Append_block_log_event *ae) /* 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. + --start-position). Assuming it's a big --start-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); @@ -316,30 +341,49 @@ Create_file event for file_id: %u\n",ae->file_id); Load_log_processor load_processor; + /* + Process an event + + SYNOPSIS + process_event() + RETURN 0 ok and continue 1 error and terminate -1 ok and terminate - + TODO This function returns 0 even in some error cases. This should be changed. */ -int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format) + + + +int process_event(LAST_EVENT_INFO *last_event_info, Log_event *ev, + my_off_t pos) { char ll_buff[21]; + Log_event_type ev_type= ev->get_type_code(); DBUG_ENTER("process_event"); + /* + Format events are not concerned by --offset and such, we always need to + read them to be able to process the wanted events. + */ if ((rec_count >= offset) && - ((my_time_t)(ev->when) >= start_datetime)) + ((my_time_t)(ev->when) >= start_datetime) || + (ev_type == FORMAT_DESCRIPTION_EVENT)) { - /* - We have found an event after start_datetime, from now on print - everything (in case the binlog has timestamps increasing and decreasing, - we do this to avoid cutting the middle). - */ - start_datetime= 0; - offset= 0; // print everything and protect against cycling rec_count + if (ev_type != FORMAT_DESCRIPTION_EVENT) + { + /* + We have found an event after start_datetime, from now on print + everything (in case the binlog has timestamps increasing and + decreasing, we do this to avoid cutting the middle). + */ + start_datetime= 0; + offset= 0; // print everything and protect against cycling rec_count + } if (((my_time_t)(ev->when) >= stop_datetime) || (pos >= stop_position_mot)) { @@ -349,7 +393,7 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format) if (!short_form) fprintf(result_file, "# at %s\n",llstr(pos,ll_buff)); - switch (ev->get_type_code()) { + switch (ev_type) { case QUERY_EVENT: if (one_database) { @@ -357,7 +401,7 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format) if ((log_dbname != NULL) && (strcmp(log_dbname, database))) goto end; } - ev->print(result_file, short_form, last_db); + ev->print(result_file, short_form, last_event_info); break; case CREATE_FILE_EVENT: { @@ -381,8 +425,9 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format) filename and use LOCAL), prepared in the 'case EXEC_LOAD_EVENT' below. */ - ce->print(result_file, short_form, last_db, TRUE); - if (!old_format) + ce->print(result_file, short_form, last_event_info, TRUE); + // If this binlog is not 3.23 ; why this test?? + if (description_event->binlog_version >= 3) { if (load_processor.process(ce)) break; // Error @@ -391,23 +436,23 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format) break; } case APPEND_BLOCK_EVENT: - ev->print(result_file, short_form, last_db); + ev->print(result_file, short_form, last_event_info); if (load_processor.process((Append_block_log_event*) ev)) break; // Error break; case EXEC_LOAD_EVENT: { - ev->print(result_file, short_form, last_db); + ev->print(result_file, short_form, last_event_info); 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 + event (a bad binlog, or most probably --start-position is after the Create_file event). Print a warning comment. */ if (ce) { - ce->print(result_file, short_form, last_db, TRUE); + ce->print(result_file, short_form, last_event_info, TRUE); my_free((char*)ce->fname,MYF(MY_WME)); delete ce; } @@ -416,8 +461,20 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format) Create_file event for file_id: %u\n",exv->file_id); break; } + case FORMAT_DESCRIPTION_EVENT: + delete description_event; + description_event= (Format_description_log_event*) ev; + ev->print(result_file, short_form, last_event_info); + /* + We don't want this event to be deleted now, so let's hide it (I + (Guilhem) should later see if this triggers a non-serious Valgrind + error). Not serious error, because we will free description_event + later. + */ + ev= 0; + break; default: - ev->print(result_file, short_form, last_db); + ev->print(result_file, short_form, last_event_info); } } @@ -558,7 +615,7 @@ static void die(const char* fmt, ...) static void print_version() { - printf("%s Ver 3.0 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE); + printf("%s Ver 3.1 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE); NETWARE_SET_SCREEN_MODE(1); } @@ -701,12 +758,17 @@ static int dump_log_entries(const char* logname) } -static int check_master_version(MYSQL* mysql) +/* + This is not as smart as check_header() (used for local log); it will not work + for a binlog which mixes format. TODO: fix this. +*/ +static int check_master_version(MYSQL* mysql, + Format_description_log_event + **description_event) { MYSQL_RES* res = 0; MYSQL_ROW row; const char* version; - int old_format = 0; if (mysql_query(mysql, "SELECT VERSION()") || !(res = mysql_store_result(mysql))) @@ -731,11 +793,18 @@ static int check_master_version(MYSQL* mysql) switch (*version) { case '3': - old_format = 1; + *description_event= new Format_description_log_event(1); break; case '4': + *description_event= new Format_description_log_event(3); case '5': - old_format = 0; + /* + The server is soon going to send us its Format_description log + event, unless it is a 5.0 server with 3.23 or 4.0 binlogs. + So we first assume that this is 4.0 (which is enough to read the + Format_desc event if one comes). + */ + *description_event= new Format_description_log_event(3); break; default: sql_print_error("Master reported unrecognized MySQL version '%s'", @@ -745,18 +814,17 @@ static int check_master_version(MYSQL* mysql) return 1; } mysql_free_result(res); - return old_format; + return 0; } static int dump_remote_log_entries(const char* logname) + { char buf[128]; - char last_db[FN_REFLEN+1] = ""; - ulong len; - uint logname_len; + LAST_EVENT_INFO last_event_info; + uint len, logname_len; NET* net; - int old_format; int error= 0; my_off_t old_off= start_position_mot; char fname[FN_REFLEN+1]; @@ -769,7 +837,18 @@ static int dump_remote_log_entries(const char* logname) */ mysql= safe_connect(); net= &mysql->net; - old_format = check_master_version(mysql); + + if (check_master_version(mysql, &description_event)) + { + fprintf(stderr, "Could not find server version"); + DBUG_RETURN(1); + } + if (!description_event || !description_event->is_valid()) + { + fprintf(stderr, "Invalid Format_description log event; \ +could be out of memory"); + DBUG_RETURN(1); + } /* COM_BINLOG_DUMP accepts only 4 bytes for the position, so we are forced to @@ -798,6 +877,8 @@ static int dump_remote_log_entries(const char* logname) for (;;) { const char *error_msg; + Log_event *ev; + len = net_safe_read(mysql); if (len == packet_error) { @@ -810,9 +891,9 @@ static int dump_remote_log_entries(const char* logname) 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, &error_msg, old_format); - if (!ev) + if (!(ev= Log_event::read_log_event((const char*) net->read_pos + 1 , + len - 1, &error_msg, + description_event))) { fprintf(stderr, "Could not construct log event object\n"); error= 1; @@ -820,25 +901,27 @@ static int dump_remote_log_entries(const char* logname) } Log_event_type type= ev->get_type_code(); - if (!old_format || ( type != LOAD_EVENT && type != CREATE_FILE_EVENT)) + if (description_event->binlog_version >= 3 || + (type != LOAD_EVENT && type != CREATE_FILE_EVENT)) { - if (ev->get_type_code() == ROTATE_EVENT) + /* + If this is a Rotate event, maybe it's the end of the requested binlog; + in this case we are done (stop transfer). + This is suitable for binlogs, not relay logs (but for now we don't read + relay logs remotely because the server is not able to do that). If one + day we read relay logs remotely, then we will have a problem with the + detection below: relay logs contain Rotate events which are about the + binlogs, so which would trigger the end-detection below. + */ + if (type == ROTATE_EVENT) { Rotate_log_event *rev= (Rotate_log_event *)ev; /* - mysqld is sending us all its binlogs after the requested one, but we - don't want them. If this is a fake Rotate event, and not about our log, we can stop transfer. If this a real Rotate event (so it's not about our log, it's in our log describing the next log), we print it (because it's part of our log) and then we will stop when we receive the fake one soon. - This is suitable for binlogs, not relay logs (but for now we don't - read relay logs remotely because the server is not able to do - that). If one day we read relay logs remotely, then we will have a - problem with the detection below: relay logs contain Rotate events - which are about the binlogs, so which would trigger the end-detection - below. */ if (rev->when == 0) { @@ -861,7 +944,7 @@ static int dump_remote_log_entries(const char* logname) len= 1; // fake Rotate, so don't increment old_off } } - if ((error= process_event(last_db,ev,old_off,old_format))) + if ((error= process_event(&last_event_info,ev,old_off))) { error= ((error < 0) ? 0 : 1); goto err; @@ -873,64 +956,135 @@ static int dump_remote_log_entries(const char* logname) const char *old_fname= le->fname; uint old_len= le->fname_len; File file; - + if ((file= load_processor.prepare_new_file_for_old_format(le,fname)) < 0) { error= 1; goto err; } - - if ((error= process_event(last_db,ev,old_off,old_format))) + + if ((error= process_event(&last_event_info,ev,old_off))) { - my_close(file,MYF(MY_WME)); + my_close(file,MYF(MY_WME)); error= ((error < 0) ? 0 : 1); goto err; } - if (load_processor.load_old_format_file(net,old_fname,old_len,file)) + error= load_processor.load_old_format_file(net,old_fname,old_len,file); + my_close(file,MYF(MY_WME)); + if (error) { - my_close(file,MYF(MY_WME)); error= 1; goto err; } - my_close(file,MYF(MY_WME)); } - /* Let's adjust offset for remote log as for local log to produce similar text. */ old_off+= len-1; } + err: mysql_close(mysql); DBUG_RETURN(error); } -static int check_header(IO_CACHE* file) +static void check_header(IO_CACHE* file, + Format_description_log_event **description_event) { byte header[BIN_LOG_HEADER_SIZE]; byte buf[PROBE_HEADER_LEN]; - int old_format=0; - DBUG_ENTER("check_header"); + my_off_t tmp_pos, pos; - my_off_t pos = my_b_tell(file); + *description_event= new Format_description_log_event(3); + pos= my_b_tell(file); my_b_seek(file, (my_off_t)0); if (my_b_read(file, header, sizeof(header))) die("Failed reading header; Probably an empty file"); if (memcmp(header, BINLOG_MAGIC, sizeof(header))) die("File is not a binary log file"); - if (!my_b_read(file, buf, sizeof(buf))) + + /* + Imagine we are running with --start-position=1000. We still need + to know the binlog format's. So we still need to find, if there is + one, the Format_desc event, or to know if this is a 3.23 + binlog. So we need to first read the first events of the log, + those around offset 4. Even if we are reading a 3.23 binlog from + the start (no --start-position): we need to know the header length + (which is 13 in 3.23, 19 in 4.x) to be able to successfully print + the first event (Start_log_event_v3). So even in this case, we + need to "probe" the first bytes of the log *before* we do a real + read_log_event(). Because read_log_event() needs to know the + header's length to work fine. + */ + for(;;) { - if (buf[4] == START_EVENT) + tmp_pos= my_b_tell(file); /* should be 4 the first time */ + if (my_b_read(file, buf, sizeof(buf))) { - uint event_len; - event_len = uint4korr(buf + EVENT_LEN_OFFSET); - old_format = (event_len < (LOG_EVENT_HEADER_LEN + START_HEADER_LEN)); + if (file->error) + die("\ +Could not read entry at offset %lu : Error in log format or read error", + tmp_pos); + /* + Otherwise this is just EOF : this log currently contains 0-2 + events. Maybe it's going to be filled in the next + milliseconds; then we are going to have a problem if this a + 3.23 log (imagine we are locally reading a 3.23 binlog which + is being written presently): we won't know it in + read_log_event() and will fail(). Similar problems could + happen with hot relay logs if --start-position is used (but a + --start-position which is posterior to the current size of the log). + These are rare problems anyway (reading a hot log + when we + read the first events there are not all there yet + when we + read a bit later there are more events + using a strange + --start-position). + */ + break; + } + else + { + DBUG_PRINT("info",("buf[4]=%d", buf[4])); + /* always test for a Start_v3, even if no --start-position */ + if (buf[4] == START_EVENT_V3) /* This is 3.23 or 4.x */ + { + if (uint4korr(buf + EVENT_LEN_OFFSET) < + (LOG_EVENT_MINIMAL_HEADER_LEN + START_V3_HEADER_LEN)) + { + /* This is 3.23 (format 1) */ + delete *description_event; + *description_event= new Format_description_log_event(1); + } + break; + } + else if (tmp_pos >= start_position) + break; + else if (buf[4] == FORMAT_DESCRIPTION_EVENT) /* This is 5.0 */ + { + my_b_seek(file, tmp_pos); /* seek back to event's start */ + if (!(*description_event= (Format_description_log_event*) + Log_event::read_log_event(file, *description_event))) + /* EOF can't be hit here normally, so it's a real error */ + die("Could not read a Format_description_log_event event \ +at offset %lu ; this could be a log format error or read error", + tmp_pos); + DBUG_PRINT("info",("Setting description_event")); + } + else if (buf[4] == ROTATE_EVENT) + { + my_b_seek(file, tmp_pos); /* seek back to event's start */ + if (!Log_event::read_log_event(file, *description_event)) + /* EOF can't be hit here normally, so it's a real error */ + die("Could not read a Rotate_log_event event \ +at offset %lu ; this could be a log format error or read error", + tmp_pos); + } + else + break; } } my_b_seek(file, pos); - DBUG_RETURN(old_format); } @@ -938,13 +1092,10 @@ static int dump_local_log_entries(const char* logname) { File fd = -1; IO_CACHE cache,*file= &cache; - char last_db[FN_REFLEN+1]; + LAST_EVENT_INFO last_event_info; byte tmp_buff[BIN_LOG_HEADER_SIZE]; - bool old_format = 0; int error= 0; - last_db[0]= 0; - if (logname && logname[0] != '-') { if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0) @@ -953,16 +1104,16 @@ static int dump_local_log_entries(const char* logname) MYF(MY_WME | MY_NABP))) { my_close(fd, MYF(MY_WME)); - exit(1); + return 1; } - old_format = check_header(file); + check_header(file, &description_event); } - else + else // reading from stdin; TODO: check that it works { 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))) return 1; - old_format = check_header(file); + check_header(file, &description_event); if (start_position) { /* skip 'start_position' characters from stdout */ @@ -972,46 +1123,44 @@ static int dump_local_log_entries(const char* logname) { tmp=min(length,sizeof(buff)); if (my_b_read(file, buff, (uint) tmp)) - { - error= 1; - goto end; - } + { + error= 1; + goto end; + } } } file->pos_in_file= start_position_mot; file->seek_not_done=0; } - if (!start_position) + if (!description_event || !description_event->is_valid()) + die("Invalid Format_description log event; could be out of memory"); + + if (!start_position && my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE)) { - // Skip header - if (my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE)) - { - error= 1; - goto end; - } + error= 1; + goto end; } - for (;;) { char llbuff[21]; my_off_t old_off = my_b_tell(file); - Log_event* ev = Log_event::read_log_event(file, old_format); + Log_event* ev = Log_event::read_log_event(file, description_event); if (!ev) { if (file->error) { fprintf(stderr, - "Could not read entry at offset %s:" - "Error in log format or read error\n", - llstr(old_off,llbuff)); - error= 1; + "Could not read entry at offset %s:" + "Error in log format or read error\n", + llstr(old_off,llbuff)); + error= 1; } // file->error == 0 means EOF, that's OK, we break in this case break; } - if ((error= process_event(last_db,ev,old_off,false))) + if ((error= process_event(&last_event_info,ev,old_off))) { if (error < 0) error= 0; @@ -1023,6 +1172,7 @@ end: if (fd >= 0) my_close(fd, MYF(MY_WME)); end_io_cache(file); + delete description_event; return error; } diff --git a/client/mysqldump.c b/client/mysqldump.c index cfe93a11024..99670dec89c 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -108,6 +108,8 @@ static uint opt_protocol= 0; static char *default_charset= (char*) MYSQL_UNIVERSAL_CLIENT_CHARSET; static CHARSET_INFO *charset_info= &my_charset_latin1; const char *default_dbug_option="d:t:o,/tmp/mysqldump.trace"; +/* do we met VIEWs during tables scaning */ +my_bool was_views= 0; const char *compatible_mode_names[]= { @@ -351,7 +353,7 @@ static struct my_option my_long_options[] = (gptr*) &where, (gptr*) &where, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"xml", 'X', "Dump a database as well formed XML.", 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} + {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; static const char *load_default_groups[]= { "mysqldump","client",0 }; @@ -368,6 +370,8 @@ static int dump_databases(char **); static int dump_all_databases(); static char *quote_name(const char *name, char *buff, my_bool force); static const char *check_if_ignore_table(const char *table_name); +static my_bool getViewStructure(char *table, char* db); +static my_bool dump_all_views_in_db(char *database); #include <help_start.h> @@ -1040,6 +1044,7 @@ static uint getTableStructure(char *table, char* db) { /* Make an sql-file, if path was given iow. option -T was given */ char buff[20+FN_REFLEN]; + MYSQL_FIELD *field; sprintf(buff,"show create table %s", result_table); if (mysql_query_with_error_report(sock, 0, buff)) @@ -1073,8 +1078,16 @@ static uint getTableStructure(char *table, char* db) check_io(sql_file); } - tableRes=mysql_store_result(sock); - row=mysql_fetch_row(tableRes); + tableRes= mysql_store_result(sock); + field= mysql_fetch_field_direct(tableRes, 0); + if (strcmp(field->name, "View") == 0) + { + if (verbose) + fprintf(stderr, "-- It's a view, skipped\n"); + was_views= 1; + DBUG_RETURN(0); + } + row= mysql_fetch_row(tableRes); fprintf(sql_file, "%s;\n", row[1]); check_io(sql_file); mysql_free_result(tableRes); @@ -1218,6 +1231,14 @@ static uint getTableStructure(char *table, char* db) sprintf(buff,"show keys from %s", result_table); if (mysql_query_with_error_report(sock, &tableRes, buff)) { + if (mysql_errno(sock) == ER_WRONG_OBJECT) + { + /* it is VIEW */ + fputs("\t\t<options Comment=\"view\" />\n", sql_file); + goto continue_xml; + } + fprintf(stderr, "%s: Can't get keys for table %s (%s)\n", + my_progname, result_table, mysql_error(sock)); if (path) my_fclose(sql_file, MYF(MY_WME)); safe_exit(EX_MYSQLERR); @@ -1321,6 +1342,7 @@ static uint getTableStructure(char *table, char* db) } mysql_free_result(tableRes); /* Is always safe to free */ } +continue_xml: if (!opt_xml) fputs(";\n", sql_file); else @@ -1846,6 +1868,21 @@ static int dump_all_databases() if (dump_all_tables_in_db(row[0])) result=1; } + if (was_views) + { + if (mysql_query(sock, "SHOW DATABASES") || + !(tableres = mysql_store_result(sock))) + { + my_printf_error(0, "Error: Couldn't execute 'SHOW DATABASES': %s", + MYF(0), mysql_error(sock)); + return 1; + } + while ((row = mysql_fetch_row(tableres))) + { + if (dump_all_views_in_db(row[0])) + result=1; + } + } return result; } /* dump_all_databases */ @@ -1854,11 +1891,20 @@ static int dump_all_databases() static int dump_databases(char **db_names) { int result=0; - for ( ; *db_names ; db_names++) + char **db; + for (db= db_names ; *db ; db++) { - if (dump_all_tables_in_db(*db_names)) + if (dump_all_tables_in_db(*db)) result=1; } + if (!result && was_views) + { + for (db= db_names ; *db ; db++) + { + if (dump_all_views_in_db(*db)) + result=1; + } + } return result; } /* dump_databases */ @@ -1966,11 +2012,64 @@ static int dump_all_tables_in_db(char *database) return 0; } /* dump_all_tables_in_db */ +/* + dump structure of views of database + + SYNOPSIS + dump_all_views_in_db() + database database name + RETURN + 0 OK + 1 ERROR +*/ + +static my_bool dump_all_views_in_db(char *database) +{ + char *table; + uint numrows; + char table_buff[NAME_LEN*2+3]; + + if (init_dumping(database)) + return 1; + if (opt_xml) + print_xml_tag1(md_result_file, "", "database name=", database, "\n"); + if (lock_tables) + { + DYNAMIC_STRING query; + init_dynamic_string(&query, "LOCK TABLES ", 256, 1024); + for (numrows= 0 ; (table= getTableName(1)); numrows++) + { + dynstr_append(&query, quote_name(table, table_buff, 1)); + dynstr_append(&query, " READ /*!32311 LOCAL */,"); + } + if (numrows && mysql_real_query(sock, query.str, query.length-1)) + DBerror(sock, "when using LOCK TABLES"); + /* We shall continue here, if --force was given */ + dynstr_free(&query); + } + if (flush_logs) + { + if (mysql_refresh(sock, REFRESH_LOG)) + DBerror(sock, "when doing refresh"); + /* We shall continue here, if --force was given */ + } + while ((table= getTableName(0))) + getViewStructure(table, database); + if (opt_xml) + { + fputs("</database>\n", md_result_file); + check_io(md_result_file); + } + if (lock_tables) + mysql_query(sock,"UNLOCK TABLES"); + return 0; +} /* dump_all_tables_in_db */ static int dump_selected_tables(char *db, char **table_names, int tables) { uint numrows; + int i; char table_buff[NAME_LEN*+3]; if (init_dumping(db)) @@ -1978,7 +2077,6 @@ static int dump_selected_tables(char *db, char **table_names, int tables) if (lock_tables) { DYNAMIC_STRING query; - int i; init_dynamic_string(&query, "LOCK TABLES ", 256, 1024); for (i=0 ; i < tables ; i++) @@ -1999,11 +2097,16 @@ static int dump_selected_tables(char *db, char **table_names, int tables) } if (opt_xml) print_xml_tag1(md_result_file, "", "database name=", db, "\n"); - for (; tables > 0 ; tables-- , table_names++) + for (i=0 ; i < tables ; i++) { - numrows = getTableStructure(*table_names, db); + numrows = getTableStructure(table_names[i], db); if (!dFlag && numrows > 0) - dumpTable(numrows, *table_names); + dumpTable(numrows, table_names[i]); + } + if (was_views) + { + for (i=0 ; i < tables ; i++) + getViewStructure(table_names[i], db); } if (opt_xml) { @@ -2208,14 +2311,113 @@ static const char *check_if_ignore_table(const char *table_name) mysql_free_result(res); return 0; /* assume table is ok */ } - if (strcmp(row[1], (result= "MRG_MyISAM")) && - strcmp(row[1], (result= "MRG_ISAM"))) - result= 0; + if (!(row[1])) + result= "VIEW"; + else + { + if (strcmp(row[1], (result= "MRG_MyISAM")) && + strcmp(row[1], (result= "MRG_ISAM"))) + result= 0; + } mysql_free_result(res); return result; } +/* + Getting VIEW structure + + SYNOPSIS + getViewStructure() + table view name + db db name + + RETURN + 0 OK + 1 ERROR +*/ + +static my_bool getViewStructure(char *table, char* db) +{ + MYSQL_RES *tableRes; + MYSQL_ROW row; + MYSQL_FIELD *field; + char *result_table, *opt_quoted_table; + char table_buff[NAME_LEN*2+3]; + char table_buff2[NAME_LEN*2+3]; + char buff[20+FN_REFLEN]; + FILE *sql_file = md_result_file; + DBUG_ENTER("getViewStructure"); + + if (tFlag) + DBUG_RETURN(0); + + if (verbose) + fprintf(stderr, "-- Retrieving view structure for table %s...\n", table); + + sprintf(insert_pat,"SET OPTION SQL_QUOTE_SHOW_CREATE=%d", + (opt_quoted || opt_keywords)); + result_table= quote_name(table, table_buff, 1); + opt_quoted_table= quote_name(table, table_buff2, 0); + + sprintf(buff,"show create table %s", result_table); + if (mysql_query(sock, buff)) + { + fprintf(stderr, "%s: Can't get CREATE TABLE for view %s (%s)\n", + my_progname, result_table, mysql_error(sock)); + safe_exit(EX_MYSQLERR); + DBUG_RETURN(0); + } + + if (path) + { + char filename[FN_REFLEN], tmp_path[FN_REFLEN]; + convert_dirname(tmp_path,path,NullS); + sql_file= my_fopen(fn_format(filename, table, tmp_path, ".sql", 4), + O_WRONLY, MYF(MY_WME)); + if (!sql_file) /* If file couldn't be opened */ + { + safe_exit(EX_MYSQLERR); + DBUG_RETURN(1); + } + write_header(sql_file, db); + } + tableRes= mysql_store_result(sock); + field= mysql_fetch_field_direct(tableRes, 0); + if (strcmp(field->name, "View") != 0) + { + if (verbose) + fprintf(stderr, "-- It's base table, skipped\n"); + DBUG_RETURN(0); + } + + if (!opt_xml && opt_comments) + { + fprintf(sql_file, "\n--\n-- View structure for view %s\n--\n\n", + result_table); + check_io(sql_file); + } + if (opt_drop) + { + fprintf(sql_file, "DROP VIEW IF EXISTS %s;\n", opt_quoted_table); + check_io(sql_file); + } + + row= mysql_fetch_row(tableRes); + fprintf(sql_file, "%s;\n", row[1]); + check_io(sql_file); + mysql_free_result(tableRes); + + if (sql_file != md_result_file) + { + fputs("\n", sql_file); + write_footer(sql_file); + my_fclose(sql_file, MYF(MY_WME)); + } + DBUG_RETURN(0); +} + + int main(int argc, char **argv) { compatible_mode_normal_str[0]= 0; diff --git a/client/mysqltest.c b/client/mysqltest.c index dfaf48dd60e..4f55320e4f3 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -280,6 +280,7 @@ Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS, Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, Q_START_TIMER, Q_END_TIMER, Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL, +Q_EXIT, Q_UNKNOWN, /* Unknown command. */ Q_COMMENT, /* Comments, ignored. */ @@ -365,6 +366,7 @@ const char *command_names[]= "character_set", "disable_ps_protocol", "enable_ps_protocol", + "exit", 0 }; @@ -3361,12 +3363,11 @@ int main(int argc, char **argv) { int error = 0; struct st_query *q; - my_bool require_file=0, q_send_flag=0; + my_bool require_file=0, q_send_flag=0, abort_flag= 0; char save_file[FN_REFLEN]; MY_INIT(argv[0]); { DBUG_ENTER("main"); - DBUG_PROCESS(argv[0]); /* Use all time until exit if no explicit 'start_timer' */ timer_start= timer_now(); @@ -3437,7 +3438,7 @@ int main(int argc, char **argv) */ var_set_errno(-1); - while (!read_query(&q)) + while (!abort_flag && !read_query(&q)) { int current_line_inc = 1, processed = 0; if (q->type == Q_UNKNOWN || q->type == Q_COMMENT_WITH_COMMAND) @@ -3618,6 +3619,9 @@ int main(int argc, char **argv) ps_protocol_enabled= ps_protocol; break; + case Q_EXIT: + abort_flag= 1; + break; default: processed = 0; break; } } |