diff options
Diffstat (limited to 'client/mysqltest.cc')
-rw-r--r-- | client/mysqltest.cc | 161 |
1 files changed, 147 insertions, 14 deletions
diff --git a/client/mysqltest.cc b/client/mysqltest.cc index d3d20f109a2..e0fed9704a5 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -487,7 +487,8 @@ VAR* var_init(VAR* v, const char *name, int name_len, const char *val, int val_len); VAR* var_get(const char *var_name, const char** var_name_end, 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, bool backtick=true); my_bool match_delimiter(int c, const char *delim, uint length); void dump_result_to_reject_file(char *buf, int size); void dump_warning_messages(); @@ -2045,9 +2046,11 @@ static void var_free(void *v) C_MODE_END -void var_set_int(VAR *v, const char *str) +void var_check_int(VAR *v) { char *endptr; + char *str= v->str_val; + /* Initially assume not a number */ v->int_val= 0; v->is_int= false; @@ -2094,7 +2097,7 @@ VAR *var_init(VAR *v, const char *name, int name_len, const char *val, memcpy(tmp_var->str_val, val, val_len); 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->str_val_len = val_len; tmp_var->alloced_len = val_alloc_len; @@ -2335,7 +2338,8 @@ void var_query_set(VAR *var, const char *query, const char** query_end) dynstr_append_mem(&result, "\t", 1); } end= result.str + result.length-1; - eval_expr(var, result.str, (const char**) &end); + /* Evaluation should not recurse via backtick */ + eval_expr(var, result.str, (const char**) &end, false, false); dynstr_free(&result); } else @@ -2545,7 +2549,8 @@ 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, bool backtick) { DBUG_ENTER("eval_expr"); @@ -2563,14 +2568,14 @@ void eval_expr(VAR *v, const char *p, const char **p_end) /* Make sure there was just a $variable and nothing else */ const char* end= *p_end + 1; - if (end < expected_end) + if (end < expected_end && !open_end) die("Found junk '%.*s' after $variable in expression", (int)(expected_end - end - 1), end); DBUG_VOID_RETURN; } - if (*p == '`') + if (*p == '`' && backtick) { var_query_set(v, p, p_end); DBUG_VOID_RETURN; @@ -2610,7 +2615,7 @@ void eval_expr(VAR *v, const char *p, const char **p_end) v->str_val_len = new_val_len; memcpy(v->str_val, p, new_val_len); v->str_val[new_val_len] = 0; - var_set_int(v, p); + var_check_int(v); } DBUG_VOID_RETURN; } @@ -4240,7 +4245,7 @@ int do_save_master_pos() const char latest_applied_binlog_epoch_str[]= "latest_applied_binlog_epoch="; if (count) - sleep(1); + my_sleep(100*1000); /* 100ms */ if (mysql_query(mysql, query= "show engine ndb status")) die("failed in '%s': %d %s", query, mysql_errno(mysql), mysql_error(mysql)); @@ -4329,7 +4334,7 @@ int do_save_master_pos() count++; if (latest_handled_binlog_epoch >= start_epoch) do_continue= 0; - else if (count > 30) + else if (count > 300) /* 30s */ { break; } @@ -5521,6 +5526,40 @@ int do_done(struct st_command *command) 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 @@ -5546,6 +5585,13 @@ int do_done(struct st_command *command) A '!' can be used before the <expr> to indicate it should 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) @@ -5581,11 +5627,16 @@ void do_block(enum block_cmd cmd, struct st_command* command) if (!expr_start++) die("missing '(' in %s", cmd_name); + while (my_isspace(charset_info, *expr_start)) + expr_start++; + /* Check for !<expr> */ if (*expr_start == '!') { not_expr= TRUE; - expr_start++; /* Step past the '!' */ + expr_start++; /* Step past the '!', then any whitespace */ + while (*expr_start && my_isspace(charset_info, *expr_start)) + expr_start++; } /* Find ending ')' */ expr_end= strrchr(expr_start, ')'); @@ -5599,14 +5650,96 @@ void do_block(enum block_cmd cmd, struct st_command* command) die("Missing '{' after %s. Found \"%s\"", cmd_name, p); 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; + case ILLEG_OP: + die("Impossible operator, this cannot happen"); + } + + 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 */ cur_block++; cur_block->cmd= cmd; - if (v.int_val) + if (v.is_int) { - cur_block->ok= TRUE; + cur_block->ok= (v.int_val != 0); } else /* Any non-empty string which does not begin with 0 is also TRUE */ { |