summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/mysql.cc189
-rw-r--r--client/mysqladmin.cc1
-rw-r--r--client/mysqlbinlog.cc382
-rw-r--r--client/mysqlcheck.c1
-rw-r--r--client/mysqlimport.c1
-rw-r--r--client/mysqlshow.c1
-rw-r--r--client/mysqltest.cc53
7 files changed, 577 insertions, 51 deletions
diff --git a/client/mysql.cc b/client/mysql.cc
index 37f506a99cd..6612b273d17 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -88,9 +88,7 @@ extern "C" {
#endif /* defined(HAVE_CURSES_H) && defined(HAVE_TERM_H) */
#undef bcmp // Fix problem with new readline
-#if defined(_WIN32)
-#include <conio.h>
-#else
+#if !defined(_WIN32)
# ifdef __APPLE__
# include <editline/readline.h>
# else
@@ -104,6 +102,98 @@ extern "C" {
#define USE_POPEN
}
+static CHARSET_INFO *charset_info= &my_charset_latin1;
+
+#if defined(_WIN32)
+/*
+ Set console mode for the whole duration of the client session.
+
+ We need for input
+ - line input (i.e read lines from console)
+ - echo typed characters
+ - "cooked" mode, i.e we do not want to handle all keystrokes,
+ like DEL etc ourselves, yet. We might want handle keystrokes
+ in the future, to implement tab completion, and better
+ (multiline) history.
+
+ Disable VT escapes for the output.We do not know what kind of escapes SELECT would return.
+*/
+struct Console_mode
+{
+ HANDLE in= GetStdHandle(STD_INPUT_HANDLE);
+ HANDLE out= GetStdHandle(STD_OUTPUT_HANDLE);
+ DWORD mode_in=0;
+ DWORD mode_out=0;
+
+ enum {STDIN_CHANGED = 1, STDOUT_CHANGED = 2};
+ int changes=0;
+
+ Console_mode()
+ {
+ if (in && in != INVALID_HANDLE_VALUE && GetConsoleMode(in, &mode_in))
+ {
+ SetConsoleMode(in, ENABLE_ECHO_INPUT|ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT);
+ changes |= STDIN_CHANGED;
+ }
+
+ if (out && out != INVALID_HANDLE_VALUE && GetConsoleMode(out, &mode_out))
+ {
+#ifdef ENABLE_VIRTUAL_TERMINAL_INPUT
+ SetConsoleMode(out, mode_out & ~ENABLE_VIRTUAL_TERMINAL_INPUT);
+ changes |= STDOUT_CHANGED;
+#endif
+ }
+ }
+
+ ~Console_mode()
+ {
+ if (changes & STDIN_CHANGED)
+ SetConsoleMode(in, mode_in);
+
+ if(changes & STDOUT_CHANGED)
+ SetConsoleMode(out, mode_out);
+ }
+};
+
+static Console_mode my_conmode;
+
+#define MAX_CGETS_LINE_LEN 65535
+/** Read line from console, chomp EOL*/
+static char *win_readline()
+{
+ static wchar_t wstrbuf[MAX_CGETS_LINE_LEN];
+ static char strbuf[MAX_CGETS_LINE_LEN * 4];
+
+ DWORD nchars= 0;
+ uint len= 0;
+ SetLastError(0);
+ if (!ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), wstrbuf, MAX_CGETS_LINE_LEN-1,
+ &nchars, NULL))
+ goto err;
+ if (nchars == 0 && GetLastError() == ERROR_OPERATION_ABORTED)
+ goto err;
+
+ for (;nchars > 0; nchars--)
+ {
+ if (wstrbuf[nchars - 1] != '\n' && wstrbuf[nchars - 1] != '\r')
+ break;
+ }
+
+ if (nchars > 0)
+ {
+ uint errors;
+ len= my_convert(strbuf, sizeof(strbuf), charset_info,
+ (const char *) wstrbuf, nchars * sizeof(wchar_t),
+ &my_charset_utf16le_bin, &errors);
+ }
+ strbuf[len]= 0;
+ return strbuf;
+err:
+ return NULL;
+}
+#endif
+
+
#ifdef HAVE_VIDATTR
static int have_curses= 0;
static void my_vidattr(chtype attrs)
@@ -208,7 +298,6 @@ unsigned short terminal_width= 80;
static uint opt_protocol=0;
static const char *opt_protocol_type= "";
-static CHARSET_INFO *charset_info= &my_charset_latin1;
static uint protocol_to_force= MYSQL_PROTOCOL_DEFAULT;
@@ -1353,6 +1442,46 @@ sig_handler mysql_end(int sig)
exit(status.exit_status);
}
+#ifdef _WIN32
+#define CNV_BUFSIZE 1024
+
+/**
+ Convert user,database,and password to requested charset.
+
+ This is done in the single case when user connects with non-UTF8
+ default-character-set, on UTF8 capable Windows.
+
+ User, password, and database are UTF8 encoded, prior to the function,
+ this needs to be fixed, in case they contain non-ASCIIs.
+
+ Mostly a workaround, to allow existng users with non-ASCII password
+ to survive upgrade without losing connectivity.
+*/
+static void maybe_convert_charset(const char **user, const char **password,
+ const char **database, const char *csname)
+{
+ if (GetACP() != CP_UTF8 || !strncmp(csname, "utf8", 4))
+ return;
+ static char bufs[3][CNV_BUFSIZE];
+ const char **from[]= {user, password, database};
+ CHARSET_INFO *cs= get_charset_by_csname(csname, MY_CS_PRIMARY,
+ MYF(MY_UTF8_IS_UTF8MB3 | MY_WME));
+ if (!cs)
+ return;
+ for (int i= 0; i < 3; i++)
+ {
+ const char *str= *from[i];
+ if (!str)
+ continue;
+ uint errors;
+ uint len= my_convert(bufs[i], CNV_BUFSIZE, cs, str, (uint32) strlen(str),
+ &my_charset_utf8mb4_bin, &errors);
+ bufs[i][len]= 0;
+ *from[i]= bufs[i];
+ }
+}
+#endif
+
/*
set connection-specific options and call mysql_real_connect
*/
@@ -1384,6 +1513,10 @@ static bool do_connect(MYSQL *mysql, const char *host, const char *user,
mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
"program_name", "mysql");
+#ifdef _WIN32
+ maybe_convert_charset(&user, &password, &database,default_charset);
+#endif
+
return mysql_real_connect(mysql, host, user, password, database,
opt_mysql_port, opt_mysql_unix_port, flags);
}
@@ -2033,11 +2166,6 @@ static int get_options(int argc, char **argv)
static int read_and_execute(bool interactive)
{
-#if defined(_WIN32)
- String tmpbuf;
- String buffer;
-#endif
-
char *line= NULL;
char in_string=0;
ulong line_number=0;
@@ -2115,26 +2243,7 @@ static int read_and_execute(bool interactive)
#if defined(_WIN32)
tee_fputs(prompt, stdout);
- if (!tmpbuf.is_alloced())
- tmpbuf.alloc(65535);
- tmpbuf.length(0);
- buffer.length(0);
- size_t clen;
- do
- {
- line= my_cgets((char*)tmpbuf.ptr(), tmpbuf.alloced_length()-1, &clen);
- buffer.append(line, clen);
- /*
- if we got buffer fully filled than there is a chance that
- something else is still in console input buffer
- */
- } while (tmpbuf.alloced_length() <= clen);
- /*
- An empty line is returned from my_cgets when there's error reading :
- Ctrl-c for example
- */
- if (line)
- line= buffer.c_ptr();
+ line= win_readline();
#else
if (opt_outfile)
fputs(prompt, OUTFILE);
@@ -2201,10 +2310,7 @@ static int read_and_execute(bool interactive)
}
}
-#if defined(_WIN32)
- buffer.free();
- tmpbuf.free();
-#else
+#if !defined(_WIN32)
if (interactive)
/*
free the last entered line.
@@ -3242,6 +3348,21 @@ com_clear(String *buffer,char *line __attribute__((unused)))
return 0;
}
+static void adjust_console_codepage(const char *name __attribute__((unused)))
+{
+#ifdef _WIN32
+ if (my_set_console_cp(name) < 0)
+ {
+ char buf[128];
+ snprintf(buf, sizeof(buf),
+ "WARNING: Could not determine Windows codepage for charset '%s',"
+ "continue using codepage %u", name, GetConsoleOutputCP());
+ put_info(buf, INFO_INFO);
+ }
+#endif
+}
+
+
/* ARGSUSED */
static int
com_charset(String *buffer __attribute__((unused)), char *line)
@@ -3263,6 +3384,7 @@ com_charset(String *buffer __attribute__((unused)), char *line)
mysql_set_character_set(&mysql, charset_info->cs_name.str);
default_charset= (char *)charset_info->cs_name.str;
put_info("Charset changed", INFO_INFO);
+ adjust_console_codepage(charset_info->cs_name.str);
}
else put_info("Charset is not found", INFO_INFO);
return 0;
@@ -4806,6 +4928,7 @@ sql_real_connect(char *host,char *database,char *user,char *password,
put_info(buff, INFO_ERROR);
return 1;
}
+ adjust_console_codepage(charset_info->cs_name.str);
connected=1;
#ifndef EMBEDDED_LIBRARY
mysql_options(&mysql, MYSQL_OPT_RECONNECT, &debug_info_flag);
diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc
index 6fa5d6c73d0..a7159d2bb6a 100644
--- a/client/mysqladmin.cc
+++ b/client/mysqladmin.cc
@@ -438,6 +438,7 @@ int main(int argc,char *argv[])
mysql_options(&mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
if (!strcmp(default_charset,MYSQL_AUTODETECT_CHARSET_NAME))
default_charset= (char *)my_default_csname();
+ my_set_console_cp(default_charset);
mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset);
error_flags= (myf)(opt_nobeep ? 0 : ME_BELL);
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc
index a642d4df0b5..21980ca03d6 100644
--- a/client/mysqlbinlog.cc
+++ b/client/mysqlbinlog.cc
@@ -128,6 +128,7 @@ static my_bool print_row_count_used= 0, print_row_event_positions_used= 0;
static my_bool debug_info_flag, debug_check_flag;
static my_bool force_if_open_opt= 1;
static my_bool opt_raw_mode= 0, opt_stop_never= 0;
+my_bool opt_gtid_strict_mode= true;
static ulong opt_stop_never_slave_server_id= 0;
static my_bool opt_verify_binlog_checksum= 1;
static ulonglong offset = 0;
@@ -143,10 +144,15 @@ static char *charset= 0;
static uint verbose= 0;
-static ulonglong start_position, stop_position;
+static char *start_pos_str, *stop_pos_str;
+static ulonglong start_position= BIN_LOG_HEADER_SIZE,
+ stop_position= (longlong)(~(my_off_t)0) ;
#define start_position_mot ((my_off_t)start_position)
#define stop_position_mot ((my_off_t)stop_position)
+static Binlog_gtid_state_validator *gtid_state_validator= NULL;
+static Domain_gtid_event_filter *domain_gtid_filter= NULL;
+
static char *start_datetime_str, *stop_datetime_str;
static my_time_t start_datetime= 0, stop_datetime= MY_TIME_T_MAX;
static ulonglong rec_count= 0;
@@ -981,6 +987,10 @@ static bool print_row_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
return result;
}
+static inline my_bool is_gtid_filtering_enabled()
+{
+ return domain_gtid_filter != NULL;
+}
/**
Print the given event, and either delete it or delegate the deletion
@@ -1008,10 +1018,23 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
char ll_buff[21];
Log_event_type ev_type= ev->get_type_code();
my_bool destroy_evt= TRUE;
+ my_bool gtid_err= FALSE;
DBUG_ENTER("process_event");
Exit_status retval= OK_CONTINUE;
IO_CACHE *const head= &print_event_info->head_cache;
+ /*
+ We use Gtid_list_log_event information to determine if there is missing
+ data between where a user expects events to start/stop (i.e. the GTIDs
+ provided by --start-position and --stop-position), and the true start of
+ the specified binary logs. The first GLLE provides the initial state of the
+ binary logs.
+
+ If --start-position is provided as a file offset, we want to skip initial
+ GTID state verification
+ */
+ static my_bool was_first_glle_processed= start_position > BIN_LOG_HEADER_SIZE;
+
/* Bypass flashback settings to event */
ev->is_flashback= opt_flashback;
#ifdef WHEN_FLASHBACK_REVIEW_READY
@@ -1019,6 +1042,120 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
#endif
/*
+ Run time estimation of the output window configuration.
+
+ Do not validate GLLE information is start position is provided as a file
+ offset.
+ */
+ if (ev_type == GTID_LIST_EVENT && ev->when)
+ {
+ Gtid_list_log_event *glev= (Gtid_list_log_event *)ev;
+
+ /*
+ If this is the first Gtid_list_log_event, initialize the state of the
+ GTID stream auditor to be consistent with the binary logs provided
+ */
+ if (gtid_state_validator && !was_first_glle_processed && glev->count)
+ {
+ if (gtid_state_validator->initialize_gtid_state(stderr, glev->list,
+ glev->count))
+ goto err;
+
+ if (domain_gtid_filter && !domain_gtid_filter->get_num_start_gtids())
+ {
+ /*
+ We need to validate the GTID list from --stop-position because we
+ couldn't prove it intrinsically (i.e. using stop > start)
+ */
+ rpl_gtid *stop_gtids= domain_gtid_filter->get_stop_gtids();
+ size_t n_stop_gtids= domain_gtid_filter->get_num_stop_gtids();
+ if (gtid_state_validator->verify_stop_state(stderr, stop_gtids,
+ n_stop_gtids))
+ {
+ my_free(stop_gtids);
+ goto err;
+ }
+ my_free(stop_gtids);
+ }
+ }
+
+ /*
+ Verify that we are able to process events from this binlog. For example,
+ if our current GTID state is behind the state of the GLLE in the new log,
+ a user may have accidentally left out a log file to process.
+ */
+ if (gtid_state_validator && verbose >= 3)
+ for (size_t k= 0; k < glev->count; k++)
+ gtid_state_validator->verify_gtid_state(stderr, &(glev->list[k]));
+
+ was_first_glle_processed= TRUE;
+ }
+
+ if (ev_type == GTID_EVENT)
+ {
+ rpl_gtid ev_gtid;
+ Gtid_log_event *gle= (Gtid_log_event*) ev;
+ ev_gtid= {gle->domain_id, gle->server_id, gle->seq_no};
+
+ /*
+ If the binlog output should be filtered using GTIDs, test the new event
+ group to see if its events should be ignored.
+ */
+ if (domain_gtid_filter)
+ {
+ if (domain_gtid_filter->has_finished())
+ {
+ retval= OK_STOP;
+ goto end;
+ }
+
+ if (!domain_gtid_filter->exclude(&ev_gtid))
+ print_event_info->activate_current_event_group();
+ else
+ print_event_info->deactivate_current_event_group();
+ }
+
+ /*
+ Where we always ensure the initial binlog state is valid, we only
+ continually monitor the GTID stream for validity if we are in GTID
+ strict mode (for errors) or if three levels of verbosity is provided
+ (for warnings).
+
+ If we don't care about ensuring GTID validity, just delete the auditor
+ object to disable it for future checks.
+ */
+ if (gtid_state_validator)
+ {
+ if (!(opt_gtid_strict_mode || verbose >= 3))
+ {
+ delete gtid_state_validator;
+
+ /*
+ Explicitly reset to NULL to simplify checks on if auditing is enabled
+ i.e. if it is defined, assume we want to use it
+ */
+ gtid_state_validator= NULL;
+ }
+ else
+ {
+ gtid_err= gtid_state_validator->record(&ev_gtid);
+ if (gtid_err && opt_gtid_strict_mode)
+ {
+ gtid_state_validator->report(stderr, opt_gtid_strict_mode);
+ goto err;
+ }
+ }
+ }
+ }
+
+ /*
+ If the GTID is ignored, it shouldn't count towards offset (rec_count should
+ not be incremented)
+ */
+ if (!print_event_info->is_event_group_active())
+ goto end_skip_count;
+
+ /*
Format events are not concerned by --offset and such, we always need to
read them to be able to process the wanted events.
*/
@@ -1458,6 +1595,7 @@ err:
retval= ERROR_STOP;
end:
rec_count++;
+end_skip_count:
DBUG_PRINT("info", ("end event processing"));
/*
@@ -1658,15 +1796,22 @@ static struct my_option my_options[] =
&start_datetime_str, &start_datetime_str,
0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"start-position", 'j',
- "Start reading the binlog at position N. Applies to the first binlog "
- "passed on the command line.",
- &start_position, &start_position, 0, GET_ULL,
- REQUIRED_ARG, BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE,
- /*
- COM_BINLOG_DUMP accepts only 4 bytes for the position
- so remote log reading has lower limit.
- */
- (ulonglong)(0xffffffffffffffffULL), 0, 0, 0},
+ "Start reading the binlog at this position. Type can either be a positive "
+ "integer or a GTID list. When using a positive integer, the value only "
+ "applies to the first binlog passed on the command line. In GTID mode, "
+ "multiple GTIDs can be passed as a comma separated list, where each must "
+ "have a unique domain id. The list represents the gtid binlog state that "
+ "the client (another \"replica\" server) is aware of. Therefore, each GTID "
+ "is exclusive; only events after a given sequence number will be printed to "
+ "allow users to receive events after their current state.",
+ &start_pos_str, &start_pos_str, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0,
+ 0, 0, 0},
+ {"gtid-strict-mode", 0, "Process binlog according to gtid-strict-mode "
+ "specification. The start, stop positions are verified to satisfy "
+ "start < stop comparison condition. Sequence numbers of any gtid domain "
+ "must comprise monotically growing sequence",
+ &opt_gtid_strict_mode, &opt_gtid_strict_mode, 0,
+ GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
{"stop-datetime", OPT_STOP_DATETIME,
"Stop reading the binlog at first event having a datetime equal or "
"posterior to the argument; the argument must be a date and time "
@@ -1684,11 +1829,14 @@ static struct my_option my_options[] =
&opt_stop_never_slave_server_id, &opt_stop_never_slave_server_id, 0,
GET_ULONG, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"stop-position", OPT_STOP_POSITION,
- "Stop reading the binlog at position N. Applies to the last binlog "
- "passed on the command line.",
- &stop_position, &stop_position, 0, GET_ULL,
- REQUIRED_ARG, (longlong)(~(my_off_t)0), BIN_LOG_HEADER_SIZE,
- (ulonglong)(~(my_off_t)0), 0, 0, 0},
+ "Stop reading the binlog at this position. Type can either be a positive "
+ "integer or a GTID list. When using a positive integer, the value only "
+ "applies to the last binlog passed on the command line. In GTID mode, "
+ "multiple GTIDs can be passed as a comma separated list, where each must "
+ "have a unique domain id. Each GTID is inclusive; only events up to the "
+ "given sequence numbers are printed.",
+ &stop_pos_str, &stop_pos_str, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0,
+ 0, 0},
{"table", 'T', "List entries for just this table (local log only).",
&table, &table, 0, GET_STR_ALLOC, REQUIRED_ARG,
0, 0, 0, 0, 0, 0},
@@ -1702,7 +1850,9 @@ that may lead to an endless loop.",
&user, &user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0,
0, 0},
{"verbose", 'v', "Reconstruct SQL statements out of row events. "
- "-v -v adds comments on column data types.",
+ "-v -v adds comments on column data types. "
+ "-v -v -v adds diagnostic warnings about event "
+ "integrity before program exit.",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"version", 'V', "Print version and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
0, 0, 0, 0, 0},
@@ -1824,8 +1974,14 @@ static void cleanup()
my_free(const_cast<char*>(dirname_for_local_load));
my_free(start_datetime_str);
my_free(stop_datetime_str);
+ my_free(start_pos_str);
+ my_free(stop_pos_str);
free_root(&glob_root, MYF(0));
+ delete domain_gtid_filter;
+ if (gtid_state_validator)
+ delete gtid_state_validator;
+
delete binlog_filter;
delete glob_description_event;
if (mysql)
@@ -2075,6 +2231,110 @@ get_one_option(const struct my_option *opt, const char *argument, const char *fi
print_version();
opt_version= 1;
break;
+ case OPT_STOP_POSITION:
+ {
+ /* Stop position was already specified, so reset it and use the new list */
+ if (domain_gtid_filter && domain_gtid_filter->get_num_stop_gtids() > 0)
+ domain_gtid_filter->clear_stop_gtids();
+
+ uint32 n_stop_gtid_ranges= 0;
+ rpl_gtid *stop_gtids= gtid_parse_string_to_list(
+ stop_pos_str, strlen(stop_pos_str), &n_stop_gtid_ranges);
+ if (stop_gtids == NULL)
+ {
+ int err= 0;
+ char *end_ptr= NULL;
+ /*
+ No GTIDs specified in OPT_STOP_POSITION specification. Treat the value
+ as a singular index.
+ */
+ stop_position= my_strtoll10(stop_pos_str, &end_ptr, &err);
+
+ if (err || *end_ptr)
+ {
+ // Can't parse the position from the user
+ sql_print_error("Stop position argument value is invalid. Should be "
+ "either a positive integer or GTID.");
+ return 1;
+ }
+ }
+ else if (n_stop_gtid_ranges > 0)
+ {
+ uint32 gtid_idx;
+
+ if (domain_gtid_filter == NULL)
+ domain_gtid_filter= new Domain_gtid_event_filter();
+
+ for (gtid_idx = 0; gtid_idx < n_stop_gtid_ranges; gtid_idx++)
+ {
+ rpl_gtid *stop_gtid= &stop_gtids[gtid_idx];
+ if (domain_gtid_filter->add_stop_gtid(stop_gtid))
+ {
+ my_free(stop_gtids);
+ return 1;
+ }
+ }
+ my_free(stop_gtids);
+ }
+ else
+ {
+ DBUG_ASSERT(0);
+ }
+ break;
+ }
+ case 'j':
+ {
+ /* Start position was already specified, so reset it and use the new list */
+ if (domain_gtid_filter && domain_gtid_filter->get_num_start_gtids() > 0)
+ domain_gtid_filter->clear_start_gtids();
+
+ uint32 n_start_gtid_ranges= 0;
+ rpl_gtid *start_gtids= gtid_parse_string_to_list(
+ start_pos_str, strlen(start_pos_str), &n_start_gtid_ranges);
+
+ if (start_gtids == NULL)
+ {
+ int err= 0;
+ char *end_ptr= NULL;
+ /*
+ No GTIDs specified in OPT_START_POSITION specification. Treat the value
+ as a singular index.
+ */
+ start_position= my_strtoll10(start_pos_str, &end_ptr, &err);
+
+ if (err || *end_ptr)
+ {
+ // Can't parse the position from the user
+ sql_print_error("Start position argument value is invalid. Should be "
+ "either a positive integer or GTID.");
+ return 1;
+ }
+ }
+ else if (n_start_gtid_ranges > 0)
+ {
+ uint32 gtid_idx;
+
+ if (domain_gtid_filter == NULL)
+ domain_gtid_filter= new Domain_gtid_event_filter();
+
+ for (gtid_idx = 0; gtid_idx < n_start_gtid_ranges; gtid_idx++)
+ {
+ rpl_gtid *start_gtid= &start_gtids[gtid_idx];
+ if (start_gtid->seq_no &&
+ domain_gtid_filter->add_start_gtid(start_gtid))
+ {
+ my_free(start_gtids);
+ return 1;
+ }
+ }
+ my_free(start_gtids);
+ }
+ else
+ {
+ DBUG_ASSERT(0);
+ }
+ break;
+ }
case '?':
usage();
opt_version= 1;
@@ -2107,6 +2367,37 @@ static int parse_args(int *argc, char*** argv)
start_position);
start_position= UINT_MAX32;
}
+
+ /*
+ Always initialize the stream auditor initially because it is used to check
+ the initial state of the binary log is correct. If we don't want it later
+ (i.e. --skip-gtid-strict-mode or -vvv is not given), it is deleted when we
+ are certain the initial gtid state is set.
+ */
+ gtid_state_validator= new Binlog_gtid_state_validator();
+
+ if (domain_gtid_filter)
+ {
+ if (opt_gtid_strict_mode && domain_gtid_filter->validate_window_filters())
+ {
+ /*
+ In strict mode, if any --start/stop-position GTID ranges are invalid,
+ quit in error. Note that any specific error messages will have
+ already been written.
+ */
+ die();
+ }
+
+ /*
+ GTIDs before a start position shouldn't be validated, so we initialize
+ the stream auditor to only monitor GTIDs after these positions.
+ */
+ size_t n_start_gtids= domain_gtid_filter->get_num_start_gtids();
+ rpl_gtid *start_gtids= domain_gtid_filter->get_start_gtids();
+ gtid_state_validator->initialize_start_gtids(start_gtids, n_start_gtids);
+ my_free(start_gtids);
+ }
+
return 0;
}
@@ -2187,6 +2478,9 @@ static Exit_status dump_log_entries(const char* logname)
if (!print_event_info.init_ok())
return ERROR_STOP;
+
+ if (domain_gtid_filter)
+ print_event_info.enable_event_group_filtering();
/*
Set safe delimiter, to dump things
like CREATE PROCEDURE safely
@@ -2288,6 +2582,40 @@ static Exit_status check_master_version()
goto err;
}
+ if (domain_gtid_filter && domain_gtid_filter->get_num_start_gtids() > 0)
+ {
+ char str_buf[256];
+ String query_str(str_buf, sizeof(str_buf), system_charset_info);
+ query_str.length(0);
+ query_str.append(STRING_WITH_LEN("SET @slave_connect_state='"),
+ system_charset_info);
+
+ size_t n_start_gtids= domain_gtid_filter->get_num_start_gtids();
+ rpl_gtid *start_gtids= domain_gtid_filter->get_start_gtids();
+
+ for (size_t gtid_idx = 0; gtid_idx < n_start_gtids; gtid_idx++)
+ {
+ char buf[256];
+ rpl_gtid *start_gtid= &start_gtids[gtid_idx];
+
+ sprintf(buf, "%u-%u-%llu",
+ start_gtid->domain_id, start_gtid->server_id,
+ start_gtid->seq_no);
+ query_str.append(buf, strlen(buf));
+ if (gtid_idx < n_start_gtids - 1)
+ query_str.append(',');
+ }
+ my_free(start_gtids);
+
+ query_str.append(STRING_WITH_LEN("'"), system_charset_info);
+ if (unlikely(mysql_real_query(mysql, query_str.ptr(), query_str.length())))
+ {
+ error("Setting @slave_connect_state failed with error: %s",
+ mysql_error(mysql));
+ goto err;
+ }
+ }
+
delete glob_description_event;
glob_description_event= NULL;
@@ -3209,14 +3537,33 @@ int main(int argc, char** argv)
"/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n"
"/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n"
"/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n");
-
fprintf(result_file, "/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;\n");
+
+ if (is_gtid_filtering_enabled())
+ {
+ fprintf(result_file,
+ "/*!100001 SET @@SESSION.SERVER_ID=@@GLOBAL.SERVER_ID */;\n"
+ "/*!100001 SET @@SESSION.GTID_DOMAIN_ID=@@GLOBAL.GTID_DOMAIN_ID "
+ "*/;\n");
+ }
}
if (tmpdir.list)
free_tmpdir(&tmpdir);
if (result_file && result_file != stdout)
my_fclose(result_file, MYF(0));
+
+ /*
+ Ensure the GTID state is correct. If not, end in error.
+
+ Note that in gtid strict mode, we will not report here if any invalid GTIDs
+ are processed because it immediately errors (i.e. retval will be
+ ERROR_STOP)
+ */
+ if (retval != ERROR_STOP && gtid_state_validator &&
+ gtid_state_validator->report(stderr, opt_gtid_strict_mode))
+ retval= ERROR_STOP;
+
cleanup();
/* We cannot free DBUG, it is used in global destructors after exit(). */
my_end(my_end_arg | MY_DONT_FREE_DBUG);
@@ -3272,3 +3619,4 @@ struct encryption_service_st encryption_handler=
#include "sql_list.cc"
#include "rpl_filter.cc"
#include "compat56.cc"
+#include "rpl_gtid.cc"
diff --git a/client/mysqlcheck.c b/client/mysqlcheck.c
index 480308aa015..eb063765a37 100644
--- a/client/mysqlcheck.c
+++ b/client/mysqlcheck.c
@@ -503,6 +503,7 @@ static int get_options(int *argc, char ***argv)
printf("Unsupported character set: %s\n", default_charset);
DBUG_RETURN(1);
}
+ my_set_console_cp(default_charset);
if (*argc > 0 && opt_alldbs)
{
printf("You should give only options, no arguments at all, with option\n");
diff --git a/client/mysqlimport.c b/client/mysqlimport.c
index 8723641c74b..48f11667cd1 100644
--- a/client/mysqlimport.c
+++ b/client/mysqlimport.c
@@ -525,6 +525,7 @@ static MYSQL *db_connect(char *host, char *database,
mysql_options(mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);
if (!strcmp(default_charset,MYSQL_AUTODETECT_CHARSET_NAME))
default_charset= (char *)my_default_csname();
+ my_set_console_cp(default_charset);
mysql_options(mysql, MYSQL_SET_CHARSET_NAME, my_default_csname());
mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
diff --git a/client/mysqlshow.c b/client/mysqlshow.c
index 9b31d87225c..cbac1817c3c 100644
--- a/client/mysqlshow.c
+++ b/client/mysqlshow.c
@@ -147,6 +147,7 @@ int main(int argc, char **argv)
if (!strcmp(default_charset,MYSQL_AUTODETECT_CHARSET_NAME))
default_charset= (char *)my_default_csname();
+ my_set_console_cp(default_charset);
mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset);
if (opt_plugin_dir && *opt_plugin_dir)
diff --git a/client/mysqltest.cc b/client/mysqltest.cc
index 56359ca6c07..7d807e00c1d 100644
--- a/client/mysqltest.cc
+++ b/client/mysqltest.cc
@@ -3282,6 +3282,47 @@ static int replace(DYNAMIC_STRING *ds_str,
return 0;
}
+#ifdef _WIN32
+/**
+ Check if background execution of command was requested.
+ Like in Unix shell, we assume background execution of the last
+ character in command is a ampersand (we do not tokenize though)
+*/
+static bool is_background_command(const DYNAMIC_STRING *ds)
+{
+ for (size_t i= ds->length - 1; i > 1; i--)
+ {
+ char c= ds->str[i];
+ if (!isspace(c))
+ return (c == '&');
+ }
+ return false;
+}
+
+/**
+ Execute OS command in background. We assume that the last character
+ is ampersand, i.e is_background_command() returned
+*/
+#include <string>
+static int execute_in_background(char *cmd)
+{
+ STARTUPINFO s{};
+ PROCESS_INFORMATION pi{};
+ char *end= strrchr(cmd, '&');
+ DBUG_ASSERT(end);
+ *end =0;
+ std::string scmd("cmd /c ");
+ scmd.append(cmd);
+ BOOL ok=
+ CreateProcess(0, (char *)scmd.c_str(), 0, 0, 0, CREATE_NO_WINDOW, 0, 0, &s, &pi);
+ *end= '&';
+ if (!ok)
+ return (int) GetLastError();
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ return 0;
+}
+#endif
/*
Execute given command.
@@ -3356,6 +3397,14 @@ void do_exec(struct st_command *command)
DBUG_PRINT("info", ("Executing '%s' as '%s'",
command->first_argument, ds_cmd.str));
+#ifdef _WIN32
+ if (is_background_command(&ds_cmd))
+ {
+ error= execute_in_background(ds_cmd.str);
+ goto end;
+ }
+#endif
+
if (!(res_file= my_popen(ds_cmd.str, "r")))
{
dynstr_free(&ds_cmd);
@@ -3382,7 +3431,9 @@ void do_exec(struct st_command *command)
dynstr_append_sorted(&ds_res, &ds_sorted, 0);
dynstr_free(&ds_sorted);
}
-
+#ifdef _WIN32
+end:
+#endif
if (error)
{
uint status= WEXITSTATUS(error);