1
0
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:
monty@mysql.com
2004-10-26 19:30:01 +03:00
parent 8b6839e644
commit 6fbc869d18
56 changed files with 934 additions and 196 deletions

View File

@ -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;
}
}