# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: chuck.bell@oracle.com-20110124162403-eig0ibfc4uikjyw1 # target_branch: file:///Users/cbell/source/bzr/mysql-5.5/ # testament_sha1: 3f1bd8eec96add71807961b64272786ee79ad105 # timestamp: 2011-01-24 11:24:54 -0500 # base_revision_id: martin.hansson@oracle.com-20110120122811-\ # ko4u8sf72seqzw3h # # Begin patch === modified file 'client/CMakeLists.txt' --- client/CMakeLists.txt 2010-09-20 14:17:32 +0000 +++ client/CMakeLists.txt 2011-01-24 16:24:03 +0000 @@ -54,6 +54,9 @@ 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 @@ 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) === added file 'client/mysql_plugin.c' --- client/mysql_plugin.c 1970-01-01 00:00:00 +0000 +++ client/mysql_plugin.c 2011-01-24 16:24:03 +0000 @@ -0,0 +1,549 @@ +/* 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 + +#define SHOW_VERSION "1.0.0" +#define APPLICATION FN_DIRSEP "mysql_feature" FN_EXEEXT +#define PRINT_VERSION do { printf("%s Ver %s Distrib %s, for %s (%s)\n", \ + APPLICATION, SHOW_VERSION, MYSQL_SERVER_VERSION, \ + SYSTEM_TYPE,MACHINE_TYPE); \ + } while(0) + +/* Global variables. */ +static uint my_end_arg= 0; +static uint opt_verbose=0; +static char *opt_datadir=0, *opt_basedir=0, *opt_plugin_dir=0; +static char bootstrap[L_tmpnam]; + +/* feature struct */ +struct st_feature +{ + const char *name; /* plugin name */ + const char *so_name; /* plugin so (library) name */ + const char *symbols[16]; /* symbols to load */ +}; + +/* + Features (plugins) supported. + + NOTE: To add a new feature, add rows to the #define below to define + and add a new element to this array. The code is designed to act + on these entries to insert/delete the correct values in the + mysql.plugin table. +*/ +static struct st_feature features_supported[]= { + { + "thread_pool", "thread_pool" FN_SOEXT, + { + "thread_pool", + "TP_THREAD_STATE", + "TP_THREAD_GROUP_STATE", + "TP_THREAD_GROUP_STATS", + } + }, + { + "daemon_example", "libdaemon_example" FN_SOEXT, + { + "daemon_example", + } + } +}; + +/* 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}, + {"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} +}; + +static const char *load_default_groups[]= { + "mysqld","server", MYSQL_BASE_VERSION, 0, 0 +}; + +/* Methods */ +static my_bool +get_one_option(int optid, const struct my_option *opt __attribute__((unused)), + char *argument); +static int check_options(int argc, char **argv, char *operation, + struct st_feature **feature); +static int find_tool(const char *tool_name, char *tool_path); +static int find_plugin(struct st_feature *feature, char *tp_path); +static int run_command(char* cmd, char *mode); + + +int main(int argc,char *argv[]) +{ + int i= 0; + int error= 0; + int ret= 0; + FILE *file; + char tp_path[FN_REFLEN]; + char server_path[FN_REFLEN]; + char query_str[512]; + char operation[16]; + char bootstrap_cmd[FN_REFLEN]; + struct st_feature *feature = 0; + + MY_INIT(argv[0]); + + /* get a temporary file name for the bootstrap file */ + tmpnam(bootstrap); + + /* Process options */ + if ((error= handle_options(&argc, &argv, my_long_options, get_one_option))) + { + fprintf(stderr, "ERROR: Cannot process options. Error code: %d.\n", error); + goto exit; + } + + load_defaults("my", load_default_groups, &argc, &argv); + if ((error = check_options(argc, argv, operation, &feature))) + { + goto exit; + } + if (!feature) + { + fprintf(stderr, "ERROR: No features specified.\n"); + goto exit; + } + + i= 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); + } + + /* Look for the utilities we need: mysqladmin, mysqld, and mysql. */ + if (!find_tool("mysqld" FN_EXEEXT, server_path)) + { + fprintf(stderr, "ERROR: Cannot find dependent tools. Ensure basedir is " + "set correctly.\n"); + error= 1; + goto exit; + } + + /* Look for the plugin. Abort if not found. */ + if ((error= find_plugin(feature, tp_path))) + { + fprintf(stderr, "ERROR: The plugin library %s is missing or in a different" + " location.\n", tp_path); + goto exit; + } + + /* Create bootstrap file. */ + file= fopen(bootstrap, "w+"); + if (strcasecmp(operation, "enable") == 0) + { + /* Here we must do a 'safe' enable in case feature is already enabled. */ + int i= 0; + fprintf(file, "INSERT IGNORE INTO mysql.plugin VALUES "); + for (i= 0; i < array_elements(feature->symbols) && + feature->symbols[i]; i++) + { + if (i > 0) + { + fprintf(file, ", "); + } + fprintf(file, "('%s','%s')", feature->symbols[i], feature->so_name); + } + fprintf(file, ";\n"); + if (opt_verbose) + { + printf("# Enabling %s...\n", feature->name); + } + } + else + { + fprintf(file, + "DELETE FROM mysql.plugin WHERE name = '%s';", feature->name); + { + printf("# Disabling %s...\n", feature->name); + } + } + fclose(file); + if (opt_verbose) + { + file = fopen(bootstrap, "r"); + fgets(query_str, 512, file); + printf("# Query: %s", query_str); + fclose(file); + } + + /* Build bootstrap command. */ + snprintf(bootstrap_cmd, sizeof(bootstrap_cmd), + "%s --no-defaults --bootstrap --datadir=%s --basedir=%s" + " < %s", server_path, opt_datadir, opt_basedir, bootstrap); + + /* Execute the command */ + if (opt_verbose) + { + printf("# Command: %s\n", bootstrap_cmd); + } + ret= run_command(bootstrap_cmd, "r"); + if (ret != 0) + { + fprintf(stderr, + "ERROR: Unexpected result from bootstrap. Error code: %d.\n", + ret); + error= 1; + } + + /* Remove file */ + my_delete(bootstrap, MYF(0)); + if (opt_verbose && error == 0) + { + printf("# Operation succeeded.\n"); + } + +exit: + my_end(my_end_arg); + exit(error ? 1 : 0); + return 0; /* No compiler warnings */ +} + +/** + Print usage. +*/ +static void usage(void) +{ + int i= 0; + + PRINT_VERSION; + puts("Copyright (c) 2011, Oracle and/or its affiliates. " + "All rights reserved.\n"); + puts("Enable or disable plugin features.\n"); + puts("Features supported:"); + for (i= 0; i < array_elements(features_supported); i++) + { + printf(" %s\n", features_supported[i].name); + } + printf("\nUsage: %s [options] ENABLE|DISABLE\n\nOptions:\n", + APPLICATION); + my_print_help(my_long_options); + print_defaults("my", load_default_groups); + puts("\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 '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; + } + return 0; +} + + +/** + Run a command in a shell. + + This function will attempt to execute the command specified. + + @param[in] cmd The command to execute. + @param[in] mode The mode for popen() (e.g. "r", "w", "rw") + + @reeturn int error code or 0 for success. +*/ +static int run_command(char* cmd, 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; +} + + +/** + 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(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(char *base_path, const char *tool_name, + char *tool_path) +{ + int i= 0; + + static const char *paths[]= { + "", "/share/", "/scripts/", "/bin/", "/libexec/", "/mysql/", "/sql/", + }; + for (i = 0 ; i < array_elements(paths); i++) + { + if (search_dir(base_path, tool_name, paths[i], tool_path)) + return 1; + } + return 0; +} + + +/** + 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; + + char *paths[]= { + opt_basedir, "/usr/local/mysql", "/usr/sbin", "/usr/share", + }; + for (i= 0; i < array_elements(paths); i++) + { + if (search_paths(paths[i], tool_name, tool_path)) + goto found; + } + return 0; +found: + if (opt_verbose) + printf("# Found tool '%s' as '%s'.\n", tool_name, tool_path); + 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) + @param[out] feature The feature selected + + @retval int error = 1, success = 0 + */ +static int check_options(int argc, char **argv, char *operation, + struct st_feature **feature) +{ + int i= 0; // loop counter + int num_found= 0; // number of options found (shortcut loop) + + if (argc == 0) + { + fprintf(stderr, "ERROR: No argument specified. Please specify " + "'ENABLE' or 'DISABLE'.\n"); + return 1; + } + for (i = 0; i < argc && num_found < 5; i++) + { + if ((strcasecmp(argv[i], "ENABLE") == 0) || + (strcasecmp(argv[i], "DISABLE") == 0)) + { + strcpy(operation, argv[i]); + num_found++; + } + else if ((strncasecmp(argv[i], "--basedir=", 10) == 0) && + !opt_basedir) + { + opt_basedir= my_strndup(argv[i]+10, strlen(argv[i])-10, MYF(MY_FAE)); + num_found++; + } + else if ((strncasecmp(argv[i], "--datadir=", 10) == 0) && + !opt_datadir) + { + opt_datadir= my_strndup(argv[i]+10, strlen(argv[i])-10, MYF(MY_FAE)); + num_found++; + } + else if ((strncasecmp(argv[i], "--plugin-dir=", 13) == 0) && + !opt_plugin_dir) + { + opt_plugin_dir= my_strndup(argv[i]+13, strlen(argv[i])-13, MYF(MY_FAE)); + num_found++; + } + else + { + int j= 0; + for (j= 0; j < array_elements(features_supported); j++) + { + if (strcasecmp(features_supported[j].name, argv[i]) == 0) + { + *feature = &features_supported[j]; + num_found++; + } + } + } + } + + 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; + } + + 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[in] feature The feature to find. + @param[out] tp_path The actual path to plugin with FN_SOEXT applied. + + @retval int error = 1, success = 0 +*/ +static int find_plugin(struct st_feature *feature, char *tp_path) +{ + /* Check for existance of plugin */ + fn_format(tp_path, feature->so_name, opt_plugin_dir, 0, MYF(0)); + if (!file_exists(tp_path)) + { + return 1; + } + else if (opt_verbose) + { + printf("# Found plugin '%s' as '%s'\n", feature->name, tp_path); + } + return 0; +} === modified file 'include/my_global.h' --- include/my_global.h 2011-01-11 09:07:37 +0000 +++ include/my_global.h 2011-01-24 16:24:03 +0000 @@ -605,6 +605,8 @@ #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 */ @@ -613,6 +615,8 @@ #define FN_LIBCHAR '/' #define FN_LIBCHAR2 '/' #define FN_DIRSEP "/" /* Valid directory separators */ +#define FN_EXEEXT "" +#define FN_SOEXT ".so" #define FN_ROOTDIR "/" #endif === modified file 'mysql-test/mysql-test-run.pl' --- mysql-test/mysql-test-run.pl 2011-01-18 10:21:37 +0000 +++ mysql-test/mysql-test-run.pl 2011-01-24 16:24:03 +0000 @@ -166,6 +166,7 @@ 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; @@ -1806,6 +1807,7 @@ # 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"); if ( ! $opt_skip_ndbcluster ) { @@ -2070,6 +2072,19 @@ } # -------------------------------------------------------------------------- + # Add the path where daemon_example can be found + # -------------------------------------------------------------------------- + if (my $lib_plugin= find_plugin("libdaemon_example", "plugin/daemon_example")) + { + my $lib_dirname= dirname($lib_plugin); + $ENV{'DAEMONEXAMPLE_PLUGIN_DIR'}= $lib_dirname; + } + else + { + $ENV{'DAEMON_EXAMPLE_PLUGIN_DIR'}=""; + } + + # -------------------------------------------------------------------------- # Read definitions from include/plugin.defs # # Plugin settings should no longer be added here, instead @@ -2192,6 +2207,7 @@ $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; # ---------------------------------------------------- # bug25714 executable may _not_ exist in === added file 'mysql-test/r/mysql_plugin.result' --- mysql-test/r/mysql_plugin.result 1970-01-01 00:00:00 +0000 +++ mysql-test/r/mysql_plugin.result 2011-01-24 16:24:03 +0000 @@ -0,0 +1,23 @@ +# +# 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... +# +# Disabling daemon_example... +# +# Ensure the plugin isn't loaded. +# +SELECT * FROM mysql.plugin WHERE name = 'daemon_example' ORDER BY name; +name dl === added file 'mysql-test/t/mysql_plugin-master.opt' --- mysql-test/t/mysql_plugin-master.opt 1970-01-01 00:00:00 +0000 +++ mysql-test/t/mysql_plugin-master.opt 2011-01-24 16:24:03 +0000 @@ -0,0 +1,1 @@ +--plugin-dir=$DAEMONEXAMPLE_PLUGIN_DIR === added file 'mysql-test/t/mysql_plugin.test' --- mysql-test/t/mysql_plugin.test 1970-01-01 00:00:00 +0000 +++ mysql-test/t/mysql_plugin.test 2011-01-24 16:24:03 +0000 @@ -0,0 +1,84 @@ +# +# 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_PLUGIN_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 # +--write_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +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 $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +restart +EOF +--enable_reconnect +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--source include/wait_until_connected_again.inc + +--echo # +--echo # Ensure the plugin is now loaded. +--echo # +--replace_regex /\.dll/.so/ +eval SELECT * FROM mysql.plugin WHERE name = 'daemon_example' ORDER BY name; + +--echo # +--echo # Disable the plugin... +--echo # +--write_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +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 $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +restart +EOF +--enable_reconnect +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--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; # Begin bundle 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 Aae0YIA=