From 083a316d1f9020d48a5b9769fafbb3accad3c03a Mon Sep 17 00:00:00 2001 From: Tatjana Azundris Nuernberg Date: Tue, 12 Jul 2011 06:08:52 +0100 Subject: [PATCH 01/29] Bug#11758414/Bug#50614: Default storage_engine not honored when set from within a stored procedure When CREATE TABLE wasn't given ENGINE=... it would determine the default ENGINE at parse-time rather than at execution time, leading to incorrect behaviour (namely, later changes to the default engine being ignore) when calling CREATE TABLE from a stored procedure. We now defer working out the default engine till execution of CREATE TABLE. mysql-test/r/sp_trans.result: results! mysql-test/t/sp_trans.test: Show that CREATE TABLE (called from store routine) heeds any changes after CREATE SP / parse-time. Show that explicitly requesting an ENGINE still works. sql/sql_parse.cc: If no ENGINE=... was given at parse-time, determine default engine at execution time of CREATE TABLE. sql/sql_yacc.yy: If CREATE TABLE is not given ENGINE=..., don't bother figuring out the default engine during parsing; we'll do it at execution time instead to be aware of the latest updates. --- mysql-test/r/sp_trans.result | 46 ++++++++++++++++++++++++++++++++++++ mysql-test/t/sp_trans.test | 33 ++++++++++++++++++++++++++ sql/sql_parse.cc | 6 +++++ sql/sql_yacc.yy | 4 ++-- 4 files changed, 87 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/sp_trans.result b/mysql-test/r/sp_trans.result index a64f53efde7..dec37db652a 100644 --- a/mysql-test/r/sp_trans.result +++ b/mysql-test/r/sp_trans.result @@ -556,3 +556,49 @@ f1 bug13575(f1) 3 ccc drop function bug13575| drop table t3| +SELECT @@GLOBAL.storage_engine INTO @old_engine| +SET @@GLOBAL.storage_engine=InnoDB| +SET @@SESSION.storage_engine=InnoDB| +SHOW GLOBAL VARIABLES LIKE 'storage_engine'| +Variable_name Value +storage_engine InnoDB +SHOW SESSION VARIABLES LIKE 'storage_engine'| +Variable_name Value +storage_engine InnoDB +CREATE PROCEDURE bug11758414() +BEGIN +SET @@GLOBAL.storage_engine="MyISAM"; +SET @@SESSION.storage_engine="MyISAM"; +# show defaults at execution time / that setting them worked +SHOW GLOBAL VARIABLES LIKE 'storage_engine'; +SHOW SESSION VARIABLES LIKE 'storage_engine'; +CREATE TABLE t1 (id int); +CREATE TABLE t2 (id int) ENGINE=InnoDB; +# show we're heeding the default (at run-time, not parse-time!) + SHOW CREATE TABLE t1; + # show that we didn't break explicit override with ENGINE=... +SHOW CREATE TABLE t2; +END; +| +CALL bug11758414| +Variable_name Value +storage_engine MyISAM +Variable_name Value +storage_engine MyISAM +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +Table Create Table +t2 CREATE TABLE `t2` ( + `id` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +SHOW GLOBAL VARIABLES LIKE 'storage_engine'| +Variable_name Value +storage_engine MyISAM +SHOW SESSION VARIABLES LIKE 'storage_engine'| +Variable_name Value +storage_engine MyISAM +DROP PROCEDURE bug11758414| +DROP TABLE t1, t2| +SET @@GLOBAL.storage_engine=@old_engine| diff --git a/mysql-test/t/sp_trans.test b/mysql-test/t/sp_trans.test index 0b04b9d7668..2d59fb20bbd 100644 --- a/mysql-test/t/sp_trans.test +++ b/mysql-test/t/sp_trans.test @@ -593,6 +593,39 @@ drop function bug13575| drop table t3| +# +# BUG#11758414: Default storage_engine not honored when set +# from within a stored procedure +# +SELECT @@GLOBAL.storage_engine INTO @old_engine| +SET @@GLOBAL.storage_engine=InnoDB| +SET @@SESSION.storage_engine=InnoDB| +# show defaults at define-time +SHOW GLOBAL VARIABLES LIKE 'storage_engine'| +SHOW SESSION VARIABLES LIKE 'storage_engine'| +CREATE PROCEDURE bug11758414() +BEGIN + SET @@GLOBAL.storage_engine="MyISAM"; + SET @@SESSION.storage_engine="MyISAM"; + # show defaults at execution time / that setting them worked + SHOW GLOBAL VARIABLES LIKE 'storage_engine'; + SHOW SESSION VARIABLES LIKE 'storage_engine'; + CREATE TABLE t1 (id int); + CREATE TABLE t2 (id int) ENGINE=InnoDB; + # show we're heeding the default (at run-time, not parse-time!) + SHOW CREATE TABLE t1; + # show that we didn't break explicit override with ENGINE=... + SHOW CREATE TABLE t2; +END; +| +CALL bug11758414| +# show that changing defaults within SP stuck +SHOW GLOBAL VARIABLES LIKE 'storage_engine'| +SHOW SESSION VARIABLES LIKE 'storage_engine'| +DROP PROCEDURE bug11758414| +DROP TABLE t1, t2| +SET @@GLOBAL.storage_engine=@old_engine| + # # BUG#NNNN: New bug synopsis # diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2eeabc2f0c1..14ad77d29fd 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2616,6 +2616,12 @@ mysql_execute_command(THD *thd) create_table->table_name)) goto end_with_restore_list; #endif + /* + If no engine type was given, work out the default now + rather than at parse-time. + */ + if (!(create_info.used_fields & HA_CREATE_USED_ENGINE)) + create_info.db_type= ha_default_handlerton(thd); /* If we are using SET CHARSET without DEFAULT, add an implicit DEFAULT to not confuse old users. (This may change). diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 6245f07b9cf..f4fb822b294 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1838,7 +1838,6 @@ create: lex->change=NullS; bzero((char*) &lex->create_info,sizeof(lex->create_info)); lex->create_info.options=$2 | $4; - lex->create_info.db_type= ha_default_handlerton(thd); lex->create_info.default_table_charset= NULL; lex->name.str= 0; lex->name.length= 0; @@ -1847,7 +1846,8 @@ create: { LEX *lex= YYTHD->lex; lex->current_select= &lex->select_lex; - if (!lex->create_info.db_type) + if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && + !lex->create_info.db_type) { lex->create_info.db_type= ha_default_handlerton(YYTHD); push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN, From a38fc8d47e121e15b82e26b5bffecb9c3d5801ee Mon Sep 17 00:00:00 2001 From: Chuck Bell Date: Tue, 19 Jul 2011 10:17:58 -0400 Subject: [PATCH 02/29] WL#5710 : mysql_plugin - enable or disable plugins This patch adds a new client utility that enables or disables plugin features. The utility disables or enables a plugin using values (name, soname, and symbols) provided via a configuration file by the same name. For example, to ENABLE the daemon_example plugin, the utility will read the daemon_example.ini configuration file and use the values contained to enable or disable the plugin. --- client/CMakeLists.txt | 5 +- client/mysql_plugin.c | 1077 +++++++++++++++++ include/my_global.h | 4 + mysql-test/include/daemon_example.ini | 8 + .../include/daemon_example_bad_format.ini | 8 + mysql-test/include/plugin.defs | 1 + mysql-test/mysql-test-run.pl | 3 + mysql-test/r/mysql_plugin.result | 97 ++ mysql-test/t/mysql_plugin-master.opt | 1 + mysql-test/t/mysql_plugin.test | 205 ++++ plugin/daemon_example/CMakeLists.txt | 2 + plugin/daemon_example/daemon_example.ini | 6 + 12 files changed, 1416 insertions(+), 1 deletion(-) create mode 100644 client/mysql_plugin.c create mode 100644 mysql-test/include/daemon_example.ini create mode 100644 mysql-test/include/daemon_example_bad_format.ini create mode 100644 mysql-test/r/mysql_plugin.result create mode 100644 mysql-test/t/mysql_plugin-master.opt create mode 100644 mysql-test/t/mysql_plugin.test create mode 100644 plugin/daemon_example/daemon_example.ini diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 80c5bbd1c9f..c5bda7d8a35 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -54,6 +54,9 @@ ADD_DEPENDENCIES(mysql_upgrade GenFixPrivs) MYSQL_ADD_EXECUTABLE(mysqlshow mysqlshow.c) TARGET_LINK_LIBRARIES(mysqlshow mysqlclient) +MYSQL_ADD_EXECUTABLE(mysql_plugin mysql_plugin.c) +TARGET_LINK_LIBRARIES(mysql_plugin mysqlclient) + MYSQL_ADD_EXECUTABLE(mysqlbinlog mysqlbinlog.cc) TARGET_LINK_LIBRARIES(mysqlbinlog mysqlclient) @@ -69,7 +72,7 @@ IF(WIN32) MYSQL_ADD_EXECUTABLE(echo echo.c) ENDIF(WIN32) -SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap +SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap mysql_plugin PROPERTIES HAS_CXX TRUE) ADD_DEFINITIONS(-DHAVE_DLOPEN) diff --git a/client/mysql_plugin.c b/client/mysql_plugin.c new file mode 100644 index 00000000000..ca3da0086e4 --- /dev/null +++ b/client/mysql_plugin.c @@ -0,0 +1,1077 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include + + +#define SHOW_VERSION "1.0.0" +#define PRINT_VERSION do { printf("%s Ver %s Distrib %s\n", \ + my_progname, SHOW_VERSION, MYSQL_SERVER_VERSION); \ + } while(0) + +/* Global variables. */ +static uint my_end_arg= 0; +static uint opt_verbose=0; +static uint opt_no_defaults= 0; +static uint opt_print_defaults= 0; +static char *opt_datadir=0, *opt_basedir=0, + *opt_plugin_dir=0, *opt_plugin_ini=0; +static char bootstrap[FN_REFLEN]; + + +/* plugin struct */ +struct st_plugin +{ + const char *name; /* plugin name */ + const char *so_name; /* plugin so (library) name */ + const char *symbols[16]; /* symbols to load */ +} plugin_data; + + +/* Options */ +static struct my_option my_long_options[] = +{ + {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, + 0, 0, 0, 0, 0, 0}, + {"basedir", 'b', "The basedir for the server.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"datadir", 'd', "The datadir for the server.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"plugin-dir", 'p', "The plugin dir for the server.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"plugin-ini", 'i', "Read plugin information from configuration file " + "specified instead of from /.ini.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"no-defaults", 'n', "Do not read values from configuration file.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"print-defaults", 'P', "Show default values from configuration file.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"verbose", 'v', + "More verbose output; you can use this multiple times to get even more " + "verbose output.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 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}, + {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} +}; + + +/* Methods */ +static int process_options(int argc, char *argv[], char *operation); +static int check_access(); +static int find_tool(const char *tool_name, char *tool_path); +static int find_plugin(char *tp_path); +static int build_bootstrap_file(char *operation, char *bootstrap); +static int dump_bootstrap_file(char *bootstrap_file); +static int bootstrap_server(char *server_path, char *bootstrap_file); + + +int main(int argc,char *argv[]) +{ + int error= 0; + char tp_path[FN_REFLEN]; + char server_path[FN_REFLEN]; + char operation[16]; + + MY_INIT(argv[0]); + plugin_data.name= 0; // initialize name + + /* + The following operations comprise the method for enabling or disabling + a plugin. We begin by processing the command options then check the + directories specified for --datadir, --basedir, --plugin-dir, and + --plugin-ini (if specified). If the directories are Ok, we then look + for the mysqld executable and the plugin soname. Finally, we build a + bootstrap command file for use in bootstraping the server. + + If any step fails, the method issues an error message and the tool exits. + + 1) Parse, execute, and verify command options. + 2) Check access to directories. + 3) Look for mysqld executable. + 4) Look for the plugin. + 5) Build a bootstrap file with commands to enable or disable plugin. + + */ + if ((error= process_options(argc, argv, operation)) || + (error= check_access()) || + (error= find_tool("mysqld" FN_EXEEXT, server_path)) || + (error= find_plugin(tp_path)) || + (error= build_bootstrap_file(operation, bootstrap))) + goto exit; + + /* Dump the bootstrap file if --verbose specified. */ + if (opt_verbose && ((error= dump_bootstrap_file(bootstrap)))) + goto exit; + + /* Start the server in bootstrap mode and execute bootstrap commands */ + error= bootstrap_server(server_path, bootstrap); + +exit: + /* Remove file */ + my_delete(bootstrap, MYF(0)); + if (opt_verbose && error == 0) + { + printf("# Operation succeeded.\n"); + } + + my_end(my_end_arg); + exit(error ? 1 : 0); + return 0; /* No compiler warnings */ +} + + +/** + Get a temporary file name. + + @param[out] filename The file name of the temporary file + @param[in] ext An extension for the file (optional) + + @retval int error = 1, success = 0 +*/ + +static int make_tempfile(char *filename, const char *ext) +{ + int fd= 0; + + if ((fd=create_temp_file(filename, NullS, ext, O_CREAT | O_WRONLY, + MYF(MY_WME))) < 0) + { + fprintf(stderr, "ERROR: Cannot generate temporary file. Error code: %d.\n", + fd); + return 1; + } + my_close(fd, MYF(0)); + return 0; +} + + +/** + Get the value of an option from a string read from my_print_defaults output. + + @param[in] line The line (string) read from the file + @param[in] item The option to search for (e.g. --datadir) + + @returns NULL if not found, string containing value if found +*/ + +static char *get_value(char *line, const char *item) +{ + char *destination= 0; + int item_len= (int)strlen(item); + int line_len = (int)strlen(line); + + if ((strncasecmp(line, item, item_len) == 0)) + { + int start= 0; + char *s= 0; + + s = line + item_len + 1; + destination= my_strndup(s, line_len - start, MYF(MY_FAE)); + destination[line_len - item_len - 2]= 0; + } + return destination; +} + + +/** + Run a command in a shell. + + This function will attempt to execute the command specified by using the + popen() method to open a shell and execute the command passed and store the + output in a result file. If the --verbose option was specified, it will open + the result file and print the contents to stdout. + + @param[in] cmd The command to execute. + @param[in] mode The mode for popen() (e.g. "r", "w", "rw") + + @return int error code or 0 for success. +*/ + +static int run_command(char* cmd, const char *mode) +{ + char buf[512]= {0}; + FILE *res_file; + int error; + + if (!(res_file= popen(cmd, mode))) + return -1; + + if (opt_verbose) + { + while (fgets(buf, sizeof(buf), res_file)) + { + fprintf(stdout, "%s", buf); + } + } + error= pclose(res_file); + return error; +} + + +/** + Get the default values from the my.cnf file. + + This method gets the default values for the following parameters: + + --datadir + --basedir + --plugin-dir + --plugin-ini + + These values are used if the user has not specified a value. + + @retval int error = 1, success = 0 +*/ + +static int get_default_values() +{ + char tool_path[FN_REFLEN]; + char defaults_cmd[FN_REFLEN]; + char defaults_file[FN_REFLEN]; + char line[FN_REFLEN]; + int error= 0; + int ret= 0; + FILE *file= 0; + + if ((error= find_tool("my_print_defaults" FN_EXEEXT, tool_path))) + goto exit; + else + { + if ((error= make_tempfile(defaults_file, "txt"))) + goto exit; + snprintf(defaults_cmd, sizeof(defaults_cmd), + "%s mysqld > %s", tool_path, defaults_file); + + /* Execute the command */ + if (opt_verbose > 1) + { + printf("# Command: %s\n", defaults_cmd); + } + error= run_command(defaults_cmd, "r"); + if (error) + { + fprintf(stderr, "ERROR: my_print_defaults failed. Error code: %d.\n", + ret); + goto exit; + } + /* Now open the file and read the defaults we want. */ + file= fopen(defaults_file, "r"); + while (fgets(line, FN_REFLEN, file) != NULL) + { + char *value= 0; + + if ((opt_datadir == 0) && ((value= get_value(line, "--datadir")))) + { + opt_datadir= my_strdup(value, MYF(MY_FAE)); + } + if ((opt_basedir == 0) && ((value= get_value(line, "--basedir")))) + { + opt_basedir= my_strdup(value, MYF(MY_FAE)); + } + if ((opt_plugin_dir == 0) && ((value= get_value(line, "--plugin_dir")))) + { + opt_plugin_dir= my_strdup(value, MYF(MY_FAE)); + } + if ((opt_plugin_ini == 0) && ((value= get_value(line, "--plugin_ini")))) + { + opt_plugin_ini= my_strdup(value, MYF(MY_FAE)); + } + } + } +exit: + if (file) + { + fclose(file); + /* Remove file */ + my_delete(defaults_file, MYF(0)); + } + return error; +} + + +/** + Print usage. +*/ + +static void usage(void) +{ + PRINT_VERSION; + puts("Copyright (c) 2011, Oracle and/or its affiliates. " + "All rights reserved.\n"); + puts("Enable or disable plugins."); + printf("\nUsage: %s [options] ENABLE|DISABLE\n\nOptions:\n", + my_progname); + my_print_help(my_long_options); + puts("\n"); +} + + +/** + Print the default values as read from the my.cnf file. + + This method displays the default values for the following parameters: + + --datadir + --basedir + --plugin-dir + --plugin-ini + +*/ + +static void print_default_values(void) +{ + printf("%s would have been started with the following arguments:\n", + my_progname); + get_default_values(); + if (opt_datadir) + { + printf("--datadir=%s ", opt_datadir); + } + if (opt_basedir) + { + printf("--basedir=%s ", opt_basedir); + } + if (opt_plugin_dir) + { + printf("--plugin_dir=%s ", opt_plugin_dir); + } + if (opt_plugin_ini) + { + printf("--plugin_ini=%s ", opt_plugin_ini); + } + printf("\n"); +} + + +/** + Process the arguments and identify an option and store its value. + + @param[in] optid The single character shortcut for the argument. + @param[in] my_option Structure of legal options. + @param[in] argument The argument value to process. +*/ + +static my_bool +get_one_option(int optid, + const struct my_option *opt __attribute__((unused)), + char *argument) +{ + switch(optid) { + case 'n': + opt_no_defaults++; + break; + case 'P': + opt_print_defaults++; + print_default_values(); + break; + case 'v': + opt_verbose++; + break; + case 'V': + PRINT_VERSION; + exit(0); + break; + case '?': + case 'I': /* Info */ + usage(); + exit(0); + case 'd': + opt_datadir= my_strdup(argument, MYF(MY_FAE)); + break; + case 'b': + opt_basedir= my_strdup(argument, MYF(MY_FAE)); + break; + case 'p': + opt_plugin_dir= my_strdup(argument, MYF(MY_FAE)); + break; + case 'i': + opt_plugin_ini= my_strdup(argument, MYF(MY_FAE)); + break; + } + return 0; +} + + +/** + Check to see if a file exists. + + @param[in] filename File to locate. + + @retval int file not found = 1, file found = 0 +*/ + +static int file_exists(char * filename) +{ + struct stat buf; + int i = stat (filename, &buf); + /* File found */ + if (i == 0) + { + return 1; + } + return 0; +} + + +/** + Search a specific path and sub directory for a file name. + + @param[in] base_path Original path to use. + @param[in] tool_name Name of the tool to locate. + @param[in] subdir The sub directory to search. + @param[out] tool_path If tool found, return complete path. + + @retval int error = 1, success = 0 +*/ + +static int search_dir(const char * base_path, const char *tool_name, + const char *subdir, char *tool_path) +{ + char new_path[FN_REFLEN]; + + strcpy(new_path, base_path); + strcat(new_path, subdir); + fn_format(new_path, new_path, "", tool_name, MY_UNPACK_FILENAME); + if (file_exists(new_path)) + { + strcpy(tool_path, new_path); + return 1; + } + return 0; +} + + +/** + Search known common paths and sub directories for a file name. + + @param[in] base_path Original path to use. + @param[in] tool_name Name of the tool to locate. + @param[out] tool_path If tool found, return complete path. + + @retval int error = 1, success = 0 +*/ + +static int search_paths(const char *base_path, const char *tool_name, + char *tool_path) +{ + int i= 0; + + static const char *paths[]= { + "", "/share/", "/scripts/", "/bin/", "/sbin/", "/libexec/", + "/mysql/", "/sql/", + }; + for (i = 0 ; i < (int)array_elements(paths); i++) + { + if (search_dir(base_path, tool_name, paths[i], tool_path)) + { + return 1; + } + } + return 0; +} + + +/** + Read a plugin data element + + This method takes as input a line from the plugin.ini file and splits it + into the st_plugin structure. + + @retval int error = 1, success = 0 +*/ + +static int read_plugin_data(char *line) +{ + const char delimiters[]= " ,"; + char *token, *cp; + int i= 0; + int error= 0; + + cp= my_strdup(line, MYF(MY_FAE)); + token= strtok (cp, delimiters); + if (token != NULL) + { + /* read name */ + plugin_data.name= my_strdup(token, MYF(MY_WME)); + /* read so_name */ + token = strtok(NULL, delimiters); + if (token == NULL) + { + return 1; + } + plugin_data.so_name= my_strdup(token, MYF(MY_WME)); + if (plugin_data.so_name == NULL) + { + error= 1; + goto exit; + } + /* Add proper file extension for soname */ + strcat((char *)plugin_data.so_name, FN_SOEXT); + /* read symbols */ + while (token != NULL) + { + token= strtok (NULL, delimiters); + if ((token != NULL) && (token[0] != '\n')) + { + plugin_data.symbols[i]= my_strdup(token, MYF(MY_WME)); + i++; + } + else + { + plugin_data.symbols[i]= NULL; + } + } + } + +exit: + if (error) + { + fprintf(stderr, "ERROR: Format incorrect for plugin config file.\n"); + } + + return error; +} + + +/** + Read the plugin ini file. + + This function attempts to read the plugin config file from the plugin_dir + path. If the file is not found, an error is generated. + + @retval int error = 1, success = 0 +*/ + +static int load_plugin_data(char *plugin_name) +{ + FILE *file_ptr; + char path[FN_REFLEN]; + char line[1024]; + int i= 0; + + if (opt_plugin_ini == 0) + { + fn_format(path, plugin_name, opt_plugin_dir, "", MYF(0)); + opt_plugin_ini= my_strdup(path, MYF(MY_FAE)); + } + if (!file_exists(opt_plugin_ini)) + { + goto error; + } + + file_ptr= fopen(opt_plugin_ini, "r"); + if (file_ptr == NULL) + { + goto error; + } + i = 0; + while (1) + { + char *res; + res= fgets(line, sizeof(line), file_ptr); + if (res == NULL) + { + break; + } + if (line[0] == '#') // skip comment lines + { + continue; + } + if (read_plugin_data(line)) + { + fclose(file_ptr); + goto error; + } + } + fclose(file_ptr); + return 0; + +error: + fprintf(stderr, "ERROR: Cannot read plugin config file %s.\n", + plugin_name); + return 1; +} + + +/** + Check the options for validity. + + This function checks the arguments for validity issuing the appropriate + error message if arguments are missing or invalid. On success, @operation + is set to either "ENABLE" or "DISABLE". + + @param[in] argc The number of arguments. + @param[in] argv The arguments. + @param[out] operation The operation chosen (enable|disable) + + @retval int error = 1, success = 0 +*/ + +static int check_options(int argc, char **argv, char *operation) +{ + int i= 0; // loop counter + int num_found= 0; // number of options found (shortcut loop) + char config_file[FN_REFLEN]; // configuration file name + char plugin_name[FN_REFLEN]; // plugin name + + /* Form prefix strings for the options. */ + const char *basedir_prefix = "--basedir="; + int basedir_len= strlen(basedir_prefix); + const char *datadir_prefix = "--datadir="; + int datadir_len= strlen(datadir_prefix); + const char *plugin_dir_prefix = "--plugin_dir="; + int plugin_dir_len= strlen(plugin_dir_prefix); + + strcpy(plugin_name, ""); + for (i = 0; i < argc && num_found < 5; i++) + { + + if (!argv[i]) + { + continue; + } + if ((strcasecmp(argv[i], "ENABLE") == 0) || + (strcasecmp(argv[i], "DISABLE") == 0)) + { + strcpy(operation, argv[i]); + num_found++; + } + else if ((strncasecmp(argv[i], basedir_prefix, basedir_len) == 0) && + !opt_basedir) + { + opt_basedir= my_strndup(argv[i]+basedir_len, + strlen(argv[i])-basedir_len, MYF(MY_FAE)); + num_found++; + } + else if ((strncasecmp(argv[i], datadir_prefix, datadir_len) == 0) && + !opt_datadir) + { + opt_datadir= my_strndup(argv[i]+datadir_len, + strlen(argv[i])-datadir_len, MYF(MY_FAE)); + num_found++; + } + else if ((strncasecmp(argv[i], plugin_dir_prefix, plugin_dir_len) == 0) && + !opt_plugin_dir) + { + opt_plugin_dir= my_strndup(argv[i]+plugin_dir_len, + strlen(argv[i])-plugin_dir_len, MYF(MY_FAE)); + num_found++; + } + /* read the plugin config file and check for match against argument */ + else + { + strcpy(plugin_name, argv[i]); + strcpy(config_file, argv[i]); + strcat(config_file, ".ini"); + } + } + + if (!opt_basedir) + { + fprintf(stderr, "ERROR: Missing --basedir option.\n"); + return 1; + } + + if (!opt_datadir) + { + fprintf(stderr, "ERROR: Missing --datadir option.\n"); + return 1; + } + + if (!opt_plugin_dir) + { + fprintf(stderr, "ERROR: Missing --plugin_dir option.\n"); + return 1; + } + /* If a plugin was specified, read the config file. */ + else if (strlen(plugin_name) > 0) + { + if (load_plugin_data(config_file)) + { + return 1; + } + if (strcasecmp(plugin_data.name, plugin_name) != 0) + { + fprintf(stderr, "ERROR: plugin name requested does not match config " + "file data.\n"); + return 1; + } + } + else + { + fprintf(stderr, "ERROR: No plugin specified.\n"); + return 1; + } + + if ((strlen(operation) == 0)) + { + fprintf(stderr, "ERROR: missing operation. Please specify either " + "' ENABLE' or ' DISABLE'.\n"); + return 1; + } + + return 0; +} + + +/** + Parse, execute, and verify command options. + + This method handles all of the option processing including the optional + features for displaying data (--print-defaults, --help ,etc.) that do not + result in an attempt to ENABLE or DISABLE of a plugin. + + @param[in] arc Count of arguments + @param[in] argv Array of arguments + @param[out] operation Operation (ENABLE or DISABLE) + + @retval int error = 1, success = 0, exit program = -1 +*/ + +static int process_options(int argc, char *argv[], char *operation) +{ + int error= 0; + int i= 0; + + /* Parse and execute command-line options */ + if ((error= handle_options(&argc, &argv, my_long_options, get_one_option))) + goto exit; + + /* If the print defaults option used, exit. */ + if (opt_print_defaults) + { + error= -1; + goto exit; + } + + /* + If the user did not specify the option to skip loading defaults from a + config file and the required options are not present or there was an error + generated when the defaults were read from the file, exit. + */ + if (!opt_no_defaults && ((error= get_default_values()))) + { + error= -1; + goto exit; + } + + /* + Check to ensure required options are present and validate the operation. + Note: this method also validates the plugin specified by attempting to + read a configuration file named .ini from the --plugin-dir + or --plugin-ini location if the --plugin-ini option presented. + */ + strcpy(operation, ""); + if ((error = check_options(argc, argv, operation))) + { + goto exit; + } + + /* Add a trailing directory separator if not present */ + i= (int)strlength(opt_basedir); + if (opt_basedir[i-1] != FN_LIBCHAR || opt_basedir[i-1] != FN_LIBCHAR2) + { + strcat(opt_basedir, FN_DIRSEP); + } + + if (opt_verbose) + { + printf("# basedir = %s\n", opt_basedir); + printf("# plugin_dir = %s\n", opt_plugin_dir); + printf("# datadir = %s\n", opt_datadir); + printf("# plugin_ini = %s\n", opt_plugin_ini); + } + +exit: + return error; +} + + +/** + Check access + + This method checks to ensure all of the directories (opt_basedir, + opt_plugin_dir, opt_datadir, and opt_plugin_ini) are accessible by + the user. + + @retval int error = 1, success = 0 +*/ + +static int check_access() +{ + int error= 0; + + if ((error= my_access(opt_basedir, F_OK))) + { + fprintf(stderr, "ERROR: Cannot access basedir at '%s'.\n", + opt_basedir); + goto exit; + } + if ((error= my_access(opt_plugin_dir, F_OK))) + { + fprintf(stderr, "ERROR: Cannot access plugin_dir at '%s'.\n", + opt_plugin_dir); + goto exit; + } + if ((error= my_access(opt_datadir, F_OK))) + { + fprintf(stderr, "ERROR: Cannot access datadir at '%s'.\n", + opt_datadir); + goto exit; + } + if ((error= my_access(opt_plugin_ini, F_OK))) + { + fprintf(stderr, "ERROR: Cannot access plugin config file at '%s'.\n", + opt_plugin_ini); + goto exit; + } + +exit: + return error; +} + + +/** + Locate the tool and form tool path. + + @param[in] tool_name Name of the tool to locate. + @param[out] tool_path If tool found, return complete path. + + @retval int error = 1, success = 0 +*/ + +static int find_tool(const char *tool_name, char *tool_path) +{ + int i= 0; + + const char *paths[]= { + opt_basedir, "/usr", "/usr/local/mysql", "/usr/sbin", "/usr/share", + }; + for (i= 0; i < (int)array_elements(paths); i++) + { + if (paths[i] && (search_paths(paths[i], tool_name, tool_path))) + goto found; + } + fprintf(stderr, "WARNING: Cannot find %s.\n", tool_name); + return 1; +found: + if (opt_verbose) + printf("# Found tool '%s' as '%s'.\n", tool_name, tool_path); + return 0; +} + + +/** + Find the plugin library. + + This function attempts to use the @c plugin_dir option passed on the + command line to locate the plugin. + + @param[out] tp_path The actual path to plugin with FN_SOEXT applied. + + @retval int error = 1, success = 0 +*/ + +static int find_plugin(char *tp_path) +{ + /* Check for existance of plugin */ + fn_format(tp_path, plugin_data.so_name, opt_plugin_dir, "", MYF(0)); + if (!file_exists(tp_path)) + { + fprintf(stderr, "ERROR: The plugin library is missing or in a different" + " location.\n"); + return 1; + } + else if (opt_verbose) + { + printf("# Found plugin '%s' as '%s'\n", plugin_data.name, tp_path); + } + return 0; +} + + +/** + Build the boostrap file. + + Create a new file and populate it with SQL commands to ENABLE or DISABLE + the plugin via INSERT and DELETE operations on the mysql.plugin table. + + param[in] operation The type of operation (ENABLE or DISABLE) + param[out] bootstrap A FILE* pointer + + @retval int error = 1, success = 0 +*/ + +static int build_bootstrap_file(char *operation, char *bootstrap) +{ + int error= 0; + FILE *file= 0; + + /* + Perform plugin operation : ENABLE or DISABLE + + The following creates a temporary bootstrap file and populates it with + the appropriate SQL commands for the operation. For ENABLE, INSERT + statements are created. For DISABLE, DELETE statements are created. The + values for these statements are derived from the plugin_data read from the + .ini configuration file. Once the file is built, a call to + mysqld is made in read only, bootstrap modes to read the SQL statements + and execute them. + */ + if ((error= make_tempfile(bootstrap, "sql"))) + { + /* Fail if we cannot create a temporary file for the bootstrap commands. */ + fprintf(stderr, "ERROR: Cannot create bootstrap file.\n"); + goto exit; + } + if ((file= fopen(bootstrap, "w+")) == NULL) + { + fprintf(stderr, "ERROR: Cannot open bootstrap file for writing.\n"); + error= 1; + goto exit; + } + if (strcasecmp(operation, "enable") == 0) + { + int i= 0; + fprintf(file, "INSERT IGNORE INTO mysql.plugin VALUES "); + for (i= 0; i < (int)array_elements(plugin_data.symbols); i++) + { + /* stop when we read the end of the symbol list - marked with NULL */ + if (plugin_data.symbols[i] == NULL) + { + break; + } + if (i > 0) + { + fprintf(file, ", "); + } + fprintf(file, "('%s','%s')", + plugin_data.symbols[i], plugin_data.so_name); + } + fprintf(file, ";\n"); + if (opt_verbose) + { + printf("# Enabling %s...\n", plugin_data.name); + } + } + else + { + fprintf(file, + "DELETE FROM mysql.plugin WHERE name = '%s';", plugin_data.name); + if (opt_verbose) + { + printf("# Disabling %s...\n", plugin_data.name); + } + } + +exit: + fclose(file); + return error; +} + + +/** + Dump bootstrap file. + + Read the contents of the bootstrap file and print it out. + + @param[in] bootstrap_file Name of bootstrap file to read + + @retval int error = 1, success = 0 +*/ + +static int dump_bootstrap_file(char *bootstrap_file) +{ + char *ret= 0; + int error= 0; + char query_str[512]; + FILE *file= 0; + + if ((file= fopen(bootstrap_file, "r")) == NULL) + { + fprintf(stderr, "ERROR: Cannot open bootstrap file for reading.\n"); + error= 1; + goto exit; + } + ret= fgets(query_str, 512, file); + if (ret == 0) + { + fprintf(stderr, "ERROR: Cannot read bootstrap file.\n"); + error= 1; + goto exit; + } + printf("# Query: %s", query_str); + +exit: + if (file) + { + fclose(file); + } + return error; +} + + +/** + Bootstrap the server + + Create a command line sequence to launch mysqld in bootstrap mode. This + will allow mysqld to launch a minimal server instance to read and + execute SQL commands from a file piped in (the boostrap file). We use + the --no-defaults option to skip reading values from the config file. + + The bootstrap mode skips loading of plugins and many other subsystems. + This allows the mysql_plugin tool to insert the correct rows into the + mysql.plugin table (for ENABLE) or delete the rows (for DISABLE). Once + the server is launched in normal mode, the plugin will be loaded + (for ENABLE) or not loaded (for DISABLE). In this way, we avoid the + (sometimes) complicated LOAD PLUGIN commands. + + @param[in] server_path Path to server executable + @param[in] bootstrap_file Name of bootstrap file to read + + @retval int error = 1, success = 0 +*/ + +static int bootstrap_server(char *server_path, char *bootstrap_file) +{ + char bootstrap_cmd[FN_REFLEN]; + int error= 0; + int ret= 0; + + snprintf(bootstrap_cmd, sizeof(bootstrap_cmd), + "%s --no-defaults --bootstrap --datadir=%s --basedir=%s" + " < %s", server_path, opt_datadir, opt_basedir, bootstrap_file); + + /* Execute the command */ + if (opt_verbose > 1) + { + printf("# Command: %s\n", bootstrap_cmd); + } + error= run_command(bootstrap_cmd, "r"); + if (error) + fprintf(stderr, + "ERROR: Unexpected result from bootstrap. Error code: %d.\n", + ret); + + return error; +} diff --git a/include/my_global.h b/include/my_global.h index f465d031b62..924c83cd951 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -603,6 +603,8 @@ typedef SOCKET_SIZE_TYPE size_socket; #define FN_LIBCHAR '\\' #define FN_LIBCHAR2 '/' #define FN_DIRSEP "/\\" /* Valid directory separators */ +#define FN_EXEEXT ".exe" +#define FN_SOEXT ".dll" #define FN_ROOTDIR "\\" #define FN_DEVCHAR ':' #define FN_NETWORK_DRIVES /* Uses \\ to indicate network drives */ @@ -611,6 +613,8 @@ typedef SOCKET_SIZE_TYPE size_socket; #define FN_LIBCHAR '/' #define FN_LIBCHAR2 '/' #define FN_DIRSEP "/" /* Valid directory separators */ +#define FN_EXEEXT "" +#define FN_SOEXT ".so" #define FN_ROOTDIR "/" #endif diff --git a/mysql-test/include/daemon_example.ini b/mysql-test/include/daemon_example.ini new file mode 100644 index 00000000000..2fbfca203ea --- /dev/null +++ b/mysql-test/include/daemon_example.ini @@ -0,0 +1,8 @@ +# +# Plugin initialization file. Format using comma-separated values: +# name, libname, symbol, [symbol, ] +# Note: trailing comma is required. +# +# File is used by mysql_plugin.test for testing missing library error. +# +daemon_example, libdaemon_example, daemon_example, diff --git a/mysql-test/include/daemon_example_bad_format.ini b/mysql-test/include/daemon_example_bad_format.ini new file mode 100644 index 00000000000..a08e50632c9 --- /dev/null +++ b/mysql-test/include/daemon_example_bad_format.ini @@ -0,0 +1,8 @@ +# +# Plugin initialization file. Format using comma-separated values: +# name, libname, symbol, [symbol, ] +# Note: trailing comma is required. +# +# File is used by mysql_plugin.test for testing bad library name. +# +daemon_BADNAME, libdaemon_example, daemon_example, diff --git a/mysql-test/include/plugin.defs b/mysql-test/include/plugin.defs index e07c603c8e5..6fbe4f68328 100644 --- a/mysql-test/include/plugin.defs +++ b/mysql-test/include/plugin.defs @@ -39,3 +39,4 @@ ha_archive storage/archive ARCHIVE_PLUGIN ha_blackhole storage/blackhole BLACKHOLE_PLUGIN ha_federated storage/federated FEDERATED_PLUGIN mypluglib plugin/fulltext SIMPLE_PARSER +libdaemon_example plugin/daemon_example DAEMONEXAMPLE diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 82d17a21f89..ec0ca74b1d1 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -168,6 +168,7 @@ my $opt_suites; our $opt_verbose= 0; # Verbose output, enable with --verbose our $exe_mysql; +our $exe_mysql_plugin; our $exe_mysqladmin; our $exe_mysqltest; our $exe_libtool; @@ -1950,6 +1951,7 @@ sub executable_setup () { # Look for the client binaries $exe_mysqladmin= mtr_exe_exists("$path_client_bindir/mysqladmin"); $exe_mysql= mtr_exe_exists("$path_client_bindir/mysql"); + $exe_mysql_plugin= mtr_exe_exists("$path_client_bindir/mysql_plugin"); $exe_mysql_embedded= mtr_exe_maybe_exists("$basedir/libmysqld/examples/mysql_embedded"); @@ -2357,6 +2359,7 @@ sub environment_setup { $ENV{'MYSQLADMIN'}= native_path($exe_mysqladmin); $ENV{'MYSQL_CLIENT_TEST'}= mysql_client_test_arguments(); $ENV{'EXE_MYSQL'}= $exe_mysql; + $ENV{'MYSQL_PLUGIN'}= $exe_mysql_plugin; $ENV{'MYSQL_EMBEDDED'}= $exe_mysql_embedded; # ---------------------------------------------------- diff --git a/mysql-test/r/mysql_plugin.result b/mysql-test/r/mysql_plugin.result new file mode 100644 index 00000000000..4aebf532db1 --- /dev/null +++ b/mysql-test/r/mysql_plugin.result @@ -0,0 +1,97 @@ +# +# Ensure the plugin isn't loaded. +# +SELECT * FROM mysql.plugin WHERE name = 'daemon_example' ORDER BY name; +name dl +# +# Enable the plugin... +# +# +# Ensure the plugin is now loaded. +# +SELECT * FROM mysql.plugin WHERE name = 'daemon_example' ORDER BY name; +name dl +daemon_example libdaemon_example.so +# +# Disable the plugin... +# +# +# Ensure the plugin isn't loaded. +# +SELECT * FROM mysql.plugin WHERE name = 'daemon_example' ORDER BY name; +name dl +# +# Attempt to load non-existant plugin +# +ERROR: Cannot read plugin config file NOT_THERE_AT_ALL.ini. +# +# Attempt to use non-existant plugin.ini file +# +ERROR: Cannot read plugin config file daemon_example.ini. +# +# Attempt to omit the plugin +# +ERROR: No plugin specified. +# +# Attempt to omit DISABLE|ENABLE +# +ERROR: missing operation. Please specify either ' ENABLE' or ' DISABLE'. +# +# Attempt to use bad paths - datadir +# +ERROR: Cannot access datadir at '/data_not_there/'. +# +# Attempt to use bad paths - basedir +# +ERROR: Cannot access basedir at '/basedir_not_there/'. +# +# Attempt to use bad paths - plugin_dir +# +ERROR: Cannot read plugin config file daemon_example.ini. +# +# Missing library +# +ERROR: The plugin library is missing or in a different location. +# +# Bad format for config file +# +ERROR: plugin name requested does not match config file data. +# +# Missing base_dir option +# +ERROR: Missing --basedir option. +# +# Missing data_dir option +# +ERROR: Missing --datadir option. +# +# Missing plugin_dir option +# +ERROR: Missing --plugin_dir option. +# +# Show the help. +# +mysql_plugin Ver 1.0.0 Distrib XX.XX.XX +Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + +Enable or disable plugins. + +Usage: mysql_plugin [options] ENABLE|DISABLE + +Options: + -?, --help Display this help and exit. + -b, --basedir=name The basedir for the server. + -d, --datadir=name The datadir for the server. + -p, --plugin-dir=name + The plugin dir for the server. + -i, --plugin-ini=name + Read plugin information from configuration file specified + instead of from /.ini. + -n, --no-defaults Do not read values from configuration file. + -P, --print-defaults + Show default values from configuration file. + -v, --verbose More verbose output; you can use this multiple times to + get even more verbose output. + -V, --version Output version information and exit. + + diff --git a/mysql-test/t/mysql_plugin-master.opt b/mysql-test/t/mysql_plugin-master.opt new file mode 100644 index 00000000000..061ca907902 --- /dev/null +++ b/mysql-test/t/mysql_plugin-master.opt @@ -0,0 +1 @@ +--plugin-dir=$DAEMONEXAMPLE_DIR diff --git a/mysql-test/t/mysql_plugin.test b/mysql-test/t/mysql_plugin.test new file mode 100644 index 00000000000..3257adfeb61 --- /dev/null +++ b/mysql-test/t/mysql_plugin.test @@ -0,0 +1,205 @@ +# +# Test mysql_plugin tool +# + +# +# Test currently does not run on Windows because in Windows build trees, +# mysqld.exe is not placed in the ./sql folder - it is placed either +# in ./sql/Debug or ./sql/release. If this behaviour is changed, this test +# can run on Windows. +# +--source include/not_windows.inc + +# Add the datadir, basedir, plugin_dir to the bootstrap command +let $MYSQLD_DATADIR= `select @@datadir`; +let $MYSQLD_BASEDIR= `select @@basedir`; +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=$DAEMONEXAMPLE_DIR; + +--echo # +--echo # Ensure the plugin isn't loaded. +--echo # +SELECT * FROM mysql.plugin WHERE name = 'daemon_example' ORDER BY name; + +--echo # +--echo # Enable the plugin... +--echo # +let $expect_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect; +# MTR will remove this file later, but this might be too late. +--error 0,1 +--remove_file $expect_file +--write_file $expect_file +wait +EOF +--shutdown_server 10 +--source include/wait_until_disconnected.inc + +# +# Enable the plugin +# +--exec $MYSQLD_BOOTSTRAP_CMD ENABLE daemon_example + +# +# Ensure enabling an enabled plugin doesn't fail +--exec $MYSQLD_BOOTSTRAP_CMD ENABLE daemon_example + +# +# Restart the server +# +--append_file $expect_file +restart +EOF +--enable_reconnect +--source include/wait_until_connected_again.inc + +--echo # +--echo # Ensure the plugin is now loaded. +--echo # +--replace_regex /\.dll/.so/ +SELECT * FROM mysql.plugin WHERE name = 'daemon_example' ORDER BY name; + +--echo # +--echo # Disable the plugin... +--echo # +# MTR will remove this file later, but this might be too late. +--error 0,1 +--remove_file $expect_file +--write_file $expect_file +wait +EOF +--shutdown_server 10 +--source include/wait_until_disconnected.inc + +# +# Disable the plugin +# +--exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example + +# +# Restart the server +# +--append_file $expect_file +restart +EOF +--enable_reconnect +--source include/wait_until_connected_again.inc + +--echo # +--echo # Ensure the plugin isn't loaded. +--echo # +SELECT * FROM mysql.plugin WHERE name = 'daemon_example' ORDER BY name; + +# +# Stop the server for error conditions +# +let $expect_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect; +# MTR will remove this file later, but this might be too late. +--error 0,1 +--remove_file $expect_file +--write_file $expect_file +wait +EOF +--shutdown_server 10 +--source include/wait_until_disconnected.inc + +--echo # +--echo # Attempt to load non-existant plugin +--echo # +--error 1,2,256 +--exec $MYSQLD_BOOTSTRAP_CMD DISABLE NOT_THERE_AT_ALL 2>&1 + +--echo # +--echo # Attempt to use non-existant plugin.ini file +--echo # +--error 1,2,256 +--exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example --plugin-ini=/NOT/THERE/pi.ini 2>&1 + +--echo # +--echo # Attempt to omit the plugin +--echo # +--error 1,2,256 +--exec $MYSQLD_BOOTSTRAP_CMD DISABLE 2>&1 + +--echo # +--echo # Attempt to omit DISABLE|ENABLE +--echo # +--error 1,2,256 +--exec $MYSQLD_BOOTSTRAP_CMD daemon_example 2>&1 + +--echo # +--echo # Attempt to use bad paths - datadir +--echo # +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=/data_not_there/ --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=$DAEMONEXAMPLE_DIR; +--error 1,2,256 +--exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 + +--echo # +--echo # Attempt to use bad paths - basedir +--echo # +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=/basedir_not_there/ --plugin-dir=$DAEMONEXAMPLE_DIR; +--error 1,2,256 +--exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 + +--echo # +--echo # Attempt to use bad paths - plugin_dir +--echo # +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=/plugin_not_there/; +--error 1,2,256 +--exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 + +--echo # +--echo # Missing library +--echo # +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=$MYSQL_TEST_DIR/include/; +--error 1,2,256 +--exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 + +--echo # +--echo # Bad format for config file +--echo # +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=$DAEMONEXAMPLE_DIR --plugin-ini=$MYSQL_TEST_DIR/include/daemon_example_bad_format.ini; +--error 1,2,256 +--exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 + +--echo # +--echo # Missing base_dir option +--echo # +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --plugin-dir=$DAEMONEXAMPLE_DIR; +--error 1,2,139,256 +--exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 + +--echo # +--echo # Missing data_dir option +--echo # +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=$DAEMONEXAMPLE_DIR; +--error 1,2,139,256 +--exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 + +--echo # +--echo # Missing plugin_dir option +--echo # +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR/sql; +--error 1,2,139,256 +--exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 + +--echo # +--echo # Show the help. +--echo # +replace_result $MYSQL_PLUGIN mysql_plugin; +--replace_regex /Distrib [0-9.]+/Distrib XX.XX.XX/ +--exec $MYSQL_PLUGIN --help + +# +# Restart the server +# +--append_file $expect_file +restart +EOF +--enable_reconnect +--source include/wait_until_connected_again.inc + +# +# Cleanup +# MTR will remove this file later, but this might be too late. +--error 0,1 +--remove_file $expect_file + diff --git a/plugin/daemon_example/CMakeLists.txt b/plugin/daemon_example/CMakeLists.txt index 60aa00687ba..1623c3025d7 100644 --- a/plugin/daemon_example/CMakeLists.txt +++ b/plugin/daemon_example/CMakeLists.txt @@ -15,3 +15,5 @@ MYSQL_ADD_PLUGIN(daemon_example daemon_example.cc MODULE_ONLY MODULE_OUTPUT_NAME "libdaemon_example") + +INSTALL(FILES daemon_example.ini DESTINATION ${INSTALL_PLUGINDIR}) diff --git a/plugin/daemon_example/daemon_example.ini b/plugin/daemon_example/daemon_example.ini new file mode 100644 index 00000000000..8d84456b635 --- /dev/null +++ b/plugin/daemon_example/daemon_example.ini @@ -0,0 +1,6 @@ +# +# Plugin initialization file. Format using comma-separated values: +# name, libname, symbol, [symbol, ] +# Note: trailing comma is required. +# +daemon_example, libdaemon_example, daemon_example, From 33a36da65a0e5403903bc85d1c85729b54c0968e Mon Sep 17 00:00:00 2001 From: Chuck Bell Date: Tue, 19 Jul 2011 10:24:14 -0400 Subject: [PATCH 03/29] WL#5710 : Enable and disable plugins (mysql_plugin) This patch changes the plugin configuration file format to make it easier to add new plugins and remove complexity. It also adds more information when plugin configuration file reads fail. --- client/mysql_plugin.c | 135 +++++++++-------------- mysql-test/r/mysql_plugin.result | 8 +- plugin/daemon_example/daemon_example.ini | 11 +- 3 files changed, 64 insertions(+), 90 deletions(-) diff --git a/client/mysql_plugin.c b/client/mysql_plugin.c index ca3da0086e4..d904d2dabcd 100644 --- a/client/mysql_plugin.c +++ b/client/mysql_plugin.c @@ -41,9 +41,9 @@ static char bootstrap[FN_REFLEN]; /* plugin struct */ struct st_plugin { - const char *name; /* plugin name */ - const char *so_name; /* plugin so (library) name */ - const char *symbols[16]; /* symbols to load */ + const char *name; /* plugin name */ + const char *so_name; /* plugin so (library) name */ + const char *components[16]; /* components to load */ } plugin_data; @@ -491,124 +491,95 @@ static int search_paths(const char *base_path, const char *tool_name, } -/** - Read a plugin data element - - This method takes as input a line from the plugin.ini file and splits it - into the st_plugin structure. - - @retval int error = 1, success = 0 -*/ - -static int read_plugin_data(char *line) -{ - const char delimiters[]= " ,"; - char *token, *cp; - int i= 0; - int error= 0; - - cp= my_strdup(line, MYF(MY_FAE)); - token= strtok (cp, delimiters); - if (token != NULL) - { - /* read name */ - plugin_data.name= my_strdup(token, MYF(MY_WME)); - /* read so_name */ - token = strtok(NULL, delimiters); - if (token == NULL) - { - return 1; - } - plugin_data.so_name= my_strdup(token, MYF(MY_WME)); - if (plugin_data.so_name == NULL) - { - error= 1; - goto exit; - } - /* Add proper file extension for soname */ - strcat((char *)plugin_data.so_name, FN_SOEXT); - /* read symbols */ - while (token != NULL) - { - token= strtok (NULL, delimiters); - if ((token != NULL) && (token[0] != '\n')) - { - plugin_data.symbols[i]= my_strdup(token, MYF(MY_WME)); - i++; - } - else - { - plugin_data.symbols[i]= NULL; - } - } - } - -exit: - if (error) - { - fprintf(stderr, "ERROR: Format incorrect for plugin config file.\n"); - } - - return error; -} - - /** Read the plugin ini file. This function attempts to read the plugin config file from the plugin_dir - path. If the file is not found, an error is generated. + path saving the data in the the st_plugin structure. If the file is not + found or the file cannot be read, an error is generated. @retval int error = 1, success = 0 */ -static int load_plugin_data(char *plugin_name) +static int load_plugin_data(char *plugin_name, char *config_file) { FILE *file_ptr; char path[FN_REFLEN]; char line[1024]; - int i= 0; + char *reason= 0; + char *res; + int i= -1; if (opt_plugin_ini == 0) { - fn_format(path, plugin_name, opt_plugin_dir, "", MYF(0)); + fn_format(path, config_file, opt_plugin_dir, "", MYF(0)); opt_plugin_ini= my_strdup(path, MYF(MY_FAE)); } if (!file_exists(opt_plugin_ini)) { + reason= "File does not exist."; goto error; } file_ptr= fopen(opt_plugin_ini, "r"); if (file_ptr == NULL) { + reason= "Cannot open file."; goto error; } - i = 0; - while (1) + + /* save name */ + plugin_data.name= my_strdup(plugin_name, MYF(MY_WME)); + + /* Read plugin components */ + while (i < 16) { - char *res; res= fgets(line, sizeof(line), file_ptr); + /* strip /n */ + if (line[strlen(line)-1] == '\n') + { + line[strlen(line)-1]= '\0'; + } if (res == NULL) { break; } - if (line[0] == '#') // skip comment lines + if ((line[0] == '#') || (line[0] == '\n')) // skip comment and blank lines { continue; } - if (read_plugin_data(line)) + if (i == -1) // if first pass, read this line as so_name { - fclose(file_ptr); - goto error; + /* save so_name */ + plugin_data.so_name= my_strdup(line, MYF(MY_WME)); + if (plugin_data.so_name == NULL) + { + reason= "Cannot read library name."; + goto error; + } + /* Add proper file extension for soname */ + strcat((char *)plugin_data.so_name, FN_SOEXT); + i++; + } + else + { + if (strlen(line) > 0) + { + plugin_data.components[i]= my_strdup(line, MYF(MY_WME)); + i++; + } + else + { + plugin_data.components[i]= NULL; + } } } fclose(file_ptr); return 0; error: - fprintf(stderr, "ERROR: Cannot read plugin config file %s.\n", - plugin_name); + fprintf(stderr, "ERROR: Cannot read plugin config file %s. %s\n", + plugin_name, reason); return 1; } @@ -706,7 +677,7 @@ static int check_options(int argc, char **argv, char *operation) /* If a plugin was specified, read the config file. */ else if (strlen(plugin_name) > 0) { - if (load_plugin_data(config_file)) + if (load_plugin_data(plugin_name, config_file)) { return 1; } @@ -954,10 +925,10 @@ static int build_bootstrap_file(char *operation, char *bootstrap) { int i= 0; fprintf(file, "INSERT IGNORE INTO mysql.plugin VALUES "); - for (i= 0; i < (int)array_elements(plugin_data.symbols); i++) + for (i= 0; i < (int)array_elements(plugin_data.components); i++) { /* stop when we read the end of the symbol list - marked with NULL */ - if (plugin_data.symbols[i] == NULL) + if (plugin_data.components[i] == NULL) { break; } @@ -966,7 +937,7 @@ static int build_bootstrap_file(char *operation, char *bootstrap) fprintf(file, ", "); } fprintf(file, "('%s','%s')", - plugin_data.symbols[i], plugin_data.so_name); + plugin_data.components[i], plugin_data.so_name); } fprintf(file, ";\n"); if (opt_verbose) diff --git a/mysql-test/r/mysql_plugin.result b/mysql-test/r/mysql_plugin.result index 4aebf532db1..f64d215978a 100644 --- a/mysql-test/r/mysql_plugin.result +++ b/mysql-test/r/mysql_plugin.result @@ -23,11 +23,11 @@ name dl # # Attempt to load non-existant plugin # -ERROR: Cannot read plugin config file NOT_THERE_AT_ALL.ini. +ERROR: Cannot read plugin config file NOT_THERE_AT_ALL. File does not exist. # # Attempt to use non-existant plugin.ini file # -ERROR: Cannot read plugin config file daemon_example.ini. +ERROR: Cannot read plugin config file daemon_example. File does not exist. # # Attempt to omit the plugin # @@ -47,7 +47,7 @@ ERROR: Cannot access basedir at '/basedir_not_there/'. # # Attempt to use bad paths - plugin_dir # -ERROR: Cannot read plugin config file daemon_example.ini. +ERROR: Cannot read plugin config file daemon_example. File does not exist. # # Missing library # @@ -55,7 +55,7 @@ ERROR: The plugin library is missing or in a different location. # # Bad format for config file # -ERROR: plugin name requested does not match config file data. +ERROR: The plugin library is missing or in a different location. # # Missing base_dir option # diff --git a/plugin/daemon_example/daemon_example.ini b/plugin/daemon_example/daemon_example.ini index 8d84456b635..7c6d4d1456e 100644 --- a/plugin/daemon_example/daemon_example.ini +++ b/plugin/daemon_example/daemon_example.ini @@ -1,6 +1,9 @@ # -# Plugin initialization file. Format using comma-separated values: -# name, libname, symbol, [symbol, ] -# Note: trailing comma is required. +# Plugin configuration file. Place the following on a separate line: # -daemon_example, libdaemon_example, daemon_example, +# library binary file name (without .so or .dll) +# component_name +# [component_name] - additional components in plugin +# +libdaemon_example +daemon_example From 9e112497785d5e7ce627c825aa39e2b17a5aa73e Mon Sep 17 00:00:00 2001 From: Chuck Bell Date: Tue, 19 Jul 2011 10:27:15 -0400 Subject: [PATCH 04/29] BUG#12707948 : mysql_plugin cannot run on Windows Patch fixes an issue with reading basedir on Windows. It fixes how the code interprets opt_basedir on Windows by adding the correct path separators and quotes for paths with spaces. BUG#12664302 : mysql_plugin cannot recognize the plugin config file Patch fixes an issue with reading a plugin config file. It adds more information to the error messages to ensure the user is using the options correctly. Also deals with paths with spacs on Windows. --- client/mysql_plugin.c | 136 +++++++++++++++--- mysql-test/include/daemon_example.ini | 8 -- .../include/daemon_example_bad_format.ini | 10 +- .../include/daemon_example_bad_soname.ini | 9 ++ mysql-test/r/mysql_plugin.result | 2 +- mysql-test/t/mysql_plugin.test | 2 +- 6 files changed, 136 insertions(+), 31 deletions(-) delete mode 100644 mysql-test/include/daemon_example.ini create mode 100644 mysql-test/include/daemon_example_bad_soname.ini diff --git a/client/mysql_plugin.c b/client/mysql_plugin.c index d904d2dabcd..2da9c2d6f16 100644 --- a/client/mysql_plugin.c +++ b/client/mysql_plugin.c @@ -228,6 +228,69 @@ static int run_command(char* cmd, const char *mode) } +#ifdef __WIN__ +/** + Check to see if there are spaces in a path. + + @param[in] path The Windows path to examine. + + @retval int spaces found = 1, no spaces = 0 +*/ +static int has_spaces(const char *path) +{ + if (strchr(path, ' ') != NULL) + return 1; + return 0; +} + + +/** + Convert a Unix path to a Windows path. + + @param[in] path The Windows path to examine. + + @returns string containing path with / changed to \\ +*/ +static char *convert_path(const char *argument) +{ + /* Convert / to \\ to make Windows paths */ + char *winfilename= my_strdup(argument, MYF(MY_FAE)); + char *pos, *end; + int length= strlen(argument); + + for (pos= winfilename, end= pos+length ; pos < end ; pos++) + { + if (*pos == '/') + { + *pos= '\\'; + } + } + return winfilename; +} + + +/** + Add quotes if the path has spaces in it. + + @param[in] path The Windows path to examine. + + @returns string containing excaped quotes if spaces found in path +*/ +static char *add_quotes(const char *path) +{ + char windows_cmd_friendly[FN_REFLEN]; + + if (has_spaces(path)) + snprintf(windows_cmd_friendly, sizeof(windows_cmd_friendly), + "\"%s\"", path); + else + snprintf(windows_cmd_friendly, sizeof(windows_cmd_friendly), + "%s", path); + return my_strdup(windows_cmd_friendly, MYF(MY_FAE)); +} +#endif + + /** Get the default values from the my.cnf file. @@ -259,11 +322,26 @@ static int get_default_values() { if ((error= make_tempfile(defaults_file, "txt"))) goto exit; + +#ifdef __WIN__ + { + char *format_str= 0; + + if (has_spaces(tool_path) || has_spaces(defaults_file)) + format_str = "\"%s mysqld > %s\""; + else + format_str = "%s mysqld > %s"; + + snprintf(defaults_cmd, sizeof(defaults_cmd), format_str, + add_quotes(tool_path), add_quotes(defaults_file)); + } +#else snprintf(defaults_cmd, sizeof(defaults_cmd), "%s mysqld > %s", tool_path, defaults_file); +#endif /* Execute the command */ - if (opt_verbose > 1) + if (opt_verbose) { printf("# Command: %s\n", defaults_cmd); } @@ -517,14 +595,14 @@ static int load_plugin_data(char *plugin_name, char *config_file) } if (!file_exists(opt_plugin_ini)) { - reason= "File does not exist."; + reason= (char *)"File does not exist."; goto error; } file_ptr= fopen(opt_plugin_ini, "r"); if (file_ptr == NULL) { - reason= "Cannot open file."; + reason= (char *)"Cannot open file."; goto error; } @@ -542,6 +620,12 @@ static int load_plugin_data(char *plugin_name, char *config_file) } if (res == NULL) { + if (i < 1) + { + reason= (char *)"Bad format in plugin configuration file."; + fclose(file_ptr); + goto error; + } break; } if ((line[0] == '#') || (line[0] == '\n')) // skip comment and blank lines @@ -552,11 +636,6 @@ static int load_plugin_data(char *plugin_name, char *config_file) { /* save so_name */ plugin_data.so_name= my_strdup(line, MYF(MY_WME)); - if (plugin_data.so_name == NULL) - { - reason= "Cannot read library name."; - goto error; - } /* Add proper file extension for soname */ strcat((char *)plugin_data.so_name, FN_SOEXT); i++; @@ -574,6 +653,7 @@ static int load_plugin_data(char *plugin_name, char *config_file) } } } + fclose(file_ptr); return 0; @@ -735,6 +815,19 @@ static int process_options(int argc, char *argv[], char *operation) goto exit; } + /* Add a trailing directory separator if not present */ + if (opt_basedir) + { + i= (int)strlength(opt_basedir); + if (opt_basedir[i-1] != FN_LIBCHAR || opt_basedir[i-1] != FN_LIBCHAR2) +#ifdef __WIN__ + if (opt_basedir[i-1] != '/') + strcat(opt_basedir, "//"); +#else + strcat(opt_basedir, FN_DIRSEP); +#endif + } + /* If the user did not specify the option to skip loading defaults from a config file and the required options are not present or there was an error @@ -758,13 +851,6 @@ static int process_options(int argc, char *argv[], char *operation) goto exit; } - /* Add a trailing directory separator if not present */ - i= (int)strlength(opt_basedir); - if (opt_basedir[i-1] != FN_LIBCHAR || opt_basedir[i-1] != FN_LIBCHAR2) - { - strcat(opt_basedir, FN_DIRSEP); - } - if (opt_verbose) { printf("# basedir = %s\n", opt_basedir); @@ -1029,12 +1115,30 @@ static int bootstrap_server(char *server_path, char *bootstrap_file) int error= 0; int ret= 0; +#ifdef __WIN__ + char *format_str= 0; + char *verbose_str= ""; + + if (opt_verbose) + strcat(verbose_str, "--console"); + if (has_spaces(opt_datadir) || has_spaces(opt_basedir) || + has_spaces(bootstrap_file)) + format_str= "\"%s %s --bootstrap --datadir=%s --basedir=%s < %s\""; + else + format_str= "%s %s --bootstrap --datadir=%s --basedir=%s < %s"; + + snprintf(bootstrap_cmd, sizeof(bootstrap_cmd), format_str, + add_quotes(convert_path(server_path)), verbose_str, + add_quotes(opt_datadir), add_quotes(opt_basedir), + add_quotes(bootstrap_file)); +#else snprintf(bootstrap_cmd, sizeof(bootstrap_cmd), "%s --no-defaults --bootstrap --datadir=%s --basedir=%s" " < %s", server_path, opt_datadir, opt_basedir, bootstrap_file); +#endif /* Execute the command */ - if (opt_verbose > 1) + if (opt_verbose) { printf("# Command: %s\n", bootstrap_cmd); } diff --git a/mysql-test/include/daemon_example.ini b/mysql-test/include/daemon_example.ini deleted file mode 100644 index 2fbfca203ea..00000000000 --- a/mysql-test/include/daemon_example.ini +++ /dev/null @@ -1,8 +0,0 @@ -# -# Plugin initialization file. Format using comma-separated values: -# name, libname, symbol, [symbol, ] -# Note: trailing comma is required. -# -# File is used by mysql_plugin.test for testing missing library error. -# -daemon_example, libdaemon_example, daemon_example, diff --git a/mysql-test/include/daemon_example_bad_format.ini b/mysql-test/include/daemon_example_bad_format.ini index a08e50632c9..8f880ef0ba0 100644 --- a/mysql-test/include/daemon_example_bad_format.ini +++ b/mysql-test/include/daemon_example_bad_format.ini @@ -1,8 +1,8 @@ # -# Plugin initialization file. Format using comma-separated values: -# name, libname, symbol, [symbol, ] -# Note: trailing comma is required. +# Plugin configuration file. Place the following on a separate line: # -# File is used by mysql_plugin.test for testing bad library name. +# library binary file name (without .so or .dll) +# component_name +# [component_name] - additional components in plugin # -daemon_BADNAME, libdaemon_example, daemon_example, +libdaemon_example diff --git a/mysql-test/include/daemon_example_bad_soname.ini b/mysql-test/include/daemon_example_bad_soname.ini new file mode 100644 index 00000000000..5f42b5a6214 --- /dev/null +++ b/mysql-test/include/daemon_example_bad_soname.ini @@ -0,0 +1,9 @@ +# +# Plugin configuration file. Place the following on a separate line: +# +# library binary file name (without .so or .dll) +# component_name +# [component_name] - additional components in plugin +# +libdaemon_BADNAME +daemon_BADNAME diff --git a/mysql-test/r/mysql_plugin.result b/mysql-test/r/mysql_plugin.result index f64d215978a..90924b6f4e3 100644 --- a/mysql-test/r/mysql_plugin.result +++ b/mysql-test/r/mysql_plugin.result @@ -55,7 +55,7 @@ ERROR: The plugin library is missing or in a different location. # # Bad format for config file # -ERROR: The plugin library is missing or in a different location. +ERROR: Cannot read plugin config file daemon_example. Bad format in plugin configuration file. # # Missing base_dir option # diff --git a/mysql-test/t/mysql_plugin.test b/mysql-test/t/mysql_plugin.test index 3257adfeb61..c2c0a403ab3 100644 --- a/mysql-test/t/mysql_plugin.test +++ b/mysql-test/t/mysql_plugin.test @@ -149,7 +149,7 @@ let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir= --echo # --echo # Missing library --echo # -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=$MYSQL_TEST_DIR/include/; +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=$DAEMONEXAMPLE_DIR --plugin-ini=$MYSQL_TEST_DIR/include/daemon_example_bad_soname.ini; --error 1,2,256 --exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 From e73ee41c6cb0fa13c6225c06d072f1d484085827 Mon Sep 17 00:00:00 2001 From: Chuck Bell Date: Tue, 19 Jul 2011 10:35:03 -0400 Subject: [PATCH 05/29] WL#5710 : Enable and disable plugins (mysql_plugin) This patch adds additional QA tests and enhances the mysql_plugin test to include more test cases. --- mysql-test/r/mysql_plugin.result | 3 +- mysql-test/t/mysql_plugin.test | 69 ++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/mysql-test/r/mysql_plugin.result b/mysql-test/r/mysql_plugin.result index 90924b6f4e3..1a39c86ab9a 100644 --- a/mysql-test/r/mysql_plugin.result +++ b/mysql-test/r/mysql_plugin.result @@ -71,7 +71,7 @@ ERROR: Missing --plugin_dir option. # # Show the help. # -mysql_plugin Ver 1.0.0 Distrib XX.XX.XX +mysql_plugin Ver V.V.VV Distrib XX.XX.XX Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. Enable or disable plugins. @@ -95,3 +95,4 @@ Options: -V, --version Output version information and exit. +mysql_plugin Ver V.V.VV Distrib XX.XX.XX diff --git a/mysql-test/t/mysql_plugin.test b/mysql-test/t/mysql_plugin.test index c2c0a403ab3..b90619774df 100644 --- a/mysql-test/t/mysql_plugin.test +++ b/mysql-test/t/mysql_plugin.test @@ -2,18 +2,34 @@ # Test mysql_plugin tool # -# -# Test currently does not run on Windows because in Windows build trees, -# mysqld.exe is not placed in the ./sql folder - it is placed either -# in ./sql/Debug or ./sql/release. If this behaviour is changed, this test -# can run on Windows. -# ---source include/not_windows.inc +--source include/not_embedded.inc # Add the datadir, basedir, plugin_dir to the bootstrap command let $MYSQLD_DATADIR= `select @@datadir`; -let $MYSQLD_BASEDIR= `select @@basedir`; -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=$DAEMONEXAMPLE_DIR; +let $MYSQL_BASEDIR= `select @@basedir`; + +# The mysql_plugin tool expects all executables in "basedir", so they will be copied to it. +# It also expects a directory structure like in the installed mysql version, so errmsg.sys +# will be copied to "basedir/share". The directories differ between Windows and Unix. +--disable_abort_on_error +if(`SELECT CONVERT(@@version_compile_os USING latin1) + IN ('Win32','Win64','Windows')`) +{ + let $MYSQLD_BASEDIR= $MYSQL_BASEDIR/sql/Debug; + --copy_file $MYSQL_BASEDIR/extra/Debug/my_print_defaults.exe $MYSQLD_BASEDIR/my_print_defaults.exe + --mkdir $MYSQLD_BASEDIR/share + --copy_file $MYSQL_BASEDIR/sql/share/english/errmsg.sys $MYSQLD_BASEDIR/share/errmsg.sys + --copy_file $MYSQL_BASEDIR/plugin/daemon_example/daemon_example.ini $DAEMONEXAMPLE_DIR/daemon_example.ini +} +if (`SELECT CONVERT(@@version_compile_os USING latin1) + NOT IN ('Win32','Win64','Windows')`) +{ + let $MYSQLD_BASEDIR= $MYSQL_BASEDIR/sql; + --copy_file $MYSQL_BASEDIR/extra/my_print_defaults $MYSQLD_BASEDIR/my_print_defaults + --copy_file $MYSQL_BASEDIR/sql/share/english/errmsg.sys $MYSQLD_BASEDIR/share/errmsg.sys +} + +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR --plugin-dir=$DAEMONEXAMPLE_DIR; --echo # --echo # Ensure the plugin isn't loaded. @@ -110,7 +126,7 @@ EOF --echo # --echo # Attempt to use non-existant plugin.ini file --echo # ---error 1,2,256 +--error 1,2,7,256 --exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example --plugin-ini=/NOT/THERE/pi.ini 2>&1 --echo # @@ -128,7 +144,7 @@ EOF --echo # --echo # Attempt to use bad paths - datadir --echo # -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=/data_not_there/ --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=$DAEMONEXAMPLE_DIR; +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=/data_not_there/ --basedir=$MYSQLD_BASEDIR --plugin-dir=$DAEMONEXAMPLE_DIR; --error 1,2,256 --exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 @@ -142,21 +158,21 @@ let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir= --echo # --echo # Attempt to use bad paths - plugin_dir --echo # -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=/plugin_not_there/; +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR --plugin-dir=/plugin_not_there/; --error 1,2,256 --exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 --echo # --echo # Missing library --echo # -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=$DAEMONEXAMPLE_DIR --plugin-ini=$MYSQL_TEST_DIR/include/daemon_example_bad_soname.ini; +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR --plugin-dir=$DAEMONEXAMPLE_DIR --plugin-ini=$MYSQL_TEST_DIR/include/daemon_example_bad_soname.ini; --error 1,2,256 --exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 --echo # --echo # Bad format for config file --echo # -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=$DAEMONEXAMPLE_DIR --plugin-ini=$MYSQL_TEST_DIR/include/daemon_example_bad_format.ini; +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR --plugin-dir=$DAEMONEXAMPLE_DIR --plugin-ini=$MYSQL_TEST_DIR/include/daemon_example_bad_format.ini; --error 1,2,256 --exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 @@ -170,14 +186,14 @@ let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --plugin-d --echo # --echo # Missing data_dir option --echo # -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=$DAEMONEXAMPLE_DIR; +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --basedir=$MYSQLD_BASEDIR --plugin-dir=$DAEMONEXAMPLE_DIR; --error 1,2,139,256 --exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 --echo # --echo # Missing plugin_dir option --echo # -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR/sql; +let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR; --error 1,2,139,256 --exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 @@ -185,9 +201,13 @@ let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir= --echo # Show the help. --echo # replace_result $MYSQL_PLUGIN mysql_plugin; ---replace_regex /Distrib [0-9.]+/Distrib XX.XX.XX/ +--replace_regex /Ver [0-9.]+ Distrib [0-9.]+/Ver V.V.VV Distrib XX.XX.XX/ --exec $MYSQL_PLUGIN --help +replace_result $MYSQL_PLUGIN mysql_plugin; +--replace_regex /Ver [0-9.]+ Distrib [0-9.]+/Ver V.V.VV Distrib XX.XX.XX/ +--exec $MYSQL_PLUGIN --version + # # Restart the server # @@ -203,3 +223,18 @@ EOF --error 0,1 --remove_file $expect_file +if(`SELECT CONVERT(@@version_compile_os USING latin1) + IN ('Win32','Win64','Windows')`) +{ + --remove_file $DAEMONEXAMPLE_DIR/daemon_example.ini + --remove_file $MYSQLD_BASEDIR/my_print_defaults.exe + --remove_file $MYSQLD_BASEDIR/share/errmsg.sys + --rmdir $MYSQLD_BASEDIR/share +} +if(`SELECT CONVERT(@@version_compile_os USING latin1) + NOT IN ('Win32','Win64','Windows')`) +{ + --remove_file $MYSQLD_BASEDIR/my_print_defaults + --remove_file $MYSQLD_BASEDIR/share/errmsg.sys +} +--enable_abort_on_error From 5ae1ff7001044c3ef222c3c781aab636bf618d35 Mon Sep 17 00:00:00 2001 From: Chuck Bell Date: Tue, 19 Jul 2011 15:55:03 -0400 Subject: [PATCH 06/29] WL#5710 : Enable and disable plugins This patch fixes line endings in the mysql_plugin.c file. --- client/mysql_plugin.c | 2304 ++++++++++++++++++++--------------------- 1 file changed, 1152 insertions(+), 1152 deletions(-) diff --git a/client/mysql_plugin.c b/client/mysql_plugin.c index 2da9c2d6f16..58ca69ed778 100644 --- a/client/mysql_plugin.c +++ b/client/mysql_plugin.c @@ -1,1152 +1,1152 @@ -/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include -#include -#include -#include -#include -#include -#include - - -#define SHOW_VERSION "1.0.0" -#define PRINT_VERSION do { printf("%s Ver %s Distrib %s\n", \ - my_progname, SHOW_VERSION, MYSQL_SERVER_VERSION); \ - } while(0) - -/* Global variables. */ -static uint my_end_arg= 0; -static uint opt_verbose=0; -static uint opt_no_defaults= 0; -static uint opt_print_defaults= 0; -static char *opt_datadir=0, *opt_basedir=0, - *opt_plugin_dir=0, *opt_plugin_ini=0; -static char bootstrap[FN_REFLEN]; - - -/* plugin struct */ -struct st_plugin -{ - const char *name; /* plugin name */ - const char *so_name; /* plugin so (library) name */ - const char *components[16]; /* components to load */ -} plugin_data; - - -/* Options */ -static struct my_option my_long_options[] = -{ - {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, - 0, 0, 0, 0, 0, 0}, - {"basedir", 'b', "The basedir for the server.", - 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"datadir", 'd', "The datadir for the server.", - 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"plugin-dir", 'p', "The plugin dir for the server.", - 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"plugin-ini", 'i', "Read plugin information from configuration file " - "specified instead of from /.ini.", - 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"no-defaults", 'n', "Do not read values from configuration file.", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"print-defaults", 'P', "Show default values from configuration file.", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"verbose", 'v', - "More verbose output; you can use this multiple times to get even more " - "verbose output.", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 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}, - {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} -}; - - -/* Methods */ -static int process_options(int argc, char *argv[], char *operation); -static int check_access(); -static int find_tool(const char *tool_name, char *tool_path); -static int find_plugin(char *tp_path); -static int build_bootstrap_file(char *operation, char *bootstrap); -static int dump_bootstrap_file(char *bootstrap_file); -static int bootstrap_server(char *server_path, char *bootstrap_file); - - -int main(int argc,char *argv[]) -{ - int error= 0; - char tp_path[FN_REFLEN]; - char server_path[FN_REFLEN]; - char operation[16]; - - MY_INIT(argv[0]); - plugin_data.name= 0; // initialize name - - /* - The following operations comprise the method for enabling or disabling - a plugin. We begin by processing the command options then check the - directories specified for --datadir, --basedir, --plugin-dir, and - --plugin-ini (if specified). If the directories are Ok, we then look - for the mysqld executable and the plugin soname. Finally, we build a - bootstrap command file for use in bootstraping the server. - - If any step fails, the method issues an error message and the tool exits. - - 1) Parse, execute, and verify command options. - 2) Check access to directories. - 3) Look for mysqld executable. - 4) Look for the plugin. - 5) Build a bootstrap file with commands to enable or disable plugin. - - */ - if ((error= process_options(argc, argv, operation)) || - (error= check_access()) || - (error= find_tool("mysqld" FN_EXEEXT, server_path)) || - (error= find_plugin(tp_path)) || - (error= build_bootstrap_file(operation, bootstrap))) - goto exit; - - /* Dump the bootstrap file if --verbose specified. */ - if (opt_verbose && ((error= dump_bootstrap_file(bootstrap)))) - goto exit; - - /* Start the server in bootstrap mode and execute bootstrap commands */ - error= bootstrap_server(server_path, bootstrap); - -exit: - /* Remove file */ - my_delete(bootstrap, MYF(0)); - if (opt_verbose && error == 0) - { - printf("# Operation succeeded.\n"); - } - - my_end(my_end_arg); - exit(error ? 1 : 0); - return 0; /* No compiler warnings */ -} - - -/** - Get a temporary file name. - - @param[out] filename The file name of the temporary file - @param[in] ext An extension for the file (optional) - - @retval int error = 1, success = 0 -*/ - -static int make_tempfile(char *filename, const char *ext) -{ - int fd= 0; - - if ((fd=create_temp_file(filename, NullS, ext, O_CREAT | O_WRONLY, - MYF(MY_WME))) < 0) - { - fprintf(stderr, "ERROR: Cannot generate temporary file. Error code: %d.\n", - fd); - return 1; - } - my_close(fd, MYF(0)); - return 0; -} - - -/** - Get the value of an option from a string read from my_print_defaults output. - - @param[in] line The line (string) read from the file - @param[in] item The option to search for (e.g. --datadir) - - @returns NULL if not found, string containing value if found -*/ - -static char *get_value(char *line, const char *item) -{ - char *destination= 0; - int item_len= (int)strlen(item); - int line_len = (int)strlen(line); - - if ((strncasecmp(line, item, item_len) == 0)) - { - int start= 0; - char *s= 0; - - s = line + item_len + 1; - destination= my_strndup(s, line_len - start, MYF(MY_FAE)); - destination[line_len - item_len - 2]= 0; - } - return destination; -} - - -/** - Run a command in a shell. - - This function will attempt to execute the command specified by using the - popen() method to open a shell and execute the command passed and store the - output in a result file. If the --verbose option was specified, it will open - the result file and print the contents to stdout. - - @param[in] cmd The command to execute. - @param[in] mode The mode for popen() (e.g. "r", "w", "rw") - - @return int error code or 0 for success. -*/ - -static int run_command(char* cmd, const char *mode) -{ - char buf[512]= {0}; - FILE *res_file; - int error; - - if (!(res_file= popen(cmd, mode))) - return -1; - - if (opt_verbose) - { - while (fgets(buf, sizeof(buf), res_file)) - { - fprintf(stdout, "%s", buf); - } - } - error= pclose(res_file); - return error; -} - - -#ifdef __WIN__ -/** - Check to see if there are spaces in a path. - - @param[in] path The Windows path to examine. - - @retval int spaces found = 1, no spaces = 0 -*/ -static int has_spaces(const char *path) -{ - if (strchr(path, ' ') != NULL) - return 1; - return 0; -} - - -/** - Convert a Unix path to a Windows path. - - @param[in] path The Windows path to examine. - - @returns string containing path with / changed to \\ -*/ -static char *convert_path(const char *argument) -{ - /* Convert / to \\ to make Windows paths */ - char *winfilename= my_strdup(argument, MYF(MY_FAE)); - char *pos, *end; - int length= strlen(argument); - - for (pos= winfilename, end= pos+length ; pos < end ; pos++) - { - if (*pos == '/') - { - *pos= '\\'; - } - } - return winfilename; -} - - -/** - Add quotes if the path has spaces in it. - - @param[in] path The Windows path to examine. - - @returns string containing excaped quotes if spaces found in path -*/ -static char *add_quotes(const char *path) -{ - char windows_cmd_friendly[FN_REFLEN]; - - if (has_spaces(path)) - snprintf(windows_cmd_friendly, sizeof(windows_cmd_friendly), - "\"%s\"", path); - else - snprintf(windows_cmd_friendly, sizeof(windows_cmd_friendly), - "%s", path); - return my_strdup(windows_cmd_friendly, MYF(MY_FAE)); -} -#endif - - -/** - Get the default values from the my.cnf file. - - This method gets the default values for the following parameters: - - --datadir - --basedir - --plugin-dir - --plugin-ini - - These values are used if the user has not specified a value. - - @retval int error = 1, success = 0 -*/ - -static int get_default_values() -{ - char tool_path[FN_REFLEN]; - char defaults_cmd[FN_REFLEN]; - char defaults_file[FN_REFLEN]; - char line[FN_REFLEN]; - int error= 0; - int ret= 0; - FILE *file= 0; - - if ((error= find_tool("my_print_defaults" FN_EXEEXT, tool_path))) - goto exit; - else - { - if ((error= make_tempfile(defaults_file, "txt"))) - goto exit; - -#ifdef __WIN__ - { - char *format_str= 0; - - if (has_spaces(tool_path) || has_spaces(defaults_file)) - format_str = "\"%s mysqld > %s\""; - else - format_str = "%s mysqld > %s"; - - snprintf(defaults_cmd, sizeof(defaults_cmd), format_str, - add_quotes(tool_path), add_quotes(defaults_file)); - } -#else - snprintf(defaults_cmd, sizeof(defaults_cmd), - "%s mysqld > %s", tool_path, defaults_file); -#endif - - /* Execute the command */ - if (opt_verbose) - { - printf("# Command: %s\n", defaults_cmd); - } - error= run_command(defaults_cmd, "r"); - if (error) - { - fprintf(stderr, "ERROR: my_print_defaults failed. Error code: %d.\n", - ret); - goto exit; - } - /* Now open the file and read the defaults we want. */ - file= fopen(defaults_file, "r"); - while (fgets(line, FN_REFLEN, file) != NULL) - { - char *value= 0; - - if ((opt_datadir == 0) && ((value= get_value(line, "--datadir")))) - { - opt_datadir= my_strdup(value, MYF(MY_FAE)); - } - if ((opt_basedir == 0) && ((value= get_value(line, "--basedir")))) - { - opt_basedir= my_strdup(value, MYF(MY_FAE)); - } - if ((opt_plugin_dir == 0) && ((value= get_value(line, "--plugin_dir")))) - { - opt_plugin_dir= my_strdup(value, MYF(MY_FAE)); - } - if ((opt_plugin_ini == 0) && ((value= get_value(line, "--plugin_ini")))) - { - opt_plugin_ini= my_strdup(value, MYF(MY_FAE)); - } - } - } -exit: - if (file) - { - fclose(file); - /* Remove file */ - my_delete(defaults_file, MYF(0)); - } - return error; -} - - -/** - Print usage. -*/ - -static void usage(void) -{ - PRINT_VERSION; - puts("Copyright (c) 2011, Oracle and/or its affiliates. " - "All rights reserved.\n"); - puts("Enable or disable plugins."); - printf("\nUsage: %s [options] ENABLE|DISABLE\n\nOptions:\n", - my_progname); - my_print_help(my_long_options); - puts("\n"); -} - - -/** - Print the default values as read from the my.cnf file. - - This method displays the default values for the following parameters: - - --datadir - --basedir - --plugin-dir - --plugin-ini - -*/ - -static void print_default_values(void) -{ - printf("%s would have been started with the following arguments:\n", - my_progname); - get_default_values(); - if (opt_datadir) - { - printf("--datadir=%s ", opt_datadir); - } - if (opt_basedir) - { - printf("--basedir=%s ", opt_basedir); - } - if (opt_plugin_dir) - { - printf("--plugin_dir=%s ", opt_plugin_dir); - } - if (opt_plugin_ini) - { - printf("--plugin_ini=%s ", opt_plugin_ini); - } - printf("\n"); -} - - -/** - Process the arguments and identify an option and store its value. - - @param[in] optid The single character shortcut for the argument. - @param[in] my_option Structure of legal options. - @param[in] argument The argument value to process. -*/ - -static my_bool -get_one_option(int optid, - const struct my_option *opt __attribute__((unused)), - char *argument) -{ - switch(optid) { - case 'n': - opt_no_defaults++; - break; - case 'P': - opt_print_defaults++; - print_default_values(); - break; - case 'v': - opt_verbose++; - break; - case 'V': - PRINT_VERSION; - exit(0); - break; - case '?': - case 'I': /* Info */ - usage(); - exit(0); - case 'd': - opt_datadir= my_strdup(argument, MYF(MY_FAE)); - break; - case 'b': - opt_basedir= my_strdup(argument, MYF(MY_FAE)); - break; - case 'p': - opt_plugin_dir= my_strdup(argument, MYF(MY_FAE)); - break; - case 'i': - opt_plugin_ini= my_strdup(argument, MYF(MY_FAE)); - break; - } - return 0; -} - - -/** - Check to see if a file exists. - - @param[in] filename File to locate. - - @retval int file not found = 1, file found = 0 -*/ - -static int file_exists(char * filename) -{ - struct stat buf; - int i = stat (filename, &buf); - /* File found */ - if (i == 0) - { - return 1; - } - return 0; -} - - -/** - Search a specific path and sub directory for a file name. - - @param[in] base_path Original path to use. - @param[in] tool_name Name of the tool to locate. - @param[in] subdir The sub directory to search. - @param[out] tool_path If tool found, return complete path. - - @retval int error = 1, success = 0 -*/ - -static int search_dir(const char * base_path, const char *tool_name, - const char *subdir, char *tool_path) -{ - char new_path[FN_REFLEN]; - - strcpy(new_path, base_path); - strcat(new_path, subdir); - fn_format(new_path, new_path, "", tool_name, MY_UNPACK_FILENAME); - if (file_exists(new_path)) - { - strcpy(tool_path, new_path); - return 1; - } - return 0; -} - - -/** - Search known common paths and sub directories for a file name. - - @param[in] base_path Original path to use. - @param[in] tool_name Name of the tool to locate. - @param[out] tool_path If tool found, return complete path. - - @retval int error = 1, success = 0 -*/ - -static int search_paths(const char *base_path, const char *tool_name, - char *tool_path) -{ - int i= 0; - - static const char *paths[]= { - "", "/share/", "/scripts/", "/bin/", "/sbin/", "/libexec/", - "/mysql/", "/sql/", - }; - for (i = 0 ; i < (int)array_elements(paths); i++) - { - if (search_dir(base_path, tool_name, paths[i], tool_path)) - { - return 1; - } - } - return 0; -} - - -/** - Read the plugin ini file. - - This function attempts to read the plugin config file from the plugin_dir - path saving the data in the the st_plugin structure. If the file is not - found or the file cannot be read, an error is generated. - - @retval int error = 1, success = 0 -*/ - -static int load_plugin_data(char *plugin_name, char *config_file) -{ - FILE *file_ptr; - char path[FN_REFLEN]; - char line[1024]; - char *reason= 0; - char *res; - int i= -1; - - if (opt_plugin_ini == 0) - { - fn_format(path, config_file, opt_plugin_dir, "", MYF(0)); - opt_plugin_ini= my_strdup(path, MYF(MY_FAE)); - } - if (!file_exists(opt_plugin_ini)) - { - reason= (char *)"File does not exist."; - goto error; - } - - file_ptr= fopen(opt_plugin_ini, "r"); - if (file_ptr == NULL) - { - reason= (char *)"Cannot open file."; - goto error; - } - - /* save name */ - plugin_data.name= my_strdup(plugin_name, MYF(MY_WME)); - - /* Read plugin components */ - while (i < 16) - { - res= fgets(line, sizeof(line), file_ptr); - /* strip /n */ - if (line[strlen(line)-1] == '\n') - { - line[strlen(line)-1]= '\0'; - } - if (res == NULL) - { - if (i < 1) - { - reason= (char *)"Bad format in plugin configuration file."; - fclose(file_ptr); - goto error; - } - break; - } - if ((line[0] == '#') || (line[0] == '\n')) // skip comment and blank lines - { - continue; - } - if (i == -1) // if first pass, read this line as so_name - { - /* save so_name */ - plugin_data.so_name= my_strdup(line, MYF(MY_WME)); - /* Add proper file extension for soname */ - strcat((char *)plugin_data.so_name, FN_SOEXT); - i++; - } - else - { - if (strlen(line) > 0) - { - plugin_data.components[i]= my_strdup(line, MYF(MY_WME)); - i++; - } - else - { - plugin_data.components[i]= NULL; - } - } - } - - fclose(file_ptr); - return 0; - -error: - fprintf(stderr, "ERROR: Cannot read plugin config file %s. %s\n", - plugin_name, reason); - return 1; -} - - -/** - Check the options for validity. - - This function checks the arguments for validity issuing the appropriate - error message if arguments are missing or invalid. On success, @operation - is set to either "ENABLE" or "DISABLE". - - @param[in] argc The number of arguments. - @param[in] argv The arguments. - @param[out] operation The operation chosen (enable|disable) - - @retval int error = 1, success = 0 -*/ - -static int check_options(int argc, char **argv, char *operation) -{ - int i= 0; // loop counter - int num_found= 0; // number of options found (shortcut loop) - char config_file[FN_REFLEN]; // configuration file name - char plugin_name[FN_REFLEN]; // plugin name - - /* Form prefix strings for the options. */ - const char *basedir_prefix = "--basedir="; - int basedir_len= strlen(basedir_prefix); - const char *datadir_prefix = "--datadir="; - int datadir_len= strlen(datadir_prefix); - const char *plugin_dir_prefix = "--plugin_dir="; - int plugin_dir_len= strlen(plugin_dir_prefix); - - strcpy(plugin_name, ""); - for (i = 0; i < argc && num_found < 5; i++) - { - - if (!argv[i]) - { - continue; - } - if ((strcasecmp(argv[i], "ENABLE") == 0) || - (strcasecmp(argv[i], "DISABLE") == 0)) - { - strcpy(operation, argv[i]); - num_found++; - } - else if ((strncasecmp(argv[i], basedir_prefix, basedir_len) == 0) && - !opt_basedir) - { - opt_basedir= my_strndup(argv[i]+basedir_len, - strlen(argv[i])-basedir_len, MYF(MY_FAE)); - num_found++; - } - else if ((strncasecmp(argv[i], datadir_prefix, datadir_len) == 0) && - !opt_datadir) - { - opt_datadir= my_strndup(argv[i]+datadir_len, - strlen(argv[i])-datadir_len, MYF(MY_FAE)); - num_found++; - } - else if ((strncasecmp(argv[i], plugin_dir_prefix, plugin_dir_len) == 0) && - !opt_plugin_dir) - { - opt_plugin_dir= my_strndup(argv[i]+plugin_dir_len, - strlen(argv[i])-plugin_dir_len, MYF(MY_FAE)); - num_found++; - } - /* read the plugin config file and check for match against argument */ - else - { - strcpy(plugin_name, argv[i]); - strcpy(config_file, argv[i]); - strcat(config_file, ".ini"); - } - } - - if (!opt_basedir) - { - fprintf(stderr, "ERROR: Missing --basedir option.\n"); - return 1; - } - - if (!opt_datadir) - { - fprintf(stderr, "ERROR: Missing --datadir option.\n"); - return 1; - } - - if (!opt_plugin_dir) - { - fprintf(stderr, "ERROR: Missing --plugin_dir option.\n"); - return 1; - } - /* If a plugin was specified, read the config file. */ - else if (strlen(plugin_name) > 0) - { - if (load_plugin_data(plugin_name, config_file)) - { - return 1; - } - if (strcasecmp(plugin_data.name, plugin_name) != 0) - { - fprintf(stderr, "ERROR: plugin name requested does not match config " - "file data.\n"); - return 1; - } - } - else - { - fprintf(stderr, "ERROR: No plugin specified.\n"); - return 1; - } - - if ((strlen(operation) == 0)) - { - fprintf(stderr, "ERROR: missing operation. Please specify either " - "' ENABLE' or ' DISABLE'.\n"); - return 1; - } - - return 0; -} - - -/** - Parse, execute, and verify command options. - - This method handles all of the option processing including the optional - features for displaying data (--print-defaults, --help ,etc.) that do not - result in an attempt to ENABLE or DISABLE of a plugin. - - @param[in] arc Count of arguments - @param[in] argv Array of arguments - @param[out] operation Operation (ENABLE or DISABLE) - - @retval int error = 1, success = 0, exit program = -1 -*/ - -static int process_options(int argc, char *argv[], char *operation) -{ - int error= 0; - int i= 0; - - /* Parse and execute command-line options */ - if ((error= handle_options(&argc, &argv, my_long_options, get_one_option))) - goto exit; - - /* If the print defaults option used, exit. */ - if (opt_print_defaults) - { - error= -1; - goto exit; - } - - /* Add a trailing directory separator if not present */ - if (opt_basedir) - { - i= (int)strlength(opt_basedir); - if (opt_basedir[i-1] != FN_LIBCHAR || opt_basedir[i-1] != FN_LIBCHAR2) -#ifdef __WIN__ - if (opt_basedir[i-1] != '/') - strcat(opt_basedir, "//"); -#else - strcat(opt_basedir, FN_DIRSEP); -#endif - } - - /* - If the user did not specify the option to skip loading defaults from a - config file and the required options are not present or there was an error - generated when the defaults were read from the file, exit. - */ - if (!opt_no_defaults && ((error= get_default_values()))) - { - error= -1; - goto exit; - } - - /* - Check to ensure required options are present and validate the operation. - Note: this method also validates the plugin specified by attempting to - read a configuration file named .ini from the --plugin-dir - or --plugin-ini location if the --plugin-ini option presented. - */ - strcpy(operation, ""); - if ((error = check_options(argc, argv, operation))) - { - goto exit; - } - - if (opt_verbose) - { - printf("# basedir = %s\n", opt_basedir); - printf("# plugin_dir = %s\n", opt_plugin_dir); - printf("# datadir = %s\n", opt_datadir); - printf("# plugin_ini = %s\n", opt_plugin_ini); - } - -exit: - return error; -} - - -/** - Check access - - This method checks to ensure all of the directories (opt_basedir, - opt_plugin_dir, opt_datadir, and opt_plugin_ini) are accessible by - the user. - - @retval int error = 1, success = 0 -*/ - -static int check_access() -{ - int error= 0; - - if ((error= my_access(opt_basedir, F_OK))) - { - fprintf(stderr, "ERROR: Cannot access basedir at '%s'.\n", - opt_basedir); - goto exit; - } - if ((error= my_access(opt_plugin_dir, F_OK))) - { - fprintf(stderr, "ERROR: Cannot access plugin_dir at '%s'.\n", - opt_plugin_dir); - goto exit; - } - if ((error= my_access(opt_datadir, F_OK))) - { - fprintf(stderr, "ERROR: Cannot access datadir at '%s'.\n", - opt_datadir); - goto exit; - } - if ((error= my_access(opt_plugin_ini, F_OK))) - { - fprintf(stderr, "ERROR: Cannot access plugin config file at '%s'.\n", - opt_plugin_ini); - goto exit; - } - -exit: - return error; -} - - -/** - Locate the tool and form tool path. - - @param[in] tool_name Name of the tool to locate. - @param[out] tool_path If tool found, return complete path. - - @retval int error = 1, success = 0 -*/ - -static int find_tool(const char *tool_name, char *tool_path) -{ - int i= 0; - - const char *paths[]= { - opt_basedir, "/usr", "/usr/local/mysql", "/usr/sbin", "/usr/share", - }; - for (i= 0; i < (int)array_elements(paths); i++) - { - if (paths[i] && (search_paths(paths[i], tool_name, tool_path))) - goto found; - } - fprintf(stderr, "WARNING: Cannot find %s.\n", tool_name); - return 1; -found: - if (opt_verbose) - printf("# Found tool '%s' as '%s'.\n", tool_name, tool_path); - return 0; -} - - -/** - Find the plugin library. - - This function attempts to use the @c plugin_dir option passed on the - command line to locate the plugin. - - @param[out] tp_path The actual path to plugin with FN_SOEXT applied. - - @retval int error = 1, success = 0 -*/ - -static int find_plugin(char *tp_path) -{ - /* Check for existance of plugin */ - fn_format(tp_path, plugin_data.so_name, opt_plugin_dir, "", MYF(0)); - if (!file_exists(tp_path)) - { - fprintf(stderr, "ERROR: The plugin library is missing or in a different" - " location.\n"); - return 1; - } - else if (opt_verbose) - { - printf("# Found plugin '%s' as '%s'\n", plugin_data.name, tp_path); - } - return 0; -} - - -/** - Build the boostrap file. - - Create a new file and populate it with SQL commands to ENABLE or DISABLE - the plugin via INSERT and DELETE operations on the mysql.plugin table. - - param[in] operation The type of operation (ENABLE or DISABLE) - param[out] bootstrap A FILE* pointer - - @retval int error = 1, success = 0 -*/ - -static int build_bootstrap_file(char *operation, char *bootstrap) -{ - int error= 0; - FILE *file= 0; - - /* - Perform plugin operation : ENABLE or DISABLE - - The following creates a temporary bootstrap file and populates it with - the appropriate SQL commands for the operation. For ENABLE, INSERT - statements are created. For DISABLE, DELETE statements are created. The - values for these statements are derived from the plugin_data read from the - .ini configuration file. Once the file is built, a call to - mysqld is made in read only, bootstrap modes to read the SQL statements - and execute them. - */ - if ((error= make_tempfile(bootstrap, "sql"))) - { - /* Fail if we cannot create a temporary file for the bootstrap commands. */ - fprintf(stderr, "ERROR: Cannot create bootstrap file.\n"); - goto exit; - } - if ((file= fopen(bootstrap, "w+")) == NULL) - { - fprintf(stderr, "ERROR: Cannot open bootstrap file for writing.\n"); - error= 1; - goto exit; - } - if (strcasecmp(operation, "enable") == 0) - { - int i= 0; - fprintf(file, "INSERT IGNORE INTO mysql.plugin VALUES "); - for (i= 0; i < (int)array_elements(plugin_data.components); i++) - { - /* stop when we read the end of the symbol list - marked with NULL */ - if (plugin_data.components[i] == NULL) - { - break; - } - if (i > 0) - { - fprintf(file, ", "); - } - fprintf(file, "('%s','%s')", - plugin_data.components[i], plugin_data.so_name); - } - fprintf(file, ";\n"); - if (opt_verbose) - { - printf("# Enabling %s...\n", plugin_data.name); - } - } - else - { - fprintf(file, - "DELETE FROM mysql.plugin WHERE name = '%s';", plugin_data.name); - if (opt_verbose) - { - printf("# Disabling %s...\n", plugin_data.name); - } - } - -exit: - fclose(file); - return error; -} - - -/** - Dump bootstrap file. - - Read the contents of the bootstrap file and print it out. - - @param[in] bootstrap_file Name of bootstrap file to read - - @retval int error = 1, success = 0 -*/ - -static int dump_bootstrap_file(char *bootstrap_file) -{ - char *ret= 0; - int error= 0; - char query_str[512]; - FILE *file= 0; - - if ((file= fopen(bootstrap_file, "r")) == NULL) - { - fprintf(stderr, "ERROR: Cannot open bootstrap file for reading.\n"); - error= 1; - goto exit; - } - ret= fgets(query_str, 512, file); - if (ret == 0) - { - fprintf(stderr, "ERROR: Cannot read bootstrap file.\n"); - error= 1; - goto exit; - } - printf("# Query: %s", query_str); - -exit: - if (file) - { - fclose(file); - } - return error; -} - - -/** - Bootstrap the server - - Create a command line sequence to launch mysqld in bootstrap mode. This - will allow mysqld to launch a minimal server instance to read and - execute SQL commands from a file piped in (the boostrap file). We use - the --no-defaults option to skip reading values from the config file. - - The bootstrap mode skips loading of plugins and many other subsystems. - This allows the mysql_plugin tool to insert the correct rows into the - mysql.plugin table (for ENABLE) or delete the rows (for DISABLE). Once - the server is launched in normal mode, the plugin will be loaded - (for ENABLE) or not loaded (for DISABLE). In this way, we avoid the - (sometimes) complicated LOAD PLUGIN commands. - - @param[in] server_path Path to server executable - @param[in] bootstrap_file Name of bootstrap file to read - - @retval int error = 1, success = 0 -*/ - -static int bootstrap_server(char *server_path, char *bootstrap_file) -{ - char bootstrap_cmd[FN_REFLEN]; - int error= 0; - int ret= 0; - -#ifdef __WIN__ - char *format_str= 0; - char *verbose_str= ""; - - if (opt_verbose) - strcat(verbose_str, "--console"); - if (has_spaces(opt_datadir) || has_spaces(opt_basedir) || - has_spaces(bootstrap_file)) - format_str= "\"%s %s --bootstrap --datadir=%s --basedir=%s < %s\""; - else - format_str= "%s %s --bootstrap --datadir=%s --basedir=%s < %s"; - - snprintf(bootstrap_cmd, sizeof(bootstrap_cmd), format_str, - add_quotes(convert_path(server_path)), verbose_str, - add_quotes(opt_datadir), add_quotes(opt_basedir), - add_quotes(bootstrap_file)); -#else - snprintf(bootstrap_cmd, sizeof(bootstrap_cmd), - "%s --no-defaults --bootstrap --datadir=%s --basedir=%s" - " < %s", server_path, opt_datadir, opt_basedir, bootstrap_file); -#endif - - /* Execute the command */ - if (opt_verbose) - { - printf("# Command: %s\n", bootstrap_cmd); - } - error= run_command(bootstrap_cmd, "r"); - if (error) - fprintf(stderr, - "ERROR: Unexpected result from bootstrap. Error code: %d.\n", - ret); - - return error; -} +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include + + +#define SHOW_VERSION "1.0.0" +#define PRINT_VERSION do { printf("%s Ver %s Distrib %s\n", \ + my_progname, SHOW_VERSION, MYSQL_SERVER_VERSION); \ + } while(0) + +/* Global variables. */ +static uint my_end_arg= 0; +static uint opt_verbose=0; +static uint opt_no_defaults= 0; +static uint opt_print_defaults= 0; +static char *opt_datadir=0, *opt_basedir=0, + *opt_plugin_dir=0, *opt_plugin_ini=0; +static char bootstrap[FN_REFLEN]; + + +/* plugin struct */ +struct st_plugin +{ + const char *name; /* plugin name */ + const char *so_name; /* plugin so (library) name */ + const char *components[16]; /* components to load */ +} plugin_data; + + +/* Options */ +static struct my_option my_long_options[] = +{ + {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, + 0, 0, 0, 0, 0, 0}, + {"basedir", 'b', "The basedir for the server.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"datadir", 'd', "The datadir for the server.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"plugin-dir", 'p', "The plugin dir for the server.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"plugin-ini", 'i', "Read plugin information from configuration file " + "specified instead of from /.ini.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"no-defaults", 'n', "Do not read values from configuration file.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"print-defaults", 'P', "Show default values from configuration file.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"verbose", 'v', + "More verbose output; you can use this multiple times to get even more " + "verbose output.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 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}, + {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} +}; + + +/* Methods */ +static int process_options(int argc, char *argv[], char *operation); +static int check_access(); +static int find_tool(const char *tool_name, char *tool_path); +static int find_plugin(char *tp_path); +static int build_bootstrap_file(char *operation, char *bootstrap); +static int dump_bootstrap_file(char *bootstrap_file); +static int bootstrap_server(char *server_path, char *bootstrap_file); + + +int main(int argc,char *argv[]) +{ + int error= 0; + char tp_path[FN_REFLEN]; + char server_path[FN_REFLEN]; + char operation[16]; + + MY_INIT(argv[0]); + plugin_data.name= 0; // initialize name + + /* + The following operations comprise the method for enabling or disabling + a plugin. We begin by processing the command options then check the + directories specified for --datadir, --basedir, --plugin-dir, and + --plugin-ini (if specified). If the directories are Ok, we then look + for the mysqld executable and the plugin soname. Finally, we build a + bootstrap command file for use in bootstraping the server. + + If any step fails, the method issues an error message and the tool exits. + + 1) Parse, execute, and verify command options. + 2) Check access to directories. + 3) Look for mysqld executable. + 4) Look for the plugin. + 5) Build a bootstrap file with commands to enable or disable plugin. + + */ + if ((error= process_options(argc, argv, operation)) || + (error= check_access()) || + (error= find_tool("mysqld" FN_EXEEXT, server_path)) || + (error= find_plugin(tp_path)) || + (error= build_bootstrap_file(operation, bootstrap))) + goto exit; + + /* Dump the bootstrap file if --verbose specified. */ + if (opt_verbose && ((error= dump_bootstrap_file(bootstrap)))) + goto exit; + + /* Start the server in bootstrap mode and execute bootstrap commands */ + error= bootstrap_server(server_path, bootstrap); + +exit: + /* Remove file */ + my_delete(bootstrap, MYF(0)); + if (opt_verbose && error == 0) + { + printf("# Operation succeeded.\n"); + } + + my_end(my_end_arg); + exit(error ? 1 : 0); + return 0; /* No compiler warnings */ +} + + +/** + Get a temporary file name. + + @param[out] filename The file name of the temporary file + @param[in] ext An extension for the file (optional) + + @retval int error = 1, success = 0 +*/ + +static int make_tempfile(char *filename, const char *ext) +{ + int fd= 0; + + if ((fd=create_temp_file(filename, NullS, ext, O_CREAT | O_WRONLY, + MYF(MY_WME))) < 0) + { + fprintf(stderr, "ERROR: Cannot generate temporary file. Error code: %d.\n", + fd); + return 1; + } + my_close(fd, MYF(0)); + return 0; +} + + +/** + Get the value of an option from a string read from my_print_defaults output. + + @param[in] line The line (string) read from the file + @param[in] item The option to search for (e.g. --datadir) + + @returns NULL if not found, string containing value if found +*/ + +static char *get_value(char *line, const char *item) +{ + char *destination= 0; + int item_len= (int)strlen(item); + int line_len = (int)strlen(line); + + if ((strncasecmp(line, item, item_len) == 0)) + { + int start= 0; + char *s= 0; + + s = line + item_len + 1; + destination= my_strndup(s, line_len - start, MYF(MY_FAE)); + destination[line_len - item_len - 2]= 0; + } + return destination; +} + + +/** + Run a command in a shell. + + This function will attempt to execute the command specified by using the + popen() method to open a shell and execute the command passed and store the + output in a result file. If the --verbose option was specified, it will open + the result file and print the contents to stdout. + + @param[in] cmd The command to execute. + @param[in] mode The mode for popen() (e.g. "r", "w", "rw") + + @return int error code or 0 for success. +*/ + +static int run_command(char* cmd, const char *mode) +{ + char buf[512]= {0}; + FILE *res_file; + int error; + + if (!(res_file= popen(cmd, mode))) + return -1; + + if (opt_verbose) + { + while (fgets(buf, sizeof(buf), res_file)) + { + fprintf(stdout, "%s", buf); + } + } + error= pclose(res_file); + return error; +} + + +#ifdef __WIN__ +/** + Check to see if there are spaces in a path. + + @param[in] path The Windows path to examine. + + @retval int spaces found = 1, no spaces = 0 +*/ +static int has_spaces(const char *path) +{ + if (strchr(path, ' ') != NULL) + return 1; + return 0; +} + + +/** + Convert a Unix path to a Windows path. + + @param[in] path The Windows path to examine. + + @returns string containing path with / changed to \\ +*/ +static char *convert_path(const char *argument) +{ + /* Convert / to \\ to make Windows paths */ + char *winfilename= my_strdup(argument, MYF(MY_FAE)); + char *pos, *end; + int length= strlen(argument); + + for (pos= winfilename, end= pos+length ; pos < end ; pos++) + { + if (*pos == '/') + { + *pos= '\\'; + } + } + return winfilename; +} + + +/** + Add quotes if the path has spaces in it. + + @param[in] path The Windows path to examine. + + @returns string containing excaped quotes if spaces found in path +*/ +static char *add_quotes(const char *path) +{ + char windows_cmd_friendly[FN_REFLEN]; + + if (has_spaces(path)) + snprintf(windows_cmd_friendly, sizeof(windows_cmd_friendly), + "\"%s\"", path); + else + snprintf(windows_cmd_friendly, sizeof(windows_cmd_friendly), + "%s", path); + return my_strdup(windows_cmd_friendly, MYF(MY_FAE)); +} +#endif + + +/** + Get the default values from the my.cnf file. + + This method gets the default values for the following parameters: + + --datadir + --basedir + --plugin-dir + --plugin-ini + + These values are used if the user has not specified a value. + + @retval int error = 1, success = 0 +*/ + +static int get_default_values() +{ + char tool_path[FN_REFLEN]; + char defaults_cmd[FN_REFLEN]; + char defaults_file[FN_REFLEN]; + char line[FN_REFLEN]; + int error= 0; + int ret= 0; + FILE *file= 0; + + if ((error= find_tool("my_print_defaults" FN_EXEEXT, tool_path))) + goto exit; + else + { + if ((error= make_tempfile(defaults_file, "txt"))) + goto exit; + +#ifdef __WIN__ + { + char *format_str= 0; + + if (has_spaces(tool_path) || has_spaces(defaults_file)) + format_str = "\"%s mysqld > %s\""; + else + format_str = "%s mysqld > %s"; + + snprintf(defaults_cmd, sizeof(defaults_cmd), format_str, + add_quotes(tool_path), add_quotes(defaults_file)); + } +#else + snprintf(defaults_cmd, sizeof(defaults_cmd), + "%s mysqld > %s", tool_path, defaults_file); +#endif + + /* Execute the command */ + if (opt_verbose) + { + printf("# Command: %s\n", defaults_cmd); + } + error= run_command(defaults_cmd, "r"); + if (error) + { + fprintf(stderr, "ERROR: my_print_defaults failed. Error code: %d.\n", + ret); + goto exit; + } + /* Now open the file and read the defaults we want. */ + file= fopen(defaults_file, "r"); + while (fgets(line, FN_REFLEN, file) != NULL) + { + char *value= 0; + + if ((opt_datadir == 0) && ((value= get_value(line, "--datadir")))) + { + opt_datadir= my_strdup(value, MYF(MY_FAE)); + } + if ((opt_basedir == 0) && ((value= get_value(line, "--basedir")))) + { + opt_basedir= my_strdup(value, MYF(MY_FAE)); + } + if ((opt_plugin_dir == 0) && ((value= get_value(line, "--plugin_dir")))) + { + opt_plugin_dir= my_strdup(value, MYF(MY_FAE)); + } + if ((opt_plugin_ini == 0) && ((value= get_value(line, "--plugin_ini")))) + { + opt_plugin_ini= my_strdup(value, MYF(MY_FAE)); + } + } + } +exit: + if (file) + { + fclose(file); + /* Remove file */ + my_delete(defaults_file, MYF(0)); + } + return error; +} + + +/** + Print usage. +*/ + +static void usage(void) +{ + PRINT_VERSION; + puts("Copyright (c) 2011, Oracle and/or its affiliates. " + "All rights reserved.\n"); + puts("Enable or disable plugins."); + printf("\nUsage: %s [options] ENABLE|DISABLE\n\nOptions:\n", + my_progname); + my_print_help(my_long_options); + puts("\n"); +} + + +/** + Print the default values as read from the my.cnf file. + + This method displays the default values for the following parameters: + + --datadir + --basedir + --plugin-dir + --plugin-ini + +*/ + +static void print_default_values(void) +{ + printf("%s would have been started with the following arguments:\n", + my_progname); + get_default_values(); + if (opt_datadir) + { + printf("--datadir=%s ", opt_datadir); + } + if (opt_basedir) + { + printf("--basedir=%s ", opt_basedir); + } + if (opt_plugin_dir) + { + printf("--plugin_dir=%s ", opt_plugin_dir); + } + if (opt_plugin_ini) + { + printf("--plugin_ini=%s ", opt_plugin_ini); + } + printf("\n"); +} + + +/** + Process the arguments and identify an option and store its value. + + @param[in] optid The single character shortcut for the argument. + @param[in] my_option Structure of legal options. + @param[in] argument The argument value to process. +*/ + +static my_bool +get_one_option(int optid, + const struct my_option *opt __attribute__((unused)), + char *argument) +{ + switch(optid) { + case 'n': + opt_no_defaults++; + break; + case 'P': + opt_print_defaults++; + print_default_values(); + break; + case 'v': + opt_verbose++; + break; + case 'V': + PRINT_VERSION; + exit(0); + break; + case '?': + case 'I': /* Info */ + usage(); + exit(0); + case 'd': + opt_datadir= my_strdup(argument, MYF(MY_FAE)); + break; + case 'b': + opt_basedir= my_strdup(argument, MYF(MY_FAE)); + break; + case 'p': + opt_plugin_dir= my_strdup(argument, MYF(MY_FAE)); + break; + case 'i': + opt_plugin_ini= my_strdup(argument, MYF(MY_FAE)); + break; + } + return 0; +} + + +/** + Check to see if a file exists. + + @param[in] filename File to locate. + + @retval int file not found = 1, file found = 0 +*/ + +static int file_exists(char * filename) +{ + struct stat buf; + int i = stat (filename, &buf); + /* File found */ + if (i == 0) + { + return 1; + } + return 0; +} + + +/** + Search a specific path and sub directory for a file name. + + @param[in] base_path Original path to use. + @param[in] tool_name Name of the tool to locate. + @param[in] subdir The sub directory to search. + @param[out] tool_path If tool found, return complete path. + + @retval int error = 1, success = 0 +*/ + +static int search_dir(const char * base_path, const char *tool_name, + const char *subdir, char *tool_path) +{ + char new_path[FN_REFLEN]; + + strcpy(new_path, base_path); + strcat(new_path, subdir); + fn_format(new_path, new_path, "", tool_name, MY_UNPACK_FILENAME); + if (file_exists(new_path)) + { + strcpy(tool_path, new_path); + return 1; + } + return 0; +} + + +/** + Search known common paths and sub directories for a file name. + + @param[in] base_path Original path to use. + @param[in] tool_name Name of the tool to locate. + @param[out] tool_path If tool found, return complete path. + + @retval int error = 1, success = 0 +*/ + +static int search_paths(const char *base_path, const char *tool_name, + char *tool_path) +{ + int i= 0; + + static const char *paths[]= { + "", "/share/", "/scripts/", "/bin/", "/sbin/", "/libexec/", + "/mysql/", "/sql/", + }; + for (i = 0 ; i < (int)array_elements(paths); i++) + { + if (search_dir(base_path, tool_name, paths[i], tool_path)) + { + return 1; + } + } + return 0; +} + + +/** + Read the plugin ini file. + + This function attempts to read the plugin config file from the plugin_dir + path saving the data in the the st_plugin structure. If the file is not + found or the file cannot be read, an error is generated. + + @retval int error = 1, success = 0 +*/ + +static int load_plugin_data(char *plugin_name, char *config_file) +{ + FILE *file_ptr; + char path[FN_REFLEN]; + char line[1024]; + char *reason= 0; + char *res; + int i= -1; + + if (opt_plugin_ini == 0) + { + fn_format(path, config_file, opt_plugin_dir, "", MYF(0)); + opt_plugin_ini= my_strdup(path, MYF(MY_FAE)); + } + if (!file_exists(opt_plugin_ini)) + { + reason= (char *)"File does not exist."; + goto error; + } + + file_ptr= fopen(opt_plugin_ini, "r"); + if (file_ptr == NULL) + { + reason= (char *)"Cannot open file."; + goto error; + } + + /* save name */ + plugin_data.name= my_strdup(plugin_name, MYF(MY_WME)); + + /* Read plugin components */ + while (i < 16) + { + res= fgets(line, sizeof(line), file_ptr); + /* strip /n */ + if (line[strlen(line)-1] == '\n') + { + line[strlen(line)-1]= '\0'; + } + if (res == NULL) + { + if (i < 1) + { + reason= (char *)"Bad format in plugin configuration file."; + fclose(file_ptr); + goto error; + } + break; + } + if ((line[0] == '#') || (line[0] == '\n')) // skip comment and blank lines + { + continue; + } + if (i == -1) // if first pass, read this line as so_name + { + /* save so_name */ + plugin_data.so_name= my_strdup(line, MYF(MY_WME)); + /* Add proper file extension for soname */ + strcat((char *)plugin_data.so_name, FN_SOEXT); + i++; + } + else + { + if (strlen(line) > 0) + { + plugin_data.components[i]= my_strdup(line, MYF(MY_WME)); + i++; + } + else + { + plugin_data.components[i]= NULL; + } + } + } + + fclose(file_ptr); + return 0; + +error: + fprintf(stderr, "ERROR: Cannot read plugin config file %s. %s\n", + plugin_name, reason); + return 1; +} + + +/** + Check the options for validity. + + This function checks the arguments for validity issuing the appropriate + error message if arguments are missing or invalid. On success, @operation + is set to either "ENABLE" or "DISABLE". + + @param[in] argc The number of arguments. + @param[in] argv The arguments. + @param[out] operation The operation chosen (enable|disable) + + @retval int error = 1, success = 0 +*/ + +static int check_options(int argc, char **argv, char *operation) +{ + int i= 0; // loop counter + int num_found= 0; // number of options found (shortcut loop) + char config_file[FN_REFLEN]; // configuration file name + char plugin_name[FN_REFLEN]; // plugin name + + /* Form prefix strings for the options. */ + const char *basedir_prefix = "--basedir="; + int basedir_len= strlen(basedir_prefix); + const char *datadir_prefix = "--datadir="; + int datadir_len= strlen(datadir_prefix); + const char *plugin_dir_prefix = "--plugin_dir="; + int plugin_dir_len= strlen(plugin_dir_prefix); + + strcpy(plugin_name, ""); + for (i = 0; i < argc && num_found < 5; i++) + { + + if (!argv[i]) + { + continue; + } + if ((strcasecmp(argv[i], "ENABLE") == 0) || + (strcasecmp(argv[i], "DISABLE") == 0)) + { + strcpy(operation, argv[i]); + num_found++; + } + else if ((strncasecmp(argv[i], basedir_prefix, basedir_len) == 0) && + !opt_basedir) + { + opt_basedir= my_strndup(argv[i]+basedir_len, + strlen(argv[i])-basedir_len, MYF(MY_FAE)); + num_found++; + } + else if ((strncasecmp(argv[i], datadir_prefix, datadir_len) == 0) && + !opt_datadir) + { + opt_datadir= my_strndup(argv[i]+datadir_len, + strlen(argv[i])-datadir_len, MYF(MY_FAE)); + num_found++; + } + else if ((strncasecmp(argv[i], plugin_dir_prefix, plugin_dir_len) == 0) && + !opt_plugin_dir) + { + opt_plugin_dir= my_strndup(argv[i]+plugin_dir_len, + strlen(argv[i])-plugin_dir_len, MYF(MY_FAE)); + num_found++; + } + /* read the plugin config file and check for match against argument */ + else + { + strcpy(plugin_name, argv[i]); + strcpy(config_file, argv[i]); + strcat(config_file, ".ini"); + } + } + + if (!opt_basedir) + { + fprintf(stderr, "ERROR: Missing --basedir option.\n"); + return 1; + } + + if (!opt_datadir) + { + fprintf(stderr, "ERROR: Missing --datadir option.\n"); + return 1; + } + + if (!opt_plugin_dir) + { + fprintf(stderr, "ERROR: Missing --plugin_dir option.\n"); + return 1; + } + /* If a plugin was specified, read the config file. */ + else if (strlen(plugin_name) > 0) + { + if (load_plugin_data(plugin_name, config_file)) + { + return 1; + } + if (strcasecmp(plugin_data.name, plugin_name) != 0) + { + fprintf(stderr, "ERROR: plugin name requested does not match config " + "file data.\n"); + return 1; + } + } + else + { + fprintf(stderr, "ERROR: No plugin specified.\n"); + return 1; + } + + if ((strlen(operation) == 0)) + { + fprintf(stderr, "ERROR: missing operation. Please specify either " + "' ENABLE' or ' DISABLE'.\n"); + return 1; + } + + return 0; +} + + +/** + Parse, execute, and verify command options. + + This method handles all of the option processing including the optional + features for displaying data (--print-defaults, --help ,etc.) that do not + result in an attempt to ENABLE or DISABLE of a plugin. + + @param[in] arc Count of arguments + @param[in] argv Array of arguments + @param[out] operation Operation (ENABLE or DISABLE) + + @retval int error = 1, success = 0, exit program = -1 +*/ + +static int process_options(int argc, char *argv[], char *operation) +{ + int error= 0; + int i= 0; + + /* Parse and execute command-line options */ + if ((error= handle_options(&argc, &argv, my_long_options, get_one_option))) + goto exit; + + /* If the print defaults option used, exit. */ + if (opt_print_defaults) + { + error= -1; + goto exit; + } + + /* Add a trailing directory separator if not present */ + if (opt_basedir) + { + i= (int)strlength(opt_basedir); + if (opt_basedir[i-1] != FN_LIBCHAR || opt_basedir[i-1] != FN_LIBCHAR2) +#ifdef __WIN__ + if (opt_basedir[i-1] != '/') + strcat(opt_basedir, "//"); +#else + strcat(opt_basedir, FN_DIRSEP); +#endif + } + + /* + If the user did not specify the option to skip loading defaults from a + config file and the required options are not present or there was an error + generated when the defaults were read from the file, exit. + */ + if (!opt_no_defaults && ((error= get_default_values()))) + { + error= -1; + goto exit; + } + + /* + Check to ensure required options are present and validate the operation. + Note: this method also validates the plugin specified by attempting to + read a configuration file named .ini from the --plugin-dir + or --plugin-ini location if the --plugin-ini option presented. + */ + strcpy(operation, ""); + if ((error = check_options(argc, argv, operation))) + { + goto exit; + } + + if (opt_verbose) + { + printf("# basedir = %s\n", opt_basedir); + printf("# plugin_dir = %s\n", opt_plugin_dir); + printf("# datadir = %s\n", opt_datadir); + printf("# plugin_ini = %s\n", opt_plugin_ini); + } + +exit: + return error; +} + + +/** + Check access + + This method checks to ensure all of the directories (opt_basedir, + opt_plugin_dir, opt_datadir, and opt_plugin_ini) are accessible by + the user. + + @retval int error = 1, success = 0 +*/ + +static int check_access() +{ + int error= 0; + + if ((error= my_access(opt_basedir, F_OK))) + { + fprintf(stderr, "ERROR: Cannot access basedir at '%s'.\n", + opt_basedir); + goto exit; + } + if ((error= my_access(opt_plugin_dir, F_OK))) + { + fprintf(stderr, "ERROR: Cannot access plugin_dir at '%s'.\n", + opt_plugin_dir); + goto exit; + } + if ((error= my_access(opt_datadir, F_OK))) + { + fprintf(stderr, "ERROR: Cannot access datadir at '%s'.\n", + opt_datadir); + goto exit; + } + if ((error= my_access(opt_plugin_ini, F_OK))) + { + fprintf(stderr, "ERROR: Cannot access plugin config file at '%s'.\n", + opt_plugin_ini); + goto exit; + } + +exit: + return error; +} + + +/** + Locate the tool and form tool path. + + @param[in] tool_name Name of the tool to locate. + @param[out] tool_path If tool found, return complete path. + + @retval int error = 1, success = 0 +*/ + +static int find_tool(const char *tool_name, char *tool_path) +{ + int i= 0; + + const char *paths[]= { + opt_basedir, "/usr", "/usr/local/mysql", "/usr/sbin", "/usr/share", + }; + for (i= 0; i < (int)array_elements(paths); i++) + { + if (paths[i] && (search_paths(paths[i], tool_name, tool_path))) + goto found; + } + fprintf(stderr, "WARNING: Cannot find %s.\n", tool_name); + return 1; +found: + if (opt_verbose) + printf("# Found tool '%s' as '%s'.\n", tool_name, tool_path); + return 0; +} + + +/** + Find the plugin library. + + This function attempts to use the @c plugin_dir option passed on the + command line to locate the plugin. + + @param[out] tp_path The actual path to plugin with FN_SOEXT applied. + + @retval int error = 1, success = 0 +*/ + +static int find_plugin(char *tp_path) +{ + /* Check for existance of plugin */ + fn_format(tp_path, plugin_data.so_name, opt_plugin_dir, "", MYF(0)); + if (!file_exists(tp_path)) + { + fprintf(stderr, "ERROR: The plugin library is missing or in a different" + " location.\n"); + return 1; + } + else if (opt_verbose) + { + printf("# Found plugin '%s' as '%s'\n", plugin_data.name, tp_path); + } + return 0; +} + + +/** + Build the boostrap file. + + Create a new file and populate it with SQL commands to ENABLE or DISABLE + the plugin via INSERT and DELETE operations on the mysql.plugin table. + + param[in] operation The type of operation (ENABLE or DISABLE) + param[out] bootstrap A FILE* pointer + + @retval int error = 1, success = 0 +*/ + +static int build_bootstrap_file(char *operation, char *bootstrap) +{ + int error= 0; + FILE *file= 0; + + /* + Perform plugin operation : ENABLE or DISABLE + + The following creates a temporary bootstrap file and populates it with + the appropriate SQL commands for the operation. For ENABLE, INSERT + statements are created. For DISABLE, DELETE statements are created. The + values for these statements are derived from the plugin_data read from the + .ini configuration file. Once the file is built, a call to + mysqld is made in read only, bootstrap modes to read the SQL statements + and execute them. + */ + if ((error= make_tempfile(bootstrap, "sql"))) + { + /* Fail if we cannot create a temporary file for the bootstrap commands. */ + fprintf(stderr, "ERROR: Cannot create bootstrap file.\n"); + goto exit; + } + if ((file= fopen(bootstrap, "w+")) == NULL) + { + fprintf(stderr, "ERROR: Cannot open bootstrap file for writing.\n"); + error= 1; + goto exit; + } + if (strcasecmp(operation, "enable") == 0) + { + int i= 0; + fprintf(file, "INSERT IGNORE INTO mysql.plugin VALUES "); + for (i= 0; i < (int)array_elements(plugin_data.components); i++) + { + /* stop when we read the end of the symbol list - marked with NULL */ + if (plugin_data.components[i] == NULL) + { + break; + } + if (i > 0) + { + fprintf(file, ", "); + } + fprintf(file, "('%s','%s')", + plugin_data.components[i], plugin_data.so_name); + } + fprintf(file, ";\n"); + if (opt_verbose) + { + printf("# Enabling %s...\n", plugin_data.name); + } + } + else + { + fprintf(file, + "DELETE FROM mysql.plugin WHERE name = '%s';", plugin_data.name); + if (opt_verbose) + { + printf("# Disabling %s...\n", plugin_data.name); + } + } + +exit: + fclose(file); + return error; +} + + +/** + Dump bootstrap file. + + Read the contents of the bootstrap file and print it out. + + @param[in] bootstrap_file Name of bootstrap file to read + + @retval int error = 1, success = 0 +*/ + +static int dump_bootstrap_file(char *bootstrap_file) +{ + char *ret= 0; + int error= 0; + char query_str[512]; + FILE *file= 0; + + if ((file= fopen(bootstrap_file, "r")) == NULL) + { + fprintf(stderr, "ERROR: Cannot open bootstrap file for reading.\n"); + error= 1; + goto exit; + } + ret= fgets(query_str, 512, file); + if (ret == 0) + { + fprintf(stderr, "ERROR: Cannot read bootstrap file.\n"); + error= 1; + goto exit; + } + printf("# Query: %s", query_str); + +exit: + if (file) + { + fclose(file); + } + return error; +} + + +/** + Bootstrap the server + + Create a command line sequence to launch mysqld in bootstrap mode. This + will allow mysqld to launch a minimal server instance to read and + execute SQL commands from a file piped in (the boostrap file). We use + the --no-defaults option to skip reading values from the config file. + + The bootstrap mode skips loading of plugins and many other subsystems. + This allows the mysql_plugin tool to insert the correct rows into the + mysql.plugin table (for ENABLE) or delete the rows (for DISABLE). Once + the server is launched in normal mode, the plugin will be loaded + (for ENABLE) or not loaded (for DISABLE). In this way, we avoid the + (sometimes) complicated LOAD PLUGIN commands. + + @param[in] server_path Path to server executable + @param[in] bootstrap_file Name of bootstrap file to read + + @retval int error = 1, success = 0 +*/ + +static int bootstrap_server(char *server_path, char *bootstrap_file) +{ + char bootstrap_cmd[FN_REFLEN]; + int error= 0; + int ret= 0; + +#ifdef __WIN__ + char *format_str= 0; + char *verbose_str= ""; + + if (opt_verbose) + strcat(verbose_str, "--console"); + if (has_spaces(opt_datadir) || has_spaces(opt_basedir) || + has_spaces(bootstrap_file)) + format_str= "\"%s %s --bootstrap --datadir=%s --basedir=%s < %s\""; + else + format_str= "%s %s --bootstrap --datadir=%s --basedir=%s < %s"; + + snprintf(bootstrap_cmd, sizeof(bootstrap_cmd), format_str, + add_quotes(convert_path(server_path)), verbose_str, + add_quotes(opt_datadir), add_quotes(opt_basedir), + add_quotes(bootstrap_file)); +#else + snprintf(bootstrap_cmd, sizeof(bootstrap_cmd), + "%s --no-defaults --bootstrap --datadir=%s --basedir=%s" + " < %s", server_path, opt_datadir, opt_basedir, bootstrap_file); +#endif + + /* Execute the command */ + if (opt_verbose) + { + printf("# Command: %s\n", bootstrap_cmd); + } + error= run_command(bootstrap_cmd, "r"); + if (error) + fprintf(stderr, + "ERROR: Unexpected result from bootstrap. Error code: %d.\n", + ret); + + return error; +} From 6f245ec94c9386fe8ce368ebe67c781dbcb8b809 Mon Sep 17 00:00:00 2001 From: Chuck Bell Date: Tue, 19 Jul 2011 16:27:55 -0400 Subject: [PATCH 07/29] WL#5710 - fixed (c) character causing compile issue. --- client/mysql_plugin.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/mysql_plugin.c b/client/mysql_plugin.c index 58ca69ed778..47646a3da81 100644 --- a/client/mysql_plugin.c +++ b/client/mysql_plugin.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. +/* + Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by From cf7c2d84014add29fb704a5cd1ddec6c3f36461c Mon Sep 17 00:00:00 2001 From: Chuck Bell Date: Tue, 19 Jul 2011 19:18:22 -0400 Subject: [PATCH 08/29] WL#5710 - remove more special characters in mysql_plugin.c. --- client/mysql_plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/mysql_plugin.c b/client/mysql_plugin.c index 47646a3da81..16ed3292c18 100644 --- a/client/mysql_plugin.c +++ b/client/mysql_plugin.c @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify From 9c99ae189c8e03e3b84241a205b87ab7f098b704 Mon Sep 17 00:00:00 2001 From: Chuck Bell Date: Thu, 21 Jul 2011 14:37:06 -0400 Subject: [PATCH 09/29] WL#5710 : enable and disable plugins Added additional search paths for finding tools. --- client/mysql_plugin.c | 1 + 1 file changed, 1 insertion(+) diff --git a/client/mysql_plugin.c b/client/mysql_plugin.c index 16ed3292c18..b59a4c6f219 100644 --- a/client/mysql_plugin.c +++ b/client/mysql_plugin.c @@ -924,6 +924,7 @@ static int find_tool(const char *tool_name, char *tool_path) const char *paths[]= { opt_basedir, "/usr", "/usr/local/mysql", "/usr/sbin", "/usr/share", + "/extra", "/extra/debug", "/extra/release" }; for (i= 0; i < (int)array_elements(paths); i++) { From 5d9d54519835d2709b690c969909cb6d81f86ada Mon Sep 17 00:00:00 2001 From: Chuck Bell Date: Thu, 21 Jul 2011 17:46:20 -0400 Subject: [PATCH 10/29] WL#5710 : enable and disable plugins Added additional search path of /bin for finding tools. --- client/mysql_plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/mysql_plugin.c b/client/mysql_plugin.c index b59a4c6f219..630800a4b39 100644 --- a/client/mysql_plugin.c +++ b/client/mysql_plugin.c @@ -924,7 +924,7 @@ static int find_tool(const char *tool_name, char *tool_path) const char *paths[]= { opt_basedir, "/usr", "/usr/local/mysql", "/usr/sbin", "/usr/share", - "/extra", "/extra/debug", "/extra/release" + "/extra", "/extra/debug", "/extra/release", "/bin" }; for (i= 0; i < (int)array_elements(paths); i++) { From 0b5b1dd197a7e93b6b2aea151b15a4aa563cc209 Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Fri, 22 Jul 2011 16:31:10 +0400 Subject: [PATCH 11/29] Fix for bug #11754210 - "45777: CHECK TABLE DOESN'T SHOW ALL PROBLEMS FOR MERGE TABLE COMPLIANCE IN 5.1". The problem was that CHECK/REPAIR TABLE for a MERGE table which had several children missing or in wrong engine reported only issue with the first such table in its result-set. While in 5.0 this statement returned the whole list of problematic tables. Ability to report problems for all children was lost during significant refactorings of MERGE code which were done as part of work on 5.1 and 5.5 releases. This patch restores status quo ante refactorings by changing code in such a way that: 1) Failure to open child table due to its absence during CHECK/ REPAIR TABLE for a MERGE table is not reported immediately when its absence is discovered in open_tables(). Instead handling/error reporting in such a situation is postponed until the moment when children are attached. 2) Code performing attaching of children no longer stops when it encounters first problem with one of the children during CHECK/REPAIR TABLE. Instead it continues iteration through the child list until all problems caused by child absence/ wrong engine are reported. Note that even after this change problem with mismatch of child/parent definition won't be reported if there is also another child missing, but this is how it was in 5.0 as well. mysql-test/r/merge.result: Added test case for bug #11754210 - "45777: CHECK TABLE DOESN'T SHOW ALL PROBLEMS FOR MERGE TABLE COMPLIANCE IN 5.1". Adjusted results of existing tests to the fact that CHECK/REPAIR TABLE statements now try to report problems about missing table/ wrong engine for all underlying tables, and to the fact that mismatch of parent/child definitions is always reported as an error and not a warning. mysql-test/t/merge.test: Added test case for bug #11754210 - "45777: CHECK TABLE DOESN'T SHOW ALL PROBLEMS FOR MERGE TABLE COMPLIANCE IN 5.1". sql/sql_base.cc: Changed code responsible for opening tables to ignore the fact that underlying tables of a MERGE table are missing, if this table is opened for CHECK/REPAIR TABLE. The absence of underlying tables in this case is now detected and appropriate error is reported at the point when child tables are attached. At this point we can produce full list of problematic child tables/errors to be returned as part of CHECK/REPAIR TABLE result-set. storage/myisammrg/ha_myisammrg.cc: Changed myisammrg_attach_children_callback() to handle new situation, when during CHECK/REPAIR TABLE we do not report error about missing child immediately when this fact is discovered during open_tables() but postpone error-reporting till the time when children are attached. Also this callback is now responsible for pushing an error mentioning problematic child table to the list of errors to be reported by CHECK/REPAIR TABLE statements. Finally, since now myrg_attach_children() no longer relies on return value from callback to determine the end of the children list, callback no longer needs to set my_errno value and can be simplified. Changed myrg_print_wrong_table() to always report a problem with child table as an error and not as a warning. This makes reporting for different types of issues with child tables more consistent and compatible with 5.0 behavior. storage/myisammrg/myrg_open.c: Changed code in myrg_attach_children() not to abort on the first problem with a child table when attaching children to parent MERGE table during CHECK/REPAIR TABLE statement execution. This allows CHECK/REPAIR TABLE to report problems about absence/wrong engine for all underlying tables as part of their result-set. --- mysql-test/r/merge.result | 46 +++++++++++++++--- mysql-test/t/merge.test | 27 ++++++++++- sql/sql_base.cc | 77 +++++++++++++++++++++++++++++++ storage/myisammrg/ha_myisammrg.cc | 70 +++++++++++++++++++--------- storage/myisammrg/myrg_open.c | 24 ++++++---- 5 files changed, 208 insertions(+), 36 deletions(-) diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index f668761cb16..6e4e6c5a443 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -904,7 +904,8 @@ SELECT * FROM tm1; ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist CHECK TABLE tm1; Table Op Msg_type Msg_text -test.tm1 check Error Table 'test.t1' doesn't exist +test.tm1 check Error Table 'test.t1' is differently defined or of non-MyISAM type or doesn't exist +test.tm1 check Error Table 'test.t2' is differently defined or of non-MyISAM type or doesn't exist test.tm1 check Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist test.tm1 check error Corrupt CREATE TABLE t1(a INT); @@ -912,7 +913,7 @@ SELECT * FROM tm1; ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist CHECK TABLE tm1; Table Op Msg_type Msg_text -test.tm1 check Error Table 'test.t2' doesn't exist +test.tm1 check Error Table 'test.t2' is differently defined or of non-MyISAM type or doesn't exist test.tm1 check Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist test.tm1 check error Corrupt CREATE TABLE t2(a BLOB); @@ -920,7 +921,7 @@ SELECT * FROM tm1; ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist CHECK TABLE tm1; Table Op Msg_type Msg_text -test.tm1 check Warning Table 'test.t2' is differently defined or of non-MyISAM type or doesn't exist +test.tm1 check Error Table 'test.t2' is differently defined or of non-MyISAM type or doesn't exist test.tm1 check Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist test.tm1 check error Corrupt ALTER TABLE t2 MODIFY a INT; @@ -3634,7 +3635,7 @@ test.t1 analyze Error Unable to open underlying table which is differently defin test.t1 analyze error Corrupt CHECK TABLE t1; Table Op Msg_type Msg_text -test.t1 check Error Table 'test.t_not_exists' doesn't exist +test.t1 check Error Table 'test.t_not_exists' is differently defined or of non-MyISAM type or doesn't exist test.t1 check Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist test.t1 check error Corrupt CHECKSUM TABLE t1; @@ -3650,7 +3651,7 @@ test.t1 optimize Error Unable to open underlying table which is differently defi test.t1 optimize error Corrupt REPAIR TABLE t1; Table Op Msg_type Msg_text -test.t1 repair Error Table 'test.t_not_exists' doesn't exist +test.t1 repair Error Table 'test.t_not_exists' is differently defined or of non-MyISAM type or doesn't exist test.t1 repair Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist test.t1 repair error Corrupt REPAIR TABLE t1 USE_FRM; @@ -3676,4 +3677,37 @@ ALTER TABLE t1 engine=myisam; ERROR HY000: Table 't1' was locked with a READ lock and can't be updated UNLOCK TABLES; DROP TABLE m1, t1; -End of 6.0 tests +# +# Test for bug #11754210 - "45777: CHECK TABLE DOESN'T SHOW ALL +# PROBLEMS FOR MERGE TABLE COMPLIANCE IN 5.1" +# +drop tables if exists t1, t2, t3, t4, m1; +create table t1(id int) engine=myisam; +create view t3 as select 1 as id; +create table t4(id int) engine=memory; +create table m1(id int) engine=merge union=(t1,t2,t3,t4); +select * from m1; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +# The below CHECK and REPAIR TABLE statements should +# report all problems with underlying tables: +# - absence of 't2', +# - missing base table for 't3', +# - wrong engine of 't4'. +check table m1; +Table Op Msg_type Msg_text +test.m1 check Error Table 'test.t2' is differently defined or of non-MyISAM type or doesn't exist +test.m1 check Error Table 'test.t3' is differently defined or of non-MyISAM type or doesn't exist +test.m1 check Error Table 'test.t4' is differently defined or of non-MyISAM type or doesn't exist +test.m1 check Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +test.m1 check error Corrupt +repair table m1; +Table Op Msg_type Msg_text +test.m1 repair Error Table 'test.t2' is differently defined or of non-MyISAM type or doesn't exist +test.m1 repair Error Table 'test.t3' is differently defined or of non-MyISAM type or doesn't exist +test.m1 repair Error Table 'test.t4' is differently defined or of non-MyISAM type or doesn't exist +test.m1 repair Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +test.m1 repair error Corrupt +# Clean-up. +drop tables m1, t1, t4; +drop view t3; +End of 5.5 tests diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index 07839257a15..74110962299 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -2798,7 +2798,32 @@ UNLOCK TABLES; DROP TABLE m1, t1; ---echo End of 6.0 tests +--echo # +--echo # Test for bug #11754210 - "45777: CHECK TABLE DOESN'T SHOW ALL +--echo # PROBLEMS FOR MERGE TABLE COMPLIANCE IN 5.1" +--echo # +--disable_warnings +drop tables if exists t1, t2, t3, t4, m1; +--enable_warnings +create table t1(id int) engine=myisam; +create view t3 as select 1 as id; +create table t4(id int) engine=memory; +create table m1(id int) engine=merge union=(t1,t2,t3,t4); +--error ER_WRONG_MRG_TABLE +select * from m1; +--echo # The below CHECK and REPAIR TABLE statements should +--echo # report all problems with underlying tables: +--echo # - absence of 't2', +--echo # - missing base table for 't3', +--echo # - wrong engine of 't4'. +check table m1; +repair table m1; +--echo # Clean-up. +drop tables m1, t1, t4; +drop view t3; + + +--echo End of 5.5 tests --disable_result_log --disable_query_log diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 43ad400ce64..d47525c3d4e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -89,6 +89,69 @@ bool No_such_table_error_handler::safely_trapped_errors() return ((m_handled_errors > 0) && (m_unhandled_errors == 0)); } + +/** + This internal handler is used to trap ER_NO_SUCH_TABLE and + ER_WRONG_MRG_TABLE errors during CHECK/REPAIR TABLE for MERGE + tables. +*/ + +class Repair_mrg_table_error_handler : public Internal_error_handler +{ +public: + Repair_mrg_table_error_handler() + : m_handled_errors(false), m_unhandled_errors(false) + {} + + bool handle_condition(THD *thd, + uint sql_errno, + const char* sqlstate, + MYSQL_ERROR::enum_warning_level level, + const char* msg, + MYSQL_ERROR ** cond_hdl); + + /** + Returns TRUE if there were ER_NO_SUCH_/WRONG_MRG_TABLE and there + were no unhandled errors. FALSE otherwise. + */ + bool safely_trapped_errors() + { + /* + Check for m_handled_errors is here for extra safety. + It can be useful in situation when call to open_table() + fails because some error which was suppressed by another + error handler (e.g. in case of MDL deadlock which we + decided to solve by back-off and retry). + */ + return (m_handled_errors && (! m_unhandled_errors)); + } + +private: + bool m_handled_errors; + bool m_unhandled_errors; +}; + + +bool +Repair_mrg_table_error_handler::handle_condition(THD *, + uint sql_errno, + const char*, + MYSQL_ERROR::enum_warning_level level, + const char*, + MYSQL_ERROR ** cond_hdl) +{ + *cond_hdl= NULL; + if (sql_errno == ER_NO_SUCH_TABLE || sql_errno == ER_WRONG_MRG_TABLE) + { + m_handled_errors= true; + return TRUE; + } + + m_unhandled_errors= true; + return FALSE; +} + + /** @defgroup Data_Dictionary Data Dictionary @{ @@ -4377,6 +4440,20 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables, thd->pop_internal_handler(); safe_to_ignore_table= no_such_table_handler.safely_trapped_errors(); } + else if (tables->parent_l && (thd->open_options & HA_OPEN_FOR_REPAIR)) + { + /* + Also fail silently for underlying tables of a MERGE table if this + table is opened for CHECK/REPAIR TABLE statement. This is needed + to provide complete list of problematic underlying tables in + CHECK/REPAIR TABLE output. + */ + Repair_mrg_table_error_handler repair_mrg_table_handler; + thd->push_internal_handler(&repair_mrg_table_handler); + error= open_table(thd, tables, new_frm_mem, ot_ctx); + thd->pop_internal_handler(); + safe_to_ignore_table= repair_mrg_table_handler.safely_trapped_errors(); + } else error= open_table(thd, tables, new_frm_mem, ot_ctx); diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index 233f63ba680..c76e9ee0bfe 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -159,9 +159,14 @@ extern "C" void myrg_print_wrong_table(const char *table_name) buf[db.length]= '.'; memcpy(buf + db.length + 1, name.str, name.length); buf[db.length + name.length + 1]= 0; - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ADMIN_WRONG_MRG_TABLE, ER(ER_ADMIN_WRONG_MRG_TABLE), - buf); + /* + Push an error to be reported as part of CHECK/REPAIR result-set. + Note that calling my_error() from handler is a hack which is kept + here to avoid refactoring. Normally engines should report errors + through return value which will be interpreted by caller using + handler::print_error() call. + */ + my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0), buf); } @@ -593,8 +598,7 @@ public: @return pointer to open MyISAM table structure @retval !=NULL OK, returning pointer - @retval NULL, my_errno == 0 Ok, no more child tables - @retval NULL, my_errno != 0 error + @retval NULL, Error. @detail This function retrieves the MyISAM table handle from the @@ -614,16 +618,32 @@ extern "C" MI_INFO *myisammrg_attach_children_callback(void *callback_param) MI_INFO *myisam= NULL; DBUG_ENTER("myisammrg_attach_children_callback"); - if (!child_l) - { - DBUG_PRINT("myrg", ("No more children to attach")); - my_errno= 0; /* Ok, no more child tables. */ - goto end; - } + /* + Number of children in the list and MYRG_INFO::tables_count, + which is used by caller of this function, should always match. + */ + DBUG_ASSERT(child_l); + child= child_l->table; /* Prepare for next child. */ param->next(); + /* + When MERGE table is opened for CHECK or REPAIR TABLE statements, + failure to open any of underlying tables is ignored until this moment + (this is needed to provide complete list of the problematic underlying + tables in CHECK/REPAIR TABLE output). + Here we detect such a situation and report an appropriate error. + */ + if (! child) + { + DBUG_PRINT("error", ("failed to open underlying table '%s'.'%s'", + child_l->db, child_l->table_name)); + /* This should only happen inside of CHECK/REPAIR TABLE. */ + DBUG_ASSERT(current_thd->open_options & HA_OPEN_FOR_REPAIR); + goto end; + } + /* Do a quick compatibility check. The table def version is set when the table share is created. The child def version is copied @@ -653,7 +673,6 @@ extern "C" MI_INFO *myisammrg_attach_children_callback(void *callback_param) { DBUG_PRINT("error", ("temporary table mismatch parent: %d child: %d", parent->s->tmp_table, child->s->tmp_table)); - my_errno= HA_ERR_WRONG_MRG_TABLE_DEF; goto end; } @@ -664,12 +683,27 @@ extern "C" MI_INFO *myisammrg_attach_children_callback(void *callback_param) DBUG_PRINT("error", ("no MyISAM handle for child table: '%s'.'%s' 0x%lx", child->s->db.str, child->s->table_name.str, (long) child)); - my_errno= HA_ERR_WRONG_MRG_TABLE_DEF; } - DBUG_PRINT("myrg", ("MyISAM handle: 0x%lx my_errno: %d", - my_errno ? 0L : (long) myisam, my_errno)); + + DBUG_PRINT("myrg", ("MyISAM handle: 0x%lx", (long) myisam)); end: + + if (!myisam && + (current_thd->open_options & HA_OPEN_FOR_REPAIR)) + { + char buf[2*NAME_LEN + 1 + 1]; + strxnmov(buf, sizeof(buf) - 1, child_l->db, ".", child_l->table_name, NULL); + /* + Push an error to be reported as part of CHECK/REPAIR result-set. + Note that calling my_error() from handler is a hack which is kept + here to avoid refactoring. Normally engines should report errors + through return value which will be interpreted by caller using + handler::print_error() call. + */ + my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0), buf); + } + DBUG_RETURN(myisam); } @@ -783,12 +817,6 @@ int ha_myisammrg::attach_children(void) /* Must call this with children list in place. */ DBUG_ASSERT(this->table->pos_in_table_list->next_global == this->children_l); - /* - 'my_errno' is set by myisammrg_attach_children_callback() in - case of an error. - */ - my_errno= 0; - if (myrg_attach_children(this->file, this->test_if_locked | current_thd->open_options, myisammrg_attach_children_callback, ¶m, diff --git a/storage/myisammrg/myrg_open.c b/storage/myisammrg/myrg_open.c index 533e5d13cdd..d51036fc69d 100644 --- a/storage/myisammrg/myrg_open.c +++ b/storage/myisammrg/myrg_open.c @@ -385,6 +385,7 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, uint UNINIT_VAR(key_parts); uint min_keys; my_bool bad_children= FALSE; + my_bool first_child= TRUE; DBUG_ENTER("myrg_attach_children"); DBUG_PRINT("myrg", ("handle_locking: %d", handle_locking)); @@ -399,16 +400,26 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, errpos= 0; file_offset= 0; min_keys= 0; - child_nr= 0; - while ((myisam= (*callback)(callback_param))) + for (child_nr= 0; child_nr < m_info->tables; child_nr++) { + if (! (myisam= (*callback)(callback_param))) + { + if (handle_locking & HA_OPEN_FOR_REPAIR) + { + /* An appropriate error should've been already pushed by callback. */ + bad_children= TRUE; + continue; + } + goto bad_children; + } + DBUG_PRINT("myrg", ("child_nr: %u table: '%s'", child_nr, myisam->filename)); - DBUG_ASSERT(child_nr < m_info->tables); /* Special handling when the first child is attached. */ - if (!child_nr) + if (first_child) { + first_child= FALSE; m_info->reclength= myisam->s->base.reclength; min_keys= myisam->s->base.keys; key_parts= myisam->s->base.key_parts; @@ -456,14 +467,11 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, for (idx= 0; idx < key_parts; idx++) m_info->rec_per_key_part[idx]+= (myisam->s->state.rec_per_key_part[idx] / m_info->tables); - child_nr++; } if (bad_children) goto bad_children; - /* Note: callback() resets my_errno, so it is safe to check it here */ - if (my_errno == HA_ERR_WRONG_MRG_TABLE_DEF) - goto err; + if (sizeof(my_off_t) == 4 && file_offset > (ulonglong) (ulong) ~0L) { my_errno= HA_ERR_RECORD_FILE_FULL; From 111570dfd6e1d0abc062f63045b371074ac2b630 Mon Sep 17 00:00:00 2001 From: Alfranio Correia Date: Sun, 24 Jul 2011 12:26:58 +0100 Subject: [PATCH 12/29] Post-push fix for BUG#11809016. In 5.5, REFRESH SLAVE is used as an alias for RESET SLAVE and was removed in 5.6. Reseting a slave through REFRESH SLAVE was causing errors in the valgrind platform since reset_slave_info was undefined. To fix the problem, we have set reset_slave_info while calling REFRESH SLAVE. --- sql/sql_yacc.yy | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 90a55471792..ba1b9e43713 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11306,7 +11306,10 @@ flush_option: | STATUS_SYM { Lex->type|= REFRESH_STATUS; } | SLAVE - { Lex->type|= REFRESH_SLAVE; } + { + Lex->type|= REFRESH_SLAVE; + Lex->reset_slave_info.all= false; + } | MASTER_SYM { Lex->type|= REFRESH_MASTER; } | DES_KEY_FILE From 6c4da66d8307bf139051864fb9def7159e72e2b8 Mon Sep 17 00:00:00 2001 From: Chuck Bell Date: Mon, 25 Jul 2011 15:30:24 -0400 Subject: [PATCH 13/29] WL#5710: enable and disable plugins This patch corrects a problem found in pushbuild with the stat() method on some platforms. The code was changed to use my_stat() instead. Also adds mysql_plugin to the mysql.spec.sh file for packaging. --- client/mysql_plugin.c | 16 ++++++++-------- support-files/mysql.spec.sh | 6 ++++++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/client/mysql_plugin.c b/client/mysql_plugin.c index 630800a4b39..dd41693161d 100644 --- a/client/mysql_plugin.c +++ b/client/mysql_plugin.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -501,14 +501,13 @@ get_one_option(int optid, static int file_exists(char * filename) { - struct stat buf; - int i = stat (filename, &buf); - /* File found */ - if (i == 0) + MY_STAT stat_arg; + + if (!my_stat(filename, &stat_arg, MYF(0))) { - return 1; + return 0; } - return 0; + return 1; } @@ -924,7 +923,8 @@ static int find_tool(const char *tool_name, char *tool_path) const char *paths[]= { opt_basedir, "/usr", "/usr/local/mysql", "/usr/sbin", "/usr/share", - "/extra", "/extra/debug", "/extra/release", "/bin" + "/extra", "/extra/debug", "/extra/release", "/bin", "/usr/bin", + "/mysql/bin" }; for (i= 0; i < (int)array_elements(paths); i++) { diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index cc4477f2fac..719800e309a 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -1000,6 +1000,7 @@ echo "=====" >> $STATUS_HISTORY %attr(755, root, root) %{_bindir}/mysql_setpermission %attr(755, root, root) %{_bindir}/mysql_tzinfo_to_sql %attr(755, root, root) %{_bindir}/mysql_upgrade +%attr(755, root, root) %{_bindir}/mysql_plugin %attr(755, root, root) %{_bindir}/mysql_zap %attr(755, root, root) %{_bindir}/mysqlbug %attr(755, root, root) %{_bindir}/mysqld_multi @@ -1017,6 +1018,7 @@ echo "=====" >> $STATUS_HISTORY %attr(755, root, root) %{_sbindir}/rcmysql %attr(755, root, root) %{_libdir}/mysql/plugin/adt_null.so %attr(755, root, root) %{_libdir}/mysql/plugin/libdaemon_example.so +%attr(755, root, root) %{_libdir}/mysql/plugin/daemon_example.ini %attr(755, root, root) %{_libdir}/mysql/plugin/mypluglib.so %attr(755, root, root) %{_libdir}/mysql/plugin/semisync_master.so %attr(755, root, root) %{_libdir}/mysql/plugin/semisync_slave.so @@ -1130,6 +1132,10 @@ echo "=====" >> $STATUS_HISTORY # merging BK trees) ############################################################################## %changelog +* Mon Jul 25 2011 Chuck Bell + +- Added the mysql_plugin client - enables or disables plugins. + * Thu Jul 07 2011 Joerg Bruehe - Fix bug#45415: "rpm upgrade recreates test database" From 3050742dd9c81e5e18b64343dc7d3ea67650e050 Mon Sep 17 00:00:00 2001 From: Sergey Glukhov Date: Wed, 27 Jul 2011 12:34:25 +0400 Subject: [PATCH 14/29] Bug#12584302 AFTER FIX FOR #12403504: ASSERTION FAILED: DELSUM+(INT) Y/4-TEMP > 0, The problem is that TIME_FUZZY_DATE is explicitly used for get_arg0_date() function in Item_date_typecast::get_date method. The fix is to use real fuzzy_date value. mysql-test/r/func_time.result: test case mysql-test/t/func_time.test: test case sql/item_timefunc.cc: use real fuzzy_date value --- mysql-test/r/func_time.result | 6 ++++++ mysql-test/t/func_time.test | 6 ++++++ sql/item_timefunc.cc | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index 63744d4ef29..68c1e667440 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -1426,4 +1426,10 @@ NULL SELECT WEEK(DATE_ADD(FROM_DAYS(1),INTERVAL 1 MONTH), 1); WEEK(DATE_ADD(FROM_DAYS(1),INTERVAL 1 MONTH), 1) NULL +# +# Bug#12584302 AFTER FIX FOR #12403504: ASSERTION FAILED: DELSUM+(INT) Y/4-TEMP > 0, +# +DO WEEK((DATE_ADD((CAST(0 AS DATE)), INTERVAL 1 YEAR_MONTH)), 5); +Warnings: +Warning 1292 Incorrect datetime value: '0' End of 5.1 tests diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index de92f313992..2dcac0d5796 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -928,4 +928,10 @@ SELECT DATE_FORMAT('0000-00-11', '%w'); SELECT MAKEDATE(11111111,1); SELECT WEEK(DATE_ADD(FROM_DAYS(1),INTERVAL 1 MONTH), 1); +--echo # +--echo # Bug#12584302 AFTER FIX FOR #12403504: ASSERTION FAILED: DELSUM+(INT) Y/4-TEMP > 0, +--echo # + +DO WEEK((DATE_ADD((CAST(0 AS DATE)), INTERVAL 1 YEAR_MONTH)), 5); + --echo End of 5.1 tests diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 83b0b7cdebc..31474f1d6ca 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2646,7 +2646,7 @@ String *Item_time_typecast::val_str(String *str) bool Item_date_typecast::get_date(MYSQL_TIME *ltime, uint fuzzy_date) { - bool res= get_arg0_date(ltime, TIME_FUZZY_DATE); + bool res= get_arg0_date(ltime, fuzzy_date); ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0; ltime->time_type= MYSQL_TIMESTAMP_DATE; return res; From 58cf757f0b28108884fd0c9f641ba31296dc6c45 Mon Sep 17 00:00:00 2001 From: Sven Sandberg Date: Wed, 27 Jul 2011 12:35:44 +0200 Subject: [PATCH 15/29] Updated default.experimental; now rpl tests are up to date as of 2011-07-25. --- mysql-test/collections/default.experimental | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mysql-test/collections/default.experimental b/mysql-test/collections/default.experimental index 8883acfa606..961a93b8741 100644 --- a/mysql-test/collections/default.experimental +++ b/mysql-test/collections/default.experimental @@ -23,7 +23,8 @@ main.query_cache_28249 # Bug#12584161 2009-03-25 main.query_ca ndb.* # joro : NDB tests marked as experimental as agreed with bochklin -rpl.rpl_innodb_bug28430 @solaris # Bug#11754425 +rpl.rpl_innodb_bug28430 # Bug#11754425 +rpl.rpl_insert # Sven: BUG#12764817 rpl.rpl_row_sp011 @solaris # Joro : Bug #11753919 rpl.rpl_stop_slave # Sven : BUG#12345981 From 8f209f3918440cd8f17a2f7cbc964d991e9d8366 Mon Sep 17 00:00:00 2001 From: Sven Sandberg Date: Wed, 27 Jul 2011 12:36:33 +0200 Subject: [PATCH 16/29] Updated default.experimental; now rpl tests are up to date as of 2011-07-25. --- mysql-test/collections/default.experimental | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mysql-test/collections/default.experimental b/mysql-test/collections/default.experimental index 90f25482137..b3623402065 100644 --- a/mysql-test/collections/default.experimental +++ b/mysql-test/collections/default.experimental @@ -12,10 +12,9 @@ main.signal_demo3 @solaris # Bug#11753919 2010-01-20 alik Several main.sp @solaris # Bug#11753919 2010-01-20 alik Several test cases fail on Solaris with error Thread stack overrun main.wait_timeout @solaris # Bug#11758972 2010-04-26 alik wait_timeout fails on OpenSolaris -rpl.rpl_innodb_bug28430 # Bug#11754425 -rpl.rpl_bug37426 # WL#5867: skozlov: test case moved from unused bugs suite rpl.rpl_heartbeat_basic # BUG#12403008 2011-04-27 sven fails sporadically -rpl.rpl_show_slave_hosts # BUG#12416700 2011-05-02 sven fails sporadically +rpl.rpl_innodb_bug28430 # Bug#11754425 +rpl.rpl_row_sp011 @solaris # Bug#11753919 2011-07-25 sven Several test cases fail on Solaris with error Thread stack overrun sys_vars.max_sp_recursion_depth_func @solaris # Bug#11753919 2010-01-20 alik Several test cases fail on Solaris with error Thread stack overrun sys_vars.wait_timeout_func # Bug#11750645 2010-04-26 alik wait_timeout_func fails From 8625a873d2ee555cd8abe8903183b51f3a355c40 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 28 Jul 2011 19:14:12 +0200 Subject: [PATCH 17/29] Merge from mysql-5.5.15-release From 3468b55a215d1c4b489dbb925f19176e12c9f242 Mon Sep 17 00:00:00 2001 From: Sergey Glukhov Date: Tue, 2 Aug 2011 11:33:45 +0400 Subject: [PATCH 18/29] Bug#11766594 59736: SELECT DISTINCT.. INCORRECT RESULT WITH DETERMINISTIC FUNCTION IN WHERE C There is an optimization of DISTINCT in JOIN::optimize() which depends on THD::used_tables value. Each SELECT statement inside SP resets used_tables value(see mysql_select()) and it leads to wrong result. The fix is to replace THD::used_tables with LEX::used_tables. mysql-test/r/sp.result: test case mysql-test/t/sp.test: test case sql/sql_base.cc: THD::used_tables is replaced with LEX::used_tables sql/sql_class.cc: THD::used_tables is replaced with LEX::used_tables sql/sql_class.h: THD::used_tables is replaced with LEX::used_tables sql/sql_insert.cc: THD::used_tables is replaced with LEX::used_tables sql/sql_lex.cc: THD::used_tables is replaced with LEX::used_tables sql/sql_lex.h: THD::used_tables is replaced with LEX::used_tables sql/sql_prepare.cc: THD::used_tables is replaced with LEX::used_tables sql/sql_select.cc: THD::used_tables is replaced with LEX::used_tables --- mysql-test/r/sp.result | 19 +++++++++++++++++++ mysql-test/t/sp.test | 27 +++++++++++++++++++++++++++ sql/sql_base.cc | 8 ++++---- sql/sql_class.cc | 1 - sql/sql_class.h | 7 ------- sql/sql_insert.cc | 4 ++-- sql/sql_lex.cc | 1 + sql/sql_lex.h | 10 ++++++++++ sql/sql_prepare.cc | 4 ++-- sql/sql_select.cc | 8 ++++---- 10 files changed, 69 insertions(+), 20 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 2180a23b91a..1ce14d38166 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -7053,6 +7053,25 @@ init_connect SET @@GLOBAL.init_connect= @old_init_connect; DROP PROCEDURE p2; DROP PROCEDURE p5; +# +# Bug#11766594 59736: SELECT DISTINCT.. INCORRECT RESULT WITH DETERMINISTIC FUNCTION IN WHERE C +# +CREATE TABLE t1 (a INT, b INT, KEY(b)); +CREATE TABLE t2 (c INT, d INT, KEY(c)); +INSERT INTO t1 VALUES (1,1),(1,1),(1,2); +INSERT INTO t2 VALUES (1,1),(1,2); +CREATE FUNCTION f1() RETURNS INT DETERMINISTIC +BEGIN +DECLARE a int; +-- SQL statement inside +SELECT 1 INTO a; +RETURN a; +END $ +SELECT COUNT(DISTINCT d) FROM t1, t2 WHERE a = c AND b = f1(); +COUNT(DISTINCT d) +2 +DROP FUNCTION f1; +DROP TABLE t1, t2; # ------------------------------------------------------------------ # -- End of 5.1 tests # ------------------------------------------------------------------ diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 5cf050146dd..9207ea22290 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -8350,6 +8350,33 @@ SET @@GLOBAL.init_connect= @old_init_connect; DROP PROCEDURE p2; DROP PROCEDURE p5; +--echo # +--echo # Bug#11766594 59736: SELECT DISTINCT.. INCORRECT RESULT WITH DETERMINISTIC FUNCTION IN WHERE C +--echo # + +CREATE TABLE t1 (a INT, b INT, KEY(b)); +CREATE TABLE t2 (c INT, d INT, KEY(c)); +INSERT INTO t1 VALUES (1,1),(1,1),(1,2); +INSERT INTO t2 VALUES (1,1),(1,2); + +DELIMITER $; + +CREATE FUNCTION f1() RETURNS INT DETERMINISTIC +BEGIN + DECLARE a int; + -- SQL statement inside + SELECT 1 INTO a; + RETURN a; +END $ + +DELIMITER ;$ + +SELECT COUNT(DISTINCT d) FROM t1, t2 WHERE a = c AND b = f1(); + +DROP FUNCTION f1; +DROP TABLE t1, t2; + + --echo # ------------------------------------------------------------------ --echo # -- End of 5.1 tests --echo # ------------------------------------------------------------------ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 87d28402e01..ab1ba156905 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7576,7 +7576,7 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM && sum_func_list) item->split_sum_func(thd, ref_pointer_array, *sum_func_list); - thd->used_tables|= item->used_tables(); + thd->lex->used_tables|= item->used_tables(); thd->lex->current_select->cur_pos_in_select_list++; } thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup; @@ -7923,7 +7923,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, views and natural joins this update is performed inside the loop below. */ if (table) - thd->used_tables|= table->map; + thd->lex->used_tables|= table->map; /* Initialize a generic field iterator for the current table reference. @@ -8008,7 +8008,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, field_table= nj_col->table_ref->table; if (field_table) { - thd->used_tables|= field_table->map; + thd->lex->used_tables|= field_table->map; field_table->covering_keys.intersect(field->part_of_key); field_table->merge_keys.merge(field->part_of_key); field_table->used_fields++; @@ -8016,7 +8016,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, } } else - thd->used_tables|= item->used_tables(); + thd->lex->used_tables|= item->used_tables(); thd->lex->current_select->cur_pos_in_select_list++; } /* diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b9df35fe8be..7fb1d2ade5f 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -649,7 +649,6 @@ THD::THD() is_slave_error= thread_specific_used= FALSE; hash_clear(&handler_tables_hash); tmp_table=0; - used_tables=0; cuted_fields= sent_row_count= row_count= 0L; limit_found_rows= 0; row_count_func= -1; diff --git a/sql/sql_class.h b/sql/sql_class.h index bf197fee9c3..3c1b2c1330f 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1734,13 +1734,6 @@ public: */ ha_rows examined_row_count; - /* - The set of those tables whose fields are referenced in all subqueries - of the query. - TODO: possibly this it is incorrect to have used tables in THD because - with more than one subquery, it is not clear what does the field mean. - */ - table_map used_tables; USER_CONN *user_connect; CHARSET_INFO *db_charset; /* diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index baf5af16e2b..e176e5c9b6d 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -631,7 +631,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, lock_type= table_list->lock_type; thd_proc_info(thd, "init"); - thd->used_tables=0; + thd->lex->used_tables=0; values= its++; value_count= values->elements; @@ -779,7 +779,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, } else { - if (thd->used_tables) // Column used in values() + if (thd->lex->used_tables) // Column used in values() restore_record(table,s->default_values); // Get empty record else { diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index f0289ab86ce..6e40f9b9614 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -360,6 +360,7 @@ void lex_start(THD *thd) lex->server_options.port= -1; lex->is_lex_started= TRUE; + lex->used_tables= 0; DBUG_VOID_RETURN; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index b4dbbc5162e..035fa1fde91 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1836,6 +1836,16 @@ typedef struct st_lex : public Query_tables_list uint create_select_pos; bool create_select_in_comment; + /* + The set of those tables whose fields are referenced in all subqueries + of the query. + TODO: possibly this it is incorrect to have used tables in LEX because + with subquery, it is not clear what does the field mean. To fix this + we should aggregate used tables information for selected expressions + into the select_lex. + */ + table_map used_tables; + st_lex(); virtual ~st_lex() diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index b296eb22cdb..ec7a7fb73b8 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1382,7 +1382,7 @@ static int mysql_test_select(Prepared_statement *stmt, if (open_normal_and_derived_tables(thd, tables, 0)) goto error; - thd->used_tables= 0; // Updated by setup_fields + thd->lex->used_tables= 0; // Updated by setup_fields /* JOIN::prepare calls @@ -1551,7 +1551,7 @@ static bool select_like_stmt_test(Prepared_statement *stmt, if (specific_prepare && (*specific_prepare)(thd)) DBUG_RETURN(TRUE); - thd->used_tables= 0; // Updated by setup_fields + thd->lex->used_tables= 0; // Updated by setup_fields /* Calls JOIN::prepare */ DBUG_RETURN(lex->unit.prepare(thd, 0, setup_tables_done_option)); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2346f744b47..516c9c37473 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -406,7 +406,7 @@ fix_inner_refs(THD *thd, List &all_fields, SELECT_LEX *select, if (!ref->fixed && ref->fix_fields(thd, 0)) return TRUE; - thd->used_tables|= item->used_tables(); + thd->lex->used_tables|= item->used_tables(); } return false; } @@ -1632,7 +1632,7 @@ JOIN::optimize() if (exec_tmp_table1->distinct) { - table_map used_tables= thd->used_tables; + table_map used_tables= thd->lex->used_tables; JOIN_TAB *last_join_tab= join_tab+tables-1; do { @@ -2526,7 +2526,7 @@ mysql_select(THD *thd, Item ***rref_pointer_array, if (!(join= new JOIN(thd, fields, select_options, result))) DBUG_RETURN(TRUE); thd_proc_info(thd, "init"); - thd->used_tables=0; // Updated by setup_fields + thd->lex->used_tables=0; // Updated by setup_fields err= join->prepare(rref_pointer_array, tables, wild_num, conds, og_num, order, group, having, proc_param, select_lex, unit); @@ -16949,7 +16949,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, need_order=0; extra.append(STRING_WITH_LEN("; Using filesort")); } - if (distinct & test_all_bits(used_tables,thd->used_tables)) + if (distinct & test_all_bits(used_tables, thd->lex->used_tables)) extra.append(STRING_WITH_LEN("; Distinct")); for (uint part= 0; part < tab->ref.key_parts; part++) From 22c2d06d3dcf8ec958b771f4a7a49ef368d79b17 Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Wed, 3 Aug 2011 17:53:44 +0400 Subject: [PATCH 19/29] backport from mysql-trunk BUG #11754979 - 46675: ON DUPLICATE KEY UPDATE AND UPDATECOUNT() POSSIBLY WRONG The mysql_affected_rows() client call returns 3 instead of 2 on INSERT ... ON DUPLICATE KEY UPDATE query with a duplicated key value. The fix for the old bug #29692 was incomplete: unnecessary double increment of "touched" rows still happened. This bugfix removes: 1) unneeded increment of "touched" rows and 2) useless double resetting of auto-increment value. sql/sql_insert.cc: write_record() function: Unneeded increment of "touched" rows and useless double resetting of auto-increment value has been removed. tests/mysql_client_test.c: New test case. --- sql/sql_insert.cc | 5 ----- tests/mysql_client_test.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 5dc2a7e2abb..eaf7f4c895b 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1610,9 +1610,6 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) goto before_trg_err; table->file->restore_auto_increment(prev_insert_id); - if (table->next_number_field) - table->file->adjust_next_insert_id_after_explicit_value( - table->next_number_field->val_int()); info->touched++; if (!records_are_comparable(table) || compare_records(table)) { @@ -1649,8 +1646,6 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (table->next_number_field) table->file->adjust_next_insert_id_after_explicit_value( table->next_number_field->val_int()); - info->touched++; - goto ok_or_after_trg_err; } else /* DUP_REPLACE */ diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index b3da65485dd..14a60f0e857 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -19660,6 +19660,34 @@ static void test_bug12337762() DBUG_VOID_RETURN; } + +/* + BUG 11754979 - 46675: ON DUPLICATE KEY UPDATE AND UPDATECOUNT() POSSIBLY WRONG +*/ + +static void test_bug11754979() +{ + MYSQL* conn; + DBUG_ENTER("test_bug11754979"); + + myheader("test_bug11754979"); + DIE_UNLESS((conn= mysql_client_init(NULL))); + DIE_UNLESS(mysql_real_connect(conn, opt_host, opt_user, + opt_password, opt_db ? opt_db:"test", opt_port, + opt_unix_socket, CLIENT_FOUND_ROWS)); + myquery(mysql_query(conn, "DROP TABLE IF EXISTS t1")); + myquery(mysql_query(conn, "CREATE TABLE t1(id INT, label CHAR(1), PRIMARY KEY(id))")); + myquery(mysql_query(conn, "INSERT INTO t1(id, label) VALUES (1, 'a')")); + myquery(mysql_query(conn, "INSERT INTO t1(id, label) VALUES (1, 'a') " + "ON DUPLICATE KEY UPDATE id = 4")); + DIE_UNLESS(mysql_affected_rows(conn) == 2); + myquery(mysql_query(conn, "DROP TABLE t1")); + mysql_close(conn); + + DBUG_VOID_RETURN; +} + + /* Read and parse arguments and MySQL options from my.cnf */ @@ -20005,6 +20033,7 @@ static struct my_tests_st my_tests[]= { { "test_bug56976", test_bug56976 }, { "test_bug11766854", test_bug11766854 }, { "test_bug12337762", test_bug12337762 }, + { "test_bug11754979", test_bug11754979 }, { 0, 0 } }; From 34a71efca71845e626d9e1712fe0aadb9ef27110 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 6 Aug 2011 01:51:25 +0200 Subject: [PATCH 20/29] fixing BUG 11759958 - 52319: MYSQL RHEL5 RPMS DO NOT REQUIRE NET-TOOLS --- support-files/mysql.spec.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index dde7d4696e4..69eecbb3d01 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -127,13 +127,13 @@ %define distro_description Oracle Enterprise Linux 4 %define distro_releasetag oel4 %define distro_buildreq gcc-c++ gperf ncurses-devel perl readline-devel time zlib-devel - %define distro_requires chkconfig coreutils grep procps shadow-utils + %define distro_requires chkconfig coreutils grep procps shadow-utils net-tools %else %if "%oelver" == "5" %define distro_description Oracle Enterprise Linux 5 %define distro_releasetag oel5 %define distro_buildreq gcc-c++ gperf ncurses-devel perl readline-devel time zlib-devel - %define distro_requires chkconfig coreutils grep procps shadow-utils + %define distro_requires chkconfig coreutils grep procps shadow-utils net-tools %else %{error:Oracle Enterprise Linux %{oelver} is unsupported} %endif @@ -145,13 +145,13 @@ %define distro_description Red Hat Enterprise Linux 4 %define distro_releasetag rhel4 %define distro_buildreq gcc-c++ gperf ncurses-devel perl readline-devel time zlib-devel - %define distro_requires chkconfig coreutils grep procps shadow-utils + %define distro_requires chkconfig coreutils grep procps shadow-utils net-tools %else %if "%rhelver" == "5" %define distro_description Red Hat Enterprise Linux 5 %define distro_releasetag rhel5 %define distro_buildreq gcc-c++ gperf ncurses-devel perl readline-devel time zlib-devel - %define distro_requires chkconfig coreutils grep procps shadow-utils + %define distro_requires chkconfig coreutils grep procps shadow-utils net-tools %else %{error:Red Hat Enterprise Linux %{rhelver} is unsupported} %endif From 6f8a80270c4f1004bfbe9bdb9e5fbce8f0e03981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 8 Aug 2011 11:22:18 +0300 Subject: [PATCH 21/29] Bug#12770537 I_S.TABLES.DATA_LENGTH does not show on-disk size for compressed InnoDB tables ha_innodb::info_low(): For calculating data_length or index_length, use the compressed page size for compressed tables instead of UNIV_PAGE_SIZE. rb:714 approved by Sunny Bains --- .../suite/innodb_plugin/r/innodb-zip.result | 114 +++++++++--------- .../suite/innodb_plugin/t/innodb-zip.test | 20 +-- storage/innodb_plugin/ChangeLog | 8 +- storage/innodb_plugin/handler/ha_innodb.cc | 19 ++- 4 files changed, 87 insertions(+), 74 deletions(-) diff --git a/mysql-test/suite/innodb_plugin/r/innodb-zip.result b/mysql-test/suite/innodb_plugin/r/innodb-zip.result index fc35143b305..16947bf16dc 100644 --- a/mysql-test/suite/innodb_plugin/r/innodb-zip.result +++ b/mysql-test/suite/innodb_plugin/r/innodb-zip.result @@ -62,42 +62,42 @@ row_format=compressed; create table t14(a int primary key) engine=innodb key_block_size=9; Warnings: Warning 1478 InnoDB: ignoring KEY_BLOCK_SIZE=9. -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; -table_schema table_name row_format -test t0 Compact -test t00 Compact -test t1 Compact -test t10 Dynamic -test t11 Compressed -test t12 Compressed -test t13 Compressed -test t14 Compact -test t2 Redundant -test t3 Compact -test t4 Compact -test t5 Redundant -test t6 Redundant -test t7 Redundant -test t8 Compact -test t9 Compact +table_schema table_name row_format data_length index_length +test t0 Compact 16384 0 +test t00 Compact 16384 0 +test t1 Compact 16384 0 +test t10 Dynamic 16384 0 +test t11 Compressed 1024 0 +test t12 Compressed 1024 0 +test t13 Compressed 8192 0 +test t14 Compact 16384 0 +test t2 Redundant 16384 0 +test t3 Compact 16384 0 +test t4 Compact 16384 0 +test t5 Redundant 16384 0 +test t6 Redundant 16384 0 +test t7 Redundant 16384 0 +test t8 Compact 16384 0 +test t9 Compact 16384 0 drop table t0,t00,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14; alter table t1 key_block_size=0; alter table t1 row_format=dynamic; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; -table_schema table_name row_format -test t1 Dynamic +table_schema table_name row_format data_length index_length +test t1 Dynamic 16384 0 alter table t1 row_format=compact; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; -table_schema table_name row_format -test t1 Compact +table_schema table_name row_format data_length index_length +test t1 Compact 16384 0 alter table t1 row_format=redundant; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; -table_schema table_name row_format -test t1 Redundant +table_schema table_name row_format data_length index_length +test t1 Redundant 16384 0 drop table t1; create table t1(a int not null, b text, index(b(10))) engine=innodb key_block_size=1; @@ -114,11 +114,11 @@ rollback; select a,left(b,40) from t1 natural join t2; a left(b,40) 1 1abcdefghijklmnopqrstuvwxyzAAAAAAAAAAAAA -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; -table_schema table_name row_format -test t1 Compressed -test t2 Compact +table_schema table_name row_format data_length index_length +test t1 Compressed 2048 1024 +test t2 Compact 16384 0 drop table t1,t2; SET SESSION innodb_strict_mode = off; CREATE TABLE t1( @@ -206,19 +206,19 @@ create table t8 (id int primary key) engine = innodb row_format = compressed; create table t9 (id int primary key) engine = innodb row_format = dynamic; create table t10(id int primary key) engine = innodb row_format = compact; create table t11(id int primary key) engine = innodb row_format = redundant; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; -table_schema table_name row_format -test t1 Compact -test t10 Compact -test t11 Redundant -test t3 Compressed -test t4 Compressed -test t5 Compressed -test t6 Compressed -test t7 Compressed -test t8 Compressed -test t9 Dynamic +table_schema table_name row_format data_length index_length +test t1 Compact 16384 0 +test t10 Compact 16384 0 +test t11 Redundant 16384 0 +test t3 Compressed 1024 0 +test t4 Compressed 2048 0 +test t5 Compressed 4096 0 +test t6 Compressed 8192 0 +test t7 Compressed 16384 0 +test t8 Compressed 8192 0 +test t9 Dynamic 16384 0 drop table t1, t3, t4, t5, t6, t7, t8, t9, t10, t11; create table t1 (id int primary key) engine = innodb key_block_size = 8 row_format = compressed; @@ -245,11 +245,11 @@ Warning 1478 InnoDB: cannot specify ROW_FORMAT = DYNAMIC with KEY_BLOCK_SIZE. Error 1005 Can't create table 'test.t4' (errno: 1478) create table t5 (id int primary key) engine = innodb key_block_size = 8 row_format = default; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; -table_schema table_name row_format -test t1 Compressed -test t5 Compressed +table_schema table_name row_format data_length index_length +test t1 Compressed 8192 0 +test t5 Compressed 8192 0 drop table t1, t5; create table t1 (id int primary key) engine = innodb key_block_size = 9 row_format = redundant; @@ -275,9 +275,9 @@ Level Code Message Warning 1478 InnoDB: invalid KEY_BLOCK_SIZE = 9. Valid values are [1, 2, 4, 8, 16] Warning 1478 InnoDB: cannot specify ROW_FORMAT = DYNAMIC with KEY_BLOCK_SIZE. Error 1005 Can't create table 'test.t2' (errno: 1478) -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; -table_schema table_name row_format +table_schema table_name row_format data_length index_length set global innodb_file_per_table = off; create table t1 (id int primary key) engine = innodb key_block_size = 1; ERROR HY000: Can't create table 'test.t1' (errno: 1478) @@ -323,11 +323,11 @@ Warning 1478 InnoDB: ROW_FORMAT=DYNAMIC requires innodb_file_per_table. Error 1005 Can't create table 'test.t7' (errno: 1478) create table t8 (id int primary key) engine = innodb row_format = compact; create table t9 (id int primary key) engine = innodb row_format = redundant; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; -table_schema table_name row_format -test t8 Compact -test t9 Redundant +table_schema table_name row_format data_length index_length +test t8 Compact 16384 0 +test t9 Redundant 16384 0 drop table t8, t9; set global innodb_file_per_table = on; set global innodb_file_format = `0`; @@ -375,11 +375,11 @@ Warning 1478 InnoDB: ROW_FORMAT=DYNAMIC requires innodb_file_format > Antelope. Error 1005 Can't create table 'test.t7' (errno: 1478) create table t8 (id int primary key) engine = innodb row_format = compact; create table t9 (id int primary key) engine = innodb row_format = redundant; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; -table_schema table_name row_format -test t8 Compact -test t9 Redundant +table_schema table_name row_format data_length index_length +test t8 Compact 16384 0 +test t9 Redundant 16384 0 drop table t8, t9; set global innodb_file_per_table=0; set global innodb_file_format=Antelope; diff --git a/mysql-test/suite/innodb_plugin/t/innodb-zip.test b/mysql-test/suite/innodb_plugin/t/innodb-zip.test index 8c1ef1f5467..5ed101b7f20 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb-zip.test +++ b/mysql-test/suite/innodb_plugin/t/innodb-zip.test @@ -36,19 +36,19 @@ create table t13(a int primary key) engine=innodb row_format=compressed; create table t14(a int primary key) engine=innodb key_block_size=9; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; drop table t0,t00,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14; alter table t1 key_block_size=0; alter table t1 row_format=dynamic; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; alter table t1 row_format=compact; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; alter table t1 row_format=redundant; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; drop table t1; @@ -81,7 +81,7 @@ connection default; disconnect a; disconnect b; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; drop table t1,t2; @@ -192,7 +192,7 @@ create table t9 (id int primary key) engine = innodb row_format = dynamic; create table t10(id int primary key) engine = innodb row_format = compact; create table t11(id int primary key) engine = innodb row_format = redundant; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; drop table t1, t3, t4, t5, t6, t7, t8, t9, t10, t11; @@ -218,7 +218,7 @@ show warnings; create table t5 (id int primary key) engine = innodb key_block_size = 8 row_format = default; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; drop table t1, t5; @@ -238,7 +238,7 @@ create table t2 (id int primary key) engine = innodb key_block_size = 9 row_format = dynamic; show warnings; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; #test valid values with innodb_file_per_table unset @@ -268,7 +268,7 @@ show warnings; create table t8 (id int primary key) engine = innodb row_format = compact; create table t9 (id int primary key) engine = innodb row_format = redundant; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; drop table t8, t9; @@ -300,7 +300,7 @@ show warnings; create table t8 (id int primary key) engine = innodb row_format = compact; create table t9 (id int primary key) engine = innodb row_format = redundant; -SELECT table_schema, table_name, row_format +SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb'; drop table t8, t9; diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 617df0a61fe..d5d1e8d1b66 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,9 +1,15 @@ +2011-08-08 The InnoDB Team + + * handler/ha_innodb.cc: + Fix Bug#12770537 I_S.TABLES.DATA_LENGTH DOES NOT SHOW ON-DISK SIZE + FOR COMPRESSED INNODB + 2011-07-19 The InnoDB Team * buf/buf0buf.c, buf/buf0rea.c, handler/ha_innodb.cc, include/buf0buf.h, include/buf0buf.ic, include/srv0srv.h, srv/srv0srv.c: - Fix bug#Bug 12356373 by reintroducing random readahead + Fix Bug#12356373 by reintroducing random readahead 2011-06-30 The InnoDB Team diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc index 6f88ea3fde1..609299efce5 100644 --- a/storage/innodb_plugin/handler/ha_innodb.cc +++ b/storage/innodb_plugin/handler/ha_innodb.cc @@ -7652,6 +7652,8 @@ ha_innobase::info_low( if (flag & HA_STATUS_VARIABLE) { + ulint page_size; + dict_table_stats_lock(ib_table, RW_S_LATCH); n_rows = ib_table->stat_n_rows; @@ -7694,14 +7696,19 @@ ha_innobase::info_low( prebuilt->autoinc_last_value = 0; } + page_size = dict_table_zip_size(ib_table); + if (page_size == 0) { + page_size = UNIV_PAGE_SIZE; + } + stats.records = (ha_rows)n_rows; stats.deleted = 0; - stats.data_file_length = ((ulonglong) - ib_table->stat_clustered_index_size) - * UNIV_PAGE_SIZE; - stats.index_file_length = ((ulonglong) - ib_table->stat_sum_of_other_index_sizes) - * UNIV_PAGE_SIZE; + stats.data_file_length + = ((ulonglong) ib_table->stat_clustered_index_size) + * page_size; + stats.index_file_length = + ((ulonglong) ib_table->stat_sum_of_other_index_sizes) + * page_size; dict_table_stats_unlock(ib_table, RW_S_LATCH); From c671a76a4dd315af1cf3b39e566b788e73a0f161 Mon Sep 17 00:00:00 2001 From: Sneha Modi Date: Mon, 8 Aug 2011 17:01:09 +0530 Subject: [PATCH 22/29] Recommitting the changes to push the code. --- mysql-test/suite/sys_vars/r/all_vars.result | 6 - .../r/innodb_file_format_check_basic.result | 125 ++++++++++-------- .../r/innodb_file_format_max_basic.result | 65 +++++++++ .../r/innodb_rollback_segments_basic.result | 64 +++++++++ .../r/innodb_stats_method_basic.result | 83 ++++++++++++ .../t/innodb_file_format_check_basic.test | 112 +++++++++++----- .../t/innodb_file_format_max_basic.test | 60 +++++++++ .../t/innodb_rollback_segments_basic.test | 58 ++++++++ .../sys_vars/t/innodb_stats_method_basic.test | 72 ++++++++++ 9 files changed, 551 insertions(+), 94 deletions(-) create mode 100644 mysql-test/suite/sys_vars/r/innodb_file_format_max_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_rollback_segments_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_stats_method_basic.result create mode 100644 mysql-test/suite/sys_vars/t/innodb_file_format_max_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_rollback_segments_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_stats_method_basic.test diff --git a/mysql-test/suite/sys_vars/r/all_vars.result b/mysql-test/suite/sys_vars/r/all_vars.result index 3cee368853f..715ad9e2c15 100644 --- a/mysql-test/suite/sys_vars/r/all_vars.result +++ b/mysql-test/suite/sys_vars/r/all_vars.result @@ -11,13 +11,7 @@ There should be *no* long test name listed below: select variable_name as `There should be *no* variables listed below:` from t2 left join t1 on variable_name=test_name where test_name is null; There should be *no* variables listed below: -INNODB_ROLLBACK_SEGMENTS -INNODB_STATS_METHOD -INNODB_FILE_FORMAT_MAX INNODB_LARGE_PREFIX -INNODB_ROLLBACK_SEGMENTS -INNODB_STATS_METHOD -INNODB_FILE_FORMAT_MAX INNODB_LARGE_PREFIX drop table t1; drop table t2; diff --git a/mysql-test/suite/sys_vars/r/innodb_file_format_check_basic.result b/mysql-test/suite/sys_vars/r/innodb_file_format_check_basic.result index c59e1b802f4..9167585f1d6 100644 --- a/mysql-test/suite/sys_vars/r/innodb_file_format_check_basic.result +++ b/mysql-test/suite/sys_vars/r/innodb_file_format_check_basic.result @@ -1,59 +1,80 @@ -SET @start_global_value = @@global.innodb_file_format_max; +SET @start_global_value = @@global.innodb_file_format_check; SELECT @start_global_value; @start_global_value -Antelope -Valid values are 'Antelope' and 'Barracuda' -select @@global.innodb_file_format_max in ('Antelope', 'Barracuda'); -@@global.innodb_file_format_max in ('Antelope', 'Barracuda') 1 -select @@global.innodb_file_format_max; -@@global.innodb_file_format_max -Antelope -select @@session.innodb_file_format_max; -ERROR HY000: Variable 'innodb_file_format_max' is a GLOBAL variable -show global variables like 'innodb_file_format_max'; +Valid values are 'ON' and 'OFF' +SELECT @@global.innodb_file_format_check in (0, 1); +@@global.innodb_file_format_check in (0, 1) +1 +SELECT @@global.innodb_file_format_check; +@@global.innodb_file_format_check +1 +SELECT @@session.innodb_file_format_check; +ERROR HY000: Variable 'innodb_file_format_check' is a GLOBAL variable +SHOW global variables LIKE 'innodb_file_format_check'; Variable_name Value -innodb_file_format_max Antelope -show session variables like 'innodb_file_format_max'; +innodb_file_format_check ON +SHOW session variables LIKE 'innodb_file_format_check'; Variable_name Value -innodb_file_format_max Antelope -select * from information_schema.global_variables where variable_name='innodb_file_format_max'; +innodb_file_format_check ON +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_file_format_check'; VARIABLE_NAME VARIABLE_VALUE -INNODB_FILE_FORMAT_MAX Antelope -select * from information_schema.session_variables where variable_name='innodb_file_format_max'; +INNODB_FILE_FORMAT_CHECK ON +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_file_format_check'; VARIABLE_NAME VARIABLE_VALUE -INNODB_FILE_FORMAT_MAX Antelope -set global innodb_file_format_max='Antelope'; -select @@global.innodb_file_format_max; -@@global.innodb_file_format_max -Antelope -select * from information_schema.global_variables where variable_name='innodb_file_format_max'; -VARIABLE_NAME VARIABLE_VALUE -INNODB_FILE_FORMAT_MAX Antelope -select * from information_schema.session_variables where variable_name='innodb_file_format_max'; -VARIABLE_NAME VARIABLE_VALUE -INNODB_FILE_FORMAT_MAX Antelope -set @@global.innodb_file_format_max='Barracuda'; -select @@global.innodb_file_format_max; -@@global.innodb_file_format_max -Barracuda -select * from information_schema.global_variables where variable_name='innodb_file_format_max'; -VARIABLE_NAME VARIABLE_VALUE -INNODB_FILE_FORMAT_MAX Barracuda -select * from information_schema.session_variables where variable_name='innodb_file_format_max'; -VARIABLE_NAME VARIABLE_VALUE -INNODB_FILE_FORMAT_MAX Barracuda -set session innodb_file_format_max='Salmon'; -ERROR HY000: Variable 'innodb_file_format_max' is a GLOBAL variable and should be set with SET GLOBAL -set @@session.innodb_file_format_max='Salmon'; -ERROR HY000: Variable 'innodb_file_format_max' is a GLOBAL variable and should be set with SET GLOBAL -set global innodb_file_format_max=1.1; -ERROR 42000: Incorrect argument type to variable 'innodb_file_format_max' -set global innodb_file_format_max=1e1; -ERROR 42000: Incorrect argument type to variable 'innodb_file_format_max' -set global innodb_file_format_max='Salmon'; -ERROR 42000: Variable 'innodb_file_format_max' can't be set to the value of 'Salmon' -SET @@global.innodb_file_format_max = @start_global_value; -SELECT @@global.innodb_file_format_max; -@@global.innodb_file_format_max -Antelope +INNODB_FILE_FORMAT_CHECK ON +SET @@global.innodb_file_format_check='Off'; +ERROR HY000: Variable 'innodb_file_format_check' is a read only variable +Expected error 'Read only variable' +SET @@global.innodb_file_format_check=1; +ERROR HY000: Variable 'innodb_file_format_check' is a read only variable +Expected error 'Read only variable' +SET @@global.innodb_file_format_check=0; +ERROR HY000: Variable 'innodb_file_format_check' is a read only variable +Expected error 'Read only variable' +SET @@global.innodb_file_format_check='On'; +ERROR HY000: Variable 'innodb_file_format_check' is a read only variable +Expected error 'Read only variable' +SET session innodb_large_prefix='OFF'; +ERROR HY000: Variable 'innodb_large_prefix' is a GLOBAL variable and should be set with SET GLOBAL +SET @@session.innodb_stats_on_metadata='ON'; +ERROR HY000: Variable 'innodb_stats_on_metadata' is a GLOBAL variable and should be set with SET GLOBAL +SELECT IF(@@GLOBAL.innodb_file_format_check, "ON", "OFF") = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='innodb_file_format_check'; +IF(@@GLOBAL.innodb_file_format_check, "ON", "OFF") = VARIABLE_VALUE +1 +1 Expected +SELECT COUNT(@@GLOBAL.innodb_file_format_check); +COUNT(@@GLOBAL.innodb_file_format_check) +1 +1 Expected +SELECT COUNT(VARIABLE_VALUE) +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='innodb_file_format_check'; +COUNT(VARIABLE_VALUE) +1 +1 Expected +SELECT @@innodb_file_format_check = @@GLOBAL.innodb_file_format_check; +@@innodb_file_format_check = @@GLOBAL.innodb_file_format_check +1 +1 Expected +SELECT COUNT(@@innodb_file_format_check); +COUNT(@@innodb_file_format_check) +1 +1 Expected +SELECT COUNT(@@local.innodb_file_format_check); +ERROR HY000: Variable 'innodb_file_format_check' is a GLOBAL variable +Expected error 'Variable is a GLOBAL variable' +SELECT COUNT(@@SESSION.innodb_file_format_check); +ERROR HY000: Variable 'innodb_file_format_check' is a GLOBAL variable +Expected error 'Variable is a GLOBAL variable' +SELECT COUNT(@@GLOBAL.innodb_file_format_check); +COUNT(@@GLOBAL.innodb_file_format_check) +1 +1 Expected +SELECT innodb_file_format_check = @@SESSION.innodb_file_format_check; +ERROR 42S22: Unknown column 'innodb_file_format_check' in 'field list' +Expected error 'Readonly variable' diff --git a/mysql-test/suite/sys_vars/r/innodb_file_format_max_basic.result b/mysql-test/suite/sys_vars/r/innodb_file_format_max_basic.result new file mode 100644 index 00000000000..32b2262c091 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_file_format_max_basic.result @@ -0,0 +1,65 @@ +SET @start_global_value = @@global.innodb_file_format_max; +SELECT @start_global_value; +@start_global_value +Antelope +Valid values are 'Antelope' and 'Barracuda' +SELECT @@global.innodb_file_format_max in ('Antelope', 'Barracuda'); +@@global.innodb_file_format_max in ('Antelope', 'Barracuda') +1 +SELECT @@global.innodb_file_format_max; +@@global.innodb_file_format_max +Antelope +SELECT @@session.innodb_file_format_max; +ERROR HY000: Variable 'innodb_file_format_max' is a GLOBAL variable +SHOW global variables LIKE 'innodb_file_format_max'; +Variable_name Value +innodb_file_format_max Antelope +SHOW session variables LIKE 'innodb_file_format_max'; +Variable_name Value +innodb_file_format_max Antelope +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_file_format_max'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT_MAX Antelope +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_file_format_max'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT_MAX Antelope +SET global innodb_file_format_max='Antelope'; +SELECT @@global.innodb_file_format_max; +@@global.innodb_file_format_max +Antelope +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_file_format_max'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT_MAX Antelope +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_file_format_max'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT_MAX Antelope +SET @@global.innodb_file_format_max='Barracuda'; +SELECT @@global.innodb_file_format_max; +@@global.innodb_file_format_max +Barracuda +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_file_format_max'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT_MAX Barracuda +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_file_format_max'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT_MAX Barracuda +SET session innodb_file_format_max='Salmon'; +ERROR HY000: Variable 'innodb_file_format_max' is a GLOBAL variable and should be set with SET GLOBAL +SET @@session.innodb_file_format_max='Salmon'; +ERROR HY000: Variable 'innodb_file_format_max' is a GLOBAL variable and should be set with SET GLOBAL +SET global innodb_file_format_max=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_file_format_max' +SET global innodb_file_format_max=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_file_format_max' +SET global innodb_file_format_max='Salmon'; +ERROR 42000: Variable 'innodb_file_format_max' can't be set to the value of 'Salmon' +SET @@global.innodb_file_format_max = @start_global_value; +SELECT @@global.innodb_file_format_max; +@@global.innodb_file_format_max +Antelope diff --git a/mysql-test/suite/sys_vars/r/innodb_rollback_segments_basic.result b/mysql-test/suite/sys_vars/r/innodb_rollback_segments_basic.result new file mode 100644 index 00000000000..a8d392eee38 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_rollback_segments_basic.result @@ -0,0 +1,64 @@ +SET @start_global_value = @@global.innodb_rollback_segments; +SELECT @start_global_value; +@start_global_value +128 +Valid values are zero or above +SELECT @@global.innodb_rollback_segments >=0; +@@global.innodb_rollback_segments >=0 +1 +SELECT @@global.innodb_rollback_segments <=128; +@@global.innodb_rollback_segments <=128 +1 +SELECT @@global.innodb_rollback_segments; +@@global.innodb_rollback_segments +128 +SELECT @@session.innodb_rollback_segments; +ERROR HY000: Variable 'innodb_rollback_segments' is a GLOBAL variable +SHOW global variables LIKE 'innodb_rollback_segments'; +Variable_name Value +innodb_rollback_segments 128 +SHOW session variables LIKE 'innodb_rollback_segments'; +Variable_name Value +innodb_rollback_segments 128 +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_rollback_segments'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ROLLBACK_SEGMENTS 128 +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_rollback_segments'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ROLLBACK_SEGMENTS 128 +SET global innodb_rollback_segments=100; +SELECT @@global.innodb_rollback_segments; +@@global.innodb_rollback_segments +100 +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_rollback_segments'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ROLLBACK_SEGMENTS 100 +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_rollback_segments'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ROLLBACK_SEGMENTS 100 +SET session innodb_rollback_segments=1; +ERROR HY000: Variable 'innodb_rollback_segments' is a GLOBAL variable and should be set with SET GLOBAL +SET global innodb_rollback_segments=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_rollback_segments' +SET global innodb_rollback_segments=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_rollback_segments' +SET global innodb_rollback_segments="foo"; +ERROR 42000: Incorrect argument type to variable 'innodb_rollback_segments' +SET global innodb_rollback_segments=-7; +Warnings: +Warning 1292 Truncated incorrect innodb_rollback_segments value: '-7' +SELECT @@global.innodb_rollback_segments; +@@global.innodb_rollback_segments +1 +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_rollback_segments'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ROLLBACK_SEGMENTS 1 +SET @@global.innodb_rollback_segments = @start_global_value; +SELECT @@global.innodb_rollback_segments; +@@global.innodb_rollback_segments +128 diff --git a/mysql-test/suite/sys_vars/r/innodb_stats_method_basic.result b/mysql-test/suite/sys_vars/r/innodb_stats_method_basic.result new file mode 100644 index 00000000000..6599ca9edb6 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_stats_method_basic.result @@ -0,0 +1,83 @@ +SET @start_global_value = @@global.innodb_stats_method; +SELECT @start_global_value; +@start_global_value +nulls_equal +Valid values are 'nulls_equal', 'nulls_unequal', 'nulls_ignored' +SELECT @@global.innodb_stats_method in ('nulls_equal', 'nulls_unequal', +'nulls_ignored'); +@@global.innodb_stats_method in ('nulls_equal', 'nulls_unequal', +'nulls_ignored') +1 +SELECT @@global.innodb_stats_method; +@@global.innodb_stats_method +nulls_equal +SELECT @@session.innodb_stats_method; +ERROR HY000: Variable 'innodb_stats_method' is a GLOBAL variable +SHOW global variables LIKE 'innodb_stats_method'; +Variable_name Value +innodb_stats_method nulls_equal +SHOW session variables LIKE 'innodb_stats_method'; +Variable_name Value +innodb_stats_method nulls_equal +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_stats_method'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_METHOD nulls_equal +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_stats_method'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_METHOD nulls_equal +SET global innodb_stats_method='nulls_equal'; +SELECT @@global.innodb_stats_method; +@@global.innodb_stats_method +nulls_equal +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_stats_method'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_METHOD nulls_equal +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_stats_method'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_METHOD nulls_equal +SET @@global.innodb_stats_method='nulls_unequal'; +SELECT @@global.innodb_stats_method; +@@global.innodb_stats_method +nulls_unequal +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_stats_method'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_METHOD nulls_unequal +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_stats_method'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_METHOD nulls_unequal +SET global innodb_stats_method=2; +SELECT @@global.innodb_stats_method; +@@global.innodb_stats_method +nulls_ignored +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_stats_method'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_METHOD nulls_ignored +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_stats_method'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_METHOD nulls_ignored +SET session innodb_stats_method='nulls_equal'; +ERROR HY000: Variable 'innodb_stats_method' is a GLOBAL variable and should be set with SET GLOBAL +SET @@session.innodb_stats_method='nulls_ignored'; +ERROR HY000: Variable 'innodb_stats_method' is a GLOBAL variable and should be set with SET GLOBAL +SET global innodb_stats_method=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_stats_method' +SET global innodb_stats_method=4; +ERROR 42000: Variable 'innodb_stats_method' can't be set to the value of '4' +SET global innodb_stats_method=-2; +ERROR 42000: Variable 'innodb_stats_method' can't be set to the value of '-2' +SET global innodb_stats_method=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_stats_method' +SET global innodb_stats_method='some'; +ERROR 42000: Variable 'innodb_stats_method' can't be set to the value of 'some' +SET @@global.innodb_stats_method = @start_global_value; +SELECT @@global.innodb_stats_method; +@@global.innodb_stats_method +nulls_equal diff --git a/mysql-test/suite/sys_vars/t/innodb_file_format_check_basic.test b/mysql-test/suite/sys_vars/t/innodb_file_format_check_basic.test index cbf25af2442..f9f61b9380c 100644 --- a/mysql-test/suite/sys_vars/t/innodb_file_format_check_basic.test +++ b/mysql-test/suite/sys_vars/t/innodb_file_format_check_basic.test @@ -1,55 +1,95 @@ - - -# 2010-01-25 - Added # +# 2011-08-02 - Added +# + --source include/not_embedded.inc --source include/have_innodb.inc -SET @start_global_value = @@global.innodb_file_format_max; +SET @start_global_value = @@global.innodb_file_format_check; SELECT @start_global_value; # # exists as global only # ---echo Valid values are 'Antelope' and 'Barracuda' -select @@global.innodb_file_format_max in ('Antelope', 'Barracuda'); -select @@global.innodb_file_format_max; +--echo Valid values are 'ON' and 'OFF' +SELECT @@global.innodb_file_format_check in (0, 1); +SELECT @@global.innodb_file_format_check; + --error ER_INCORRECT_GLOBAL_LOCAL_VAR -select @@session.innodb_file_format_max; -show global variables like 'innodb_file_format_max'; -show session variables like 'innodb_file_format_max'; -select * from information_schema.global_variables where variable_name='innodb_file_format_max'; -select * from information_schema.session_variables where variable_name='innodb_file_format_max'; +SELECT @@session.innodb_file_format_check; +SHOW global variables LIKE 'innodb_file_format_check'; +SHOW session variables LIKE 'innodb_file_format_check'; +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_file_format_check'; +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_file_format_check'; # -# show that it's writable +# show that it's read only # -set global innodb_file_format_max='Antelope'; -select @@global.innodb_file_format_max; -select * from information_schema.global_variables where variable_name='innodb_file_format_max'; -select * from information_schema.session_variables where variable_name='innodb_file_format_max'; -set @@global.innodb_file_format_max='Barracuda'; -select @@global.innodb_file_format_max; -select * from information_schema.global_variables where variable_name='innodb_file_format_max'; -select * from information_schema.session_variables where variable_name='innodb_file_format_max'; + +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SET @@global.innodb_file_format_check='Off'; +--echo Expected error 'Read only variable' + +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SET @@global.innodb_file_format_check=1; +--echo Expected error 'Read only variable' + +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SET @@global.innodb_file_format_check=0; +--echo Expected error 'Read only variable' + +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SET @@global.innodb_file_format_check='On'; +--echo Expected error 'Read only variable' + --error ER_GLOBAL_VARIABLE -set session innodb_file_format_max='Salmon'; +SET session innodb_large_prefix='OFF'; --error ER_GLOBAL_VARIABLE -set @@session.innodb_file_format_max='Salmon'; +SET @@session.innodb_stats_on_metadata='ON'; # -# incorrect types -# ---error ER_WRONG_TYPE_FOR_VAR -set global innodb_file_format_max=1.1; ---error ER_WRONG_TYPE_FOR_VAR -set global innodb_file_format_max=1e1; ---error ER_WRONG_VALUE_FOR_VAR -set global innodb_file_format_max='Salmon'; - -# -# Cleanup +# Check if the value in GLOBAL Table matches value in variable # -SET @@global.innodb_file_format_max = @start_global_value; -SELECT @@global.innodb_file_format_max; +SELECT IF(@@GLOBAL.innodb_file_format_check, "ON", "OFF") = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='innodb_file_format_check'; +--echo 1 Expected + +SELECT COUNT(@@GLOBAL.innodb_file_format_check); +--echo 1 Expected + +SELECT COUNT(VARIABLE_VALUE) +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='innodb_file_format_check'; +--echo 1 Expected + +# +# Check if accessing variable with and without GLOBAL point to same variable +# +SELECT @@innodb_file_format_check = @@GLOBAL.innodb_file_format_check; +--echo 1 Expected + +# +# Check if innodb_doublewrite can be accessed with and without @@ sign +# + +SELECT COUNT(@@innodb_file_format_check); +--echo 1 Expected + +--Error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT COUNT(@@local.innodb_file_format_check); +--echo Expected error 'Variable is a GLOBAL variable' + +--Error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT COUNT(@@SESSION.innodb_file_format_check); +--echo Expected error 'Variable is a GLOBAL variable' + +SELECT COUNT(@@GLOBAL.innodb_file_format_check); +--echo 1 Expected + +--Error ER_BAD_FIELD_ERROR +SELECT innodb_file_format_check = @@SESSION.innodb_file_format_check; +--echo Expected error 'Readonly variable' diff --git a/mysql-test/suite/sys_vars/t/innodb_file_format_max_basic.test b/mysql-test/suite/sys_vars/t/innodb_file_format_max_basic.test new file mode 100644 index 00000000000..c2cb4cb47bb --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_file_format_max_basic.test @@ -0,0 +1,60 @@ +# +# 2011-08-02 - Added +# +--source include/not_embedded.inc +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_file_format_max; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are 'Antelope' and 'Barracuda' +SELECT @@global.innodb_file_format_max in ('Antelope', 'Barracuda'); +SELECT @@global.innodb_file_format_max; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT @@session.innodb_file_format_max; +SHOW global variables LIKE 'innodb_file_format_max'; +SHOW session variables LIKE 'innodb_file_format_max'; +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_file_format_max'; +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_file_format_max'; + +# +# show that it's writable +# +SET global innodb_file_format_max='Antelope'; +SELECT @@global.innodb_file_format_max; +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_file_format_max'; +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_file_format_max'; +SET @@global.innodb_file_format_max='Barracuda'; +SELECT @@global.innodb_file_format_max; +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_file_format_max'; +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_file_format_max'; +--error ER_GLOBAL_VARIABLE +SET session innodb_file_format_max='Salmon'; +--error ER_GLOBAL_VARIABLE +SET @@session.innodb_file_format_max='Salmon'; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +SET global innodb_file_format_max=1.1; +--error ER_WRONG_TYPE_FOR_VAR +SET global innodb_file_format_max=1e1; +--error ER_WRONG_VALUE_FOR_VAR +SET global innodb_file_format_max='Salmon'; + +# +# Cleanup +# + +SET @@global.innodb_file_format_max = @start_global_value; +SELECT @@global.innodb_file_format_max; diff --git a/mysql-test/suite/sys_vars/t/innodb_rollback_segments_basic.test b/mysql-test/suite/sys_vars/t/innodb_rollback_segments_basic.test new file mode 100644 index 00000000000..9f0b70a528f --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_rollback_segments_basic.test @@ -0,0 +1,58 @@ +# +# 2011-08-01 Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_rollback_segments; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are zero or above +SELECT @@global.innodb_rollback_segments >=0; +SELECT @@global.innodb_rollback_segments <=128; +SELECT @@global.innodb_rollback_segments; + +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT @@session.innodb_rollback_segments; +SHOW global variables LIKE 'innodb_rollback_segments'; +SHOW session variables LIKE 'innodb_rollback_segments'; +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_rollback_segments'; +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_rollback_segments'; + +# +# show that it's writable +# +SET global innodb_rollback_segments=100; +SELECT @@global.innodb_rollback_segments; +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_rollback_segments'; +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_rollback_segments'; +--error ER_GLOBAL_VARIABLE +SET session innodb_rollback_segments=1; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +SET global innodb_rollback_segments=1.1; +--error ER_WRONG_TYPE_FOR_VAR +SET global innodb_rollback_segments=1e1; +--error ER_WRONG_TYPE_FOR_VAR +SET global innodb_rollback_segments="foo"; +SET global innodb_rollback_segments=-7; +SELECT @@global.innodb_rollback_segments; +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_rollback_segments'; + +# +# cleanup +# + +SET @@global.innodb_rollback_segments = @start_global_value; +SELECT @@global.innodb_rollback_segments; diff --git a/mysql-test/suite/sys_vars/t/innodb_stats_method_basic.test b/mysql-test/suite/sys_vars/t/innodb_stats_method_basic.test new file mode 100644 index 00000000000..f01574c3683 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_stats_method_basic.test @@ -0,0 +1,72 @@ +# +# 2011-08-05 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_stats_method; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are 'nulls_equal', 'nulls_unequal', 'nulls_ignored' +SELECT @@global.innodb_stats_method in ('nulls_equal', 'nulls_unequal', +'nulls_ignored'); +SELECT @@global.innodb_stats_method; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT @@session.innodb_stats_method; +SHOW global variables LIKE 'innodb_stats_method'; +SHOW session variables LIKE 'innodb_stats_method'; +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_stats_method'; +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_stats_method'; + +# +# show that it's writable +# +SET global innodb_stats_method='nulls_equal'; +SELECT @@global.innodb_stats_method; +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_stats_method'; +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_stats_method'; +SET @@global.innodb_stats_method='nulls_unequal'; +SELECT @@global.innodb_stats_method; +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_stats_method'; +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_stats_method'; +SET global innodb_stats_method=2; +SELECT @@global.innodb_stats_method; +SELECT * FROM information_schema.global_variables +WHERE variable_name='innodb_stats_method'; +SELECT * FROM information_schema.session_variables +WHERE variable_name='innodb_stats_method'; + +--error ER_GLOBAL_VARIABLE +SET session innodb_stats_method='nulls_equal'; +--error ER_GLOBAL_VARIABLE +SET @@session.innodb_stats_method='nulls_ignored'; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +SET global innodb_stats_method=1.1; +--error ER_WRONG_VALUE_FOR_VAR +SET global innodb_stats_method=4; +--error ER_WRONG_VALUE_FOR_VAR +SET global innodb_stats_method=-2; +--error ER_WRONG_TYPE_FOR_VAR +SET global innodb_stats_method=1e1; +--error ER_WRONG_VALUE_FOR_VAR +SET global innodb_stats_method='some'; + +# +# Cleanup +# + +SET @@global.innodb_stats_method = @start_global_value; +SELECT @@global.innodb_stats_method; From 99f2e85b6cefa8e864a5099324301d110aa3048b Mon Sep 17 00:00:00 2001 From: Chuck Bell Date: Tue, 9 Aug 2011 08:58:18 -0400 Subject: [PATCH 23/29] WL#5710 : enable and disable plugins This patch corrects a problem found in PB. Some platforms have very different locations for the mysql installation. The client was not able to find either my_print_defaults or mysqld predictably. The patch adds two new command options --mysqld and --my-print-defaults which can be used to provide the location of mysqld and my_print_defaults by providing the paths. The patch also changes the concatenation of the soname extension to fix a problem found on some Ubuntu systems. The patch contains changes to the test to ensure it will run on all platforms. A trap is set in the test to skip testing if the location of mysqld, my_print_defaults, or the daemon_example.ini files cannot be determined. --- client/mysql_plugin.c | 58 ++++++++-- mysql-test/r/mysql_plugin.result | 12 ++ mysql-test/t/mysql_plugin.test | 188 ++++++++++++++++++++++--------- 3 files changed, 193 insertions(+), 65 deletions(-) diff --git a/client/mysql_plugin.c b/client/mysql_plugin.c index dd41693161d..6679fa9ca6f 100644 --- a/client/mysql_plugin.c +++ b/client/mysql_plugin.c @@ -35,7 +35,8 @@ static uint opt_verbose=0; static uint opt_no_defaults= 0; static uint opt_print_defaults= 0; static char *opt_datadir=0, *opt_basedir=0, - *opt_plugin_dir=0, *opt_plugin_ini=0; + *opt_plugin_dir=0, *opt_plugin_ini=0, + *opt_mysqld=0, *opt_my_print_defaults=0; static char bootstrap[FN_REFLEN]; @@ -66,6 +67,11 @@ static struct my_option my_long_options[] = 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"print-defaults", 'P', "Show default values from configuration file.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"mysqld", 'm', "Path to mysqld executable. Example: /sbin/temp1/mysql/bin", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"my-print-defaults", 'f', "Path to my_print_defaults executable. " + "Example: /source/temp11/extra", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"verbose", 'v', "More verbose output; you can use this multiple times to get even more " "verbose output.", @@ -317,6 +323,7 @@ static int get_default_values() int ret= 0; FILE *file= 0; + bzero(tool_path, FN_REFLEN); if ((error= find_tool("my_print_defaults" FN_EXEEXT, tool_path))) goto exit; else @@ -335,6 +342,10 @@ static int get_default_values() snprintf(defaults_cmd, sizeof(defaults_cmd), format_str, add_quotes(tool_path), add_quotes(defaults_file)); + if (opt_verbose) + { + printf("# my_print_defaults found: %s\n", tool_path); + } } #else snprintf(defaults_cmd, sizeof(defaults_cmd), @@ -438,6 +449,14 @@ static void print_default_values(void) { printf("--plugin_ini=%s ", opt_plugin_ini); } + if (opt_mysqld) + { + printf("--mysqld=%s ", opt_mysqld); + } + if (opt_my_print_defaults) + { + printf("--my_print_defaults=%s ", opt_my_print_defaults); + } printf("\n"); } @@ -486,6 +505,12 @@ get_one_option(int optid, case 'i': opt_plugin_ini= my_strdup(argument, MYF(MY_FAE)); break; + case 'm': + opt_mysqld= my_strdup(argument, MYF(MY_FAE)); + break; + case 'f': + opt_my_print_defaults= my_strdup(argument, MYF(MY_FAE)); + break; } return 0; } @@ -526,10 +551,11 @@ static int search_dir(const char * base_path, const char *tool_name, const char *subdir, char *tool_path) { char new_path[FN_REFLEN]; + char source_path[FN_REFLEN]; - strcpy(new_path, base_path); - strcat(new_path, subdir); - fn_format(new_path, new_path, "", tool_name, MY_UNPACK_FILENAME); + strcpy(source_path, base_path); + strcat(source_path, subdir); + fn_format(new_path, tool_name, source_path, "", MY_UNPACK_FILENAME); if (file_exists(new_path)) { strcpy(tool_path, new_path); @@ -634,10 +660,10 @@ static int load_plugin_data(char *plugin_name, char *config_file) } if (i == -1) // if first pass, read this line as so_name { - /* save so_name */ - plugin_data.so_name= my_strdup(line, MYF(MY_WME)); /* Add proper file extension for soname */ - strcat((char *)plugin_data.so_name, FN_SOEXT); + strcat(line, FN_SOEXT); + /* save so_name */ + plugin_data.so_name= my_strdup(line, MYF(MY_WME|MY_ZEROFILL)); i++; } else @@ -902,6 +928,18 @@ static int check_access() opt_plugin_ini); goto exit; } + if ((error= my_access(opt_mysqld, F_OK))) + { + fprintf(stderr, "ERROR: Cannot access mysqld path '%s'.\n", + opt_mysqld); + goto exit; + } + if ((error= my_access(opt_my_print_defaults, F_OK))) + { + fprintf(stderr, "ERROR: Cannot access my-print-defaults path '%s'.\n", + opt_my_print_defaults); + goto exit; + } exit: return error; @@ -922,9 +960,9 @@ static int find_tool(const char *tool_name, char *tool_path) int i= 0; const char *paths[]= { - opt_basedir, "/usr", "/usr/local/mysql", "/usr/sbin", "/usr/share", - "/extra", "/extra/debug", "/extra/release", "/bin", "/usr/bin", - "/mysql/bin" + opt_basedir, opt_mysqld, opt_my_print_defaults, "/usr", + "/usr/local/mysql", "/usr/sbin", "/usr/share", "/extra", "/extra/debug", + "/extra/release", "/bin", "/usr/bin", "/mysql/bin" }; for (i= 0; i < (int)array_elements(paths); i++) { diff --git a/mysql-test/r/mysql_plugin.result b/mysql-test/r/mysql_plugin.result index 1a39c86ab9a..949f3748236 100644 --- a/mysql-test/r/mysql_plugin.result +++ b/mysql-test/r/mysql_plugin.result @@ -49,6 +49,14 @@ ERROR: Cannot access basedir at '/basedir_not_there/'. # ERROR: Cannot read plugin config file daemon_example. File does not exist. # +# Attempt to use bad paths - mysqld +# +ERROR: Cannot access mysqld path '/mysqld_not_there/'. +# +# Attempt to use bad paths - my_print_defaults +# +ERROR: Cannot access my-print-defaults path '/my_print_defaults_not_there/'. +# # Missing library # ERROR: The plugin library is missing or in a different location. @@ -90,6 +98,10 @@ Options: -n, --no-defaults Do not read values from configuration file. -P, --print-defaults Show default values from configuration file. + -m, --mysqld=name Path to mysqld executable. Example: /sbin/temp1/mysql/bin + -f, --my-print-defaults=name + Path to my_print_defaults executable. Example: + /source/temp11/extra -v, --verbose More verbose output; you can use this multiple times to get even more verbose output. -V, --version Output version information and exit. diff --git a/mysql-test/t/mysql_plugin.test b/mysql-test/t/mysql_plugin.test index b90619774df..cad89d9de7a 100644 --- a/mysql-test/t/mysql_plugin.test +++ b/mysql-test/t/mysql_plugin.test @@ -1,35 +1,107 @@ # # Test mysql_plugin tool # +# This test contains test cases for testing the mysql_plugin client with +# the daemon_example plugin. Test cases include tests for: +# +# - successful enable/disable +# - incorrect paths +# - missing paths/options +# +# Implementation Notes +# +# The mysql_plugin tool now accepts --mysqld the path to mysqld server. The +# mysqld path is extracted from MYSQLD_BOOTSTRAP_CMD line. We also extract +# the path of MYSQLD_BASEDIR (where mysql exists) and use it for the errmsg +# file. The directories differ between Windows and Unix but the Perl script +# included below will pick as per platform. +# +# The test is also designed to issue the --skip directive if the location of +# the mysqld, my_print_defaults, or daemon_example.ini files cannot be found. +# --source include/not_embedded.inc # Add the datadir, basedir, plugin_dir to the bootstrap command let $MYSQLD_DATADIR= `select @@datadir`; let $MYSQL_BASEDIR= `select @@basedir`; +let $MYSQL_ERRMSG_BASEDIR=`select @@lc_messages_dir`; +let $PLUGIN_DIR=`select @@plugin_dir`; -# The mysql_plugin tool expects all executables in "basedir", so they will be copied to it. -# It also expects a directory structure like in the installed mysql version, so errmsg.sys -# will be copied to "basedir/share". The directories differ between Windows and Unix. --disable_abort_on_error -if(`SELECT CONVERT(@@version_compile_os USING latin1) - IN ('Win32','Win64','Windows')`) + +# Perl script to extract the location of the basedir from environment +# variables. This is needed to ensure the test will run on the PB machines +# designed to test release as well as debug builds. It also checks for the +# location of the my_print_defaults and daemon_example.ini files. + +perl; +use File::Basename; + my ($mysqld)= split " ", $ENV{MYSQLD_BOOTSTRAP_CMD}; + my $mysqld_basedir=dirname($mysqld); + my $my_print_defaults= $ENV{MYSQL_MY_PRINT_DEFAULTS}; + my $my_print_defaults_basedir=dirname($my_print_defaults); + my $daemonexample_ini= "$ENV{DAEMONEXAMPLE_DIR}/daemon_example.ini"; + my $plugindir_ini= "$ENV{PLUGIN_DIR}/daemon_example.ini"; + my $notfound= ""; + open(FILE, ">", "$ENV{MYSQL_TMP_DIR}/mysqld.inc") or die; + print FILE "let \$MYSQLD= $mysqld;\n"; + print FILE "let \$MYSQLD_BASEDIR= $mysqld_basedir;\n"; + print FILE "let \$MYSQL_MY_PRINT_DEFAULTS_BASEDIR= $my_print_defaults_basedir;\n"; + if ((!-e $daemonexample_ini) || (!-r $daemonexample_ini)) + { + print FILE "let \$DAEMONEXAMPLE_DIR= $not_found;\n"; + } + if ((!-e $plugindir_ini) || (!-r $plugindir_ini)) + { + print FILE "let \$PLUGIN_DIR= $not_found;\n"; + } + close FILE; +EOF + + +source $MYSQL_TMP_DIR/mysqld.inc; +remove_file $MYSQL_TMP_DIR/mysqld.inc; + +# The mysql_plugin tool expects a directory structure like in the installed +# mysql version, so errmsg.sys will be copied to "basedir/share", we create +# and remove this structure. + +--mkdir $MYSQLD_BASEDIR/share +--mkdir $MYSQLD_BASEDIR/share/mysql +--copy_file $MYSQL_ERRMSG_BASEDIR/english/errmsg.sys $MYSQLD_BASEDIR/share/errmsg.sys +--copy_file $MYSQL_ERRMSG_BASEDIR/english/errmsg.sys $MYSQLD_BASEDIR/share/mysql/errmsg.sys + +# The mysql_plugin tool now accepts --my-print-defaults which points to the +# executable my_print_defaults.exe we can get this path from the variable +# $MYSQL_MY_PRINT_DEFAULTS. + +# Check for my_print_defaults location. Skip if not found. +if ($MYSQL_MY_PRINT_DEFAULTS_BASEDIR == '') { - let $MYSQLD_BASEDIR= $MYSQL_BASEDIR/sql/Debug; - --copy_file $MYSQL_BASEDIR/extra/Debug/my_print_defaults.exe $MYSQLD_BASEDIR/my_print_defaults.exe - --mkdir $MYSQLD_BASEDIR/share - --copy_file $MYSQL_BASEDIR/sql/share/english/errmsg.sys $MYSQLD_BASEDIR/share/errmsg.sys - --copy_file $MYSQL_BASEDIR/plugin/daemon_example/daemon_example.ini $DAEMONEXAMPLE_DIR/daemon_example.ini -} -if (`SELECT CONVERT(@@version_compile_os USING latin1) - NOT IN ('Win32','Win64','Windows')`) -{ - let $MYSQLD_BASEDIR= $MYSQL_BASEDIR/sql; - --copy_file $MYSQL_BASEDIR/extra/my_print_defaults $MYSQLD_BASEDIR/my_print_defaults - --copy_file $MYSQL_BASEDIR/sql/share/english/errmsg.sys $MYSQLD_BASEDIR/share/errmsg.sys + --skip Test requires known location of my_print_defaults executable. } -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR --plugin-dir=$DAEMONEXAMPLE_DIR; +# Check for mysqld location. Skip if not found. +if ($MYSQLD == '') +{ + --skip Test requires known location of mysqld executable. +} + +# Check for daemon_example.ini location. Skip if not found in either +# the plugin_dir path or the daemon_example_dir path. +if ($PLUGIN_DIR == '') +{ + if ($DAEMONEXAMPLE_DIR == '') + { + --skip Test requires known location of daemon_example.ini file. + } + let $PLUGIN_DIR = $DAEMONEXAMPLE_DIR; +} + +# Build client command for reuse. + +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --echo # --echo # Ensure the plugin isn't loaded. @@ -52,11 +124,11 @@ EOF # # Enable the plugin # ---exec $MYSQLD_BOOTSTRAP_CMD ENABLE daemon_example +--exec $MYSQL_PLUGIN_CMD ENABLE daemon_example # # Ensure enabling an enabled plugin doesn't fail ---exec $MYSQLD_BOOTSTRAP_CMD ENABLE daemon_example +--exec $MYSQL_PLUGIN_CMD ENABLE daemon_example # # Restart the server @@ -88,7 +160,7 @@ EOF # # Disable the plugin # ---exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example +--exec $MYSQL_PLUGIN_CMD DISABLE daemon_example # # Restart the server @@ -121,81 +193,96 @@ EOF --echo # Attempt to load non-existant plugin --echo # --error 1,2,256 ---exec $MYSQLD_BOOTSTRAP_CMD DISABLE NOT_THERE_AT_ALL 2>&1 +--exec $MYSQL_PLUGIN_CMD DISABLE NOT_THERE_AT_ALL 2>&1 --echo # --echo # Attempt to use non-existant plugin.ini file --echo # --error 1,2,7,256 ---exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example --plugin-ini=/NOT/THERE/pi.ini 2>&1 +--exec $MYSQL_PLUGIN_CMD DISABLE daemon_example --plugin-ini=/NOT/THERE/pi.ini 2>&1 --echo # --echo # Attempt to omit the plugin --echo # --error 1,2,256 ---exec $MYSQLD_BOOTSTRAP_CMD DISABLE 2>&1 +--exec $MYSQL_PLUGIN_CMD DISABLE 2>&1 --echo # --echo # Attempt to omit DISABLE|ENABLE --echo # --error 1,2,256 ---exec $MYSQLD_BOOTSTRAP_CMD daemon_example 2>&1 +--exec $MYSQL_PLUGIN_CMD daemon_example 2>&1 --echo # --echo # Attempt to use bad paths - datadir --echo # -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=/data_not_there/ --basedir=$MYSQLD_BASEDIR --plugin-dir=$DAEMONEXAMPLE_DIR; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=/data_not_there/ --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,256 ---exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 +--exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 --echo # --echo # Attempt to use bad paths - basedir --echo # -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=/basedir_not_there/ --plugin-dir=$DAEMONEXAMPLE_DIR; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=/basedir_not_there/ --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,256 ---exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 +--exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 --echo # --echo # Attempt to use bad paths - plugin_dir --echo # -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR --plugin-dir=/plugin_not_there/; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQL_BASEDIR --plugin-dir=/plugin_not_there/ --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,256 ---exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 +--exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 + +--echo # +--echo # Attempt to use bad paths - mysqld +--echo # +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --mysqld=/mysqld_not_there/ --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; +--error 1,2,256 +--exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 + +--echo # +--echo # Attempt to use bad paths - my_print_defaults +--echo # +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD --my-print-defaults=/my_print_defaults_not_there/; +--error 1,2,256 +--exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 + --echo # --echo # Missing library --echo # -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR --plugin-dir=$DAEMONEXAMPLE_DIR --plugin-ini=$MYSQL_TEST_DIR/include/daemon_example_bad_soname.ini; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --plugin-ini=$MYSQL_TEST_DIR/include/daemon_example_bad_soname.ini --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,256 ---exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 +--exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 --echo # --echo # Bad format for config file --echo # -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR --plugin-dir=$DAEMONEXAMPLE_DIR --plugin-ini=$MYSQL_TEST_DIR/include/daemon_example_bad_format.ini; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --plugin-ini=$MYSQL_TEST_DIR/include/daemon_example_bad_format.ini --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,256 ---exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 +--exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 --echo # --echo # Missing base_dir option --echo # -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --plugin-dir=$DAEMONEXAMPLE_DIR; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,139,256 ---exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 +--exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 --echo # --echo # Missing data_dir option --echo # -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --basedir=$MYSQLD_BASEDIR --plugin-dir=$DAEMONEXAMPLE_DIR; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,139,256 ---exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 +--exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 --echo # --echo # Missing plugin_dir option --echo # -let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQL_DATADIR --basedir=$MYSQL_BASEDIR --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,139,256 ---exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1 +--exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 --echo # --echo # Show the help. @@ -223,18 +310,9 @@ EOF --error 0,1 --remove_file $expect_file -if(`SELECT CONVERT(@@version_compile_os USING latin1) - IN ('Win32','Win64','Windows')`) -{ - --remove_file $DAEMONEXAMPLE_DIR/daemon_example.ini - --remove_file $MYSQLD_BASEDIR/my_print_defaults.exe - --remove_file $MYSQLD_BASEDIR/share/errmsg.sys - --rmdir $MYSQLD_BASEDIR/share -} -if(`SELECT CONVERT(@@version_compile_os USING latin1) - NOT IN ('Win32','Win64','Windows')`) -{ - --remove_file $MYSQLD_BASEDIR/my_print_defaults - --remove_file $MYSQLD_BASEDIR/share/errmsg.sys -} +# Cleanup the share folder in the binary path. +--remove_file $MYSQLD_BASEDIR/share/errmsg.sys +--rmdir $MYSQLD_BASEDIR/share/mysql +--rmdir $MYSQLD_BASEDIR/share + --enable_abort_on_error From 1b730666d4a1e3874741a472b7c125bc5d704dd8 Mon Sep 17 00:00:00 2001 From: Chuck Bell Date: Tue, 9 Aug 2011 15:09:28 -0400 Subject: [PATCH 24/29] WL#5710 : enable and disable plugins This patch corrects an error in the test to ensure the proper path is used for the --mysqld option. --- mysql-test/t/mysql_plugin.test | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mysql-test/t/mysql_plugin.test b/mysql-test/t/mysql_plugin.test index cad89d9de7a..9e71db1a451 100644 --- a/mysql-test/t/mysql_plugin.test +++ b/mysql-test/t/mysql_plugin.test @@ -101,7 +101,7 @@ if ($PLUGIN_DIR == '') # Build client command for reuse. -let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD_BASEDIR --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --echo # --echo # Ensure the plugin isn't loaded. @@ -216,21 +216,21 @@ EOF --echo # --echo # Attempt to use bad paths - datadir --echo # -let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=/data_not_there/ --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=/data_not_there/ --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD_BASEDIR --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,256 --exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 --echo # --echo # Attempt to use bad paths - basedir --echo # -let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=/basedir_not_there/ --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=/basedir_not_there/ --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD_BASEDIR --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,256 --exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 --echo # --echo # Attempt to use bad paths - plugin_dir --echo # -let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQL_BASEDIR --plugin-dir=/plugin_not_there/ --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQL_BASEDIR --plugin-dir=/plugin_not_there/ --mysqld=$MYSQLD_BASEDIR --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,256 --exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 @@ -244,7 +244,7 @@ let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYS --echo # --echo # Attempt to use bad paths - my_print_defaults --echo # -let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD --my-print-defaults=/my_print_defaults_not_there/; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD_BASEDIR --my-print-defaults=/my_print_defaults_not_there/; --error 1,2,256 --exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 @@ -252,35 +252,35 @@ let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYS --echo # --echo # Missing library --echo # -let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --plugin-ini=$MYSQL_TEST_DIR/include/daemon_example_bad_soname.ini --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --plugin-ini=$MYSQL_TEST_DIR/include/daemon_example_bad_soname.ini --mysqld=$MYSQLD_BASEDIR --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,256 --exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 --echo # --echo # Bad format for config file --echo # -let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --plugin-ini=$MYSQL_TEST_DIR/include/daemon_example_bad_format.ini --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --plugin-ini=$MYSQL_TEST_DIR/include/daemon_example_bad_format.ini --mysqld=$MYSQLD_BASEDIR --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,256 --exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 --echo # --echo # Missing base_dir option --echo # -let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD_BASEDIR --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,139,256 --exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 --echo # --echo # Missing data_dir option --echo # -let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --basedir=$MYSQL_BASEDIR --plugin-dir=$PLUGIN_DIR --mysqld=$MYSQLD_BASEDIR --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,139,256 --exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 --echo # --echo # Missing plugin_dir option --echo # -let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQL_DATADIR --basedir=$MYSQL_BASEDIR --mysqld=$MYSQLD --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; +let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQL_DATADIR --basedir=$MYSQL_BASEDIR --mysqld=$MYSQLD_BASEDIR --my-print-defaults=$MYSQL_MY_PRINT_DEFAULTS_BASEDIR; --error 1,2,139,256 --exec $MYSQL_PLUGIN_CMD DISABLE daemon_example 2>&1 From 7645c5ee90415a15d8ad4ab2772f135bb10a44ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 10 Aug 2011 12:25:24 +0300 Subject: [PATCH 25/29] Bug#12835650 VARCHAR maximum length performance impact row_sel_field_store_in_mysql_format(): Do not pad the unused part of the buffer reserved for a True VARCHAR column (introduced in 5.0.3). Add Valgrind instrumentation ensuring that the unused part will be flagged uninitialized. row_sel_copy_cached_field_for_mysql(): New function: Copy a field that is in the MySQL row format, not copying the unused tail of VARCHAR columns. row_sel_pop_cached_row_for_mysql(): Invoke row_sel_copy_cached_field_for_mysql() for copying fields. When the row is long, copy it field-by-field. rb:715 approved by Inaam Rana --- storage/innobase/row/row0sel.c | 86 ++++++++++++++++++++-------- storage/innodb_plugin/ChangeLog | 5 ++ storage/innodb_plugin/row/row0sel.c | 88 +++++++++++++++++++++-------- 3 files changed, 132 insertions(+), 47 deletions(-) diff --git a/storage/innobase/row/row0sel.c b/storage/innobase/row/row0sel.c index e03d3d79768..9ab6424a012 100644 --- a/storage/innobase/row/row0sel.c +++ b/storage/innobase/row/row0sel.c @@ -2468,6 +2468,8 @@ row_sel_field_store_in_mysql_format( ut_ad(len != UNIV_SQL_NULL); UNIV_MEM_ASSERT_RW(data, len); + UNIV_MEM_ASSERT_W(dest, templ->mysql_col_len); + UNIV_MEM_INVALID(dest, templ->mysql_col_len); if (templ->type == DATA_INT) { /* Convert integer data from Innobase to a little-endian @@ -2502,14 +2504,16 @@ row_sel_field_store_in_mysql_format( dest = row_mysql_store_true_var_len( dest, len, templ->mysql_length_bytes); + /* Copy the actual data. Leave the rest of the + buffer uninitialized. */ + ut_memcpy(dest, data, len); + return; } /* Copy the actual data */ ut_memcpy(dest, data, len); - /* Pad with trailing spaces. We pad with spaces also the - unused end of a >= 5.0.3 true VARCHAR column, just in case - MySQL expects its contents to be deterministic. */ + /* Pad with trailing spaces. */ pad_ptr = dest + len; @@ -3012,6 +3016,39 @@ sel_restore_position_for_mysql( return(TRUE); } +/************************************************************************ +Copies a cached field for MySQL from the fetch cache. */ +static +void +row_sel_copy_cached_field_for_mysql( +/*================================*/ + byte* buf, /* in/out: row buffer */ + byte* cache, /* in: cached row */ + const mysql_row_templ_t*templ) /* in: column template */ +{ + ulint len; + + buf += templ->mysql_col_offset; + cache += templ->mysql_col_offset; + + UNIV_MEM_ASSERT_W(buf, templ->mysql_col_len); + + if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR + && templ->type != DATA_INT) { + /* Check for != DATA_INT to make sure we do + not treat MySQL ENUM or SET as a true VARCHAR! + Find the actual length of the true VARCHAR field. */ + row_mysql_read_true_varchar( + &len, cache, templ->mysql_length_bytes); + len += templ->mysql_length_bytes; + UNIV_MEM_INVALID(buf, templ->mysql_col_len); + } else { + len = templ->mysql_col_len; + } + + ut_memcpy(buf, cache, len); +} + /************************************************************************ Pops a cached row for MySQL from the fetch cache. */ UNIV_INLINE @@ -3028,22 +3065,18 @@ row_sel_pop_cached_row_for_mysql( ut_ad(prebuilt->n_fetch_cached > 0); ut_ad(prebuilt->mysql_prefix_len <= prebuilt->mysql_row_len); + UNIV_MEM_ASSERT_W(buf, prebuilt->mysql_row_len); + + cached_rec = prebuilt->fetch_cache[prebuilt->fetch_cache_first]; + if (UNIV_UNLIKELY(prebuilt->keep_other_fields_on_keyread)) { /* Copy cache record field by field, don't touch fields that are not covered by current key */ - cached_rec = prebuilt->fetch_cache[ - prebuilt->fetch_cache_first]; for (i = 0; i < prebuilt->n_template; i++) { templ = prebuilt->mysql_template + i; -#if 0 /* Some of the cached_rec may legitimately be uninitialized. */ - UNIV_MEM_ASSERT_RW(cached_rec - + templ->mysql_col_offset, - templ->mysql_col_len); -#endif - ut_memcpy(buf + templ->mysql_col_offset, - cached_rec + templ->mysql_col_offset, - templ->mysql_col_len); + row_sel_copy_cached_field_for_mysql( + buf, cached_rec, templ); /* Copy NULL bit of the current field from cached_rec to buf */ if (templ->mysql_null_bit_mask) { @@ -3053,17 +3086,24 @@ row_sel_pop_cached_row_for_mysql( & (byte)templ->mysql_null_bit_mask; } } + } else if (prebuilt->mysql_prefix_len > 63) { + /* The record is long. Copy it field by field, in case + there are some long VARCHAR column of which only a + small length is being used. */ + UNIV_MEM_INVALID(buf, prebuilt->mysql_prefix_len); + + /* First copy the NULL bits. */ + ut_memcpy(buf, cached_rec, prebuilt->null_bitmap_len); + /* Then copy the requested fields. */ + + for (i = 0; i < prebuilt->n_template; i++) { + row_sel_copy_cached_field_for_mysql( + buf, cached_rec, prebuilt->mysql_template + i); + } + } else { + ut_memcpy(buf, cached_rec, prebuilt->mysql_prefix_len); } - else { -#if 0 /* Some of the cached_rec may legitimately be uninitialized. */ - UNIV_MEM_ASSERT_RW(prebuilt->fetch_cache - [prebuilt->fetch_cache_first], - prebuilt->mysql_prefix_len); -#endif - ut_memcpy(buf, - prebuilt->fetch_cache[prebuilt->fetch_cache_first], - prebuilt->mysql_prefix_len); - } + prebuilt->n_fetch_cached--; prebuilt->fetch_cache_first++; diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index d5d1e8d1b66..f873f3a24bd 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,8 @@ +2011-08-08 The InnoDB Team + + * row/row0sel.c: + Fix Bug#12835650 VARCHAR maximum length performance impact + 2011-08-08 The InnoDB Team * handler/ha_innodb.cc: diff --git a/storage/innodb_plugin/row/row0sel.c b/storage/innodb_plugin/row/row0sel.c index 423ddfade22..241584eaa20 100644 --- a/storage/innodb_plugin/row/row0sel.c +++ b/storage/innodb_plugin/row/row0sel.c @@ -2544,6 +2544,8 @@ row_sel_field_store_in_mysql_format( ut_ad(len != UNIV_SQL_NULL); UNIV_MEM_ASSERT_RW(data, len); + UNIV_MEM_ASSERT_W(dest, templ->mysql_col_len); + UNIV_MEM_INVALID(dest, templ->mysql_col_len); switch (templ->type) { case DATA_INT: @@ -2580,14 +2582,16 @@ row_sel_field_store_in_mysql_format( dest = row_mysql_store_true_var_len( dest, len, templ->mysql_length_bytes); + /* Copy the actual data. Leave the rest of the + buffer uninitialized. */ + memcpy(dest, data, len); + break; } /* Copy the actual data */ ut_memcpy(dest, data, len); - /* Pad with trailing spaces. We pad with spaces also the - unused end of a >= 5.0.3 true VARCHAR column, just in case - MySQL expects its contents to be deterministic. */ + /* Pad with trailing spaces. */ pad_ptr = dest + len; @@ -3119,6 +3123,39 @@ sel_restore_position_for_mysql( return(TRUE); } +/********************************************************************//** +Copies a cached field for MySQL from the fetch cache. */ +static +void +row_sel_copy_cached_field_for_mysql( +/*================================*/ + byte* buf, /*!< in/out: row buffer */ + const byte* cache, /*!< in: cached row */ + const mysql_row_templ_t*templ) /*!< in: column template */ +{ + ulint len; + + buf += templ->mysql_col_offset; + cache += templ->mysql_col_offset; + + UNIV_MEM_ASSERT_W(buf, templ->mysql_col_len); + + if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR + && templ->type != DATA_INT) { + /* Check for != DATA_INT to make sure we do + not treat MySQL ENUM or SET as a true VARCHAR! + Find the actual length of the true VARCHAR field. */ + row_mysql_read_true_varchar( + &len, cache, templ->mysql_length_bytes); + len += templ->mysql_length_bytes; + UNIV_MEM_INVALID(buf, templ->mysql_col_len); + } else { + len = templ->mysql_col_len; + } + + ut_memcpy(buf, cache, len); +} + /********************************************************************//** Pops a cached row for MySQL from the fetch cache. */ UNIV_INLINE @@ -3131,26 +3168,22 @@ row_sel_pop_cached_row_for_mysql( { ulint i; const mysql_row_templ_t*templ; - byte* cached_rec; + const byte* cached_rec; ut_ad(prebuilt->n_fetch_cached > 0); ut_ad(prebuilt->mysql_prefix_len <= prebuilt->mysql_row_len); + UNIV_MEM_ASSERT_W(buf, prebuilt->mysql_row_len); + + cached_rec = prebuilt->fetch_cache[prebuilt->fetch_cache_first]; + if (UNIV_UNLIKELY(prebuilt->keep_other_fields_on_keyread)) { /* Copy cache record field by field, don't touch fields that are not covered by current key */ - cached_rec = prebuilt->fetch_cache[ - prebuilt->fetch_cache_first]; for (i = 0; i < prebuilt->n_template; i++) { templ = prebuilt->mysql_template + i; -#if 0 /* Some of the cached_rec may legitimately be uninitialized. */ - UNIV_MEM_ASSERT_RW(cached_rec - + templ->mysql_col_offset, - templ->mysql_col_len); -#endif - ut_memcpy(buf + templ->mysql_col_offset, - cached_rec + templ->mysql_col_offset, - templ->mysql_col_len); + row_sel_copy_cached_field_for_mysql( + buf, cached_rec, templ); /* Copy NULL bit of the current field from cached_rec to buf */ if (templ->mysql_null_bit_mask) { @@ -3160,17 +3193,24 @@ row_sel_pop_cached_row_for_mysql( & (byte)templ->mysql_null_bit_mask; } } + } else if (prebuilt->mysql_prefix_len > 63) { + /* The record is long. Copy it field by field, in case + there are some long VARCHAR column of which only a + small length is being used. */ + UNIV_MEM_INVALID(buf, prebuilt->mysql_prefix_len); + + /* First copy the NULL bits. */ + ut_memcpy(buf, cached_rec, prebuilt->null_bitmap_len); + /* Then copy the requested fields. */ + + for (i = 0; i < prebuilt->n_template; i++) { + row_sel_copy_cached_field_for_mysql( + buf, cached_rec, prebuilt->mysql_template + i); + } + } else { + ut_memcpy(buf, cached_rec, prebuilt->mysql_prefix_len); } - else { -#if 0 /* Some of the cached_rec may legitimately be uninitialized. */ - UNIV_MEM_ASSERT_RW(prebuilt->fetch_cache - [prebuilt->fetch_cache_first], - prebuilt->mysql_prefix_len); -#endif - ut_memcpy(buf, - prebuilt->fetch_cache[prebuilt->fetch_cache_first], - prebuilt->mysql_prefix_len); - } + prebuilt->n_fetch_cached--; prebuilt->fetch_cache_first++; From 00dad1c086a3dfedb36af237ba324925831f22c4 Mon Sep 17 00:00:00 2001 From: Tatjana Azundris Nuernberg Date: Thu, 11 Aug 2011 09:34:16 +0100 Subject: [PATCH 26/29] maintain behavior introduced in fix for MySQL bug 35765 (avert regression) --- sql/sql_yacc.yy | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ff2f2a8f273..700995d5bb6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5036,8 +5036,7 @@ create_table_option: ENGINE_SYM opt_equal storage_engines { Lex->create_info.db_type= $3; - if ($3) - Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; + Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; } | MAX_ROWS opt_equal ulonglong_num { @@ -6748,6 +6747,11 @@ alter_list_item: { LEX *lex=Lex; lex->alter_info.flags|= ALTER_OPTIONS; + if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && + !lex->create_info.db_type) + { + lex->create_info.used_fields&= ~HA_CREATE_USED_ENGINE; + } } | FORCE_SYM { From ea4de078af914cd89d3414c62f197c32e73353d7 Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Thu, 11 Aug 2011 19:58:49 +0400 Subject: [PATCH 27/29] Fix for bug #12828477 - "MDL SUBSYSTEM CREATES BIG OVERHEAD FOR CERTAIN QUERIES TO INFORMATION_SCHEMA". The problem was that metadata locking subsystem introduced too much overhead for queries to I_S which were processed by opening only .FRM or .TRG files and had to scanned a lot of tables (e.g. SELECT COUNT(*) FROM I_S.TRIGGERS was affected). The same effect was not observed for similar queries which performed full-blown table open in order to fill I_S table. The problem stemmed from the fact that in case when I_S implementation opened only .FRM or .TRG file for each table processed it didn't release metadata lock it has acquired on the table after finishing its processing. As result, list of acquired metadata locks were growing until the end of statement. Since acquisition of each new lock required search in the list of already acquired locks performance degraded. The same effect is not observed when I_S implementation performs full-blown table open for each table being processed, as in the latter cases metadata lock on the table is released right after table processing. This fix addressed the problem by ensuring that I_S implementation releases metadata lock after processing the table in both cases of full-blown table open and in case when only .FRM or .TRG file is read. mysql-test/r/information_schema.result: Added coverage for bug #12828477 - "MDL SUBSYSTEM CREATES BIG OVERHEAD FOR CERTAIN QUERIES TO INFORMATION_SCHEMA". mysql-test/t/information_schema.test: Added coverage for bug #12828477 - "MDL SUBSYSTEM CREATES BIG OVERHEAD FOR CERTAIN QUERIES TO INFORMATION_SCHEMA". sql/sql_show.cc: Changed fill_schema_table_from_frm() to release metadata lock it has acquired after processing the .FRM or .TRG file for table. Without this step metadata locks acquired for each table processed will be accumulated. In situation when a lot of tables are processed by I_S query this will result in transaction with too many metadata locks. As result performance of acquisition of new lock will degrade. --- mysql-test/r/information_schema.result | 114 ++++++++++++++++ mysql-test/t/information_schema.test | 182 ++++++++++++++++++++++++- sql/sql_show.cc | 33 ++++- 3 files changed, 322 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 1418af5b813..88a56edbb5c 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -1849,5 +1849,119 @@ unlock tables; drop table t1; drop view v1; # +# Test for bug #12828477 - "MDL SUBSYSTEM CREATES BIG OVERHEAD FOR +# CERTAIN QUERIES TO INFORMATION_SCHEMA". +# +# Check that metadata locks which are acquired during the process +# of opening tables/.FRMs/.TRG files while filling I_S table are +# not kept to the end of statement. Keeping the locks has caused +# performance problems in cases when big number of tables (.FRMs +# or .TRG files) were scanned as cost of new lock acquisition has +# increased linearly. +drop database if exists mysqltest; +create database mysqltest; +use mysqltest; +create table t0 (i int); +create table t1 (j int); +create table t2 (k int); +# +# Test that we don't keep locks in case when we to fill +# I_S table we perform full-blown table open. +# +# Acquire lock on 't2' so upcoming RENAME is +# blocked. +lock tables t2 read; +# +# Switching to connection 'con12828477_1'. +# +# The below RENAME should wait on 't2' while +# keeping X lock on 't1'. +rename table t1 to t3, t2 to t1, t3 to t2; +# +# Switching to connection 'con12828477_2'. +# +# Wait while the above RENAME is blocked. +# Issue query to I_S which will open 't0' and get +# blocked on 't1' because of RENAME. +select table_name, auto_increment from information_schema.tables where table_schema='mysqltest'; +# +# Switching to connection 'con12828477_3'. +# +# Wait while the above SELECT is blocked. +# +# Check that it holds no lock on 't0' so it can be renamed. +rename table t0 to t4; +# +# Switching to connection 'default'. +# +# +# Unblock the first RENAME. +unlock tables; +# +# Switching to connection 'con12828477_1'. +# +# Reap the first RENAME +# +# Switching to connection 'con12828477_2'. +# +# Reap SELECT to I_S. +table_name auto_increment +t0 NULL +t1 NULL +t2 NULL +# +# Switching to connection 'default'. +# +# +# Now test that we don't keep locks in case when we to fill +# I_S table we read .FRM or .TRG file only (this was the case +# for which problem existed). +# +rename table t4 to t0; +# Acquire lock on 't2' so upcoming RENAME is +# blocked. +lock tables t2 read; +# +# Switching to connection 'con12828477_1'. +# +# The below RENAME should wait on 't2' while +# keeping X lock on 't1'. +rename table t1 to t3, t2 to t1, t3 to t2; +# +# Switching to connection 'con12828477_2'. +# +# Wait while the above RENAME is blocked. +# Issue query to I_S which will open 't0' and get +# blocked on 't1' because of RENAME. +select event_object_table, trigger_name from information_schema.triggers where event_object_schema='mysqltest'; +# +# Switching to connection 'con12828477_3'. +# +# Wait while the above SELECT is blocked. +# +# Check that it holds no lock on 't0' so it can be renamed. +rename table t0 to t4; +# +# Switching to connection 'default'. +# +# +# Unblock the first RENAME. +unlock tables; +# +# Switching to connection 'con12828477_1'. +# +# Reap the first RENAME +# +# Switching to connection 'con12828477_2'. +# +# Reap SELECT to I_S. +event_object_table trigger_name +# +# Switching to connection 'default'. +# +# +# Clean-up. +drop database mysqltest; +# # End of 5.5 tests # diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index 589bb898d6a..e6dc989bdb9 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -1543,8 +1543,6 @@ DROP TABLE t1, information_schema.tables; LOCK TABLES t1 READ, information_schema.tables READ; DROP TABLE t1; -# Wait till all disconnects are completed ---source include/wait_until_count_sessions.inc # # Bug #43834 Assertion in Natural_join_column::db_name() on an I_S query @@ -1608,6 +1606,186 @@ drop table t1; drop view v1; +--echo # +--echo # Test for bug #12828477 - "MDL SUBSYSTEM CREATES BIG OVERHEAD FOR +--echo # CERTAIN QUERIES TO INFORMATION_SCHEMA". +--echo # +--echo # Check that metadata locks which are acquired during the process +--echo # of opening tables/.FRMs/.TRG files while filling I_S table are +--echo # not kept to the end of statement. Keeping the locks has caused +--echo # performance problems in cases when big number of tables (.FRMs +--echo # or .TRG files) were scanned as cost of new lock acquisition has +--echo # increased linearly. +--disable_warnings +drop database if exists mysqltest; +--enable_warnings +create database mysqltest; +use mysqltest; +create table t0 (i int); +create table t1 (j int); +create table t2 (k int); + +--echo # +--echo # Test that we don't keep locks in case when we to fill +--echo # I_S table we perform full-blown table open. +--echo # + +--echo # Acquire lock on 't2' so upcoming RENAME is +--echo # blocked. +lock tables t2 read; + +--echo # +--echo # Switching to connection 'con12828477_1'. +--echo # +connect (con12828477_1, localhost, root,,mysqltest); +--echo # The below RENAME should wait on 't2' while +--echo # keeping X lock on 't1'. +--send rename table t1 to t3, t2 to t1, t3 to t2 + +--echo # +--echo # Switching to connection 'con12828477_2'. +--echo # +connect (con12828477_2, localhost, root,,mysqltest); +--echo # Wait while the above RENAME is blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table metadata lock" and + info = "rename table t1 to t3, t2 to t1, t3 to t2"; +--source include/wait_condition.inc + +--echo # Issue query to I_S which will open 't0' and get +--echo # blocked on 't1' because of RENAME. +--send select table_name, auto_increment from information_schema.tables where table_schema='mysqltest' + +--echo # +--echo # Switching to connection 'con12828477_3'. +--echo # +connect (con12828477_3, localhost, root,,mysqltest); +--echo # Wait while the above SELECT is blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table metadata lock" and + info = "select table_name, auto_increment from information_schema.tables where table_schema='mysqltest'"; +--source include/wait_condition.inc + +--echo # +--echo # Check that it holds no lock on 't0' so it can be renamed. +rename table t0 to t4; + +--echo # +--echo # Switching to connection 'default'. +--echo # +connection default; +--echo # +--echo # Unblock the first RENAME. +unlock tables; + +--echo # +--echo # Switching to connection 'con12828477_1'. +--echo # +connection con12828477_1; +--echo # Reap the first RENAME +--reap + +--echo # +--echo # Switching to connection 'con12828477_2'. +--echo # +connection con12828477_2; +--echo # Reap SELECT to I_S. +--reap + +--echo # +--echo # Switching to connection 'default'. +--echo # +connection default; + +--echo # +--echo # Now test that we don't keep locks in case when we to fill +--echo # I_S table we read .FRM or .TRG file only (this was the case +--echo # for which problem existed). +--echo # + +rename table t4 to t0; +--echo # Acquire lock on 't2' so upcoming RENAME is +--echo # blocked. +lock tables t2 read; + +--echo # +--echo # Switching to connection 'con12828477_1'. +--echo # +connection con12828477_1; +--echo # The below RENAME should wait on 't2' while +--echo # keeping X lock on 't1'. +--send rename table t1 to t3, t2 to t1, t3 to t2 + +--echo # +--echo # Switching to connection 'con12828477_2'. +--echo # +connection con12828477_2; +--echo # Wait while the above RENAME is blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table metadata lock" and + info = "rename table t1 to t3, t2 to t1, t3 to t2"; +--source include/wait_condition.inc + +--echo # Issue query to I_S which will open 't0' and get +--echo # blocked on 't1' because of RENAME. +--send select event_object_table, trigger_name from information_schema.triggers where event_object_schema='mysqltest' + +--echo # +--echo # Switching to connection 'con12828477_3'. +--echo # +connection con12828477_3; +--echo # Wait while the above SELECT is blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table metadata lock" and + info = "select event_object_table, trigger_name from information_schema.triggers where event_object_schema='mysqltest'"; +--source include/wait_condition.inc + +--echo # +--echo # Check that it holds no lock on 't0' so it can be renamed. +rename table t0 to t4; + +--echo # +--echo # Switching to connection 'default'. +--echo # +connection default; +--echo # +--echo # Unblock the first RENAME. +unlock tables; + +--echo # +--echo # Switching to connection 'con12828477_1'. +--echo # +connection con12828477_1; +--echo # Reap the first RENAME +--reap + +--echo # +--echo # Switching to connection 'con12828477_2'. +--echo # +connection con12828477_2; +--echo # Reap SELECT to I_S. +--reap + +--echo # +--echo # Switching to connection 'default'. +--echo # +connection default; +disconnect con12828477_1; +disconnect con12828477_2; +disconnect con12828477_3; + +--echo # +--echo # Clean-up. +drop database mysqltest; + + --echo # --echo # End of 5.5 tests --echo # + +# Wait till all disconnects are completed +--source include/wait_until_count_sessions.inc diff --git a/sql/sql_show.cc b/sql/sql_show.cc index fb5ed62ecdf..887115b38ad 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3157,6 +3157,10 @@ end: */ thd->temporary_tables= NULL; close_thread_tables(thd); + /* + Release metadata lock we might have acquired. + See comment in fill_schema_table_from_frm() for details. + */ thd->mdl_context.rollback_to_savepoint(open_tables_state_backup->mdl_system_tables_svp); thd->lex= old_lex; @@ -3339,6 +3343,9 @@ try_acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table, @param[in] db_name database name @param[in] table_name table name @param[in] schema_table_idx I_S table index + @param[in] open_tables_state_backup Open_tables_state object which is used + to save/restore original state of metadata + locks. @param[in] can_deadlock Indicates that deadlocks are possible due to metadata locks, so to avoid them we should not wait in case if @@ -3356,6 +3363,7 @@ static int fill_schema_table_from_frm(THD *thd, TABLE_LIST *tables, LEX_STRING *db_name, LEX_STRING *table_name, enum enum_schema_tables schema_table_idx, + Open_tables_backup *open_tables_state_backup, bool can_deadlock) { TABLE *table= tables->table; @@ -3501,13 +3509,27 @@ end_share: end_unlock: mysql_mutex_unlock(&LOCK_open); - /* - Don't release the MDL lock, it can be part of a transaction. - If it is not, it will be released by the call to - MDL_context::rollback_to_savepoint() in the caller. - */ end: + /* + Release metadata lock we might have acquired. + + Without this step metadata locks acquired for each table processed + will be accumulated. In situation when a lot of tables are processed + by I_S query this will result in transaction with too many metadata + locks. As result performance of acquisition of new lock will suffer. + + Of course, the fact that we don't hold metadata lock on tables which + were processed till the end of I_S query makes execution less isolated + from concurrent DDL. Consequently one might get 'dirty' results from + such a query. But we have never promised serializability of I_S queries + anyway. + + We don't have any tables open since we took backup, so rolling back to + savepoint is safe. + */ + DBUG_ASSERT(thd->open_tables == NULL); + thd->mdl_context.rollback_to_savepoint(open_tables_state_backup->mdl_system_tables_svp); thd->clear_error(); return res; } @@ -3758,6 +3780,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) int res= fill_schema_table_from_frm(thd, tables, schema_table, db_name, table_name, schema_table_idx, + &open_tables_state_backup, can_deadlock); thd->pop_internal_handler(); From dfbe4b3367567e1e02b02bba9e57613a072850b6 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Fri, 12 Aug 2011 12:49:38 +0200 Subject: [PATCH 28/29] Small followup fix to WL 5710: Test mysql_plugin failed if version string ended in -mN --- mysql-test/t/mysql_plugin.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/t/mysql_plugin.test b/mysql-test/t/mysql_plugin.test index 9e71db1a451..b07ce40e43a 100644 --- a/mysql-test/t/mysql_plugin.test +++ b/mysql-test/t/mysql_plugin.test @@ -288,11 +288,11 @@ let $MYSQL_PLUGIN_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQL_DATADIR --basedir=$MYSQ --echo # Show the help. --echo # replace_result $MYSQL_PLUGIN mysql_plugin; ---replace_regex /Ver [0-9.]+ Distrib [0-9.]+/Ver V.V.VV Distrib XX.XX.XX/ +--replace_regex /Ver [0-9.]+ Distrib [0-9.]+/Ver V.V.VV Distrib XX.XX.XX/ /XX-m[0-9]+/XX/ --exec $MYSQL_PLUGIN --help replace_result $MYSQL_PLUGIN mysql_plugin; ---replace_regex /Ver [0-9.]+ Distrib [0-9.]+/Ver V.V.VV Distrib XX.XX.XX/ +--replace_regex /Ver [0-9.]+ Distrib [0-9.]+/Ver V.V.VV Distrib XX.XX.XX/ /XX-m[0-9]+/XX/ --exec $MYSQL_PLUGIN --version # From a79cabcfefbf79648597c65de559880f78ae0563 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Fri, 12 Aug 2011 14:20:17 +0300 Subject: [PATCH 29/29] bumped the version to 5.1.60 --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 0e336c5522b..e56a18540b5 100644 --- a/configure.in +++ b/configure.in @@ -12,7 +12,7 @@ dnl dnl When changing the major version number please also check the switch dnl statement in mysqlbinlog::check_master_version(). You may also need dnl to update version.c in ndb. -AC_INIT([MySQL Server], [5.1.59], [], [mysql]) +AC_INIT([MySQL Server], [5.1.60], [], [mysql]) AC_CONFIG_SRCDIR([sql/mysqld.cc]) AC_CANONICAL_SYSTEM