summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorunknown <monty@mysql.com>2004-10-26 19:30:01 +0300
committerunknown <monty@mysql.com>2004-10-26 19:30:01 +0300
commit853c2c788c8bd7120a95c2357de7dabb1b6fcf3b (patch)
tree355a805ce6da2de5e852c4f9b8372d612070cd6d /client
parent08c39dd283a8723912f5ce798701af3d25aa2747 (diff)
downloadmariadb-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.am4
-rw-r--r--client/mysqltest.c691
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;
}
}