Commit fbedc971 authored by monty@donna.mysql.com's avatar monty@donna.mysql.com

Added --replace to mysqltest

Fixed that GROUP BY can take DESC
parent ffe30298
......@@ -7011,7 +7011,8 @@ table. @xref{Crashing}.
To get a core dump on Linux if mysqld dies with a SIGSEGV
signal, you can start mysqld with the @code{--core-file} option. Note
that you also probably need to raise the @code{core file size} by adding
@code{ulimit -c 1000000} to @code{safe_mysqld}. @xref{safe_mysqld}.
@code{ulimit -c 1000000} to @code{safe_mysqld} or starting @code{safe_mysqld}
with @code{--core-file-sizes=1000000}. @xref{safe_mysqld}.
If you are using LinuxThreads and @code{mysqladmin shutdown} doesn't work,
you must upgrade to LinuxThreads Version 0.7.1 or newer.
......@@ -13465,7 +13466,7 @@ One way to avoid this problem is to start @code{mysqld} with @code{-O
lower_case_table_names=1}.
In this case @strong{MySQL} will convert all table names to lower case on
storage and lookup. Not that you need to first convert your old table
storage and lookup. Note that you need to first convert your old table
names to lower case before starting @code{mysqld} with this option.
@cindex variables, user
......@@ -20227,6 +20228,9 @@ or SHOW LOGS
or SHOW [FULL] PROCESSLIST
or SHOW GRANTS FOR user
or SHOW CREATE TABLE table_name
or SHOW MASTER STATUS
or SHOW MASTER LOGS
or SHOW SLAVE STATUS
@end example
@code{SHOW} provides information about databases, tables, columns, or
......@@ -26423,14 +26427,10 @@ If this is not desirable, you should delete the @file{master.info} file before
restarting, and the slave will read its master from @code{my.cnf} or the
command line. (Slave)
@item @code{SHOW MASTER STATUS}
@tab Provides status information on the binlog of the master. (Master)
@item @code{SHOW MASTER STATUS} @tab Provides status information on the binlog of the master. (Master)
@item @code{SHOW SLAVE STATUS}
@tab Provides status information on essential parameters of the slave thread. (Slave)
@item @code{SHOW MASTER LOGS}
@tab Only available starting in Version 3.23.28. Lists the binary logs on the master. You should use this command
prior to @code{PURGE MASTER LOGS TO} to find out how far you should go.
@item @code{SHOW SLAVE STATUS} @tab Provides status information on essential parameters of the slave thread. (Slave)
@item @code{SHOW MASTER LOGS} @tab Only available starting in Version 3.23.28. Lists the binary logs on the master. You should use this command prior to @code{PURGE MASTER LOGS TO} to find out how far you should go.
@item @code{PURGE MASTER LOGS TO 'logname'}
@tab Available starting in Version 3.23.28. Deletes all the
......@@ -26442,13 +26442,12 @@ log index, so that the given log now becomes first. Example:
PURGE MASTER LOGS TO 'mysql-bin.010'
@end example
This command will do nothing and fail with an error
if you have an active slave that
is currently reading one of the logs you are trying to delete. However,
if you have a dormant slave, and happen to purge one of the logs it
wants to read, the slave will be unable to replicate once it comes up.
The command is safe to run while slaves are replicating - you do not
need to stop them.
This command will do nothing and fail with an error if you have an
active slave that is currently reading one of the logs you are trying to
delete. However, if you have a dormant slave, and happen to purge one of
the logs it wants to read, the slave will be unable to replicate once it
comes up. The command is safe to run while slaves are replicating - you
do not need to stop them.
You must first check all the slaves with @code{SHOW SLAVE STATUS} to
see which log they are on, then do a listing of the logs on the
......@@ -26490,21 +26489,20 @@ it up from @code{pthread_cond_wait()}. In the meantime, the slave
could have opened another connection, which resulted in another
@code{Binlog_Dump} thread.
The above problem should not be present in Version 3.23.26 and later versions.
In Version 3.23.26 we added @code{server-id} to each replication server, and
now all the old zombie threads are killed on the master when a new replication thread
connects from the same slave
The above problem should not be present in Version 3.23.26 and later
versions. In Version 3.23.26 we added @code{server-id} to each
replication server, and now all the old zombie threads are killed on the
master when a new replication thread connects from the same slave
@strong{Q}: How do I rotate replication logs?
@strong{A}: In Version 3.23.28 you should use @code{PURGE MASTER LOGS TO}
command after determining which logs can be deleted, and optionally
@strong{A}: In Version 3.23.28 you should use @code{PURGE MASTER LOGS
TO} command after determining which logs can be deleted, and optionally
backing them up first. In earlier versions the process is much more
painful, and cannot be safely done without stopping all the slaves in
the case that you plan to re-use log names .
You will need to stop the slave threads, edit the binary log index
file, delete all the old logs, restart the master, start slave threads,
and then remove the old log files.
the case that you plan to re-use log names. You will need to stop the
slave threads, edit the binary log index file, delete all the old logs,
restart the master, start slave threads,and then remove the old log files.
@strong{Q}: How do I upgrade on a hot replication setup?
......@@ -26811,14 +26809,14 @@ sketchy information, it would take us a while to track down the problem. The
evidence you should collect is:
@itemize @bullet
@item
all binary logs on the master
All binary logs on the master
@item
all binary log on the slave
All binary log on the slave
@item
the output of @code{SHOW MASTER STATUS} on the master at the time
The output of @code{SHOW MASTER STATUS} on the master at the time
you have discovered the problem
@item
the output of @code{SHOW SLAVE STATUS} on the master at the time
The output of @code{SHOW SLAVE STATUS} on the master at the time
you have discovered the problem
@item
Error logs on the master and on the slave
......@@ -29107,7 +29105,7 @@ Path to @code{mysqld}
Name of the mysqld version in the @code{ledir} directory you want to start.
@item --no-defaults
@item --open-files-limit=#
Number of files @code{mysqld} should be able to open. Passed to @code{ulimit -n}. Not that you need to start @code{safe_mysqld} as root for this to work properly!
Number of files @code{mysqld} should be able to open. Passed to @code{ulimit -n}. Note that you need to start @code{safe_mysqld} as root for this to work properly!
@item --pid-file=path
@item --port=#
@item --socket=path
......@@ -35670,19 +35668,7 @@ None.
@subsubheading Errors
@table @code
@item CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
@item CR_SERVER_GONE_ERROR
The @strong{MySQL} server has gone away.
@item CR_SERVER_LOST
The connection to the server was lost during the query.
@item CR_UNKNOWN_ERROR
An unknown error occurred.
@end table
None.
@findex @code{mysql_connect()}
@node mysql_connect, mysql_change_user, mysql_close, C API functions
......@@ -41175,7 +41161,7 @@ not yet 100 % confident in this code.
@node News-3.23.33, News-3.23.32, News-3.23.x, News-3.23.x
@appendixsubsec Changes in release 3.23.33
@itemize bullet
@itemize @bullet
@item
Added @code{--character-sets-dir} to @code{myisampack}.
@item
......@@ -108,11 +108,10 @@ struct connection
char *name;
};
typedef
struct
{
int read_lines,current_line;
} PARSER;
typedef struct
{
int read_lines,current_line;
} PARSER;
PARSER parser;
MASTER_POS master_pos;
......@@ -151,13 +150,14 @@ struct st_query
Q_DISCONNECT,Q_LET, Q_ECHO, Q_WHILE, Q_END_BLOCK,
Q_SYSTEM, Q_RESULT, Q_REQUIRE, Q_SAVE_MASTER_POS,
Q_SYNC_WITH_MASTER, Q_ERROR, Q_SEND, Q_REAP, Q_DIRTY_CLOSE,
Q_REPLACE,
Q_UNKNOWN, Q_COMMENT, Q_COMMENT_WITH_COMMAND} type;
};
const char *command_names[] = {
"connection", "query","connect","sleep","inc","dec","source","disconnect",
"let","echo","while","end","system","result", "require", "save_master_pos",
"sync_with_master", "error", "send", "reap", "dirty_close", 0
"sync_with_master", "error", "send", "reap", "dirty_close", "replace", 0
};
TYPELIB command_typelib= {array_elements(command_names),"",
......@@ -171,6 +171,30 @@ void reject_dump(const char* record_file, char* buf, int size);
int close_connection(struct st_query* q);
VAR* var_get(char* var_name, char* var_name_end, int raw);
/* Definitions for replace */
typedef struct st_pointer_array { /* when using array-strings */
TYPELIB typelib; /* Pointer to strings */
byte *str; /* Strings is here */
int7 *flag; /* Flag about each var. */
uint array_allocs,max_count,length,max_length;
} POINTER_ARRAY;
struct st_replace;
struct st_replace *init_replace(my_string *from, my_string *to, uint count,
my_string word_end_chars);
uint replace_strings(struct st_replace *rep, my_string *start,
uint *max_length, my_string from);
static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name);
void free_pointer_array(POINTER_ARRAY *pa);
static int initialize_replace_buffer(void);
static void free_replace_buffer(void);
struct st_replace *glob_replace;
static char *out_buff;
static uint out_length;
static void close_cons()
{
DBUG_ENTER("close_cons");
......@@ -610,7 +634,6 @@ static void get_ints(uint *to,struct st_query* q)
long val;
DBUG_ENTER("get_ints");
while (*p && isspace(*p)) p++;
if (!*p)
die("Missing argument in %s\n", q->query);
......@@ -624,6 +647,123 @@ static void get_ints(uint *to,struct st_query* q)
DBUG_VOID_RETURN;
}
/*
Get a string; Return ptr to end of string
Strings may be surrounded by " or '
*/
static void get_string(char **to_ptr, char **from_ptr,
struct st_query* q)
{
reg1 char c,sep;
char *to= *to_ptr, *from= *from_ptr;
DBUG_ENTER("get_string");
/* 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;
}
}
else if (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\n", q->query);
while (isspace(*from)) /* Point to next string */
from++;
*to++ =0; /* End of string marker */
*to_ptr= to;
*from_ptr= from;
}
/*
Get arguments for replace. The syntax is:
replace from to [from to ...]
Where each argument may be quoted with ' or "
*/
static void get_replace(struct st_query *q)
{
uint i;
char *from=q->first_argument;
char *buff=my_malloc(strlen(from),MYF(MY_WME | MY_FAE));
char word_end_chars[256],*pos;
POINTER_ARRAY to_array,from_array;
DBUG_ENTER("get_replace");
bzero((char*) &to_array,sizeof(to_array));
bzero((char*) &from_array,sizeof(from_array));
if (!*from)
die("Missing argument in %s\n", q->query);
while (*from)
{
char *to=buff;
get_string(&buff, &from, q);
if (!*from)
die("Wrong number of arguments in %s\n", q->query);
insert_pointer_name(&from_array,to);
to=buff;
get_string(&buff, &from, q);
insert_pointer_name(&to_array,to);
}
for (i=1,pos=word_end_chars ; i < 256 ; i++)
if (isspace(i))
*pos++=i;
if (!(glob_replace=init_replace((char**) from_array.typelib.type_names,
(char**) to_array.typelib.type_names,
(uint) from_array.typelib.count,
word_end_chars)) ||
initialize_replace_buffer())
die("Can't initialize replace from %s\n", q->query);
free_pointer_array(&from_array);
free_pointer_array(&to_array);
my_free(buff, MYF(0));
}
void free_replace()
{
my_free((char*) glob_replace,MYF(0));
free_replace_buffer();
}
int select_connection(struct st_query* q)
{
......@@ -845,6 +985,7 @@ int safe_copy_unescape(char* dest, char* src, int size)
return (p_dest - dest);
}
int read_line(char* buf, int size)
{
int c;
......@@ -1340,11 +1481,20 @@ int run_query(MYSQL* mysql, struct st_query* q, int flags)
if (i)
dynstr_append_mem(ds, "\t", 1);
if (glob_replace)
{
len=(int) replace_strings(glob_replace, &out_buff, &out_length, val);
if (len == -1)
die("Out of memory in replace\n");
val=out_buff;
}
dynstr_append_mem(ds, val, len);
}
dynstr_append_mem(ds, "\n", 1);
}
if (glob_replace)
free_replace();
if (record)
{
......@@ -1481,6 +1631,9 @@ int main(int argc, char** argv)
get_file_name(save_file,q);
require_file=1;
break;
case Q_REPLACE:
get_replace(q);
break;
case Q_SAVE_MASTER_POS: do_save_master_pos(q); break;
case Q_SYNC_WITH_MASTER: do_sync_with_master(q); break;
case Q_COMMENT: /* Ignore row */
......@@ -1523,3 +1676,718 @@ int main(int argc, char** argv)
exit(error);
return error;
}
/****************************************************************************
* Handle replacement of strings
****************************************************************************/
#define PC_MALLOC 256 /* Bytes for pointers */
#define PS_MALLOC 512 /* Bytes for data */
#define SPACE_CHAR 256
#define START_OF_LINE 257
#define END_OF_LINE 258
#define LAST_CHAR_CODE 259
typedef struct st_replace {
bool found;
struct st_replace *next[256];
} REPLACE;
typedef struct st_replace_found {
bool found;
char *replace_string;
uint to_offset;
int from_offset;
} REPLACE_STRING;
#ifndef WORD_BIT
#define WORD_BIT (8*sizeof(uint))
#endif
static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name)
{
uint i,length,old_count;
byte *new_pos;
const char **new_array;
DBUG_ENTER("insert_pointer_name");
if (! pa->typelib.count)
{
if (!(pa->typelib.type_names=(const char **)
my_malloc(((PC_MALLOC-MALLOC_OVERHEAD)/
(sizeof(my_string)+sizeof(*pa->flag))*
(sizeof(my_string)+sizeof(*pa->flag))),MYF(MY_WME))))
DBUG_RETURN(-1);
if (!(pa->str= (byte*) my_malloc((uint) (PS_MALLOC-MALLOC_OVERHEAD),
MYF(MY_WME))))
{
my_free((gptr) pa->typelib.type_names,MYF(0));
DBUG_RETURN (-1);
}
pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(byte*)+
sizeof(*pa->flag));
pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
pa->length=0;
pa->max_length=PS_MALLOC-MALLOC_OVERHEAD;
pa->array_allocs=1;
}
length=(uint) strlen(name)+1;
if (pa->length+length >= pa->max_length)
{
if (!(new_pos= (byte*) my_realloc((gptr) pa->str,
(uint) (pa->max_length+PS_MALLOC),
MYF(MY_WME))))
DBUG_RETURN(1);
if (new_pos != pa->str)
{
my_ptrdiff_t diff=PTR_BYTE_DIFF(new_pos,pa->str);
for (i=0 ; i < pa->typelib.count ; i++)
pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff,
char*);
pa->str=new_pos;
}
pa->max_length+=PS_MALLOC;
}
if (pa->typelib.count >= pa->max_count-1)
{
int len;
pa->array_allocs++;
len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD);
if (!(new_array=(const char **) my_realloc((gptr) pa->typelib.type_names,
(uint) len/
(sizeof(byte*)+sizeof(*pa->flag))*
(sizeof(byte*)+sizeof(*pa->flag)),
MYF(MY_WME))))
DBUG_RETURN(1);
pa->typelib.type_names=new_array;
old_count=pa->max_count;
pa->max_count=len/(sizeof(byte*) + sizeof(*pa->flag));
pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
memcpy((byte*) pa->flag,(my_string) (pa->typelib.type_names+old_count),
old_count*sizeof(*pa->flag));
}
pa->flag[pa->typelib.count]=0; /* Reset flag */
pa->typelib.type_names[pa->typelib.count++]= pa->str+pa->length;
pa->typelib.type_names[pa->typelib.count]= NullS; /* Put end-mark */
VOID(strmov(pa->str+pa->length,name));
pa->length+=length;
DBUG_RETURN(0);
} /* insert_pointer_name */
/* free pointer array */
void free_pointer_array(POINTER_ARRAY *pa)
{
if (pa->typelib.count)
{
pa->typelib.count=0;
my_free((gptr) pa->typelib.type_names,MYF(0));
pa->typelib.type_names=0;
my_free((gptr) pa->str,MYF(0));
}
return;
} /* free_pointer_array */
/* Code for replace rutines */
#define SET_MALLOC_HUNC 64
typedef struct 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 convinience */
} REP_SET;
typedef struct 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;
typedef struct st_found_set {
uint table_offset;
int found_offset;
} FOUND_SET;
typedef struct st_follow {
int chr;
uint table_offset;
uint len;
} FOLLOWS;
static int init_sets(REP_SETS *sets,uint states);
static REP_SET *make_new_set(REP_SETS *sets);
static void make_sets_invisible(REP_SETS *sets);
static void free_last_set(REP_SETS *sets);
static void free_sets(REP_SETS *sets);
static void set_bit(REP_SET *set, uint bit);
static void clear_bit(REP_SET *set, uint bit);
static void or_bits(REP_SET *to,REP_SET *from);
static void copy_bits(REP_SET *to,REP_SET *from);
static int cmp_bits(REP_SET *set1,REP_SET *set2);
static int get_next_bit(REP_SET *set,uint lastpos);
static int find_set(REP_SETS *sets,REP_SET *find);
static int find_found(FOUND_SET *found_set,uint table_offset,
int found_offset);
static uint start_at_word(my_string pos);
static uint end_of_word(my_string pos);
static uint replace_len(my_string pos);
static uint found_sets=0;
/* Init a replace structure for further calls */
REPLACE *init_replace(my_string *from, my_string *to,uint count,
my_string word_end_chars)
{
uint i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr;
int used_sets,chr,default_state;
char used_chars[LAST_CHAR_CODE],is_word_end[256];
my_string pos,to_pos,*to_array;
REP_SETS sets;
REP_SET *set,*start_states,*word_states,*new_set;
FOLLOWS *follow,*follow_ptr;
REPLACE *replace;
FOUND_SET *found_set;
REPLACE_STRING *rep_str;
DBUG_ENTER("init_replace");
/* 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;
my_message(0,"No to-string for last from-string",MYF(ME_BELL));
DBUG_RETURN(0);
}
states+=len+1;
result_len+=(uint) strlen(to[i])+1;
if (len > max_length)
max_length=len;
}
bzero((char*) 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(sizeof(FOUND_SET)*max_length*count,
MYF(MY_WME))))
{
free_sets(&sets);
DBUG_RETURN(0);
}
VOID(make_new_set(&sets)); /* Set starting set */
make_sets_invisible(&sets); /* Hide previus 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((states+2)*sizeof(FOLLOWS),MYF(MY_WME))))
{
free_sets(&sets);
my_free((gptr) found_set,MYF(0));
DBUG_RETURN(0);
}
/* Init follow_ptr[] */
for (i=0, states=1, follow_ptr=follow+1 ; i < count ; i++)
{
if (from[i][0] == '\\' && from[i][1] == '^')
{
set_bit(start_states,states+1);
if (!from[i][2])
{
start_states->table_offset=i;
start_states->found_offset=1;
}
}
else if (from[i][0] == '\\' && from[i][1] == '$')
{
set_bit(start_states,states);
set_bit(word_states,states);
if (!from[i][2] && start_states->table_offset == (uint) ~0)
{
start_states->table_offset=i;
start_states->found_offset=0;
}
}
else
{
set_bit(word_states,states);
if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2]))
set_bit(start_states,states+1);
else
set_bit(start_states,states);
}
for (pos=from[i], len=0; *pos ; pos++)
{
if (*pos == '\\' && *(pos+1))
{
pos++;
switch (*pos) {
case 'b':
follow_ptr->chr = SPACE_CHAR;
break;
case '^':
follow_ptr->chr = START_OF_LINE;
break;
case '$':
follow_ptr->chr = END_OF_LINE;
break;
case 'r':
follow_ptr->chr = '\r';
break;
case 't':
follow_ptr->chr = '\t';
break;
case 'v':
follow_ptr->chr = '\v';
break;
default:
follow_ptr->chr = (uchar) *pos;
break;
}
}
else
follow_ptr->chr= (uchar) *pos;
follow_ptr->table_offset=i;
follow_ptr->len= ++len;
follow_ptr++;
}
follow_ptr->chr=0;
follow_ptr->table_offset=i;
follow_ptr->len=len;
follow_ptr++;
states+=(uint) len+1;
}
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((char*) 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)
set_bit(new_set,i+1); /* To next set */
else
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)))
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(sizeof(REPLACE)*(sets.count)+
sizeof(REPLACE_STRING)*(found_sets+1)+
sizeof(my_string)*count+result_len,
MYF(MY_WME | MY_ZEROFILL))))
{
rep_str=(REPLACE_STRING*) (replace+sets.count);
to_array=(my_string*) (rep_str+found_sets+1);
to_pos=(my_string) (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= !bcmp(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((gptr) follow,MYF(0));
free_sets(&sets);
my_free((gptr) found_set,MYF(0));
DBUG_PRINT("exit",("Replace table has %d states",sets.count));
DBUG_RETURN(replace);
}
static int init_sets(REP_SETS *sets,uint states)
{
bzero((char*) sets,sizeof(*sets));
sets->size_of_bits=((states+7)/8);
if (!(sets->set_buffer=(REP_SET*) my_malloc(sizeof(REP_SET)*SET_MALLOC_HUNC,
MYF(MY_WME))))
return 1;
if (!(sets->bit_buffer=(uint*) my_malloc(sizeof(uint)*sets->size_of_bits*
SET_MALLOC_HUNC,MYF(MY_WME))))
{
my_free((gptr) sets->set,MYF(0));
return 1;
}
return 0;
}
/* Make help sets invisible for nicer codeing */
static void make_sets_invisible(REP_SETS *sets)
{
sets->invisible=sets->count;
sets->set+=sets->count;
sets->count=0;
}
static REP_SET *make_new_set(REP_SETS *sets)
{
uint i,count,*bit_buffer;
REP_SET *set;
if (sets->extra)
{
sets->extra--;
set=sets->set+ sets->count++;
bzero((char*) set->bits,sizeof(uint)*sets->size_of_bits);
bzero((char*) &set->next[0],sizeof(set->next[0])*LAST_CHAR_CODE);
set->found_offset=0;
set->found_len=0;
set->table_offset= (uint) ~0;
set->size_of_bits=sets->size_of_bits;
return set;
}
count=sets->count+sets->invisible+SET_MALLOC_HUNC;
if (!(set=(REP_SET*) my_realloc((gptr) sets->set_buffer,
sizeof(REP_SET)*count,
MYF(MY_WME))))
return 0;
sets->set_buffer=set;
sets->set=set+sets->invisible;
if (!(bit_buffer=(uint*) my_realloc((gptr) sets->bit_buffer,
(sizeof(uint)*sets->size_of_bits)*count,
MYF(MY_WME))))
return 0;
sets->bit_buffer=bit_buffer;
for (i=0 ; i < count ; i++)
{
sets->set_buffer[i].bits=bit_buffer;
bit_buffer+=sets->size_of_bits;
}
sets->extra=SET_MALLOC_HUNC;
return make_new_set(sets);
}
static void free_last_set(REP_SETS *sets)
{
sets->count--;
sets->extra++;
return;
}
static void free_sets(REP_SETS *sets)
{
my_free((gptr)sets->set_buffer,MYF(0));
my_free((gptr)sets->bit_buffer,MYF(0));
return;
}
static void set_bit(REP_SET *set, uint bit)
{
set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
return;
}
static void clear_bit(REP_SET *set, uint bit)
{
set->bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT));
return;
}
static void or_bits(REP_SET *to,REP_SET *from)
{
reg1 uint i;
for (i=0 ; i < to->size_of_bits ; i++)
to->bits[i]|=from->bits[i];
return;
}
static void copy_bits(REP_SET *to,REP_SET *from)
{
memcpy((byte*) to->bits,(byte*) from->bits,
(size_t) (sizeof(uint) * to->size_of_bits));
}
static int cmp_bits(REP_SET *set1,REP_SET *set2)
{
return bcmp((byte*) set1->bits,(byte*) set2->bits,
sizeof(uint) * set1->size_of_bits);
}
/* Get next set bit from set. */
static int get_next_bit(REP_SET *set,uint lastpos)
{
uint pos,*start,*end,bits;
start=set->bits+ ((lastpos+1) / WORD_BIT);
end=set->bits + set->size_of_bits;
bits=start[0] & ~((1 << ((lastpos+1) % WORD_BIT)) -1);
while (! bits && ++start < end)
bits=start[0];
if (!bits)
return 0;
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
free given set, else put in given set in sets and return it's
position */
static 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 postion */
}
/* find if there is a found_set with same table_offset & found_offset
If there is return offset to it, else add new offset and return pos.
Pos returned is -offset-2 in found_set_structure because it's is
saved in set->next and set->next[] >= 0 points to next set and
set->next[] == -1 is reserved for end without replaces.
*/
static 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 postion */
}
/* Return 1 if regexp starts with \b or ends with \b*/
static uint start_at_word(my_string pos)
{
return (((!bcmp(pos,"\\b",2) && pos[2]) || !bcmp(pos,"\\^",2)) ? 1 : 0);
}
static uint end_of_word(my_string pos)
{
my_string end=strend(pos);
return ((end > pos+2 && !bcmp(end-2,"\\b",2)) ||
(end >= pos+2 && !bcmp(end-2,"\\$",2))) ?
1 : 0;
}
static uint replace_len(my_string str)
{
uint len=0;
while (*str)
{
if (str[0] == '\\' && str[1])
str++;
str++;
len++;
}
return len;
}
/* Replace strings; Return length of result string */
uint replace_strings(REPLACE *rep, my_string *start,uint *max_length,
my_string from)
{
reg1 REPLACE *rep_pos;
reg2 REPLACE_STRING *rep_str;
my_string to,end,pos,new;
end=(to= *start) + *max_length-1;
rep_pos=rep+1;
for(;;)
{
while (!rep_pos->found)
{
rep_pos= rep_pos->next[(uchar) *from];
if (to == end)
{
(*max_length)+=8192;
if (!(new=my_realloc(*start,*max_length,MYF(MY_WME))))
return (uint) -1;
to=new+(to - *start);
end=(*start=new)+ *max_length-1;
}
*to++= *from++;
}
if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string)
return (uint) (to - *start)-1;
to-=rep_str->to_offset;
for (pos=rep_str->replace_string; *pos ; pos++)
{
if (to == end)
{
(*max_length)*=2;
if (!(new=my_realloc(*start,*max_length,MYF(MY_WME))))
return (uint) -1;
to=new+(to - *start);
end=(*start=new)+ *max_length-1;
}
*to++= *pos;
}
if (!*(from-=rep_str->from_offset) && rep_pos->found != 2)
return (uint) (to - *start);
rep_pos=rep;
}
}
static int initialize_replace_buffer(void)
{
out_length=8192;
if (!(out_buff=my_malloc(out_length,MYF(MY_WME))))
return(1);
return 0;
}
static void free_replace_buffer(void)
{
my_free(out_buff,MYF(MY_WME));
}
......@@ -4,6 +4,7 @@ connection con1;
set SQL_LOG_BIN=0;
drop table if exists t1;
create table t1(n int);
--replace "errno = 2" "errno = X" "errno = 22" "errno = X"
backup table t1 to '../bogus';
backup table t1 to '../tmp';
drop table t1;
......
......@@ -38,7 +38,7 @@
# as such, and clarify ones such as "mediumint" with comments such as
# "3-byte int" or "same as xxx".
$version="1.55";
$version="1.56";
use DBI;
use Getopt::Long;
......@@ -1333,7 +1333,7 @@ report("index in create table",'index_in_create',
# The following must be executed as we need the value of end_drop_keyword
# later
if (defined($limits{'create_index'}) && defined($limits{'drop_index'}))
if (!(defined($limits{'create_index'}) && defined($limits{'drop_index'})))
{
if ($res=safe_query("create index crash_q on crash_me (a)"))
{
......
......@@ -1870,7 +1870,7 @@ double Item_func_match::val()
if (ft_handler==NULL)
init_search(1);
if (null_value=(ft_handler==NULL))
if ((null_value= (ft_handler==NULL)))
return 0.0;
if (join_key)
......
......@@ -1841,9 +1841,9 @@ group_clause:
group_list:
group_list ',' group_ident
{ if (add_group_to_list($3,(bool) 1)) YYABORT; }
| group_ident order_dir
{ if (add_group_to_list($1,(bool) 1)) YYABORT; }
{ if (add_group_to_list($3,(bool) 1)) YYABORT; }
| group_ident
{ if (add_group_to_list($1,(bool) 1)) YYABORT; }
/*
** Order by statement in select
......@@ -2410,7 +2410,7 @@ table_wild:
{ $$ = new Item_field((current_thd->client_capabilities & CLIENT_NO_SCHEMA ? NullS : $1.str),$3.str,"*"); }
group_ident:
order_ident
order_ident order_dir
order_ident:
expr { $$=$1; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment