mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
Added --replace to mysqltest
Fixed that GROUP BY can take DESC
This commit is contained in:
@ -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,8 +108,7 @@ struct connection
|
||||
char *name;
|
||||
};
|
||||
|
||||
typedef
|
||||
struct
|
||||
typedef struct
|
||||
{
|
||||
int read_lines,current_line;
|
||||
} PARSER;
|
||||
@ -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)
|
||||
|
@ -1842,7 +1842,7 @@ group_clause:
|
||||
group_list:
|
||||
group_list ',' group_ident
|
||||
{ if (add_group_to_list($3,(bool) 1)) YYABORT; }
|
||||
| group_ident order_dir
|
||||
| group_ident
|
||||
{ if (add_group_to_list($1,(bool) 1)) YYABORT; }
|
||||
|
||||
/*
|
||||
@ -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; }
|
||||
|
Reference in New Issue
Block a user