From 86fbb58c3ac0865482819c10a3e81f2eea001c36 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 15:39:13 +0200 Subject: Merging upstream version 1:10.11.7. Signed-off-by: Daniel Baumann --- client/CMakeLists.txt | 2 +- client/mysql.cc | 139 ++++++++++++---- client/mysql_upgrade.c | 9 +- client/mysqladmin.cc | 32 ++-- client/mysqltest.cc | 419 ++++++++++++++++++++++++++++++++----------------- 5 files changed, 419 insertions(+), 182 deletions(-) (limited to 'client') diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 55fd02b2..4dfce247 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -16,7 +16,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/include - ${PCRE_INCLUDES} + ${PCRE_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/mysys_ssl ${ZLIB_INCLUDE_DIR} ${SSL_INCLUDE_DIRS} diff --git a/client/mysql.cc b/client/mysql.cc index 1c842dae..ab4c3cd7 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -261,6 +261,9 @@ static int connect_flag=CLIENT_INTERACTIVE; static my_bool opt_binary_mode= FALSE; static my_bool opt_connect_expired_password= FALSE; static int interrupted_query= 0; +#ifdef USE_LIBEDIT_INTERFACE +static int sigint_received= 0; +#endif static char *current_host,*current_db,*current_user=0,*opt_password=0, *current_prompt=0, *delimiter_str= 0, *default_charset= (char*) MYSQL_AUTODETECT_CHARSET_NAME, @@ -1162,6 +1165,8 @@ extern "C" sig_handler handle_sigint(int sig); static sig_handler window_resize(int sig); #endif +static void end_in_sig_handler(int sig); +static bool kill_query(const char *reason); const char DELIMITER_NAME[]= "delimiter"; const uint DELIMITER_NAME_LEN= sizeof(DELIMITER_NAME) - 1; @@ -1301,8 +1306,8 @@ int main(int argc,char *argv[]) if (opt_sigint_ignore) signal(SIGINT, SIG_IGN); else - signal(SIGINT, handle_sigint); // Catch SIGINT to clean up - signal(SIGQUIT, mysql_end); // Catch SIGQUIT to clean up + signal(SIGINT, handle_sigint); // Catch SIGINT to clean up + signal(SIGQUIT, mysql_end); // Catch SIGQUIT to clean up #if defined(HAVE_TERMIOS_H) && defined(GWINSZ_IN_SYS_IOCTL) /* Readline will call this if it installs a handler */ @@ -1512,30 +1517,35 @@ static bool do_connect(MYSQL *mysql, const char *host, const char *user, } -/* - This function handles sigint calls - If query is in process, kill query - If 'source' is executed, abort source command - no query in process, terminate like previous behavior - */ +void end_in_sig_handler(int sig) +{ +#ifdef _WIN32 + /* + When SIGINT is raised on Windows, the OS creates a new thread to handle the + interrupt. Once that thread completes, the main thread continues running + only to find that it's resources have already been free'd when the sigint + handler called mysql_end(). + */ + mysql_thread_end(); +#else + mysql_end(sig); +#endif +} -sig_handler handle_sigint(int sig) + +/* + Kill a running query. Returns true if we were unable to connect to the server. +*/ +bool kill_query(const char *reason) { char kill_buffer[40]; MYSQL *kill_mysql= NULL; - /* terminate if no query being executed, or we already tried interrupting */ - if (!executing_query || (interrupted_query == 2)) - { - tee_fprintf(stdout, "Ctrl-C -- exit!\n"); - goto err; - } - kill_mysql= mysql_init(kill_mysql); if (!do_connect(kill_mysql,current_host, current_user, opt_password, "", 0)) { - tee_fprintf(stdout, "Ctrl-C -- sorry, cannot connect to server to kill query, giving up ...\n"); - goto err; + tee_fprintf(stdout, "%s -- sorry, cannot connect to server to kill query, giving up ...\n", reason); + return true; } /* First time try to kill the query, second time the connection */ @@ -1550,27 +1560,62 @@ sig_handler handle_sigint(int sig) (interrupted_query == 1) ? "QUERY " : "", mysql_thread_id(&mysql)); if (verbose) - tee_fprintf(stdout, "Ctrl-C -- sending \"%s\" to server ...\n", + tee_fprintf(stdout, "%s -- sending \"%s\" to server ...\n", reason, kill_buffer); mysql_real_query(kill_mysql, kill_buffer, (uint) strlen(kill_buffer)); mysql_close(kill_mysql); - tee_fprintf(stdout, "Ctrl-C -- query killed. Continuing normally.\n"); + if (interrupted_query == 1) + tee_fprintf(stdout, "%s -- query killed.\n", reason); + else + tee_fprintf(stdout, "%s -- connection killed.\n", reason); + if (in_com_source) aborted= 1; // Abort source command - return; + return false; +} -err: -#ifdef _WIN32 +/* + This function handles sigint calls + If query is in process, kill query + If 'source' is executed, abort source command + no query in process, regenerate prompt. +*/ +sig_handler handle_sigint(int sig) +{ /* - When SIGINT is raised on Windows, the OS creates a new thread to handle the - interrupt. Once that thread completes, the main thread continues running - only to find that it's resources have already been free'd when the sigint - handler called mysql_end(). + On Unix only, if no query is being executed just clear the prompt, + don't exit. On Windows we exit. */ - mysql_thread_end(); + if (!executing_query) + { +#ifndef _WIN32 + tee_fprintf(stdout, "^C\n"); +#ifdef USE_LIBEDIT_INTERFACE + /* Libedit will regenerate it outside of the signal handler. */ + sigint_received= 1; #else - mysql_end(sig); -#endif + rl_on_new_line(); // Regenerate the prompt on a newline + rl_replace_line("", 0); // Clear the previous text + rl_redisplay(); +#endif +#else // WIN32 + tee_fprintf(stdout, "Ctrl-C -- exit!\n"); + end_in_sig_handler(sig); +#endif + return; + } + + /* + When executing a query, this newline makes the prompt look like so: + ^C + Ctrl-C -- query killed. + */ + tee_fprintf(stdout, "\n"); + if (kill_query("Ctrl-C")) + { + aborted= 1; + end_in_sig_handler(sig); + } } @@ -2137,6 +2182,15 @@ static int get_options(int argc, char **argv) return(0); } + +#if !defined(_WIN32) && defined(USE_LIBEDIT_INTERFACE) +static inline void reset_prompt(char *in_string, bool *ml_comment) { + glob_buffer.length(0); + *ml_comment = false; + *in_string = 0; +} +#endif + static int read_and_execute(bool interactive) { char *line= NULL; @@ -2228,7 +2282,30 @@ static int read_and_execute(bool interactive) if (line) free(line); line= readline(prompt); -#endif /* defined(_WIN32) */ +#ifdef USE_LIBEDIT_INTERFACE + /* + libedit handles interrupts different than libreadline. + libreadline has its own signal handlers, thus a sigint during readline + doesn't force readline to return null string. + + However libedit returns null if the interrupt signal is raised. + We can also get an empty string when ctrl+d is pressed (EoF). + + We need this sigint_received flag, to differentiate between the two + cases. This flag is only set during our handle_sigint function when + LIBEDIT_INTERFACE is used. + */ + if (!line && sigint_received) + { + // User asked to clear the input. + sigint_received= 0; + reset_prompt(&in_string, &ml_comment); + continue; + } + // For safety, we always mark this as cleared. + sigint_received= 0; +#endif +#endif /* defined(__WIN__) */ /* When Ctrl+d or Ctrl+z is pressed, the line may be NULL on some OS diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c index a6d497b2..7bac797f 100644 --- a/client/mysql_upgrade.c +++ b/client/mysql_upgrade.c @@ -1159,6 +1159,8 @@ static int install_used_plugin_data_types(void) DYNAMIC_STRING ds_result; const char *query = "SELECT table_comment FROM information_schema.tables" " WHERE table_comment LIKE 'Unknown data type: %'"; + if (opt_systables_only) + return 0; if (init_dynamic_string(&ds_result, "", 512, 512)) die("Out of memory"); run_query(query, &ds_result, TRUE); @@ -1475,7 +1477,12 @@ int main(int argc, char **argv) open_mysql_upgrade_file(); if (opt_check_upgrade) - exit(upgrade_already_done(0) == 0); + { + int upgrade_needed = upgrade_already_done(0); + free_used_memory(); + my_end(my_end_arg); + exit(upgrade_needed == 0); + } /* Find mysqlcheck */ find_tool(mysqlcheck_path, IF_WIN("mariadb-check.exe", "mariadb-check"), self_name); diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc index 022ba2ae..69bd3cd7 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -28,7 +28,7 @@ #include #include -#define ADMIN_VERSION "9.1" +#define ADMIN_VERSION "10.0" #define MAX_MYSQL_VAR 512 #define SHUTDOWN_DEF_TIMEOUT 3600 /* Wait for shutdown */ #define MAX_TRUNC_LENGTH 3 @@ -40,12 +40,12 @@ char ex_var_names[MAX_MYSQL_VAR+100][FN_REFLEN]; ulonglong last_values[MAX_MYSQL_VAR+100]; static int interval=0; static my_bool option_force=0,interrupted=0,new_line=0, - opt_compress= 0, opt_local= 0, opt_relative= 0, opt_verbose= 0, + opt_compress= 0, opt_local= 0, opt_relative= 0, opt_vertical= 0, tty_password= 0, opt_nobeep, - opt_shutdown_wait_for_slaves= 0; + opt_shutdown_wait_for_slaves= 0, opt_not_used; static my_bool debug_info_flag= 0, debug_check_flag= 0; static uint tcp_port = 0, option_wait = 0, option_silent=0, nr_iterations; -static uint opt_count_iterations= 0, my_end_arg; +static uint opt_count_iterations= 0, my_end_arg, opt_verbose= 0; static ulong opt_connect_timeout, opt_shutdown_timeout; static char * unix_port=0; static char *opt_plugin_dir= 0, *opt_default_auth= 0; @@ -204,8 +204,10 @@ static struct my_option my_long_options[] = {"user", 'u', "User for login if not current user.", &user, &user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #endif - {"verbose", 'v', "Write more information.", &opt_verbose, - &opt_verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"verbose", 'v', "Write more information." + "Using it will print more information for 'processlist." + "Using it 2 times will print even more information for 'processlist'.", + &opt_not_used, &opt_not_used, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"vertical", 'E', @@ -298,6 +300,11 @@ get_one_option(const struct my_option *opt, const char *argument, case 'I': /* Info */ usage(); exit(0); + case 'v': /* --verbose */ + opt_verbose++; + if (argument == disabled_my_option) + opt_verbose= 0; + break; case OPT_CHARSETS_DIR: #if MYSQL_VERSION_ID > 32300 charsets_dir = argument; @@ -828,10 +835,17 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) { MYSQL_RES *result; MYSQL_ROW row; + const char *query; + + if (!opt_verbose) + query= "show processlist"; + else if (opt_verbose == 1) + query= "show full processlist"; + else + query= "select * from information_schema.processlist where id != connection_id()"; - if (mysql_query(mysql, (opt_verbose ? "show full processlist" : - "show processlist")) || - !(result = mysql_store_result(mysql))) + if (mysql_query(mysql, query) || + !(result = mysql_store_result(mysql))) { my_printf_error(0, "process list failed; error: '%s'", error_flags, mysql_error(mysql)); diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 87c5a62a..a49895c9 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -80,6 +80,9 @@ static my_bool non_blocking_api_enabled= 0; #define DIE_BUFF_SIZE 256*1024 +#define RESULT_STRING_INIT_MEM 2048 +#define RESULT_STRING_INCREMENT_MEM 2048 + /* Flags controlling send and reap */ #define QUERY_SEND_FLAG 1 #define QUERY_REAP_FLAG 2 @@ -88,6 +91,8 @@ static my_bool non_blocking_api_enabled= 0; #define CLOSED_CONNECTION "-closed_connection-" +#define dynstr_append DO_NO_USE + #ifndef HAVE_SETENV static int setenv(const char *name, const char *value, int overwrite); #endif @@ -1729,7 +1734,7 @@ void log_msg(const char *fmt, ...) va_end(args); dynstr_append_mem(&ds_res, buff, len); - dynstr_append(&ds_res, "\n"); + dynstr_append_mem(&ds_res, STRING_WITH_LEN("\n")); DBUG_VOID_RETURN; } @@ -1865,7 +1870,7 @@ static int run_tool(const char *tool_path, DYNAMIC_STRING *ds_res, ...) die("Out of memory"); dynstr_append_os_quoted(&ds_cmdline, tool_path, NullS); - dynstr_append(&ds_cmdline, " "); + dynstr_append_mem(&ds_cmdline, STRING_WITH_LEN(" ")); va_start(args, ds_res); @@ -1875,14 +1880,14 @@ static int run_tool(const char *tool_path, DYNAMIC_STRING *ds_res, ...) if (strncmp(arg, "--", 2) == 0) dynstr_append_os_quoted(&ds_cmdline, arg, NullS); else - dynstr_append(&ds_cmdline, arg); - dynstr_append(&ds_cmdline, " "); + dynstr_append_mem(&ds_cmdline, arg, strlen(arg)); + dynstr_append_mem(&ds_cmdline, STRING_WITH_LEN(" ")); } va_end(args); #ifdef _WIN32 - dynstr_append(&ds_cmdline, "\""); + dynstr_append_mem(&ds_cmdline, STRING_WITH_LEN("\"")); #endif DBUG_PRINT("info", ("Running: %s", ds_cmdline.str)); @@ -2017,8 +2022,8 @@ void show_diff(DYNAMIC_STRING* ds, Fallback to dump both files to result file and inform about installing "diff" */ - dynstr_append(&ds_tmp, "\n"); - dynstr_append(&ds_tmp, + char message[]= +"\n" "\n" "The two files differ but it was not possible to execute 'diff' in\n" "order to show only the difference. Instead the whole content of the\n" @@ -2028,17 +2033,18 @@ void show_diff(DYNAMIC_STRING* ds, #ifdef _WIN32 "or http://gnuwin32.sourceforge.net/packages/diffutils.htm\n" #endif -"\n"); +"\n"; + dynstr_append_mem(&ds_tmp, message, sizeof(message)); - dynstr_append(&ds_tmp, " --- "); - dynstr_append(&ds_tmp, filename1); - dynstr_append(&ds_tmp, " >>>\n"); + dynstr_append_mem(&ds_tmp, STRING_WITH_LEN(" --- ")); + dynstr_append_mem(&ds_tmp, filename1, strlen(filename1)); + dynstr_append_mem(&ds_tmp, STRING_WITH_LEN(" >>>\n")); cat_file(&ds_tmp, filename1); - dynstr_append(&ds_tmp, "<<<\n --- "); - dynstr_append(&ds_tmp, filename1); - dynstr_append(&ds_tmp, " >>>\n"); + dynstr_append_mem(&ds_tmp, STRING_WITH_LEN("<<<\n --- ")); + dynstr_append_mem(&ds_tmp, filename1, strlen(filename1)); + dynstr_append_mem(&ds_tmp, STRING_WITH_LEN(" >>>\n")); cat_file(&ds_tmp, filename2); - dynstr_append(&ds_tmp, "<<<<\n"); + dynstr_append_mem(&ds_tmp, STRING_WITH_LEN("<<<<\n")); } if (ds) @@ -2818,9 +2824,9 @@ do_result_format_version(struct st_command *command) set_result_format_version(version); - dynstr_append(&ds_res, "result_format: "); + dynstr_append_mem(&ds_res, STRING_WITH_LEN("result_format: ")); dynstr_append_mem(&ds_res, ds_version.str, ds_version.length); - dynstr_append(&ds_res, "\n"); + dynstr_append_mem(&ds_res, STRING_WITH_LEN("\n")); dynstr_free(&ds_version); } @@ -3291,13 +3297,15 @@ static int replace(DYNAMIC_STRING *ds_str, { DYNAMIC_STRING ds_tmp; const char *start= strstr(ds_str->str, search_str); + size_t prefixlen= start - ds_str->str; if (!start) return 1; init_dynamic_string(&ds_tmp, "", ds_str->length + replace_len, 256); - dynstr_append_mem(&ds_tmp, ds_str->str, start - ds_str->str); + dynstr_append_mem(&ds_tmp, ds_str->str, prefixlen); dynstr_append_mem(&ds_tmp, replace_str, replace_len); - dynstr_append(&ds_tmp, start + search_len); + dynstr_append_mem(&ds_tmp, start + search_len, + ds_str->length - prefixlen - search_len); dynstr_set(ds_str, ds_tmp.str); dynstr_free(&ds_tmp); return 0; @@ -3412,7 +3420,7 @@ void do_exec(struct st_command *command) if (disable_result_log) { /* Collect stderr output as well, for the case app. crashes or returns error.*/ - dynstr_append(&ds_cmd, " 2>&1"); + dynstr_append_mem(&ds_cmd, STRING_WITH_LEN(" 2>&1")); } DBUG_PRINT("info", ("Executing '%s' as '%s'", @@ -3624,9 +3632,9 @@ void do_system(struct st_command *command) else { /* If ! abort_on_error, log message and continue */ - dynstr_append(&ds_res, "system command '"); + dynstr_append_mem(&ds_res, STRING_WITH_LEN("system command '")); replace_dynstr_append(&ds_res, command->first_argument); - dynstr_append(&ds_res, "' failed\n"); + dynstr_append_mem(&ds_res, STRING_WITH_LEN("' failed\n")); } } @@ -3802,7 +3810,7 @@ void do_remove_files_wildcard(struct st_command *command) wild_compare(file->name, ds_wild.str, 0)) continue; ds_file_to_remove.length= directory_length; - dynstr_append(&ds_file_to_remove, file->name); + dynstr_append_mem(&ds_file_to_remove, file->name, strlen(file->name)); DBUG_PRINT("info", ("removing file: %s", ds_file_to_remove.str)); if ((error= (my_delete(ds_file_to_remove.str, MYF(MY_WME)) != 0))) sys_errno= my_errno; @@ -4092,7 +4100,7 @@ static int get_list_files(DYNAMIC_STRING *ds, const DYNAMIC_STRING *ds_dirname, wild_compare(file->name, ds_wild->str, 0)) continue; replace_dynstr_append(ds, file->name); - dynstr_append(ds, "\n"); + dynstr_append_mem(ds, STRING_WITH_LEN("\n")); } set_wild_chars(0); my_dirend(dir_info); @@ -4690,15 +4698,11 @@ void do_perl(struct st_command *command) /* Check for error code that indicates perl could not be started */ int exstat= WEXITSTATUS(error); -#ifdef _WIN32 - if (exstat == 1) - /* Text must begin 'perl not found' as mtr looks for it */ - abort_not_supported_test("perl not found in path or did not start"); -#else +#ifndef _WIN32 if (exstat == 127) abort_not_supported_test("perl not found in path"); -#endif else +#endif handle_command_error(command, exstat, my_errno); } dynstr_free(&ds_delimiter); @@ -7681,7 +7685,7 @@ void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD* field, } else { - dynstr_append(ds, field->name); + dynstr_append_mem(ds, field->name, strlen(field->name)); dynstr_append_mem(ds, "\t", 1); replace_dynstr_append_mem(ds, val, len); dynstr_append_mem(ds, "\n", 1); @@ -7792,9 +7796,10 @@ void append_metadata(DYNAMIC_STRING *ds, uint num_fields) { MYSQL_FIELD *field_end; - dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t" - "Column_alias\tType\tLength\tMax length\tIs_null\t" - "Flags\tDecimals\tCharsetnr\n"); + dynstr_append_mem(ds, STRING_WITH_LEN( + "Catalog\tDatabase\tTable\tTable_alias\tColumn\t" + "Column_alias\tType\tLength\tMax length\tIs_null\t" + "Flags\tDecimals\tCharsetnr\n")); for (field_end= field+num_fields ; field < field_end ; @@ -7853,13 +7858,13 @@ void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows, const char *info) { char buf[40], buff2[21]; - sprintf(buf,"affected rows: %s\n", llstr(affected_rows, buff2)); - dynstr_append(ds, buf); + size_t len= sprintf(buf,"affected rows: %s\n", llstr(affected_rows, buff2)); + dynstr_append_mem(ds, buf, len); if (info) { - dynstr_append(ds, "info: "); - dynstr_append(ds, info); - dynstr_append_mem(ds, "\n", 1); + dynstr_append_mem(ds, STRING_WITH_LEN("info: ")); + dynstr_append_mem(ds, info, strlen(info)); + dynstr_append_mem(ds, STRING_WITH_LEN("\n")); } } @@ -7905,18 +7910,19 @@ static void append_session_track_info(DYNAMIC_STRING *ds, MYSQL *mysql) (enum_session_state_type) type, &data, &data_length)) { - dynstr_append(ds, "-- "); + dynstr_append_mem(ds, STRING_WITH_LEN("-- ")); if (type <= SESSION_TRACK_END) { - dynstr_append(ds, trking_info_desc[type]); + dynstr_append_mem(ds, trking_info_desc[type], + strlen(trking_info_desc[type])); } else { DBUG_ASSERT(0); - dynstr_append(ds, "Tracker???\n"); + dynstr_append_mem(ds, STRING_WITH_LEN("Tracker???\n")); } - dynstr_append(ds, "-- "); + dynstr_append_mem(ds, STRING_WITH_LEN("-- ")); dynstr_append_mem(ds, data, data_length); } else @@ -7925,16 +7931,16 @@ static void append_session_track_info(DYNAMIC_STRING *ds, MYSQL *mysql) (enum_session_state_type) type, &data, &data_length)) { - dynstr_append(ds, "\n-- "); + dynstr_append_mem(ds, STRING_WITH_LEN("\n-- ")); if (data == NULL) { DBUG_ASSERT(data_length == 0); - dynstr_append_mem(ds, "", sizeof("") - 1); + dynstr_append_mem(ds, STRING_WITH_LEN("")); } else dynstr_append_mem(ds, data, data_length); } - dynstr_append(ds, "\n\n"); + dynstr_append_mem(ds, STRING_WITH_LEN("\n\n")); } #endif /* EMBEDDED_LIBRARY */ } @@ -8334,7 +8340,8 @@ void handle_error(struct st_command *command, else if (command->expected_errors.err[0].type == ERR_SQLSTATE || (command->expected_errors.err[0].type == ERR_ERRNO && command->expected_errors.err[0].code.errnum != 0)) - dynstr_append(ds,"Got one of the listed errors\n"); + dynstr_append_mem(ds, STRING_WITH_LEN("Got one of the listed " + "errors\n")); } /* OK */ revert_properties(); @@ -8413,6 +8420,85 @@ void handle_no_error(struct st_command *command) } +/* + Read result set after prepare statement execution + + SYNOPSIS + read_stmt_results + stmt - prepare statemet + mysql - mysql handle + command - current command pointer + ds - output buffer where to store result form query + + RETURN VALUE + 1 - if there is an error in result set +*/ + +int read_stmt_results(MYSQL_STMT* stmt, + DYNAMIC_STRING* ds, + struct st_command *command) +{ + MYSQL_RES *res= NULL; + + /* + We instruct that we want to update the "max_length" field in + mysql_stmt_store_result(), this is our only way to know how much + buffer to allocate for result data + */ + { + my_bool one= 1; + if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one)) + die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s", + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); + } + + /* + If we got here the statement succeeded and was expected to do so, + get data. Note that this can still give errors found during execution! + Store the result of the query if if will return any fields + */ + if (mysql_stmt_field_count(stmt) && mysql_stmt_store_result(stmt)) + { + handle_error(command, mysql_stmt_errno(stmt), + mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); + return 1; + } + + if (!disable_result_log) + { + /* + Not all statements creates a result set. If there is one we can + now create another normal result set that contains the meta + data. This set can be handled almost like any other non prepared + statement result set. + */ + if ((res= mysql_stmt_result_metadata(stmt)) != NULL) + { + /* Take the column count from meta info */ + MYSQL_FIELD *fields= mysql_fetch_fields(res); + uint num_fields= mysql_num_fields(res); + + if (display_metadata) + append_metadata(ds, fields, num_fields); + + if (!display_result_vertically) + append_table_headings(ds, fields, num_fields); + + append_stmt_result(ds, stmt, fields, num_fields); + + mysql_free_result(res); /* Free normal result set with meta data */ + + } + else + { + /* + This is a query without resultset + */ + } + } + return 0; +} + /* Run query using prepared statement C API @@ -8433,11 +8519,17 @@ void run_query_stmt(struct st_connection *cn, struct st_command *command, DYNAMIC_STRING *ds_warnings) { my_bool ignore_second_execution= 0; - MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */ MYSQL *mysql= cn->mysql; MYSQL_STMT *stmt; DYNAMIC_STRING ds_prepare_warnings; DYNAMIC_STRING ds_execute_warnings; + DYNAMIC_STRING ds_res_1st_execution; + DYNAMIC_STRING ds_res_2_execution_unsorted; + DYNAMIC_STRING *ds_res_2_output; + my_bool ds_res_1st_execution_init = FALSE; + my_bool compare_2nd_execution = TRUE; + int query_match_ps2_re; + DBUG_ENTER("run_query_stmt"); DBUG_PRINT("query", ("'%-.60s'", query)); DBUG_PRINT("info", @@ -8453,7 +8545,7 @@ void run_query_stmt(struct st_connection *cn, struct st_command *command, /* Init a new stmt if it's not already one created for this connection */ - if(!(stmt= cn->stmt)) + if (!(stmt= cn->stmt)) { if (!(stmt= mysql_stmt_init(mysql))) die("unable to init stmt structure"); @@ -8467,6 +8559,12 @@ void run_query_stmt(struct st_connection *cn, struct st_command *command, init_dynamic_string(&ds_execute_warnings, NULL, 0, 256); } + /* Check and remove potential trash */ + if (strlen(ds->str) != 0) + { + dynstr_trunc(ds, 0); + } + /* Prepare the query */ @@ -8502,10 +8600,12 @@ void run_query_stmt(struct st_connection *cn, struct st_command *command, } #endif + query_match_ps2_re = match_re(&ps2_re, query); + /* Execute the query first time if second execution enable */ - if(ps2_protocol_enabled && match_re(&ps2_re, query)) + if (ps2_protocol_enabled && query_match_ps2_re) { if (do_stmt_execute(cn)) { @@ -8513,12 +8613,32 @@ void run_query_stmt(struct st_connection *cn, struct st_command *command, mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); goto end; } + /* We cannot run query twice if we get prepare warnings as these will otherwise be disabled */ ignore_second_execution= (prepare_warnings_enabled && mysql_warning_count(mysql) != 0); + + if (ignore_second_execution) + compare_2nd_execution = 0; + else + { + init_dynamic_string(&ds_res_1st_execution, "", + RESULT_STRING_INIT_MEM, RESULT_STRING_INCREMENT_MEM); + ds_res_1st_execution_init = TRUE; + if (read_stmt_results(stmt, &ds_res_1st_execution, command)) + { + /* + There was an error during execution + and there is no result set to compare + */ + compare_2nd_execution = 0; + } + else + handle_no_error(command); + } } /* @@ -8531,6 +8651,8 @@ void run_query_stmt(struct st_connection *cn, struct st_command *command, goto end; } + DBUG_ASSERT(ds->length == 0); + int err; do { @@ -8541,75 +8663,82 @@ void run_query_stmt(struct st_connection *cn, struct st_command *command, if (cursor_protocol_enabled && !disable_warnings) append_warnings(&ds_execute_warnings, mysql); - /* - We instruct that we want to update the "max_length" field in - mysql_stmt_store_result(), this is our only way to know how much - buffer to allocate for result data - */ + if (!disable_result_log && + compare_2nd_execution && + ps2_protocol_enabled && + query_match_ps2_re && + display_result_sorted) { - my_bool one= 1; - if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one)) - die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s", - mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); + init_dynamic_string(&ds_res_2_execution_unsorted, "", + RESULT_STRING_INIT_MEM, + RESULT_STRING_INCREMENT_MEM); + ds_res_2_output= &ds_res_2_execution_unsorted; } + else + ds_res_2_output= ds; - /* - If we got here the statement succeeded and was expected to do so, - get data. Note that this can still give errors found during execution! - Store the result of the query if if will return any fields - */ - if (mysql_stmt_field_count(stmt) && mysql_stmt_store_result(stmt)) + if (read_stmt_results(stmt, ds_res_2_output, command)) { - handle_error(command, mysql_stmt_errno(stmt), - mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); - goto end; + if (ds_res_2_output != ds) + { + dynstr_append_mem(ds, ds_res_2_output->str, ds_res_2_output->length); + dynstr_free(ds_res_2_output); + } + goto end; } if (!disable_result_log) { /* - Not all statements creates a result set. If there is one we can - now create another normal result set that contains the meta - data. This set can be handled almost like any other non prepared - statement result set. + The results of the first and second execution are compared + only if result logging is enabled */ - if ((res= mysql_stmt_result_metadata(stmt)) != NULL) + if (compare_2nd_execution && ps2_protocol_enabled && query_match_ps2_re) { - /* Take the column count from meta info */ - MYSQL_FIELD *fields= mysql_fetch_fields(res); - uint num_fields= mysql_num_fields(res); - - if (display_metadata) - append_metadata(ds, fields, num_fields); - - if (!display_result_vertically) - append_table_headings(ds, fields, num_fields); - - append_stmt_result(ds, stmt, fields, num_fields); - - mysql_free_result(res); /* Free normal result set with meta data */ - - /* - Normally, if there is a result set, we do not show warnings from the - prepare phase. This is because some warnings are generated both during - prepare and execute; this would generate different warning output - between normal and ps-protocol test runs. - - The --enable_prepare_warnings command can be used to change this so - that warnings from both the prepare and execute phase are shown. - */ - if (!disable_warnings && !prepare_warnings_enabled) + DYNAMIC_STRING *ds_res_1_execution_compare; + DYNAMIC_STRING ds_res_1_execution_sorted; + if (display_result_sorted) { - DBUG_PRINT("info", ("warnings disabled")); - dynstr_set(&ds_prepare_warnings, NULL); + init_dynamic_string(&ds_res_1_execution_sorted, "", + RESULT_STRING_INIT_MEM, + RESULT_STRING_INCREMENT_MEM); + dynstr_append_sorted(&ds_res_1_execution_sorted, + &ds_res_1st_execution, 1); + dynstr_append_sorted(ds, &ds_res_2_execution_unsorted, 1); + ds_res_1_execution_compare= &ds_res_1_execution_sorted; + } + else + { + ds_res_1_execution_compare= &ds_res_1st_execution; + } + if (ds->length != ds_res_1_execution_compare->length || + !(memcmp(ds_res_1_execution_compare->str, ds->str, ds->length) == 0)) + { + die("The result of the 1st execution does not match with \n" + "the result of the 2nd execution of ps-protocol:\n 1st:\n" + "%s\n 2nd:\n %s", + ds_res_1_execution_compare->str, + ds->str); + } + if (display_result_sorted) + { + dynstr_free(&ds_res_1_execution_sorted); + dynstr_free(&ds_res_2_execution_unsorted); } } - else - { - /* - This is a query without resultset - */ - } + + /* + Normally, if there is a result set, we do not show warnings from the + prepare phase. This is because some warnings are generated both during + prepare and execute; this would generate different warning output + between normal and ps-protocol test runs. + The --enable_prepare_warnings command can be used to change this so + that warnings from both the prepare and execute phase are shown. + */ + if ((mysql_stmt_result_metadata(stmt) != NULL) && + !disable_warnings && + !prepare_warnings_enabled) + dynstr_set(&ds_prepare_warnings, NULL); /* Fetch info before fetching warnings, since it will be reset @@ -8621,7 +8750,6 @@ void run_query_stmt(struct st_connection *cn, struct st_command *command, if (display_session_track_info) append_session_track_info(ds, mysql); - if (!disable_warnings && !mysql_more_results(stmt->mysql)) { /* Get the warnings from execute */ @@ -8653,7 +8781,15 @@ void run_query_stmt(struct st_connection *cn, struct st_command *command, mysql_sqlstate(mysql), ds); else handle_no_error(command); + end: + + if (ds_res_1st_execution_init) + { + dynstr_free(&ds_res_1st_execution); + ds_res_1st_execution_init= FALSE; + } + if (!disable_warnings) { dynstr_free(&ds_prepare_warnings); @@ -9164,11 +9300,14 @@ int util_query(MYSQL* org_mysql, const char* query){ void run_query(struct st_connection *cn, struct st_command *command, int flags) { MYSQL *mysql= cn->mysql; - DYNAMIC_STRING *ds; - DYNAMIC_STRING *save_ds= NULL; - DYNAMIC_STRING ds_result; - DYNAMIC_STRING ds_sorted; - DYNAMIC_STRING ds_warnings; + DYNAMIC_STRING *rs_output; /* where to put results */ + DYNAMIC_STRING rs_cmp_result; /* here we put results to compare with + pre-recrded file */ + DYNAMIC_STRING rs_unsorted; /* if we need sorted results, here we store + results before sorting them */ + DYNAMIC_STRING *rs_sorted_save= NULL; /* here we store where to put sorted + result if needed */ + DYNAMIC_STRING rs_warnings; char *query; size_t query_len; my_bool view_created= 0, sp_created= 0; @@ -9181,10 +9320,10 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) if (!(flags & QUERY_SEND_FLAG) && !cn->pending) die("Cannot reap on a connection without pending send"); - - init_dynamic_string(&ds_warnings, NULL, 0, 256); - ds_warn= &ds_warnings; - + + init_dynamic_string(&rs_warnings, NULL, 0, 256); + ds_warn= &rs_warnings; + /* Evaluate query if this is an eval command */ @@ -9214,11 +9353,11 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) */ if (command->require_file) { - init_dynamic_string(&ds_result, "", 1024, 1024); - ds= &ds_result; + init_dynamic_string(&rs_cmp_result, "", 1024, 1024); + rs_output= &rs_cmp_result; } else - ds= &ds_res; + rs_output= &ds_res; // will be shown to colsole /* Log the query into the output buffer @@ -9232,9 +9371,9 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) print_query= command->query; print_len= (int)(command->end - command->query); } - replace_dynstr_append_mem(ds, print_query, print_len); - dynstr_append_mem(ds, delimiter, delimiter_length); - dynstr_append_mem(ds, "\n", 1); + replace_dynstr_append_mem(rs_output, print_query, print_len); + dynstr_append_mem(rs_output, delimiter, delimiter_length); + dynstr_append_mem(rs_output, "\n", 1); } /* We're done with this flag */ @@ -9289,7 +9428,7 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) Collect warnings from create of the view that should otherwise have been produced when the SELECT was executed */ - append_warnings(&ds_warnings, + append_warnings(&rs_warnings, service_connection_enabled ? cur_con->util_mysql : mysql); @@ -9345,9 +9484,9 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) that can be sorted before it's added to the global result string */ - init_dynamic_string(&ds_sorted, "", 1024, 1024); - save_ds= ds; /* Remember original ds */ - ds= &ds_sorted; + init_dynamic_string(&rs_unsorted, "", 1024, 1024); + rs_sorted_save= rs_output; /* Remember original ds */ + rs_output= &rs_unsorted; } /* @@ -9368,20 +9507,20 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) All other statements can be run using prepared statement C API. */ !match_re(&ps_re, query)) - run_query_stmt(cn, command, query, query_len, ds, &ds_warnings); + run_query_stmt(cn, command, query, query_len, rs_output, &rs_warnings); else run_query_normal(cn, command, flags, query, query_len, - ds, &ds_warnings); + rs_output, &rs_warnings); - dynstr_free(&ds_warnings); + dynstr_free(&rs_warnings); ds_warn= 0; if (display_result_sorted) { /* Sort the result set and append it to result */ - dynstr_append_sorted(save_ds, &ds_sorted, 1); - ds= save_ds; - dynstr_free(&ds_sorted); + dynstr_append_sorted(rs_sorted_save, &rs_unsorted, 1); + rs_output= rs_sorted_save; + dynstr_free(&rs_unsorted); } if (sp_created) @@ -9404,11 +9543,11 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) and the output should be checked against an already existing file which has been specified using --require or --result */ - check_require(ds, command->require_file); + check_require(rs_output, command->require_file); } - if (ds == &ds_result) - dynstr_free(&ds_result); + if (rs_output == &rs_cmp_result) + dynstr_free(&rs_cmp_result); DBUG_VOID_RETURN; } @@ -9667,7 +9806,7 @@ void mark_progress(struct st_command* command __attribute__((unused)), dynstr_append_mem(&ds_progress, "\t", 1); /* Filename */ - dynstr_append(&ds_progress, cur_file->file_name); + dynstr_append_mem(&ds_progress, cur_file->file_name, strlen(cur_file->file_name)); dynstr_append_mem(&ds_progress, ":", 1); /* Line in file */ @@ -9865,7 +10004,7 @@ int main(int argc, char **argv) read_command_buf= (char*)my_malloc(PSI_NOT_INSTRUMENTED, read_command_buflen= 65536, MYF(MY_FAE)); - init_dynamic_string(&ds_res, "", 2048, 2048); + init_dynamic_string(&ds_res, "", RESULT_STRING_INIT_MEM, RESULT_STRING_INCREMENT_MEM); init_alloc_root(PSI_NOT_INSTRUMENTED, &require_file_root, 1024, 1024, MYF(0)); parse_args(argc, argv); @@ -10292,7 +10431,7 @@ int main(int argc, char **argv) if (p && *p == '#' && *(p+1) == '#') { dynstr_append_mem(&ds_res, command->query, command->query_len); - dynstr_append(&ds_res, "\n"); + dynstr_append_mem(&ds_res, STRING_WITH_LEN("\n")); } break; } @@ -10305,7 +10444,7 @@ int main(int argc, char **argv) if (disable_query_log) break; - dynstr_append(&ds_res, "\n"); + dynstr_append_mem(&ds_res, STRING_WITH_LEN("\n")); break; case Q_PING: handle_command_error(command, mysql_ping(cur_con->mysql), -1); @@ -11967,8 +12106,8 @@ void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING *ds_input, for (i= 0; i < lines.elements ; i++) { const char **line= dynamic_element(&lines, i, const char**); - dynstr_append(ds, *line); - dynstr_append(ds, "\n"); + dynstr_append_mem(ds, *line, strlen(*line)); + dynstr_append_mem(ds, STRING_WITH_LEN("\n")); } delete_dynamic(&lines); -- cgit v1.2.3