Commit 3fab2946 authored by Bjorn Munch's avatar Bjorn Munch

Bug #57276 mysqltest: add support for simple compares in if/while conditions

      Added more parsing in do_block()
      Limitation: left operand must be variable
      Also changed var_set_int from 57036 to var_check_int
      Added tests to mysqltest.test
      Some tests can now be simplified but will take this later
      Updated after comments, now white space around operator not needed
parent e0678e2c
...@@ -482,7 +482,8 @@ VAR* var_init(VAR* v, const char *name, int name_len, const char *val, ...@@ -482,7 +482,8 @@ VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
int val_len); int val_len);
VAR* var_get(const char *var_name, const char** var_name_end, VAR* var_get(const char *var_name, const char** var_name_end,
my_bool raw, my_bool ignore_not_existing); my_bool raw, my_bool ignore_not_existing);
void eval_expr(VAR* v, const char *p, const char** p_end); void eval_expr(VAR* v, const char *p, const char** p_end,
bool open_end=false);
my_bool match_delimiter(int c, const char *delim, uint length); my_bool match_delimiter(int c, const char *delim, uint length);
void dump_result_to_reject_file(char *buf, int size); void dump_result_to_reject_file(char *buf, int size);
void dump_warning_messages(); void dump_warning_messages();
...@@ -2040,9 +2041,11 @@ static void var_free(void *v) ...@@ -2040,9 +2041,11 @@ static void var_free(void *v)
C_MODE_END C_MODE_END
void var_set_int(VAR *v, const char *str) void var_check_int(VAR *v)
{ {
char *endptr; char *endptr;
char *str= v->str_val;
/* Initially assume not a number */ /* Initially assume not a number */
v->int_val= 0; v->int_val= 0;
v->is_int= false; v->is_int= false;
...@@ -2089,7 +2092,7 @@ VAR *var_init(VAR *v, const char *name, int name_len, const char *val, ...@@ -2089,7 +2092,7 @@ VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
memcpy(tmp_var->str_val, val, val_len); memcpy(tmp_var->str_val, val, val_len);
tmp_var->str_val[val_len]= 0; tmp_var->str_val[val_len]= 0;
} }
var_set_int(tmp_var, val); var_check_int(tmp_var);
tmp_var->name_len = name_len; tmp_var->name_len = name_len;
tmp_var->str_val_len = val_len; tmp_var->str_val_len = val_len;
tmp_var->alloced_len = val_alloc_len; tmp_var->alloced_len = val_alloc_len;
...@@ -2540,7 +2543,7 @@ void var_copy(VAR *dest, VAR *src) ...@@ -2540,7 +2543,7 @@ 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, bool open_end)
{ {
DBUG_ENTER("eval_expr"); DBUG_ENTER("eval_expr");
...@@ -2558,7 +2561,7 @@ void eval_expr(VAR *v, const char *p, const char **p_end) ...@@ -2558,7 +2561,7 @@ void eval_expr(VAR *v, const char *p, const char **p_end)
/* Make sure there was just a $variable and nothing else */ /* Make sure there was just a $variable and nothing else */
const char* end= *p_end + 1; const char* end= *p_end + 1;
if (end < expected_end) if (end < expected_end && !open_end)
die("Found junk '%.*s' after $variable in expression", die("Found junk '%.*s' after $variable in expression",
(int)(expected_end - end - 1), end); (int)(expected_end - end - 1), end);
...@@ -2605,7 +2608,7 @@ void eval_expr(VAR *v, const char *p, const char **p_end) ...@@ -2605,7 +2608,7 @@ void eval_expr(VAR *v, const char *p, const char **p_end)
v->str_val_len = new_val_len; v->str_val_len = new_val_len;
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;
var_set_int(v, p); var_check_int(v);
} }
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -5498,6 +5501,40 @@ int do_done(struct st_command *command) ...@@ -5498,6 +5501,40 @@ int do_done(struct st_command *command)
return 0; return 0;
} }
/* Operands available in if or while conditions */
enum block_op {
EQ_OP,
NE_OP,
GT_OP,
GE_OP,
LT_OP,
LE_OP,
ILLEG_OP
};
enum block_op find_operand(const char *start)
{
char first= *start;
char next= *(start+1);
if (first == '=' && next == '=')
return EQ_OP;
if (first == '!' && next == '=')
return NE_OP;
if (first == '>' && next == '=')
return GE_OP;
if (first == '>')
return GT_OP;
if (first == '<' && next == '=')
return LE_OP;
if (first == '<')
return LT_OP;
return ILLEG_OP;
}
/* /*
Process start of a "if" or "while" statement Process start of a "if" or "while" statement
...@@ -5523,6 +5560,13 @@ int do_done(struct st_command *command) ...@@ -5523,6 +5560,13 @@ int do_done(struct st_command *command)
A '!' can be used before the <expr> to indicate it should A '!' can be used before the <expr> to indicate it should
be executed if it evaluates to zero. be executed if it evaluates to zero.
<expr> can also be a simple comparison condition:
<variable> <op> <expr>
The left hand side must be a variable, the right hand side can be a
variable, number, string or `query`. Operands are ==, !=, <, <=, >, >=.
== and != can be used for strings, all can be used for numerical values.
*/ */
void do_block(enum block_cmd cmd, struct st_command* command) void do_block(enum block_cmd cmd, struct st_command* command)
...@@ -5558,6 +5602,9 @@ void do_block(enum block_cmd cmd, struct st_command* command) ...@@ -5558,6 +5602,9 @@ void do_block(enum block_cmd cmd, struct st_command* command)
if (!expr_start++) if (!expr_start++)
die("missing '(' in %s", cmd_name); die("missing '(' in %s", cmd_name);
while (my_isspace(charset_info, *expr_start))
expr_start++;
/* Check for !<expr> */ /* Check for !<expr> */
if (*expr_start == '!') if (*expr_start == '!')
{ {
...@@ -5576,14 +5623,94 @@ void do_block(enum block_cmd cmd, struct st_command* command) ...@@ -5576,14 +5623,94 @@ void do_block(enum block_cmd cmd, struct st_command* command)
die("Missing '{' after %s. Found \"%s\"", cmd_name, p); die("Missing '{' after %s. Found \"%s\"", cmd_name, p);
var_init(&v,0,0,0,0); var_init(&v,0,0,0,0);
eval_expr(&v, expr_start, &expr_end);
/* If expression starts with a variable, it may be a compare condition */
if (*expr_start == '$')
{
const char *curr_ptr= expr_end;
eval_expr(&v, expr_start, &curr_ptr, true);
while (my_isspace(charset_info, *++curr_ptr))
{}
/* If there was nothing past the variable, skip condition part */
if (curr_ptr == expr_end)
goto NO_COMPARE;
enum block_op operand= find_operand(curr_ptr);
if (operand == ILLEG_OP)
die("Found junk '%.*s' after $variable in condition",
(int)(expr_end - curr_ptr), curr_ptr);
/* We could silently allow this, but may be confusing */
if (not_expr)
die("Negation and comparison should not be combined, please rewrite");
/* Skip the 1 or 2 chars of the operand, then white space */
if (operand == LT_OP || operand == GT_OP)
{
curr_ptr++;
}
else
{
curr_ptr+= 2;
}
while (my_isspace(charset_info, *curr_ptr))
curr_ptr++;
VAR v2;
var_init(&v2,0,0,0,0);
eval_expr(&v2, curr_ptr, &expr_end);
if ((operand!=EQ_OP && operand!=NE_OP) && ! (v.is_int && v2.is_int))
die ("Only == and != are supported for string values");
/* Now we overwrite the first variable with 0 or 1 (for false or true) */
switch (operand)
{
case EQ_OP:
if (v.is_int)
v.int_val= (v2.is_int && v2.int_val == v.int_val);
else
v.int_val= !strcmp (v.str_val, v2.str_val);
break;
case NE_OP:
if (v.is_int)
v.int_val= ! (v2.is_int && v2.int_val == v.int_val);
else
v.int_val= (strcmp (v.str_val, v2.str_val) != 0);
break;
case LT_OP:
v.int_val= (v.int_val < v2.int_val);
break;
case LE_OP:
v.int_val= (v.int_val <= v2.int_val);
break;
case GT_OP:
v.int_val= (v.int_val > v2.int_val);
break;
case GE_OP:
v.int_val= (v.int_val >= v2.int_val);
break;
}
v.is_int= TRUE;
} else
{
if (*expr_start != '`' && ! my_isdigit(charset_info, *expr_start))
die("Expression in if/while must beging with $, ` or a number");
eval_expr(&v, expr_start, &expr_end);
}
NO_COMPARE:
/* Define inner block */ /* Define inner block */
cur_block++; cur_block++;
cur_block->cmd= cmd; cur_block->cmd= cmd;
if (v.int_val) if (v.is_int)
{ {
cur_block->ok= TRUE; cur_block->ok= (v.int_val != 0);
} else } else
/* Any non-empty string which does not begin with 0 is also TRUE */ /* Any non-empty string which does not begin with 0 is also TRUE */
{ {
......
...@@ -401,8 +401,36 @@ true-outer ...@@ -401,8 +401,36 @@ true-outer
Counter is greater than 0, (counter=10) Counter is greater than 0, (counter=10)
Counter is not 0, (counter=0) Counter is not 0, (counter=0)
Counter is true, (counter=alpha) Counter is true, (counter=alpha)
Beta is true
while with string, only once while with string, only once
5<7
5<7 again
5<7 still
5<6
5>=5
5>=5 again
5>3
5==5
5!=8
5!=five
5==3+2
5 == 5
hello == hello
hello == hello
hello != goodbye
two words
two words are two words
right answer
anything goes
0 != string
mysqltest: At line 2: Only == and != are supported for string values
mysqltest: At line 2: Found junk '~= 6' after $variable in condition
mysqltest: At line 2: Expression in if/while must beging with $, ` or a number
counter is 2
counter is 3
counter is 4
counter is 5
counter is 6
counter is 7
1 1
Testing while with not Testing while with not
mysqltest: In included file "MYSQLTEST_VARDIR/tmp/mysqltest_while.inc": At line 64: Nesting too deeply mysqltest: In included file "MYSQLTEST_VARDIR/tmp/mysqltest_while.inc": At line 64: Nesting too deeply
...@@ -807,8 +835,6 @@ dir-list.txt ...@@ -807,8 +835,6 @@ dir-list.txt
SELECT 'c:\\a.txt' AS col; SELECT 'c:\\a.txt' AS col;
col col
z z
hej
mysqltest: At line 1: Found junk ' != 143' after $variable in expression
select 1; select 1;
1 1
1 1
......
...@@ -1163,10 +1163,11 @@ if ($counter) ...@@ -1163,10 +1163,11 @@ if ($counter)
{ {
echo oops, -0 is true; echo oops, -0 is true;
} }
if (beta) # This is no longer allowed, as a precaution against mistyped conditionals
{ # if (beta)
echo Beta is true; # {
} # echo Beta is true;
# }
let $counter=gamma; let $counter=gamma;
while ($counter) while ($counter)
{ {
...@@ -1174,6 +1175,179 @@ while ($counter) ...@@ -1174,6 +1175,179 @@ while ($counter)
let $counter=000; let $counter=000;
} }
# ----------------------------------------------------------------------------
# Test if with compare conditions
# ----------------------------------------------------------------------------
let $ifvar= 5;
let $ifvar2= 6;
if ($ifvar < 7)
{
echo 5<7;
}
if ($ifvar< 7)
{
echo 5<7 again;
}
if ($ifvar<7)
{
echo 5<7 still;
}
if ($ifvar < $ifvar2)
{
echo 5<6;
}
if ($ifvar <= 4)
{
echo 5<=4;
}
if ($ifvar >= 5)
{
echo 5>=5;
}
if ($ifvar>=5)
{
echo 5>=5 again;
}
if ($ifvar > 3)
{
echo 5>3;
}
if ($ifvar == 4)
{
echo 5==4;
}
if ($ifvar == 5)
{
echo 5==5;
}
if ($ifvar != 8)
{
echo 5!=8;
}
# Any number should compare unequal to any string
if ($ifvar != five)
{
echo 5!=five;
}
if ($ifvar == `SELECT 3+2`)
{
echo 5==3+2;
}
if ($ifvar == 5)
{
echo 5 == 5;
}
let $ifvar= hello;
if ($ifvar == hello there)
{
echo hello == hello there;
}
if ($ifvar == hello)
{
echo hello == hello;
}
if ($ifvar == hell)
{
echo hello == hell;
}
if ($ifvar == hello)
{
echo hello == hello;
}
if ($ifvar != goodbye)
{
echo hello != goodbye;
}
let $ifvar= two words;
if ($ifvar == two words)
{
echo two words;
}
if ($ifvar == `SELECT 'two words'`)
{
echo two words are two words;
}
if (42)
{
echo right answer;
}
if (0)
{
echo wrong answer;
}
# Non-empty string treated as 'true'
if (`SELECT 'something'`)
{
echo anything goes;
}
# Make sure 0 and string compare right
let $ifvar= 0;
if ($ifvar == string)
{
echo 0 == string;
}
if ($ifvar != string)
{
echo 0 != string;
}
--write_file $MYSQL_TMP_DIR/mysqltest.sql
let $var= 5;
if ($var >= four)
{
echo 5>=four;
}
EOF
--error 1
--exec $MYSQL_TEST < $MYSQL_TMP_DIR/mysqltest.sql 2>&1
remove_file $MYSQL_TMP_DIR/mysqltest.sql;
--write_file $MYSQL_TMP_DIR/mysqltest.sql
let $var= 5;
if ($var ~= 6)
{
echo 5~=6;
}
EOF
--error 1
--exec $MYSQL_TEST < $MYSQL_TMP_DIR/mysqltest.sql 2>&1
remove_file $MYSQL_TMP_DIR/mysqltest.sql;
--write_file $MYSQL_TMP_DIR/mysqltest.sql
let $var= text;
if (var == text)
{
echo Oops I forgot the $;
}
EOF
--error 1
--exec $MYSQL_TEST < $MYSQL_TMP_DIR/mysqltest.sql 2>&1
remove_file $MYSQL_TMP_DIR/mysqltest.sql;
# ----------------------------------------------------------------------------
# Test while with compare conditions
# ----------------------------------------------------------------------------
let $counter= 2;
while ($counter < 5)
{
echo counter is $counter;
inc $counter;
}
let $ifvar=;
while ($ifvar != stop)
{
if ($counter >= 7)
{
let $ifvar= stop;
}
echo counter is $counter;
inc $counter;
}
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
# Test while, { and } # Test while, { and }
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
...@@ -2529,26 +2703,6 @@ rmdir $MYSQLTEST_VARDIR/tmp/testdir; ...@@ -2529,26 +2703,6 @@ rmdir $MYSQLTEST_VARDIR/tmp/testdir;
--replace_result c:\\a.txt z --replace_result c:\\a.txt z
SELECT 'c:\\a.txt' AS col; SELECT 'c:\\a.txt' AS col;
#
# Bug#32307 mysqltest - does not detect illegal if syntax
#
let $test= 1;
if ($test){
echo hej;
}
--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql
if ($mysql_errno != 1436)
{
echo ^ Should not be allowed!
}
EOF
--error 1
--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1
remove_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql;
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
# Test that -- is not allowed as comment, only as mysqltest builtin command # Test that -- is not allowed as comment, only as mysqltest builtin command
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
......
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