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}. ...@@ -7011,7 +7011,8 @@ table. @xref{Crashing}.
To get a core dump on Linux if mysqld dies with a SIGSEGV 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 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 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, If you are using LinuxThreads and @code{mysqladmin shutdown} doesn't work,
you must upgrade to LinuxThreads Version 0.7.1 or newer. 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 ...@@ -13465,7 +13466,7 @@ One way to avoid this problem is to start @code{mysqld} with @code{-O
lower_case_table_names=1}. lower_case_table_names=1}.
In this case @strong{MySQL} will convert all table names to lower case on 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. names to lower case before starting @code{mysqld} with this option.
@cindex variables, user @cindex variables, user
...@@ -20227,6 +20228,9 @@ or SHOW LOGS ...@@ -20227,6 +20228,9 @@ or SHOW LOGS
or SHOW [FULL] PROCESSLIST or SHOW [FULL] PROCESSLIST
or SHOW GRANTS FOR user or SHOW GRANTS FOR user
or SHOW CREATE TABLE table_name or SHOW CREATE TABLE table_name
or SHOW MASTER STATUS
or SHOW MASTER LOGS
or SHOW SLAVE STATUS
@end example @end example
@code{SHOW} provides information about databases, tables, columns, or @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 ...@@ -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 restarting, and the slave will read its master from @code{my.cnf} or the
command line. (Slave) command line. (Slave)
@item @code{SHOW MASTER STATUS} @item @code{SHOW MASTER STATUS} @tab Provides status information on the binlog of the master. (Master)
@tab Provides status information on the binlog of the master. (Master)
@item @code{SHOW SLAVE STATUS} @item @code{SHOW SLAVE STATUS} @tab Provides status information on essential parameters of the slave thread. (Slave)
@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 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'} @item @code{PURGE MASTER LOGS TO 'logname'}
@tab Available starting in Version 3.23.28. Deletes all the @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: ...@@ -26442,13 +26442,12 @@ log index, so that the given log now becomes first. Example:
PURGE MASTER LOGS TO 'mysql-bin.010' PURGE MASTER LOGS TO 'mysql-bin.010'
@end example @end example
This command will do nothing and fail with an error This command will do nothing and fail with an error if you have an
if you have an active slave that active slave that is currently reading one of the logs you are trying to
is currently reading one of the logs you are trying to delete. However, delete. However, if you have a dormant slave, and happen to purge one of
if you have a dormant slave, and happen to purge one of the logs it the logs it wants to read, the slave will be unable to replicate once it
wants to read, the slave will be unable to replicate once it comes up. comes up. The command is safe to run while slaves are replicating - you
The command is safe to run while slaves are replicating - you do not do not need to stop them.
need to stop them.
You must first check all the slaves with @code{SHOW SLAVE STATUS} to 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 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 ...@@ -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 could have opened another connection, which resulted in another
@code{Binlog_Dump} thread. @code{Binlog_Dump} thread.
The above problem should not be present in Version 3.23.26 and later versions. The above problem should not be present in Version 3.23.26 and later
In Version 3.23.26 we added @code{server-id} to each replication server, and versions. In Version 3.23.26 we added @code{server-id} to each
now all the old zombie threads are killed on the master when a new replication thread replication server, and now all the old zombie threads are killed on the
connects from the same slave master when a new replication thread connects from the same slave
@strong{Q}: How do I rotate replication logs? @strong{Q}: How do I rotate replication logs?
@strong{A}: In Version 3.23.28 you should use @code{PURGE MASTER LOGS TO} @strong{A}: In Version 3.23.28 you should use @code{PURGE MASTER LOGS
command after determining which logs can be deleted, and optionally TO} command after determining which logs can be deleted, and optionally
backing them up first. In earlier versions the process is much more backing them up first. In earlier versions the process is much more
painful, and cannot be safely done without stopping all the slaves in painful, and cannot be safely done without stopping all the slaves in
the case that you plan to re-use log names . the case that you plan to re-use log names. You will need to stop the
You will need to stop the slave threads, edit the binary log index slave threads, edit the binary log index file, delete all the old logs,
file, delete all the old logs, restart the master, start slave threads, restart the master, start slave threads,and then remove the old log files.
and then remove the old log files.
@strong{Q}: How do I upgrade on a hot replication setup? @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 ...@@ -26811,14 +26809,14 @@ sketchy information, it would take us a while to track down the problem. The
evidence you should collect is: evidence you should collect is:
@itemize @bullet @itemize @bullet
@item @item
all binary logs on the master All binary logs on the master
@item @item
all binary log on the slave All binary log on the slave
@item @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 you have discovered the problem
@item @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 you have discovered the problem
@item @item
Error logs on the master and on the slave Error logs on the master and on the slave
...@@ -29107,7 +29105,7 @@ Path to @code{mysqld} ...@@ -29107,7 +29105,7 @@ Path to @code{mysqld}
Name of the mysqld version in the @code{ledir} directory you want to start. Name of the mysqld version in the @code{ledir} directory you want to start.
@item --no-defaults @item --no-defaults
@item --open-files-limit=# @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 --pid-file=path
@item --port=# @item --port=#
@item --socket=path @item --socket=path
...@@ -35670,19 +35668,7 @@ None. ...@@ -35670,19 +35668,7 @@ None.
@subsubheading Errors @subsubheading Errors
@table @code None.
@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
@findex @code{mysql_connect()} @findex @code{mysql_connect()}
@node mysql_connect, mysql_change_user, mysql_close, C API functions @node mysql_connect, mysql_change_user, mysql_close, C API functions
...@@ -41175,7 +41161,7 @@ not yet 100 % confident in this code. ...@@ -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 @node News-3.23.33, News-3.23.32, News-3.23.x, News-3.23.x
@appendixsubsec Changes in release 3.23.33 @appendixsubsec Changes in release 3.23.33
@itemize bullet @itemize @bullet
@item @item
Added @code{--character-sets-dir} to @code{myisampack}. Added @code{--character-sets-dir} to @code{myisampack}.
@item @item
...@@ -108,11 +108,10 @@ struct connection ...@@ -108,11 +108,10 @@ struct connection
char *name; char *name;
}; };
typedef typedef struct
struct {
{ int read_lines,current_line;
int read_lines,current_line; } PARSER;
} PARSER;
PARSER parser; PARSER parser;
MASTER_POS master_pos; MASTER_POS master_pos;
...@@ -151,13 +150,14 @@ struct st_query ...@@ -151,13 +150,14 @@ struct st_query
Q_DISCONNECT,Q_LET, Q_ECHO, Q_WHILE, Q_END_BLOCK, Q_DISCONNECT,Q_LET, Q_ECHO, Q_WHILE, Q_END_BLOCK,
Q_SYSTEM, Q_RESULT, Q_REQUIRE, Q_SAVE_MASTER_POS, Q_SYSTEM, Q_RESULT, Q_REQUIRE, Q_SAVE_MASTER_POS,
Q_SYNC_WITH_MASTER, Q_ERROR, Q_SEND, Q_REAP, Q_DIRTY_CLOSE, Q_SYNC_WITH_MASTER, Q_ERROR, Q_SEND, Q_REAP, Q_DIRTY_CLOSE,
Q_REPLACE,
Q_UNKNOWN, Q_COMMENT, Q_COMMENT_WITH_COMMAND} type; Q_UNKNOWN, Q_COMMENT, Q_COMMENT_WITH_COMMAND} type;
}; };
const char *command_names[] = { const char *command_names[] = {
"connection", "query","connect","sleep","inc","dec","source","disconnect", "connection", "query","connect","sleep","inc","dec","source","disconnect",
"let","echo","while","end","system","result", "require", "save_master_pos", "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),"", TYPELIB command_typelib= {array_elements(command_names),"",
...@@ -171,6 +171,30 @@ void reject_dump(const char* record_file, char* buf, int size); ...@@ -171,6 +171,30 @@ void reject_dump(const char* record_file, char* buf, int size);
int close_connection(struct st_query* q); int close_connection(struct st_query* q);
VAR* var_get(char* var_name, char* var_name_end, int raw); 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() static void close_cons()
{ {
DBUG_ENTER("close_cons"); DBUG_ENTER("close_cons");
...@@ -610,7 +634,6 @@ static void get_ints(uint *to,struct st_query* q) ...@@ -610,7 +634,6 @@ static void get_ints(uint *to,struct st_query* q)
long val; long val;
DBUG_ENTER("get_ints"); DBUG_ENTER("get_ints");
while (*p && isspace(*p)) p++;
if (!*p) if (!*p)
die("Missing argument in %s\n", q->query); die("Missing argument in %s\n", q->query);
...@@ -624,6 +647,123 @@ static void get_ints(uint *to,struct st_query* q) ...@@ -624,6 +647,123 @@ static void get_ints(uint *to,struct st_query* q)
DBUG_VOID_RETURN; 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) int select_connection(struct st_query* q)
{ {
...@@ -845,6 +985,7 @@ int safe_copy_unescape(char* dest, char* src, int size) ...@@ -845,6 +985,7 @@ int safe_copy_unescape(char* dest, char* src, int size)
return (p_dest - dest); return (p_dest - dest);
} }
int read_line(char* buf, int size) int read_line(char* buf, int size)
{ {
int c; int c;
...@@ -1340,11 +1481,20 @@ int run_query(MYSQL* mysql, struct st_query* q, int flags) ...@@ -1340,11 +1481,20 @@ int run_query(MYSQL* mysql, struct st_query* q, int flags)
if (i) if (i)
dynstr_append_mem(ds, "\t", 1); 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, val, len);
} }
dynstr_append_mem(ds, "\n", 1); dynstr_append_mem(ds, "\n", 1);
} }
if (glob_replace)
free_replace();
if (record) if (record)
{ {
...@@ -1481,6 +1631,9 @@ int main(int argc, char** argv) ...@@ -1481,6 +1631,9 @@ int main(int argc, char** argv)
get_file_name(save_file,q); get_file_name(save_file,q);
require_file=1; require_file=1;
break; break;
case Q_REPLACE:
get_replace(q);
break;
case Q_SAVE_MASTER_POS: do_save_master_pos(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_SYNC_WITH_MASTER: do_sync_with_master(q); break;
case Q_COMMENT: /* Ignore row */ case Q_COMMENT: /* Ignore row */
...@@ -1523,3 +1676,718 @@ int main(int argc, char** argv) ...@@ -1523,3 +1676,718 @@ int main(int argc, char** argv)
exit(error); exit(error);
return 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; ...@@ -4,6 +4,7 @@ connection con1;
set SQL_LOG_BIN=0; set SQL_LOG_BIN=0;
drop table if exists t1; drop table if exists t1;
create table t1(n int); create table t1(n int);
--replace "errno = 2" "errno = X" "errno = 22" "errno = X"
backup table t1 to '../bogus'; backup table t1 to '../bogus';
backup table t1 to '../tmp'; backup table t1 to '../tmp';
drop table t1; drop table t1;
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
# as such, and clarify ones such as "mediumint" with comments such as # as such, and clarify ones such as "mediumint" with comments such as
# "3-byte int" or "same as xxx". # "3-byte int" or "same as xxx".
$version="1.55"; $version="1.56";
use DBI; use DBI;
use Getopt::Long; use Getopt::Long;
...@@ -1333,7 +1333,7 @@ report("index in create table",'index_in_create', ...@@ -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 # The following must be executed as we need the value of end_drop_keyword
# later # 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)")) if ($res=safe_query("create index crash_q on crash_me (a)"))
{ {
......
...@@ -1870,7 +1870,7 @@ double Item_func_match::val() ...@@ -1870,7 +1870,7 @@ double Item_func_match::val()
if (ft_handler==NULL) if (ft_handler==NULL)
init_search(1); init_search(1);
if (null_value=(ft_handler==NULL)) if ((null_value= (ft_handler==NULL)))
return 0.0; return 0.0;
if (join_key) if (join_key)
......
...@@ -1841,9 +1841,9 @@ group_clause: ...@@ -1841,9 +1841,9 @@ group_clause:
group_list: group_list:
group_list ',' group_ident group_list ',' group_ident
{ if (add_group_to_list($3,(bool) 1)) YYABORT; } { if (add_group_to_list($3,(bool) 1)) YYABORT; }
| group_ident order_dir | group_ident
{ if (add_group_to_list($1,(bool) 1)) YYABORT; } { if (add_group_to_list($1,(bool) 1)) YYABORT; }
/* /*
** Order by statement in select ** Order by statement in select
...@@ -2410,7 +2410,7 @@ table_wild: ...@@ -2410,7 +2410,7 @@ table_wild:
{ $$ = new Item_field((current_thd->client_capabilities & CLIENT_NO_SCHEMA ? NullS : $1.str),$3.str,"*"); } { $$ = new Item_field((current_thd->client_capabilities & CLIENT_NO_SCHEMA ? NullS : $1.str),$3.str,"*"); }
group_ident: group_ident:
order_ident order_ident order_dir
order_ident: order_ident:
expr { $$=$1; } 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