Bug#28497 wait_for_slave_to_stop can cause random replication mysql-test failures

 - Add funtion "query_get_value to allow reading a fields value
   into a $variable
parent b34660a8
...@@ -1125,6 +1125,50 @@ void check_require(DYNAMIC_STRING* ds, const char *fname) ...@@ -1125,6 +1125,50 @@ void check_require(DYNAMIC_STRING* ds, const char *fname)
} }
/*
Remove surrounding chars from string
Return 1 if first character is found but not last
*/
static int strip_surrounding(char* str, char c1, char c2)
{
char* ptr= str;
/* Check if the first non space character is c1 */
while(*ptr && my_isspace(charset_info, *ptr))
ptr++;
if (*ptr == c1)
{
/* Replace it with a space */
*ptr= ' ';
/* Last non space charecter should be c2 */
ptr= strend(str)-1;
while(*ptr && my_isspace(charset_info, *ptr))
ptr--;
if (*ptr == c2)
{
/* Replace it with \0 */
*ptr= 0;
}
else
{
/* Mismatch detected */
return 1;
}
}
return 0;
}
static void strip_parentheses(struct st_command *command)
{
if (strip_surrounding(command->first_argument, '(', ')'))
die("%.*s - argument list started with '%c' must be ended with '%c'",
command->first_word_len, command->query, '(', ')');
}
static byte *get_var_key(const byte* var, uint* len, static byte *get_var_key(const byte* var, uint* len,
my_bool __attribute__((unused)) t) my_bool __attribute__((unused)) t)
{ {
...@@ -1380,12 +1424,11 @@ void var_query_set(VAR *var, const char *query, const char** query_end) ...@@ -1380,12 +1424,11 @@ void var_query_set(VAR *var, const char *query, const char** query_end)
init_dynamic_string(&ds_query, 0, (end - query) + 32, 256); init_dynamic_string(&ds_query, 0, (end - query) + 32, 256);
do_eval(&ds_query, query, end, FALSE); do_eval(&ds_query, query, end, FALSE);
if (mysql_real_query(mysql, ds_query.str, ds_query.length) || if (mysql_real_query(mysql, ds_query.str, ds_query.length))
!(res = mysql_store_result(mysql)))
{
die("Error running query '%s': %d %s", ds_query.str, die("Error running query '%s': %d %s", ds_query.str,
mysql_errno(mysql), mysql_error(mysql)); mysql_errno(mysql), mysql_error(mysql));
} if (!(res= mysql_store_result(mysql)))
die("Query '%s' didn't return a result set", ds_query.str);
dynstr_free(&ds_query); dynstr_free(&ds_query);
if ((row = mysql_fetch_row(res)) && row[0]) if ((row = mysql_fetch_row(res)) && row[0])
...@@ -1440,6 +1483,130 @@ void var_query_set(VAR *var, const char *query, const char** query_end) ...@@ -1440,6 +1483,130 @@ void var_query_set(VAR *var, const char *query, const char** query_end)
} }
/*
Set variable from the result of a field in a query
This function is useful when checking for a certain value
in the output from a query that can't be restricted to only
return some values. A very good example of that is most SHOW
commands.
SYNOPSIS
var_set_query_get_value()
DESCRIPTION
let $variable= query_get_value(<query to run>,<column name>,<row no>);
<query to run> - The query that should be sent to the server
<column name> - Name of the column that holds the field be compared
against the expected value
<row no> - Number of the row that holds the field to be
compared against the expected value
*/
void var_set_query_get_value(struct st_command *command, VAR *var)
{
ulong row_no;
int col_no= -1;
MYSQL_RES* res;
MYSQL* mysql= &cur_con->mysql;
LINT_INIT(res);
static DYNAMIC_STRING ds_query;
static DYNAMIC_STRING ds_col;
static DYNAMIC_STRING ds_row;
const struct command_arg query_get_value_args[] = {
"query", ARG_STRING, TRUE, &ds_query, "Query to run",
"column name", ARG_STRING, TRUE, &ds_col, "Name of column",
"row number", ARG_STRING, TRUE, &ds_row, "Number for row",
};
DBUG_ENTER("var_set_query_get_value");
strip_parentheses(command);
DBUG_PRINT("info", ("query: %s", command->query));
check_command_args(command, command->first_argument, query_get_value_args,
sizeof(query_get_value_args)/sizeof(struct command_arg),
',');
DBUG_PRINT("info", ("query: %s", ds_query.str));
DBUG_PRINT("info", ("col: %s", ds_col.str));
/* Convert row number to int */
if (!str2int(ds_row.str, 10, (long) 0, (long) INT_MAX, &row_no))
die("Invalid row number: '%s'", ds_row.str);
DBUG_PRINT("info", ("row: %s, row_no: %ld", ds_row.str, row_no));
dynstr_free(&ds_row);
/* Remove any surrounding "'s from the query - if there is any */
if (strip_surrounding(ds_query.str, '"', '"'))
die("Mismatched \"'s around query '%s'", ds_query.str);
/* Run the query */
if (mysql_real_query(mysql, ds_query.str, ds_query.length))
die("Error running query '%s': %d %s", ds_query.str,
mysql_errno(mysql), mysql_error(mysql));
if (!(res= mysql_store_result(mysql)))
die("Query '%s' didn't return a result set", ds_query.str);
{
/* Find column number from the given column name */
uint i;
uint num_fields= mysql_num_fields(res);
MYSQL_FIELD *fields= mysql_fetch_fields(res);
for (i= 0; i < num_fields; i++)
{
if (strcmp(fields[i].name, ds_col.str) == 0 &&
strlen(fields[i].name) == ds_col.length)
{
col_no= i;
break;
}
}
if (col_no == -1)
{
mysql_free_result(res);
die("Could not find column '%s' in the result of '%s'",
ds_col.str, ds_query.str);
}
DBUG_PRINT("info", ("Found column %d with name '%s'",
i, fields[i].name));
}
dynstr_free(&ds_col);
{
/* Get the value */
MYSQL_ROW row;
ulong rows= 0;
const char* value= "No such row";
while ((row= mysql_fetch_row(res)))
{
if (++rows == row_no)
{
DBUG_PRINT("info", ("At row %ld, column %d is '%s'",
row_no, col_no, row[col_no]));
/* Found the row to get */
if (row[col_no])
value= row[col_no];
else
value= "NULL";
break;
}
}
eval_expr(var, value, 0);
}
dynstr_free(&ds_query);
mysql_free_result(res);
DBUG_VOID_RETURN;
}
void var_copy(VAR *dest, VAR *src) void var_copy(VAR *dest, VAR *src)
{ {
dest->int_val= src->int_val; dest->int_val= src->int_val;
...@@ -1463,26 +1630,47 @@ void var_copy(VAR *dest, VAR *src) ...@@ -1463,26 +1630,47 @@ void var_copy(VAR *dest, VAR *src)
void eval_expr(VAR *v, const char *p, const char **p_end) void eval_expr(VAR *v, const char *p, const char **p_end)
{ {
static int MIN_VAR_ALLOC= 32; /* MASV why 32? */
VAR *vp; DBUG_ENTER("eval_expr");
DBUG_PRINT("enter", ("p: '%s'", p));
if (*p == '$') if (*p == '$')
{ {
VAR *vp;
if ((vp= var_get(p, p_end, 0, 0))) if ((vp= var_get(p, p_end, 0, 0)))
{
var_copy(v, vp); var_copy(v, vp);
return; DBUG_VOID_RETURN;
}
} }
else if (*p == '`')
if (*p == '`')
{ {
var_query_set(v, p, p_end); var_query_set(v, p, p_end);
DBUG_VOID_RETURN;
} }
else
{
/* Check if this is a "let $var= query_get_value()" */
const char* get_value_str= "query_get_value";
const size_t len= strlen(get_value_str);
if (strncmp(p, get_value_str, len)==0)
{
struct st_command command;
memset(&command, 0, sizeof(command));
command.query= (char*)p;
command.first_word_len= len;
command.first_argument= command.query + len;
command.end= (char*)*p_end;
var_set_query_get_value(&command, v);
DBUG_VOID_RETURN;
}
}
{ {
int new_val_len = (p_end && *p_end) ? int new_val_len = (p_end && *p_end) ?
(int) (*p_end - p) : (int) strlen(p); (int) (*p_end - p) : (int) strlen(p);
if (new_val_len + 1 >= v->alloced_len) if (new_val_len + 1 >= v->alloced_len)
{ {
static int MIN_VAR_ALLOC= 32;
v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ? v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
MIN_VAR_ALLOC : new_val_len + 1; MIN_VAR_ALLOC : new_val_len + 1;
if (!(v->str_val = if (!(v->str_val =
...@@ -1495,9 +1683,10 @@ void eval_expr(VAR *v, const char *p, const char **p_end) ...@@ -1495,9 +1683,10 @@ void eval_expr(VAR *v, const char *p, const char **p_end)
memcpy(v->str_val, p, new_val_len); memcpy(v->str_val, p, new_val_len);
v->str_val[new_val_len] = 0; v->str_val[new_val_len] = 0;
v->int_val=atoi(p); v->int_val=atoi(p);
DBUG_PRINT("info", ("atoi on '%s', returns: %d", p, v->int_val));
v->int_dirty=0; v->int_dirty=0;
} }
return; DBUG_VOID_RETURN;
} }
...@@ -3432,7 +3621,6 @@ void do_connect(struct st_command *command) ...@@ -3432,7 +3621,6 @@ void do_connect(struct st_command *command)
int con_port= port; int con_port= port;
char *con_options; char *con_options;
bool con_ssl= 0, con_compress= 0; bool con_ssl= 0, con_compress= 0;
char *ptr;
static DYNAMIC_STRING ds_connection_name; static DYNAMIC_STRING ds_connection_name;
static DYNAMIC_STRING ds_host; static DYNAMIC_STRING ds_host;
...@@ -3460,20 +3648,7 @@ void do_connect(struct st_command *command) ...@@ -3460,20 +3648,7 @@ void do_connect(struct st_command *command)
DBUG_ENTER("do_connect"); DBUG_ENTER("do_connect");
DBUG_PRINT("enter",("connect: %s", command->first_argument)); DBUG_PRINT("enter",("connect: %s", command->first_argument));
/* Remove parenteses around connect arguments */ strip_parentheses(command);
if ((ptr= strstr(command->first_argument, "(")))
{
/* Replace it with a space */
*ptr= ' ';
if ((ptr= strstr(command->first_argument, ")")))
{
/* Replace it with \0 */
*ptr= 0;
}
else
die("connect - argument list started with '(' must be ended with ')'");
}
check_command_args(command, command->first_argument, connect_args, check_command_args(command, command->first_argument, connect_args,
sizeof(connect_args)/sizeof(struct command_arg), sizeof(connect_args)/sizeof(struct command_arg),
','); ',');
...@@ -4173,16 +4348,12 @@ int read_command(struct st_command** command_ptr) ...@@ -4173,16 +4348,12 @@ int read_command(struct st_command** command_ptr)
DBUG_RETURN(0); DBUG_RETURN(0);
} }
if (!(*command_ptr= command= if (!(*command_ptr= command=
(struct st_command*) my_malloc(sizeof(*command), MYF(MY_WME))) || (struct st_command*) my_malloc(sizeof(*command),
MYF(MY_WME|MY_ZEROFILL))) ||
insert_dynamic(&q_lines, (gptr) &command)) insert_dynamic(&q_lines, (gptr) &command))
die(NullS); die(NullS);
command->require_file[0]= 0;
command->first_word_len= 0;
command->query_len= 0;
command->type= Q_UNKNOWN; command->type= Q_UNKNOWN;
command->query_buf= command->query= 0;
read_command_buf[0]= 0; read_command_buf[0]= 0;
if (read_line(read_command_buf, sizeof(read_command_buf))) if (read_line(read_command_buf, sizeof(read_command_buf)))
{ {
......
...@@ -655,4 +655,43 @@ INSERT INTO t1 SELECT f1 - 256 FROM t1; ...@@ -655,4 +655,43 @@ INSERT INTO t1 SELECT f1 - 256 FROM t1;
INSERT INTO t1 SELECT f1 - 512 FROM t1; INSERT INTO t1 SELECT f1 - 512 FROM t1;
SELECT * FROM t1; SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;
CREATE TABLE t1(
a int, b varchar(255), c datetime
);
SHOW COLUMNS FROM t1;
Field Type Null Key Default Extra
a int(11) YES NULL
b varchar(255) YES NULL
c datetime YES NULL
statement=SHOW COLUMNS FROM t1 row_number=1, column_name="Type", Value=int(11)
statement="SHOW COLUMNS FROM t1" row_number=1, column_name="Type", Value=int(11)
statement=SHOW COLUMNS FROM t1 row_number=1, column_name=Default, Value=NULL
value= ->A B<-
value= 1
mysqltest: At line 1: query_get_value - argument list started with '(' must be ended with ')'
mysqltest: At line 1: Missing required argument 'query' to command 'query_get_value'
mysqltest: At line 1: Missing required argument 'column name' to command 'query_get_value'
mysqltest: At line 1: Missing required argument 'row number' to command 'query_get_value'
value= No such row
value= No such row
mysqltest: At line 1: Invalid row number: 'notnumber'
mysqltest: At line 1: Could not find column 'column_not_exists' in the result of 'SHOW COLUMNS FROM t1'
mysqltest: At line 1: Query 'SET @A = 1' didn't return a result set
mysqltest: At line 1: Could not find column '1 AS B' in the result of 'SELECT 1 AS A'
value= No such row
mysqltest: At line 1: Error running query 'SHOW COLNS FROM t1': 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'COLNS FROM t1' at line 1
Field Type Null Key Default Extra
a int(11) YES -><- NULL
b varchar(255) YES -><- NULL
c datetime YES -><- NULL
Number of columns with Default NULL: 3
SHOW COLUMNS FROM t1;
Field Type Null Key Default Extra
a int(11) YES NULL
b varchar(255) YES NULL
c datetime YES NULL
drop table t1;
End of tests End of tests
...@@ -1811,4 +1811,125 @@ SELECT * FROM t1; ...@@ -1811,4 +1811,125 @@ SELECT * FROM t1;
--enable_result_log --enable_result_log
DROP TABLE t1; DROP TABLE t1;
# ----------------------------------------------------------------------------
# test for query_get_value
# ----------------------------------------------------------------------------
CREATE TABLE t1(
a int, b varchar(255), c datetime
);
SHOW COLUMNS FROM t1;
#------------ Positive tests ------------
# 1. constant parameters
# value is simple string without spaces
let $value= query_get_value(SHOW COLUMNS FROM t1, Type, 1);
--echo statement=SHOW COLUMNS FROM t1 row_number=1, column_name="Type", Value=$value
let $value= query_get_value("SHOW COLUMNS FROM t1", Type, 1);
--echo statement="SHOW COLUMNS FROM t1" row_number=1, column_name="Type", Value=$value
#
# 2. $variables as parameters
# value IS NULL
let $my_show= SHOW COLUMNS FROM t1;
let $column_name= Default;
let $row_number= 1;
let $value= query_get_value($my_show, $column_name, $row_number);
--echo statement=$my_show row_number=$row_number, column_name=$column_name, Value=$value
#
# 3. result set of a SELECT (not recommended, because projection and
# selection could be done much better by pure SELECT functionality)
# value is string with space in the middle
let $value= query_get_value(SELECT 'A B' AS "MyColumn", MyColumn, 1);
--echo value= ->$value<-
#
# 4. column name with space
let $value= query_get_value(SELECT 1 AS "My Column", My Column, 1);
--echo value= $value
#
#------------ Negative tests ------------
# 5. Incomplete statement including missing parameters
# 5.1 incomplete statement
--error 1
--exec echo "let \$value= query_get_value(SHOW;" | $MYSQL_TEST 2>&1
# 5.2 missing query
--error 1
--exec echo "let \$value= query_get_value;" | $MYSQL_TEST 2>&1
# 5.3 missing column name
--error 1
--exec echo "let \$value= query_get_value(SHOW COLUMNS FROM t1);" | $MYSQL_TEST 2>&1
# 5.4 missing row number
--error 1
--exec echo "let \$value= query_get_value(SHOW COLUMNS FROM t1, Field);" | $MYSQL_TEST 2>&1
#
# 6. Somehow "wrong" value of parameters
# 6.1 row parameter
# 6.1.1 non sense number 0
let $value= initialized;
let $value= query_get_value(SHOW COLUMNS FROM t1, Field, 0);
--echo value= $value
# 6.1.2 after the last row
let $value= initialized;
let $value= query_get_value(SHOW COLUMNS FROM t1, Field, 10);
--echo value= $value
# 6.1.3 invalid row number
--error 1
--exec echo "let \$value= query_get_value(SHOW COLUMNS FROM t1, Field, notnumber);" | $MYSQL_TEST 2>&1
# 6.2 column name parameter, name of not existing column
--error 1
--exec echo "let \$value= query_get_value(SHOW COLUMNS FROM t1, column_not_exists, 1);" | $MYSQL_TEST 2>&1
# 6.3. statement which never gives a result set
--error 1
--exec echo "let \$value= query_get_value(SET @A = 1, Field, 1);" | $MYSQL_TEST 2>&1
# 6.4. statement contains a ","
# Note: There is no need to improve this, because we need query_get_value
# for SHOW commands only.
--error 1
--exec echo "let \$value= query_get_value(SELECT 1 AS "A", 1 AS "B", 1);" | $MYSQL_TEST 2>&1
#
# 7. empty result set
let $value= initialized;
let $value= query_get_value(SELECT a FROM t1, a, 1);
--echo value= $value
#
# 9. failing statement
--error 1
--exec echo "let \$value= query_get_value(SHOW COLNS FROM t1, Field, 1);" | $MYSQL_TEST 2>&1
#
# 10. Artificial example how to process a complete SHOW result set:
let $show_statement= SHOW COLUMNS FROM t1;
let $rowno= 1;
let $run=1;
let $count= 0;
--echo
--echo Field Type Null Key Default Extra
while ($run)
{
let $Field= query_get_value($show_statement, Field, $rowno);
if (`SELECT '$Field' = 'No such row'`)
{
let $run= 0;
}
if (`SELECT '$Field' <> 'No such row'`)
{
let $Type= query_get_value($show_statement, Type, $rowno);
let $Null= query_get_value($show_statement, Null, $rowno);
if (`SELECT '$Null' = 'YES'`)
{
inc $count;
}
let $Key= query_get_value($show_statement, Key, $rowno);
let $Default= query_get_value($show_statement, Default, $rowno);
let $Extra= query_get_value($show_statement, Extra, $rowno);
--echo $Field $Type $Null ->$Key<- $Default $Extra
inc $rowno;
}
}
--echo
--echo Number of columns with Default NULL: $count
--echo
eval $show_statement;
drop table t1;
--echo End of tests --echo End of tests
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment