summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorunknown <monty@mysql.com>2004-10-29 19:26:52 +0300
committerunknown <monty@mysql.com>2004-10-29 19:26:52 +0300
commitf095274fe8c3d3394d6c0ce0a68f4bea04311999 (patch)
tree23bcc9a71fe7237887a111b158e30f5a6bb665d3 /client
parentf41bba8c6156a7adf4c67dfa75e16112767a5d3c (diff)
parent5be6c328f5a9f78f37176bbbd88a538fa3b65fe9 (diff)
downloadmariadb-git-f095274fe8c3d3394d6c0ce0a68f4bea04311999.tar.gz
merge with 4.1
BitKeeper/etc/ignore: auto-union BitKeeper/etc/logging_ok: auto-union BitKeeper/triggers/post-commit: Auto merged Docs/Support/texi2html: Auto merged Makefile.am: Auto merged client/Makefile.am: Auto merged client/mysql.cc: Auto merged client/mysqldump.c: Auto merged include/my_base.h: Auto merged include/my_global.h: Auto merged include/my_pthread.h: Auto merged include/my_sys.h: Auto merged include/my_time.h: Auto merged include/mysql.h: Auto merged include/mysql_com.h: Auto merged innobase/buf/buf0buf.c: Auto merged innobase/include/row0mysql.h: Auto merged innobase/row/row0sel.c: Auto merged libmysql/libmysql.c: Auto merged libmysqld/examples/Makefile.am: Auto merged myisam/mi_check.c: Auto merged mysql-test/include/ps_modify.inc: Auto merged mysql-test/install_test_db.sh: Auto merged mysql-test/r/alter_table.result: Auto merged mysql-test/r/auto_increment.result: Auto merged mysql-test/r/bdb.result: Auto merged mysql-test/r/ctype_latin1_de.result: Auto merged mysql-test/r/ctype_recoding.result: Auto merged mysql-test/r/fulltext.result: Auto merged mysql-test/r/func_gconcat.result: Auto merged mysql-test/r/func_group.result: Auto merged mysql-test/r/func_if.result: Auto merged mysql-test/t/derived.test: Auto merged mysql-test/t/insert.test: merge with 4.1 Fixed test case to not use 'if exists' when it shouldn't mysql-test/t/range.test: merge with 4.1 Added missing drop table sql/ha_ndbcluster.cc: merge with 4.1 Simple optimization: use max() instead of ? : sql/item_func.cc: merge with 4.1 (Added back old variable names for easier merges) sql/opt_range.cc: merge with 4.1 Removed argument 'parent_alloc' from QUICK_RANGE_SELECT as this was not used Added assert if using QUICK_GROUP_MIN_MAX_SELECT with parent_alloc as the init() function can't handle this Changed back get_quick_select_for_ref() to use it's own alloc root becasue this function may be called several times for one query sql/sql_handler.cc: merge with 4.1 change variable 'err' to 'error' as same function had a label named 'err' sql/sql_update.cc: Use multi-update code from 5.0 instead of 4.1 We will fix the locking code shortly in 5.0 to be faster than in 4.1
Diffstat (limited to 'client')
-rw-r--r--client/Makefile.am6
-rw-r--r--client/client_priv.h3
-rw-r--r--client/mysql.cc91
-rw-r--r--client/mysqladmin.c24
-rw-r--r--client/mysqlcheck.c11
-rw-r--r--client/mysqldump.c66
-rw-r--r--client/mysqlimport.c12
-rw-r--r--client/mysqlshow.c3
-rw-r--r--client/mysqltest.c1010
9 files changed, 1064 insertions, 162 deletions
diff --git a/client/Makefile.am b/client/Makefile.am
index 7400717731d..7fdba8f43d8 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)
@@ -34,8 +35,9 @@ mysqlcheck_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) $(DEPLIB)
mysqlshow_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) $(DEPLIB)
mysqldump_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) $(DEPLIB)
mysqlimport_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) $(DEPLIB)
-mysqltest_SOURCES= mysqltest.c
+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/client_priv.h b/client/client_priv.h
index ad08484b706..f16ec0e802b 100644
--- a/client/client_priv.h
+++ b/client/client_priv.h
@@ -44,5 +44,6 @@ enum options_client
OPT_SHARED_MEMORY_BASE_NAME, OPT_FRM, OPT_SKIP_OPTIMIZATION,
OPT_COMPATIBLE, OPT_RECONNECT, OPT_DELIMITER, OPT_SECURE_AUTH,
OPT_OPEN_FILES_LIMIT, OPT_SET_CHARSET, OPT_CREATE_OPTIONS,
- OPT_START_POSITION, OPT_STOP_POSITION, OPT_START_DATETIME, OPT_STOP_DATETIME
+ OPT_START_POSITION, OPT_STOP_POSITION, OPT_START_DATETIME, OPT_STOP_DATETIME,
+ OPT_SIGINT_IGNORE, OPT_HEXBLOB
};
diff --git a/client/mysql.cc b/client/mysql.cc
index 0b43f9b80ec..358b13e652b 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -44,7 +44,7 @@
#include <locale.h>
#endif
-const char *VER= "14.6";
+const char *VER= "14.7";
/* Don't try to make a nice table if the data is too big */
#define MAX_COLUMN_LENGTH 1024
@@ -135,7 +135,7 @@ static my_bool info_flag=0,ignore_errors=0,wait_flag=0,quick=0,
opt_xml=0,opt_nopager=1, opt_outfile=0, named_cmds= 0,
tty_password= 0, opt_nobeep=0, opt_reconnect=1,
default_charset_used= 0, opt_secure_auth= 0,
- default_pager_set= 0;
+ default_pager_set= 0, opt_sigint_ignore= 0;
static ulong opt_max_allowed_packet, opt_net_buffer_length;
static uint verbose=0,opt_silent=0,opt_mysql_port=0, opt_local_infile=0;
static my_string opt_mysql_unix_port=0;
@@ -394,7 +394,11 @@ int main(int argc,char *argv[])
}
if (!status.batch)
ignore_errors=1; // Don't abort monitor
- signal(SIGINT, mysql_end); // Catch SIGINT to clean up
+
+ if (opt_sigint_ignore)
+ signal(SIGINT, SIG_IGN);
+ else
+ signal(SIGINT, mysql_end); // Catch SIGINT to clean up
signal(SIGQUIT, mysql_end); // Catch SIGQUIT to clean up
/*
@@ -573,6 +577,9 @@ static struct my_option my_long_options[] =
{"set-variable", 'O',
"Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value.",
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"sigint-ignore", OPT_SIGINT_IGNORE, "Ignore SIGINT (CTRL-C)",
+ (gptr*) &opt_sigint_ignore, (gptr*) &opt_sigint_ignore, 0, GET_BOOL,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
{"one-database", 'o',
"Only update the default database. This is useful for skipping updates to other database in the update log.",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
@@ -1964,7 +1971,7 @@ print_field_types(MYSQL_RES *result)
MYSQL_FIELD *field;
while ((field = mysql_fetch_field(result)))
{
- tee_fprintf(PAGER,"Catalog: '%s'\nDatabase: '%s'\nTable: '%s'\nName: '%s'\nType: %d\nLength: %d\nMax length: %d\nIs_null: %d\nFlags: %d\nDecimals: %d\n\n",
+ tee_fprintf(PAGER,"Catalog: '%s'\nDatabase: '%s'\nTable: '%s'\nName: '%s'\nType: %d\nLength: %ld\nMax length: %ld\nIs_null: %d\nFlags: %u\nDecimals: %u\n\n",
field->catalog, field->db, field->table, field->name,
(int) field->type,
field->length, field->max_length,
@@ -2010,7 +2017,8 @@ print_table_data(MYSQL_RES *result)
(void) tee_fputs("|", PAGER);
for (uint off=0; (field = mysql_fetch_field(result)) ; off++)
{
- tee_fprintf(PAGER, " %-*s|",min(field->max_length,MAX_COLUMN_LENGTH),
+ tee_fprintf(PAGER, " %-*s|",(int) min(field->max_length,
+ MAX_COLUMN_LENGTH),
field->name);
num_flag[off]= IS_NUM(field->type);
}
@@ -2602,32 +2610,31 @@ com_use(String *buffer __attribute__((unused)), char *line)
put_info("USE must be followed by a database name", INFO_ERROR);
return 0;
}
-
- /*
- We need to recheck the current database, because it may change
- under our feet, for example if DROP DATABASE or RENAME DATABASE
- (latter one not yet available by the time the comment was written)
+ /*
+ We need to recheck the current database, because it may change
+ under our feet, for example if DROP DATABASE or RENAME DATABASE
+ (latter one not yet available by the time the comment was written)
*/
- current_db= 0; // Let's reset current_db, assume it's gone
- /*
- We don't care about in case of an error below because current_db
- was just set to 0.
+ /* Let's reset current_db, assume it's gone */
+ my_free(current_db, MYF(MY_ALLOW_ZERO_PTR));
+ current_db= 0;
+ /*
+ We don't care about in case of an error below because current_db
+ was just set to 0.
*/
if (!mysql_query(&mysql, "SELECT DATABASE()") &&
(res= mysql_use_result(&mysql)))
{
row= mysql_fetch_row(res);
- if (row[0] &&
- (!current_db || cmp_database(charset_info, current_db, row[0])))
+ if (row[0])
{
- my_free(current_db, MYF(MY_ALLOW_ZERO_PTR));
current_db= my_strdup(row[0], MYF(MY_WME));
}
- (void) mysql_fetch_row(res); // Read eof
+ (void) mysql_fetch_row(res); // Read eof
mysql_free_result(res);
}
-
- if (!current_db || cmp_database(charset_info, current_db, tmp))
+
+ if (!current_db || cmp_database(charset_info, current_db,tmp))
{
if (one_database)
skip_updates= 1;
@@ -2835,14 +2842,18 @@ com_status(String *buffer __attribute__((unused)),
const char *status;
char buff[22];
ulonglong id;
+ MYSQL_RES *result;
+ LINT_INIT(result);
tee_puts("--------------", stdout);
usage(1); /* Print version */
if (connected)
{
- MYSQL_RES *result;
- LINT_INIT(result);
tee_fprintf(stdout, "\nConnection id:\t\t%lu\n",mysql_thread_id(&mysql));
+ /*
+ Don't remove "limit 1",
+ it is protection againts SQL_SELECT_LIMIT=0
+ */
if (!mysql_query(&mysql,"select DATABASE(), USER() limit 1") &&
(result=mysql_use_result(&mysql)))
{
@@ -2853,7 +2864,7 @@ com_status(String *buffer __attribute__((unused)),
tee_fprintf(stdout, "Current user:\t\t%s\n", cur[1]);
}
mysql_free_result(result);
- }
+ }
#ifdef HAVE_OPENSSL
if (mysql.net.vio && mysql.net.vio->ssl_arg &&
SSL_get_cipher((SSL*) mysql.net.vio->ssl_arg))
@@ -2887,9 +2898,30 @@ com_status(String *buffer __attribute__((unused)),
if ((id= mysql_insert_id(&mysql)))
tee_fprintf(stdout, "Insert id:\t\t%s\n", llstr(id, buff));
- tee_fprintf(stdout, "Client characterset:\t%s\n",
- charset_info->name);
- tee_fprintf(stdout, "Server characterset:\t%s\n", mysql.charset->name);
+ /*
+ Don't remove "limit 1",
+ it is protection againts SQL_SELECT_LIMIT=0
+ */
+ if (!mysql_query(&mysql,"select @@character_set_client, @@character_set_connection, @@character_set_server, @@character_set_database limit 1") &&
+ (result=mysql_use_result(&mysql)))
+ {
+ MYSQL_ROW cur=mysql_fetch_row(result);
+ if (cur)
+ {
+ tee_fprintf(stdout, "Server characterset:\t%s\n", cur[0] ? cur[0] : "");
+ tee_fprintf(stdout, "Db characterset:\t%s\n", cur[3] ? cur[3] : "");
+ tee_fprintf(stdout, "Client characterset:\t%s\n", cur[2] ? cur[2] : "");
+ tee_fprintf(stdout, "Conn. characterset:\t%s\n", cur[1] ? cur[1] : "");
+ }
+ mysql_free_result(result);
+ }
+ else
+ {
+ /* Probably pre-4.1 server */
+ tee_fprintf(stdout, "Client characterset:\t%s\n", charset_info->csname);
+ tee_fprintf(stdout, "Server characterset:\t%s\n", mysql.charset->csname);
+ }
+
#ifndef EMBEDDED_LIBRARY
if (strstr(mysql_get_host_info(&mysql),"TCP/IP") || ! mysql.unix_socket)
tee_fprintf(stdout, "TCP port:\t\t%d\n", mysql.port);
@@ -2948,7 +2980,12 @@ put_info(const char *str,INFO_TYPE info_type, uint error, const char *sqlstate)
(void) fflush(file);
fprintf(file,"ERROR");
if (error)
- (void) fprintf(file," %d",error);
+ {
+ if (sqlstate)
+ (void) fprintf(file," %d (%s)",error, sqlstate);
+ else
+ (void) fprintf(file," %d",error);
+ }
if (status.query_start_line && line_numbers)
{
(void) fprintf(file," at line %lu",status.query_start_line);
diff --git a/client/mysqladmin.c b/client/mysqladmin.c
index df3e8dfed62..bccbf29ef83 100644
--- a/client/mysqladmin.c
+++ b/client/mysqladmin.c
@@ -25,7 +25,7 @@
#include <sys/stat.h>
#include <mysql.h>
-#define ADMIN_VERSION "8.40"
+#define ADMIN_VERSION "8.41"
#define MAX_MYSQL_VAR 256
#define SHUTDOWN_DEF_TIMEOUT 3600 /* Wait for shutdown */
#define MAX_TRUNC_LENGTH 3
@@ -209,6 +209,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
while (*argument) *argument++= 'x'; /* Destroy argument */
if (*start)
start[1]=0; /* Cut length of argument */
+ tty_password= 0;
}
else
tty_password=1;
@@ -268,7 +269,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
int main(int argc,char *argv[])
{
- int error, ho_error;
+ int error= 0, ho_error;
MYSQL mysql;
char **commands, **save_argv;
@@ -313,10 +314,25 @@ int main(int argc,char *argv[])
mysql_options(&mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name);
#endif
if (sql_connect(&mysql, option_wait))
- error = 1;
+ {
+ unsigned int err= mysql_errno(&mysql);
+ if (err >= CR_MIN_ERROR && err <= CR_MAX_ERROR)
+ error= 1;
+ else
+ {
+ /* Return 0 if all commands are PING */
+ for (; argc > 0; argv++, argc--)
+ {
+ if (find_type(argv[0], &command_typelib, 2) != ADMIN_PING)
+ {
+ error= 1;
+ break;
+ }
+ }
+ }
+ }
else
{
- error = 0;
while (!interrupted && (!opt_count_iterations || nr_iterations))
{
new_line = 0;
diff --git a/client/mysqlcheck.c b/client/mysqlcheck.c
index b072d1c86fe..8182b95fb83 100644
--- a/client/mysqlcheck.c
+++ b/client/mysqlcheck.c
@@ -16,7 +16,7 @@
/* By Jani Tolonen, 2001-04-20, MySQL Development Team */
-#define CHECK_VERSION "2.4.3"
+#define CHECK_VERSION "2.4.4"
#include "client_priv.h"
#include <m_ctype.h>
@@ -246,6 +246,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
while (*argument) *argument++= 'x'; /* Destroy argument */
if (*start)
start[1] = 0; /* Cut length of argument */
+ tty_password= 0;
}
else
tty_password = 1;
@@ -436,18 +437,18 @@ static int process_all_tables_in_db(char *database)
LINT_INIT(res);
if (use_db(database))
return 1;
- if (!(mysql_query(sock, "SHOW TABLES") ||
- (res = mysql_store_result(sock))))
+ if (mysql_query(sock, "SHOW TABLES") ||
+ !((res= mysql_store_result(sock))))
return 1;
if (opt_all_in_1)
{
- /*
+ /*
We need table list in form `a`, `b`, `c`
that's why we need 4 more chars added to to each table name
space is for more readable output in logs and in case of error
*/
-
+
char *tables, *end;
uint tot_length = 0;
diff --git a/client/mysqldump.c b/client/mysqldump.c
index 57367a7d8af..af271343ab9 100644
--- a/client/mysqldump.c
+++ b/client/mysqldump.c
@@ -37,7 +37,7 @@
** 10 Jun 2003: SET NAMES and --no-set-names by Alexander Barkov
*/
-#define DUMP_VERSION "10.7"
+#define DUMP_VERSION "10.8"
#include <my_global.h>
#include <my_sys.h>
@@ -81,7 +81,8 @@ static my_bool verbose=0,tFlag=0,cFlag=0,dFlag=0,quick= 1, extended_insert= 1,
opt_alldbs=0,opt_create_db=0,opt_first_slave=0,opt_set_charset,
opt_autocommit=0,opt_master_data,opt_disable_keys=1,opt_xml=0,
opt_delete_master_logs=0, tty_password=0,
- opt_single_transaction=0, opt_comments= 0, opt_compact= 0;
+ opt_single_transaction=0, opt_comments= 0, opt_compact= 0,
+ opt_hex_blob=0;
static ulong opt_max_allowed_packet, opt_net_buffer_length;
static MYSQL mysql_connection,*sock=0;
static char insert_pat[12 * 1024],*opt_password=0,*current_user=0,
@@ -318,6 +319,8 @@ static struct my_option my_long_options[] =
{"comments", 'i', "Write additional information.",
(gptr*) &opt_comments, (gptr*) &opt_comments, 0, GET_BOOL, NO_ARG,
1, 0, 0, 0, 0, 0},
+ {"hex-blob", OPT_HEXBLOB, "Dump BLOBs in HEX. this mode does not work with extended-insert",
+ (gptr*) &opt_hex_blob, (gptr*) &opt_hex_blob, 0, GET_BOOL, 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}
};
@@ -490,6 +493,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
while (*argument) *argument++= 'x'; /* Destroy argument */
if (*start)
start[1]=0; /* Cut length of argument */
+ tty_password= 0;
}
else
tty_password=1;
@@ -1526,6 +1530,7 @@ static void dumpTable(uint numFields, char *table)
for (i = 0; i < mysql_num_fields(res); i++)
{
+ int is_blob;
if (!(field = mysql_fetch_field(res)))
{
sprintf(query,"%s: Not enough fields from table %s! Aborting.\n",
@@ -1534,6 +1539,17 @@ static void dumpTable(uint numFields, char *table)
error= EX_CONSCHECK;
goto err;
}
+
+ /*
+ 63 is my_charset_bin. If charsetnr is not 63,
+ we have not a BLOB but a TEXT column.
+ we'll dump it in hex only BLOB columns.
+ */
+ is_blob= (opt_hex_blob && field->charsetnr == 63 &&
+ (field->type == FIELD_TYPE_BLOB ||
+ field->type == FIELD_TYPE_LONG_BLOB ||
+ field->type == FIELD_TYPE_MEDIUM_BLOB ||
+ field->type == FIELD_TYPE_TINY_BLOB)) ? 1 : 0;
if (extended_insert)
{
ulong length = lengths[i];
@@ -1554,12 +1570,28 @@ static void dumpTable(uint numFields, char *table)
error= EX_EOM;
goto err;
}
- dynstr_append(&extended_row,"'");
- extended_row.length +=
- mysql_real_escape_string(&mysql_connection,
- &extended_row.str[extended_row.length],row[i],length);
- extended_row.str[extended_row.length]='\0';
- dynstr_append(&extended_row,"'");
+ if (opt_hex_blob && is_blob)
+ {
+ ulong counter;
+ unsigned char *ptr= row[i];
+ dynstr_append(&extended_row, "0x");
+ for (counter = 0; counter < lengths[i]; counter++)
+ {
+ char xx[3];
+ sprintf(xx, "%02X", ptr[counter]);
+ dynstr_append(&extended_row, xx);
+ }
+ }
+ else
+ {
+ dynstr_append(&extended_row,"'");
+ extended_row.length +=
+ mysql_real_escape_string(&mysql_connection,
+ &extended_row.str[extended_row.length],
+ row[i],length);
+ extended_row.str[extended_row.length]='\0';
+ dynstr_append(&extended_row,"'");
+ }
}
else
{
@@ -1610,8 +1642,20 @@ static void dumpTable(uint numFields, char *table)
print_quoted_xml(md_result_file, row[i], lengths[i]);
fputs("</field>\n", md_result_file);
}
- else
- unescape(md_result_file, row[i], lengths[i]);
+ else if (opt_hex_blob && is_blob)
+ { /* sakaik got this idea. */
+ ulong counter;
+ char xx[4];
+ unsigned char *ptr= row[i];
+ fputs("0x", md_result_file);
+ for (counter = 0; counter < lengths[i]; counter++)
+ {
+ sprintf(xx, "%02X", ptr[counter]);
+ fputs(xx, md_result_file);
+ }
+ }
+ else
+ unescape(md_result_file, row[i], lengths[i]);
}
else
{
@@ -1825,7 +1869,7 @@ static int init_dumping(char *database)
if (opt_databases || opt_alldbs)
{
/*
- length of table name * 2 (if name contain quotas), 2 quotas and 0
+ length of table name * 2 (if name contains quotes), 2 quotes and 0
*/
char quoted_database_buf[64*2+3];
char *qdatabase= quote_name(database,quoted_database_buf,opt_quoted);
diff --git a/client/mysqlimport.c b/client/mysqlimport.c
index c68d2d9f724..fae84be610a 100644
--- a/client/mysqlimport.c
+++ b/client/mysqlimport.c
@@ -25,7 +25,7 @@
** * *
** *************************
*/
-#define IMPORT_VERSION "3.4"
+#define IMPORT_VERSION "3.5"
#include "client_priv.h"
#include "mysql_version.h"
@@ -191,6 +191,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
while (*argument) *argument++= 'x'; /* Destroy argument */
if (*start)
start[1]=0; /* Cut length of argument */
+ tty_password= 0;
}
else
tty_password= 1;
@@ -263,14 +264,11 @@ static int write_to_table(char *filename, MYSQL *sock)
{
char tablename[FN_REFLEN], hard_path[FN_REFLEN],
sql_statement[FN_REFLEN*16+256], *end;
- my_bool local_file;
DBUG_ENTER("write_to_table");
DBUG_PRINT("enter",("filename: %s",filename));
- local_file= sock->unix_socket == 0 || opt_local_file;
-
fn_format(tablename, filename, "", "", 1 | 2); /* removes path & ext. */
- if (local_file)
+ if (! opt_local_file)
strmov(hard_path,filename);
else
my_load_path(hard_path, filename, NULL); /* filename includes the path */
@@ -289,7 +287,7 @@ static int write_to_table(char *filename, MYSQL *sock)
to_unix_path(hard_path);
if (verbose)
{
- if (local_file)
+ if (opt_local_file)
fprintf(stdout, "Loading data from LOCAL file: %s into %s\n",
hard_path, tablename);
else
@@ -298,7 +296,7 @@ static int write_to_table(char *filename, MYSQL *sock)
}
sprintf(sql_statement, "LOAD DATA %s %s INFILE '%s'",
opt_low_priority ? "LOW_PRIORITY" : "",
- local_file ? "LOCAL" : "", hard_path);
+ opt_local_file ? "LOCAL" : "", hard_path);
end= strend(sql_statement);
if (replace)
end= strmov(end, " REPLACE");
diff --git a/client/mysqlshow.c b/client/mysqlshow.c
index 9c9fdf6e443..ee478058cdc 100644
--- a/client/mysqlshow.c
+++ b/client/mysqlshow.c
@@ -16,7 +16,7 @@
/* Show databases, tables or columns */
-#define SHOW_VERSION "9.4"
+#define SHOW_VERSION "9.5"
#include "client_priv.h"
#include <my_sys.h>
@@ -257,6 +257,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
while (*argument) *argument++= 'x'; /* Destroy argument */
if (*start)
start[1]=0; /* Cut length of argument */
+ tty_password= 0;
}
else
tty_password=1;
diff --git a/client/mysqltest.c b/client/mysqltest.c
index 02982e8e37f..e5a7a6228e0 100644
--- a/client/mysqltest.c
+++ b/client/mysqltest.c
@@ -42,7 +42,7 @@
**********************************************************************/
-#define MTEST_VERSION "2.1"
+#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
@@ -75,7 +76,7 @@
#ifndef MYSQL_MANAGER_PORT
#define MYSQL_MANAGER_PORT 23546
#endif
-#define MAX_SERVER_ARGS 20
+#define MAX_SERVER_ARGS 64
/*
Sometimes in a test the client starts before
@@ -93,14 +94,46 @@
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};
+
+/* ************************************************************************ */
+/*
+ A line that starts with !$ or $S, and the list of error codes to
+ --error are stored in an internal array of structs. This struct can
+ hold numeric SQL error codes or SQLSTATE codes as strings. The
+ element next to the last active element in the list is set to type
+ ERR_EMPTY. When an SQL statement return an error we use this list to
+ check if this is an expected error.
+*/
+
+enum match_err_type
+{
+ ERR_EMPTY= 0,
+ ERR_ERRNO,
+ ERR_SQLSTATE
+};
+
+typedef struct
+{
+ enum match_err_type type;
+ union
+ {
+ uint errnum;
+ char sqlstate[SQLSTATE_LENGTH+1]; /* \0 terminated string */
+ } code;
+} match_err;
+
+static match_err global_expected_errno[MAX_EXPECTED_ERRORS];
+static uint global_expected_errors;
+
+/* ************************************************************************ */
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;
@@ -124,14 +157,27 @@ static int *cur_block, *block_stack_end;
static int block_stack[BLOCK_STACK_DEPTH];
static int block_ok_stack[BLOCK_STACK_DEPTH];
-static uint global_expected_errno[MAX_EXPECTED_ERRORS], global_expected_errors;
-static CHARSET_INFO *charset_info= &my_charset_latin1;
+static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */
+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];
static my_bool display_result_vertically= FALSE, display_metadata= FALSE;
+/* See the timer_output() definition for details */
+static char *timer_file = NULL;
+static ulonglong timer_start;
+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",
@@ -229,7 +275,9 @@ Q_ENABLE_INFO, Q_DISABLE_INFO,
Q_ENABLE_METADATA, Q_DISABLE_METADATA,
Q_EXEC, Q_DELIMITER,
Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
-Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, Q_EXIT,
+Q_START_TIMER, Q_END_TIMER,
+Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL,
+Q_EXIT,
Q_UNKNOWN, /* Unknown command. */
Q_COMMENT, /* Comments, ignored. */
@@ -242,7 +290,7 @@ struct st_query
char *query, *query_buf,*first_argument,*end;
int first_word_len;
my_bool abort_on_error, require_file;
- uint expected_errno[MAX_EXPECTED_ERRORS];
+ match_err expected_errno[MAX_EXPECTED_ERRORS];
uint expected_errors;
char record_file[FN_REFLEN];
enum enum_commands type;
@@ -308,6 +356,11 @@ const char *command_names[]=
"horizontal_results",
"query_vertical",
"query_horizontal",
+ "start_timer",
+ "end_timer",
+ "character_set",
+ "disable_ps_protocol",
+ "enable_ps_protocol",
"exit",
0
};
@@ -319,8 +372,7 @@ DYNAMIC_STRING ds_res;
static void die(const char *fmt, ...);
static void init_var_hash();
static VAR* var_from_env(const char *, const char *);
-static byte* get_var_key(const byte* rec, uint* len,
- my_bool __attribute__((unused)) t);
+static byte* get_var_key(const byte* rec, uint* len, my_bool t);
static VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
int val_len);
@@ -330,6 +382,7 @@ int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname);
void reject_dump(const char *record_file, char *buf, int size);
int close_connection(struct st_query* q);
+static void set_charset(struct st_query*);
VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw,
my_bool ignore_not_existing);
int eval_expr(VAR* v, const char *p, const char** p_end);
@@ -480,6 +533,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;
}
@@ -845,10 +900,10 @@ int do_source(struct st_query* q)
1 error
*/
-int do_exec(struct st_query* q)
+static void do_exec(struct st_query* q)
{
- int error= 0;
- DYNAMIC_STRING *ds;
+ int error;
+ DYNAMIC_STRING *ds= NULL; /* Assign just to avoid warning */
DYNAMIC_STRING ds_tmp;
char buf[1024];
FILE *res_file;
@@ -885,7 +940,15 @@ int do_exec(struct st_query* q)
while (fgets(buf, sizeof(buf), res_file))
replace_dynstr_append_mem(ds, buf, strlen(buf));
+ }
+
+ error= pclose(res_file);
+ if (error != 0)
+ die("command \"%s\" failed", cmd);
+
+ if (!disable_result_log)
+ {
if (glob_replace)
free_replace();
@@ -903,9 +966,6 @@ int do_exec(struct st_query* q)
if (ds == &ds_tmp)
dynstr_free(&ds_tmp);
}
- pclose(res_file);
-
- DBUG_RETURN(error);
}
@@ -1225,25 +1285,58 @@ static void get_file_name(char *filename, struct st_query* q)
p[0]=0;
}
+static void set_charset(struct st_query* q)
+{
+ char* charset_name= q->first_argument;
+ char* tmp;
+
+ if (!charset_name || !*charset_name)
+ die("Missing charset name in 'character_set'\n");
+ /* Remove end space */
+ tmp= charset_name;
+ while (*tmp && !my_isspace(charset_info,*tmp))
+ tmp++;
+ *tmp= 0;
+
+ charset_info= get_charset_by_csname(charset_name,MY_CS_PRIMARY,MYF(MY_WME));
+ if (!charset_info)
+ abort_not_supported_test();
+}
-static uint get_ints(uint *to,struct st_query* q)
+static uint get_errcodes(match_err *to,struct st_query* q)
{
- char* p=q->first_argument;
- long val;
- uint count=0;
- DBUG_ENTER("get_ints");
+ char* p= q->first_argument;
+ uint count= 0;
+ DBUG_ENTER("get_errcodes");
if (!*p)
die("Missing argument in %s\n", q->query);
- for (; (p=str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val)) ; p++)
+ do
{
+ if (*p == 'S')
+ {
+ /* SQLSTATE string */
+ int i;
+ p++;
+ for (i = 0; my_isalnum(charset_info, *p) && i < SQLSTATE_LENGTH; p++, i++)
+ to[count].code.sqlstate[i]= *p;
+ to[count].code.sqlstate[i]= '\0';
+ to[count].type= ERR_SQLSTATE;
+ }
+ else
+ {
+ long val;
+ p=str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val);
+ if (p == NULL)
+ die("Invalid argument in %s\n", q->query);
+ to[count].code.errnum= (uint) val;
+ to[count].type= ERR_ERRNO;
+ }
count++;
- *to++= (uint) val;
- if (*p != ',')
- break;
- }
- *to++=0; /* End of data */
+ } while (*(p++) == ',');
+
+ to[count].type= ERR_EMPTY; /* End of data */
DBUG_RETURN(count);
}
@@ -1569,7 +1662,7 @@ int do_connect(struct st_query* q)
if (opt_compress)
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, "latin1");
+ mysql_options(&next_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name);
#ifdef HAVE_OPENSSL
if (opt_use_ssl)
@@ -1715,6 +1808,7 @@ int read_line(char* buf, int size)
c= my_getc(*cur_file);
if (feof(*cur_file))
{
+ found_eof:
if ((*cur_file) != stdin)
my_fclose(*cur_file, MYF(0));
cur_file--;
@@ -1724,6 +1818,10 @@ int read_line(char* buf, int size)
continue;
}
+ /* Line counting is independent of state */
+ if (c == '\n')
+ (*lineno)++;
+
switch(state) {
case R_NORMAL:
/* Only accept '{' in the beginning of a line */
@@ -1739,14 +1837,12 @@ int read_line(char* buf, int size)
else if (c == '\n')
{
state = R_LINE_START;
- (*lineno)++;
}
break;
case R_COMMENT:
if (c == '\n')
{
*p= 0;
- (*lineno)++;
DBUG_RETURN(0);
}
break;
@@ -1758,7 +1854,7 @@ int read_line(char* buf, int size)
else if (my_isspace(charset_info, c))
{
if (c == '\n')
- start_lineno= ++*lineno; /* Query hasn't started yet */
+ start_lineno= *lineno; /* Query hasn't started yet */
no_save= 1;
}
else if (c == '}')
@@ -1824,7 +1920,39 @@ int read_line(char* buf, int size)
}
if (!no_save)
- *p++= c;
+ {
+ /* Could be a multibyte character */
+ /* This code is based on the code in "sql_load.cc" */
+#ifdef USE_MB
+ int charlen = my_mbcharlen(charset_info, c);
+ /* We give up if multibyte character is started but not */
+ /* completed before we pass buf_end */
+ if ((charlen > 1) && (p + charlen) <= buf_end)
+ {
+ int i;
+ char* mb_start = p;
+
+ *p++ = c;
+
+ for (i= 1; i < charlen; i++)
+ {
+ if (feof(*cur_file))
+ goto found_eof; /* FIXME: could we just break here?! */
+ c= my_getc(*cur_file);
+ *p++ = c;
+ }
+ if (! my_ismbchar(charset_info, mb_start, p))
+ {
+ /* It was not a multiline char, push back the characters */
+ /* We leave first 'c', i.e. pretend it was a normal char */
+ while (p > mb_start)
+ my_ungetc(*--p);
+ }
+ }
+ else
+#endif
+ *p++= c;
+ }
}
*p= 0; /* Always end with \0 */
DBUG_RETURN(feof(*cur_file));
@@ -1836,7 +1964,6 @@ static char read_query_buf[MAX_QUERY];
int read_query(struct st_query** q_ptr)
{
char *p = read_query_buf, * p1 ;
- int expected_errno;
struct st_query* q;
DBUG_ENTER("read_query");
@@ -1886,13 +2013,25 @@ int read_query(struct st_query** q_ptr)
p++;
if (*p == '$')
{
- expected_errno= 0;
- p++;
- for (; my_isdigit(charset_info, *p); p++)
- expected_errno = expected_errno * 10 + *p - '0';
- q->expected_errno[0] = expected_errno;
- q->expected_errno[1] = 0;
- q->expected_errors=1;
+ int expected_errno= 0;
+ p++;
+ for (; my_isdigit(charset_info, *p); p++)
+ expected_errno = expected_errno * 10 + *p - '0';
+ q->expected_errno[0].code.errnum = expected_errno;
+ q->expected_errno[0].type= ERR_ERRNO;
+ q->expected_errno[1].type= ERR_EMPTY;
+ q->expected_errors=1;
+ }
+ else if (*p == 'S') /* SQLSTATE */
+ {
+ int i;
+ p++;
+ for (i = 0; my_isalnum(charset_info, *p) && i < SQLSTATE_LENGTH; p++, i++)
+ q->expected_errno[0].code.sqlstate[i]= *p;
+ q->expected_errno[0].code.sqlstate[i]= '\0';
+ q->expected_errno[0].type= ERR_SQLSTATE;
+ q->expected_errno[1].type= ERR_EMPTY;
+ q->expected_errors=1;
}
}
@@ -1962,6 +2101,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.",
@@ -1987,6 +2129,8 @@ static struct my_option my_long_options[] =
#include "sslopt-longopts.h"
{"test-file", 'x', "Read test from/in this file (default stdin).",
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"timer-file", 'm', "File where the timing in micro seconds is stored.",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"tmpdir", 't', "Temporary directory where sockets are put.",
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"user", 'u', "User for login.", (gptr*) &user, (gptr*) &user, 0, GET_STR,
@@ -2048,12 +2192,26 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
die("Could not open %s: errno = %d", argument, errno);
break;
}
+ case 'm':
+ {
+ static char buff[FN_REFLEN];
+ if (!test_if_hard_path(argument))
+ {
+ strxmov(buff, opt_basedir, argument, NullS);
+ argument= buff;
+ }
+ fn_format(buff, argument, "", "", 4);
+ timer_file= buff;
+ unlink(timer_file); /* Ignore error, may not exist */
+ break;
+ }
case 'p':
if (argument)
{
my_free(pass, MYF(MY_ALLOW_ZERO_PTR));
pass= my_strdup(argument, MYF(MY_FAE));
while (*argument) *argument++= 'x'; /* Destroy argument */
+ tty_password= 0;
}
else
tty_password= 1;
@@ -2068,10 +2226,9 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
embedded_server_arg_count=1;
embedded_server_args[0]= (char*) "";
}
- embedded_server_args[embedded_server_arg_count++]=
- my_strdup(argument, MYF(MY_FAE));
- if (embedded_server_arg_count == MAX_SERVER_ARGS ||
- !embedded_server_args[embedded_server_arg_count-1])
+ if (embedded_server_arg_count == MAX_SERVER_ARGS-1 ||
+ !(embedded_server_args[embedded_server_arg_count++]=
+ my_strdup(argument, MYF(MY_FAE))))
{
die("Can't use server argument");
}
@@ -2225,7 +2382,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;
@@ -2235,7 +2421,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)
@@ -2263,7 +2449,7 @@ int run_query(MYSQL* mysql, struct st_query* q, int flags)
if (flags & QUERY_SEND)
{
got_error_on_send= mysql_send_query(mysql, query, query_len);
- if (got_error_on_send && !q->expected_errno[0])
+ if (got_error_on_send && q->expected_errno[0].type == ERR_EMPTY)
die("At line %u: unable to send query '%s' (mysql_errno=%d , errno=%d)",
start_lineno, query, mysql_errno(mysql), errno);
}
@@ -2295,7 +2481,10 @@ int run_query(MYSQL* mysql, struct st_query* q, int flags)
{
for (i=0 ; (uint) i < q->expected_errors ; i++)
{
- if ((q->expected_errno[i] == mysql_errno(mysql)))
+ if (((q->expected_errno[i].type == ERR_ERRNO) &&
+ (q->expected_errno[i].code.errnum == mysql_errno(mysql))) ||
+ ((q->expected_errno[i].type == ERR_SQLSTATE) &&
+ (strcmp(q->expected_errno[i].code.sqlstate,mysql_sqlstate(mysql)) == 0)))
{
if (i == 0 && q->expected_errors == 1)
{
@@ -2309,7 +2498,9 @@ int run_query(MYSQL* mysql, struct st_query* q, int flags)
dynstr_append_mem(ds,"\n",1);
}
/* Don't log error if we may not get an error */
- else if (q->expected_errno[0] != 0)
+ 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");
goto end; /* Ok */
}
@@ -2325,8 +2516,12 @@ int run_query(MYSQL* mysql, struct st_query* q, int flags)
dynstr_append_mem(ds,"\n",1);
if (i)
{
- verbose_msg("query '%s' failed with wrong errno %d instead of %d...",
- q->query, mysql_errno(mysql), q->expected_errno[0]);
+ if (q->expected_errno[0].type == ERR_ERRNO)
+ verbose_msg("query '%s' failed with wrong errno %d instead of %d...",
+ q->query, mysql_errno(mysql), q->expected_errno[0].code.errnum);
+ else
+ verbose_msg("query '%s' failed with wrong sqlstate %s instead of %s...",
+ q->query, mysql_sqlstate(mysql), q->expected_errno[0].code.sqlstate);
error= 1;
goto end;
}
@@ -2346,11 +2541,22 @@ int run_query(MYSQL* mysql, struct st_query* q, int flags)
}*/
}
- if (q->expected_errno[0])
+ if (q->expected_errno[0].type == ERR_ERRNO &&
+ q->expected_errno[0].code.errnum != 0)
{
- error = 1;
+ /* Error code we wanted was != 0, i.e. not an expected success */
verbose_msg("query '%s' succeeded - should have failed with errno %d...",
- q->query, q->expected_errno[0]);
+ q->query, q->expected_errno[0].code.errnum);
+ error = 1;
+ goto end;
+ }
+ else if (q->expected_errno[0].type == ERR_SQLSTATE &&
+ strcmp(q->expected_errno[0].code.sqlstate,"00000") != 0)
+ {
+ /* SQLSTATE we wanted was != "00000", i.e. not an expected success */
+ verbose_msg("query '%s' succeeded - should have failed with sqlstate %s...",
+ q->query, q->expected_errno[0].code.sqlstate);
+ error = 1;
goto end;
}
@@ -2358,56 +2564,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)
@@ -2483,6 +2647,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;
@@ -2599,6 +3333,9 @@ int main(int argc, char **argv)
{
DBUG_ENTER("main");
+ /* Use all time until exit if no explicit 'start_timer' */
+ timer_start= timer_now();
+
save_file[0]=0;
TMPDIR[0]=0;
memset(cons, 0, sizeof(cons));
@@ -2632,12 +3369,17 @@ 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)
mysql_options(&cur_con->mysql,MYSQL_OPT_COMPRESS,NullS);
mysql_options(&cur_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
- mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, "latin1");
+ mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name);
#ifdef HAVE_OPENSSL
if (opt_use_ssl)
@@ -2777,7 +3519,7 @@ int main(int argc, char **argv)
require_file=0;
break;
case Q_ERROR:
- global_expected_errors=get_ints(global_expected_errno,q);
+ global_expected_errors=get_errcodes(global_expected_errno,q);
break;
case Q_REQUIRE:
get_file_name(save_file,q);
@@ -2811,8 +3553,27 @@ int main(int argc, char **argv)
(void) mysql_ping(&cur_con->mysql);
break;
case Q_EXEC:
- (void) do_exec(q);
+ do_exec(q);
+ break;
+ case Q_START_TIMER:
+ /* Overwrite possible earlier start of timer */
+ timer_start= timer_now();
+ break;
+ case Q_END_TIMER:
+ /* End timer before ending mysqltest */
+ timer_output();
+ got_end_timer= TRUE;
+ break;
+ 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;
+
case Q_EXIT:
abort_flag= 1;
break;
@@ -2850,6 +3611,8 @@ int main(int argc, char **argv)
printf("ok\n");
}
+ if (!got_end_timer)
+ timer_output(); /* No end_timer cmd, end it */
free_used_memory();
exit(error ? 1 : 0);
return error ? 1 : 0; /* Keep compiler happy */
@@ -2903,6 +3666,45 @@ static int read_server_arguments(const char *name)
return 0;
}
+/****************************************************************************\
+ *
+ * A primitive timer that give results in milliseconds if the
+ * --timer-file=<filename> is given. The timer result is written
+ * to that file when the result is available. To not confuse
+ * mysql-test-run with an old obsolete result, we remove the file
+ * before executing any commands. The time we measure is
+ *
+ * - If no explicit 'start_timer' or 'end_timer' is given in the
+ * test case, the timer measure how long we execute in mysqltest.
+ *
+ * - If only 'start_timer' is given we measure how long we execute
+ * from that point until we terminate mysqltest.
+ *
+ * - If only 'end_timer' is given we measure how long we execute
+ * from that we enter mysqltest to the 'end_timer' is command is
+ * executed.
+ *
+ * - If both 'start_timer' and 'end_timer' are given we measure
+ * the time between executing the two commands.
+ *
+\****************************************************************************/
+
+static void timer_output(void)
+{
+ if (timer_file)
+ {
+ char buf[32], *end;
+ ulonglong timer= timer_now() - timer_start;
+ end= longlong2str(timer, buf, 10);
+ str_to_file(timer_file,buf, (int) (end-buf));
+ }
+}
+
+static ulonglong timer_now(void)
+{
+ return my_getsystime() / 10000;
+}
+
/****************************************************************************
* Handle replacement of strings
****************************************************************************/