YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense alongwiththisprogram;ifnot,writetotheFreeSoftware
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
/* Info on properties that can be set with --enable_X and --disable_X */
struct property {
my_bool *var; /* Actual variable */
my_bool set; /* Has been set for ONE command */
my_bool old; /* If set, thus is the old value */
my_bool reverse; /* Variable is true if disabled */ constchar *env_name; /* Env. variable name */
};
/* Precompiled re's */ static regex_t ps_re; /* the query can be run using PS protocol */ static regex_t ps2_re; /* the query can be run using PS protocol with second execution*/ static regex_t sp_re; /* the query can be run as a SP */ static regex_t view_re; /* the query can be run as a view*/ static regex_t cursor_re; /* the query can be run with cursor protocol*/
/* if set, all results are concated and compared against this file */ constchar *result_file_name= 0;
typedefstruct
{ char *name;
size_t name_len; char *str_val;
size_t str_val_len; int int_val;
size_t alloced_len; bool int_dirty; /* do not update string if int is updated until first read */ bool is_int; bool alloced;
} VAR;
/*Perl/shell-like variable registers */
VAR var_reg[10];
HASH var_hash;
struct st_connection
{
MYSQL *mysql; /* Used when creating views and sp, to avoid implicit commit */
MYSQL* util_mysql; char *name;
size_t name_len;
MYSQL_STMT* stmt;
MYSQL_BIND *ps_params; /* Set after send to disallow other queries before reap */
my_bool pending;
#ifdef EMBEDDED_LIBRARY
pthread_t tid; constchar *cur_query; int cur_query_len; int command, result;
pthread_mutex_t query_mutex;
pthread_cond_t query_cond;
pthread_mutex_t result_mutex;
pthread_cond_t result_cond; int query_done;
my_bool has_thread; #endif/*EMBEDDED_LIBRARY*/
};
longlong to_int() const
{ if (is_numeric) return (longlong)int_val;
errno= 0; longlong val= strtoll(str_val.ptr(), NULL, 10); if (errno == ERANGE)
die("Range error: %.*s value out of range for Integer type",
(int)str_val.length(), str_val.ptr()); return val;
}
unsignedlonglong to_uint() const
{ if (is_numeric) return int_val;
errno= 0; unsignedlonglong val= strtoull(str_val.ptr(), NULL, 10); if (errno == ERANGE)
die("Range error: %.*s value out of range for Integer type",
(int)str_val.length(), str_val.ptr()); return val;
}
// Check for special prefixes (0x, 0b) if (token_len >= 3 && token_start[0] == '0')
{ if (token_start[1] == 'x')
{
base= 16;
} elseif (token_start[1] == 'b')
{
base= 2;
token_start+= 2; // Skip "0b" prefix for strtol
token_len-= 2;
}
}
errno= 0;
parsed_int= strtoull(token_start, &endptr, base); if (errno == ERANGE)
die("Range error: %.*s value out of range for Integer type",
(int)token_len, token_start);
// If the entire token was parsed as an integer, set the type to integer if (endptr == token_start + token_len)
{ if (base == 16 || base == 2 || parsed_int > LLONG_MAX)
set_uint(parsed_int); else
set_int((longlong)parsed_int); return;
}
if (!m_file || m_file == stdout)
DBUG_VOID_RETURN;
if (lines == 0)
DBUG_VOID_RETURN;
lines++;
int show_offset= 0; char buf[256+1]; /* + zero termination for DBUG_PRINT */
size_t bytes; bool found_bof= false;
/* Search backward in file until "lines" newline has been found */ while (lines && !found_bof)
{
show_offset-= sizeof(buf)-1; while(fseek(m_file, show_offset, SEEK_END) != 0 && show_offset < 0)
{
found_bof= true; // Seeking before start of file
show_offset++;
}
if ((bytes= fread(buf, 1, sizeof(buf)-1, m_file)) <= 0)
{ // ferror=0 will happen here if no queries executed yet if (ferror(m_file))
fprintf(stderr, "Failed to read from '%s', errno: %d, feof:%d, ferror:%d\n",
m_file_name, errno, feof(m_file), ferror(m_file));
DBUG_VOID_RETURN;
}
if (fseek(m_file, show_offset, SEEK_END) != 0)
{
fprintf(stderr, "Failed to seek to position %d in '%s', errno: %d",
show_offset, m_file_name, errno);
DBUG_VOID_RETURN;
}
} else {
DBUG_PRINT("info", ("Showing the whole file")); if (fseek(m_file, 0L, SEEK_SET) != 0)
{
fprintf(stderr, "Failed to seek to pos 0 in '%s', errno: %d",
m_file_name, errno);
DBUG_VOID_RETURN;
}
}
while ((bytes= fread(buf, 1, sizeof(buf)-1, m_file)) > 0) if (bytes != fwrite(buf, 1, bytes, stderr))
die("Failed to write to '%s', errno: %d",
m_file_name, errno);
if (!lines)
{
fprintf(stderr, "\nMore results from queries before failure can be found in %s\n",
m_file_name);
}
fflush(stderr);
/* Wrapper for fgets.Strips \r off newlines on Windows. Shouldbeusedwithtogetherwithmy_popen().
*/ staticchar *my_fgets(char * s, int n, FILE * stream, int *len)
{ char *buf = fgets(s, n, stream); if (!buf)
{
*len= 0; return buf;
}
if ((res= mysql_store_result(mysql)) == NULL)
{ /* No result set returned */
DBUG_VOID_RETURN;
}
if (mysql_num_rows(res) <= 1)
{ /* Don't display the last row, it's "last error" */
} else
{
MYSQL_ROW row; unsignedint row_num= 0; unsignedint num_fields= mysql_num_fields(res);
fprintf(stderr, "\nWarnings from just before the error:\n"); while ((row= mysql_fetch_row(res)))
{ unsignedint i; unsignedlong *lengths= mysql_fetch_lengths(res);
if (++row_num >= mysql_num_rows(res))
{ /* Don't display the last row, it's "last error" */ break;
}
for (i= 0; i < num_args; i++)
{ conststruct command_arg *arg= &args[i]; char delimiter;
switch (arg->type) { /* A string */ case ARG_STRING: /* Skip leading spaces */ while (*ptr && *ptr == ' ')
ptr++;
start= ptr;
delimiter = delimiter_arg; /* If start of arg is ' ` or " search to matching quote end instead */ if (*ptr && strchr ("'`\"", *ptr))
{
delimiter= *ptr;
start= ++ptr;
} /* Find end of arg, terminated by "delimiter" */ while (*ptr && *ptr != delimiter)
ptr++; if (ptr > start)
{
init_dynamic_string(arg->ds, 0, ptr-start, 32);
do_eval(arg->ds, start, ptr, FALSE);
} else
{ /* Empty string */
init_dynamic_string(arg->ds, "", 0, 0);
} /* Find real end of arg, terminated by "delimiter_arg" */ /* This will do nothing if arg was not closed by quotes */ while (*ptr && *ptr != delimiter_arg)
ptr++;
command->last_argument= (char*)ptr;
/* Step past the delimiter */ if (*ptr && *ptr == delimiter_arg)
ptr++;
DBUG_PRINT("info", ("val: %s", arg->ds->str)); break;
/* Rest of line */ case ARG_REST:
start= ptr;
init_dynamic_string(arg->ds, 0, command->query_len, 256);
do_eval(arg->ds, start, command->end, FALSE);
command->last_argument= command->end;
DBUG_PRINT("info", ("val: %s", arg->ds->str)); break;
for (;;)
{
err_file--;
s+= my_snprintf(s, end - s, "included from %s at line %d:\n",
err_file->file_name, err_file->lineno); if (err_file == file_stack) break;
} return s - start;
}
staticvoid make_error_message(char *buf, size_t len, constchar *fmt, va_list args)
{ char *s= buf, *end= buf + len;
s+= my_snprintf(s, end - s, "mysqltest: "); if (cur_file && cur_file != file_stack)
{
s+= my_snprintf(s, end - s, "In included file \"%s\": \n",
cur_file->file_name);
s+= print_file_stack(s, end);
}
if (start_lineno > 0)
s+= my_snprintf(s, end -s, "At line %u: ", start_lineno); if (!fmt)
fmt= "unknown error";
s+= my_vsnprintf(s, end - s, fmt, args);
s+= my_snprintf(s, end -s, "\n");
}
/* Print include filestack */
fflush(stdout);
fprintf(stderr, "The test '%s' is not supported by this installation\n",
file_stack->file_name);
fprintf(stderr, "Detected in file %s at line %d\n",
cur_file->file_name, cur_file->lineno);
int len; while (my_fgets(buf, sizeof(buf), res_file, &len))
{
DBUG_PRINT("info", ("buf: %s", buf)); if(ds_res)
{ /* Save the output of this command in the supplied string */
dynstr_append_mem(ds_res, buf,len);
} else
{ /* Print it directly on screen */
fprintf(stdout, "%s", buf);
}
}
if (init_dynamic_string(&ds_tmp, "", 256, 256))
die("Out of memory");
/* determine if we have diff on Windows needsspecialprocessingduetoreturnvalues onthatOS ThistestisonlydoneonWindowssinceit'sonlyneededthere inordertocorrectlydetectnon-availabilityof'diff',and thewayit'simplementeddoesnotworkwithdefault'diff'onSolaris.
*/ #ifdef _WIN32 if (diff_check("diff"))
diff_name = "diff"; elseif (diff_check("mtrdiff"))
diff_name = "mtrdiff"; else
diff_name = 0; #else
diff_name = "diff"; /* Otherwise always assume it's called diff */ #endif
if (diff_name)
{ /* First try with unified diff */ if (run_tool(diff_name,
&ds_tmp, /* Get output from diff in ds_tmp */ "-u",
filename1,
filename2, "2>&1",
NULL) > 1) /* Most "diff" tools return >1 if error */
{
dynstr_set(&ds_tmp, "");
/* Fallback to context diff with "diff -c" */ if (run_tool(diff_name,
&ds_tmp, /* Get output from diff in ds_tmp */ "-c",
filename1,
filename2, "2>&1",
NULL) > 1) /* Most "diff" tools return >1 if error */
{
dynstr_set(&ds_tmp, "");
/* Fallback to simple diff with "diff" */ if (run_tool(diff_name,
&ds_tmp, /* Get output from diff in ds_tmp */
filename1,
filename2, "2>&1",
NULL) > 1) /* Most "diff" tools return >1 if error */
{
diff_name= 0;
}
}
}
}
if (! diff_name)
{ /* Fallbacktodumpbothfilestoresultfileandinform aboutinstalling"diff"
*/ char message[]= "\n" "\n" "The two files differ but it was not possible to execute 'diff' in\n" "order to show only the difference. Instead the whole content of the\n" "two files was shown for you to diff manually.\n\n" "To get a better report you should install 'diff' on your system, which you\n" "for example can get from http://www.gnu.org/software/diffutils/diffutils.html\n" #ifdef _WIN32 "or http://gnuwin32.sourceforge.net/packages/diffutils.htm\n" #endif "\n";
dynstr_append_mem(&ds_tmp, message, sizeof(message));
if (init_dynamic_string(&fd1_result, 0, fd1_length, 0) ||
init_dynamic_string(&fd2_result, 0, fd2_length, 0))
die("Out of memory when allocating data for result");
(void) my_seek(fd1, 0, SEEK_SET, MYF(0));
(void) my_seek(fd2, 0, SEEK_SET, MYF(0)); if (my_read(fd1, (uchar*) fd1_result.str, fd1_length, MYF(MY_WME | MY_NABP)))
die("Error when reading data from result file"); if (my_read(fd2, (uchar*) fd2_result.str, fd2_length, MYF(MY_WME | MY_NABP)))
die("Error when reading data from result file");
if (global_subst &&
(fd1_length != fd2_length ||
memcmp(fd1_result.str, fd2_result.str, fd1_length)))
{ /** @todoMARIA_HACK Thisservesforwhenatestisrunwith--default-storage-engine=X whereXisnotMyISAM:testsusingSHOWCREATETABLEwillalwaysfail becauseSHOWCREATETABLEprintsXinsteadofMyISAM.With --global-subst=X,MyISAM,suchtrivialdifferencesareeliminatedand testmaybereportedaspassing. --global-substisonlyaquickwaytorunalotofexistingtests withMariaandfindbugs;itisnotgoodenoughforreachingthemain treeswhenMariaismergedintothem. --global-substshouldberemoved.
*/
size_t global_subst_from_len= strlen(global_subst_from);
size_t global_subst_to_len= strlen(global_subst_to); while (replace(&fd1_result,
global_subst_from, global_subst_from_len,
global_subst_to, global_subst_to_len) == 0) /* do nothing */ ; /* let's compare again to see if it is ok now */
}
/* 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 character 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 */ return1;
}
} return0;
}
staticvoid strip_parentheses(struct st_command *command)
{ if (strip_surrounding(command->first_argument, '(', ')'))
die("%.*sB - argument list started with '%c' must be ended with '%c'",
command->first_word_len, command->query, '(', ')');
}
/* Initially assume not a number */
v->int_val= 0;
v->is_int= false;
v->int_dirty= false; if (!str) return;
v->int_val = (int) strtol(str, &endptr, 10); /* It is an int if strtol consumed something up to end/space/tab */ if (endptr > str && (!*endptr || *endptr == ' ' || *endptr == '\t'))
v->is_int= true;
}
VAR *var_init(VAR *v, constchar *name, size_t name_len, constchar *val, size_t val_len)
{
size_t val_alloc_len;
VAR *tmp_var; if (!name_len && name)
name_len = strlen(name); if (!val_len && val)
val_len = strlen(val) ; if (!val)
val_len= 0;
val_alloc_len = val_len + 16; /* room to grow */ if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(PSI_NOT_INSTRUMENTED, sizeof(*tmp_var)
+ name_len+2, MYF(MY_WME|MY_FAE))))
die("Out of memory");
init_dynamic_string(&result, "", 512, 512);
lengths= mysql_fetch_lengths(res); for (i= 0; i < mysql_num_fields(res); i++)
{ if (row[i])
{ /* Add column to tab separated string */ char *val= row[i];
size_t len= lengths[i];
if (glob_replace_regex)
{ /* Regex replace */ if (!multi_reg_replace(glob_replace_regex, (char*)val))
{
val= glob_replace_regex->buf;
len= strlen(val);
}
}
if (glob_replace)
replace_strings_append(glob_replace, &result, val); else
dynstr_append_mem(&result, val, len);
}
dynstr_append_mem(&result, "\t", 1);
}
end= result.str + result.length-1; /* Evaluation should not recurse via backtick */
eval_expr(var, result.str, (constchar**) &end, false, false);
dynstr_free(&result);
} else
eval_expr(var, "", 0);
mysql_free_result(res);
DBUG_VOID_RETURN;
}
staticvoid
set_result_format_version(ulong new_version)
{ switch (new_version){ case1: /* The first format */ break; case2: /* New format that also writes comments and empty lines
from test file to result */ break; default:
die("Version format %lu has not yet been implemented", new_version); break;
}
opt_result_format_version= new_version;
}
/* Convert version number to int */ if (!str2int(ds_version.str, ds_version.length, 10, &version))
die("Invalid version number: '%s'", ds_version.str);
/* Convert row number to int */ if (!str2int(ds_row.str, ds_row.length, 10, &row_no))
die("Invalid row number: '%s'", ds_row.str);
DBUG_PRINT("info", ("row: %s, row_no: %u", 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, (ulong)ds_query.length))
{
handle_error(curr_command, mysql_errno(mysql), mysql_error(mysql),
mysql_sqlstate(mysql), &ds_res); /* If error was acceptable, return empty string */
dynstr_free(&ds_query);
dynstr_free(&ds_col);
eval_expr(var, "", 0);
DBUG_VOID_RETURN;
}
if (!(res= mysql_store_result(mysql)))
{
report_or_die("Query '%s' didn't return a result set", ds_query.str);
dynstr_free(&ds_query);
dynstr_free(&ds_col);
eval_expr(var, "", 0);
DBUG_VOID_RETURN;
}
{ /* 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);
report_or_die("Could not find column '%s' in the result of '%s'",
ds_col.str, ds_query.str);
dynstr_free(&ds_query);
dynstr_free(&ds_col);
DBUG_VOID_RETURN;
}
DBUG_PRINT("info", ("Found column %d with name '%s'",
i, fields[i].name));
}
dynstr_free(&ds_col);
{ /* Get the value */
MYSQL_ROW row;
uint rows= 0; constchar* value= "No such row";
while ((row= mysql_fetch_row(res)))
{ if (++rows == row_no)
{
DBUG_PRINT("info", ("At row %u, 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";
/* Alloc/realloc data for str_val in dest */ if (dest->alloced_len < src->alloced_len &&
!(dest->str_val= dest->str_val
? (char*)my_realloc(PSI_NOT_INSTRUMENTED, dest->str_val, src->alloced_len,
MYF(MY_WME|MY_FAE))
: (char*)my_malloc(PSI_NOT_INSTRUMENTED, src->alloced_len,
MYF(MY_WME|MY_FAE))))
die("Out of memory"); else
dest->alloced_len= src->alloced_len;
/* Copy str_val data to dest */
dest->str_val_len= src->str_val_len; if (src->str_val_len)
memcpy(dest->str_val, src->str_val, src->str_val_len);
}
/* Skip to treat as pure string if no evaluation */ if (! do_eval) goto NO_EVAL;
if (*p == '$')
{
VAR *vp; constchar* expected_end= *p_end; // Remember var end if ((vp= var_get(p, p_end, 0, 0)))
var_copy(v, vp);
/* Apparently it is not safe to assume null-terminated string */
v->str_val[v->str_val_len]= 0;
/* Make sure there was just a $variable and nothing else */ constchar* end= *p_end + 1; if (end < expected_end && !open_end)
die("Found junk '%.*sB' after $variable in expression",
(int)(expected_end - end - 1), end);
DBUG_VOID_RETURN;
}
if (*p == '`')
{
var_query_set(v, p, p_end);
DBUG_VOID_RETURN;
}
/* 2. if that failed, we look in the suite dir */
strxnmov(buff, sizeof(buff), opt_suite_dir, suffix, name, NullS);
/* buff can not be equal to curname, as a file can never include itself */ if (open_and_set_current(buff))
DBUG_VOID_RETURN;
} else
{ /* 1. try in current file dirname */
dirname_part(buff, curname, &length);
strxnmov(buff, sizeof(buff), buff, name, NullS); if (open_and_set_current(buff))
DBUG_VOID_RETURN;
}
/* 3. now, look in the overlay dir */ if (opt_overlay_dir)
{
strxmov(buff, opt_overlay_dir, name, NullS); if (open_and_set_current(buff))
DBUG_VOID_RETURN;
}
/* 4. if that failed - look in the suite dir */
strxmov(buff, opt_suite_dir, name, NullS); if (open_and_set_current(buff))
DBUG_VOID_RETURN;
/* 5. the last resort - look in the base dir */
strxnmov(buff, sizeof(buff), opt_basedir, name, NullS); if (open_and_set_current(buff))
DBUG_VOID_RETURN;
}
die("Could not open '%s' for reading, errno: %d", name, errno);
DBUG_VOID_RETURN;
}
/* Look for "echo.exe" in same dir as mysqltest was started from */
dirname_part(builtin_echo, my_progname, &echo_length);
fn_format(builtin_echo, ".\\echo.exe",
builtin_echo, "", MYF(MY_REPLACE_DIR));
/* Make sure echo.exe exists */ if (access(builtin_echo, F_OK) != 0)
builtin_echo[0]= 0; return;
/* Skip leading space */ while (*cmd && my_isspace(charset_info, *cmd))
cmd++; if (!*cmd)
{
report_or_die("Missing argument in exec");
DBUG_VOID_RETURN;
}
command->last_argument= command->end;
init_dynamic_string(&ds_cmd, 0, command->query_len+256, 256); /* Eval the command, thus replacing all environment variables */
do_eval(&ds_cmd, cmd, command->end, !is_windows);
/* Check if echo should be replaced with "builtin" echo */ if (builtin_echo[0] && strncmp(cmd, "echo", 4) == 0)
{ /* Replace echo with our "builtin" echo */
replace(&ds_cmd, "echo", 4, builtin_echo, strlen(builtin_echo));
}
if (disable_result_log)
{ /* Collect stderr output as well, for the case app. crashes or returns error.*/
dynstr_append_mem(&ds_cmd, STRING_WITH_LEN(" 2>&1"));
}
DBUG_PRINT("info", ("Executing '%s' as '%s'",
command->first_argument, ds_cmd.str));
if (!(res_file= my_popen(ds_cmd.str, "r")))
{
dynstr_free(&ds_cmd); if (command->abort_on_error)
report_or_die("popen(\"%s\", \"r\") failed", command->first_argument);
DBUG_VOID_RETURN;
}
ds_result= &ds_res; if (display_result_sorted)
{
init_dynamic_string(&ds_sorted, "", 1024, 1024);
ds_result= &ds_sorted;
} int len; while (my_fgets(buf, sizeof(buf), res_file,&len))
{
replace_dynstr_append_mem(ds_result, buf, len);
}
error= my_pclose(res_file);
if (display_result_sorted)
{
dynstr_append_sorted(&ds_res, &ds_sorted);
dynstr_free(&ds_sorted);
} #ifdef _WIN32
end: #endif if (error)
{
uint status; int i;
#ifdef _WIN32
status= WEXITSTATUS(error); #else /* WEXITSTATUS() is only valid for a normal exit; a process killed by an uncaughtsignalmustbetranslatedusingtheshell's128+signal
convention, or the real error is silently lost as status 0. */ if (WIFEXITED(error))
status= WEXITSTATUS(error); elseif (WIFSIGNALED(error))
status= 128 + WTERMSIG(error); else
status= error;
#endif
if (command->abort_on_error)
{
report_or_die("exec of '%s' failed, error: %d, status: %d, errno: %d\n" "Output from before failure:\n%s\n",
ds_cmd.str, error, status, errno,
ds_res.str);
dynstr_free(&ds_cmd);
DBUG_VOID_RETURN;
}
/* Set default wild chars for wild_compare, is changed in embedded mode */
set_wild_chars(1);
for (i= 0; i < dir_info->number_of_files; i++)
{
file= dir_info->dir_entry + i; /* Remove only regular files, i.e. no directories etc. */ /* if (!MY_S_ISREG(file->mystat->st_mode)) */ /* MY_S_ISREG does not work here on Windows, just skip directories */ if (MY_S_ISDIR(file->mystat->st_mode)) continue; if (ds_wild.length &&
wild_compare(file->name, ds_wild.str, 0)) continue;
ds_file_to_remove.length= directory_length;
dynstr_append_mem(&ds_file_to_remove, file->name, strlen(file->name));
DBUG_PRINT("info", ("removing file: %s", ds_file_to_remove.str)); if ((error= (my_delete(ds_file_to_remove.str, MYF(MY_WME)) != 0)))
sys_errno= my_errno; if (error) break;
}
set_wild_chars(0);
my_dirend(dir_info);
if (!((is_sub_path(ds_from_file.str, from_plen, vardir) &&
is_sub_path(ds_to_file.str, to_plen, vardir)) ||
(is_sub_path(ds_from_file.str, from_plen, tmpdir) &&
is_sub_path(ds_to_file.str, to_plen, tmpdir)))) {
report_or_die("Paths '%s' and '%s' are not both under " "MYSQLTEST_VARDIR '%s' or both under MYSQL_TMP_DIR '%s'",
ds_from_file.str, ds_to_file.str, vardir, tmpdir);
DBUG_VOID_RETURN;
}
/* Parse what mode to set */ if (ds_mode.length != 4 ||
str2int(ds_mode.str, ds_mode.length, 8, &mode) == NullS)
die("You must write a 4 digit octal number for mode");
if (ds_delimiter->length > MAX_DELIMITER_LENGTH)
die("Max delimiter length(%d) exceeded", MAX_DELIMITER_LENGTH);
/* Read from file until delimiter is found */ while (1)
{
c= my_getc(cur_file->file); if (c == '\r')
c= my_getc(cur_file->file); if (c == '\n')
{
cur_file->lineno++;
/* Skip newline from the same line as the command */ if (start_lineno == (cur_file->lineno - 1)) continue;
} elseif (start_lineno == cur_file->lineno)
{ /* Nocharactersexcept\nareallowedon thesamelineasthecommand
*/
report_or_die("Trailing characters found after command");
}
if (feof(cur_file->file))
report_or_die("End of file encountered before '%s' delimiter was found",
ds_delimiter->str);
if ((eval_content= (ds_eval_flag.length != 0)) &&
strcasecmp(ds_eval_flag.str, "eval"))
{
report_or_die("Invalid argument '%s' to '%.*s', only 'eval' is allowed",
ds_eval_flag.str, command->first_word_len, command->query);
DBUG_VOID_RETURN;
}
if (!append && access(ds_filename.str, F_OK) == 0)
{ /* The file should not be overwritten */
die("File already exist: '%s'", ds_filename.str);
}
ds_content= command->content; /* If it hasn't been done already by a loop iteration, fill it in */ if (! ds_content.str)
{ /* If no delimiter was provided, use EOF */ if (ds_delimiter.length == 0)
dynstr_set(&ds_delimiter, "EOF");
init_dynamic_string(&ds_content, "", 1024, 1024);
read_until_delimiter(&ds_content, &ds_delimiter);
command->content= ds_content;
} /* This function could be called even if "false", so check before printing */ if (cur_block->ok)
{
DBUG_PRINT("info", ("Writing to file: %s", ds_filename.str)); if (eval_content)
{ /* Evaluate on every execution, keep command->content unevaluated */
DYNAMIC_STRING ds_eval_content; if (init_dynamic_string(&ds_eval_content, "", ds_content.length + 256, 256))
die("Out of memory");
do_eval(&ds_eval_content, ds_content.str,
ds_content.str + ds_content.length, FALSE);
str_to_file2(ds_filename.str, ds_eval_content.str, ds_eval_content.length,
append);
dynstr_free(&ds_eval_content);
} else
str_to_file2(ds_filename.str, ds_content.str, ds_content.length, append);
}
dynstr_free(&ds_filename);
dynstr_free(&ds_delimiter);
dynstr_free(&ds_eval_flag);
DBUG_VOID_RETURN;
}
ds_script= command->content; /* If it hasn't been done already by a loop iteration, fill it in */ if (! ds_script.str)
{ /* If no delimiter was provided, use EOF */ if (ds_delimiter.length == 0)
dynstr_set(&ds_delimiter, "EOF");
/* This function could be called even if "false", so check before doing */ if (cur_block->ok)
{
DBUG_PRINT("info", ("Executing perl: %s", ds_script.str));
/* Create temporary file name */ if ((fd= create_temp_file(temp_file_path, getenv("MYSQLTEST_VARDIR"), "tmp", O_SHARE, MYF(MY_WME))) < 0)
die("Failed to create temporary file for perl command");
my_close(fd, MYF(0));
/* Use the same perl executable as the one that runs mysql-test-run.pl */ constchar *mtr_perl=getenv("MTR_PERL"); if (!mtr_perl)
mtr_perl="perl";
/* Format the "perl <filename>" command */ if (strchr(mtr_perl, ' '))
my_snprintf(buf, sizeof(buf), "\"%s\" %s", mtr_perl, temp_file_path); else
my_snprintf(buf, sizeof(buf), "%s %s", mtr_perl, temp_file_path);
if (!(res_file= my_popen(buf, "r")))
{ if (command->abort_on_error)
die("popen(\"%s\", \"r\") failed", buf);
dynstr_free(&ds_delimiter);
DBUG_VOID_RETURN;
}
int len; while (my_fgets(buf, sizeof(buf), res_file,&len))
{ if (disable_result_log)
{
buf[len - 1] = 0;
DBUG_PRINT("exec_result", ("%s", buf));
} else
{
replace_dynstr_append_mem(&ds_res, buf, len);
}
}
error= my_pclose(res_file);
/* Remove the temporary file, but keep it if perl failed */ if (!error)
my_delete(temp_file_path, MYF(MY_WME));
/* Check for error code that indicates perl could not be started */ int exstat= WEXITSTATUS(error); #ifndef _WIN32 if (exstat == 127)
abort_not_supported_test("perl not found in path"); else #endif
handle_command_error(command, exstat, my_errno);
}
dynstr_free(&ds_delimiter);
DBUG_VOID_RETURN;
}
void do_wait_for_slave_to_stop(struct st_command *c __attribute__((unused)))
{ staticint SLAVE_POLL_INTERVAL= 300000;
MYSQL* mysql = cur_con->mysql; for (;;)
{
MYSQL_RES *UNINIT_VAR(res);
MYSQL_ROW row; int done;
if (mysql_query(mysql,"show status like 'Slave_running'") ||
!(res=mysql_store_result(mysql)))
die("Query failed while probing slave for stop: %s",
mysql_error(mysql)); if (!(row=mysql_fetch_row(res)) || !row[1])
{
mysql_free_result(res);
die("Strange result from query while probing slave for stop");
}
done = !strcmp(row[1],"OFF");
mysql_free_result(res); if (done) break;
my_sleep(SLAVE_POLL_INTERVAL);
} return;
}
if (mysql_query(mysql, query_buf))
die("failed in '%s': %d: %s", query_buf, mysql_errno(mysql),
mysql_error(mysql));
if (!(res= mysql_store_result(mysql)))
die("mysql_store_result() returned NULL for '%s'", query_buf); if (!(row= mysql_fetch_row(res)))
{
mysql_free_result(res);
die("empty result in %s", query_buf);
}
int result= -99; constchar* result_str= row[0]; if (result_str)
result= atoi(result_str);
mysql_free_result(res);
if (!result_str || result < 0)
{ /* master_pos_wait returned NULL or < 0 */
fprintf(stderr, "analyze: sync_with_master\n");
snprintf(query_buf2, sizeof(query_buf2)-1, "select Relay_Master_Log_File, Read_Master_Log_Pos, Gtid_IO_Pos, " "Gtid_Slave_Pos from information_schema.slave_status where " "Connection_name='%.64s'", connection_name);
while (*p && my_isspace(charset_info, *p))
p++; if (*p == ',')
{
p++; while (*p && my_isspace(charset_info, *p))
p++;
start= buff= (char*)my_malloc(PSI_NOT_INSTRUMENTED, strlen(p)+1,
MYF(MY_WME|MY_FAE));
get_string(&buff, &p, command);
}
command->last_argument= p;
}
do_sync_with_master2(command, offset, start); if (buff)
my_free(start); return;
}
int do_save_master_pos()
{
MYSQL_RES *res;
MYSQL_ROW row;
MYSQL *mysql = cur_con->mysql; constchar *query;
DBUG_ENTER("do_save_master_pos");
if (mysql_query(mysql, query= "show master status"))
die("failed in 'show master status': %d %s",
mysql_errno(mysql), mysql_error(mysql));
if (!(res = mysql_store_result(mysql)))
die("mysql_store_result() returned NULL for '%s'", query); if (!(row = mysql_fetch_row(res)))
die("empty result in show master status");
strmake(master_pos.file, row[0], sizeof(master_pos.file)-1);
master_pos.pos= strtoul(row[1], (char**) 0, 10);
strmake(master_pos.gtid, row[4], sizeof(master_pos.gtid)-1);
mysql_free_result(res);
DBUG_RETURN(0);
}
staticvoid convert_base_helper(const My_string &str, int from_base, int to_base,
Expression_value *value)
{ char temp_buffer[66]; // should be enough for any base longlong result; char *endptr; int err;
size_t str_len= str.length();
if (from_base < 0) // Negative base = treat input as SIGNED
result= my_strntoll_8bit(charset_info, str.ptr(), str_len, -from_base,
&endptr, &err); else// Positive base = treat input as UNSIGNED
result= (longlong) my_strntoull_8bit(charset_info, str.ptr(), str_len,
from_base, &endptr, &err);
if (err == ERANGE)
die("Range error: value out of range for Integer type");
if (err != 0 || endptr != str.ptr() + str_len)
die("invalid number '%.*s' for base %d", (int)str_len, str.ptr(), from_base);
endptr= longlong2str(result, temp_buffer, to_base); if (!endptr)
die("could not convert number '%.*s' for base %d",
(int)str_len, str.ptr(), to_base);
// Skip leading zeros while (a_len > 0 && *a_ptr == '0') a_ptr++, a_len--; while (b_len > 0 && *b_ptr == '0') b_ptr++, b_len--;
// Find actual numeric length (digits only)
size_t a_digits= 0, b_digits= 0; for (size_t i= 0; i < a_len && my_isdigit(charset_info, a_ptr[i]); i++) a_digits++; for (size_t i= 0; i < b_len && my_isdigit(charset_info, b_ptr[i]); i++) b_digits++;
// Handle zero cases after removing leading zeros bool a_is_zero= (a_digits == 0); bool b_is_zero= (b_digits == 0);
if (a_is_zero && b_is_zero) return0; if (a_is_zero) return b_negative ? 1 : -1; if (b_is_zero) return a_negative ? -1 : 1;
// Different signs: negative < positive if (a_negative && !b_negative) return -1; if (!a_negative && b_negative) return1;
// Same sign: compare absolute values int abs_cmp= 0;
// First compare by number of digits if (a_digits != b_digits)
{
abs_cmp= (a_digits > b_digits) ? 1 : -1;
} else
{ // Same number of digits: compare digit by digit for (size_t i= 0; i < a_digits; i++)
{ if (a_ptr[i] != b_ptr[i])
{
abs_cmp= (a_ptr[i] > b_ptr[i]) ? 1 : -1; break;
}
}
}
// If both negative, reverse the comparison result return a_negative ? -abs_cmp : abs_cmp;
}
/* Expression Built-in Function Implementations */
if (!args[1].is_numeric || !args[2].is_numeric)
die("conv() bases must be numeric");
if (abs(from_base) < 2 || abs(from_base) > 62)
die("conv() from_base must be between 2 and 62, got %d", from_base); if (abs(to_base) < 2 || abs(to_base) > 62)
die("conv() to_base must be between 2 and 62, got %d", to_base);
p= ds_sleep.str;
sleep_end= ds_sleep.str + ds_sleep.length; while (my_isspace(charset_info, *p))
p++; if (!*p)
die("Missing argument to %.*sB", command->first_word_len,
command->query);
sleep_start= p; /* Check that arg starts with a digit, not handled by my_strtod */ if (!my_isdigit(charset_info, *sleep_start))
die("Invalid argument to %.*sB \"%s\"", command->first_word_len,
command->query, sleep_start);
sleep_val= my_strtod(sleep_start, &sleep_end, &error);
check_eol_junk_line(sleep_end); if (error)
die("Invalid argument to %.*sB \"%s\"", command->first_word_len,
command->query, command->first_argument);
dynstr_free(&ds_sleep);
/* Fixed sleep time selected by --sleep option */ if (opt_sleep >= 0 && !real_sleep)
sleep_val= opt_sleep;
staticint wait_until_dead(int pid, int timeout)
{
DBUG_ENTER("wait_until_dead"); /* Check that server dies */ while (timeout--)
{ if (my_kill(pid, 0) < 0)
{
DBUG_PRINT("info", ("Process %d does not exist anymore", pid));
DBUG_RETURN(0);
}
DBUG_PRINT("info", ("Sleeping, timeout: %d", timeout)); /* Sleep one second */
my_sleep(1000000L);
}
DBUG_RETURN(1); // Did not die
} #endif/* _WIN32 */
void do_shutdown_server(struct st_command *command)
{ long timeout= opt_wait_for_pos_timeout ? opt_wait_for_pos_timeout / 5 : 300; int pid;
DYNAMIC_STRING ds_pidfile_name;
MYSQL* mysql = cur_con->mysql; static DYNAMIC_STRING ds_timeout; conststruct command_arg shutdown_args[] = {
{"timeout", ARG_STRING, FALSE, &ds_timeout, "Timeout before killing server"}
};
DBUG_ENTER("do_shutdown_server");
/* the wait-for-pos' default based value of 'timeout' must fit to MDEV-23511 */
compile_time_assert(default_wait_for_pos_timeout / 5 >= 60);
check_command_args(command, command->first_argument, shutdown_args, sizeof(shutdown_args)/sizeof(struct command_arg), ' ');
if (ds_timeout.length)
{ char* endptr;
timeout= strtol(ds_timeout.str, &endptr, 10); if (*endptr != '\0')
die("Illegal argument for timeout: '%s'", ds_timeout.str);
}
dynstr_free(&ds_timeout);
/* Get the servers pid_file name and use it to read pid */ if (query_get_string(mysql, "SHOW VARIABLES LIKE 'pid_file'", 1,
&ds_pidfile_name))
die("Failed to get pid_file from server");
/* Read the pid from the file */
{ int fd; char buff[32];
if ((fd= my_open(ds_pidfile_name.str, O_RDONLY, MYF(0))) < 0)
die("Failed to open file '%s'", ds_pidfile_name.str);
dynstr_free(&ds_pidfile_name);
if (my_read(fd, (uchar*)&buff, sizeof(buff), MYF(0)) <= 0){
my_close(fd, MYF(0));
die("pid file was empty");
}
my_close(fd, MYF(0));
pid= atoi(buff); if (pid == 0)
die("Pidfile didn't contain a valid number");
}
DBUG_PRINT("info", ("Got pid %d", pid));
if (!timeout || wait_until_dead(pid, timeout))
{ if (timeout)
(void) my_kill(pid, SIGABRT); /* Give server a few seconds to die in all cases */ if (!timeout || wait_until_dead(pid, timeout < 5 ? 5 : timeout))
{
(void) my_kill(pid, SIGKILL);
wait_until_dead(pid, 5);
}
}
DBUG_VOID_RETURN;
}
/* List of error names to error codes */ typedefstruct
{ constchar *name;
uint code; constchar *text;
} st_error;
if (! error_code)
{
DBUG_RETURN("");
} for (; e->name; e++)
{ if (e->code == error_code)
{
DBUG_RETURN(e->name);
}
} /* Apparently, errors without known names may occur */
DBUG_RETURN(unknown_error);
}
/* Skip leading spaces */ while (*p && *p == ' ')
p++;
/* Find end */
end= p; while (*end && *end != ',' && *end != ' ')
end++;
next=end;
/* code to handle variables passed to mysqltest */ if( *p == '$')
{ constchar* fin;
VAR *var = var_get(p,&fin,0,0);
p=var->str_val;
end=p+var->str_val_len;
}
if (*p == 'S')
{ char *to_ptr= to->code.sqlstate;
/* SQLSTATEstring -MustbeSQLSTATE_LENGTHlong -Maycontainonlydigits[0-9]and_uppercase_letters
*/
p++; /* Step past the S */ if ((end - p) != SQLSTATE_LENGTH)
die("The sqlstate must be exactly %d chars long", SQLSTATE_LENGTH);
/* Check sqlstate string validity */ while (*p && p < end)
{ if (my_isdigit(charset_info, *p) || my_isupper(charset_info, *p))
*to_ptr++= *p++; else
die("The sqlstate may only consist of digits[0-9] " \ "and _uppercase_ letters");
}
DBUG_PRINT("info", ("Error name: %s", p));
to->code.errnum= get_errcode_from_name(p, end);
to->type= ERR_ERRNO;
DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum));
} elseif (*p == 'e' || *p == 'w' || *p == 'h')
{
die("The error name definition must start with an uppercase E or W or H");
} else
{
uint val; char *start= p; /* Check that the string passed to str2int only contain digits */ while (*p && p != end)
{ if (!my_isdigit(charset_info, *p))
die("Invalid argument to error: '%s' - " \ "the errno may only consist of digits[0-9]",
command->first_argument);
p++;
}
/* Convert the string to int */ if (!str2int(start, p - start, 10, &val))
die("Invalid argument to error: '%s'", command->first_argument);
/* Find separator */ if (*from == '"' || *from == '\'')
sep= *from++; else
sep=' '; /* Separated with space */
for ( ; (c=*from) ; from++)
{ if (c == '\\' && from[1])
{ /* Escaped character */ /* We can't translate \0 -> ASCII 0 as replace can't handle ASCII 0 */ switch (*++from) { case'n':
*to++= '\n'; break; case't':
*to++= '\t'; break; case'r':
*to++ = '\r'; break; case'b':
*to++ = '\b'; break; case'Z': /* ^Z must be escaped on Win32 */
*to++='\032'; break; default:
*to++ = *from; break;
}
} elseif (c == sep)
{ if (c == ' ' || c != *++from) break; /* Found end of string */
*to++=c; /* Copy duplicated separator */
} else
*to++=c;
} if (*from != ' ' && *from)
die("Wrong string argument in %s", command->query);
while (my_isspace(charset_info,*from)) /* Point to next string */
from++;
*to =0; /* End of string marker */
*to_ptr= to+1; /* Store pointer to end */
*from_ptr= from;
/* Check if this was a variable */ if (*start == '$')
{ constchar *end= to;
VAR *var=var_get(start, &end, 0, 1); if (var && to == (char*) end+1)
{
DBUG_PRINT("info",("var: '%s' -> '%s'", start, var->str_val));
DBUG_RETURN(var->str_val); /* return found variable value */
}
}
DBUG_RETURN(start);
}
/** Changethecurrentconnectiontothegivenst_connection,andupdate $mysql_get_server_versionand$CURRENT_CONNECTIONaccordingly.
*/ void set_current_connection(struct st_connection *con)
{
cur_con= con; /* Update $mysql_get_server_version to that of current connection */
var_set_int("$mysql_get_server_version",
mysql_get_server_version(con->mysql)); /* Update $CURRENT_CONNECTION to the name of the current connection */
var_set_string("$CURRENT_CONNECTION", con->name);
}
if (find_connection_by_name(ds_connection_name.str))
die("Connection %s already exists", ds_connection_name.str);
if (next_con != connections_end)
con_slot= next_con; else
{ if (!(con_slot= find_connection_by_name(CLOSED_CONNECTION)))
die("Connection limit exhausted, you can have max %d connections",
opt_max_connections);
my_free(con_slot->name);
con_slot->name= 0;
}
init_connection_thd(con_slot);
if (!(con_slot->mysql= mysql_init(0)))
die("Failed on mysql_init()");
if (opt_connect_timeout)
mysql_options(con_slot->mysql, MYSQL_OPT_CONNECT_TIMEOUT,
(void *) &opt_connect_timeout); if (mysql_options(con_slot->mysql, MYSQL_OPT_NONBLOCK, 0))
die("Failed to initialise non-blocking API"); if (opt_compress || con_compress)
mysql_options(con_slot->mysql, MYSQL_OPT_COMPRESS, NullS);
mysql_options(con_slot->mysql, MYSQL_SET_CHARSET_NAME,
csname ? csname : charset_info->cs_name.str); if (opt_charsets_dir)
mysql_options(con_slot->mysql, MYSQL_SET_CHARSET_DIR,
opt_charsets_dir); #ifndef EMBEDDED_LIBRARY if (rauth)
mysql_options(con_slot->mysql, MARIADB_OPT_RESTRICTED_AUTH, rauth);
if (protocol)
mysql_options(con_slot->mysql, MYSQL_OPT_PROTOCOL, (char*) &protocol);
if (read_timeout)
{
mysql_options(con_slot->mysql, MYSQL_OPT_READ_TIMEOUT,
(char*)&read_timeout);
}
if (write_timeout)
{
mysql_options(con_slot->mysql, MYSQL_OPT_WRITE_TIMEOUT,
(char*)&write_timeout);
}
if (connect_timeout)
{
mysql_options(con_slot->mysql, MYSQL_OPT_CONNECT_TIMEOUT,
(char*)&connect_timeout);
}
/* Use default db name */ if (ds_database.length == 0)
{
dynstr_set(&ds_database, opt_db);
default_db= 1;
} else
default_db= 0;
if (opt_plugin_dir && *opt_plugin_dir)
mysql_options(con_slot->mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
if (ds_default_auth.length)
mysql_options(con_slot->mysql, MYSQL_DEFAULT_AUTH, ds_default_auth.str);
/* Special database to allow one to connect without a database name */ if (ds_database.length && !strcmp(ds_database.str,"*NO-ONE*"))
dynstr_set(&ds_database, "");
if (connect_n_handle_errors(command, con_slot->mysql,
ds_host.str,ds_user.str,
ds_password.str, ds_database.str,
con_port, ds_sock.str, default_db))
{
DBUG_PRINT("info", ("Inserting connection %s in connection pool",
ds_connection_name.str)); if (!(con_slot->name= my_strdup(PSI_NOT_INSTRUMENTED, ds_connection_name.str, MYF(MY_WME))))
die("Out of memory");
con_slot->name_len= strlen(con_slot->name);
set_current_connection(con_slot);
if (con_slot == next_con)
next_con++; /* if we used the next_con slot, advance the pointer */
} else// Failed to connect. Free the memory.
{
mysql_close(con_slot->mysql);
con_slot->mysql= NULL;
}
int do_done(struct st_command *command)
{ /* Check if empty block stack */ if (cur_block == block_stack)
{ if (*command->query != '}')
die("Stray 'end' command - end of block before beginning");
die("Stray '}' - end of block before beginning");
}
/* Test if inner block has been executed */ if (cur_block->ok && cur_block->cmd == cmd_while)
{ /* Pop block from stack, re-execute outer block */
cur_block--;
parser.current_line = cur_block->line;
} else
{ if (*cur_block->delim)
{ /* Restore "old" delimiter after false if block */ if (safe_strcpy_truncated(delimiter, sizeof delimiter, cur_block->delim))
die("Delimiter too long, truncated");
delimiter_length= strlen(delimiter);
} /* Pop block from stack, goto next line */
cur_block--;
parser.current_line++;
} return0;
}
/* Operands available in if or while conditions */
/* Disable every outer block until while found or block stack ends */ while (cur_block->cmd != cmd_while && cur_block > block_stack)
{
cur_block--;
cur_block->ok= false;
depth++;
}
/* Check if the top block is not 'while' */ if (cur_block->cmd != cmd_while)
{
die("Stray break was found");
} /* Set current block back */
cur_block+= depth;
}
/* Check stack overflow */ if (cur_block == block_stack_end)
die("Nesting too deeply");
/* Set way to find outer block again, increase line counter */
cur_block->line= parser.current_line++;
/* If this block is ignored */ if (!cur_block->ok)
{ /* Inner block should be ignored too */
cur_block++;
cur_block->cmd= cmd;
cur_block->ok= FALSE;
cur_block->delim[0]= '\0';
DBUG_VOID_RETURN;
}
/* Parse and evaluate test expression */
expr_start= strchr(p, '('); if (!expr_start)
die("missing '(' in %s", cmd_name);
expr_start++;
while (my_isspace(charset_info, *expr_start))
expr_start++;
/* Check for !<expr> */ if (*expr_start == '!')
{
not_expr= TRUE;
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, ')'); if (!expr_end)
die("missing ')' in %s", cmd_name);
p= (char*)expr_end+1;
while (*p && my_isspace(charset_info, *p))
p++; if (*p && *p != '{')
die("Missing '{' after %s. Found \"%s\"", cmd_name, p);
if (*expr_start == '$' && *(expr_start + 1) == '(')
{ constchar *scanner= expr_start + 2; int paren_level= 1; char in_quote= 0; // Track quote state bool escaped= false; // Track if the current character is escaped
while (*scanner && paren_level > 0)
{ if (!in_quote)
{ // Not inside quotes - handle parentheses normally if (*scanner == '(') paren_level++; elseif (*scanner == ')') paren_level--; elseif (*scanner == '\'' || *scanner == '"')
in_quote= *scanner; // Start of quoted string
} else
{ // Inside quotes - only look for closing quote if (!escaped && *scanner == in_quote)
in_quote= 0; // End of quoted string
}
escaped= (!escaped && *scanner == '\\');
scanner++;
}
// if the $(...) is the entire condition, evaluate it if (scanner == end_ptr)
{
DYNAMIC_STRING evaluated_expr;
init_dynamic_string(&evaluated_expr, "", 64, 256);
if (cur_block->ok)
{
cur_block->delim[0]= '\0';
} else
{ /* Remember "old" delimiter if entering a false if block */ if (safe_strcpy_truncated(cur_block->delim, sizeof cur_block->delim,
delimiter))
die("Delimiter too long, truncated");
}
DBUG_PRINT("info", ("OK: %d", cur_block->ok));
dynstr_free(&evaluated_expr);
DBUG_VOID_RETURN;
} else
{
die("Expression evaluator $(...) must be the entire condition");
}
}
var_init(&v,0,0,0,0);
/* If expression starts with a variable, it may be a compare condition */
if (*expr_start == '$')
{ constchar *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 '%.*sB' 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++; if (curr_ptr == expr_end)
die("Missing right operand in comparison");
/* Strip off trailing white space */ while (my_isspace(charset_info, expr_end[-1]))
expr_end--; /* strip off ' or " around the string */ if (*curr_ptr == '\'' || *curr_ptr == '"')
{ if (expr_end[-1] != *curr_ptr)
die("Unterminated string value");
curr_ptr++;
expr_end--;
}
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;
var_free(&v2);
} else
{ if (*expr_start != '`' && ! my_isdigit(charset_info, *expr_start))
die("Expression in if/while must begin with $, ` or a number");
eval_expr(&v, expr_start, &expr_end);
}
NO_COMPARE: /* Define inner block */
cur_block++;
cur_block->cmd= cmd; if (v.is_int)
{
cur_block->ok= (v.int_val != 0);
} else /* Any non-empty string which does not begin with 0 is also TRUE */
{
p= v.str_val; /* First skip any leading white space or unary -+ */ while (*p && ((my_isspace(charset_info, *p) || *p == '-' || *p == '+')))
p++;
if (cur_block->ok)
{
cur_block->delim[0]= '\0';
} else
{ /* Remember "old" delimiter if entering a false if block */ if (safe_strcpy_truncated(cur_block->delim, sizeof cur_block->delim,
delimiter))
die("Delimiter too long, truncated");
}
*p= 0;
start_lineno= cur_file->lineno;
DBUG_PRINT("info", ("Starting to read at lineno: %d", start_lineno)); while (1)
{ if (p >= buf_end)
{
my_ptrdiff_t off= p - read_command_buf;
read_command_buf= (char*)my_realloc(PSI_NOT_INSTRUMENTED, read_command_buf,
read_command_buflen*2, MYF(MY_FAE));
p= read_command_buf + off;
read_command_buflen*= 2;
buf_end= read_command_buf + read_command_buflen - max_multibyte_length;
}
skip_char= 0;
c= my_getc(cur_file->file); if (feof(cur_file->file))
{
found_eof: if (cur_file->file != stdin)
{
fclose(cur_file->file);
cur_file->file= 0;
}
my_free(cur_file->file_name);
cur_file->file_name= 0; if (cur_file == file_stack)
{ /* We're back at the first file, check if all{havematching}
*/ if (cur_block != block_stack)
die("Missing end of block");
*p= 0;
DBUG_PRINT("info", ("end of file at line %d", cur_file->lineno));
DBUG_RETURN(1);
}
cur_file--;
start_lineno= cur_file->lineno; continue;
}
if (c == '\n')
{ /* Line counting is independent of state */
cur_file->lineno++;
/* Convert cr/lf to lf */ if (p != read_command_buf && *(p-1) == '\r')
p--;
}
bool drop_last_char= false; switch(state) { case R_NORMAL: case R_HINT: if (end_of_query(c))
{
*p= 0;
DBUG_PRINT("exit", ("Found delimiter '%s' at line %d",
delimiter, cur_file->lineno)); goto ret;
} elseif ((c == '{' &&
(!my_strnncoll_simple(charset_info, (const uchar*) "while", 5,
(uchar*) read_command_buf, MY_MIN(5, p - read_command_buf), 0) ||
!my_strnncoll_simple(charset_info, (const uchar*) "if", 2,
(uchar*) read_command_buf, MY_MIN(2, p - read_command_buf), 0))))
{ /* Only if and while commands can be terminated by { */
*p++= c;
*p= 0;
DBUG_PRINT("exit", ("Found '{' indicating start of block at line %d",
cur_file->lineno)); goto ret;
} elseif (c == '\'' || c == '"' || c == '`')
{ if (! have_slash)
{
last_quote= c;
state= R_Q;
}
} elseif (c == '*' && last_char == '/')
{
state= R_CSTYLE_COMMENT; break;
} elseif (c == '/' && last_char == '*') // Closing sequence `*/`
{
state= R_NORMAL; // The hint is finished, and we don't want to interpret the current slash // as an opener for a next hint or a C-style comment like it can happen // for a statement like `SELECT /*+ BNL(t1) */* FROM t1` where there is //no space between `*/` and `*`. So discard the current slash
drop_last_char= true;
}
have_slash= is_escape_char(c, last_quote); break;
case R_CSTYLE_COMMENT: if (c == '!' || c == '+')
{ // Got hint introducer '/*!' or '/*+'
state= R_HINT;
} elseif (c == '/' && last_char == '*')
{
state= R_NORMAL;
drop_last_char= true; // See comment for `drop_last_char` above
} break;
case R_COMMENT: if (c == '\n')
{ /* Comments are terminated by newline */
*p= 0;
DBUG_PRINT("exit", ("Found newline in comment at line: %d",
cur_file->lineno)); goto ret;
} break;
case R_LINE_START: if (c == '#' || c == '-')
{ /* A # or - in the first position of the line - this is a comment */
state = R_COMMENT;
} elseif (my_isspace(charset_info, c))
{ if (c == '\n')
{ if (last_char == '\n')
{ /* Two new lines in a row, return empty line */
DBUG_PRINT("info", ("Found two new lines in a row"));
*p++= c;
*p= 0; goto ret;
}
/* Query hasn't started yet */
start_lineno= cur_file->lineno;
DBUG_PRINT("info", ("Query hasn't started yet, start_lineno: %d",
start_lineno));
}
/* Skip all space at beginning of line */
skip_char= 1;
} elseif (end_of_query(c))
{
*p= 0;
DBUG_PRINT("exit", ("Found delimiter '%s' at line: %d",
delimiter, cur_file->lineno)); goto ret;
} elseif (c == '}')
{ /* A "}" need to be by itself in the beginning of a line to terminate */
*p++= c;
*p= 0;
DBUG_PRINT("exit", ("Found '}' in beginning of a line at line: %d",
cur_file->lineno)); goto ret;
} elseif (c == '\'' || c == '"' || c == '`')
{
last_quote= c;
state= R_Q;
} else
state= R_NORMAL; break;
case R_Q: if (c == last_quote)
state= R_NORMAL; elseif (is_escape_char(c, last_quote))
state= R_SLASH_IN_Q; break;
if (!skip_char)
{
*p++= c; if (charset_info->use_mb())
{ constchar *mb_start= p - 1; /* Could be a multibyte character */ /* See a similar code in "sql_load.cc" */ for ( ; p < buf_end; )
{ int charlen= charset_info->charlen(mb_start, p); if (charlen > 0) break; /* Full character */ if (MY_CS_IS_TOOSMALL(charlen))
{ /* We give up if multibyte character is started but not */ /* completed before we pass buf_end */
c= my_getc(cur_file->file); if (feof(cur_file->file)) goto found_eof;
*p++ = c; continue;
}
DBUG_ASSERT(charlen == MY_CS_ILSEQ); /* It was not a multiline char, push back the characters */ /* We leave first 'c', i.e. pretend it was a normal char */ while (p - 1 > mb_start)
my_ungetc(*--p); break;
}
}
}
}
ret:
read_command_buf_end= p;
DBUG_RETURN(0);
}
/* Skip past all spacing chars and comments */ while (*p && (my_isspace(charset_info, *p) || *p == '#' || *p == '\n'))
{ /* Skip past comments started with # and ended with newline */ if (*p && *p == '#')
{
p++; while (*p && *p != '\n')
p++;
}
/* Check this line */ if (*p && *p == '\n')
check_eol_junk_line(p);
/* Skip spaces between command and first argument */ while (p < end && my_isspace(charset_info, *p))
p++;
command->first_argument= p;
parser.read_lines++;
DBUG_RETURN(0);
}
staticstruct my_option my_long_options[] =
{
{"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"basedir", 'b', "Basedir for tests.", &opt_basedir,
&opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"character-sets-dir", 0, "Directory for character set files.", &opt_charsets_dir,
&opt_charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"compress", 'C', "Use the compressed server/client protocol.",
&opt_compress, &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"continue-on-error", 0, "Continue test even if we got an error. " "This is mostly useful when testing a storage engine to see what from a test file it can execute, " "or to find all syntax errors in a newly created big test file",
&opt_continue_on_error, &opt_continue_on_error, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"cursor-protocol", 0, "Use cursors for prepared statements.",
&cursor_protocol, &cursor_protocol, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"database", 'D', "Database to use.", &opt_db, &opt_db, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #ifdef DBUG_OFF
{"debug", '#', "This is a non-debug version. Catch this and exit", 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, #else
{"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, #endif
{"debug-check", 0, "Check memory and open file usage at exit.",
&debug_check_flag, &debug_check_flag, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"debug-info", 0, "Print some debug info at exit.",
&debug_info_flag, &debug_info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"host", 'h', "Connect to host.", &opt_host, &opt_host, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"prologue", 0, "Include SQL before each test case.", &opt_prologue,
&opt_prologue, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"logdir", OPT_LOG_DIR, "Directory for log files", &opt_logdir,
&opt_logdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"mark-progress", 0, "Write line number and elapsed time to <testname>.progress.",
&opt_mark_progress, &opt_mark_progress, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"max-connect-retries", 0, "Maximum number of attempts to connect to server.",
&opt_max_connect_retries, &opt_max_connect_retries, 0,
GET_INT, REQUIRED_ARG, 500, 1, 10000, 0, 0, 0},
{"max-connections", 0, "Max number of open connections to server",
&opt_max_connections, &opt_max_connections, 0,
GET_INT, REQUIRED_ARG, DEFAULT_MAX_CONN, 8, 5120, 0, 0, 0},
{"password", 'p', "Password to use when connecting to server.", 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
{"protocol", OPT_MYSQL_PROTOCOL, "The protocol of connection (tcp,socket,pipe).", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"port", 'P', "Port number to use for connection or 0 for default to, in " "order of preference, my.cnf, $MYSQL_TCP_PORT, " #if MYSQL_PORT_DEFAULT == 0 "/etc/services, " #endif "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
&opt_port, &opt_port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"ps-protocol", 0, "Use prepared-statement protocol for communication.",
&ps_protocol, &ps_protocol, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"non-blocking-api", 0, "Use the non-blocking client API for communication.",
&non_blocking_api_enabled, &non_blocking_api_enabled, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"quiet", 's', "Suppress all normal output.", &silent,
&silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"record", 'r', "Record output of test_file into result file.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"result-file", 'R', "Read/store result from/in this file.",
&result_file_name, &result_file_name, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"result-format-version", OPT_RESULT_FORMAT_VERSION, "Version of the result file format to use",
&opt_result_format_version,
&opt_result_format_version, 0,
GET_INT, REQUIRED_ARG, 1, 1, 2, 0, 0, 0},
{"server-arg", 'A', "Send option value to embedded server as a parameter.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"server-file", 'F', "Read embedded server arguments from file.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"silent", 's', "Suppress all normal output. Synonym for --quiet.",
&silent, &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"sleep", 'T', "Always sleep this many seconds on sleep commands.",
&opt_sleep, &opt_sleep, 0, GET_INT, REQUIRED_ARG, -1, -1, 0, 0, 0, 0},
{"socket", 'S', "The socket file to use for connection.",
&unix_sock, &unix_sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"sp-protocol", 0, "Use stored procedures for select.",
&sp_protocol, &sp_protocol, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #include"sslopt-longopts.h"
{"tail-lines", 0, "Number of lines of the result to include in a failure report.",
&opt_tail_lines, &opt_tail_lines, 0,
GET_INT, REQUIRED_ARG, 0, 0, 10000, 0, 0, 0},
{"test-file", 'x', "Read test from/in this file (default stdin).", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"timer-file", 'm', "File where the timing in microseconds is stored.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"tmpdir", 't', "Temporary directory where sockets are put.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"user", 'u', "User for login.", &opt_user, &opt_user, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"verbose", 'v', "Write more.", &verbose, &verbose, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"view-protocol", 0, "Use views for select.",
&view_protocol, &view_protocol, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"connect_timeout", 0, "Number of seconds before connection timeout.",
&opt_connect_timeout, &opt_connect_timeout, 0, GET_UINT, REQUIRED_ARG, 120, 0, 3600 * 12, 0, 0, 0},
{"wait_for_pos_timeout", 0, "Number of seconds to wait for master_pos_wait",
&opt_wait_for_pos_timeout, &opt_wait_for_pos_timeout, 0, GET_UINT,
REQUIRED_ARG, default_wait_for_pos_timeout, 0, 3600 * 12, 0, 0, 0},
{"plugin_dir", 0, "Directory for client-side plugins.",
&opt_plugin_dir, &opt_plugin_dir, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"overlay-dir", 0, "Overlay directory.", &opt_overlay_dir,
&opt_overlay_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"suite-dir", 0, "Suite directory.", &opt_suite_dir,
&opt_suite_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
void usage()
{
print_version();
puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
printf("Runs a test against the MariaDB server and compares output with a results file.\n\n");
printf("Usage: %s [OPTIONS] [database] < test_file\n", my_progname);
print_defaults("my",load_default_groups);
puts("");
my_print_help(my_long_options);
my_print_variables(my_long_options);
}
if (!record)
{ /* Check that the result file exists */ if (result_file_name && access(result_file_name, F_OK) != 0)
die("The specified result file '%s' does not exist",
result_file_name);
}
void init_win_path_patterns()
{ /* List of string patterns to match in order to find paths */ constchar* paths[] = { "$MYSQL_TEST_DIR", "$MYSQL_TMP_DIR", "$MYSQLTEST_VARDIR", "$MASTER_MYSOCK", "$MYSQL_SHAREDIR", "$MYSQL_LIBDIR", "./test/" }; int num_paths= sizeof(paths)/sizeof(char*); int i; char* p;
if (display_result_sorted)
{
init_dynamic_string(&rs_unsorted, "", 1024, 1024);
rs= &rs_unsorted;
}
while ((row = mysql_fetch_row(res)))
{
uint i;
lengths = mysql_fetch_lengths(res); for (i = 0; i < num_fields; i++)
append_field(rs, i, &fields[i],
row[i], lengths[i], !row[i]); if (!display_result_vertically)
dynstr_append_mem(rs, "\n", 1);
}
if (display_result_sorted)
{
dynstr_append_sorted(ds, &rs_unsorted);
dynstr_free(&rs_unsorted);
}
}
/* Allocate data for the result of each field */ for (i= 0; i < num_fields; i++)
{
uint max_length= fields[i].max_length + 1;
my_bind[i].buffer_type= MYSQL_TYPE_STRING;
my_bind[i].buffer= my_malloc(PSI_NOT_INSTRUMENTED, max_length,
MYF(MY_WME|MY_FAE));
my_bind[i].buffer_length= max_length;
my_bind[i].is_null= &is_null[i];
my_bind[i].length= &length[i];
DBUG_PRINT("bind", ("col[%d]: buffer_type: %d, buffer_length: %lu",
i, my_bind[i].buffer_type, my_bind[i].buffer_length));
}
if (mysql_stmt_bind_result(stmt, my_bind))
die("mysql_stmt_bind_result failed: %d: %s",
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
while ((error=mysql_stmt_fetch(stmt)) == 0)
{ for (i= 0; i < num_fields; i++)
append_field(rs, i, &fields[i], (char*)my_bind[i].buffer,
*my_bind[i].length, *my_bind[i].is_null); if (!display_result_vertically)
dynstr_append_mem(rs, "\n", 1);
}
if (error != MYSQL_NO_DATA)
die("mysql_fetch didn't end with MYSQL_NO_DATA from statement: " "error: %d", error); if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
die("mysql_fetch didn't end with MYSQL_NO_DATA from statement: %d %s",
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
for (i= 0; i < num_fields; i++)
{ /* Free data for output */
my_free(my_bind[i].buffer);
} /* Free array with bind structs, lengths and NULL flags */
my_free(my_bind);
my_free(length);
my_free(is_null);
if (display_result_sorted)
{
dynstr_append_sorted(ds, &rs_unsorted);
dynstr_free(&rs_unsorted);
}
}
do
{ /* Whenonfirstresultset,callmysql_read_query_resulttoretrieve answertothequerysentearlier
*/ if ((counter==0) && do_read_query_result(cn))
{ /* we've failed to collect the result set */
cn->pending= TRUE;
handle_error(command, mysql_errno(mysql), mysql_error(mysql),
mysql_sqlstate(mysql), ds); goto end;
if (res)
{
mysql_free_result(res);
res= 0;
}
counter++;
} while (!(err= mysql_next_result(mysql))); if (err > 0)
{ /* We got an error from mysql_next_result, maybe expected */
handle_error(command, mysql_errno(mysql), mysql_error(mysql),
mysql_sqlstate(mysql), ds); goto end;
}
DBUG_ASSERT(err == -1); /* Successful and there are no more results */
/* If we come here the query is both executed and read successfully */
handle_no_error(command);
display_optimizer_trace(cn, ds);
revert_properties();
/* Abort the run of this test, pass the failed query as reason */
abort_not_supported_test("Query '%s' failed, required functionality " \ "not supported", command->query);
}
if (i >= 0)
{ if (!disable_result_log)
{ if (command->expected_errors.count == 1)
{ /* Only log error if there is one possible error */
dynstr_append_mem(ds, "ERROR ", 6);
replace_dynstr_append(ds, err_sqlstate);
dynstr_append_mem(ds, ": ", 2);
replace_dynstr_append(ds, err_error);
dynstr_append_mem(ds,"\n",1);
} /* Don't log error if we may not get an error */ elseif (command->expected_errors.err[0].type == ERR_SQLSTATE ||
(command->expected_errors.err[0].type == ERR_ERRNO &&
command->expected_errors.err[0].code.errnum != 0))
dynstr_append_mem(ds, STRING_WITH_LEN("Got one of the listed " "errors\n"));
} /* OK */
revert_properties();
DBUG_VOID_RETURN;
}
DBUG_PRINT("info",("i: %d expected_errors: %d", i,
command->expected_errors.count));
if (command->expected_errors.err[0].type == ERR_ERRNO &&
command->expected_errors.err[0].code.errnum != 0)
{ /* Error code we wanted was != 0, i.e. not an expected success */
report_or_die("query '%s' succeeded - should have failed with error " "%s (%d)...",
command->query,
get_errname_from_code(command->expected_errors.err[0].code.errnum),
command->expected_errors.err[0].code.errnum);
} elseif (command->expected_errors.err[0].type == ERR_SQLSTATE &&
strcmp(command->expected_errors.err[0].code.sqlstate,"00000") != 0)
{ /* SQLSTATE we wanted was != "00000", i.e. not an expected success */
report_or_die("query '%s' succeeded - should have failed with " "sqlstate %s...",
command->query,
command->expected_errors.err[0].code.sqlstate);
}
DBUG_VOID_RETURN;
}
if (!disable_result_log)
{ /* Notallstatementscreatesaresultset.Ifthereisonewecan nowcreateanothernormalresultsetthatcontainsthemeta data.Thissetcanbehandledalmostlikeanyothernonprepared statementresultset.
*/ if ((res= mysql_stmt_result_metadata(stmt)) != NULL)
{ /* Take the column count from meta info */
MYSQL_FIELD *fields= mysql_fetch_fields(res);
uint num_fields= mysql_num_fields(res);
if (display_metadata)
append_metadata(ds, fields, num_fields);
if (!display_result_vertically)
append_table_headings(ds, fields, num_fields);
append_stmt_result(ds, stmt, fields, num_fields);
mysql_free_result(res); /* Free normal result set with meta data */
int err; do
{ /* Whenrunningincursor_protocolgetthewarningsfromexecutehere andkeeptheminaseparatestringforlater.Cursor_protocolisused onlyforqueriesmatchingthefilter"cursor_re".
*/ if (query_match_cursor_re && !disable_warnings)
append_warnings(&ds_execute_warnings, mysql);
if (read_stmt_results(stmt, ds, command)) goto end;
if (!disable_result_log)
{ /* Theresultsofthefirstandsecondexecutionarecompared onlyifresultloggingisenabled
*/ if (compare_2nd_execution && query_match_ps2_re)
{ if (ds->length != ds_res_1st_execution.length ||
!(memcmp(ds_res_1st_execution.str, ds->str, ds->length) == 0))
{
die("The result of the 1st execution does not match with \n" "the result of the 2nd execution of ps-protocol:\n 1st:\n" "%s\n 2nd:\n %s", ds_res_1st_execution.str, ds->str);
}
}
/* Normally,ifthereisaresultset,wedonotshowwarningsfromthe preparephase.Thisisbecausesomewarningsaregeneratedbothduring prepareandexecute;thiswouldgeneratedifferentwarningoutput betweennormalandps-protocoltestruns. The--enable_prepare_warningscommandcanbeusedtochangethisso thatwarningsfromboththeprepareandexecutephaseareshown.
*/ if ((res= mysql_stmt_result_metadata(stmt)))
{ if (!disable_warnings &&
!prepare_warnings_enabled)
dynstr_set(&ds_prepare_warnings, NULL);
mysql_free_result(res);
}
/* Fetchinfobeforefetchingwarnings,sinceitwillbereset otherwise.
*/ if (!disable_info)
append_info(ds, mysql_stmt_affected_rows(stmt), mysql_info(mysql));
if (display_session_track_info)
append_session_track_info(ds, mysql);
if (!disable_warnings && !mysql_more_results(stmt->mysql))
{ /* Get the warnings from execute */
/* Append warnings to ds - if there are any */ if (append_warnings(&ds_execute_warnings, mysql) ||
ds_execute_warnings.length ||
ds_prepare_warnings.length ||
ds_warnings->length)
{
dynstr_append_mem(ds, "Warnings:\n", 10); if (ds_warnings->length)
dynstr_append_mem(ds, ds_warnings->str,
ds_warnings->length); if (ds_prepare_warnings.length)
dynstr_append_mem(ds, ds_prepare_warnings.str,
ds_prepare_warnings.length); if (ds_execute_warnings.length)
dynstr_append_mem(ds, ds_execute_warnings.str,
ds_execute_warnings.length);
}
}
}
} while ( !(err= mysql_stmt_next_result(stmt)));
if (err > 0) /* We got an error from mysql_next_result, maybe expected */
handle_error(command, mysql_errno(mysql), mysql_error(mysql),
mysql_sqlstate(mysql), ds); else
handle_no_error(command);
end:
if (ds_res_1st_execution_init)
{
dynstr_free(&ds_res_1st_execution);
ds_res_1st_execution_init= FALSE;
}
if (!disable_warnings)
{
dynstr_free(&ds_prepare_warnings);
dynstr_free(&ds_execute_warnings);
}
display_optimizer_trace(cn, ds); if (cursor_protocol_enabled)
{
ulong type= CURSOR_TYPE_NO_CURSOR; if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type))
die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s",
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
}
revert_properties();
/* Close the statement if reconnect, need new prepare */
{ #ifndef EMBEDDED_LIBRARY
my_bool reconnect;
mysql_get_option(mysql, MYSQL_OPT_RECONNECT, &reconnect); if (reconnect) #else if (mysql->reconnect) #endif
{
mysql_stmt_close(stmt);
cn->stmt= NULL;
}
}
/* If we got here the statement was both executed and read successfully */
handle_no_error(command); if (!disable_result_log)
{ /* Notallstatementscreatesaresultset.Ifthereisonewecan nowcreateanothernormalresultsetthatcontainsthemeta data.Thissetcanbehandledalmostlikeanyothernonprepared statementresultset.
*/ if ((res= mysql_stmt_result_metadata(stmt)) != NULL)
{ /* Take the column count from meta info */
MYSQL_FIELD *fields= mysql_fetch_fields(res);
uint num_fields= mysql_num_fields(res);
if (display_metadata)
append_metadata(ds, fields, num_fields);
if (!display_result_vertically)
append_table_headings(ds, fields, num_fields);
append_stmt_result(ds, stmt, fields, num_fields);
mysql_free_result(res); /* Free normal result set with meta data */
/* Fetchinfobeforefetchingwarnings,sinceitwillbereset otherwise.
*/ if (!disable_info)
append_info(ds, mysql_stmt_affected_rows(stmt), mysql_info(mysql));
if (display_session_track_info)
append_session_track_info(ds, mysql);
if (!disable_warnings)
{ /* Get the warnings from execute */
/* Append warnings to ds - if there are any */ if (append_warnings(&ds_execute_warnings, mysql) ||
ds_execute_warnings.length ||
ds_warnings->length)
{
dynstr_append_mem(ds, "Warnings:\n", 10); if (ds_warnings->length)
dynstr_append_mem(ds, ds_warnings->str,
ds_warnings->length); if (ds_execute_warnings.length)
dynstr_append_mem(ds, ds_execute_warnings.str,
ds_execute_warnings.length);
}
}
}
end: if (!disable_warnings)
{
dynstr_free(&ds_execute_warnings);
}
int util_query(MYSQL* org_mysql, constchar* query){
MYSQL* mysql;
DBUG_ENTER("util_query");
if (service_connection_enabled)
{ if(!(mysql= cur_con->util_mysql))
{
DBUG_PRINT("info", ("Creating util_mysql")); if (!(mysql= mysql_init(mysql)))
die("Failed in mysql_init()");
if (opt_connect_timeout)
mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT,
(void *) &opt_connect_timeout);
/* enable local infile, in non-binary builds often disabled by default */
mysql_options(mysql, MYSQL_OPT_LOCAL_INFILE, 0);
mysql_options(mysql, MYSQL_OPT_NONBLOCK, 0);
mysql_options(mysql,MYSQL_OPT_PROTOCOL,(char*)&(org_mysql->options.protocol));
SET_SSL_OPTS(mysql);
safe_connect(mysql, "util", org_mysql->host, org_mysql->user,
org_mysql->passwd, org_mysql->db, org_mysql->port,
org_mysql->unix_socket);
if (sp_created)
{ if (util_query(mysql, "DROP PROCEDURE mysqltest_tmp_sp "))
report_or_die("Failed to drop sp: %d: %s", mysql_errno(mysql),
mysql_error(mysql));
}
if (view_created)
{ if (util_query(mysql, "DROP VIEW mysqltest_tmp_v "))
report_or_die("Failed to drop view: %d: %s",
mysql_errno(mysql), mysql_error(mysql));
}
if (command->require_file)
{ /* A result file was specified for _this_ query andtheoutputshouldbecheckedagainstanalready existingfilewhichhasbeenspecifiedusing--requireor--result
*/
check_require(rs_output, command->require_file);
}
if (rs_output == &rs_cmp_result)
dynstr_free(&rs_cmp_result);
DBUG_VOID_RETURN;
}
/* Lookforcasewhere"query"wasexplicitlyspecifiedto forcecommandbeingsenttoserver
*/ if (type == Q_QUERY)
{ /* Skip the "query" part */
command->query_len-= (int)(command->first_argument - command->query);
command->query= command->first_argument;
}
} else
{ /* No mysqltest command matched */
if (command->type != Q_COMMENT_WITH_COMMAND)
{ /* A query that will sent to mysqld */
command->type= Q_QUERY;
} else
{ /* -- "comment" that didn't contain a mysqltest command */
report_or_die("Found line beginning with -- that didn't contain " \ "a valid mysqltest command, check your syntax or " \ "use # if you intended to write a comment");
command->type= Q_COMMENT;
}
}
/* Set expected error on command */
memcpy(&command->expected_errors, &saved_expected_errors, sizeof(saved_expected_errors));
DBUG_PRINT("info", ("There are %d expected errors",
command->expected_errors.count));
DBUG_VOID_RETURN;
}
fprintf(stderr, "Writing a core file...\n");
fflush(stderr);
my_write_core(sig); #ifndef _WIN32
sf_leaking_memory= 1; exit(1); // Shouldn't get here but just in case #endif
}
#ifdef _WIN32
LONG WINAPI exception_filter(EXCEPTION_POINTERS *exp)
{
__try
{
my_set_exception_pointers(exp);
signal_handler(exp->ExceptionRecord->ExceptionCode);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
fputs("Got exception in exception handler!\n", stderr);
}
/* Set output destination of messages to the standard error stream. */
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
/* Do not not display the a error message box. */
mode= SetErrorMode(0) | SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX;
SetErrorMode(mode);
/* Enable second execution of SELECT for ps-protocol
if ps-protocol is used */
ps2_protocol_enabled= ps_protocol;
ps_protocol_enabled= ps_protocol;
sp_protocol_enabled= sp_protocol;
view_protocol_enabled= view_protocol;
cursor_protocol_enabled= cursor_protocol;
st_connection *con= connections;
init_connection_thd(con); if (! (con->mysql= mysql_init(0)))
die("Failed in mysql_init()"); if (opt_connect_timeout)
mysql_options(con->mysql, MYSQL_OPT_CONNECT_TIMEOUT,
(void *) &opt_connect_timeout); if (opt_compress)
mysql_options(con->mysql,MYSQL_OPT_COMPRESS,NullS);
mysql_options(con->mysql, MYSQL_SET_CHARSET_NAME,
charset_info->cs_name.str); if (opt_charsets_dir)
mysql_options(con->mysql, MYSQL_SET_CHARSET_DIR,
opt_charsets_dir);
if (opt_protocol)
mysql_options(con->mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
if (opt_plugin_dir && *opt_plugin_dir)
mysql_options(con->mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
SET_SSL_OPTS(con->mysql);
if (!(con->name = my_strdup(PSI_NOT_INSTRUMENTED, "default", MYF(MY_WME))))
die("Out of memory");
mysql_options(con->mysql, MYSQL_OPT_NONBLOCK, 0);
verbose_msg("Start processing test commands from '%s' ...", cur_file->file_name); while (!abort_flag && !read_command(&command))
{
my_bool ok_to_do; int current_line_inc = 1, processed = 0; if (command->type == Q_UNKNOWN || command->type == Q_COMMENT_WITH_COMMAND)
get_command_type(command);
if (parsing_disabled &&
command->type != Q_ENABLE_PARSING &&
command->type != Q_DISABLE_PARSING)
{ /* Parsing is disabled, silently convert this line to a comment */
command->type= Q_COMMENT;
}
/* (Re-)set abort_on_error for this command */
command->abort_on_error= (command->expected_errors.count == 0 &&
abort_on_error);
if (ok_to_do)
{
command->last_argument= command->first_argument;
processed = 1; /* Need to remember this for handle_error() */
curr_command= command; switch (command->type) { case Q_CONNECT:
do_connect(command); break; case Q_CONNECTION: select_connection(command); break; case Q_DISCONNECT: case Q_DIRTY_CLOSE:
do_close_connection(command); break; case Q_ENABLE_PREPARE_WARNINGS: prepare_warnings_enabled=1; break; case Q_DISABLE_PREPARE_WARNINGS: prepare_warnings_enabled=0; break; case Q_ENABLE_QUERY_LOG:
set_property(command, P_QUERY, 0); break; case Q_DISABLE_QUERY_LOG:
set_property(command, P_QUERY, 1); break; case Q_ENABLE_ABORT_ON_ERROR:
set_property(command, P_ABORT, 1); break; case Q_DISABLE_ABORT_ON_ERROR:
set_property(command, P_ABORT, 0); break; case Q_ENABLE_RESULT_LOG:
set_property(command, P_RESULT, 0); break; case Q_DISABLE_RESULT_LOG:
set_property(command, P_RESULT, 1); break; case Q_ENABLE_CONNECT_LOG:
set_property(command, P_CONNECT, 0); break; case Q_DISABLE_CONNECT_LOG:
set_property(command, P_CONNECT, 1); break; case Q_ENABLE_WARNINGS:
set_property(command, P_WARN, 0); break; case Q_DISABLE_WARNINGS:
set_property(command, P_WARN, 1); break; case Q_ENABLE_INFO:
set_property(command, P_INFO, 0); break; case Q_DISABLE_INFO:
set_property(command, P_INFO, 1); break; case Q_ENABLE_SESSION_TRACK_INFO:
set_property(command, P_SESSION_TRACK, 1); break; case Q_DISABLE_SESSION_TRACK_INFO:
set_property(command, P_SESSION_TRACK, 0); break; case Q_ENABLE_METADATA:
set_property(command, P_META, 1); break; case Q_DISABLE_METADATA:
set_property(command, P_META, 0); break; case Q_ENABLE_COLUMN_NAMES:
disable_column_names= 0;
var_set_int("$ENABLED_COLUMN_NAMES", 0); break; case Q_DISABLE_COLUMN_NAMES:
disable_column_names= 1;
var_set_int("$ENABLED_COLUMN_NAMES", 1); break; case Q_SOURCE: do_source(command); break; case Q_SLEEP: do_sleep(command, 0); break; case Q_REAL_SLEEP: do_sleep(command, 1); break; case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(command); break; case Q_INC: do_modify_var(command, DO_INC); break; case Q_DEC: do_modify_var(command, DO_DEC); break; case Q_ECHO: do_echo(command); command_executed++; break; case Q_BREAK: do_break(command); break; case Q_SYSTEM: do_system(command); break; case Q_REMOVE_FILE: do_remove_file(command); break; case Q_REMOVE_FILES_WILDCARD: do_remove_files_wildcard(command); break; case Q_MKDIR: do_mkdir(command); break; case Q_RMDIR: do_rmdir(command); break; case Q_LIST_FILES: do_list_files(command); break; case Q_LIST_FILES_WRITE_FILE:
do_list_files_write_file_command(command, FALSE); break; case Q_LIST_FILES_APPEND_FILE:
do_list_files_write_file_command(command, TRUE); break; case Q_FILE_EXIST: do_file_exist(command); break; case Q_WRITE_FILE: do_write_file(command); break; case Q_WRITE_LINE: do_write_line(command); break; case Q_APPEND_FILE: do_append_file(command); break; case Q_DIFF_FILES: do_diff_files(command); break; case Q_SEND_QUIT: do_send_quit(command); break; case Q_CHANGE_USER: do_change_user(command); break; case Q_CAT_FILE: do_cat_file(command); command_executed++; break; case Q_COPY_FILE: do_copy_file(command); break; case Q_MOVE_FILE: do_move_file(command); break; case Q_CHMOD_FILE: do_chmod_file(command); break; case Q_PERL: do_perl(command); break; case Q_RESULT_FORMAT_VERSION: do_result_format_version(command); break; case Q_DELIMITER:
do_delimiter(command); break; case Q_DISPLAY_VERTICAL_RESULTS:
display_result_vertically= TRUE; break; case Q_DISPLAY_HORIZONTAL_RESULTS:
display_result_vertically= FALSE; break; case Q_SORTED_RESULT: /* Turnonsortingofresultset,willberesetafternext command
*/
display_result_sorted= TRUE; break; case Q_LOWERCASE: /* Turnonlowercasingofresult,willberesetafternext command
*/
display_result_lower= TRUE; break; case Q_LET: do_let(command); break; case Q_EVAL_RESULT:
die("'eval_result' command is deprecated"); break; // never called but keep compiler calm case Q_EVAL: case Q_EVALP: case Q_QUERY_VERTICAL: case Q_QUERY_HORIZONTAL: if (command->query == command->query_buf)
{ /* Skip the first part of command, i.e query_xxx */
command->query_len-= (int)(command->first_argument - command->query);
command->query= command->first_argument;
command->first_word_len= 0;
} /* fall through */ case Q_QUERY: case Q_REAP: case Q_PS_PREPARE: case Q_PS_BIND: case Q_PS_EXECUTE: case Q_PS_CLOSE:
{
my_bool old_display_result_vertically= display_result_vertically; /* Default is full query, both reap and send */ int flags= QUERY_REAP_FLAG | QUERY_SEND_FLAG;
if (q_send_flag)
{ /* Last command was an empty 'send' */
flags= QUERY_SEND_FLAG;
q_send_flag= 0;
} elseif (command->type == Q_REAP)
{
flags= QUERY_REAP_FLAG;
}
if (command->type == Q_EVALP)
flags |= QUERY_PRINT_ORIGINAL_FLAG;
/* Check for special property for this query */
display_result_vertically|= (command->type == Q_QUERY_VERTICAL);
if (save_file[0])
{ if (!(command->require_file= strdup_root(&require_file_root,
save_file)))
die("out of memory for require_file");
save_file[0]= 0;
}
run_query(cur_con, command, flags);
command_executed++;
command->last_argument= command->end;
break;
} case Q_SEND: case Q_SEND_EVAL: if (!*command->first_argument)
{ /* Thisisasendwithoutarguments,itindicatesthat_next_query shouldbesendonly
*/
q_send_flag= 1; break;
}
/* Remove "send" if this is first iteration */ if (command->query == command->query_buf)
{
command->query_len-= (int)(command->first_argument - command->query);
command->query= command->first_argument;
}
/* run_query()canexecuteaquerypartially,dependingontheflags. QUERY_SEND_FLAGflagwithoutQUERY_REAP_FLAGtellsittojustsend thequeryandreadtheresultsometimelaterwhenreapinstruction isgivenonthisconnection.
*/
run_query(cur_con, command, QUERY_SEND_FLAG);
command_executed++;
command->last_argument= command->end; break; case Q_REQUIRE:
do_get_file_name(command, save_file, sizeof(save_file)); break; case Q_ERROR:
do_get_errcodes(command); break; case Q_REPLACE:
do_get_replace(command); break; case Q_REPLACE_REGEX:
do_get_replace_regex(command); break; case Q_REPLACE_COLUMN:
do_get_replace_column(command); break; case Q_SAVE_MASTER_POS: do_save_master_pos(); break; case Q_SYNC_WITH_MASTER: do_sync_with_master(command); break; case Q_SYNC_SLAVE_WITH_MASTER:
{
do_save_master_pos(); if (*command->first_argument)
select_connection(command); else
select_connection_name("slave");
do_sync_with_master2(command, 0, ""); break;
} case Q_COMMENT:
{
command->last_argument= command->end;
/* Don't output comments in v1 */ if (opt_result_format_version == 1) break;
/* Don't output comments if query logging is off */ if (disable_query_log) break;
/* Write comment's with two starting #'s to result file */ constchar* p= command->query; if (p && *p == '#' && *(p+1) == '#')
{
dynstr_append_mem(&ds_res, command->query, command->query_len);
dynstr_append_mem(&ds_res, STRING_WITH_LEN("\n"));
} break;
} case Q_EMPTY_LINE: /* Don't output newline in v1 */ if (opt_result_format_version == 1) break;
/* Don't output newline if query logging is off */ if (disable_query_log) break;
dynstr_append_mem(&ds_res, STRING_WITH_LEN("\n")); break; case Q_PING:
handle_command_error(command, mysql_ping(cur_con->mysql), -1); break; case Q_RESET_CONNECTION:
do_reset_connection(); break; case Q_OPTIMIZER_TRACE:
enable_optimizer_trace(cur_con); break; case Q_DISABLE_REPLAY:
do_disable_replay(command); break; case Q_SEND_SHUTDOWN:
handle_command_error(command,
mysql_shutdown(cur_con->mysql,
SHUTDOWN_DEFAULT), -1); break; case Q_SHUTDOWN_SERVER:
do_shutdown_server(command); break; case Q_EXEC:
do_exec(command);
command_executed++; break; case Q_START_TIMER: /* Overwrite possible earlier start of timer */
timer_start= timer_now(); break; case Q_END_TIMER: /* End timer before ending mysqltest */
timer_output(); break; case Q_CHARACTER_SET:
do_set_charset(command); break; case Q_DISABLE_PS_PROTOCOL:
set_property(command, P_PS, 0); /* Close any open statements */
close_statements(); break; case Q_ENABLE_PS_PROTOCOL:
set_property(command, P_PS, ps_protocol); break; case Q_DISABLE_PS2_PROTOCOL:
set_property(command, P_PS2, 0); break; case Q_ENABLE_PS2_PROTOCOL:
set_property(command, P_PS2, ps_protocol); break; case Q_DISABLE_VIEW_PROTOCOL:
set_property(command, P_VIEW, 0); /* Close only util connections */
close_util_connections(); break; case Q_ENABLE_VIEW_PROTOCOL:
set_property(command, P_VIEW, view_protocol); break; case Q_DISABLE_CURSOR_PROTOCOL:
set_property(command, P_CURSOR, 0); if (cursor_protocol)
set_property(command, P_PS, 0); /* Close any open statements */
close_statements(); break; case Q_ENABLE_CURSOR_PROTOCOL:
set_property(command, P_CURSOR, cursor_protocol);
set_property(command, P_PS, ps_protocol); break; case Q_DISABLE_SERVICE_CONNECTION:
set_property(command, P_CONN, 0); /* Close only util connections */
close_util_connections(); break; case Q_ENABLE_SERVICE_CONNECTION:
set_property(command, P_CONN, view_protocol); break; case Q_DISABLE_NON_BLOCKING_API:
non_blocking_api_enabled= 0; break; case Q_ENABLE_NON_BLOCKING_API:
non_blocking_api_enabled= 1; break; case Q_DISABLE_RECONNECT:
mysql_options(cur_con->mysql, MYSQL_OPT_RECONNECT, &my_false); break; case Q_ENABLE_RECONNECT:
mysql_options(cur_con->mysql, MYSQL_OPT_RECONNECT, &my_true); /* Close any open statements - no reconnect, need new prepare */
close_statements(); break; case Q_DISABLE_PARSING: if (parsing_disabled == 0)
parsing_disabled= 1; else
report_or_die("Parsing is already disabled"); break; case Q_ENABLE_PARSING: /* Ensurewedon'tgetparsing_disabled<0asthiswouldaccidentally disablecodewedon'twanttohavedisabled
*/ if (parsing_disabled == 1)
parsing_disabled= 0; else
report_or_die("Parsing is already enabled"); break; case Q_DIE:
{ char message[160]; constchar *msg;
DYNAMIC_STRING ds_echo;
/* Also reset "sorted_result" and "lowercase"*/
display_result_sorted= FALSE;
display_result_lower= FALSE;
}
last_command_executed= command_executed;
parser.current_line += current_line_inc; if ( opt_mark_progress )
mark_progress(command, parser.current_line);
/* Write result from command to log file immediately */
log_file.write(&ds_res);
log_file.flush();
dynstr_set(&ds_res, 0);
}
log_file.close();
start_lineno= 0;
verbose_msg("... Done processing test commands.");
if (parsing_disabled)
die("Test ended with parsing disabled");
/* Thewholetesthasbeenexecutedsuccessfully. Timetocompareresultorsaveittorecordfile. Theentireoutputfromtestisinthelogfile
*/ if (log_file.bytes_written())
{ if (result_file_name)
{ /* A result file has been specified */
if (record)
{ /* Recording */
/* save a copy of the log to result file */ if (my_copy(log_file.file_name(), result_file_name, MYF(0)) != 0)
die("Failed to copy '%s' to '%s', errno: %d",
log_file.file_name(), result_file_name, errno);
} else
{ /* Check that the output from test is equal to result file */
error= check_result();
}
}
} else
{ /* Empty output is an error *unless* we also have an empty result file */ if (! result_file_name || record ||
compare_files (log_file.file_name(), result_file_name))
{
fprintf(stderr, "mysqltest: The test didn't produce any output\n");
error= 1;
} else
{
empty_result= TRUE; /* Meaning empty was expected */
}
}
if (!command_executed && result_file_name && !empty_result)
{
fprintf(stderr, "mysqltest: No queries executed but non-empty result file found!\n");
error= 1;
}
if (!error)
verbose_msg("Test has succeeded!");
timer_output();
cleanup_and_exit(error, 0); return0; /* Keep compiler happy too */
}
void timer_output(void)
{ if (timer_file)
{ char buf[32], *end;
ulonglong timer= timer_now() - timer_start;
end= longlong10_to_str(timer, buf, 10);
str_to_file(timer_file,buf, (int) (end-buf)); /* Timer has been written to the file, don't use it anymore */
timer_file= 0;
}
}
free_replace_column(); if (!*from)
die("Missing argument in %s", command->query);
/* Allocate a buffer for results */
start= buff= (char*)my_malloc(PSI_NOT_INSTRUMENTED, strlen(from)+1,
MYF(MY_WME|MY_FAE)); while (*from)
{ char *to;
uint column_number;
to= get_string(&buff, &from, command); if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS)
die("Wrong column number to replace_column in '%s'", command->query); if (!*from)
die("Wrong number of arguments to replace_column in '%s'",
command->query);
to= get_string(&buff, &from, command);
my_free(replace_column[column_number-1]);
replace_column[column_number-1]= my_strdup(PSI_NOT_INSTRUMENTED, to,
MYF(MY_WME|MY_FAE));
set_if_bigger(max_replace_column, column_number);
}
my_free(start);
command->last_argument= command->end;
DBUG_VOID_RETURN;
}
void free_replace_column()
{
uint i; for (i=0 ; i < max_replace_column ; i++)
{ if (replace_column[i])
{
my_free(replace_column[i]);
replace_column[i]= 0;
}
}
max_replace_column= 0;
}
typedefstruct st_pointer_array { /* when using array-strings */
TYPELIB typelib; /* Pointer to strings */
uchar *str; /* Strings is here */
uint8 *flag; /* Flag about each var. */
uint array_allocs,max_count,length,max_length;
} POINTER_ARRAY;
start= from= str;
rep_pos=rep+1; for (;;)
{ /* Loop through states */
DBUG_PRINT("info", ("Looping through states")); while (!rep_pos->found)
rep_pos= rep_pos->next[(uchar) *from++];
/* Does this state contain a string to be replaced */ if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string)
{ /* No match found */
dynstr_append_mem(ds, start, from - start - 1);
DBUG_PRINT("exit", ("Found no more string to replace, appended: %s", start));
DBUG_VOID_RETURN;
}
/* Found a string that needs to be replaced */
DBUG_PRINT("info", ("found: %d, to_offset: %u, from_offset: %d, string: %s",
rep_str->found, rep_str->to_offset,
rep_str->from_offset, rep_str->replace_string));
/* Append part of original string before replace string */
dynstr_append_mem(ds, start, (from - rep_str->to_offset) - start);
if (!*(from-=rep_str->from_offset) && rep_pos->found != 2)
{ /* End of from string */
DBUG_PRINT("exit", ("Found end of from string"));
DBUG_VOID_RETURN;
}
start= from;
rep_pos=rep;
}
}
/* Regexreplacefunctions
*/
/* Stores regex substitutions */
struct st_regex
{ char* pattern; /* Pattern to be replaced */ char* replace; /* String or expression to replace the pattern with */ int icase; /* true if the match is case insensitive */
};
int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace, char *string, int icase);
/* my_malloc() will die on fail with MY_FAE */
res=(struct st_replace_regex*)my_malloc(PSI_NOT_INSTRUMENTED, sizeof(*res)+8192 ,MYF(MY_FAE+MY_WME));
my_init_dynamic_array(PSI_NOT_INSTRUMENTED, &res->regex_arr, sizeof(struct st_regex), 128, 128, MYF(0));
/* for each regexp substitution statement */ while (p < expr_end)
{
bzero(®,sizeof(reg)); /* find the start of the statement */ while (my_isspace(charset_info, *p) && p < expr_end)
p++;
if (p >= expr_end)
{ if (res->regex_arr.elements) break; else goto err;
}
start_re= 0;
reg.pattern= *buf_p;
/* Allow variable for the *entire* list of replacements */ if (*p == '$')
{ constchar *v_end= 0;
VAR *val= var_get(p, &v_end, 0, 1);
/* For each substitution, do the replace */ for (i= 0; i < r->regex_arr.elements; i++)
{ struct st_regex re; char* save_out_buf= out_buf;
get_dynamic(&r->regex_arr, &re, i);
if (!reg_replace(&out_buf, buf_len_p, re.pattern, re.replace,
in_buf, re.icase))
{ /* if the buffer has been reallocated, make adjustments */ if (save_out_buf != out_buf)
{ if (save_out_buf == r->even_buf)
r->even_buf= out_buf; else
r->odd_buf= out_buf;
}
r->buf= out_buf; if (in_buf == val)
in_buf= r->odd_buf;
/* for each pattern match instance perform a replacement */ while (!err_code)
{ /* find the match */
err_code= regexec(&r,str_p, r.re_nsub+1, subs,
(str_p == string) ? 0 : REG_NOTBOL);
/* if regular expression error (eg. bad syntax, or out of memory) */ if (err_code && err_code != REG_NOMATCH)
{
check_regerr(&r,err_code);
regfree(&r); return1;
}
/* if match found */ if (!err_code)
{ char* expr_p= replace; int c;
typedefstruct st_rep_set {
uint *bits; /* Pointer to used sets */ short next[LAST_CHAR_CODE]; /* Pointer to next sets */
uint found_len; /* Best match to date */ int found_offset;
uint table_offset;
uint size_of_bits; /* For convenience */
} REP_SET;
typedefstruct st_rep_sets {
uint count; /* Number of sets */
uint extra; /* Extra sets in buffer */
uint invisible; /* Sets not chown */
uint size_of_bits;
REP_SET *set,*set_buffer;
uint *bit_buffer;
} REP_SETS;
typedefstruct st_found_set {
uint table_offset; int found_offset;
} FOUND_SET;
/* Count number of states */ for (i=result_len=max_length=0 , states=2 ; i < count ; i++)
{
len=replace_len(from[i]); if (!len)
{
errno=EINVAL;
DBUG_RETURN(0);
}
states+=len+1;
result_len+=(uint) strlen(to[i])+1; if (len > max_length)
max_length=len;
}
bzero(is_word_end, sizeof(is_word_end)); for (i=0 ; word_end_chars[i] ; i++)
is_word_end[(uchar) word_end_chars[i]]=1;
if (init_sets(&sets,states))
DBUG_RETURN(0);
found_sets=0; if (!(found_set= (FOUND_SET*) my_malloc(PSI_NOT_INSTRUMENTED, sizeof(FOUND_SET)*max_length*count,
MYF(MY_WME|MY_FAE))))
{
free_sets(&sets);
DBUG_RETURN(0);
}
(void) make_new_set(&sets); /* Set starting set */
make_sets_invisible(&sets); /* Hide previous sets */
used_sets=-1;
word_states=make_new_set(&sets); /* Start of new word */
start_states=make_new_set(&sets); /* This is first state */ if (!(follow=(FOLLOWS*) my_malloc(PSI_NOT_INSTRUMENTED, (states+2)*sizeof(FOLLOWS),MYF(MY_WME|MY_FAE))))
{
free_sets(&sets);
my_free(found_set);
DBUG_RETURN(0);
}
for (set_nr=0,pos=0 ; set_nr < sets.count ; set_nr++)
{
set=sets.set+set_nr;
default_state= 0; /* Start from beginning */
/* If end of found-string not found or start-set with current set */
for (i= (uint) ~0; (i=get_next_bit(set,i)) ;)
{ if (!follow[i].chr)
{ if (! default_state)
default_state= find_found(found_set,set->table_offset,
set->found_offset+1);
}
}
copy_bits(sets.set+used_sets,set); /* Save set for changes */ if (!default_state)
or_bits(sets.set+used_sets,sets.set); /* Can restart from start */
/* Find all chars that follows current sets */
bzero(used_chars, sizeof(used_chars)); for (i= (uint) ~0; (i=get_next_bit(sets.set+used_sets,i)) ;)
{
used_chars[follow[i].chr]=1; if ((follow[i].chr == SPACE_CHAR && !follow[i+1].chr &&
follow[i].len > 1) || follow[i].chr == END_OF_LINE)
used_chars[0]=1;
}
/* Mark word_chars used if \b is in state */ if (used_chars[SPACE_CHAR]) for (pos= word_end_chars ; *pos ; pos++)
used_chars[(int) (uchar) *pos] = 1;
/* Handle other used characters */ for (chr= 0 ; chr < 256 ; chr++)
{ if (! used_chars[chr])
set->next[chr]= chr ? default_state : -1; else
{
new_set=make_new_set(&sets);
set=sets.set+set_nr; /* if realloc */
new_set->table_offset=set->table_offset;
new_set->found_len=set->found_len;
new_set->found_offset=set->found_offset+1;
found_end=0;
for (i= (uint) ~0 ; (i=get_next_bit(sets.set+used_sets,i)) ; )
{ if (!follow[i].chr || follow[i].chr == chr ||
(follow[i].chr == SPACE_CHAR &&
(is_word_end[chr] ||
(!chr && follow[i].len > 1 && ! follow[i+1].chr))) ||
(follow[i].chr == END_OF_LINE && ! chr))
{ if ((! chr || (follow[i].chr && !follow[i+1].chr)) &&
follow[i].len > found_end)
found_end=follow[i].len; if (chr && follow[i].chr)
internal_set_bit(new_set,i+1); /* To next set */ else
internal_set_bit(new_set,i);
}
} if (found_end)
{
new_set->found_len=0; /* Set for testing if first */
bits_set=0; for (i= (uint) ~0; (i=get_next_bit(new_set,i)) ;)
{ if ((follow[i].chr == SPACE_CHAR ||
follow[i].chr == END_OF_LINE) && ! chr)
bit_nr=i+1; else
bit_nr=i; if (follow[bit_nr-1].len < found_end ||
(new_set->found_len &&
(chr == 0 || !follow[bit_nr].chr)))
internal_clear_bit(new_set,i); else
{ if (chr == 0 || !follow[bit_nr].chr)
{ /* best match */
new_set->table_offset=follow[bit_nr].table_offset; if (chr || (follow[i].chr == SPACE_CHAR ||
follow[i].chr == END_OF_LINE))
new_set->found_offset=found_end; /* New match */
new_set->found_len=found_end;
}
bits_set++;
}
} if (bits_set == 1)
{
set->next[chr] = find_found(found_set,
new_set->table_offset,
new_set->found_offset);
free_last_set(&sets);
} else
set->next[chr] = find_set(&sets,new_set);
} else
set->next[chr] = find_set(&sets,new_set);
}
}
}
/* Alloc replace structure for the replace-state-machine */
if ((replace=(REPLACE*) my_malloc(PSI_NOT_INSTRUMENTED, sizeof(REPLACE)*(sets.count)+ sizeof(REPLACE_STRING)*(found_sets+1)+ sizeof(char *)*count+result_len,
MYF(MY_WME|MY_FAE|MY_ZEROFILL))))
{
rep_str=(REPLACE_STRING*) (replace+sets.count);
to_array= (char **) (rep_str+found_sets+1);
to_pos=(char *) (to_array+count); for (i=0 ; i < count ; i++)
{
to_array[i]=to_pos;
to_pos=strmov(to_pos,to[i])+1;
}
rep_str[0].found=1;
rep_str[0].replace_string=0; for (i=1 ; i <= found_sets ; i++)
{
pos=from[found_set[i-1].table_offset];
rep_str[i].found= !strncmp(pos, "\\^", 3) ? 2 : 1;
rep_str[i].replace_string=to_array[found_set[i-1].table_offset];
rep_str[i].to_offset=found_set[i-1].found_offset-start_at_word(pos);
rep_str[i].from_offset=found_set[i-1].found_offset-replace_len(pos)+
end_of_word(pos);
} for (i=0 ; i < sets.count ; i++)
{ for (j=0 ; j < 256 ; j++) if (sets.set[i].next[j] >= 0)
replace[i].next[j]=replace+sets.set[i].next[j]; else
replace[i].next[j]=(REPLACE*) (rep_str+(-sets.set[i].next[j]-1));
}
}
my_free(follow);
free_sets(&sets);
my_free(found_set);
DBUG_PRINT("exit",("Replace table has %d states",sets.count));
DBUG_RETURN(replace);
}
int init_sets(REP_SETS *sets,uint states)
{
bzero(sets, sizeof(*sets));
sets->size_of_bits=((states+7)/8); if (!(sets->set_buffer=(REP_SET*) my_malloc(PSI_NOT_INSTRUMENTED, sizeof(REP_SET)*SET_MALLOC_HUNC,
MYF(MY_WME|MY_FAE)))) return1; if (!(sets->bit_buffer=(uint*) my_malloc(PSI_NOT_INSTRUMENTED, sizeof(uint)*sets->size_of_bits*
SET_MALLOC_HUNC,MYF(MY_WME|MY_FAE))))
{
my_free(sets->set); return1;
} return0;
}
while (! bits && ++start < end)
bits=start[0]; if (!bits) return0;
pos=(uint) (start-set->bits)*WORD_BIT; while (! (bits & 1))
{
bits>>=1;
pos++;
} return pos;
}
/* find if there is a same set in sets. If there is, use it and freegivenset,elseputingivensetinsetsandreturnits
position */
int find_set(REP_SETS *sets,REP_SET *find)
{
uint i; for (i=0 ; i < sets->count-1 ; i++)
{ if (!cmp_bits(sets->set+i,find))
{
free_last_set(sets); return i;
}
} return i; /* return new position */
}
/* find if there is a found_set with same table_offset & found_offset Ifthereisreturnoffsettoit,elseaddnewoffsetandreturnpos. Posreturnedis-offset-2infound_set_structurebecauseitis savedinset->nextandset->next[]>=0pointstonextsetand set->next[]==-1isreservedforendwithoutreplaces.
*/
int find_found(FOUND_SET *found_set,uint table_offset, int found_offset)
{ int i; for (i=0 ; (uint) i < found_sets ; i++) if (found_set[i].table_offset == table_offset &&
found_set[i].found_offset == found_offset) return -i-2;
found_set[i].table_offset=table_offset;
found_set[i].found_offset=found_offset;
found_sets++; return -i-2; /* return new position */
}
/* Return 1 if regexp starts with \b or ends with \b*/
/* Functions that uses replace and replace_regex */
/* Append the string to ds, with optional replace */ void replace_dynstr_append_mem(DYNAMIC_STRING *ds, constchar *val, size_t len)
{ char lower[1024];
if (len < sizeof(lower) - 1)
{ if (display_result_lower)
{ /* Convert to lower case, and do this first */ char *c= lower; for (constchar *v= val, *end_v= v + len; v < end_v; v++)
*c++= my_tolower(charset_info, *v);
*c= '\0'; /* Copy from this buffer instead */
} else
{
memcpy(lower, val, len);
lower[len]= 0;
}
fix_win_paths(lower, len);
val= lower;
}
if (glob_replace_regex)
{ /* Regex replace */ if (!multi_reg_replace(glob_replace_regex, (char*)val))
{
val= glob_replace_regex->buf;
len= strlen(val);
}
}
if (glob_replace)
{ /* Normal replace */
replace_strings_append(glob_replace, ds, val);
} else
dynstr_append_mem(ds, val, len);
}
/* Append zero-terminated string to ds, with optional replace */ void replace_dynstr_append(DYNAMIC_STRING *ds, constchar *val)
{
replace_dynstr_append_mem(ds, val, strlen(val));
}
/* Append uint to ds, with optional replace */ void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val)
{ char buff[22]; /* This should be enough for any int */ char *end= longlong10_to_str(val, buff, 10);
replace_dynstr_append_mem(ds, buff, end - buff);
}
/* Create new result */ for (i= 0; i < lines.elements ; i++)
{ constchar **line= dynamic_element(&lines, i, constchar**);
dynstr_append_mem(ds, *line, strlen(*line));
dynstr_append_mem(ds, STRING_WITH_LEN("\n"));
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.