diff options
author | unknown <monty@mysql.com> | 2004-10-26 19:30:01 +0300 |
---|---|---|
committer | unknown <monty@mysql.com> | 2004-10-26 19:30:01 +0300 |
commit | 853c2c788c8bd7120a95c2357de7dabb1b6fcf3b (patch) | |
tree | 355a805ce6da2de5e852c4f9b8372d612070cd6d /client | |
parent | 08c39dd283a8723912f5ce798701af3d25aa2747 (diff) | |
download | mariadb-git-853c2c788c8bd7120a95c2357de7dabb1b6fcf3b.tar.gz |
A lot of fixes for prepared statements (PS):
New mysqltest that can run mysqltest with PS
Added support for ZEROFILL in PS
Fixed crash when one called mysql_stmt_store_result() without a preceding mysql_stmt_bind_result()
Updated test cases to support --ps-protocol
(Some tests are still run using old protocol)
Fixed crash in PS when using SELECT * FROM t1 NATURAL JOIN t2...
Fixed crash in PS when using sub queries
Create table didn't signal when table was created. This could cause a "DROP TABLE created_table" in another thread to wait "forever"
Fixed wrong permissions check in PS and multi-table updates (one could get permission denied for legal quries)
Fix for PS and SELECT ... PROCEDURE
Reset all warnings when executing a new PS query
group_concat(...ORDER BY) didn't work with PS
Fixed problem with test suite when not using innodb
BitKeeper/deleted/.del-innodb-lock-master.opt~f76a4a1999728f87:
Delete: mysql-test/t/innodb-lock-master.opt
client/Makefile.am:
mysqltest now uses regex
client/mysqltest.c:
Added support for testing of prepared statements (with --ps-protocol)
Main code was done by Kent, I did mainly some cleanups and minor bug fixes
New test commands:
--disable_ps_protocol
--enable_ps_protocol
NOTE: new code still has some things that needs to be cleaned up.
For example run_query_stmt_handle_error() should be made more general so that same code can be used also by 'normal' queries
configure.in:
mysqltest now uses regex
libmysql/libmysql.c:
Reset warning_count after prepare (safety). In the future we should also provide warnings on prepare
integer -> string conversion now handles ZEROFILL
double -> string conversion is now closer to the one in the server
Fixed crash when one called mysql_stmt_store_result() without preceding mysql_stmt_bind_result()
libmysqld/examples/Makefile.am:
mysqltest now uses regex
mysql-test/include/have_query_cache.inc:
Fixes for --ps-protocol
mysql-test/include/ps_conv.inc:
Fixes for --ps-protocol
mysql-test/mysql-test-run.sh:
Added options --ps-protocol
mysql-test/r/ctype_utf8.result:
Fixed test case
mysql-test/r/fulltext_cache.result:
Changed output of MATCH to use round() to get same numbers with --ps-protocol
mysql-test/r/fulltext_left_join.result:
Changed output of MATCH to use round() to get same numbers with --ps-protocol
mysql-test/r/fulltext_multi.result:
Changed output of MATCH to use round() to get same numbers with --ps-protocol
mysql-test/r/innodb-lock.result:
Fixed test to work even if Innodb is not compiled in.
mysql-test/t/create.test:
Fixes for --ps-protocol
mysql-test/t/ctype_utf8.test:
Remove warnings
mysql-test/t/date_formats.test:
Fixes for --ps-protocol
mysql-test/t/fulltext_cache.test:
Changed output of MATCH to use round() to get same numbers with --ps-protocol
mysql-test/t/fulltext_left_join.test:
Changed output of MATCH to use round() to get same numbers with --ps-protocol
mysql-test/t/fulltext_multi.test:
Changed output of MATCH to use round() to get same numbers with --ps-protocol
mysql-test/t/func_group.test:
Fixes for --ps-protocol
mysql-test/t/func_sapdb.test:
Fixes for --ps-protocol
mysql-test/t/innodb-lock.test:
Fixed test to work even if Innodb is not compiled in.
mysql-test/t/insert.test:
Fixes for --ps-protocol
mysql-test/t/insert_select.test:
Fixes for --ps-protocol
mysql-test/t/insert_update.test:
Fixes for --ps-protocol
mysql-test/t/metadata.test:
Fixes for --ps-protocol
mysql-test/t/multi_statement.test:
Fixes for --ps-protocol
mysql-test/t/ps_1general.test:
Fixes for --ps-protocol
mysql-test/t/rollback.test:
Fixes for --ps-protocol
mysql-test/t/rpl_redirect.test:
Fixes for --ps-protocol
mysql-test/t/rpl_user_variables.test:
Fixes for --ps-protocol
mysql-test/t/select.test:
Fixes for --ps-protocol
mysql-test/t/status.test:
Fixes for --ps-protocol
mysql-test/t/type_blob.test:
Fixes for --ps-protocol
mysql-test/t/type_float.test:
Fixes for --ps-protocol
mysql-test/t/union.test:
Fixes for --ps-protocol
mysql-test/t/warnings.test:
Fixes for --ps-protocol
mysys/my_alloc.c:
More debugging information
sql-common/client.c:
More debugging information
sql-common/my_time.c:
TIME didn't support full range with PS
sql/field.cc:
TIME didn't support full range with PS
sql/item_cmpfunc.cc:
IN(constants,...) didn't work with PS
sql/item_subselect.cc:
Some subqueries didn't work with PS
sql/item_sum.cc:
group_concat(...ORDER BY) didn't work with PS
Removed variable warning_available as 'warning' can be used for this.
sql/item_sum.h:
Removed not needed variable
sql/protocol.cc:
TIME didn't support full range with PS
sql/set_var.cc:
Style fix
sql/sql_base.cc:
setup_wild() didn't properly restore old arena, which caused core dump in PS when using
SELECT * FROM t1 NATURAL JOIN t2...
sql/sql_class.cc:
Style fix
sql/sql_error.cc:
Style fix
sql/sql_insert.cc:
Create table didn't signal when table was created. This could cause a "DROP TABLE created_table" in another thread to wait "forever"
sql/sql_lex.h:
Fix for PS and procedures
sql/sql_parse.cc:
More debugging information
Make a copy of 'db' in PS as this may change
Fixed wrong permissions check in PS and multi-table updates
sql/sql_prepare.cc:
Fix for PS and SELECT ... PROCEDURE
Reset all warnings when executing a new query
sql/sql_union.cc:
Fixes for PS and SELECT ... PROCEDURE
Reset 'with_wild' as 'wild' is resolved on prepare
Diffstat (limited to 'client')
-rw-r--r-- | client/Makefile.am | 4 | ||||
-rw-r--r-- | client/mysqltest.c | 691 |
2 files changed, 640 insertions, 55 deletions
diff --git a/client/Makefile.am b/client/Makefile.am index 24a4bece825..a9da284a753 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -17,7 +17,8 @@ # This file is public domain and comes with NO WARRANTY of any kind #AUTOMAKE_OPTIONS = nostdinc -INCLUDES = -I$(top_srcdir)/include $(openssl_includes) +INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/regex \ + $(openssl_includes) LIBS = @CLIENT_LIBS@ DEPLIB= ../libmysql/libmysqlclient.la LDADD = @CLIENT_EXTRA_LDFLAGS@ $(DEPLIB) @@ -36,6 +37,7 @@ mysqldump_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) $(DEPLIB) mysqlimport_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) $(DEPLIB) mysqltest_SOURCES= mysqltest.c ../mysys/my_getsystime.c mysqltest_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) $(DEPLIB) +mysqltest_LDADD = $(LDADD) $(top_builddir)/regex/libregex.a mysqlbinlog_SOURCES = mysqlbinlog.cc ../mysys/mf_tempdir.c mysqlbinlog_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) $(DEPLIB) mysqlmanagerc_SOURCES = mysqlmanagerc.c diff --git a/client/mysqltest.c b/client/mysqltest.c index 7623b9bde73..4c87561ab84 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -42,7 +42,7 @@ **********************************************************************/ -#define MTEST_VERSION "2.2" +#define MTEST_VERSION "2.3" #include <my_global.h> #include <mysql_embed.h> @@ -53,12 +53,13 @@ #include <mysqld_error.h> #include <m_ctype.h> #include <my_dir.h> +#include <errmsg.h> /* Error codes */ #include <hash.h> #include <my_getopt.h> #include <stdarg.h> #include <sys/stat.h> #include <violite.h> - +#include <regex.h> /* Our own version of lib */ #define MAX_QUERY 131072 #define MAX_VAR_NAME 256 #define MAX_COLUMNS 256 @@ -93,7 +94,7 @@ 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_SSL_CIPHER,OPT_PS_PROTOCOL}; /* ************************************************************************ */ /* @@ -131,8 +132,8 @@ static int record = 0, opt_sleep=0; 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, - tty_password= 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 uint start_lineno, *lineno; const char* manager_user="root",*manager_host=0; char *manager_pass=0; @@ -157,7 +158,7 @@ static int block_stack[BLOCK_STACK_DEPTH]; static int block_ok_stack[BLOCK_STACK_DEPTH]; static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */ -static char *charset_name = "latin1"; /* Default character set name */ +static const char *charset_name= "latin1"; /* Default character set name */ static int embedded_server_arg_count=0; static char *embedded_server_args[MAX_SERVER_ARGS]; @@ -171,6 +172,12 @@ static int got_end_timer= FALSE; static void timer_output(void); static ulonglong timer_now(void); +static 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); + static const char *embedded_server_groups[] = { "server", "embedded", @@ -270,7 +277,7 @@ Q_EXEC, Q_DELIMITER, Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS, Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, Q_START_TIMER, Q_END_TIMER, -Q_CHARACTER_SET, +Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL, Q_UNKNOWN, /* Unknown command. */ Q_COMMENT, /* Comments, ignored. */ @@ -352,6 +359,8 @@ const char *command_names[]= "start_timer", "end_timer", "character_set", + "disable_ps_protocol", + "enable_ps_protocol", 0 }; @@ -523,6 +532,8 @@ 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(); my_end(MY_CHECK_ERROR); DBUG_VOID_RETURN; } @@ -2089,6 +2100,9 @@ static struct my_option my_long_options[] = 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"port", 'P', "Port number to use for connection.", (gptr*) &port, (gptr*) &port, 0, GET_INT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0, 0}, + {"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}, {"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.", @@ -2367,7 +2381,36 @@ static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res) * the result will be read - for regular query, both bits must be on */ -int run_query(MYSQL* mysql, struct st_query* q, int flags) +static int run_query_normal(MYSQL *mysql, struct st_query *q, int flags); +static int run_query_stmt (MYSQL *mysql, struct st_query *q, int flags); +static void run_query_stmt_handle_warnings(MYSQL *mysql, DYNAMIC_STRING *ds); +static int run_query_stmt_handle_error(char *query, struct st_query *q, + MYSQL_STMT *stmt, DYNAMIC_STRING *ds); +static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields, + DYNAMIC_STRING *ds); + +static int run_query(MYSQL *mysql, struct st_query *q, int flags) +{ + + /* + Try to find out if we can run this statement using the prepared + statement protocol. + + We don't have a mysql_stmt_send_execute() so we only handle + 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 + */ + + if (ps_protocol_enabled && disable_info && + (flags & QUERY_SEND) && (flags & QUERY_REAP) && ps_match_re(q->query)) + return run_query_stmt (mysql, q, flags); + return run_query_normal(mysql, q, flags); +} + + +static int run_query_normal(MYSQL* mysql, struct st_query* q, int flags) { MYSQL_RES* res= 0; uint i; @@ -2377,7 +2420,7 @@ int run_query(MYSQL* mysql, struct st_query* q, int flags) DYNAMIC_STRING eval_query; char* query; int query_len, got_error_on_send= 0; - DBUG_ENTER("run_query"); + DBUG_ENTER("run_query_normal"); DBUG_PRINT("enter",("flags: %d", flags)); if (q->type != Q_EVAL) @@ -2520,56 +2563,14 @@ int run_query(MYSQL* mysql, struct st_query* q, int flags) { if (res) { - MYSQL_FIELD *field, *field_end; + MYSQL_FIELD *field= mysql_fetch_fields(res); uint num_fields= mysql_num_fields(res); if (display_metadata) - { - dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\tColumn_alias\tName\tType\tLength\tMax length\tIs_null\tFlags\tDecimals\tCharsetnr\n"); - for (field= mysql_fetch_fields(res), field_end= field+num_fields ; - field < field_end ; - field++) - { - char buff[22]; - dynstr_append_mem(ds, field->catalog, field->catalog_length); - dynstr_append_mem(ds, "\t", 1); - dynstr_append_mem(ds, field->db, field->db_length); - dynstr_append_mem(ds, "\t", 1); - dynstr_append_mem(ds, field->org_table, field->org_table_length); - dynstr_append_mem(ds, "\t", 1); - dynstr_append_mem(ds, field->table, field->table_length); - dynstr_append_mem(ds, "\t", 1); - dynstr_append_mem(ds, field->org_name, field->org_name_length); - dynstr_append_mem(ds, "\t", 1); - dynstr_append_mem(ds, field->name, field->name_length); - dynstr_append_mem(ds, "\t", 1); - int10_to_str((int) field->type, buff, 10); - dynstr_append(ds, buff); - dynstr_append_mem(ds, "\t", 1); - int10_to_str((int) field->length, buff, 10); - dynstr_append(ds, buff); - dynstr_append_mem(ds, "\t", 1); - int10_to_str((int) field->max_length, buff, 10); - dynstr_append(ds, buff); - dynstr_append_mem(ds, "\t", 1); - dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ? - "N" : "Y"), 1); - dynstr_append_mem(ds, "\t", 1); - - int10_to_str((int) field->flags, buff, 10); - dynstr_append(ds, buff); - dynstr_append_mem(ds, "\t", 1); - int10_to_str((int) field->decimals, buff, 10); - dynstr_append(ds, buff); - dynstr_append_mem(ds, "\t", 1); - int10_to_str((int) field->charsetnr, buff, 10); - dynstr_append(ds, buff); - dynstr_append_mem(ds, "\n", 1); - } - } + run_query_display_metadata(field, num_fields, ds); + if (!display_result_vertically) { - field= mysql_fetch_fields(res); for (i = 0; i < num_fields; i++) { if (i) @@ -2645,6 +2646,576 @@ end: } +/****************************************************************************\ + * If --ps-protocol run ordinary statements using prepared statemnt C API +\****************************************************************************/ + +/* + We don't have a mysql_stmt_send_execute() so we only handle + complete SEND+REAP +*/ + +static int run_query_stmt(MYSQL *mysql, struct st_query *q, int flags) +{ + int error= 0; /* Function return code if "goto end;" */ + int err; /* Temporary storage of return code from calls */ + int query_len, got_error_on_execute; + uint num_rows; + char *query; + MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */ + DYNAMIC_STRING *ds; + DYNAMIC_STRING ds_tmp; + DYNAMIC_STRING eval_query; + MYSQL_STMT *stmt; + DBUG_ENTER("run_query_stmt"); + + /* + We must allocate a new stmt for each query in this program becasue this + may be a new connection. + */ + if (!(stmt= mysql_stmt_init(mysql))) + die("At line %u: unable init stmt structure"); + + if (q->type != Q_EVAL) + { + query= q->query; + query_len= strlen(query); + } + else + { + init_dynamic_string(&eval_query, "", 16384, 65536); + do_eval(&eval_query, q->query); + query= eval_query.str; + query_len= eval_query.length; + } + DBUG_PRINT("query", ("'%-.60s'", query)); + + if (q->record_file[0]) + { + init_dynamic_string(&ds_tmp, "", 16384, 65536); + ds= &ds_tmp; + } + else + ds= &ds_res; + + /* Store the query into the output buffer if not disabled */ + if (!disable_query_log) + { + replace_dynstr_append_mem(ds,query, query_len); + dynstr_append_mem(ds, delimiter, delimiter_length); + dynstr_append_mem(ds, "\n", 1); + } + + /* + We use the prepared statement interface but there is actually no + '?' in the query. If unpreparable we fall back to use normal + C API. + */ + if ((err= mysql_stmt_prepare(stmt, query, query_len)) == CR_NO_PREPARE_STMT) + return run_query_normal(mysql, q, flags); + + if (err != 0) + { + if (q->abort_on_error) + { + die("At line %u: unable to prepare statement '%s': " + "%s (mysql_stmt_errno=%d returned=%d)", + start_lineno, query, + mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err); + } + else + { + /* + Preparing is part of normal execution and some errors may be expected + */ + error= run_query_stmt_handle_error(query, q, stmt, ds); + goto end; + } + } + + /* We may have got warnings already, collect them if any */ + /* FIXME we only want this if the statement succeeds I think */ + run_query_stmt_handle_warnings(mysql, ds); + + /* + No need to call mysql_stmt_bind_param() because we have no + parameter markers. + + To optimize performance we use a global 'stmt' that is initiated + once. A new prepare will implicitely close the old one. When we + terminate we will lose the connection, this also closes the last + prepared statement. + */ + + if ((got_error_on_execute= mysql_stmt_execute(stmt)) != 0) /* 0 == Success */ + { + if (q->abort_on_error) + { + /* We got an error, unexpected */ + die("At line %u: unable to execute statement '%s': " + "%s (mysql_stmt_errno=%d returned=%d)", + start_lineno, query, mysql_stmt_error(stmt), + mysql_stmt_errno(stmt), got_error_on_execute); + } + else + { + /* We got an error, maybe expected */ + error= run_query_stmt_handle_error(query, q, stmt, ds); + goto end; + } + } + + /* + We instruct that we want to update the "max_length" field in + mysql_stmt_store_result(), this is our only way to know how much + buffer to allocate for result data + */ + { + my_bool one= 1; + if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, + (void*) &one) != 0) + die("At line %u: unable to set stmt attribute " + "'STMT_ATTR_UPDATE_MAX_LENGTH': %s (returned=%d)", + start_lineno, query, err); + } + + /* + If we got here the statement succeeded and was expected to do so, + get data. Note that this can still give errors found during execution! + */ + if ((err= mysql_stmt_store_result(stmt)) != 0) + { + if (q->abort_on_error) + { + /* We got an error, unexpected */ + die("At line %u: unable to execute statement '%s': " + "%s (mysql_stmt_errno=%d returned=%d)", + start_lineno, query, mysql_stmt_error(stmt), + mysql_stmt_errno(stmt), got_error_on_execute); + } + else + { + /* We got an error, maybe expected */ + error= run_query_stmt_handle_error(query, q, stmt, ds); + goto end; + } + } + + /* If we got here the statement was both executed and read succeesfully */ + + if (q->expected_errno[0].type == ERR_ERRNO && + q->expected_errno[0].code.errnum != 0) + { + verbose_msg("query '%s' succeeded - should have failed with errno %d...", + q->query, q->expected_errno[0].code.errnum); + error= 1; + goto end; + } + + num_rows= mysql_stmt_num_rows(stmt); + + /* + 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 && ((res= mysql_stmt_result_metadata(stmt)) != NULL)) + { + /* Take the column count from meta info */ + MYSQL_FIELD *field= mysql_fetch_fields(res); + uint num_fields= mysql_num_fields(res); + + /* FIXME check error from the above? */ + + if (display_metadata) + run_query_display_metadata(field, num_fields, ds); + + if (!display_result_vertically) + { + /* Display the table heading with the names tab separated */ + uint col_idx; + for (col_idx= 0; col_idx < num_fields; col_idx++) + { + if (col_idx) + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append_mem(ds, field[col_idx].name, + strlen(field[col_idx].name)); + } + dynstr_append_mem(ds, "\n", 1); + } + + /* Now we are to put the real result into the output buffer */ + /* FIXME when it works, create function append_stmt_result() */ + { + MYSQL_BIND *bind; + my_bool *is_null; + unsigned long *length; + /* FIXME we don't handle vertical display ..... */ + uint col_idx, row_idx; + + /* Allocate array with bind structs, lengths and NULL flags */ + bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND), + MYF(MY_WME | MY_FAE)); + length= (unsigned long*) my_malloc(num_fields * sizeof(unsigned long), + MYF(MY_WME | MY_FAE)); + is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool), + MYF(MY_WME | MY_FAE)); + + for (col_idx= 0; col_idx < num_fields; col_idx++) + { + /* Allocate data for output */ + /* + FIXME it may be a bug that for non string/blob types + 'max_length' is 0, should try out 'length' in that case + */ + uint max_length= max(field[col_idx].max_length + 1, 1024); + char *str_data= (char *) my_malloc(max_length, MYF(MY_WME | MY_FAE)); + + bind[col_idx].buffer_type= MYSQL_TYPE_STRING; + bind[col_idx].buffer= (char *)str_data; + bind[col_idx].buffer_length= max_length; + bind[col_idx].is_null= &is_null[col_idx]; + bind[col_idx].length= &length[col_idx]; + } + + /* Fill in the data into the structures created above */ + if ((err= mysql_stmt_bind_result(stmt, bind)) != 0) + die("At line %u: unable to bind result to statement '%s': " + "%s (mysql_stmt_errno=%d returned=%d)", + start_lineno, query, + mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err); + + /* Read result from each row */ + for (row_idx= 0; row_idx < num_rows; row_idx++) + { + if ((err= mysql_stmt_fetch(stmt)) != 0) + die("At line %u: unable to fetch all rows from statement '%s': " + "%s (mysql_stmt_errno=%d returned=%d)", + start_lineno, query, + mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err); + + /* Read result from each column */ + for (col_idx= 0; col_idx < num_fields; col_idx++) + { + /* FIXME is string terminated? */ + const char *val= (const char *)bind[col_idx].buffer; + ulonglong len= *bind[col_idx].length; + if (col_idx < max_replace_column && replace_column[col_idx]) + { + val= replace_column[col_idx]; + len= strlen(val); + } + if (*bind[col_idx].is_null) + { + val= "NULL"; + len= 4; + } + if (!display_result_vertically) + { + if (col_idx) /* No tab before first col */ + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append_mem(ds, val, len); + } + else + { + dynstr_append(ds, field[col_idx].name); + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append_mem(ds, val, len); + dynstr_append_mem(ds, "\n", 1); + } + } + if (!display_result_vertically) + dynstr_append_mem(ds, "\n", 1); + } + + if ((err= mysql_stmt_fetch(stmt)) != MYSQL_NO_DATA) + die("At line %u: fetch didn't end with MYSQL_NO_DATA from statement " + "'%s': %s (mysql_stmt_errno=%d returned=%d)", + start_lineno, query, + mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err); + + free_replace_column(); + + for (col_idx= 0; col_idx < num_fields; col_idx++) + { + /* Free data for output */ + my_free((gptr)bind[col_idx].buffer, MYF(MY_WME | MY_FAE)); + } + /* Free array with bind structs, lengths and NULL flags */ + my_free((gptr)bind , MYF(MY_WME | MY_FAE)); + my_free((gptr)length , MYF(MY_WME | MY_FAE)); + my_free((gptr)is_null , MYF(MY_WME | MY_FAE)); + } + + /* Add all warnings to the result */ + run_query_stmt_handle_warnings(mysql, ds); + + if (!disable_info) + { + char buf[40]; + sprintf(buf,"affected rows: %lu\n",(ulong) mysql_affected_rows(mysql)); + dynstr_append(ds, buf); + if (mysql_info(mysql)) + { + dynstr_append(ds, "info: "); + dynstr_append(ds, mysql_info(mysql)); + dynstr_append_mem(ds, "\n", 1); + } + } + } + run_query_stmt_handle_warnings(mysql, ds); + + if (record) + { + if (!q->record_file[0] && !result_file) + die("At line %u: Missing result file", start_lineno); + if (!result_file) + str_to_file(q->record_file, ds->str, ds->length); + } + else if (q->record_file[0]) + { + error= check_result(ds, q->record_file, q->require_file); + } + if (res) + mysql_free_result(res); /* Free normal result set with meta data */ + last_result= 0; /* FIXME have no idea what this is about... */ + + if (err >= 1) + mysql_error(mysql); /* FIXME strange, has no effect... */ + +end: + free_replace(); + last_result=0; + if (ds == &ds_tmp) + dynstr_free(&ds_tmp); + if (q->type == Q_EVAL) + dynstr_free(&eval_query); + mysql_stmt_close(stmt); + DBUG_RETURN(error); +} + + +/****************************************************************************\ + * Broken out sub functions to run_query_stmt() +\****************************************************************************/ + +static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields, + DYNAMIC_STRING *ds) +{ + MYSQL_FIELD *field_end; + dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t" + "Column_alias\tName\tType\tLength\tMax length\tIs_null\t" + "Flags\tDecimals\tCharsetnr\n"); + + for (field_end= field+num_fields ; + field < field_end ; + field++) + { + char buff[22]; + dynstr_append_mem(ds, field->catalog, + field->catalog_length); + dynstr_append_mem(ds, "\t", 1); + dynstr_append_mem(ds, field->db, field->db_length); + dynstr_append_mem(ds, "\t", 1); + dynstr_append_mem(ds, field->org_table, + field->org_table_length); + dynstr_append_mem(ds, "\t", 1); + dynstr_append_mem(ds, field->table, + field->table_length); + dynstr_append_mem(ds, "\t", 1); + dynstr_append_mem(ds, field->org_name, + field->org_name_length); + dynstr_append_mem(ds, "\t", 1); + dynstr_append_mem(ds, field->name, field->name_length); + dynstr_append_mem(ds, "\t", 1); + int10_to_str((int) field->type, buff, 10); + dynstr_append(ds, buff); + dynstr_append_mem(ds, "\t", 1); + int10_to_str((int) field->length, buff, 10); + dynstr_append(ds, buff); + dynstr_append_mem(ds, "\t", 1); + int10_to_str((int) field->max_length, buff, 10); + dynstr_append(ds, buff); + dynstr_append_mem(ds, "\t", 1); + dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ? + "N" : "Y"), 1); + dynstr_append_mem(ds, "\t", 1); + + int10_to_str((int) field->flags, buff, 10); + dynstr_append(ds, buff); + dynstr_append_mem(ds, "\t", 1); + int10_to_str((int) field->decimals, buff, 10); + dynstr_append(ds, buff); + dynstr_append_mem(ds, "\t", 1); + int10_to_str((int) field->charsetnr, buff, 10); + dynstr_append(ds, buff); + dynstr_append_mem(ds, "\n", 1); + } +} + + +static void run_query_stmt_handle_warnings(MYSQL *mysql, DYNAMIC_STRING *ds) +{ + uint count; + DBUG_ENTER("run_query_stmt_handle_warnings"); + + if (!disable_warnings && (count= mysql_warning_count(mysql))) + { + if (mysql_real_query(mysql, "SHOW WARNINGS", 13) == 0) + { + MYSQL_RES *warn_res= mysql_store_result(mysql); + if (!warn_res) + verbose_msg("Warning count is %u but didn't get any warnings\n", + count); + else + { + dynstr_append_mem(ds, "Warnings:\n", 10); + append_result(ds, warn_res); + mysql_free_result(warn_res); + } + } + } + DBUG_VOID_RETURN; +} + + +static int run_query_stmt_handle_error(char *query, struct st_query *q, + MYSQL_STMT *stmt, DYNAMIC_STRING *ds) +{ + if (q->require_file) /* FIXME don't understand this one */ + { + abort_not_supported_test(); + } + + if (q->abort_on_error) + die("At line %u: query '%s' failed: %d: %s", start_lineno, query, + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); + else + { + int i; + + for (i=0 ; (uint) i < q->expected_errors ; i++) + { + if (((q->expected_errno[i].type == ERR_ERRNO) && + (q->expected_errno[i].code.errnum == mysql_stmt_errno(stmt))) || + ((q->expected_errno[i].type == ERR_SQLSTATE) && + (strcmp(q->expected_errno[i].code.sqlstate, + mysql_stmt_sqlstate(stmt)) == 0))) + { + if (i == 0 && q->expected_errors == 1) + { + /* Only log error if there is one possible error */ + dynstr_append_mem(ds,"ERROR ",6); + replace_dynstr_append_mem(ds, mysql_stmt_sqlstate(stmt), + strlen(mysql_stmt_sqlstate(stmt))); + dynstr_append_mem(ds, ": ", 2); + replace_dynstr_append_mem(ds,mysql_stmt_error(stmt), + strlen(mysql_stmt_error(stmt))); + dynstr_append_mem(ds,"\n",1); + } + /* Don't log error if we may not get an error */ + else if (q->expected_errno[0].type == ERR_SQLSTATE || + (q->expected_errno[0].type == ERR_ERRNO && + q->expected_errno[0].code.errnum != 0)) + dynstr_append(ds,"Got one of the listed errors\n"); + return 0; /* Ok */ + } + } + DBUG_PRINT("info",("i: %d expected_errors: %d", i, + q->expected_errors)); + dynstr_append_mem(ds, "ERROR ",6); + replace_dynstr_append_mem(ds, mysql_stmt_sqlstate(stmt), + strlen(mysql_stmt_sqlstate(stmt))); + dynstr_append_mem(ds,": ",2); + replace_dynstr_append_mem(ds, mysql_stmt_error(stmt), + strlen(mysql_stmt_error(stmt))); + dynstr_append_mem(ds,"\n",1); + if (i) + { + verbose_msg("query '%s' failed with wrong errno %d instead of %d...", + q->query, mysql_stmt_errno(stmt), q->expected_errno[0]); + return 1; /* Error */ + } + verbose_msg("query '%s' failed: %d: %s", q->query, mysql_stmt_errno(stmt), + mysql_stmt_error(stmt)); + /* + if we do not abort on error, failure to run the query does + not fail the whole test case + */ + return 0; + } + + return 0; +} + +/****************************************************************************\ + * Functions to match SQL statements that can be prepared +\****************************************************************************/ + +static void ps_init_re(void) +{ + const char *ps_re_str = + "^(" + "[[:space:]]*REPLACE[[:space:]]|" + "[[:space:]]*INSERT[[:space:]]|" + "[[:space:]]*UPDATE[[:space:]]|" + "[[:space:]]*DELETE[[:space:]]|" + "[[:space:]]*SELECT[[:space:]]|" + "[[:space:]]*CREATE[[:space:]]+TABLE[[:space:]]|" + "[[:space:]]*DO[[:space:]]|" + "[[:space:]]*SET[[:space:]]+OPTION[[:space:]]|" + "[[:space:]]*DELETE[[:space:]]+MULTI[[:space:]]|" + "[[:space:]]*UPDATE[[:space:]]+MULTI[[:space:]]|" + "[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])"; + + int err= regcomp(&ps_re, ps_re_str, (REG_EXTENDED | REG_ICASE | REG_NOSUB), + &my_charset_latin1); + if (err) + { + char erbuf[100]; + int len= 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); + } +} + + +static int ps_match_re(char *stmt_str) +{ + int err= regexec(&ps_re, stmt_str, (size_t)0, NULL, 0); + + if (err == 0) + return 1; + else if (err == REG_NOMATCH) + return 0; + else + { + char erbuf[100]; + int len= 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); + } +} + +static char *ps_eprint(int err) +{ + static char epbuf[100]; + size_t len= regerror(REG_ITOA|err, (regex_t *)NULL, epbuf, sizeof(epbuf)); + assert(len <= sizeof(epbuf)); + return(epbuf); +} + + +static void ps_free_reg(void) +{ + regfree(&ps_re); +} + +/****************************************************************************/ + void get_query_type(struct st_query* q) { char save; @@ -2798,6 +3369,11 @@ int main(int argc, char **argv) if (manager_host) init_manager(); #endif + if (ps_protocol) + { + ps_protocol_enabled= 1; + ps_init_re(); + } if (!( mysql_init(&cur_con->mysql))) die("Failed in mysql_init()"); if (opt_compress) @@ -2991,6 +3567,13 @@ int main(int argc, char **argv) case Q_CHARACTER_SET: set_charset(q); break; + case Q_DISABLE_PS_PROTOCOL: + ps_protocol_enabled= 0; + break; + case Q_ENABLE_PS_PROTOCOL: + ps_protocol_enabled= ps_protocol; + break; + default: processed = 0; break; } } |