mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
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
This commit is contained in:
@ -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);
|
||||
run_query_display_metadata(field, num_fields, ds);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user