From f814e19d3d79ece91fec0cbd1626ccc31446fe2d Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 3 Nov 2005 19:00:37 +0100 Subject: [PATCH] WL#2930 Adding view and cursor 'protocols' to mysqltest - Part2, actually adding the new functionality client/mysqltest.c: adding the new functionality run with views, cursor and stored procedures mysql-test/mysql-test-run.pl: Adding new switches --- client/mysqltest.c | 353 ++++++++++++++++++++++++++--------- mysql-test/mysql-test-run.pl | 25 +++ 2 files changed, 290 insertions(+), 88 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index b5a0f65f55c..1b24d44bdf3 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -42,7 +42,7 @@ **********************************************************************/ -#define MTEST_VERSION "2.5" +#define MTEST_VERSION "2.6" #include #include @@ -108,7 +108,8 @@ enum {OPT_MANAGER_USER=256,OPT_MANAGER_HOST,OPT_MANAGER_PASSWD, OPT_MANAGER_PORT,OPT_MANAGER_WAIT_TIMEOUT, OPT_SKIP_SAFEMALLOC, OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, OPT_SSL_CAPATH, - OPT_SSL_CIPHER,OPT_PS_PROTOCOL}; + OPT_SSL_CIPHER,OPT_PS_PROTOCOL,OPT_SP_PROTOCOL,OPT_CURSOR_PROTOCOL, + OPT_VIEW_PROTOCOL}; /* ************************************************************************ */ /* @@ -158,7 +159,11 @@ static char *db = 0, *pass=0; const char *user = 0, *host = 0, *unix_sock = 0, *opt_basedir="./"; static int port = 0; static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0; -static my_bool tty_password= 0, ps_protocol= 0, ps_protocol_enabled= 0; +static my_bool tty_password= 0; +static my_bool ps_protocol= 0, ps_protocol_enabled= 0; +static my_bool sp_protocol= 0, sp_protocol_enabled= 0; +static my_bool view_protocol= 0, view_protocol_enabled= 0; +static my_bool cursor_protocol= 0, cursor_protocol_enabled= 0; static int parsing_disabled= 0; static uint start_lineno, *lineno; const char *manager_user="root",*manager_host=0; @@ -212,11 +217,14 @@ static int got_end_timer= FALSE; static void timer_output(void); static ulonglong timer_now(void); -static my_regex_t ps_re; /* Holds precompiled re for valid PS statements */ -static void ps_init_re(void); -static int ps_match_re(char *); -static char *ps_eprint(int); -static void ps_free_reg(void); +/* Precompiled re's */ +static my_regex_t ps_re; /* the query can be run using PS protocol */ +static my_regex_t sp_re; /* the query can be run as a SP */ +static my_regex_t view_re; /* the query can be run as a view*/ + +static void init_re(void); +static int match_re(my_regex_t *, char *); +static void free_re(void); static const char *embedded_server_groups[]= { @@ -240,6 +248,7 @@ struct connection { MYSQL mysql; char *name; + MYSQL_STMT* stmt; }; typedef struct @@ -433,7 +442,6 @@ static VAR* var_init(VAR* v, const char *name, int name_len, const char *val, static void var_free(void* v); -int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname); void dump_result_to_reject_file(const char *record_file, char *buf, int size); int close_connection(struct st_query*); @@ -539,6 +547,9 @@ static void close_cons() DBUG_ENTER("close_cons"); for (--next_con; next_con >= cons; --next_con) { + if (next_con->stmt) + mysql_stmt_close(next_con->stmt); + next_con->stmt= 0; mysql_close(&next_con->mysql); my_free(next_con->name, MYF(MY_ALLOW_ZERO_PTR)); } @@ -593,8 +604,7 @@ static void free_used_memory() my_free(pass,MYF(MY_ALLOW_ZERO_PTR)); free_defaults(default_argv); mysql_server_end(); - if (ps_protocol) - ps_free_reg(); + free_re(); DBUG_VOID_RETURN; } @@ -660,7 +670,7 @@ void init_parser() } -int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) +static int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) { MY_STAT stat_info; char *tmp, *res_ptr; @@ -2153,7 +2163,7 @@ int do_connect(struct st_query *q) p= safe_get_param(p, &con_db, "Missing connection db"); /* Port */ - VAR* var_port; + VAR *var_port; p= safe_get_param(p, &con_port_str, 0); if (*con_port_str) { @@ -2223,7 +2233,7 @@ int do_connect(struct st_query *q) if (!mysql_init(&next_con->mysql)) die("Failed on mysql_init()"); if (opt_compress || con_compress) - mysql_options(&next_con->mysql,MYSQL_OPT_COMPRESS,NullS); + mysql_options(&next_con->mysql, MYSQL_OPT_COMPRESS, NullS); mysql_options(&next_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0); mysql_options(&next_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name); @@ -2715,6 +2725,15 @@ static struct my_option my_long_options[] = {"ps-protocol", OPT_PS_PROTOCOL, "Use prepared statements protocol for communication", (gptr*) &ps_protocol, (gptr*) &ps_protocol, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"sp-protocol", OPT_SP_PROTOCOL, "Use stored procedures for select", + (gptr*) &sp_protocol, (gptr*) &sp_protocol, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"cursor-protocol", OPT_CURSOR_PROTOCOL, "Use cursors for prepared statment", + (gptr*) &cursor_protocol, (gptr*) &cursor_protocol, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"view-protocol", OPT_VIEW_PROTOCOL, "Use views for select", + (gptr*) &view_protocol, (gptr*) &view_protocol, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"quiet", 's', "Suppress all normal output.", (gptr*) &silent, (gptr*) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"record", 'r', "Record output of test_file into result file.", @@ -2776,8 +2795,6 @@ void usage() #include -#include - static my_bool get_one_option(int optid, const struct my_option *opt __attribute__((unused)), @@ -3222,6 +3239,7 @@ static void append_table_headings(DYNAMIC_STRING* ds, static void append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql) { uint count; + MYSQL_RES *warn_res; DBUG_ENTER("append_warnings"); if (!(count= mysql_warning_count(mysql))) @@ -3237,11 +3255,9 @@ static void append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql) if (mysql_real_query(mysql, "SHOW WARNINGS", 13)) die("Error running query \"SHOW WARNINGS\": %s", mysql_error(mysql)); - MYSQL_RES *warn_res= mysql_store_result(mysql); - if (!warn_res) + if (!(warn_res= mysql_store_result(mysql))) die("Warning count is %u but didn't get any warnings", count); - dynstr_append_mem(ds, "Warnings:\n", 10); append_result(ds, warn_res); @@ -3258,6 +3274,7 @@ static void append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql) run_query_normal mysql - mysql handle command - currrent command pointer + flags -flags indicating wheter to SEND and/or REAP query - query string to execute query_len - length query string to execute ds - output buffer wherte to store result form query @@ -3279,7 +3296,7 @@ static void run_query_normal(MYSQL *mysql, struct st_query *command, if (flags & QUERY_SEND) { /* - Send the query, using the undocumented function mysql_send_query + Send the query */ if (mysql_send_query(mysql, query, query_len)) { @@ -3295,7 +3312,7 @@ static void run_query_normal(MYSQL *mysql, struct st_query *command, do { /* - When on first result set, call mysql_read_query_result to retrrieve + When on first result set, call mysql_read_query_result to retrieve answer to the query sent earlier */ if ((counter==0) && mysql_read_query_result(mysql)) @@ -3526,18 +3543,20 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, { MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */ MYSQL_STMT *stmt; + DYNAMIC_STRING ds_prepare_warnings; DBUG_ENTER("run_query_stmt"); DBUG_PRINT("query", ("'%-.60s'", query)); - + /* - We must allocate a new stmt for each query in this program because this - may be a new connection. - - Well, it could be stored togeter with mysql pointer in cur_con struct + Init a new stmt if it's not alreday one created for this connectoon */ - if (!(stmt= mysql_stmt_init(mysql))) - die("unable to init stmt structure"); - + if(!(stmt= cur_con->stmt)) + { + if (!(stmt= mysql_stmt_init(mysql))) + die("unable to init stmt structure"); + cur_con->stmt= stmt; + } + /* Prepare the query */ @@ -3548,11 +3567,36 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, goto end; } + /* + Get the warnings from mysql_stmt_prepare and keep them in a + separate string + */ + + if (!disable_warnings) + { + init_dynamic_string(&ds_prepare_warnings, "", 1024, 1024); + append_warnings(&ds_prepare_warnings, mysql); + } + + /* No need to call mysql_stmt_bind_param() because we have no parameter markers. */ +#ifdef BUG14013_FIXED + /* + Use cursor when retrieving result + */ + if (cursor_protocol_enabled) + { + unsigned long type= CURSOR_TYPE_READ_ONLY; + if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type)) + die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s", + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); + } +#endif + /* Execute the query */ @@ -3571,7 +3615,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, { my_bool one= 1; if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one)) - die("mysql_stmt_attr_set failed': %d %s", + die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s", mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); } @@ -3589,14 +3633,14 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, /* If we got here the statement was both executed and read succeesfully */ handle_no_error(command); - /* - Not all statements creates a result set. If there is one we can - now create another normal result set that contains the meta - data. This set can be handled almost like any other non prepared - statement result set. - */ if (!disable_result_log) { + /* + Not all statements creates a result set. If there is one we can + now create another normal result set that contains the meta + data. This set can be handled almost like any other non prepared + statement result set. + */ if ((res= mysql_stmt_result_metadata(stmt)) != NULL) { /* Take the column count from meta info */ @@ -3612,7 +3656,19 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, append_stmt_result(ds, stmt, fields, num_fields); mysql_free_result(res); /* Free normal result set with meta data */ + + } + else + { + /* + This is a query without resultset + */ + /* + Add warnings from prepare to output + */ + if (!disable_warnings) + dynstr_append(ds, ds_prepare_warnings.str); } if (!disable_warnings) @@ -3620,23 +3676,26 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, if (!disable_info) append_info(ds, (ulong)mysql_affected_rows(mysql), mysql_info(mysql)); + } end: free_replace(); + + if (!disable_warnings) + dynstr_free(&ds_prepare_warnings); + /* We save the return code (mysql_stmt_errno(stmt)) from the last call sent to the server into the mysqltest builtin variable $mysql_errno. This variable then can be used from the test case itself. */ var_set_errno(mysql_stmt_errno(stmt)); - mysql_stmt_close(stmt); DBUG_VOID_RETURN; } /* - Run query flags control the phased/stages of query execution to be performed @@ -3653,10 +3712,12 @@ end: static void run_query(MYSQL *mysql, struct st_query *command, int flags) { DYNAMIC_STRING *ds; - DYNAMIC_STRING ds_tmp; + DYNAMIC_STRING ds_result; DYNAMIC_STRING eval_query; char *query; int query_len; + my_bool view_created= 0, sp_created= 0; + my_bool complete_query= ((flags & QUERY_SEND) && (flags & QUERY_REAP)); /* Evaluate query if this is an eval command @@ -3682,35 +3743,122 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags) */ if (command->record_file[0]) { - init_dynamic_string(&ds_tmp, "", 16384, 65536); - ds= &ds_tmp; + init_dynamic_string(&ds_result, "", 16384, 65536); + ds= &ds_result; } else ds= &ds_res; - /* Store the query into the output buffer */ + /* + Log the query into the output buffer + */ if (!disable_query_log && (flags & QUERY_SEND)) { - replace_dynstr_append_mem(ds,query, query_len); + replace_dynstr_append_mem(ds, query, query_len); dynstr_append_mem(ds, delimiter, delimiter_length); dynstr_append_mem(ds, "\n", 1); } + if (view_protocol_enabled && + complete_query && + match_re(&view_re, query)) + { + /* + Create the query as a view. + Use replace since view can exist from a failed mysqltest run + */ + DYNAMIC_STRING query_str; + init_dynamic_string(&query_str, + "CREATE OR REPLACE VIEW mysqltest_tmp_v AS ", + query_len+64, 256); + dynstr_append_mem(&query_str, query, query_len); + if (mysql_query(mysql, query_str.str)) + { + /* + Failed to create the view, this is not fatal + just run the query the normal way + */ + DBUG_PRINT("view_create_error", + ("Failed to create view '%s': %d: %s", query_str.str, + mysql_errno(mysql), mysql_error(mysql))); + } + else + { + /* + Yes, it was possible to create this query as a view + */ + view_created= 1; + query= (char*)"SELECT * FROM mysqltest_tmp_v"; + query_len = strlen(query); + } + + dynstr_free(&query_str); + + } + + if (sp_protocol_enabled && + complete_query && + match_re(&sp_re, query)) + { + /* + Create the query as a stored procedure + Drop first since sp can exist from a failed mysqltest run + */ + DYNAMIC_STRING query_str; + init_dynamic_string(&query_str, + "DROP PROCEDURE IF EXISTS mysqltest_tmp_sp;\n", + query_len+64, 256); + mysql_query(mysql, query_str.str); + dynstr_set(&query_str, "CREATE PROCEDURE mysqltest_tmp_sp()\n"); + dynstr_append_mem(&query_str, query, query_len); + if (mysql_query(mysql, query_str.str)) + { + /* + Failed to create the stored procedure for this query, + this is not fatal just run the query the normal way + */ + DBUG_PRINT("sp_create_error", + ("Failed to create sp '%s': %d: %s", query_str.str, + mysql_errno(mysql), mysql_error(mysql))); + } + else + { + sp_created= 1; + + query= (char*)"CALL mysqltest_tmp_sp()"; + query_len = strlen(query); + } + dynstr_free(&query_str); + } + /* Find out how to run this query - We don't have a mysql_stmt_send_execute() so it must be a - complete SEND+REAP to use prepared statement + Always run with normal C API if it's not a complete + SEND + REAP If it is a '?' in the query it may be a SQL level prepared - statement already and we can't do it twice + statement already and we can't do it twice */ if (ps_protocol_enabled && - (flags & QUERY_SEND) && (flags & QUERY_REAP) && - ps_match_re(query)) + complete_query && + match_re(&ps_re, query)) run_query_stmt(mysql, command, query, query_len, ds); else run_query_normal(mysql, command, flags, query, query_len, ds); + + if (sp_created) + { + if (mysql_query(mysql, "DROP PROCEDURE mysqltest_tmp_sp ")) + die("Failed to drop sp: %d: %s", mysql_errno(mysql), mysql_error(mysql)); + } + + if (view_created) + { + if (mysql_query(mysql, "DROP VIEW mysqltest_tmp_v ")) + die("Failed to drop view: %d: %s", + mysql_errno(mysql), mysql_error(mysql)); + } if (record) { @@ -3730,19 +3878,45 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags) check_result(ds, command->record_file, command->require_file); } - if (ds == &ds_tmp) - dynstr_free(&ds_tmp); + if (ds == &ds_result) + dynstr_free(&ds_result); if (command->type == Q_EVAL) dynstr_free(&eval_query); } /****************************************************************************\ - * Functions to match SQL statements that can be prepared + * Functions to detect different SQL statements \****************************************************************************/ -static void ps_init_re(void) +static char *re_eprint(int err) { + static char epbuf[100]; + size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL, + epbuf, sizeof(epbuf)); + assert(len <= sizeof(epbuf)); + return(epbuf); +} + +static void init_re_comp(my_regex_t *re, const char* str) +{ + int err= my_regcomp(re, str, (REG_EXTENDED | REG_ICASE | REG_NOSUB), + &my_charset_latin1); + if (err) + { + char erbuf[100]; + int len= my_regerror(err, re, erbuf, sizeof(erbuf)); + die("error %s, %d/%d `%s'\n", + re_eprint(err), len, (int)sizeof(erbuf), erbuf); + } +} + +static void init_re(void) +{ + /* + Filter for queries that can be run using the + MySQL Prepared Statements C API + */ const char *ps_re_str = "^(" "[[:space:]]*REPLACE[[:space:]]|" @@ -3757,50 +3931,48 @@ static void ps_init_re(void) "[[:space:]]*UPDATE[[:space:]]+MULTI[[:space:]]|" "[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])"; - int err= my_regcomp(&ps_re, ps_re_str, - (REG_EXTENDED | REG_ICASE | REG_NOSUB), - &my_charset_latin1); - if (err) - { - char erbuf[100]; - int len= my_regerror(err, &ps_re, erbuf, sizeof(erbuf)); - fprintf(stderr, "error %s, %d/%d `%s'\n", - ps_eprint(err), len, (int)sizeof(erbuf), erbuf); - exit(1); - } + /* + Filter for queries that can be run using the + Stored procedures + */ + const char *sp_re_str =ps_re_str; + + /* + Filter for queries that can be run as views + */ + const char *view_re_str = + "^(" + "[[:space:]]*SELECT[[:space:]])"; + + init_re_comp(&ps_re, ps_re_str); + init_re_comp(&sp_re, sp_re_str); + init_re_comp(&view_re, view_re_str); } -static int ps_match_re(char *stmt_str) +static int match_re(my_regex_t *re, char *str) { - int err= my_regexec(&ps_re, stmt_str, (size_t)0, NULL, 0); + int err= my_regexec(re, str, (size_t)0, NULL, 0); if (err == 0) return 1; else if (err == REG_NOMATCH) return 0; - else - { + + { char erbuf[100]; - int len= my_regerror(err, &ps_re, erbuf, sizeof(erbuf)); - fprintf(stderr, "error %s, %d/%d `%s'\n", - ps_eprint(err), len, (int)sizeof(erbuf), erbuf); - exit(1); + int len= my_regerror(err, re, erbuf, sizeof(erbuf)); + die("error %s, %d/%d `%s'\n", + re_eprint(err), len, (int)sizeof(erbuf), erbuf); } + return 0; } -static char *ps_eprint(int err) -{ - static char epbuf[100]; - size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL, epbuf, sizeof(epbuf)); - assert(len <= sizeof(epbuf)); - return(epbuf); -} - - -static void ps_free_reg(void) +static void free_re(void) { my_regfree(&ps_re); + my_regfree(&sp_re); + my_regfree(&view_re); } /****************************************************************************/ @@ -3930,8 +4102,7 @@ static void init_var_hash(MYSQL *mysql) v= var_init(0,"MAX_TABLES", 0, (sizeof(ulong) == 4) ? "31" : "62",0); my_hash_insert(&var_hash, (byte*) v); v= var_init(0,"SERVER_VERSION", 0, mysql_get_server_info(mysql), 0); - my_hash_insert(&var_hash, (byte*) v); - v= var_init(0,"DB", 2, db, 0); + my_hash_insert(&var_hash, (byte*) v); v= var_init(0,"DB", 2, db, 0); my_hash_insert(&var_hash, (byte*) v); DBUG_VOID_RETURN; } @@ -3969,7 +4140,7 @@ int main(int argc, char **argv) cur_block= block_stack; cur_block->ok= TRUE; /* Outer block should always be executed */ cur_block->cmd= cmd_none; - + init_dynamic_string(&ds_res, "", 0, 65536); parse_args(argc, argv); @@ -3988,11 +4159,15 @@ int main(int argc, char **argv) if (manager_host) init_manager(); #endif - if (ps_protocol) - { + init_re(); + ps_protocol_enabled= ps_protocol; + sp_protocol_enabled= sp_protocol; + view_protocol_enabled= view_protocol; + cursor_protocol_enabled= cursor_protocol; + /* Cursor protcol implies ps protocol */ + if (cursor_protocol_enabled) ps_protocol_enabled= 1; - ps_init_re(); - } + if (!( mysql_init(&cur_con->mysql))) die("Failed in mysql_init()"); if (opt_compress) @@ -5282,3 +5457,5 @@ FILE *my_popen(const char *cmd, const char *mode __attribute__((unused))) } #endif /* __NETWARE__ or __WIN__*/ + + diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 2ca7e7c5e15..3ab0d016f48 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -191,6 +191,9 @@ our $opt_ssl; our $opt_skip_ssl; our $opt_ssl_supported; our $opt_ps_protocol; +our $opt_sp_protocol; +our $opt_cursor_protocol; +our $opt_view_protocol; our $opt_current_test; our $opt_ddd; @@ -490,6 +493,9 @@ sub command_line_setup () { # Control what engine/variation to run 'embedded-server' => \$opt_embedded_server, 'ps-protocol' => \$opt_ps_protocol, + 'sp-protocol' => \$opt_sp_protocol, + 'view-protocol' => \$opt_view_protocol, + 'cursor-protocol' => \$opt_cursor_protocol, 'ssl|with-openssl' => \$opt_ssl, 'skip-ssl' => \$opt_skip_ssl, 'compress' => \$opt_compress, @@ -2554,6 +2560,21 @@ sub run_mysqltest ($) { mtr_add_arg($args, "--ps-protocol"); } + if ( $opt_sp_protocol ) + { + mtr_add_arg($args, "--sp-protocol"); + } + + if ( $opt_view_protocol ) + { + mtr_add_arg($args, "--view-protocol"); + } + + if ( $opt_cursor_protocol ) + { + mtr_add_arg($args, "--cursor-protocol"); + } + if ( $opt_strace_client ) { $exe= "strace"; # FIXME there are ktrace, .... @@ -2685,6 +2706,10 @@ Options to control what engine/variation to run embedded-server Use the embedded server, i.e. no mysqld daemons ps-protocol Use the binary protocol between client and server + cursor-protocol Use the cursor protocol between client and server + (implies --ps-protocol) + view-protocol Create a view to execute all non updating queries + sp-protocol Create a stored procedure to execute all queries compress Use the compressed protocol between client and server ssl Use ssl protocol between client and server skip-ssl Dont start sterver with support for ssl connections